mirror of
https://github.com/winfsp/winfsp.git
synced 2025-04-22 08:23:05 -05:00
This commit adds a new Stderr registry setting that can be used to redirect the standard error output of a launched service instance.
488 lines
15 KiB
C
488 lines
15 KiB
C
/**
|
|
* @file dll/launch.c
|
|
*
|
|
* @copyright 2015-2020 Bill Zissimopoulos
|
|
*/
|
|
/*
|
|
* This file is part of WinFsp.
|
|
*
|
|
* You can redistribute it and/or modify it under the terms of the GNU
|
|
* General Public License version 3 as published by the Free Software
|
|
* Foundation.
|
|
*
|
|
* Licensees holding a valid commercial license may use this software
|
|
* in accordance with the commercial license agreement provided in
|
|
* conjunction with the software. The terms and conditions of any such
|
|
* commercial license agreement shall govern, supersede, and render
|
|
* ineffective any application of the GPLv3 license to this software,
|
|
* notwithstanding of any reference thereto in the software or
|
|
* associated repository.
|
|
*/
|
|
|
|
#include <dll/library.h>
|
|
|
|
FSP_API NTSTATUS FspLaunchCallLauncherPipe(
|
|
WCHAR Command, ULONG Argc, PWSTR *Argv, ULONG *Argl,
|
|
PWSTR Buffer, PULONG PSize,
|
|
PULONG PLauncherError)
|
|
{
|
|
return FspLaunchCallLauncherPipeEx(
|
|
Command, Argc, Argv, Argl, Buffer, PSize, FALSE, PLauncherError);
|
|
}
|
|
|
|
FSP_API NTSTATUS FspLaunchCallLauncherPipeEx(
|
|
WCHAR Command, ULONG Argc, PWSTR *Argv, ULONG *Argl,
|
|
PWSTR Buffer, PULONG PSize,
|
|
BOOLEAN AllowImpersonation,
|
|
PULONG PLauncherError)
|
|
{
|
|
PWSTR PipeBuf = 0, P;
|
|
ULONG Length, BytesTransferred;
|
|
NTSTATUS Result;
|
|
ULONG ErrorCode;
|
|
|
|
*PLauncherError = 0;
|
|
|
|
PipeBuf = MemAlloc(FSP_LAUNCH_PIPE_BUFFER_SIZE);
|
|
if (0 == PipeBuf)
|
|
{
|
|
Result = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto exit;
|
|
}
|
|
|
|
P = PipeBuf;
|
|
*P++ = Command;
|
|
for (ULONG I = 0; Argc > I; I++)
|
|
if (0 != Argv[I])
|
|
{
|
|
Length = 0 == Argl || -1 == Argl[I] ? lstrlenW(Argv[I]) : Argl[I];
|
|
if (FSP_LAUNCH_PIPE_BUFFER_SIZE < ((ULONG)(P - PipeBuf) + Length + 1) * sizeof(WCHAR))
|
|
{
|
|
Result = STATUS_INVALID_PARAMETER;
|
|
goto exit;
|
|
}
|
|
memcpy(P, Argv[I], Length * sizeof(WCHAR)); P += Length; *P++ = L'\0';
|
|
}
|
|
|
|
Result = FspCallNamedPipeSecurelyEx(L"" FSP_LAUNCH_PIPE_NAME,
|
|
PipeBuf, (ULONG)(P - PipeBuf) * sizeof(WCHAR), PipeBuf, FSP_LAUNCH_PIPE_BUFFER_SIZE,
|
|
&BytesTransferred, NMPWAIT_USE_DEFAULT_WAIT, AllowImpersonation, FSP_LAUNCH_PIPE_OWNER);
|
|
if (!NT_SUCCESS(Result))
|
|
goto exit;
|
|
|
|
Result = STATUS_SUCCESS;
|
|
ErrorCode = ERROR_BROKEN_PIPE; /* protocol error! */
|
|
if (sizeof(WCHAR) <= BytesTransferred)
|
|
{
|
|
if (FspLaunchCmdSuccess == PipeBuf[0])
|
|
{
|
|
ErrorCode = 0;
|
|
|
|
if (0 != PSize)
|
|
{
|
|
BytesTransferred -= sizeof(WCHAR);
|
|
memcpy(Buffer, PipeBuf + 1, *PSize < BytesTransferred ? *PSize : BytesTransferred);
|
|
*PSize = BytesTransferred;
|
|
}
|
|
}
|
|
else if (FspLaunchCmdFailure == PipeBuf[0])
|
|
{
|
|
ErrorCode = 0;
|
|
|
|
for (PWSTR P = PipeBuf + 1, EndP = PipeBuf + BytesTransferred / sizeof(WCHAR); EndP > P; P++)
|
|
{
|
|
if (L'0' > *P || *P > L'9')
|
|
break;
|
|
ErrorCode = 10 * ErrorCode + (*P - L'0');
|
|
}
|
|
|
|
if (0 == ErrorCode)
|
|
ErrorCode = ERROR_BROKEN_PIPE; /* protocol error! */
|
|
}
|
|
}
|
|
|
|
*PLauncherError = ErrorCode;
|
|
|
|
exit:
|
|
if (!NT_SUCCESS(Result) && 0 != PSize)
|
|
*PSize = 0;
|
|
|
|
MemFree(PipeBuf);
|
|
|
|
return Result;
|
|
}
|
|
|
|
FSP_API NTSTATUS FspLaunchStart(
|
|
PWSTR ClassName, PWSTR InstanceName, ULONG Argc, PWSTR *Argv,
|
|
BOOLEAN HasSecret,
|
|
PULONG PLauncherError)
|
|
{
|
|
return FspLaunchStartEx(ClassName, InstanceName, Argc, Argv, HasSecret, FALSE, PLauncherError);
|
|
}
|
|
|
|
FSP_API NTSTATUS FspLaunchStartEx(
|
|
PWSTR ClassName, PWSTR InstanceName, ULONG Argc, PWSTR *Argv0,
|
|
BOOLEAN HasSecret,
|
|
BOOLEAN AllowImpersonation,
|
|
PULONG PLauncherError)
|
|
{
|
|
PWSTR Argv[9 + 2];
|
|
|
|
if (9 < Argc)
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
Argv[0] = ClassName;
|
|
Argv[1] = InstanceName;
|
|
memcpy(Argv + 2, Argv0, Argc * sizeof(PWSTR));
|
|
|
|
return FspLaunchCallLauncherPipeEx(
|
|
HasSecret ? FspLaunchCmdStartWithSecret : FspLaunchCmdStart,
|
|
Argc + 2, Argv, 0, 0, 0, AllowImpersonation, PLauncherError);
|
|
}
|
|
|
|
FSP_API NTSTATUS FspLaunchStop(
|
|
PWSTR ClassName, PWSTR InstanceName,
|
|
PULONG PLauncherError)
|
|
{
|
|
PWSTR Argv[2];
|
|
|
|
Argv[0] = ClassName;
|
|
Argv[1] = InstanceName;
|
|
|
|
return FspLaunchCallLauncherPipe(FspLaunchCmdStop,
|
|
2, Argv, 0, 0, 0, PLauncherError);
|
|
}
|
|
|
|
FSP_API NTSTATUS FspLaunchGetInfo(
|
|
PWSTR ClassName, PWSTR InstanceName,
|
|
PWSTR Buffer, PULONG PSize,
|
|
PULONG PLauncherError)
|
|
{
|
|
PWSTR Argv[2];
|
|
|
|
Argv[0] = ClassName;
|
|
Argv[1] = InstanceName;
|
|
|
|
return FspLaunchCallLauncherPipe(FspLaunchCmdGetInfo,
|
|
2, Argv, 0, Buffer, PSize, PLauncherError);
|
|
}
|
|
|
|
FSP_API NTSTATUS FspLaunchGetNameList(
|
|
PWSTR Buffer, PULONG PSize,
|
|
PULONG PLauncherError)
|
|
{
|
|
return FspLaunchCallLauncherPipe(FspLaunchCmdGetNameList,
|
|
0, 0, 0, Buffer, PSize, PLauncherError);
|
|
}
|
|
|
|
FSP_API NTSTATUS FspLaunchRegSetRecord(
|
|
PWSTR ClassName,
|
|
const FSP_LAUNCH_REG_RECORD *Record)
|
|
{
|
|
#define SETFIELD(FieldName) \
|
|
do \
|
|
{ \
|
|
if (0 != Record->FieldName) \
|
|
{ \
|
|
RegResult = RegSetValueExW(RegKey,\
|
|
L"" #FieldName, 0, REG_SZ,\
|
|
(PVOID)Record->FieldName, (lstrlenW(Record->FieldName) + 1) * sizeof(WCHAR));\
|
|
if (ERROR_SUCCESS != RegResult)\
|
|
{ \
|
|
Result = FspNtStatusFromWin32(RegResult);\
|
|
goto exit; \
|
|
} \
|
|
} \
|
|
else \
|
|
{ \
|
|
RegResult = RegDeleteValueW(RegKey,\
|
|
L"" #FieldName);\
|
|
if (ERROR_SUCCESS != RegResult && ERROR_FILE_NOT_FOUND != RegResult)\
|
|
{ \
|
|
Result = FspNtStatusFromWin32(RegResult);\
|
|
goto exit; \
|
|
} \
|
|
} \
|
|
} while (0,0)
|
|
#define SETFIELDI(FieldName, Deflt) \
|
|
do \
|
|
{ \
|
|
if (Deflt != Record->FieldName) \
|
|
{ \
|
|
RegResult = RegSetValueExW(RegKey,\
|
|
L"" #FieldName, 0, REG_DWORD,\
|
|
(PVOID)&Record->FieldName, sizeof Record->FieldName);\
|
|
if (ERROR_SUCCESS != RegResult)\
|
|
{ \
|
|
Result = FspNtStatusFromWin32(RegResult);\
|
|
goto exit; \
|
|
} \
|
|
} \
|
|
else \
|
|
{ \
|
|
RegResult = RegDeleteValueW(RegKey,\
|
|
L"" #FieldName);\
|
|
if (ERROR_SUCCESS != RegResult && ERROR_FILE_NOT_FOUND != RegResult)\
|
|
{ \
|
|
Result = FspNtStatusFromWin32(RegResult);\
|
|
goto exit; \
|
|
} \
|
|
} \
|
|
} while (0,0)
|
|
|
|
NTSTATUS Result;
|
|
ULONG ClassNameLen;
|
|
WCHAR RegPath[MAX_PATH];
|
|
HKEY RegKey = 0;
|
|
DWORD RegResult;
|
|
|
|
if (0 != Record && 0 == Record->Executable)
|
|
{
|
|
Result = STATUS_INVALID_PARAMETER;
|
|
goto exit;
|
|
}
|
|
|
|
ClassNameLen = lstrlenW(ClassName);
|
|
if (sizeof RegPath - sizeof L"" FSP_LAUNCH_REGKEY <= (ClassNameLen + 1) * sizeof(WCHAR))
|
|
{
|
|
Result = STATUS_INVALID_PARAMETER;
|
|
goto exit;
|
|
}
|
|
|
|
memcpy(RegPath, L"" FSP_LAUNCH_REGKEY, sizeof L"" FSP_LAUNCH_REGKEY - sizeof(WCHAR));
|
|
RegPath[sizeof L"" FSP_LAUNCH_REGKEY / sizeof(WCHAR) - 1] = L'\\';
|
|
memcpy(RegPath + sizeof L"" FSP_LAUNCH_REGKEY / sizeof(WCHAR),
|
|
ClassName, (ClassNameLen + 1) * sizeof(WCHAR));
|
|
|
|
if (0 != Record)
|
|
{
|
|
RegResult = RegCreateKeyExW(HKEY_LOCAL_MACHINE, RegPath,
|
|
0, 0, 0, FSP_LAUNCH_REGKEY_WOW64 | KEY_SET_VALUE, 0, &RegKey, 0);
|
|
if (ERROR_SUCCESS != RegResult)
|
|
{
|
|
Result = FspNtStatusFromWin32(RegResult);
|
|
goto exit;
|
|
}
|
|
|
|
SETFIELD(Agent);
|
|
SETFIELD(Executable);
|
|
SETFIELD(CommandLine);
|
|
SETFIELD(WorkDirectory);
|
|
SETFIELD(RunAs);
|
|
SETFIELD(Security);
|
|
SETFIELD(AuthPackage);
|
|
SETFIELD(Stderr);
|
|
SETFIELDI(JobControl, ~0); /* JobControl default is 1; but we treat as without default */
|
|
SETFIELDI(Credentials, 0);
|
|
SETFIELDI(AuthPackageId, 0);
|
|
SETFIELDI(Recovery, 0);
|
|
}
|
|
else
|
|
{
|
|
RegResult = RegDeleteKeyEx(HKEY_LOCAL_MACHINE, RegPath,
|
|
FSP_LAUNCH_REGKEY_WOW64, 0);
|
|
if (ERROR_SUCCESS != RegResult && ERROR_FILE_NOT_FOUND != RegResult)
|
|
{
|
|
Result = FspNtStatusFromWin32(RegResult);
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
Result = STATUS_SUCCESS;
|
|
|
|
exit:
|
|
if (0 != RegKey)
|
|
RegCloseKey(RegKey);
|
|
|
|
return Result;
|
|
|
|
#undef SETFIELD
|
|
#undef SETFIELDI
|
|
}
|
|
|
|
FSP_API NTSTATUS FspLaunchRegGetRecord(
|
|
PWSTR ClassName, PWSTR Agent,
|
|
FSP_LAUNCH_REG_RECORD **PRecord)
|
|
{
|
|
#define GETFIELD(FieldName) \
|
|
do \
|
|
{ \
|
|
RegSize = sizeof RegBuf - RegMark;\
|
|
RegResult = RegQueryValueEx(RegKey,\
|
|
L"" #FieldName, 0, &RegType,\
|
|
(PVOID)(RegBuf + RegMark), &RegSize);\
|
|
if (ERROR_SUCCESS != RegResult) \
|
|
{ \
|
|
if (ERROR_FILE_NOT_FOUND != RegResult)\
|
|
{ \
|
|
Result = FspNtStatusFromWin32(RegResult);\
|
|
goto exit; \
|
|
} \
|
|
} \
|
|
else if (REG_SZ != RegType || \
|
|
sizeof(WCHAR) > RegSize || \
|
|
L'\0' != *(PWSTR)(RegBuf + RegMark + RegSize - sizeof(WCHAR)))\
|
|
{ \
|
|
Result = STATUS_OBJECT_NAME_NOT_FOUND;\
|
|
goto exit; \
|
|
} \
|
|
else \
|
|
{ \
|
|
Record->FieldName = (PWSTR)(RegBuf + RegMark);\
|
|
RegMark += RegSize; \
|
|
} \
|
|
} while (0,0)
|
|
#define GETFIELDI(FieldName) \
|
|
do \
|
|
{ \
|
|
RegSize = sizeof RegDword; \
|
|
RegResult = RegQueryValueEx(RegKey,\
|
|
L"" #FieldName, 0, &RegType,\
|
|
(PVOID)&RegDword, &RegSize);\
|
|
if (ERROR_SUCCESS != RegResult) \
|
|
{ \
|
|
if (ERROR_FILE_NOT_FOUND != RegResult)\
|
|
{ \
|
|
Result = FspNtStatusFromWin32(RegResult);\
|
|
goto exit; \
|
|
} \
|
|
} \
|
|
else if (REG_DWORD != RegType) \
|
|
{ \
|
|
Result = STATUS_OBJECT_NAME_NOT_FOUND;\
|
|
goto exit; \
|
|
} \
|
|
else \
|
|
Record->FieldName = RegDword;\
|
|
} while (0,0)
|
|
|
|
NTSTATUS Result;
|
|
ULONG ClassNameLen;
|
|
WCHAR RegPath[MAX_PATH];
|
|
FSP_LAUNCH_REG_RECORD RecordBuf, *Record = &RecordBuf;
|
|
HKEY RegKey = 0;
|
|
DWORD RegResult, RegDword, RegType, RegSize, RegMark;
|
|
UINT8 RegBuf[2 * 1024];
|
|
PWSTR P, Part;
|
|
BOOLEAN FoundAgent;
|
|
|
|
*PRecord = 0;
|
|
|
|
ClassNameLen = lstrlenW(ClassName);
|
|
if (sizeof RegPath - sizeof L"" FSP_LAUNCH_REGKEY <= (ClassNameLen + 1) * sizeof(WCHAR))
|
|
{
|
|
Result = STATUS_INVALID_PARAMETER;
|
|
goto exit;
|
|
}
|
|
|
|
memcpy(RegPath, L"" FSP_LAUNCH_REGKEY, sizeof L"" FSP_LAUNCH_REGKEY - sizeof(WCHAR));
|
|
RegPath[sizeof L"" FSP_LAUNCH_REGKEY / sizeof(WCHAR) - 1] = L'\\';
|
|
memcpy(RegPath + sizeof L"" FSP_LAUNCH_REGKEY / sizeof(WCHAR),
|
|
ClassName, (ClassNameLen + 1) * sizeof(WCHAR));
|
|
|
|
RegResult = RegOpenKeyExW(HKEY_LOCAL_MACHINE, RegPath,
|
|
0, FSP_LAUNCH_REGKEY_WOW64 | KEY_QUERY_VALUE, &RegKey);
|
|
if (ERROR_SUCCESS != RegResult)
|
|
{
|
|
Result = FspNtStatusFromWin32(RegResult);
|
|
goto exit;
|
|
}
|
|
|
|
memset(Record, 0, sizeof *Record);
|
|
Record->JobControl = 1; /* default is YES! */
|
|
RegMark = 0;
|
|
|
|
GETFIELD(Agent);
|
|
if (0 != Agent && L'\0' != Agent[0] &&
|
|
0 != Record->Agent && L'\0' != Record->Agent[0])
|
|
{
|
|
FoundAgent = FALSE;
|
|
P = Record->Agent, Part = P;
|
|
do
|
|
{
|
|
if (L',' == *P || '\0' == *P)
|
|
{
|
|
if (0 == invariant_wcsnicmp(Part, Agent, P - Part))
|
|
{
|
|
FoundAgent = TRUE;
|
|
break;
|
|
}
|
|
else
|
|
Part = P + 1;
|
|
}
|
|
} while (L'\0' != *P++);
|
|
|
|
if (!FoundAgent)
|
|
{
|
|
Result = STATUS_OBJECT_NAME_NOT_FOUND;
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
GETFIELD(Executable);
|
|
GETFIELD(CommandLine);
|
|
GETFIELD(WorkDirectory);
|
|
GETFIELD(RunAs);
|
|
GETFIELD(Security);
|
|
GETFIELD(AuthPackage);
|
|
GETFIELD(Stderr);
|
|
GETFIELDI(JobControl);
|
|
GETFIELDI(Credentials);
|
|
GETFIELDI(AuthPackageId);
|
|
GETFIELDI(Recovery);
|
|
|
|
if (0 == Record->Executable)
|
|
{
|
|
Result = STATUS_OBJECT_NAME_NOT_FOUND;
|
|
goto exit;
|
|
}
|
|
|
|
Record = MemAlloc(FIELD_OFFSET(FSP_LAUNCH_REG_RECORD, Buffer) + RegMark);
|
|
if (0 == Record)
|
|
{
|
|
Result = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto exit;
|
|
}
|
|
|
|
memset(Record, 0, sizeof *Record);
|
|
memcpy(Record->Buffer, RegBuf, RegMark);
|
|
Record->Agent = 0 != RecordBuf.Agent ?
|
|
(PVOID)(Record->Buffer + ((PUINT8)RecordBuf.Agent - RegBuf)) : 0;
|
|
Record->Executable = 0 != RecordBuf.Executable ?
|
|
(PVOID)(Record->Buffer + ((PUINT8)RecordBuf.Executable - RegBuf)) : 0;
|
|
Record->CommandLine = 0 != RecordBuf.CommandLine ?
|
|
(PVOID)(Record->Buffer + ((PUINT8)RecordBuf.CommandLine - RegBuf)) : 0;
|
|
Record->WorkDirectory = 0 != RecordBuf.WorkDirectory ?
|
|
(PVOID)(Record->Buffer + ((PUINT8)RecordBuf.WorkDirectory - RegBuf)) : 0;
|
|
Record->RunAs = 0 != RecordBuf.RunAs ?
|
|
(PVOID)(Record->Buffer + ((PUINT8)RecordBuf.RunAs - RegBuf)) : 0;
|
|
Record->Security = 0 != RecordBuf.Security ?
|
|
(PVOID)(Record->Buffer + ((PUINT8)RecordBuf.Security - RegBuf)) : 0;
|
|
Record->AuthPackage = 0 != RecordBuf.AuthPackage ?
|
|
(PVOID)(Record->Buffer + ((PUINT8)RecordBuf.AuthPackage - RegBuf)) : 0;
|
|
Record->Stderr = 0 != RecordBuf.Stderr ?
|
|
(PVOID)(Record->Buffer + ((PUINT8)RecordBuf.Stderr - RegBuf)) : 0;
|
|
Record->JobControl = RecordBuf.JobControl;
|
|
Record->Credentials = RecordBuf.Credentials;
|
|
Record->AuthPackageId = RecordBuf.AuthPackageId;
|
|
Record->Recovery = RecordBuf.Recovery;
|
|
|
|
*PRecord = Record;
|
|
Result = STATUS_SUCCESS;
|
|
|
|
exit:
|
|
if (0 != RegKey)
|
|
RegCloseKey(RegKey);
|
|
|
|
return Result;
|
|
|
|
#undef GETFIELDI
|
|
#undef GETFIELD
|
|
}
|
|
|
|
FSP_API VOID FspLaunchRegFreeRecord(
|
|
FSP_LAUNCH_REG_RECORD *Record)
|
|
{
|
|
MemFree(Record);
|
|
}
|