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