Merge branch 'mr/default-explorer-token' into 'master'

explorer, services: Use admin tokens when appropriate.

See merge request wine/wine!6602
This commit is contained in:
Tim Clem 2024-11-19 09:20:29 +00:00
commit bb3a7726ba
11 changed files with 113 additions and 21 deletions

View file

@ -28,6 +28,7 @@
#include "winbase.h" #include "winbase.h"
#include "winerror.h" #include "winerror.h"
#include "winternl.h" #include "winternl.h"
#include "winuser.h"
#include "aclapi.h" #include "aclapi.h"
#include "winnt.h" #include "winnt.h"
#include "sddl.h" #include "sddl.h"
@ -8571,6 +8572,42 @@ static void test_elevation(void)
CloseHandle(token); 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) static void test_group_as_file_owner(void)
{ {
char sd_buffer[200], sid_buffer[100]; char sd_buffer[200], sid_buffer[100];
@ -8717,6 +8754,7 @@ START_TEST(security)
test_AccessCheck(); test_AccessCheck();
test_token_attr(); test_token_attr();
test_GetTokenInformation(); test_GetTokenInformation();
test_admin_elevation();
test_LookupAccountSid(); test_LookupAccountSid();
test_LookupAccountName(); test_LookupAccountName();
test_security_descriptor(); test_security_descriptor();

View file

@ -704,8 +704,8 @@ BOOL WINAPI DuplicateTokenEx( HANDLE token, DWORD access, LPSECURITY_ATTRIBUTES
BOOL WINAPI GetTokenInformation( HANDLE token, TOKEN_INFORMATION_CLASS class, BOOL WINAPI GetTokenInformation( HANDLE token, TOKEN_INFORMATION_CLASS class,
LPVOID info, DWORD len, LPDWORD retlen ) LPVOID info, DWORD len, LPDWORD retlen )
{ {
TRACE("(%p, %s, %p, %ld, %p):\n", TRACE("(%p, %d [%s], %p, %ld, %p):\n",
token, token, class,
(class == TokenUser) ? "TokenUser" : (class == TokenUser) ? "TokenUser" :
(class == TokenGroups) ? "TokenGroups" : (class == TokenGroups) ? "TokenGroups" :
(class == TokenPrivileges) ? "TokenPrivileges" : (class == TokenPrivileges) ? "TokenPrivileges" :
@ -721,6 +721,8 @@ BOOL WINAPI GetTokenInformation( HANDLE token, TOKEN_INFORMATION_CLASS class,
(class == TokenGroupsAndPrivileges) ? "TokenGroupsAndPrivileges" : (class == TokenGroupsAndPrivileges) ? "TokenGroupsAndPrivileges" :
(class == TokenSessionReference) ? "TokenSessionReference" : (class == TokenSessionReference) ? "TokenSessionReference" :
(class == TokenSandBoxInert) ? "TokenSandBoxInert" : (class == TokenSandBoxInert) ? "TokenSandBoxInert" :
(class == TokenElevation) ? "TokenElevation" :
(class == TokenElevationType) ? "TokenElevationType" :
"Unknown", "Unknown",
info, len, retlen); info, len, retlen);

View file

@ -1837,6 +1837,19 @@ NTSTATUS WINAPI NtSetInformationProcess( HANDLE handle, PROCESSINFOCLASS class,
SERVER_END_REQ; SERVER_END_REQ;
return ret; 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: case ProcessPowerThrottlingState:
FIXME( "ProcessPowerThrottlingState - stub\n" ); FIXME( "ProcessPowerThrottlingState - stub\n" );
return STATUS_SUCCESS; return STATUS_SUCCESS;

View file

@ -516,7 +516,7 @@ NTSTATUS WINAPI NtQueryInformationToken( HANDLE token, TOKEN_INFORMATION_CLASS c
req->handle = wine_server_obj_handle( token ); req->handle = wine_server_obj_handle( token );
status = wine_server_call( req ); status = wine_server_call( req );
if (!status) *type = reply->elevation; if (!status) *type = reply->elevation_type;
} }
SERVER_END_REQ; SERVER_END_REQ;
break; break;
@ -528,7 +528,7 @@ NTSTATUS WINAPI NtQueryInformationToken( HANDLE token, TOKEN_INFORMATION_CLASS c
req->handle = wine_server_obj_handle( token ); req->handle = wine_server_obj_handle( token );
status = wine_server_call( req ); status = wine_server_call( req );
if (!status) elevation->TokenIsElevated = (reply->elevation == TokenElevationTypeFull); if (!status) elevation->TokenIsElevated = reply->is_elevated;
} }
SERVER_END_REQ; SERVER_END_REQ;
break; break;

View file

@ -765,8 +765,7 @@ HWND get_desktop_window(void)
static const WCHAR system_dir[] = {'C',':','\\','w','i','n','d','o','w','s','\\', static const WCHAR system_dir[] = {'C',':','\\','w','i','n','d','o','w','s','\\',
's','y','s','t','e','m','3','2','\\',0}; 's','y','s','t','e','m','3','2','\\',0};
RTL_USER_PROCESS_PARAMETERS params = { sizeof(params), sizeof(params) }; 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 *ps_attr = (PS_ATTRIBUTE_LIST *)buffer;
PS_CREATE_INFO create_info; PS_CREATE_INFO create_info;
WCHAR desktop[MAX_PATH]; WCHAR desktop[MAX_PATH];
PEB *peb = NtCurrentTeb()->Peb; PEB *peb = NtCurrentTeb()->Peb;
@ -799,30 +798,24 @@ HWND get_desktop_window(void)
RtlInitUnicodeString( &params.WindowTitle, appnameW + 4 ); RtlInitUnicodeString( &params.WindowTitle, appnameW + 4 );
RtlInitUnicodeString( &params.Desktop, desktop ); RtlInitUnicodeString( &params.Desktop, desktop );
ps_attr->Attributes[0].Attribute = PS_ATTRIBUTE_IMAGE_NAME; ps_attr.TotalLength = sizeof(ps_attr);
ps_attr->Attributes[0].Size = sizeof(appnameW) - sizeof(WCHAR); ps_attr.Attributes[0].Attribute = PS_ATTRIBUTE_IMAGE_NAME;
ps_attr->Attributes[0].ValuePtr = (WCHAR *)appnameW; ps_attr.Attributes[0].Size = sizeof(appnameW) - sizeof(WCHAR);
ps_attr->Attributes[0].ReturnLength = NULL; 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] );
if (NtCurrentTeb64() && !NtCurrentTeb64()->TlsSlots[WOW64_TLS_FILESYSREDIR]) if (NtCurrentTeb64() && !NtCurrentTeb64()->TlsSlots[WOW64_TLS_FILESYSREDIR])
{ {
NtCurrentTeb64()->TlsSlots[WOW64_TLS_FILESYSREDIR] = TRUE; NtCurrentTeb64()->TlsSlots[WOW64_TLS_FILESYSREDIR] = TRUE;
status = NtCreateUserProcess( &process, &thread, PROCESS_ALL_ACCESS, THREAD_ALL_ACCESS, status = NtCreateUserProcess( &process, &thread, PROCESS_ALL_ACCESS, THREAD_ALL_ACCESS,
NULL, NULL, 0, THREAD_CREATE_FLAGS_CREATE_SUSPENDED, &params, NULL, NULL, 0, THREAD_CREATE_FLAGS_CREATE_SUSPENDED, &params,
&create_info, ps_attr ); &create_info, &ps_attr );
NtCurrentTeb64()->TlsSlots[WOW64_TLS_FILESYSREDIR] = FALSE; NtCurrentTeb64()->TlsSlots[WOW64_TLS_FILESYSREDIR] = FALSE;
} }
else else
status = NtCreateUserProcess( &process, &thread, PROCESS_ALL_ACCESS, THREAD_ALL_ACCESS, status = NtCreateUserProcess( &process, &thread, PROCESS_ALL_ACCESS, THREAD_ALL_ACCESS,
NULL, NULL, 0, THREAD_CREATE_FLAGS_CREATE_SUSPENDED, &params, NULL, NULL, 0, THREAD_CREATE_FLAGS_CREATE_SUSPENDED, &params,
&create_info, ps_attr ); &create_info, &ps_attr );
if (!status) if (!status)
{ {
NtResumeThread( thread, NULL ); NtResumeThread( thread, NULL );

View file

@ -872,6 +872,7 @@ NTSTATUS WINAPI wow64_NtSetInformationProcess( UINT *args )
case ProcessPagePriority: /* MEMORY_PRIORITY_INFORMATION */ case ProcessPagePriority: /* MEMORY_PRIORITY_INFORMATION */
case ProcessPowerThrottlingState: /* PROCESS_POWER_THROTTLING_STATE */ case ProcessPowerThrottlingState: /* PROCESS_POWER_THROTTLING_STATE */
case ProcessLeapSecondInformation: /* PROCESS_LEAP_SECOND_INFO */ case ProcessLeapSecondInformation: /* PROCESS_LEAP_SECOND_INFO */
case ProcessWineSetAdminToken: /* NULL */
return NtSetInformationProcess( handle, class, ptr, len ); return NtSetInformationProcess( handle, class, ptr, len );
case ProcessAccessToken: /* PROCESS_ACCESS_TOKEN */ case ProcessAccessToken: /* PROCESS_ACCESS_TOKEN */

View file

@ -1897,6 +1897,7 @@ typedef enum _PROCESSINFOCLASS {
#ifdef __WINESRC__ #ifdef __WINESRC__
ProcessWineMakeProcessSystem = 1000, ProcessWineMakeProcessSystem = 1000,
ProcessWineLdtCopy, ProcessWineLdtCopy,
ProcessWineSetAdminToken,
#endif #endif
} PROCESSINFOCLASS; } PROCESSINFOCLASS;

View file

@ -24,6 +24,7 @@
#define COBJMACROS #define COBJMACROS
#define OEMRESOURCE #define OEMRESOURCE
#include <windows.h> #include <windows.h>
#include <winternl.h>
#include <rpc.h> #include <rpc.h>
#include <shlobj.h> #include <shlobj.h>
#include <shellapi.h> #include <shellapi.h>
@ -1208,6 +1209,7 @@ void manage_desktop( WCHAR *arg )
HMODULE shell32; HMODULE shell32;
HANDLE thread; HANDLE thread;
DWORD id; DWORD id;
NTSTATUS status;
/* get the rest of the command line (if any) */ /* get the rest of the command line (if any) */
while (*p && !is_whitespace(*p)) p++; while (*p && !is_whitespace(*p)) p++;
@ -1260,6 +1262,10 @@ void manage_desktop( WCHAR *arg )
SetThreadDesktop( desktop ); 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 */ /* create the desktop window */
hwnd = CreateWindowExW( 0, DESKTOP_CLASS_ATOM, NULL, hwnd = CreateWindowExW( 0, DESKTOP_CLASS_ATOM, NULL,
WS_POPUP | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, 0, 0, 0, 0, 0, 0, 0, &guid ); WS_POPUP | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, 0, 0, 0, 0, 0, 0, 0, &guid );

View file

@ -1279,6 +1279,13 @@ int __cdecl main(int argc, char *argv[])
JOBOBJECT_ASSOCIATE_COMPLETION_PORT port_info; JOBOBJECT_ASSOCIATE_COMPLETION_PORT port_info;
HANDLE started_event, process_monitor_thread; HANDLE started_event, process_monitor_thread;
DWORD err; 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_object = CreateJobObjectW(NULL, NULL);
job_limit.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_BREAKAWAY_OK | JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK; job_limit.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_BREAKAWAY_OK | JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK;

View file

@ -3305,6 +3305,13 @@ enum caret_state
@END @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 */ /* Open a security token */
@REQ(open_token) @REQ(open_token)
obj_handle_t handle; /* handle to the thread or process */ obj_handle_t handle; /* handle to the thread or process */
@ -3762,7 +3769,8 @@ typedef union
unsigned int session_id; /* token session id */ unsigned int session_id; /* token session id */
int primary; /* is the token primary or impersonation? */ int primary; /* is the token primary or impersonation? */
int impersonation_level; /* level of 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 group_count; /* the number of groups the token is a member of */
int privilege_count; /* the number of privileges the token has */ int privilege_count; /* the number of privileges the token has */
@END @END

View file

@ -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 */ /* open a security token */
DECL_HANDLER(open_token) DECL_HANDLER(open_token)
{ {
@ -1538,7 +1554,14 @@ DECL_HANDLER(get_token_info)
reply->session_id = token->session_id; reply->session_id = token->session_id;
reply->primary = token->primary; reply->primary = token->primary;
reply->impersonation_level = token->impersonation_level; 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->group_count = list_count( &token->groups );
reply->privilege_count = list_count( &token->privileges ); reply->privilege_count = list_count( &token->privileges );
release_object( token ); release_object( token );