launcher: can now perform DefineDosDevice in LocalSystem context

dll: uses launcher to DefineDosDevice for LocalService, etc.
This commit is contained in:
Bill Zissimopoulos 2017-12-19 14:49:22 -08:00
parent 5d3b37122c
commit ff155694ce
No known key found for this signature in database
GPG Key ID: 3D4F95D52C7B3EA3
6 changed files with 397 additions and 18 deletions

View File

@ -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.
*

View File

@ -16,6 +16,7 @@
*/
#include <dll/library.h>
#include <launcher/launcher.h>
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);

View File

@ -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;

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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 = '$',