xref: /reactos/win32ss/user/ntuser/security.c (revision 0f9be539)
1 /*
2  * PROJECT:         ReactOS Win32k subsystem
3  * LICENSE:         GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4  * PURPOSE:         Security infrastructure of NTUSER component of Win32k
5  * COPYRIGHT:       Copyright 2022-2023 George Bișoc <george.bisoc@reactos.org>
6  */
7 
8 /* INCLUDES ******************************************************************/
9 
10 #include <win32k.h>
11 DBG_DEFAULT_CHANNEL(UserSecurity);
12 
13 /* FUNCTIONS *****************************************************************/
14 
15 /**
16  * @brief
17  * Opens an access token that represents the effective security
18  * context of the caller. The purpose of this function is to query
19  * the authenticated user that is associated with the security
20  * context.
21  *
22  * @return
23  * Returns a handle to an opened access token that represents the
24  * security context of the authenticated user, otherwise NULL.
25  */
26 HANDLE
IntGetCurrentAccessToken(VOID)27 IntGetCurrentAccessToken(VOID)
28 {
29     NTSTATUS Status;
30     HANDLE TokenHandle;
31 
32     /*
33      * Try acquiring the security context by opening
34      * the current thread (or so called impersonation)
35      * token. Such token represents the effective caller.
36      * Otherwise if the current thread does not have a
37      * token (hence no impersonation occurs) then open
38      * the token of main calling process instead.
39      */
40     Status = ZwOpenThreadToken(ZwCurrentThread(),
41                                TOKEN_QUERY,
42                                FALSE,
43                                &TokenHandle);
44     if (!NT_SUCCESS(Status))
45     {
46         /*
47          * We might likely fail to open the thread
48          * token if the process isn't impersonating
49          * a client. In scenarios where the server
50          * isn't impersonating, open the main process
51          * token.
52          */
53         if (Status == STATUS_NO_TOKEN)
54         {
55             TRACE("IntGetCurrentAccessToken(): The thread doesn't have a token, trying to open the process one...\n");
56             Status = ZwOpenProcessToken(ZwCurrentProcess(),
57                                         TOKEN_QUERY,
58                                         &TokenHandle);
59             if (!NT_SUCCESS(Status))
60             {
61                 /* We failed opening process token as well, bail out... */
62                 ERR("IntGetCurrentAccessToken(): Failed to capture security context, couldn't open the process token (Status 0x%08lx)\n", Status);
63                 return NULL;
64             }
65 
66             /* Return the opened token handle */
67             return TokenHandle;
68         }
69 
70         /* There's a thread token but we couldn't open it so bail out */
71         ERR("IntGetCurrentAccessToken(): Failed to capture security context, couldn't open the thread token (Status 0x%08lx)\n", Status);
72         return NULL;
73     }
74 
75     /* Return the opened token handle */
76     return TokenHandle;
77 }
78 
79 /**
80  * @brief
81  * Allocates a buffer within UM (user mode) address
82  * space area. Such buffer is reserved for security
83  * purposes, such as allocating a buffer for a DACL
84  * or a security descriptor.
85  *
86  * @param[in] Length
87  * The length of the buffer that has to be allocated,
88  * in bytes.
89  *
90  * @return
91  * Returns a pointer to an allocated buffer whose
92  * contents are arbitrary. If the function fails,
93  * it means no pages are available to reserve for
94  * memory allocation for this buffer.
95  */
96 PVOID
IntAllocateSecurityBuffer(_In_ SIZE_T Length)97 IntAllocateSecurityBuffer(
98     _In_ SIZE_T Length)
99 {
100     NTSTATUS Status;
101     PVOID Buffer = NULL;
102 
103     /* Allocate the buffer in UM memory space */
104     Status = ZwAllocateVirtualMemory(ZwCurrentProcess(),
105                                      &Buffer,
106                                      0,
107                                      &Length,
108                                      MEM_COMMIT,
109                                      PAGE_READWRITE);
110     if (!NT_SUCCESS(Status))
111     {
112         ERR("IntAllocateSecurityBuffer(): Failed to allocate the buffer (Status 0x%08lx)\n", Status);
113         return NULL;
114     }
115 
116     return Buffer;
117 }
118 
119 /**
120  * @brief
121  * Frees an allocated security buffer from UM
122  * memory that is been previously allocated by
123  * IntAllocateSecurityBuffer function.
124  *
125  * @param[in] Buffer
126  * A pointer to a buffer whose contents are
127  * arbitrary, to be freed from UM memory space.
128  *
129  * @return
130  * Nothing.
131  */
132 VOID
IntFreeSecurityBuffer(_In_ PVOID Buffer)133 IntFreeSecurityBuffer(
134     _In_ PVOID Buffer)
135 {
136     SIZE_T Size = 0;
137 
138     ZwFreeVirtualMemory(ZwCurrentProcess(),
139                         &Buffer,
140                         &Size,
141                         MEM_RELEASE);
142 }
143 
144 /**
145  * @brief
146  * Queries the authenticated user security identifier
147  * (SID) that is associated with the security context
148  * of the access token that is being opened.
149  *
150  * @param[out] User
151  * A pointer to the token user that contains the security
152  * identifier of the authenticated user.
153  *
154  * @return
155  * Returns STATUS_SUCCESS if the function has successfully
156  * queried the token user. STATUS_UNSUCCESSFUL is returned
157  * if the effective token of the caller couldn't be opened.
158  * STATUS_NO_MEMORY is returned if memory allocation for
159  * token user buffer has failed because of lack of necessary
160  * pages reserved for such allocation. A failure NTSTATUS
161  * code is returned otherwise.
162  *
163  * @remarks
164  * !!!WARNING!!! -- THE CALLER WHO QUERIES THE TOKEN USER IS
165  * RESPONSIBLE TO FREE THE ALLOCATED TOKEN USER BUFFER THAT IS
166  * BEING GIVEN.
167  */
168 NTSTATUS
IntQueryUserSecurityIdentification(_Out_ PTOKEN_USER * User)169 IntQueryUserSecurityIdentification(
170     _Out_ PTOKEN_USER *User)
171 {
172     NTSTATUS Status;
173     PTOKEN_USER UserToken = NULL;
174     HANDLE Token;
175     ULONG BufferLength;
176 
177     /* Initialize the parameter */
178     *User = NULL;
179 
180     /* Open the current token of the caller */
181     Token = IntGetCurrentAccessToken();
182     if (!Token)
183     {
184         ERR("IntQueryUserSecurityIdentification(): Couldn't capture the token!\n");
185         return STATUS_UNSUCCESSFUL;
186     }
187 
188     /*
189      * Since we do not know what the length
190      * of the buffer size should be exactly to
191      * hold the user data, let the function
192      * tell us the size.
193      */
194     Status = ZwQueryInformationToken(Token,
195                                      TokenUser,
196                                      NULL,
197                                      0,
198                                      &BufferLength);
199     if (Status == STATUS_BUFFER_TOO_SMALL)
200     {
201         /*
202          * Allocate some memory for the buffer
203          * based on the size that the function
204          * gave us.
205          */
206         UserToken = IntAllocateSecurityBuffer(BufferLength);
207         if (!UserToken)
208         {
209             /* Bail out if we failed */
210             ERR("IntQueryUserSecurityIdentification(): Couldn't allocate memory for the token user!\n");
211             ZwClose(Token);
212             return STATUS_NO_MEMORY;
213         }
214     }
215     else if (!NT_SUCCESS(Status))
216     {
217         ERR("IntQueryUserSecurityIdentification(): Failed to query the necessary length for the buffer (Status 0x%08lx)!\n", Status);
218         ZwClose(Token);
219         return Status;
220     }
221 
222     /* Query the user now as we have plenty of space to hold it */
223     Status = ZwQueryInformationToken(Token,
224                                      TokenUser,
225                                      UserToken,
226                                      BufferLength,
227                                      &BufferLength);
228     if (!NT_SUCCESS(Status))
229     {
230         /* We failed, bail out */
231         ERR("IntQueryUserSecurityIdentification(): Failed to query token user (Status 0x%08lx)\n", Status);
232         IntFreeSecurityBuffer(UserToken);
233         ZwClose(Token);
234         return Status;
235     }
236 
237     /* All good, give the buffer to the caller and close the captured token */
238     *User = UserToken;
239     ZwClose(Token);
240 
241     return STATUS_SUCCESS;
242 }
243 
244 /**
245  * @brief
246  * Assigns a security descriptor to the desktop
247  * object during a desktop object parse procedure.
248  *
249  * @param[in] WinSta
250  * A pointer to a window station object, of which
251  * such object contains its own security descriptor
252  * that will be captured.
253  *
254  * @param[in] Desktop
255  * A pointer to a desktop object that is created
256  * during a parse procedure.
257  *
258  * @param[in] AccessState
259  * A pointer to an access state structure that
260  * describes the progress state of an access in
261  * action.
262  *
263  * @return
264  * Returns STATUS_SUCCESS if the function has successfully
265  * assigned new security descriptor to the desktop object.
266  * A NTSTATUS failure code is returned otherwise.
267  */
268 NTSTATUS
269 NTAPI
IntAssignDesktopSecurityOnParse(_In_ PWINSTATION_OBJECT WinSta,_In_ PDESKTOP Desktop,_In_ PACCESS_STATE AccessState)270 IntAssignDesktopSecurityOnParse(
271     _In_ PWINSTATION_OBJECT WinSta,
272     _In_ PDESKTOP Desktop,
273     _In_ PACCESS_STATE AccessState)
274 {
275     NTSTATUS Status;
276     PSECURITY_DESCRIPTOR CapturedDescriptor;
277     BOOLEAN MemoryAllocated;
278 
279     /*
280      * Capture the security descriptor from
281      * the window station. The window station
282      * in question has a descriptor that is
283      * inheritable and contains desktop access
284      * rights as well.
285      */
286     Status = ObGetObjectSecurity(WinSta,
287                                  &CapturedDescriptor,
288                                  &MemoryAllocated);
289     if (!NT_SUCCESS(Status))
290     {
291         ERR("IntAssignDesktopSecurityOnParse(): Failed to capture the security descriptor from window station (Status 0x%08lx)\n", Status);
292         return Status;
293     }
294 
295     /* Assign new security to the desktop */
296     Status = ObAssignSecurity(AccessState,
297                               CapturedDescriptor,
298                               Desktop,
299                               ExDesktopObjectType);
300     if (!NT_SUCCESS(Status))
301     {
302         ERR("IntAssignDesktopSecurityOnParse(): Failed to assign security information to the desktop object (Status 0x%08lx)\n", Status);
303     }
304 
305     /* Release the descriptor that we have captured */
306     ObReleaseObjectSecurity(CapturedDescriptor, MemoryAllocated);
307     return Status;
308 }
309 
310 /**
311  * @brief
312  * Creates a security descriptor for the service.
313  *
314  * @param[out] ServiceSd
315  * A pointer to a newly allocated and created security
316  * descriptor for the service.
317  *
318  * @return
319  * Returns STATUS_SUCCESS if the function has successfully
320  * queried created the security descriptor. STATUS_NO_MEMORY
321  * is returned if memory allocation for security buffers because
322  * of a lack of needed pages to reserve for such allocation. A
323  * failure NTSTATUS code is returned otherwise.
324  */
325 NTSTATUS
326 NTAPI
IntCreateServiceSecurity(_Out_ PSECURITY_DESCRIPTOR * ServiceSd)327 IntCreateServiceSecurity(
328     _Out_ PSECURITY_DESCRIPTOR *ServiceSd)
329 {
330     NTSTATUS Status;
331     PACL ServiceDacl;
332     ULONG DaclSize;
333     ULONG RelSDSize;
334     SECURITY_DESCRIPTOR AbsSD;
335     PSECURITY_DESCRIPTOR RelSD;
336     PTOKEN_USER TokenUser;
337 
338     /* Initialize our local variables */
339     RelSDSize = 0;
340     TokenUser = NULL;
341     RelSD = NULL;
342     ServiceDacl = NULL;
343 
344     /* Query the logged in user of the current security context (aka token) */
345     Status = IntQueryUserSecurityIdentification(&TokenUser);
346     if (!TokenUser)
347     {
348         ERR("IntCreateServiceSecurity(): Failed to query the token user (Status 0x%08lx)\n", Status);
349         return Status;
350     }
351 
352     /* Initialize the absolute security descriptor */
353     Status = RtlCreateSecurityDescriptor(&AbsSD, SECURITY_DESCRIPTOR_REVISION);
354     if (!NT_SUCCESS(Status))
355     {
356         ERR("IntCreateServiceSecurity(): Failed to initialize absolute SD (Status 0x%08lx)\n", Status);
357         goto Quit;
358     }
359 
360     /*
361      * Build up the size of access control
362      * list (the DACL) necessary to initialize
363      * our ACL. The first two entry members
364      * of ACL field are the authenticated user
365      * that is associated with the security
366      * context of the token. Then here come
367      * the last two entries which are admins.
368      * Why the ACL contains two ACEs of the
369      * same SID is because of service access
370      * rights and ACE inheritance.
371      *
372      * A service is composed of a default
373      * desktop and window station upon
374      * booting the system. On Windows connection
375      * to such service is being made if no
376      * default window station and desktop handles
377      * were created before. The desktop and winsta
378      * objects grant access on a separate type basis.
379      * The user is granted full access to the window
380      * station first and then full access to the desktop.
381      * After that admins are granted specific rights
382      * separately, just like the user. Ultimately the
383      * ACEs that handle desktop rights management are
384      * inherited to the default desktop object so
385      * that there's no need to have a separate security
386      * descriptor for the desktop object alone.
387      */
388     DaclSize = sizeof(ACL) +
389                sizeof(ACCESS_ALLOWED_ACE) + RtlLengthSid(TokenUser->User.Sid) +
390                sizeof(ACCESS_ALLOWED_ACE) + RtlLengthSid(TokenUser->User.Sid) +
391                sizeof(ACCESS_ALLOWED_ACE) + RtlLengthSid(SeExports->SeAliasAdminsSid) +
392                sizeof(ACCESS_ALLOWED_ACE) + RtlLengthSid(SeExports->SeAliasAdminsSid);
393 
394     /* Allocate memory for service DACL */
395     ServiceDacl = IntAllocateSecurityBuffer(DaclSize);
396     if (!ServiceDacl)
397     {
398         ERR("IntCreateServiceSecurity(): Failed to allocate memory for service DACL!\n");
399         Status = STATUS_NO_MEMORY;
400         goto Quit;
401     }
402 
403     /* Now create the DACL */
404     Status = RtlCreateAcl(ServiceDacl,
405                           DaclSize,
406                           ACL_REVISION);
407     if (!NT_SUCCESS(Status))
408     {
409         ERR("IntCreateServiceSecurity(): Failed to create service DACL (Status 0x%08lx)\n", Status);
410         goto Quit;
411     }
412 
413     /*
414      * The authenticated user is the ultimate and absolute
415      * king in charge of the created (or opened, whatever that is)
416      * window station object.
417      */
418     Status = RtlAddAccessAllowedAceEx(ServiceDacl,
419                                       ACL_REVISION,
420                                       0,
421                                       WINSTA_ACCESS_ALL,
422                                       TokenUser->User.Sid);
423     if (!NT_SUCCESS(Status))
424     {
425         ERR("IntCreateServiceSecurity(): Failed to set up window station ACE for authenticated user (Status 0x%08lx)\n", Status);
426         goto Quit;
427     }
428 
429     /*
430      * The authenticated user also has the ultimate power
431      * over the desktop object as well. This ACE cannot
432      * be propagated but inherited. See the comment
433      * above regarding ACL size for further explanation.
434      */
435     Status = RtlAddAccessAllowedAceEx(ServiceDacl,
436                                       ACL_REVISION,
437                                       INHERIT_ONLY_ACE | NO_PROPAGATE_INHERIT_ACE | OBJECT_INHERIT_ACE,
438                                       DESKTOP_ALL_ACCESS,
439                                       TokenUser->User.Sid);
440     if (!NT_SUCCESS(Status))
441     {
442         ERR("IntCreateServiceSecurity(): Failed to set up desktop ACE for authenticated user (Status 0x%08lx)\n", Status);
443         goto Quit;
444     }
445 
446     /*
447      * Administrators can only enumerate window
448      * stations within a desktop.
449      */
450     Status = RtlAddAccessAllowedAceEx(ServiceDacl,
451                                       ACL_REVISION,
452                                       0,
453                                       WINSTA_ENUMERATE,
454                                       SeExports->SeAliasAdminsSid);
455     if (!NT_SUCCESS(Status))
456     {
457         ERR("IntCreateServiceSecurity(): Failed to set up window station ACE for admins (Status 0x%08lx)\n", Status);
458         goto Quit;
459     }
460 
461     /*
462      * Administrators have some share of power over
463      * the desktop object. They can enumerate desktops,
464      * write and read upon the object itself.
465      */
466     Status = RtlAddAccessAllowedAceEx(ServiceDacl,
467                                       ACL_REVISION,
468                                       INHERIT_ONLY_ACE | NO_PROPAGATE_INHERIT_ACE | OBJECT_INHERIT_ACE,
469                                       DESKTOP_ENUMERATE | DESKTOP_READOBJECTS | DESKTOP_WRITEOBJECTS,
470                                       SeExports->SeAliasAdminsSid);
471     if (!NT_SUCCESS(Status))
472     {
473         ERR("IntCreateServiceSecurity(): Failed to set up desktop ACE for admins (Status 0x%08lx)\n", Status);
474         goto Quit;
475     }
476 
477     /* Set the DACL to absolute SD */
478     Status = RtlSetDaclSecurityDescriptor(&AbsSD,
479                                           TRUE,
480                                           ServiceDacl,
481                                           FALSE);
482     if (!NT_SUCCESS(Status))
483     {
484         ERR("IntCreateServiceSecurity(): Failed to set up service DACL to absolute SD (Status 0x%08lx)\n", Status);
485         goto Quit;
486     }
487 
488     /* This descriptor is ownerless */
489     Status = RtlSetOwnerSecurityDescriptor(&AbsSD,
490                                            NULL,
491                                            FALSE);
492     if (!NT_SUCCESS(Status))
493     {
494         ERR("IntCreateServiceSecurity(): Failed to make the absolute SD as ownerless (Status 0x%08lx)\n", Status);
495         goto Quit;
496     }
497 
498     /* This descriptor has no primary group */
499     Status = RtlSetGroupSecurityDescriptor(&AbsSD,
500                                            NULL,
501                                            FALSE);
502     if (!NT_SUCCESS(Status))
503     {
504         ERR("IntCreateServiceSecurity(): Failed to make the absolute SD as having no primary group (Status 0x%08lx)\n", Status);
505         goto Quit;
506     }
507 
508     /*
509      * Determine how much size is needed to allocate
510      * memory space for our relative security descriptor.
511      */
512     Status = RtlAbsoluteToSelfRelativeSD(&AbsSD,
513                                          NULL,
514                                          &RelSDSize);
515     if (Status != STATUS_BUFFER_TOO_SMALL)
516     {
517         ERR("IntCreateServiceSecurity(): Unexpected status code, must be STATUS_BUFFER_TOO_SMALL (Status 0x%08lx)\n", Status);
518         goto Quit;
519     }
520 
521     /* Allocate memory for this */
522     RelSD = IntAllocateSecurityBuffer(RelSDSize);
523     if (!RelSD)
524     {
525         ERR("IntCreateServiceSecurity(): Failed to allocate memory pool for relative SD!\n");
526         Status = STATUS_NO_MEMORY;
527         goto Quit;
528     }
529 
530     /* Convert the absolute SD into a relative one now */
531     Status = RtlAbsoluteToSelfRelativeSD(&AbsSD,
532                                          RelSD,
533                                          &RelSDSize);
534     if (!NT_SUCCESS(Status))
535     {
536         ERR("IntCreateServiceSecurity(): Failed to convert absolute SD to a relative one (Status 0x%08lx)\n", Status);
537         goto Quit;
538     }
539 
540     /* All good, give the SD to the caller */
541     *ServiceSd = RelSD;
542 
543 Quit:
544     if (ServiceDacl)
545     {
546         IntFreeSecurityBuffer(ServiceDacl);
547     }
548 
549     if (TokenUser)
550     {
551         IntFreeSecurityBuffer(TokenUser);
552     }
553 
554     if (!NT_SUCCESS(Status))
555     {
556         if (RelSD)
557         {
558             IntFreeSecurityBuffer(RelSD);
559         }
560     }
561 
562     return Status;
563 }
564 
565 /* EOF */
566