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 /* 669 * Filter the token by removing the disabled privileges 670 * and groups if the caller wants to duplicate an access 671 * token as effective only. 672 */ 673 if (EffectiveOnly) 674 { 675 /* Begin querying the groups and search for disabled ones */ 676 for (GroupsIndex = 0; GroupsIndex < AccessToken->UserAndGroupCount; GroupsIndex++) 677 { 678 /* 679 * A group or user is considered disabled if its attributes is either 680 * 0 or SE_GROUP_ENABLED is not included in the attributes flags list. 681 * That is because a certain user and/or group can have several attributes 682 * that bear no influence on whether a user/group is enabled or not 683 * (SE_GROUP_ENABLED_BY_DEFAULT for example which is a mere indicator 684 * that the group has just been enabled by default). A mandatory 685 * group (that is, the group has SE_GROUP_MANDATORY attribute) 686 * by standards it's always enabled and no one can disable it. 687 */ 688 if (AccessToken->UserAndGroups[GroupsIndex].Attributes == 0 || 689 (AccessToken->UserAndGroups[GroupsIndex].Attributes & SE_GROUP_ENABLED) == 0) 690 { 691 /* 692 * If this group is an administrators group 693 * and the token belongs to such group, 694 * we've to take away TOKEN_HAS_ADMIN_GROUP 695 * for the fact that's not enabled and as 696 * such the token no longer belongs to 697 * this group. 698 */ 699 if (RtlEqualSid(SeAliasAdminsSid, 700 &AccessToken->UserAndGroups[GroupsIndex].Sid)) 701 { 702 AccessToken->TokenFlags &= ~TOKEN_HAS_ADMIN_GROUP; 703 } 704 705 /* 706 * A group is not enabled, it's time to remove 707 * from the token and update the groups index 708 * accordingly and continue with the next group. 709 */ 710 SepRemoveUserGroupToken(AccessToken, GroupsIndex); 711 GroupsIndex--; 712 } 713 } 714 715 /* Begin querying the privileges and search for disabled ones */ 716 for (PrivilegesIndex = 0; PrivilegesIndex < AccessToken->PrivilegeCount; PrivilegesIndex++) 717 { 718 /* 719 * A privilege is considered disabled if its attributes is either 720 * 0 or SE_PRIVILEGE_ENABLED is not included in the attributes flags list. 721 * That is because a certain privilege can have several attributes 722 * that bear no influence on whether a privilege is enabled or not 723 * (SE_PRIVILEGE_ENABLED_BY_DEFAULT for example which is a mere indicator 724 * that the privilege has just been enabled by default). 725 */ 726 if (AccessToken->Privileges[PrivilegesIndex].Attributes == 0 || 727 (AccessToken->Privileges[PrivilegesIndex].Attributes & SE_PRIVILEGE_ENABLED) == 0) 728 { 729 /* 730 * A privilege is not enabled, therefor it's time 731 * to strip it from the token and continue with the next 732 * privilege. Of course we must also want to update the 733 * privileges index accordingly. 734 */ 735 SepRemovePrivilegeToken(AccessToken, PrivilegesIndex); 736 PrivilegesIndex--; 737 } 738 } 739 } 740 741 /* Now allocate the token's dynamic information area and set the data */ 742 AccessToken->DynamicPart = ExAllocatePoolWithTag(PagedPool, 743 DynamicPartSize, 744 TAG_TOKEN_DYNAMIC); 745 if (AccessToken->DynamicPart == NULL) 746 { 747 Status = STATUS_INSUFFICIENT_RESOURCES; 748 goto Quit; 749 } 750 751 /* Unused memory in the dynamic area */ 752 AccessToken->DynamicAvailable = 0; 753 754 /* 755 * Assign the primary group to the token 756 * and put it in the dynamic part as well. 757 */ 758 EndMem = (PVOID)AccessToken->DynamicPart; 759 AccessToken->PrimaryGroup = EndMem; 760 RtlCopySid(RtlLengthSid(AccessToken->UserAndGroups[PrimaryGroupIndex].Sid), 761 EndMem, 762 AccessToken->UserAndGroups[PrimaryGroupIndex].Sid); 763 AccessToken->DefaultOwnerIndex = Token->DefaultOwnerIndex; 764 EndMem = (PVOID)((ULONG_PTR)EndMem + RtlLengthSid(AccessToken->UserAndGroups[PrimaryGroupIndex].Sid)); 765 766 /* 767 * The existing token has a default DACL only 768 * if it has an allocated dynamic part. 769 */ 770 if (Token->DynamicPart && Token->DefaultDacl) 771 { 772 AccessToken->DefaultDacl = EndMem; 773 774 RtlCopyMemory(EndMem, 775 Token->DefaultDacl, 776 Token->DefaultDacl->AclSize); 777 } 778 779 /* Return the token to the caller */ 780 *NewAccessToken = AccessToken; 781 Status = STATUS_SUCCESS; 782 783 Quit: 784 if (!NT_SUCCESS(Status)) 785 { 786 /* Dereference the token, the delete procedure will clean it up */ 787 ObDereferenceObject(AccessToken); 788 } 789 790 /* Unlock the source token */ 791 SepReleaseTokenLock(Token); 792 793 return Status; 794 } 795 796 /** 797 * @brief 798 * Private helper function responsible for creating a restricted access 799 * token, that is, a filtered token from privileges and groups and with 800 * restricted SIDs added into the token on demand by the caller. 801 * 802 * @param[in] Token 803 * An existing and valid access token. 804 * 805 * @param[in] PrivilegesToBeDeleted 806 * A list of privileges to be deleted within the token that's going 807 * to be filtered. This parameter is ignored if the caller wants to disable 808 * all the privileges by specifying DISABLE_MAX_PRIVILEGE in the flags 809 * parameter. 810 * 811 * @param[in] SidsToBeDisabled 812 * A list of group SIDs to be disabled within the token. This parameter 813 * can be NULL. 814 * 815 * @param[in] RestrictedSidsIntoToken 816 * A list of restricted SIDs to be added into the token. This parameter 817 * can be NULL. 818 * 819 * @param[in] PrivilegesCount 820 * The privilege count of the privileges list. 821 * 822 * @param[in] RegularGroupsSidCount 823 * The SIDs count of the group SIDs list. 824 * 825 * @param[in] RestrictedSidsCount 826 * The restricted SIDs count of restricted SIDs list. 827 * 828 * @param[in] PrivilegeFlags 829 * Influences how the privileges should be filtered in an access 830 * token. See NtFilterToken syscall for more information. 831 * 832 * @param[in] PreviousMode 833 * Processor level access mode. 834 * 835 * @param[out] FilteredToken 836 * The filtered token, returned to the caller. 837 * 838 * @return 839 * Returns STATUS_SUCCESS if token token filtering has completed successfully. 840 * STATUS_INVALID_PARAMETER is returned if one or more of the parameters 841 * do not meet the conditions imposed by the function. A failure NTSTATUS 842 * code is returned otherwise. 843 * 844 * @remarks 845 * The final outcome of privileges and/or SIDs filtering is not always 846 * deterministic. That is, any privileges or SIDs that aren't present 847 * in the access token are ignored and the function continues with the 848 * next privilege or SID to find for filtering. For a fully deterministic 849 * outcome the caller is responsible for querying the information details 850 * of privileges and SIDs present in the token and then afterwards use 851 * such obtained information to do any kind of filtering to the token. 852 */ 853 static 854 NTSTATUS 855 SepPerformTokenFiltering( 856 _In_ PTOKEN Token, 857 _In_opt_ PLUID_AND_ATTRIBUTES PrivilegesToBeDeleted, 858 _In_opt_ PSID_AND_ATTRIBUTES SidsToBeDisabled, 859 _In_opt_ PSID_AND_ATTRIBUTES RestrictedSidsIntoToken, 860 _When_(PrivilegesToBeDeleted != NULL, _In_) ULONG PrivilegesCount, 861 _When_(SidsToBeDisabled != NULL, _In_) ULONG RegularGroupsSidCount, 862 _When_(RestrictedSidsIntoToken != NULL, _In_) ULONG RestrictedSidsCount, 863 _In_ ULONG PrivilegeFlags, 864 _In_ KPROCESSOR_MODE PreviousMode, 865 _Out_ PTOKEN *FilteredToken) 866 { 867 NTSTATUS Status; 868 PTOKEN AccessToken; 869 PVOID EndMem; 870 ULONG DynamicPartSize; 871 ULONG RestrictedSidsLength; 872 ULONG PrivilegesLength; 873 ULONG PrimaryGroupIndex; 874 ULONG RestrictedSidsInList; 875 ULONG RestrictedSidsInToken; 876 ULONG VariableLength, TotalSize; 877 ULONG PrivsInToken, PrivsInList; 878 ULONG GroupsInToken, GroupsInList; 879 BOOLEAN WantPrivilegesDisabled; 880 BOOLEAN FoundPrivilege; 881 BOOLEAN FoundGroup; 882 883 PAGED_CODE(); 884 885 /* Ensure that the source token is valid, and lock it */ 886 ASSERT(Token); 887 SepAcquireTokenLockShared(Token); 888 889 /* Assume the caller doesn't want privileges disabled */ 890 WantPrivilegesDisabled = FALSE; 891 892 /* Assume we haven't found anything */ 893 FoundPrivilege = FALSE; 894 FoundGroup = FALSE; 895 896 /* 897 * Take the size that we need for filtered token 898 * allocation based upon the existing access token 899 * we've been given. 900 */ 901 VariableLength = Token->VariableLength; 902 903 if (RestrictedSidsIntoToken != NULL) 904 { 905 /* 906 * If the caller provided a list of restricted SIDs 907 * to be added onto the filtered access token then 908 * we must compute the size which is the total space 909 * of the current token and the length of the restricted 910 * SIDs for the filtered token. 911 */ 912 RestrictedSidsLength = RestrictedSidsCount * sizeof(SID_AND_ATTRIBUTES); 913 RestrictedSidsLength += RtlLengthSidAndAttributes(RestrictedSidsCount, RestrictedSidsIntoToken); 914 RestrictedSidsLength = ALIGN_UP_BY(RestrictedSidsLength, sizeof(PVOID)); 915 916 /* 917 * The variable length of the token is not just 918 * the actual space length of the existing token 919 * but also the sum of the restricted SIDs length. 920 */ 921 VariableLength += RestrictedSidsLength; 922 TotalSize = FIELD_OFFSET(TOKEN, VariablePart) + VariableLength + RestrictedSidsLength; 923 } 924 else 925 { 926 /* Otherwise the size is of the actual current token */ 927 TotalSize = FIELD_OFFSET(TOKEN, VariablePart) + VariableLength; 928 } 929 930 /* 931 * Compute how much size we need to allocate 932 * the dynamic part of the newly duplicated 933 * token. 934 */ 935 DynamicPartSize = Token->DefaultDacl ? Token->DefaultDacl->AclSize : 0; 936 DynamicPartSize += RtlLengthSid(Token->PrimaryGroup); 937 938 /* Set up a filtered token object */ 939 Status = ObCreateObject(PreviousMode, 940 SeTokenObjectType, 941 NULL, 942 PreviousMode, 943 NULL, 944 TotalSize, 945 Token->DynamicCharged, 946 TotalSize, 947 (PVOID*)&AccessToken); 948 if (!NT_SUCCESS(Status)) 949 { 950 DPRINT1("SepPerformTokenFiltering(): Failed to create the filtered token object (Status 0x%lx)\n", Status); 951 952 /* Unlock the source token and bail out */ 953 SepReleaseTokenLock(Token); 954 return Status; 955 } 956 957 /* Initialize the token and begin filling stuff to it */ 958 RtlZeroMemory(AccessToken, TotalSize); 959 960 /* Set up a lock for the new token */ 961 Status = SepCreateTokenLock(AccessToken); 962 if (!NT_SUCCESS(Status)) 963 goto Quit; 964 965 /* Allocate new IDs for the token */ 966 ExAllocateLocallyUniqueId(&AccessToken->TokenId); 967 ExAllocateLocallyUniqueId(&AccessToken->ModifiedId); 968 969 /* Copy the type and impersonation level from the token */ 970 AccessToken->TokenType = Token->TokenType; 971 AccessToken->ImpersonationLevel = Token->ImpersonationLevel; 972 973 /* Copy the immutable fields */ 974 AccessToken->TokenSource.SourceIdentifier = Token->TokenSource.SourceIdentifier; 975 RtlCopyMemory(AccessToken->TokenSource.SourceName, 976 Token->TokenSource.SourceName, 977 sizeof(Token->TokenSource.SourceName)); 978 979 AccessToken->AuthenticationId = Token->AuthenticationId; 980 AccessToken->ParentTokenId = Token->TokenId; 981 AccessToken->OriginatingLogonSession = Token->OriginatingLogonSession; 982 AccessToken->DynamicCharged = Token->DynamicCharged; 983 984 AccessToken->ExpirationTime = Token->ExpirationTime; 985 986 /* Copy the mutable fields */ 987 AccessToken->SessionId = Token->SessionId; 988 AccessToken->TokenFlags = Token->TokenFlags & ~TOKEN_SESSION_NOT_REFERENCED; 989 990 /* Reference the logon session */ 991 Status = SepRmReferenceLogonSession(&AccessToken->AuthenticationId); 992 if (!NT_SUCCESS(Status)) 993 { 994 /* We failed, bail out*/ 995 DPRINT1("SepPerformTokenFiltering(): Failed to reference the logon session (Status 0x%lx)\n", Status); 996 AccessToken->TokenFlags |= TOKEN_SESSION_NOT_REFERENCED; 997 goto Quit; 998 } 999 1000 /* Insert the referenced logon session into the token */ 1001 Status = SepRmInsertLogonSessionIntoToken(AccessToken); 1002 if (!NT_SUCCESS(Status)) 1003 { 1004 /* Failed to insert the logon session into the token, bail out */ 1005 DPRINT1("SepPerformTokenFiltering(): Failed to insert the logon session into token (Status 0x%lx)\n", Status); 1006 goto Quit; 1007 } 1008 1009 /* Fill in token debug information */ 1010 #if DBG 1011 RtlCopyMemory(AccessToken->ImageFileName, 1012 PsGetCurrentProcess()->ImageFileName, 1013 min(sizeof(AccessToken->ImageFileName), sizeof(PsGetCurrentProcess()->ImageFileName))); 1014 1015 AccessToken->ProcessCid = PsGetCurrentProcessId(); 1016 AccessToken->ThreadCid = PsGetCurrentThreadId(); 1017 AccessToken->CreateMethod = TOKEN_FILTER_METHOD; 1018 #endif 1019 1020 /* Assign the data that reside in the token's variable information area */ 1021 AccessToken->VariableLength = VariableLength; 1022 EndMem = (PVOID)&AccessToken->VariablePart; 1023 1024 /* Copy the privileges from the existing token */ 1025 AccessToken->PrivilegeCount = 0; 1026 AccessToken->Privileges = NULL; 1027 if (Token->Privileges && (Token->PrivilegeCount > 0)) 1028 { 1029 PrivilegesLength = Token->PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES); 1030 PrivilegesLength = ALIGN_UP_BY(PrivilegesLength, sizeof(PVOID)); 1031 1032 /* 1033 * Ensure that the token can actually hold all 1034 * the privileges from the existing token. 1035 * Otherwise something's seriously wrong and 1036 * we've to guard ourselves. 1037 */ 1038 ASSERT(VariableLength >= PrivilegesLength); 1039 1040 AccessToken->PrivilegeCount = Token->PrivilegeCount; 1041 AccessToken->Privileges = EndMem; 1042 EndMem = (PVOID)((ULONG_PTR)EndMem + PrivilegesLength); 1043 VariableLength -= PrivilegesLength; 1044 1045 RtlCopyMemory(AccessToken->Privileges, 1046 Token->Privileges, 1047 AccessToken->PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES)); 1048 } 1049 1050 /* Copy the user and groups */ 1051 AccessToken->UserAndGroupCount = 0; 1052 AccessToken->UserAndGroups = NULL; 1053 if (Token->UserAndGroups && (Token->UserAndGroupCount > 0)) 1054 { 1055 AccessToken->UserAndGroupCount = Token->UserAndGroupCount; 1056 AccessToken->UserAndGroups = EndMem; 1057 EndMem = &AccessToken->UserAndGroups[AccessToken->UserAndGroupCount]; 1058 VariableLength -= ((ULONG_PTR)EndMem - (ULONG_PTR)AccessToken->UserAndGroups); 1059 1060 Status = RtlCopySidAndAttributesArray(AccessToken->UserAndGroupCount, 1061 Token->UserAndGroups, 1062 VariableLength, 1063 AccessToken->UserAndGroups, 1064 EndMem, 1065 &EndMem, 1066 &VariableLength); 1067 if (!NT_SUCCESS(Status)) 1068 { 1069 DPRINT1("SepPerformTokenFiltering(): Failed to copy the groups into token (Status 0x%lx)\n", Status); 1070 goto Quit; 1071 } 1072 } 1073 1074 /* Copy the restricted SIDs */ 1075 AccessToken->RestrictedSidCount = 0; 1076 AccessToken->RestrictedSids = NULL; 1077 if (Token->RestrictedSids && (Token->RestrictedSidCount > 0)) 1078 { 1079 AccessToken->RestrictedSidCount = Token->RestrictedSidCount; 1080 AccessToken->RestrictedSids = EndMem; 1081 EndMem = &AccessToken->RestrictedSids[AccessToken->RestrictedSidCount]; 1082 VariableLength -= ((ULONG_PTR)EndMem - (ULONG_PTR)AccessToken->RestrictedSids); 1083 1084 Status = RtlCopySidAndAttributesArray(AccessToken->RestrictedSidCount, 1085 Token->RestrictedSids, 1086 VariableLength, 1087 AccessToken->RestrictedSids, 1088 EndMem, 1089 &EndMem, 1090 &VariableLength); 1091 if (!NT_SUCCESS(Status)) 1092 { 1093 DPRINT1("SepPerformTokenFiltering(): Failed to copy the restricted SIDs into token (Status 0x%lx)\n", Status); 1094 goto Quit; 1095 } 1096 } 1097 1098 /* 1099 * Insert the restricted SIDs into the token on 1100 * the request by the caller. 1101 */ 1102 if (RestrictedSidsIntoToken != NULL) 1103 { 1104 for (RestrictedSidsInList = 0; RestrictedSidsInList < RestrictedSidsCount; RestrictedSidsInList++) 1105 { 1106 /* Did the caller assign attributes to the restricted SIDs? */ 1107 if (RestrictedSidsIntoToken[RestrictedSidsInList].Attributes != 0) 1108 { 1109 /* There mustn't be any attributes, bail out */ 1110 DPRINT1("SepPerformTokenFiltering(): There mustn't be any attributes to restricted SIDs!\n"); 1111 Status = STATUS_INVALID_PARAMETER; 1112 goto Quit; 1113 } 1114 } 1115 1116 /* 1117 * Ensure that the token can hold the restricted SIDs 1118 * (the variable length is calculated at the beginning 1119 * of the routine call). 1120 */ 1121 ASSERT(VariableLength >= RestrictedSidsLength); 1122 1123 /* 1124 * Now let's begin inserting the restricted SIDs into the filtered 1125 * access token from the list the caller gave us. 1126 */ 1127 AccessToken->RestrictedSidCount = RestrictedSidsCount; 1128 AccessToken->RestrictedSids = EndMem; 1129 EndMem = (PVOID)((ULONG_PTR)EndMem + RestrictedSidsLength); 1130 VariableLength -= RestrictedSidsLength; 1131 1132 RtlCopyMemory(AccessToken->RestrictedSids, 1133 RestrictedSidsIntoToken, 1134 AccessToken->RestrictedSidCount * sizeof(SID_AND_ATTRIBUTES)); 1135 1136 /* 1137 * As we've copied the restricted SIDs into 1138 * the token, we must assign them the following 1139 * combination of attributes SE_GROUP_ENABLED, 1140 * SE_GROUP_ENABLED_BY_DEFAULT and SE_GROUP_MANDATORY. 1141 * With such attributes we estabilish that restricting 1142 * SIDs into the token are enabled for access checks. 1143 */ 1144 for (RestrictedSidsInToken = 0; RestrictedSidsInToken < AccessToken->RestrictedSidCount; RestrictedSidsInToken++) 1145 { 1146 AccessToken->RestrictedSids[RestrictedSidsInToken].Attributes |= (SE_GROUP_ENABLED | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_MANDATORY); 1147 } 1148 1149 /* 1150 * As we added restricted SIDs into the token, mark 1151 * it as restricted. 1152 */ 1153 AccessToken->TokenFlags |= TOKEN_IS_RESTRICTED; 1154 } 1155 1156 /* Search for the primary group */ 1157 Status = SepFindPrimaryGroupAndDefaultOwner(AccessToken, 1158 Token->PrimaryGroup, 1159 NULL, 1160 &PrimaryGroupIndex, 1161 NULL); 1162 if (!NT_SUCCESS(Status)) 1163 { 1164 DPRINT1("SepPerformTokenFiltering(): Failed searching for the primary group (Status 0x%lx)\n", Status); 1165 goto Quit; 1166 } 1167 1168 /* Now allocate the token's dynamic information area and set the data */ 1169 AccessToken->DynamicPart = ExAllocatePoolWithTag(PagedPool, 1170 DynamicPartSize, 1171 TAG_TOKEN_DYNAMIC); 1172 if (AccessToken->DynamicPart == NULL) 1173 { 1174 Status = STATUS_INSUFFICIENT_RESOURCES; 1175 goto Quit; 1176 } 1177 1178 /* Unused memory in the dynamic area */ 1179 AccessToken->DynamicAvailable = 0; 1180 1181 /* 1182 * Assign the primary group to the token 1183 * and put it in the dynamic part as well. 1184 */ 1185 EndMem = (PVOID)AccessToken->DynamicPart; 1186 AccessToken->PrimaryGroup = EndMem; 1187 RtlCopySid(RtlLengthSid(AccessToken->UserAndGroups[PrimaryGroupIndex].Sid), 1188 EndMem, 1189 AccessToken->UserAndGroups[PrimaryGroupIndex].Sid); 1190 AccessToken->DefaultOwnerIndex = Token->DefaultOwnerIndex; 1191 EndMem = (PVOID)((ULONG_PTR)EndMem + RtlLengthSid(AccessToken->UserAndGroups[PrimaryGroupIndex].Sid)); 1192 1193 /* 1194 * The existing token has a default DACL only 1195 * if it has an allocated dynamic part. 1196 */ 1197 if (Token->DynamicPart && Token->DefaultDacl) 1198 { 1199 AccessToken->DefaultDacl = EndMem; 1200 1201 RtlCopyMemory(EndMem, 1202 Token->DefaultDacl, 1203 Token->DefaultDacl->AclSize); 1204 } 1205 1206 /* 1207 * Now figure out what does the caller 1208 * want with the privileges. 1209 */ 1210 if (PrivilegeFlags & DISABLE_MAX_PRIVILEGE) 1211 { 1212 /* 1213 * The caller wants them disabled, cache this request 1214 * for later operations. 1215 */ 1216 WantPrivilegesDisabled = TRUE; 1217 } 1218 1219 if (PrivilegeFlags & SANDBOX_INERT) 1220 { 1221 /* The caller wants an inert token, store the TOKEN_SANDBOX_INERT flag now */ 1222 AccessToken->TokenFlags |= TOKEN_SANDBOX_INERT; 1223 } 1224 1225 /* 1226 * Now it's time to filter the token's privileges. 1227 * Loop all the privileges in the token. 1228 */ 1229 for (PrivsInToken = 0; PrivsInToken < AccessToken->PrivilegeCount; PrivsInToken++) 1230 { 1231 if (WantPrivilegesDisabled) 1232 { 1233 /* 1234 * We got the acknowledgement that the caller wants 1235 * to disable all the privileges so let's just do it. 1236 * However, as per the general documentation is stated 1237 * that only SE_CHANGE_NOTIFY_PRIVILEGE must be kept 1238 * therefore in that case we must skip this privilege. 1239 */ 1240 if (AccessToken->Privileges[PrivsInToken].Luid.LowPart == SE_CHANGE_NOTIFY_PRIVILEGE) 1241 { 1242 continue; 1243 } 1244 else 1245 { 1246 /* 1247 * The act of disabling privileges actually means 1248 * "deleting" them from the access token entirely. 1249 * First we must disable them so that we can update 1250 * token flags accordingly. 1251 */ 1252 AccessToken->Privileges[PrivsInToken].Attributes &= ~SE_PRIVILEGE_ENABLED; 1253 SepUpdateSinglePrivilegeFlagToken(AccessToken, PrivsInToken); 1254 1255 /* Remove the privileges now */ 1256 SepRemovePrivilegeToken(AccessToken, PrivsInToken); 1257 PrivsInToken--; 1258 } 1259 } 1260 else 1261 { 1262 if (PrivilegesToBeDeleted != NULL) 1263 { 1264 /* Loop the privileges we've got to delete */ 1265 for (PrivsInList = 0; PrivsInList < PrivilegesCount; PrivsInList++) 1266 { 1267 /* Does this privilege exist in the token? */ 1268 if (RtlEqualLuid(&AccessToken->Privileges[PrivsInToken].Luid, 1269 &PrivilegesToBeDeleted[PrivsInList].Luid)) 1270 { 1271 /* Mark that we found it */ 1272 FoundPrivilege = TRUE; 1273 break; 1274 } 1275 } 1276 1277 /* Did we find the privilege? */ 1278 if (PrivsInList == PrivilegesCount) 1279 { 1280 /* We didn't, continue with next one */ 1281 continue; 1282 } 1283 } 1284 } 1285 1286 /* 1287 * If we have found the target privilege in the token 1288 * based on the privileges list given by the caller 1289 * then begin deleting it. 1290 */ 1291 if (FoundPrivilege) 1292 { 1293 /* Disable the privilege and update the flags */ 1294 AccessToken->Privileges[PrivsInToken].Attributes &= ~SE_PRIVILEGE_ENABLED; 1295 SepUpdateSinglePrivilegeFlagToken(AccessToken, PrivsInToken); 1296 1297 /* Delete the privilege */ 1298 SepRemovePrivilegeToken(AccessToken, PrivsInToken); 1299 1300 /* 1301 * Adjust the index and reset the FoundPrivilege indicator 1302 * so that we can continue with the next privilege to delete. 1303 */ 1304 PrivsInToken--; 1305 FoundPrivilege = FALSE; 1306 continue; 1307 } 1308 } 1309 1310 /* 1311 * Loop the group SIDs that we want to disable as 1312 * per on the request by the caller. 1313 */ 1314 if (SidsToBeDisabled != NULL) 1315 { 1316 for (GroupsInToken = 0; GroupsInToken < AccessToken->UserAndGroupCount; GroupsInToken++) 1317 { 1318 for (GroupsInList = 0; GroupsInList < RegularGroupsSidCount; GroupsInList++) 1319 { 1320 /* Does this group SID exist in the token? */ 1321 if (RtlEqualSid(&AccessToken->UserAndGroups[GroupsInToken].Sid, 1322 &SidsToBeDisabled[GroupsInList].Sid)) 1323 { 1324 /* Mark that we found it */ 1325 FoundGroup = TRUE; 1326 break; 1327 } 1328 } 1329 1330 /* Did we find the group? */ 1331 if (GroupsInList == RegularGroupsSidCount) 1332 { 1333 /* We didn't, continue with next one */ 1334 continue; 1335 } 1336 1337 /* If we have found the group, disable it */ 1338 if (FoundGroup) 1339 { 1340 /* 1341 * If the acess token belongs to the administrators 1342 * group and this is the target group, we must take 1343 * away TOKEN_HAS_ADMIN_GROUP flag from the token. 1344 */ 1345 if (RtlEqualSid(SeAliasAdminsSid, 1346 &AccessToken->UserAndGroups[GroupsInToken].Sid)) 1347 { 1348 AccessToken->TokenFlags &= ~TOKEN_HAS_ADMIN_GROUP; 1349 } 1350 1351 /* 1352 * If the target group that we have found it is the 1353 * owner then from now on it no longer is but the user. 1354 * Therefore assign the default owner index as the user. 1355 */ 1356 if (AccessToken->DefaultOwnerIndex == GroupsInToken) 1357 { 1358 AccessToken->DefaultOwnerIndex = 0; 1359 } 1360 1361 /* 1362 * The principle of disabling a group SID is by 1363 * taking away SE_GROUP_ENABLED_BY_DEFAULT and 1364 * SE_GROUP_ENABLED attributes and assign 1365 * SE_GROUP_USE_FOR_DENY_ONLY. This renders 1366 * SID a "Deny only" SID. 1367 */ 1368 AccessToken->UserAndGroups[GroupsInToken].Attributes &= ~(SE_GROUP_ENABLED | SE_GROUP_ENABLED_BY_DEFAULT); 1369 AccessToken->UserAndGroups[GroupsInToken].Attributes |= SE_GROUP_USE_FOR_DENY_ONLY; 1370 1371 /* Adjust the index and continue with the next group */ 1372 GroupsInToken--; 1373 FoundGroup = FALSE; 1374 continue; 1375 } 1376 } 1377 } 1378 1379 /* We've finally filtered the token, return it to the caller */ 1380 *FilteredToken = AccessToken; 1381 Status = STATUS_SUCCESS; 1382 DPRINT("SepPerformTokenFiltering(): The token has been filtered!\n"); 1383 1384 Quit: 1385 if (!NT_SUCCESS(Status)) 1386 { 1387 /* Dereference the created token */ 1388 ObDereferenceObject(AccessToken); 1389 } 1390 1391 /* Unlock the source token */ 1392 SepReleaseTokenLock(Token); 1393 1394 return Status; 1395 } 1396 1397 /* PUBLIC FUNCTIONS ***********************************************************/ 1398 1399 /** 1400 * @brief 1401 * Filters an access token from an existing token, making it more restricted 1402 * than the previous one. 1403 * 1404 * @param[in] ExistingToken 1405 * An existing token for filtering. 1406 * 1407 * @param[in] Flags 1408 * Privilege flag options. This parameter argument influences how the token 1409 * is filtered. Such parameter can be 0. See NtFilterToken syscall for 1410 * more information. 1411 * 1412 * @param[in] SidsToDisable 1413 * Array of SIDs to disable. Such parameter can be NULL. 1414 * 1415 * @param[in] PrivilegesToDelete 1416 * Array of privileges to delete. If DISABLE_MAX_PRIVILEGE flag is specified 1417 * in the Flags parameter, PrivilegesToDelete is ignored. 1418 * 1419 * @param[in] RestrictedSids 1420 * An array of restricted SIDs for the new filtered token. Such parameter 1421 * can be NULL. 1422 * 1423 * @param[out] FilteredToken 1424 * The newly filtered token, returned to the caller. 1425 * 1426 * @return 1427 * Returns STATUS_SUCCESS if the function has successfully completed its 1428 * operations and that the access token has been filtered. STATUS_INVALID_PARAMETER 1429 * is returned if one or more of the parameter are not valid. A failure NTSTATUS code 1430 * is returned otherwise. 1431 */ 1432 NTSTATUS 1433 NTAPI 1434 SeFilterToken( 1435 _In_ PACCESS_TOKEN ExistingToken, 1436 _In_ ULONG Flags, 1437 _In_opt_ PTOKEN_GROUPS SidsToDisable, 1438 _In_opt_ PTOKEN_PRIVILEGES PrivilegesToDelete, 1439 _In_opt_ PTOKEN_GROUPS RestrictedSids, 1440 _Out_ PACCESS_TOKEN *FilteredToken) 1441 { 1442 NTSTATUS Status; 1443 PTOKEN AccessToken; 1444 ULONG PrivilegesCount = 0; 1445 ULONG SidsCount = 0; 1446 ULONG RestrictedSidsCount = 0; 1447 1448 PAGED_CODE(); 1449 1450 /* Begin copying the counters */ 1451 if (SidsToDisable != NULL) 1452 { 1453 SidsCount = SidsToDisable->GroupCount; 1454 } 1455 1456 if (PrivilegesToDelete != NULL) 1457 { 1458 PrivilegesCount = PrivilegesToDelete->PrivilegeCount; 1459 } 1460 1461 if (RestrictedSids != NULL) 1462 { 1463 RestrictedSidsCount = RestrictedSids->GroupCount; 1464 } 1465 1466 /* Call the internal API */ 1467 Status = SepPerformTokenFiltering(ExistingToken, 1468 PrivilegesToDelete->Privileges, 1469 SidsToDisable->Groups, 1470 RestrictedSids->Groups, 1471 PrivilegesCount, 1472 SidsCount, 1473 RestrictedSidsCount, 1474 Flags, 1475 KernelMode, 1476 &AccessToken); 1477 if (!NT_SUCCESS(Status)) 1478 { 1479 DPRINT1("SeFilterToken(): Failed to filter the token (Status 0x%lx)\n", Status); 1480 return Status; 1481 } 1482 1483 /* Insert the filtered token */ 1484 Status = ObInsertObject(AccessToken, 1485 NULL, 1486 0, 1487 0, 1488 NULL, 1489 NULL); 1490 if (!NT_SUCCESS(Status)) 1491 { 1492 DPRINT1("SeFilterToken(): Failed to insert the filtered token (Status 0x%lx)\n", Status); 1493 return Status; 1494 } 1495 1496 /* Return it to the caller */ 1497 *FilteredToken = AccessToken; 1498 return Status; 1499 } 1500 1501 /* SYSTEM CALLS ***************************************************************/ 1502 1503 /** 1504 * @brief 1505 * Creates an access token. 1506 * 1507 * @param[out] TokenHandle 1508 * The returned created token handle to the caller. 1509 * 1510 * @param[in] DesiredAccess 1511 * The desired access rights for the token that we're creating. 1512 * 1513 * @param[in] ObjectAttributes 1514 * The object attributes for the token object that we're creating. 1515 * 1516 * @param[in] TokenType 1517 * The type of token to assign for the newly created token. 1518 * 1519 * @param[in] AuthenticationId 1520 * Authentication ID that represents the token's identity. 1521 * 1522 * @param[in] ExpirationTime 1523 * Expiration time for the token. If set to -1, the token never expires. 1524 * 1525 * @param[in] TokenUser 1526 * The main user entity for the token to assign. 1527 * 1528 * @param[in] TokenGroups 1529 * Group list of SIDs for the token to assign. 1530 * 1531 * @param[in] TokenPrivileges 1532 * Privileges for the token. 1533 * 1534 * @param[in] TokenOwner 1535 * The main user that owns the newly created token. 1536 * 1537 * @param[in] TokenPrimaryGroup 1538 * The primary group that represents as the main group of the token. 1539 * 1540 * @param[in] TokenDefaultDacl 1541 * Discretionary access control list for the token. This limits on how 1542 * the token can be used, accessed and used by whom. 1543 * 1544 * @param[in] TokenSource 1545 * The source origin of the token who creates it. 1546 * 1547 * @return 1548 * Returns STATUS_SUCCESS if the function has successfully created the token. 1549 * A failure NTSTATUS code is returned otherwise. 1550 */ 1551 __kernel_entry 1552 NTSTATUS 1553 NTAPI 1554 NtCreateToken( 1555 _Out_ PHANDLE TokenHandle, 1556 _In_ ACCESS_MASK DesiredAccess, 1557 _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes, 1558 _In_ TOKEN_TYPE TokenType, 1559 _In_ PLUID AuthenticationId, 1560 _In_ PLARGE_INTEGER ExpirationTime, 1561 _In_ PTOKEN_USER TokenUser, 1562 _In_ PTOKEN_GROUPS TokenGroups, 1563 _In_ PTOKEN_PRIVILEGES TokenPrivileges, 1564 _In_opt_ PTOKEN_OWNER TokenOwner, 1565 _In_ PTOKEN_PRIMARY_GROUP TokenPrimaryGroup, 1566 _In_opt_ PTOKEN_DEFAULT_DACL TokenDefaultDacl, 1567 _In_ PTOKEN_SOURCE TokenSource) 1568 { 1569 HANDLE hToken; 1570 KPROCESSOR_MODE PreviousMode; 1571 ULONG PrivilegeCount, GroupCount; 1572 PSID OwnerSid, PrimaryGroupSid; 1573 PACL DefaultDacl; 1574 LARGE_INTEGER LocalExpirationTime = {{0, 0}}; 1575 LUID LocalAuthenticationId; 1576 TOKEN_SOURCE LocalTokenSource; 1577 SECURITY_QUALITY_OF_SERVICE LocalSecurityQos; 1578 PLUID_AND_ATTRIBUTES CapturedPrivileges = NULL; 1579 PSID_AND_ATTRIBUTES CapturedUser = NULL; 1580 PSID_AND_ATTRIBUTES CapturedGroups = NULL; 1581 PSID CapturedOwnerSid = NULL; 1582 PSID CapturedPrimaryGroupSid = NULL; 1583 PACL CapturedDefaultDacl = NULL; 1584 ULONG PrivilegesLength, UserLength, GroupsLength; 1585 NTSTATUS Status; 1586 1587 PAGED_CODE(); 1588 1589 PreviousMode = ExGetPreviousMode(); 1590 1591 if (PreviousMode != KernelMode) 1592 { 1593 _SEH2_TRY 1594 { 1595 ProbeForWriteHandle(TokenHandle); 1596 1597 if (ObjectAttributes != NULL) 1598 { 1599 ProbeForRead(ObjectAttributes, 1600 sizeof(OBJECT_ATTRIBUTES), 1601 sizeof(ULONG)); 1602 LocalSecurityQos = *(SECURITY_QUALITY_OF_SERVICE*)ObjectAttributes->SecurityQualityOfService; 1603 } 1604 1605 ProbeForRead(AuthenticationId, 1606 sizeof(LUID), 1607 sizeof(ULONG)); 1608 LocalAuthenticationId = *AuthenticationId; 1609 1610 LocalExpirationTime = ProbeForReadLargeInteger(ExpirationTime); 1611 1612 ProbeForRead(TokenUser, 1613 sizeof(TOKEN_USER), 1614 sizeof(ULONG)); 1615 1616 ProbeForRead(TokenGroups, 1617 sizeof(TOKEN_GROUPS), 1618 sizeof(ULONG)); 1619 GroupCount = TokenGroups->GroupCount; 1620 1621 ProbeForRead(TokenPrivileges, 1622 sizeof(TOKEN_PRIVILEGES), 1623 sizeof(ULONG)); 1624 PrivilegeCount = TokenPrivileges->PrivilegeCount; 1625 1626 if (TokenOwner != NULL) 1627 { 1628 ProbeForRead(TokenOwner, 1629 sizeof(TOKEN_OWNER), 1630 sizeof(ULONG)); 1631 OwnerSid = TokenOwner->Owner; 1632 } 1633 else 1634 { 1635 OwnerSid = NULL; 1636 } 1637 1638 ProbeForRead(TokenPrimaryGroup, 1639 sizeof(TOKEN_PRIMARY_GROUP), 1640 sizeof(ULONG)); 1641 PrimaryGroupSid = TokenPrimaryGroup->PrimaryGroup; 1642 1643 if (TokenDefaultDacl != NULL) 1644 { 1645 ProbeForRead(TokenDefaultDacl, 1646 sizeof(TOKEN_DEFAULT_DACL), 1647 sizeof(ULONG)); 1648 DefaultDacl = TokenDefaultDacl->DefaultDacl; 1649 } 1650 else 1651 { 1652 DefaultDacl = NULL; 1653 } 1654 1655 ProbeForRead(TokenSource, 1656 sizeof(TOKEN_SOURCE), 1657 sizeof(ULONG)); 1658 LocalTokenSource = *TokenSource; 1659 } 1660 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 1661 { 1662 /* Return the exception code */ 1663 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 1664 } 1665 _SEH2_END; 1666 } 1667 else 1668 { 1669 if (ObjectAttributes != NULL) 1670 LocalSecurityQos = *(SECURITY_QUALITY_OF_SERVICE*)ObjectAttributes->SecurityQualityOfService; 1671 LocalAuthenticationId = *AuthenticationId; 1672 LocalExpirationTime = *ExpirationTime; 1673 GroupCount = TokenGroups->GroupCount; 1674 PrivilegeCount = TokenPrivileges->PrivilegeCount; 1675 OwnerSid = TokenOwner ? TokenOwner->Owner : NULL; 1676 PrimaryGroupSid = TokenPrimaryGroup->PrimaryGroup; 1677 DefaultDacl = TokenDefaultDacl ? TokenDefaultDacl->DefaultDacl : NULL; 1678 LocalTokenSource = *TokenSource; 1679 } 1680 1681 /* Check token type */ 1682 if ((TokenType < TokenPrimary) || 1683 (TokenType > TokenImpersonation)) 1684 { 1685 return STATUS_BAD_TOKEN_TYPE; 1686 } 1687 1688 /* Check for token creation privilege */ 1689 if (!SeSinglePrivilegeCheck(SeCreateTokenPrivilege, PreviousMode)) 1690 { 1691 return STATUS_PRIVILEGE_NOT_HELD; 1692 } 1693 1694 /* Capture the user SID and attributes */ 1695 Status = SeCaptureSidAndAttributesArray(&TokenUser->User, 1696 1, 1697 PreviousMode, 1698 NULL, 1699 0, 1700 PagedPool, 1701 FALSE, 1702 &CapturedUser, 1703 &UserLength); 1704 if (!NT_SUCCESS(Status)) 1705 { 1706 goto Cleanup; 1707 } 1708 1709 /* Capture the groups SID and attributes array */ 1710 Status = SeCaptureSidAndAttributesArray(&TokenGroups->Groups[0], 1711 GroupCount, 1712 PreviousMode, 1713 NULL, 1714 0, 1715 PagedPool, 1716 FALSE, 1717 &CapturedGroups, 1718 &GroupsLength); 1719 if (!NT_SUCCESS(Status)) 1720 { 1721 goto Cleanup; 1722 } 1723 1724 /* Capture privileges */ 1725 Status = SeCaptureLuidAndAttributesArray(&TokenPrivileges->Privileges[0], 1726 PrivilegeCount, 1727 PreviousMode, 1728 NULL, 1729 0, 1730 PagedPool, 1731 FALSE, 1732 &CapturedPrivileges, 1733 &PrivilegesLength); 1734 if (!NT_SUCCESS(Status)) 1735 { 1736 goto Cleanup; 1737 } 1738 1739 /* Capture the token owner SID */ 1740 if (TokenOwner != NULL) 1741 { 1742 Status = SepCaptureSid(OwnerSid, 1743 PreviousMode, 1744 PagedPool, 1745 FALSE, 1746 &CapturedOwnerSid); 1747 if (!NT_SUCCESS(Status)) 1748 { 1749 goto Cleanup; 1750 } 1751 } 1752 1753 /* Capture the token primary group SID */ 1754 Status = SepCaptureSid(PrimaryGroupSid, 1755 PreviousMode, 1756 PagedPool, 1757 FALSE, 1758 &CapturedPrimaryGroupSid); 1759 if (!NT_SUCCESS(Status)) 1760 { 1761 goto Cleanup; 1762 } 1763 1764 /* Capture DefaultDacl */ 1765 if (DefaultDacl != NULL) 1766 { 1767 Status = SepCaptureAcl(DefaultDacl, 1768 PreviousMode, 1769 NonPagedPool, 1770 FALSE, 1771 &CapturedDefaultDacl); 1772 if (!NT_SUCCESS(Status)) 1773 { 1774 goto Cleanup; 1775 } 1776 } 1777 1778 /* Call the internal function */ 1779 Status = SepCreateToken(&hToken, 1780 PreviousMode, 1781 DesiredAccess, 1782 ObjectAttributes, 1783 TokenType, 1784 LocalSecurityQos.ImpersonationLevel, 1785 &LocalAuthenticationId, 1786 &LocalExpirationTime, 1787 CapturedUser, 1788 GroupCount, 1789 CapturedGroups, 1790 GroupsLength, 1791 PrivilegeCount, 1792 CapturedPrivileges, 1793 CapturedOwnerSid, 1794 CapturedPrimaryGroupSid, 1795 CapturedDefaultDacl, 1796 &LocalTokenSource, 1797 FALSE); 1798 if (NT_SUCCESS(Status)) 1799 { 1800 _SEH2_TRY 1801 { 1802 *TokenHandle = hToken; 1803 } 1804 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 1805 { 1806 Status = _SEH2_GetExceptionCode(); 1807 } 1808 _SEH2_END; 1809 } 1810 1811 Cleanup: 1812 1813 /* Release what we captured */ 1814 SeReleaseSidAndAttributesArray(CapturedUser, PreviousMode, FALSE); 1815 SeReleaseSidAndAttributesArray(CapturedGroups, PreviousMode, FALSE); 1816 SeReleaseLuidAndAttributesArray(CapturedPrivileges, PreviousMode, FALSE); 1817 SepReleaseSid(CapturedOwnerSid, PreviousMode, FALSE); 1818 SepReleaseSid(CapturedPrimaryGroupSid, PreviousMode, FALSE); 1819 SepReleaseAcl(CapturedDefaultDacl, PreviousMode, FALSE); 1820 1821 return Status; 1822 } 1823 1824 /** 1825 * @brief 1826 * Duplicates a token. 1827 * 1828 * @param[in] ExistingTokenHandle 1829 * An existing token to duplicate. 1830 * 1831 * @param[in] DesiredAccess 1832 * The desired access rights for the new duplicated token. 1833 * 1834 * @param[in] ObjectAttributes 1835 * Object attributes for the new duplicated token. 1836 * 1837 * @param[in] EffectiveOnly 1838 * If set to TRUE, the function removes all the disabled privileges and groups 1839 * of the token to duplicate. 1840 * 1841 * @param[in] TokenType 1842 * Type of token to assign to the duplicated token. 1843 * 1844 * @param[out] NewTokenHandle 1845 * The returned duplicated token handle. 1846 * 1847 * @return 1848 * STATUS_SUCCESS is returned if token duplication has completed successfully. 1849 * STATUS_BAD_IMPERSONATION_LEVEL is returned if the caller erroneously wants 1850 * to raise the impersonation level even though the conditions do not permit 1851 * it. A failure NTSTATUS code is returned otherwise. 1852 * 1853 * @remarks 1854 * Some sources claim 4th param is ImpersonationLevel, but on W2K 1855 * this is certainly NOT true, although I can't say for sure that EffectiveOnly 1856 * is correct either. -Gunnar 1857 * This is true. EffectiveOnly overrides SQOS.EffectiveOnly. - IAI 1858 * NOTE for readers: http://hex.pp.ua/nt/NtDuplicateToken.php is therefore 1859 * wrong in that regard, while MSDN documentation is correct. 1860 */ 1861 _Must_inspect_result_ 1862 __kernel_entry 1863 NTSTATUS 1864 NTAPI 1865 NtDuplicateToken( 1866 _In_ HANDLE ExistingTokenHandle, 1867 _In_ ACCESS_MASK DesiredAccess, 1868 _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes, 1869 _In_ BOOLEAN EffectiveOnly, 1870 _In_ TOKEN_TYPE TokenType, 1871 _Out_ PHANDLE NewTokenHandle) 1872 { 1873 KPROCESSOR_MODE PreviousMode; 1874 HANDLE hToken; 1875 PTOKEN Token; 1876 PTOKEN NewToken; 1877 PSECURITY_QUALITY_OF_SERVICE CapturedSecurityQualityOfService; 1878 BOOLEAN QoSPresent; 1879 OBJECT_HANDLE_INFORMATION HandleInformation; 1880 NTSTATUS Status; 1881 1882 PAGED_CODE(); 1883 1884 if (TokenType != TokenImpersonation && 1885 TokenType != TokenPrimary) 1886 { 1887 return STATUS_INVALID_PARAMETER; 1888 } 1889 1890 PreviousMode = KeGetPreviousMode(); 1891 1892 if (PreviousMode != KernelMode) 1893 { 1894 _SEH2_TRY 1895 { 1896 ProbeForWriteHandle(NewTokenHandle); 1897 } 1898 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 1899 { 1900 /* Return the exception code */ 1901 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 1902 } 1903 _SEH2_END; 1904 } 1905 1906 Status = SepCaptureSecurityQualityOfService(ObjectAttributes, 1907 PreviousMode, 1908 PagedPool, 1909 FALSE, 1910 &CapturedSecurityQualityOfService, 1911 &QoSPresent); 1912 if (!NT_SUCCESS(Status)) 1913 { 1914 DPRINT1("NtDuplicateToken() failed to capture QoS! Status: 0x%x\n", Status); 1915 return Status; 1916 } 1917 1918 Status = ObReferenceObjectByHandle(ExistingTokenHandle, 1919 TOKEN_DUPLICATE, 1920 SeTokenObjectType, 1921 PreviousMode, 1922 (PVOID*)&Token, 1923 &HandleInformation); 1924 if (!NT_SUCCESS(Status)) 1925 { 1926 DPRINT1("Failed to reference token (Status 0x%lx)\n", Status); 1927 SepReleaseSecurityQualityOfService(CapturedSecurityQualityOfService, 1928 PreviousMode, 1929 FALSE); 1930 return Status; 1931 } 1932 1933 /* 1934 * Fail, if the original token is an impersonation token and the caller 1935 * tries to raise the impersonation level of the new token above the 1936 * impersonation level of the original token. 1937 */ 1938 if (Token->TokenType == TokenImpersonation) 1939 { 1940 if (QoSPresent && 1941 CapturedSecurityQualityOfService->ImpersonationLevel >Token->ImpersonationLevel) 1942 { 1943 ObDereferenceObject(Token); 1944 SepReleaseSecurityQualityOfService(CapturedSecurityQualityOfService, 1945 PreviousMode, 1946 FALSE); 1947 return STATUS_BAD_IMPERSONATION_LEVEL; 1948 } 1949 } 1950 1951 /* 1952 * Fail, if a primary token is to be created from an impersonation token 1953 * and and the impersonation level of the impersonation token is below SecurityImpersonation. 1954 */ 1955 if (Token->TokenType == TokenImpersonation && 1956 TokenType == TokenPrimary && 1957 Token->ImpersonationLevel < SecurityImpersonation) 1958 { 1959 ObDereferenceObject(Token); 1960 SepReleaseSecurityQualityOfService(CapturedSecurityQualityOfService, 1961 PreviousMode, 1962 FALSE); 1963 return STATUS_BAD_IMPERSONATION_LEVEL; 1964 } 1965 1966 Status = SepDuplicateToken(Token, 1967 ObjectAttributes, 1968 EffectiveOnly, 1969 TokenType, 1970 (QoSPresent ? CapturedSecurityQualityOfService->ImpersonationLevel : SecurityAnonymous), 1971 PreviousMode, 1972 &NewToken); 1973 1974 ObDereferenceObject(Token); 1975 1976 if (NT_SUCCESS(Status)) 1977 { 1978 Status = ObInsertObject(NewToken, 1979 NULL, 1980 (DesiredAccess ? DesiredAccess : HandleInformation.GrantedAccess), 1981 0, 1982 NULL, 1983 &hToken); 1984 if (NT_SUCCESS(Status)) 1985 { 1986 _SEH2_TRY 1987 { 1988 *NewTokenHandle = hToken; 1989 } 1990 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 1991 { 1992 Status = _SEH2_GetExceptionCode(); 1993 } 1994 _SEH2_END; 1995 } 1996 } 1997 1998 /* Free the captured structure */ 1999 SepReleaseSecurityQualityOfService(CapturedSecurityQualityOfService, 2000 PreviousMode, 2001 FALSE); 2002 2003 return Status; 2004 } 2005 2006 /** 2007 * @brief 2008 * Creates an access token in a restricted form 2009 * from the original existing token, that is, such 2010 * action is called filtering. 2011 * 2012 * @param[in] ExistingTokenHandle 2013 * A handle to an access token which is to be filtered. 2014 * 2015 * @param[in] Flags 2016 * Privilege flag options. This parameter argument influences how the 2017 * token's privileges are filtered. For further details see remarks. 2018 * 2019 * @param[in] SidsToDisable 2020 * Array of SIDs to disable. The action of doing so assigns the 2021 * SE_GROUP_USE_FOR_DENY_ONLY attribute to the respective group 2022 * SID and takes away SE_GROUP_ENABLED and SE_GROUP_ENABLED_BY_DEFAULT. 2023 * This parameter can be NULL. This can be a UM pointer. 2024 * 2025 * @param[in] PrivilegesToDelete 2026 * Array of privileges to delete. The function will walk within this 2027 * array to determine if the specified privileges do exist in the 2028 * access token. Any missing privileges gets ignored. This parameter 2029 * can be NULL. This can be a UM pointer. 2030 * 2031 * @param[in] RestrictedSids 2032 * An array list of restricted groups SID to be added in the access 2033 * token. A token that is already restricted the newly added restricted 2034 * SIDs are redundant information in addition to the existing restricted 2035 * SIDs in the token. This parameter can be NULL. This can be a UM pointer. 2036 * 2037 * @param[out] NewTokenHandle 2038 * A new handle to the restricted (filtered) access token. This can be a 2039 * UM pointer. 2040 * 2041 * @return 2042 * Returns STATUS_SUCCESS if the routine has successfully filtered the 2043 * access token. STATUS_INVALID_PARAMETER is returned if one or more 2044 * parameters are not valid (see SepPerformTokenFiltering routine call 2045 * for more information). A failure NTSTATUS code is returned otherwise. 2046 * 2047 * @remarks 2048 * The Flags parameter determines the final outcome of how the privileges 2049 * in an access token are filtered. This parameter can take these supported 2050 * values (these can be combined): 2051 * 2052 * 0 -- Filter the token's privileges in the usual way. The function expects 2053 * that the caller MUST PROVIDE a valid array list of privileges to be 2054 * deleted (that is, PrivilegesToDelete MUSTN'T BE NULL). 2055 * 2056 * DISABLE_MAX_PRIVILEGE -- Disables (deletes) all the privileges except SeChangeNotifyPrivilege 2057 * in the new access token. Bear in mind if this flag is specified 2058 * the routine ignores PrivilegesToDelete. 2059 * 2060 * SANDBOX_INERT -- Stores the TOKEN_SANDBOX_INERT token flag within the access token. 2061 * 2062 * LUA_TOKEN -- The newly filtered access token is a LUA token. This flag is not 2063 * supported in Windows Server 2003. 2064 * 2065 * WRITE_RESTRICTED -- The newly filtered token has the restricted SIDs that are 2066 * considered only when evaluating write access onto the token. 2067 * This value is not supported in Windows Server 2003. 2068 */ 2069 NTSTATUS 2070 NTAPI 2071 NtFilterToken( 2072 _In_ HANDLE ExistingTokenHandle, 2073 _In_ ULONG Flags, 2074 _In_opt_ PTOKEN_GROUPS SidsToDisable, 2075 _In_opt_ PTOKEN_PRIVILEGES PrivilegesToDelete, 2076 _In_opt_ PTOKEN_GROUPS RestrictedSids, 2077 _Out_ PHANDLE NewTokenHandle) 2078 { 2079 PTOKEN Token, FilteredToken; 2080 HANDLE FilteredTokenHandle; 2081 NTSTATUS Status; 2082 KPROCESSOR_MODE PreviousMode; 2083 OBJECT_HANDLE_INFORMATION HandleInfo; 2084 ULONG ResultLength; 2085 ULONG CapturedSidsCount = 0; 2086 ULONG CapturedPrivilegesCount = 0; 2087 ULONG CapturedRestrictedSidsCount = 0; 2088 ULONG ProbeSize = 0; 2089 PSID_AND_ATTRIBUTES CapturedSids = NULL; 2090 PSID_AND_ATTRIBUTES CapturedRestrictedSids = NULL; 2091 PLUID_AND_ATTRIBUTES CapturedPrivileges = NULL; 2092 2093 PAGED_CODE(); 2094 2095 PreviousMode = ExGetPreviousMode(); 2096 2097 _SEH2_TRY 2098 { 2099 /* Probe SidsToDisable */ 2100 if (SidsToDisable != NULL) 2101 { 2102 /* Probe the header */ 2103 ProbeForRead(SidsToDisable, sizeof(*SidsToDisable), sizeof(ULONG)); 2104 2105 CapturedSidsCount = SidsToDisable->GroupCount; 2106 ProbeSize = FIELD_OFFSET(TOKEN_GROUPS, Groups[CapturedSidsCount]); 2107 2108 ProbeForRead(SidsToDisable, ProbeSize, sizeof(ULONG)); 2109 } 2110 2111 /* Probe PrivilegesToDelete */ 2112 if (PrivilegesToDelete != NULL) 2113 { 2114 /* Probe the header */ 2115 ProbeForRead(PrivilegesToDelete, sizeof(*PrivilegesToDelete), sizeof(ULONG)); 2116 2117 CapturedPrivilegesCount = PrivilegesToDelete->PrivilegeCount; 2118 ProbeSize = FIELD_OFFSET(TOKEN_PRIVILEGES, Privileges[CapturedPrivilegesCount]); 2119 2120 ProbeForRead(PrivilegesToDelete, ProbeSize, sizeof(ULONG)); 2121 } 2122 2123 /* Probe RestrictedSids */ 2124 if (RestrictedSids != NULL) 2125 { 2126 /* Probe the header */ 2127 ProbeForRead(RestrictedSids, sizeof(*RestrictedSids), sizeof(ULONG)); 2128 2129 CapturedRestrictedSidsCount = RestrictedSids->GroupCount; 2130 ProbeSize = FIELD_OFFSET(TOKEN_GROUPS, Groups[CapturedRestrictedSidsCount]); 2131 2132 ProbeForRead(RestrictedSids, ProbeSize, sizeof(ULONG)); 2133 } 2134 2135 /* Probe the handle */ 2136 ProbeForWriteHandle(NewTokenHandle); 2137 } 2138 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 2139 { 2140 /* Return the exception code */ 2141 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 2142 } 2143 _SEH2_END; 2144 2145 /* Reference the token */ 2146 Status = ObReferenceObjectByHandle(ExistingTokenHandle, 2147 TOKEN_DUPLICATE, 2148 SeTokenObjectType, 2149 PreviousMode, 2150 (PVOID*)&Token, 2151 &HandleInfo); 2152 if (!NT_SUCCESS(Status)) 2153 { 2154 DPRINT1("NtFilterToken(): Failed to reference the token (Status 0x%lx)\n", Status); 2155 return Status; 2156 } 2157 2158 /* Capture the group SIDs */ 2159 if (SidsToDisable != NULL) 2160 { 2161 Status = SeCaptureSidAndAttributesArray(SidsToDisable->Groups, 2162 CapturedSidsCount, 2163 PreviousMode, 2164 NULL, 2165 0, 2166 PagedPool, 2167 TRUE, 2168 &CapturedSids, 2169 &ResultLength); 2170 if (!NT_SUCCESS(Status)) 2171 { 2172 DPRINT1("NtFilterToken(): Failed to capture the SIDs (Status 0x%lx)\n", Status); 2173 goto Quit; 2174 } 2175 } 2176 2177 /* Capture the privileges */ 2178 if (PrivilegesToDelete != NULL) 2179 { 2180 Status = SeCaptureLuidAndAttributesArray(PrivilegesToDelete->Privileges, 2181 CapturedPrivilegesCount, 2182 PreviousMode, 2183 NULL, 2184 0, 2185 PagedPool, 2186 TRUE, 2187 &CapturedPrivileges, 2188 &ResultLength); 2189 if (!NT_SUCCESS(Status)) 2190 { 2191 DPRINT1("NtFilterToken(): Failed to capture the privileges (Status 0x%lx)\n", Status); 2192 goto Quit; 2193 } 2194 } 2195 2196 /* Capture the restricted SIDs */ 2197 if (RestrictedSids != NULL) 2198 { 2199 Status = SeCaptureSidAndAttributesArray(RestrictedSids->Groups, 2200 CapturedRestrictedSidsCount, 2201 PreviousMode, 2202 NULL, 2203 0, 2204 PagedPool, 2205 TRUE, 2206 &CapturedRestrictedSids, 2207 &ResultLength); 2208 if (!NT_SUCCESS(Status)) 2209 { 2210 DPRINT1("NtFilterToken(): Failed to capture the restricted SIDs (Status 0x%lx)\n", Status); 2211 goto Quit; 2212 } 2213 } 2214 2215 /* Call the internal API */ 2216 Status = SepPerformTokenFiltering(Token, 2217 CapturedPrivileges, 2218 CapturedSids, 2219 CapturedRestrictedSids, 2220 CapturedPrivilegesCount, 2221 CapturedSidsCount, 2222 CapturedRestrictedSidsCount, 2223 Flags, 2224 PreviousMode, 2225 &FilteredToken); 2226 if (!NT_SUCCESS(Status)) 2227 { 2228 DPRINT1("NtFilterToken(): Failed to filter the token (Status 0x%lx)\n", Status); 2229 goto Quit; 2230 } 2231 2232 /* Insert the filtered token and retrieve a handle to it */ 2233 Status = ObInsertObject(FilteredToken, 2234 NULL, 2235 HandleInfo.GrantedAccess, 2236 0, 2237 NULL, 2238 &FilteredTokenHandle); 2239 if (!NT_SUCCESS(Status)) 2240 { 2241 DPRINT1("NtFilterToken(): Failed to insert the filtered token (Status 0x%lx)\n", Status); 2242 goto Quit; 2243 } 2244 2245 /* And return it to the caller once we're done */ 2246 _SEH2_TRY 2247 { 2248 *NewTokenHandle = FilteredTokenHandle; 2249 } 2250 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 2251 { 2252 Status = _SEH2_GetExceptionCode(); 2253 _SEH2_YIELD(goto Quit); 2254 } 2255 _SEH2_END; 2256 2257 Quit: 2258 /* Dereference the token */ 2259 ObDereferenceObject(Token); 2260 2261 /* Release all the captured data */ 2262 if (CapturedSids != NULL) 2263 { 2264 SeReleaseSidAndAttributesArray(CapturedSids, 2265 PreviousMode, 2266 TRUE); 2267 } 2268 2269 if (CapturedPrivileges != NULL) 2270 { 2271 SeReleaseLuidAndAttributesArray(CapturedPrivileges, 2272 PreviousMode, 2273 TRUE); 2274 } 2275 2276 if (CapturedRestrictedSids != NULL) 2277 { 2278 SeReleaseSidAndAttributesArray(CapturedRestrictedSids, 2279 PreviousMode, 2280 TRUE); 2281 } 2282 2283 return Status; 2284 } 2285 2286 /* EOF */ 2287