From ff155694ce2ac8d3d4c235a5fb96438671710e00 Mon Sep 17 00:00:00 2001 From: Bill Zissimopoulos Date: Tue, 19 Dec 2017 14:49:22 -0800 Subject: [PATCH] launcher: can now perform DefineDosDevice in LocalSystem context dll: uses launcher to DefineDosDevice for LocalService, etc. --- inc/winfsp/winfsp.h | 13 ++++ src/dll/fs.c | 112 ++++++++++++++++++++++++-- src/dll/service.c | 100 ++++++++++++++++++++++++ src/dll/wksid.c | 18 ++--- src/launcher/launcher.c | 169 ++++++++++++++++++++++++++++++++++++++++ src/launcher/launcher.h | 3 +- 6 files changed, 397 insertions(+), 18 deletions(-) diff --git a/inc/winfsp/winfsp.h b/inc/winfsp/winfsp.h index 5b25d8cb..8da15a75 100644 --- a/inc/winfsp/winfsp.h +++ b/inc/winfsp/winfsp.h @@ -1675,6 +1675,19 @@ FSP_API VOID FspServiceStop(FSP_SERVICE *Service); * TRUE if the process is running in running user interactive mode. */ FSP_API BOOLEAN FspServiceIsInteractive(VOID); +/** + * Check if the supplied token is from the service context. + * + * @param Token + * Token to check. Pass NULL to check the current process token. + * @param PIsLocalSystem + * Pointer to a boolean that will receive a TRUE value if the token belongs to LocalSystem + * and FALSE otherwise. May be NULL. + * @return + * STATUS_SUCCESS if the token is from the service context. STATUS_ACCESS_DENIED if it is not. + * Other error codes are possible. + */ +FSP_API NTSTATUS FspServiceContextCheck(HANDLE Token, PBOOLEAN PIsLocalSystem); /** * Log a service message. * diff --git a/src/dll/fs.c b/src/dll/fs.c index 66d7e634..e222d16f 100644 --- a/src/dll/fs.c +++ b/src/dll/fs.c @@ -16,6 +16,7 @@ */ #include +#include enum { @@ -187,17 +188,101 @@ FSP_API VOID FspFileSystemDelete(FSP_FILE_SYSTEM *FileSystem) MemFree(FileSystem); } +static NTSTATUS FspFileSystemLauncherDefineDosDevice( + WCHAR Sign, PWSTR MountPoint, PWSTR VolumeName) +{ + NTSTATUS Result; + ULONG MountPointLen, VolumeNameLen; + PWSTR PipeBuf = 0, P; + DWORD BytesTransferred; + + MountPointLen = lstrlenW(MountPoint); + VolumeNameLen = lstrlenW(VolumeName); + + if (2 != MountPointLen || + FSP_FSCTL_VOLUME_NAME_SIZEMAX / sizeof(WCHAR) <= VolumeNameLen) + return STATUS_INVALID_PARAMETER; + + PipeBuf = MemAlloc(LAUNCHER_PIPE_BUFFER_SIZE); + if (0 == PipeBuf) + { + Result = STATUS_INSUFFICIENT_RESOURCES; + goto exit; + } + + P = PipeBuf; + *P++ = LauncherDefineDosDevice; + *P++ = Sign; + memcpy(P, MountPoint, MountPointLen * sizeof(WCHAR)); P += MountPointLen; *P++ = L'\0'; + memcpy(P, VolumeName, VolumeNameLen * sizeof(WCHAR)); P += VolumeNameLen; *P++ = L'\0'; + + Result = FspCallNamedPipeSecurely(L"" LAUNCHER_PIPE_NAME, + PipeBuf, (ULONG)(P - PipeBuf) * sizeof(WCHAR), PipeBuf, LAUNCHER_PIPE_BUFFER_SIZE, + &BytesTransferred, NMPWAIT_USE_DEFAULT_WAIT, LAUNCHER_PIPE_OWNER); + if (!NT_SUCCESS(Result)) + goto exit; + + if (sizeof(WCHAR) > BytesTransferred) + Result = RPC_NT_PROTOCOL_ERROR; + else if (LauncherSuccess == PipeBuf[0]) + Result = STATUS_SUCCESS; + else if (LauncherFailure == PipeBuf[0]) + { + DWORD 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'); + } + + Result = FspNtStatusFromWin32(ErrorCode); + if (0 == Result) + Result = RPC_NT_PROTOCOL_ERROR; + } + else + Result = RPC_NT_PROTOCOL_ERROR; + +exit: + MemFree(PipeBuf); + + return Result; +} + static NTSTATUS FspFileSystemSetMountPoint_Drive(PWSTR MountPoint, PWSTR VolumeName, PHANDLE PMountHandle) { + NTSTATUS Result; + BOOLEAN IsLocalSystem, IsServiceContext; + *PMountHandle = 0; - if (!DefineDosDeviceW(DDD_RAW_TARGET_PATH, MountPoint, VolumeName)) - return FspNtStatusFromWin32(GetLastError()); + Result = FspServiceContextCheck(0, &IsLocalSystem); + IsServiceContext = NT_SUCCESS(Result) && !IsLocalSystem; + if (IsServiceContext) + { + /* + * If the current process is in the service context but not LocalSystem, + * ask the launcher to DefineDosDevice for us. This is because the launcher + * runs in the LocalSystem context and can create global drives. + * + * In this case the launcher will also add DELETE access to the drive symlink + * for us, so that we can make it temporary below. + */ + Result = FspFileSystemLauncherDefineDosDevice(L'+', MountPoint, VolumeName); + if (!NT_SUCCESS(Result)) + return Result; + } + else + { + if (!DefineDosDeviceW(DDD_RAW_TARGET_PATH, MountPoint, VolumeName)) + return FspNtStatusFromWin32(GetLastError()); + } if (0 != FspNtOpenSymbolicLinkObject) { - NTSTATUS Result; WCHAR SymlinkBuf[6]; UNICODE_STRING Symlink; OBJECT_ATTRIBUTES Obja; @@ -224,6 +309,13 @@ static NTSTATUS FspFileSystemSetMountPoint_Drive(PWSTR MountPoint, PWSTR VolumeN } } + /* HACK: + * + * Handles do not use the low 2 bits (unless they are console handles). + * Abuse this fact to remember that we are running in the service context. + */ + *PMountHandle = (HANDLE)(UINT_PTR)((DWORD)(UINT_PTR)*PMountHandle | IsServiceContext); + return STATUS_SUCCESS; } @@ -411,8 +503,18 @@ exit: static VOID FspFileSystemRemoveMountPoint_Drive(PWSTR MountPoint, PWSTR VolumeName, HANDLE MountHandle) { - DefineDosDeviceW(DDD_RAW_TARGET_PATH | DDD_REMOVE_DEFINITION | DDD_EXACT_MATCH_ON_REMOVE, - MountPoint, VolumeName); + BOOLEAN IsServiceContext = 0 != ((DWORD)(UINT_PTR)MountHandle & 1); + MountHandle = (HANDLE)(UINT_PTR)((DWORD)(UINT_PTR)MountHandle & ~1); + if (IsServiceContext) + /* + * If the current process is in the service context but not LocalSystem, + * ask the launcher to DefineDosDevice for us. This is because the launcher + * runs in the LocalSystem context and can remove global drives. + */ + FspFileSystemLauncherDefineDosDevice(L'-', MountPoint, VolumeName); + else + DefineDosDeviceW(DDD_RAW_TARGET_PATH | DDD_REMOVE_DEFINITION | DDD_EXACT_MATCH_ON_REMOVE, + MountPoint, VolumeName); if (0 != MountHandle) FspNtClose(MountHandle); diff --git a/src/dll/service.c b/src/dll/service.c index 3bc318cb..c0ce5326 100644 --- a/src/dll/service.c +++ b/src/dll/service.c @@ -566,6 +566,106 @@ FSP_API BOOLEAN FspServiceIsInteractive(VOID) return IsInteractive; } +FSP_API NTSTATUS FspServiceContextCheck(HANDLE Token, PBOOLEAN PIsLocalSystem) +{ + NTSTATUS Result; + PSID LocalSystemSid, ServiceSid; + BOOLEAN IsLocalSystem = FALSE; + BOOL HasServiceSid = FALSE; + HANDLE ProcessToken = 0, ImpersonationToken = 0; + DWORD SessionId, Size; + union + { + TOKEN_USER V; + UINT8 B[128]; + } UserInfoBuf; + PTOKEN_USER UserInfo = &UserInfoBuf.V; + + LocalSystemSid = FspWksidGet(WinLocalSystemSid); + ServiceSid = FspWksidGet(WinServiceSid); + if (0 == LocalSystemSid || 0 == ServiceSid) + { + Result = STATUS_INSUFFICIENT_RESOURCES; + goto exit; + } + + if (0 == Token) + { + if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_DUPLICATE, &ProcessToken) || + !DuplicateToken(ProcessToken, SecurityImpersonation, &ImpersonationToken)) + { + Result = FspNtStatusFromWin32(GetLastError()); + goto exit; + } + + Token = ImpersonationToken; + } + + if (!GetTokenInformation(Token, TokenSessionId, &SessionId, sizeof SessionId, &Size)) + { + Result = FspNtStatusFromWin32(GetLastError()); + goto exit; + } + + if (0 != SessionId) + { + Result = STATUS_ACCESS_DENIED; + goto exit; + } + + if (!GetTokenInformation(Token, TokenUser, UserInfo, sizeof UserInfoBuf, &Size)) + { + if (ERROR_INSUFFICIENT_BUFFER != GetLastError()) + { + Result = FspNtStatusFromWin32(GetLastError()); + goto exit; + } + + UserInfo = MemAlloc(Size); + if (0 == UserInfo) + { + Result = STATUS_INSUFFICIENT_RESOURCES; + goto exit; + } + + if (!GetTokenInformation(Token, TokenUser, UserInfo, Size, &Size)) + { + Result = FspNtStatusFromWin32(GetLastError()); + goto exit; + } + } + + IsLocalSystem = EqualSid(LocalSystemSid, UserInfo->User.Sid); + if (IsLocalSystem) + { + Result = STATUS_SUCCESS; + goto exit; + } + + if (!CheckTokenMembership(Token, ServiceSid, &HasServiceSid)) + { + Result = FspNtStatusFromWin32(GetLastError()); + goto exit; + } + + Result = HasServiceSid ? STATUS_SUCCESS : STATUS_ACCESS_DENIED; + +exit: + if (0 != PIsLocalSystem) + *PIsLocalSystem = NT_SUCCESS(Result) ? IsLocalSystem : FALSE; + + if (UserInfo != &UserInfoBuf.V) + MemFree(UserInfo); + + if (0 != ImpersonationToken) + CloseHandle(ImpersonationToken); + + if (0 != ProcessToken) + CloseHandle(ProcessToken); + + return Result; +} + FSP_API VOID FspServiceLog(ULONG Type, PWSTR Format, ...) { va_list ap; diff --git a/src/dll/wksid.c b/src/dll/wksid.c index 4617fcde..a89b50e3 100644 --- a/src/dll/wksid.c +++ b/src/dll/wksid.c @@ -21,8 +21,7 @@ static INIT_ONCE FspWksidInitOnce = INIT_ONCE_STATIC_INIT; static PSID FspWksidWorld; static PSID FspWksidAuthenticatedUser; static PSID FspWksidLocalSystem; -static PSID FspWksidLocalService; -static PSID FspWksidNetworkService; +static PSID FspWksidService; static BOOL WINAPI FspWksidInitialize( PINIT_ONCE InitOnce, PVOID Parameter, PVOID *Context) @@ -30,14 +29,12 @@ static BOOL WINAPI FspWksidInitialize( FspWksidWorld = FspWksidNew(WinWorldSid, 0); FspWksidAuthenticatedUser = FspWksidNew(WinAuthenticatedUserSid, 0); FspWksidLocalSystem = FspWksidNew(WinLocalSystemSid, 0); - FspWksidLocalService = FspWksidNew(WinLocalServiceSid, 0); - FspWksidNetworkService = FspWksidNew(WinNetworkServiceSid, 0); + FspWksidService = FspWksidNew(WinServiceSid, 0); //DEBUGLOGSID("FspWksidWorld=%s", FspWksidWorld); //DEBUGLOGSID("FspWksidAuthenticatedUser=%s", FspWksidAuthenticatedUser); //DEBUGLOGSID("FspWksidLocalSystem=%s", FspWksidLocalSystem); - //DEBUGLOGSID("FspWksidLocalService=%s", FspWksidLocalService); - //DEBUGLOGSID("FspWksidNetworkService=%s", FspWksidNetworkService); + //DEBUGLOGSID("FspWksidService=%s", FspWksidService); return TRUE; } @@ -57,8 +54,7 @@ VOID FspWksidFinalize(BOOLEAN Dynamic) MemFree(FspWksidWorld); FspWksidWorld = 0; MemFree(FspWksidAuthenticatedUser); FspWksidAuthenticatedUser = 0; MemFree(FspWksidLocalSystem); FspWksidLocalSystem = 0; - MemFree(FspWksidLocalService); FspWksidLocalService = 0; - MemFree(FspWksidNetworkService); FspWksidNetworkService = 0; + MemFree(FspWksidService); FspWksidService = 0; } } @@ -104,10 +100,8 @@ PSID FspWksidGet(WELL_KNOWN_SID_TYPE WellKnownSidType) return FspWksidAuthenticatedUser; case WinLocalSystemSid: return FspWksidLocalSystem; - case WinLocalServiceSid: - return FspWksidLocalService; - case WinNetworkServiceSid: - return FspWksidNetworkService; + case WinServiceSid: + return FspWksidService; default: return 0; } diff --git a/src/launcher/launcher.c b/src/launcher/launcher.c index d7cae7d5..52f94f65 100644 --- a/src/launcher/launcher.c +++ b/src/launcher/launcher.c @@ -22,6 +22,13 @@ #define PROGNAME "WinFsp.Launcher" +static NTSTATUS (NTAPI *SvcNtOpenSymbolicLinkObject)( + PHANDLE LinkHandle, + ACCESS_MASK DesiredAccess, + POBJECT_ATTRIBUTES ObjectAttributes); +static NTSTATUS (NTAPI *SvcNtClose)( + HANDLE Handle); + BOOL CreateOverlappedPipe( PHANDLE PReadPipe, PHANDLE PWritePipe, DWORD Size, @@ -143,6 +150,96 @@ exit: return Result; } +static NTSTATUS AddAccessForTokenUser(HANDLE Handle, DWORD Access, HANDLE Token) +{ + TOKEN_USER *User = 0; + PSECURITY_DESCRIPTOR SecurityDescriptor = 0; + PSECURITY_DESCRIPTOR NewSecurityDescriptor = 0; + EXPLICIT_ACCESSW AccessEntry; + DWORD Size, LastError; + NTSTATUS Result; + + if (GetTokenInformation(Token, TokenUser, 0, 0, &Size)) + { + Result = STATUS_INVALID_PARAMETER; + goto exit; + } + if (ERROR_INSUFFICIENT_BUFFER != GetLastError()) + { + Result = FspNtStatusFromWin32(GetLastError()); + goto exit; + } + + User = MemAlloc(Size); + if (0 == User) + { + Result = STATUS_INSUFFICIENT_RESOURCES; + goto exit; + } + + if (!GetTokenInformation(Token, TokenUser, User, Size, &Size)) + { + Result = FspNtStatusFromWin32(GetLastError()); + goto exit; + } + + if (GetKernelObjectSecurity(Handle, DACL_SECURITY_INFORMATION, 0, 0, &Size)) + { + Result = STATUS_INVALID_PARAMETER; + goto exit; + } + if (ERROR_INSUFFICIENT_BUFFER != GetLastError()) + { + Result = FspNtStatusFromWin32(GetLastError()); + goto exit; + } + + SecurityDescriptor = MemAlloc(Size); + if (0 == SecurityDescriptor) + { + Result = STATUS_INSUFFICIENT_RESOURCES; + goto exit; + } + + if (!GetKernelObjectSecurity(Handle, DACL_SECURITY_INFORMATION, SecurityDescriptor, Size, &Size)) + { + Result = FspNtStatusFromWin32(GetLastError()); + goto exit; + } + + AccessEntry.grfAccessPermissions = Access; + AccessEntry.grfAccessMode = GRANT_ACCESS; + AccessEntry.grfInheritance = NO_INHERITANCE; + AccessEntry.Trustee.pMultipleTrustee = 0; + AccessEntry.Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE; + AccessEntry.Trustee.TrusteeForm = TRUSTEE_IS_SID; + AccessEntry.Trustee.TrusteeType = TRUSTEE_IS_UNKNOWN; + AccessEntry.Trustee.ptstrName = User->User.Sid; + + LastError = BuildSecurityDescriptorW(0, 0, 1, &AccessEntry, 0, 0, SecurityDescriptor, + &Size, &NewSecurityDescriptor); + if (0 != LastError) + { + Result = FspNtStatusFromWin32(LastError); + goto exit; + } + + if (!SetKernelObjectSecurity(Handle, DACL_SECURITY_INFORMATION, NewSecurityDescriptor)) + { + Result = FspNtStatusFromWin32(GetLastError()); + goto exit; + } + + Result = STATUS_SUCCESS; + +exit: + LocalFree(NewSecurityDescriptor); + MemFree(SecurityDescriptor); + MemFree(User); + + return Result; +} + static BOOL LogonCreateProcess( PWSTR UserName, LPCWSTR ApplicationName, @@ -1206,6 +1303,53 @@ NTSTATUS SvcInstanceStopAndWaitAll(VOID) return STATUS_SUCCESS; } +NTSTATUS SvcDefineDosDevice(HANDLE ClientToken, + PWSTR DeviceName, PWSTR TargetPath) +{ + NTSTATUS Result; + + if (L'+' != DeviceName[0] && L'-' != DeviceName[0]) + return STATUS_INVALID_PARAMETER; + + Result = FspServiceContextCheck(ClientToken, 0); + if (!NT_SUCCESS(Result)) + return Result; + + if (!DefineDosDeviceW( + DDD_RAW_TARGET_PATH | + (L'+' == DeviceName[0] ? 0 : DDD_REMOVE_DEFINITION | DDD_EXACT_MATCH_ON_REMOVE), + DeviceName + 1, TargetPath)) + return FspNtStatusFromWin32(GetLastError()); + + if (L'+' == DeviceName[0] && 0 != SvcNtOpenSymbolicLinkObject) + { + /* The drive symlink now exists; add DELETE access to it for the ClientToken. */ + WCHAR SymlinkBuf[6]; + UNICODE_STRING Symlink; + OBJECT_ATTRIBUTES Obja; + HANDLE MountHandle; + + memcpy(SymlinkBuf, L"\\??\\X:", sizeof SymlinkBuf); + SymlinkBuf[4] = DeviceName[1]; + Symlink.Length = Symlink.MaximumLength = sizeof SymlinkBuf; + Symlink.Buffer = SymlinkBuf; + + memset(&Obja, 0, sizeof Obja); + Obja.Length = sizeof Obja; + Obja.ObjectName = &Symlink; + Obja.Attributes = OBJ_CASE_INSENSITIVE; + + Result = SvcNtOpenSymbolicLinkObject(&MountHandle, READ_CONTROL | WRITE_DAC, &Obja); + if (NT_SUCCESS(Result)) + { + AddAccessForTokenUser(MountHandle, DELETE, ClientToken); + SvcNtClose(MountHandle); + } + } + + return STATUS_SUCCESS; +} + static HANDLE SvcJob, SvcThread, SvcEvent; static DWORD SvcThreadId; static HANDLE SvcPipe = INVALID_HANDLE_VALUE; @@ -1526,6 +1670,7 @@ static VOID SvcPipeTransact(HANDLE ClientToken, PWSTR PipeBuf, PULONG PSize) PWSTR P = PipeBuf, PipeBufEnd = PipeBuf + *PSize / sizeof(WCHAR); PWSTR ClassName, InstanceName, UserName; + PWSTR DeviceName, TargetPath; ULONG Argc; PWSTR Argv[9]; BOOLEAN HasSecret = FALSE; NTSTATUS Result; @@ -1587,6 +1732,17 @@ static VOID SvcPipeTransact(HANDLE ClientToken, PWSTR PipeBuf, PULONG PSize) SvcPipeTransactResult(Result, PipeBuf, PSize); break; + case LauncherDefineDosDevice: + DeviceName = SvcPipeTransactGetPart(&P, PipeBufEnd); + TargetPath = SvcPipeTransactGetPart(&P, PipeBufEnd); + + Result = STATUS_INVALID_PARAMETER; + if (0 != DeviceName && 0 != TargetPath) + Result = SvcDefineDosDevice(ClientToken, DeviceName, TargetPath); + + SvcPipeTransactResult(Result, PipeBuf, PSize); + break; + #if !defined(NDEBUG) case LauncherQuit: SetEvent(SvcEvent); @@ -1605,6 +1761,19 @@ static VOID SvcPipeTransact(HANDLE ClientToken, PWSTR PipeBuf, PULONG PSize) int wmain(int argc, wchar_t **argv) { + HANDLE Handle = GetModuleHandleW(L"ntdll.dll"); + if (0 != Handle) + { + SvcNtOpenSymbolicLinkObject = (PVOID)GetProcAddress(Handle, "NtOpenSymbolicLinkObject"); + SvcNtClose = (PVOID)GetProcAddress(Handle, "NtClose"); + + if (0 == SvcNtOpenSymbolicLinkObject || 0 == SvcNtClose) + { + SvcNtOpenSymbolicLinkObject = 0; + SvcNtClose = 0; + } + } + return FspServiceRun(L"" PROGNAME, SvcStart, SvcStop, 0); } diff --git a/src/launcher/launcher.h b/src/launcher/launcher.h index 06e1e869..2ac642bb 100644 --- a/src/launcher/launcher.h +++ b/src/launcher/launcher.h @@ -29,7 +29,7 @@ #define LAUNCHER_PIPE_NAME "\\\\.\\pipe\\WinFsp.{14E7137D-22B4-437A-B0C1-D21D1BDF3767}" #define LAUNCHER_PIPE_BUFFER_SIZE 4096 -#define LAUNCHER_PIPE_DEFAULT_TIMEOUT 3000 +#define LAUNCHER_PIPE_DEFAULT_TIMEOUT (2 * 15000 + 1000) #define LAUNCHER_START_WITH_SECRET_TIMEOUT 15000 @@ -62,6 +62,7 @@ enum LauncherSvcInstanceStop = 'T', /* requires: SERVICE_STOP */ LauncherSvcInstanceInfo = 'I', /* requires: SERVICE_QUERY_STATUS */ LauncherSvcInstanceList = 'L', /* requires: none*/ + LauncherDefineDosDevice = 'D', LauncherQuit = 'Q', /* DEBUG version only */ LauncherSuccess = '$',