launcher: Stderr registry setting

This commit adds a new Stderr registry setting that can be used to redirect
the standard error output of a launched service instance.
This commit is contained in:
Bill Zissimopoulos 2020-04-25 00:48:40 -07:00
parent 3eb115eb22
commit 01744e8193
No known key found for this signature in database
GPG Key ID: 3D4F95D52C7B3EA3
3 changed files with 114 additions and 49 deletions

View File

@ -287,7 +287,8 @@ typedef struct _FSP_LAUNCH_REG_RECORD
PWSTR RunAs; PWSTR RunAs;
PWSTR Security; PWSTR Security;
PWSTR AuthPackage; PWSTR AuthPackage;
PVOID Reserved0[5]; PWSTR Stderr;
PVOID Reserved0[4];
ULONG JobControl; ULONG JobControl;
ULONG Credentials; ULONG Credentials;
ULONG AuthPackageId; ULONG AuthPackageId;

View File

@ -271,6 +271,7 @@ FSP_API NTSTATUS FspLaunchRegSetRecord(
SETFIELD(RunAs); SETFIELD(RunAs);
SETFIELD(Security); SETFIELD(Security);
SETFIELD(AuthPackage); SETFIELD(AuthPackage);
SETFIELD(Stderr);
SETFIELDI(JobControl, ~0); /* JobControl default is 1; but we treat as without default */ SETFIELDI(JobControl, ~0); /* JobControl default is 1; but we treat as without default */
SETFIELDI(Credentials, 0); SETFIELDI(Credentials, 0);
SETFIELDI(AuthPackageId, 0); SETFIELDI(AuthPackageId, 0);
@ -424,6 +425,7 @@ FSP_API NTSTATUS FspLaunchRegGetRecord(
GETFIELD(RunAs); GETFIELD(RunAs);
GETFIELD(Security); GETFIELD(Security);
GETFIELD(AuthPackage); GETFIELD(AuthPackage);
GETFIELD(Stderr);
GETFIELDI(JobControl); GETFIELDI(JobControl);
GETFIELDI(Credentials); GETFIELDI(Credentials);
GETFIELDI(AuthPackageId); GETFIELDI(AuthPackageId);
@ -458,6 +460,8 @@ FSP_API NTSTATUS FspLaunchRegGetRecord(
(PVOID)(Record->Buffer + ((PUINT8)RecordBuf.Security - RegBuf)) : 0; (PVOID)(Record->Buffer + ((PUINT8)RecordBuf.Security - RegBuf)) : 0;
Record->AuthPackage = 0 != RecordBuf.AuthPackage ? Record->AuthPackage = 0 != RecordBuf.AuthPackage ?
(PVOID)(Record->Buffer + ((PUINT8)RecordBuf.AuthPackage - RegBuf)) : 0; (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->JobControl = RecordBuf.JobControl;
Record->Credentials = RecordBuf.Credentials; Record->Credentials = RecordBuf.Credentials;
Record->AuthPackageId = RecordBuf.AuthPackageId; Record->AuthPackageId = RecordBuf.AuthPackageId;

View File

@ -464,7 +464,7 @@ typedef struct
DWORD ProcessId; DWORD ProcessId;
HANDLE Process; HANDLE Process;
HANDLE ProcessWait; HANDLE ProcessWait;
HANDLE StdioHandles[2]; HANDLE StdioHandles[3];
DWORD Recovery; DWORD Recovery;
ULONG Argc; ULONG Argc;
PWSTR *Argv; PWSTR *Argv;
@ -502,27 +502,28 @@ static SVC_INSTANCE *SvcInstanceLookup(PWSTR ClassName, PWSTR InstanceName)
return 0; return 0;
} }
static inline ULONG SvcInstanceArgumentLength(PWSTR Arg, PWSTR Pattern) static inline ULONG SvcInstanceArgumentLength(PWSTR Arg, PWSTR Pattern, BOOLEAN Quote)
{ {
PWSTR PathTransform(PWSTR Dest, PWSTR Arg, PWSTR Pattern); PWSTR PathTransform(PWSTR Dest, PWSTR Arg, PWSTR Pattern);
return 2 + (ULONG)(UINT_PTR)PathTransform(0, Arg, Pattern); return (Quote ? 2 : 0) + (ULONG)((UINT_PTR)PathTransform(0, Arg, Pattern) / sizeof(WCHAR));
} }
static inline PWSTR SvcInstanceArgumentCopy(PWSTR Dest, PWSTR Arg, PWSTR Pattern) static inline PWSTR SvcInstanceArgumentCopy(PWSTR Dest, PWSTR Arg, PWSTR Pattern, BOOLEAN Quote)
{ {
PWSTR PathTransform(PWSTR Dest, PWSTR Arg, PWSTR Pattern); PWSTR PathTransform(PWSTR Dest, PWSTR Arg, PWSTR Pattern);
if (Quote)
*Dest++ = L'"'; *Dest++ = L'"';
Dest = PathTransform(Dest, Arg, Pattern); Dest = PathTransform(Dest, Arg, Pattern);
if (Quote)
*Dest++ = L'"'; *Dest++ = L'"';
return Dest; return Dest;
} }
static NTSTATUS SvcInstanceReplaceArguments(PWSTR String, static NTSTATUS SvcInstanceReplaceArguments(PWSTR String,
ULONG Argc, PWSTR *Argv, ULONG Argc, PWSTR *Argv, PWSTR *Varv, BOOLEAN Quote,
PWSTR UserName,
PWSTR *PNewString) PWSTR *PNewString)
{ {
PWSTR NewString = 0, P, Q; PWSTR NewString = 0, P, Q;
@ -544,24 +545,24 @@ static NTSTATUS SvcInstanceReplaceArguments(PWSTR String,
{ {
Pattern = ++P; Pattern = ++P;
while (!(L'\0' == *P || while (!(L'\0' == *P ||
(L'0' <= *P && *P <= '9') || (L'0' <= *P && *P <= L'9') ||
(L'A' <= *P && *P <= 'Z'))) (L'A' <= *P && *P <= L'Z')))
P++; P++;
} }
if (L'0' <= *P && *P <= '9') if (L'0' <= *P && *P <= L'9')
{ {
if (Argc > (ULONG)(*P - L'0')) if (Argc > (ULONG)(*P - L'0'))
Length += SvcInstanceArgumentLength(Argv[*P - L'0'], Pattern); Length += SvcInstanceArgumentLength(Argv[*P - L'0'], Pattern, Quote);
else else
Length += SvcInstanceArgumentLength(EmptyArg, 0); Length += SvcInstanceArgumentLength(EmptyArg, 0, Quote);
} }
else else
if (L'U' == *P) if (L'A' <= *P && *P <= L'Z')
{ {
if (0 != UserName) if (0 != Varv[*P - L'A'])
Length += SvcInstanceArgumentLength(UserName, Pattern); Length += SvcInstanceArgumentLength(Varv[*P - L'A'], Pattern, Quote);
else else
Length += SvcInstanceArgumentLength(EmptyArg, 0); Length += SvcInstanceArgumentLength(EmptyArg, 0, Quote);
} }
else else
if (*P) if (*P)
@ -590,24 +591,24 @@ static NTSTATUS SvcInstanceReplaceArguments(PWSTR String,
{ {
Pattern = ++P; Pattern = ++P;
while (!(L'\0' == *P || while (!(L'\0' == *P ||
(L'0' <= *P && *P <= '9') || (L'0' <= *P && *P <= L'9') ||
(L'A' <= *P && *P <= 'Z'))) (L'A' <= *P && *P <= L'Z')))
P++; P++;
} }
if (L'0' <= *P && *P <= '9') if (L'0' <= *P && *P <= L'9')
{ {
if (Argc > (ULONG)(*P - L'0')) if (Argc > (ULONG)(*P - L'0'))
Q = SvcInstanceArgumentCopy(Q, Argv[*P - L'0'], Pattern); Q = SvcInstanceArgumentCopy(Q, Argv[*P - L'0'], Pattern, Quote);
else else
Q = SvcInstanceArgumentCopy(Q, EmptyArg, 0); Q = SvcInstanceArgumentCopy(Q, EmptyArg, 0, Quote);
} }
else else
if (L'U' == *P) if (L'A' <= *P && *P <= L'Z')
{ {
if (0 != UserName) if (0 != Varv[*P - L'A'])
Q = SvcInstanceArgumentCopy(Q, UserName, Pattern); Q = SvcInstanceArgumentCopy(Q, Varv[*P - L'A'], Pattern, Quote);
else else
Q = SvcInstanceArgumentCopy(Q, EmptyArg, 0); Q = SvcInstanceArgumentCopy(Q, EmptyArg, 0, Quote);
} }
else else
if (*P) if (*P)
@ -718,14 +719,15 @@ static NTSTATUS SvcInstanceAccessCheck(HANDLE ClientToken, ULONG DesiredAccess,
static NTSTATUS SvcInstanceCreateProcess(PWSTR UserName, HANDLE ClientToken, static NTSTATUS SvcInstanceCreateProcess(PWSTR UserName, HANDLE ClientToken,
PWSTR Executable, PWSTR CommandLine, PWSTR WorkDirectory, PWSTR Executable, PWSTR CommandLine, PWSTR WorkDirectory,
HANDLE StdioHandles[2], HANDLE StdioHandles[2], HANDLE StderrHandle,
PPROCESS_INFORMATION ProcessInfo) PPROCESS_INFORMATION ProcessInfo)
{ {
WCHAR WorkDirectoryBuf[MAX_PATH]; WCHAR WorkDirectoryBuf[MAX_PATH];
STARTUPINFOEXW StartupInfoEx; STARTUPINFOEXW StartupInfoEx;
HANDLE ChildHandles[3] = { INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, 0/* DO NOT CLOSE!*/ }; HANDLE ChildHandles[3] = { INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE/* NO CLOSE!*/ };
HANDLE ParentHandles[2] = { INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE }; HANDLE ParentHandles[2] = { INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE };
PPROC_THREAD_ATTRIBUTE_LIST AttrList = 0; PPROC_THREAD_ATTRIBUTE_LIST AttrList = 0;
BOOLEAN InitDoneAttrList = FALSE;
SIZE_T Size; SIZE_T Size;
NTSTATUS Result; NTSTATUS Result;
@ -748,7 +750,7 @@ static NTSTATUS SvcInstanceCreateProcess(PWSTR UserName, HANDLE ClientToken,
memset(&StartupInfoEx, 0, sizeof StartupInfoEx); memset(&StartupInfoEx, 0, sizeof StartupInfoEx);
StartupInfoEx.StartupInfo.cb = sizeof StartupInfoEx.StartupInfo; StartupInfoEx.StartupInfo.cb = sizeof StartupInfoEx.StartupInfo;
if (0 != StdioHandles) if (0 != StdioHandles || INVALID_HANDLE_VALUE != StderrHandle)
{ {
/* /*
* Create child process and redirect stdin/stdout. Do *not* inherit other handles. * Create child process and redirect stdin/stdout. Do *not* inherit other handles.
@ -757,6 +759,8 @@ static NTSTATUS SvcInstanceCreateProcess(PWSTR UserName, HANDLE ClientToken,
* https://blogs.msdn.microsoft.com/oldnewthing/20111216-00/?p=8873/ * https://blogs.msdn.microsoft.com/oldnewthing/20111216-00/?p=8873/
*/ */
if (0 != StdioHandles)
{
/* create stdin read/write ends; make them inheritable */ /* create stdin read/write ends; make them inheritable */
if (!CreateOverlappedPipe(&ChildHandles[0], &ParentHandles[0], if (!CreateOverlappedPipe(&ChildHandles[0], &ParentHandles[0],
0, TRUE, FALSE, 0, 0)) 0, TRUE, FALSE, 0, 0))
@ -764,7 +768,10 @@ static NTSTATUS SvcInstanceCreateProcess(PWSTR UserName, HANDLE ClientToken,
Result = FspNtStatusFromWin32(GetLastError()); Result = FspNtStatusFromWin32(GetLastError());
goto exit; goto exit;
} }
}
if (0 != StdioHandles)
{
/* create stdout read/write ends; make them inheritable */ /* create stdout read/write ends; make them inheritable */
if (!CreateOverlappedPipe(&ParentHandles[1], &ChildHandles[1], if (!CreateOverlappedPipe(&ParentHandles[1], &ChildHandles[1],
0, FALSE, TRUE, FILE_FLAG_OVERLAPPED, 0)) 0, FALSE, TRUE, FILE_FLAG_OVERLAPPED, 0))
@ -772,8 +779,10 @@ static NTSTATUS SvcInstanceCreateProcess(PWSTR UserName, HANDLE ClientToken,
Result = FspNtStatusFromWin32(GetLastError()); Result = FspNtStatusFromWin32(GetLastError());
goto exit; goto exit;
} }
}
ChildHandles[2] = GetStdHandle(STD_ERROR_HANDLE); if (INVALID_HANDLE_VALUE != StderrHandle)
ChildHandles[2] = StderrHandle;
Size = 0; Size = 0;
if (!InitializeProcThreadAttributeList(0, 1, 0, &Size) && if (!InitializeProcThreadAttributeList(0, 1, 0, &Size) &&
@ -795,10 +804,14 @@ static NTSTATUS SvcInstanceCreateProcess(PWSTR UserName, HANDLE ClientToken,
Result = FspNtStatusFromWin32(GetLastError()); Result = FspNtStatusFromWin32(GetLastError());
goto exit; goto exit;
} }
InitDoneAttrList = TRUE;
/* only the child ends of stdin/stdout are actually inherited */ /* only the child ends of stdin/stdout/stderr are actually inherited */
if (!UpdateProcThreadAttribute(AttrList, 0, PROC_THREAD_ATTRIBUTE_HANDLE_LIST, if (!UpdateProcThreadAttribute(AttrList, 0, PROC_THREAD_ATTRIBUTE_HANDLE_LIST,
ChildHandles, sizeof ChildHandles, 0, 0)) 0 != StdioHandles ? ChildHandles : ChildHandles + 2,
((0 != StdioHandles ? 2 : 0) + (INVALID_HANDLE_VALUE != StderrHandle ? 1 : 0)) *
sizeof ChildHandles[0],
0, 0))
{ {
Result = FspNtStatusFromWin32(GetLastError()); Result = FspNtStatusFromWin32(GetLastError());
goto exit; goto exit;
@ -877,6 +890,8 @@ exit:
if (INVALID_HANDLE_VALUE != ChildHandles[1]) if (INVALID_HANDLE_VALUE != ChildHandles[1])
CloseHandle(ChildHandles[1]); CloseHandle(ChildHandles[1]);
if (InitDoneAttrList)
DeleteProcThreadAttributeList(AttrList);
MemFree(AttrList); MemFree(AttrList);
return Result; return Result;
@ -888,13 +903,16 @@ NTSTATUS SvcInstanceCreate(HANDLE ClientToken,
SVC_INSTANCE **PSvcInstance) SVC_INSTANCE **PSvcInstance)
{ {
SVC_INSTANCE *SvcInstance = 0; SVC_INSTANCE *SvcInstance = 0;
PWSTR ClientUserName = 0; PWSTR Argv[10];
PWSTR Varv[26];
SYSTEMTIME SystemTime;
PWSTR ClientUserName = 0, StderrFileName = 0;
DWORD ClientTokenInformation = -1; DWORD ClientTokenInformation = -1;
SECURITY_ATTRIBUTES StderrSecurityAttributes = { sizeof(SECURITY_ATTRIBUTES), 0, TRUE };
FSP_LAUNCH_REG_RECORD *Record = 0; FSP_LAUNCH_REG_RECORD *Record = 0;
WCHAR CommandLine[512], Security[512]; WCHAR CurrentTime[32], CommandLine[512], Security[512];
DWORD Length, ClassNameSize, InstanceNameSize; DWORD Length, ClassNameSize, InstanceNameSize;
PSECURITY_DESCRIPTOR SecurityDescriptor = 0, NewSecurityDescriptor; PSECURITY_DESCRIPTOR SecurityDescriptor = 0, NewSecurityDescriptor;
PWSTR Argv[10];
PROCESS_INFORMATION ProcessInfo; PROCESS_INFORMATION ProcessInfo;
NTSTATUS Result; NTSTATUS Result;
@ -906,6 +924,8 @@ NTSTATUS SvcInstanceCreate(HANDLE ClientToken,
Argv[0] = 0; Argv[0] = 0;
Argc++; Argc++;
memset(Varv, 0, sizeof Varv);
memset(&ProcessInfo, 0, sizeof ProcessInfo); memset(&ProcessInfo, 0, sizeof ProcessInfo);
EnterCriticalSection(&SvcInstanceLock); EnterCriticalSection(&SvcInstanceLock);
@ -916,9 +936,17 @@ NTSTATUS SvcInstanceCreate(HANDLE ClientToken,
goto exit; goto exit;
} }
GetSystemTime(&SystemTime);
wsprintfW(CurrentTime, L"%04hu%02hu%02huT%02hu%02hu%02hu.%03huZ",
SystemTime.wYear, SystemTime.wMonth, SystemTime.wDay,
SystemTime.wHour, SystemTime.wMinute, SystemTime.wSecond,
SystemTime.wMilliseconds);
Varv[L'T' - L'A'] = CurrentTime;
Result = GetTokenUserName(ClientToken, &ClientUserName); Result = GetTokenUserName(ClientToken, &ClientUserName);
if (!NT_SUCCESS(Result)) if (!NT_SUCCESS(Result))
goto exit; goto exit;
Varv[L'U' - L'A'] = ClientUserName;
Result = FspLaunchRegGetRecord(ClassName, 0, &Record); Result = FspLaunchRegGetRecord(ClassName, 0, &Record);
if (!NT_SUCCESS(Result)) if (!NT_SUCCESS(Result))
@ -1007,15 +1035,36 @@ NTSTATUS SvcInstanceCreate(HANDLE ClientToken,
SvcInstance->SecurityDescriptor = SecurityDescriptor; SvcInstance->SecurityDescriptor = SecurityDescriptor;
SvcInstance->StdioHandles[0] = INVALID_HANDLE_VALUE; SvcInstance->StdioHandles[0] = INVALID_HANDLE_VALUE;
SvcInstance->StdioHandles[1] = INVALID_HANDLE_VALUE; SvcInstance->StdioHandles[1] = INVALID_HANDLE_VALUE;
SvcInstance->StdioHandles[2] = INVALID_HANDLE_VALUE;
SvcInstance->Recovery = Record->Recovery; SvcInstance->Recovery = Record->Recovery;
Result = SvcInstanceReplaceArguments(CommandLine, Result = SvcInstanceReplaceArguments(CommandLine, Argc, Argv, Varv, TRUE,
Argc, Argv,
ClientUserName,
&SvcInstance->CommandLine); &SvcInstance->CommandLine);
if (!NT_SUCCESS(Result)) if (!NT_SUCCESS(Result))
goto exit; goto exit;
if (0 != Record->Stderr)
{
Result = SvcInstanceReplaceArguments(Record->Stderr, Argc, Argv, Varv, FALSE,
&StderrFileName);
if (!NT_SUCCESS(Result))
goto exit;
SvcInstance->StdioHandles[2] = CreateFileW(
StderrFileName,
FILE_APPEND_DATA,
FILE_SHARE_READ | FILE_SHARE_WRITE,
&StderrSecurityAttributes,
OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
0);
if (INVALID_HANDLE_VALUE == SvcInstance->StdioHandles[2])
{
Result = FspNtStatusFromWin32(GetLastError());
goto exit;
}
}
Result = SvcInstanceCreateProcess( Result = SvcInstanceCreateProcess(
Record->RunAs, Record->RunAs,
ClientToken, ClientToken,
@ -1023,6 +1072,7 @@ NTSTATUS SvcInstanceCreate(HANDLE ClientToken,
SvcInstance->CommandLine, SvcInstance->CommandLine,
Record->WorkDirectory, Record->WorkDirectory,
RedirectStdio ? SvcInstance->StdioHandles : 0, RedirectStdio ? SvcInstance->StdioHandles : 0,
SvcInstance->StdioHandles[2],
&ProcessInfo); &ProcessInfo);
if (!NT_SUCCESS(Result)) if (!NT_SUCCESS(Result))
goto exit; goto exit;
@ -1078,6 +1128,8 @@ exit:
CloseHandle(SvcInstance->StdioHandles[0]); CloseHandle(SvcInstance->StdioHandles[0]);
if (INVALID_HANDLE_VALUE != SvcInstance->StdioHandles[1]) if (INVALID_HANDLE_VALUE != SvcInstance->StdioHandles[1])
CloseHandle(SvcInstance->StdioHandles[1]); CloseHandle(SvcInstance->StdioHandles[1]);
if (INVALID_HANDLE_VALUE != SvcInstance->StdioHandles[2])
CloseHandle(SvcInstance->StdioHandles[2]);
if (0 != SvcInstance->ProcessWait) if (0 != SvcInstance->ProcessWait)
UnregisterWaitEx(SvcInstance->ProcessWait, 0); UnregisterWaitEx(SvcInstance->ProcessWait, 0);
@ -1096,6 +1148,7 @@ exit:
if (0 != Record) if (0 != Record)
FspLaunchRegFreeRecord(Record); FspLaunchRegFreeRecord(Record);
MemFree(StderrFileName);
MemFree(ClientUserName); MemFree(ClientUserName);
LeaveCriticalSection(&SvcInstanceLock); LeaveCriticalSection(&SvcInstanceLock);
@ -1133,6 +1186,8 @@ static VOID SvcInstanceRelease(SVC_INSTANCE *SvcInstance)
CloseHandle(SvcInstance->StdioHandles[0]); CloseHandle(SvcInstance->StdioHandles[0]);
if (INVALID_HANDLE_VALUE != SvcInstance->StdioHandles[1]) if (INVALID_HANDLE_VALUE != SvcInstance->StdioHandles[1])
CloseHandle(SvcInstance->StdioHandles[1]); CloseHandle(SvcInstance->StdioHandles[1]);
if (INVALID_HANDLE_VALUE != SvcInstance->StdioHandles[2])
CloseHandle(SvcInstance->StdioHandles[2]);
if (0 != SvcInstance->ProcessWait) if (0 != SvcInstance->ProcessWait)
UnregisterWaitEx(SvcInstance->ProcessWait, 0); UnregisterWaitEx(SvcInstance->ProcessWait, 0);
@ -1256,6 +1311,11 @@ exit:
CloseHandle(SvcInstance->StdioHandles[1]); CloseHandle(SvcInstance->StdioHandles[1]);
SvcInstance->StdioHandles[1] = INVALID_HANDLE_VALUE; SvcInstance->StdioHandles[1] = INVALID_HANDLE_VALUE;
} }
if (INVALID_HANDLE_VALUE != SvcInstance->StdioHandles[2])
{
CloseHandle(SvcInstance->StdioHandles[2]);
SvcInstance->StdioHandles[2] = INVALID_HANDLE_VALUE;
}
if (NT_SUCCESS(Result)) if (NT_SUCCESS(Result))
{ {