xref: /reactos/win32ss/user/ntuser/security.c (revision 1734f297)
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 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
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
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
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
169 IntQueryUserSecurityIdentification(
170     _Out_ PTOKEN_USER *User)
171 {
172     NTSTATUS Status;
173     PTOKEN_USER UserToken;
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 (!NT_SUCCESS(Status) && 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 
216     /* Query the user now as we have plenty of space to hold it */
217     Status = ZwQueryInformationToken(Token,
218                                      TokenUser,
219                                      UserToken,
220                                      BufferLength,
221                                      &BufferLength);
222     if (!NT_SUCCESS(Status))
223     {
224         /* We failed, bail out */
225         ERR("IntQueryUserSecurityIdentification(): Failed to query token user (Status 0x%08lx)\n", Status);
226         IntFreeSecurityBuffer(UserToken);
227         ZwClose(Token);
228         return Status;
229     }
230 
231     /* All good, give the buffer to the caller and close the captured token */
232     *User = UserToken;
233     ZwClose(Token);
234 
235     return STATUS_SUCCESS;
236 }
237 
238 /**
239  * @brief
240  * Assigns a security descriptor to the desktop
241  * object during a desktop object parse procedure.
242  *
243  * @param[in] WinSta
244  * A pointer to a window station object, of which
245  * such object contains its own security descriptor
246  * that will be captured.
247  *
248  * @param[in] Desktop
249  * A pointer to a desktop object that is created
250  * during a parse procedure.
251  *
252  * @param[in] AccessState
253  * A pointer to an access state structure that
254  * describes the progress state of an access in
255  * action.
256  *
257  * @return
258  * Returns STATUS_SUCCESS if the function has successfully
259  * assigned new security descriptor to the desktop object.
260  * A NTSTATUS failure code is returned otherwise.
261  */
262 NTSTATUS
263 NTAPI
264 IntAssignDesktopSecurityOnParse(
265     _In_ PWINSTATION_OBJECT WinSta,
266     _In_ PDESKTOP Desktop,
267     _In_ PACCESS_STATE AccessState)
268 {
269     NTSTATUS Status;
270     PSECURITY_DESCRIPTOR CapturedDescriptor;
271     BOOLEAN MemoryAllocated;
272 
273     /*
274      * Capture the security descriptor from
275      * the window station. The window station
276      * in question has a descriptor that is
277      * inheritable and contains desktop access
278      * rights as well.
279      */
280     Status = ObGetObjectSecurity(WinSta,
281                                  &CapturedDescriptor,
282                                  &MemoryAllocated);
283     if (!NT_SUCCESS(Status))
284     {
285         ERR("IntAssignDesktopSecurityOnParse(): Failed to capture the security descriptor from window station (Status 0x%08lx)\n", Status);
286         return Status;
287     }
288 
289     /* Assign new security to the desktop */
290     Status = ObAssignSecurity(AccessState,
291                               CapturedDescriptor,
292                               Desktop,
293                               ExDesktopObjectType);
294     if (!NT_SUCCESS(Status))
295     {
296         ERR("IntAssignDesktopSecurityOnParse(): Failed to assign security information to the desktop object (Status 0x%08lx)\n", Status);
297     }
298 
299     /* Release the descriptor that we have captured */
300     ObReleaseObjectSecurity(CapturedDescriptor, MemoryAllocated);
301     return Status;
302 }
303 
304 /**
305  * @brief
306  * Creates a security descriptor for the service.
307  *
308  * @param[out] ServiceSd
309  * A pointer to a newly allocated and created security
310  * descriptor for the service.
311  *
312  * @return
313  * Returns STATUS_SUCCESS if the function has successfully
314  * queried created the security descriptor. STATUS_NO_MEMORY
315  * is returned if memory allocation for security buffers because
316  * of a lack of needed pages to reserve for such allocation. A
317  * failure NTSTATUS code is returned otherwise.
318  */
319 NTSTATUS
320 NTAPI
321 IntCreateServiceSecurity(
322     _Out_ PSECURITY_DESCRIPTOR *ServiceSd)
323 {
324     NTSTATUS Status;
325     PACL ServiceDacl;
326     ULONG DaclSize;
327     ULONG RelSDSize;
328     SECURITY_DESCRIPTOR AbsSD;
329     PSECURITY_DESCRIPTOR RelSD;
330     PTOKEN_USER TokenUser;
331 
332     /* Initialize our local variables */
333     RelSDSize = 0;
334     TokenUser = NULL;
335     RelSD = NULL;
336     ServiceDacl = NULL;
337 
338     /* Query the logged in user of the current security context (aka token) */
339     Status = IntQueryUserSecurityIdentification(&TokenUser);
340     if (!TokenUser)
341     {
342         ERR("IntCreateServiceSecurity(): Failed to query the token user (Status 0x%08lx)\n", Status);
343         return Status;
344     }
345 
346     /* Initialize the absolute security descriptor */
347     Status = RtlCreateSecurityDescriptor(&AbsSD, SECURITY_DESCRIPTOR_REVISION);
348     if (!NT_SUCCESS(Status))
349     {
350         ERR("IntCreateServiceSecurity(): Failed to initialize absolute SD (Status 0x%08lx)\n", Status);
351         goto Quit;
352     }
353 
354     /*
355      * Build up the size of access control
356      * list (the DACL) necessary to initialize
357      * our ACL. The first two entry members
358      * of ACL field are the authenticated user
359      * that is associated with the security
360      * context of the token. Then here come
361      * the last two entries which are admins.
362      * Why the ACL contains two ACEs of the
363      * same SID is because of service access
364      * rights and ACE inheritance.
365      *
366      * A service is composed of a default
367      * desktop and window station upon
368      * booting the system. On Windows connection
369      * to such service is being made if no
370      * default window station and desktop handles
371      * were created before. The desktop and winsta
372      * objects grant access on a separate type basis.
373      * The user is granted full access to the window
374      * station first and then full access to the desktop.
375      * After that admins are granted specific rights
376      * separately, just like the user. Ultimately the
377      * ACEs that handle desktop rights management are
378      * inherited to the default desktop object so
379      * that there's no need to have a separate security
380      * descriptor for the desktop object alone.
381      */
382     DaclSize = sizeof(ACL) +
383                sizeof(ACCESS_ALLOWED_ACE) + RtlLengthSid(TokenUser->User.Sid) +
384                sizeof(ACCESS_ALLOWED_ACE) + RtlLengthSid(TokenUser->User.Sid) +
385                sizeof(ACCESS_ALLOWED_ACE) + RtlLengthSid(SeExports->SeAliasAdminsSid) +
386                sizeof(ACCESS_ALLOWED_ACE) + RtlLengthSid(SeExports->SeAliasAdminsSid);
387 
388     /* Allocate memory for service DACL */
389     ServiceDacl = IntAllocateSecurityBuffer(DaclSize);
390     if (!ServiceDacl)
391     {
392         ERR("IntCreateServiceSecurity(): Failed to allocate memory for service DACL!\n");
393         Status = STATUS_NO_MEMORY;
394         goto Quit;
395     }
396 
397     /* Now create the DACL */
398     Status = RtlCreateAcl(ServiceDacl,
399                           DaclSize,
400                           ACL_REVISION);
401     if (!NT_SUCCESS(Status))
402     {
403         ERR("IntCreateServiceSecurity(): Failed to create service DACL (Status 0x%08lx)\n", Status);
404         goto Quit;
405     }
406 
407     /*
408      * The authenticated user is the ultimate and absolute
409      * king in charge of the created (or opened, whatever that is)
410      * window station object.
411      */
412     Status = RtlAddAccessAllowedAceEx(ServiceDacl,
413                                       ACL_REVISION,
414                                       0,
415                                       WINSTA_ACCESS_ALL,
416                                       TokenUser->User.Sid);
417     if (!NT_SUCCESS(Status))
418     {
419         ERR("IntCreateServiceSecurity(): Failed to set up window station ACE for authenticated user (Status 0x%08lx)\n", Status);
420         goto Quit;
421     }
422 
423     /*
424      * The authenticated user also has the ultimate power
425      * over the desktop object as well. This ACE cannot
426      * be propagated but inherited. See the comment
427      * above regarding ACL size for further explanation.
428      */
429     Status = RtlAddAccessAllowedAceEx(ServiceDacl,
430                                       ACL_REVISION,
431                                       INHERIT_ONLY_ACE | NO_PROPAGATE_INHERIT_ACE | OBJECT_INHERIT_ACE,
432                                       DESKTOP_ALL_ACCESS,
433                                       TokenUser->User.Sid);
434     if (!NT_SUCCESS(Status))
435     {
436         ERR("IntCreateServiceSecurity(): Failed to set up desktop ACE for authenticated user (Status 0x%08lx)\n", Status);
437         goto Quit;
438     }
439 
440     /*
441      * Administrators can only enumerate window
442      * stations within a desktop.
443      */
444     Status = RtlAddAccessAllowedAceEx(ServiceDacl,
445                                       ACL_REVISION,
446                                       0,
447                                       WINSTA_ENUMERATE,
448                                       SeExports->SeAliasAdminsSid);
449     if (!NT_SUCCESS(Status))
450     {
451         ERR("IntCreateServiceSecurity(): Failed to set up window station ACE for admins (Status 0x%08lx)\n", Status);
452         goto Quit;
453     }
454 
455     /*
456      * Administrators have some share of power over
457      * the desktop object. They can enumerate desktops,
458      * write and read upon the object itself.
459      */
460     Status = RtlAddAccessAllowedAceEx(ServiceDacl,
461                                       ACL_REVISION,
462                                       INHERIT_ONLY_ACE | NO_PROPAGATE_INHERIT_ACE | OBJECT_INHERIT_ACE,
463                                       DESKTOP_ENUMERATE | DESKTOP_READOBJECTS | DESKTOP_WRITEOBJECTS,
464                                       SeExports->SeAliasAdminsSid);
465     if (!NT_SUCCESS(Status))
466     {
467         ERR("IntCreateServiceSecurity(): Failed to set up desktop ACE for admins (Status 0x%08lx)\n", Status);
468         goto Quit;
469     }
470 
471     /* Set the DACL to absolute SD */
472     Status = RtlSetDaclSecurityDescriptor(&AbsSD,
473                                           TRUE,
474                                           ServiceDacl,
475                                           FALSE);
476     if (!NT_SUCCESS(Status))
477     {
478         ERR("IntCreateServiceSecurity(): Failed to set up service DACL to absolute SD (Status 0x%08lx)\n", Status);
479         goto Quit;
480     }
481 
482     /* This descriptor is ownerless */
483     Status = RtlSetOwnerSecurityDescriptor(&AbsSD,
484                                            NULL,
485                                            FALSE);
486     if (!NT_SUCCESS(Status))
487     {
488         ERR("IntCreateServiceSecurity(): Failed to make the absolute SD as ownerless (Status 0x%08lx)\n", Status);
489         goto Quit;
490     }
491 
492     /* This descriptor has no primary group */
493     Status = RtlSetGroupSecurityDescriptor(&AbsSD,
494                                            NULL,
495                                            FALSE);
496     if (!NT_SUCCESS(Status))
497     {
498         ERR("IntCreateServiceSecurity(): Failed to make the absolute SD as having no primary group (Status 0x%08lx)\n", Status);
499         goto Quit;
500     }
501 
502     /*
503      * Determine how much size is needed to allocate
504      * memory space for our relative security descriptor.
505      */
506     Status = RtlAbsoluteToSelfRelativeSD(&AbsSD,
507                                          NULL,
508                                          &RelSDSize);
509     if (Status != STATUS_BUFFER_TOO_SMALL)
510     {
511         ERR("IntCreateServiceSecurity(): Unexpected status code, must be STATUS_BUFFER_TOO_SMALL (Status 0x%08lx)\n", Status);
512         goto Quit;
513     }
514 
515     /* Allocate memory for this */
516     RelSD = IntAllocateSecurityBuffer(RelSDSize);
517     if (!RelSD)
518     {
519         ERR("IntCreateServiceSecurity(): Failed to allocate memory pool for relative SD!\n");
520         Status = STATUS_NO_MEMORY;
521         goto Quit;
522     }
523 
524     /* Convert the absolute SD into a relative one now */
525     Status = RtlAbsoluteToSelfRelativeSD(&AbsSD,
526                                          RelSD,
527                                          &RelSDSize);
528     if (!NT_SUCCESS(Status))
529     {
530         ERR("IntCreateServiceSecurity(): Failed to convert absolute SD to a relative one (Status 0x%08lx)\n", Status);
531         goto Quit;
532     }
533 
534     /* All good, give the SD to the caller */
535     *ServiceSd = RelSD;
536 
537 Quit:
538     if (ServiceDacl)
539     {
540         IntFreeSecurityBuffer(ServiceDacl);
541     }
542 
543     if (TokenUser)
544     {
545         IntFreeSecurityBuffer(TokenUser);
546     }
547 
548     if (!NT_SUCCESS(Status))
549     {
550         if (RelSD)
551         {
552             IntFreeSecurityBuffer(RelSD);
553         }
554     }
555 
556     return Status;
557 }
558 
559 /* EOF */
560