diff --git a/patches/advapi32-CreateRestrictedToken/0001-ntdll-Implement-NtFilterToken.patch b/patches/advapi32-CreateRestrictedToken/0001-ntdll-Implement-NtFilterToken.patch new file mode 100644 index 00000000..775a2b99 --- /dev/null +++ b/patches/advapi32-CreateRestrictedToken/0001-ntdll-Implement-NtFilterToken.patch @@ -0,0 +1,315 @@ +From 3f314cc8251f62f592013abe7b1c3b977de0699a Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Michael=20M=C3=BCller?= +Date: Fri, 4 Aug 2017 02:33:14 +0200 +Subject: ntdll: Implement NtFilterToken. + +--- + dlls/ntdll/nt.c | 59 ++++++++++++++++++++++++++++++++++++ + dlls/ntdll/ntdll.spec | 2 +- + include/winnt.h | 5 +++ + include/winternl.h | 1 + + server/process.c | 2 +- + server/protocol.def | 10 ++++++ + server/security.h | 4 ++- + server/token.c | 84 +++++++++++++++++++++++++++++++++++++++++++++++++-- + 8 files changed, 162 insertions(+), 5 deletions(-) + +diff --git a/dlls/ntdll/nt.c b/dlls/ntdll/nt.c +index 93554e929be..5822dec9b15 100644 +--- a/dlls/ntdll/nt.c ++++ b/dlls/ntdll/nt.c +@@ -136,6 +136,65 @@ NTSTATUS WINAPI NtDuplicateToken( + } + + /****************************************************************************** ++ * NtFilterToken [NTDLL.@] ++ * ZwFilterToken [NTDLL.@] ++ */ ++NTSTATUS WINAPI NtFilterToken( HANDLE token, ULONG flags, TOKEN_GROUPS *disable_sids, ++ TOKEN_PRIVILEGES *privileges, TOKEN_GROUPS *restrict_sids, ++ HANDLE *new_token ) ++{ ++ data_size_t privileges_len = 0; ++ data_size_t sids_len = 0; ++ SID *sids = NULL; ++ NTSTATUS status; ++ ++ TRACE( "(%p, 0x%08x, %p, %p, %p, %p)\n", token, flags, disable_sids, privileges, ++ restrict_sids, new_token ); ++ ++ if (flags) ++ FIXME( "flags %x unsupported\n", flags ); ++ ++ if (restrict_sids) ++ FIXME( "support for restricting sids not yet implemented\n" ); ++ ++ if (privileges) ++ privileges_len = privileges->PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES); ++ ++ if (disable_sids) ++ { ++ DWORD len, i; ++ BYTE *tmp; ++ ++ for (i = 0; i < disable_sids->GroupCount; i++) ++ sids_len += RtlLengthSid( disable_sids->Groups[i].Sid ); ++ ++ sids = RtlAllocateHeap( GetProcessHeap(), 0, sids_len ); ++ if (!sids) return STATUS_NO_MEMORY; ++ ++ for (i = 0, tmp = (BYTE *)sids; i < disable_sids->GroupCount; i++, tmp += len) ++ { ++ len = RtlLengthSid( disable_sids->Groups[i].Sid ); ++ memcpy( tmp, disable_sids->Groups[i].Sid, len ); ++ } ++ } ++ ++ SERVER_START_REQ( filter_token ) ++ { ++ req->handle = wine_server_obj_handle( token ); ++ req->flags = flags; ++ req->privileges_size = privileges_len; ++ wine_server_add_data( req, privileges->Privileges, privileges_len ); ++ wine_server_add_data( req, sids, sids_len ); ++ status = wine_server_call( req ); ++ if (!status) *new_token = wine_server_ptr_handle( reply->new_handle ); ++ } ++ SERVER_END_REQ; ++ ++ RtlFreeHeap( GetProcessHeap(), 0, sids ); ++ return status; ++} ++ ++/****************************************************************************** + * NtOpenProcessToken [NTDLL.@] + * ZwOpenProcessToken [NTDLL.@] + */ +diff --git a/dlls/ntdll/ntdll.spec b/dlls/ntdll/ntdll.spec +index 4f7ee496437..275fda57970 100644 +--- a/dlls/ntdll/ntdll.spec ++++ b/dlls/ntdll/ntdll.spec +@@ -179,7 +179,7 @@ + # @ stub NtEnumerateSystemEnvironmentValuesEx + @ stdcall NtEnumerateValueKey(long long long ptr long ptr) + @ stub NtExtendSection +-# @ stub NtFilterToken ++@ stdcall NtFilterToken(long long ptr ptr ptr ptr) + @ stdcall NtFindAtom(ptr long ptr) + @ stdcall NtFlushBuffersFile(long ptr) + @ stdcall NtFlushInstructionCache(long ptr long) +diff --git a/include/winnt.h b/include/winnt.h +index f91f81eb559..891c9b6d4bb 100644 +--- a/include/winnt.h ++++ b/include/winnt.h +@@ -3844,6 +3844,11 @@ typedef enum _TOKEN_INFORMATION_CLASS { + TOKEN_ADJUST_SESSIONID | \ + TOKEN_ADJUST_DEFAULT ) + ++#define DISABLE_MAX_PRIVILEGE 0x1 ++#define SANDBOX_INERT 0x2 ++#define LUA_TOKEN 0x4 ++#define WRITE_RESTRICTED 0x8 ++ + #ifndef _SECURITY_DEFINED + #define _SECURITY_DEFINED + +diff --git a/include/winternl.h b/include/winternl.h +index 140669b0105..899e8324d67 100644 +--- a/include/winternl.h ++++ b/include/winternl.h +@@ -2348,6 +2348,7 @@ NTSYSAPI NTSTATUS WINAPI NtDuplicateToken(HANDLE,ACCESS_MASK,POBJECT_ATTRIBUTES + NTSYSAPI NTSTATUS WINAPI NtEnumerateKey(HANDLE,ULONG,KEY_INFORMATION_CLASS,void *,DWORD,DWORD *); + NTSYSAPI NTSTATUS WINAPI NtEnumerateValueKey(HANDLE,ULONG,KEY_VALUE_INFORMATION_CLASS,PVOID,ULONG,PULONG); + NTSYSAPI NTSTATUS WINAPI NtExtendSection(HANDLE,PLARGE_INTEGER); ++NTSYSAPI NTSTATUS WINAPI NtFilterToken(HANDLE,ULONG,TOKEN_GROUPS*,TOKEN_PRIVILEGES*,TOKEN_GROUPS*,HANDLE*); + NTSYSAPI NTSTATUS WINAPI NtFindAtom(const WCHAR*,ULONG,RTL_ATOM*); + NTSYSAPI NTSTATUS WINAPI NtFlushBuffersFile(HANDLE,IO_STATUS_BLOCK*); + NTSYSAPI NTSTATUS WINAPI NtFlushInstructionCache(HANDLE,LPCVOID,SIZE_T); +diff --git a/server/process.c b/server/process.c +index cbe726afe81..f0f60edcd3f 100644 +--- a/server/process.c ++++ b/server/process.c +@@ -571,7 +571,7 @@ struct thread *create_process( int fd, struct thread *parent_thread, int inherit + : alloc_handle_table( process, 0 ); + /* Note: for security reasons, starting a new process does not attempt + * to use the current impersonation token for the new process */ +- process->token = token_duplicate( parent->token, TRUE, 0, NULL ); ++ process->token = token_duplicate( parent->token, TRUE, 0, NULL, NULL, 0, NULL, 0 ); + process->affinity = parent->affinity; + } + if (!process->handles || !process->token) goto error; +diff --git a/server/protocol.def b/server/protocol.def +index fc6e343af52..b3dce66eb9c 100644 +--- a/server/protocol.def ++++ b/server/protocol.def +@@ -3391,6 +3391,16 @@ enum caret_state + obj_handle_t new_handle; /* duplicated handle */ + @END + ++@REQ(filter_token) ++ obj_handle_t handle; /* handle to the token to duplicate */ ++ unsigned int flags; /* flags */ ++ data_size_t privileges_size; /* size of privileges */ ++ VARARG(privileges,LUID_AND_ATTRIBUTES,privileges_size); /* privileges to remove from new token */ ++ VARARG(disable_sids,SID); /* array of groups to remove from new token */ ++@REPLY ++ obj_handle_t new_handle; /* filtered handle */ ++@END ++ + @REQ(access_check) + obj_handle_t handle; /* handle to the token */ + unsigned int desired_access; /* desired access to the object */ +diff --git a/server/security.h b/server/security.h +index 606dbb2ab2c..6c337143c3d 100644 +--- a/server/security.h ++++ b/server/security.h +@@ -56,7 +56,9 @@ extern const PSID security_high_label_sid; + extern struct token *token_create_admin(void); + extern int token_assign_label( struct token *token, PSID label ); + extern struct token *token_duplicate( struct token *src_token, unsigned primary, +- int impersonation_level, const struct security_descriptor *sd ); ++ int impersonation_level, const struct security_descriptor *sd, ++ const LUID_AND_ATTRIBUTES *filter_privileges, unsigned int priv_count, ++ const SID *filter_groups, unsigned int group_count ); + extern int token_check_privileges( struct token *token, int all_required, + const LUID_AND_ATTRIBUTES *reqprivs, + unsigned int count, LUID_AND_ATTRIBUTES *usedprivs); +diff --git a/server/token.c b/server/token.c +index 74db66e1e24..acd7a4dedb5 100644 +--- a/server/token.c ++++ b/server/token.c +@@ -299,6 +299,19 @@ static int acl_is_valid( const ACL *acl, data_size_t size ) + return TRUE; + } + ++static unsigned int get_sid_count( const SID *sid, data_size_t size ) ++{ ++ unsigned int count; ++ ++ for (count = 0; size >= sizeof(SID) && security_sid_len( sid ) <= size; count++) ++ { ++ size -= security_sid_len( sid ); ++ sid = (const SID *)((char *)sid + security_sid_len( sid )); ++ } ++ ++ return count; ++} ++ + /* checks whether all members of a security descriptor fit inside the size + * of memory specified */ + int sd_is_valid( const struct security_descriptor *sd, data_size_t size ) +@@ -639,8 +652,36 @@ static struct token *create_token( unsigned primary, const SID *user, + return token; + } + ++static int filter_group( struct group *group, const SID *filter, unsigned int count ) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < count; i++) ++ { ++ if (security_equal_sid( &group->sid, filter )) return 1; ++ filter = (const SID *)((char *)filter + security_sid_len( filter )); ++ } ++ ++ return 0; ++} ++ ++static int filter_privilege( struct privilege *privilege, const LUID_AND_ATTRIBUTES *filter, unsigned int count ) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < count; i++) ++ { ++ if (!memcmp( &privilege->luid, &filter[i].Luid, sizeof(LUID) )) ++ return 1; ++ } ++ ++ return 0; ++} ++ + struct token *token_duplicate( struct token *src_token, unsigned primary, +- int impersonation_level, const struct security_descriptor *sd ) ++ int impersonation_level, const struct security_descriptor *sd, ++ const LUID_AND_ATTRIBUTES *filter_privileges, unsigned int priv_count, ++ const SID *filter_groups, unsigned int group_count) + { + const luid_t *modified_id = + primary || (impersonation_level == src_token->impersonation_level) ? +@@ -676,6 +717,12 @@ struct token *token_duplicate( struct token *src_token, unsigned primary, + return NULL; + } + memcpy( newgroup, group, size ); ++ if (filter_group( group, filter_groups, group_count )) ++ { ++ newgroup->enabled = 0; ++ newgroup->def = 0; ++ newgroup->deny_only = 1; ++ } + list_add_tail( &token->groups, &newgroup->entry ); + if (src_token->primary_group == &group->sid) + token->primary_group = &newgroup->sid; +@@ -684,11 +731,14 @@ struct token *token_duplicate( struct token *src_token, unsigned primary, + + /* copy privileges */ + LIST_FOR_EACH_ENTRY( privilege, &src_token->privileges, struct privilege, entry ) ++ { ++ if (filter_privilege( privilege, filter_privileges, priv_count )) continue; + if (!privilege_add( token, &privilege->luid, privilege->enabled )) + { + release_object( token ); + return NULL; + } ++ } + + if (sd) default_set_sd( &token->obj, sd, OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | + DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION ); +@@ -1322,7 +1372,7 @@ DECL_HANDLER(duplicate_token) + TOKEN_DUPLICATE, + &token_ops ))) + { +- struct token *token = token_duplicate( src_token, req->primary, req->impersonation_level, sd ); ++ struct token *token = token_duplicate( src_token, req->primary, req->impersonation_level, sd, NULL, 0, NULL, 0 ); + if (token) + { + reply->new_handle = alloc_handle_no_access_check( current->process, token, req->access, objattr->attributes ); +@@ -1332,6 +1382,36 @@ DECL_HANDLER(duplicate_token) + } + } + ++/* creates a restricted version of a token */ ++DECL_HANDLER(filter_token) ++{ ++ struct token *src_token; ++ ++ if ((src_token = (struct token *)get_handle_obj( current->process, req->handle, ++ TOKEN_DUPLICATE, ++ &token_ops ))) ++ { ++ const LUID_AND_ATTRIBUTES *filter_privileges = get_req_data(); ++ unsigned int priv_count, group_count; ++ const SID *filter_groups; ++ struct token *token; ++ ++ priv_count = min( req->privileges_size, get_req_data_size() ) / sizeof(LUID_AND_ATTRIBUTES); ++ filter_groups = (const SID *)((char *)filter_privileges + priv_count * sizeof(LUID_AND_ATTRIBUTES)); ++ group_count = get_sid_count( filter_groups, get_req_data_size() - priv_count * sizeof(LUID_AND_ATTRIBUTES) ); ++ ++ token = token_duplicate( src_token, src_token->primary, src_token->impersonation_level, NULL, ++ filter_privileges, priv_count, filter_groups, group_count ); ++ if (token) ++ { ++ unsigned int access = get_handle_access( current->process, req->handle ); ++ reply->new_handle = alloc_handle_no_access_check( current->process, token, access, 0 ); ++ release_object( token ); ++ } ++ release_object( src_token ); ++ } ++} ++ + /* checks the specified privileges are held by the token */ + DECL_HANDLER(check_token_privileges) + { +-- +2.13.1 + diff --git a/patches/advapi32-CreateRestrictedToken/0002-advapi32-Implement-CreateRestrictedToken.patch b/patches/advapi32-CreateRestrictedToken/0002-advapi32-Implement-CreateRestrictedToken.patch new file mode 100644 index 00000000..681fcc4f --- /dev/null +++ b/patches/advapi32-CreateRestrictedToken/0002-advapi32-Implement-CreateRestrictedToken.patch @@ -0,0 +1,271 @@ +From 2a1064c5f90beac2bd709ab5d1c454c90a16189b Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Michael=20M=C3=BCller?= +Date: Fri, 4 Aug 2017 02:51:57 +0200 +Subject: advapi32: Implement CreateRestrictedToken. + +--- + dlls/advapi32/security.c | 88 +++++++++++++++++++++++++++++++++++------- + dlls/advapi32/tests/security.c | 88 +++++++++++++++++++++++++++++++++++++++--- + 2 files changed, 157 insertions(+), 19 deletions(-) + +diff --git a/dlls/advapi32/security.c b/dlls/advapi32/security.c +index 82bb6689d43..c531e45c9a0 100644 +--- a/dlls/advapi32/security.c ++++ b/dlls/advapi32/security.c +@@ -840,6 +840,60 @@ BOOL WINAPI SetThreadToken(PHANDLE thread, HANDLE token) + ThreadImpersonationToken, &token, sizeof token )); + } + ++static BOOL allocate_groups(TOKEN_GROUPS **groups_ret, SID_AND_ATTRIBUTES *sids, DWORD count) ++{ ++ TOKEN_GROUPS *groups; ++ DWORD i; ++ ++ if (!count) ++ { ++ *groups_ret = NULL; ++ return TRUE; ++ } ++ ++ groups = (TOKEN_GROUPS *)heap_alloc(FIELD_OFFSET(TOKEN_GROUPS, Groups) + ++ count * sizeof(SID_AND_ATTRIBUTES)); ++ if (!groups) ++ { ++ SetLastError(ERROR_OUTOFMEMORY); ++ return FALSE; ++ } ++ ++ groups->GroupCount = count; ++ for (i = 0; i < count; i++) ++ groups->Groups[i] = sids[i]; ++ ++ *groups_ret = groups; ++ return TRUE; ++} ++ ++static BOOL allocate_privileges(TOKEN_PRIVILEGES **privileges_ret, LUID_AND_ATTRIBUTES *privs, DWORD count) ++{ ++ TOKEN_PRIVILEGES *privileges; ++ DWORD i; ++ ++ if (!count) ++ { ++ *privileges_ret = NULL; ++ return TRUE; ++ } ++ ++ privileges = (TOKEN_PRIVILEGES *)heap_alloc(FIELD_OFFSET(TOKEN_PRIVILEGES, Privileges) + ++ count * sizeof(LUID_AND_ATTRIBUTES)); ++ if (!privileges) ++ { ++ SetLastError(ERROR_OUTOFMEMORY); ++ return FALSE; ++ } ++ ++ privileges->PrivilegeCount = count; ++ for (i = 0; i < count; i++) ++ privileges->Privileges[i] = privs[i]; ++ ++ *privileges_ret = privileges; ++ return TRUE; ++} ++ + /************************************************************************* + * CreateRestrictedToken [ADVAPI32.@] + * +@@ -871,25 +925,33 @@ BOOL WINAPI CreateRestrictedToken( + PSID_AND_ATTRIBUTES restrictSids, + PHANDLE newToken) + { +- TOKEN_TYPE type; +- SECURITY_IMPERSONATION_LEVEL level = SecurityAnonymous; +- DWORD size; ++ TOKEN_PRIVILEGES *delete_privs = NULL; ++ TOKEN_GROUPS *disable_groups = NULL; ++ TOKEN_GROUPS *restrict_sids = NULL; ++ BOOL ret = FALSE; + +- FIXME("(%p, 0x%x, %u, %p, %u, %p, %u, %p, %p): stub\n", ++ TRACE("(%p, 0x%x, %u, %p, %u, %p, %u, %p, %p)\n", + baseToken, flags, nDisableSids, disableSids, + nDeletePrivs, deletePrivs, + nRestrictSids, restrictSids, + newToken); + +- size = sizeof(type); +- if (!GetTokenInformation( baseToken, TokenType, &type, size, &size )) return FALSE; +- if (type == TokenImpersonation) +- { +- size = sizeof(level); +- if (!GetTokenInformation( baseToken, TokenImpersonationLevel, &level, size, &size )) +- return FALSE; +- } +- return DuplicateTokenEx( baseToken, MAXIMUM_ALLOWED, NULL, level, type, newToken ); ++ if (!allocate_groups(&disable_groups, disableSids, nDisableSids)) ++ goto done; ++ ++ if (!allocate_privileges(&delete_privs, deletePrivs, nDeletePrivs)) ++ goto done; ++ ++ if (!allocate_groups(&restrict_sids, restrictSids, nRestrictSids)) ++ goto done; ++ ++ ret = set_ntstatus(NtFilterToken(baseToken, flags, disable_groups, delete_privs, restrict_sids, newToken)); ++ ++done: ++ heap_free(disable_groups); ++ heap_free(delete_privs); ++ heap_free(restrict_sids); ++ return ret; + } + + /* ############################## +diff --git a/dlls/advapi32/tests/security.c b/dlls/advapi32/tests/security.c +index a1ecc409b73..0fd41fe82fa 100644 +--- a/dlls/advapi32/tests/security.c ++++ b/dlls/advapi32/tests/security.c +@@ -5292,10 +5292,13 @@ static void test_GetUserNameW(void) + + static void test_CreateRestrictedToken(void) + { ++ TOKEN_PRIMARY_GROUP *primary_group, *primary_group2; + HANDLE process_token, token, r_token; + PTOKEN_GROUPS token_groups, groups2; + SID_AND_ATTRIBUTES sattr; + SECURITY_IMPERSONATION_LEVEL level; ++ TOKEN_PRIVILEGES *privs; ++ PRIVILEGE_SET privset; + TOKEN_TYPE type; + BOOL is_member; + DWORD size; +@@ -5311,7 +5314,7 @@ static void test_CreateRestrictedToken(void) + ret = OpenProcessToken(GetCurrentProcess(), TOKEN_DUPLICATE|TOKEN_QUERY, &process_token); + ok(ret, "got error %d\n", GetLastError()); + +- ret = DuplicateTokenEx(process_token, TOKEN_DUPLICATE|TOKEN_ADJUST_GROUPS|TOKEN_QUERY, ++ ret = DuplicateTokenEx(process_token, TOKEN_DUPLICATE|TOKEN_ADJUST_GROUPS|TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY, + NULL, SecurityImpersonation, TokenImpersonation, &token); + ok(ret, "got error %d\n", GetLastError()); + +@@ -5342,11 +5345,21 @@ static void test_CreateRestrictedToken(void) + ok(ret, "got error %d\n", GetLastError()); + ok(is_member, "not a member\n"); + +- /* disable a SID in new token */ ++ privset.PrivilegeCount = 1; ++ privset.Control = PRIVILEGE_SET_ALL_NECESSARY; ++ ret = LookupPrivilegeValueA(NULL, "SeChangeNotifyPrivilege", &privset.Privilege[0].Luid); ++ ok(ret, "got error %d\n", GetLastError()); ++ ++ is_member = FALSE; ++ ret = PrivilegeCheck(token, &privset, &is_member); ++ ok(ret, "got error %d\n", GetLastError()); ++ ok(is_member, "Expected SeChangeNotifyPrivilege to be enabled\n"); ++ ++ /* disable a SID and a privilege in new token */ + sattr.Sid = token_groups->Groups[i].Sid; + sattr.Attributes = 0; + r_token = NULL; +- ret = pCreateRestrictedToken(token, 0, 1, &sattr, 0, NULL, 0, NULL, &r_token); ++ ret = pCreateRestrictedToken(token, 0, 1, &sattr, 1, &privset.Privilege[0], 0, NULL, &r_token); + ok(ret, "got error %d\n", GetLastError()); + + if (ret) +@@ -5355,7 +5368,7 @@ static void test_CreateRestrictedToken(void) + is_member = TRUE; + ret = pCheckTokenMembership(r_token, token_groups->Groups[i].Sid, &is_member); + ok(ret, "got error %d\n", GetLastError()); +- todo_wine ok(!is_member, "not a member\n"); ++ ok(!is_member, "not a member\n"); + + ret = GetTokenInformation(r_token, TokenGroups, NULL, 0, &size); + ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER, "got %d with error %d\n", +@@ -5370,9 +5383,9 @@ static void test_CreateRestrictedToken(void) + break; + } + +- todo_wine ok(groups2->Groups[j].Attributes & SE_GROUP_USE_FOR_DENY_ONLY, ++ ok(groups2->Groups[j].Attributes & SE_GROUP_USE_FOR_DENY_ONLY, + "got wrong attributes\n"); +- todo_wine ok((groups2->Groups[j].Attributes & SE_GROUP_ENABLED) == 0, ++ ok((groups2->Groups[j].Attributes & SE_GROUP_ENABLED) == 0, + "got wrong attributes\n"); + + HeapFree(GetProcessHeap(), 0, groups2); +@@ -5386,10 +5399,73 @@ static void test_CreateRestrictedToken(void) + ret = GetTokenInformation(r_token, TokenImpersonationLevel, &level, size, &size); + ok(ret, "got error %d\n", GetLastError()); + ok(level == SecurityImpersonation, "got level %u\n", type); ++ ++ is_member = TRUE; ++ ret = PrivilegeCheck(r_token, &privset, &is_member); ++ ok(ret, "got error %d\n", GetLastError()); ++ ok(!is_member, "Expected SeChangeNotifyPrivilege not to be enabled\n"); ++ ++ ret = GetTokenInformation(r_token, TokenPrivileges, NULL, 0, &size); ++ ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER, "got %d with error %d\n", ++ ret, GetLastError()); ++ privs = HeapAlloc(GetProcessHeap(), 0, size); ++ ret = GetTokenInformation(r_token, TokenPrivileges, privs, size, &size); ++ ok(ret, "got error %d\n", GetLastError()); ++ ++ is_member = FALSE; ++ for (j = 0; j < privs->PrivilegeCount; j++) ++ { ++ if (RtlEqualLuid(&privs->Privileges[j].Luid, &privset.Privilege[0].Luid)) ++ { ++ is_member = TRUE; ++ break; ++ } ++ } ++ ++ ok(!is_member, "Expected not to find privilege\n"); ++ HeapFree(GetProcessHeap(), 0, privs); + } + + HeapFree(GetProcessHeap(), 0, token_groups); + CloseHandle(r_token); ++ ++ ret = GetTokenInformation(token, TokenPrimaryGroup, NULL, 0, &size); ++ ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER, "got %d with error %d\n", ++ ret, GetLastError()); ++ primary_group = HeapAlloc(GetProcessHeap(), 0, size); ++ ret = GetTokenInformation(token, TokenPrimaryGroup, primary_group, size, &size); ++ ok(ret, "got error %d\n", GetLastError()); ++ ++ /* disable primary group */ ++ sattr.Sid = primary_group->PrimaryGroup; ++ sattr.Attributes = 0; ++ r_token = NULL; ++ ret = pCreateRestrictedToken(token, 0, 1, &sattr, 0, NULL, 0, NULL, &r_token); ++ ok(ret, "got error %d\n", GetLastError()); ++ ++ if (ret) ++ { ++ is_member = TRUE; ++ ret = pCheckTokenMembership(r_token, primary_group->PrimaryGroup, &is_member); ++ ok(ret, "got error %d\n", GetLastError()); ++ ok(!is_member, "not a member\n"); ++ ++ ret = GetTokenInformation(r_token, TokenPrimaryGroup, NULL, 0, &size); ++ ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER, "got %d with error %d\n", ++ ret, GetLastError()); ++ primary_group2 = HeapAlloc(GetProcessHeap(), 0, size); ++ ret = GetTokenInformation(r_token, TokenPrimaryGroup, primary_group2, size, &size); ++ ok(ret, "got error %d\n", GetLastError()); ++ ++ ok(EqualSid(primary_group2->PrimaryGroup, primary_group->PrimaryGroup), ++ "Expected same primary group\n"); ++ ++ HeapFree(GetProcessHeap(), 0, primary_group2); ++ } ++ ++ HeapFree(GetProcessHeap(), 0, primary_group); ++ CloseHandle(r_token); ++ + CloseHandle(token); + CloseHandle(process_token); + } +-- +2.13.1 + diff --git a/patches/advapi32-CreateRestrictedToken/0003-server-Correctly-validate-SID-length-in-sd_is_valid.patch b/patches/advapi32-CreateRestrictedToken/0003-server-Correctly-validate-SID-length-in-sd_is_valid.patch new file mode 100644 index 00000000..b2cad7ac --- /dev/null +++ b/patches/advapi32-CreateRestrictedToken/0003-server-Correctly-validate-SID-length-in-sd_is_valid.patch @@ -0,0 +1,36 @@ +From 22a49dfa50cda9b1f5a5c64eabed2d17b0033896 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Michael=20M=C3=BCller?= +Date: Fri, 4 Aug 2017 02:52:50 +0200 +Subject: server: Correctly validate SID length in sd_is_valid. + +--- + server/token.c | 6 ++---- + 1 file changed, 2 insertions(+), 4 deletions(-) + +diff --git a/server/token.c b/server/token.c +index acd7a4dedb5..7ab0f634c05 100644 +--- a/server/token.c ++++ b/server/token.c +@@ -332,8 +332,7 @@ int sd_is_valid( const struct security_descriptor *sd, data_size_t size ) + owner = sd_get_owner( sd ); + if (owner) + { +- size_t needed_size = security_sid_len( owner ); +- if ((sd->owner_len < sizeof(SID)) || (needed_size > sd->owner_len)) ++ if ((sd->owner_len < sizeof(SID)) || (security_sid_len( owner ) > sd->owner_len)) + return FALSE; + } + offset += sd->owner_len; +@@ -344,8 +343,7 @@ int sd_is_valid( const struct security_descriptor *sd, data_size_t size ) + group = sd_get_group( sd ); + if (group) + { +- size_t needed_size = security_sid_len( group ); +- if ((sd->group_len < sizeof(SID)) || (needed_size > sd->group_len)) ++ if ((sd->group_len < sizeof(SID)) || (security_sid_len( group ) > sd->group_len)) + return FALSE; + } + offset += sd->group_len; +-- +2.13.1 + diff --git a/patches/advapi32-CreateRestrictedToken/definition b/patches/advapi32-CreateRestrictedToken/definition new file mode 100644 index 00000000..d1e5dba1 --- /dev/null +++ b/patches/advapi32-CreateRestrictedToken/definition @@ -0,0 +1 @@ +Fixes: Implement advapi32.CreateRestrictedToken diff --git a/patches/patchinstall.sh b/patches/patchinstall.sh index c61d0735..e068a618 100755 --- a/patches/patchinstall.sh +++ b/patches/patchinstall.sh @@ -88,6 +88,7 @@ patch_enable_all () enable_Staging="$1" enable_advapi_LsaLookupPrivilegeName="$1" enable_advapi32_BuildSecurityDescriptor="$1" + enable_advapi32_CreateRestrictedToken="$1" enable_advapi32_GetExplicitEntriesFromAclW="$1" enable_advapi32_LsaLookupSids="$1" enable_advapi32_Performance_Counters="$1" @@ -532,6 +533,9 @@ patch_enable () advapi32-BuildSecurityDescriptor) enable_advapi32_BuildSecurityDescriptor="$2" ;; + advapi32-CreateRestrictedToken) + enable_advapi32_CreateRestrictedToken="$2" + ;; advapi32-GetExplicitEntriesFromAclW) enable_advapi32_GetExplicitEntriesFromAclW="$2" ;; @@ -3018,6 +3022,23 @@ if test "$enable_advapi32_BuildSecurityDescriptor" -eq 1; then ) >> "$patchlist" fi +# Patchset advapi32-CreateRestrictedToken +# | +# | Modified files: +# | * dlls/advapi32/security.c, dlls/advapi32/tests/security.c, dlls/ntdll/nt.c, dlls/ntdll/ntdll.spec, include/winnt.h, +# | include/winternl.h, server/process.c, server/protocol.def, server/security.h, server/token.c +# | +if test "$enable_advapi32_CreateRestrictedToken" -eq 1; then + patch_apply advapi32-CreateRestrictedToken/0001-ntdll-Implement-NtFilterToken.patch + patch_apply advapi32-CreateRestrictedToken/0002-advapi32-Implement-CreateRestrictedToken.patch + patch_apply advapi32-CreateRestrictedToken/0003-server-Correctly-validate-SID-length-in-sd_is_valid.patch + ( + printf '%s\n' '+ { "Michael Müller", "ntdll: Implement NtFilterToken.", 1 },'; + printf '%s\n' '+ { "Michael Müller", "advapi32: Implement CreateRestrictedToken.", 1 },'; + printf '%s\n' '+ { "Michael Müller", "server: Correctly validate SID length in sd_is_valid.", 1 },'; + ) >> "$patchlist" +fi + # Patchset server-CreateProcess_ACLs # | # | This patchset fixes the following Wine bugs: