Signed-off-by: Zebediah Figura z.figura12@gmail.com --- dlls/advapi32/tests/security.c | 195 ++++++++++++++++++++++++++++++++- 1 file changed, 194 insertions(+), 1 deletion(-)
diff --git a/dlls/advapi32/tests/security.c b/dlls/advapi32/tests/security.c index 825f8451904..ae5531db800 100644 --- a/dlls/advapi32/tests/security.c +++ b/dlls/advapi32/tests/security.c @@ -7529,6 +7529,196 @@ static void test_EqualDomainSid(void) FreeSid(domainsid); }
+static DWORD WINAPI duplicate_handle_access_thread(void *arg) +{ + HANDLE event = arg, event2; + BOOL ret; + + event2 = OpenEventA(SYNCHRONIZE, FALSE, "test_dup"); + ok(!!event2, "got error %u\n", GetLastError()); + CloseHandle(event2); + + event2 = OpenEventA(EVENT_MODIFY_STATE, FALSE, "test_dup"); + ok(!!event2, "got error %u\n", GetLastError()); + CloseHandle(event2); + + ret = DuplicateHandle(GetCurrentProcess(), event, GetCurrentProcess(), + &event2, EVENT_MODIFY_STATE, FALSE, 0); + ok(ret, "got error %u\n", GetLastError()); + CloseHandle(event2); + + return 0; +} + +static void test_duplicate_handle_access(void) +{ + char acl_buffer[200], everyone_sid_buffer[100], local_sid_buffer[100], cmdline[300]; + HANDLE token, restricted, impersonation, all_event, sync_event, event2, thread; + SECURITY_ATTRIBUTES sa = {.nLength = sizeof(sa)}; + SID *everyone_sid = (SID *)everyone_sid_buffer; + SID *local_sid = (SID *)local_sid_buffer; + ACL *acl = (ACL *)acl_buffer; + SID_AND_ATTRIBUTES sid_attr; + SECURITY_DESCRIPTOR sd; + PROCESS_INFORMATION pi; + STARTUPINFOA si = {0}; + DWORD size; + BOOL ret; + + /* DuplicateHandle() validates access against the calling thread's token and + * the target process's token. It does *not* validate access against the + * calling process's token, even if the calling thread is not impersonating. + */ + + ret = OpenProcessToken(GetCurrentProcess(), TOKEN_DUPLICATE | TOKEN_QUERY | TOKEN_ASSIGN_PRIMARY, &token); + ok(ret, "got error %u\n", GetLastError()); + + size = sizeof(everyone_sid_buffer); + ret = CreateWellKnownSid(WinWorldSid, NULL, everyone_sid, &size); + ok(ret, "got error %u\n", GetLastError()); + size = sizeof(local_sid_buffer); + ret = CreateWellKnownSid(WinLocalSid, NULL, local_sid, &size); + ok(ret, "got error %u\n", GetLastError()); + + InitializeAcl(acl, sizeof(acl_buffer), ACL_REVISION); + ret = AddAccessAllowedAce(acl, ACL_REVISION, SYNCHRONIZE, everyone_sid); + ok(ret, "got error %u\n", GetLastError()); + InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION); + ret = AddAccessAllowedAce(acl, ACL_REVISION, EVENT_MODIFY_STATE, local_sid); + ok(ret, "got error %u\n", GetLastError()); + InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION); + ret = SetSecurityDescriptorDacl(&sd, TRUE, acl, FALSE); + ok(ret, "got error %u\n", GetLastError()); + sa.lpSecurityDescriptor = &sd; + + sid_attr.Sid = local_sid; + sid_attr.Attributes = 0; + ret = CreateRestrictedToken(token, 0, 1, &sid_attr, 0, NULL, 0, NULL, &restricted); + ok(ret, "got error %u\n", GetLastError()); + ret = DuplicateTokenEx(restricted, TOKEN_IMPERSONATE, NULL, + SecurityImpersonation, TokenImpersonation, &impersonation); + ok(ret, "got error %u\n", GetLastError()); + + all_event = CreateEventA(&sa, TRUE, TRUE, "test_dup"); + ok(!!all_event, "got error %u\n", GetLastError()); + sync_event = OpenEventA(SYNCHRONIZE, FALSE, "test_dup"); + ok(!!sync_event, "got error %u\n", GetLastError()); + + event2 = OpenEventA(SYNCHRONIZE, FALSE, "test_dup"); + ok(!!event2, "got error %u\n", GetLastError()); + CloseHandle(event2); + + event2 = OpenEventA(EVENT_MODIFY_STATE, FALSE, "test_dup"); + ok(!!event2, "got error %u\n", GetLastError()); + CloseHandle(event2); + + ret = DuplicateHandle(GetCurrentProcess(), all_event, GetCurrentProcess(), &event2, EVENT_MODIFY_STATE, FALSE, 0); + ok(ret, "got error %u\n", GetLastError()); + CloseHandle(event2); + + ret = DuplicateHandle(GetCurrentProcess(), sync_event, GetCurrentProcess(), &event2, EVENT_MODIFY_STATE, FALSE, 0); + ok(ret, "got error %u\n", GetLastError()); + CloseHandle(event2); + + ret = SetThreadToken(NULL, impersonation); + ok(ret, "got error %u\n", GetLastError()); + + thread = CreateThread(NULL, 0, duplicate_handle_access_thread, sync_event, 0, NULL); + ret = WaitForSingleObject(thread, 1000); + ok(!ret, "wait failed\n"); + + event2 = OpenEventA(SYNCHRONIZE, FALSE, "test_dup"); + ok(!!event2, "got error %u\n", GetLastError()); + CloseHandle(event2); + + SetLastError(0xdeadbeef); + event2 = OpenEventA(EVENT_MODIFY_STATE, FALSE, "test_dup"); + todo_wine ok(!event2, "expected failure\n"); + todo_wine ok(GetLastError() == ERROR_ACCESS_DENIED, "got error %u\n", GetLastError()); + + ret = DuplicateHandle(GetCurrentProcess(), all_event, GetCurrentProcess(), &event2, EVENT_MODIFY_STATE, FALSE, 0); + ok(ret, "got error %u\n", GetLastError()); + CloseHandle(event2); + + SetLastError(0xdeadbeef); + ret = DuplicateHandle(GetCurrentProcess(), sync_event, GetCurrentProcess(), &event2, EVENT_MODIFY_STATE, FALSE, 0); + todo_wine ok(!ret, "expected failure\n"); + todo_wine ok(GetLastError() == ERROR_ACCESS_DENIED, "got error %u\n", GetLastError()); + + ret = RevertToSelf(); + ok(ret, "got error %u\n", GetLastError()); + + sprintf(cmdline, "%s security duplicate %Iu %u %Iu", myARGV[0], + (ULONG_PTR)sync_event, GetCurrentProcessId(), (ULONG_PTR)impersonation ); + ret = CreateProcessAsUserA(restricted, NULL, cmdline, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi); + ok(ret, "got error %u\n", GetLastError()); + + ret = DuplicateHandle(GetCurrentProcess(), all_event, pi.hProcess, &event2, EVENT_MODIFY_STATE, FALSE, 0); + ok(ret, "got error %u\n", GetLastError()); + + SetLastError(0xdeadbeef); + ret = DuplicateHandle(GetCurrentProcess(), sync_event, pi.hProcess, &event2, EVENT_MODIFY_STATE, FALSE, 0); + todo_wine ok(!ret, "expected failure\n"); + todo_wine ok(GetLastError() == ERROR_ACCESS_DENIED, "got error %u\n", GetLastError()); + + ret = WaitForSingleObject(pi.hProcess, 1000); + ok(!ret, "wait failed\n"); + + CloseHandle(impersonation); + CloseHandle(restricted); + CloseHandle(token); + CloseHandle(sync_event); + CloseHandle(all_event); +} + +static void test_duplicate_handle_access_child(void) +{ + HANDLE event, event2, process, token; + BOOL ret; + + event = (HANDLE)(ULONG_PTR)_atoi64(myARGV[3]); + process = OpenProcess(PROCESS_DUP_HANDLE, FALSE, atoi(myARGV[4])); + ok(!!process, "failed to open process, error %u\n", GetLastError()); + + event2 = OpenEventA(SYNCHRONIZE, FALSE, "test_dup"); + ok(!!event2, "got error %u\n", GetLastError()); + CloseHandle(event2); + + SetLastError(0xdeadbeef); + event2 = OpenEventA(EVENT_MODIFY_STATE, FALSE, "test_dup"); + todo_wine ok(!event2, "expected failure\n"); + todo_wine ok(GetLastError() == ERROR_ACCESS_DENIED, "got error %u\n", GetLastError()); + + ret = DuplicateHandle(process, event, process, &event2, EVENT_MODIFY_STATE, FALSE, 0); + ok(ret, "got error %u\n", GetLastError()); + + SetLastError(0xdeadbeef); + ret = DuplicateHandle(process, event, GetCurrentProcess(), &event2, EVENT_MODIFY_STATE, FALSE, 0); + todo_wine ok(!ret, "expected failure\n"); + todo_wine ok(GetLastError() == ERROR_ACCESS_DENIED, "got error %u\n", GetLastError()); + + ret = DuplicateHandle(process, (HANDLE)(ULONG_PTR)_atoi64(myARGV[5]), + GetCurrentProcess(), &token, 0, FALSE, DUPLICATE_SAME_ACCESS); + ok(ret, "failed to retrieve token, error %u\n", GetLastError()); + ret = SetThreadToken(NULL, token); + ok(ret, "failed to set thread token, error %u\n", GetLastError()); + + SetLastError(0xdeadbeef); + ret = DuplicateHandle(process, event, process, &event2, EVENT_MODIFY_STATE, FALSE, 0); + todo_wine ok(!ret, "expected failure\n"); + todo_wine ok(GetLastError() == ERROR_ACCESS_DENIED, "got error %u\n", GetLastError()); + + SetLastError(0xdeadbeef); + ret = DuplicateHandle(process, event, GetCurrentProcess(), &event2, EVENT_MODIFY_STATE, FALSE, 0); + todo_wine ok(!ret, "expected failure\n"); + todo_wine ok(GetLastError() == ERROR_ACCESS_DENIED, "got error %u\n", GetLastError()); + + ret = RevertToSelf(); + ok(ret, "failed to revert, error %u\n", GetLastError()); + CloseHandle(token); + CloseHandle(process); +} + START_TEST(security) { init(); @@ -7538,8 +7728,10 @@ START_TEST(security) { if (!strcmp(myARGV[2], "test_token_sd")) test_child_token_sd(); - else + else if (!strcmp(myARGV[2], "test")) test_process_security_child(); + else if (!strcmp(myARGV[2], "duplicate")) + test_duplicate_handle_access_child(); return; } test_kernel_objects_security(); @@ -7585,6 +7777,7 @@ START_TEST(security) test_token_label(); test_GetExplicitEntriesFromAclW(); test_BuildSecurityDescriptorW(); + test_duplicate_handle_access();
/* Must be the last test, modifies process token */ test_token_security_descriptor();
Signed-off-by: Zebediah Figura z.figura12@gmail.com --- dlls/advapi32/tests/security.c | 150 +++++++++++++++++++++------------ 1 file changed, 98 insertions(+), 52 deletions(-)
diff --git a/dlls/advapi32/tests/security.c b/dlls/advapi32/tests/security.c index ae5531db800..957ebc3036a 100644 --- a/dlls/advapi32/tests/security.c +++ b/dlls/advapi32/tests/security.c @@ -104,8 +104,6 @@ static DWORD (WINAPI *pSetSecurityInfo)(HANDLE, SE_OBJECT_TYPE, SECURITY_INFORMA PSID, PSID, PACL, PACL); static NTSTATUS (WINAPI *pNtAccessCheck)(PSECURITY_DESCRIPTOR, HANDLE, ACCESS_MASK, PGENERIC_MAPPING, PPRIVILEGE_SET, PULONG, PULONG, NTSTATUS*); -static BOOL (WINAPI *pCreateRestrictedToken)(HANDLE, DWORD, DWORD, PSID_AND_ATTRIBUTES, DWORD, - PLUID_AND_ATTRIBUTES, DWORD, PSID_AND_ATTRIBUTES, PHANDLE); static NTSTATUS (WINAPI *pNtSetSecurityObject)(HANDLE,SECURITY_INFORMATION,PSECURITY_DESCRIPTOR); static NTSTATUS (WINAPI *pNtCreateFile)(PHANDLE,ACCESS_MASK,POBJECT_ATTRIBUTES,PIO_STATUS_BLOCK,PLARGE_INTEGER,ULONG,ULONG,ULONG,ULONG,PVOID,ULONG); static BOOL (WINAPI *pRtlDosPathNameToNtPathName_U)(LPCWSTR,PUNICODE_STRING,PWSTR*,CURDIR*); @@ -175,7 +173,6 @@ static void init(void) pSetEntriesInAclW = (void *)GetProcAddress(hmod, "SetEntriesInAclW"); pSetSecurityDescriptorControl = (void *)GetProcAddress(hmod, "SetSecurityDescriptorControl"); pSetSecurityInfo = (void *)GetProcAddress(hmod, "SetSecurityInfo"); - pCreateRestrictedToken = (void *)GetProcAddress(hmod, "CreateRestrictedToken"); pGetWindowsAccountDomainSid = (void *)GetProcAddress(hmod, "GetWindowsAccountDomainSid"); pEqualDomainSid = (void *)GetProcAddress(hmod, "EqualDomainSid"); pGetSidIdentifierAuthority = (void *)GetProcAddress(hmod, "GetSidIdentifierAuthority"); @@ -5188,19 +5185,19 @@ static void test_CreateRestrictedToken(void) { HANDLE process_token, token, r_token; PTOKEN_GROUPS token_groups, groups2; + LUID_AND_ATTRIBUTES lattr; SID_AND_ATTRIBUTES sattr; SECURITY_IMPERSONATION_LEVEL level; + SID *removed_sid = NULL; + char privs_buffer[1000]; + TOKEN_PRIVILEGES *privs = (TOKEN_PRIVILEGES *)privs_buffer; + PRIVILEGE_SET priv_set; TOKEN_TYPE type; BOOL is_member; DWORD size; + LUID luid; BOOL ret; - DWORD i, j; - - if (!pCreateRestrictedToken) - { - win_skip("CreateRestrictedToken is not available\n"); - return; - } + DWORD i;
ret = OpenProcessToken(GetCurrentProcess(), TOKEN_DUPLICATE|TOKEN_QUERY, &process_token); ok(ret, "got error %d\n", GetLastError()); @@ -5209,7 +5206,6 @@ static void test_CreateRestrictedToken(void) NULL, SecurityImpersonation, TokenImpersonation, &token); ok(ret, "got error %d\n", GetLastError());
- /* groups */ ret = GetTokenInformation(token, TokenGroups, NULL, 0, &size); ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER, "got %d with error %d\n", ret, GetLastError()); @@ -5220,70 +5216,120 @@ static void test_CreateRestrictedToken(void) for (i = 0; i < token_groups->GroupCount; i++) { if (token_groups->Groups[i].Attributes & SE_GROUP_ENABLED) + { + removed_sid = token_groups->Groups[i].Sid; break; + } } - - if (i == token_groups->GroupCount) - { - HeapFree(GetProcessHeap(), 0, token_groups); - CloseHandle(token); - skip("User not a member of any group\n"); - return; - } + ok(!!removed_sid, "user is not a member of any group\n");
is_member = FALSE; - ret = pCheckTokenMembership(token, token_groups->Groups[i].Sid, &is_member); + ret = pCheckTokenMembership(token, removed_sid, &is_member); ok(ret, "got error %d\n", GetLastError()); ok(is_member, "not a member\n");
- /* disable a SID in new token */ - sattr.Sid = token_groups->Groups[i].Sid; + sattr.Sid = removed_sid; sattr.Attributes = 0; r_token = NULL; - ret = pCreateRestrictedToken(token, 0, 1, &sattr, 0, NULL, 0, NULL, &r_token); + ret = CreateRestrictedToken(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, removed_sid, &is_member); + ok(ret, "got error %d\n", GetLastError()); + todo_wine 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", + ret, GetLastError()); + groups2 = HeapAlloc(GetProcessHeap(), 0, size); + ret = GetTokenInformation(r_token, TokenGroups, groups2, size, &size); + ok(ret, "got error %d\n", GetLastError()); + + for (i = 0; i < groups2->GroupCount; i++) { - /* check if a SID is enabled */ - 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"); + if (EqualSid(groups2->Groups[i].Sid, removed_sid)) + { + DWORD attr = groups2->Groups[i].Attributes; + todo_wine ok(attr & SE_GROUP_USE_FOR_DENY_ONLY, "got wrong attributes %#x\n", attr); + todo_wine ok(!(attr & SE_GROUP_ENABLED), "got wrong attributes %#x\n", attr); + break; + } + } + + HeapFree(GetProcessHeap(), 0, groups2);
- ret = GetTokenInformation(r_token, TokenGroups, NULL, 0, &size); - ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER, "got %d with error %d\n", - ret, GetLastError()); - groups2 = HeapAlloc(GetProcessHeap(), 0, size); - ret = GetTokenInformation(r_token, TokenGroups, groups2, size, &size); - ok(ret, "got error %d\n", GetLastError()); + size = sizeof(type); + ret = GetTokenInformation(r_token, TokenType, &type, size, &size); + ok(ret, "got error %d\n", GetLastError()); + ok(type == TokenImpersonation, "got type %u\n", type);
- for (j = 0; j < groups2->GroupCount; j++) + size = sizeof(level); + ret = GetTokenInformation(r_token, TokenImpersonationLevel, &level, size, &size); + ok(ret, "got error %d\n", GetLastError()); + ok(level == SecurityImpersonation, "got level %u\n", type); + + CloseHandle(r_token); + + r_token = NULL; + ret = CreateRestrictedToken(process_token, 0, 1, &sattr, 0, NULL, 0, NULL, &r_token); + ok(ret, "got error %u\n", GetLastError()); + + size = sizeof(type); + ret = GetTokenInformation(r_token, TokenType, &type, size, &size); + ok(ret, "got error %u\n", GetLastError()); + ok(type == TokenPrimary, "got type %u\n", type); + + CloseHandle(r_token); + + ret = GetTokenInformation(token, TokenPrivileges, privs, sizeof(privs_buffer), &size); + ok(ret, "got error %u\n", GetLastError()); + + for (i = 0; i < privs->PrivilegeCount; i++) + { + if (privs->Privileges[i].Attributes & SE_PRIVILEGE_ENABLED) { - if (EqualSid(groups2->Groups[j].Sid, token_groups->Groups[i].Sid)) - break; + luid = privs->Privileges[i].Luid; + break; } + } + ok(i < privs->PrivilegeCount, "user has no privileges\n");
- todo_wine 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, - "got wrong attributes\n"); + lattr.Luid = luid; + lattr.Attributes = 0; + r_token = NULL; + ret = CreateRestrictedToken(token, 0, 0, NULL, 1, &lattr, 0, NULL, &r_token); + ok(ret, "got error %u\n", GetLastError());
- HeapFree(GetProcessHeap(), 0, groups2); + priv_set.PrivilegeCount = 1; + priv_set.Control = 0; + priv_set.Privilege[0].Luid = luid; + priv_set.Privilege[0].Attributes = 0; + ret = PrivilegeCheck(r_token, &priv_set, &is_member); + ok(ret, "got error %u\n", GetLastError()); + todo_wine ok(!is_member, "privilege should not be enabled\n");
- size = sizeof(type); - ret = GetTokenInformation(r_token, TokenType, &type, size, &size); - ok(ret, "got error %d\n", GetLastError()); - ok(type == TokenImpersonation, "got type %u\n", type); + ret = GetTokenInformation(r_token, TokenPrivileges, privs, sizeof(privs_buffer), &size); + ok(ret, "got error %u\n", GetLastError());
- size = sizeof(level); - 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 = FALSE; + for (i = 0; i < privs->PrivilegeCount; i++) + { + if (!memcmp(&privs->Privileges[i].Luid, &luid, sizeof(luid))) + is_member = TRUE; } + todo_wine ok(!is_member, "disabled privilege should not be present\n");
- HeapFree(GetProcessHeap(), 0, token_groups); CloseHandle(r_token); + + removed_sid->SubAuthority[0] = 0xdeadbeef; + lattr.Luid.LowPart = 0xdeadbeef; + r_token = NULL; + ret = CreateRestrictedToken(token, 0, 1, &sattr, 1, &lattr, 0, NULL, &r_token); + ok(ret, "got error %u\n", GetLastError()); + CloseHandle(r_token); + + HeapFree(GetProcessHeap(), 0, token_groups); CloseHandle(token); CloseHandle(process_token); }
From: Michael Müller michael@fds-team.de
Signed-off-by: Zebediah Figura z.figura12@gmail.com --- dlls/ntdll/ntdll.spec | 2 +- dlls/ntdll/unix/security.c | 63 +++++++++++++++++++++++++++++ include/winnt.h | 5 +++ include/winternl.h | 1 + server/named_pipe.c | 2 +- server/process.c | 2 +- server/protocol.def | 10 +++++ server/security.h | 4 +- server/token.c | 82 +++++++++++++++++++++++++++++++++++++- 9 files changed, 165 insertions(+), 6 deletions(-)
diff --git a/dlls/ntdll/ntdll.spec b/dlls/ntdll/ntdll.spec index 8f6869d4995..1a2143ca132 100644 --- a/dlls/ntdll/ntdll.spec +++ b/dlls/ntdll/ntdll.spec @@ -209,7 +209,7 @@ # @ stub NtEnumerateSystemEnvironmentValuesEx @ stdcall -syscall NtEnumerateValueKey(long long long ptr long ptr) @ stub NtExtendSection -# @ stub NtFilterToken +@ stdcall -syscall NtFilterToken(long long ptr ptr ptr ptr) @ stdcall -syscall NtFindAtom(ptr long ptr) @ stdcall -syscall NtFlushBuffersFile(long ptr) @ stdcall -syscall NtFlushInstructionCache(long ptr long) diff --git a/dlls/ntdll/unix/security.c b/dlls/ntdll/unix/security.c index 09a1d787c09..ad85020d6c0 100644 --- a/dlls/ntdll/unix/security.c +++ b/dlls/ntdll/unix/security.c @@ -604,6 +604,69 @@ NTSTATUS WINAPI NtAdjustPrivilegesToken( HANDLE token, BOOLEAN disable, TOKEN_PR }
+/*********************************************************************** + * NtFilterToken (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 %#x %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++) + { + SID *sid = disable_sids->Groups[i].Sid; + sids_len += offsetof( SID, SubAuthority[sid->SubAuthorityCount] ); + } + + sids = malloc( sids_len ); + if (!sids) return STATUS_NO_MEMORY; + + for (i = 0, tmp = (BYTE *)sids; i < disable_sids->GroupCount; i++, tmp += len) + { + SID *sid = disable_sids->Groups[i].Sid; + len = offsetof( SID, SubAuthority[sid->SubAuthorityCount] ); + 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; + + free( sids ); + return status; +} + + + /*********************************************************************** * NtPrivilegeCheck (NTDLL.@) */ diff --git a/include/winnt.h b/include/winnt.h index 63567ba62ee..1eb82876f1b 100644 --- a/include/winnt.h +++ b/include/winnt.h @@ -4288,6 +4288,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 fc2f0285183..e47c7824fcc 100644 --- a/include/winternl.h +++ b/include/winternl.h @@ -3045,6 +3045,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/named_pipe.c b/server/named_pipe.c index 1ac35cf1185..5c7f035619c 100644 --- a/server/named_pipe.c +++ b/server/named_pipe.c @@ -1161,7 +1161,7 @@ static int pipe_server_ioctl( struct fd *fd, ioctl_code_t code, struct async *as if (current->process->token) /* FIXME: use the client token */ { struct token *token; - if (!(token = token_duplicate( current->process->token, 0, SecurityImpersonation, NULL ))) + if (!(token = token_duplicate( current->process->token, 0, SecurityImpersonation, NULL, NULL, 0, NULL, 0 ))) return 0; if (current->token) release_object( current->token ); current->token = token; diff --git a/server/process.c b/server/process.c index 1c9148f1e0c..1fd110978af 100644 --- a/server/process.c +++ b/server/process.c @@ -581,7 +581,7 @@ struct process *create_process( int fd, struct process *parent, int inherit_all, : 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 f64e7d0ca81..9437a303501 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -3142,6 +3142,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..7c35300afe4 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 *remove_privs, unsigned int remove_priv_count, + const SID *remove_groups, unsigned int remove_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 23e85ebbff3..8404e651196 100644 --- a/server/token.c +++ b/server/token.c @@ -286,6 +286,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 ) @@ -627,8 +640,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 *remove_privs, unsigned int remove_priv_count, + const SID *remove_groups, unsigned int remove_group_count) { const luid_t *modified_id = primary || (impersonation_level == src_token->impersonation_level) ? @@ -664,6 +705,12 @@ struct token *token_duplicate( struct token *src_token, unsigned primary, return NULL; } memcpy( newgroup, group, size ); + if (filter_group( group, remove_groups, remove_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) { @@ -675,11 +722,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, remove_privs, remove_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 ); @@ -1312,7 +1362,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 ); @@ -1322,6 +1372,34 @@ 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) {
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=79174
Your paranoid android.
=== debiant (build log) ===
The task timed out
=== debiant (build log) ===
The task timed out
Based on a patch by Michael Müller.
Signed-off-by: Zebediah Figura z.figura12@gmail.com --- dlls/advapi32/tests/security.c | 26 ++++++++--------- dlls/kernelbase/security.c | 51 +++++++++++++++++++++++----------- 2 files changed, 48 insertions(+), 29 deletions(-)
diff --git a/dlls/advapi32/tests/security.c b/dlls/advapi32/tests/security.c index 957ebc3036a..6919ea64ce9 100644 --- a/dlls/advapi32/tests/security.c +++ b/dlls/advapi32/tests/security.c @@ -5237,7 +5237,7 @@ static void test_CreateRestrictedToken(void) is_member = TRUE; ret = pCheckTokenMembership(r_token, removed_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", @@ -5251,8 +5251,8 @@ static void test_CreateRestrictedToken(void) if (EqualSid(groups2->Groups[i].Sid, removed_sid)) { DWORD attr = groups2->Groups[i].Attributes; - todo_wine ok(attr & SE_GROUP_USE_FOR_DENY_ONLY, "got wrong attributes %#x\n", attr); - todo_wine ok(!(attr & SE_GROUP_ENABLED), "got wrong attributes %#x\n", attr); + ok(attr & SE_GROUP_USE_FOR_DENY_ONLY, "got wrong attributes %#x\n", attr); + ok(!(attr & SE_GROUP_ENABLED), "got wrong attributes %#x\n", attr); break; } } @@ -5307,7 +5307,7 @@ static void test_CreateRestrictedToken(void) priv_set.Privilege[0].Attributes = 0; ret = PrivilegeCheck(r_token, &priv_set, &is_member); ok(ret, "got error %u\n", GetLastError()); - todo_wine ok(!is_member, "privilege should not be enabled\n"); + ok(!is_member, "privilege should not be enabled\n");
ret = GetTokenInformation(r_token, TokenPrivileges, privs, sizeof(privs_buffer), &size); ok(ret, "got error %u\n", GetLastError()); @@ -5318,7 +5318,7 @@ static void test_CreateRestrictedToken(void) if (!memcmp(&privs->Privileges[i].Luid, &luid, sizeof(luid))) is_member = TRUE; } - todo_wine ok(!is_member, "disabled privilege should not be present\n"); + ok(!is_member, "disabled privilege should not be present\n");
CloseHandle(r_token);
@@ -7679,8 +7679,8 @@ static void test_duplicate_handle_access(void)
SetLastError(0xdeadbeef); event2 = OpenEventA(EVENT_MODIFY_STATE, FALSE, "test_dup"); - todo_wine ok(!event2, "expected failure\n"); - todo_wine ok(GetLastError() == ERROR_ACCESS_DENIED, "got error %u\n", GetLastError()); + ok(!event2, "expected failure\n"); + ok(GetLastError() == ERROR_ACCESS_DENIED, "got error %u\n", GetLastError());
ret = DuplicateHandle(GetCurrentProcess(), all_event, GetCurrentProcess(), &event2, EVENT_MODIFY_STATE, FALSE, 0); ok(ret, "got error %u\n", GetLastError()); @@ -7688,8 +7688,8 @@ static void test_duplicate_handle_access(void)
SetLastError(0xdeadbeef); ret = DuplicateHandle(GetCurrentProcess(), sync_event, GetCurrentProcess(), &event2, EVENT_MODIFY_STATE, FALSE, 0); - todo_wine ok(!ret, "expected failure\n"); - todo_wine ok(GetLastError() == ERROR_ACCESS_DENIED, "got error %u\n", GetLastError()); + ok(!ret, "expected failure\n"); + ok(GetLastError() == ERROR_ACCESS_DENIED, "got error %u\n", GetLastError());
ret = RevertToSelf(); ok(ret, "got error %u\n", GetLastError()); @@ -7751,13 +7751,13 @@ static void test_duplicate_handle_access_child(void)
SetLastError(0xdeadbeef); ret = DuplicateHandle(process, event, process, &event2, EVENT_MODIFY_STATE, FALSE, 0); - todo_wine ok(!ret, "expected failure\n"); - todo_wine ok(GetLastError() == ERROR_ACCESS_DENIED, "got error %u\n", GetLastError()); + ok(!ret, "expected failure\n"); + ok(GetLastError() == ERROR_ACCESS_DENIED, "got error %u\n", GetLastError());
SetLastError(0xdeadbeef); ret = DuplicateHandle(process, event, GetCurrentProcess(), &event2, EVENT_MODIFY_STATE, FALSE, 0); - todo_wine ok(!ret, "expected failure\n"); - todo_wine ok(GetLastError() == ERROR_ACCESS_DENIED, "got error %u\n", GetLastError()); + ok(!ret, "expected failure\n"); + ok(GetLastError() == ERROR_ACCESS_DENIED, "got error %u\n", GetLastError());
ret = RevertToSelf(); ok(ret, "failed to revert, error %u\n", GetLastError()); diff --git a/dlls/kernelbase/security.c b/dlls/kernelbase/security.c index 72bb5892d3b..97943048b01 100644 --- a/dlls/kernelbase/security.c +++ b/dlls/kernelbase/security.c @@ -646,27 +646,46 @@ exit: * CreateRestrictedToken (kernelbase.@) */ BOOL WINAPI CreateRestrictedToken( HANDLE token, DWORD flags, - DWORD disable_count, PSID_AND_ATTRIBUTES disable_sids, - DWORD delete_count, PLUID_AND_ATTRIBUTES delete_privs, - DWORD restrict_count, PSID_AND_ATTRIBUTES restrict_sids, PHANDLE ret ) + DWORD disable_sid_count, SID_AND_ATTRIBUTES *disable_sids, + DWORD delete_priv_count, LUID_AND_ATTRIBUTES *delete_privs, + DWORD restrict_sid_count, SID_AND_ATTRIBUTES *restrict_sids, HANDLE *ret ) { - TOKEN_TYPE type; - SECURITY_IMPERSONATION_LEVEL level = SecurityAnonymous; - DWORD size; + TOKEN_PRIVILEGES *nt_privs = NULL; + TOKEN_GROUPS *nt_disable_sids = NULL, *nt_restrict_sids = NULL; + NTSTATUS status = STATUS_NO_MEMORY;
- FIXME("(%p, 0x%x, %u, %p, %u, %p, %u, %p, %p): stub\n", - token, flags, disable_count, disable_sids, delete_count, delete_privs, - restrict_count, restrict_sids, ret ); + TRACE("token %p, flags %#x, disable_sids %u %p, delete_privs %u %p, restrict_sids %u %p, ret %p\n", + token, flags, disable_sid_count, disable_sids, delete_priv_count, delete_privs, + restrict_sid_count, restrict_sids, ret);
- size = sizeof(type); - if (!GetTokenInformation( token, TokenType, &type, size, &size )) return FALSE; - if (type == TokenImpersonation) + if (disable_sid_count) { - size = sizeof(level); - if (!GetTokenInformation( token, TokenImpersonationLevel, &level, size, &size )) - return FALSE; + if (!(nt_disable_sids = heap_alloc( offsetof( TOKEN_GROUPS, Groups[disable_sid_count] ) ))) goto out; + nt_disable_sids->GroupCount = disable_sid_count; + memcpy( nt_disable_sids->Groups, disable_sids, disable_sid_count * sizeof(SID_AND_ATTRIBUTES) ); + } + + if (delete_priv_count) + { + if (!(nt_privs = heap_alloc( offsetof( TOKEN_GROUPS, Groups[delete_priv_count] ) ))) goto out; + nt_privs->PrivilegeCount = delete_priv_count; + memcpy( nt_privs->Privileges, delete_privs, delete_priv_count * sizeof(SID_AND_ATTRIBUTES) ); } - return DuplicateTokenEx( token, MAXIMUM_ALLOWED, NULL, level, type, ret ); + + if (restrict_sid_count) + { + if (!(nt_restrict_sids = heap_alloc( offsetof( TOKEN_GROUPS, Groups[restrict_sid_count] ) ))) goto out; + nt_restrict_sids->GroupCount = restrict_sid_count; + memcpy( nt_restrict_sids->Groups, restrict_sids, restrict_sid_count * sizeof(SID_AND_ATTRIBUTES) ); + } + + status = NtFilterToken(token, flags, nt_disable_sids, nt_privs, nt_restrict_sids, ret); + +out: + heap_free(nt_disable_sids); + heap_free(nt_privs); + heap_free(nt_restrict_sids); + return set_ntstatus( status ); }
/******************************************************************************
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=79175
Your paranoid android.
=== debiant (build log) ===
The task timed out
=== debiant (build log) ===
The task timed out