1 /* 2 * PROJECT: ReactOS Kernel 3 * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) 4 * PURPOSE: Access token lifetime management implementation (Creation/Duplication/Filtering) 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 /* DEFINES ********************************************************************/ 16 17 #define SE_TOKEN_DYNAMIC_SLIM 500 18 19 /* PRIVATE FUNCTIONS *********************************************************/ 20 21 /** 22 * @brief 23 * Internal function responsible for access token object creation in the kernel. 24 * A fully created token objected is inserted into the token handle, thus the handle 25 * becoming a valid handle to an access token object and ready for use. 26 * 27 * @param[out] TokenHandle 28 * Valid token handle that's ready for use after token creation and object insertion. 29 * 30 * @param[in] PreviousMode 31 * Processor request level mode. 32 * 33 * @param[in] DesiredAccess 34 * Desired access right for the token object to be granted. This kind of access right 35 * impacts how the token can be used and who. 36 * 37 * @param[in] ObjectAttributes 38 * Object attributes for the token to be created. 39 * 40 * @param[in] TokenType 41 * Type of token to assign upon creation. 42 * 43 * @param[in] ImpersonationLevel 44 * Security impersonation level of token to assign upon creation. 45 * 46 * @param[in] AuthenticationId 47 * Authentication ID that represents the authentication information of the token. 48 * 49 * @param[in] ExpirationTime 50 * Expiration time of the token to assign. A value of -1 means that the token never 51 * expires and its life depends upon the amount of references this token object has. 52 * 53 * @param[in] User 54 * User entry to assign to the token. 55 * 56 * @param[in] GroupCount 57 * The total number of groups count for the token. 58 * 59 * @param[in] Groups 60 * The group entries for the token. 61 * 62 * @param[in] GroupsLength 63 * The length size of the groups array, pointed by the Groups parameter. 64 * 65 * @param[in] PrivilegeCount 66 * The total number of priivleges that the newly created token has. 67 * 68 * @param[in] Privileges 69 * The privileges for the token. 70 * 71 * @param[in] Owner 72 * The main user (or also owner) that represents the token that we create. 73 * 74 * @param[in] PrimaryGroup 75 * The main group that represents the token that we create. 76 * 77 * @param[in] DefaultDacl 78 * A discretionary access control list for the token. 79 * 80 * @param[in] TokenSource 81 * Source (or the origin) of the access token that creates it. 82 * 83 * @param[in] SystemToken 84 * If set to TRUE, the newly created token is a system token and only in charge 85 * by the internal system. The function directly returns a pointer to the 86 * created token object for system kernel use. Otherwise if set to FALSE, the 87 * function inserts the object to a handle making it a regular access token. 88 * 89 * @return 90 * Returns STATUS_SUCCESS if token creation has completed successfully. 91 * STATUS_INSUFFICIENT_RESOURCES is returned if the dynamic area of memory of the 92 * token hasn't been allocated because of lack of memory resources. A failure 93 * NTSTATUS code is returned otherwise. 94 */ 95 NTSTATUS 96 NTAPI 97 SepCreateToken( 98 _Out_ PHANDLE TokenHandle, 99 _In_ KPROCESSOR_MODE PreviousMode, 100 _In_ ACCESS_MASK DesiredAccess, 101 _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes, 102 _In_ TOKEN_TYPE TokenType, 103 _In_ SECURITY_IMPERSONATION_LEVEL ImpersonationLevel, 104 _In_ PLUID AuthenticationId, 105 _In_ PLARGE_INTEGER ExpirationTime, 106 _In_ PSID_AND_ATTRIBUTES User, 107 _In_ ULONG GroupCount, 108 _In_ PSID_AND_ATTRIBUTES Groups, 109 _In_ ULONG GroupsLength, 110 _In_ ULONG PrivilegeCount, 111 _In_ PLUID_AND_ATTRIBUTES Privileges, 112 _In_opt_ PSID Owner, 113 _In_ PSID PrimaryGroup, 114 _In_opt_ PACL DefaultDacl, 115 _In_ PTOKEN_SOURCE TokenSource, 116 _In_ BOOLEAN SystemToken) 117 { 118 NTSTATUS Status; 119 PTOKEN AccessToken; 120 ULONG TokenFlags = 0; 121 ULONG PrimaryGroupIndex, DefaultOwnerIndex; 122 LUID TokenId; 123 LUID ModifiedId; 124 PVOID EndMem; 125 ULONG PrivilegesLength; 126 ULONG UserGroupsLength; 127 ULONG VariableLength; 128 ULONG DynamicPartSize, TotalSize; 129 ULONG TokenPagedCharges; 130 ULONG i; 131 132 PAGED_CODE(); 133 134 /* Loop all groups */ 135 for (i = 0; i < GroupCount; i++) 136 { 137 /* Check for mandatory groups */ 138 if (Groups[i].Attributes & SE_GROUP_MANDATORY) 139 { 140 /* Force them to be enabled */ 141 Groups[i].Attributes |= (SE_GROUP_ENABLED | SE_GROUP_ENABLED_BY_DEFAULT); 142 } 143 144 /* Check of the group is an admin group */ 145 if (RtlEqualSid(SeAliasAdminsSid, Groups[i].Sid)) 146 { 147 /* Remember this so we can optimize queries later */ 148 TokenFlags |= TOKEN_HAS_ADMIN_GROUP; 149 } 150 } 151 152 /* Allocate unique IDs for the token */ 153 ExAllocateLocallyUniqueId(&TokenId); 154 ExAllocateLocallyUniqueId(&ModifiedId); 155 156 /* Compute how much size we need to allocate for the token */ 157 158 /* Privileges size */ 159 PrivilegesLength = PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES); 160 PrivilegesLength = ALIGN_UP_BY(PrivilegesLength, sizeof(PVOID)); 161 162 /* User and groups size */ 163 UserGroupsLength = (1 + GroupCount) * sizeof(SID_AND_ATTRIBUTES); 164 UserGroupsLength += RtlLengthSid(User->Sid); 165 for (i = 0; i < GroupCount; i++) 166 { 167 UserGroupsLength += RtlLengthSid(Groups[i].Sid); 168 } 169 UserGroupsLength = ALIGN_UP_BY(UserGroupsLength, sizeof(PVOID)); 170 171 /* Add the additional groups array length */ 172 UserGroupsLength += ALIGN_UP_BY(GroupsLength, sizeof(PVOID)); 173 174 VariableLength = PrivilegesLength + UserGroupsLength; 175 TotalSize = FIELD_OFFSET(TOKEN, VariablePart) + VariableLength; 176 177 /* 178 * A token is considered slim if it has the default dynamic 179 * contents, or in other words, the primary group and ACL. 180 * We judge if such contents are default by checking their 181 * total size if it's over the range. On Windows this range 182 * is 0x1F4 (aka 500). If the size of the whole dynamic contents 183 * is over that range then the token is considered fat and 184 * the token will be charged the whole of its token body length 185 * plus the dynamic size. 186 */ 187 DynamicPartSize = DefaultDacl ? DefaultDacl->AclSize : 0; 188 DynamicPartSize += RtlLengthSid(PrimaryGroup); 189 if (DynamicPartSize > SE_TOKEN_DYNAMIC_SLIM) 190 { 191 TokenPagedCharges = DynamicPartSize + TotalSize; 192 } 193 else 194 { 195 TokenPagedCharges = SE_TOKEN_DYNAMIC_SLIM + TotalSize; 196 } 197 198 Status = ObCreateObject(PreviousMode, 199 SeTokenObjectType, 200 ObjectAttributes, 201 PreviousMode, 202 NULL, 203 TotalSize, 204 TokenPagedCharges, 205 0, 206 (PVOID*)&AccessToken); 207 if (!NT_SUCCESS(Status)) 208 { 209 DPRINT1("ObCreateObject() failed (Status 0x%lx)\n", Status); 210 return Status; 211 } 212 213 /* Zero out the buffer and initialize the token */ 214 RtlZeroMemory(AccessToken, TotalSize); 215 216 AccessToken->TokenId = TokenId; 217 AccessToken->TokenType = TokenType; 218 AccessToken->ImpersonationLevel = ImpersonationLevel; 219 220 /* Initialise the lock for the access token */ 221 Status = SepCreateTokenLock(AccessToken); 222 if (!NT_SUCCESS(Status)) 223 goto Quit; 224 225 AccessToken->TokenSource.SourceIdentifier = TokenSource->SourceIdentifier; 226 RtlCopyMemory(AccessToken->TokenSource.SourceName, 227 TokenSource->SourceName, 228 sizeof(TokenSource->SourceName)); 229 230 AccessToken->ExpirationTime = *ExpirationTime; 231 AccessToken->ModifiedId = ModifiedId; 232 AccessToken->DynamicCharged = TokenPagedCharges - TotalSize; 233 234 AccessToken->TokenFlags = TokenFlags & ~TOKEN_SESSION_NOT_REFERENCED; 235 236 /* Copy and reference the logon session */ 237 AccessToken->AuthenticationId = *AuthenticationId; 238 Status = SepRmReferenceLogonSession(&AccessToken->AuthenticationId); 239 if (!NT_SUCCESS(Status)) 240 { 241 /* No logon session could be found, bail out */ 242 DPRINT1("SepRmReferenceLogonSession() failed (Status 0x%lx)\n", Status); 243 /* Set the flag for proper cleanup by the delete procedure */ 244 AccessToken->TokenFlags |= TOKEN_SESSION_NOT_REFERENCED; 245 goto Quit; 246 } 247 248 /* Insert the referenced logon session into the token */ 249 Status = SepRmInsertLogonSessionIntoToken(AccessToken); 250 if (!NT_SUCCESS(Status)) 251 { 252 /* Failed to insert the logon session into the token, bail out */ 253 DPRINT1("SepRmInsertLogonSessionIntoToken() failed (Status 0x%lx)\n", Status); 254 goto Quit; 255 } 256 257 /* Fill in token debug information */ 258 #if DBG 259 /* 260 * We must determine ourselves that the current 261 * process is not the initial CPU one. The initial 262 * process is not a "real" process, that is, the 263 * Process Manager has not yet been initialized and 264 * as a matter of fact we are creating a token before 265 * any process gets created by Ps. If it turns out 266 * that the current process is the initial CPU process 267 * where token creation execution takes place, don't 268 * do anything. 269 */ 270 if (PsGetCurrentProcess() != &KiInitialProcess) 271 { 272 RtlCopyMemory(AccessToken->ImageFileName, 273 PsGetCurrentProcess()->ImageFileName, 274 min(sizeof(AccessToken->ImageFileName), sizeof(PsGetCurrentProcess()->ImageFileName))); 275 276 AccessToken->ProcessCid = PsGetCurrentProcessId(); 277 AccessToken->ThreadCid = PsGetCurrentThreadId(); 278 } 279 280 AccessToken->CreateMethod = TOKEN_CREATE_METHOD; 281 #endif 282 283 /* Assign the data that reside in the token's variable information area */ 284 AccessToken->VariableLength = VariableLength; 285 EndMem = (PVOID)&AccessToken->VariablePart; 286 287 /* Copy the privileges */ 288 AccessToken->PrivilegeCount = PrivilegeCount; 289 AccessToken->Privileges = NULL; 290 if (PrivilegeCount > 0) 291 { 292 AccessToken->Privileges = EndMem; 293 EndMem = (PVOID)((ULONG_PTR)EndMem + PrivilegesLength); 294 VariableLength -= PrivilegesLength; 295 296 if (PreviousMode != KernelMode) 297 { 298 _SEH2_TRY 299 { 300 RtlCopyMemory(AccessToken->Privileges, 301 Privileges, 302 PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES)); 303 } 304 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 305 { 306 Status = _SEH2_GetExceptionCode(); 307 } 308 _SEH2_END; 309 } 310 else 311 { 312 RtlCopyMemory(AccessToken->Privileges, 313 Privileges, 314 PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES)); 315 } 316 317 if (!NT_SUCCESS(Status)) 318 goto Quit; 319 } 320 321 /* Update the privilege flags */ 322 SepUpdatePrivilegeFlagsToken(AccessToken); 323 324 /* Copy the user and groups */ 325 AccessToken->UserAndGroupCount = 1 + GroupCount; 326 AccessToken->UserAndGroups = EndMem; 327 EndMem = &AccessToken->UserAndGroups[AccessToken->UserAndGroupCount]; 328 VariableLength -= ((ULONG_PTR)EndMem - (ULONG_PTR)AccessToken->UserAndGroups); 329 330 Status = RtlCopySidAndAttributesArray(1, 331 User, 332 VariableLength, 333 &AccessToken->UserAndGroups[0], 334 EndMem, 335 &EndMem, 336 &VariableLength); 337 if (!NT_SUCCESS(Status)) 338 goto Quit; 339 340 Status = RtlCopySidAndAttributesArray(GroupCount, 341 Groups, 342 VariableLength, 343 &AccessToken->UserAndGroups[1], 344 EndMem, 345 &EndMem, 346 &VariableLength); 347 if (!NT_SUCCESS(Status)) 348 goto Quit; 349 350 /* Find the token primary group and default owner */ 351 Status = SepFindPrimaryGroupAndDefaultOwner(AccessToken, 352 PrimaryGroup, 353 Owner, 354 &PrimaryGroupIndex, 355 &DefaultOwnerIndex); 356 if (!NT_SUCCESS(Status)) 357 { 358 DPRINT1("SepFindPrimaryGroupAndDefaultOwner failed (Status 0x%lx)\n", Status); 359 goto Quit; 360 } 361 362 /* 363 * Now allocate the token's dynamic information area 364 * and set the data. The dynamic part consists of two 365 * contents, the primary group SID and the default DACL 366 * of the token, in this strict order. 367 */ 368 AccessToken->DynamicPart = ExAllocatePoolWithTag(PagedPool, 369 DynamicPartSize, 370 TAG_TOKEN_DYNAMIC); 371 if (AccessToken->DynamicPart == NULL) 372 { 373 Status = STATUS_INSUFFICIENT_RESOURCES; 374 goto Quit; 375 } 376 377 /* Unused memory in the dynamic area */ 378 AccessToken->DynamicAvailable = 0; 379 380 /* 381 * Assign the primary group to the token 382 * and put it in the dynamic part as well. 383 */ 384 EndMem = (PVOID)AccessToken->DynamicPart; 385 AccessToken->PrimaryGroup = EndMem; 386 RtlCopySid(RtlLengthSid(AccessToken->UserAndGroups[PrimaryGroupIndex].Sid), 387 EndMem, 388 AccessToken->UserAndGroups[PrimaryGroupIndex].Sid); 389 AccessToken->DefaultOwnerIndex = DefaultOwnerIndex; 390 EndMem = (PVOID)((ULONG_PTR)EndMem + RtlLengthSid(AccessToken->UserAndGroups[PrimaryGroupIndex].Sid)); 391 392 /* 393 * We have assigned a primary group and put it in the 394 * dynamic part, now it's time to copy the provided 395 * default DACL (if it's provided to begin with) into 396 * the DACL field of the token and put it at the end 397 * tail of the dynamic part too. 398 */ 399 if (DefaultDacl != NULL) 400 { 401 AccessToken->DefaultDacl = EndMem; 402 403 RtlCopyMemory(EndMem, 404 DefaultDacl, 405 DefaultDacl->AclSize); 406 } 407 408 /* Insert the token only if it's not the system token, otherwise return it directly */ 409 if (!SystemToken) 410 { 411 Status = ObInsertObject(AccessToken, 412 NULL, 413 DesiredAccess, 414 0, 415 NULL, 416 TokenHandle); 417 if (!NT_SUCCESS(Status)) 418 { 419 DPRINT1("ObInsertObject() failed (Status 0x%lx)\n", Status); 420 } 421 } 422 else 423 { 424 /* Return pointer instead of handle */ 425 *TokenHandle = (HANDLE)AccessToken; 426 } 427 428 Quit: 429 if (!NT_SUCCESS(Status)) 430 { 431 /* Dereference the token, the delete procedure will clean it up */ 432 ObDereferenceObject(AccessToken); 433 } 434 435 return Status; 436 } 437 438 /** 439 * @brief 440 * Duplicates an access token, from an existing valid token. 441 * 442 * @param[in] Token 443 * Access token to duplicate. 444 * 445 * @param[in] ObjectAttributes 446 * Object attributes for the new token. 447 * 448 * @param[in] EffectiveOnly 449 * If set to TRUE, the function removes all the disabled privileges and groups of the token 450 * to duplicate. 451 * 452 * @param[in] TokenType 453 * Type of token. 454 * 455 * @param[in] Level 456 * Security impersonation level of a token. 457 * 458 * @param[in] PreviousMode 459 * The processor request level mode. 460 * 461 * @param[out] NewAccessToken 462 * The duplicated token. 463 * 464 * @return 465 * Returns STATUS_SUCCESS if the token has been duplicated. STATUS_INSUFFICIENT_RESOURCES is returned 466 * if memory pool allocation of the dynamic part of the token for duplication has failed due to the lack 467 * of memory resources. A failure NTSTATUS code is returned otherwise. 468 */ 469 NTSTATUS 470 NTAPI 471 SepDuplicateToken( 472 _In_ PTOKEN Token, 473 _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes, 474 _In_ BOOLEAN EffectiveOnly, 475 _In_ TOKEN_TYPE TokenType, 476 _In_ SECURITY_IMPERSONATION_LEVEL Level, 477 _In_ KPROCESSOR_MODE PreviousMode, 478 _Out_ PTOKEN* NewAccessToken) 479 { 480 NTSTATUS Status; 481 PTOKEN AccessToken; 482 PVOID EndMem; 483 ULONG PrimaryGroupIndex; 484 ULONG VariableLength; 485 ULONG DynamicPartSize, TotalSize; 486 ULONG PrivilegesIndex, GroupsIndex; 487 488 PAGED_CODE(); 489 490 /* Compute how much size we need to allocate for the token */ 491 VariableLength = Token->VariableLength; 492 TotalSize = FIELD_OFFSET(TOKEN, VariablePart) + VariableLength; 493 494 /* 495 * Compute how much size we need to allocate 496 * the dynamic part of the newly duplicated 497 * token. 498 */ 499 DynamicPartSize = Token->DefaultDacl ? Token->DefaultDacl->AclSize : 0; 500 DynamicPartSize += RtlLengthSid(Token->PrimaryGroup); 501 502 Status = ObCreateObject(PreviousMode, 503 SeTokenObjectType, 504 ObjectAttributes, 505 PreviousMode, 506 NULL, 507 TotalSize, 508 Token->DynamicCharged, 509 TotalSize, 510 (PVOID*)&AccessToken); 511 if (!NT_SUCCESS(Status)) 512 { 513 DPRINT1("ObCreateObject() failed (Status 0x%lx)\n", Status); 514 return Status; 515 } 516 517 /* Zero out the buffer and initialize the token */ 518 RtlZeroMemory(AccessToken, TotalSize); 519 520 ExAllocateLocallyUniqueId(&AccessToken->TokenId); 521 522 AccessToken->TokenType = TokenType; 523 AccessToken->ImpersonationLevel = Level; 524 525 /* Initialise the lock for the access token */ 526 Status = SepCreateTokenLock(AccessToken); 527 if (!NT_SUCCESS(Status)) 528 { 529 ObDereferenceObject(AccessToken); 530 return Status; 531 } 532 533 /* Copy the immutable fields */ 534 AccessToken->TokenSource.SourceIdentifier = Token->TokenSource.SourceIdentifier; 535 RtlCopyMemory(AccessToken->TokenSource.SourceName, 536 Token->TokenSource.SourceName, 537 sizeof(Token->TokenSource.SourceName)); 538 539 AccessToken->AuthenticationId = Token->AuthenticationId; 540 AccessToken->ParentTokenId = Token->ParentTokenId; 541 AccessToken->ExpirationTime = Token->ExpirationTime; 542 AccessToken->OriginatingLogonSession = Token->OriginatingLogonSession; 543 AccessToken->DynamicCharged = Token->DynamicCharged; 544 545 /* Lock the source token and copy the mutable fields */ 546 SepAcquireTokenLockShared(Token); 547 548 AccessToken->SessionId = Token->SessionId; 549 AccessToken->ModifiedId = Token->ModifiedId; 550 551 AccessToken->TokenFlags = Token->TokenFlags & ~TOKEN_SESSION_NOT_REFERENCED; 552 553 /* Reference the logon session */ 554 Status = SepRmReferenceLogonSession(&AccessToken->AuthenticationId); 555 if (!NT_SUCCESS(Status)) 556 { 557 /* No logon session could be found, bail out */ 558 DPRINT1("SepRmReferenceLogonSession() failed (Status 0x%lx)\n", Status); 559 /* Set the flag for proper cleanup by the delete procedure */ 560 AccessToken->TokenFlags |= TOKEN_SESSION_NOT_REFERENCED; 561 goto Quit; 562 } 563 564 /* Insert the referenced logon session into the token */ 565 Status = SepRmInsertLogonSessionIntoToken(AccessToken); 566 if (!NT_SUCCESS(Status)) 567 { 568 /* Failed to insert the logon session into the token, bail out */ 569 DPRINT1("SepRmInsertLogonSessionIntoToken() failed (Status 0x%lx)\n", Status); 570 goto Quit; 571 } 572 573 /* Fill in token debug information */ 574 #if DBG 575 RtlCopyMemory(AccessToken->ImageFileName, 576 PsGetCurrentProcess()->ImageFileName, 577 min(sizeof(AccessToken->ImageFileName), sizeof(PsGetCurrentProcess()->ImageFileName))); 578 579 AccessToken->ProcessCid = PsGetCurrentProcessId(); 580 AccessToken->ThreadCid = PsGetCurrentThreadId(); 581 AccessToken->CreateMethod = TOKEN_DUPLICATE_METHOD; 582 #endif 583 584 /* Assign the data that reside in the token's variable information area */ 585 AccessToken->VariableLength = VariableLength; 586 EndMem = (PVOID)&AccessToken->VariablePart; 587 588 /* Copy the privileges */ 589 AccessToken->PrivilegeCount = 0; 590 AccessToken->Privileges = NULL; 591 if (Token->Privileges && (Token->PrivilegeCount > 0)) 592 { 593 ULONG PrivilegesLength = Token->PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES); 594 PrivilegesLength = ALIGN_UP_BY(PrivilegesLength, sizeof(PVOID)); 595 596 ASSERT(VariableLength >= PrivilegesLength); 597 598 AccessToken->PrivilegeCount = Token->PrivilegeCount; 599 AccessToken->Privileges = EndMem; 600 EndMem = (PVOID)((ULONG_PTR)EndMem + PrivilegesLength); 601 VariableLength -= PrivilegesLength; 602 603 RtlCopyMemory(AccessToken->Privileges, 604 Token->Privileges, 605 AccessToken->PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES)); 606 } 607 608 /* Copy the user and groups */ 609 AccessToken->UserAndGroupCount = 0; 610 AccessToken->UserAndGroups = NULL; 611 if (Token->UserAndGroups && (Token->UserAndGroupCount > 0)) 612 { 613 AccessToken->UserAndGroupCount = Token->UserAndGroupCount; 614 AccessToken->UserAndGroups = EndMem; 615 EndMem = &AccessToken->UserAndGroups[AccessToken->UserAndGroupCount]; 616 VariableLength -= ((ULONG_PTR)EndMem - (ULONG_PTR)AccessToken->UserAndGroups); 617 618 Status = RtlCopySidAndAttributesArray(AccessToken->UserAndGroupCount, 619 Token->UserAndGroups, 620 VariableLength, 621 AccessToken->UserAndGroups, 622 EndMem, 623 &EndMem, 624 &VariableLength); 625 if (!NT_SUCCESS(Status)) 626 { 627 DPRINT1("RtlCopySidAndAttributesArray(UserAndGroups) failed (Status 0x%lx)\n", Status); 628 goto Quit; 629 } 630 } 631 632 /* Find the token primary group */ 633 Status = SepFindPrimaryGroupAndDefaultOwner(AccessToken, 634 Token->PrimaryGroup, 635 NULL, 636 &PrimaryGroupIndex, 637 NULL); 638 if (!NT_SUCCESS(Status)) 639 { 640 DPRINT1("SepFindPrimaryGroupAndDefaultOwner failed (Status 0x%lx)\n", Status); 641 goto Quit; 642 } 643 644 /* Copy the restricted SIDs */ 645 AccessToken->RestrictedSidCount = 0; 646 AccessToken->RestrictedSids = NULL; 647 if (Token->RestrictedSids && (Token->RestrictedSidCount > 0)) 648 { 649 AccessToken->RestrictedSidCount = Token->RestrictedSidCount; 650 AccessToken->RestrictedSids = EndMem; 651 EndMem = &AccessToken->RestrictedSids[AccessToken->RestrictedSidCount]; 652 VariableLength -= ((ULONG_PTR)EndMem - (ULONG_PTR)AccessToken->RestrictedSids); 653 654 Status = RtlCopySidAndAttributesArray(AccessToken->RestrictedSidCount, 655 Token->RestrictedSids, 656 VariableLength, 657 AccessToken->RestrictedSids, 658 EndMem, 659 &EndMem, 660 &VariableLength); 661 if (!NT_SUCCESS(Status)) 662 { 663 DPRINT1("RtlCopySidAndAttributesArray(RestrictedSids) failed (Status 0x%lx)\n", Status); 664 goto Quit; 665 } 666 } 667 668 /* Now allocate the token's dynamic information area and set the data */ 669 AccessToken->DynamicPart = ExAllocatePoolWithTag(PagedPool, 670 DynamicPartSize, 671 TAG_TOKEN_DYNAMIC); 672 if (AccessToken->DynamicPart == NULL) 673 { 674 Status = STATUS_INSUFFICIENT_RESOURCES; 675 goto Quit; 676 } 677 678 /* Unused memory in the dynamic area */ 679 AccessToken->DynamicAvailable = 0; 680 681 /* 682 * Assign the primary group to the token 683 * and put it in the dynamic part as well. 684 */ 685 EndMem = (PVOID)AccessToken->DynamicPart; 686 AccessToken->PrimaryGroup = EndMem; 687 RtlCopySid(RtlLengthSid(AccessToken->UserAndGroups[PrimaryGroupIndex].Sid), 688 EndMem, 689 AccessToken->UserAndGroups[PrimaryGroupIndex].Sid); 690 AccessToken->DefaultOwnerIndex = Token->DefaultOwnerIndex; 691 EndMem = (PVOID)((ULONG_PTR)EndMem + RtlLengthSid(AccessToken->UserAndGroups[PrimaryGroupIndex].Sid)); 692 693 /* 694 * The existing token has a default DACL only 695 * if it has an allocated dynamic part. 696 */ 697 if (Token->DynamicPart && Token->DefaultDacl) 698 { 699 AccessToken->DefaultDacl = EndMem; 700 701 RtlCopyMemory(EndMem, 702 Token->DefaultDacl, 703 Token->DefaultDacl->AclSize); 704 } 705 706 /* 707 * Filter the token by removing the disabled privileges 708 * and groups if the caller wants to duplicate an access 709 * token as effective only. 710 */ 711 if (EffectiveOnly) 712 { 713 /* 714 * Begin querying the groups and search for disabled ones. Do not touch the 715 * user which is at the first position because it cannot be disabled, no 716 * matter what attributes it has. 717 */ 718 for (GroupsIndex = 1; GroupsIndex < AccessToken->UserAndGroupCount; GroupsIndex++) 719 { 720 /* 721 * A group is considered disabled if its attributes is either 722 * 0 or SE_GROUP_ENABLED is not included in the attributes flags list. 723 * That is because a certain user and/or group can have several attributes 724 * that bear no influence on whether a user/group is enabled or not 725 * (SE_GROUP_ENABLED_BY_DEFAULT for example which is a mere indicator 726 * that the group has just been enabled by default). A mandatory 727 * group (that is, the group has SE_GROUP_MANDATORY attribute) 728 * by standards it's always enabled and no one can disable it. 729 */ 730 if (AccessToken->UserAndGroups[GroupsIndex].Attributes == 0 || 731 (AccessToken->UserAndGroups[GroupsIndex].Attributes & SE_GROUP_ENABLED) == 0) 732 { 733 /* 734 * If this group is an administrators group 735 * and the token belongs to such group, 736 * we've to take away TOKEN_HAS_ADMIN_GROUP 737 * for the fact that's not enabled and as 738 * such the token no longer belongs to 739 * this group. 740 */ 741 if (RtlEqualSid(SeAliasAdminsSid, 742 &AccessToken->UserAndGroups[GroupsIndex].Sid)) 743 { 744 AccessToken->TokenFlags &= ~TOKEN_HAS_ADMIN_GROUP; 745 } 746 747 /* 748 * A group is not enabled, it's time to remove 749 * from the token and update the groups index 750 * accordingly and continue with the next group. 751 */ 752 SepRemoveUserGroupToken(AccessToken, GroupsIndex); 753 GroupsIndex--; 754 } 755 } 756 757 /* Begin querying the privileges and search for disabled ones */ 758 for (PrivilegesIndex = 0; PrivilegesIndex < AccessToken->PrivilegeCount; PrivilegesIndex++) 759 { 760 /* 761 * A privilege is considered disabled if its attributes is either 762 * 0 or SE_PRIVILEGE_ENABLED is not included in the attributes flags list. 763 * That is because a certain privilege can have several attributes 764 * that bear no influence on whether a privilege is enabled or not 765 * (SE_PRIVILEGE_ENABLED_BY_DEFAULT for example which is a mere indicator 766 * that the privilege has just been enabled by default). 767 */ 768 if (AccessToken->Privileges[PrivilegesIndex].Attributes == 0 || 769 (AccessToken->Privileges[PrivilegesIndex].Attributes & SE_PRIVILEGE_ENABLED) == 0) 770 { 771 /* 772 * A privilege is not enabled, therefor it's time 773 * to strip it from the token and continue with the next 774 * privilege. Of course we must also want to update the 775 * privileges index accordingly. 776 */ 777 SepRemovePrivilegeToken(AccessToken, PrivilegesIndex); 778 PrivilegesIndex--; 779 } 780 } 781 } 782 783 /* Return the token to the caller */ 784 *NewAccessToken = AccessToken; 785 Status = STATUS_SUCCESS; 786 787 Quit: 788 if (!NT_SUCCESS(Status)) 789 { 790 /* Dereference the token, the delete procedure will clean it up */ 791 ObDereferenceObject(AccessToken); 792 } 793 794 /* Unlock the source token */ 795 SepReleaseTokenLock(Token); 796 797 return Status; 798 } 799 800 /** 801 * @brief 802 * Private helper function responsible for creating a restricted access 803 * token, that is, a filtered token from privileges and groups and with 804 * restricted SIDs added into the token on demand by the caller. 805 * 806 * @param[in] Token 807 * An existing and valid access token. 808 * 809 * @param[in] PrivilegesToBeDeleted 810 * A list of privileges to be deleted within the token that's going 811 * to be filtered. This parameter is ignored if the caller wants to disable 812 * all the privileges by specifying DISABLE_MAX_PRIVILEGE in the flags 813 * parameter. 814 * 815 * @param[in] SidsToBeDisabled 816 * A list of group SIDs to be disabled within the token. This parameter 817 * can be NULL. 818 * 819 * @param[in] RestrictedSidsIntoToken 820 * A list of restricted SIDs to be added into the token. This parameter 821 * can be NULL. 822 * 823 * @param[in] PrivilegesCount 824 * The privilege count of the privileges list. 825 * 826 * @param[in] RegularGroupsSidCount 827 * The SIDs count of the group SIDs list. 828 * 829 * @param[in] RestrictedSidsCount 830 * The restricted SIDs count of restricted SIDs list. 831 * 832 * @param[in] PrivilegeFlags 833 * Influences how the privileges should be filtered in an access 834 * token. See NtFilterToken syscall for more information. 835 * 836 * @param[in] PreviousMode 837 * Processor level access mode. 838 * 839 * @param[out] FilteredToken 840 * The filtered token, returned to the caller. 841 * 842 * @return 843 * Returns STATUS_SUCCESS if token token filtering has completed successfully. 844 * STATUS_INVALID_PARAMETER is returned if one or more of the parameters 845 * do not meet the conditions imposed by the function. A failure NTSTATUS 846 * code is returned otherwise. 847 * 848 * @remarks 849 * The final outcome of privileges and/or SIDs filtering is not always 850 * deterministic. That is, any privileges or SIDs that aren't present 851 * in the access token are ignored and the function continues with the 852 * next privilege or SID to find for filtering. For a fully deterministic 853 * outcome the caller is responsible for querying the information details 854 * of privileges and SIDs present in the token and then afterwards use 855 * such obtained information to do any kind of filtering to the token. 856 */ 857 static 858 NTSTATUS 859 SepPerformTokenFiltering( 860 _In_ PTOKEN Token, 861 _In_opt_ PLUID_AND_ATTRIBUTES PrivilegesToBeDeleted, 862 _In_opt_ PSID_AND_ATTRIBUTES SidsToBeDisabled, 863 _In_opt_ PSID_AND_ATTRIBUTES RestrictedSidsIntoToken, 864 _When_(PrivilegesToBeDeleted != NULL, _In_) ULONG PrivilegesCount, 865 _When_(SidsToBeDisabled != NULL, _In_) ULONG RegularGroupsSidCount, 866 _When_(RestrictedSidsIntoToken != NULL, _In_) ULONG RestrictedSidsCount, 867 _In_ ULONG PrivilegeFlags, 868 _In_ KPROCESSOR_MODE PreviousMode, 869 _Out_ PTOKEN *FilteredToken) 870 { 871 NTSTATUS Status; 872 PTOKEN AccessToken; 873 PVOID EndMem; 874 ULONG DynamicPartSize; 875 ULONG RestrictedSidsLength; 876 ULONG PrivilegesLength; 877 ULONG PrimaryGroupIndex; 878 ULONG RestrictedSidsInList; 879 ULONG RestrictedSidsInToken; 880 ULONG VariableLength, TotalSize; 881 ULONG PrivsInToken, PrivsInList; 882 ULONG GroupsInToken, GroupsInList; 883 BOOLEAN WantPrivilegesDisabled; 884 BOOLEAN FoundPrivilege; 885 BOOLEAN FoundGroup; 886 887 PAGED_CODE(); 888 889 /* Ensure that the source token is valid, and lock it */ 890 ASSERT(Token); 891 SepAcquireTokenLockShared(Token); 892 893 /* Assume the caller doesn't want privileges disabled */ 894 WantPrivilegesDisabled = FALSE; 895 896 /* Assume we haven't found anything */ 897 FoundPrivilege = FALSE; 898 FoundGroup = FALSE; 899 900 /* 901 * Take the size that we need for filtered token 902 * allocation based upon the existing access token 903 * we've been given. 904 */ 905 VariableLength = Token->VariableLength; 906 907 if (RestrictedSidsIntoToken != NULL) 908 { 909 /* 910 * If the caller provided a list of restricted SIDs 911 * to be added onto the filtered access token then 912 * we must compute the size which is the total space 913 * of the current token and the length of the restricted 914 * SIDs for the filtered token. 915 */ 916 RestrictedSidsLength = RestrictedSidsCount * sizeof(SID_AND_ATTRIBUTES); 917 RestrictedSidsLength += RtlLengthSidAndAttributes(RestrictedSidsCount, RestrictedSidsIntoToken); 918 RestrictedSidsLength = ALIGN_UP_BY(RestrictedSidsLength, sizeof(PVOID)); 919 920 /* 921 * The variable length of the token is not just 922 * the actual space length of the existing token 923 * but also the sum of the restricted SIDs length. 924 */ 925 VariableLength += RestrictedSidsLength; 926 TotalSize = FIELD_OFFSET(TOKEN, VariablePart) + VariableLength + RestrictedSidsLength; 927 } 928 else 929 { 930 /* Otherwise the size is of the actual current token */ 931 TotalSize = FIELD_OFFSET(TOKEN, VariablePart) + VariableLength; 932 } 933 934 /* 935 * Compute how much size we need to allocate 936 * the dynamic part of the newly duplicated 937 * token. 938 */ 939 DynamicPartSize = Token->DefaultDacl ? Token->DefaultDacl->AclSize : 0; 940 DynamicPartSize += RtlLengthSid(Token->PrimaryGroup); 941 942 /* Set up a filtered token object */ 943 Status = ObCreateObject(PreviousMode, 944 SeTokenObjectType, 945 NULL, 946 PreviousMode, 947 NULL, 948 TotalSize, 949 Token->DynamicCharged, 950 TotalSize, 951 (PVOID*)&AccessToken); 952 if (!NT_SUCCESS(Status)) 953 { 954 DPRINT1("SepPerformTokenFiltering(): Failed to create the filtered token object (Status 0x%lx)\n", Status); 955 956 /* Unlock the source token and bail out */ 957 SepReleaseTokenLock(Token); 958 return Status; 959 } 960 961 /* Initialize the token and begin filling stuff to it */ 962 RtlZeroMemory(AccessToken, TotalSize); 963 964 /* Set up a lock for the new token */ 965 Status = SepCreateTokenLock(AccessToken); 966 if (!NT_SUCCESS(Status)) 967 goto Quit; 968 969 /* Allocate new IDs for the token */ 970 ExAllocateLocallyUniqueId(&AccessToken->TokenId); 971 ExAllocateLocallyUniqueId(&AccessToken->ModifiedId); 972 973 /* Copy the type and impersonation level from the token */ 974 AccessToken->TokenType = Token->TokenType; 975 AccessToken->ImpersonationLevel = Token->ImpersonationLevel; 976 977 /* Copy the immutable fields */ 978 AccessToken->TokenSource.SourceIdentifier = Token->TokenSource.SourceIdentifier; 979 RtlCopyMemory(AccessToken->TokenSource.SourceName, 980 Token->TokenSource.SourceName, 981 sizeof(Token->TokenSource.SourceName)); 982 983 AccessToken->AuthenticationId = Token->AuthenticationId; 984 AccessToken->ParentTokenId = Token->TokenId; 985 AccessToken->OriginatingLogonSession = Token->OriginatingLogonSession; 986 AccessToken->DynamicCharged = Token->DynamicCharged; 987 988 AccessToken->ExpirationTime = Token->ExpirationTime; 989 990 /* Copy the mutable fields */ 991 AccessToken->SessionId = Token->SessionId; 992 AccessToken->TokenFlags = Token->TokenFlags & ~TOKEN_SESSION_NOT_REFERENCED; 993 994 /* Reference the logon session */ 995 Status = SepRmReferenceLogonSession(&AccessToken->AuthenticationId); 996 if (!NT_SUCCESS(Status)) 997 { 998 /* We failed, bail out*/ 999 DPRINT1("SepPerformTokenFiltering(): Failed to reference the logon session (Status 0x%lx)\n", Status); 1000 AccessToken->TokenFlags |= TOKEN_SESSION_NOT_REFERENCED; 1001 goto Quit; 1002 } 1003 1004 /* Insert the referenced logon session into the token */ 1005 Status = SepRmInsertLogonSessionIntoToken(AccessToken); 1006 if (!NT_SUCCESS(Status)) 1007 { 1008 /* Failed to insert the logon session into the token, bail out */ 1009 DPRINT1("SepPerformTokenFiltering(): Failed to insert the logon session into token (Status 0x%lx)\n", Status); 1010 goto Quit; 1011 } 1012 1013 /* Fill in token debug information */ 1014 #if DBG 1015 RtlCopyMemory(AccessToken->ImageFileName, 1016 PsGetCurrentProcess()->ImageFileName, 1017 min(sizeof(AccessToken->ImageFileName), sizeof(PsGetCurrentProcess()->ImageFileName))); 1018 1019 AccessToken->ProcessCid = PsGetCurrentProcessId(); 1020 AccessToken->ThreadCid = PsGetCurrentThreadId(); 1021 AccessToken->CreateMethod = TOKEN_FILTER_METHOD; 1022 #endif 1023 1024 /* Assign the data that reside in the token's variable information area */ 1025 AccessToken->VariableLength = VariableLength; 1026 EndMem = (PVOID)&AccessToken->VariablePart; 1027 1028 /* Copy the privileges from the existing token */ 1029 AccessToken->PrivilegeCount = 0; 1030 AccessToken->Privileges = NULL; 1031 if (Token->Privileges && (Token->PrivilegeCount > 0)) 1032 { 1033 PrivilegesLength = Token->PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES); 1034 PrivilegesLength = ALIGN_UP_BY(PrivilegesLength, sizeof(PVOID)); 1035 1036 /* 1037 * Ensure that the token can actually hold all 1038 * the privileges from the existing token. 1039 * Otherwise something's seriously wrong and 1040 * we've to guard ourselves. 1041 */ 1042 ASSERT(VariableLength >= PrivilegesLength); 1043 1044 AccessToken->PrivilegeCount = Token->PrivilegeCount; 1045 AccessToken->Privileges = EndMem; 1046 EndMem = (PVOID)((ULONG_PTR)EndMem + PrivilegesLength); 1047 VariableLength -= PrivilegesLength; 1048 1049 RtlCopyMemory(AccessToken->Privileges, 1050 Token->Privileges, 1051 AccessToken->PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES)); 1052 } 1053 1054 /* Copy the user and groups */ 1055 AccessToken->UserAndGroupCount = 0; 1056 AccessToken->UserAndGroups = NULL; 1057 if (Token->UserAndGroups && (Token->UserAndGroupCount > 0)) 1058 { 1059 AccessToken->UserAndGroupCount = Token->UserAndGroupCount; 1060 AccessToken->UserAndGroups = EndMem; 1061 EndMem = &AccessToken->UserAndGroups[AccessToken->UserAndGroupCount]; 1062 VariableLength -= ((ULONG_PTR)EndMem - (ULONG_PTR)AccessToken->UserAndGroups); 1063 1064 Status = RtlCopySidAndAttributesArray(AccessToken->UserAndGroupCount, 1065 Token->UserAndGroups, 1066 VariableLength, 1067 AccessToken->UserAndGroups, 1068 EndMem, 1069 &EndMem, 1070 &VariableLength); 1071 if (!NT_SUCCESS(Status)) 1072 { 1073 DPRINT1("SepPerformTokenFiltering(): Failed to copy the groups into token (Status 0x%lx)\n", Status); 1074 goto Quit; 1075 } 1076 } 1077 1078 /* Copy the restricted SIDs */ 1079 AccessToken->RestrictedSidCount = 0; 1080 AccessToken->RestrictedSids = NULL; 1081 if (Token->RestrictedSids && (Token->RestrictedSidCount > 0)) 1082 { 1083 AccessToken->RestrictedSidCount = Token->RestrictedSidCount; 1084 AccessToken->RestrictedSids = EndMem; 1085 EndMem = &AccessToken->RestrictedSids[AccessToken->RestrictedSidCount]; 1086 VariableLength -= ((ULONG_PTR)EndMem - (ULONG_PTR)AccessToken->RestrictedSids); 1087 1088 Status = RtlCopySidAndAttributesArray(AccessToken->RestrictedSidCount, 1089 Token->RestrictedSids, 1090 VariableLength, 1091 AccessToken->RestrictedSids, 1092 EndMem, 1093 &EndMem, 1094 &VariableLength); 1095 if (!NT_SUCCESS(Status)) 1096 { 1097 DPRINT1("SepPerformTokenFiltering(): Failed to copy the restricted SIDs into token (Status 0x%lx)\n", Status); 1098 goto Quit; 1099 } 1100 } 1101 1102 /* 1103 * Insert the restricted SIDs into the token on 1104 * the request by the caller. 1105 */ 1106 if (RestrictedSidsIntoToken != NULL) 1107 { 1108 for (RestrictedSidsInList = 0; RestrictedSidsInList < RestrictedSidsCount; RestrictedSidsInList++) 1109 { 1110 /* Did the caller assign attributes to the restricted SIDs? */ 1111 if (RestrictedSidsIntoToken[RestrictedSidsInList].Attributes != 0) 1112 { 1113 /* There mustn't be any attributes, bail out */ 1114 DPRINT1("SepPerformTokenFiltering(): There mustn't be any attributes to restricted SIDs!\n"); 1115 Status = STATUS_INVALID_PARAMETER; 1116 goto Quit; 1117 } 1118 } 1119 1120 /* 1121 * Ensure that the token can hold the restricted SIDs 1122 * (the variable length is calculated at the beginning 1123 * of the routine call). 1124 */ 1125 ASSERT(VariableLength >= RestrictedSidsLength); 1126 1127 /* 1128 * Now let's begin inserting the restricted SIDs into the filtered 1129 * access token from the list the caller gave us. 1130 */ 1131 AccessToken->RestrictedSidCount = RestrictedSidsCount; 1132 AccessToken->RestrictedSids = EndMem; 1133 EndMem = (PVOID)((ULONG_PTR)EndMem + RestrictedSidsLength); 1134 VariableLength -= RestrictedSidsLength; 1135 1136 RtlCopyMemory(AccessToken->RestrictedSids, 1137 RestrictedSidsIntoToken, 1138 AccessToken->RestrictedSidCount * sizeof(SID_AND_ATTRIBUTES)); 1139 1140 /* 1141 * As we've copied the restricted SIDs into 1142 * the token, we must assign them the following 1143 * combination of attributes SE_GROUP_ENABLED, 1144 * SE_GROUP_ENABLED_BY_DEFAULT and SE_GROUP_MANDATORY. 1145 * With such attributes we estabilish that restricting 1146 * SIDs into the token are enabled for access checks. 1147 */ 1148 for (RestrictedSidsInToken = 0; RestrictedSidsInToken < AccessToken->RestrictedSidCount; RestrictedSidsInToken++) 1149 { 1150 AccessToken->RestrictedSids[RestrictedSidsInToken].Attributes |= (SE_GROUP_ENABLED | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_MANDATORY); 1151 } 1152 1153 /* 1154 * As we added restricted SIDs into the token, mark 1155 * it as restricted. 1156 */ 1157 AccessToken->TokenFlags |= TOKEN_IS_RESTRICTED; 1158 } 1159 1160 /* Search for the primary group */ 1161 Status = SepFindPrimaryGroupAndDefaultOwner(AccessToken, 1162 Token->PrimaryGroup, 1163 NULL, 1164 &PrimaryGroupIndex, 1165 NULL); 1166 if (!NT_SUCCESS(Status)) 1167 { 1168 DPRINT1("SepPerformTokenFiltering(): Failed searching for the primary group (Status 0x%lx)\n", Status); 1169 goto Quit; 1170 } 1171 1172 /* Now allocate the token's dynamic information area and set the data */ 1173 AccessToken->DynamicPart = ExAllocatePoolWithTag(PagedPool, 1174 DynamicPartSize, 1175 TAG_TOKEN_DYNAMIC); 1176 if (AccessToken->DynamicPart == NULL) 1177 { 1178 Status = STATUS_INSUFFICIENT_RESOURCES; 1179 goto Quit; 1180 } 1181 1182 /* Unused memory in the dynamic area */ 1183 AccessToken->DynamicAvailable = 0; 1184 1185 /* 1186 * Assign the primary group to the token 1187 * and put it in the dynamic part as well. 1188 */ 1189 EndMem = (PVOID)AccessToken->DynamicPart; 1190 AccessToken->PrimaryGroup = EndMem; 1191 RtlCopySid(RtlLengthSid(AccessToken->UserAndGroups[PrimaryGroupIndex].Sid), 1192 EndMem, 1193 AccessToken->UserAndGroups[PrimaryGroupIndex].Sid); 1194 AccessToken->DefaultOwnerIndex = Token->DefaultOwnerIndex; 1195 EndMem = (PVOID)((ULONG_PTR)EndMem + RtlLengthSid(AccessToken->UserAndGroups[PrimaryGroupIndex].Sid)); 1196 1197 /* 1198 * The existing token has a default DACL only 1199 * if it has an allocated dynamic part. 1200 */ 1201 if (Token->DynamicPart && Token->DefaultDacl) 1202 { 1203 AccessToken->DefaultDacl = EndMem; 1204 1205 RtlCopyMemory(EndMem, 1206 Token->DefaultDacl, 1207 Token->DefaultDacl->AclSize); 1208 } 1209 1210 /* 1211 * Now figure out what does the caller 1212 * want with the privileges. 1213 */ 1214 if (PrivilegeFlags & DISABLE_MAX_PRIVILEGE) 1215 { 1216 /* 1217 * The caller wants them disabled, cache this request 1218 * for later operations. 1219 */ 1220 WantPrivilegesDisabled = TRUE; 1221 } 1222 1223 if (PrivilegeFlags & SANDBOX_INERT) 1224 { 1225 /* The caller wants an inert token, store the TOKEN_SANDBOX_INERT flag now */ 1226 AccessToken->TokenFlags |= TOKEN_SANDBOX_INERT; 1227 } 1228 1229 /* 1230 * Now it's time to filter the token's privileges. 1231 * Loop all the privileges in the token. 1232 */ 1233 for (PrivsInToken = 0; PrivsInToken < AccessToken->PrivilegeCount; PrivsInToken++) 1234 { 1235 if (WantPrivilegesDisabled) 1236 { 1237 /* 1238 * We got the acknowledgement that the caller wants 1239 * to disable all the privileges so let's just do it. 1240 * However, as per the general documentation is stated 1241 * that only SE_CHANGE_NOTIFY_PRIVILEGE must be kept 1242 * therefore in that case we must skip this privilege. 1243 */ 1244 if (AccessToken->Privileges[PrivsInToken].Luid.LowPart == SE_CHANGE_NOTIFY_PRIVILEGE) 1245 { 1246 continue; 1247 } 1248 else 1249 { 1250 /* 1251 * The act of disabling privileges actually means 1252 * "deleting" them from the access token entirely. 1253 * First we must disable them so that we can update 1254 * token flags accordingly. 1255 */ 1256 AccessToken->Privileges[PrivsInToken].Attributes &= ~SE_PRIVILEGE_ENABLED; 1257 SepUpdateSinglePrivilegeFlagToken(AccessToken, PrivsInToken); 1258 1259 /* Remove the privileges now */ 1260 SepRemovePrivilegeToken(AccessToken, PrivsInToken); 1261 PrivsInToken--; 1262 } 1263 } 1264 else 1265 { 1266 if (PrivilegesToBeDeleted != NULL) 1267 { 1268 /* Loop the privileges we've got to delete */ 1269 for (PrivsInList = 0; PrivsInList < PrivilegesCount; PrivsInList++) 1270 { 1271 /* Does this privilege exist in the token? */ 1272 if (RtlEqualLuid(&AccessToken->Privileges[PrivsInToken].Luid, 1273 &PrivilegesToBeDeleted[PrivsInList].Luid)) 1274 { 1275 /* Mark that we found it */ 1276 FoundPrivilege = TRUE; 1277 break; 1278 } 1279 } 1280 1281 /* Did we find the privilege? */ 1282 if (PrivsInList == PrivilegesCount) 1283 { 1284 /* We didn't, continue with next one */ 1285 continue; 1286 } 1287 } 1288 } 1289 1290 /* 1291 * If we have found the target privilege in the token 1292 * based on the privileges list given by the caller 1293 * then begin deleting it. 1294 */ 1295 if (FoundPrivilege) 1296 { 1297 /* Disable the privilege and update the flags */ 1298 AccessToken->Privileges[PrivsInToken].Attributes &= ~SE_PRIVILEGE_ENABLED; 1299 SepUpdateSinglePrivilegeFlagToken(AccessToken, PrivsInToken); 1300 1301 /* Delete the privilege */ 1302 SepRemovePrivilegeToken(AccessToken, PrivsInToken); 1303 1304 /* 1305 * Adjust the index and reset the FoundPrivilege indicator 1306 * so that we can continue with the next privilege to delete. 1307 */ 1308 PrivsInToken--; 1309 FoundPrivilege = FALSE; 1310 continue; 1311 } 1312 } 1313 1314 /* 1315 * Loop the group SIDs that we want to disable as 1316 * per on the request by the caller. 1317 */ 1318 if (SidsToBeDisabled != NULL) 1319 { 1320 for (GroupsInToken = 0; GroupsInToken < AccessToken->UserAndGroupCount; GroupsInToken++) 1321 { 1322 for (GroupsInList = 0; GroupsInList < RegularGroupsSidCount; GroupsInList++) 1323 { 1324 /* Does this group SID exist in the token? */ 1325 if (RtlEqualSid(&AccessToken->UserAndGroups[GroupsInToken].Sid, 1326 &SidsToBeDisabled[GroupsInList].Sid)) 1327 { 1328 /* Mark that we found it */ 1329 FoundGroup = TRUE; 1330 break; 1331 } 1332 } 1333 1334 /* Did we find the group? */ 1335 if (GroupsInList == RegularGroupsSidCount) 1336 { 1337 /* We didn't, continue with next one */ 1338 continue; 1339 } 1340 1341 /* If we have found the group, disable it */ 1342 if (FoundGroup) 1343 { 1344 /* 1345 * If the acess token belongs to the administrators 1346 * group and this is the target group, we must take 1347 * away TOKEN_HAS_ADMIN_GROUP flag from the token. 1348 */ 1349 if (RtlEqualSid(SeAliasAdminsSid, 1350 &AccessToken->UserAndGroups[GroupsInToken].Sid)) 1351 { 1352 AccessToken->TokenFlags &= ~TOKEN_HAS_ADMIN_GROUP; 1353 } 1354 1355 /* 1356 * If the target group that we have found it is the 1357 * owner then from now on it no longer is but the user. 1358 * Therefore assign the default owner index as the user. 1359 */ 1360 if (AccessToken->DefaultOwnerIndex == GroupsInToken) 1361 { 1362 AccessToken->DefaultOwnerIndex = 0; 1363 } 1364 1365 /* 1366 * The principle of disabling a group SID is by 1367 * taking away SE_GROUP_ENABLED_BY_DEFAULT and 1368 * SE_GROUP_ENABLED attributes and assign 1369 * SE_GROUP_USE_FOR_DENY_ONLY. This renders 1370 * SID a "Deny only" SID. 1371 */ 1372 AccessToken->UserAndGroups[GroupsInToken].Attributes &= ~(SE_GROUP_ENABLED | SE_GROUP_ENABLED_BY_DEFAULT); 1373 AccessToken->UserAndGroups[GroupsInToken].Attributes |= SE_GROUP_USE_FOR_DENY_ONLY; 1374 1375 /* Adjust the index and continue with the next group */ 1376 GroupsInToken--; 1377 FoundGroup = FALSE; 1378 continue; 1379 } 1380 } 1381 } 1382 1383 /* We've finally filtered the token, return it to the caller */ 1384 *FilteredToken = AccessToken; 1385 Status = STATUS_SUCCESS; 1386 DPRINT("SepPerformTokenFiltering(): The token has been filtered!\n"); 1387 1388 Quit: 1389 if (!NT_SUCCESS(Status)) 1390 { 1391 /* Dereference the created token */ 1392 ObDereferenceObject(AccessToken); 1393 } 1394 1395 /* Unlock the source token */ 1396 SepReleaseTokenLock(Token); 1397 1398 return Status; 1399 } 1400 1401 /* PUBLIC FUNCTIONS ***********************************************************/ 1402 1403 /** 1404 * @brief 1405 * Filters an access token from an existing token, making it more restricted 1406 * than the previous one. 1407 * 1408 * @param[in] ExistingToken 1409 * An existing token for filtering. 1410 * 1411 * @param[in] Flags 1412 * Privilege flag options. This parameter argument influences how the token 1413 * is filtered. Such parameter can be 0. See NtFilterToken syscall for 1414 * more information. 1415 * 1416 * @param[in] SidsToDisable 1417 * Array of SIDs to disable. Such parameter can be NULL. 1418 * 1419 * @param[in] PrivilegesToDelete 1420 * Array of privileges to delete. If DISABLE_MAX_PRIVILEGE flag is specified 1421 * in the Flags parameter, PrivilegesToDelete is ignored. 1422 * 1423 * @param[in] RestrictedSids 1424 * An array of restricted SIDs for the new filtered token. Such parameter 1425 * can be NULL. 1426 * 1427 * @param[out] FilteredToken 1428 * The newly filtered token, returned to the caller. 1429 * 1430 * @return 1431 * Returns STATUS_SUCCESS if the function has successfully completed its 1432 * operations and that the access token has been filtered. STATUS_INVALID_PARAMETER 1433 * is returned if one or more of the parameter are not valid. A failure NTSTATUS code 1434 * is returned otherwise. 1435 */ 1436 NTSTATUS 1437 NTAPI 1438 SeFilterToken( 1439 _In_ PACCESS_TOKEN ExistingToken, 1440 _In_ ULONG Flags, 1441 _In_opt_ PTOKEN_GROUPS SidsToDisable, 1442 _In_opt_ PTOKEN_PRIVILEGES PrivilegesToDelete, 1443 _In_opt_ PTOKEN_GROUPS RestrictedSids, 1444 _Out_ PACCESS_TOKEN *FilteredToken) 1445 { 1446 NTSTATUS Status; 1447 PTOKEN AccessToken; 1448 ULONG PrivilegesCount = 0; 1449 ULONG SidsCount = 0; 1450 ULONG RestrictedSidsCount = 0; 1451 1452 PAGED_CODE(); 1453 1454 /* Begin copying the counters */ 1455 if (SidsToDisable != NULL) 1456 { 1457 SidsCount = SidsToDisable->GroupCount; 1458 } 1459 1460 if (PrivilegesToDelete != NULL) 1461 { 1462 PrivilegesCount = PrivilegesToDelete->PrivilegeCount; 1463 } 1464 1465 if (RestrictedSids != NULL) 1466 { 1467 RestrictedSidsCount = RestrictedSids->GroupCount; 1468 } 1469 1470 /* Call the internal API */ 1471 Status = SepPerformTokenFiltering(ExistingToken, 1472 PrivilegesToDelete->Privileges, 1473 SidsToDisable->Groups, 1474 RestrictedSids->Groups, 1475 PrivilegesCount, 1476 SidsCount, 1477 RestrictedSidsCount, 1478 Flags, 1479 KernelMode, 1480 &AccessToken); 1481 if (!NT_SUCCESS(Status)) 1482 { 1483 DPRINT1("SeFilterToken(): Failed to filter the token (Status 0x%lx)\n", Status); 1484 return Status; 1485 } 1486 1487 /* Insert the filtered token */ 1488 Status = ObInsertObject(AccessToken, 1489 NULL, 1490 0, 1491 0, 1492 NULL, 1493 NULL); 1494 if (!NT_SUCCESS(Status)) 1495 { 1496 DPRINT1("SeFilterToken(): Failed to insert the filtered token (Status 0x%lx)\n", Status); 1497 return Status; 1498 } 1499 1500 /* Return it to the caller */ 1501 *FilteredToken = AccessToken; 1502 return Status; 1503 } 1504 1505 /* SYSTEM CALLS ***************************************************************/ 1506 1507 /** 1508 * @brief 1509 * Creates an access token. 1510 * 1511 * @param[out] TokenHandle 1512 * The returned created token handle to the caller. 1513 * 1514 * @param[in] DesiredAccess 1515 * The desired access rights for the token that we're creating. 1516 * 1517 * @param[in] ObjectAttributes 1518 * The object attributes for the token object that we're creating. 1519 * 1520 * @param[in] TokenType 1521 * The type of token to assign for the newly created token. 1522 * 1523 * @param[in] AuthenticationId 1524 * Authentication ID that represents the token's identity. 1525 * 1526 * @param[in] ExpirationTime 1527 * Expiration time for the token. If set to -1, the token never expires. 1528 * 1529 * @param[in] TokenUser 1530 * The main user entity for the token to assign. 1531 * 1532 * @param[in] TokenGroups 1533 * Group list of SIDs for the token to assign. 1534 * 1535 * @param[in] TokenPrivileges 1536 * Privileges for the token. 1537 * 1538 * @param[in] TokenOwner 1539 * The main user that owns the newly created token. 1540 * 1541 * @param[in] TokenPrimaryGroup 1542 * The primary group that represents as the main group of the token. 1543 * 1544 * @param[in] TokenDefaultDacl 1545 * Discretionary access control list for the token. This limits on how 1546 * the token can be used, accessed and used by whom. 1547 * 1548 * @param[in] TokenSource 1549 * The source origin of the token who creates it. 1550 * 1551 * @return 1552 * Returns STATUS_SUCCESS if the function has successfully created the token. 1553 * A failure NTSTATUS code is returned otherwise. 1554 */ 1555 __kernel_entry 1556 NTSTATUS 1557 NTAPI 1558 NtCreateToken( 1559 _Out_ PHANDLE TokenHandle, 1560 _In_ ACCESS_MASK DesiredAccess, 1561 _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes, 1562 _In_ TOKEN_TYPE TokenType, 1563 _In_ PLUID AuthenticationId, 1564 _In_ PLARGE_INTEGER ExpirationTime, 1565 _In_ PTOKEN_USER TokenUser, 1566 _In_ PTOKEN_GROUPS TokenGroups, 1567 _In_ PTOKEN_PRIVILEGES TokenPrivileges, 1568 _In_opt_ PTOKEN_OWNER TokenOwner, 1569 _In_ PTOKEN_PRIMARY_GROUP TokenPrimaryGroup, 1570 _In_opt_ PTOKEN_DEFAULT_DACL TokenDefaultDacl, 1571 _In_ PTOKEN_SOURCE TokenSource) 1572 { 1573 HANDLE hToken; 1574 KPROCESSOR_MODE PreviousMode; 1575 ULONG PrivilegeCount, GroupCount; 1576 PSID OwnerSid, PrimaryGroupSid; 1577 PACL DefaultDacl; 1578 LARGE_INTEGER LocalExpirationTime = {{0, 0}}; 1579 LUID LocalAuthenticationId; 1580 TOKEN_SOURCE LocalTokenSource; 1581 SECURITY_QUALITY_OF_SERVICE LocalSecurityQos; 1582 PLUID_AND_ATTRIBUTES CapturedPrivileges = NULL; 1583 PSID_AND_ATTRIBUTES CapturedUser = NULL; 1584 PSID_AND_ATTRIBUTES CapturedGroups = NULL; 1585 PSID CapturedOwnerSid = NULL; 1586 PSID CapturedPrimaryGroupSid = NULL; 1587 PACL CapturedDefaultDacl = NULL; 1588 ULONG PrivilegesLength, UserLength, GroupsLength; 1589 NTSTATUS Status; 1590 1591 PAGED_CODE(); 1592 1593 PreviousMode = ExGetPreviousMode(); 1594 1595 if (PreviousMode != KernelMode) 1596 { 1597 _SEH2_TRY 1598 { 1599 ProbeForWriteHandle(TokenHandle); 1600 1601 if (ObjectAttributes != NULL) 1602 { 1603 ProbeForRead(ObjectAttributes, 1604 sizeof(OBJECT_ATTRIBUTES), 1605 sizeof(ULONG)); 1606 LocalSecurityQos = *(SECURITY_QUALITY_OF_SERVICE*)ObjectAttributes->SecurityQualityOfService; 1607 } 1608 1609 ProbeForRead(AuthenticationId, 1610 sizeof(LUID), 1611 sizeof(ULONG)); 1612 LocalAuthenticationId = *AuthenticationId; 1613 1614 LocalExpirationTime = ProbeForReadLargeInteger(ExpirationTime); 1615 1616 ProbeForRead(TokenUser, 1617 sizeof(TOKEN_USER), 1618 sizeof(ULONG)); 1619 1620 ProbeForRead(TokenGroups, 1621 sizeof(TOKEN_GROUPS), 1622 sizeof(ULONG)); 1623 GroupCount = TokenGroups->GroupCount; 1624 1625 ProbeForRead(TokenPrivileges, 1626 sizeof(TOKEN_PRIVILEGES), 1627 sizeof(ULONG)); 1628 PrivilegeCount = TokenPrivileges->PrivilegeCount; 1629 1630 if (TokenOwner != NULL) 1631 { 1632 ProbeForRead(TokenOwner, 1633 sizeof(TOKEN_OWNER), 1634 sizeof(ULONG)); 1635 OwnerSid = TokenOwner->Owner; 1636 } 1637 else 1638 { 1639 OwnerSid = NULL; 1640 } 1641 1642 ProbeForRead(TokenPrimaryGroup, 1643 sizeof(TOKEN_PRIMARY_GROUP), 1644 sizeof(ULONG)); 1645 PrimaryGroupSid = TokenPrimaryGroup->PrimaryGroup; 1646 1647 if (TokenDefaultDacl != NULL) 1648 { 1649 ProbeForRead(TokenDefaultDacl, 1650 sizeof(TOKEN_DEFAULT_DACL), 1651 sizeof(ULONG)); 1652 DefaultDacl = TokenDefaultDacl->DefaultDacl; 1653 } 1654 else 1655 { 1656 DefaultDacl = NULL; 1657 } 1658 1659 ProbeForRead(TokenSource, 1660 sizeof(TOKEN_SOURCE), 1661 sizeof(ULONG)); 1662 LocalTokenSource = *TokenSource; 1663 } 1664 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 1665 { 1666 /* Return the exception code */ 1667 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 1668 } 1669 _SEH2_END; 1670 } 1671 else 1672 { 1673 if (ObjectAttributes != NULL) 1674 LocalSecurityQos = *(SECURITY_QUALITY_OF_SERVICE*)ObjectAttributes->SecurityQualityOfService; 1675 LocalAuthenticationId = *AuthenticationId; 1676 LocalExpirationTime = *ExpirationTime; 1677 GroupCount = TokenGroups->GroupCount; 1678 PrivilegeCount = TokenPrivileges->PrivilegeCount; 1679 OwnerSid = TokenOwner ? TokenOwner->Owner : NULL; 1680 PrimaryGroupSid = TokenPrimaryGroup->PrimaryGroup; 1681 DefaultDacl = TokenDefaultDacl ? TokenDefaultDacl->DefaultDacl : NULL; 1682 LocalTokenSource = *TokenSource; 1683 } 1684 1685 /* Check token type */ 1686 if ((TokenType < TokenPrimary) || 1687 (TokenType > TokenImpersonation)) 1688 { 1689 return STATUS_BAD_TOKEN_TYPE; 1690 } 1691 1692 /* Check for token creation privilege */ 1693 if (!SeSinglePrivilegeCheck(SeCreateTokenPrivilege, PreviousMode)) 1694 { 1695 return STATUS_PRIVILEGE_NOT_HELD; 1696 } 1697 1698 /* Capture the user SID and attributes */ 1699 Status = SeCaptureSidAndAttributesArray(&TokenUser->User, 1700 1, 1701 PreviousMode, 1702 NULL, 1703 0, 1704 PagedPool, 1705 FALSE, 1706 &CapturedUser, 1707 &UserLength); 1708 if (!NT_SUCCESS(Status)) 1709 { 1710 goto Cleanup; 1711 } 1712 1713 /* Capture the groups SID and attributes array */ 1714 Status = SeCaptureSidAndAttributesArray(&TokenGroups->Groups[0], 1715 GroupCount, 1716 PreviousMode, 1717 NULL, 1718 0, 1719 PagedPool, 1720 FALSE, 1721 &CapturedGroups, 1722 &GroupsLength); 1723 if (!NT_SUCCESS(Status)) 1724 { 1725 goto Cleanup; 1726 } 1727 1728 /* Capture privileges */ 1729 Status = SeCaptureLuidAndAttributesArray(&TokenPrivileges->Privileges[0], 1730 PrivilegeCount, 1731 PreviousMode, 1732 NULL, 1733 0, 1734 PagedPool, 1735 FALSE, 1736 &CapturedPrivileges, 1737 &PrivilegesLength); 1738 if (!NT_SUCCESS(Status)) 1739 { 1740 goto Cleanup; 1741 } 1742 1743 /* Capture the token owner SID */ 1744 if (TokenOwner != NULL) 1745 { 1746 Status = SepCaptureSid(OwnerSid, 1747 PreviousMode, 1748 PagedPool, 1749 FALSE, 1750 &CapturedOwnerSid); 1751 if (!NT_SUCCESS(Status)) 1752 { 1753 goto Cleanup; 1754 } 1755 } 1756 1757 /* Capture the token primary group SID */ 1758 Status = SepCaptureSid(PrimaryGroupSid, 1759 PreviousMode, 1760 PagedPool, 1761 FALSE, 1762 &CapturedPrimaryGroupSid); 1763 if (!NT_SUCCESS(Status)) 1764 { 1765 goto Cleanup; 1766 } 1767 1768 /* Capture DefaultDacl */ 1769 if (DefaultDacl != NULL) 1770 { 1771 Status = SepCaptureAcl(DefaultDacl, 1772 PreviousMode, 1773 NonPagedPool, 1774 FALSE, 1775 &CapturedDefaultDacl); 1776 if (!NT_SUCCESS(Status)) 1777 { 1778 goto Cleanup; 1779 } 1780 } 1781 1782 /* Call the internal function */ 1783 Status = SepCreateToken(&hToken, 1784 PreviousMode, 1785 DesiredAccess, 1786 ObjectAttributes, 1787 TokenType, 1788 LocalSecurityQos.ImpersonationLevel, 1789 &LocalAuthenticationId, 1790 &LocalExpirationTime, 1791 CapturedUser, 1792 GroupCount, 1793 CapturedGroups, 1794 GroupsLength, 1795 PrivilegeCount, 1796 CapturedPrivileges, 1797 CapturedOwnerSid, 1798 CapturedPrimaryGroupSid, 1799 CapturedDefaultDacl, 1800 &LocalTokenSource, 1801 FALSE); 1802 if (NT_SUCCESS(Status)) 1803 { 1804 _SEH2_TRY 1805 { 1806 *TokenHandle = hToken; 1807 } 1808 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 1809 { 1810 Status = _SEH2_GetExceptionCode(); 1811 } 1812 _SEH2_END; 1813 } 1814 1815 Cleanup: 1816 1817 /* Release what we captured */ 1818 SeReleaseSidAndAttributesArray(CapturedUser, PreviousMode, FALSE); 1819 SeReleaseSidAndAttributesArray(CapturedGroups, PreviousMode, FALSE); 1820 SeReleaseLuidAndAttributesArray(CapturedPrivileges, PreviousMode, FALSE); 1821 SepReleaseSid(CapturedOwnerSid, PreviousMode, FALSE); 1822 SepReleaseSid(CapturedPrimaryGroupSid, PreviousMode, FALSE); 1823 SepReleaseAcl(CapturedDefaultDacl, PreviousMode, FALSE); 1824 1825 return Status; 1826 } 1827 1828 /** 1829 * @brief 1830 * Duplicates a token. 1831 * 1832 * @param[in] ExistingTokenHandle 1833 * An existing token to duplicate. 1834 * 1835 * @param[in] DesiredAccess 1836 * The desired access rights for the new duplicated token. 1837 * 1838 * @param[in] ObjectAttributes 1839 * Object attributes for the new duplicated token. 1840 * 1841 * @param[in] EffectiveOnly 1842 * If set to TRUE, the function removes all the disabled privileges and groups 1843 * of the token to duplicate. 1844 * 1845 * @param[in] TokenType 1846 * Type of token to assign to the duplicated token. 1847 * 1848 * @param[out] NewTokenHandle 1849 * The returned duplicated token handle. 1850 * 1851 * @return 1852 * STATUS_SUCCESS is returned if token duplication has completed successfully. 1853 * STATUS_BAD_IMPERSONATION_LEVEL is returned if the caller erroneously wants 1854 * to raise the impersonation level even though the conditions do not permit 1855 * it. A failure NTSTATUS code is returned otherwise. 1856 * 1857 * @remarks 1858 * Some sources claim 4th param is ImpersonationLevel, but on W2K 1859 * this is certainly NOT true, although I can't say for sure that EffectiveOnly 1860 * is correct either. -Gunnar 1861 * This is true. EffectiveOnly overrides SQOS.EffectiveOnly. - IAI 1862 * NOTE for readers: http://hex.pp.ua/nt/NtDuplicateToken.php is therefore 1863 * wrong in that regard, while MSDN documentation is correct. 1864 */ 1865 _Must_inspect_result_ 1866 __kernel_entry 1867 NTSTATUS 1868 NTAPI 1869 NtDuplicateToken( 1870 _In_ HANDLE ExistingTokenHandle, 1871 _In_ ACCESS_MASK DesiredAccess, 1872 _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes, 1873 _In_ BOOLEAN EffectiveOnly, 1874 _In_ TOKEN_TYPE TokenType, 1875 _Out_ PHANDLE NewTokenHandle) 1876 { 1877 KPROCESSOR_MODE PreviousMode; 1878 HANDLE hToken; 1879 PTOKEN Token; 1880 PTOKEN NewToken; 1881 PSECURITY_QUALITY_OF_SERVICE CapturedSecurityQualityOfService; 1882 BOOLEAN QoSPresent; 1883 OBJECT_HANDLE_INFORMATION HandleInformation; 1884 NTSTATUS Status; 1885 1886 PAGED_CODE(); 1887 1888 if (TokenType != TokenImpersonation && 1889 TokenType != TokenPrimary) 1890 { 1891 return STATUS_INVALID_PARAMETER; 1892 } 1893 1894 PreviousMode = KeGetPreviousMode(); 1895 1896 if (PreviousMode != KernelMode) 1897 { 1898 _SEH2_TRY 1899 { 1900 ProbeForWriteHandle(NewTokenHandle); 1901 } 1902 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 1903 { 1904 /* Return the exception code */ 1905 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 1906 } 1907 _SEH2_END; 1908 } 1909 1910 Status = SepCaptureSecurityQualityOfService(ObjectAttributes, 1911 PreviousMode, 1912 PagedPool, 1913 FALSE, 1914 &CapturedSecurityQualityOfService, 1915 &QoSPresent); 1916 if (!NT_SUCCESS(Status)) 1917 { 1918 DPRINT1("NtDuplicateToken() failed to capture QoS! Status: 0x%x\n", Status); 1919 return Status; 1920 } 1921 1922 Status = ObReferenceObjectByHandle(ExistingTokenHandle, 1923 TOKEN_DUPLICATE, 1924 SeTokenObjectType, 1925 PreviousMode, 1926 (PVOID*)&Token, 1927 &HandleInformation); 1928 if (!NT_SUCCESS(Status)) 1929 { 1930 DPRINT1("Failed to reference token (Status 0x%lx)\n", Status); 1931 SepReleaseSecurityQualityOfService(CapturedSecurityQualityOfService, 1932 PreviousMode, 1933 FALSE); 1934 return Status; 1935 } 1936 1937 /* 1938 * Fail, if the original token is an impersonation token and the caller 1939 * tries to raise the impersonation level of the new token above the 1940 * impersonation level of the original token. 1941 */ 1942 if (Token->TokenType == TokenImpersonation) 1943 { 1944 if (QoSPresent && 1945 CapturedSecurityQualityOfService->ImpersonationLevel >Token->ImpersonationLevel) 1946 { 1947 ObDereferenceObject(Token); 1948 SepReleaseSecurityQualityOfService(CapturedSecurityQualityOfService, 1949 PreviousMode, 1950 FALSE); 1951 return STATUS_BAD_IMPERSONATION_LEVEL; 1952 } 1953 } 1954 1955 /* 1956 * Fail, if a primary token is to be created from an impersonation token 1957 * and and the impersonation level of the impersonation token is below SecurityImpersonation. 1958 */ 1959 if (Token->TokenType == TokenImpersonation && 1960 TokenType == TokenPrimary && 1961 Token->ImpersonationLevel < SecurityImpersonation) 1962 { 1963 ObDereferenceObject(Token); 1964 SepReleaseSecurityQualityOfService(CapturedSecurityQualityOfService, 1965 PreviousMode, 1966 FALSE); 1967 return STATUS_BAD_IMPERSONATION_LEVEL; 1968 } 1969 1970 Status = SepDuplicateToken(Token, 1971 ObjectAttributes, 1972 EffectiveOnly, 1973 TokenType, 1974 (QoSPresent ? CapturedSecurityQualityOfService->ImpersonationLevel : SecurityAnonymous), 1975 PreviousMode, 1976 &NewToken); 1977 1978 ObDereferenceObject(Token); 1979 1980 if (NT_SUCCESS(Status)) 1981 { 1982 Status = ObInsertObject(NewToken, 1983 NULL, 1984 (DesiredAccess ? DesiredAccess : HandleInformation.GrantedAccess), 1985 0, 1986 NULL, 1987 &hToken); 1988 if (NT_SUCCESS(Status)) 1989 { 1990 _SEH2_TRY 1991 { 1992 *NewTokenHandle = hToken; 1993 } 1994 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 1995 { 1996 Status = _SEH2_GetExceptionCode(); 1997 } 1998 _SEH2_END; 1999 } 2000 } 2001 2002 /* Free the captured structure */ 2003 SepReleaseSecurityQualityOfService(CapturedSecurityQualityOfService, 2004 PreviousMode, 2005 FALSE); 2006 2007 return Status; 2008 } 2009 2010 /** 2011 * @brief 2012 * Creates an access token in a restricted form 2013 * from the original existing token, that is, such 2014 * action is called filtering. 2015 * 2016 * @param[in] ExistingTokenHandle 2017 * A handle to an access token which is to be filtered. 2018 * 2019 * @param[in] Flags 2020 * Privilege flag options. This parameter argument influences how the 2021 * token's privileges are filtered. For further details see remarks. 2022 * 2023 * @param[in] SidsToDisable 2024 * Array of SIDs to disable. The action of doing so assigns the 2025 * SE_GROUP_USE_FOR_DENY_ONLY attribute to the respective group 2026 * SID and takes away SE_GROUP_ENABLED and SE_GROUP_ENABLED_BY_DEFAULT. 2027 * This parameter can be NULL. This can be a UM pointer. 2028 * 2029 * @param[in] PrivilegesToDelete 2030 * Array of privileges to delete. The function will walk within this 2031 * array to determine if the specified privileges do exist in the 2032 * access token. Any missing privileges gets ignored. This parameter 2033 * can be NULL. This can be a UM pointer. 2034 * 2035 * @param[in] RestrictedSids 2036 * An array list of restricted groups SID to be added in the access 2037 * token. A token that is already restricted the newly added restricted 2038 * SIDs are redundant information in addition to the existing restricted 2039 * SIDs in the token. This parameter can be NULL. This can be a UM pointer. 2040 * 2041 * @param[out] NewTokenHandle 2042 * A new handle to the restricted (filtered) access token. This can be a 2043 * UM pointer. 2044 * 2045 * @return 2046 * Returns STATUS_SUCCESS if the routine has successfully filtered the 2047 * access token. STATUS_INVALID_PARAMETER is returned if one or more 2048 * parameters are not valid (see SepPerformTokenFiltering routine call 2049 * for more information). A failure NTSTATUS code is returned otherwise. 2050 * 2051 * @remarks 2052 * The Flags parameter determines the final outcome of how the privileges 2053 * in an access token are filtered. This parameter can take these supported 2054 * values (these can be combined): 2055 * 2056 * 0 -- Filter the token's privileges in the usual way. The function expects 2057 * that the caller MUST PROVIDE a valid array list of privileges to be 2058 * deleted (that is, PrivilegesToDelete MUSTN'T BE NULL). 2059 * 2060 * DISABLE_MAX_PRIVILEGE -- Disables (deletes) all the privileges except SeChangeNotifyPrivilege 2061 * in the new access token. Bear in mind if this flag is specified 2062 * the routine ignores PrivilegesToDelete. 2063 * 2064 * SANDBOX_INERT -- Stores the TOKEN_SANDBOX_INERT token flag within the access token. 2065 * 2066 * LUA_TOKEN -- The newly filtered access token is a LUA token. This flag is not 2067 * supported in Windows Server 2003. 2068 * 2069 * WRITE_RESTRICTED -- The newly filtered token has the restricted SIDs that are 2070 * considered only when evaluating write access onto the token. 2071 * This value is not supported in Windows Server 2003. 2072 */ 2073 NTSTATUS 2074 NTAPI 2075 NtFilterToken( 2076 _In_ HANDLE ExistingTokenHandle, 2077 _In_ ULONG Flags, 2078 _In_opt_ PTOKEN_GROUPS SidsToDisable, 2079 _In_opt_ PTOKEN_PRIVILEGES PrivilegesToDelete, 2080 _In_opt_ PTOKEN_GROUPS RestrictedSids, 2081 _Out_ PHANDLE NewTokenHandle) 2082 { 2083 PTOKEN Token, FilteredToken; 2084 HANDLE FilteredTokenHandle; 2085 NTSTATUS Status; 2086 KPROCESSOR_MODE PreviousMode; 2087 OBJECT_HANDLE_INFORMATION HandleInfo; 2088 ULONG ResultLength; 2089 ULONG CapturedSidsCount = 0; 2090 ULONG CapturedPrivilegesCount = 0; 2091 ULONG CapturedRestrictedSidsCount = 0; 2092 ULONG ProbeSize = 0; 2093 PSID_AND_ATTRIBUTES CapturedSids = NULL; 2094 PSID_AND_ATTRIBUTES CapturedRestrictedSids = NULL; 2095 PLUID_AND_ATTRIBUTES CapturedPrivileges = NULL; 2096 2097 PAGED_CODE(); 2098 2099 PreviousMode = ExGetPreviousMode(); 2100 2101 _SEH2_TRY 2102 { 2103 /* Probe SidsToDisable */ 2104 if (SidsToDisable != NULL) 2105 { 2106 /* Probe the header */ 2107 ProbeForRead(SidsToDisable, sizeof(*SidsToDisable), sizeof(ULONG)); 2108 2109 CapturedSidsCount = SidsToDisable->GroupCount; 2110 ProbeSize = FIELD_OFFSET(TOKEN_GROUPS, Groups[CapturedSidsCount]); 2111 2112 ProbeForRead(SidsToDisable, ProbeSize, sizeof(ULONG)); 2113 } 2114 2115 /* Probe PrivilegesToDelete */ 2116 if (PrivilegesToDelete != NULL) 2117 { 2118 /* Probe the header */ 2119 ProbeForRead(PrivilegesToDelete, sizeof(*PrivilegesToDelete), sizeof(ULONG)); 2120 2121 CapturedPrivilegesCount = PrivilegesToDelete->PrivilegeCount; 2122 ProbeSize = FIELD_OFFSET(TOKEN_PRIVILEGES, Privileges[CapturedPrivilegesCount]); 2123 2124 ProbeForRead(PrivilegesToDelete, ProbeSize, sizeof(ULONG)); 2125 } 2126 2127 /* Probe RestrictedSids */ 2128 if (RestrictedSids != NULL) 2129 { 2130 /* Probe the header */ 2131 ProbeForRead(RestrictedSids, sizeof(*RestrictedSids), sizeof(ULONG)); 2132 2133 CapturedRestrictedSidsCount = RestrictedSids->GroupCount; 2134 ProbeSize = FIELD_OFFSET(TOKEN_GROUPS, Groups[CapturedRestrictedSidsCount]); 2135 2136 ProbeForRead(RestrictedSids, ProbeSize, sizeof(ULONG)); 2137 } 2138 2139 /* Probe the handle */ 2140 ProbeForWriteHandle(NewTokenHandle); 2141 } 2142 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 2143 { 2144 /* Return the exception code */ 2145 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 2146 } 2147 _SEH2_END; 2148 2149 /* Reference the token */ 2150 Status = ObReferenceObjectByHandle(ExistingTokenHandle, 2151 TOKEN_DUPLICATE, 2152 SeTokenObjectType, 2153 PreviousMode, 2154 (PVOID*)&Token, 2155 &HandleInfo); 2156 if (!NT_SUCCESS(Status)) 2157 { 2158 DPRINT1("NtFilterToken(): Failed to reference the token (Status 0x%lx)\n", Status); 2159 return Status; 2160 } 2161 2162 /* Capture the group SIDs */ 2163 if (SidsToDisable != NULL) 2164 { 2165 Status = SeCaptureSidAndAttributesArray(SidsToDisable->Groups, 2166 CapturedSidsCount, 2167 PreviousMode, 2168 NULL, 2169 0, 2170 PagedPool, 2171 TRUE, 2172 &CapturedSids, 2173 &ResultLength); 2174 if (!NT_SUCCESS(Status)) 2175 { 2176 DPRINT1("NtFilterToken(): Failed to capture the SIDs (Status 0x%lx)\n", Status); 2177 goto Quit; 2178 } 2179 } 2180 2181 /* Capture the privileges */ 2182 if (PrivilegesToDelete != NULL) 2183 { 2184 Status = SeCaptureLuidAndAttributesArray(PrivilegesToDelete->Privileges, 2185 CapturedPrivilegesCount, 2186 PreviousMode, 2187 NULL, 2188 0, 2189 PagedPool, 2190 TRUE, 2191 &CapturedPrivileges, 2192 &ResultLength); 2193 if (!NT_SUCCESS(Status)) 2194 { 2195 DPRINT1("NtFilterToken(): Failed to capture the privileges (Status 0x%lx)\n", Status); 2196 goto Quit; 2197 } 2198 } 2199 2200 /* Capture the restricted SIDs */ 2201 if (RestrictedSids != NULL) 2202 { 2203 Status = SeCaptureSidAndAttributesArray(RestrictedSids->Groups, 2204 CapturedRestrictedSidsCount, 2205 PreviousMode, 2206 NULL, 2207 0, 2208 PagedPool, 2209 TRUE, 2210 &CapturedRestrictedSids, 2211 &ResultLength); 2212 if (!NT_SUCCESS(Status)) 2213 { 2214 DPRINT1("NtFilterToken(): Failed to capture the restricted SIDs (Status 0x%lx)\n", Status); 2215 goto Quit; 2216 } 2217 } 2218 2219 /* Call the internal API */ 2220 Status = SepPerformTokenFiltering(Token, 2221 CapturedPrivileges, 2222 CapturedSids, 2223 CapturedRestrictedSids, 2224 CapturedPrivilegesCount, 2225 CapturedSidsCount, 2226 CapturedRestrictedSidsCount, 2227 Flags, 2228 PreviousMode, 2229 &FilteredToken); 2230 if (!NT_SUCCESS(Status)) 2231 { 2232 DPRINT1("NtFilterToken(): Failed to filter the token (Status 0x%lx)\n", Status); 2233 goto Quit; 2234 } 2235 2236 /* Insert the filtered token and retrieve a handle to it */ 2237 Status = ObInsertObject(FilteredToken, 2238 NULL, 2239 HandleInfo.GrantedAccess, 2240 0, 2241 NULL, 2242 &FilteredTokenHandle); 2243 if (!NT_SUCCESS(Status)) 2244 { 2245 DPRINT1("NtFilterToken(): Failed to insert the filtered token (Status 0x%lx)\n", Status); 2246 goto Quit; 2247 } 2248 2249 /* And return it to the caller once we're done */ 2250 _SEH2_TRY 2251 { 2252 *NewTokenHandle = FilteredTokenHandle; 2253 } 2254 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 2255 { 2256 Status = _SEH2_GetExceptionCode(); 2257 _SEH2_YIELD(goto Quit); 2258 } 2259 _SEH2_END; 2260 2261 Quit: 2262 /* Dereference the token */ 2263 ObDereferenceObject(Token); 2264 2265 /* Release all the captured data */ 2266 if (CapturedSids != NULL) 2267 { 2268 SeReleaseSidAndAttributesArray(CapturedSids, 2269 PreviousMode, 2270 TRUE); 2271 } 2272 2273 if (CapturedPrivileges != NULL) 2274 { 2275 SeReleaseLuidAndAttributesArray(CapturedPrivileges, 2276 PreviousMode, 2277 TRUE); 2278 } 2279 2280 if (CapturedRestrictedSids != NULL) 2281 { 2282 SeReleaseSidAndAttributesArray(CapturedRestrictedSids, 2283 PreviousMode, 2284 TRUE); 2285 } 2286 2287 return Status; 2288 } 2289 2290 /* EOF */ 2291