diff --git a/build/VStudio/launcher/launcher.vcxproj b/build/VStudio/launcher/launcher.vcxproj index 4f0c6a86..e687d74a 100644 --- a/build/VStudio/launcher/launcher.vcxproj +++ b/build/VStudio/launcher/launcher.vcxproj @@ -110,6 +110,7 @@ Console true true + %(AdditionalDependencies);rpcrt4.lib @@ -128,6 +129,7 @@ Console true true + %(AdditionalDependencies);rpcrt4.lib @@ -149,6 +151,7 @@ true true true + %(AdditionalDependencies);rpcrt4.lib @@ -170,6 +173,7 @@ true true true + %(AdditionalDependencies);rpcrt4.lib diff --git a/src/dll/np.c b/src/dll/np.c index 80110ec2..602a8d19 100644 --- a/src/dll/np.c +++ b/src/dll/np.c @@ -18,6 +18,7 @@ #include #include #include +#include #define FSP_NP_NAME LIBRARY_NAME ".Np" #define FSP_NP_TYPE ' spF' /* pick a value hopefully not in use */ @@ -403,7 +404,7 @@ DWORD APIENTRY NPAddConnection3(HWND hwndOwner, LPNETRESOURCEW lpNetResource, LPWSTR lpPassword, LPWSTR lpUserName, DWORD dwFlags) { DWORD NpResult; - WCHAR UserName[256], Password[256]; + WCHAR UserName[CREDUI_MAX_USERNAME_LENGTH], Password[CREDUI_MAX_PASSWORD_LENGTH]; /* CONNECT_PROMPT is only valid if CONNECT_INTERACTIVE is also set */ if (CONNECT_PROMPT == (dwFlags & (CONNECT_INTERACTIVE | CONNECT_PROMPT))) diff --git a/src/launcher/launchctl.c b/src/launcher/launchctl.c index 6047c9bd..d48f4399 100644 --- a/src/launcher/launchctl.c +++ b/src/launcher/launchctl.c @@ -115,7 +115,8 @@ static int call_pipe_and_report(PWSTR PipeBuf, ULONG SendSize, ULONG RecvSize) } int start(PWSTR PipeBuf, ULONG PipeBufSize, - PWSTR ClassName, PWSTR InstanceName, DWORD Argc, PWSTR *Argv) + PWSTR ClassName, PWSTR InstanceName, DWORD Argc, PWSTR *Argv, + BOOLEAN HasSecret) { PWSTR P; DWORD ClassNameSize, InstanceNameSize, ArgvSize; @@ -130,7 +131,7 @@ int start(PWSTR PipeBuf, ULONG PipeBufSize, return ERROR_INVALID_PARAMETER; P = PipeBuf; - *P++ = LauncherSvcInstanceStart; + *P++ = HasSecret ? LauncherSvcInstanceStartWithSecret : LauncherSvcInstanceStart; memcpy(P, ClassName, ClassNameSize * sizeof(WCHAR)); P += ClassNameSize; memcpy(P, InstanceName, InstanceNameSize * sizeof(WCHAR)); P += InstanceNameSize; for (DWORD Argi = 0; Argc > Argi; Argi++) @@ -230,7 +231,17 @@ int wmain(int argc, wchar_t **argv) if (3 > argc || argc > 12) usage(); - return start(PipeBuf, LAUNCHER_PIPE_BUFFER_SIZE, argv[1], argv[2], argc - 3, argv + 3); + return start(PipeBuf, LAUNCHER_PIPE_BUFFER_SIZE, argv[1], argv[2], argc - 3, argv + 3, + FALSE); + } + else + if (0 == lstrcmpW(L"startWithSecret", argv[0])) + { + if (3 > argc || argc > 12) + usage(); + + return start(PipeBuf, LAUNCHER_PIPE_BUFFER_SIZE, argv[1], argv[2], argc - 3, argv + 3, + TRUE); } else if (0 == lstrcmpW(L"stop", argv[0])) diff --git a/src/launcher/launcher.c b/src/launcher/launcher.c index 398c206d..8383167d 100644 --- a/src/launcher/launcher.c +++ b/src/launcher/launcher.c @@ -21,6 +21,53 @@ #define PROGNAME "WinFsp.Launcher" #define REGKEY "SYSTEM\\CurrentControlSet\\Services\\" PROGNAME "\\Services" +BOOL CreateOverlappedPipe( + PHANDLE PReadPipe, PHANDLE PWritePipe, PSECURITY_ATTRIBUTES SecurityAttributes, DWORD Size, + DWORD ReadMode, DWORD WriteMode) +{ + RPC_STATUS RpcStatus; + UUID Uuid; + WCHAR PipeNameBuf[MAX_PATH]; + HANDLE ReadPipe, WritePipe; + DWORD LastError; + + RpcStatus = UuidCreate(&Uuid); + if (S_OK != RpcStatus && RPC_S_UUID_LOCAL_ONLY != RpcStatus) + { + SetLastError(ERROR_INTERNAL_ERROR); + return FALSE; + } + + wsprintfW(PipeNameBuf, L"\\\\.\\pipe\\" + "%08lX-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X", + Uuid.Data1, Uuid.Data2, Uuid.Data3, + Uuid.Data4[0], Uuid.Data4[1], Uuid.Data4[2], Uuid.Data4[3], + Uuid.Data4[4], Uuid.Data4[5], Uuid.Data4[6], Uuid.Data4[7]); + + ReadPipe = CreateNamedPipeW(PipeNameBuf, + PIPE_ACCESS_INBOUND | FILE_FLAG_FIRST_PIPE_INSTANCE | ReadMode, + PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT | PIPE_REJECT_REMOTE_CLIENTS, + 1, Size, Size, 120 * 1000, SecurityAttributes); + if (INVALID_HANDLE_VALUE == ReadPipe) + return FALSE; + + WritePipe = CreateFileW(PipeNameBuf, + GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, + SecurityAttributes, OPEN_EXISTING, WriteMode, 0); + if (INVALID_HANDLE_VALUE == WritePipe) + { + LastError = GetLastError(); + CloseHandle(ReadPipe); + SetLastError(LastError); + return FALSE; + } + + *PReadPipe = ReadPipe; + *PWritePipe = WritePipe; + + return TRUE; +} + typedef struct { HANDLE Process; @@ -80,6 +127,7 @@ static VOID CALLBACK KillProcessWait(PVOID Context, BOOLEAN Timeout) typedef struct { + LONG RefCount; PWSTR ClassName; PWSTR InstanceName; PWSTR CommandLine; @@ -87,6 +135,7 @@ typedef struct DWORD ProcessId; HANDLE Process; HANDLE ProcessWait; + HANDLE StdioHandles[2]; LIST_ENTRY ListEntry; WCHAR Buffer[]; } SVC_INSTANCE; @@ -231,8 +280,129 @@ static NTSTATUS SvcInstanceAccessCheck(HANDLE ClientToken, ULONG DesiredAccess, return Result; } +NTSTATUS SvcInstanceCreateProcess(PWSTR Executable, PWSTR CommandLine, + HANDLE StdioHandles[2], + PPROCESS_INFORMATION ProcessInfo) +{ + STARTUPINFOEXW StartupInfoEx; + HANDLE ChildHandles[2] = { INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE }; + HANDLE ParentHandles[2] = { INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE }; + SECURITY_ATTRIBUTES PipeAttributes = { sizeof(SECURITY_ATTRIBUTES), 0, TRUE }; + PPROC_THREAD_ATTRIBUTE_LIST AttrList = 0; + SIZE_T Size; + NTSTATUS Result; + + memset(&StartupInfoEx, 0, sizeof StartupInfoEx); + StartupInfoEx.StartupInfo.cb = sizeof StartupInfoEx.StartupInfo; + + if (0 != StdioHandles) + { + /* + * Create child process and redirect stdin/stdout. Do *not* inherit other handles. + * + * For explanation see: + * https://blogs.msdn.microsoft.com/oldnewthing/20111216-00/?p=8873/ + */ + + /* create stdin read/write ends; make them inheritable */ + if (!CreateOverlappedPipe(&ChildHandles[0], &ParentHandles[0], &PipeAttributes, 0, + 0, 0)) + { + Result = FspNtStatusFromWin32(GetLastError()); + goto exit; + } + + /* create stdout read/write ends; make them inheritable */ + if (!CreateOverlappedPipe(&ParentHandles[1], &ChildHandles[1], &PipeAttributes, 0, + FILE_FLAG_OVERLAPPED, 0)) + { + Result = FspNtStatusFromWin32(GetLastError()); + goto exit; + } + + Size = 0; + if (!InitializeProcThreadAttributeList(0, 1, 0, &Size) && + ERROR_INSUFFICIENT_BUFFER != GetLastError()) + { + Result = FspNtStatusFromWin32(GetLastError()); + goto exit; + } + + AttrList = MemAlloc(Size); + if (0 == AttrList) + { + Result = STATUS_INSUFFICIENT_RESOURCES; + goto exit; + } + + if (!InitializeProcThreadAttributeList(AttrList, 1, 0, &Size)) + { + Result = FspNtStatusFromWin32(GetLastError()); + goto exit; + } + + /* only the child ends of stdin/stdout are actually inherited */ + if (!UpdateProcThreadAttribute(AttrList, 0, PROC_THREAD_ATTRIBUTE_HANDLE_LIST, + ChildHandles, sizeof ChildHandles, 0, 0)) + { + Result = FspNtStatusFromWin32(GetLastError()); + goto exit; + } + + StartupInfoEx.lpAttributeList = AttrList; + StartupInfoEx.StartupInfo.dwFlags = STARTF_USESTDHANDLES; + StartupInfoEx.StartupInfo.hStdInput = ChildHandles[0]; + StartupInfoEx.StartupInfo.hStdOutput = ChildHandles[1]; + StartupInfoEx.StartupInfo.hStdError = INVALID_HANDLE_VALUE; + + if (!CreateProcessW(Executable, CommandLine, 0, 0, TRUE, + CREATE_SUSPENDED | CREATE_NEW_PROCESS_GROUP | EXTENDED_STARTUPINFO_PRESENT, 0, 0, + (PVOID)&StartupInfoEx, ProcessInfo)) + { + Result = FspNtStatusFromWin32(GetLastError()); + goto exit; + } + } + else + { + if (!CreateProcessW(Executable, CommandLine, 0, 0, FALSE, + CREATE_SUSPENDED | CREATE_NEW_PROCESS_GROUP, 0, 0, + &StartupInfoEx.StartupInfo, ProcessInfo)) + { + Result = FspNtStatusFromWin32(GetLastError()); + goto exit; + } + } + + Result = STATUS_SUCCESS; + +exit: + if (!NT_SUCCESS(Result)) + { + if (INVALID_HANDLE_VALUE != ParentHandles[0]) + CloseHandle(ParentHandles[0]); + if (INVALID_HANDLE_VALUE != ParentHandles[0]) + CloseHandle(ParentHandles[0]); + } + else if (0 != StdioHandles) + { + StdioHandles[0] = ParentHandles[0]; + StdioHandles[1] = ParentHandles[1]; + } + + if (INVALID_HANDLE_VALUE != ChildHandles[0]) + CloseHandle(ChildHandles[0]); + if (INVALID_HANDLE_VALUE != ChildHandles[1]) + CloseHandle(ChildHandles[1]); + + MemFree(AttrList); + + return Result; +} + NTSTATUS SvcInstanceCreate(HANDLE ClientToken, PWSTR ClassName, PWSTR InstanceName, ULONG Argc, PWSTR *Argv0, HANDLE Job, + BOOLEAN RedirectStdio, SVC_INSTANCE **PSvcInstance) { SVC_INSTANCE *SvcInstance = 0; @@ -244,7 +414,6 @@ NTSTATUS SvcInstanceCreate(HANDLE ClientToken, DWORD JobControl; PSECURITY_DESCRIPTOR SecurityDescriptor = 0; PWSTR Argv[10]; - STARTUPINFOW StartupInfo; PROCESS_INFORMATION ProcessInfo; NTSTATUS Result; @@ -259,7 +428,6 @@ NTSTATUS SvcInstanceCreate(HANDLE ClientToken, Argv[0] = 0; Argc++; - memset(&StartupInfo, 0, sizeof StartupInfo); memset(&ProcessInfo, 0, sizeof ProcessInfo); EnterCriticalSection(&SvcInstanceLock); @@ -353,23 +521,23 @@ NTSTATUS SvcInstanceCreate(HANDLE ClientToken, } memset(SvcInstance, 0, sizeof *SvcInstance); + SvcInstance->RefCount = 2; memcpy(SvcInstance->Buffer, ClassName, ClassNameSize); memcpy(SvcInstance->Buffer + ClassNameSize / sizeof(WCHAR), InstanceName, InstanceNameSize); SvcInstance->ClassName = SvcInstance->Buffer; SvcInstance->InstanceName = SvcInstance->Buffer + ClassNameSize / sizeof(WCHAR); SvcInstance->SecurityDescriptor = SecurityDescriptor; + SvcInstance->StdioHandles[0] = INVALID_HANDLE_VALUE; + SvcInstance->StdioHandles[1] = INVALID_HANDLE_VALUE; Result = SvcInstanceReplaceArguments(CommandLine, Argc, Argv, &SvcInstance->CommandLine); if (!NT_SUCCESS(Result)) goto exit; - StartupInfo.cb = sizeof StartupInfo; - if (!CreateProcessW(Executable, SvcInstance->CommandLine, 0, 0, FALSE, - CREATE_SUSPENDED | CREATE_NEW_PROCESS_GROUP, 0, 0, &StartupInfo, &ProcessInfo)) - { - Result = FspNtStatusFromWin32(GetLastError()); + Result = SvcInstanceCreateProcess(Executable, CommandLine, + RedirectStdio ? SvcInstance->StdioHandles : 0, &ProcessInfo); + if (!NT_SUCCESS(Result)) goto exit; - } SvcInstance->ProcessId = ProcessInfo.dwProcessId; SvcInstance->Process = ProcessInfo.hProcess; @@ -413,6 +581,11 @@ exit: if (0 != SvcInstance) { + if (INVALID_HANDLE_VALUE != SvcInstance->StdioHandles[0]) + CloseHandle(SvcInstance->StdioHandles[0]); + if (INVALID_HANDLE_VALUE != SvcInstance->StdioHandles[1]) + CloseHandle(SvcInstance->StdioHandles[1]); + if (0 != SvcInstance->ProcessWait) UnregisterWaitEx(SvcInstance->ProcessWait, 0); @@ -435,13 +608,21 @@ exit: return Result; } -VOID SvcInstanceDelete(SVC_INSTANCE *SvcInstance) +static VOID SvcInstanceRelease(SVC_INSTANCE *SvcInstance) { + if (0 != InterlockedDecrement(&SvcInstance->RefCount)) + return; + EnterCriticalSection(&SvcInstanceLock); if (RemoveEntryList(&SvcInstance->ListEntry)) SetEvent(SvcInstanceEvent); LeaveCriticalSection(&SvcInstanceLock); + if (INVALID_HANDLE_VALUE != SvcInstance->StdioHandles[0]) + CloseHandle(SvcInstance->StdioHandles[0]); + if (INVALID_HANDLE_VALUE != SvcInstance->StdioHandles[1]) + CloseHandle(SvcInstance->StdioHandles[1]); + if (0 != SvcInstance->ProcessWait) UnregisterWaitEx(SvcInstance->ProcessWait, 0); if (0 != SvcInstance->Process) @@ -457,15 +638,79 @@ static VOID CALLBACK SvcInstanceTerminated(PVOID Context, BOOLEAN Timeout) { SVC_INSTANCE *SvcInstance = Context; - SvcInstanceDelete(SvcInstance); + SvcInstanceRelease(SvcInstance); } NTSTATUS SvcInstanceStart(HANDLE ClientToken, - PWSTR ClassName, PWSTR InstanceName, ULONG Argc, PWSTR *Argv, HANDLE Job) + PWSTR ClassName, PWSTR InstanceName, ULONG Argc, PWSTR *Argv, HANDLE Job, + BOOLEAN HasSecret) { SVC_INSTANCE *SvcInstance; + NTSTATUS Result; - return SvcInstanceCreate(ClientToken, ClassName, InstanceName, Argc, Argv, Job, &SvcInstance); + HasSecret = HasSecret && 0 < Argc && L'\0' != Argv[Argc - 1][0]; + + Result = SvcInstanceCreate(ClientToken, ClassName, InstanceName, + Argc - HasSecret, Argv, Job, HasSecret, + &SvcInstance); + if (!NT_SUCCESS(Result)) + return Result; + + if (!HasSecret) + Result = STATUS_SUCCESS; + else + { + PWSTR Secret = Argv[Argc - 1]; + UINT8 RspBuf[2]; + DWORD BytesTransferred; + OVERLAPPED Overlapped; + + if (!WriteFile(SvcInstance->StdioHandles[0], Secret, lstrlenW(Secret), &BytesTransferred, 0)) + { + Result = FspNtStatusFromWin32(GetLastError()); + goto exit; + } + + memset(&Overlapped, 0, sizeof Overlapped); + if (!ReadFile(SvcInstance->StdioHandles[1], RspBuf, sizeof RspBuf, 0, &Overlapped) && + ERROR_IO_PENDING != GetLastError()) + { + Result = FspNtStatusFromWin32(GetLastError()); + goto exit; + } + + if (!GetOverlappedResultEx(SvcInstance->StdioHandles[1], &Overlapped, &BytesTransferred, + LAUNCHER_START_WITH_SECRET_TIMEOUT, FALSE)) + { + if (WAIT_TIMEOUT == GetLastError()) + Result = STATUS_TIMEOUT; + else + Result = FspNtStatusFromWin32(GetLastError()); + CancelIoEx(SvcInstance->StdioHandles[1], &Overlapped); + goto exit; + } + + if (sizeof RspBuf <= BytesTransferred && 'O' == RspBuf[0] && 'K' == RspBuf[1]) + Result = STATUS_SUCCESS; + else + Result = STATUS_ACCESS_DENIED; + } + +exit: + if (INVALID_HANDLE_VALUE != SvcInstance->StdioHandles[0]) + { + CloseHandle(SvcInstance->StdioHandles[0]); + SvcInstance->StdioHandles[0] = INVALID_HANDLE_VALUE; + } + if (INVALID_HANDLE_VALUE != SvcInstance->StdioHandles[1]) + { + CloseHandle(SvcInstance->StdioHandles[1]); + SvcInstance->StdioHandles[1] = INVALID_HANDLE_VALUE; + } + + SvcInstanceRelease(SvcInstance); + + return Result; } NTSTATUS SvcInstanceStop(HANDLE ClientToken, @@ -909,12 +1154,16 @@ static VOID SvcPipeTransact(HANDLE ClientToken, PWSTR PipeBuf, PULONG PSize) PWSTR P = PipeBuf, PipeBufEnd = PipeBuf + *PSize / sizeof(WCHAR); PWSTR ClassName, InstanceName; ULONG Argc; PWSTR Argv[9]; + BOOLEAN HasSecret = FALSE; NTSTATUS Result; *PSize = 0; switch (*P++) { + case LauncherSvcInstanceStartWithSecret: + HasSecret = TRUE; + /* fall through! */ case LauncherSvcInstanceStart: ClassName = SvcPipeTransactGetPart(&P, PipeBufEnd); InstanceName = SvcPipeTransactGetPart(&P, PipeBufEnd); @@ -924,7 +1173,8 @@ static VOID SvcPipeTransact(HANDLE ClientToken, PWSTR PipeBuf, PULONG PSize) Result = STATUS_INVALID_PARAMETER; if (0 != ClassName && 0 != InstanceName) - Result = SvcInstanceStart(ClientToken, ClassName, InstanceName, Argc, Argv, SvcJob); + Result = SvcInstanceStart(ClientToken, ClassName, InstanceName, Argc, Argv, SvcJob, + HasSecret); SvcPipeTransactResult(Result, PipeBuf, PSize); break; diff --git a/src/launcher/launcher.h b/src/launcher/launcher.h index d71f73b0..f1645b15 100644 --- a/src/launcher/launcher.h +++ b/src/launcher/launcher.h @@ -28,7 +28,9 @@ #define LAUNCHER_PIPE_BUFFER_SIZE 2048 #define LAUNCHER_PIPE_DEFAULT_TIMEOUT 3000 -/* +#define LAUNCHER_START_WITH_SECRET_TIMEOUT 30000 + + /* * The launcher named pipe SDDL gives full access to LocalSystem and Administrators and * GENERIC_READ and FILE_WRITE_DATA access to Everyone. We are careful not to give the * FILE_CREATE_PIPE_INSTANCE right to Everyone to disallow the creation of additional @@ -53,6 +55,7 @@ enum { LauncherSvcInstanceStart = 'S', /* requires: SERVICE_START */ + LauncherSvcInstanceStartWithSecret = 'X', /* requires: SERVICE_START */ LauncherSvcInstanceStop = 'T', /* requires: SERVICE_STOP */ LauncherSvcInstanceInfo = 'I', /* requires: SERVICE_QUERY_STATUS */ LauncherSvcInstanceList = 'L', /* requires: none*/