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 "winerror.h"
#include "winternl.h"
#include "winuser.h"
#include "aclapi.h"
#include "winnt.h"
#include "sddl.h"
@ -8571,6 +8572,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];
@ -8717,6 +8754,7 @@ START_TEST(security)
test_AccessCheck();
test_token_attr();
test_GetTokenInformation();
test_admin_elevation();
test_LookupAccountSid();
test_LookupAccountName();
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,
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);

View file

@ -1837,6 +1837,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;

View file

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

View file

@ -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( &params.WindowTitle, appnameW + 4 );
RtlInitUnicodeString( &params.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, &params,
&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, &params,
&create_info, ps_attr );
&create_info, &ps_attr );
if (!status)
{
NtResumeThread( thread, NULL );

View file

@ -872,6 +872,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 */

View file

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

View file

@ -24,6 +24,7 @@
#define COBJMACROS
#define OEMRESOURCE
#include <windows.h>
#include <winternl.h>
#include <rpc.h>
#include <shlobj.h>
#include <shellapi.h>
@ -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 );

View file

@ -1279,6 +1279,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;

View file

@ -3305,6 +3305,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 */
@ -3762,7 +3769,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

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 */
DECL_HANDLER(open_token)
{
@ -1538,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 );