api: elevate only when needed for system operations

Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
This commit is contained in:
Jason A. Donenfeld 2020-10-30 12:25:24 +01:00 committed by Simon Rozman
parent 779d0e0c38
commit 7964694e1e
8 changed files with 181 additions and 117 deletions

View File

@ -597,10 +597,16 @@ WintunGetAdapter(
_In_z_count_c_(MAX_ADAPTER_NAME) const WCHAR *Name, _In_z_count_c_(MAX_ADAPTER_NAME) const WCHAR *Name,
_Out_ WINTUN_ADAPTER **Adapter) _Out_ WINTUN_ADAPTER **Adapter)
{ {
if (!ElevateToSystem())
return LOG(WINTUN_LOG_ERR, L"Failed to impersonate SYSTEM user"), ERROR_ACCESS_DENIED;
DWORD Result; DWORD Result;
HANDLE Mutex = NamespaceTakeMutex(Pool); HANDLE Mutex = NamespaceTakeMutex(Pool);
if (!Mutex) if (!Mutex)
return ERROR_INVALID_HANDLE; {
Result = ERROR_INVALID_HANDLE;
goto cleanupToken;
}
HDEVINFO DevInfo = SetupDiGetClassDevsExW(&GUID_DEVCLASS_NET, NULL, NULL, DIGCF_PRESENT, NULL, NULL, NULL); HDEVINFO DevInfo = SetupDiGetClassDevsExW(&GUID_DEVCLASS_NET, NULL, NULL, DIGCF_PRESENT, NULL, NULL, NULL);
if (DevInfo == INVALID_HANDLE_VALUE) if (DevInfo == INVALID_HANDLE_VALUE)
@ -675,6 +681,8 @@ cleanupDevInfo:
SetupDiDestroyDeviceInfoList(DevInfo); SetupDiDestroyDeviceInfoList(DevInfo);
cleanupMutex: cleanupMutex:
NamespaceReleaseMutex(Mutex); NamespaceReleaseMutex(Mutex);
cleanupToken:
RevertToSelf();
return Result; return Result;
} }
@ -1562,15 +1570,25 @@ WintunCreateAdapter(
_Out_ WINTUN_ADAPTER **Adapter, _Out_ WINTUN_ADAPTER **Adapter,
_Inout_ BOOL *RebootRequired) _Inout_ BOOL *RebootRequired)
{ {
#ifdef MAYBE_WOW64 if (!ElevateToSystem())
if (NativeMachine != IMAGE_FILE_PROCESS) return LOG(WINTUN_LOG_ERR, L"Failed to impersonate SYSTEM user"), ERROR_ACCESS_DENIED;
return CreateAdapterNatively(Pool, Name, RequestedGUID, Adapter, RebootRequired);
#endif
DWORD Result = ERROR_SUCCESS; DWORD Result = ERROR_SUCCESS;
#ifdef MAYBE_WOW64
if (NativeMachine != IMAGE_FILE_PROCESS)
{
Result = CreateAdapterNatively(Pool, Name, RequestedGUID, Adapter, RebootRequired);
RevertToSelf();
return Result;
}
#endif
WCHAR RandomTempSubDirectory[MAX_PATH]; WCHAR RandomTempSubDirectory[MAX_PATH];
if ((Result = CreateTemporaryDirectory(RandomTempSubDirectory)) != ERROR_SUCCESS) if ((Result = CreateTemporaryDirectory(RandomTempSubDirectory)) != ERROR_SUCCESS)
return LOG(WINTUN_LOG_ERR, L"Failed to create temporary folder"), Result; {
LOG(WINTUN_LOG_ERR, L"Failed to create temporary folder");
goto cleanupToken;
}
WCHAR CatPath[MAX_PATH] = { 0 }; WCHAR CatPath[MAX_PATH] = { 0 };
WCHAR SysPath[MAX_PATH] = { 0 }; WCHAR SysPath[MAX_PATH] = { 0 };
@ -1619,6 +1637,8 @@ cleanupDelete:
DeleteFileW(InfPath); DeleteFileW(InfPath);
cleanupDirectory: cleanupDirectory:
RemoveDirectoryW(RandomTempSubDirectory); RemoveDirectoryW(RandomTempSubDirectory);
cleanupToken:
RevertToSelf();
return Result; return Result;
} }
@ -1663,20 +1683,31 @@ cleanupArgv:
WINTUN_STATUS WINAPI WINTUN_STATUS WINAPI
WintunDeleteAdapter(_In_ const WINTUN_ADAPTER *Adapter, _Inout_ BOOL *RebootRequired) WintunDeleteAdapter(_In_ const WINTUN_ADAPTER *Adapter, _Inout_ BOOL *RebootRequired)
{ {
if (!ElevateToSystem())
return LOG(WINTUN_LOG_ERR, L"Failed to impersonate SYSTEM user"), ERROR_ACCESS_DENIED;
DWORD Result;
#ifdef MAYBE_WOW64 #ifdef MAYBE_WOW64
if (NativeMachine != IMAGE_FILE_PROCESS) if (NativeMachine != IMAGE_FILE_PROCESS)
return DeleteAdapterNatively(Adapter, RebootRequired); {
Result = DeleteAdapterNatively(Adapter, RebootRequired);
RevertToSelf();
return Result;
}
#endif #endif
HDEVINFO DevInfo; HDEVINFO DevInfo;
SP_DEVINFO_DATA DevInfoData; SP_DEVINFO_DATA DevInfoData;
DWORD Result = GetDevInfoData(&Adapter->CfgInstanceID, &DevInfo, &DevInfoData); Result = GetDevInfoData(&Adapter->CfgInstanceID, &DevInfo, &DevInfoData);
if (Result == ERROR_FILE_NOT_FOUND) if (Result == ERROR_FILE_NOT_FOUND)
return ERROR_SUCCESS; {
if (Result != ERROR_SUCCESS) Result = ERROR_SUCCESS;
goto cleanupToken;
}
else if (Result != ERROR_SUCCESS)
{ {
LOG(WINTUN_LOG_ERR, L"Failed to get device info data"); LOG(WINTUN_LOG_ERR, L"Failed to get device info data");
return Result; goto cleanupToken;
} }
SetQuietInstall(DevInfo, &DevInfoData); SetQuietInstall(DevInfo, &DevInfoData);
SP_REMOVEDEVICE_PARAMS RemoveDeviceParams = { .ClassInstallHeader = { .cbSize = sizeof(SP_CLASSINSTALL_HEADER), SP_REMOVEDEVICE_PARAMS RemoveDeviceParams = { .ClassInstallHeader = { .cbSize = sizeof(SP_CLASSINSTALL_HEADER),
@ -1689,6 +1720,8 @@ WintunDeleteAdapter(_In_ const WINTUN_ADAPTER *Adapter, _Inout_ BOOL *RebootRequ
else else
Result = LOG_LAST_ERROR(L"Unable to remove existing adapter"); Result = LOG_LAST_ERROR(L"Unable to remove existing adapter");
SetupDiDestroyDeviceInfoList(DevInfo); SetupDiDestroyDeviceInfoList(DevInfo);
cleanupToken:
RevertToSelf();
return Result; return Result;
} }

View File

@ -195,6 +195,7 @@
<ClInclude Include="api.h" /> <ClInclude Include="api.h" />
<ClInclude Include="adapter.h" /> <ClInclude Include="adapter.h" />
<ClInclude Include="atomic.h" /> <ClInclude Include="atomic.h" />
<ClInclude Include="elevate.h" />
<ClInclude Include="logger.h" /> <ClInclude Include="logger.h" />
<ClInclude Include="namespace.h" /> <ClInclude Include="namespace.h" />
<ClInclude Include="nci.h" /> <ClInclude Include="nci.h" />
@ -206,6 +207,7 @@
<ItemGroup> <ItemGroup>
<ClCompile Include="api.c" /> <ClCompile Include="api.c" />
<ClCompile Include="adapter.c" /> <ClCompile Include="adapter.c" />
<ClCompile Include="elevate.c" />
<ClCompile Include="logger.c" /> <ClCompile Include="logger.c" />
<ClCompile Include="namespace.c" /> <ClCompile Include="namespace.c" />
<ClCompile Include="nci.c" /> <ClCompile Include="nci.c" />

View File

@ -55,6 +55,9 @@
<ClInclude Include="atomic.h"> <ClInclude Include="atomic.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="elevate.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="api.c"> <ClCompile Include="api.c">
@ -87,5 +90,8 @@
<ClCompile Include="session.c"> <ClCompile Include="session.c">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="elevate.c">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
</Project> </Project>

101
api/elevate.c Normal file
View File

@ -0,0 +1,101 @@
/* SPDX-License-Identifier: GPL-2.0
*
* Copyright (C) 2018-2020 WireGuard LLC. All Rights Reserved.
*/
#include "pch.h"
BOOL
ElevateToSystem(void)
{
HANDLE CurrentProcessToken, ThreadToken, ProcessSnapshot, WinlogonProcess, WinlogonToken, DuplicatedToken;
PROCESSENTRY32W ProcessEntry = { .dwSize = sizeof(PROCESSENTRY32W) };
BOOL Ret;
DWORD LastError = ERROR_SUCCESS;
TOKEN_PRIVILEGES Privileges = { .PrivilegeCount = 1, .Privileges = { { .Attributes = SE_PRIVILEGE_ENABLED } } };
CHAR LocalSystemSid[0x400];
DWORD RequiredBytes = sizeof(LocalSystemSid);
struct
{
TOKEN_USER MaybeLocalSystem;
CHAR LargeEnoughForLocalSystem[0x400];
} TokenUserBuffer;
Ret = CreateWellKnownSid(WinLocalSystemSid, NULL, &LocalSystemSid, &RequiredBytes);
LastError = GetLastError();
if (!Ret)
goto cleanup;
Ret = OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &CurrentProcessToken);
LastError = GetLastError();
if (!Ret)
goto cleanup;
Ret =
GetTokenInformation(CurrentProcessToken, TokenUser, &TokenUserBuffer, sizeof(TokenUserBuffer), &RequiredBytes);
LastError = GetLastError();
CloseHandle(CurrentProcessToken);
if (!Ret)
goto cleanup;
if (EqualSid(TokenUserBuffer.MaybeLocalSystem.User.Sid, LocalSystemSid))
return TRUE;
Ret = LookupPrivilegeValueW(NULL, SE_DEBUG_NAME, &Privileges.Privileges[0].Luid);
LastError = GetLastError();
if (!Ret)
goto cleanup;
ProcessSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
LastError = GetLastError();
if (ProcessSnapshot == INVALID_HANDLE_VALUE)
goto cleanup;
for (Ret = Process32FirstW(ProcessSnapshot, &ProcessEntry); Ret;
Ret = Process32NextW(ProcessSnapshot, &ProcessEntry))
{
if (_wcsicmp(ProcessEntry.szExeFile, L"winlogon.exe"))
continue;
RevertToSelf();
Ret = ImpersonateSelf(SecurityImpersonation);
LastError = GetLastError();
if (!Ret)
continue;
Ret = OpenThreadToken(GetCurrentThread(), TOKEN_ADJUST_PRIVILEGES, FALSE, &ThreadToken);
LastError = GetLastError();
if (!Ret)
continue;
Ret = AdjustTokenPrivileges(ThreadToken, FALSE, &Privileges, sizeof(Privileges), NULL, NULL);
LastError = GetLastError();
CloseHandle(ThreadToken);
if (!Ret)
continue;
WinlogonProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, ProcessEntry.th32ProcessID);
LastError = GetLastError();
if (!WinlogonProcess)
continue;
Ret = OpenProcessToken(WinlogonProcess, TOKEN_IMPERSONATE | TOKEN_DUPLICATE, &WinlogonToken);
LastError = GetLastError();
CloseHandle(WinlogonProcess);
if (!Ret)
continue;
Ret = DuplicateToken(WinlogonToken, SecurityImpersonation, &DuplicatedToken);
LastError = GetLastError();
CloseHandle(WinlogonToken);
if (!Ret)
continue;
if (!GetTokenInformation(DuplicatedToken, TokenUser, &TokenUserBuffer, sizeof(TokenUserBuffer), &RequiredBytes))
goto next;
if (SetLastError(ERROR_ACCESS_DENIED), !EqualSid(TokenUserBuffer.MaybeLocalSystem.User.Sid, LocalSystemSid))
goto next;
if (!SetThreadToken(NULL, DuplicatedToken))
goto next;
CloseHandle(DuplicatedToken);
CloseHandle(ProcessSnapshot);
SetLastError(ERROR_SUCCESS);
return TRUE;
next:
LastError = GetLastError();
CloseHandle(DuplicatedToken);
}
RevertToSelf();
CloseHandle(ProcessSnapshot);
cleanup:
SetLastError(LastError);
return FALSE;
}

9
api/elevate.h Normal file
View File

@ -0,0 +1,9 @@
/* SPDX-License-Identifier: GPL-2.0
*
* Copyright (C) 2018-2020 WireGuard LLC. All Rights Reserved.
*/
#pragma once
BOOL
ElevateToSystem(void);

View File

@ -8,6 +8,7 @@
#include "adapter.h" #include "adapter.h"
#include "atomic.h" #include "atomic.h"
#include "api.h" #include "api.h"
#include "elevate.h"
#include "logger.h" #include "logger.h"
#include "namespace.h" #include "namespace.h"
#include "nci.h" #include "nci.h"

View File

@ -56,101 +56,6 @@ ConsoleLogger(_In_ WINTUN_LOGGER_LEVEL Level, _In_ const WCHAR *LogLine)
return TRUE; return TRUE;
} }
static BOOL
ElevateToSystem(void)
{
HANDLE CurrentProcessToken, ThreadToken, ProcessSnapshot, WinlogonProcess, WinlogonToken, DuplicatedToken;
PROCESSENTRY32W ProcessEntry = { .dwSize = sizeof(PROCESSENTRY32W) };
BOOL Ret;
DWORD LastError = ERROR_SUCCESS;
TOKEN_PRIVILEGES Privileges = { .PrivilegeCount = 1, .Privileges = { { .Attributes = SE_PRIVILEGE_ENABLED } } };
CHAR LocalSystemSid[0x400];
DWORD RequiredBytes = sizeof(LocalSystemSid);
struct
{
TOKEN_USER MaybeLocalSystem;
CHAR LargeEnoughForLocalSystem[0x400];
} TokenUserBuffer;
Ret = CreateWellKnownSid(WinLocalSystemSid, NULL, &LocalSystemSid, &RequiredBytes);
LastError = GetLastError();
if (!Ret)
goto cleanup;
Ret = OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &CurrentProcessToken);
LastError = GetLastError();
if (!Ret)
goto cleanup;
Ret =
GetTokenInformation(CurrentProcessToken, TokenUser, &TokenUserBuffer, sizeof(TokenUserBuffer), &RequiredBytes);
LastError = GetLastError();
CloseHandle(CurrentProcessToken);
if (!Ret)
goto cleanup;
if (EqualSid(TokenUserBuffer.MaybeLocalSystem.User.Sid, LocalSystemSid))
return TRUE;
Ret = LookupPrivilegeValueW(NULL, SE_DEBUG_NAME, &Privileges.Privileges[0].Luid);
LastError = GetLastError();
if (!Ret)
goto cleanup;
ProcessSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
LastError = GetLastError();
if (ProcessSnapshot == INVALID_HANDLE_VALUE)
goto cleanup;
for (Ret = Process32FirstW(ProcessSnapshot, &ProcessEntry); Ret;
Ret = Process32NextW(ProcessSnapshot, &ProcessEntry))
{
if (_wcsicmp(ProcessEntry.szExeFile, L"winlogon.exe"))
continue;
RevertToSelf();
Ret = ImpersonateSelf(SecurityImpersonation);
LastError = GetLastError();
if (!Ret)
continue;
Ret = OpenThreadToken(GetCurrentThread(), TOKEN_ADJUST_PRIVILEGES, FALSE, &ThreadToken);
LastError = GetLastError();
if (!Ret)
continue;
Ret = AdjustTokenPrivileges(ThreadToken, FALSE, &Privileges, sizeof(Privileges), NULL, NULL);
LastError = GetLastError();
CloseHandle(ThreadToken);
if (!Ret)
continue;
WinlogonProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, ProcessEntry.th32ProcessID);
LastError = GetLastError();
if (!WinlogonProcess)
continue;
Ret = OpenProcessToken(WinlogonProcess, TOKEN_IMPERSONATE | TOKEN_DUPLICATE, &WinlogonToken);
LastError = GetLastError();
CloseHandle(WinlogonProcess);
if (!Ret)
continue;
Ret = DuplicateToken(WinlogonToken, SecurityImpersonation, &DuplicatedToken);
LastError = GetLastError();
CloseHandle(WinlogonToken);
if (!Ret)
continue;
if (!GetTokenInformation(DuplicatedToken, TokenUser, &TokenUserBuffer, sizeof(TokenUserBuffer), &RequiredBytes))
goto next;
if (SetLastError(ERROR_ACCESS_DENIED), !EqualSid(TokenUserBuffer.MaybeLocalSystem.User.Sid, LocalSystemSid))
goto next;
if (!SetThreadToken(NULL, DuplicatedToken))
goto next;
CloseHandle(DuplicatedToken);
CloseHandle(ProcessSnapshot);
SetLastError(ERROR_SUCCESS);
return TRUE;
next:
LastError = GetLastError();
CloseHandle(DuplicatedToken);
}
RevertToSelf();
CloseHandle(ProcessSnapshot);
cleanup:
SetLastError(LastError);
return FALSE;
}
static int Argc; static int Argc;
static WCHAR **Argv; static WCHAR **Argv;
@ -159,13 +64,11 @@ Init(void)
{ {
WintunSetLogger(ConsoleLogger); WintunSetLogger(ConsoleLogger);
Argv = CommandLineToArgvW(GetCommandLineW(), &Argc); Argv = CommandLineToArgvW(GetCommandLineW(), &Argc);
ElevateToSystem();
} }
static void static void
Done(void) Done(void)
{ {
RevertToSelf();
LocalFree(Argv); LocalFree(Argv);
} }

View File

@ -78,13 +78,19 @@ WintunStartSession(_In_ const WINTUN_ADAPTER *Adapter, _In_ DWORD Capacity, _Out
Result = LOG_LAST_ERROR(L"Failed to allocate ring memory"); Result = LOG_LAST_ERROR(L"Failed to allocate ring memory");
goto cleanupRings; goto cleanupRings;
} }
if (!ElevateToSystem())
{
LOG(WINTUN_LOG_ERR, L"Failed to impersonate SYSTEM user");
Result = ERROR_ACCESS_DENIED;
goto cleanupAllocatedRegion;
}
(*Session)->Descriptor.Send.RingSize = RingSize; (*Session)->Descriptor.Send.RingSize = RingSize;
(*Session)->Descriptor.Send.Ring = (TUN_RING *)AllocatedRegion; (*Session)->Descriptor.Send.Ring = (TUN_RING *)AllocatedRegion;
(*Session)->Descriptor.Send.TailMoved = CreateEventW(SecurityAttributes, FALSE, FALSE, NULL); (*Session)->Descriptor.Send.TailMoved = CreateEventW(SecurityAttributes, FALSE, FALSE, NULL);
if (!(*Session)->Descriptor.Send.TailMoved) if (!(*Session)->Descriptor.Send.TailMoved)
{ {
Result = LOG_LAST_ERROR(L"Failed to create send event"); Result = LOG_LAST_ERROR(L"Failed to create send event");
goto cleanupAllocatedRegion; goto cleanupToken;
} }
(*Session)->Descriptor.Receive.RingSize = RingSize; (*Session)->Descriptor.Receive.RingSize = RingSize;
@ -104,18 +110,19 @@ WintunStartSession(_In_ const WINTUN_ADAPTER *Adapter, _In_ DWORD Capacity, _Out
} }
DWORD BytesReturned; DWORD BytesReturned;
if (!DeviceIoControl( if (!DeviceIoControl(
(*Session)->Handle, (*Session)->Handle,
TUN_IOCTL_REGISTER_RINGS, TUN_IOCTL_REGISTER_RINGS,
&(*Session)->Descriptor, &(*Session)->Descriptor,
sizeof(TUN_REGISTER_RINGS), sizeof(TUN_REGISTER_RINGS),
NULL, NULL,
0, 0,
&BytesReturned, &BytesReturned,
NULL)) NULL))
{ {
Result = LOG_LAST_ERROR(L"Failed to perform ioctl"); Result = LOG_LAST_ERROR(L"Failed to perform ioctl");
goto cleanupHandle; goto cleanupHandle;
} }
RevertToSelf();
(*Session)->Capacity = Capacity; (*Session)->Capacity = Capacity;
(void)InitializeCriticalSectionAndSpinCount(&(*Session)->Receive.Lock, LOCK_SPIN_COUNT); (void)InitializeCriticalSectionAndSpinCount(&(*Session)->Receive.Lock, LOCK_SPIN_COUNT);
(void)InitializeCriticalSectionAndSpinCount(&(*Session)->Send.Lock, LOCK_SPIN_COUNT); (void)InitializeCriticalSectionAndSpinCount(&(*Session)->Send.Lock, LOCK_SPIN_COUNT);
@ -126,6 +133,8 @@ cleanupReceiveTailMoved:
CloseHandle((*Session)->Descriptor.Receive.TailMoved); CloseHandle((*Session)->Descriptor.Receive.TailMoved);
cleanupSendTailMoved: cleanupSendTailMoved:
CloseHandle((*Session)->Descriptor.Send.TailMoved); CloseHandle((*Session)->Descriptor.Send.TailMoved);
cleanupToken:
RevertToSelf();
cleanupAllocatedRegion: cleanupAllocatedRegion:
VirtualFree(AllocatedRegion, 0, MEM_RELEASE); VirtualFree(AllocatedRegion, 0, MEM_RELEASE);
cleanupRings: cleanupRings:
@ -137,7 +146,7 @@ cleanupRings:
void WINAPI void WINAPI
WintunEndSession(_In_ TUN_SESSION *Session) WintunEndSession(_In_ TUN_SESSION *Session)
{ {
SetEvent(Session->Descriptor.Send.TailMoved); // wake the reader if it's sleeping SetEvent(Session->Descriptor.Send.TailMoved); // Wake the reader if it's sleeping.
DeleteCriticalSection(&Session->Send.Lock); DeleteCriticalSection(&Session->Send.Lock);
DeleteCriticalSection(&Session->Receive.Lock); DeleteCriticalSection(&Session->Receive.Lock);
CloseHandle(Session->Handle); CloseHandle(Session->Handle);