From: Zebediah Figura zfigura@codeweavers.com
The fourth parameter is a boolean flag. The impersonation level is specified only through the SECURITY_QUALITY_OF_SERVICE structure.
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=54913 --- dlls/kernelbase/security.c | 8 +++++++- dlls/ntdll/sec.c | 18 ++++++++++-------- dlls/ntdll/tests/om.c | 8 ++++---- dlls/ntdll/unix/security.c | 6 +++++- dlls/wow64/security.c | 4 ++-- include/winternl.h | 2 +- 6 files changed, 29 insertions(+), 17 deletions(-)
diff --git a/dlls/kernelbase/security.c b/dlls/kernelbase/security.c index bf653933ad6..4434a59a53a 100644 --- a/dlls/kernelbase/security.c +++ b/dlls/kernelbase/security.c @@ -682,13 +682,19 @@ BOOL WINAPI DuplicateToken( HANDLE token, SECURITY_IMPERSONATION_LEVEL level, PH BOOL WINAPI DuplicateTokenEx( HANDLE token, DWORD access, LPSECURITY_ATTRIBUTES sa, SECURITY_IMPERSONATION_LEVEL level, TOKEN_TYPE type, PHANDLE ret ) { + SECURITY_QUALITY_OF_SERVICE qos; OBJECT_ATTRIBUTES attr;
TRACE("%p 0x%08lx 0x%08x 0x%08x %p\n", token, access, level, type, ret );
+ qos.Length = sizeof(qos); + qos.ImpersonationLevel = level; + qos.ContextTrackingMode = SECURITY_STATIC_TRACKING; + qos.EffectiveOnly = FALSE; InitializeObjectAttributes( &attr, NULL, (sa && sa->bInheritHandle) ? OBJ_INHERIT : 0, NULL, sa ? sa->lpSecurityDescriptor : NULL ); - return set_ntstatus( NtDuplicateToken( token, access, &attr, level, type, ret )); + attr.SecurityQualityOfService = &qos; + return set_ntstatus( NtDuplicateToken( token, access, &attr, FALSE, type, ret )); }
/****************************************************************************** diff --git a/dlls/ntdll/sec.c b/dlls/ntdll/sec.c index 34e5df7a533..4188faafb4b 100644 --- a/dlls/ntdll/sec.c +++ b/dlls/ntdll/sec.c @@ -1647,8 +1647,9 @@ RtlAdjustPrivilege(ULONG Privilege, NTSTATUS WINAPI RtlImpersonateSelf(SECURITY_IMPERSONATION_LEVEL ImpersonationLevel) { + SECURITY_QUALITY_OF_SERVICE qos; NTSTATUS Status; - OBJECT_ATTRIBUTES ObjectAttributes; + OBJECT_ATTRIBUTES attr; HANDLE ProcessToken; HANDLE ImpersonationToken;
@@ -1659,14 +1660,15 @@ RtlImpersonateSelf(SECURITY_IMPERSONATION_LEVEL ImpersonationLevel) if (Status != STATUS_SUCCESS) return Status;
- InitializeObjectAttributes( &ObjectAttributes, NULL, 0, NULL, NULL ); + qos.Length = sizeof(qos); + qos.ImpersonationLevel = ImpersonationLevel; + qos.ContextTrackingMode = SECURITY_STATIC_TRACKING; + qos.EffectiveOnly = FALSE; + InitializeObjectAttributes( &attr, NULL, 0, NULL, NULL ); + attr.SecurityQualityOfService = &qos;
- Status = NtDuplicateToken( ProcessToken, - TOKEN_IMPERSONATE, - &ObjectAttributes, - ImpersonationLevel, - TokenImpersonation, - &ImpersonationToken ); + Status = NtDuplicateToken( ProcessToken, TOKEN_IMPERSONATE, &attr, FALSE, + TokenImpersonation, &ImpersonationToken ); if (Status != STATUS_SUCCESS) { NtClose( ProcessToken ); diff --git a/dlls/ntdll/tests/om.c b/dlls/ntdll/tests/om.c index afe3bad33b8..6e5a9ef64b1 100644 --- a/dlls/ntdll/tests/om.c +++ b/dlls/ntdll/tests/om.c @@ -69,7 +69,7 @@ static NTSTATUS (WINAPI *pNtGetNextThread)(HANDLE process, HANDLE thread, ACCESS ULONG flags, HANDLE *handle); static NTSTATUS (WINAPI *pNtOpenProcessToken)(HANDLE,DWORD,HANDLE*); static NTSTATUS (WINAPI *pNtOpenThreadToken)(HANDLE,DWORD,BOOLEAN,HANDLE*); -static NTSTATUS (WINAPI *pNtDuplicateToken)(HANDLE,ACCESS_MASK,OBJECT_ATTRIBUTES*,SECURITY_IMPERSONATION_LEVEL,TOKEN_TYPE,HANDLE*); +static NTSTATUS (WINAPI *pNtDuplicateToken)(HANDLE,ACCESS_MASK,OBJECT_ATTRIBUTES*,BOOLEAN,TOKEN_TYPE,HANDLE*); static NTSTATUS (WINAPI *pNtDuplicateObject)(HANDLE,HANDLE,HANDLE,HANDLE*,ACCESS_MASK,ULONG,ULONG); static NTSTATUS (WINAPI *pNtCompareObjects)(HANDLE,HANDLE);
@@ -2145,13 +2145,13 @@ static void test_token(void)
status = pNtOpenProcessToken( GetCurrentProcess(), TOKEN_ALL_ACCESS, &handle ); ok( status == STATUS_SUCCESS, "NtOpenProcessToken failed: %lx\n", status); - status = pNtDuplicateToken( handle, TOKEN_ALL_ACCESS, NULL, 0, TokenPrimary, &handle2 ); + status = pNtDuplicateToken( handle, TOKEN_ALL_ACCESS, NULL, FALSE, TokenPrimary, &handle2 ); ok( status == STATUS_SUCCESS, "NtOpenProcessToken failed: %lx\n", status); pNtClose( handle2 ); - status = pNtDuplicateToken( handle, TOKEN_ALL_ACCESS, NULL, 0, TokenPrimary, (HANDLE *)0xdeadbee0 ); + status = pNtDuplicateToken( handle, TOKEN_ALL_ACCESS, NULL, FALSE, TokenPrimary, (HANDLE *)0xdeadbee0 ); ok( status == STATUS_ACCESS_VIOLATION, "NtOpenProcessToken failed: %lx\n", status); handle2 = (HANDLE)0xdeadbeef; - status = pNtDuplicateToken( (HANDLE)0xdead, TOKEN_ALL_ACCESS, NULL, 0, TokenPrimary, &handle2 ); + status = pNtDuplicateToken( (HANDLE)0xdead, TOKEN_ALL_ACCESS, NULL, FALSE, TokenPrimary, &handle2 ); ok( status == STATUS_INVALID_HANDLE, "NtOpenProcessToken failed: %lx\n", status); ok( !handle2 || broken(handle2 == (HANDLE)0xdeadbeef) /* vista */, "handle set %p\n", handle2 ); pNtClose( handle ); diff --git a/dlls/ntdll/unix/security.c b/dlls/ntdll/unix/security.c index 35e5c6abf99..8351f8b1993 100644 --- a/dlls/ntdll/unix/security.c +++ b/dlls/ntdll/unix/security.c @@ -111,12 +111,16 @@ NTSTATUS WINAPI NtOpenThreadTokenEx( HANDLE thread, DWORD access, BOOLEAN self, * NtDuplicateToken (NTDLL.@) */ NTSTATUS WINAPI NtDuplicateToken( HANDLE token, ACCESS_MASK access, OBJECT_ATTRIBUTES *attr, - SECURITY_IMPERSONATION_LEVEL level, TOKEN_TYPE type, HANDLE *handle ) + BOOLEAN effective_only, TOKEN_TYPE type, HANDLE *handle ) { + SECURITY_IMPERSONATION_LEVEL level = SecurityAnonymous; unsigned int status; data_size_t len; struct object_attributes *objattr;
+ if (effective_only) + FIXME( "ignoring effective-only flag\n" ); + *handle = 0; if ((status = alloc_object_attributes( attr, &objattr, &len ))) return status;
diff --git a/dlls/wow64/security.c b/dlls/wow64/security.c index 680f5a6ec56..65962f806cc 100644 --- a/dlls/wow64/security.c +++ b/dlls/wow64/security.c @@ -162,7 +162,7 @@ NTSTATUS WINAPI wow64_NtDuplicateToken( UINT *args ) HANDLE token = get_handle( &args ); ACCESS_MASK access = get_ulong( &args ); OBJECT_ATTRIBUTES32 *attr32 = get_ptr( &args ); - SECURITY_IMPERSONATION_LEVEL level = get_ulong( &args ); + BOOLEAN effective_only = get_ulong( &args ); TOKEN_TYPE type = get_ulong( &args ); ULONG *handle_ptr = get_ptr( &args );
@@ -171,7 +171,7 @@ NTSTATUS WINAPI wow64_NtDuplicateToken( UINT *args ) NTSTATUS status;
*handle_ptr = 0; - status = NtDuplicateToken( token, access, objattr_32to64( &attr, attr32 ), level, type, &handle ); + status = NtDuplicateToken( token, access, objattr_32to64( &attr, attr32 ), effective_only, type, &handle ); put_handle( handle_ptr, handle ); return status; } diff --git a/include/winternl.h b/include/winternl.h index 5ad1d2fa7e2..04676fe4b99 100644 --- a/include/winternl.h +++ b/include/winternl.h @@ -4169,7 +4169,7 @@ NTSYSAPI NTSTATUS WINAPI NtDeleteValueKey(HANDLE,const UNICODE_STRING *); NTSYSAPI NTSTATUS WINAPI NtDeviceIoControlFile(HANDLE,HANDLE,PIO_APC_ROUTINE,PVOID,PIO_STATUS_BLOCK,ULONG,PVOID,ULONG,PVOID,ULONG); NTSYSAPI NTSTATUS WINAPI NtDisplayString(PUNICODE_STRING); NTSYSAPI NTSTATUS WINAPI NtDuplicateObject(HANDLE,HANDLE,HANDLE,PHANDLE,ACCESS_MASK,ULONG,ULONG); -NTSYSAPI NTSTATUS WINAPI NtDuplicateToken(HANDLE,ACCESS_MASK,POBJECT_ATTRIBUTES,SECURITY_IMPERSONATION_LEVEL,TOKEN_TYPE,PHANDLE); +NTSYSAPI NTSTATUS WINAPI NtDuplicateToken(HANDLE,ACCESS_MASK,POBJECT_ATTRIBUTES,BOOLEAN,TOKEN_TYPE,PHANDLE); 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);
From: Zebediah Figura zfigura@codeweavers.com
--- dlls/advapi32/tests/security.c | 103 ++++++++++++++++++++++++++++++++- 1 file changed, 101 insertions(+), 2 deletions(-)
diff --git a/dlls/advapi32/tests/security.c b/dlls/advapi32/tests/security.c index bef0d2d5f2c..5e5000cfcb5 100644 --- a/dlls/advapi32/tests/security.c +++ b/dlls/advapi32/tests/security.c @@ -8091,22 +8091,70 @@ static void test_pseudo_handle_security(void) } }
+static const LUID_AND_ATTRIBUTES *find_privilege(const TOKEN_PRIVILEGES *privs, const LUID *luid) +{ + DWORD i; + + for (i = 0; i < privs->PrivilegeCount; ++i) + { + if (!memcmp(luid, &privs->Privileges[i].Luid, sizeof(LUID))) + return &privs->Privileges[i]; + } + + return NULL; +} + static void test_duplicate_token(void) { + const DWORD orig_access = TOKEN_QUERY | TOKEN_DUPLICATE | TOKEN_ADJUST_DEFAULT | TOKEN_ADJUST_PRIVILEGES; + char prev_privs_buffer[128], ret_privs_buffer[1024]; + TOKEN_PRIVILEGES *prev_privs = (void *)prev_privs_buffer; + TOKEN_PRIVILEGES *ret_privs = (void *)ret_privs_buffer; + const LUID_AND_ATTRIBUTES *priv; + TOKEN_PRIVILEGES privs; + SECURITY_QUALITY_OF_SERVICE qos = {.Length = sizeof(qos)}; + OBJECT_ATTRIBUTES attr = {.Length = sizeof(attr)}; + SECURITY_IMPERSONATION_LEVEL level; HANDLE token, token2; + DWORD size; BOOL ret;
- ret = OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_DUPLICATE | TOKEN_ADJUST_DEFAULT, &token); + ret = OpenProcessToken(GetCurrentProcess(), orig_access, &token); + ok(ret, "got error %lu\n", GetLastError()); + + /* Disable a privilege, to see if that privilege modification is preserved + * in the duplicated tokens. */ + privs.PrivilegeCount = 1; + ret = LookupPrivilegeValueA(NULL, "SeChangeNotifyPrivilege", &privs.Privileges[0].Luid); + ok(ret, "got error %lu\n", GetLastError()); + privs.Privileges[0].Attributes = 0; + ret = AdjustTokenPrivileges(token, FALSE, &privs, sizeof(prev_privs_buffer), prev_privs, &size); ok(ret, "got error %lu\n", GetLastError());
ret = DuplicateToken(token, SecurityAnonymous, &token2); ok(ret, "got error %lu\n", GetLastError()); TEST_GRANTED_ACCESS(token2, TOKEN_QUERY | TOKEN_IMPERSONATE); + ret = GetTokenInformation(token2, TokenImpersonationLevel, &level, sizeof(level), &size); + ok(ret, "got error %lu\n", GetLastError()); + ok(level == SecurityAnonymous, "got impersonation level %#x\n", level); + ret = GetTokenInformation(token2, TokenPrivileges, ret_privs, sizeof(ret_privs_buffer), &size); + ok(ret, "got error %lu\n", GetLastError()); + priv = find_privilege(ret_privs, &privs.Privileges[0].Luid); + ok(!!priv, "Privilege should exist\n"); + todo_wine ok(priv->Attributes == SE_GROUP_MANDATORY, "Got attributes %#lx\n", priv->Attributes); CloseHandle(token2);
ret = DuplicateTokenEx(token, 0, NULL, SecurityAnonymous, TokenPrimary, &token2); ok(ret, "got error %lu\n", GetLastError()); - TEST_GRANTED_ACCESS(token2, TOKEN_QUERY | TOKEN_DUPLICATE | TOKEN_ADJUST_DEFAULT); + TEST_GRANTED_ACCESS(token2, orig_access); + ret = GetTokenInformation(token2, TokenImpersonationLevel, &level, sizeof(level), &size); + ok(!ret, "expected failure\n"); + ok(GetLastError() == ERROR_INVALID_PARAMETER, "Got error %lu.\n", GetLastError()); + ret = GetTokenInformation(token2, TokenPrivileges, ret_privs, sizeof(ret_privs_buffer), &size); + ok(ret, "got error %lu\n", GetLastError()); + priv = find_privilege(ret_privs, &privs.Privileges[0].Luid); + ok(!!priv, "Privilege should exist\n"); + todo_wine ok(priv->Attributes == SE_GROUP_MANDATORY, "Got attributes %#lx\n", priv->Attributes); CloseHandle(token2);
ret = DuplicateTokenEx(token, MAXIMUM_ALLOWED, NULL, SecurityAnonymous, TokenPrimary, &token2); @@ -8119,6 +8167,57 @@ static void test_duplicate_token(void) TEST_GRANTED_ACCESS(token2, TOKEN_QUERY_SOURCE); CloseHandle(token2);
+ ret = DuplicateTokenEx(token, 0, NULL, SecurityIdentification, TokenImpersonation, &token2); + ok(ret, "got error %lu\n", GetLastError()); + TEST_GRANTED_ACCESS(token2, orig_access); + ret = GetTokenInformation(token2, TokenImpersonationLevel, &level, sizeof(level), &size); + ok(ret, "got error %lu\n", GetLastError()); + ok(level == SecurityIdentification, "got impersonation level %#x\n", level); + ret = GetTokenInformation(token2, TokenPrivileges, ret_privs, sizeof(ret_privs_buffer), &size); + ok(ret, "got error %lu\n", GetLastError()); + priv = find_privilege(ret_privs, &privs.Privileges[0].Luid); + ok(!!priv, "Privilege should exist\n"); + todo_wine ok(priv->Attributes == SE_GROUP_MANDATORY, "Got attributes %#lx\n", priv->Attributes); + CloseHandle(token2); + + ret = NtDuplicateToken(token, 0, &attr, FALSE, TokenImpersonation, &token2); + ok(ret == STATUS_SUCCESS, "Got status %#x.\n", ret); + TEST_GRANTED_ACCESS(token2, orig_access); + ret = GetTokenInformation(token2, TokenImpersonationLevel, &level, sizeof(level), &size); + ok(ret, "got error %lu\n", GetLastError()); + ok(level == SecurityAnonymous, "got impersonation level %#x\n", level); + ret = GetTokenInformation(token2, TokenPrivileges, ret_privs, sizeof(ret_privs_buffer), &size); + ok(ret, "got error %lu\n", GetLastError()); + priv = find_privilege(ret_privs, &privs.Privileges[0].Luid); + ok(!!priv, "Privilege should exist\n"); + todo_wine ok(priv->Attributes == SE_GROUP_MANDATORY, "Got attributes %#lx\n", priv->Attributes); + CloseHandle(token2); + + ret = NtDuplicateToken(token, 0, &attr, TRUE, TokenImpersonation, &token2); + ok(ret == STATUS_SUCCESS, "Got status %#x.\n", ret); + TEST_GRANTED_ACCESS(token2, orig_access); + ret = GetTokenInformation(token2, TokenPrivileges, ret_privs, sizeof(ret_privs_buffer), &size); + ok(ret, "got error %lu\n", GetLastError()); + priv = find_privilege(ret_privs, &privs.Privileges[0].Luid); + todo_wine ok(!priv, "Privilege shouldn't exist\n"); + CloseHandle(token2); + + qos.ImpersonationLevel = SecurityIdentification; + qos.ContextTrackingMode = SECURITY_STATIC_TRACKING; + qos.EffectiveOnly = FALSE; + attr.SecurityQualityOfService = &qos; + ret = NtDuplicateToken(token, 0, &attr, FALSE, TokenImpersonation, &token2); + ok(ret == STATUS_SUCCESS, "Got status %#x.\n", ret); + TEST_GRANTED_ACCESS(token2, orig_access); + ret = GetTokenInformation(token2, TokenImpersonationLevel, &level, sizeof(level), &size); + ok(ret, "got error %lu\n", GetLastError()); + ok(level == SecurityIdentification, "got impersonation level %#x\n", level); + CloseHandle(token2); + + privs.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; + ret = AdjustTokenPrivileges(token, FALSE, &privs, sizeof(prev_privs_buffer), prev_privs, &size); + ok(ret, "got error %lu\n", GetLastError()); + CloseHandle(token); }