From 3119922708a6004d28ef58a464cd120e424b69fa Mon Sep 17 00:00:00 2001 From: Bill Zissimopoulos Date: Mon, 26 Nov 2018 13:29:34 -0800 Subject: [PATCH] np, launcher: allow RunAs=. registry setting --- inc/winfsp/launch.h | 67 +++++++++++++++++++++++++++++++++++++++- inc/winfsp/winfsp.h | 4 +++ src/dll/launch.c | 30 +++++++++++++++--- src/dll/np.c | 28 ++++++++++++----- src/dll/util.c | 16 ++++++++-- src/launcher/launchctl.c | 4 +-- src/launcher/launcher.c | 65 +++++++++++++++++++++++++++++--------- 7 files changed, 181 insertions(+), 33 deletions(-) diff --git a/inc/winfsp/launch.h b/inc/winfsp/launch.h index 065dc6df..1ee53547 100644 --- a/inc/winfsp/launch.h +++ b/inc/winfsp/launch.h @@ -111,7 +111,43 @@ enum */ FSP_API NTSTATUS FspLaunchCallLauncherPipe( WCHAR Command, ULONG Argc, PWSTR *Argv, ULONG *Argl, - PWSTR Buffer, PULONG PSize, PULONG PLauncherError); + PWSTR Buffer, PULONG PSize, + PULONG PLauncherError); +/** + * Call launcher pipe. + * + * This function is used to send a command to the launcher and receive a response. + * + * @param Command + * Launcher command to send. For example, the 'L' launcher command instructs + * the launcher to list all running service instances. + * @param Argc + * Command argument count. May be 0. + * @param Argv + * Command argument array. May be NULL. + * @param Argl + * Command argument length array. May be NULL. If this is NULL all command arguments + * are assumed to be NULL-terminated strings. It is also possible for specific arguments + * to be NULL-terminated; in this case pass -1 in the corresponding Argl position. + * @param Buffer + * Buffer that receives the command response. May be NULL. + * @param PSize + * Pointer to a ULONG. On input it contains the size of the Buffer. On output it + * contains the number of bytes transferred. May be NULL. + * @param AllowImpersonation + * Allow caller to be impersonated by launcher. + * @param PLauncherError + * Receives the launcher error if any. This is always a Win32 error code. May not be NULL. + * @return + * STATUS_SUCCESS if the command is sent successfully to the launcher, even if the launcher + * returns an error. Other status codes indicate a communication error. Launcher errors are + * reported through PLauncherError. + */ +FSP_API NTSTATUS FspLaunchCallLauncherPipeEx( + WCHAR Command, ULONG Argc, PWSTR *Argv, ULONG *Argl, + PWSTR Buffer, PULONG PSize, + BOOLEAN AllowImpersonation, + PULONG PLauncherError); /** * Start a service instance. * @@ -138,6 +174,35 @@ FSP_API NTSTATUS FspLaunchStart( PWSTR ClassName, PWSTR InstanceName, ULONG Argc, PWSTR *Argv, BOOLEAN HasSecret, PULONG PLauncherError); +/** + * Start a service instance. + * + * @param ClassName + * Class name of the service instance to start. + * @param InstanceName + * Instance name of the service instance to start. + * @param Argc + * Service instance argument count. May be 0. + * @param Argv + * Service instance argument array. May be NULL. + * @param HasSecret + * Whether the last argument in Argv is assumed to be a secret (e.g. password) or not. + * Secrets are passed to service instances through standard input rather than the command + * line. + * @param AllowImpersonation + * Allow caller to be impersonated by launcher. + * @param PLauncherError + * Receives the launcher error if any. This is always a Win32 error code. May not be NULL. + * @return + * STATUS_SUCCESS if the command is sent successfully to the launcher, even if the launcher + * returns an error. Other status codes indicate a communication error. Launcher errors are + * reported through PLauncherError. + */ +FSP_API NTSTATUS FspLaunchStartEx( + PWSTR ClassName, PWSTR InstanceName, ULONG Argc, PWSTR *Argv, + BOOLEAN HasSecret, + BOOLEAN AllowImpersonation, + PULONG PLauncherError); /** * Stop a service instance. * diff --git a/inc/winfsp/winfsp.h b/inc/winfsp/winfsp.h index b299086a..a3b4359f 100644 --- a/inc/winfsp/winfsp.h +++ b/inc/winfsp/winfsp.h @@ -1795,6 +1795,10 @@ FSP_API NTSTATUS FspCallNamedPipeSecurely(PWSTR PipeName, PVOID InBuffer, ULONG InBufferSize, PVOID OutBuffer, ULONG OutBufferSize, PULONG PBytesTransferred, ULONG Timeout, PSID Sid); +FSP_API NTSTATUS FspCallNamedPipeSecurelyEx(PWSTR PipeName, + PVOID InBuffer, ULONG InBufferSize, PVOID OutBuffer, ULONG OutBufferSize, + PULONG PBytesTransferred, ULONG Timeout, BOOLEAN AllowImpersonation, + PSID Sid); FSP_API NTSTATUS FspVersion(PUINT32 PVersion); /* diff --git a/src/dll/launch.c b/src/dll/launch.c index 649119e8..adb343fb 100644 --- a/src/dll/launch.c +++ b/src/dll/launch.c @@ -23,7 +23,18 @@ FSP_API NTSTATUS FspLaunchCallLauncherPipe( WCHAR Command, ULONG Argc, PWSTR *Argv, ULONG *Argl, - PWSTR Buffer, PULONG PSize, PULONG PLauncherError) + 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; @@ -53,9 +64,9 @@ FSP_API NTSTATUS FspLaunchCallLauncherPipe( memcpy(P, Argv[I], Length * sizeof(WCHAR)); P += Length; *P++ = L'\0'; } - Result = FspCallNamedPipeSecurely(L"" FSP_LAUNCH_PIPE_NAME, + Result = FspCallNamedPipeSecurelyEx(L"" FSP_LAUNCH_PIPE_NAME, PipeBuf, (ULONG)(P - PipeBuf) * sizeof(WCHAR), PipeBuf, FSP_LAUNCH_PIPE_BUFFER_SIZE, - &BytesTransferred, NMPWAIT_USE_DEFAULT_WAIT, FSP_LAUNCH_PIPE_OWNER); + &BytesTransferred, NMPWAIT_USE_DEFAULT_WAIT, AllowImpersonation, FSP_LAUNCH_PIPE_OWNER); if (!NT_SUCCESS(Result)) goto exit; @@ -102,8 +113,17 @@ exit: } 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]; @@ -115,9 +135,9 @@ FSP_API NTSTATUS FspLaunchStart( Argv[1] = InstanceName; memcpy(Argv + 2, Argv0, Argc * sizeof(PWSTR)); - return FspLaunchCallLauncherPipe( + return FspLaunchCallLauncherPipeEx( HasSecret ? FspLaunchCmdStartWithSecret : FspLaunchCmdStart, - Argc + 2, Argv, 0, 0, 0, PLauncherError); + Argc + 2, Argv, 0, 0, 0, AllowImpersonation, PLauncherError); } FSP_API NTSTATUS FspLaunchStop( diff --git a/src/dll/np.c b/src/dll/np.c index 358e0a4a..9d96b482 100644 --- a/src/dll/np.c +++ b/src/dll/np.c @@ -179,12 +179,14 @@ static inline BOOLEAN FspNpParseRemoteUserName(PWSTR RemoteName, static inline DWORD FspNpCallLauncherPipe( WCHAR Command, ULONG Argc, PWSTR *Argv, ULONG *Argl, - PWSTR Buffer, PULONG PSize) + PWSTR Buffer, PULONG PSize, + BOOLEAN AllowImpersonation) { NTSTATUS Result; ULONG ErrorCode; - Result = FspLaunchCallLauncherPipe(Command, Argc, Argv, Argl, Buffer, PSize, &ErrorCode); + Result = FspLaunchCallLauncherPipeEx(Command, Argc, Argv, Argl, Buffer, PSize, AllowImpersonation, + &ErrorCode); return !NT_SUCCESS(Result) ? WN_NO_NETWORK : (ERROR_BROKEN_PIPE == ErrorCode ? WN_NO_NETWORK : ErrorCode); @@ -251,7 +253,8 @@ static WCHAR FspNpGetDriveLetter(PDWORD PLogicalDrives, PWSTR VolumeName) return 0; } -static DWORD FspNpGetRemoteInfo(PWSTR RemoteName, PDWORD PCredentialsKind) +static DWORD FspNpGetRemoteInfo(PWSTR RemoteName, + PDWORD PCredentialsKind, PBOOLEAN PAllowImpersonation) { PWSTR ClassName, InstanceName; ULONG ClassNameLen, InstanceNameLen; @@ -260,6 +263,7 @@ static DWORD FspNpGetRemoteInfo(PWSTR RemoteName, PDWORD PCredentialsKind) NTSTATUS Result; *PCredentialsKind = FSP_NP_CREDENTIALS_NONE; + *PAllowImpersonation = FALSE; if (!FspNpParseRemoteName(RemoteName, &ClassName, &ClassNameLen, &InstanceName, &InstanceNameLen)) @@ -283,6 +287,9 @@ static DWORD FspNpGetRemoteInfo(PWSTR RemoteName, PDWORD PCredentialsKind) break; } + *PAllowImpersonation = 0 != Record->RunAs && + L'.' == Record->RunAs[0] && L'\0' == Record->RunAs[1]; + FspLaunchRegFreeRecord(Record); return WN_SUCCESS; @@ -464,6 +471,7 @@ DWORD APIENTRY NPAddConnection(LPNETRESOURCEW lpNetResource, LPWSTR lpPassword, PWSTR ClassName, InstanceName, RemoteName, P; ULONG ClassNameLen, InstanceNameLen; DWORD CredentialsKind; + BOOLEAN AllowImpersonation; ULONG Argc; PWSTR Argv[6]; ULONG Argl[6]; @@ -493,7 +501,7 @@ DWORD APIENTRY NPAddConnection(LPNETRESOURCEW lpNetResource, LPWSTR lpPassword, return WN_ALREADY_CONNECTED; } - NpResult = FspNpGetRemoteInfo(lpRemoteName, &CredentialsKind); + NpResult = FspNpGetRemoteInfo(lpRemoteName, &CredentialsKind, &AllowImpersonation); if (WN_SUCCESS != NpResult) return NpResult; @@ -550,7 +558,8 @@ DWORD APIENTRY NPAddConnection(LPNETRESOURCEW lpNetResource, LPWSTR lpPassword, NpResult = FspNpCallLauncherPipe( FSP_NP_CREDENTIALS_NONE != CredentialsKind ? FspLaunchCmdStartWithSecret : FspLaunchCmdStart, - Argc, Argv, Argl, 0, 0); + Argc, Argv, Argl, 0, 0, + AllowImpersonation); switch (NpResult) { case WN_SUCCESS: @@ -602,7 +611,8 @@ DWORD APIENTRY NPAddConnection(LPNETRESOURCEW lpNetResource, LPWSTR lpPassword, if (WN_SUCCESS != FspNpCallLauncherPipe( FspLaunchCmdGetInfo, - Argc, Argv, Argl, 0, 0)) + Argc, Argv, Argl, 0, 0, + FALSE)) { /* looks like the file system is gone! */ NpResult = WN_NO_NETWORK; @@ -660,6 +670,7 @@ DWORD APIENTRY NPAddConnection3(HWND hwndOwner, DWORD NpResult; PWSTR RemoteName = lpNetResource->lpRemoteName; DWORD CredentialsKind; + BOOLEAN AIDummy; WCHAR UserName[CREDUI_MAX_USERNAME_LENGTH + 1], Password[CREDUI_MAX_PASSWORD_LENGTH + 1]; #if defined(FSP_NP_CREDENTIAL_MANAGER) BOOL Save = TRUE; @@ -679,7 +690,7 @@ DWORD APIENTRY NPAddConnection3(HWND hwndOwner, return NpResult; } - NpResult = FspNpGetRemoteInfo(RemoteName, &CredentialsKind); + NpResult = FspNpGetRemoteInfo(RemoteName, &CredentialsKind, &AIDummy); if (WN_SUCCESS != NpResult) return NpResult; if (FSP_NP_CREDENTIALS_NONE == CredentialsKind) @@ -766,7 +777,8 @@ DWORD APIENTRY NPCancelConnection(LPWSTR lpName, BOOL fForce) NpResult = FspNpCallLauncherPipe( FspLaunchCmdStop, - Argc, Argv, Argl, 0, 0); + Argc, Argv, Argl, 0, 0, + FALSE); switch (NpResult) { case WN_SUCCESS: diff --git a/src/dll/util.c b/src/dll/util.c index 6191ce2d..1f31a7b0 100644 --- a/src/dll/util.c +++ b/src/dll/util.c @@ -67,6 +67,16 @@ FSP_API NTSTATUS FspCallNamedPipeSecurely(PWSTR PipeName, PVOID InBuffer, ULONG InBufferSize, PVOID OutBuffer, ULONG OutBufferSize, PULONG PBytesTransferred, ULONG Timeout, PSID Sid) +{ + return FspCallNamedPipeSecurelyEx(PipeName, + InBuffer, InBufferSize, OutBuffer, OutBufferSize, PBytesTransferred, Timeout, + FALSE, Sid); +} + +FSP_API NTSTATUS FspCallNamedPipeSecurelyEx(PWSTR PipeName, + PVOID InBuffer, ULONG InBufferSize, PVOID OutBuffer, ULONG OutBufferSize, + PULONG PBytesTransferred, ULONG Timeout, BOOLEAN AllowImpersonation, + PSID Sid) { NTSTATUS Result; HANDLE Pipe = INVALID_HANDLE_VALUE; @@ -75,7 +85,8 @@ FSP_API NTSTATUS FspCallNamedPipeSecurely(PWSTR PipeName, Pipe = CreateFileW(PipeName, GENERIC_READ | FILE_WRITE_DATA | FILE_WRITE_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, - SECURITY_SQOS_PRESENT | SECURITY_IDENTIFICATION, 0); + SECURITY_SQOS_PRESENT | (AllowImpersonation ? SECURITY_IMPERSONATION : SECURITY_IDENTIFICATION), + 0); if (INVALID_HANDLE_VALUE == Pipe) { if (ERROR_PIPE_BUSY != GetLastError()) @@ -89,7 +100,8 @@ FSP_API NTSTATUS FspCallNamedPipeSecurely(PWSTR PipeName, Pipe = CreateFileW(PipeName, GENERIC_READ | FILE_WRITE_DATA | FILE_WRITE_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, - SECURITY_SQOS_PRESENT | SECURITY_IDENTIFICATION, 0); + SECURITY_SQOS_PRESENT | (AllowImpersonation ? SECURITY_IMPERSONATION : SECURITY_IDENTIFICATION), + 0); if (INVALID_HANDLE_VALUE == Pipe) { Result = FspNtStatusFromWin32(GetLastError()); diff --git a/src/launcher/launchctl.c b/src/launcher/launchctl.c index 3f0f566f..5f3590f7 100644 --- a/src/launcher/launchctl.c +++ b/src/launcher/launchctl.c @@ -72,8 +72,8 @@ static int call_pipe_and_report(PWSTR PipeBuf, ULONG SendSize, ULONG RecvSize) NTSTATUS Result; DWORD LastError, BytesTransferred; - Result = FspCallNamedPipeSecurely(L"" FSP_LAUNCH_PIPE_NAME, PipeBuf, SendSize, PipeBuf, RecvSize, - &BytesTransferred, NMPWAIT_USE_DEFAULT_WAIT, FSP_LAUNCH_PIPE_OWNER); + Result = FspCallNamedPipeSecurelyEx(L"" FSP_LAUNCH_PIPE_NAME, PipeBuf, SendSize, PipeBuf, RecvSize, + &BytesTransferred, NMPWAIT_USE_DEFAULT_WAIT, TRUE, FSP_LAUNCH_PIPE_OWNER); LastError = FspWin32FromNtStatus(Result); if (0 != LastError) diff --git a/src/launcher/launcher.c b/src/launcher/launcher.c index c3d1a247..3368159e 100644 --- a/src/launcher/launcher.c +++ b/src/launcher/launcher.c @@ -255,6 +255,7 @@ exit: static BOOL LogonCreateProcess( PWSTR UserName, + HANDLE Token, LPCWSTR ApplicationName, LPWSTR CommandLine, LPSECURITY_ATTRIBUTES ProcessAttributes, @@ -271,11 +272,20 @@ static BOOL LogonCreateProcess( if (0 != UserName) { if (0 == invariant_wcsicmp(UserName, L"LocalSystem")) + { UserName = 0; + Token = 0; + } else if (0 == invariant_wcsicmp(UserName, L"LocalService") || 0 == invariant_wcsicmp(UserName, L"NetworkService")) + { DomainName = L"NT AUTHORITY"; + Token = 0; + } + else + if (0 == invariant_wcsicmp(UserName, L".")) + ; else { SetLastError(ERROR_ACCESS_DENIED); @@ -299,18 +309,40 @@ static BOOL LogonCreateProcess( HANDLE LogonToken = 0; PVOID EnvironmentBlock = 0; + DWORD SessionId; DWORD LastError; BOOL Success; - Success = LogonUserW( - UserName, - DomainName, - 0, - LOGON32_LOGON_SERVICE, - LOGON32_PROVIDER_DEFAULT, - &LogonToken); - if (!Success) - goto exit; + if (0 == Token) + { + Success = LogonUserW( + UserName, + DomainName, + 0, + LOGON32_LOGON_SERVICE, + LOGON32_PROVIDER_DEFAULT, + &LogonToken); + if (!Success) + goto exit; + } + else + { + /* convert the impersonation token to a primary token */ + Success = DuplicateTokenEx(Token, + TOKEN_ALL_ACCESS, + 0, + SecurityAnonymous, + TokenPrimary, + &LogonToken); + if (!Success) + goto exit; + if (!ProcessIdToSessionId(GetCurrentProcessId(), &SessionId)) + SessionId = 0; + /* place the duplicated token in the service session (session 0) */ + Success = SetTokenInformation(LogonToken, TokenSessionId, &SessionId, sizeof SessionId); + if (!Success) + goto exit; + } if (0 == Environment) { @@ -663,7 +695,7 @@ static NTSTATUS SvcInstanceAccessCheck(HANDLE ClientToken, ULONG DesiredAccess, return Result; } -static NTSTATUS SvcInstanceCreateProcess(PWSTR UserName, +static NTSTATUS SvcInstanceCreateProcess(PWSTR UserName, HANDLE ClientToken, PWSTR Executable, PWSTR CommandLine, PWSTR WorkDirectory, HANDLE StdioHandles[2], PPROCESS_INFORMATION ProcessInfo) @@ -758,7 +790,7 @@ static NTSTATUS SvcInstanceCreateProcess(PWSTR UserName, StartupInfoEx.StartupInfo.hStdOutput = ChildHandles[1]; StartupInfoEx.StartupInfo.hStdError = ChildHandles[2]; - if (!LogonCreateProcess(UserName, + if (!LogonCreateProcess(UserName, ClientToken, Executable, CommandLine, 0, 0, TRUE, CREATE_SUSPENDED | CREATE_NEW_PROCESS_GROUP | EXTENDED_STARTUPINFO_PRESENT, 0, WorkDirectory, @@ -779,7 +811,7 @@ static NTSTATUS SvcInstanceCreateProcess(PWSTR UserName, * Not ideal, but... */ StartupInfoEx.StartupInfo.cb = sizeof StartupInfoEx.StartupInfo; - if (!LogonCreateProcess(UserName, + if (!LogonCreateProcess(UserName, ClientToken, Executable, CommandLine, 0, 0, TRUE, CREATE_SUSPENDED | CREATE_NEW_PROCESS_GROUP, 0, WorkDirectory, @@ -792,7 +824,7 @@ static NTSTATUS SvcInstanceCreateProcess(PWSTR UserName, } else { - if (!LogonCreateProcess(UserName, + if (!LogonCreateProcess(UserName, ClientToken, Executable, CommandLine, 0, 0, FALSE, CREATE_SUSPENDED | CREATE_NEW_PROCESS_GROUP, 0, WorkDirectory, @@ -1009,7 +1041,7 @@ NTSTATUS SvcInstanceCreate(HANDLE ClientToken, if (!NT_SUCCESS(Result)) goto exit; - Result = SvcInstanceCreateProcess(L'\0' != RunAsBuf[0] ? RunAsBuf : 0, + Result = SvcInstanceCreateProcess(L'\0' != RunAsBuf[0] ? RunAsBuf : 0, ClientToken, Executable, SvcInstance->CommandLine, L'\0' != WorkDirectory[0] ? WorkDirectory : 0, RedirectStdio ? SvcInstance->StdioHandles : 0, &ProcessInfo); if (!NT_SUCCESS(Result)) @@ -1629,7 +1661,10 @@ static DWORD WINAPI SvcPipeServer(PVOID Context) ClientToken = 0; if (!ImpersonateNamedPipeClient(SvcPipe) || - !OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, FALSE, &ClientToken) || + ( + !OpenThreadToken(GetCurrentThread(), TOKEN_QUERY | TOKEN_DUPLICATE, FALSE, &ClientToken) && + !OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, FALSE, &ClientToken) + ) || !RevertToSelf()) { LastError = GetLastError();