From 58c67e7d8a5afaf6c94729cfa2f46492d1910167 Mon Sep 17 00:00:00 2001 From: Tim Clem Date: Wed, 16 Oct 2024 09:31:43 -0700 Subject: [PATCH 1/4] explorer: Apply a default admin token when running for the desktop. --- dlls/ntdll/unix/process.c | 13 +++++++++++++ dlls/wow64/process.c | 1 + include/winternl.h | 1 + programs/explorer/desktop.c | 6 ++++++ server/protocol.def | 7 +++++++ server/token.c | 16 ++++++++++++++++ 6 files changed, 44 insertions(+) diff --git a/dlls/ntdll/unix/process.c b/dlls/ntdll/unix/process.c index f3551c2e7d7..674d0d0068d 100644 --- a/dlls/ntdll/unix/process.c +++ b/dlls/ntdll/unix/process.c @@ -1775,6 +1775,19 @@ NTSTATUS WINAPI NtSetInformationProcess( HANDLE handle, PROCESSINFOCLASS class, SERVER_END_REQ; return ret; + case ProcessWineSetAdminToken: + SERVER_START_REQ( create_primary_admin_token ) + { + if (!(ret = wine_server_call( req ))) + { + HANDLE token = wine_server_ptr_handle( reply->token ); + PROCESS_ACCESS_TOKEN info = { .Token = token, .Thread = NULL }; + ret = NtSetInformationProcess( handle, ProcessAccessToken, &info, sizeof(info) ); + } + } + SERVER_END_REQ; + return ret; + case ProcessPowerThrottlingState: FIXME( "ProcessPowerThrottlingState - stub\n" ); return STATUS_SUCCESS; diff --git a/dlls/wow64/process.c b/dlls/wow64/process.c index 4f6233002bc..1c0057eff9a 100644 --- a/dlls/wow64/process.c +++ b/dlls/wow64/process.c @@ -871,6 +871,7 @@ NTSTATUS WINAPI wow64_NtSetInformationProcess( UINT *args ) case ProcessPagePriority: /* MEMORY_PRIORITY_INFORMATION */ case ProcessPowerThrottlingState: /* PROCESS_POWER_THROTTLING_STATE */ case ProcessLeapSecondInformation: /* PROCESS_LEAP_SECOND_INFO */ + case ProcessWineSetAdminToken: /* NULL */ return NtSetInformationProcess( handle, class, ptr, len ); case ProcessAccessToken: /* PROCESS_ACCESS_TOKEN */ diff --git a/include/winternl.h b/include/winternl.h index 4f24a921bb1..1f4fdc172bd 100644 --- a/include/winternl.h +++ b/include/winternl.h @@ -1897,6 +1897,7 @@ typedef enum _PROCESSINFOCLASS { #ifdef __WINESRC__ ProcessWineMakeProcessSystem = 1000, ProcessWineLdtCopy, + ProcessWineSetAdminToken, #endif } PROCESSINFOCLASS; diff --git a/programs/explorer/desktop.c b/programs/explorer/desktop.c index 800b03f48bf..1e70a5b74da 100644 --- a/programs/explorer/desktop.c +++ b/programs/explorer/desktop.c @@ -24,6 +24,7 @@ #define COBJMACROS #define OEMRESOURCE #include +#include #include #include #include @@ -1208,6 +1209,7 @@ void manage_desktop( WCHAR *arg ) HMODULE shell32; HANDLE thread; DWORD id; + NTSTATUS status; /* get the rest of the command line (if any) */ while (*p && !is_whitespace(*p)) p++; @@ -1260,6 +1262,10 @@ void manage_desktop( WCHAR *arg ) SetThreadDesktop( desktop ); } + /* the desktop process should always have an admin token */ + status = NtSetInformationProcess( GetCurrentProcess(), ProcessWineSetAdminToken, NULL, 0 ); + if (status) WARN( "couldn't set admin token for desktop, error %08lx\n", status ); + /* create the desktop window */ hwnd = CreateWindowExW( 0, DESKTOP_CLASS_ATOM, NULL, WS_POPUP | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, 0, 0, 0, 0, 0, 0, 0, &guid ); diff --git a/server/protocol.def b/server/protocol.def index c6af7379f17..a7a89724d31 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -3279,6 +3279,13 @@ enum caret_state @END +/* Create a new primary admin token with Default elevation. */ +@REQ(create_primary_admin_token) +@REPLY + obj_handle_t token; /* handle to the token */ +@END + + /* Open a security token */ @REQ(open_token) obj_handle_t handle; /* handle to the thread or process */ diff --git a/server/token.c b/server/token.c index 48ee1eca8fe..698b2627ccb 100644 --- a/server/token.c +++ b/server/token.c @@ -1209,6 +1209,22 @@ DECL_HANDLER(create_token) } +/* create a new primary admin token with Default elevation */ +DECL_HANDLER(create_primary_admin_token) +{ + struct token *token = token_create_admin( TRUE, SecurityIdentification, + TokenElevationTypeDefault, default_session_id ); + if (!token) + { + set_error( STATUS_UNSUCCESSFUL ); + return; + } + + reply->token = alloc_handle( current->process, token, TOKEN_ALL_ACCESS, 0 ); + release_object( token ); +} + + /* open a security token */ DECL_HANDLER(open_token) { From cf4a330eb38acee99b8f3bf5316cf248aa94f29e Mon Sep 17 00:00:00 2001 From: Tim Clem Date: Wed, 16 Oct 2024 08:51:54 -0700 Subject: [PATCH 2/4] Revert "win32u: Create explorer with the thread effective access token." This reverts commit 99e2fad1bf4433d82c3f77c9bdeac1872e6d6ee9. --- dlls/win32u/winstation.c | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/dlls/win32u/winstation.c b/dlls/win32u/winstation.c index d2386fd5af5..386f6924c2f 100644 --- a/dlls/win32u/winstation.c +++ b/dlls/win32u/winstation.c @@ -765,8 +765,7 @@ HWND get_desktop_window(void) static const WCHAR system_dir[] = {'C',':','\\','w','i','n','d','o','w','s','\\', 's','y','s','t','e','m','3','2','\\',0}; RTL_USER_PROCESS_PARAMETERS params = { sizeof(params), sizeof(params) }; - ULONG_PTR buffer[offsetof( PS_ATTRIBUTE_LIST, Attributes[2] ) / sizeof(ULONG_PTR)]; - PS_ATTRIBUTE_LIST *ps_attr = (PS_ATTRIBUTE_LIST *)buffer; + PS_ATTRIBUTE_LIST ps_attr; PS_CREATE_INFO create_info; WCHAR desktop[MAX_PATH]; PEB *peb = NtCurrentTeb()->Peb; @@ -799,30 +798,24 @@ HWND get_desktop_window(void) RtlInitUnicodeString( ¶ms.WindowTitle, appnameW + 4 ); RtlInitUnicodeString( ¶ms.Desktop, desktop ); - ps_attr->Attributes[0].Attribute = PS_ATTRIBUTE_IMAGE_NAME; - ps_attr->Attributes[0].Size = sizeof(appnameW) - sizeof(WCHAR); - ps_attr->Attributes[0].ValuePtr = (WCHAR *)appnameW; - ps_attr->Attributes[0].ReturnLength = NULL; - - ps_attr->Attributes[1].Attribute = PS_ATTRIBUTE_TOKEN; - ps_attr->Attributes[1].Size = sizeof(HANDLE); - ps_attr->Attributes[1].ValuePtr = GetCurrentThreadEffectiveToken(); - ps_attr->Attributes[1].ReturnLength = NULL; - - ps_attr->TotalLength = offsetof( PS_ATTRIBUTE_LIST, Attributes[2] ); + ps_attr.TotalLength = sizeof(ps_attr); + ps_attr.Attributes[0].Attribute = PS_ATTRIBUTE_IMAGE_NAME; + ps_attr.Attributes[0].Size = sizeof(appnameW) - sizeof(WCHAR); + ps_attr.Attributes[0].ValuePtr = (WCHAR *)appnameW; + ps_attr.Attributes[0].ReturnLength = NULL; if (NtCurrentTeb64() && !NtCurrentTeb64()->TlsSlots[WOW64_TLS_FILESYSREDIR]) { NtCurrentTeb64()->TlsSlots[WOW64_TLS_FILESYSREDIR] = TRUE; status = NtCreateUserProcess( &process, &thread, PROCESS_ALL_ACCESS, THREAD_ALL_ACCESS, NULL, NULL, 0, THREAD_CREATE_FLAGS_CREATE_SUSPENDED, ¶ms, - &create_info, ps_attr ); + &create_info, &ps_attr ); NtCurrentTeb64()->TlsSlots[WOW64_TLS_FILESYSREDIR] = FALSE; } else status = NtCreateUserProcess( &process, &thread, PROCESS_ALL_ACCESS, THREAD_ALL_ACCESS, NULL, NULL, 0, THREAD_CREATE_FLAGS_CREATE_SUSPENDED, ¶ms, - &create_info, ps_attr ); + &create_info, &ps_attr ); if (!status) { NtResumeThread( thread, NULL ); From fb08b8fb8ea1ba7649d34bb72a347fa7b21c7083 Mon Sep 17 00:00:00 2001 From: Tim Clem Date: Wed, 16 Oct 2024 10:01:52 -0700 Subject: [PATCH 3/4] ntdll: Count default-elevation admin tokens as elevated from NtQueryInformationToken(TokenElevation). --- dlls/advapi32/tests/security.c | 38 ++++++++++++++++++++++++++++++++++ dlls/kernelbase/security.c | 6 ++++-- dlls/ntdll/unix/security.c | 4 ++-- server/protocol.def | 3 ++- server/token.c | 9 +++++++- 5 files changed, 54 insertions(+), 6 deletions(-) diff --git a/dlls/advapi32/tests/security.c b/dlls/advapi32/tests/security.c index 0917b144648..b51f359a84c 100644 --- a/dlls/advapi32/tests/security.c +++ b/dlls/advapi32/tests/security.c @@ -28,6 +28,7 @@ #include "winbase.h" #include "winerror.h" #include "winternl.h" +#include "winuser.h" #include "aclapi.h" #include "winnt.h" #include "sddl.h" @@ -8537,6 +8538,42 @@ static void test_elevation(void) CloseHandle(token); } +static void test_admin_elevation(void) +{ + /* Tokens with elevation type TokenElevationTypeDefault should still come + back as elevated from a TokenElevation query if they belong to the admin + group. The owner of the desktop window should have such a token. */ + DWORD tid, pid; + HANDLE hproc, htok; + TOKEN_ELEVATION_TYPE elevation_type; + TOKEN_ELEVATION elevation; + DWORD size; + BOOL ret; + + tid = GetWindowThreadProcessId(GetDesktopWindow(), &pid); + ok(tid, "got error %lu\n", GetLastError()); + + hproc = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid); + ok(hproc != NULL, "got error %lu\n", GetLastError()); + + ret = OpenProcessToken(hproc, TOKEN_READ, &htok); + ok(ret, "got error %lu\n", GetLastError()); + + CloseHandle(hproc); + + size = sizeof(elevation_type); + ret = GetTokenInformation(htok, TokenElevationType, &elevation_type, size, &size); + ok(ret, "got error %lu\n", GetLastError()); + ok(elevation_type == TokenElevationTypeDefault, "unexpected elevation type %d\n", elevation_type); + + size = sizeof(elevation); + ret = GetTokenInformation(htok, TokenElevation, &elevation, size, &size); + ok(ret, "got error %lu\n", GetLastError()); + ok(elevation.TokenIsElevated, "expected token to be elevated\n"); + + CloseHandle(htok); +} + static void test_group_as_file_owner(void) { char sd_buffer[200], sid_buffer[100]; @@ -8683,6 +8720,7 @@ START_TEST(security) test_AccessCheck(); test_token_attr(); test_GetTokenInformation(); + test_admin_elevation(); test_LookupAccountSid(); test_LookupAccountName(); test_security_descriptor(); diff --git a/dlls/kernelbase/security.c b/dlls/kernelbase/security.c index 1dd975a7aad..dcf361882df 100644 --- a/dlls/kernelbase/security.c +++ b/dlls/kernelbase/security.c @@ -704,8 +704,8 @@ BOOL WINAPI DuplicateTokenEx( HANDLE token, DWORD access, LPSECURITY_ATTRIBUTES BOOL WINAPI GetTokenInformation( HANDLE token, TOKEN_INFORMATION_CLASS class, LPVOID info, DWORD len, LPDWORD retlen ) { - TRACE("(%p, %s, %p, %ld, %p):\n", - token, + TRACE("(%p, %d [%s], %p, %ld, %p):\n", + token, class, (class == TokenUser) ? "TokenUser" : (class == TokenGroups) ? "TokenGroups" : (class == TokenPrivileges) ? "TokenPrivileges" : @@ -721,6 +721,8 @@ BOOL WINAPI GetTokenInformation( HANDLE token, TOKEN_INFORMATION_CLASS class, (class == TokenGroupsAndPrivileges) ? "TokenGroupsAndPrivileges" : (class == TokenSessionReference) ? "TokenSessionReference" : (class == TokenSandBoxInert) ? "TokenSandBoxInert" : + (class == TokenElevation) ? "TokenElevation" : + (class == TokenElevationType) ? "TokenElevationType" : "Unknown", info, len, retlen); diff --git a/dlls/ntdll/unix/security.c b/dlls/ntdll/unix/security.c index 03cf26c1555..956c0ec4723 100644 --- a/dlls/ntdll/unix/security.c +++ b/dlls/ntdll/unix/security.c @@ -516,7 +516,7 @@ NTSTATUS WINAPI NtQueryInformationToken( HANDLE token, TOKEN_INFORMATION_CLASS c req->handle = wine_server_obj_handle( token ); status = wine_server_call( req ); - if (!status) *type = reply->elevation; + if (!status) *type = reply->elevation_type; } SERVER_END_REQ; break; @@ -528,7 +528,7 @@ NTSTATUS WINAPI NtQueryInformationToken( HANDLE token, TOKEN_INFORMATION_CLASS c req->handle = wine_server_obj_handle( token ); status = wine_server_call( req ); - if (!status) elevation->TokenIsElevated = (reply->elevation == TokenElevationTypeFull); + if (!status) elevation->TokenIsElevated = reply->is_elevated; } SERVER_END_REQ; break; diff --git a/server/protocol.def b/server/protocol.def index a7a89724d31..a4de7cf5ed9 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -3743,7 +3743,8 @@ typedef union unsigned int session_id; /* token session id */ int primary; /* is the token primary or impersonation? */ int impersonation_level; /* level of impersonation */ - int elevation; /* elevation type */ + int elevation_type; /* elevation type (TokenElevation*) */ + int is_elevated; /* is the token elevated as per GetTokenInformation(TokenElevation)? */ int group_count; /* the number of groups the token is a member of */ int privilege_count; /* the number of privileges the token has */ @END diff --git a/server/token.c b/server/token.c index 698b2627ccb..ab3f4b1b5f7 100644 --- a/server/token.c +++ b/server/token.c @@ -1554,7 +1554,14 @@ DECL_HANDLER(get_token_info) reply->session_id = token->session_id; reply->primary = token->primary; reply->impersonation_level = token->impersonation_level; - reply->elevation = token->elevation; + reply->elevation_type = token->elevation; + /* Tokens with TokenElevationTypeDefault are considered elevated for the + purposes of GetTokenInformation(TokenElevation) if they belong to the + admins group. */ + if (token->elevation == TokenElevationTypeDefault) + reply->is_elevated = token_sid_present( token, &builtin_admins_sid, FALSE ); + else + reply->is_elevated = token->elevation == TokenElevationTypeFull; reply->group_count = list_count( &token->groups ); reply->privilege_count = list_count( &token->privileges ); release_object( token ); From 3bd119d7c1aeb166d61d2e903f3e4215702b1690 Mon Sep 17 00:00:00 2001 From: Tim Clem Date: Wed, 16 Oct 2024 09:51:05 -0700 Subject: [PATCH 4/4] services: Apply a default admin token to the process. --- programs/services/services.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/programs/services/services.c b/programs/services/services.c index 9636752438e..097e052b74c 100644 --- a/programs/services/services.c +++ b/programs/services/services.c @@ -1343,6 +1343,13 @@ int __cdecl main(int argc, char *argv[]) JOBOBJECT_ASSOCIATE_COMPLETION_PORT port_info; HANDLE started_event, process_monitor_thread; DWORD err; + NTSTATUS status; + + /* FIXME: Until we start honoring each service's lpServiceStartName, we want + services to inherit a default admin token. */ + status = NtSetInformationProcess( GetCurrentProcess(), ProcessWineSetAdminToken, NULL, 0 ); + if (status) + WARN( "couldn't set admin token, error %08lx\n", status ); job_object = CreateJobObjectW(NULL, NULL); job_limit.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_BREAKAWAY_OK | JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK;