diff --git a/src/launcher/launchctl.c b/src/launcher/launchctl.c index bfe92e12..ff81bb5d 100644 --- a/src/launcher/launchctl.c +++ b/src/launcher/launchctl.c @@ -54,9 +54,9 @@ static void usage(void) "\n" "commands:\n" " start ClassName InstanceName Args...\n" - " stop InstanceName\n" + " stop ClassName InstanceName\n" " list\n" - " info\n", + " info ClassName InstanceName\n", PROGNAME); } @@ -105,8 +105,8 @@ int start(PWSTR PipeBuf, ULONG PipeBufSize, ClassNameSize = lstrlenW(ClassName) + 1; InstanceNameSize = lstrlenW(InstanceName) + 1; ArgvSize = 0; - for (DWORD Argi = Argc; Argc > Argi; Argi++) - ArgvSize += lstrlenW(Argv[Argi]); + for (DWORD Argi = 0; Argc > Argi; Argi++) + ArgvSize += lstrlenW(Argv[Argi]) + 1; if (PipeBufSize < (1 + ClassNameSize + InstanceNameSize + ArgvSize) * sizeof(WCHAR)) return ERROR_INVALID_PARAMETER; @@ -115,9 +115,9 @@ int start(PWSTR PipeBuf, ULONG PipeBufSize, *P++ = LauncherSvcInstanceStart; memcpy(P, ClassName, ClassNameSize * sizeof(WCHAR)); P += ClassNameSize; memcpy(P, InstanceName, InstanceNameSize * sizeof(WCHAR)); P += InstanceNameSize; - for (DWORD Argi = Argc; Argc > Argi; Argi++) + for (DWORD Argi = 0; Argc > Argi; Argi++) { - ArgvSize = lstrlenW(Argv[Argi]); + ArgvSize = lstrlenW(Argv[Argi]) + 1; memcpy(P, Argv[Argi], ArgvSize * sizeof(WCHAR)); P += ArgvSize; } @@ -135,20 +135,22 @@ int start(PWSTR PipeBuf, ULONG PipeBufSize, } int stop(PWSTR PipeBuf, ULONG PipeBufSize, - PWSTR InstanceName) + PWSTR ClassName, PWSTR InstanceName) { PWSTR P; - DWORD InstanceNameSize; + DWORD ClassNameSize, InstanceNameSize; DWORD LastError, BytesTransferred; + ClassNameSize = lstrlenW(ClassName) + 1; InstanceNameSize = lstrlenW(InstanceName) + 1; - if (PipeBufSize < (1 + InstanceNameSize) * sizeof(WCHAR)) + if (PipeBufSize < (1 + ClassNameSize + InstanceNameSize) * sizeof(WCHAR)) return ERROR_INVALID_PARAMETER; P = PipeBuf; *P++ = LauncherSvcInstanceStop; - memcpy(P, InstanceName, InstanceNameSize * sizeof(WCHAR)); + memcpy(P, ClassName, ClassNameSize * sizeof(WCHAR)); P += ClassNameSize; + memcpy(P, InstanceName, InstanceNameSize * sizeof(WCHAR)); P += InstanceNameSize; if (CallNamedPipeW(L"" PIPE_NAME, PipeBuf, (DWORD)((P - PipeBuf) * sizeof(WCHAR)), PipeBuf, PipeBufSize, @@ -188,20 +190,22 @@ int list(PWSTR PipeBuf, ULONG PipeBufSize) } int info(PWSTR PipeBuf, ULONG PipeBufSize, - PWSTR InstanceName) + PWSTR ClassName, PWSTR InstanceName) { PWSTR P; - DWORD InstanceNameSize; + DWORD ClassNameSize, InstanceNameSize; DWORD LastError, BytesTransferred; + ClassNameSize = lstrlenW(ClassName) + 1; InstanceNameSize = lstrlenW(InstanceName) + 1; - if (PipeBufSize < (1 + InstanceNameSize) * sizeof(WCHAR)) + if (PipeBufSize < (1 + ClassNameSize + InstanceNameSize) * sizeof(WCHAR)) return ERROR_INVALID_PARAMETER; P = PipeBuf; *P++ = LauncherSvcInstanceInfo; - memcpy(P, InstanceName, InstanceNameSize * sizeof(WCHAR)); + memcpy(P, ClassName, ClassNameSize * sizeof(WCHAR)); P += ClassNameSize; + memcpy(P, InstanceName, InstanceNameSize * sizeof(WCHAR)); P += InstanceNameSize; if (CallNamedPipeW(L"" PIPE_NAME, PipeBuf, (DWORD)((P - PipeBuf) * sizeof(WCHAR)), PipeBuf, PipeBufSize, @@ -231,7 +235,7 @@ int wmain(int argc, wchar_t **argv) if (0 == argc) usage(); - if (0 == lstrcmp(L"start", argv[0])) + if (0 == lstrcmpW(L"start", argv[0])) { if (3 > argc || argc > 12) usage(); @@ -239,26 +243,28 @@ int wmain(int argc, wchar_t **argv) return start(PipeBuf, PIPE_BUFFER_SIZE, argv[1], argv[2], argc - 3, argv + 3); } else - if (0 == lstrcmp(L"stop", argv[0])) + if (0 == lstrcmpW(L"stop", argv[0])) { - if (2 != argc) + if (3 != argc) usage(); - return stop(PipeBuf, PIPE_BUFFER_SIZE, argv[1]); + return stop(PipeBuf, PIPE_BUFFER_SIZE, argv[1], argv[2]); } - if (0 == lstrcmp(L"list", argv[0])) + else + if (0 == lstrcmpW(L"list", argv[0])) { if (1 != argc) usage(); return list(PipeBuf, PIPE_BUFFER_SIZE); } - if (0 == lstrcmp(L"info", argv[0])) + else + if (0 == lstrcmpW(L"info", argv[0])) { - if (2 != argc) + if (3 != argc) usage(); - return info(PipeBuf, PIPE_BUFFER_SIZE, argv[1]); + return info(PipeBuf, PIPE_BUFFER_SIZE, argv[1], argv[2]); } else usage(); diff --git a/src/launcher/launcher.c b/src/launcher/launcher.c index fd9194db..5c345946 100644 --- a/src/launcher/launcher.c +++ b/src/launcher/launcher.c @@ -18,7 +18,7 @@ #include #include -#define PROGNAME "WinFsp-Launcher" +#define PROGNAME "WinFsp.Launcher" #define REGKEY "SYSTEM\\CurrentControlSet\\Services\\" PROGNAME "\\Services" typedef struct @@ -39,7 +39,7 @@ static LIST_ENTRY SvcInstanceList = { &SvcInstanceList, &SvcInstanceList }; static VOID CALLBACK SvcInstanceTerminated(PVOID Context, BOOLEAN Fired); -static SVC_INSTANCE *SvcInstanceFromName(PWSTR InstanceName) +static SVC_INSTANCE *SvcInstanceLookup(PWSTR ClassName, PWSTR InstanceName) { SVC_INSTANCE *SvcInstance; PLIST_ENTRY ListEntry; @@ -50,7 +50,8 @@ static SVC_INSTANCE *SvcInstanceFromName(PWSTR InstanceName) { SvcInstance = CONTAINING_RECORD(ListEntry, SVC_INSTANCE, ListEntry); - if (0 == lstrcmpW(InstanceName, SvcInstance->InstanceName)) + if (0 == lstrcmpW(ClassName, SvcInstance->ClassName) && + 0 == lstrcmpW(InstanceName, SvcInstance->InstanceName)) return SvcInstance; } @@ -174,7 +175,7 @@ NTSTATUS SvcInstanceCreate(HANDLE ClientToken, EnterCriticalSection(&SvcInstanceLock); - if (0 != SvcInstanceFromName(InstanceName)) + if (0 != SvcInstanceLookup(ClassName, InstanceName)) { Result = STATUS_OBJECT_NAME_COLLISION; goto exit; @@ -295,8 +296,11 @@ exit: if (!NT_SUCCESS(Result)) { LocalFree(SecurityDescriptor); - MemFree(SvcInstance->CommandLine); - MemFree(SvcInstance); + if (0 != SvcInstance) + { + MemFree(SvcInstance->CommandLine); + MemFree(SvcInstance); + } } if (0 != RegKey) @@ -337,14 +341,15 @@ NTSTATUS SvcInstanceStart(HANDLE ClientToken, return SvcInstanceCreate(ClientToken, ClassName, InstanceName, Argc, Argv, &SvcInstance); } -NTSTATUS SvcInstanceStop(HANDLE ClientToken, PWSTR InstanceName) +NTSTATUS SvcInstanceStop(HANDLE ClientToken, + PWSTR ClassName, PWSTR InstanceName) { SVC_INSTANCE *SvcInstance; NTSTATUS Result; EnterCriticalSection(&SvcInstanceLock); - SvcInstance = SvcInstanceFromName(InstanceName); + SvcInstance = SvcInstanceLookup(ClassName, InstanceName); if (0 == SvcInstance) { Result = STATUS_OBJECT_NAME_NOT_FOUND; @@ -369,12 +374,13 @@ exit: return Result; } -NTSTATUS SvcInstanceGetNameList(HANDLE ClientToken, PWSTR Buffer, PULONG PSize) +NTSTATUS SvcInstanceGetNameList(HANDLE ClientToken, + PWSTR Buffer, PULONG PSize) { SVC_INSTANCE *SvcInstance; PLIST_ENTRY ListEntry; - PWSTR BufferBeg = Buffer, BufferEnd = Buffer + *PSize / sizeof(WCHAR); - ULONG InstanceNameSize; + PWSTR P = Buffer, BufferEnd = P + *PSize / sizeof(WCHAR); + ULONG ClassNameSize, InstanceNameSize; EnterCriticalSection(&SvcInstanceLock); @@ -384,22 +390,26 @@ NTSTATUS SvcInstanceGetNameList(HANDLE ClientToken, PWSTR Buffer, PULONG PSize) { SvcInstance = CONTAINING_RECORD(ListEntry, SVC_INSTANCE, ListEntry); + ClassNameSize = lstrlenW(SvcInstance->ClassName) + 1; InstanceNameSize = lstrlenW(SvcInstance->InstanceName) + 1; - if (BufferEnd < Buffer + InstanceNameSize) + + if (BufferEnd < P + ClassNameSize + InstanceNameSize) break; - memcpy(Buffer, SvcInstance->InstanceName, InstanceNameSize * sizeof(WCHAR)); - Buffer += InstanceNameSize; + memcpy(P, SvcInstance->ClassName, ClassNameSize * sizeof(WCHAR)); P += ClassNameSize; + *Buffer++ = L' '; + memcpy(P, SvcInstance->InstanceName, InstanceNameSize * sizeof(WCHAR)); P += InstanceNameSize; } LeaveCriticalSection(&SvcInstanceLock); - *PSize = (ULONG)(Buffer - BufferBeg); + *PSize = (ULONG)(P - Buffer); return STATUS_SUCCESS; } -NTSTATUS SvcInstanceGetInfo(HANDLE ClientToken, PWSTR InstanceName, PWSTR Buffer, PULONG PSize) +NTSTATUS SvcInstanceGetInfo(HANDLE ClientToken, + PWSTR ClassName, PWSTR InstanceName, PWSTR Buffer, PULONG PSize) { SVC_INSTANCE *SvcInstance; ULONG ClassNameSize, InstanceNameSize, CommandLineSize; @@ -407,7 +417,7 @@ NTSTATUS SvcInstanceGetInfo(HANDLE ClientToken, PWSTR InstanceName, PWSTR Buffer EnterCriticalSection(&SvcInstanceLock); - SvcInstance = SvcInstanceFromName(InstanceName); + SvcInstance = SvcInstanceLookup(ClassName, InstanceName); if (0 == SvcInstance) { Result = STATUS_OBJECT_NAME_NOT_FOUND; @@ -476,7 +486,7 @@ static NTSTATUS SvcStart(FSP_SERVICE *Service, ULONG argc, PWSTR *argv) PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE | FILE_FLAG_WRITE_THROUGH | FILE_FLAG_OVERLAPPED, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT | PIPE_REJECT_REMOTE_CLIENTS, - 1, PIPE_BUFFER_SIZE, PIPE_BUFFER_SIZE, PIPE_DEFAULT_TIMEOUT, 0); + 1, PIPE_BUFFER_SIZE, PIPE_BUFFER_SIZE, PIPE_DEFAULT_TIMEOUT, &SecurityAttributes); if (INVALID_HANDLE_VALUE == SvcPipe) goto fail; @@ -582,7 +592,8 @@ static DWORD WINAPI SvcPipeServer(PVOID Context) SvcEvent, SvcPipe, &SvcOverlapped, &BytesTransferred); if (-1 == LastError) break; - else if (ERROR_PIPE_CONNECTED != LastError && ERROR_NO_DATA != LastError) + else if (0 != LastError && + ERROR_PIPE_CONNECTED != LastError && ERROR_NO_DATA != LastError) { FspServiceLog(EVENTLOG_WARNING_TYPE, LoopWarningMessage, L"ConnectNamedPipe", LastError); @@ -594,7 +605,7 @@ static DWORD WINAPI SvcPipeServer(PVOID Context) SvcEvent, SvcPipe, &SvcOverlapped, &BytesTransferred); if (-1 == LastError) break; - else if (0 != LastError || sizeof(WCHAR) <= BytesTransferred) + else if (0 != LastError || sizeof(WCHAR) > BytesTransferred) { DisconnectNamedPipe(SvcPipe); if (0 != LastError) @@ -679,7 +690,7 @@ static inline VOID SvcPipeTransactResult(NTSTATUS Result, PWSTR PipeBuf, PULONG if (NT_SUCCESS(Result)) { *PipeBuf = L'$'; - *PSize++; + (*PSize)++; } else *PSize = wsprintfW(PipeBuf, L"!%08lx", FspNtStatusFromWin32(Result)); @@ -706,7 +717,7 @@ static VOID SvcPipeTransact(HANDLE ClientToken, PWSTR PipeBuf, PULONG PSize) if (0 == (Argv[Argc] = SvcPipeTransactGetPart(&P, PipeBufEnd))) break; - Result = STATUS_UNSUCCESSFUL; + Result = STATUS_INVALID_PARAMETER; if (0 != ClassName && 0 != InstanceName) Result = SvcInstanceStart(ClientToken, ClassName, InstanceName, Argc, Argv); @@ -714,9 +725,12 @@ static VOID SvcPipeTransact(HANDLE ClientToken, PWSTR PipeBuf, PULONG PSize) break; case LauncherSvcInstanceStop: + ClassName = SvcPipeTransactGetPart(&P, PipeBufEnd); InstanceName = SvcPipeTransactGetPart(&P, PipeBufEnd); + + Result = STATUS_INVALID_PARAMETER; if (0 != InstanceName) - Result = SvcInstanceStop(ClientToken, InstanceName); + Result = SvcInstanceStop(ClientToken, ClassName, InstanceName); SvcPipeTransactResult(Result, PipeBuf, PSize); break; @@ -729,19 +743,21 @@ static VOID SvcPipeTransact(HANDLE ClientToken, PWSTR PipeBuf, PULONG PSize) break; case LauncherSvcInstanceInfo: + ClassName = SvcPipeTransactGetPart(&P, PipeBufEnd); InstanceName = SvcPipeTransactGetPart(&P, PipeBufEnd); - Result = STATUS_UNSUCCESSFUL; + Result = STATUS_INVALID_PARAMETER; if (0 != InstanceName) { *PSize = PIPE_BUFFER_SIZE - 1; - Result = SvcInstanceGetInfo(ClientToken, InstanceName, PipeBuf + 1, PSize); + Result = SvcInstanceGetInfo(ClientToken, ClassName, InstanceName, PipeBuf + 1, PSize); } SvcPipeTransactResult(Result, PipeBuf, PSize); break; default: + SvcPipeTransactResult(STATUS_INVALID_PARAMETER, PipeBuf, PSize); break; } } diff --git a/src/launcher/launcher.h b/src/launcher/launcher.h index f62a903b..1c039ca4 100644 --- a/src/launcher/launcher.h +++ b/src/launcher/launcher.h @@ -27,11 +27,15 @@ /* * The launcher named pipe SDDL gives full access to LocalSystem and Administrators. - * It also gives generic read access and FILE_WRITE_DATA (DC) to Everyone. Note that - * we cannot give generic write access or equivalently FILE_GENERIC_WRITE (FW) because - * we would also grant the FILE_CREATE_PIPE_INSTANCE right. + * It also gives GENERIC_READ and GENERIC_WRITE access to Everyone. This includes the + * FILE_CREATE_PIPE_INSTANCE right which should not normally be granted to any process + * that is not the pipe server. The reason that the GENERIC_WRITE is required is to allow + * clients to use CallNamedPipeW which opens the pipe handle using CreateFileW and the + * GENERIC_READ | GENERIC_WRITE access right. The reason that it should be safe to grant + * the FILE_CREATE_PIPE_INSTANCE right is that the server creates the named pipe with + * MaxInstances == 1 (and therefore no client can create additional instances). */ -#define PIPE_SDDL "D:P(A;;GA;;;SY)(A;;GA;;;BA)(A;;GRDC;;;WD)" +#define PIPE_SDDL "D:P(A;;GA;;;SY)(A;;GA;;;BA)(A;;GRGW;;;WD)" /* * The default service instance SDDL gives full access to LocalSystem and Administrators.