1 /*
2  * PROJECT:         ReactOS API tests
3  * LICENSE:         GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4  * PURPOSE:         Tests for the NtAccessCheck API
5  * COPYRIGHT:       Copyright 2023 George Bișoc <george.bisoc@reactos.org>
6  */
7 
8 #include "precomp.h"
9 
10 static
11 HANDLE
12 GetToken(VOID)
13 {
14     NTSTATUS Status;
15     HANDLE Token;
16     HANDLE DuplicatedToken;
17     OBJECT_ATTRIBUTES ObjectAttributes;
18     SECURITY_QUALITY_OF_SERVICE Sqos;
19 
20     Status = NtOpenProcessToken(NtCurrentProcess(),
21                                 TOKEN_QUERY | TOKEN_DUPLICATE,
22                                 &Token);
23     if (!NT_SUCCESS(Status))
24     {
25         trace("Failed to get current process token (Status 0x%08lx)\n", Status);
26         return NULL;
27     }
28 
29     Sqos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
30     Sqos.ImpersonationLevel = SecurityImpersonation;
31     Sqos.ContextTrackingMode = 0;
32     Sqos.EffectiveOnly = FALSE;
33 
34     InitializeObjectAttributes(&ObjectAttributes,
35                                NULL,
36                                0,
37                                NULL,
38                                NULL);
39     ObjectAttributes.SecurityQualityOfService = &Sqos;
40 
41     Status = NtDuplicateToken(Token,
42                               TOKEN_QUERY | TOKEN_DUPLICATE,
43                               &ObjectAttributes,
44                               FALSE,
45                               TokenImpersonation,
46                               &DuplicatedToken);
47     if (!NT_SUCCESS(Status))
48     {
49         trace("Failed to duplicate token (Status 0x%08lx)\n", Status);
50         NtClose(Token);
51         return NULL;
52     }
53 
54     return DuplicatedToken;
55 }
56 
57 static
58 VOID
59 AccessCheckEmptyMappingTest(VOID)
60 {
61     NTSTATUS Status;
62     NTSTATUS AccessStatus;
63     ACCESS_MASK GrantedAccess;
64     PPRIVILEGE_SET PrivilegeSet = NULL;
65     ULONG PrivilegeSetLength;
66     HANDLE Token = NULL;
67     PACL Dacl = NULL;
68     ULONG DaclSize;
69     SECURITY_DESCRIPTOR Sd;
70     PSID WorldSid = NULL;
71     static SID_IDENTIFIER_AUTHORITY WorldAuthority = {SECURITY_WORLD_SID_AUTHORITY};
72     static GENERIC_MAPPING EmptyMapping = {0, 0, 0, 0};
73 
74     /* Allocate all the stuff we need */
75     PrivilegeSetLength = FIELD_OFFSET(PRIVILEGE_SET, Privilege[16]);
76     PrivilegeSet = RtlAllocateHeap(RtlGetProcessHeap(), 0, PrivilegeSetLength);
77     if (PrivilegeSet == NULL)
78     {
79         skip("Failed to allocate PrivilegeSet, skipping tests\n");
80         return;
81     }
82 
83     Status = RtlAllocateAndInitializeSid(&WorldAuthority,
84                                          1,
85                                          SECURITY_WORLD_RID,
86                                          0,
87                                          0,
88                                          0,
89                                          0,
90                                          0,
91                                          0,
92                                          0,
93                                          &WorldSid);
94     if (!NT_SUCCESS(Status))
95     {
96         skip("Failed to create World SID, skipping tests\n");
97         goto Quit;
98     }
99 
100     Token = GetToken();
101     if (Token == NULL)
102     {
103         skip("Failed to get token, skipping tests\n");
104         goto Quit;
105     }
106 
107     Status = RtlCreateSecurityDescriptor(&Sd, SECURITY_DESCRIPTOR_REVISION);
108     if (!NT_SUCCESS(Status))
109     {
110         skip("Failed to create a security descriptor, skipping tests\n");
111         goto Quit;
112     }
113 
114     DaclSize = sizeof(ACL) +
115                sizeof(ACCESS_ALLOWED_OBJECT_ACE) + RtlLengthSid(WorldSid);
116     Dacl = RtlAllocateHeap(RtlGetProcessHeap(),
117                            HEAP_ZERO_MEMORY,
118                            DaclSize);
119     if (Dacl == NULL)
120     {
121         skip("Failed to allocate memory for DACL, skipping tests\n");
122         goto Quit;
123     }
124 
125     /* Setup a ACL and give full access to everyone */
126     Status = RtlCreateAcl(Dacl,
127                           DaclSize,
128                           ACL_REVISION);
129     if (!NT_SUCCESS(Status))
130     {
131         skip("Failed to create DACL, skipping tests\n");
132         goto Quit;
133     }
134 
135     Status = RtlAddAccessAllowedAce(Dacl,
136                                     ACL_REVISION,
137                                     GENERIC_ALL,
138                                     WorldSid);
139     if (!NT_SUCCESS(Status))
140     {
141         skip("Failed to add allowed ACE for World SID, skipping tests\n");
142         goto Quit;
143     }
144 
145     /* Setup the descriptor */
146     RtlSetGroupSecurityDescriptor(&Sd, WorldSid, FALSE);
147     RtlSetOwnerSecurityDescriptor(&Sd, WorldSid, FALSE);
148     RtlSetDaclSecurityDescriptor(&Sd, TRUE, Dacl, FALSE);
149 
150     /* Do an access check with empty mapping */
151     Status = NtAccessCheck(&Sd,
152                            Token,
153                            MAXIMUM_ALLOWED,
154                            &EmptyMapping,
155                            PrivilegeSet,
156                            &PrivilegeSetLength,
157                            &GrantedAccess,
158                            &AccessStatus);
159     ok_hex(Status, STATUS_SUCCESS);
160     ok(AccessStatus == STATUS_SUCCESS, "Expected a success status but got 0x%08lx\n", AccessStatus);
161     trace("GrantedAccess == 0x%08lx\n", GrantedAccess);
162 
163 Quit:
164     if (Dacl)
165     {
166         RtlFreeHeap(RtlGetProcessHeap(), 0, Dacl);
167     }
168 
169     if (Token)
170     {
171         NtClose(Token);
172     }
173 
174     if (WorldSid)
175     {
176         RtlFreeSid(WorldSid);
177     }
178 
179     if (PrivilegeSet)
180     {
181         RtlFreeHeap(RtlGetProcessHeap(), 0, PrivilegeSet);
182     }
183 }
184 
185 START_TEST(NtAccessCheck)
186 {
187     AccessCheckEmptyMappingTest();
188 }
189