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 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 = 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 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 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