1 /* 2 * PROJECT: ReactOS Kernel 3 * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) 4 * PURPOSE: Security token implementation support 5 * COPYRIGHT: Copyright David Welch <welch@cwcom.net> 6 * Copyright 2021-2022 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 #include <ntlsa.h> 16 17 /* GLOBALS ********************************************************************/ 18 19 POBJECT_TYPE SeTokenObjectType = NULL; 20 21 TOKEN_SOURCE SeSystemTokenSource = {"*SYSTEM*", {0}}; 22 LUID SeSystemAuthenticationId = SYSTEM_LUID; 23 LUID SeAnonymousAuthenticationId = ANONYMOUS_LOGON_LUID; 24 25 static GENERIC_MAPPING SepTokenMapping = { 26 TOKEN_READ, 27 TOKEN_WRITE, 28 TOKEN_EXECUTE, 29 TOKEN_ALL_ACCESS 30 }; 31 32 static const INFORMATION_CLASS_INFO SeTokenInformationClass[] = { 33 34 /* Class 0 not used, blame MS! */ 35 IQS_NONE, 36 37 /* TokenUser */ 38 IQS_SAME(TOKEN_USER, ULONG, ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE), 39 /* TokenGroups */ 40 IQS_SAME(TOKEN_GROUPS, ULONG, ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE), 41 /* TokenPrivileges */ 42 IQS_SAME(TOKEN_PRIVILEGES, ULONG, ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE), 43 /* TokenOwner */ 44 IQS_SAME(TOKEN_OWNER, ULONG, ICIF_QUERY | ICIF_SET | ICIF_SIZE_VARIABLE), 45 /* TokenPrimaryGroup */ 46 IQS_SAME(TOKEN_PRIMARY_GROUP, ULONG, ICIF_QUERY | ICIF_SET | ICIF_SIZE_VARIABLE), 47 /* TokenDefaultDacl */ 48 IQS_SAME(TOKEN_DEFAULT_DACL, ULONG, ICIF_QUERY | ICIF_SET | ICIF_SIZE_VARIABLE), 49 /* TokenSource */ 50 IQS_SAME(TOKEN_SOURCE, ULONG, ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE), 51 /* TokenType */ 52 IQS_SAME(TOKEN_TYPE, ULONG, ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE), 53 /* TokenImpersonationLevel */ 54 IQS_SAME(SECURITY_IMPERSONATION_LEVEL, ULONG, ICIF_QUERY), 55 /* TokenStatistics */ 56 IQS_SAME(TOKEN_STATISTICS, ULONG, ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE), 57 /* TokenRestrictedSids */ 58 IQS_SAME(TOKEN_GROUPS, ULONG, ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE), 59 /* TokenSessionId */ 60 IQS_SAME(ULONG, ULONG, ICIF_QUERY | ICIF_SET), 61 /* TokenGroupsAndPrivileges */ 62 IQS_SAME(TOKEN_GROUPS_AND_PRIVILEGES, ULONG, ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE), 63 /* TokenSessionReference */ 64 IQS_SAME(ULONG, ULONG, ICIF_SET), 65 /* TokenSandBoxInert */ 66 IQS_SAME(ULONG, ULONG, ICIF_QUERY), 67 /* TokenAuditPolicy */ 68 IQS_SAME(TOKEN_AUDIT_POLICY_INFORMATION, ULONG, ICIF_SET | ICIF_SET_SIZE_VARIABLE), 69 /* TokenOrigin */ 70 IQS_SAME(TOKEN_ORIGIN, ULONG, ICIF_QUERY | ICIF_SET), 71 }; 72 73 /* FUNCTIONS *****************************************************************/ 74 75 /** 76 * @brief 77 * Creates a lock for the token. 78 * 79 * @param[in,out] Token 80 * A token which lock has to be created. 81 * 82 * @return 83 * STATUS_SUCCESS if the pool allocation and resource initialisation have 84 * completed successfully, otherwise STATUS_INSUFFICIENT_RESOURCES on a 85 * pool allocation failure. 86 */ 87 static 88 NTSTATUS 89 SepCreateTokenLock( 90 _Inout_ PTOKEN Token) 91 { 92 PAGED_CODE(); 93 94 Token->TokenLock = ExAllocatePoolWithTag(NonPagedPool, 95 sizeof(ERESOURCE), 96 TAG_SE_TOKEN_LOCK); 97 if (Token->TokenLock == NULL) 98 { 99 DPRINT1("SepCreateTokenLock(): Failed to allocate memory!\n"); 100 return STATUS_INSUFFICIENT_RESOURCES; 101 } 102 103 ExInitializeResourceLite(Token->TokenLock); 104 return STATUS_SUCCESS; 105 } 106 107 /** 108 * @brief 109 * Deletes a lock of a token. 110 * 111 * @param[in,out] Token 112 * A token which contains the lock. 113 * 114 * @return 115 * Nothing. 116 */ 117 static 118 VOID 119 SepDeleteTokenLock( 120 _Inout_ PTOKEN Token) 121 { 122 PAGED_CODE(); 123 124 ExDeleteResourceLite(Token->TokenLock); 125 ExFreePoolWithTag(Token->TokenLock, TAG_SE_TOKEN_LOCK); 126 } 127 128 /** 129 * @brief 130 * Compares the elements of SID arrays provided by tokens. 131 * The elements that are being compared for equality are 132 * the SIDs and their attributes. 133 * 134 * @param[in] SidArrayToken1 135 * SID array from the first token. 136 * 137 * @param[in] CountSidArray1 138 * SID count array from the first token. 139 * 140 * @param[in] SidArrayToken2 141 * SID array from the second token. 142 * 143 * @param[in] CountSidArray2 144 * SID count array from the second token. 145 * 146 * @return 147 * Returns TRUE if the elements match from either arrays, 148 * FALSE otherwise. 149 */ 150 static 151 BOOLEAN 152 SepCompareSidAndAttributesFromTokens( 153 _In_ PSID_AND_ATTRIBUTES SidArrayToken1, 154 _In_ ULONG CountSidArray1, 155 _In_ PSID_AND_ATTRIBUTES SidArrayToken2, 156 _In_ ULONG CountSidArray2) 157 { 158 ULONG FirstCount, SecondCount; 159 PSID_AND_ATTRIBUTES FirstSidArray, SecondSidArray; 160 PAGED_CODE(); 161 162 /* Bail out if index counters provided are not equal */ 163 if (CountSidArray1 != CountSidArray2) 164 { 165 DPRINT("SepCompareSidAndAttributesFromTokens(): Index counters are not the same!\n"); 166 return FALSE; 167 } 168 169 /* Loop over the SID arrays and compare them */ 170 for (FirstCount = 0; FirstCount < CountSidArray1; FirstCount++) 171 { 172 for (SecondCount = 0; SecondCount < CountSidArray2; SecondCount++) 173 { 174 FirstSidArray = &SidArrayToken1[FirstCount]; 175 SecondSidArray = &SidArrayToken2[SecondCount]; 176 177 if (RtlEqualSid(FirstSidArray->Sid, SecondSidArray->Sid) && 178 FirstSidArray->Attributes == SecondSidArray->Attributes) 179 { 180 break; 181 } 182 } 183 184 /* We've exhausted the array of the second token without finding this one */ 185 if (SecondCount == CountSidArray2) 186 { 187 DPRINT("SepCompareSidAndAttributesFromTokens(): No matching elements could be found in either token!\n"); 188 return FALSE; 189 } 190 } 191 192 return TRUE; 193 } 194 195 /** 196 * @brief 197 * Compares the elements of privilege arrays provided by tokens. 198 * The elements that are being compared for equality are 199 * the privileges and their attributes. 200 * 201 * @param[in] PrivArrayToken1 202 * Privilege array from the first token. 203 * 204 * @param[in] CountPrivArray1 205 * Privilege count array from the first token. 206 * 207 * @param[in] PrivArrayToken2 208 * Privilege array from the second token. 209 * 210 * @param[in] CountPrivArray2 211 * Privilege count array from the second token. 212 * 213 * @return 214 * Returns TRUE if the elements match from either arrays, 215 * FALSE otherwise. 216 */ 217 static 218 BOOLEAN 219 SepComparePrivilegeAndAttributesFromTokens( 220 _In_ PLUID_AND_ATTRIBUTES PrivArrayToken1, 221 _In_ ULONG CountPrivArray1, 222 _In_ PLUID_AND_ATTRIBUTES PrivArrayToken2, 223 _In_ ULONG CountPrivArray2) 224 { 225 ULONG FirstCount, SecondCount; 226 PLUID_AND_ATTRIBUTES FirstPrivArray, SecondPrivArray; 227 PAGED_CODE(); 228 229 /* Bail out if index counters provided are not equal */ 230 if (CountPrivArray1 != CountPrivArray2) 231 { 232 DPRINT("SepComparePrivilegeAndAttributesFromTokens(): Index counters are not the same!\n"); 233 return FALSE; 234 } 235 236 /* Loop over the privilege arrays and compare them */ 237 for (FirstCount = 0; FirstCount < CountPrivArray1; FirstCount++) 238 { 239 for (SecondCount = 0; SecondCount < CountPrivArray2; SecondCount++) 240 { 241 FirstPrivArray = &PrivArrayToken1[FirstCount]; 242 SecondPrivArray = &PrivArrayToken2[SecondCount]; 243 244 if (RtlEqualLuid(&FirstPrivArray->Luid, &SecondPrivArray->Luid) && 245 FirstPrivArray->Attributes == SecondPrivArray->Attributes) 246 { 247 break; 248 } 249 } 250 251 /* We've exhausted the array of the second token without finding this one */ 252 if (SecondCount == CountPrivArray2) 253 { 254 DPRINT("SepComparePrivilegeAndAttributesFromTokens(): No matching elements could be found in either token!\n"); 255 return FALSE; 256 } 257 } 258 259 return TRUE; 260 } 261 262 /** 263 * @brief 264 * Compares tokens if they're equal based on all the following properties. If all 265 * of the said conditions are met then the tokens are deemed as equal. 266 * 267 * - Every SID that is present in either token is also present in the other one. 268 * - Both or none of the tokens are restricted. 269 * - If both tokens are restricted, every SID that is restricted in either token is 270 * also restricted in the other one. 271 * - Every privilege present in either token is also present in the other one. 272 * 273 * @param[in] FirstToken 274 * The first token. 275 * 276 * @param[in] SecondToken 277 * The second token. 278 * 279 * @param[out] Equal 280 * The retrieved value which determines if the tokens are 281 * equal or not. 282 * 283 * @return 284 * Returns STATUS_SUCCESS. 285 */ 286 static 287 NTSTATUS 288 SepCompareTokens( 289 _In_ PTOKEN FirstToken, 290 _In_ PTOKEN SecondToken, 291 _Out_ PBOOLEAN Equal) 292 { 293 BOOLEAN Restricted, IsEqual = FALSE; 294 PAGED_CODE(); 295 296 ASSERT(FirstToken != SecondToken); 297 298 /* Lock the tokens */ 299 SepAcquireTokenLockShared(FirstToken); 300 SepAcquireTokenLockShared(SecondToken); 301 302 /* Check if every SID that is present in either token is also present in the other one */ 303 if (!SepCompareSidAndAttributesFromTokens(FirstToken->UserAndGroups, 304 FirstToken->UserAndGroupCount, 305 SecondToken->UserAndGroups, 306 SecondToken->UserAndGroupCount)) 307 { 308 goto Quit; 309 } 310 311 /* Is one token restricted but the other isn't? */ 312 Restricted = SeTokenIsRestricted(FirstToken); 313 if (Restricted != SeTokenIsRestricted(SecondToken)) 314 { 315 /* If that's the case then bail out */ 316 goto Quit; 317 } 318 319 /* 320 * If both tokens are restricted check if every SID 321 * that is restricted in either token is also restricted 322 * in the other one. 323 */ 324 if (Restricted) 325 { 326 if (!SepCompareSidAndAttributesFromTokens(FirstToken->RestrictedSids, 327 FirstToken->RestrictedSidCount, 328 SecondToken->RestrictedSids, 329 SecondToken->RestrictedSidCount)) 330 { 331 goto Quit; 332 } 333 } 334 335 /* Check if every privilege present in either token is also present in the other one */ 336 if (!SepComparePrivilegeAndAttributesFromTokens(FirstToken->Privileges, 337 FirstToken->PrivilegeCount, 338 SecondToken->Privileges, 339 SecondToken->PrivilegeCount)) 340 { 341 goto Quit; 342 } 343 344 /* If we're here then the tokens are equal */ 345 IsEqual = TRUE; 346 DPRINT("SepCompareTokens(): Tokens are equal!\n"); 347 348 Quit: 349 /* Unlock the tokens */ 350 SepReleaseTokenLock(SecondToken); 351 SepReleaseTokenLock(FirstToken); 352 353 *Equal = IsEqual; 354 return STATUS_SUCCESS; 355 } 356 357 /** 358 * @brief 359 * Private function that impersonates the system's anonymous logon token. 360 * The major bulk of the impersonation procedure is done here. 361 * 362 * @param[in] Thread 363 * The executive thread object that is to impersonate the client. 364 * 365 * @param[in] PreviousMode 366 * The access processor mode, indicating if the call is executed 367 * in kernel or user mode. 368 * 369 * @return 370 * Returns STATUS_SUCCESS if the impersonation has succeeded. 371 * STATUS_UNSUCCESSFUL is returned if the primary token couldn't be 372 * obtained from the current process to perform additional tasks. 373 * STATUS_ACCESS_DENIED is returned if the process' primary token is 374 * restricted, which for this matter we cannot impersonate onto a 375 * restricted process. Otherwise a failure NTSTATUS code is returned. 376 */ 377 static 378 NTSTATUS 379 SepImpersonateAnonymousToken( 380 _In_ PETHREAD Thread, 381 _In_ KPROCESSOR_MODE PreviousMode) 382 { 383 NTSTATUS Status; 384 PTOKEN TokenToImpersonate, ProcessToken; 385 ULONG IncludeEveryoneValueData; 386 PAGED_CODE(); 387 388 /* 389 * We must check first which kind of token 390 * shall we assign for the thread to impersonate, 391 * the one with Everyone Group SID or the other 392 * without. Invoke the registry helper to 393 * return the data value for us. 394 */ 395 Status = SepRegQueryHelper(L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\Control\\Lsa", 396 L"EveryoneIncludesAnonymous", 397 REG_DWORD, 398 sizeof(IncludeEveryoneValueData), 399 &IncludeEveryoneValueData); 400 if (!NT_SUCCESS(Status)) 401 { 402 DPRINT1("SepRegQueryHelper(): Failed to query the registry value (Status 0x%lx)\n", Status); 403 return Status; 404 } 405 406 if (IncludeEveryoneValueData == 0) 407 { 408 DPRINT("SepImpersonateAnonymousToken(): Assigning the token not including the Everyone Group SID...\n"); 409 TokenToImpersonate = SeAnonymousLogonTokenNoEveryone; 410 } 411 else 412 { 413 DPRINT("SepImpersonateAnonymousToken(): Assigning the token including the Everyone Group SID...\n"); 414 TokenToImpersonate = SeAnonymousLogonToken; 415 } 416 417 /* 418 * Tell the object manager that we're going to use this token 419 * object now by incrementing the reference count. 420 */ 421 Status = ObReferenceObjectByPointer(TokenToImpersonate, 422 TOKEN_IMPERSONATE, 423 SeTokenObjectType, 424 PreviousMode); 425 if (!NT_SUCCESS(Status)) 426 { 427 DPRINT1("SepImpersonateAnonymousToken(): Couldn't be able to use the token, bail out...\n"); 428 return Status; 429 } 430 431 /* 432 * Reference the primary token of the current process that the anonymous 433 * logon token impersonation procedure is being performed. We'll be going 434 * to use the process' token to figure out if the process is actually 435 * restricted or not. 436 */ 437 ProcessToken = PsReferencePrimaryToken(PsGetCurrentProcess()); 438 if (!ProcessToken) 439 { 440 DPRINT1("SepImpersonateAnonymousToken(): Couldn't be able to get the process' primary token, bail out...\n"); 441 ObDereferenceObject(TokenToImpersonate); 442 return STATUS_UNSUCCESSFUL; 443 } 444 445 /* Now, is the token from the current process restricted? */ 446 if (SeTokenIsRestricted(ProcessToken)) 447 { 448 DPRINT1("SepImpersonateAnonymousToken(): The process is restricted, can't do anything. Bail out...\n"); 449 PsDereferencePrimaryToken(ProcessToken); 450 ObDereferenceObject(TokenToImpersonate); 451 return STATUS_ACCESS_DENIED; 452 } 453 454 /* 455 * Finally it's time to impersonate! But first, fast dereference the 456 * process' primary token as we no longer need it. 457 */ 458 ObFastDereferenceObject(&PsGetCurrentProcess()->Token, ProcessToken); 459 Status = PsImpersonateClient(Thread, TokenToImpersonate, TRUE, FALSE, SecurityImpersonation); 460 if (!NT_SUCCESS(Status)) 461 { 462 DPRINT1("SepImpersonateAnonymousToken(): Failed to impersonate, bail out...\n"); 463 ObDereferenceObject(TokenToImpersonate); 464 return Status; 465 } 466 467 return Status; 468 } 469 470 /** 471 * @brief 472 * Updates the token's flags based upon the privilege that the token 473 * has been granted. The flag can either be taken out or given to the token 474 * if the attributes of the specified privilege is enabled or not. 475 * 476 * @param[in,out] Token 477 * The token where the flags are to be changed. 478 * 479 * @param[in] Index 480 * The index count which represents the total sum of privileges. The count in question 481 * MUST NOT exceed the expected privileges count of the token. 482 * 483 * @return 484 * Nothing. 485 */ 486 static 487 VOID 488 SepUpdateSinglePrivilegeFlagToken( 489 _Inout_ PTOKEN Token, 490 _In_ ULONG Index) 491 { 492 ULONG TokenFlag; 493 ASSERT(Index < Token->PrivilegeCount); 494 495 /* The high part of all values we are interested in is 0 */ 496 if (Token->Privileges[Index].Luid.HighPart != 0) 497 { 498 return; 499 } 500 501 /* Check for certain privileges to update flags */ 502 if (Token->Privileges[Index].Luid.LowPart == SE_CHANGE_NOTIFY_PRIVILEGE) 503 { 504 TokenFlag = TOKEN_HAS_TRAVERSE_PRIVILEGE; 505 } 506 else if (Token->Privileges[Index].Luid.LowPart == SE_BACKUP_PRIVILEGE) 507 { 508 TokenFlag = TOKEN_HAS_BACKUP_PRIVILEGE; 509 } 510 else if (Token->Privileges[Index].Luid.LowPart == SE_RESTORE_PRIVILEGE) 511 { 512 TokenFlag = TOKEN_HAS_RESTORE_PRIVILEGE; 513 } 514 else if (Token->Privileges[Index].Luid.LowPart == SE_IMPERSONATE_PRIVILEGE) 515 { 516 TokenFlag = TOKEN_HAS_IMPERSONATE_PRIVILEGE; 517 } 518 else 519 { 520 /* Nothing to do */ 521 return; 522 } 523 524 /* Check if the specified privilege is enabled */ 525 if (Token->Privileges[Index].Attributes & SE_PRIVILEGE_ENABLED) 526 { 527 /* It is enabled, so set the flag */ 528 Token->TokenFlags |= TokenFlag; 529 } 530 else 531 { 532 /* Is is disabled, so remove the flag */ 533 Token->TokenFlags &= ~TokenFlag; 534 } 535 } 536 537 /** 538 * @brief 539 * Updates the token's flags based upon the privilege that the token 540 * has been granted. The function uses the private helper, SepUpdateSinglePrivilegeFlagToken, 541 * in order to update the flags of a token. 542 * 543 * @param[in,out] Token 544 * The token where the flags are to be changed. 545 * 546 * @return 547 * Nothing. 548 */ 549 static 550 VOID 551 SepUpdatePrivilegeFlagsToken( 552 _Inout_ PTOKEN Token) 553 { 554 ULONG i; 555 556 /* Loop all privileges */ 557 for (i = 0; i < Token->PrivilegeCount; i++) 558 { 559 /* Updates the flags for this privilege */ 560 SepUpdateSinglePrivilegeFlagToken(Token, i); 561 } 562 } 563 564 /** 565 * @brief 566 * Removes a privilege from the token. 567 * 568 * @param[in,out] Token 569 * The token where the privilege is to be removed. 570 * 571 * @param[in] Index 572 * The index count which represents the number position of the privilege 573 * we want to remove. 574 * 575 * @return 576 * Nothing. 577 */ 578 static 579 VOID 580 SepRemovePrivilegeToken( 581 _Inout_ PTOKEN Token, 582 _In_ ULONG Index) 583 { 584 ULONG MoveCount; 585 ASSERT(Index < Token->PrivilegeCount); 586 587 /* Calculate the number of trailing privileges */ 588 MoveCount = Token->PrivilegeCount - Index - 1; 589 if (MoveCount != 0) 590 { 591 /* Move them one location ahead */ 592 RtlMoveMemory(&Token->Privileges[Index], 593 &Token->Privileges[Index + 1], 594 MoveCount * sizeof(LUID_AND_ATTRIBUTES)); 595 } 596 597 /* Update privilege count */ 598 Token->PrivilegeCount--; 599 } 600 601 /** 602 * @brief 603 * Removes a group from the token. 604 * 605 * @param[in,out] Token 606 * The token where the group is to be removed. 607 * 608 * @param[in] Index 609 * The index count which represents the number position of the group 610 * we want to remove. 611 * 612 * @return 613 * Nothing. 614 */ 615 static 616 VOID 617 SepRemoveUserGroupToken( 618 _Inout_ PTOKEN Token, 619 _In_ ULONG Index) 620 { 621 ULONG MoveCount; 622 ASSERT(Index < Token->UserAndGroupCount); 623 624 /* Calculate the number of trailing groups */ 625 MoveCount = Token->UserAndGroupCount - Index - 1; 626 if (MoveCount != 0) 627 { 628 /* Time to remove the group by moving one location ahead */ 629 RtlMoveMemory(&Token->UserAndGroups[Index], 630 &Token->UserAndGroups[Index + 1], 631 MoveCount * sizeof(SID_AND_ATTRIBUTES)); 632 } 633 634 /* Remove one group count */ 635 Token->UserAndGroupCount--; 636 } 637 638 /** 639 * @unimplemented 640 * @brief 641 * Frees (de-allocates) the proxy data memory block of a token. 642 * 643 * @param[in,out] ProxyData 644 * The proxy data to be freed. 645 * 646 * @return 647 * Nothing. 648 */ 649 VOID 650 NTAPI 651 SepFreeProxyData( 652 _Inout_ PVOID ProxyData) 653 { 654 UNIMPLEMENTED; 655 } 656 657 /** 658 * @unimplemented 659 * @brief 660 * Copies the proxy data from the source into the destination of a token. 661 * 662 * @param[out] Dest 663 * The destination path where the proxy data is to be copied to. 664 * 665 * @param[in] Src 666 * The source path where the proxy data is be copied from. 667 * 668 * @return 669 * To be added... 670 */ 671 NTSTATUS 672 NTAPI 673 SepCopyProxyData( 674 _Out_ PVOID* Dest, 675 _In_ PVOID Src) 676 { 677 UNIMPLEMENTED; 678 return STATUS_NOT_IMPLEMENTED; 679 } 680 681 /** 682 * @brief 683 * Replaces the old access token of a process (pointed by the EPROCESS kernel structure) with a 684 * new access token. The new access token must be a primary token for use. 685 * 686 * @param[in] Process 687 * The process instance where its access token is about to be replaced. 688 * 689 * @param[in] NewAccessToken 690 * The new token that it's going to replace the old one. 691 * 692 * @param[out] OldAccessToken 693 * The returned old token that's been replaced, which the caller can do anything. 694 * 695 * @return 696 * Returns STATUS_SUCCESS if the exchange operation between tokens has completed successfully. 697 * STATUS_BAD_TOKEN_TYPE is returned if the new token is not a primary one so that we cannot 698 * exchange it with the old one from the process. STATUS_TOKEN_ALREADY_IN_USE is returned if 699 * both tokens aren't equal which means one of them has different properties (groups, privileges, etc.) 700 * and as such one of them is currently in use. A failure NTSTATUS code is returned otherwise. 701 */ 702 NTSTATUS 703 NTAPI 704 SeExchangePrimaryToken( 705 _In_ PEPROCESS Process, 706 _In_ PACCESS_TOKEN NewAccessToken, 707 _Out_ PACCESS_TOKEN* OldAccessToken) 708 { 709 PTOKEN OldToken; 710 PTOKEN NewToken = (PTOKEN)NewAccessToken; 711 712 PAGED_CODE(); 713 714 if (NewToken->TokenType != TokenPrimary) 715 return STATUS_BAD_TOKEN_TYPE; 716 717 if (NewToken->TokenInUse) 718 { 719 BOOLEAN IsEqual; 720 NTSTATUS Status; 721 722 /* Maybe we're trying to set the same token */ 723 OldToken = PsReferencePrimaryToken(Process); 724 if (OldToken == NewToken) 725 { 726 /* So it's a nop. */ 727 *OldAccessToken = OldToken; 728 return STATUS_SUCCESS; 729 } 730 731 Status = SepCompareTokens(OldToken, NewToken, &IsEqual); 732 if (!NT_SUCCESS(Status)) 733 { 734 PsDereferencePrimaryToken(OldToken); 735 *OldAccessToken = NULL; 736 return Status; 737 } 738 739 if (!IsEqual) 740 { 741 PsDereferencePrimaryToken(OldToken); 742 *OldAccessToken = NULL; 743 return STATUS_TOKEN_ALREADY_IN_USE; 744 } 745 /* Silently return STATUS_SUCCESS but do not set the new token, 746 * as it's already in use elsewhere. */ 747 *OldAccessToken = OldToken; 748 return STATUS_SUCCESS; 749 } 750 751 /* Lock the new token */ 752 SepAcquireTokenLockExclusive(NewToken); 753 754 /* Mark new token in use */ 755 NewToken->TokenInUse = TRUE; 756 757 /* Set the session ID for the new token */ 758 NewToken->SessionId = MmGetSessionId(Process); 759 760 /* Unlock the new token */ 761 SepReleaseTokenLock(NewToken); 762 763 /* Reference the new token */ 764 ObReferenceObject(NewToken); 765 766 /* Replace the old with the new */ 767 OldToken = ObFastReplaceObject(&Process->Token, NewToken); 768 769 /* Lock the old token */ 770 SepAcquireTokenLockExclusive(OldToken); 771 772 /* Mark the old token as free */ 773 OldToken->TokenInUse = FALSE; 774 775 /* Unlock the old token */ 776 SepReleaseTokenLock(OldToken); 777 778 *OldAccessToken = (PACCESS_TOKEN)OldToken; 779 return STATUS_SUCCESS; 780 } 781 782 /** 783 * @brief 784 * Removes the primary token of a process. 785 * 786 * @param[in,out] Process 787 * The process instance with the access token to be removed. 788 * 789 * @return 790 * Nothing. 791 */ 792 VOID 793 NTAPI 794 SeDeassignPrimaryToken( 795 _Inout_ PEPROCESS Process) 796 { 797 PTOKEN OldToken; 798 799 /* Remove the Token */ 800 OldToken = ObFastReplaceObject(&Process->Token, NULL); 801 802 /* Mark the Old Token as free */ 803 OldToken->TokenInUse = FALSE; 804 805 /* Dereference the Token */ 806 ObDereferenceObject(OldToken); 807 } 808 809 /** 810 * @brief 811 * Computes the length size of a SID. 812 * 813 * @param[in] Count 814 * Total count of entries that have SIDs in them (that being PSID_AND_ATTRIBUTES in this context). 815 * 816 * @param[in] Src 817 * Source that points to the attributes and SID entry structure. 818 * 819 * @return 820 * Returns the total length of a SID size. 821 */ 822 static ULONG 823 RtlLengthSidAndAttributes( 824 _In_ ULONG Count, 825 _In_ PSID_AND_ATTRIBUTES Src) 826 { 827 ULONG i; 828 ULONG uLength; 829 830 PAGED_CODE(); 831 832 uLength = Count * sizeof(SID_AND_ATTRIBUTES); 833 for (i = 0; i < Count; i++) 834 uLength += RtlLengthSid(Src[i].Sid); 835 836 return uLength; 837 } 838 839 /** 840 * @brief 841 * Finds the primary group and default owner entity based on the submitted primary group instance 842 * and an access token. 843 * 844 * @param[in] Token 845 * Access token to begin the search query of primary group and default owner. 846 * 847 * @param[in] PrimaryGroup 848 * A primary group SID to be used for search query, determining if user & groups of a token 849 * and the submitted primary group do match. 850 * 851 * @param[in] DefaultOwner 852 * The default owner. If specified, it's used to determine if the token belongs to the actual user, 853 * that is, being the owner himself. 854 * 855 * @param[out] PrimaryGroupIndex 856 * Returns the primary group index. 857 * 858 * @param[out] DefaultOwnerIndex 859 * Returns the default owner index. 860 * 861 * @return 862 * Returns STATUS_SUCCESS if the find query operation has completed successfully and that at least one 863 * search result is requested by the caller. STATUS_INVALID_PARAMETER is returned if the caller hasn't requested 864 * any search result. STATUS_INVALID_OWNER is returned if the specified default user owner does not match with the other 865 * user from the token. STATUS_INVALID_PRIMARY_GROUP is returned if the specified default primary group does not match with the 866 * other group from the token. 867 */ 868 static NTSTATUS 869 SepFindPrimaryGroupAndDefaultOwner( 870 _In_ PTOKEN Token, 871 _In_ PSID PrimaryGroup, 872 _In_opt_ PSID DefaultOwner, 873 _Out_opt_ PULONG PrimaryGroupIndex, 874 _Out_opt_ PULONG DefaultOwnerIndex) 875 { 876 ULONG i; 877 878 /* We should return at least a search result */ 879 if (!PrimaryGroupIndex && !DefaultOwnerIndex) 880 return STATUS_INVALID_PARAMETER; 881 882 if (PrimaryGroupIndex) 883 { 884 /* Initialize with an invalid index */ 885 // Token->PrimaryGroup = NULL; 886 *PrimaryGroupIndex = Token->UserAndGroupCount; 887 } 888 889 if (DefaultOwnerIndex) 890 { 891 if (DefaultOwner) 892 { 893 /* An owner is specified: check whether this is actually the user */ 894 if (RtlEqualSid(Token->UserAndGroups[0].Sid, DefaultOwner)) 895 { 896 /* 897 * It's the user (first element in array): set it 898 * as the owner and stop the search for it. 899 */ 900 *DefaultOwnerIndex = 0; 901 DefaultOwnerIndex = NULL; 902 } 903 else 904 { 905 /* An owner is specified: initialize with an invalid index */ 906 *DefaultOwnerIndex = Token->UserAndGroupCount; 907 } 908 } 909 else 910 { 911 /* 912 * No owner specified: set the user (first element in array) 913 * as the owner and stop the search for it. 914 */ 915 *DefaultOwnerIndex = 0; 916 DefaultOwnerIndex = NULL; 917 } 918 } 919 920 /* Validate and set the primary group and default owner indices */ 921 for (i = 0; i < Token->UserAndGroupCount; i++) 922 { 923 /* Stop the search if we have found what we searched for */ 924 if (!PrimaryGroupIndex && !DefaultOwnerIndex) 925 break; 926 927 if (DefaultOwnerIndex && DefaultOwner && 928 RtlEqualSid(Token->UserAndGroups[i].Sid, DefaultOwner) && 929 (Token->UserAndGroups[i].Attributes & SE_GROUP_OWNER)) 930 { 931 /* Owner is found, stop the search for it */ 932 *DefaultOwnerIndex = i; 933 DefaultOwnerIndex = NULL; 934 } 935 936 if (PrimaryGroupIndex && 937 RtlEqualSid(Token->UserAndGroups[i].Sid, PrimaryGroup)) 938 { 939 /* Primary group is found, stop the search for it */ 940 // Token->PrimaryGroup = Token->UserAndGroups[i].Sid; 941 *PrimaryGroupIndex = i; 942 PrimaryGroupIndex = NULL; 943 } 944 } 945 946 if (DefaultOwnerIndex) 947 { 948 if (*DefaultOwnerIndex == Token->UserAndGroupCount) 949 return STATUS_INVALID_OWNER; 950 } 951 952 if (PrimaryGroupIndex) 953 { 954 if (*PrimaryGroupIndex == Token->UserAndGroupCount) 955 // if (Token->PrimaryGroup == NULL) 956 return STATUS_INVALID_PRIMARY_GROUP; 957 } 958 959 return STATUS_SUCCESS; 960 } 961 962 /** 963 * @brief 964 * Duplicates an access token, from an existing valid token. 965 * 966 * @param[in] Token 967 * Access token to duplicate. 968 * 969 * @param[in] ObjectAttributes 970 * Object attributes for the new token. 971 * 972 * @param[in] EffectiveOnly 973 * If set to TRUE, the function removes all the disabled privileges and groups of the token 974 * to duplicate. 975 * 976 * @param[in] TokenType 977 * Type of token. 978 * 979 * @param[in] Level 980 * Security impersonation level of a token. 981 * 982 * @param[in] PreviousMode 983 * The processor request level mode. 984 * 985 * @param[out] NewAccessToken 986 * The duplicated token. 987 * 988 * @return 989 * Returns STATUS_SUCCESS if the token has been duplicated. STATUS_INSUFFICIENT_RESOURCES is returned 990 * if memory pool allocation of the dynamic part of the token for duplication has failed due to the lack 991 * of memory resources. A failure NTSTATUS code is returned otherwise. 992 */ 993 NTSTATUS 994 NTAPI 995 SepDuplicateToken( 996 _In_ PTOKEN Token, 997 _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes, 998 _In_ BOOLEAN EffectiveOnly, 999 _In_ TOKEN_TYPE TokenType, 1000 _In_ SECURITY_IMPERSONATION_LEVEL Level, 1001 _In_ KPROCESSOR_MODE PreviousMode, 1002 _Out_ PTOKEN* NewAccessToken) 1003 { 1004 NTSTATUS Status; 1005 PTOKEN AccessToken; 1006 PVOID EndMem; 1007 ULONG PrimaryGroupIndex; 1008 ULONG VariableLength; 1009 ULONG TotalSize; 1010 ULONG PrivilegesIndex, GroupsIndex; 1011 1012 PAGED_CODE(); 1013 1014 /* Compute how much size we need to allocate for the token */ 1015 VariableLength = Token->VariableLength; 1016 TotalSize = FIELD_OFFSET(TOKEN, VariablePart) + VariableLength; 1017 1018 Status = ObCreateObject(PreviousMode, 1019 SeTokenObjectType, 1020 ObjectAttributes, 1021 PreviousMode, 1022 NULL, 1023 TotalSize, 1024 0, 1025 0, 1026 (PVOID*)&AccessToken); 1027 if (!NT_SUCCESS(Status)) 1028 { 1029 DPRINT1("ObCreateObject() failed (Status 0x%lx)\n", Status); 1030 return Status; 1031 } 1032 1033 /* Zero out the buffer and initialize the token */ 1034 RtlZeroMemory(AccessToken, TotalSize); 1035 1036 ExAllocateLocallyUniqueId(&AccessToken->TokenId); 1037 1038 AccessToken->TokenType = TokenType; 1039 AccessToken->ImpersonationLevel = Level; 1040 1041 /* Initialise the lock for the access token */ 1042 Status = SepCreateTokenLock(AccessToken); 1043 if (!NT_SUCCESS(Status)) 1044 { 1045 ObDereferenceObject(AccessToken); 1046 return Status; 1047 } 1048 1049 /* Copy the immutable fields */ 1050 RtlCopyLuid(&AccessToken->TokenSource.SourceIdentifier, 1051 &Token->TokenSource.SourceIdentifier); 1052 RtlCopyMemory(AccessToken->TokenSource.SourceName, 1053 Token->TokenSource.SourceName, 1054 sizeof(Token->TokenSource.SourceName)); 1055 1056 AccessToken->AuthenticationId = Token->AuthenticationId; 1057 AccessToken->ParentTokenId = Token->ParentTokenId; 1058 AccessToken->ExpirationTime = Token->ExpirationTime; 1059 AccessToken->OriginatingLogonSession = Token->OriginatingLogonSession; 1060 1061 /* Lock the source token and copy the mutable fields */ 1062 SepAcquireTokenLockExclusive(Token); 1063 1064 AccessToken->SessionId = Token->SessionId; 1065 RtlCopyLuid(&AccessToken->ModifiedId, &Token->ModifiedId); 1066 1067 AccessToken->TokenFlags = Token->TokenFlags & ~TOKEN_SESSION_NOT_REFERENCED; 1068 1069 /* Reference the logon session */ 1070 Status = SepRmReferenceLogonSession(&AccessToken->AuthenticationId); 1071 if (!NT_SUCCESS(Status)) 1072 { 1073 /* No logon session could be found, bail out */ 1074 DPRINT1("SepRmReferenceLogonSession() failed (Status 0x%lx)\n", Status); 1075 /* Set the flag for proper cleanup by the delete procedure */ 1076 AccessToken->TokenFlags |= TOKEN_SESSION_NOT_REFERENCED; 1077 goto Quit; 1078 } 1079 1080 /* Insert the referenced logon session into the token */ 1081 Status = SepRmInsertLogonSessionIntoToken(AccessToken); 1082 if (!NT_SUCCESS(Status)) 1083 { 1084 /* Failed to insert the logon session into the token, bail out */ 1085 DPRINT1("SepRmInsertLogonSessionIntoToken() failed (Status 0x%lx)\n", Status); 1086 goto Quit; 1087 } 1088 1089 /* Fill in token debug information */ 1090 #if DBG 1091 RtlCopyMemory(AccessToken->ImageFileName, 1092 PsGetCurrentProcess()->ImageFileName, 1093 min(sizeof(AccessToken->ImageFileName), sizeof(PsGetCurrentProcess()->ImageFileName))); 1094 1095 AccessToken->ProcessCid = PsGetCurrentProcessId(); 1096 AccessToken->ThreadCid = PsGetCurrentThreadId(); 1097 AccessToken->CreateMethod = TOKEN_DUPLICATE_METHOD; 1098 #endif 1099 1100 /* Assign the data that reside in the TOKEN's variable information area */ 1101 AccessToken->VariableLength = VariableLength; 1102 EndMem = (PVOID)&AccessToken->VariablePart; 1103 1104 /* Copy the privileges */ 1105 AccessToken->PrivilegeCount = 0; 1106 AccessToken->Privileges = NULL; 1107 if (Token->Privileges && (Token->PrivilegeCount > 0)) 1108 { 1109 ULONG PrivilegesLength = Token->PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES); 1110 PrivilegesLength = ALIGN_UP_BY(PrivilegesLength, sizeof(PVOID)); 1111 1112 ASSERT(VariableLength >= PrivilegesLength); 1113 1114 AccessToken->PrivilegeCount = Token->PrivilegeCount; 1115 AccessToken->Privileges = EndMem; 1116 EndMem = (PVOID)((ULONG_PTR)EndMem + PrivilegesLength); 1117 VariableLength -= PrivilegesLength; 1118 1119 RtlCopyMemory(AccessToken->Privileges, 1120 Token->Privileges, 1121 AccessToken->PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES)); 1122 } 1123 1124 /* Copy the user and groups */ 1125 AccessToken->UserAndGroupCount = 0; 1126 AccessToken->UserAndGroups = NULL; 1127 if (Token->UserAndGroups && (Token->UserAndGroupCount > 0)) 1128 { 1129 AccessToken->UserAndGroupCount = Token->UserAndGroupCount; 1130 AccessToken->UserAndGroups = EndMem; 1131 EndMem = &AccessToken->UserAndGroups[AccessToken->UserAndGroupCount]; 1132 VariableLength -= ((ULONG_PTR)EndMem - (ULONG_PTR)AccessToken->UserAndGroups); 1133 1134 Status = RtlCopySidAndAttributesArray(AccessToken->UserAndGroupCount, 1135 Token->UserAndGroups, 1136 VariableLength, 1137 AccessToken->UserAndGroups, 1138 EndMem, 1139 &EndMem, 1140 &VariableLength); 1141 if (!NT_SUCCESS(Status)) 1142 { 1143 DPRINT1("RtlCopySidAndAttributesArray(UserAndGroups) failed (Status 0x%lx)\n", Status); 1144 goto Quit; 1145 } 1146 } 1147 1148 /* Find the token primary group */ 1149 Status = SepFindPrimaryGroupAndDefaultOwner(AccessToken, 1150 Token->PrimaryGroup, 1151 NULL, 1152 &PrimaryGroupIndex, 1153 NULL); 1154 if (!NT_SUCCESS(Status)) 1155 { 1156 DPRINT1("SepFindPrimaryGroupAndDefaultOwner failed (Status 0x%lx)\n", Status); 1157 goto Quit; 1158 } 1159 1160 AccessToken->PrimaryGroup = AccessToken->UserAndGroups[PrimaryGroupIndex].Sid; 1161 AccessToken->DefaultOwnerIndex = Token->DefaultOwnerIndex; 1162 1163 /* Copy the restricted SIDs */ 1164 AccessToken->RestrictedSidCount = 0; 1165 AccessToken->RestrictedSids = NULL; 1166 if (Token->RestrictedSids && (Token->RestrictedSidCount > 0)) 1167 { 1168 AccessToken->RestrictedSidCount = Token->RestrictedSidCount; 1169 AccessToken->RestrictedSids = EndMem; 1170 EndMem = &AccessToken->RestrictedSids[AccessToken->RestrictedSidCount]; 1171 VariableLength -= ((ULONG_PTR)EndMem - (ULONG_PTR)AccessToken->RestrictedSids); 1172 1173 Status = RtlCopySidAndAttributesArray(AccessToken->RestrictedSidCount, 1174 Token->RestrictedSids, 1175 VariableLength, 1176 AccessToken->RestrictedSids, 1177 EndMem, 1178 &EndMem, 1179 &VariableLength); 1180 if (!NT_SUCCESS(Status)) 1181 { 1182 DPRINT1("RtlCopySidAndAttributesArray(RestrictedSids) failed (Status 0x%lx)\n", Status); 1183 goto Quit; 1184 } 1185 } 1186 1187 /* 1188 * Filter the token by removing the disabled privileges 1189 * and groups if the caller wants to duplicate an access 1190 * token as effective only. 1191 */ 1192 if (EffectiveOnly) 1193 { 1194 /* Begin querying the groups and search for disabled ones */ 1195 for (GroupsIndex = 0; GroupsIndex < AccessToken->UserAndGroupCount; GroupsIndex++) 1196 { 1197 /* 1198 * A group or user is considered disabled if its attributes is either 1199 * 0 or SE_GROUP_ENABLED is not included in the attributes flags list. 1200 * That is because a certain user and/or group can have several attributes 1201 * that bear no influence on whether a user/group is enabled or not 1202 * (SE_GROUP_ENABLED_BY_DEFAULT for example which is a mere indicator 1203 * that the group has just been enabled by default). A mandatory 1204 * group (that is, the group has SE_GROUP_MANDATORY attribute) 1205 * by standards it's always enabled and no one can disable it. 1206 */ 1207 if (AccessToken->UserAndGroups[GroupsIndex].Attributes == 0 || 1208 (AccessToken->UserAndGroups[GroupsIndex].Attributes & SE_GROUP_ENABLED) == 0) 1209 { 1210 /* 1211 * If this group is an administrators group 1212 * and the token belongs to such group, 1213 * we've to take away TOKEN_HAS_ADMIN_GROUP 1214 * for the fact that's not enabled and as 1215 * such the token no longer belongs to 1216 * this group. 1217 */ 1218 if (RtlEqualSid(SeAliasAdminsSid, 1219 &AccessToken->UserAndGroups[GroupsIndex].Sid)) 1220 { 1221 AccessToken->TokenFlags &= ~TOKEN_HAS_ADMIN_GROUP; 1222 } 1223 1224 /* 1225 * A group is not enabled, it's time to remove 1226 * from the token and update the groups index 1227 * accordingly and continue with the next group. 1228 */ 1229 SepRemoveUserGroupToken(AccessToken, GroupsIndex); 1230 GroupsIndex--; 1231 } 1232 } 1233 1234 /* Begin querying the privileges and search for disabled ones */ 1235 for (PrivilegesIndex = 0; PrivilegesIndex < AccessToken->PrivilegeCount; PrivilegesIndex++) 1236 { 1237 /* 1238 * A privilege is considered disabled if its attributes is either 1239 * 0 or SE_PRIVILEGE_ENABLED is not included in the attributes flags list. 1240 * That is because a certain privilege can have several attributes 1241 * that bear no influence on whether a privilege is enabled or not 1242 * (SE_PRIVILEGE_ENABLED_BY_DEFAULT for example which is a mere indicator 1243 * that the privilege has just been enabled by default). 1244 */ 1245 if (AccessToken->Privileges[PrivilegesIndex].Attributes == 0 || 1246 (AccessToken->Privileges[PrivilegesIndex].Attributes & SE_PRIVILEGE_ENABLED) == 0) 1247 { 1248 /* 1249 * A privilege is not enabled, therefor it's time 1250 * to strip it from the token and continue with the next 1251 * privilege. Of course we must also want to update the 1252 * privileges index accordingly. 1253 */ 1254 SepRemovePrivilegeToken(AccessToken, PrivilegesIndex); 1255 PrivilegesIndex--; 1256 } 1257 } 1258 } 1259 1260 // 1261 // NOTE: So far our dynamic area only contains 1262 // the default dacl, so this makes the following 1263 // code pretty simple. The day where it stores 1264 // other data, the code will require adaptations. 1265 // 1266 1267 /* Now allocate the TOKEN's dynamic information area and set the data */ 1268 AccessToken->DynamicAvailable = 0; // Unused memory in the dynamic area. 1269 AccessToken->DynamicPart = NULL; 1270 if (Token->DynamicPart && Token->DefaultDacl) 1271 { 1272 AccessToken->DynamicPart = ExAllocatePoolWithTag(PagedPool, 1273 Token->DefaultDacl->AclSize, 1274 TAG_TOKEN_DYNAMIC); 1275 if (AccessToken->DynamicPart == NULL) 1276 { 1277 Status = STATUS_INSUFFICIENT_RESOURCES; 1278 goto Quit; 1279 } 1280 EndMem = (PVOID)AccessToken->DynamicPart; 1281 1282 AccessToken->DefaultDacl = EndMem; 1283 1284 RtlCopyMemory(AccessToken->DefaultDacl, 1285 Token->DefaultDacl, 1286 Token->DefaultDacl->AclSize); 1287 } 1288 1289 /* Unlock the source token */ 1290 SepReleaseTokenLock(Token); 1291 1292 /* Return the token */ 1293 *NewAccessToken = AccessToken; 1294 Status = STATUS_SUCCESS; 1295 1296 Quit: 1297 if (!NT_SUCCESS(Status)) 1298 { 1299 /* Unlock the source token */ 1300 SepReleaseTokenLock(Token); 1301 1302 /* Dereference the token, the delete procedure will clean it up */ 1303 ObDereferenceObject(AccessToken); 1304 } 1305 1306 return Status; 1307 } 1308 1309 /** 1310 * @brief 1311 * Subtracts a token in exchange of duplicating a new one. 1312 * 1313 * @param[in] ParentToken 1314 * The parent access token for duplication. 1315 * 1316 * @param[out] Token 1317 * The new duplicated token. 1318 * 1319 * @param[in] InUse 1320 * Set this to TRUE if the token is about to be used immediately after the call execution 1321 * of this function, FALSE otherwise. 1322 * 1323 * @param[in] SessionId 1324 * Session ID for the token to be assigned. 1325 * 1326 * @return 1327 * Returns STATUS_SUCCESS if token subtracting and duplication have completed successfully. 1328 * A failure NTSTATUS code is returned otherwise. 1329 */ 1330 NTSTATUS 1331 NTAPI 1332 SeSubProcessToken( 1333 _In_ PTOKEN ParentToken, 1334 _Out_ PTOKEN *Token, 1335 _In_ BOOLEAN InUse, 1336 _In_ ULONG SessionId) 1337 { 1338 PTOKEN NewToken; 1339 OBJECT_ATTRIBUTES ObjectAttributes; 1340 NTSTATUS Status; 1341 1342 /* Initialize the attributes and duplicate it */ 1343 InitializeObjectAttributes(&ObjectAttributes, NULL, 0, NULL, NULL); 1344 Status = SepDuplicateToken(ParentToken, 1345 &ObjectAttributes, 1346 FALSE, 1347 TokenPrimary, 1348 ParentToken->ImpersonationLevel, 1349 KernelMode, 1350 &NewToken); 1351 if (NT_SUCCESS(Status)) 1352 { 1353 /* Insert it */ 1354 Status = ObInsertObject(NewToken, 1355 NULL, 1356 0, 1357 0, 1358 NULL, 1359 NULL); 1360 if (NT_SUCCESS(Status)) 1361 { 1362 /* Set the session ID */ 1363 NewToken->SessionId = SessionId; 1364 NewToken->TokenInUse = InUse; 1365 1366 /* Return the token */ 1367 *Token = NewToken; 1368 } 1369 } 1370 1371 /* Return status */ 1372 return Status; 1373 } 1374 1375 /** 1376 * @brief 1377 * Checks if the token is a child of the other token 1378 * of the current process that the calling thread is invoking this function. 1379 * 1380 * @param[in] Token 1381 * An access token to determine if it's a child or not. 1382 * 1383 * @param[out] IsChild 1384 * The returned boolean result. 1385 * 1386 * @return 1387 * Returns STATUS_SUCCESS when the function finishes its operation. STATUS_UNSUCCESSFUL is 1388 * returned if primary token of the current calling process couldn't be referenced otherwise. 1389 */ 1390 NTSTATUS 1391 NTAPI 1392 SeIsTokenChild( 1393 _In_ PTOKEN Token, 1394 _Out_ PBOOLEAN IsChild) 1395 { 1396 PTOKEN ProcessToken; 1397 LUID ProcessTokenId, CallerParentId; 1398 1399 /* Assume failure */ 1400 *IsChild = FALSE; 1401 1402 /* Reference the process token */ 1403 ProcessToken = PsReferencePrimaryToken(PsGetCurrentProcess()); 1404 if (!ProcessToken) 1405 return STATUS_UNSUCCESSFUL; 1406 1407 /* Get its token ID */ 1408 ProcessTokenId = ProcessToken->TokenId; 1409 1410 /* Dereference the token */ 1411 ObFastDereferenceObject(&PsGetCurrentProcess()->Token, ProcessToken); 1412 1413 /* Get our parent token ID */ 1414 CallerParentId = Token->ParentTokenId; 1415 1416 /* Compare the token IDs */ 1417 if (RtlEqualLuid(&CallerParentId, &ProcessTokenId)) 1418 *IsChild = TRUE; 1419 1420 /* Return success */ 1421 return STATUS_SUCCESS; 1422 } 1423 1424 /** 1425 * @brief 1426 * Checks if the token is a sibling of the other token of 1427 * the current process that the calling thread is invoking this function. 1428 * 1429 * @param[in] Token 1430 * An access token to determine if it's a sibling or not. 1431 * 1432 * @param[out] IsSibling 1433 * The returned boolean result. 1434 * 1435 * @return 1436 * Returns STATUS_SUCCESS when the function finishes its operation. STATUS_UNSUCCESSFUL is 1437 * returned if primary token of the current calling process couldn't be referenced otherwise. 1438 */ 1439 NTSTATUS 1440 NTAPI 1441 SeIsTokenSibling( 1442 _In_ PTOKEN Token, 1443 _Out_ PBOOLEAN IsSibling) 1444 { 1445 PTOKEN ProcessToken; 1446 LUID ProcessParentId, ProcessAuthId; 1447 LUID CallerParentId, CallerAuthId; 1448 1449 /* Assume failure */ 1450 *IsSibling = FALSE; 1451 1452 /* Reference the process token */ 1453 ProcessToken = PsReferencePrimaryToken(PsGetCurrentProcess()); 1454 if (!ProcessToken) 1455 return STATUS_UNSUCCESSFUL; 1456 1457 /* Get its parent and authentication IDs */ 1458 ProcessParentId = ProcessToken->ParentTokenId; 1459 ProcessAuthId = ProcessToken->AuthenticationId; 1460 1461 /* Dereference the token */ 1462 ObFastDereferenceObject(&PsGetCurrentProcess()->Token, ProcessToken); 1463 1464 /* Get our parent and authentication IDs */ 1465 CallerParentId = Token->ParentTokenId; 1466 CallerAuthId = Token->AuthenticationId; 1467 1468 /* Compare the token IDs */ 1469 if (RtlEqualLuid(&CallerParentId, &ProcessParentId) && 1470 RtlEqualLuid(&CallerAuthId, &ProcessAuthId)) 1471 { 1472 *IsSibling = TRUE; 1473 } 1474 1475 /* Return success */ 1476 return STATUS_SUCCESS; 1477 } 1478 1479 /** 1480 * @brief 1481 * Copies an existing access token (technically duplicating a new one). 1482 * 1483 * @param[in] Token 1484 * Token to copy. 1485 * 1486 * @param[in] Level 1487 * Impersonation security level to assign to the newly copied token. 1488 * 1489 * @param[in] PreviousMode 1490 * Processor request level mode. 1491 * 1492 * @param[out] NewToken 1493 * The newly copied token. 1494 * 1495 * @return 1496 * Returns STATUS_SUCCESS when token copying has finished successfully. A failure 1497 * NTSTATUS code is returned otherwise. 1498 */ 1499 NTSTATUS 1500 NTAPI 1501 SeCopyClientToken( 1502 _In_ PACCESS_TOKEN Token, 1503 _In_ SECURITY_IMPERSONATION_LEVEL Level, 1504 _In_ KPROCESSOR_MODE PreviousMode, 1505 _Out_ PACCESS_TOKEN* NewToken) 1506 { 1507 NTSTATUS Status; 1508 OBJECT_ATTRIBUTES ObjectAttributes; 1509 1510 PAGED_CODE(); 1511 1512 InitializeObjectAttributes(&ObjectAttributes, 1513 NULL, 1514 0, 1515 NULL, 1516 NULL); 1517 1518 Status = SepDuplicateToken(Token, 1519 &ObjectAttributes, 1520 FALSE, 1521 TokenImpersonation, 1522 Level, 1523 PreviousMode, 1524 (PTOKEN*)NewToken); 1525 1526 return Status; 1527 } 1528 1529 /** 1530 * @brief 1531 * Internal function that deals with access token object destruction and deletion. 1532 * The function is used solely by the object manager mechanism that handles the life 1533 * management of a token object. 1534 * 1535 * @param[in] ObjectBody 1536 * The object body that represents an access token object. 1537 * 1538 * @return 1539 * Nothing. 1540 */ 1541 VOID 1542 NTAPI 1543 SepDeleteToken( 1544 _In_ PVOID ObjectBody) 1545 { 1546 NTSTATUS Status; 1547 PTOKEN AccessToken = (PTOKEN)ObjectBody; 1548 1549 DPRINT("SepDeleteToken()\n"); 1550 1551 /* Remove the referenced logon session from token */ 1552 if (AccessToken->LogonSession) 1553 { 1554 Status = SepRmRemoveLogonSessionFromToken(AccessToken); 1555 if (!NT_SUCCESS(Status)) 1556 { 1557 /* Something seriously went wrong */ 1558 DPRINT1("SepDeleteToken(): Failed to remove the logon session from token (Status: 0x%lx)\n", Status); 1559 return; 1560 } 1561 } 1562 1563 /* Dereference the logon session */ 1564 if ((AccessToken->TokenFlags & TOKEN_SESSION_NOT_REFERENCED) == 0) 1565 SepRmDereferenceLogonSession(&AccessToken->AuthenticationId); 1566 1567 /* Delete the token lock */ 1568 if (AccessToken->TokenLock) 1569 SepDeleteTokenLock(AccessToken); 1570 1571 /* Delete the dynamic information area */ 1572 if (AccessToken->DynamicPart) 1573 ExFreePoolWithTag(AccessToken->DynamicPart, TAG_TOKEN_DYNAMIC); 1574 } 1575 1576 /** 1577 * @brief 1578 * Internal function that initializes critical kernel data for access 1579 * token implementation in SRM. 1580 * 1581 * @return 1582 * Nothing. 1583 */ 1584 CODE_SEG("INIT") 1585 VOID 1586 NTAPI 1587 SepInitializeTokenImplementation(VOID) 1588 { 1589 UNICODE_STRING Name; 1590 OBJECT_TYPE_INITIALIZER ObjectTypeInitializer; 1591 1592 DPRINT("Creating Token Object Type\n"); 1593 1594 /* Initialize the Token type */ 1595 RtlZeroMemory(&ObjectTypeInitializer, sizeof(ObjectTypeInitializer)); 1596 RtlInitUnicodeString(&Name, L"Token"); 1597 ObjectTypeInitializer.Length = sizeof(ObjectTypeInitializer); 1598 ObjectTypeInitializer.InvalidAttributes = OBJ_OPENLINK; 1599 ObjectTypeInitializer.SecurityRequired = TRUE; 1600 ObjectTypeInitializer.DefaultPagedPoolCharge = sizeof(TOKEN); 1601 ObjectTypeInitializer.GenericMapping = SepTokenMapping; 1602 ObjectTypeInitializer.PoolType = PagedPool; 1603 ObjectTypeInitializer.ValidAccessMask = TOKEN_ALL_ACCESS; 1604 ObjectTypeInitializer.UseDefaultObject = TRUE; 1605 ObjectTypeInitializer.DeleteProcedure = SepDeleteToken; 1606 ObCreateObjectType(&Name, &ObjectTypeInitializer, NULL, &SeTokenObjectType); 1607 } 1608 1609 /** 1610 * @brief 1611 * Assigns a primary access token to a given process. 1612 * 1613 * @param[in] Process 1614 * Process where the token is about to be assigned. 1615 * 1616 * @param[in] Token 1617 * The token to be assigned. 1618 * 1619 * @return 1620 * Nothing. 1621 */ 1622 VOID 1623 NTAPI 1624 SeAssignPrimaryToken( 1625 _In_ PEPROCESS Process, 1626 _In_ PTOKEN Token) 1627 { 1628 PAGED_CODE(); 1629 1630 /* Sanity checks */ 1631 ASSERT(Token->TokenType == TokenPrimary); 1632 ASSERT(!Token->TokenInUse); 1633 1634 /* Clean any previous token */ 1635 if (Process->Token.Object) SeDeassignPrimaryToken(Process); 1636 1637 /* Set the new token */ 1638 ObReferenceObject(Token); 1639 Token->TokenInUse = TRUE; 1640 ObInitializeFastReference(&Process->Token, Token); 1641 } 1642 1643 /** 1644 * @brief 1645 * Internal function responsible for access token object creation in the kernel. 1646 * A fully created token objected is inserted into the token handle, thus the handle 1647 * becoming a valid handle to an access token object and ready for use. 1648 * 1649 * @param[out] TokenHandle 1650 * Valid token handle that's ready for use after token creation and object insertion. 1651 * 1652 * @param[in] PreviousMode 1653 * Processor request level mode. 1654 * 1655 * @param[in] DesiredAccess 1656 * Desired access right for the token object to be granted. This kind of access right 1657 * impacts how the token can be used and who. 1658 * 1659 * @param[in] ObjectAttributes 1660 * Object attributes for the token to be created. 1661 * 1662 * @param[in] TokenType 1663 * Type of token to assign upon creation. 1664 * 1665 * @param[in] ImpersonationLevel 1666 * Security impersonation level of token to assign upon creation. 1667 * 1668 * @param[in] AuthenticationId 1669 * Authentication ID that represents the authentication information of the token. 1670 * 1671 * @param[in] ExpirationTime 1672 * Expiration time of the token to assign. A value of -1 means that the token never 1673 * expires and its life depends upon the amount of references this token object has. 1674 * 1675 * @param[in] User 1676 * User entry to assign to the token. 1677 * 1678 * @param[in] GroupCount 1679 * The total number of groups count for the token. 1680 * 1681 * @param[in] Groups 1682 * The group entries for the token. 1683 * 1684 * @param[in] GroupsLength 1685 * The length size of the groups array, pointed by the Groups parameter. 1686 * 1687 * @param[in] PrivilegeCount 1688 * The total number of priivleges that the newly created token has. 1689 * 1690 * @param[in] Privileges 1691 * The privileges for the token. 1692 * 1693 * @param[in] Owner 1694 * The main user (or also owner) that represents the token that we create. 1695 * 1696 * @param[in] PrimaryGroup 1697 * The main group that represents the token that we create. 1698 * 1699 * @param[in] DefaultDacl 1700 * A discretionary access control list for the token. 1701 * 1702 * @param[in] TokenSource 1703 * Source (or the origin) of the access token that creates it. 1704 * 1705 * @param[in] SystemToken 1706 * If set to TRUE, the newly created token is a system token and only in charge 1707 * by the internal system. The function directly returns a pointer to the 1708 * created token object for system kernel use. Otherwise if set to FALSE, the 1709 * function inserts the object to a handle making it a regular access token. 1710 * 1711 * @return 1712 * Returns STATUS_SUCCESS if token creation has completed successfully. 1713 * STATUS_INSUFFICIENT_RESOURCES is returned if the dynamic area of memory of the 1714 * token hasn't been allocated because of lack of memory resources. A failure 1715 * NTSTATUS code is returned otherwise. 1716 */ 1717 NTSTATUS 1718 NTAPI 1719 SepCreateToken( 1720 _Out_ PHANDLE TokenHandle, 1721 _In_ KPROCESSOR_MODE PreviousMode, 1722 _In_ ACCESS_MASK DesiredAccess, 1723 _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes, 1724 _In_ TOKEN_TYPE TokenType, 1725 _In_ SECURITY_IMPERSONATION_LEVEL ImpersonationLevel, 1726 _In_ PLUID AuthenticationId, 1727 _In_ PLARGE_INTEGER ExpirationTime, 1728 _In_ PSID_AND_ATTRIBUTES User, 1729 _In_ ULONG GroupCount, 1730 _In_ PSID_AND_ATTRIBUTES Groups, 1731 _In_ ULONG GroupsLength, 1732 _In_ ULONG PrivilegeCount, 1733 _In_ PLUID_AND_ATTRIBUTES Privileges, 1734 _In_opt_ PSID Owner, 1735 _In_ PSID PrimaryGroup, 1736 _In_opt_ PACL DefaultDacl, 1737 _In_ PTOKEN_SOURCE TokenSource, 1738 _In_ BOOLEAN SystemToken) 1739 { 1740 NTSTATUS Status; 1741 PTOKEN AccessToken; 1742 ULONG TokenFlags = 0; 1743 ULONG PrimaryGroupIndex, DefaultOwnerIndex; 1744 LUID TokenId; 1745 LUID ModifiedId; 1746 PVOID EndMem; 1747 ULONG PrivilegesLength; 1748 ULONG UserGroupsLength; 1749 ULONG VariableLength; 1750 ULONG TotalSize; 1751 ULONG i; 1752 1753 PAGED_CODE(); 1754 1755 /* Loop all groups */ 1756 for (i = 0; i < GroupCount; i++) 1757 { 1758 /* Check for mandatory groups */ 1759 if (Groups[i].Attributes & SE_GROUP_MANDATORY) 1760 { 1761 /* Force them to be enabled */ 1762 Groups[i].Attributes |= (SE_GROUP_ENABLED | SE_GROUP_ENABLED_BY_DEFAULT); 1763 } 1764 1765 /* Check of the group is an admin group */ 1766 if (RtlEqualSid(SeAliasAdminsSid, Groups[i].Sid)) 1767 { 1768 /* Remember this so we can optimize queries later */ 1769 TokenFlags |= TOKEN_HAS_ADMIN_GROUP; 1770 } 1771 } 1772 1773 /* Allocate unique IDs for the token */ 1774 ExAllocateLocallyUniqueId(&TokenId); 1775 ExAllocateLocallyUniqueId(&ModifiedId); 1776 1777 /* Compute how much size we need to allocate for the token */ 1778 1779 /* Privileges size */ 1780 PrivilegesLength = PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES); 1781 PrivilegesLength = ALIGN_UP_BY(PrivilegesLength, sizeof(PVOID)); 1782 1783 /* User and groups size */ 1784 UserGroupsLength = (1 + GroupCount) * sizeof(SID_AND_ATTRIBUTES); 1785 UserGroupsLength += RtlLengthSid(User->Sid); 1786 for (i = 0; i < GroupCount; i++) 1787 { 1788 UserGroupsLength += RtlLengthSid(Groups[i].Sid); 1789 } 1790 UserGroupsLength = ALIGN_UP_BY(UserGroupsLength, sizeof(PVOID)); 1791 1792 /* Add the additional groups array length */ 1793 UserGroupsLength += ALIGN_UP_BY(GroupsLength, sizeof(PVOID)); 1794 1795 VariableLength = PrivilegesLength + UserGroupsLength; 1796 TotalSize = FIELD_OFFSET(TOKEN, VariablePart) + VariableLength; 1797 1798 Status = ObCreateObject(PreviousMode, 1799 SeTokenObjectType, 1800 ObjectAttributes, 1801 PreviousMode, 1802 NULL, 1803 TotalSize, 1804 0, 1805 0, 1806 (PVOID*)&AccessToken); 1807 if (!NT_SUCCESS(Status)) 1808 { 1809 DPRINT1("ObCreateObject() failed (Status 0x%lx)\n", Status); 1810 return Status; 1811 } 1812 1813 /* Zero out the buffer and initialize the token */ 1814 RtlZeroMemory(AccessToken, TotalSize); 1815 1816 RtlCopyLuid(&AccessToken->TokenId, &TokenId); 1817 1818 AccessToken->TokenType = TokenType; 1819 AccessToken->ImpersonationLevel = ImpersonationLevel; 1820 1821 /* Initialise the lock for the access token */ 1822 Status = SepCreateTokenLock(AccessToken); 1823 if (!NT_SUCCESS(Status)) 1824 goto Quit; 1825 1826 RtlCopyLuid(&AccessToken->TokenSource.SourceIdentifier, 1827 &TokenSource->SourceIdentifier); 1828 RtlCopyMemory(AccessToken->TokenSource.SourceName, 1829 TokenSource->SourceName, 1830 sizeof(TokenSource->SourceName)); 1831 1832 AccessToken->ExpirationTime = *ExpirationTime; 1833 RtlCopyLuid(&AccessToken->ModifiedId, &ModifiedId); 1834 1835 AccessToken->TokenFlags = TokenFlags & ~TOKEN_SESSION_NOT_REFERENCED; 1836 1837 /* Copy and reference the logon session */ 1838 RtlCopyLuid(&AccessToken->AuthenticationId, AuthenticationId); 1839 Status = SepRmReferenceLogonSession(&AccessToken->AuthenticationId); 1840 if (!NT_SUCCESS(Status)) 1841 { 1842 /* No logon session could be found, bail out */ 1843 DPRINT1("SepRmReferenceLogonSession() failed (Status 0x%lx)\n", Status); 1844 /* Set the flag for proper cleanup by the delete procedure */ 1845 AccessToken->TokenFlags |= TOKEN_SESSION_NOT_REFERENCED; 1846 goto Quit; 1847 } 1848 1849 /* Insert the referenced logon session into the token */ 1850 Status = SepRmInsertLogonSessionIntoToken(AccessToken); 1851 if (!NT_SUCCESS(Status)) 1852 { 1853 /* Failed to insert the logon session into the token, bail out */ 1854 DPRINT1("SepRmInsertLogonSessionIntoToken() failed (Status 0x%lx)\n", Status); 1855 goto Quit; 1856 } 1857 1858 /* Fill in token debug information */ 1859 #if DBG 1860 /* 1861 * We must determine ourselves that the current 1862 * process is not the initial CPU one. The initial 1863 * process is not a "real" process, that is, the 1864 * Process Manager has not yet been initialized and 1865 * as a matter of fact we are creating a token before 1866 * any process gets created by Ps. If it turns out 1867 * that the current process is the initial CPU process 1868 * where token creation execution takes place, don't 1869 * do anything. 1870 */ 1871 if (PsGetCurrentProcess() != &KiInitialProcess) 1872 { 1873 RtlCopyMemory(AccessToken->ImageFileName, 1874 PsGetCurrentProcess()->ImageFileName, 1875 min(sizeof(AccessToken->ImageFileName), sizeof(PsGetCurrentProcess()->ImageFileName))); 1876 1877 AccessToken->ProcessCid = PsGetCurrentProcessId(); 1878 AccessToken->ThreadCid = PsGetCurrentThreadId(); 1879 } 1880 1881 AccessToken->CreateMethod = TOKEN_CREATE_METHOD; 1882 #endif 1883 1884 /* Assign the data that reside in the TOKEN's variable information area */ 1885 AccessToken->VariableLength = VariableLength; 1886 EndMem = (PVOID)&AccessToken->VariablePart; 1887 1888 /* Copy the privileges */ 1889 AccessToken->PrivilegeCount = PrivilegeCount; 1890 AccessToken->Privileges = NULL; 1891 if (PrivilegeCount > 0) 1892 { 1893 AccessToken->Privileges = EndMem; 1894 EndMem = (PVOID)((ULONG_PTR)EndMem + PrivilegesLength); 1895 VariableLength -= PrivilegesLength; 1896 1897 if (PreviousMode != KernelMode) 1898 { 1899 _SEH2_TRY 1900 { 1901 RtlCopyMemory(AccessToken->Privileges, 1902 Privileges, 1903 PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES)); 1904 } 1905 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 1906 { 1907 Status = _SEH2_GetExceptionCode(); 1908 } 1909 _SEH2_END; 1910 } 1911 else 1912 { 1913 RtlCopyMemory(AccessToken->Privileges, 1914 Privileges, 1915 PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES)); 1916 } 1917 1918 if (!NT_SUCCESS(Status)) 1919 goto Quit; 1920 } 1921 1922 /* Update the privilege flags */ 1923 SepUpdatePrivilegeFlagsToken(AccessToken); 1924 1925 /* Copy the user and groups */ 1926 AccessToken->UserAndGroupCount = 1 + GroupCount; 1927 AccessToken->UserAndGroups = EndMem; 1928 EndMem = &AccessToken->UserAndGroups[AccessToken->UserAndGroupCount]; 1929 VariableLength -= ((ULONG_PTR)EndMem - (ULONG_PTR)AccessToken->UserAndGroups); 1930 1931 Status = RtlCopySidAndAttributesArray(1, 1932 User, 1933 VariableLength, 1934 &AccessToken->UserAndGroups[0], 1935 EndMem, 1936 &EndMem, 1937 &VariableLength); 1938 if (!NT_SUCCESS(Status)) 1939 goto Quit; 1940 1941 Status = RtlCopySidAndAttributesArray(GroupCount, 1942 Groups, 1943 VariableLength, 1944 &AccessToken->UserAndGroups[1], 1945 EndMem, 1946 &EndMem, 1947 &VariableLength); 1948 if (!NT_SUCCESS(Status)) 1949 goto Quit; 1950 1951 /* Find the token primary group and default owner */ 1952 Status = SepFindPrimaryGroupAndDefaultOwner(AccessToken, 1953 PrimaryGroup, 1954 Owner, 1955 &PrimaryGroupIndex, 1956 &DefaultOwnerIndex); 1957 if (!NT_SUCCESS(Status)) 1958 { 1959 DPRINT1("SepFindPrimaryGroupAndDefaultOwner failed (Status 0x%lx)\n", Status); 1960 goto Quit; 1961 } 1962 1963 AccessToken->PrimaryGroup = AccessToken->UserAndGroups[PrimaryGroupIndex].Sid; 1964 AccessToken->DefaultOwnerIndex = DefaultOwnerIndex; 1965 1966 /* Now allocate the TOKEN's dynamic information area and set the data */ 1967 AccessToken->DynamicAvailable = 0; // Unused memory in the dynamic area. 1968 AccessToken->DynamicPart = NULL; 1969 if (DefaultDacl != NULL) 1970 { 1971 AccessToken->DynamicPart = ExAllocatePoolWithTag(PagedPool, 1972 DefaultDacl->AclSize, 1973 TAG_TOKEN_DYNAMIC); 1974 if (AccessToken->DynamicPart == NULL) 1975 { 1976 Status = STATUS_INSUFFICIENT_RESOURCES; 1977 goto Quit; 1978 } 1979 EndMem = (PVOID)AccessToken->DynamicPart; 1980 1981 AccessToken->DefaultDacl = EndMem; 1982 1983 RtlCopyMemory(AccessToken->DefaultDacl, 1984 DefaultDacl, 1985 DefaultDacl->AclSize); 1986 } 1987 1988 /* Insert the token only if it's not the system token, otherwise return it directly */ 1989 if (!SystemToken) 1990 { 1991 Status = ObInsertObject(AccessToken, 1992 NULL, 1993 DesiredAccess, 1994 0, 1995 NULL, 1996 TokenHandle); 1997 if (!NT_SUCCESS(Status)) 1998 { 1999 DPRINT1("ObInsertObject() failed (Status 0x%lx)\n", Status); 2000 } 2001 } 2002 else 2003 { 2004 /* Return pointer instead of handle */ 2005 *TokenHandle = (HANDLE)AccessToken; 2006 } 2007 2008 Quit: 2009 if (!NT_SUCCESS(Status)) 2010 { 2011 /* Dereference the token, the delete procedure will clean it up */ 2012 ObDereferenceObject(AccessToken); 2013 } 2014 2015 return Status; 2016 } 2017 2018 /** 2019 * @brief 2020 * Private helper function responsible for creating a restricted access 2021 * token, that is, a filtered token from privileges and groups and with 2022 * restricted SIDs added into the token on demand by the caller. 2023 * 2024 * @param[in] Token 2025 * An existing and valid access token. 2026 * 2027 * @param[in] PrivilegesToBeDeleted 2028 * A list of privileges to be deleted within the token that's going 2029 * to be filtered. This parameter is ignored if the caller wants to disable 2030 * all the privileges by specifying DISABLE_MAX_PRIVILEGE in the flags 2031 * parameter. 2032 * 2033 * @param[in] SidsToBeDisabled 2034 * A list of group SIDs to be disabled within the token. This parameter 2035 * can be NULL. 2036 * 2037 * @param[in] RestrictedSidsIntoToken 2038 * A list of restricted SIDs to be added into the token. This parameter 2039 * can be NULL. 2040 * 2041 * @param[in] PrivilegesCount 2042 * The privilege count of the privileges list. 2043 * 2044 * @param[in] RegularGroupsSidCount 2045 * The SIDs count of the group SIDs list. 2046 * 2047 * @param[in] RestrictedSidsCount 2048 * The restricted SIDs count of restricted SIDs list. 2049 * 2050 * @param[in] PrivilegeFlags 2051 * Influences how the privileges should be filtered in an access 2052 * token. See NtFilterToken syscall for more information. 2053 * 2054 * @param[in] PreviousMode 2055 * Processor level access mode. 2056 * 2057 * @param[out] FilteredToken 2058 * The filtered token, returned to the caller. 2059 * 2060 * @return 2061 * Returns STATUS_SUCCESS if token token filtering has completed successfully. 2062 * STATUS_INVALID_PARAMETER is returned if one or more of the parameters 2063 * do not meet the conditions imposed by the function. A failure NTSTATUS 2064 * code is returned otherwise. 2065 * 2066 * @remarks 2067 * The final outcome of privileges and/or SIDs filtering is not always 2068 * deterministic. That is, any privileges or SIDs that aren't present 2069 * in the access token are ignored and the function continues with the 2070 * next privilege or SID to find for filtering. For a fully deterministic 2071 * outcome the caller is responsible for querying the information details 2072 * of privileges and SIDs present in the token and then afterwards use 2073 * such obtained information to do any kind of filtering to the token. 2074 */ 2075 static 2076 NTSTATUS 2077 SepPerformTokenFiltering( 2078 _In_ PTOKEN Token, 2079 _In_opt_ PLUID_AND_ATTRIBUTES PrivilegesToBeDeleted, 2080 _In_opt_ PSID_AND_ATTRIBUTES SidsToBeDisabled, 2081 _In_opt_ PSID_AND_ATTRIBUTES RestrictedSidsIntoToken, 2082 _When_(PrivilegesToBeDeleted != NULL, _In_) ULONG PrivilegesCount, 2083 _When_(SidsToBeDisabled != NULL, _In_) ULONG RegularGroupsSidCount, 2084 _When_(RestrictedSidsIntoToken != NULL, _In_) ULONG RestrictedSidsCount, 2085 _In_ ULONG PrivilegeFlags, 2086 _In_ KPROCESSOR_MODE PreviousMode, 2087 _Out_ PTOKEN *FilteredToken) 2088 { 2089 PTOKEN AccessToken; 2090 NTSTATUS Status; 2091 PVOID EndMem; 2092 ULONG RestrictedSidsLength; 2093 ULONG PrivilegesLength; 2094 ULONG PrimaryGroupIndex; 2095 ULONG RestrictedSidsInList; 2096 ULONG RestrictedSidsInToken; 2097 ULONG VariableLength, TotalSize; 2098 ULONG PrivsInToken, PrivsInList; 2099 ULONG GroupsInToken, GroupsInList; 2100 BOOLEAN WantPrivilegesDisabled; 2101 BOOLEAN FoundPrivilege; 2102 BOOLEAN FoundGroup; 2103 PAGED_CODE(); 2104 2105 /* Ensure that the token we get is not garbage */ 2106 ASSERT(Token); 2107 2108 /* Assume the caller doesn't want privileges disabled */ 2109 WantPrivilegesDisabled = FALSE; 2110 2111 /* Assume we haven't found anything */ 2112 FoundPrivilege = FALSE; 2113 FoundGroup = FALSE; 2114 2115 /* 2116 * Take the size that we need for filtered token 2117 * allocation based upon the existing access token 2118 * we've been given. 2119 */ 2120 VariableLength = Token->VariableLength; 2121 2122 if (RestrictedSidsIntoToken != NULL) 2123 { 2124 /* 2125 * If the caller provided a list of restricted SIDs 2126 * to be added onto the filtered access token then 2127 * we must compute the size which is the total space 2128 * of the current token and the length of the restricted 2129 * SIDs for the filtered token. 2130 */ 2131 RestrictedSidsLength = RestrictedSidsCount * sizeof(SID_AND_ATTRIBUTES); 2132 RestrictedSidsLength += RtlLengthSidAndAttributes(RestrictedSidsCount, RestrictedSidsIntoToken); 2133 RestrictedSidsLength = ALIGN_UP_BY(RestrictedSidsLength, sizeof(PVOID)); 2134 2135 /* 2136 * The variable length of the token is not just 2137 * the actual space length of the existing token 2138 * but also the sum of the restricted SIDs length. 2139 */ 2140 VariableLength += RestrictedSidsLength; 2141 TotalSize = FIELD_OFFSET(TOKEN, VariablePart) + VariableLength + RestrictedSidsLength; 2142 } 2143 else 2144 { 2145 /* Otherwise the size is of the actual current token */ 2146 TotalSize = FIELD_OFFSET(TOKEN, VariablePart) + VariableLength; 2147 } 2148 2149 /* Set up a filtered token object */ 2150 Status = ObCreateObject(PreviousMode, 2151 SeTokenObjectType, 2152 NULL, 2153 PreviousMode, 2154 NULL, 2155 TotalSize, 2156 0, 2157 0, 2158 (PVOID*)&AccessToken); 2159 if (!NT_SUCCESS(Status)) 2160 { 2161 DPRINT1("SepPerformTokenFiltering(): Failed to create the filtered token object (Status 0x%lx)\n", Status); 2162 return Status; 2163 } 2164 2165 /* Initialize the token and begin filling stuff to it */ 2166 RtlZeroMemory(AccessToken, TotalSize); 2167 2168 /* Set up a lock for the new token */ 2169 Status = SepCreateTokenLock(AccessToken); 2170 if (!NT_SUCCESS(Status)) 2171 { 2172 ObDereferenceObject(AccessToken); 2173 return Status; 2174 } 2175 2176 /* Allocate new IDs for the token */ 2177 ExAllocateLocallyUniqueId(&AccessToken->TokenId); 2178 ExAllocateLocallyUniqueId(&AccessToken->ModifiedId); 2179 2180 /* Copy the type and impersonation level from the token */ 2181 AccessToken->TokenType = Token->TokenType; 2182 AccessToken->ImpersonationLevel = Token->ImpersonationLevel; 2183 2184 /* Copy the immutable fields */ 2185 RtlCopyLuid(&AccessToken->TokenSource.SourceIdentifier, 2186 &Token->TokenSource.SourceIdentifier); 2187 RtlCopyMemory(AccessToken->TokenSource.SourceName, 2188 Token->TokenSource.SourceName, 2189 sizeof(Token->TokenSource.SourceName)); 2190 2191 RtlCopyLuid(&AccessToken->AuthenticationId, &Token->AuthenticationId); 2192 RtlCopyLuid(&AccessToken->ParentTokenId, &Token->TokenId); 2193 RtlCopyLuid(&AccessToken->OriginatingLogonSession, 2194 &Token->OriginatingLogonSession); 2195 2196 AccessToken->ExpirationTime = Token->ExpirationTime; 2197 2198 /* Copy the mutable fields */ 2199 AccessToken->SessionId = Token->SessionId; 2200 AccessToken->TokenFlags = Token->TokenFlags & ~TOKEN_SESSION_NOT_REFERENCED; 2201 2202 /* Reference the logon session */ 2203 Status = SepRmReferenceLogonSession(&AccessToken->AuthenticationId); 2204 if (!NT_SUCCESS(Status)) 2205 { 2206 /* We failed, bail out*/ 2207 DPRINT1("SepPerformTokenFiltering(): Failed to reference the logon session (Status 0x%lx)\n", Status); 2208 AccessToken->TokenFlags |= TOKEN_SESSION_NOT_REFERENCED; 2209 goto Quit; 2210 } 2211 2212 /* Insert the referenced logon session into the token */ 2213 Status = SepRmInsertLogonSessionIntoToken(AccessToken); 2214 if (!NT_SUCCESS(Status)) 2215 { 2216 /* Failed to insert the logon session into the token, bail out */ 2217 DPRINT1("SepPerformTokenFiltering(): Failed to insert the logon session into token (Status 0x%lx)\n", Status); 2218 goto Quit; 2219 } 2220 2221 /* Fill in token debug information */ 2222 #if DBG 2223 RtlCopyMemory(AccessToken->ImageFileName, 2224 PsGetCurrentProcess()->ImageFileName, 2225 min(sizeof(AccessToken->ImageFileName), sizeof(PsGetCurrentProcess()->ImageFileName))); 2226 2227 AccessToken->ProcessCid = PsGetCurrentProcessId(); 2228 AccessToken->ThreadCid = PsGetCurrentThreadId(); 2229 AccessToken->CreateMethod = TOKEN_FILTER_METHOD; 2230 #endif 2231 2232 /* Assign the data that reside in the token's variable information area */ 2233 AccessToken->VariableLength = VariableLength; 2234 EndMem = (PVOID)&AccessToken->VariablePart; 2235 2236 /* Copy the privileges from the existing token */ 2237 AccessToken->PrivilegeCount = 0; 2238 AccessToken->Privileges = NULL; 2239 if (Token->Privileges && (Token->PrivilegeCount > 0)) 2240 { 2241 PrivilegesLength = Token->PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES); 2242 PrivilegesLength = ALIGN_UP_BY(PrivilegesLength, sizeof(PVOID)); 2243 2244 /* 2245 * Ensure that the token can actually hold all 2246 * the privileges from the existing token. 2247 * Otherwise something's seriously wrong and 2248 * we've to guard ourselves. 2249 */ 2250 ASSERT(VariableLength >= PrivilegesLength); 2251 2252 AccessToken->PrivilegeCount = Token->PrivilegeCount; 2253 AccessToken->Privileges = EndMem; 2254 EndMem = (PVOID)((ULONG_PTR)EndMem + PrivilegesLength); 2255 VariableLength -= PrivilegesLength; 2256 2257 if (PreviousMode != KernelMode) 2258 { 2259 _SEH2_TRY 2260 { 2261 RtlCopyMemory(AccessToken->Privileges, 2262 Token->Privileges, 2263 AccessToken->PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES)); 2264 } 2265 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 2266 { 2267 Status = _SEH2_GetExceptionCode(); 2268 _SEH2_YIELD(goto Quit); 2269 } 2270 _SEH2_END; 2271 } 2272 else 2273 { 2274 RtlCopyMemory(AccessToken->Privileges, 2275 Token->Privileges, 2276 AccessToken->PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES)); 2277 } 2278 } 2279 2280 /* Copy the user and groups */ 2281 AccessToken->UserAndGroupCount = 0; 2282 AccessToken->UserAndGroups = NULL; 2283 if (Token->UserAndGroups && (Token->UserAndGroupCount > 0)) 2284 { 2285 AccessToken->UserAndGroupCount = Token->UserAndGroupCount; 2286 AccessToken->UserAndGroups = EndMem; 2287 EndMem = &AccessToken->UserAndGroups[AccessToken->UserAndGroupCount]; 2288 VariableLength -= ((ULONG_PTR)EndMem - (ULONG_PTR)AccessToken->UserAndGroups); 2289 2290 if (PreviousMode != KernelMode) 2291 { 2292 _SEH2_TRY 2293 { 2294 Status = RtlCopySidAndAttributesArray(AccessToken->UserAndGroupCount, 2295 Token->UserAndGroups, 2296 VariableLength, 2297 AccessToken->UserAndGroups, 2298 EndMem, 2299 &EndMem, 2300 &VariableLength); 2301 } 2302 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 2303 { 2304 Status = _SEH2_GetExceptionCode(); 2305 _SEH2_YIELD(goto Quit); 2306 } 2307 _SEH2_END; 2308 } 2309 else 2310 { 2311 Status = RtlCopySidAndAttributesArray(AccessToken->UserAndGroupCount, 2312 Token->UserAndGroups, 2313 VariableLength, 2314 AccessToken->UserAndGroups, 2315 EndMem, 2316 &EndMem, 2317 &VariableLength); 2318 if (!NT_SUCCESS(Status)) 2319 { 2320 DPRINT1("SepPerformTokenFiltering(): Failed to copy the groups into token (Status 0x%lx)\n", Status); 2321 goto Quit; 2322 } 2323 } 2324 } 2325 2326 /* Copy the restricted SIDs */ 2327 AccessToken->RestrictedSidCount = 0; 2328 AccessToken->RestrictedSids = NULL; 2329 if (Token->RestrictedSids && (Token->RestrictedSidCount > 0)) 2330 { 2331 AccessToken->RestrictedSidCount = Token->RestrictedSidCount; 2332 AccessToken->RestrictedSids = EndMem; 2333 EndMem = &AccessToken->RestrictedSids[AccessToken->RestrictedSidCount]; 2334 VariableLength -= ((ULONG_PTR)EndMem - (ULONG_PTR)AccessToken->RestrictedSids); 2335 2336 if (PreviousMode != KernelMode) 2337 { 2338 _SEH2_TRY 2339 { 2340 Status = RtlCopySidAndAttributesArray(AccessToken->RestrictedSidCount, 2341 Token->RestrictedSids, 2342 VariableLength, 2343 AccessToken->RestrictedSids, 2344 EndMem, 2345 &EndMem, 2346 &VariableLength); 2347 } 2348 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 2349 { 2350 Status = _SEH2_GetExceptionCode(); 2351 _SEH2_YIELD(goto Quit); 2352 } 2353 _SEH2_END; 2354 } 2355 else 2356 { 2357 Status = RtlCopySidAndAttributesArray(AccessToken->RestrictedSidCount, 2358 Token->RestrictedSids, 2359 VariableLength, 2360 AccessToken->RestrictedSids, 2361 EndMem, 2362 &EndMem, 2363 &VariableLength); 2364 if (!NT_SUCCESS(Status)) 2365 { 2366 DPRINT1("SepPerformTokenFiltering(): Failed to copy the restricted SIDs into token (Status 0x%lx)\n", Status); 2367 goto Quit; 2368 } 2369 } 2370 } 2371 2372 /* Search for the primary group */ 2373 Status = SepFindPrimaryGroupAndDefaultOwner(AccessToken, 2374 Token->PrimaryGroup, 2375 NULL, 2376 &PrimaryGroupIndex, 2377 NULL); 2378 if (!NT_SUCCESS(Status)) 2379 { 2380 DPRINT1("SepPerformTokenFiltering(): Failed searching for the primary group (Status 0x%lx)\n", Status); 2381 goto Quit; 2382 } 2383 2384 /* Assign the primary group and default owner index now */ 2385 AccessToken->PrimaryGroup = AccessToken->UserAndGroups[PrimaryGroupIndex].Sid; 2386 AccessToken->DefaultOwnerIndex = Token->DefaultOwnerIndex; 2387 2388 /* Now allocate the token's dynamic information area and set the data */ 2389 AccessToken->DynamicAvailable = 0; 2390 AccessToken->DynamicPart = NULL; 2391 if (Token->DynamicPart && Token->DefaultDacl) 2392 { 2393 AccessToken->DynamicPart = ExAllocatePoolWithTag(PagedPool, 2394 Token->DefaultDacl->AclSize, 2395 TAG_TOKEN_DYNAMIC); 2396 if (AccessToken->DynamicPart == NULL) 2397 { 2398 Status = STATUS_INSUFFICIENT_RESOURCES; 2399 goto Quit; 2400 } 2401 2402 EndMem = (PVOID)AccessToken->DynamicPart; 2403 AccessToken->DefaultDacl = EndMem; 2404 2405 RtlCopyMemory(AccessToken->DefaultDacl, 2406 Token->DefaultDacl, 2407 Token->DefaultDacl->AclSize); 2408 } 2409 2410 /* 2411 * Now figure out what does the caller 2412 * want with the privileges. 2413 */ 2414 if (PrivilegeFlags & DISABLE_MAX_PRIVILEGE) 2415 { 2416 /* 2417 * The caller wants them disabled, cache this request 2418 * for later operations. 2419 */ 2420 WantPrivilegesDisabled = TRUE; 2421 } 2422 2423 if (PrivilegeFlags & SANDBOX_INERT) 2424 { 2425 /* The caller wants an inert token, store the TOKEN_SANDBOX_INERT flag now */ 2426 AccessToken->TokenFlags |= TOKEN_SANDBOX_INERT; 2427 } 2428 2429 /* 2430 * Now it's time to filter the token's privileges. 2431 * Loop all the privileges in the token. 2432 */ 2433 for (PrivsInToken = 0; PrivsInToken < AccessToken->PrivilegeCount; PrivsInToken++) 2434 { 2435 if (WantPrivilegesDisabled) 2436 { 2437 /* 2438 * We got the acknowledgement that the caller wants 2439 * to disable all the privileges so let's just do it. 2440 * However, as per the general documentation is stated 2441 * that only SE_CHANGE_NOTIFY_PRIVILEGE must be kept 2442 * therefore in that case we must skip this privilege. 2443 */ 2444 if (AccessToken->Privileges[PrivsInToken].Luid.LowPart == SE_CHANGE_NOTIFY_PRIVILEGE) 2445 { 2446 continue; 2447 } 2448 else 2449 { 2450 /* 2451 * The act of disabling privileges actually means 2452 * "deleting" them from the access token entirely. 2453 * First we must disable them so that we can update 2454 * token flags accordingly. 2455 */ 2456 AccessToken->Privileges[PrivsInToken].Attributes &= ~SE_PRIVILEGE_ENABLED; 2457 SepUpdateSinglePrivilegeFlagToken(AccessToken, PrivsInToken); 2458 2459 /* Remove the privileges now */ 2460 SepRemovePrivilegeToken(AccessToken, PrivsInToken); 2461 PrivsInToken--; 2462 } 2463 } 2464 else 2465 { 2466 if (PrivilegesToBeDeleted != NULL) 2467 { 2468 /* Loop the privileges we've got to delete */ 2469 for (PrivsInList = 0; PrivsInList < PrivilegesCount; PrivsInList++) 2470 { 2471 /* Does this privilege exist in the token? */ 2472 if (RtlEqualLuid(&AccessToken->Privileges[PrivsInToken].Luid, 2473 &PrivilegesToBeDeleted[PrivsInList].Luid)) 2474 { 2475 /* Mark that we found it */ 2476 FoundPrivilege = TRUE; 2477 break; 2478 } 2479 } 2480 2481 /* Did we find the privilege? */ 2482 if (PrivsInList == PrivilegesCount) 2483 { 2484 /* We didn't, continue with next one */ 2485 continue; 2486 } 2487 } 2488 } 2489 2490 /* 2491 * If we have found the target privilege in the token 2492 * based on the privileges list given by the caller 2493 * then begin deleting it. 2494 */ 2495 if (FoundPrivilege) 2496 { 2497 /* Disable the privilege and update the flags */ 2498 AccessToken->Privileges[PrivsInToken].Attributes &= ~SE_PRIVILEGE_ENABLED; 2499 SepUpdateSinglePrivilegeFlagToken(AccessToken, PrivsInToken); 2500 2501 /* Delete the privilege */ 2502 SepRemovePrivilegeToken(AccessToken, PrivsInToken); 2503 2504 /* 2505 * Adjust the index and reset the FoundPrivilege indicator 2506 * so that we can continue with the next privilege to delete. 2507 */ 2508 PrivsInToken--; 2509 FoundPrivilege = FALSE; 2510 continue; 2511 } 2512 } 2513 2514 /* 2515 * Loop the group SIDs that we want to disable as 2516 * per on the request by the caller. 2517 */ 2518 if (SidsToBeDisabled != NULL) 2519 { 2520 for (GroupsInToken = 0; GroupsInToken < AccessToken->UserAndGroupCount; GroupsInToken++) 2521 { 2522 for (GroupsInList = 0; GroupsInList < RegularGroupsSidCount; GroupsInList++) 2523 { 2524 /* Does this group SID exist in the token? */ 2525 if (RtlEqualSid(&AccessToken->UserAndGroups[GroupsInToken].Sid, 2526 &SidsToBeDisabled[GroupsInList].Sid)) 2527 { 2528 /* Mark that we found it */ 2529 FoundGroup = TRUE; 2530 break; 2531 } 2532 } 2533 2534 /* Did we find the group? */ 2535 if (GroupsInList == RegularGroupsSidCount) 2536 { 2537 /* We didn't, continue with next one */ 2538 continue; 2539 } 2540 2541 /* If we have found the group, disable it */ 2542 if (FoundGroup) 2543 { 2544 /* 2545 * If the acess token belongs to the administrators 2546 * group and this is the target group, we must take 2547 * away TOKEN_HAS_ADMIN_GROUP flag from the token. 2548 */ 2549 if (RtlEqualSid(SeAliasAdminsSid, 2550 &AccessToken->UserAndGroups[GroupsInToken].Sid)) 2551 { 2552 AccessToken->TokenFlags &= ~TOKEN_HAS_ADMIN_GROUP; 2553 } 2554 2555 /* 2556 * If the target group that we have found it is the 2557 * owner then from now on it no longer is but the user. 2558 * Therefore assign the default owner index as the user. 2559 */ 2560 if (AccessToken->DefaultOwnerIndex == GroupsInToken) 2561 { 2562 AccessToken->DefaultOwnerIndex = 0; 2563 } 2564 2565 /* 2566 * The principle of disabling a group SID is by 2567 * taking away SE_GROUP_ENABLED_BY_DEFAULT and 2568 * SE_GROUP_ENABLED attributes and assign 2569 * SE_GROUP_USE_FOR_DENY_ONLY. This renders 2570 * SID a "Deny only" SID. 2571 */ 2572 AccessToken->UserAndGroups[GroupsInToken].Attributes &= ~(SE_GROUP_ENABLED | SE_GROUP_ENABLED_BY_DEFAULT); 2573 AccessToken->UserAndGroups[GroupsInToken].Attributes |= SE_GROUP_USE_FOR_DENY_ONLY; 2574 2575 /* Adjust the index and continue with the next group */ 2576 GroupsInToken--; 2577 FoundGroup = FALSE; 2578 continue; 2579 } 2580 } 2581 } 2582 2583 /* 2584 * Insert the restricted SIDs into the token on 2585 * the request by the caller. 2586 */ 2587 if (RestrictedSidsIntoToken != NULL) 2588 { 2589 for (RestrictedSidsInList = 0; RestrictedSidsInList < RestrictedSidsCount; RestrictedSidsInList++) 2590 { 2591 /* Did the caller assign attributes to the restricted SIDs? */ 2592 if (RestrictedSidsIntoToken[RestrictedSidsInList].Attributes != 0) 2593 { 2594 /* There mustn't be any attributes, bail out */ 2595 DPRINT1("SepPerformTokenFiltering(): There mustn't be any attributes to restricted SIDs!\n"); 2596 Status = STATUS_INVALID_PARAMETER; 2597 goto Quit; 2598 } 2599 } 2600 2601 /* 2602 * Ensure that the token can hold the restricted SIDs 2603 * (the variable length is calculated at the beginning 2604 * of the routine call). 2605 */ 2606 ASSERT(VariableLength >= RestrictedSidsLength); 2607 2608 /* 2609 * Now let's begin inserting the restricted SIDs into the filtered 2610 * access token from the list the caller gave us. 2611 */ 2612 AccessToken->RestrictedSidCount = RestrictedSidsCount; 2613 AccessToken->RestrictedSids = EndMem; 2614 EndMem = (PVOID)((ULONG_PTR)EndMem + RestrictedSidsLength); 2615 VariableLength -= RestrictedSidsLength; 2616 2617 if (PreviousMode != KernelMode) 2618 { 2619 _SEH2_TRY 2620 { 2621 RtlCopyMemory(AccessToken->RestrictedSids, 2622 RestrictedSidsIntoToken, 2623 AccessToken->RestrictedSidCount * sizeof(SID_AND_ATTRIBUTES)); 2624 } 2625 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 2626 { 2627 Status = _SEH2_GetExceptionCode(); 2628 _SEH2_YIELD(goto Quit); 2629 } 2630 _SEH2_END; 2631 } 2632 else 2633 { 2634 RtlCopyMemory(AccessToken->RestrictedSids, 2635 RestrictedSidsIntoToken, 2636 AccessToken->RestrictedSidCount * sizeof(SID_AND_ATTRIBUTES)); 2637 } 2638 2639 /* 2640 * As we've copied the restricted SIDs into 2641 * the token, we must assign them the following 2642 * combination of attributes SE_GROUP_ENABLED, 2643 * SE_GROUP_ENABLED_BY_DEFAULT and SE_GROUP_MANDATORY. 2644 * With such attributes we estabilish that restricting 2645 * SIDs into the token are enabled for access checks. 2646 */ 2647 for (RestrictedSidsInToken = 0; RestrictedSidsInToken < AccessToken->RestrictedSidCount; RestrictedSidsInToken++) 2648 { 2649 AccessToken->RestrictedSids[RestrictedSidsInToken].Attributes |= (SE_GROUP_ENABLED | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_MANDATORY); 2650 } 2651 2652 /* 2653 * As we added restricted SIDs into the token, mark 2654 * it as restricted. 2655 */ 2656 AccessToken->TokenFlags |= TOKEN_IS_RESTRICTED; 2657 } 2658 2659 /* We've finally filtered the token, give it to the caller */ 2660 *FilteredToken = AccessToken; 2661 Status = STATUS_SUCCESS; 2662 DPRINT("SepPerformTokenFiltering(): The token has been filtered!\n"); 2663 2664 Quit: 2665 if (!NT_SUCCESS(Status)) 2666 { 2667 /* Dereference the token */ 2668 ObDereferenceObject(AccessToken); 2669 } 2670 2671 return Status; 2672 } 2673 2674 /** 2675 * @brief 2676 * Creates the system process token. 2677 * 2678 * @return 2679 * Returns the system process token if the operations have 2680 * completed successfully. 2681 */ 2682 CODE_SEG("INIT") 2683 PTOKEN 2684 NTAPI 2685 SepCreateSystemProcessToken(VOID) 2686 { 2687 LUID_AND_ATTRIBUTES Privileges[25]; 2688 ULONG GroupAttributes, OwnerAttributes; 2689 SID_AND_ATTRIBUTES Groups[32]; 2690 LARGE_INTEGER Expiration; 2691 SID_AND_ATTRIBUTES UserSid; 2692 ULONG GroupsLength; 2693 PSID PrimaryGroup; 2694 OBJECT_ATTRIBUTES ObjectAttributes; 2695 PSID Owner; 2696 ULONG i; 2697 PTOKEN Token; 2698 NTSTATUS Status; 2699 2700 /* Don't ever expire */ 2701 Expiration.QuadPart = -1; 2702 2703 /* All groups mandatory and enabled */ 2704 GroupAttributes = SE_GROUP_ENABLED | SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT; 2705 OwnerAttributes = SE_GROUP_ENABLED | SE_GROUP_OWNER | SE_GROUP_ENABLED_BY_DEFAULT; 2706 2707 /* User is Local System */ 2708 UserSid.Sid = SeLocalSystemSid; 2709 UserSid.Attributes = 0; 2710 2711 /* Primary group is Local System */ 2712 PrimaryGroup = SeLocalSystemSid; 2713 2714 /* Owner is Administrators */ 2715 Owner = SeAliasAdminsSid; 2716 2717 /* Groups are Administrators, World, and Authenticated Users */ 2718 Groups[0].Sid = SeAliasAdminsSid; 2719 Groups[0].Attributes = OwnerAttributes; 2720 Groups[1].Sid = SeWorldSid; 2721 Groups[1].Attributes = GroupAttributes; 2722 Groups[2].Sid = SeAuthenticatedUsersSid; 2723 Groups[2].Attributes = GroupAttributes; 2724 GroupsLength = sizeof(SID_AND_ATTRIBUTES) + 2725 SeLengthSid(Groups[0].Sid) + 2726 SeLengthSid(Groups[1].Sid) + 2727 SeLengthSid(Groups[2].Sid); 2728 ASSERT(GroupsLength <= sizeof(Groups)); 2729 2730 /* Setup the privileges */ 2731 i = 0; 2732 Privileges[i].Attributes = SE_PRIVILEGE_ENABLED_BY_DEFAULT | SE_PRIVILEGE_ENABLED; 2733 Privileges[i++].Luid = SeTcbPrivilege; 2734 2735 Privileges[i].Attributes = 0; 2736 Privileges[i++].Luid = SeCreateTokenPrivilege; 2737 2738 Privileges[i].Attributes = 0; 2739 Privileges[i++].Luid = SeTakeOwnershipPrivilege; 2740 2741 Privileges[i].Attributes = SE_PRIVILEGE_ENABLED_BY_DEFAULT | SE_PRIVILEGE_ENABLED; 2742 Privileges[i++].Luid = SeCreatePagefilePrivilege; 2743 2744 Privileges[i].Attributes = SE_PRIVILEGE_ENABLED_BY_DEFAULT | SE_PRIVILEGE_ENABLED; 2745 Privileges[i++].Luid = SeLockMemoryPrivilege; 2746 2747 Privileges[i].Attributes = 0; 2748 Privileges[i++].Luid = SeAssignPrimaryTokenPrivilege; 2749 2750 Privileges[i].Attributes = 0; 2751 Privileges[i++].Luid = SeIncreaseQuotaPrivilege; 2752 2753 Privileges[i].Attributes = SE_PRIVILEGE_ENABLED_BY_DEFAULT | SE_PRIVILEGE_ENABLED; 2754 Privileges[i++].Luid = SeIncreaseBasePriorityPrivilege; 2755 2756 Privileges[i].Attributes = SE_PRIVILEGE_ENABLED_BY_DEFAULT | SE_PRIVILEGE_ENABLED; 2757 Privileges[i++].Luid = SeCreatePermanentPrivilege; 2758 2759 Privileges[i].Attributes = SE_PRIVILEGE_ENABLED_BY_DEFAULT | SE_PRIVILEGE_ENABLED; 2760 Privileges[i++].Luid = SeDebugPrivilege; 2761 2762 Privileges[i].Attributes = SE_PRIVILEGE_ENABLED_BY_DEFAULT | SE_PRIVILEGE_ENABLED; 2763 Privileges[i++].Luid = SeAuditPrivilege; 2764 2765 Privileges[i].Attributes = 0; 2766 Privileges[i++].Luid = SeSecurityPrivilege; 2767 2768 Privileges[i].Attributes = 0; 2769 Privileges[i++].Luid = SeSystemEnvironmentPrivilege; 2770 2771 Privileges[i].Attributes = SE_PRIVILEGE_ENABLED_BY_DEFAULT | SE_PRIVILEGE_ENABLED; 2772 Privileges[i++].Luid = SeChangeNotifyPrivilege; 2773 2774 Privileges[i].Attributes = 0; 2775 Privileges[i++].Luid = SeBackupPrivilege; 2776 2777 Privileges[i].Attributes = 0; 2778 Privileges[i++].Luid = SeRestorePrivilege; 2779 2780 Privileges[i].Attributes = 0; 2781 Privileges[i++].Luid = SeShutdownPrivilege; 2782 2783 Privileges[i].Attributes = 0; 2784 Privileges[i++].Luid = SeLoadDriverPrivilege; 2785 2786 Privileges[i].Attributes = SE_PRIVILEGE_ENABLED_BY_DEFAULT | SE_PRIVILEGE_ENABLED; 2787 Privileges[i++].Luid = SeProfileSingleProcessPrivilege; 2788 2789 Privileges[i].Attributes = 0; 2790 Privileges[i++].Luid = SeSystemtimePrivilege; 2791 ASSERT(i == 20); 2792 2793 /* Setup the object attributes */ 2794 InitializeObjectAttributes(&ObjectAttributes, NULL, 0, NULL, NULL); 2795 ASSERT(SeSystemDefaultDacl != NULL); 2796 2797 /* Create the token */ 2798 Status = SepCreateToken((PHANDLE)&Token, 2799 KernelMode, 2800 0, 2801 &ObjectAttributes, 2802 TokenPrimary, 2803 SecurityAnonymous, 2804 &SeSystemAuthenticationId, 2805 &Expiration, 2806 &UserSid, 2807 3, 2808 Groups, 2809 GroupsLength, 2810 20, 2811 Privileges, 2812 Owner, 2813 PrimaryGroup, 2814 SeSystemDefaultDacl, 2815 &SeSystemTokenSource, 2816 TRUE); 2817 ASSERT(Status == STATUS_SUCCESS); 2818 2819 /* Return the token */ 2820 return Token; 2821 } 2822 2823 /** 2824 * @brief 2825 * Creates the anonymous logon token for the system. The difference between this 2826 * token and the other one is the inclusion of everyone SID group (being SeWorldSid). 2827 * The other token lacks such group. 2828 * 2829 * @return 2830 * Returns the system's anonymous logon token if the operations have 2831 * completed successfully. 2832 */ 2833 CODE_SEG("INIT") 2834 PTOKEN 2835 SepCreateSystemAnonymousLogonToken(VOID) 2836 { 2837 SID_AND_ATTRIBUTES Groups[32], UserSid; 2838 PSID PrimaryGroup; 2839 PTOKEN Token; 2840 ULONG GroupsLength; 2841 LARGE_INTEGER Expiration; 2842 OBJECT_ATTRIBUTES ObjectAttributes; 2843 NTSTATUS Status; 2844 2845 /* The token never expires */ 2846 Expiration.QuadPart = -1; 2847 2848 /* The user is the anonymous logon */ 2849 UserSid.Sid = SeAnonymousLogonSid; 2850 UserSid.Attributes = 0; 2851 2852 /* The primary group is also the anonymous logon */ 2853 PrimaryGroup = SeAnonymousLogonSid; 2854 2855 /* The only group for the token is the World */ 2856 Groups[0].Sid = SeWorldSid; 2857 Groups[0].Attributes = SE_GROUP_ENABLED | SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT; 2858 GroupsLength = sizeof(SID_AND_ATTRIBUTES) + 2859 SeLengthSid(Groups[0].Sid); 2860 ASSERT(GroupsLength <= sizeof(Groups)); 2861 2862 /* Initialise the object attributes for the token */ 2863 InitializeObjectAttributes(&ObjectAttributes, NULL, 0, NULL, NULL); 2864 ASSERT(SeSystemAnonymousLogonDacl != NULL); 2865 2866 /* Create token */ 2867 Status = SepCreateToken((PHANDLE)&Token, 2868 KernelMode, 2869 0, 2870 &ObjectAttributes, 2871 TokenPrimary, 2872 SecurityAnonymous, 2873 &SeAnonymousAuthenticationId, 2874 &Expiration, 2875 &UserSid, 2876 1, 2877 Groups, 2878 GroupsLength, 2879 0, 2880 NULL, 2881 NULL, 2882 PrimaryGroup, 2883 SeSystemAnonymousLogonDacl, 2884 &SeSystemTokenSource, 2885 TRUE); 2886 ASSERT(Status == STATUS_SUCCESS); 2887 2888 /* Return the anonymous logon token */ 2889 return Token; 2890 } 2891 2892 /** 2893 * @brief 2894 * Creates the anonymous logon token for the system. This kind of token 2895 * doesn't include the everyone SID group (being SeWorldSid). 2896 * 2897 * @return 2898 * Returns the system's anonymous logon token if the operations have 2899 * completed successfully. 2900 */ 2901 CODE_SEG("INIT") 2902 PTOKEN 2903 SepCreateSystemAnonymousLogonTokenNoEveryone(VOID) 2904 { 2905 SID_AND_ATTRIBUTES UserSid; 2906 PSID PrimaryGroup; 2907 PTOKEN Token; 2908 LARGE_INTEGER Expiration; 2909 OBJECT_ATTRIBUTES ObjectAttributes; 2910 NTSTATUS Status; 2911 2912 /* The token never expires */ 2913 Expiration.QuadPart = -1; 2914 2915 /* The user is the anonymous logon */ 2916 UserSid.Sid = SeAnonymousLogonSid; 2917 UserSid.Attributes = 0; 2918 2919 /* The primary group is also the anonymous logon */ 2920 PrimaryGroup = SeAnonymousLogonSid; 2921 2922 /* Initialise the object attributes for the token */ 2923 InitializeObjectAttributes(&ObjectAttributes, NULL, 0, NULL, NULL); 2924 ASSERT(SeSystemAnonymousLogonDacl != NULL); 2925 2926 /* Create token */ 2927 Status = SepCreateToken((PHANDLE)&Token, 2928 KernelMode, 2929 0, 2930 &ObjectAttributes, 2931 TokenPrimary, 2932 SecurityAnonymous, 2933 &SeAnonymousAuthenticationId, 2934 &Expiration, 2935 &UserSid, 2936 0, 2937 NULL, 2938 0, 2939 0, 2940 NULL, 2941 NULL, 2942 PrimaryGroup, 2943 SeSystemAnonymousLogonDacl, 2944 &SeSystemTokenSource, 2945 TRUE); 2946 ASSERT(Status == STATUS_SUCCESS); 2947 2948 /* Return the anonymous (not including everyone) logon token */ 2949 return Token; 2950 } 2951 2952 /* PUBLIC FUNCTIONS ***********************************************************/ 2953 2954 /** 2955 * @brief 2956 * Filters an access token from an existing token, making it more restricted 2957 * than the previous one. 2958 * 2959 * @param[in] ExistingToken 2960 * An existing token for filtering. 2961 * 2962 * @param[in] Flags 2963 * Privilege flag options. This parameter argument influences how the token 2964 * is filtered. Such parameter can be 0. See NtFilterToken syscall for 2965 * more information. 2966 * 2967 * @param[in] SidsToDisable 2968 * Array of SIDs to disable. Such parameter can be NULL. 2969 * 2970 * @param[in] PrivilegesToDelete 2971 * Array of privileges to delete. If DISABLE_MAX_PRIVILEGE flag is specified 2972 * in the Flags parameter, PrivilegesToDelete is ignored. 2973 * 2974 * @param[in] RestrictedSids 2975 * An array of restricted SIDs for the new filtered token. Such parameter 2976 * can be NULL. 2977 * 2978 * @param[out] FilteredToken 2979 * The newly filtered token, returned to the caller. 2980 * 2981 * @return 2982 * Returns STATUS_SUCCESS if the function has successfully completed its 2983 * operations and that the access token has been filtered. STATUS_INVALID_PARAMETER 2984 * is returned if one or more of the parameter are not valid. A failure NTSTATUS code 2985 * is returned otherwise. 2986 * 2987 * @remarks 2988 * WARNING -- The caller IS RESPONSIBLE for locking the existing access token 2989 * before attempting to do any kind of filtering operation into 2990 * the token. The lock MUST BE RELEASED after this kernel routine 2991 * has finished doing its work. 2992 */ 2993 NTSTATUS 2994 NTAPI 2995 SeFilterToken( 2996 _In_ PACCESS_TOKEN ExistingToken, 2997 _In_ ULONG Flags, 2998 _In_opt_ PTOKEN_GROUPS SidsToDisable, 2999 _In_opt_ PTOKEN_PRIVILEGES PrivilegesToDelete, 3000 _In_opt_ PTOKEN_GROUPS RestrictedSids, 3001 _Out_ PACCESS_TOKEN *FilteredToken) 3002 { 3003 NTSTATUS Status; 3004 PTOKEN AccessToken; 3005 ULONG PrivilegesCount = 0; 3006 ULONG SidsCount = 0; 3007 ULONG RestrictedSidsCount = 0; 3008 PAGED_CODE(); 3009 3010 /* Begin copying the counters */ 3011 if (SidsToDisable != NULL) 3012 { 3013 SidsCount = SidsToDisable->GroupCount; 3014 } 3015 3016 if (PrivilegesToDelete != NULL) 3017 { 3018 PrivilegesCount = PrivilegesToDelete->PrivilegeCount; 3019 } 3020 3021 if (RestrictedSids != NULL) 3022 { 3023 RestrictedSidsCount = RestrictedSids->GroupCount; 3024 } 3025 3026 /* Call the internal API */ 3027 Status = SepPerformTokenFiltering(ExistingToken, 3028 PrivilegesToDelete->Privileges, 3029 SidsToDisable->Groups, 3030 RestrictedSids->Groups, 3031 PrivilegesCount, 3032 SidsCount, 3033 RestrictedSidsCount, 3034 Flags, 3035 KernelMode, 3036 &AccessToken); 3037 if (!NT_SUCCESS(Status)) 3038 { 3039 DPRINT1("SeFilterToken(): Failed to filter the token (Status 0x%lx)\n", Status); 3040 return Status; 3041 } 3042 3043 /* Insert the filtered token */ 3044 Status = ObInsertObject(AccessToken, 3045 NULL, 3046 0, 3047 0, 3048 NULL, 3049 NULL); 3050 if (!NT_SUCCESS(Status)) 3051 { 3052 DPRINT1("SeFilterToken(): Failed to insert the token (Status 0x%lx)\n", Status); 3053 return Status; 3054 } 3055 3056 /* Give it to the caller */ 3057 *FilteredToken = AccessToken; 3058 return Status; 3059 } 3060 3061 /** 3062 * @brief 3063 * Queries information details about the given token to the call. The difference 3064 * between NtQueryInformationToken and this routine is that the system call has 3065 * user mode buffer data probing and additional protection checks whereas this 3066 * routine doesn't have any of these. The routine is used exclusively in kernel 3067 * mode. 3068 * 3069 * @param[in] AccessToken 3070 * An access token to be given. 3071 * 3072 * @param[in] TokenInformationClass 3073 * Token information class. 3074 * 3075 * @param[out] TokenInformation 3076 * Buffer with retrieved information. Such information is arbitrary, depending 3077 * on the requested information class. 3078 * 3079 * @return 3080 * Returns STATUS_SUCCESS if the operation to query the desired information 3081 * has completed successfully. STATUS_INSUFFICIENT_RESOURCES is returned if 3082 * pool memory allocation has failed to satisfy an operation. Otherwise 3083 * STATUS_INVALID_INFO_CLASS is returned indicating that the information 3084 * class provided is not supported by the routine. 3085 * 3086 * @remarks 3087 * Only certain information classes are not implemented in this function and 3088 * these are TokenOrigin, TokenGroupsAndPrivileges, TokenRestrictedSids and 3089 * TokenSandBoxInert. The following classes are implemented in NtQueryInformationToken 3090 * only. 3091 */ 3092 NTSTATUS 3093 NTAPI 3094 SeQueryInformationToken( 3095 _In_ PACCESS_TOKEN AccessToken, 3096 _In_ TOKEN_INFORMATION_CLASS TokenInformationClass, 3097 _Outptr_result_buffer_(_Inexpressible_(token-dependent)) PVOID *TokenInformation) 3098 { 3099 NTSTATUS Status; 3100 PTOKEN Token = (PTOKEN)AccessToken; 3101 ULONG RequiredLength; 3102 union 3103 { 3104 PSID PSid; 3105 ULONG Ulong; 3106 } Unused; 3107 3108 PAGED_CODE(); 3109 3110 /* Lock the token */ 3111 SepAcquireTokenLockShared(Token); 3112 3113 switch (TokenInformationClass) 3114 { 3115 case TokenUser: 3116 { 3117 PTOKEN_USER tu; 3118 3119 DPRINT("SeQueryInformationToken(TokenUser)\n"); 3120 RequiredLength = sizeof(TOKEN_USER) + 3121 RtlLengthSid(Token->UserAndGroups[0].Sid); 3122 3123 /* Allocate the output buffer */ 3124 tu = ExAllocatePoolWithTag(PagedPool, RequiredLength, TAG_SE); 3125 if (tu == NULL) 3126 { 3127 Status = STATUS_INSUFFICIENT_RESOURCES; 3128 break; 3129 } 3130 3131 Status = RtlCopySidAndAttributesArray(1, 3132 &Token->UserAndGroups[0], 3133 RequiredLength - sizeof(TOKEN_USER), 3134 &tu->User, 3135 (PSID)(tu + 1), 3136 &Unused.PSid, 3137 &Unused.Ulong); 3138 3139 /* Return the structure */ 3140 *TokenInformation = tu; 3141 Status = STATUS_SUCCESS; 3142 break; 3143 } 3144 3145 case TokenGroups: 3146 { 3147 PTOKEN_GROUPS tg; 3148 ULONG SidLen; 3149 PSID Sid; 3150 3151 DPRINT("SeQueryInformationToken(TokenGroups)\n"); 3152 RequiredLength = sizeof(tg->GroupCount) + 3153 RtlLengthSidAndAttributes(Token->UserAndGroupCount - 1, &Token->UserAndGroups[1]); 3154 3155 SidLen = RequiredLength - sizeof(tg->GroupCount) - 3156 ((Token->UserAndGroupCount - 1) * sizeof(SID_AND_ATTRIBUTES)); 3157 3158 /* Allocate the output buffer */ 3159 tg = ExAllocatePoolWithTag(PagedPool, RequiredLength, TAG_SE); 3160 if (tg == NULL) 3161 { 3162 Status = STATUS_INSUFFICIENT_RESOURCES; 3163 break; 3164 } 3165 3166 Sid = (PSID)((ULONG_PTR)tg + sizeof(tg->GroupCount) + 3167 ((Token->UserAndGroupCount - 1) * sizeof(SID_AND_ATTRIBUTES))); 3168 3169 tg->GroupCount = Token->UserAndGroupCount - 1; 3170 Status = RtlCopySidAndAttributesArray(Token->UserAndGroupCount - 1, 3171 &Token->UserAndGroups[1], 3172 SidLen, 3173 &tg->Groups[0], 3174 Sid, 3175 &Unused.PSid, 3176 &Unused.Ulong); 3177 3178 /* Return the structure */ 3179 *TokenInformation = tg; 3180 Status = STATUS_SUCCESS; 3181 break; 3182 } 3183 3184 case TokenPrivileges: 3185 { 3186 PTOKEN_PRIVILEGES tp; 3187 3188 DPRINT("SeQueryInformationToken(TokenPrivileges)\n"); 3189 RequiredLength = sizeof(tp->PrivilegeCount) + 3190 (Token->PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES)); 3191 3192 /* Allocate the output buffer */ 3193 tp = ExAllocatePoolWithTag(PagedPool, RequiredLength, TAG_SE); 3194 if (tp == NULL) 3195 { 3196 Status = STATUS_INSUFFICIENT_RESOURCES; 3197 break; 3198 } 3199 3200 tp->PrivilegeCount = Token->PrivilegeCount; 3201 RtlCopyLuidAndAttributesArray(Token->PrivilegeCount, 3202 Token->Privileges, 3203 &tp->Privileges[0]); 3204 3205 /* Return the structure */ 3206 *TokenInformation = tp; 3207 Status = STATUS_SUCCESS; 3208 break; 3209 } 3210 3211 case TokenOwner: 3212 { 3213 PTOKEN_OWNER to; 3214 ULONG SidLen; 3215 3216 DPRINT("SeQueryInformationToken(TokenOwner)\n"); 3217 SidLen = RtlLengthSid(Token->UserAndGroups[Token->DefaultOwnerIndex].Sid); 3218 RequiredLength = sizeof(TOKEN_OWNER) + SidLen; 3219 3220 /* Allocate the output buffer */ 3221 to = ExAllocatePoolWithTag(PagedPool, RequiredLength, TAG_SE); 3222 if (to == NULL) 3223 { 3224 Status = STATUS_INSUFFICIENT_RESOURCES; 3225 break; 3226 } 3227 3228 to->Owner = (PSID)(to + 1); 3229 Status = RtlCopySid(SidLen, 3230 to->Owner, 3231 Token->UserAndGroups[Token->DefaultOwnerIndex].Sid); 3232 3233 /* Return the structure */ 3234 *TokenInformation = to; 3235 Status = STATUS_SUCCESS; 3236 break; 3237 } 3238 3239 case TokenPrimaryGroup: 3240 { 3241 PTOKEN_PRIMARY_GROUP tpg; 3242 ULONG SidLen; 3243 3244 DPRINT("SeQueryInformationToken(TokenPrimaryGroup)\n"); 3245 SidLen = RtlLengthSid(Token->PrimaryGroup); 3246 RequiredLength = sizeof(TOKEN_PRIMARY_GROUP) + SidLen; 3247 3248 /* Allocate the output buffer */ 3249 tpg = ExAllocatePoolWithTag(PagedPool, RequiredLength, TAG_SE); 3250 if (tpg == NULL) 3251 { 3252 Status = STATUS_INSUFFICIENT_RESOURCES; 3253 break; 3254 } 3255 3256 tpg->PrimaryGroup = (PSID)(tpg + 1); 3257 Status = RtlCopySid(SidLen, 3258 tpg->PrimaryGroup, 3259 Token->PrimaryGroup); 3260 3261 /* Return the structure */ 3262 *TokenInformation = tpg; 3263 Status = STATUS_SUCCESS; 3264 break; 3265 } 3266 3267 case TokenDefaultDacl: 3268 { 3269 PTOKEN_DEFAULT_DACL tdd; 3270 3271 DPRINT("SeQueryInformationToken(TokenDefaultDacl)\n"); 3272 RequiredLength = sizeof(TOKEN_DEFAULT_DACL); 3273 3274 if (Token->DefaultDacl != NULL) 3275 RequiredLength += Token->DefaultDacl->AclSize; 3276 3277 /* Allocate the output buffer */ 3278 tdd = ExAllocatePoolWithTag(PagedPool, RequiredLength, TAG_SE); 3279 if (tdd == NULL) 3280 { 3281 Status = STATUS_INSUFFICIENT_RESOURCES; 3282 break; 3283 } 3284 3285 if (Token->DefaultDacl != NULL) 3286 { 3287 tdd->DefaultDacl = (PACL)(tdd + 1); 3288 RtlCopyMemory(tdd->DefaultDacl, 3289 Token->DefaultDacl, 3290 Token->DefaultDacl->AclSize); 3291 } 3292 else 3293 { 3294 tdd->DefaultDacl = NULL; 3295 } 3296 3297 /* Return the structure */ 3298 *TokenInformation = tdd; 3299 Status = STATUS_SUCCESS; 3300 break; 3301 } 3302 3303 case TokenSource: 3304 { 3305 PTOKEN_SOURCE ts; 3306 3307 DPRINT("SeQueryInformationToken(TokenSource)\n"); 3308 RequiredLength = sizeof(TOKEN_SOURCE); 3309 3310 /* Allocate the output buffer */ 3311 ts = ExAllocatePoolWithTag(PagedPool, RequiredLength, TAG_SE); 3312 if (ts == NULL) 3313 { 3314 Status = STATUS_INSUFFICIENT_RESOURCES; 3315 break; 3316 } 3317 3318 *ts = Token->TokenSource; 3319 3320 /* Return the structure */ 3321 *TokenInformation = ts; 3322 Status = STATUS_SUCCESS; 3323 break; 3324 } 3325 3326 case TokenType: 3327 { 3328 PTOKEN_TYPE tt; 3329 3330 DPRINT("SeQueryInformationToken(TokenType)\n"); 3331 RequiredLength = sizeof(TOKEN_TYPE); 3332 3333 /* Allocate the output buffer */ 3334 tt = ExAllocatePoolWithTag(PagedPool, RequiredLength, TAG_SE); 3335 if (tt == NULL) 3336 { 3337 Status = STATUS_INSUFFICIENT_RESOURCES; 3338 break; 3339 } 3340 3341 *tt = Token->TokenType; 3342 3343 /* Return the structure */ 3344 *TokenInformation = tt; 3345 Status = STATUS_SUCCESS; 3346 break; 3347 } 3348 3349 case TokenImpersonationLevel: 3350 { 3351 PSECURITY_IMPERSONATION_LEVEL sil; 3352 3353 DPRINT("SeQueryInformationToken(TokenImpersonationLevel)\n"); 3354 RequiredLength = sizeof(SECURITY_IMPERSONATION_LEVEL); 3355 3356 /* Fail if the token is not an impersonation token */ 3357 if (Token->TokenType != TokenImpersonation) 3358 { 3359 Status = STATUS_INVALID_INFO_CLASS; 3360 break; 3361 } 3362 3363 /* Allocate the output buffer */ 3364 sil = ExAllocatePoolWithTag(PagedPool, RequiredLength, TAG_SE); 3365 if (sil == NULL) 3366 { 3367 Status = STATUS_INSUFFICIENT_RESOURCES; 3368 break; 3369 } 3370 3371 *sil = Token->ImpersonationLevel; 3372 3373 /* Return the structure */ 3374 *TokenInformation = sil; 3375 Status = STATUS_SUCCESS; 3376 break; 3377 } 3378 3379 case TokenStatistics: 3380 { 3381 PTOKEN_STATISTICS ts; 3382 3383 DPRINT("SeQueryInformationToken(TokenStatistics)\n"); 3384 RequiredLength = sizeof(TOKEN_STATISTICS); 3385 3386 /* Allocate the output buffer */ 3387 ts = ExAllocatePoolWithTag(PagedPool, RequiredLength, TAG_SE); 3388 if (ts == NULL) 3389 { 3390 Status = STATUS_INSUFFICIENT_RESOURCES; 3391 break; 3392 } 3393 3394 ts->TokenId = Token->TokenId; 3395 ts->AuthenticationId = Token->AuthenticationId; 3396 ts->ExpirationTime = Token->ExpirationTime; 3397 ts->TokenType = Token->TokenType; 3398 ts->ImpersonationLevel = Token->ImpersonationLevel; 3399 ts->DynamicCharged = Token->DynamicCharged; 3400 ts->DynamicAvailable = Token->DynamicAvailable; 3401 ts->GroupCount = Token->UserAndGroupCount - 1; 3402 ts->PrivilegeCount = Token->PrivilegeCount; 3403 ts->ModifiedId = Token->ModifiedId; 3404 3405 /* Return the structure */ 3406 *TokenInformation = ts; 3407 Status = STATUS_SUCCESS; 3408 break; 3409 } 3410 3411 case TokenSessionId: 3412 { 3413 DPRINT("SeQueryInformationToken(TokenSessionId)\n"); 3414 Status = SeQuerySessionIdToken(Token, (PULONG)TokenInformation); 3415 break; 3416 } 3417 3418 default: 3419 DPRINT1("SeQueryInformationToken(%d) invalid information class\n", TokenInformationClass); 3420 Status = STATUS_INVALID_INFO_CLASS; 3421 break; 3422 } 3423 3424 /* Release the lock of the token */ 3425 SepReleaseTokenLock(Token); 3426 3427 return Status; 3428 } 3429 3430 /** 3431 * @brief 3432 * Queries the session ID of an access token. 3433 * 3434 * @param[in] Token 3435 * A valid access token where the session ID has to be gathered. 3436 * 3437 * @param[out] pSessionId 3438 * The returned pointer to a session ID to the caller. 3439 * 3440 * @return 3441 * Returns STATUS_SUCCESS. 3442 */ 3443 NTSTATUS 3444 NTAPI 3445 SeQuerySessionIdToken( 3446 _In_ PACCESS_TOKEN Token, 3447 _Out_ PULONG pSessionId) 3448 { 3449 PAGED_CODE(); 3450 3451 /* Lock the token */ 3452 SepAcquireTokenLockShared(Token); 3453 3454 *pSessionId = ((PTOKEN)Token)->SessionId; 3455 3456 /* Unlock the token */ 3457 SepReleaseTokenLock(Token); 3458 3459 return STATUS_SUCCESS; 3460 } 3461 3462 /** 3463 * @brief 3464 * Queries the authentication ID of an access token. 3465 * 3466 * @param[in] Token 3467 * A valid access token where the authentication ID has to be gathered. 3468 * 3469 * @param[out] pSessionId 3470 * The returned pointer to an authentication ID to the caller. 3471 * 3472 * @return 3473 * Returns STATUS_SUCCESS. 3474 */ 3475 NTSTATUS 3476 NTAPI 3477 SeQueryAuthenticationIdToken( 3478 _In_ PACCESS_TOKEN Token, 3479 _Out_ PLUID LogonId) 3480 { 3481 PAGED_CODE(); 3482 3483 *LogonId = ((PTOKEN)Token)->AuthenticationId; 3484 3485 return STATUS_SUCCESS; 3486 } 3487 3488 /** 3489 * @brief 3490 * Gathers the security impersonation level of an access token. 3491 * 3492 * @param[in] Token 3493 * A valid access token where the impersonation level has to be gathered. 3494 * 3495 * @return 3496 * Returns the security impersonation level from a valid token. 3497 */ 3498 SECURITY_IMPERSONATION_LEVEL 3499 NTAPI 3500 SeTokenImpersonationLevel( 3501 _In_ PACCESS_TOKEN Token) 3502 { 3503 PAGED_CODE(); 3504 3505 return ((PTOKEN)Token)->ImpersonationLevel; 3506 } 3507 3508 /** 3509 * @brief 3510 * Gathers the token type of an access token. A token ca be either 3511 * a primary token or impersonation token. 3512 * 3513 * @param[in] Token 3514 * A valid access token where the token type has to be gathered. 3515 * 3516 * @return 3517 * Returns the token type from a valid token. 3518 */ 3519 TOKEN_TYPE 3520 NTAPI 3521 SeTokenType( 3522 _In_ PACCESS_TOKEN Token) 3523 { 3524 PAGED_CODE(); 3525 3526 return ((PTOKEN)Token)->TokenType; 3527 } 3528 3529 /** 3530 * @brief 3531 * Determines if a token is either an admin token or not. Such 3532 * condition is checked based upon TOKEN_HAS_ADMIN_GROUP flag, 3533 * which means if the respective access token belongs to an 3534 * administrator group or not. 3535 * 3536 * @param[in] Token 3537 * A valid access token to determine if such token is admin or not. 3538 * 3539 * @return 3540 * Returns TRUE if the token is an admin one, FALSE otherwise. 3541 */ 3542 BOOLEAN 3543 NTAPI 3544 SeTokenIsAdmin( 3545 _In_ PACCESS_TOKEN Token) 3546 { 3547 PAGED_CODE(); 3548 3549 // NOTE: Win7+ instead really checks the list of groups in the token 3550 // (since TOKEN_HAS_ADMIN_GROUP == TOKEN_WRITE_RESTRICTED ...) 3551 return (((PTOKEN)Token)->TokenFlags & TOKEN_HAS_ADMIN_GROUP) != 0; 3552 } 3553 3554 /** 3555 * @brief 3556 * Determines if a token is restricted or not, based upon the token 3557 * flags. 3558 * 3559 * @param[in] Token 3560 * A valid access token to determine if such token is restricted. 3561 * 3562 * @return 3563 * Returns TRUE if the token is restricted, FALSE otherwise. 3564 */ 3565 BOOLEAN 3566 NTAPI 3567 SeTokenIsRestricted( 3568 _In_ PACCESS_TOKEN Token) 3569 { 3570 PAGED_CODE(); 3571 3572 return (((PTOKEN)Token)->TokenFlags & TOKEN_IS_RESTRICTED) != 0; 3573 } 3574 3575 /** 3576 * @brief 3577 * Determines if a token is write restricted, that is, nobody can write anything 3578 * to it. 3579 * 3580 * @param[in] Token 3581 * A valid access token to determine if such token is write restricted. 3582 * 3583 * @return 3584 * Returns TRUE if the token is write restricted, FALSE otherwise. 3585 * 3586 * @remarks 3587 * First introduced in NT 5.1 SP2 x86 (5.1.2600.2622), absent in NT 5.2, 3588 * then finally re-introduced in Vista+. 3589 */ 3590 BOOLEAN 3591 NTAPI 3592 SeTokenIsWriteRestricted( 3593 _In_ PACCESS_TOKEN Token) 3594 { 3595 PAGED_CODE(); 3596 3597 // NOTE: NT 5.1 SP2 x86 checks the SE_BACKUP_PRIVILEGES_CHECKED flag 3598 // while Vista+ checks the TOKEN_WRITE_RESTRICTED flag as one expects. 3599 return (((PTOKEN)Token)->TokenFlags & SE_BACKUP_PRIVILEGES_CHECKED) != 0; 3600 } 3601 3602 /** 3603 * @brief 3604 * Ensures that client impersonation can occur by checking if the token 3605 * we're going to assign as the impersonation token can be actually impersonated 3606 * in the first place. The routine is used primarily by PsImpersonateClient. 3607 * 3608 * @param[in] ProcessToken 3609 * Token from a process. 3610 * 3611 * @param[in] TokenToImpersonate 3612 * Token that we are going to impersonate. 3613 * 3614 * @param[in] ImpersonationLevel 3615 * Security impersonation level grade. 3616 * 3617 * @return 3618 * Returns TRUE if the conditions checked are met for token impersonation, 3619 * FALSE otherwise. 3620 */ 3621 BOOLEAN 3622 NTAPI 3623 SeTokenCanImpersonate( 3624 _In_ PTOKEN ProcessToken, 3625 _In_ PTOKEN TokenToImpersonate, 3626 _In_ SECURITY_IMPERSONATION_LEVEL ImpersonationLevel) 3627 { 3628 BOOLEAN CanImpersonate; 3629 PAGED_CODE(); 3630 3631 /* 3632 * SecurityAnonymous and SecurityIdentification levels do not 3633 * allow impersonation. 3634 */ 3635 if (ImpersonationLevel == SecurityAnonymous || 3636 ImpersonationLevel == SecurityIdentification) 3637 { 3638 return FALSE; 3639 } 3640 3641 /* Time to lock our tokens */ 3642 SepAcquireTokenLockShared(ProcessToken); 3643 SepAcquireTokenLockShared(TokenToImpersonate); 3644 3645 /* What kind of authentication ID does the token have? */ 3646 if (RtlEqualLuid(&TokenToImpersonate->AuthenticationId, 3647 &SeAnonymousAuthenticationId)) 3648 { 3649 /* 3650 * OK, it looks like the token has an anonymous 3651 * authentication. Is that token created by the system? 3652 */ 3653 if (TokenToImpersonate->TokenSource.SourceName != SeSystemTokenSource.SourceName && 3654 !RtlEqualLuid(&TokenToImpersonate->TokenSource.SourceIdentifier, &SeSystemTokenSource.SourceIdentifier)) 3655 { 3656 /* It isn't, we can't impersonate regular tokens */ 3657 DPRINT("SeTokenCanImpersonate(): Token has an anonymous authentication ID, can't impersonate!\n"); 3658 CanImpersonate = FALSE; 3659 goto Quit; 3660 } 3661 } 3662 3663 /* Are the SID values from both tokens equal? */ 3664 if (!RtlEqualSid(ProcessToken->UserAndGroups->Sid, 3665 TokenToImpersonate->UserAndGroups->Sid)) 3666 { 3667 /* They aren't, bail out */ 3668 DPRINT("SeTokenCanImpersonate(): Tokens SIDs are not equal!\n"); 3669 CanImpersonate = FALSE; 3670 goto Quit; 3671 } 3672 3673 /* 3674 * Make sure the tokens aren't diverged in terms of 3675 * restrictions, that is, one token is restricted 3676 * but the other one isn't. 3677 */ 3678 if (SeTokenIsRestricted(ProcessToken) != 3679 SeTokenIsRestricted(TokenToImpersonate)) 3680 { 3681 /* 3682 * One token is restricted so we cannot 3683 * continue further at this point, bail out. 3684 */ 3685 DPRINT("SeTokenCanImpersonate(): One token is restricted, can't continue!\n"); 3686 CanImpersonate = FALSE; 3687 goto Quit; 3688 } 3689 3690 /* If we've reached that far then we can impersonate! */ 3691 DPRINT("SeTokenCanImpersonate(): We can impersonate.\n"); 3692 CanImpersonate = TRUE; 3693 3694 Quit: 3695 /* We're done, unlock the tokens now */ 3696 SepReleaseTokenLock(ProcessToken); 3697 SepReleaseTokenLock(TokenToImpersonate); 3698 3699 return CanImpersonate; 3700 } 3701 3702 /* SYSTEM CALLS ***************************************************************/ 3703 3704 /** 3705 * @brief 3706 * Queries a specific type of information in regard of an access token based upon 3707 * the information class. The calling thread must have specific access rights in order 3708 * to obtain specific information about the token. 3709 * 3710 * @param[in] TokenHandle 3711 * A handle of a token where information is to be gathered. 3712 * 3713 * @param[in] TokenInformationClass 3714 * Token information class. 3715 * 3716 * @param[out] TokenInformation 3717 * A returned output buffer with token information, which information is arbitrarily upon 3718 * the information class chosen. 3719 * 3720 * @param[in] TokenInformationLength 3721 * Length of the token information buffer, in bytes. 3722 * 3723 * @param[out] ReturnLength 3724 * If specified in the call, the function returns the total length size of the token 3725 * information buffer.. 3726 * 3727 * @return 3728 * Returns STATUS_SUCCESS if information querying has completed successfully. 3729 * STATUS_BUFFER_TOO_SMALL is returned if the information length that represents 3730 * the token information buffer is not greater than the required length. 3731 * STATUS_INVALID_HANDLE is returned if the token handle is not a valid one. 3732 * STATUS_INVALID_INFO_CLASS is returned if the information class is not a valid 3733 * one (that is, the class doesn't belong to TOKEN_INFORMATION_CLASS). A failure 3734 * NTSTATUS code is returned otherwise. 3735 */ 3736 _Must_inspect_result_ 3737 __kernel_entry 3738 NTSTATUS 3739 NTAPI 3740 NtQueryInformationToken( 3741 _In_ HANDLE TokenHandle, 3742 _In_ TOKEN_INFORMATION_CLASS TokenInformationClass, 3743 _Out_writes_bytes_to_opt_(TokenInformationLength, *ReturnLength) 3744 PVOID TokenInformation, 3745 _In_ ULONG TokenInformationLength, 3746 _Out_ PULONG ReturnLength) 3747 { 3748 NTSTATUS Status; 3749 KPROCESSOR_MODE PreviousMode; 3750 PTOKEN Token; 3751 ULONG RequiredLength; 3752 union 3753 { 3754 PSID PSid; 3755 ULONG Ulong; 3756 } Unused; 3757 3758 PAGED_CODE(); 3759 3760 PreviousMode = ExGetPreviousMode(); 3761 3762 /* Check buffers and class validity */ 3763 Status = DefaultQueryInfoBufferCheck(TokenInformationClass, 3764 SeTokenInformationClass, 3765 RTL_NUMBER_OF(SeTokenInformationClass), 3766 TokenInformation, 3767 TokenInformationLength, 3768 ReturnLength, 3769 NULL, 3770 PreviousMode, 3771 TRUE); 3772 if (!NT_SUCCESS(Status)) 3773 { 3774 DPRINT("NtQueryInformationToken() failed, Status: 0x%x\n", Status); 3775 return Status; 3776 } 3777 3778 Status = ObReferenceObjectByHandle(TokenHandle, 3779 (TokenInformationClass == TokenSource) ? TOKEN_QUERY_SOURCE : TOKEN_QUERY, 3780 SeTokenObjectType, 3781 PreviousMode, 3782 (PVOID*)&Token, 3783 NULL); 3784 if (NT_SUCCESS(Status)) 3785 { 3786 /* Lock the token */ 3787 SepAcquireTokenLockShared(Token); 3788 3789 switch (TokenInformationClass) 3790 { 3791 case TokenUser: 3792 { 3793 PTOKEN_USER tu = (PTOKEN_USER)TokenInformation; 3794 3795 DPRINT("NtQueryInformationToken(TokenUser)\n"); 3796 RequiredLength = sizeof(TOKEN_USER) + 3797 RtlLengthSid(Token->UserAndGroups[0].Sid); 3798 3799 _SEH2_TRY 3800 { 3801 if (TokenInformationLength >= RequiredLength) 3802 { 3803 Status = RtlCopySidAndAttributesArray(1, 3804 &Token->UserAndGroups[0], 3805 RequiredLength - sizeof(TOKEN_USER), 3806 &tu->User, 3807 (PSID)(tu + 1), 3808 &Unused.PSid, 3809 &Unused.Ulong); 3810 } 3811 else 3812 { 3813 Status = STATUS_BUFFER_TOO_SMALL; 3814 } 3815 3816 if (ReturnLength != NULL) 3817 { 3818 *ReturnLength = RequiredLength; 3819 } 3820 } 3821 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 3822 { 3823 Status = _SEH2_GetExceptionCode(); 3824 } 3825 _SEH2_END; 3826 3827 break; 3828 } 3829 3830 case TokenGroups: 3831 { 3832 PTOKEN_GROUPS tg = (PTOKEN_GROUPS)TokenInformation; 3833 3834 DPRINT("NtQueryInformationToken(TokenGroups)\n"); 3835 RequiredLength = sizeof(tg->GroupCount) + 3836 RtlLengthSidAndAttributes(Token->UserAndGroupCount - 1, &Token->UserAndGroups[1]); 3837 3838 _SEH2_TRY 3839 { 3840 if (TokenInformationLength >= RequiredLength) 3841 { 3842 ULONG SidLen = RequiredLength - sizeof(tg->GroupCount) - 3843 ((Token->UserAndGroupCount - 1) * sizeof(SID_AND_ATTRIBUTES)); 3844 PSID Sid = (PSID_AND_ATTRIBUTES)((ULONG_PTR)tg + sizeof(tg->GroupCount) + 3845 ((Token->UserAndGroupCount - 1) * sizeof(SID_AND_ATTRIBUTES))); 3846 3847 tg->GroupCount = Token->UserAndGroupCount - 1; 3848 Status = RtlCopySidAndAttributesArray(Token->UserAndGroupCount - 1, 3849 &Token->UserAndGroups[1], 3850 SidLen, 3851 &tg->Groups[0], 3852 Sid, 3853 &Unused.PSid, 3854 &Unused.Ulong); 3855 } 3856 else 3857 { 3858 Status = STATUS_BUFFER_TOO_SMALL; 3859 } 3860 3861 if (ReturnLength != NULL) 3862 { 3863 *ReturnLength = RequiredLength; 3864 } 3865 } 3866 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 3867 { 3868 Status = _SEH2_GetExceptionCode(); 3869 } 3870 _SEH2_END; 3871 3872 break; 3873 } 3874 3875 case TokenPrivileges: 3876 { 3877 PTOKEN_PRIVILEGES tp = (PTOKEN_PRIVILEGES)TokenInformation; 3878 3879 DPRINT("NtQueryInformationToken(TokenPrivileges)\n"); 3880 RequiredLength = sizeof(tp->PrivilegeCount) + 3881 (Token->PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES)); 3882 3883 _SEH2_TRY 3884 { 3885 if (TokenInformationLength >= RequiredLength) 3886 { 3887 tp->PrivilegeCount = Token->PrivilegeCount; 3888 RtlCopyLuidAndAttributesArray(Token->PrivilegeCount, 3889 Token->Privileges, 3890 &tp->Privileges[0]); 3891 } 3892 else 3893 { 3894 Status = STATUS_BUFFER_TOO_SMALL; 3895 } 3896 3897 if (ReturnLength != NULL) 3898 { 3899 *ReturnLength = RequiredLength; 3900 } 3901 } 3902 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 3903 { 3904 Status = _SEH2_GetExceptionCode(); 3905 } 3906 _SEH2_END; 3907 3908 break; 3909 } 3910 3911 case TokenOwner: 3912 { 3913 PTOKEN_OWNER to = (PTOKEN_OWNER)TokenInformation; 3914 ULONG SidLen; 3915 3916 DPRINT("NtQueryInformationToken(TokenOwner)\n"); 3917 SidLen = RtlLengthSid(Token->UserAndGroups[Token->DefaultOwnerIndex].Sid); 3918 RequiredLength = sizeof(TOKEN_OWNER) + SidLen; 3919 3920 _SEH2_TRY 3921 { 3922 if (TokenInformationLength >= RequiredLength) 3923 { 3924 to->Owner = (PSID)(to + 1); 3925 Status = RtlCopySid(SidLen, 3926 to->Owner, 3927 Token->UserAndGroups[Token->DefaultOwnerIndex].Sid); 3928 } 3929 else 3930 { 3931 Status = STATUS_BUFFER_TOO_SMALL; 3932 } 3933 3934 if (ReturnLength != NULL) 3935 { 3936 *ReturnLength = RequiredLength; 3937 } 3938 } 3939 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 3940 { 3941 Status = _SEH2_GetExceptionCode(); 3942 } 3943 _SEH2_END; 3944 3945 break; 3946 } 3947 3948 case TokenPrimaryGroup: 3949 { 3950 PTOKEN_PRIMARY_GROUP tpg = (PTOKEN_PRIMARY_GROUP)TokenInformation; 3951 ULONG SidLen; 3952 3953 DPRINT("NtQueryInformationToken(TokenPrimaryGroup)\n"); 3954 SidLen = RtlLengthSid(Token->PrimaryGroup); 3955 RequiredLength = sizeof(TOKEN_PRIMARY_GROUP) + SidLen; 3956 3957 _SEH2_TRY 3958 { 3959 if (TokenInformationLength >= RequiredLength) 3960 { 3961 tpg->PrimaryGroup = (PSID)(tpg + 1); 3962 Status = RtlCopySid(SidLen, 3963 tpg->PrimaryGroup, 3964 Token->PrimaryGroup); 3965 } 3966 else 3967 { 3968 Status = STATUS_BUFFER_TOO_SMALL; 3969 } 3970 3971 if (ReturnLength != NULL) 3972 { 3973 *ReturnLength = RequiredLength; 3974 } 3975 } 3976 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 3977 { 3978 Status = _SEH2_GetExceptionCode(); 3979 } 3980 _SEH2_END; 3981 3982 break; 3983 } 3984 3985 case TokenDefaultDacl: 3986 { 3987 PTOKEN_DEFAULT_DACL tdd = (PTOKEN_DEFAULT_DACL)TokenInformation; 3988 3989 DPRINT("NtQueryInformationToken(TokenDefaultDacl)\n"); 3990 RequiredLength = sizeof(TOKEN_DEFAULT_DACL); 3991 3992 if (Token->DefaultDacl != NULL) 3993 RequiredLength += Token->DefaultDacl->AclSize; 3994 3995 _SEH2_TRY 3996 { 3997 if (TokenInformationLength >= RequiredLength) 3998 { 3999 if (Token->DefaultDacl != NULL) 4000 { 4001 tdd->DefaultDacl = (PACL)(tdd + 1); 4002 RtlCopyMemory(tdd->DefaultDacl, 4003 Token->DefaultDacl, 4004 Token->DefaultDacl->AclSize); 4005 } 4006 else 4007 { 4008 tdd->DefaultDacl = NULL; 4009 } 4010 } 4011 else 4012 { 4013 Status = STATUS_BUFFER_TOO_SMALL; 4014 } 4015 4016 if (ReturnLength != NULL) 4017 { 4018 *ReturnLength = RequiredLength; 4019 } 4020 } 4021 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 4022 { 4023 Status = _SEH2_GetExceptionCode(); 4024 } 4025 _SEH2_END; 4026 4027 break; 4028 } 4029 4030 case TokenSource: 4031 { 4032 PTOKEN_SOURCE ts = (PTOKEN_SOURCE)TokenInformation; 4033 4034 DPRINT("NtQueryInformationToken(TokenSource)\n"); 4035 RequiredLength = sizeof(TOKEN_SOURCE); 4036 4037 _SEH2_TRY 4038 { 4039 if (TokenInformationLength >= RequiredLength) 4040 { 4041 *ts = Token->TokenSource; 4042 } 4043 else 4044 { 4045 Status = STATUS_BUFFER_TOO_SMALL; 4046 } 4047 4048 if (ReturnLength != NULL) 4049 { 4050 *ReturnLength = RequiredLength; 4051 } 4052 } 4053 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 4054 { 4055 Status = _SEH2_GetExceptionCode(); 4056 } 4057 _SEH2_END; 4058 4059 break; 4060 } 4061 4062 case TokenType: 4063 { 4064 PTOKEN_TYPE tt = (PTOKEN_TYPE)TokenInformation; 4065 4066 DPRINT("NtQueryInformationToken(TokenType)\n"); 4067 RequiredLength = sizeof(TOKEN_TYPE); 4068 4069 _SEH2_TRY 4070 { 4071 if (TokenInformationLength >= RequiredLength) 4072 { 4073 *tt = Token->TokenType; 4074 } 4075 else 4076 { 4077 Status = STATUS_BUFFER_TOO_SMALL; 4078 } 4079 4080 if (ReturnLength != NULL) 4081 { 4082 *ReturnLength = RequiredLength; 4083 } 4084 } 4085 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 4086 { 4087 Status = _SEH2_GetExceptionCode(); 4088 } 4089 _SEH2_END; 4090 4091 break; 4092 } 4093 4094 case TokenImpersonationLevel: 4095 { 4096 PSECURITY_IMPERSONATION_LEVEL sil = (PSECURITY_IMPERSONATION_LEVEL)TokenInformation; 4097 4098 DPRINT("NtQueryInformationToken(TokenImpersonationLevel)\n"); 4099 4100 /* Fail if the token is not an impersonation token */ 4101 if (Token->TokenType != TokenImpersonation) 4102 { 4103 Status = STATUS_INVALID_INFO_CLASS; 4104 break; 4105 } 4106 4107 RequiredLength = sizeof(SECURITY_IMPERSONATION_LEVEL); 4108 4109 _SEH2_TRY 4110 { 4111 if (TokenInformationLength >= RequiredLength) 4112 { 4113 *sil = Token->ImpersonationLevel; 4114 } 4115 else 4116 { 4117 Status = STATUS_BUFFER_TOO_SMALL; 4118 } 4119 4120 if (ReturnLength != NULL) 4121 { 4122 *ReturnLength = RequiredLength; 4123 } 4124 } 4125 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 4126 { 4127 Status = _SEH2_GetExceptionCode(); 4128 } 4129 _SEH2_END; 4130 4131 break; 4132 } 4133 4134 case TokenStatistics: 4135 { 4136 PTOKEN_STATISTICS ts = (PTOKEN_STATISTICS)TokenInformation; 4137 4138 DPRINT("NtQueryInformationToken(TokenStatistics)\n"); 4139 RequiredLength = sizeof(TOKEN_STATISTICS); 4140 4141 _SEH2_TRY 4142 { 4143 if (TokenInformationLength >= RequiredLength) 4144 { 4145 ts->TokenId = Token->TokenId; 4146 ts->AuthenticationId = Token->AuthenticationId; 4147 ts->ExpirationTime = Token->ExpirationTime; 4148 ts->TokenType = Token->TokenType; 4149 ts->ImpersonationLevel = Token->ImpersonationLevel; 4150 ts->DynamicCharged = Token->DynamicCharged; 4151 ts->DynamicAvailable = Token->DynamicAvailable; 4152 ts->GroupCount = Token->UserAndGroupCount - 1; 4153 ts->PrivilegeCount = Token->PrivilegeCount; 4154 ts->ModifiedId = Token->ModifiedId; 4155 } 4156 else 4157 { 4158 Status = STATUS_BUFFER_TOO_SMALL; 4159 } 4160 4161 if (ReturnLength != NULL) 4162 { 4163 *ReturnLength = RequiredLength; 4164 } 4165 } 4166 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 4167 { 4168 Status = _SEH2_GetExceptionCode(); 4169 } 4170 _SEH2_END; 4171 4172 break; 4173 } 4174 4175 case TokenOrigin: 4176 { 4177 PTOKEN_ORIGIN to = (PTOKEN_ORIGIN)TokenInformation; 4178 4179 DPRINT("NtQueryInformationToken(TokenOrigin)\n"); 4180 RequiredLength = sizeof(TOKEN_ORIGIN); 4181 4182 _SEH2_TRY 4183 { 4184 if (TokenInformationLength >= RequiredLength) 4185 { 4186 RtlCopyLuid(&to->OriginatingLogonSession, 4187 &Token->AuthenticationId); 4188 } 4189 else 4190 { 4191 Status = STATUS_BUFFER_TOO_SMALL; 4192 } 4193 4194 if (ReturnLength != NULL) 4195 { 4196 *ReturnLength = RequiredLength; 4197 } 4198 } 4199 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 4200 { 4201 Status = _SEH2_GetExceptionCode(); 4202 } 4203 _SEH2_END; 4204 4205 break; 4206 } 4207 4208 case TokenGroupsAndPrivileges: 4209 DPRINT1("NtQueryInformationToken(TokenGroupsAndPrivileges) not implemented\n"); 4210 Status = STATUS_NOT_IMPLEMENTED; 4211 break; 4212 4213 case TokenRestrictedSids: 4214 { 4215 PTOKEN_GROUPS tg = (PTOKEN_GROUPS)TokenInformation; 4216 4217 DPRINT("NtQueryInformationToken(TokenRestrictedSids)\n"); 4218 RequiredLength = sizeof(tg->GroupCount) + 4219 RtlLengthSidAndAttributes(Token->RestrictedSidCount, Token->RestrictedSids); 4220 4221 _SEH2_TRY 4222 { 4223 if (TokenInformationLength >= RequiredLength) 4224 { 4225 ULONG SidLen = RequiredLength - sizeof(tg->GroupCount) - 4226 (Token->RestrictedSidCount * sizeof(SID_AND_ATTRIBUTES)); 4227 PSID Sid = (PSID)((ULONG_PTR)tg + sizeof(tg->GroupCount) + 4228 (Token->RestrictedSidCount * sizeof(SID_AND_ATTRIBUTES))); 4229 4230 tg->GroupCount = Token->RestrictedSidCount; 4231 Status = RtlCopySidAndAttributesArray(Token->RestrictedSidCount, 4232 Token->RestrictedSids, 4233 SidLen, 4234 &tg->Groups[0], 4235 Sid, 4236 &Unused.PSid, 4237 &Unused.Ulong); 4238 } 4239 else 4240 { 4241 Status = STATUS_BUFFER_TOO_SMALL; 4242 } 4243 4244 if (ReturnLength != NULL) 4245 { 4246 *ReturnLength = RequiredLength; 4247 } 4248 } 4249 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 4250 { 4251 Status = _SEH2_GetExceptionCode(); 4252 } 4253 _SEH2_END; 4254 4255 break; 4256 } 4257 4258 case TokenSandBoxInert: 4259 DPRINT1("NtQueryInformationToken(TokenSandboxInert) not implemented\n"); 4260 Status = STATUS_NOT_IMPLEMENTED; 4261 break; 4262 4263 case TokenSessionId: 4264 { 4265 ULONG SessionId = 0; 4266 4267 DPRINT("NtQueryInformationToken(TokenSessionId)\n"); 4268 4269 Status = SeQuerySessionIdToken(Token, &SessionId); 4270 if (NT_SUCCESS(Status)) 4271 { 4272 _SEH2_TRY 4273 { 4274 /* Buffer size was already verified, no need to check here again */ 4275 *(PULONG)TokenInformation = SessionId; 4276 4277 if (ReturnLength != NULL) 4278 { 4279 *ReturnLength = sizeof(ULONG); 4280 } 4281 } 4282 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 4283 { 4284 Status = _SEH2_GetExceptionCode(); 4285 } 4286 _SEH2_END; 4287 } 4288 4289 break; 4290 } 4291 4292 default: 4293 DPRINT1("NtQueryInformationToken(%d) invalid information class\n", TokenInformationClass); 4294 Status = STATUS_INVALID_INFO_CLASS; 4295 break; 4296 } 4297 4298 /* Unlock and dereference the token */ 4299 SepReleaseTokenLock(Token); 4300 ObDereferenceObject(Token); 4301 } 4302 4303 return Status; 4304 } 4305 4306 /** 4307 * @unimplemented 4308 * @brief 4309 * Sets (modifies) some specific information in regard of an access token. The 4310 * calling thread must have specific access rights in order to modify token's 4311 * information data. 4312 * 4313 * @param[in] TokenHandle 4314 * A handle of a token where information is to be modified. 4315 * 4316 * @param[in] TokenInformationClass 4317 * Token information class. 4318 * 4319 * @param[in] TokenInformation 4320 * An arbitrary pointer to a buffer with token information to set. Such 4321 * arbitrary buffer depends on the information class chosen that the caller 4322 * wants to modify such information data of a token. 4323 * 4324 * @param[in] TokenInformationLength 4325 * Length of the token information buffer, in bytes. 4326 * 4327 * @return 4328 * Returns STATUS_SUCCESS if information setting has completed successfully. 4329 * STATUS_INFO_LENGTH_MISMATCH is returned if the information length of the 4330 * buffer is less than the required length. STATUS_INSUFFICIENT_RESOURCES is 4331 * returned if memory pool allocation has failed. STATUS_PRIVILEGE_NOT_HELD 4332 * is returned if the calling thread hasn't the required privileges to perform 4333 * the operation in question. A failure NTSTATUS code is returned otherwise. 4334 * 4335 * @remarks 4336 * The function is partly implemented, mainly TokenOrigin and TokenDefaultDacl. 4337 */ 4338 _Must_inspect_result_ 4339 __kernel_entry 4340 NTSTATUS 4341 NTAPI 4342 NtSetInformationToken( 4343 _In_ HANDLE TokenHandle, 4344 _In_ TOKEN_INFORMATION_CLASS TokenInformationClass, 4345 _In_reads_bytes_(TokenInformationLength) PVOID TokenInformation, 4346 _In_ ULONG TokenInformationLength) 4347 { 4348 NTSTATUS Status; 4349 PTOKEN Token; 4350 KPROCESSOR_MODE PreviousMode; 4351 ULONG NeededAccess = TOKEN_ADJUST_DEFAULT; 4352 4353 PAGED_CODE(); 4354 4355 PreviousMode = ExGetPreviousMode(); 4356 4357 Status = DefaultSetInfoBufferCheck(TokenInformationClass, 4358 SeTokenInformationClass, 4359 RTL_NUMBER_OF(SeTokenInformationClass), 4360 TokenInformation, 4361 TokenInformationLength, 4362 PreviousMode); 4363 if (!NT_SUCCESS(Status)) 4364 { 4365 /* Invalid buffers */ 4366 DPRINT("NtSetInformationToken() failed, Status: 0x%x\n", Status); 4367 return Status; 4368 } 4369 4370 if (TokenInformationClass == TokenSessionId) 4371 { 4372 NeededAccess |= TOKEN_ADJUST_SESSIONID; 4373 } 4374 4375 Status = ObReferenceObjectByHandle(TokenHandle, 4376 NeededAccess, 4377 SeTokenObjectType, 4378 PreviousMode, 4379 (PVOID*)&Token, 4380 NULL); 4381 if (NT_SUCCESS(Status)) 4382 { 4383 switch (TokenInformationClass) 4384 { 4385 case TokenOwner: 4386 { 4387 if (TokenInformationLength >= sizeof(TOKEN_OWNER)) 4388 { 4389 PTOKEN_OWNER to = (PTOKEN_OWNER)TokenInformation; 4390 PSID InputSid = NULL, CapturedSid; 4391 ULONG DefaultOwnerIndex; 4392 4393 _SEH2_TRY 4394 { 4395 InputSid = to->Owner; 4396 } 4397 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 4398 { 4399 Status = _SEH2_GetExceptionCode(); 4400 _SEH2_YIELD(goto Cleanup); 4401 } 4402 _SEH2_END; 4403 4404 Status = SepCaptureSid(InputSid, 4405 PreviousMode, 4406 PagedPool, 4407 FALSE, 4408 &CapturedSid); 4409 if (NT_SUCCESS(Status)) 4410 { 4411 /* Lock the token */ 4412 SepAcquireTokenLockExclusive(Token); 4413 4414 /* Find the owner amongst the existing token user and groups */ 4415 Status = SepFindPrimaryGroupAndDefaultOwner(Token, 4416 NULL, 4417 CapturedSid, 4418 NULL, 4419 &DefaultOwnerIndex); 4420 if (NT_SUCCESS(Status)) 4421 { 4422 /* Found it */ 4423 Token->DefaultOwnerIndex = DefaultOwnerIndex; 4424 ExAllocateLocallyUniqueId(&Token->ModifiedId); 4425 } 4426 4427 /* Unlock the token */ 4428 SepReleaseTokenLock(Token); 4429 4430 SepReleaseSid(CapturedSid, 4431 PreviousMode, 4432 FALSE); 4433 } 4434 } 4435 else 4436 { 4437 Status = STATUS_INFO_LENGTH_MISMATCH; 4438 } 4439 break; 4440 } 4441 4442 case TokenPrimaryGroup: 4443 { 4444 if (TokenInformationLength >= sizeof(TOKEN_PRIMARY_GROUP)) 4445 { 4446 PTOKEN_PRIMARY_GROUP tpg = (PTOKEN_PRIMARY_GROUP)TokenInformation; 4447 PSID InputSid = NULL, CapturedSid; 4448 ULONG PrimaryGroupIndex; 4449 4450 _SEH2_TRY 4451 { 4452 InputSid = tpg->PrimaryGroup; 4453 } 4454 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 4455 { 4456 Status = _SEH2_GetExceptionCode(); 4457 _SEH2_YIELD(goto Cleanup); 4458 } 4459 _SEH2_END; 4460 4461 Status = SepCaptureSid(InputSid, 4462 PreviousMode, 4463 PagedPool, 4464 FALSE, 4465 &CapturedSid); 4466 if (NT_SUCCESS(Status)) 4467 { 4468 /* Lock the token */ 4469 SepAcquireTokenLockExclusive(Token); 4470 4471 /* Find the primary group amongst the existing token user and groups */ 4472 Status = SepFindPrimaryGroupAndDefaultOwner(Token, 4473 CapturedSid, 4474 NULL, 4475 &PrimaryGroupIndex, 4476 NULL); 4477 if (NT_SUCCESS(Status)) 4478 { 4479 /* Found it */ 4480 Token->PrimaryGroup = Token->UserAndGroups[PrimaryGroupIndex].Sid; 4481 ExAllocateLocallyUniqueId(&Token->ModifiedId); 4482 } 4483 4484 /* Unlock the token */ 4485 SepReleaseTokenLock(Token); 4486 4487 SepReleaseSid(CapturedSid, 4488 PreviousMode, 4489 FALSE); 4490 } 4491 } 4492 else 4493 { 4494 Status = STATUS_INFO_LENGTH_MISMATCH; 4495 } 4496 break; 4497 } 4498 4499 case TokenDefaultDacl: 4500 { 4501 if (TokenInformationLength >= sizeof(TOKEN_DEFAULT_DACL)) 4502 { 4503 PTOKEN_DEFAULT_DACL tdd = (PTOKEN_DEFAULT_DACL)TokenInformation; 4504 PACL InputAcl = NULL; 4505 4506 _SEH2_TRY 4507 { 4508 InputAcl = tdd->DefaultDacl; 4509 } 4510 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 4511 { 4512 Status = _SEH2_GetExceptionCode(); 4513 _SEH2_YIELD(goto Cleanup); 4514 } 4515 _SEH2_END; 4516 4517 if (InputAcl != NULL) 4518 { 4519 PACL CapturedAcl; 4520 4521 /* Capture and copy the dacl */ 4522 Status = SepCaptureAcl(InputAcl, 4523 PreviousMode, 4524 PagedPool, 4525 TRUE, 4526 &CapturedAcl); 4527 if (NT_SUCCESS(Status)) 4528 { 4529 ULONG DynamicLength; 4530 4531 /* Lock the token */ 4532 SepAcquireTokenLockExclusive(Token); 4533 4534 // 4535 // NOTE: So far our dynamic area only contains 4536 // the default dacl, so this makes the following 4537 // code pretty simple. The day where it stores 4538 // other data, the code will require adaptations. 4539 // 4540 4541 DynamicLength = Token->DynamicAvailable; 4542 // Add here any other data length present in the dynamic area... 4543 if (Token->DefaultDacl) 4544 DynamicLength += Token->DefaultDacl->AclSize; 4545 4546 /* Reallocate the dynamic area if it is too small */ 4547 Status = STATUS_SUCCESS; 4548 if ((DynamicLength < CapturedAcl->AclSize) || 4549 (Token->DynamicPart == NULL)) 4550 { 4551 PVOID NewDynamicPart; 4552 4553 NewDynamicPart = ExAllocatePoolWithTag(PagedPool, 4554 CapturedAcl->AclSize, 4555 TAG_TOKEN_DYNAMIC); 4556 if (NewDynamicPart == NULL) 4557 { 4558 Status = STATUS_INSUFFICIENT_RESOURCES; 4559 } 4560 else 4561 { 4562 if (Token->DynamicPart != NULL) 4563 { 4564 // RtlCopyMemory(NewDynamicPart, Token->DynamicPart, DynamicLength); 4565 ExFreePoolWithTag(Token->DynamicPart, TAG_TOKEN_DYNAMIC); 4566 } 4567 Token->DynamicPart = NewDynamicPart; 4568 Token->DynamicAvailable = 0; 4569 } 4570 } 4571 else 4572 { 4573 Token->DynamicAvailable = DynamicLength - CapturedAcl->AclSize; 4574 } 4575 4576 if (NT_SUCCESS(Status)) 4577 { 4578 /* Set the new dacl */ 4579 Token->DefaultDacl = (PVOID)Token->DynamicPart; 4580 RtlCopyMemory(Token->DefaultDacl, 4581 CapturedAcl, 4582 CapturedAcl->AclSize); 4583 4584 ExAllocateLocallyUniqueId(&Token->ModifiedId); 4585 } 4586 4587 /* Unlock the token */ 4588 SepReleaseTokenLock(Token); 4589 4590 ExFreePoolWithTag(CapturedAcl, TAG_ACL); 4591 } 4592 } 4593 else 4594 { 4595 /* Lock the token */ 4596 SepAcquireTokenLockExclusive(Token); 4597 4598 /* Clear the default dacl if present */ 4599 if (Token->DefaultDacl != NULL) 4600 { 4601 Token->DynamicAvailable += Token->DefaultDacl->AclSize; 4602 RtlZeroMemory(Token->DefaultDacl, Token->DefaultDacl->AclSize); 4603 Token->DefaultDacl = NULL; 4604 4605 ExAllocateLocallyUniqueId(&Token->ModifiedId); 4606 } 4607 4608 /* Unlock the token */ 4609 SepReleaseTokenLock(Token); 4610 } 4611 } 4612 else 4613 { 4614 Status = STATUS_INFO_LENGTH_MISMATCH; 4615 } 4616 break; 4617 } 4618 4619 case TokenSessionId: 4620 { 4621 ULONG SessionId = 0; 4622 4623 _SEH2_TRY 4624 { 4625 /* Buffer size was already verified, no need to check here again */ 4626 SessionId = *(PULONG)TokenInformation; 4627 } 4628 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 4629 { 4630 Status = _SEH2_GetExceptionCode(); 4631 _SEH2_YIELD(goto Cleanup); 4632 } 4633 _SEH2_END; 4634 4635 /* Check for TCB privilege */ 4636 if (!SeSinglePrivilegeCheck(SeTcbPrivilege, PreviousMode)) 4637 { 4638 Status = STATUS_PRIVILEGE_NOT_HELD; 4639 break; 4640 } 4641 4642 /* Lock the token */ 4643 SepAcquireTokenLockExclusive(Token); 4644 4645 Token->SessionId = SessionId; 4646 ExAllocateLocallyUniqueId(&Token->ModifiedId); 4647 4648 /* Unlock the token */ 4649 SepReleaseTokenLock(Token); 4650 4651 break; 4652 } 4653 4654 case TokenSessionReference: 4655 { 4656 ULONG SessionReference; 4657 4658 _SEH2_TRY 4659 { 4660 /* Buffer size was already verified, no need to check here again */ 4661 SessionReference = *(PULONG)TokenInformation; 4662 } 4663 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 4664 { 4665 Status = _SEH2_GetExceptionCode(); 4666 _SEH2_YIELD(goto Cleanup); 4667 } 4668 _SEH2_END; 4669 4670 /* Check for TCB privilege */ 4671 if (!SeSinglePrivilegeCheck(SeTcbPrivilege, PreviousMode)) 4672 { 4673 Status = STATUS_PRIVILEGE_NOT_HELD; 4674 goto Cleanup; 4675 } 4676 4677 /* Check if it is 0 */ 4678 if (SessionReference == 0) 4679 { 4680 ULONG OldTokenFlags; 4681 4682 /* Lock the token */ 4683 SepAcquireTokenLockExclusive(Token); 4684 4685 /* Atomically set the flag in the token */ 4686 OldTokenFlags = RtlInterlockedSetBits(&Token->TokenFlags, 4687 TOKEN_SESSION_NOT_REFERENCED); 4688 /* 4689 * If the flag was already set, do not dereference again 4690 * the logon session. Use SessionReference as an indicator 4691 * to know whether to really dereference the session. 4692 */ 4693 if (OldTokenFlags == Token->TokenFlags) 4694 SessionReference = ULONG_MAX; 4695 4696 /* 4697 * Otherwise if the flag was never set but just for this first time then 4698 * remove the referenced logon session data from the token and dereference 4699 * the logon session when needed. 4700 */ 4701 if (SessionReference == 0) 4702 { 4703 SepRmRemoveLogonSessionFromToken(Token); 4704 SepRmDereferenceLogonSession(&Token->AuthenticationId); 4705 } 4706 4707 /* Unlock the token */ 4708 SepReleaseTokenLock(Token); 4709 } 4710 break; 4711 } 4712 4713 case TokenAuditPolicy: 4714 { 4715 PTOKEN_AUDIT_POLICY_INFORMATION PolicyInformation = 4716 (PTOKEN_AUDIT_POLICY_INFORMATION)TokenInformation; 4717 SEP_AUDIT_POLICY AuditPolicy; 4718 ULONG i; 4719 4720 _SEH2_TRY 4721 { 4722 ProbeForRead(PolicyInformation, 4723 FIELD_OFFSET(TOKEN_AUDIT_POLICY_INFORMATION, 4724 Policies[PolicyInformation->PolicyCount]), 4725 sizeof(ULONG)); 4726 4727 /* Loop all policies in the structure */ 4728 for (i = 0; i < PolicyInformation->PolicyCount; i++) 4729 { 4730 /* Set the corresponding bits in the packed structure */ 4731 switch (PolicyInformation->Policies[i].Category) 4732 { 4733 case AuditCategorySystem: 4734 AuditPolicy.PolicyElements.System = PolicyInformation->Policies[i].Value; 4735 break; 4736 4737 case AuditCategoryLogon: 4738 AuditPolicy.PolicyElements.Logon = PolicyInformation->Policies[i].Value; 4739 break; 4740 4741 case AuditCategoryObjectAccess: 4742 AuditPolicy.PolicyElements.ObjectAccess = PolicyInformation->Policies[i].Value; 4743 break; 4744 4745 case AuditCategoryPrivilegeUse: 4746 AuditPolicy.PolicyElements.PrivilegeUse = PolicyInformation->Policies[i].Value; 4747 break; 4748 4749 case AuditCategoryDetailedTracking: 4750 AuditPolicy.PolicyElements.DetailedTracking = PolicyInformation->Policies[i].Value; 4751 break; 4752 4753 case AuditCategoryPolicyChange: 4754 AuditPolicy.PolicyElements.PolicyChange = PolicyInformation->Policies[i].Value; 4755 break; 4756 4757 case AuditCategoryAccountManagement: 4758 AuditPolicy.PolicyElements.AccountManagement = PolicyInformation->Policies[i].Value; 4759 break; 4760 4761 case AuditCategoryDirectoryServiceAccess: 4762 AuditPolicy.PolicyElements.DirectoryServiceAccess = PolicyInformation->Policies[i].Value; 4763 break; 4764 4765 case AuditCategoryAccountLogon: 4766 AuditPolicy.PolicyElements.AccountLogon = PolicyInformation->Policies[i].Value; 4767 break; 4768 } 4769 } 4770 } 4771 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 4772 { 4773 Status = _SEH2_GetExceptionCode(); 4774 _SEH2_YIELD(goto Cleanup); 4775 } 4776 _SEH2_END; 4777 4778 /* Check for TCB privilege */ 4779 if (!SeSinglePrivilegeCheck(SeTcbPrivilege, PreviousMode)) 4780 { 4781 Status = STATUS_PRIVILEGE_NOT_HELD; 4782 break; 4783 } 4784 4785 /* Lock the token */ 4786 SepAcquireTokenLockExclusive(Token); 4787 4788 /* Set the new audit policy */ 4789 Token->AuditPolicy = AuditPolicy; 4790 ExAllocateLocallyUniqueId(&Token->ModifiedId); 4791 4792 /* Unlock the token */ 4793 SepReleaseTokenLock(Token); 4794 4795 break; 4796 } 4797 4798 case TokenOrigin: 4799 { 4800 TOKEN_ORIGIN TokenOrigin; 4801 4802 _SEH2_TRY 4803 { 4804 /* Copy the token origin */ 4805 TokenOrigin = *(PTOKEN_ORIGIN)TokenInformation; 4806 } 4807 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 4808 { 4809 Status = _SEH2_GetExceptionCode(); 4810 _SEH2_YIELD(goto Cleanup); 4811 } 4812 _SEH2_END; 4813 4814 /* Check for TCB privilege */ 4815 if (!SeSinglePrivilegeCheck(SeTcbPrivilege, PreviousMode)) 4816 { 4817 Status = STATUS_PRIVILEGE_NOT_HELD; 4818 break; 4819 } 4820 4821 /* Lock the token */ 4822 SepAcquireTokenLockExclusive(Token); 4823 4824 /* Check if there is no token origin set yet */ 4825 if (RtlIsZeroLuid(&Token->OriginatingLogonSession)) 4826 { 4827 /* Set the token origin */ 4828 Token->OriginatingLogonSession = 4829 TokenOrigin.OriginatingLogonSession; 4830 4831 ExAllocateLocallyUniqueId(&Token->ModifiedId); 4832 } 4833 4834 /* Unlock the token */ 4835 SepReleaseTokenLock(Token); 4836 4837 break; 4838 } 4839 4840 default: 4841 { 4842 DPRINT1("Invalid TokenInformationClass: 0x%lx\n", 4843 TokenInformationClass); 4844 Status = STATUS_INVALID_INFO_CLASS; 4845 break; 4846 } 4847 } 4848 Cleanup: 4849 ObDereferenceObject(Token); 4850 } 4851 4852 if (!NT_SUCCESS(Status)) 4853 { 4854 DPRINT1("NtSetInformationToken failed with Status 0x%lx\n", Status); 4855 } 4856 4857 return Status; 4858 } 4859 4860 /** 4861 * @brief 4862 * Duplicates a token. 4863 * 4864 * @param[in] ExistingTokenHandle 4865 * An existing token to duplicate. 4866 * 4867 * @param[in] DesiredAccess 4868 * The desired access rights for the new duplicated token. 4869 * 4870 * @param[in] ObjectAttributes 4871 * Object attributes for the new duplicated token. 4872 * 4873 * @param[in] EffectiveOnly 4874 * If set to TRUE, the function removes all the disabled privileges and groups 4875 * of the token to duplicate. 4876 * 4877 * @param[in] TokenType 4878 * Type of token to assign to the duplicated token. 4879 * 4880 * @param[out] NewTokenHandle 4881 * The returned duplicated token handle. 4882 * 4883 * @return 4884 * STATUS_SUCCESS is returned if token duplication has completed successfully. 4885 * STATUS_BAD_IMPERSONATION_LEVEL is returned if the caller erroneously wants 4886 * to raise the impersonation level even though the conditions do not permit 4887 * it. A failure NTSTATUS code is returned otherwise. 4888 * 4889 * @remarks 4890 * Some sources claim 4th param is ImpersonationLevel, but on W2K 4891 * this is certainly NOT true, although I can't say for sure that EffectiveOnly 4892 * is correct either. -Gunnar 4893 * This is true. EffectiveOnly overrides SQOS.EffectiveOnly. - IAI 4894 * NOTE for readers: http://hex.pp.ua/nt/NtDuplicateToken.php is therefore 4895 * wrong in that regard, while MSDN documentation is correct. 4896 */ 4897 _Must_inspect_result_ 4898 __kernel_entry 4899 NTSTATUS 4900 NTAPI 4901 NtDuplicateToken( 4902 _In_ HANDLE ExistingTokenHandle, 4903 _In_ ACCESS_MASK DesiredAccess, 4904 _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes, 4905 _In_ BOOLEAN EffectiveOnly, 4906 _In_ TOKEN_TYPE TokenType, 4907 _Out_ PHANDLE NewTokenHandle) 4908 { 4909 KPROCESSOR_MODE PreviousMode; 4910 HANDLE hToken; 4911 PTOKEN Token; 4912 PTOKEN NewToken; 4913 PSECURITY_QUALITY_OF_SERVICE CapturedSecurityQualityOfService; 4914 BOOLEAN QoSPresent; 4915 OBJECT_HANDLE_INFORMATION HandleInformation; 4916 NTSTATUS Status; 4917 4918 PAGED_CODE(); 4919 4920 if (TokenType != TokenImpersonation && 4921 TokenType != TokenPrimary) 4922 { 4923 return STATUS_INVALID_PARAMETER; 4924 } 4925 4926 PreviousMode = KeGetPreviousMode(); 4927 4928 if (PreviousMode != KernelMode) 4929 { 4930 _SEH2_TRY 4931 { 4932 ProbeForWriteHandle(NewTokenHandle); 4933 } 4934 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 4935 { 4936 /* Return the exception code */ 4937 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 4938 } 4939 _SEH2_END; 4940 } 4941 4942 Status = SepCaptureSecurityQualityOfService(ObjectAttributes, 4943 PreviousMode, 4944 PagedPool, 4945 FALSE, 4946 &CapturedSecurityQualityOfService, 4947 &QoSPresent); 4948 if (!NT_SUCCESS(Status)) 4949 { 4950 DPRINT1("NtDuplicateToken() failed to capture QoS! Status: 0x%x\n", Status); 4951 return Status; 4952 } 4953 4954 Status = ObReferenceObjectByHandle(ExistingTokenHandle, 4955 TOKEN_DUPLICATE, 4956 SeTokenObjectType, 4957 PreviousMode, 4958 (PVOID*)&Token, 4959 &HandleInformation); 4960 if (!NT_SUCCESS(Status)) 4961 { 4962 DPRINT1("Failed to reference token (Status 0x%lx)\n", Status); 4963 SepReleaseSecurityQualityOfService(CapturedSecurityQualityOfService, 4964 PreviousMode, 4965 FALSE); 4966 return Status; 4967 } 4968 4969 /* 4970 * Fail, if the original token is an impersonation token and the caller 4971 * tries to raise the impersonation level of the new token above the 4972 * impersonation level of the original token. 4973 */ 4974 if (Token->TokenType == TokenImpersonation) 4975 { 4976 if (QoSPresent && 4977 CapturedSecurityQualityOfService->ImpersonationLevel >Token->ImpersonationLevel) 4978 { 4979 ObDereferenceObject(Token); 4980 SepReleaseSecurityQualityOfService(CapturedSecurityQualityOfService, 4981 PreviousMode, 4982 FALSE); 4983 return STATUS_BAD_IMPERSONATION_LEVEL; 4984 } 4985 } 4986 4987 /* 4988 * Fail, if a primary token is to be created from an impersonation token 4989 * and and the impersonation level of the impersonation token is below SecurityImpersonation. 4990 */ 4991 if (Token->TokenType == TokenImpersonation && 4992 TokenType == TokenPrimary && 4993 Token->ImpersonationLevel < SecurityImpersonation) 4994 { 4995 ObDereferenceObject(Token); 4996 SepReleaseSecurityQualityOfService(CapturedSecurityQualityOfService, 4997 PreviousMode, 4998 FALSE); 4999 return STATUS_BAD_IMPERSONATION_LEVEL; 5000 } 5001 5002 Status = SepDuplicateToken(Token, 5003 ObjectAttributes, 5004 EffectiveOnly, 5005 TokenType, 5006 (QoSPresent ? CapturedSecurityQualityOfService->ImpersonationLevel : SecurityAnonymous), 5007 PreviousMode, 5008 &NewToken); 5009 5010 ObDereferenceObject(Token); 5011 5012 if (NT_SUCCESS(Status)) 5013 { 5014 Status = ObInsertObject(NewToken, 5015 NULL, 5016 (DesiredAccess ? DesiredAccess : HandleInformation.GrantedAccess), 5017 0, 5018 NULL, 5019 &hToken); 5020 if (NT_SUCCESS(Status)) 5021 { 5022 _SEH2_TRY 5023 { 5024 *NewTokenHandle = hToken; 5025 } 5026 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 5027 { 5028 Status = _SEH2_GetExceptionCode(); 5029 } 5030 _SEH2_END; 5031 } 5032 } 5033 5034 /* Free the captured structure */ 5035 SepReleaseSecurityQualityOfService(CapturedSecurityQualityOfService, 5036 PreviousMode, 5037 FALSE); 5038 5039 return Status; 5040 } 5041 5042 /** 5043 * @brief 5044 * Private routine that iterates over the groups of an 5045 * access token to be adjusted as per on request by the 5046 * caller, where a group can be enabled or disabled. 5047 * 5048 * @param[in] Token 5049 * Access token where its groups are to be enabled or disabled. 5050 * 5051 * @param[in] NewState 5052 * A list of groups with new state attributes to be assigned to 5053 * the token. 5054 * 5055 * @param[in] NewStateCount 5056 * The captured count number of groups in the list. 5057 * 5058 * @param[in] ApplyChanges 5059 * If set to FALSE, the function will only iterate over the token's 5060 * groups without performing any kind of modification. If set to TRUE, 5061 * the changes will be applied immediately when the function has done 5062 * looping the groups. 5063 * 5064 * @param[in] ResetToDefaultStates 5065 * The function will reset the groups in an access token to default 5066 * states if set to TRUE. In such scenario the function ignores 5067 * NewState outright. Otherwise if set to FALSE, the function will 5068 * use NewState to assign the newly attributes to adjust the token's 5069 * groups. SE_GROUP_ENABLED_BY_DEFAULT is a flag indicator that is used 5070 * for such purpose. 5071 * 5072 * @param[out] ChangesMade 5073 * Returns TRUE if changes to token's groups have been made, otherwise 5074 * FALSE is returned. Bear in mind such changes aren't always deterministic. 5075 * See remarks for further details. 5076 * 5077 * @param[out] PreviousGroupsState 5078 * If requested by the caller, the function will return the previous state 5079 * of groups in an access token prior taking action on adjusting the token. 5080 * This is a UM (user mode) pointer and it's prone to raise exceptions 5081 * if such pointer address is not valid. 5082 * 5083 * @param[out] ChangedGroups 5084 * Returns the total number of changed groups in an access token. This 5085 * argument could also indicate the number of groups to be changed if 5086 * the calling thread hasn't chosen to apply the changes yet. A number 5087 * of 0 indicates no groups have been or to be changed because the groups' 5088 * attributes in a token are the same as the ones from NewState given by 5089 * the caller. 5090 * 5091 * @return 5092 * STATUS_SUCCESS is returned if the function has successfully completed 5093 * the operation of adjusting groups in a token. STATUS_CANT_DISABLE_MANDATORY 5094 * is returned if there was an attempt to disable a mandatory group which is 5095 * not possible. STATUS_CANT_ENABLE_DENY_ONLY is returned if there was an attempt 5096 * to enable a "use for Deny only" group which is not allowed, that is, a restricted 5097 * group. STATUS_NOT_ALL_ASSIGNED is returned if not all the groups are actually 5098 * assigned to the token. 5099 * 5100 * @remarks 5101 * Token groups adjusting can be judged to be deterministic or not based on the 5102 * NT status code value. That is, STATUS_SUCCESS indicates the function not only 5103 * has iterated over the whole groups in a token, it also has applied the changes 5104 * thoroughly without impediment and the results perfectly match with the request 5105 * desired by the caller. In this situation the condition is deemed deterministic. 5106 * In a different situation however, if the status code was STATUS_NOT_ALL_ASSIGNED, 5107 * the function would still continue looping the groups in a token and apply the 5108 * changes whenever possible where the respective groups actually exist in the 5109 * token. This kind of situation is deemed as indeterministic. 5110 * For STATUS_CANT_DISABLE_MANDATORY and STATUS_CANT_ENABLE_DENY_ONLY the scenario 5111 * is even more indeterministic as the iteration of groups comes to a halt thus 5112 * leaving all other possible groups to be adjusted. 5113 */ 5114 static 5115 NTSTATUS 5116 SepAdjustGroups( 5117 _In_ PTOKEN Token, 5118 _In_opt_ PSID_AND_ATTRIBUTES NewState, 5119 _In_ ULONG NewStateCount, 5120 _In_ BOOLEAN ApplyChanges, 5121 _In_ BOOLEAN ResetToDefaultStates, 5122 _Out_ PBOOLEAN ChangesMade, 5123 _Out_opt_ PTOKEN_GROUPS PreviousGroupsState, 5124 _Out_ PULONG ChangedGroups) 5125 { 5126 ULONG GroupsInToken, GroupsInList; 5127 ULONG ChangeCount, GroupsCount, NewAttributes; 5128 5129 PAGED_CODE(); 5130 5131 /* Ensure that the token we get is not plain garbage */ 5132 ASSERT(Token); 5133 5134 /* Initialize the counters and begin the work */ 5135 *ChangesMade = FALSE; 5136 GroupsCount = 0; 5137 ChangeCount = 0; 5138 5139 /* Begin looping all the groups in the token */ 5140 for (GroupsInToken = 0; GroupsInToken < Token->UserAndGroupCount; GroupsInToken++) 5141 { 5142 /* Does the caller want to reset groups to default states? */ 5143 if (ResetToDefaultStates) 5144 { 5145 /* 5146 * SE_GROUP_ENABLED_BY_DEFAULT is a special indicator that informs us 5147 * if a certain group has been enabled by default or not. In case 5148 * a group is enabled by default but it is not currently enabled then 5149 * at that point we must enable it back by default. For now just 5150 * assign the respective SE_GROUP_ENABLED attribute as we'll do the 5151 * eventual work later. 5152 */ 5153 if ((Token->UserAndGroups[GroupsInToken].Attributes & SE_GROUP_ENABLED_BY_DEFAULT) && 5154 (Token->UserAndGroups[GroupsInToken].Attributes & SE_GROUP_ENABLED) == 0) 5155 { 5156 NewAttributes = Token->UserAndGroups[GroupsInToken].Attributes |= SE_GROUP_ENABLED; 5157 } 5158 5159 /* 5160 * Unlike the case above, a group that hasn't been enabled by 5161 * default but it's currently enabled then we must disable 5162 * it back. 5163 */ 5164 if ((Token->UserAndGroups[GroupsInToken].Attributes & SE_GROUP_ENABLED_BY_DEFAULT) == 0 && 5165 (Token->UserAndGroups[GroupsInToken].Attributes & SE_GROUP_ENABLED)) 5166 { 5167 NewAttributes = Token->UserAndGroups[GroupsInToken].Attributes & ~SE_GROUP_ENABLED; 5168 } 5169 } 5170 else 5171 { 5172 /* Loop the provided groups in the list then */ 5173 for (GroupsInList = 0; GroupsInList < NewStateCount; GroupsInList++) 5174 { 5175 /* Does this group exist in the token? */ 5176 if (RtlEqualSid(&Token->UserAndGroups[GroupsInToken].Sid, 5177 &NewState[GroupsInList].Sid)) 5178 { 5179 /* 5180 * This is the group that we're looking for. 5181 * However, it could be that the group is a 5182 * mandatory group which we are not allowed 5183 * and cannot disable it. 5184 */ 5185 if ((Token->UserAndGroups[GroupsInToken].Attributes & SE_GROUP_MANDATORY) && 5186 (NewState[GroupsInList].Attributes & SE_GROUP_ENABLED) == 0) 5187 { 5188 /* It is mandatory, forget about this group */ 5189 DPRINT1("SepAdjustGroups(): The SID group is mandatory!\n"); 5190 return STATUS_CANT_DISABLE_MANDATORY; 5191 } 5192 5193 /* 5194 * We've to ensure that apart the group mustn't be 5195 * mandatory, it mustn't be a restricted group as 5196 * well. That is, the group is marked with 5197 * SE_GROUP_USE_FOR_DENY_ONLY flag and no one 5198 * can enable it because it's for "deny" use only. 5199 */ 5200 if ((Token->UserAndGroups[GroupsInToken].Attributes & SE_GROUP_USE_FOR_DENY_ONLY) && 5201 (NewState[GroupsInList].Attributes & SE_GROUP_ENABLED)) 5202 { 5203 /* This group is restricted, forget about it */ 5204 DPRINT1("SepAdjustGroups(): The SID group is for use deny only!\n"); 5205 return STATUS_CANT_ENABLE_DENY_ONLY; 5206 } 5207 5208 /* Copy the attributes and stop searching */ 5209 NewAttributes = NewState[GroupsInList].Attributes; 5210 NewAttributes &= SE_GROUP_ENABLED; 5211 NewAttributes = Token->UserAndGroups[GroupsInToken].Attributes & ~SE_GROUP_ENABLED; 5212 break; 5213 } 5214 5215 /* Did we find the specific group we wanted? */ 5216 if (GroupsInList == NewStateCount) 5217 { 5218 /* We didn't, continue with the next token's group */ 5219 continue; 5220 } 5221 } 5222 5223 /* Count the group that we found it */ 5224 GroupsCount++; 5225 5226 /* Does the token have the same attributes as the caller requested them? */ 5227 if (Token->UserAndGroups[GroupsInToken].Attributes != NewAttributes) 5228 { 5229 /* 5230 * No, then it's time to make some adjustment to the 5231 * token's groups. Does the caller want the previous states 5232 * of groups? 5233 */ 5234 if (PreviousGroupsState != NULL) 5235 { 5236 PreviousGroupsState->Groups[ChangeCount] = Token->UserAndGroups[GroupsInToken]; 5237 } 5238 5239 /* Time to apply the changes now? */ 5240 if (ApplyChanges) 5241 { 5242 /* The caller gave us consent, apply and report that we made changes! */ 5243 Token->UserAndGroups[GroupsInToken].Attributes = NewAttributes; 5244 *ChangesMade = TRUE; 5245 } 5246 5247 /* Increment the count change */ 5248 ChangeCount++; 5249 } 5250 } 5251 } 5252 5253 /* Report the number of previous saved groups */ 5254 if (PreviousGroupsState != NULL) 5255 { 5256 PreviousGroupsState->GroupCount = ChangeCount; 5257 } 5258 5259 /* Report the number of changed groups */ 5260 *ChangedGroups = ChangeCount; 5261 5262 /* Did we miss some groups? */ 5263 if (!ResetToDefaultStates && (GroupsCount < NewStateCount)) 5264 { 5265 /* 5266 * If we're at this stage then we are in a situation 5267 * where the adjust changes done to token's groups is 5268 * not deterministic as the caller might have wanted 5269 * as per NewState parameter. 5270 */ 5271 DPRINT1("SepAdjustGroups(): The token hasn't all the groups assigned!\n"); 5272 return STATUS_NOT_ALL_ASSIGNED; 5273 } 5274 5275 return STATUS_SUCCESS; 5276 } 5277 5278 /** 5279 * @brief 5280 * Changes the list of groups by enabling or disabling them 5281 * in an access token. Unlike NtAdjustPrivilegesToken, 5282 * this API routine does not remove groups. 5283 * 5284 * @param[in] TokenHandle 5285 * Token handle where the list of groups SID are to be adjusted. 5286 * The access token must have TOKEN_ADJUST_GROUPS access right 5287 * in order to change the groups in a token. The token must also 5288 * have TOKEN_QUERY access right if the caller requests the previous 5289 * states of groups list, that is, PreviousState is not NULL. 5290 * 5291 * @param[in] ResetToDefault 5292 * If set to TRUE, the function resets the list of groups to default 5293 * enabled and disabled states. NewState is ignored in this case. 5294 * Otherwise if the parameter is set to FALSE, the function expects 5295 * a new list of groups from NewState to be adjusted within the token. 5296 * 5297 * @param[in] NewState 5298 * A new list of groups SID that the function will use it accordingly to 5299 * modify the current list of groups SID of a token. 5300 * 5301 * @param[in] BufferLength 5302 * The length size of the buffer that is pointed by the NewState parameter 5303 * argument, in bytes. 5304 * 5305 * @param[out] PreviousState 5306 * If specified, the function will return to the caller the old list of groups 5307 * SID. If this parameter is NULL, ReturnLength must also be NULL. 5308 * 5309 * @param[out] ReturnLength 5310 * If specified, the function will return the total size length of the old list 5311 * of groups SIDs, in bytes. 5312 * 5313 * @return 5314 * STATUS_SUCCESS is returned if the function has successfully adjusted the 5315 * token's groups. STATUS_INVALID_PARAMETER is returned if the caller has 5316 * submitted one or more invalid parameters, that is, the caller didn't want 5317 * to reset the groups to default state but no NewState argument list has been 5318 * provided. STATUS_BUFFER_TOO_SMALL is returned if the buffer length given 5319 * by the caller is smaller than the required length size. A failure NTSTATUS 5320 * code is returned otherwise. 5321 */ 5322 NTSTATUS 5323 NTAPI 5324 NtAdjustGroupsToken( 5325 _In_ HANDLE TokenHandle, 5326 _In_ BOOLEAN ResetToDefault, 5327 _In_ PTOKEN_GROUPS NewState, 5328 _In_ ULONG BufferLength, 5329 _Out_writes_bytes_to_opt_(BufferLength, *ReturnLength) 5330 PTOKEN_GROUPS PreviousState, 5331 _When_(PreviousState != NULL, _Out_) PULONG ReturnLength) 5332 { 5333 PTOKEN Token; 5334 NTSTATUS Status; 5335 KPROCESSOR_MODE PreviousMode; 5336 ULONG ChangeCount, RequiredLength; 5337 ULONG CapturedCount = 0; 5338 ULONG CapturedLength = 0; 5339 ULONG NewStateSize = 0; 5340 PSID_AND_ATTRIBUTES CapturedGroups = NULL; 5341 BOOLEAN ChangesMade = FALSE; 5342 BOOLEAN LockAndReferenceAcquired = FALSE; 5343 5344 PAGED_CODE(); 5345 5346 /* 5347 * If the caller doesn't want to reset the groups of an 5348 * access token to default states then at least we must 5349 * expect a list of groups to be adjusted based on NewState 5350 * parameter. Otherwise bail out because the caller has 5351 * no idea what they're doing. 5352 */ 5353 if (!ResetToDefault && !NewState) 5354 { 5355 DPRINT1("NtAdjustGroupsToken(): The caller hasn't provided any list of groups to adjust!\n"); 5356 return STATUS_INVALID_PARAMETER; 5357 } 5358 5359 PreviousMode = ExGetPreviousMode(); 5360 5361 if (PreviousMode != KernelMode) 5362 { 5363 _SEH2_TRY 5364 { 5365 /* Probe NewState */ 5366 if (!ResetToDefault) 5367 { 5368 /* Probe the header */ 5369 ProbeForRead(NewState, sizeof(*NewState), sizeof(ULONG)); 5370 5371 CapturedCount = NewState->GroupCount; 5372 NewStateSize = FIELD_OFFSET(TOKEN_GROUPS, Groups[CapturedCount]); 5373 5374 ProbeForRead(NewState, NewStateSize, sizeof(ULONG)); 5375 } 5376 5377 if (PreviousState != NULL) 5378 { 5379 ProbeForWrite(PreviousState, BufferLength, sizeof(ULONG)); 5380 ProbeForWrite(ReturnLength, sizeof(*ReturnLength), sizeof(ULONG)); 5381 } 5382 } 5383 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 5384 { 5385 /* Return the exception code */ 5386 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 5387 } 5388 _SEH2_END; 5389 } 5390 else 5391 { 5392 /* 5393 * We're calling directly from the kernel, just retrieve 5394 * the number count of captured groups outright. 5395 */ 5396 if (!ResetToDefault) 5397 { 5398 CapturedCount = NewState->GroupCount; 5399 } 5400 } 5401 5402 /* Time to capture the NewState list */ 5403 if (!ResetToDefault) 5404 { 5405 _SEH2_TRY 5406 { 5407 Status = SeCaptureSidAndAttributesArray(NewState->Groups, 5408 CapturedCount, 5409 PreviousMode, 5410 NULL, 5411 0, 5412 PagedPool, 5413 TRUE, 5414 &CapturedGroups, 5415 &CapturedLength); 5416 } 5417 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 5418 { 5419 Status = _SEH2_GetExceptionCode(); 5420 } 5421 _SEH2_END; 5422 5423 if (!NT_SUCCESS(Status)) 5424 { 5425 DPRINT1("NtAdjustGroupsToken(): Failed to capture the NewState list of groups (Status 0x%lx)\n", Status); 5426 return Status; 5427 } 5428 } 5429 5430 /* Time to reference the token */ 5431 Status = ObReferenceObjectByHandle(TokenHandle, 5432 TOKEN_ADJUST_GROUPS | (PreviousState != NULL ? TOKEN_QUERY : 0), 5433 SeTokenObjectType, 5434 PreviousMode, 5435 (PVOID*)&Token, 5436 NULL); 5437 if (!NT_SUCCESS(Status)) 5438 { 5439 /* We couldn't reference the access token, bail out */ 5440 DPRINT1("NtAdjustGroupsToken(): Failed to reference the token (Status 0x%lx)\n", Status); 5441 5442 if (CapturedGroups != NULL) 5443 { 5444 SeReleaseSidAndAttributesArray(CapturedGroups, 5445 PreviousMode, 5446 TRUE); 5447 } 5448 5449 goto Quit; 5450 } 5451 5452 /* Lock the token */ 5453 SepAcquireTokenLockExclusive(Token); 5454 LockAndReferenceAcquired = TRUE; 5455 5456 /* Count the number of groups to be changed */ 5457 Status = SepAdjustGroups(Token, 5458 CapturedGroups, 5459 CapturedCount, 5460 FALSE, 5461 ResetToDefault, 5462 &ChangesMade, 5463 NULL, 5464 &ChangeCount); 5465 5466 /* Does the caller want the previous state of groups? */ 5467 if (PreviousState != NULL) 5468 { 5469 /* Calculate the required length */ 5470 RequiredLength = FIELD_OFFSET(TOKEN_GROUPS, Groups[ChangeCount]); 5471 5472 /* Return the required length to the caller */ 5473 _SEH2_TRY 5474 { 5475 *ReturnLength = RequiredLength; 5476 } 5477 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 5478 { 5479 /* Bail out and return the exception code */ 5480 Status = _SEH2_GetExceptionCode(); 5481 _SEH2_YIELD(goto Quit); 5482 } 5483 _SEH2_END; 5484 5485 /* The buffer length provided is smaller than the required length, bail out */ 5486 if (BufferLength < RequiredLength) 5487 { 5488 Status = STATUS_BUFFER_TOO_SMALL; 5489 goto Quit; 5490 } 5491 } 5492 5493 /* 5494 * Now it's time to apply changes. Wrap the code 5495 * in SEH as we are returning the old groups state 5496 * list to the caller since PreviousState is a 5497 * UM pointer. 5498 */ 5499 _SEH2_TRY 5500 { 5501 Status = SepAdjustGroups(Token, 5502 CapturedGroups, 5503 CapturedCount, 5504 TRUE, 5505 ResetToDefault, 5506 &ChangesMade, 5507 PreviousState, 5508 &ChangeCount); 5509 } 5510 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 5511 { 5512 /* Bail out and return the exception code */ 5513 Status = _SEH2_GetExceptionCode(); 5514 5515 /* Force the write as we touched the token still */ 5516 ChangesMade = TRUE; 5517 _SEH2_YIELD(goto Quit); 5518 } 5519 _SEH2_END; 5520 5521 Quit: 5522 /* Allocate a new ID for the token as we made changes */ 5523 if (ChangesMade) 5524 { 5525 ExAllocateLocallyUniqueId(&Token->ModifiedId); 5526 } 5527 5528 /* Have we successfully acquired the lock and referenced the token before? */ 5529 if (LockAndReferenceAcquired) 5530 { 5531 /* Unlock and dereference the token */ 5532 SepReleaseTokenLock(Token); 5533 ObDereferenceObject(Token); 5534 } 5535 5536 /* Release the captured groups */ 5537 if (CapturedGroups != NULL) 5538 { 5539 SeReleaseSidAndAttributesArray(CapturedGroups, 5540 PreviousMode, 5541 TRUE); 5542 } 5543 5544 return Status; 5545 } 5546 5547 /** 5548 * @brief 5549 * Removes a certain amount of privileges of a token based upon the request 5550 * by the caller. 5551 * 5552 * @param[in,out] Token 5553 * Token handle where the privileges are about to be modified. 5554 * 5555 * @param[in] DisableAllPrivileges 5556 * If set to TRUE, the function disables all the privileges. 5557 * 5558 * @param[in] NewState 5559 * A new list of privileges that the function will use it accordingly to 5560 * either disable or enable the said privileges and change them. 5561 * 5562 * @param[in] NewStateCount 5563 * The new total number count of privileges. 5564 * 5565 * @param[out] PreviousState 5566 * If specified, the function will return the previous state list of privileges. 5567 * 5568 * @param[in] ApplyChanges 5569 * If set to TRUE, the function will immediatelly apply the changes onto the 5570 * token's privileges. 5571 * 5572 * @param[out] ChangedPrivileges 5573 * The returned count number of changed privileges. 5574 * 5575 * @param[out] ChangesMade 5576 * If TRUE, the function has made changes to the token's privileges. FALSE 5577 * otherwise. 5578 * 5579 * @return 5580 * Returns STATUS_SUCCESS if the function has successfully changed the list 5581 * of privileges. STATUS_NOT_ALL_ASSIGNED is returned if not every privilege 5582 * has been changed. 5583 */ 5584 static 5585 NTSTATUS 5586 SepAdjustPrivileges( 5587 _Inout_ PTOKEN Token, 5588 _In_ BOOLEAN DisableAllPrivileges, 5589 _In_opt_ PLUID_AND_ATTRIBUTES NewState, 5590 _In_ ULONG NewStateCount, 5591 _Out_opt_ PTOKEN_PRIVILEGES PreviousState, 5592 _In_ BOOLEAN ApplyChanges, 5593 _Out_ PULONG ChangedPrivileges, 5594 _Out_ PBOOLEAN ChangesMade) 5595 { 5596 ULONG i, j, PrivilegeCount, ChangeCount, NewAttributes; 5597 5598 PAGED_CODE(); 5599 5600 /* Count the found privileges and those that need to be changed */ 5601 PrivilegeCount = 0; 5602 ChangeCount = 0; 5603 *ChangesMade = FALSE; 5604 5605 /* Loop all privileges in the token */ 5606 for (i = 0; i < Token->PrivilegeCount; i++) 5607 { 5608 /* Shall all of them be disabled? */ 5609 if (DisableAllPrivileges) 5610 { 5611 /* The new attributes are the old ones, but disabled */ 5612 NewAttributes = Token->Privileges[i].Attributes & ~SE_PRIVILEGE_ENABLED; 5613 } 5614 else 5615 { 5616 /* Otherwise loop all provided privileges */ 5617 for (j = 0; j < NewStateCount; j++) 5618 { 5619 /* Check if this is the LUID we are looking for */ 5620 if (RtlEqualLuid(&Token->Privileges[i].Luid, &NewState[j].Luid)) 5621 { 5622 DPRINT("Found privilege\n"); 5623 5624 /* Copy SE_PRIVILEGE_ENABLED | SE_PRIVILEGE_REMOVED */ 5625 NewAttributes = NewState[j].Attributes; 5626 NewAttributes &= (SE_PRIVILEGE_ENABLED | SE_PRIVILEGE_REMOVED); 5627 NewAttributes |= Token->Privileges[i].Attributes & ~SE_PRIVILEGE_ENABLED; 5628 5629 /* Stop looking */ 5630 break; 5631 } 5632 } 5633 5634 /* Check if we didn't find the privilege */ 5635 if (j == NewStateCount) 5636 { 5637 /* Continue with the token's next privilege */ 5638 continue; 5639 } 5640 } 5641 5642 /* We found a privilege, count it */ 5643 PrivilegeCount++; 5644 5645 /* Does the privilege need to be changed? */ 5646 if (Token->Privileges[i].Attributes != NewAttributes) 5647 { 5648 /* Does the caller want the old privileges? */ 5649 if (PreviousState != NULL) 5650 { 5651 /* Copy the old privilege */ 5652 PreviousState->Privileges[ChangeCount] = Token->Privileges[i]; 5653 } 5654 5655 /* Does the caller want to apply the changes? */ 5656 if (ApplyChanges) 5657 { 5658 /* Shall we remove the privilege? */ 5659 if (NewAttributes & SE_PRIVILEGE_REMOVED) 5660 { 5661 /* Set the token as disabled and update flags for it */ 5662 Token->Privileges[i].Attributes &= ~SE_PRIVILEGE_ENABLED; 5663 SepUpdateSinglePrivilegeFlagToken(Token, i); 5664 5665 /* Remove the privilege */ 5666 SepRemovePrivilegeToken(Token, i); 5667 5668 *ChangesMade = TRUE; 5669 5670 /* Fix the running index and continue with next one */ 5671 i--; 5672 continue; 5673 } 5674 5675 /* Set the new attributes and update flags */ 5676 Token->Privileges[i].Attributes = NewAttributes; 5677 SepUpdateSinglePrivilegeFlagToken(Token, i); 5678 *ChangesMade = TRUE; 5679 } 5680 5681 /* Increment the change count */ 5682 ChangeCount++; 5683 } 5684 } 5685 5686 /* Set the number of saved privileges */ 5687 if (PreviousState != NULL) 5688 PreviousState->PrivilegeCount = ChangeCount; 5689 5690 /* Return the number of changed privileges */ 5691 *ChangedPrivileges = ChangeCount; 5692 5693 /* Check if we missed some */ 5694 if (!DisableAllPrivileges && (PrivilegeCount < NewStateCount)) 5695 { 5696 return STATUS_NOT_ALL_ASSIGNED; 5697 } 5698 5699 return STATUS_SUCCESS; 5700 } 5701 5702 /** 5703 * @brief 5704 * Removes a certain amount of privileges of a token based upon the request 5705 * by the caller. 5706 * 5707 * @param[in,out] Token 5708 * Token handle where the privileges are about to be modified. 5709 * 5710 * @param[in] DisableAllPrivileges 5711 * If set to TRUE, the function disables all the privileges. 5712 * 5713 * @param[in] NewState 5714 * A new list of privileges that the function will use it accordingly to 5715 * either disable or enable the said privileges and change them. 5716 * 5717 * @param[in] NewStateCount 5718 * The new total number count of privileges. 5719 * 5720 * @param[out] PreviousState 5721 * If specified, the function will return the previous state list of privileges. 5722 * 5723 * @param[in] ApplyChanges 5724 * If set to TRUE, the function will immediatelly apply the changes onto the 5725 * token's privileges. 5726 * 5727 * @param[out] ChangedPrivileges 5728 * The returned count number of changed privileges. 5729 * 5730 * @param[out] ChangesMade 5731 * If TRUE, the function has made changes to the token's privileges. FALSE 5732 * otherwise. 5733 * 5734 * @return 5735 * Returns STATUS_SUCCESS if the function has successfully changed the list 5736 * of privileges. STATUS_NOT_ALL_ASSIGNED is returned if not every privilege 5737 * has been changed. 5738 */ 5739 _Must_inspect_result_ 5740 __kernel_entry 5741 NTSTATUS 5742 NTAPI 5743 NtAdjustPrivilegesToken( 5744 _In_ HANDLE TokenHandle, 5745 _In_ BOOLEAN DisableAllPrivileges, 5746 _In_opt_ PTOKEN_PRIVILEGES NewState, 5747 _In_ ULONG BufferLength, 5748 _Out_writes_bytes_to_opt_(BufferLength,*ReturnLength) 5749 PTOKEN_PRIVILEGES PreviousState, 5750 _When_(PreviousState!=NULL, _Out_) PULONG ReturnLength) 5751 { 5752 NTSTATUS Status; 5753 KPROCESSOR_MODE PreviousMode; 5754 PTOKEN Token; 5755 PLUID_AND_ATTRIBUTES CapturedPrivileges = NULL; 5756 ULONG CapturedCount = 0; 5757 ULONG CapturedLength = 0; 5758 ULONG NewStateSize = 0; 5759 ULONG ChangeCount; 5760 ULONG RequiredLength; 5761 BOOLEAN ChangesMade = FALSE; 5762 5763 PAGED_CODE(); 5764 5765 DPRINT("NtAdjustPrivilegesToken() called\n"); 5766 5767 /* Fail, if we do not disable all privileges but NewState is NULL */ 5768 if (DisableAllPrivileges == FALSE && NewState == NULL) 5769 return STATUS_INVALID_PARAMETER; 5770 5771 PreviousMode = KeGetPreviousMode(); 5772 if (PreviousMode != KernelMode) 5773 { 5774 _SEH2_TRY 5775 { 5776 /* Probe NewState */ 5777 if (DisableAllPrivileges == FALSE) 5778 { 5779 /* First probe the header */ 5780 ProbeForRead(NewState, sizeof(TOKEN_PRIVILEGES), sizeof(ULONG)); 5781 5782 CapturedCount = NewState->PrivilegeCount; 5783 NewStateSize = FIELD_OFFSET(TOKEN_PRIVILEGES, Privileges[CapturedCount]); 5784 5785 ProbeForRead(NewState, NewStateSize, sizeof(ULONG)); 5786 } 5787 5788 /* Probe PreviousState and ReturnLength */ 5789 if (PreviousState != NULL) 5790 { 5791 ProbeForWrite(PreviousState, BufferLength, sizeof(ULONG)); 5792 ProbeForWrite(ReturnLength, sizeof(ULONG), sizeof(ULONG)); 5793 } 5794 } 5795 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 5796 { 5797 /* Return the exception code */ 5798 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 5799 } 5800 _SEH2_END; 5801 } 5802 else 5803 { 5804 /* This is kernel mode, we trust the caller */ 5805 if (DisableAllPrivileges == FALSE) 5806 CapturedCount = NewState->PrivilegeCount; 5807 } 5808 5809 /* Do we need to capture the new state? */ 5810 if (DisableAllPrivileges == FALSE) 5811 { 5812 _SEH2_TRY 5813 { 5814 /* Capture the new state array of privileges */ 5815 Status = SeCaptureLuidAndAttributesArray(NewState->Privileges, 5816 CapturedCount, 5817 PreviousMode, 5818 NULL, 5819 0, 5820 PagedPool, 5821 TRUE, 5822 &CapturedPrivileges, 5823 &CapturedLength); 5824 } 5825 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 5826 { 5827 /* Return the exception code */ 5828 Status = _SEH2_GetExceptionCode(); 5829 } 5830 _SEH2_END; 5831 5832 if (!NT_SUCCESS(Status)) 5833 return Status; 5834 } 5835 5836 /* Reference the token */ 5837 Status = ObReferenceObjectByHandle(TokenHandle, 5838 TOKEN_ADJUST_PRIVILEGES | (PreviousState != NULL ? TOKEN_QUERY : 0), 5839 SeTokenObjectType, 5840 PreviousMode, 5841 (PVOID*)&Token, 5842 NULL); 5843 if (!NT_SUCCESS(Status)) 5844 { 5845 DPRINT1("Failed to reference token (Status 0x%lx)\n", Status); 5846 5847 /* Release the captured privileges */ 5848 if (CapturedPrivileges != NULL) 5849 { 5850 SeReleaseLuidAndAttributesArray(CapturedPrivileges, 5851 PreviousMode, 5852 TRUE); 5853 } 5854 5855 return Status; 5856 } 5857 5858 /* Lock the token */ 5859 SepAcquireTokenLockExclusive(Token); 5860 5861 /* Count the privileges that need to be changed, do not apply them yet */ 5862 Status = SepAdjustPrivileges(Token, 5863 DisableAllPrivileges, 5864 CapturedPrivileges, 5865 CapturedCount, 5866 NULL, 5867 FALSE, 5868 &ChangeCount, 5869 &ChangesMade); 5870 5871 /* Check if the caller asked for the previous state */ 5872 if (PreviousState != NULL) 5873 { 5874 /* Calculate the required length */ 5875 RequiredLength = FIELD_OFFSET(TOKEN_PRIVILEGES, Privileges[ChangeCount]); 5876 5877 /* Try to return the required buffer length */ 5878 _SEH2_TRY 5879 { 5880 *ReturnLength = RequiredLength; 5881 } 5882 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 5883 { 5884 /* Do cleanup and return the exception code */ 5885 Status = _SEH2_GetExceptionCode(); 5886 _SEH2_YIELD(goto Cleanup); 5887 } 5888 _SEH2_END; 5889 5890 /* Fail, if the buffer length is smaller than the required length */ 5891 if (BufferLength < RequiredLength) 5892 { 5893 Status = STATUS_BUFFER_TOO_SMALL; 5894 goto Cleanup; 5895 } 5896 } 5897 5898 /* Now enter SEH, since we might return the old privileges */ 5899 _SEH2_TRY 5900 { 5901 /* This time apply the changes */ 5902 Status = SepAdjustPrivileges(Token, 5903 DisableAllPrivileges, 5904 CapturedPrivileges, 5905 CapturedCount, 5906 PreviousState, 5907 TRUE, 5908 &ChangeCount, 5909 &ChangesMade); 5910 } 5911 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 5912 { 5913 /* Do cleanup and return the exception code */ 5914 Status = _SEH2_GetExceptionCode(); 5915 ChangesMade = TRUE; // Force write. 5916 _SEH2_YIELD(goto Cleanup); 5917 } 5918 _SEH2_END; 5919 5920 Cleanup: 5921 /* Touch the token if we made changes */ 5922 if (ChangesMade) 5923 ExAllocateLocallyUniqueId(&Token->ModifiedId); 5924 5925 /* Unlock and dereference the token */ 5926 SepReleaseTokenLock(Token); 5927 ObDereferenceObject(Token); 5928 5929 /* Release the captured privileges */ 5930 if (CapturedPrivileges != NULL) 5931 { 5932 SeReleaseLuidAndAttributesArray(CapturedPrivileges, 5933 PreviousMode, 5934 TRUE); 5935 } 5936 5937 DPRINT ("NtAdjustPrivilegesToken() done\n"); 5938 return Status; 5939 } 5940 5941 /** 5942 * @brief 5943 * Creates an access token. 5944 * 5945 * @param[out] TokenHandle 5946 * The returned created token handle to the caller. 5947 * 5948 * @param[in] DesiredAccess 5949 * The desired access rights for the token that we're creating. 5950 * 5951 * @param[in] ObjectAttributes 5952 * The object attributes for the token object that we're creating. 5953 * 5954 * @param[in] TokenType 5955 * The type of token to assign for the newly created token. 5956 * 5957 * @param[in] AuthenticationId 5958 * Authentication ID that represents the token's identity. 5959 * 5960 * @param[in] ExpirationTime 5961 * Expiration time for the token. If set to -1, the token never expires. 5962 * 5963 * @param[in] TokenUser 5964 * The main user entity for the token to assign. 5965 * 5966 * @param[in] TokenGroups 5967 * Group list of SIDs for the token to assign. 5968 * 5969 * @param[in] TokenPrivileges 5970 * Privileges for the token. 5971 * 5972 * @param[in] TokenOwner 5973 * The main user that owns the newly created token. 5974 * 5975 * @param[in] TokenPrimaryGroup 5976 * The primary group that represents as the main group of the token. 5977 * 5978 * @param[in] TokenDefaultDacl 5979 * Discretionary access control list for the token. This limits on how 5980 * the token can be used, accessed and used by whom. 5981 * 5982 * @param[in] TokenSource 5983 * The source origin of the token who creates it. 5984 * 5985 * @return 5986 * Returns STATUS_SUCCESS if the function has successfully created the token. 5987 * A failure NTSTATUS code is returned otherwise. 5988 */ 5989 __kernel_entry 5990 NTSTATUS 5991 NTAPI 5992 NtCreateToken( 5993 _Out_ PHANDLE TokenHandle, 5994 _In_ ACCESS_MASK DesiredAccess, 5995 _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes, 5996 _In_ TOKEN_TYPE TokenType, 5997 _In_ PLUID AuthenticationId, 5998 _In_ PLARGE_INTEGER ExpirationTime, 5999 _In_ PTOKEN_USER TokenUser, 6000 _In_ PTOKEN_GROUPS TokenGroups, 6001 _In_ PTOKEN_PRIVILEGES TokenPrivileges, 6002 _In_opt_ PTOKEN_OWNER TokenOwner, 6003 _In_ PTOKEN_PRIMARY_GROUP TokenPrimaryGroup, 6004 _In_opt_ PTOKEN_DEFAULT_DACL TokenDefaultDacl, 6005 _In_ PTOKEN_SOURCE TokenSource) 6006 { 6007 HANDLE hToken; 6008 KPROCESSOR_MODE PreviousMode; 6009 ULONG PrivilegeCount, GroupCount; 6010 PSID OwnerSid, PrimaryGroupSid; 6011 PACL DefaultDacl; 6012 LARGE_INTEGER LocalExpirationTime = {{0, 0}}; 6013 LUID LocalAuthenticationId; 6014 TOKEN_SOURCE LocalTokenSource; 6015 SECURITY_QUALITY_OF_SERVICE LocalSecurityQos; 6016 PLUID_AND_ATTRIBUTES CapturedPrivileges = NULL; 6017 PSID_AND_ATTRIBUTES CapturedUser = NULL; 6018 PSID_AND_ATTRIBUTES CapturedGroups = NULL; 6019 PSID CapturedOwnerSid = NULL; 6020 PSID CapturedPrimaryGroupSid = NULL; 6021 PACL CapturedDefaultDacl = NULL; 6022 ULONG PrivilegesLength, UserLength, GroupsLength; 6023 NTSTATUS Status; 6024 6025 PAGED_CODE(); 6026 6027 PreviousMode = ExGetPreviousMode(); 6028 6029 if (PreviousMode != KernelMode) 6030 { 6031 _SEH2_TRY 6032 { 6033 ProbeForWriteHandle(TokenHandle); 6034 6035 if (ObjectAttributes != NULL) 6036 { 6037 ProbeForRead(ObjectAttributes, 6038 sizeof(OBJECT_ATTRIBUTES), 6039 sizeof(ULONG)); 6040 LocalSecurityQos = *(SECURITY_QUALITY_OF_SERVICE*)ObjectAttributes->SecurityQualityOfService; 6041 } 6042 6043 ProbeForRead(AuthenticationId, 6044 sizeof(LUID), 6045 sizeof(ULONG)); 6046 LocalAuthenticationId = *AuthenticationId; 6047 6048 LocalExpirationTime = ProbeForReadLargeInteger(ExpirationTime); 6049 6050 ProbeForRead(TokenUser, 6051 sizeof(TOKEN_USER), 6052 sizeof(ULONG)); 6053 6054 ProbeForRead(TokenGroups, 6055 sizeof(TOKEN_GROUPS), 6056 sizeof(ULONG)); 6057 GroupCount = TokenGroups->GroupCount; 6058 6059 ProbeForRead(TokenPrivileges, 6060 sizeof(TOKEN_PRIVILEGES), 6061 sizeof(ULONG)); 6062 PrivilegeCount = TokenPrivileges->PrivilegeCount; 6063 6064 if (TokenOwner != NULL) 6065 { 6066 ProbeForRead(TokenOwner, 6067 sizeof(TOKEN_OWNER), 6068 sizeof(ULONG)); 6069 OwnerSid = TokenOwner->Owner; 6070 } 6071 else 6072 { 6073 OwnerSid = NULL; 6074 } 6075 6076 ProbeForRead(TokenPrimaryGroup, 6077 sizeof(TOKEN_PRIMARY_GROUP), 6078 sizeof(ULONG)); 6079 PrimaryGroupSid = TokenPrimaryGroup->PrimaryGroup; 6080 6081 if (TokenDefaultDacl != NULL) 6082 { 6083 ProbeForRead(TokenDefaultDacl, 6084 sizeof(TOKEN_DEFAULT_DACL), 6085 sizeof(ULONG)); 6086 DefaultDacl = TokenDefaultDacl->DefaultDacl; 6087 } 6088 else 6089 { 6090 DefaultDacl = NULL; 6091 } 6092 6093 ProbeForRead(TokenSource, 6094 sizeof(TOKEN_SOURCE), 6095 sizeof(ULONG)); 6096 LocalTokenSource = *TokenSource; 6097 } 6098 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 6099 { 6100 /* Return the exception code */ 6101 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 6102 } 6103 _SEH2_END; 6104 } 6105 else 6106 { 6107 if (ObjectAttributes != NULL) 6108 LocalSecurityQos = *(SECURITY_QUALITY_OF_SERVICE*)ObjectAttributes->SecurityQualityOfService; 6109 LocalAuthenticationId = *AuthenticationId; 6110 LocalExpirationTime = *ExpirationTime; 6111 GroupCount = TokenGroups->GroupCount; 6112 PrivilegeCount = TokenPrivileges->PrivilegeCount; 6113 OwnerSid = TokenOwner ? TokenOwner->Owner : NULL; 6114 PrimaryGroupSid = TokenPrimaryGroup->PrimaryGroup; 6115 DefaultDacl = TokenDefaultDacl ? TokenDefaultDacl->DefaultDacl : NULL; 6116 LocalTokenSource = *TokenSource; 6117 } 6118 6119 /* Check token type */ 6120 if ((TokenType < TokenPrimary) || 6121 (TokenType > TokenImpersonation)) 6122 { 6123 return STATUS_BAD_TOKEN_TYPE; 6124 } 6125 6126 /* Check for token creation privilege */ 6127 if (!SeSinglePrivilegeCheck(SeCreateTokenPrivilege, PreviousMode)) 6128 { 6129 return STATUS_PRIVILEGE_NOT_HELD; 6130 } 6131 6132 /* Capture the user SID and attributes */ 6133 Status = SeCaptureSidAndAttributesArray(&TokenUser->User, 6134 1, 6135 PreviousMode, 6136 NULL, 6137 0, 6138 PagedPool, 6139 FALSE, 6140 &CapturedUser, 6141 &UserLength); 6142 if (!NT_SUCCESS(Status)) 6143 { 6144 goto Cleanup; 6145 } 6146 6147 /* Capture the groups SID and attributes array */ 6148 Status = SeCaptureSidAndAttributesArray(&TokenGroups->Groups[0], 6149 GroupCount, 6150 PreviousMode, 6151 NULL, 6152 0, 6153 PagedPool, 6154 FALSE, 6155 &CapturedGroups, 6156 &GroupsLength); 6157 if (!NT_SUCCESS(Status)) 6158 { 6159 goto Cleanup; 6160 } 6161 6162 /* Capture privileges */ 6163 Status = SeCaptureLuidAndAttributesArray(&TokenPrivileges->Privileges[0], 6164 PrivilegeCount, 6165 PreviousMode, 6166 NULL, 6167 0, 6168 PagedPool, 6169 FALSE, 6170 &CapturedPrivileges, 6171 &PrivilegesLength); 6172 if (!NT_SUCCESS(Status)) 6173 { 6174 goto Cleanup; 6175 } 6176 6177 /* Capture the token owner SID */ 6178 if (TokenOwner != NULL) 6179 { 6180 Status = SepCaptureSid(OwnerSid, 6181 PreviousMode, 6182 PagedPool, 6183 FALSE, 6184 &CapturedOwnerSid); 6185 if (!NT_SUCCESS(Status)) 6186 { 6187 goto Cleanup; 6188 } 6189 } 6190 6191 /* Capture the token primary group SID */ 6192 Status = SepCaptureSid(PrimaryGroupSid, 6193 PreviousMode, 6194 PagedPool, 6195 FALSE, 6196 &CapturedPrimaryGroupSid); 6197 if (!NT_SUCCESS(Status)) 6198 { 6199 goto Cleanup; 6200 } 6201 6202 /* Capture DefaultDacl */ 6203 if (DefaultDacl != NULL) 6204 { 6205 Status = SepCaptureAcl(DefaultDacl, 6206 PreviousMode, 6207 NonPagedPool, 6208 FALSE, 6209 &CapturedDefaultDacl); 6210 if (!NT_SUCCESS(Status)) 6211 { 6212 goto Cleanup; 6213 } 6214 } 6215 6216 /* Call the internal function */ 6217 Status = SepCreateToken(&hToken, 6218 PreviousMode, 6219 DesiredAccess, 6220 ObjectAttributes, 6221 TokenType, 6222 LocalSecurityQos.ImpersonationLevel, 6223 &LocalAuthenticationId, 6224 &LocalExpirationTime, 6225 CapturedUser, 6226 GroupCount, 6227 CapturedGroups, 6228 GroupsLength, 6229 PrivilegeCount, 6230 CapturedPrivileges, 6231 CapturedOwnerSid, 6232 CapturedPrimaryGroupSid, 6233 CapturedDefaultDacl, 6234 &LocalTokenSource, 6235 FALSE); 6236 if (NT_SUCCESS(Status)) 6237 { 6238 _SEH2_TRY 6239 { 6240 *TokenHandle = hToken; 6241 } 6242 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 6243 { 6244 Status = _SEH2_GetExceptionCode(); 6245 } 6246 _SEH2_END; 6247 } 6248 6249 Cleanup: 6250 6251 /* Release what we captured */ 6252 SeReleaseSidAndAttributesArray(CapturedUser, PreviousMode, FALSE); 6253 SeReleaseSidAndAttributesArray(CapturedGroups, PreviousMode, FALSE); 6254 SeReleaseLuidAndAttributesArray(CapturedPrivileges, PreviousMode, FALSE); 6255 SepReleaseSid(CapturedOwnerSid, PreviousMode, FALSE); 6256 SepReleaseSid(CapturedPrimaryGroupSid, PreviousMode, FALSE); 6257 SepReleaseAcl(CapturedDefaultDacl, PreviousMode, FALSE); 6258 6259 return Status; 6260 } 6261 6262 /** 6263 * @brief 6264 * Opens a token that is tied to a thread handle. 6265 * 6266 * @param[out] ThreadHandle 6267 * Thread handle where the token is about to be opened. 6268 * 6269 * @param[in] DesiredAccess 6270 * The request access right for the token. 6271 * 6272 * @param[in] OpenAsSelf 6273 * If set to TRUE, the access check will be made with the security context 6274 * of the process of the calling thread (opening as self). Otherwise the access 6275 * check will be made with the security context of the calling thread instead. 6276 * 6277 * @param[in] HandleAttributes 6278 * Handle attributes for the opened thread token handle. 6279 * 6280 * @param[out] TokenHandle 6281 * The opened token handle returned to the caller for use. 6282 * 6283 * @return 6284 * Returns STATUS_SUCCESS if the function has successfully opened the thread 6285 * token. STATUS_CANT_OPEN_ANONYMOUS is returned if a token has SecurityAnonymous 6286 * as impersonation level and we cannot open it. A failure NTSTATUS code is returned 6287 * otherwise. 6288 */ 6289 NTSTATUS 6290 NTAPI 6291 NtOpenThreadTokenEx( 6292 _In_ HANDLE ThreadHandle, 6293 _In_ ACCESS_MASK DesiredAccess, 6294 _In_ BOOLEAN OpenAsSelf, 6295 _In_ ULONG HandleAttributes, 6296 _Out_ PHANDLE TokenHandle) 6297 { 6298 PETHREAD Thread; 6299 HANDLE hToken; 6300 PTOKEN Token, NewToken = NULL, PrimaryToken; 6301 BOOLEAN CopyOnOpen, EffectiveOnly; 6302 SECURITY_IMPERSONATION_LEVEL ImpersonationLevel; 6303 SE_IMPERSONATION_STATE ImpersonationState; 6304 OBJECT_ATTRIBUTES ObjectAttributes; 6305 SECURITY_DESCRIPTOR SecurityDescriptor; 6306 PACL Dacl = NULL; 6307 KPROCESSOR_MODE PreviousMode; 6308 NTSTATUS Status; 6309 BOOLEAN RestoreImpersonation = FALSE; 6310 6311 PAGED_CODE(); 6312 6313 PreviousMode = ExGetPreviousMode(); 6314 6315 if (PreviousMode != KernelMode) 6316 { 6317 _SEH2_TRY 6318 { 6319 ProbeForWriteHandle(TokenHandle); 6320 } 6321 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 6322 { 6323 /* Return the exception code */ 6324 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 6325 } 6326 _SEH2_END; 6327 } 6328 6329 /* Validate object attributes */ 6330 HandleAttributes = ObpValidateAttributes(HandleAttributes, PreviousMode); 6331 6332 /* 6333 * At first open the thread token for information access and verify 6334 * that the token associated with thread is valid. 6335 */ 6336 6337 Status = ObReferenceObjectByHandle(ThreadHandle, THREAD_QUERY_INFORMATION, 6338 PsThreadType, PreviousMode, (PVOID*)&Thread, 6339 NULL); 6340 if (!NT_SUCCESS(Status)) 6341 { 6342 return Status; 6343 } 6344 6345 Token = PsReferenceImpersonationToken(Thread, &CopyOnOpen, &EffectiveOnly, 6346 &ImpersonationLevel); 6347 if (Token == NULL) 6348 { 6349 ObDereferenceObject(Thread); 6350 return STATUS_NO_TOKEN; 6351 } 6352 6353 if (ImpersonationLevel == SecurityAnonymous) 6354 { 6355 PsDereferenceImpersonationToken(Token); 6356 ObDereferenceObject(Thread); 6357 return STATUS_CANT_OPEN_ANONYMOUS; 6358 } 6359 6360 /* 6361 * Revert to self if OpenAsSelf is specified. 6362 */ 6363 6364 if (OpenAsSelf) 6365 { 6366 RestoreImpersonation = PsDisableImpersonation(PsGetCurrentThread(), 6367 &ImpersonationState); 6368 } 6369 6370 if (CopyOnOpen) 6371 { 6372 PrimaryToken = PsReferencePrimaryToken(Thread->ThreadsProcess); 6373 6374 Status = SepCreateImpersonationTokenDacl(Token, PrimaryToken, &Dacl); 6375 6376 ObFastDereferenceObject(&Thread->ThreadsProcess->Token, PrimaryToken); 6377 6378 if (NT_SUCCESS(Status)) 6379 { 6380 if (Dacl) 6381 { 6382 Status = RtlCreateSecurityDescriptor(&SecurityDescriptor, 6383 SECURITY_DESCRIPTOR_REVISION); 6384 if (!NT_SUCCESS(Status)) 6385 { 6386 DPRINT1("NtOpenThreadTokenEx(): Failed to create a security descriptor (Status 0x%lx)\n", Status); 6387 } 6388 6389 Status = RtlSetDaclSecurityDescriptor(&SecurityDescriptor, TRUE, Dacl, 6390 FALSE); 6391 if (!NT_SUCCESS(Status)) 6392 { 6393 DPRINT1("NtOpenThreadTokenEx(): Failed to set a DACL to the security descriptor (Status 0x%lx)\n", Status); 6394 } 6395 } 6396 6397 InitializeObjectAttributes(&ObjectAttributes, NULL, HandleAttributes, 6398 NULL, Dacl ? &SecurityDescriptor : NULL); 6399 6400 Status = SepDuplicateToken(Token, &ObjectAttributes, EffectiveOnly, 6401 TokenImpersonation, ImpersonationLevel, 6402 KernelMode, &NewToken); 6403 if (!NT_SUCCESS(Status)) 6404 { 6405 DPRINT1("NtOpenThreadTokenEx(): Failed to duplicate the token (Status 0x%lx)\n", Status); 6406 } 6407 6408 ObReferenceObject(NewToken); 6409 Status = ObInsertObject(NewToken, NULL, DesiredAccess, 0, NULL, 6410 &hToken); 6411 if (!NT_SUCCESS(Status)) 6412 { 6413 DPRINT1("NtOpenThreadTokenEx(): Failed to insert the token object (Status 0x%lx)\n", Status); 6414 } 6415 } 6416 else 6417 { 6418 DPRINT1("NtOpenThreadTokenEx(): Failed to impersonate token from DACL (Status 0x%lx)\n", Status); 6419 } 6420 } 6421 else 6422 { 6423 Status = ObOpenObjectByPointer(Token, HandleAttributes, 6424 NULL, DesiredAccess, SeTokenObjectType, 6425 PreviousMode, &hToken); 6426 if (!NT_SUCCESS(Status)) 6427 { 6428 DPRINT1("NtOpenThreadTokenEx(): Failed to open the object (Status 0x%lx)\n", Status); 6429 } 6430 } 6431 6432 if (Dacl) ExFreePoolWithTag(Dacl, TAG_ACL); 6433 6434 if (RestoreImpersonation) 6435 { 6436 PsRestoreImpersonation(PsGetCurrentThread(), &ImpersonationState); 6437 } 6438 6439 ObDereferenceObject(Token); 6440 6441 if (NT_SUCCESS(Status) && CopyOnOpen) 6442 { 6443 Status = PsImpersonateClient(Thread, NewToken, FALSE, EffectiveOnly, ImpersonationLevel); 6444 if (!NT_SUCCESS(Status)) 6445 { 6446 DPRINT1("NtOpenThreadTokenEx(): Failed to impersonate the client (Status 0x%lx)\n", Status); 6447 } 6448 } 6449 6450 if (NewToken) ObDereferenceObject(NewToken); 6451 6452 ObDereferenceObject(Thread); 6453 6454 if (NT_SUCCESS(Status)) 6455 { 6456 _SEH2_TRY 6457 { 6458 *TokenHandle = hToken; 6459 } 6460 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 6461 { 6462 Status = _SEH2_GetExceptionCode(); 6463 } 6464 _SEH2_END; 6465 } 6466 6467 return Status; 6468 } 6469 6470 /** 6471 * @brief 6472 * Opens a token that is tied to a thread handle. 6473 * 6474 * @param[out] ThreadHandle 6475 * Thread handle where the token is about to be opened. 6476 * 6477 * @param[in] DesiredAccess 6478 * The request access right for the token. 6479 * 6480 * @param[in] OpenAsSelf 6481 * If set to TRUE, the access check will be made with the security context 6482 * of the process of the calling thread (opening as self). Otherwise the access 6483 * check will be made with the security context of the calling thread instead. 6484 * 6485 * @param[out] TokenHandle 6486 * The opened token handle returned to the caller for use. 6487 * 6488 * @return 6489 * See NtOpenThreadTokenEx. 6490 */ 6491 NTSTATUS 6492 NTAPI 6493 NtOpenThreadToken( 6494 _In_ HANDLE ThreadHandle, 6495 _In_ ACCESS_MASK DesiredAccess, 6496 _In_ BOOLEAN OpenAsSelf, 6497 _Out_ PHANDLE TokenHandle) 6498 { 6499 return NtOpenThreadTokenEx(ThreadHandle, DesiredAccess, OpenAsSelf, 0, 6500 TokenHandle); 6501 } 6502 6503 /** 6504 * @brief 6505 * Compares tokens if they're equal or not. 6506 * 6507 * @param[in] FirstToken 6508 * The first token. 6509 * 6510 * @param[in] SecondToken 6511 * The second token. 6512 * 6513 * @param[out] Equal 6514 * The retrieved value which determines if the tokens are 6515 * equal or not. 6516 * 6517 * @return 6518 * Returns STATUS_SUCCESS, otherwise it returns a failure NTSTATUS code. 6519 */ 6520 NTSTATUS 6521 NTAPI 6522 NtCompareTokens( 6523 _In_ HANDLE FirstTokenHandle, 6524 _In_ HANDLE SecondTokenHandle, 6525 _Out_ PBOOLEAN Equal) 6526 { 6527 KPROCESSOR_MODE PreviousMode; 6528 PTOKEN FirstToken, SecondToken; 6529 BOOLEAN IsEqual; 6530 NTSTATUS Status; 6531 6532 PAGED_CODE(); 6533 6534 PreviousMode = ExGetPreviousMode(); 6535 6536 if (PreviousMode != KernelMode) 6537 { 6538 _SEH2_TRY 6539 { 6540 ProbeForWriteBoolean(Equal); 6541 } 6542 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 6543 { 6544 /* Return the exception code */ 6545 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 6546 } 6547 _SEH2_END; 6548 } 6549 6550 Status = ObReferenceObjectByHandle(FirstTokenHandle, 6551 TOKEN_QUERY, 6552 SeTokenObjectType, 6553 PreviousMode, 6554 (PVOID*)&FirstToken, 6555 NULL); 6556 if (!NT_SUCCESS(Status)) 6557 { 6558 DPRINT1("ObReferenceObjectByHandle() failed (Status 0x%lx)\n", Status); 6559 return Status; 6560 } 6561 6562 Status = ObReferenceObjectByHandle(SecondTokenHandle, 6563 TOKEN_QUERY, 6564 SeTokenObjectType, 6565 PreviousMode, 6566 (PVOID*)&SecondToken, 6567 NULL); 6568 if (!NT_SUCCESS(Status)) 6569 { 6570 DPRINT1("ObReferenceObjectByHandle() failed (Status 0x%lx)\n", Status); 6571 ObDereferenceObject(FirstToken); 6572 return Status; 6573 } 6574 6575 if (FirstToken != SecondToken) 6576 { 6577 Status = SepCompareTokens(FirstToken, 6578 SecondToken, 6579 &IsEqual); 6580 } 6581 else 6582 { 6583 IsEqual = TRUE; 6584 } 6585 6586 ObDereferenceObject(SecondToken); 6587 ObDereferenceObject(FirstToken); 6588 6589 if (NT_SUCCESS(Status)) 6590 { 6591 _SEH2_TRY 6592 { 6593 *Equal = IsEqual; 6594 } 6595 _SEH2_EXCEPT(ExSystemExceptionFilter()) 6596 { 6597 Status = _SEH2_GetExceptionCode(); 6598 } 6599 _SEH2_END; 6600 } 6601 6602 return Status; 6603 } 6604 6605 /** 6606 * @brief 6607 * Creates an access token in a restricted form 6608 * from the original existing token, that is, such 6609 * action is called filtering. 6610 * 6611 * @param[in] ExistingTokenHandle 6612 * A handle to an access token which is to be filtered. 6613 * 6614 * @param[in] Flags 6615 * Privilege flag options. This parameter argument influences how the 6616 * token's privileges are filtered. For further details see remarks. 6617 * 6618 * @param[in] SidsToDisable 6619 * Array of SIDs to disable. The action of doing so assigns the 6620 * SE_GROUP_USE_FOR_DENY_ONLY attribute to the respective group 6621 * SID and takes away SE_GROUP_ENABLED and SE_GROUP_ENABLED_BY_DEFAULT. 6622 * This parameter can be NULL. This can be a UM pointer. 6623 * 6624 * @param[in] PrivilegesToDelete 6625 * Array of privileges to delete. The function will walk within this 6626 * array to determine if the specified privileges do exist in the 6627 * access token. Any missing privileges gets ignored. This parameter 6628 * can be NULL. This can be a UM pointer. 6629 * 6630 * @param[in] RestrictedSids 6631 * An array list of restricted groups SID to be added in the access 6632 * token. A token that is already restricted the newly added restricted 6633 * SIDs are redundant information in addition to the existing restricted 6634 * SIDs in the token. This parameter can be NULL. This can be a UM pointer. 6635 * 6636 * @param[out] NewTokenHandle 6637 * A new handle to the restricted (filtered) access token. This can be a 6638 * UM pointer. 6639 * 6640 * @return 6641 * Returns STATUS_SUCCESS if the routine has successfully filtered the 6642 * access token. STATUS_INVALID_PARAMETER is returned if one or more 6643 * parameters are not valid (see SepPerformTokenFiltering routine call 6644 * for more information). A failure NTSTATUS code is returned otherwise. 6645 * 6646 * @remarks 6647 * The Flags parameter determines the final outcome of how the privileges 6648 * in an access token are filtered. This parameter can take these supported 6649 * values (these can be combined): 6650 * 6651 * 0 -- Filter the token's privileges in the usual way. The function expects 6652 * that the caller MUST PROVIDE a valid array list of privileges to be 6653 * deleted (that is, PrivilegesToDelete MUSTN'T BE NULL). 6654 * 6655 * DISABLE_MAX_PRIVILEGE -- Disables (deletes) all the privileges except SeChangeNotifyPrivilege 6656 * in the new access token. Bear in mind if this flag is specified 6657 * the routine ignores PrivilegesToDelete. 6658 * 6659 * SANDBOX_INERT -- Stores the TOKEN_SANDBOX_INERT token flag within the access token. 6660 * 6661 * LUA_TOKEN -- The newly filtered access token is a LUA token. This flag is not 6662 * supported in Windows Server 2003. 6663 * 6664 * WRITE_RESTRICTED -- The newly filtered token has the restricted SIDs that are 6665 * considered only when evaluating write access onto the token. 6666 * This value is not supported in Windows Server 2003. 6667 */ 6668 NTSTATUS 6669 NTAPI 6670 NtFilterToken( 6671 _In_ HANDLE ExistingTokenHandle, 6672 _In_ ULONG Flags, 6673 _In_opt_ PTOKEN_GROUPS SidsToDisable, 6674 _In_opt_ PTOKEN_PRIVILEGES PrivilegesToDelete, 6675 _In_opt_ PTOKEN_GROUPS RestrictedSids, 6676 _Out_ PHANDLE NewTokenHandle) 6677 { 6678 PTOKEN Token, FilteredToken; 6679 HANDLE FilteredTokenHandle; 6680 NTSTATUS Status; 6681 KPROCESSOR_MODE PreviousMode; 6682 OBJECT_HANDLE_INFORMATION HandleInfo; 6683 ULONG ResultLength; 6684 ULONG CapturedSidsCount = 0; 6685 ULONG CapturedPrivilegesCount = 0; 6686 ULONG CapturedRestrictedSidsCount = 0; 6687 ULONG ProbeSize = 0; 6688 PSID_AND_ATTRIBUTES CapturedSids = NULL; 6689 PSID_AND_ATTRIBUTES CapturedRestrictedSids = NULL; 6690 PLUID_AND_ATTRIBUTES CapturedPrivileges = NULL; 6691 6692 PAGED_CODE(); 6693 6694 PreviousMode = ExGetPreviousMode(); 6695 6696 _SEH2_TRY 6697 { 6698 /* Probe SidsToDisable */ 6699 if (SidsToDisable != NULL) 6700 { 6701 /* Probe the header */ 6702 ProbeForRead(SidsToDisable, sizeof(*SidsToDisable), sizeof(ULONG)); 6703 6704 CapturedSidsCount = SidsToDisable->GroupCount; 6705 ProbeSize = FIELD_OFFSET(TOKEN_GROUPS, Groups[CapturedSidsCount]); 6706 6707 ProbeForRead(SidsToDisable, ProbeSize, sizeof(ULONG)); 6708 } 6709 6710 /* Probe PrivilegesToDelete */ 6711 if (PrivilegesToDelete != NULL) 6712 { 6713 /* Probe the header */ 6714 ProbeForRead(PrivilegesToDelete, sizeof(*PrivilegesToDelete), sizeof(ULONG)); 6715 6716 CapturedPrivilegesCount = PrivilegesToDelete->PrivilegeCount; 6717 ProbeSize = FIELD_OFFSET(TOKEN_PRIVILEGES, Privileges[CapturedPrivilegesCount]); 6718 6719 ProbeForRead(PrivilegesToDelete, ProbeSize, sizeof(ULONG)); 6720 } 6721 6722 /* Probe RestrictedSids */ 6723 if (RestrictedSids != NULL) 6724 { 6725 /* Probe the header */ 6726 ProbeForRead(RestrictedSids, sizeof(*RestrictedSids), sizeof(ULONG)); 6727 6728 CapturedRestrictedSidsCount = RestrictedSids->GroupCount; 6729 ProbeSize = FIELD_OFFSET(TOKEN_GROUPS, Groups[CapturedRestrictedSidsCount]); 6730 6731 ProbeForRead(RestrictedSids, ProbeSize, sizeof(ULONG)); 6732 } 6733 6734 /* Probe the handle */ 6735 ProbeForWriteHandle(NewTokenHandle); 6736 } 6737 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 6738 { 6739 /* Return the exception code */ 6740 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 6741 } 6742 _SEH2_END; 6743 6744 /* Reference the token and do the job */ 6745 Status = ObReferenceObjectByHandle(ExistingTokenHandle, 6746 TOKEN_DUPLICATE, 6747 SeTokenObjectType, 6748 PreviousMode, 6749 (PVOID*)&Token, 6750 &HandleInfo); 6751 if (!NT_SUCCESS(Status)) 6752 { 6753 DPRINT1("NtFilterToken(): Failed to reference the token (Status 0x%lx)\n", Status); 6754 return Status; 6755 } 6756 6757 /* Lock the token */ 6758 SepAcquireTokenLockExclusive(Token); 6759 6760 /* Capture the group SIDs */ 6761 if (SidsToDisable != NULL) 6762 { 6763 Status = SeCaptureSidAndAttributesArray(SidsToDisable->Groups, 6764 CapturedSidsCount, 6765 PreviousMode, 6766 NULL, 6767 0, 6768 PagedPool, 6769 TRUE, 6770 &CapturedSids, 6771 &ResultLength); 6772 if (!NT_SUCCESS(Status)) 6773 { 6774 DPRINT1("NtFilterToken(): Failed to capture the SIDs (Status 0x%lx)\n", Status); 6775 goto Quit; 6776 } 6777 } 6778 6779 /* Capture the privileges */ 6780 if (PrivilegesToDelete != NULL) 6781 { 6782 Status = SeCaptureLuidAndAttributesArray(PrivilegesToDelete->Privileges, 6783 CapturedPrivilegesCount, 6784 PreviousMode, 6785 NULL, 6786 0, 6787 PagedPool, 6788 TRUE, 6789 &CapturedPrivileges, 6790 &ResultLength); 6791 if (!NT_SUCCESS(Status)) 6792 { 6793 DPRINT1("NtFilterToken(): Failed to capture the privileges (Status 0x%lx)\n", Status); 6794 goto Quit; 6795 } 6796 } 6797 6798 /* Capture the restricted SIDs */ 6799 if (RestrictedSids != NULL) 6800 { 6801 Status = SeCaptureSidAndAttributesArray(RestrictedSids->Groups, 6802 CapturedRestrictedSidsCount, 6803 PreviousMode, 6804 NULL, 6805 0, 6806 PagedPool, 6807 TRUE, 6808 &CapturedRestrictedSids, 6809 &ResultLength); 6810 if (!NT_SUCCESS(Status)) 6811 { 6812 DPRINT1("NtFilterToken(): Failed to capture the restricted SIDs (Status 0x%lx)\n", Status); 6813 goto Quit; 6814 } 6815 } 6816 6817 /* Call the internal API so that it can filter the token for us */ 6818 Status = SepPerformTokenFiltering(Token, 6819 CapturedPrivileges, 6820 CapturedSids, 6821 CapturedRestrictedSids, 6822 CapturedPrivilegesCount, 6823 CapturedSidsCount, 6824 CapturedRestrictedSidsCount, 6825 Flags, 6826 PreviousMode, 6827 &FilteredToken); 6828 if (!NT_SUCCESS(Status)) 6829 { 6830 DPRINT1("NtFilterToken(): Failed to filter the token (Status 0x%lx)\n", Status); 6831 goto Quit; 6832 } 6833 6834 /* We got our filtered token, insert it to the handle */ 6835 Status = ObInsertObject(FilteredToken, 6836 NULL, 6837 HandleInfo.GrantedAccess, 6838 0, 6839 NULL, 6840 &FilteredTokenHandle); 6841 if (!NT_SUCCESS(Status)) 6842 { 6843 DPRINT1("NtFilterToken(): Failed to insert the filtered token object into the handle (Status 0x%lx)\n", Status); 6844 goto Quit; 6845 } 6846 6847 /* And give it to the caller once we're done */ 6848 _SEH2_TRY 6849 { 6850 *NewTokenHandle = FilteredTokenHandle; 6851 } 6852 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 6853 { 6854 Status = _SEH2_GetExceptionCode(); 6855 _SEH2_YIELD(goto Quit); 6856 } 6857 _SEH2_END; 6858 6859 Quit: 6860 /* Unlock and dereference the token */ 6861 SepReleaseTokenLock(Token); 6862 ObDereferenceObject(Token); 6863 6864 /* Release all the stuff we've captured */ 6865 if (CapturedSids != NULL) 6866 { 6867 SeReleaseSidAndAttributesArray(CapturedSids, 6868 PreviousMode, 6869 TRUE); 6870 CapturedSids = NULL; 6871 } 6872 6873 if (CapturedPrivileges != NULL) 6874 { 6875 SeReleaseLuidAndAttributesArray(CapturedPrivileges, 6876 PreviousMode, 6877 TRUE); 6878 CapturedPrivileges = NULL; 6879 } 6880 6881 if (CapturedRestrictedSids != NULL) 6882 { 6883 SeReleaseSidAndAttributesArray(CapturedRestrictedSids, 6884 PreviousMode, 6885 TRUE); 6886 CapturedRestrictedSids = NULL; 6887 } 6888 6889 return Status; 6890 } 6891 6892 /** 6893 * @brief 6894 * Allows the calling thread to impersonate the system's anonymous 6895 * logon token. 6896 * 6897 * @param[in] ThreadHandle 6898 * A handle to the thread to start the procedure of logon token 6899 * impersonation. The thread must have the THREAD_IMPERSONATE 6900 * access right. 6901 * 6902 * @return 6903 * Returns STATUS_SUCCESS if the thread has successfully impersonated the 6904 * anonymous logon token, otherwise a failure NTSTATUS code is returned. 6905 * 6906 * @remarks 6907 * By default the system gives the opportunity to the caller to impersonate 6908 * the anonymous logon token without including the Everyone Group SID. 6909 * In cases where the caller wants to impersonate the token including such 6910 * group, the EveryoneIncludesAnonymous registry value setting has to be set 6911 * to 1, from HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa registry 6912 * path. The calling thread must invoke PsRevertToSelf when impersonation 6913 * is no longer needed or RevertToSelf if the calling execution is done 6914 * in user mode. 6915 */ 6916 NTSTATUS 6917 NTAPI 6918 NtImpersonateAnonymousToken( 6919 _In_ HANDLE ThreadHandle) 6920 { 6921 PETHREAD Thread; 6922 KPROCESSOR_MODE PreviousMode; 6923 NTSTATUS Status; 6924 PAGED_CODE(); 6925 6926 PreviousMode = ExGetPreviousMode(); 6927 6928 /* Obtain the thread object from the handle */ 6929 Status = ObReferenceObjectByHandle(ThreadHandle, 6930 THREAD_IMPERSONATE, 6931 PsThreadType, 6932 PreviousMode, 6933 (PVOID*)&Thread, 6934 NULL); 6935 if (!NT_SUCCESS(Status)) 6936 { 6937 DPRINT1("NtImpersonateAnonymousToken(): Failed to reference the object (Status 0x%lx)\n", Status); 6938 return Status; 6939 } 6940 6941 /* Call the private routine to impersonate the token */ 6942 Status = SepImpersonateAnonymousToken(Thread, PreviousMode); 6943 if (!NT_SUCCESS(Status)) 6944 { 6945 DPRINT1("NtImpersonateAnonymousToken(): Failed to impersonate the token (Status 0x%lx)\n", Status); 6946 } 6947 6948 ObDereferenceObject(Thread); 6949 return Status; 6950 } 6951 6952 /* EOF */ 6953