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