From: Jinoh Kang jinoh.kang.kr@gmail.com
--- dlls/ntdll/tests/om.c | 195 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 195 insertions(+)
diff --git a/dlls/ntdll/tests/om.c b/dlls/ntdll/tests/om.c index 62659fc8cb4..74451ae891c 100644 --- a/dlls/ntdll/tests/om.c +++ b/dlls/ntdll/tests/om.c @@ -3156,6 +3156,200 @@ static void test_null_in_object_name(void) skip("Limited access to \Registry\Machine\Software key, skipping the tests\n"); }
+static void test_object_permanence(void) +{ + static const struct object_permanence_test { + const char *name; + ULONG initial_attr; + ACCESS_MASK access; + BOOLEAN make_temporary; + NTSTATUS make_temp_status; + } tests[] = { + { + .name = "permanent object persists", + .initial_attr = OBJ_PERMANENT, + .access = GENERIC_ALL, + .make_temporary = FALSE, + }, + { + .name = "NtMakeTemporaryObject() succeeds", + .initial_attr = OBJ_PERMANENT, + .access = GENERIC_ALL, + .make_temporary = TRUE, + .make_temp_status = STATUS_SUCCESS, + }, + { + .name = "NtMakeTemporaryObject() fails w/o DELETE access", + .initial_attr = OBJ_PERMANENT, + .access = EVENT_ALL_ACCESS & ~DELETE, + .make_temporary = TRUE, + .make_temp_status = STATUS_ACCESS_DENIED, + }, + + { + .name = "temporary object disappears", + .initial_attr = 0, + .access = GENERIC_ALL, + .make_temporary = FALSE, + }, + { + .name = "NtMakeTemporaryObject() succeeds even if already temporary", + .initial_attr = 0, + .access = GENERIC_ALL, + .make_temporary = TRUE, + .make_temp_status = STATUS_SUCCESS, + }, + { + .name = "NtMakeTemporaryObject() fails w/o DELETE access even if already temporary", + .initial_attr = 0, + .access = EVENT_ALL_ACCESS & ~DELETE, + .make_temporary = TRUE, + .make_temp_status = STATUS_ACCESS_DENIED, + }, + }; + const struct object_permanence_test *test; + HANDLE process_token = NULL, thread_token = NULL; + SECURITY_QUALITY_OF_SERVICE token_qos = { + .Length = sizeof(token_qos), + .ImpersonationLevel = SecurityDelegation, + .ContextTrackingMode = SECURITY_STATIC_TRACKING, + .EffectiveOnly = FALSE, + }; + OBJECT_ATTRIBUTES token_attr = { + .Length = sizeof(token_attr), + .SecurityQualityOfService = &token_qos, + }; + TOKEN_PRIVILEGES new_privs = { + .PrivilegeCount = 1, + .Privileges = { + { + .Luid = { .LowPart = SE_CREATE_PERMANENT_PRIVILEGE }, + .Attributes = SE_PRIVILEGE_ENABLED, + }, + }, + }; + NTSTATUS status; + BOOL creatpermapriv = FALSE; + + status = NtOpenProcessToken( GetCurrentProcess(), TOKEN_DUPLICATE, &process_token ); + ok( status == STATUS_SUCCESS, "NtOpenProcessToken returned %08lx\n", status ); + + status = NtDuplicateToken( process_token, TOKEN_IMPERSONATE | TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, + &token_attr, FALSE, TokenImpersonation, &thread_token ); + ok( status == STATUS_SUCCESS, "NtDuplicateToken returned %08lx\n", status ); + NtClose( process_token ); + + status = NtAdjustPrivilegesToken( thread_token, FALSE, &new_privs, sizeof(new_privs), NULL, NULL ); + todo_wine_if(status != STATUS_NOT_ALL_ASSIGNED) + ok( status == STATUS_SUCCESS || status == STATUS_NOT_ALL_ASSIGNED, "NtAdjustPrivilegesToken returned %08lx\n", status ); + creatpermapriv = (status == STATUS_SUCCESS); + + status = NtSetInformationThread( GetCurrentThread(), ThreadImpersonationToken, &thread_token, sizeof(thread_token) ); + ok( status == STATUS_SUCCESS, "NtSetInformationThread returned %08lx\n", status ); + NtClose( thread_token ); + + for (test = &tests[0]; test != &tests[ARRAY_SIZE(tests)]; test++) + { + HANDLE handle, handle2, handle3; + OBJECT_BASIC_INFORMATION obi; + OBJECT_ATTRIBUTES attr; + UNICODE_STRING name; + BOOL is_permanent; + ULONG len = 0; + + winetest_push_context( "test#%Iu", test - &tests[0] ); + trace( "(%s)\n", test->name ); + + RtlInitUnicodeString( &name, L"\BaseNamedObjects\test_object_permanence" ); + InitializeObjectAttributes( &attr, &name, test->initial_attr, 0, NULL ); + status = NtCreateEvent( &handle, test->access, &attr, NotificationEvent, FALSE ); + if (test->initial_attr & OBJ_PERMANENT) + { + NTSTATUS expected_status = creatpermapriv ? STATUS_SUCCESS : STATUS_PRIVILEGE_NOT_HELD; + todo_wine + ok( status == expected_status, "NtCreateEvent returned %08lx (expected %08lx)\n", status, expected_status ); + is_permanent = TRUE; + } + else + { + ok( status == STATUS_SUCCESS, "NtCreateEvent returned %08lx\n", status ); + is_permanent = FALSE; + } + if (NT_ERROR(status)) + { + winetest_pop_context(); + continue; + } + + status = NtQueryObject( handle, ObjectBasicInformation, &obi, sizeof(obi), &len ); + ok( status == STATUS_SUCCESS, "NtQueryObject returned %08lx\n", status ); + todo_wine_if(test->initial_attr != 0) + ok( obi.Attributes == test->initial_attr, "expected attr %08lx, got %08lx\n", test->initial_attr, obi.Attributes ); + + if (test->make_temporary) + { + if (test->make_temp_status == STATUS_ACCESS_DENIED) + ok( !(obi.GrantedAccess & DELETE), "expected no DELETE access in %08lx\n", obi.GrantedAccess ); + if (test->make_temp_status == STATUS_SUCCESS) + ok( !!(obi.GrantedAccess & DELETE), "expected DELETE access in %08lx\n", obi.GrantedAccess ); + + status = NtMakeTemporaryObject( handle ); + todo_wine_if(test->make_temp_status == STATUS_ACCESS_DENIED) + ok( status == test->make_temp_status, "NtMakeTemporaryObject returned %08lx\n", status ); + if (!NT_ERROR(status)) is_permanent = FALSE; + } + + if (winetest_debug > 1) + trace( "NOTE: object still has unclosed handle (%p) and shouldn't be deleted", handle ); + + winetest_push_context( "first handle (%p) still open", handle ); + status = NtOpenEvent( &handle2, GENERIC_ALL, &attr ); + ok( status == STATUS_SUCCESS, "NtOpenEvent returned %08lx\n", status ); + if (!NT_ERROR(status)) + { + ULONG expect_attr = (obi.Attributes & ~OBJ_PERMANENT) | (is_permanent ? OBJ_PERMANENT : 0); + OBJECT_BASIC_INFORMATION obi2; + + status = NtQueryObject( handle2, ObjectBasicInformation, &obi2, sizeof(obi2), &len ); + ok( status == STATUS_SUCCESS, "NtQueryObject returned %08lx\n", status ); + todo_wine_if(expect_attr != 0) + ok( obi2.Attributes == expect_attr, "expected attr %08lx, got %08lx\n", expect_attr, obi2.Attributes ); + + NtClose( handle2 ); + } + winetest_pop_context(); + + if (winetest_debug > 1) + trace( "NOTE: about to close earlier handle (%p) which should be the last", handle ); + NtClose( handle ); + + winetest_push_context( "first handle closed" ); + status = NtOpenEvent( &handle3, GENERIC_ALL, &attr ); + ok( status == (is_permanent ? STATUS_SUCCESS : STATUS_OBJECT_NAME_NOT_FOUND), "NtOpenEvent returned %08lx\n", status ); + if (!NT_ERROR(status)) + { + ULONG expect_attr = (obi.Attributes & ~OBJ_PERMANENT) | (is_permanent ? OBJ_PERMANENT : 0); + OBJECT_BASIC_INFORMATION obi3; + + status = NtQueryObject( handle3, ObjectBasicInformation, &obi3, sizeof(obi3), &len ); + ok( status == STATUS_SUCCESS, "NtQueryObject returned %08lx\n", status ); + todo_wine_if(expect_attr != 0) + ok( obi3.Attributes == expect_attr, "expected attr %08lx, got %08lx\n", expect_attr, obi3.Attributes ); + + /* ensure object is deleted */ + NtMakeTemporaryObject( handle3 ); + NtClose( handle3 ); + } + winetest_pop_context(); + + winetest_pop_context(); + } + + thread_token = NULL; + status = NtSetInformationThread( GetCurrentThread(), ThreadImpersonationToken, &thread_token, sizeof(thread_token) ); + ok( status == STATUS_SUCCESS, "NtSetInformationThread returned %08lx\n", status ); +} + START_TEST(om) { HMODULE hntdll = GetModuleHandleA("ntdll.dll"); @@ -3219,4 +3413,5 @@ START_TEST(om) test_globalroot(); test_object_identity(); test_query_directory(); + test_object_permanence(); }