/** * @file dll/launch.c * * @copyright 2015-2022 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 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); }