1 /* 2 * PROJECT: ReactOS Kernel 3 * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) 4 * PURPOSE: Security access token implementation base support routines 5 * COPYRIGHT: Copyright David Welch <welch@cwcom.net> 6 * Copyright 2021-2023 George Bișoc <george.bisoc@reactos.org> 7 */ 8 9 /* INCLUDES *******************************************************************/ 10 11 #include <ntoskrnl.h> 12 #define NDEBUG 13 #include <debug.h> 14 15 /* GLOBALS ********************************************************************/ 16 17 POBJECT_TYPE SeTokenObjectType = NULL; 18 19 TOKEN_SOURCE SeSystemTokenSource = {"*SYSTEM*", {0}}; 20 LUID SeSystemAuthenticationId = SYSTEM_LUID; 21 LUID SeAnonymousAuthenticationId = ANONYMOUS_LOGON_LUID; 22 23 static GENERIC_MAPPING SepTokenMapping = { 24 TOKEN_READ, 25 TOKEN_WRITE, 26 TOKEN_EXECUTE, 27 TOKEN_ALL_ACCESS 28 }; 29 30 /* PRIVATE FUNCTIONS *****************************************************************/ 31 32 /** 33 * @brief 34 * Creates a lock for the token. 35 * 36 * @param[in,out] Token 37 * A token which lock has to be created. 38 * 39 * @return 40 * STATUS_SUCCESS if the pool allocation and resource initialisation have 41 * completed successfully, otherwise STATUS_INSUFFICIENT_RESOURCES on a 42 * pool allocation failure. 43 */ 44 NTSTATUS 45 SepCreateTokenLock( 46 _Inout_ PTOKEN Token) 47 { 48 PAGED_CODE(); 49 50 Token->TokenLock = ExAllocatePoolWithTag(NonPagedPool, 51 sizeof(ERESOURCE), 52 TAG_SE_TOKEN_LOCK); 53 if (Token->TokenLock == NULL) 54 { 55 DPRINT1("SepCreateTokenLock(): Failed to allocate memory!\n"); 56 return STATUS_INSUFFICIENT_RESOURCES; 57 } 58 59 ExInitializeResourceLite(Token->TokenLock); 60 return STATUS_SUCCESS; 61 } 62 63 /** 64 * @brief 65 * Deletes a lock of a token. 66 * 67 * @param[in,out] Token 68 * A token which contains the lock. 69 * 70 * @return 71 * Nothing. 72 */ 73 VOID 74 SepDeleteTokenLock( 75 _Inout_ PTOKEN Token) 76 { 77 PAGED_CODE(); 78 79 ExDeleteResourceLite(Token->TokenLock); 80 ExFreePoolWithTag(Token->TokenLock, TAG_SE_TOKEN_LOCK); 81 } 82 83 /** 84 * @brief 85 * Compares the elements of SID arrays provided by tokens. 86 * The elements that are being compared for equality are 87 * the SIDs and their attributes. 88 * 89 * @param[in] SidArrayToken1 90 * SID array from the first token. 91 * 92 * @param[in] CountSidArray1 93 * SID count array from the first token. 94 * 95 * @param[in] SidArrayToken2 96 * SID array from the second token. 97 * 98 * @param[in] CountSidArray2 99 * SID count array from the second token. 100 * 101 * @return 102 * Returns TRUE if the elements match from either arrays, 103 * FALSE otherwise. 104 */ 105 static 106 BOOLEAN 107 SepCompareSidAndAttributesFromTokens( 108 _In_ PSID_AND_ATTRIBUTES SidArrayToken1, 109 _In_ ULONG CountSidArray1, 110 _In_ PSID_AND_ATTRIBUTES SidArrayToken2, 111 _In_ ULONG CountSidArray2) 112 { 113 ULONG FirstCount, SecondCount; 114 PSID_AND_ATTRIBUTES FirstSidArray, SecondSidArray; 115 PAGED_CODE(); 116 117 /* Bail out if index counters provided are not equal */ 118 if (CountSidArray1 != CountSidArray2) 119 { 120 DPRINT("SepCompareSidAndAttributesFromTokens(): Index counters are not the same!\n"); 121 return FALSE; 122 } 123 124 /* Loop over the SID arrays and compare them */ 125 for (FirstCount = 0; FirstCount < CountSidArray1; FirstCount++) 126 { 127 for (SecondCount = 0; SecondCount < CountSidArray2; SecondCount++) 128 { 129 FirstSidArray = &SidArrayToken1[FirstCount]; 130 SecondSidArray = &SidArrayToken2[SecondCount]; 131 132 if (RtlEqualSid(FirstSidArray->Sid, SecondSidArray->Sid) && 133 FirstSidArray->Attributes == SecondSidArray->Attributes) 134 { 135 break; 136 } 137 } 138 139 /* We've exhausted the array of the second token without finding this one */ 140 if (SecondCount == CountSidArray2) 141 { 142 DPRINT("SepCompareSidAndAttributesFromTokens(): No matching elements could be found in either token!\n"); 143 return FALSE; 144 } 145 } 146 147 return TRUE; 148 } 149 150 /** 151 * @brief 152 * Compares the elements of privilege arrays provided by tokens. 153 * The elements that are being compared for equality are 154 * the privileges and their attributes. 155 * 156 * @param[in] PrivArrayToken1 157 * Privilege array from the first token. 158 * 159 * @param[in] CountPrivArray1 160 * Privilege count array from the first token. 161 * 162 * @param[in] PrivArrayToken2 163 * Privilege array from the second token. 164 * 165 * @param[in] CountPrivArray2 166 * Privilege count array from the second token. 167 * 168 * @return 169 * Returns TRUE if the elements match from either arrays, 170 * FALSE otherwise. 171 */ 172 static 173 BOOLEAN 174 SepComparePrivilegeAndAttributesFromTokens( 175 _In_ PLUID_AND_ATTRIBUTES PrivArrayToken1, 176 _In_ ULONG CountPrivArray1, 177 _In_ PLUID_AND_ATTRIBUTES PrivArrayToken2, 178 _In_ ULONG CountPrivArray2) 179 { 180 ULONG FirstCount, SecondCount; 181 PLUID_AND_ATTRIBUTES FirstPrivArray, SecondPrivArray; 182 PAGED_CODE(); 183 184 /* Bail out if index counters provided are not equal */ 185 if (CountPrivArray1 != CountPrivArray2) 186 { 187 DPRINT("SepComparePrivilegeAndAttributesFromTokens(): Index counters are not the same!\n"); 188 return FALSE; 189 } 190 191 /* Loop over the privilege arrays and compare them */ 192 for (FirstCount = 0; FirstCount < CountPrivArray1; FirstCount++) 193 { 194 for (SecondCount = 0; SecondCount < CountPrivArray2; SecondCount++) 195 { 196 FirstPrivArray = &PrivArrayToken1[FirstCount]; 197 SecondPrivArray = &PrivArrayToken2[SecondCount]; 198 199 if (RtlEqualLuid(&FirstPrivArray->Luid, &SecondPrivArray->Luid) && 200 FirstPrivArray->Attributes == SecondPrivArray->Attributes) 201 { 202 break; 203 } 204 } 205 206 /* We've exhausted the array of the second token without finding this one */ 207 if (SecondCount == CountPrivArray2) 208 { 209 DPRINT("SepComparePrivilegeAndAttributesFromTokens(): No matching elements could be found in either token!\n"); 210 return FALSE; 211 } 212 } 213 214 return TRUE; 215 } 216 217 /** 218 * @brief 219 * Compares tokens if they're equal based on all the following properties. If all 220 * of the said conditions are met then the tokens are deemed as equal. 221 * 222 * - Every SID that is present in either token is also present in the other one. 223 * - Both or none of the tokens are restricted. 224 * - If both tokens are restricted, every SID that is restricted in either token is 225 * also restricted in the other one. 226 * - Every privilege present in either token is also present in the other one. 227 * 228 * @param[in] FirstToken 229 * The first token. 230 * 231 * @param[in] SecondToken 232 * The second token. 233 * 234 * @param[out] Equal 235 * The retrieved value which determines if the tokens are 236 * equal or not. 237 * 238 * @return 239 * Returns STATUS_SUCCESS. 240 */ 241 static 242 NTSTATUS 243 SepCompareTokens( 244 _In_ PTOKEN FirstToken, 245 _In_ PTOKEN SecondToken, 246 _Out_ PBOOLEAN Equal) 247 { 248 BOOLEAN Restricted, IsEqual = FALSE; 249 PAGED_CODE(); 250 251 ASSERT(FirstToken != SecondToken); 252 253 /* Lock the tokens */ 254 SepAcquireTokenLockShared(FirstToken); 255 SepAcquireTokenLockShared(SecondToken); 256 257 /* Check if every SID that is present in either token is also present in the other one */ 258 if (!SepCompareSidAndAttributesFromTokens(FirstToken->UserAndGroups, 259 FirstToken->UserAndGroupCount, 260 SecondToken->UserAndGroups, 261 SecondToken->UserAndGroupCount)) 262 { 263 goto Quit; 264 } 265 266 /* Is one token restricted but the other isn't? */ 267 Restricted = SeTokenIsRestricted(FirstToken); 268 if (Restricted != SeTokenIsRestricted(SecondToken)) 269 { 270 /* If that's the case then bail out */ 271 goto Quit; 272 } 273 274 /* 275 * If both tokens are restricted check if every SID 276 * that is restricted in either token is also restricted 277 * in the other one. 278 */ 279 if (Restricted) 280 { 281 if (!SepCompareSidAndAttributesFromTokens(FirstToken->RestrictedSids, 282 FirstToken->RestrictedSidCount, 283 SecondToken->RestrictedSids, 284 SecondToken->RestrictedSidCount)) 285 { 286 goto Quit; 287 } 288 } 289 290 /* Check if every privilege present in either token is also present in the other one */ 291 if (!SepComparePrivilegeAndAttributesFromTokens(FirstToken->Privileges, 292 FirstToken->PrivilegeCount, 293 SecondToken->Privileges, 294 SecondToken->PrivilegeCount)) 295 { 296 goto Quit; 297 } 298 299 /* If we're here then the tokens are equal */ 300 IsEqual = TRUE; 301 DPRINT("SepCompareTokens(): Tokens are equal!\n"); 302 303 Quit: 304 /* Unlock the tokens */ 305 SepReleaseTokenLock(SecondToken); 306 SepReleaseTokenLock(FirstToken); 307 308 *Equal = IsEqual; 309 return STATUS_SUCCESS; 310 } 311 312 /** 313 * @brief 314 * Private function that impersonates the system's anonymous logon token. 315 * The major bulk of the impersonation procedure is done here. 316 * 317 * @param[in] Thread 318 * The executive thread object that is to impersonate the client. 319 * 320 * @param[in] PreviousMode 321 * The access processor mode, indicating if the call is executed 322 * in kernel or user mode. 323 * 324 * @return 325 * Returns STATUS_SUCCESS if the impersonation has succeeded. 326 * STATUS_UNSUCCESSFUL is returned if the primary token couldn't be 327 * obtained from the current process to perform additional tasks. 328 * STATUS_ACCESS_DENIED is returned if the process' primary token is 329 * restricted, which for this matter we cannot impersonate onto a 330 * restricted process. Otherwise a failure NTSTATUS code is returned. 331 */ 332 static 333 NTSTATUS 334 SepImpersonateAnonymousToken( 335 _In_ PETHREAD Thread, 336 _In_ KPROCESSOR_MODE PreviousMode) 337 { 338 NTSTATUS Status; 339 PTOKEN TokenToImpersonate, ProcessToken; 340 ULONG IncludeEveryoneValueData; 341 PAGED_CODE(); 342 343 /* 344 * We must check first which kind of token 345 * shall we assign for the thread to impersonate, 346 * the one with Everyone Group SID or the other 347 * without. Invoke the registry helper to 348 * return the data value for us. 349 */ 350 Status = SepRegQueryHelper(L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\Control\\Lsa", 351 L"EveryoneIncludesAnonymous", 352 REG_DWORD, 353 sizeof(IncludeEveryoneValueData), 354 &IncludeEveryoneValueData); 355 if (!NT_SUCCESS(Status)) 356 { 357 DPRINT1("SepRegQueryHelper(): Failed to query the registry value (Status 0x%lx)\n", Status); 358 return Status; 359 } 360 361 if (IncludeEveryoneValueData == 0) 362 { 363 DPRINT("SepImpersonateAnonymousToken(): Assigning the token not including the Everyone Group SID...\n"); 364 TokenToImpersonate = SeAnonymousLogonTokenNoEveryone; 365 } 366 else 367 { 368 DPRINT("SepImpersonateAnonymousToken(): Assigning the token including the Everyone Group SID...\n"); 369 TokenToImpersonate = SeAnonymousLogonToken; 370 } 371 372 /* 373 * Tell the object manager that we're going to use this token 374 * object now by incrementing the reference count. 375 */ 376 Status = ObReferenceObjectByPointer(TokenToImpersonate, 377 TOKEN_IMPERSONATE, 378 SeTokenObjectType, 379 PreviousMode); 380 if (!NT_SUCCESS(Status)) 381 { 382 DPRINT1("SepImpersonateAnonymousToken(): Couldn't be able to use the token, bail out...\n"); 383 return Status; 384 } 385 386 /* 387 * Reference the primary token of the current process that the anonymous 388 * logon token impersonation procedure is being performed. We'll be going 389 * to use the process' token to figure out if the process is actually 390 * restricted or not. 391 */ 392 ProcessToken = PsReferencePrimaryToken(PsGetCurrentProcess()); 393 if (!ProcessToken) 394 { 395 DPRINT1("SepImpersonateAnonymousToken(): Couldn't be able to get the process' primary token, bail out...\n"); 396 ObDereferenceObject(TokenToImpersonate); 397 return STATUS_UNSUCCESSFUL; 398 } 399 400 /* Now, is the token from the current process restricted? */ 401 if (SeTokenIsRestricted(ProcessToken)) 402 { 403 DPRINT1("SepImpersonateAnonymousToken(): The process is restricted, can't do anything. Bail out...\n"); 404 PsDereferencePrimaryToken(ProcessToken); 405 ObDereferenceObject(TokenToImpersonate); 406 return STATUS_ACCESS_DENIED; 407 } 408 409 /* 410 * Finally it's time to impersonate! But first, fast dereference the 411 * process' primary token as we no longer need it. 412 */ 413 ObFastDereferenceObject(&PsGetCurrentProcess()->Token, ProcessToken); 414 Status = PsImpersonateClient(Thread, TokenToImpersonate, TRUE, FALSE, SecurityImpersonation); 415 if (!NT_SUCCESS(Status)) 416 { 417 DPRINT1("SepImpersonateAnonymousToken(): Failed to impersonate, bail out...\n"); 418 ObDereferenceObject(TokenToImpersonate); 419 return Status; 420 } 421 422 return Status; 423 } 424 425 /** 426 * @brief 427 * Updates the token's flags based upon the privilege that the token 428 * has been granted. The flag can either be taken out or given to the token 429 * if the attributes of the specified privilege is enabled or not. 430 * 431 * @param[in,out] Token 432 * The token where the flags are to be changed. 433 * 434 * @param[in] Index 435 * The index count which represents the total sum of privileges. The count in question 436 * MUST NOT exceed the expected privileges count of the token. 437 * 438 * @return 439 * Nothing. 440 */ 441 VOID 442 SepUpdateSinglePrivilegeFlagToken( 443 _Inout_ PTOKEN Token, 444 _In_ ULONG Index) 445 { 446 ULONG TokenFlag; 447 ASSERT(Index < Token->PrivilegeCount); 448 449 /* The high part of all values we are interested in is 0 */ 450 if (Token->Privileges[Index].Luid.HighPart != 0) 451 { 452 return; 453 } 454 455 /* Check for certain privileges to update flags */ 456 if (Token->Privileges[Index].Luid.LowPart == SE_CHANGE_NOTIFY_PRIVILEGE) 457 { 458 TokenFlag = TOKEN_HAS_TRAVERSE_PRIVILEGE; 459 } 460 else if (Token->Privileges[Index].Luid.LowPart == SE_BACKUP_PRIVILEGE) 461 { 462 TokenFlag = TOKEN_HAS_BACKUP_PRIVILEGE; 463 } 464 else if (Token->Privileges[Index].Luid.LowPart == SE_RESTORE_PRIVILEGE) 465 { 466 TokenFlag = TOKEN_HAS_RESTORE_PRIVILEGE; 467 } 468 else if (Token->Privileges[Index].Luid.LowPart == SE_IMPERSONATE_PRIVILEGE) 469 { 470 TokenFlag = TOKEN_HAS_IMPERSONATE_PRIVILEGE; 471 } 472 else 473 { 474 /* Nothing to do */ 475 return; 476 } 477 478 /* Check if the specified privilege is enabled */ 479 if (Token->Privileges[Index].Attributes & SE_PRIVILEGE_ENABLED) 480 { 481 /* It is enabled, so set the flag */ 482 Token->TokenFlags |= TokenFlag; 483 } 484 else 485 { 486 /* Is is disabled, so remove the flag */ 487 Token->TokenFlags &= ~TokenFlag; 488 } 489 } 490 491 /** 492 * @brief 493 * Checks if a token belongs to the main user, being the owner. 494 * 495 * @param[in] _Token 496 * A valid token object. 497 * 498 * @param[in] SecurityDescriptor 499 * A security descriptor where the owner is to be found. 500 * 501 * @param[in] TokenLocked 502 * If set to TRUE, the token has been already locked and there's 503 * no need to lock it again. Otherwise the function will acquire 504 * the lock. 505 * 506 * @return 507 * Returns TRUE if the token belongs to a owner, FALSE otherwise. 508 */ 509 BOOLEAN 510 NTAPI 511 SepTokenIsOwner( 512 _In_ PACCESS_TOKEN _Token, 513 _In_ PSECURITY_DESCRIPTOR SecurityDescriptor, 514 _In_ BOOLEAN TokenLocked) 515 { 516 PSID Sid; 517 BOOLEAN Result; 518 PTOKEN Token = _Token; 519 520 /* Get the owner SID */ 521 Sid = SepGetOwnerFromDescriptor(SecurityDescriptor); 522 ASSERT(Sid != NULL); 523 524 /* Lock the token if needed */ 525 if (!TokenLocked) SepAcquireTokenLockShared(Token); 526 527 /* Check if the owner SID is found, handling restricted case as well */ 528 Result = SepSidInToken(Token, Sid); 529 if ((Result) && (Token->TokenFlags & TOKEN_IS_RESTRICTED)) 530 { 531 Result = SepSidInTokenEx(Token, NULL, Sid, FALSE, TRUE); 532 } 533 534 /* Release the lock if we had acquired it */ 535 if (!TokenLocked) SepReleaseTokenLock(Token); 536 537 /* Return the result */ 538 return Result; 539 } 540 541 /** 542 * @brief 543 * Updates the token's flags based upon the privilege that the token 544 * has been granted. The function uses the private helper, SepUpdateSinglePrivilegeFlagToken, 545 * in order to update the flags of a token. 546 * 547 * @param[in,out] Token 548 * The token where the flags are to be changed. 549 * 550 * @return 551 * Nothing. 552 */ 553 VOID 554 SepUpdatePrivilegeFlagsToken( 555 _Inout_ PTOKEN Token) 556 { 557 ULONG i; 558 559 /* Loop all privileges */ 560 for (i = 0; i < Token->PrivilegeCount; i++) 561 { 562 /* Updates the flags for this privilege */ 563 SepUpdateSinglePrivilegeFlagToken(Token, i); 564 } 565 } 566 567 /** 568 * @brief 569 * Removes a privilege from the token. 570 * 571 * @param[in,out] Token 572 * The token where the privilege is to be removed. 573 * 574 * @param[in] Index 575 * The index count which represents the number position of the privilege 576 * we want to remove. 577 * 578 * @return 579 * Nothing. 580 */ 581 VOID 582 SepRemovePrivilegeToken( 583 _Inout_ PTOKEN Token, 584 _In_ ULONG Index) 585 { 586 ULONG MoveCount; 587 ASSERT(Index < Token->PrivilegeCount); 588 589 /* Calculate the number of trailing privileges */ 590 MoveCount = Token->PrivilegeCount - Index - 1; 591 if (MoveCount != 0) 592 { 593 /* Move them one location ahead */ 594 RtlMoveMemory(&Token->Privileges[Index], 595 &Token->Privileges[Index + 1], 596 MoveCount * sizeof(LUID_AND_ATTRIBUTES)); 597 } 598 599 /* Update privilege count */ 600 Token->PrivilegeCount--; 601 } 602 603 /** 604 * @brief 605 * Removes a group from the token. 606 * 607 * @param[in,out] Token 608 * The token where the group is to be removed. 609 * 610 * @param[in] Index 611 * The index count which represents the number position of the group 612 * we want to remove. 613 * 614 * @return 615 * Nothing. 616 */ 617 VOID 618 SepRemoveUserGroupToken( 619 _Inout_ PTOKEN Token, 620 _In_ ULONG Index) 621 { 622 ULONG MoveCount; 623 ASSERT(Index < Token->UserAndGroupCount); 624 625 /* Calculate the number of trailing groups */ 626 MoveCount = Token->UserAndGroupCount - Index - 1; 627 if (MoveCount != 0) 628 { 629 /* Time to remove the group by moving one location ahead */ 630 RtlMoveMemory(&Token->UserAndGroups[Index], 631 &Token->UserAndGroups[Index + 1], 632 MoveCount * sizeof(SID_AND_ATTRIBUTES)); 633 } 634 635 /* Remove one group count */ 636 Token->UserAndGroupCount--; 637 } 638 639 /** 640 * @brief 641 * Computes the exact available dynamic area of an access 642 * token whilst querying token statistics. 643 * 644 * @param[in] DynamicCharged 645 * The current charged dynamic area of an access token. 646 * This must not be 0! 647 * 648 * @param[in] PrimaryGroup 649 * A pointer to a primary group SID. 650 * 651 * @param[in] DefaultDacl 652 * If provided, this pointer points to a default DACL of an 653 * access token. 654 * 655 * @return 656 * Returns the calculated available dynamic area. 657 */ 658 ULONG 659 SepComputeAvailableDynamicSpace( 660 _In_ ULONG DynamicCharged, 661 _In_ PSID PrimaryGroup, 662 _In_opt_ PACL DefaultDacl) 663 { 664 ULONG DynamicAvailable; 665 666 PAGED_CODE(); 667 668 /* A token's dynamic area is always charged */ 669 ASSERT(DynamicCharged != 0); 670 671 /* 672 * Take into account the default DACL if 673 * the token has one. Otherwise the occupied 674 * space is just the present primary group. 675 */ 676 DynamicAvailable = DynamicCharged - RtlLengthSid(PrimaryGroup); 677 if (DefaultDacl) 678 { 679 DynamicAvailable -= DefaultDacl->AclSize; 680 } 681 682 return DynamicAvailable; 683 } 684 685 /** 686 * @brief 687 * Re-builds the dynamic part area of an access token 688 * during an a default DACL or primary group replacement 689 * within the said token if the said dynamic area can't 690 * hold the new security content. 691 * 692 * @param[in] AccessToken 693 * A pointer to an access token where its dynamic part 694 * is to be re-built and expanded based upon the new 695 * dynamic part size provided by the caller. Dynamic 696 * part expansion is not always guaranteed. See Remarks 697 * for further information. 698 * 699 * @param[in] NewDynamicPartSize 700 * The new dynamic part size. 701 * 702 * @return 703 * Returns STATUS_SUCCESS if the function has completed its 704 * operations successfully. STATUS_INSUFFICIENT_RESOURCES 705 * is returned if the new dynamic part could not be allocated. 706 * 707 * @remarks 708 * STATUS_SUCCESS does not indicate if the function has re-built 709 * the dynamic part of a token. If the current dynamic area size 710 * suffices the new dynamic area length provided by the caller 711 * then the dynamic area can hold the new security content buffer 712 * so dynamic part expansion is not necessary. 713 */ 714 NTSTATUS 715 SepRebuildDynamicPartOfToken( 716 _Inout_ PTOKEN AccessToken, 717 _In_ ULONG NewDynamicPartSize) 718 { 719 PVOID NewDynamicPart; 720 PVOID PreviousDynamicPart; 721 ULONG CurrentDynamicLength; 722 723 PAGED_CODE(); 724 725 /* Sanity checks */ 726 ASSERT(AccessToken); 727 ASSERT(NewDynamicPartSize != 0); 728 729 /* 730 * Compute the exact length of the available 731 * dynamic part of the access token. 732 */ 733 CurrentDynamicLength = AccessToken->DynamicAvailable + RtlLengthSid(AccessToken->PrimaryGroup); 734 if (AccessToken->DefaultDacl) 735 { 736 CurrentDynamicLength += AccessToken->DefaultDacl->AclSize; 737 } 738 739 /* 740 * Figure out if the current dynamic part is too small 741 * to fit new contents inside the said dynamic part. 742 * Rebuild the dynamic area and expand it if necessary. 743 */ 744 if (CurrentDynamicLength < NewDynamicPartSize) 745 { 746 NewDynamicPart = ExAllocatePoolWithTag(PagedPool, 747 NewDynamicPartSize, 748 TAG_TOKEN_DYNAMIC); 749 if (NewDynamicPart == NULL) 750 { 751 DPRINT1("SepRebuildDynamicPartOfToken(): Insufficient resources to allocate new dynamic part!\n"); 752 return STATUS_INSUFFICIENT_RESOURCES; 753 } 754 755 /* Copy the existing dynamic part */ 756 PreviousDynamicPart = AccessToken->DynamicPart; 757 RtlCopyMemory(NewDynamicPart, PreviousDynamicPart, CurrentDynamicLength); 758 759 /* Update the available dynamic area and assign new dynamic */ 760 AccessToken->DynamicAvailable += NewDynamicPartSize - CurrentDynamicLength; 761 AccessToken->DynamicPart = NewDynamicPart; 762 763 /* Move the contents (primary group and default DACL) addresses as well */ 764 AccessToken->PrimaryGroup = (PSID)((ULONG_PTR)AccessToken->DynamicPart + 765 ((ULONG_PTR)AccessToken->PrimaryGroup - (ULONG_PTR)PreviousDynamicPart)); 766 if (AccessToken->DefaultDacl != NULL) 767 { 768 AccessToken->DefaultDacl = (PACL)((ULONG_PTR)AccessToken->DynamicPart + 769 ((ULONG_PTR)AccessToken->DefaultDacl - (ULONG_PTR)PreviousDynamicPart)); 770 } 771 772 /* And discard the previous dynamic part */ 773 DPRINT("SepRebuildDynamicPartOfToken(): The dynamic part has been re-built with success!\n"); 774 ExFreePoolWithTag(PreviousDynamicPart, TAG_TOKEN_DYNAMIC); 775 } 776 777 return STATUS_SUCCESS; 778 } 779 780 /** 781 * @unimplemented 782 * @brief 783 * Frees (de-allocates) the proxy data memory block of a token. 784 * 785 * @param[in,out] ProxyData 786 * The proxy data to be freed. 787 * 788 * @return 789 * Nothing. 790 */ 791 VOID 792 NTAPI 793 SepFreeProxyData( 794 _Inout_ PVOID ProxyData) 795 { 796 UNIMPLEMENTED; 797 } 798 799 /** 800 * @unimplemented 801 * @brief 802 * Copies the proxy data from the source into the destination of a token. 803 * 804 * @param[out] Dest 805 * The destination path where the proxy data is to be copied to. 806 * 807 * @param[in] Src 808 * The source path where the proxy data is be copied from. 809 * 810 * @return 811 * To be added... 812 */ 813 NTSTATUS 814 NTAPI 815 SepCopyProxyData( 816 _Out_ PVOID* Dest, 817 _In_ PVOID Src) 818 { 819 UNIMPLEMENTED; 820 return STATUS_NOT_IMPLEMENTED; 821 } 822 823 /** 824 * @brief 825 * Replaces the old access token of a process (pointed by the EPROCESS kernel structure) with a 826 * new access token. The new access token must be a primary token for use. 827 * 828 * @param[in] Process 829 * The process instance where its access token is about to be replaced. 830 * 831 * @param[in] NewAccessToken 832 * The new token that it's going to replace the old one. 833 * 834 * @param[out] OldAccessToken 835 * The returned old token that's been replaced, which the caller can do anything. 836 * 837 * @return 838 * Returns STATUS_SUCCESS if the exchange operation between tokens has completed successfully. 839 * STATUS_BAD_TOKEN_TYPE is returned if the new token is not a primary one so that we cannot 840 * exchange it with the old one from the process. STATUS_TOKEN_ALREADY_IN_USE is returned if 841 * both tokens aren't equal which means one of them has different properties (groups, privileges, etc.) 842 * and as such one of them is currently in use. A failure NTSTATUS code is returned otherwise. 843 */ 844 NTSTATUS 845 NTAPI 846 SeExchangePrimaryToken( 847 _In_ PEPROCESS Process, 848 _In_ PACCESS_TOKEN NewAccessToken, 849 _Out_ PACCESS_TOKEN* OldAccessToken) 850 { 851 PTOKEN OldToken; 852 PTOKEN NewToken = (PTOKEN)NewAccessToken; 853 854 PAGED_CODE(); 855 856 if (NewToken->TokenType != TokenPrimary) 857 return STATUS_BAD_TOKEN_TYPE; 858 859 if (NewToken->TokenInUse) 860 { 861 BOOLEAN IsEqual; 862 NTSTATUS Status; 863 864 /* Maybe we're trying to set the same token */ 865 OldToken = PsReferencePrimaryToken(Process); 866 if (OldToken == NewToken) 867 { 868 /* So it's a nop. */ 869 *OldAccessToken = OldToken; 870 return STATUS_SUCCESS; 871 } 872 873 Status = SepCompareTokens(OldToken, NewToken, &IsEqual); 874 if (!NT_SUCCESS(Status)) 875 { 876 PsDereferencePrimaryToken(OldToken); 877 *OldAccessToken = NULL; 878 return Status; 879 } 880 881 if (!IsEqual) 882 { 883 PsDereferencePrimaryToken(OldToken); 884 *OldAccessToken = NULL; 885 return STATUS_TOKEN_ALREADY_IN_USE; 886 } 887 /* Silently return STATUS_SUCCESS but do not set the new token, 888 * as it's already in use elsewhere. */ 889 *OldAccessToken = OldToken; 890 return STATUS_SUCCESS; 891 } 892 893 /* Lock the new token */ 894 SepAcquireTokenLockExclusive(NewToken); 895 896 /* Mark new token in use */ 897 NewToken->TokenInUse = TRUE; 898 899 /* Set the session ID for the new token */ 900 NewToken->SessionId = MmGetSessionId(Process); 901 902 /* Unlock the new token */ 903 SepReleaseTokenLock(NewToken); 904 905 /* Reference the new token */ 906 ObReferenceObject(NewToken); 907 908 /* Replace the old with the new */ 909 OldToken = ObFastReplaceObject(&Process->Token, NewToken); 910 911 /* Lock the old token */ 912 SepAcquireTokenLockExclusive(OldToken); 913 914 /* Mark the old token as free */ 915 OldToken->TokenInUse = FALSE; 916 917 /* Unlock the old token */ 918 SepReleaseTokenLock(OldToken); 919 920 *OldAccessToken = (PACCESS_TOKEN)OldToken; 921 return STATUS_SUCCESS; 922 } 923 924 /** 925 * @brief 926 * Removes the primary token of a process. 927 * 928 * @param[in,out] Process 929 * The process instance with the access token to be removed. 930 * 931 * @return 932 * Nothing. 933 */ 934 VOID 935 NTAPI 936 SeDeassignPrimaryToken( 937 _Inout_ PEPROCESS Process) 938 { 939 PTOKEN OldToken; 940 941 /* Remove the Token */ 942 OldToken = ObFastReplaceObject(&Process->Token, NULL); 943 944 /* Mark the Old Token as free */ 945 OldToken->TokenInUse = FALSE; 946 947 /* Dereference the Token */ 948 ObDereferenceObject(OldToken); 949 } 950 951 /** 952 * @brief 953 * Computes the length size of a SID. 954 * 955 * @param[in] Count 956 * Total count of entries that have SIDs in them (that being PSID_AND_ATTRIBUTES in this context). 957 * 958 * @param[in] Src 959 * Source that points to the attributes and SID entry structure. 960 * 961 * @return 962 * Returns the total length of a SID size. 963 */ 964 ULONG 965 RtlLengthSidAndAttributes( 966 _In_ ULONG Count, 967 _In_ PSID_AND_ATTRIBUTES Src) 968 { 969 ULONG i; 970 ULONG uLength; 971 972 PAGED_CODE(); 973 974 uLength = Count * sizeof(SID_AND_ATTRIBUTES); 975 for (i = 0; i < Count; i++) 976 uLength += RtlLengthSid(Src[i].Sid); 977 978 return uLength; 979 } 980 981 /** 982 * @brief 983 * Finds the primary group and default owner entity based on the submitted primary group instance 984 * and an access token. 985 * 986 * @param[in] Token 987 * Access token to begin the search query of primary group and default owner. 988 * 989 * @param[in] PrimaryGroup 990 * A primary group SID to be used for search query, determining if user & groups of a token 991 * and the submitted primary group do match. 992 * 993 * @param[in] DefaultOwner 994 * The default owner. If specified, it's used to determine if the token belongs to the actual user, 995 * that is, being the owner himself. 996 * 997 * @param[out] PrimaryGroupIndex 998 * Returns the primary group index. 999 * 1000 * @param[out] DefaultOwnerIndex 1001 * Returns the default owner index. 1002 * 1003 * @return 1004 * Returns STATUS_SUCCESS if the find query operation has completed successfully and that at least one 1005 * search result is requested by the caller. STATUS_INVALID_PARAMETER is returned if the caller hasn't requested 1006 * any search result. STATUS_INVALID_OWNER is returned if the specified default user owner does not match with the other 1007 * user from the token. STATUS_INVALID_PRIMARY_GROUP is returned if the specified default primary group does not match with the 1008 * other group from the token. 1009 */ 1010 NTSTATUS 1011 SepFindPrimaryGroupAndDefaultOwner( 1012 _In_ PTOKEN Token, 1013 _In_ PSID PrimaryGroup, 1014 _In_opt_ PSID DefaultOwner, 1015 _Out_opt_ PULONG PrimaryGroupIndex, 1016 _Out_opt_ PULONG DefaultOwnerIndex) 1017 { 1018 ULONG i; 1019 1020 /* We should return at least a search result */ 1021 if (!PrimaryGroupIndex && !DefaultOwnerIndex) 1022 return STATUS_INVALID_PARAMETER; 1023 1024 if (PrimaryGroupIndex) 1025 { 1026 /* Initialize with an invalid index */ 1027 // Token->PrimaryGroup = NULL; 1028 *PrimaryGroupIndex = Token->UserAndGroupCount; 1029 } 1030 1031 if (DefaultOwnerIndex) 1032 { 1033 if (DefaultOwner) 1034 { 1035 /* An owner is specified: check whether this is actually the user */ 1036 if (RtlEqualSid(Token->UserAndGroups[0].Sid, DefaultOwner)) 1037 { 1038 /* 1039 * It's the user (first element in array): set it 1040 * as the owner and stop the search for it. 1041 */ 1042 *DefaultOwnerIndex = 0; 1043 DefaultOwnerIndex = NULL; 1044 } 1045 else 1046 { 1047 /* An owner is specified: initialize with an invalid index */ 1048 *DefaultOwnerIndex = Token->UserAndGroupCount; 1049 } 1050 } 1051 else 1052 { 1053 /* 1054 * No owner specified: set the user (first element in array) 1055 * as the owner and stop the search for it. 1056 */ 1057 *DefaultOwnerIndex = 0; 1058 DefaultOwnerIndex = NULL; 1059 } 1060 } 1061 1062 /* Validate and set the primary group and default owner indices */ 1063 for (i = 0; i < Token->UserAndGroupCount; i++) 1064 { 1065 /* Stop the search if we have found what we searched for */ 1066 if (!PrimaryGroupIndex && !DefaultOwnerIndex) 1067 break; 1068 1069 if (DefaultOwnerIndex && DefaultOwner && 1070 RtlEqualSid(Token->UserAndGroups[i].Sid, DefaultOwner) && 1071 (Token->UserAndGroups[i].Attributes & SE_GROUP_OWNER)) 1072 { 1073 /* Owner is found, stop the search for it */ 1074 *DefaultOwnerIndex = i; 1075 DefaultOwnerIndex = NULL; 1076 } 1077 1078 if (PrimaryGroupIndex && 1079 RtlEqualSid(Token->UserAndGroups[i].Sid, PrimaryGroup)) 1080 { 1081 /* Primary group is found, stop the search for it */ 1082 // Token->PrimaryGroup = Token->UserAndGroups[i].Sid; 1083 *PrimaryGroupIndex = i; 1084 PrimaryGroupIndex = NULL; 1085 } 1086 } 1087 1088 if (DefaultOwnerIndex) 1089 { 1090 if (*DefaultOwnerIndex == Token->UserAndGroupCount) 1091 return STATUS_INVALID_OWNER; 1092 } 1093 1094 if (PrimaryGroupIndex) 1095 { 1096 if (*PrimaryGroupIndex == Token->UserAndGroupCount) 1097 // if (Token->PrimaryGroup == NULL) 1098 return STATUS_INVALID_PRIMARY_GROUP; 1099 } 1100 1101 return STATUS_SUCCESS; 1102 } 1103 1104 /** 1105 * @brief 1106 * Internal private function that returns an opened handle 1107 * of an access token associated with a thread. 1108 * 1109 * @param[in] Thread 1110 * A pointer to a Executive thread. This parameter is used to 1111 * validate that the newly obtained thread in this function 1112 * hasn't diverged. This could potentially lead to a scenario 1113 * that we might get an access token from a different token 1114 * which is not what we want. The validation is performed 1115 * if the token has to copied and can't be opened directly. 1116 * 1117 * @param[in] ThreadHandle 1118 * A handle to a thread, of which an access token is to be opened 1119 * and given from that thread. 1120 * 1121 * @param[in] ThreadToken 1122 * A pointer to an access token associated with the specific thread. 1123 * The function assumes that the token is an impersonation one 1124 * prior the calling of this function. 1125 * 1126 * @param[in] DesiredAccess 1127 * The desired access rights for the access token. 1128 * 1129 * @param[in] HandleAttributes 1130 * Handle attributes of which they are used for the newly creation 1131 * of the opened thread token. The function assumes that they have 1132 * been validated prior the calling of this function. 1133 * 1134 * @param[in] EffectiveOnly 1135 * If set to TRUE, the function will copy a new access token with 1136 * privileges and groups that are effectively enabled. Any disabled 1137 * privilege or group is removed from the copied token. Otherwise 1138 * if set to FALSE, the function retains all the enabled and disabled 1139 * privielges and groups. 1140 * 1141 * @param[in] CopyOnOpen 1142 * If set to TRUE, it tells the function that the access token cannot 1143 * be directly opened due to the security impersonation info of the 1144 * associated thread being enforced. In this case the function will 1145 * make a copy of the said token by duplicating it. Otherwise if set 1146 * to FALSE, the function will just open the access token directly. 1147 * 1148 * @param[in] ImpersonationLevel 1149 * The security impersonation level, at which it is allowed to 1150 * access the token. 1151 * 1152 * @param[in] PreviousMode 1153 * The processor request level mode. 1154 * 1155 * @param[out] OpenedTokenHandle 1156 * A pointer to an opened access token handle associated with the 1157 * specific thread, returned to the caller. Initially this parameter 1158 * is set to NULL and if the function fails to open the thread's token, 1159 * it will stay NULL. 1160 * 1161 * @return 1162 * Returns STATUS_SUCCESS if the function has successfully opened the thread's 1163 * token. STATUS_OBJECT_TYPE_MISMATCH is returned if the obtained thread object 1164 * no longer matches with the other thread that has been obtained previously. 1165 * STATUS_NO_TOKEN is returned if the associated thread's process has no 1166 * primary access token. A failure NTSTATUS code is returned otherwise. 1167 */ 1168 static 1169 NTSTATUS 1170 SepOpenThreadToken( 1171 _In_ PETHREAD Thread, 1172 _In_ HANDLE ThreadHandle, 1173 _In_ PTOKEN ThreadToken, 1174 _In_ ACCESS_MASK DesiredAccess, 1175 _In_ ULONG HandleAttributes, 1176 _In_ BOOLEAN EffectiveOnly, 1177 _In_ BOOLEAN CopyOnOpen, 1178 _In_ SECURITY_IMPERSONATION_LEVEL ImpersonationLevel, 1179 _In_ KPROCESSOR_MODE PreviousMode, 1180 _Out_ PHANDLE OpenedTokenHandle) 1181 { 1182 NTSTATUS Status; 1183 HANDLE TokenHandle; 1184 PETHREAD Thread2; 1185 OBJECT_ATTRIBUTES ObjectAttributes; 1186 PTOKEN NewToken, PrimaryToken; 1187 SECURITY_DESCRIPTOR SecurityDescriptor; 1188 PACL Dacl; 1189 1190 PAGED_CODE(); 1191 1192 /* Assume no opened token handle at first */ 1193 *OpenedTokenHandle = NULL; 1194 1195 /* Check if we have to do a copy of the token on open or not */ 1196 if (!CopyOnOpen) 1197 { 1198 /* Just open the thread's token directly */ 1199 Status = ObOpenObjectByPointer(ThreadToken, 1200 HandleAttributes, 1201 NULL, 1202 DesiredAccess, 1203 SeTokenObjectType, 1204 PreviousMode, 1205 &TokenHandle); 1206 if (!NT_SUCCESS(Status)) 1207 { 1208 DPRINT1("Failed to open the thread's token object (Status 0x%lx)\n", Status); 1209 return Status; 1210 } 1211 1212 /* Give it to caller */ 1213 *OpenedTokenHandle = TokenHandle; 1214 return STATUS_SUCCESS; 1215 } 1216 1217 /* 1218 * The caller asks to do a copy of that token whilst it's opened. 1219 * Obtain a thread object again but this time we have to obtain 1220 * it in our side, kernel mode, and request all the access needed 1221 * to do a copy of the token because the original thread only has 1222 * query access needed for access token validation. 1223 */ 1224 Status = ObReferenceObjectByHandle(ThreadHandle, 1225 THREAD_ALL_ACCESS, 1226 PsThreadType, 1227 KernelMode, 1228 (PVOID*)&Thread2, 1229 NULL); 1230 if (!NT_SUCCESS(Status)) 1231 { 1232 DPRINT1("Failed to reference the object thread (Status 0x%lx)\n", Status); 1233 return Status; 1234 } 1235 1236 /* Check that one of the threads hasn't diverged */ 1237 if (Thread != Thread2) 1238 { 1239 DPRINT1("One of the threads aren't the same (original thread 0x%p, thread 0x%p)\n", Thread, Thread2); 1240 ObDereferenceObject(Thread2); 1241 return STATUS_OBJECT_TYPE_MISMATCH; 1242 } 1243 1244 /* Reference the primary token of the process' thread */ 1245 PrimaryToken = PsReferencePrimaryToken(Thread2->ThreadsProcess); 1246 if (!PrimaryToken) 1247 { 1248 DPRINT1("Failed to reference the primary token of thread\n"); 1249 ObDereferenceObject(Thread2); 1250 return STATUS_NO_TOKEN; 1251 } 1252 1253 /* Create an impersonation DACL from the tokens we got */ 1254 Status = SepCreateImpersonationTokenDacl(ThreadToken, PrimaryToken, &Dacl); 1255 ObFastDereferenceObject(&Thread2->ThreadsProcess->Token, PrimaryToken); 1256 if (!NT_SUCCESS(Status)) 1257 { 1258 DPRINT1("Failed to create an impersonation token DACL (Status 0x%lx)\n", Status); 1259 ObDereferenceObject(Thread2); 1260 return Status; 1261 } 1262 1263 /* Create a security descriptor with the DACL we got */ 1264 Status = RtlCreateSecurityDescriptor(&SecurityDescriptor, 1265 SECURITY_DESCRIPTOR_REVISION); 1266 if (!NT_SUCCESS(Status)) 1267 { 1268 DPRINT1("Failed to create a security descriptor (Status 0x%lx)\n", Status); 1269 ExFreePoolWithTag(Dacl, TAG_ACL); 1270 ObDereferenceObject(Thread2); 1271 return Status; 1272 } 1273 1274 /* Attach the DACL to that security descriptor */ 1275 Status = RtlSetDaclSecurityDescriptor(&SecurityDescriptor, 1276 TRUE, 1277 Dacl, 1278 FALSE); 1279 if (!NT_SUCCESS(Status)) 1280 { 1281 DPRINT1("Failed to set the DACL to the security descriptor (Status 0x%lx)\n", Status); 1282 ExFreePoolWithTag(Dacl, TAG_ACL); 1283 ObDereferenceObject(Thread2); 1284 return Status; 1285 } 1286 1287 /* 1288 * Initialize the object attributes for the token we 1289 * are going to duplicate. 1290 */ 1291 InitializeObjectAttributes(&ObjectAttributes, 1292 NULL, 1293 HandleAttributes, 1294 NULL, 1295 &SecurityDescriptor); 1296 1297 /* Duplicate (copy) it now */ 1298 Status = SepDuplicateToken(ThreadToken, 1299 &ObjectAttributes, 1300 EffectiveOnly, 1301 TokenImpersonation, 1302 ImpersonationLevel, 1303 KernelMode, 1304 &NewToken); 1305 if (!NT_SUCCESS(Status)) 1306 { 1307 DPRINT1("Failed to duplicate the token (Status 0x%lx)\n", Status); 1308 ExFreePoolWithTag(Dacl, TAG_ACL); 1309 ObDereferenceObject(Thread2); 1310 return Status; 1311 } 1312 1313 /* Insert that copied token into the handle now */ 1314 ObReferenceObject(NewToken); 1315 Status = ObInsertObject(NewToken, 1316 NULL, 1317 DesiredAccess, 1318 0, 1319 NULL, 1320 &TokenHandle); 1321 if (!NT_SUCCESS(Status)) 1322 { 1323 DPRINT1("Failed to insert the token object (Status 0x%lx)\n", Status); 1324 ExFreePoolWithTag(Dacl, TAG_ACL); 1325 ObDereferenceObject(NewToken); 1326 ObDereferenceObject(Thread2); 1327 return Status; 1328 } 1329 1330 /* We're almost done, free the DACL if we got one */ 1331 ExFreePoolWithTag(Dacl, TAG_ACL); 1332 1333 /* Impersonate the client finally */ 1334 Status = PsImpersonateClient(Thread, NewToken, FALSE, EffectiveOnly, ImpersonationLevel); 1335 if (!NT_SUCCESS(Status)) 1336 { 1337 DPRINT1("Failed to impersonate the client (Status 0x%lx)\n", Status); 1338 ObDereferenceObject(NewToken); 1339 ObDereferenceObject(Thread2); 1340 return Status; 1341 } 1342 1343 /* Give the newly opened token handle to caller */ 1344 *OpenedTokenHandle = TokenHandle; 1345 ObDereferenceObject(NewToken); 1346 ObDereferenceObject(Thread2); 1347 return Status; 1348 } 1349 1350 /** 1351 * @brief 1352 * Subtracts a token in exchange of duplicating a new one. 1353 * 1354 * @param[in] ParentToken 1355 * The parent access token for duplication. 1356 * 1357 * @param[out] Token 1358 * The new duplicated token. 1359 * 1360 * @param[in] InUse 1361 * Set this to TRUE if the token is about to be used immediately after the call execution 1362 * of this function, FALSE otherwise. 1363 * 1364 * @param[in] SessionId 1365 * Session ID for the token to be assigned. 1366 * 1367 * @return 1368 * Returns STATUS_SUCCESS if token subtracting and duplication have completed successfully. 1369 * A failure NTSTATUS code is returned otherwise. 1370 */ 1371 NTSTATUS 1372 NTAPI 1373 SeSubProcessToken( 1374 _In_ PTOKEN ParentToken, 1375 _Out_ PTOKEN *Token, 1376 _In_ BOOLEAN InUse, 1377 _In_ ULONG SessionId) 1378 { 1379 PTOKEN NewToken; 1380 OBJECT_ATTRIBUTES ObjectAttributes; 1381 NTSTATUS Status; 1382 1383 /* Initialize the attributes and duplicate it */ 1384 InitializeObjectAttributes(&ObjectAttributes, NULL, 0, NULL, NULL); 1385 Status = SepDuplicateToken(ParentToken, 1386 &ObjectAttributes, 1387 FALSE, 1388 TokenPrimary, 1389 ParentToken->ImpersonationLevel, 1390 KernelMode, 1391 &NewToken); 1392 if (NT_SUCCESS(Status)) 1393 { 1394 /* Insert it */ 1395 Status = ObInsertObject(NewToken, 1396 NULL, 1397 0, 1398 0, 1399 NULL, 1400 NULL); 1401 if (NT_SUCCESS(Status)) 1402 { 1403 /* Set the session ID */ 1404 NewToken->SessionId = SessionId; 1405 NewToken->TokenInUse = InUse; 1406 1407 /* Return the token */ 1408 *Token = NewToken; 1409 } 1410 } 1411 1412 /* Return status */ 1413 return Status; 1414 } 1415 1416 /** 1417 * @brief 1418 * Checks if the token is a child of the other token 1419 * of the current process that the calling thread is invoking this function. 1420 * 1421 * @param[in] Token 1422 * An access token to determine if it's a child or not. 1423 * 1424 * @param[out] IsChild 1425 * The returned boolean result. 1426 * 1427 * @return 1428 * Returns STATUS_SUCCESS when the function finishes its operation. STATUS_UNSUCCESSFUL is 1429 * returned if primary token of the current calling process couldn't be referenced otherwise. 1430 */ 1431 NTSTATUS 1432 NTAPI 1433 SeIsTokenChild( 1434 _In_ PTOKEN Token, 1435 _Out_ PBOOLEAN IsChild) 1436 { 1437 PTOKEN ProcessToken; 1438 LUID ProcessTokenId, CallerParentId; 1439 1440 /* Assume failure */ 1441 *IsChild = FALSE; 1442 1443 /* Reference the process token */ 1444 ProcessToken = PsReferencePrimaryToken(PsGetCurrentProcess()); 1445 if (!ProcessToken) 1446 return STATUS_UNSUCCESSFUL; 1447 1448 /* Get its token ID */ 1449 ProcessTokenId = ProcessToken->TokenId; 1450 1451 /* Dereference the token */ 1452 ObFastDereferenceObject(&PsGetCurrentProcess()->Token, ProcessToken); 1453 1454 /* Get our parent token ID */ 1455 CallerParentId = Token->ParentTokenId; 1456 1457 /* Compare the token IDs */ 1458 if (RtlEqualLuid(&CallerParentId, &ProcessTokenId)) 1459 *IsChild = TRUE; 1460 1461 /* Return success */ 1462 return STATUS_SUCCESS; 1463 } 1464 1465 /** 1466 * @brief 1467 * Checks if the token is a sibling of the other token of 1468 * the current process that the calling thread is invoking this function. 1469 * 1470 * @param[in] Token 1471 * An access token to determine if it's a sibling or not. 1472 * 1473 * @param[out] IsSibling 1474 * The returned boolean result. 1475 * 1476 * @return 1477 * Returns STATUS_SUCCESS when the function finishes its operation. STATUS_UNSUCCESSFUL is 1478 * returned if primary token of the current calling process couldn't be referenced otherwise. 1479 */ 1480 NTSTATUS 1481 NTAPI 1482 SeIsTokenSibling( 1483 _In_ PTOKEN Token, 1484 _Out_ PBOOLEAN IsSibling) 1485 { 1486 PTOKEN ProcessToken; 1487 LUID ProcessParentId, ProcessAuthId; 1488 LUID CallerParentId, CallerAuthId; 1489 1490 /* Assume failure */ 1491 *IsSibling = FALSE; 1492 1493 /* Reference the process token */ 1494 ProcessToken = PsReferencePrimaryToken(PsGetCurrentProcess()); 1495 if (!ProcessToken) 1496 return STATUS_UNSUCCESSFUL; 1497 1498 /* Get its parent and authentication IDs */ 1499 ProcessParentId = ProcessToken->ParentTokenId; 1500 ProcessAuthId = ProcessToken->AuthenticationId; 1501 1502 /* Dereference the token */ 1503 ObFastDereferenceObject(&PsGetCurrentProcess()->Token, ProcessToken); 1504 1505 /* Get our parent and authentication IDs */ 1506 CallerParentId = Token->ParentTokenId; 1507 CallerAuthId = Token->AuthenticationId; 1508 1509 /* Compare the token IDs */ 1510 if (RtlEqualLuid(&CallerParentId, &ProcessParentId) && 1511 RtlEqualLuid(&CallerAuthId, &ProcessAuthId)) 1512 { 1513 *IsSibling = TRUE; 1514 } 1515 1516 /* Return success */ 1517 return STATUS_SUCCESS; 1518 } 1519 1520 /** 1521 * @brief 1522 * Copies an existing access token (technically duplicating a new one). 1523 * 1524 * @param[in] Token 1525 * Token to copy. 1526 * 1527 * @param[in] Level 1528 * Impersonation security level to assign to the newly copied token. 1529 * 1530 * @param[in] PreviousMode 1531 * Processor request level mode. 1532 * 1533 * @param[out] NewToken 1534 * The newly copied token. 1535 * 1536 * @return 1537 * Returns STATUS_SUCCESS when token copying has finished successfully. A failure 1538 * NTSTATUS code is returned otherwise. 1539 */ 1540 NTSTATUS 1541 NTAPI 1542 SeCopyClientToken( 1543 _In_ PACCESS_TOKEN Token, 1544 _In_ SECURITY_IMPERSONATION_LEVEL Level, 1545 _In_ KPROCESSOR_MODE PreviousMode, 1546 _Out_ PACCESS_TOKEN* NewToken) 1547 { 1548 NTSTATUS Status; 1549 OBJECT_ATTRIBUTES ObjectAttributes; 1550 1551 PAGED_CODE(); 1552 1553 InitializeObjectAttributes(&ObjectAttributes, 1554 NULL, 1555 0, 1556 NULL, 1557 NULL); 1558 1559 Status = SepDuplicateToken(Token, 1560 &ObjectAttributes, 1561 FALSE, 1562 TokenImpersonation, 1563 Level, 1564 PreviousMode, 1565 (PTOKEN*)NewToken); 1566 1567 return Status; 1568 } 1569 1570 /** 1571 * @brief 1572 * Determines if a token is a sandbox inert token or not, 1573 * based upon the token flags. 1574 * 1575 * @param[in] Token 1576 * A valid access token to determine if such token is inert. 1577 * 1578 * @return 1579 * Returns TRUE if the token is inert, FALSE otherwise. 1580 */ 1581 BOOLEAN 1582 NTAPI 1583 SeTokenIsInert( 1584 _In_ PTOKEN Token) 1585 { 1586 PAGED_CODE(); 1587 1588 return (((PTOKEN)Token)->TokenFlags & TOKEN_SANDBOX_INERT) != 0; 1589 } 1590 1591 /** 1592 * @brief 1593 * Internal function that deals with access token object destruction and deletion. 1594 * The function is used solely by the object manager mechanism that handles the life 1595 * management of a token object. 1596 * 1597 * @param[in] ObjectBody 1598 * The object body that represents an access token object. 1599 * 1600 * @return 1601 * Nothing. 1602 */ 1603 VOID 1604 NTAPI 1605 SepDeleteToken( 1606 _In_ PVOID ObjectBody) 1607 { 1608 NTSTATUS Status; 1609 PTOKEN AccessToken = (PTOKEN)ObjectBody; 1610 1611 DPRINT("SepDeleteToken()\n"); 1612 1613 /* Remove the referenced logon session from token */ 1614 if (AccessToken->LogonSession) 1615 { 1616 Status = SepRmRemoveLogonSessionFromToken(AccessToken); 1617 if (!NT_SUCCESS(Status)) 1618 { 1619 /* Something seriously went wrong */ 1620 DPRINT1("SepDeleteToken(): Failed to remove the logon session from token (Status: 0x%lx)\n", Status); 1621 return; 1622 } 1623 } 1624 1625 /* Dereference the logon session */ 1626 if ((AccessToken->TokenFlags & TOKEN_SESSION_NOT_REFERENCED) == 0) 1627 SepRmDereferenceLogonSession(&AccessToken->AuthenticationId); 1628 1629 /* Delete the token lock */ 1630 if (AccessToken->TokenLock) 1631 SepDeleteTokenLock(AccessToken); 1632 1633 /* Delete the dynamic information area */ 1634 if (AccessToken->DynamicPart) 1635 ExFreePoolWithTag(AccessToken->DynamicPart, TAG_TOKEN_DYNAMIC); 1636 } 1637 1638 /** 1639 * @brief 1640 * Internal function that initializes critical kernel data for access 1641 * token implementation in SRM. 1642 * 1643 * @return 1644 * Nothing. 1645 */ 1646 CODE_SEG("INIT") 1647 VOID 1648 NTAPI 1649 SepInitializeTokenImplementation(VOID) 1650 { 1651 UNICODE_STRING Name; 1652 OBJECT_TYPE_INITIALIZER ObjectTypeInitializer; 1653 1654 DPRINT("Creating Token Object Type\n"); 1655 1656 /* Initialize the Token type */ 1657 RtlZeroMemory(&ObjectTypeInitializer, sizeof(ObjectTypeInitializer)); 1658 RtlInitUnicodeString(&Name, L"Token"); 1659 ObjectTypeInitializer.Length = sizeof(ObjectTypeInitializer); 1660 ObjectTypeInitializer.InvalidAttributes = OBJ_OPENLINK; 1661 ObjectTypeInitializer.SecurityRequired = TRUE; 1662 ObjectTypeInitializer.DefaultPagedPoolCharge = sizeof(TOKEN); 1663 ObjectTypeInitializer.GenericMapping = SepTokenMapping; 1664 ObjectTypeInitializer.PoolType = PagedPool; 1665 ObjectTypeInitializer.ValidAccessMask = TOKEN_ALL_ACCESS; 1666 ObjectTypeInitializer.UseDefaultObject = TRUE; 1667 ObjectTypeInitializer.DeleteProcedure = SepDeleteToken; 1668 ObCreateObjectType(&Name, &ObjectTypeInitializer, NULL, &SeTokenObjectType); 1669 } 1670 1671 /** 1672 * @brief 1673 * Assigns a primary access token to a given process. 1674 * 1675 * @param[in] Process 1676 * Process where the token is about to be assigned. 1677 * 1678 * @param[in] Token 1679 * The token to be assigned. 1680 * 1681 * @return 1682 * Nothing. 1683 */ 1684 VOID 1685 NTAPI 1686 SeAssignPrimaryToken( 1687 _In_ PEPROCESS Process, 1688 _In_ PTOKEN Token) 1689 { 1690 PAGED_CODE(); 1691 1692 /* Sanity checks */ 1693 ASSERT(Token->TokenType == TokenPrimary); 1694 ASSERT(!Token->TokenInUse); 1695 1696 /* Clean any previous token */ 1697 if (Process->Token.Object) SeDeassignPrimaryToken(Process); 1698 1699 /* Set the new token */ 1700 ObReferenceObject(Token); 1701 Token->TokenInUse = TRUE; 1702 ObInitializeFastReference(&Process->Token, Token); 1703 } 1704 1705 /** 1706 * @brief 1707 * Retrieves token control information. 1708 * 1709 * @param[in] _Token 1710 * A valid token object. 1711 * 1712 * @param[out] SecurityDescriptor 1713 * The returned token control information. 1714 * 1715 * @return 1716 * Nothing. 1717 */ 1718 VOID 1719 NTAPI 1720 SeGetTokenControlInformation( 1721 _In_ PACCESS_TOKEN _Token, 1722 _Out_ PTOKEN_CONTROL TokenControl) 1723 { 1724 PTOKEN Token = _Token; 1725 PAGED_CODE(); 1726 1727 /* Capture the main fields */ 1728 TokenControl->AuthenticationId = Token->AuthenticationId; 1729 TokenControl->TokenId = Token->TokenId; 1730 TokenControl->TokenSource = Token->TokenSource; 1731 1732 /* Lock the token */ 1733 SepAcquireTokenLockShared(Token); 1734 1735 /* Capture the modified ID */ 1736 TokenControl->ModifiedId = Token->ModifiedId; 1737 1738 /* Unlock it */ 1739 SepReleaseTokenLock(Token); 1740 } 1741 1742 /** 1743 * @brief 1744 * Creates the system process token. 1745 * 1746 * @return 1747 * Returns the system process token if the operations have 1748 * completed successfully. 1749 */ 1750 CODE_SEG("INIT") 1751 PTOKEN 1752 NTAPI 1753 SepCreateSystemProcessToken(VOID) 1754 { 1755 ULONG GroupAttributes, OwnerAttributes; 1756 LARGE_INTEGER Expiration; 1757 SID_AND_ATTRIBUTES UserSid; 1758 ULONG GroupsLength; 1759 PSID PrimaryGroup; 1760 OBJECT_ATTRIBUTES ObjectAttributes; 1761 PSID Owner; 1762 PTOKEN Token; 1763 NTSTATUS Status; 1764 1765 /* Don't ever expire */ 1766 Expiration.QuadPart = -1; 1767 1768 /* All groups mandatory and enabled */ 1769 GroupAttributes = SE_GROUP_ENABLED | SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT; 1770 OwnerAttributes = SE_GROUP_ENABLED | SE_GROUP_OWNER | SE_GROUP_ENABLED_BY_DEFAULT; 1771 1772 /* User is Local System */ 1773 UserSid.Sid = SeLocalSystemSid; 1774 UserSid.Attributes = 0; 1775 1776 /* Primary group is Local System */ 1777 PrimaryGroup = SeLocalSystemSid; 1778 1779 /* Owner is Administrators */ 1780 Owner = SeAliasAdminsSid; 1781 1782 /* Groups are Administrators, World, and Authenticated Users */ 1783 SID_AND_ATTRIBUTES Groups[] = 1784 { 1785 {SeAliasAdminsSid, OwnerAttributes}, 1786 {SeWorldSid, GroupAttributes}, 1787 {SeAuthenticatedUsersSid, GroupAttributes}, 1788 {SeLocalSid, SE_GROUP_ENABLED} // HACK: Temporarily add the local group. See CORE-18250. 1789 }; 1790 GroupsLength = sizeof(SID_AND_ATTRIBUTES) + 1791 SeLengthSid(Groups[0].Sid) + 1792 SeLengthSid(Groups[1].Sid) + 1793 SeLengthSid(Groups[2].Sid) + 1794 SeLengthSid(Groups[3].Sid); // HACK 1795 ASSERT(GroupsLength <= (sizeof(Groups) * sizeof(ULONG))); 1796 1797 /* Setup the privileges */ 1798 LUID_AND_ATTRIBUTES Privileges[] = 1799 { 1800 {SeTcbPrivilege, SE_PRIVILEGE_ENABLED_BY_DEFAULT | SE_PRIVILEGE_ENABLED}, 1801 {SeCreateTokenPrivilege, 0}, 1802 {SeTakeOwnershipPrivilege, 0}, 1803 {SeCreatePagefilePrivilege, SE_PRIVILEGE_ENABLED_BY_DEFAULT | SE_PRIVILEGE_ENABLED}, 1804 {SeLockMemoryPrivilege, SE_PRIVILEGE_ENABLED_BY_DEFAULT | SE_PRIVILEGE_ENABLED}, 1805 {SeAssignPrimaryTokenPrivilege, 0}, 1806 {SeIncreaseQuotaPrivilege, 0}, 1807 {SeIncreaseBasePriorityPrivilege, SE_PRIVILEGE_ENABLED_BY_DEFAULT | SE_PRIVILEGE_ENABLED}, 1808 {SeCreatePermanentPrivilege, SE_PRIVILEGE_ENABLED_BY_DEFAULT | SE_PRIVILEGE_ENABLED}, 1809 {SeDebugPrivilege, SE_PRIVILEGE_ENABLED_BY_DEFAULT | SE_PRIVILEGE_ENABLED}, 1810 {SeAuditPrivilege, SE_PRIVILEGE_ENABLED_BY_DEFAULT | SE_PRIVILEGE_ENABLED}, 1811 {SeSecurityPrivilege, 0}, 1812 {SeSystemEnvironmentPrivilege, 0}, 1813 {SeChangeNotifyPrivilege, SE_PRIVILEGE_ENABLED_BY_DEFAULT | SE_PRIVILEGE_ENABLED}, 1814 {SeBackupPrivilege, 0}, 1815 {SeRestorePrivilege, 0}, 1816 {SeShutdownPrivilege, 0}, 1817 {SeLoadDriverPrivilege, 0}, 1818 {SeProfileSingleProcessPrivilege, SE_PRIVILEGE_ENABLED_BY_DEFAULT | SE_PRIVILEGE_ENABLED}, 1819 {SeSystemtimePrivilege, 0}, 1820 {SeUndockPrivilege, 0}, 1821 {SeManageVolumePrivilege, 0}, 1822 {SeImpersonatePrivilege, SE_PRIVILEGE_ENABLED_BY_DEFAULT | SE_PRIVILEGE_ENABLED}, 1823 {SeCreateGlobalPrivilege, SE_PRIVILEGE_ENABLED_BY_DEFAULT | SE_PRIVILEGE_ENABLED}, 1824 }; 1825 1826 /* Setup the object attributes */ 1827 InitializeObjectAttributes(&ObjectAttributes, NULL, 0, NULL, NULL); 1828 ASSERT(SeSystemDefaultDacl != NULL); 1829 1830 /* Create the token */ 1831 Status = SepCreateToken((PHANDLE)&Token, 1832 KernelMode, 1833 0, 1834 &ObjectAttributes, 1835 TokenPrimary, 1836 SecurityAnonymous, 1837 &SeSystemAuthenticationId, 1838 &Expiration, 1839 &UserSid, 1840 RTL_NUMBER_OF(Groups), 1841 Groups, 1842 GroupsLength, 1843 RTL_NUMBER_OF(Privileges), 1844 Privileges, 1845 Owner, 1846 PrimaryGroup, 1847 SeSystemDefaultDacl, 1848 &SeSystemTokenSource, 1849 TRUE); 1850 ASSERT(Status == STATUS_SUCCESS); 1851 1852 /* Return the token */ 1853 return Token; 1854 } 1855 1856 /** 1857 * @brief 1858 * Creates the anonymous logon token for the system. The difference between this 1859 * token and the other one is the inclusion of everyone SID group (being SeWorldSid). 1860 * The other token lacks such group. 1861 * 1862 * @return 1863 * Returns the system's anonymous logon token if the operations have 1864 * completed successfully. 1865 */ 1866 CODE_SEG("INIT") 1867 PTOKEN 1868 SepCreateSystemAnonymousLogonToken(VOID) 1869 { 1870 SID_AND_ATTRIBUTES UserSid; 1871 PSID PrimaryGroup; 1872 PTOKEN Token; 1873 ULONG GroupsLength; 1874 LARGE_INTEGER Expiration; 1875 OBJECT_ATTRIBUTES ObjectAttributes; 1876 NTSTATUS Status; 1877 1878 /* The token never expires */ 1879 Expiration.QuadPart = -1; 1880 1881 /* The user is the anonymous logon */ 1882 UserSid.Sid = SeAnonymousLogonSid; 1883 UserSid.Attributes = 0; 1884 1885 /* The primary group is also the anonymous logon */ 1886 PrimaryGroup = SeAnonymousLogonSid; 1887 1888 /* The only group for the token is the World */ 1889 SID_AND_ATTRIBUTES Groups[] = 1890 { 1891 {SeWorldSid, SE_GROUP_ENABLED | SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT} 1892 }; 1893 GroupsLength = sizeof(SID_AND_ATTRIBUTES) + 1894 SeLengthSid(Groups[0].Sid); 1895 ASSERT(GroupsLength <= (sizeof(Groups) * sizeof(ULONG))); 1896 1897 /* Initialise the object attributes for the token */ 1898 InitializeObjectAttributes(&ObjectAttributes, NULL, 0, NULL, NULL); 1899 ASSERT(SeSystemAnonymousLogonDacl != NULL); 1900 1901 /* Create token */ 1902 Status = SepCreateToken((PHANDLE)&Token, 1903 KernelMode, 1904 0, 1905 &ObjectAttributes, 1906 TokenPrimary, 1907 SecurityAnonymous, 1908 &SeAnonymousAuthenticationId, 1909 &Expiration, 1910 &UserSid, 1911 RTL_NUMBER_OF(Groups), 1912 Groups, 1913 GroupsLength, 1914 0, 1915 NULL, 1916 NULL, 1917 PrimaryGroup, 1918 SeSystemAnonymousLogonDacl, 1919 &SeSystemTokenSource, 1920 TRUE); 1921 ASSERT(Status == STATUS_SUCCESS); 1922 1923 /* Return the anonymous logon token */ 1924 return Token; 1925 } 1926 1927 /** 1928 * @brief 1929 * Creates the anonymous logon token for the system. This kind of token 1930 * doesn't include the everyone SID group (being SeWorldSid). 1931 * 1932 * @return 1933 * Returns the system's anonymous logon token if the operations have 1934 * completed successfully. 1935 */ 1936 CODE_SEG("INIT") 1937 PTOKEN 1938 SepCreateSystemAnonymousLogonTokenNoEveryone(VOID) 1939 { 1940 SID_AND_ATTRIBUTES UserSid; 1941 PSID PrimaryGroup; 1942 PTOKEN Token; 1943 LARGE_INTEGER Expiration; 1944 OBJECT_ATTRIBUTES ObjectAttributes; 1945 NTSTATUS Status; 1946 1947 /* The token never expires */ 1948 Expiration.QuadPart = -1; 1949 1950 /* The user is the anonymous logon */ 1951 UserSid.Sid = SeAnonymousLogonSid; 1952 UserSid.Attributes = 0; 1953 1954 /* The primary group is also the anonymous logon */ 1955 PrimaryGroup = SeAnonymousLogonSid; 1956 1957 /* Initialise the object attributes for the token */ 1958 InitializeObjectAttributes(&ObjectAttributes, NULL, 0, NULL, NULL); 1959 ASSERT(SeSystemAnonymousLogonDacl != NULL); 1960 1961 /* Create token */ 1962 Status = SepCreateToken((PHANDLE)&Token, 1963 KernelMode, 1964 0, 1965 &ObjectAttributes, 1966 TokenPrimary, 1967 SecurityAnonymous, 1968 &SeAnonymousAuthenticationId, 1969 &Expiration, 1970 &UserSid, 1971 0, 1972 NULL, 1973 0, 1974 0, 1975 NULL, 1976 NULL, 1977 PrimaryGroup, 1978 SeSystemAnonymousLogonDacl, 1979 &SeSystemTokenSource, 1980 TRUE); 1981 ASSERT(Status == STATUS_SUCCESS); 1982 1983 /* Return the anonymous (not including everyone) logon token */ 1984 return Token; 1985 } 1986 1987 /* PUBLIC FUNCTIONS ***********************************************************/ 1988 1989 /** 1990 * @brief 1991 * Queries the session ID of an access token. 1992 * 1993 * @param[in] Token 1994 * A valid access token where the session ID has to be gathered. 1995 * 1996 * @param[out] pSessionId 1997 * The returned pointer to a session ID to the caller. 1998 * 1999 * @return 2000 * Returns STATUS_SUCCESS. 2001 */ 2002 NTSTATUS 2003 NTAPI 2004 SeQuerySessionIdToken( 2005 _In_ PACCESS_TOKEN Token, 2006 _Out_ PULONG pSessionId) 2007 { 2008 PAGED_CODE(); 2009 2010 /* Lock the token */ 2011 SepAcquireTokenLockShared(Token); 2012 2013 *pSessionId = ((PTOKEN)Token)->SessionId; 2014 2015 /* Unlock the token */ 2016 SepReleaseTokenLock(Token); 2017 2018 return STATUS_SUCCESS; 2019 } 2020 2021 /** 2022 * @brief 2023 * Queries the authentication ID of an access token. 2024 * 2025 * @param[in] Token 2026 * A valid access token where the authentication ID has to be gathered. 2027 * 2028 * @param[out] pSessionId 2029 * The returned pointer to an authentication ID to the caller. 2030 * 2031 * @return 2032 * Returns STATUS_SUCCESS. 2033 */ 2034 NTSTATUS 2035 NTAPI 2036 SeQueryAuthenticationIdToken( 2037 _In_ PACCESS_TOKEN Token, 2038 _Out_ PLUID LogonId) 2039 { 2040 PAGED_CODE(); 2041 2042 *LogonId = ((PTOKEN)Token)->AuthenticationId; 2043 2044 return STATUS_SUCCESS; 2045 } 2046 2047 /** 2048 * @brief 2049 * Gathers the security impersonation level of an access token. 2050 * 2051 * @param[in] Token 2052 * A valid access token where the impersonation level has to be gathered. 2053 * 2054 * @return 2055 * Returns the security impersonation level from a valid token. 2056 */ 2057 SECURITY_IMPERSONATION_LEVEL 2058 NTAPI 2059 SeTokenImpersonationLevel( 2060 _In_ PACCESS_TOKEN Token) 2061 { 2062 PAGED_CODE(); 2063 2064 return ((PTOKEN)Token)->ImpersonationLevel; 2065 } 2066 2067 /** 2068 * @brief 2069 * Gathers the token type of an access token. A token ca be either 2070 * a primary token or impersonation token. 2071 * 2072 * @param[in] Token 2073 * A valid access token where the token type has to be gathered. 2074 * 2075 * @return 2076 * Returns the token type from a valid token. 2077 */ 2078 TOKEN_TYPE 2079 NTAPI 2080 SeTokenType( 2081 _In_ PACCESS_TOKEN Token) 2082 { 2083 PAGED_CODE(); 2084 2085 return ((PTOKEN)Token)->TokenType; 2086 } 2087 2088 /** 2089 * @brief 2090 * Determines if a token is either an admin token or not. Such 2091 * condition is checked based upon TOKEN_HAS_ADMIN_GROUP flag, 2092 * which means if the respective access token belongs to an 2093 * administrator group or not. 2094 * 2095 * @param[in] Token 2096 * A valid access token to determine if such token is admin or not. 2097 * 2098 * @return 2099 * Returns TRUE if the token is an admin one, FALSE otherwise. 2100 */ 2101 BOOLEAN 2102 NTAPI 2103 SeTokenIsAdmin( 2104 _In_ PACCESS_TOKEN Token) 2105 { 2106 PAGED_CODE(); 2107 2108 // NOTE: Win7+ instead really checks the list of groups in the token 2109 // (since TOKEN_HAS_ADMIN_GROUP == TOKEN_WRITE_RESTRICTED ...) 2110 return (((PTOKEN)Token)->TokenFlags & TOKEN_HAS_ADMIN_GROUP) != 0; 2111 } 2112 2113 /** 2114 * @brief 2115 * Determines if a token is restricted or not, based upon the token 2116 * flags. 2117 * 2118 * @param[in] Token 2119 * A valid access token to determine if such token is restricted. 2120 * 2121 * @return 2122 * Returns TRUE if the token is restricted, FALSE otherwise. 2123 */ 2124 BOOLEAN 2125 NTAPI 2126 SeTokenIsRestricted( 2127 _In_ PACCESS_TOKEN Token) 2128 { 2129 PAGED_CODE(); 2130 2131 return (((PTOKEN)Token)->TokenFlags & TOKEN_IS_RESTRICTED) != 0; 2132 } 2133 2134 /** 2135 * @brief 2136 * Determines if a token is write restricted, that is, nobody can write anything 2137 * to it. 2138 * 2139 * @param[in] Token 2140 * A valid access token to determine if such token is write restricted. 2141 * 2142 * @return 2143 * Returns TRUE if the token is write restricted, FALSE otherwise. 2144 * 2145 * @remarks 2146 * First introduced in NT 5.1 SP2 x86 (5.1.2600.2622), absent in NT 5.2, 2147 * then finally re-introduced in Vista+. 2148 */ 2149 BOOLEAN 2150 NTAPI 2151 SeTokenIsWriteRestricted( 2152 _In_ PACCESS_TOKEN Token) 2153 { 2154 PAGED_CODE(); 2155 2156 // NOTE: NT 5.1 SP2 x86 checks the SE_BACKUP_PRIVILEGES_CHECKED flag 2157 // while Vista+ checks the TOKEN_WRITE_RESTRICTED flag as one expects. 2158 return (((PTOKEN)Token)->TokenFlags & SE_BACKUP_PRIVILEGES_CHECKED) != 0; 2159 } 2160 2161 /** 2162 * @brief 2163 * Determines whether the server is allowed to impersonate on behalf 2164 * of a client or not. For further details, see Remarks. 2165 * 2166 * @param[in] ProcessToken 2167 * A pointer to the primary access token of the server process 2168 * that requests impersonation of the client target. 2169 * 2170 * @param[in] TokenToImpersonate 2171 * A pointer to an access token that represents a client that is to 2172 * be impersonated. 2173 * 2174 * @param[in] ImpersonationLevel 2175 * The requested impersonation level. 2176 * 2177 * @return 2178 * Returns TRUE if the conditions checked are met for token impersonation, 2179 * FALSE otherwise. 2180 * 2181 * @remarks 2182 * The server has to meet the following criteria in order to impersonate 2183 * a client, that is: 2184 * 2185 * - The server must not impersonate a client beyond the level that 2186 * the client imposed on itself. 2187 * 2188 * - The server must be authenticated on the same logon session of 2189 * the target client. 2190 * 2191 * - IF NOT then the server's user ID has to match to that of the 2192 * target client. 2193 * 2194 * - The server must not be restricted in order to impersonate a 2195 * client that is not restricted. 2196 * 2197 * If the associated access token that represents the security properties 2198 * of the server is granted the SeImpersonatePrivilege privilege the server 2199 * is given immediate impersonation, regardless of the conditions above. 2200 * If the client in question is associated with an anonymous token then 2201 * the server is given immediate impersonation. Or if the server simply 2202 * doesn't ask for impersonation but instead it wants to get the security 2203 * identification of a client, the server is given immediate impersonation. 2204 */ 2205 BOOLEAN 2206 NTAPI 2207 SeTokenCanImpersonate( 2208 _In_ PTOKEN ProcessToken, 2209 _In_ PTOKEN TokenToImpersonate, 2210 _In_ SECURITY_IMPERSONATION_LEVEL ImpersonationLevel) 2211 { 2212 BOOLEAN CanImpersonate; 2213 PAGED_CODE(); 2214 2215 /* 2216 * The server may want to obtain identification details of a client 2217 * instead of impersonating so just give the server a pass. 2218 */ 2219 if (ImpersonationLevel < SecurityIdentification) 2220 { 2221 DPRINT("The server doesn't ask for impersonation\n"); 2222 return TRUE; 2223 } 2224 2225 /* Time to lock our tokens */ 2226 SepAcquireTokenLockShared(ProcessToken); 2227 SepAcquireTokenLockShared(TokenToImpersonate); 2228 2229 /* 2230 * As the name implies, an anonymous token has invisible security 2231 * identification details. By the general rule these tokens do not 2232 * pose a danger in terms of power escalation so give the server a pass. 2233 */ 2234 if (RtlEqualLuid(&TokenToImpersonate->AuthenticationId, 2235 &SeAnonymousAuthenticationId)) 2236 { 2237 DPRINT("The token to impersonate has an anonymous authentication ID, allow impersonation either way\n"); 2238 CanImpersonate = TRUE; 2239 goto Quit; 2240 } 2241 2242 /* Allow impersonation for the process server if it's granted the impersonation privilege */ 2243 if ((ProcessToken->TokenFlags & TOKEN_HAS_IMPERSONATE_PRIVILEGE) != 0) 2244 { 2245 DPRINT("The process is granted the impersonation privilege, allow impersonation\n"); 2246 CanImpersonate = TRUE; 2247 goto Quit; 2248 } 2249 2250 /* 2251 * Deny impersonation for the server if it wants to impersonate a client 2252 * beyond what the impersonation level originally permits. 2253 */ 2254 if (ImpersonationLevel > TokenToImpersonate->ImpersonationLevel) 2255 { 2256 DPRINT1("Cannot impersonate a client above the permitted impersonation level!\n"); 2257 CanImpersonate = FALSE; 2258 goto Quit; 2259 } 2260 2261 /* Is the server authenticated on the same client originating session? */ 2262 if (!RtlEqualLuid(&ProcessToken->AuthenticationId, 2263 &TokenToImpersonate->OriginatingLogonSession)) 2264 { 2265 /* It's not, check that at least both the server and client are the same user */ 2266 if (!RtlEqualSid(ProcessToken->UserAndGroups[0].Sid, 2267 TokenToImpersonate->UserAndGroups[0].Sid)) 2268 { 2269 DPRINT1("Server and client aren't the same user!\n"); 2270 CanImpersonate = FALSE; 2271 goto Quit; 2272 } 2273 2274 /* 2275 * Make sure the tokens haven't diverged in terms of restrictions 2276 * that is one token is restricted but the other one isn't. If that 2277 * would have been the case then the server would have impersonated 2278 * a less restricted client thus potentially triggering an elevation, 2279 * which is not what we want. 2280 */ 2281 if (SeTokenIsRestricted(ProcessToken) != 2282 SeTokenIsRestricted(TokenToImpersonate)) 2283 { 2284 DPRINT1("Attempting to impersonate a less restricted client token, bail out!\n"); 2285 CanImpersonate = FALSE; 2286 goto Quit; 2287 } 2288 } 2289 2290 /* If we've reached that far then we can impersonate! */ 2291 DPRINT("We can impersonate\n"); 2292 CanImpersonate = TRUE; 2293 2294 Quit: 2295 SepReleaseTokenLock(TokenToImpersonate); 2296 SepReleaseTokenLock(ProcessToken); 2297 return CanImpersonate; 2298 } 2299 2300 /* SYSTEM CALLS ***************************************************************/ 2301 2302 /** 2303 * @brief 2304 * Opens a token that is tied to a thread handle. 2305 * 2306 * @param[out] ThreadHandle 2307 * Thread handle where the token is about to be opened. 2308 * 2309 * @param[in] DesiredAccess 2310 * The request access right for the token. 2311 * 2312 * @param[in] OpenAsSelf 2313 * If set to TRUE, the access check will be made with the security context 2314 * of the process of the calling thread (opening as self). Otherwise the access 2315 * check will be made with the security context of the calling thread instead. 2316 * 2317 * @param[in] HandleAttributes 2318 * Handle attributes for the opened thread token handle. 2319 * 2320 * @param[out] TokenHandle 2321 * The opened token handle returned to the caller for use. 2322 * 2323 * @return 2324 * Returns STATUS_SUCCESS if the function has successfully opened the thread 2325 * token. STATUS_CANT_OPEN_ANONYMOUS is returned if a token has SecurityAnonymous 2326 * as impersonation level and we cannot open it. A failure NTSTATUS code is returned 2327 * otherwise. 2328 */ 2329 NTSTATUS 2330 NTAPI 2331 NtOpenThreadTokenEx( 2332 _In_ HANDLE ThreadHandle, 2333 _In_ ACCESS_MASK DesiredAccess, 2334 _In_ BOOLEAN OpenAsSelf, 2335 _In_ ULONG HandleAttributes, 2336 _Out_ PHANDLE TokenHandle) 2337 { 2338 PETHREAD Thread; 2339 HANDLE hToken; 2340 PTOKEN Token; 2341 BOOLEAN CopyOnOpen, EffectiveOnly; 2342 SECURITY_IMPERSONATION_LEVEL ImpersonationLevel; 2343 SE_IMPERSONATION_STATE ImpersonationState; 2344 KPROCESSOR_MODE PreviousMode; 2345 NTSTATUS Status; 2346 BOOLEAN RestoreImpersonation = FALSE; 2347 2348 PAGED_CODE(); 2349 2350 PreviousMode = ExGetPreviousMode(); 2351 2352 /* Ensure that we can give the handle to the caller */ 2353 if (PreviousMode != KernelMode) 2354 { 2355 _SEH2_TRY 2356 { 2357 ProbeForWriteHandle(TokenHandle); 2358 } 2359 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 2360 { 2361 /* Return the exception code */ 2362 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 2363 } 2364 _SEH2_END; 2365 } 2366 2367 /* Validate object attributes */ 2368 HandleAttributes = ObpValidateAttributes(HandleAttributes, PreviousMode); 2369 2370 /* 2371 * At first open the thread token for information access and verify 2372 * that the token associated with the thread is valid. 2373 */ 2374 Status = ObReferenceObjectByHandle(ThreadHandle, THREAD_QUERY_INFORMATION, 2375 PsThreadType, PreviousMode, (PVOID*)&Thread, 2376 NULL); 2377 if (!NT_SUCCESS(Status)) 2378 { 2379 DPRINT1("Failed to reference the object thread (Status 0x%lx)\n", Status); 2380 return Status; 2381 } 2382 2383 /* Reference the token from the thread */ 2384 Token = PsReferenceImpersonationToken(Thread, &CopyOnOpen, &EffectiveOnly, 2385 &ImpersonationLevel); 2386 if (Token == NULL) 2387 { 2388 DPRINT("Failed to reference the thread's impersonation token, thread has no token\n"); 2389 ObDereferenceObject(Thread); 2390 return STATUS_NO_TOKEN; 2391 } 2392 2393 /* Ensure the token has no anonymous security */ 2394 if (ImpersonationLevel == SecurityAnonymous) 2395 { 2396 DPRINT1("The thread token has anonymous security, can't open it\n"); 2397 PsDereferenceImpersonationToken(Token); 2398 ObDereferenceObject(Thread); 2399 return STATUS_CANT_OPEN_ANONYMOUS; 2400 } 2401 2402 /* Revert to self if OpenAsSelf is specified */ 2403 if (OpenAsSelf) 2404 { 2405 RestoreImpersonation = PsDisableImpersonation(PsGetCurrentThread(), 2406 &ImpersonationState); 2407 } 2408 2409 /* Call the private function to do the job */ 2410 Status = SepOpenThreadToken(Thread, 2411 ThreadHandle, 2412 Token, 2413 DesiredAccess, 2414 HandleAttributes, 2415 EffectiveOnly, 2416 CopyOnOpen, 2417 ImpersonationLevel, 2418 PreviousMode, 2419 &hToken); 2420 2421 /* Restore the impersonation back if needed */ 2422 if (RestoreImpersonation) 2423 { 2424 PsRestoreImpersonation(PsGetCurrentThread(), &ImpersonationState); 2425 } 2426 2427 /* Dereference the access token and the associated thread */ 2428 ObDereferenceObject(Token); 2429 ObDereferenceObject(Thread); 2430 2431 if (!NT_SUCCESS(Status)) 2432 { 2433 DPRINT1("Failed to open the thread's token (Status 0x%lx)\n", Status); 2434 return Status; 2435 } 2436 2437 /* Give the opened token handle to the caller */ 2438 _SEH2_TRY 2439 { 2440 *TokenHandle = hToken; 2441 } 2442 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 2443 { 2444 Status = _SEH2_GetExceptionCode(); 2445 } 2446 _SEH2_END; 2447 2448 return Status; 2449 } 2450 2451 /** 2452 * @brief 2453 * Opens a token that is tied to a thread handle. 2454 * 2455 * @param[out] ThreadHandle 2456 * Thread handle where the token is about to be opened. 2457 * 2458 * @param[in] DesiredAccess 2459 * The request access right for the token. 2460 * 2461 * @param[in] OpenAsSelf 2462 * If set to TRUE, the access check will be made with the security context 2463 * of the process of the calling thread (opening as self). Otherwise the access 2464 * check will be made with the security context of the calling thread instead. 2465 * 2466 * @param[out] TokenHandle 2467 * The opened token handle returned to the caller for use. 2468 * 2469 * @return 2470 * See NtOpenThreadTokenEx. 2471 */ 2472 NTSTATUS 2473 NTAPI 2474 NtOpenThreadToken( 2475 _In_ HANDLE ThreadHandle, 2476 _In_ ACCESS_MASK DesiredAccess, 2477 _In_ BOOLEAN OpenAsSelf, 2478 _Out_ PHANDLE TokenHandle) 2479 { 2480 return NtOpenThreadTokenEx(ThreadHandle, DesiredAccess, OpenAsSelf, 0, 2481 TokenHandle); 2482 } 2483 2484 /** 2485 * @brief 2486 * Compares tokens if they're equal or not. 2487 * 2488 * @param[in] FirstToken 2489 * The first token. 2490 * 2491 * @param[in] SecondToken 2492 * The second token. 2493 * 2494 * @param[out] Equal 2495 * The retrieved value which determines if the tokens are 2496 * equal or not. 2497 * 2498 * @return 2499 * Returns STATUS_SUCCESS, otherwise it returns a failure NTSTATUS code. 2500 */ 2501 NTSTATUS 2502 NTAPI 2503 NtCompareTokens( 2504 _In_ HANDLE FirstTokenHandle, 2505 _In_ HANDLE SecondTokenHandle, 2506 _Out_ PBOOLEAN Equal) 2507 { 2508 KPROCESSOR_MODE PreviousMode; 2509 PTOKEN FirstToken, SecondToken; 2510 BOOLEAN IsEqual; 2511 NTSTATUS Status; 2512 2513 PAGED_CODE(); 2514 2515 PreviousMode = ExGetPreviousMode(); 2516 2517 if (PreviousMode != KernelMode) 2518 { 2519 _SEH2_TRY 2520 { 2521 ProbeForWriteBoolean(Equal); 2522 } 2523 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 2524 { 2525 /* Return the exception code */ 2526 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 2527 } 2528 _SEH2_END; 2529 } 2530 2531 Status = ObReferenceObjectByHandle(FirstTokenHandle, 2532 TOKEN_QUERY, 2533 SeTokenObjectType, 2534 PreviousMode, 2535 (PVOID*)&FirstToken, 2536 NULL); 2537 if (!NT_SUCCESS(Status)) 2538 { 2539 DPRINT1("ObReferenceObjectByHandle() failed (Status 0x%lx)\n", Status); 2540 return Status; 2541 } 2542 2543 Status = ObReferenceObjectByHandle(SecondTokenHandle, 2544 TOKEN_QUERY, 2545 SeTokenObjectType, 2546 PreviousMode, 2547 (PVOID*)&SecondToken, 2548 NULL); 2549 if (!NT_SUCCESS(Status)) 2550 { 2551 DPRINT1("ObReferenceObjectByHandle() failed (Status 0x%lx)\n", Status); 2552 ObDereferenceObject(FirstToken); 2553 return Status; 2554 } 2555 2556 if (FirstToken != SecondToken) 2557 { 2558 Status = SepCompareTokens(FirstToken, 2559 SecondToken, 2560 &IsEqual); 2561 } 2562 else 2563 { 2564 IsEqual = TRUE; 2565 } 2566 2567 ObDereferenceObject(SecondToken); 2568 ObDereferenceObject(FirstToken); 2569 2570 if (NT_SUCCESS(Status)) 2571 { 2572 _SEH2_TRY 2573 { 2574 *Equal = IsEqual; 2575 } 2576 _SEH2_EXCEPT(ExSystemExceptionFilter()) 2577 { 2578 Status = _SEH2_GetExceptionCode(); 2579 } 2580 _SEH2_END; 2581 } 2582 2583 return Status; 2584 } 2585 2586 /** 2587 * @brief 2588 * Allows the calling thread to impersonate the system's anonymous 2589 * logon token. 2590 * 2591 * @param[in] ThreadHandle 2592 * A handle to the thread to start the procedure of logon token 2593 * impersonation. The thread must have the THREAD_IMPERSONATE 2594 * access right. 2595 * 2596 * @return 2597 * Returns STATUS_SUCCESS if the thread has successfully impersonated the 2598 * anonymous logon token, otherwise a failure NTSTATUS code is returned. 2599 * 2600 * @remarks 2601 * By default the system gives the opportunity to the caller to impersonate 2602 * the anonymous logon token without including the Everyone Group SID. 2603 * In cases where the caller wants to impersonate the token including such 2604 * group, the EveryoneIncludesAnonymous registry value setting has to be set 2605 * to 1, from HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa registry 2606 * path. The calling thread must invoke PsRevertToSelf when impersonation 2607 * is no longer needed or RevertToSelf if the calling execution is done 2608 * in user mode. 2609 */ 2610 NTSTATUS 2611 NTAPI 2612 NtImpersonateAnonymousToken( 2613 _In_ HANDLE ThreadHandle) 2614 { 2615 PETHREAD Thread; 2616 KPROCESSOR_MODE PreviousMode; 2617 NTSTATUS Status; 2618 PAGED_CODE(); 2619 2620 PreviousMode = ExGetPreviousMode(); 2621 2622 /* Obtain the thread object from the handle */ 2623 Status = ObReferenceObjectByHandle(ThreadHandle, 2624 THREAD_IMPERSONATE, 2625 PsThreadType, 2626 PreviousMode, 2627 (PVOID*)&Thread, 2628 NULL); 2629 if (!NT_SUCCESS(Status)) 2630 { 2631 DPRINT1("NtImpersonateAnonymousToken(): Failed to reference the object (Status 0x%lx)\n", Status); 2632 return Status; 2633 } 2634 2635 /* Call the private routine to impersonate the token */ 2636 Status = SepImpersonateAnonymousToken(Thread, PreviousMode); 2637 if (!NT_SUCCESS(Status)) 2638 { 2639 DPRINT1("NtImpersonateAnonymousToken(): Failed to impersonate the token (Status 0x%lx)\n", Status); 2640 } 2641 2642 ObDereferenceObject(Thread); 2643 return Status; 2644 } 2645 2646 /* EOF */ 2647