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