1 /* 2 * PROJECT: ReactOS Kernel 3 * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) 4 * PURPOSE: Security privileges support 5 * COPYRIGHT: Copyright Alex Ionescu <alex@relsoft.net> 6 * Copyright Timo Kreuzer <timo.kreuzer@reactos.org> 7 * Copyright Eric Kohl 8 */ 9 10 /* INCLUDES ******************************************************************/ 11 12 #include <ntoskrnl.h> 13 #define NDEBUG 14 #include <debug.h> 15 16 /* GLOBALS ********************************************************************/ 17 18 #define SE_MAXIMUM_PRIVILEGE_LIMIT 0x3C 19 20 #define CONST_LUID(x1, x2) {x1, x2} 21 const LUID SeCreateTokenPrivilege = CONST_LUID(SE_CREATE_TOKEN_PRIVILEGE, 0); 22 const LUID SeAssignPrimaryTokenPrivilege = CONST_LUID(SE_ASSIGNPRIMARYTOKEN_PRIVILEGE, 0); 23 const LUID SeLockMemoryPrivilege = CONST_LUID(SE_LOCK_MEMORY_PRIVILEGE, 0); 24 const LUID SeIncreaseQuotaPrivilege = CONST_LUID(SE_INCREASE_QUOTA_PRIVILEGE, 0); 25 const LUID SeUnsolicitedInputPrivilege = CONST_LUID(6, 0); 26 const LUID SeTcbPrivilege = CONST_LUID(SE_TCB_PRIVILEGE, 0); 27 const LUID SeSecurityPrivilege = CONST_LUID(SE_SECURITY_PRIVILEGE, 0); 28 const LUID SeTakeOwnershipPrivilege = CONST_LUID(SE_TAKE_OWNERSHIP_PRIVILEGE, 0); 29 const LUID SeLoadDriverPrivilege = CONST_LUID(SE_LOAD_DRIVER_PRIVILEGE, 0); 30 const LUID SeSystemProfilePrivilege = CONST_LUID(SE_SYSTEM_PROFILE_PRIVILEGE, 0); 31 const LUID SeSystemtimePrivilege = CONST_LUID(SE_SYSTEMTIME_PRIVILEGE, 0); 32 const LUID SeProfileSingleProcessPrivilege = CONST_LUID(SE_PROF_SINGLE_PROCESS_PRIVILEGE, 0); 33 const LUID SeIncreaseBasePriorityPrivilege = CONST_LUID(SE_INC_BASE_PRIORITY_PRIVILEGE, 0); 34 const LUID SeCreatePagefilePrivilege = CONST_LUID(SE_CREATE_PAGEFILE_PRIVILEGE, 0); 35 const LUID SeCreatePermanentPrivilege = CONST_LUID(SE_CREATE_PERMANENT_PRIVILEGE, 0); 36 const LUID SeBackupPrivilege = CONST_LUID(SE_BACKUP_PRIVILEGE, 0); 37 const LUID SeRestorePrivilege = CONST_LUID(SE_RESTORE_PRIVILEGE, 0); 38 const LUID SeShutdownPrivilege = CONST_LUID(SE_SHUTDOWN_PRIVILEGE, 0); 39 const LUID SeDebugPrivilege = CONST_LUID(SE_DEBUG_PRIVILEGE, 0); 40 const LUID SeAuditPrivilege = CONST_LUID(SE_AUDIT_PRIVILEGE, 0); 41 const LUID SeSystemEnvironmentPrivilege = CONST_LUID(SE_SYSTEM_ENVIRONMENT_PRIVILEGE, 0); 42 const LUID SeChangeNotifyPrivilege = CONST_LUID(SE_CHANGE_NOTIFY_PRIVILEGE, 0); 43 const LUID SeRemoteShutdownPrivilege = CONST_LUID(SE_REMOTE_SHUTDOWN_PRIVILEGE, 0); 44 const LUID SeUndockPrivilege = CONST_LUID(SE_UNDOCK_PRIVILEGE, 0); 45 const LUID SeSyncAgentPrivilege = CONST_LUID(SE_SYNC_AGENT_PRIVILEGE, 0); 46 const LUID SeEnableDelegationPrivilege = CONST_LUID(SE_ENABLE_DELEGATION_PRIVILEGE, 0); 47 const LUID SeManageVolumePrivilege = CONST_LUID(SE_MANAGE_VOLUME_PRIVILEGE, 0); 48 const LUID SeImpersonatePrivilege = CONST_LUID(SE_IMPERSONATE_PRIVILEGE, 0); 49 const LUID SeCreateGlobalPrivilege = CONST_LUID(SE_CREATE_GLOBAL_PRIVILEGE, 0); 50 const LUID SeTrustedCredmanPrivilege = CONST_LUID(SE_TRUSTED_CREDMAN_ACCESS_PRIVILEGE, 0); 51 const LUID SeRelabelPrivilege = CONST_LUID(SE_RELABEL_PRIVILEGE, 0); 52 const LUID SeIncreaseWorkingSetPrivilege = CONST_LUID(SE_INC_WORKING_SET_PRIVILEGE, 0); 53 const LUID SeTimeZonePrivilege = CONST_LUID(SE_TIME_ZONE_PRIVILEGE, 0); 54 const LUID SeCreateSymbolicLinkPrivilege = CONST_LUID(SE_CREATE_SYMBOLIC_LINK_PRIVILEGE, 0); 55 56 57 /* PRIVATE FUNCTIONS **********************************************************/ 58 59 /** 60 * @brief 61 * Initializes the privileges during the startup phase of the security 62 * manager module. This function serves as a placeholder as it currently 63 * does nothing. 64 * 65 * @return 66 * Nothing. 67 */ 68 CODE_SEG("INIT") 69 VOID 70 NTAPI 71 SepInitPrivileges(VOID) 72 { 73 74 } 75 76 /** 77 * @brief 78 * Checks the privileges pointed by Privileges array argument if they exist and 79 * match with the privileges from an access token. 80 * 81 * @param[in] Token 82 * An access token where privileges are to be checked. 83 * 84 * @param[in] Privileges 85 * An array of privileges with attributes used as checking indicator for 86 * the function. 87 * 88 * @param[in] PrivilegeCount 89 * The total number count of privileges in the array. 90 * 91 * @param[in] PrivilegeControl 92 * Privilege control bit mask to determine if we should check all the 93 * privileges based on the number count of privileges or not. 94 * 95 * @param[in] PreviousMode 96 * Processor level access mode. 97 * 98 * @return 99 * Returns TRUE if the required privileges exist and that they do match. 100 * Otherwise the functions returns FALSE. 101 */ 102 BOOLEAN 103 NTAPI 104 SepPrivilegeCheck( 105 _In_ PTOKEN Token, 106 _In_ PLUID_AND_ATTRIBUTES Privileges, 107 _In_ ULONG PrivilegeCount, 108 _In_ ULONG PrivilegeControl, 109 _In_ KPROCESSOR_MODE PreviousMode) 110 { 111 ULONG i; 112 ULONG j; 113 ULONG Required; 114 115 DPRINT("SepPrivilegeCheck() called\n"); 116 117 PAGED_CODE(); 118 119 if (PreviousMode == KernelMode) 120 return TRUE; 121 122 /* Get the number of privileges that are required to match */ 123 Required = (PrivilegeControl & PRIVILEGE_SET_ALL_NECESSARY) ? PrivilegeCount : 1; 124 125 /* Acquire a shared token lock */ 126 SepAcquireTokenLockShared(Token); 127 128 /* Loop all requested privileges until we found the required ones */ 129 for (i = 0; i < PrivilegeCount; i++) 130 { 131 /* Loop the privileges of the token */ 132 for (j = 0; j < Token->PrivilegeCount; j++) 133 { 134 /* Check if the LUIDs match */ 135 if (RtlEqualLuid(&Token->Privileges[j].Luid, &Privileges[i].Luid)) 136 { 137 DPRINT("Found privilege. Attributes: %lx\n", 138 Token->Privileges[j].Attributes); 139 140 /* Check if the privilege is enabled */ 141 if (Token->Privileges[j].Attributes & SE_PRIVILEGE_ENABLED) 142 { 143 Privileges[i].Attributes |= SE_PRIVILEGE_USED_FOR_ACCESS; 144 Required--; 145 146 /* Check if we have found all privileges */ 147 if (Required == 0) 148 { 149 /* We're done! */ 150 SepReleaseTokenLock(Token); 151 return TRUE; 152 } 153 } 154 155 /* Leave the inner loop */ 156 break; 157 } 158 } 159 } 160 161 /* Release the token lock */ 162 SepReleaseTokenLock(Token); 163 164 /* When we reached this point, we did not find all privileges */ 165 ASSERT(Required > 0); 166 return FALSE; 167 } 168 169 /** 170 * @brief 171 * Checks only single privilege based upon the privilege pointed by a LUID and 172 * if it matches with the one from an access token. 173 * 174 * @param[in] PrivilegeValue 175 * The privilege to be checked. 176 * 177 * @param[in] Token 178 * An access token where its privilege is to be checked against the one 179 * provided by the caller. 180 * 181 * @param[in] PreviousMode 182 * Processor level access mode. 183 * 184 * @return 185 * Returns TRUE if the required privilege exists and that it matches 186 * with the one from the access token, FALSE otherwise. 187 */ 188 BOOLEAN 189 NTAPI 190 SepSinglePrivilegeCheck( 191 _In_ LUID PrivilegeValue, 192 _In_ PTOKEN Token, 193 _In_ KPROCESSOR_MODE PreviousMode) 194 { 195 LUID_AND_ATTRIBUTES Privilege; 196 PAGED_CODE(); 197 ASSERT(!RtlEqualLuid(&PrivilegeValue, &SeTcbPrivilege)); 198 199 Privilege.Luid = PrivilegeValue; 200 Privilege.Attributes = SE_PRIVILEGE_ENABLED; 201 return SepPrivilegeCheck(Token, 202 &Privilege, 203 1, 204 PRIVILEGE_SET_ALL_NECESSARY, 205 PreviousMode); 206 } 207 208 /** 209 * @brief 210 * Checks the security policy and returns a set of privileges 211 * based upon the said security policy context. 212 * 213 * @param[in,out] DesiredAccess 214 * The desired access right mask. 215 * 216 * @param[in,out] GrantedAccess 217 * The granted access rights masks. The rights are granted depending 218 * on the desired access rights requested by the calling thread. 219 * 220 * @param[in] SubjectContext 221 * Security subject context. If the caller supplies one, the access token 222 * supplied by the caller will be assigned to one of client or primary tokens 223 * of the subject context in question. 224 * 225 * @param[in] Token 226 * An access token. 227 * 228 * @param[out] OutPrivilegeSet 229 * An array set of privileges to be reported to the caller, if the actual 230 * calling thread wants such set of privileges in the first place. 231 * 232 * @param[in] PreviousMode 233 * Processor level access mode. 234 * 235 * @return 236 * Returns STATUS_PRIVILEGE_NOT_HELD if the respective operations have succeeded 237 * without problems. STATUS_PRIVILEGE_NOT_HELD is returned if the access token 238 * doesn't have SeSecurityPrivilege privilege to warrant ACCESS_SYSTEM_SECURITY 239 * access right. STATUS_INSUFFICIENT_RESOURCES is returned if we failed 240 * to allocate block of memory pool for the array set of privileges. 241 */ 242 NTSTATUS 243 NTAPI 244 SePrivilegePolicyCheck( 245 _Inout_ PACCESS_MASK DesiredAccess, 246 _Inout_ PACCESS_MASK GrantedAccess, 247 _In_ PSECURITY_SUBJECT_CONTEXT SubjectContext, 248 _In_ PTOKEN Token, 249 _Out_opt_ PPRIVILEGE_SET *OutPrivilegeSet, 250 _In_ KPROCESSOR_MODE PreviousMode) 251 { 252 SIZE_T PrivilegeSize; 253 PPRIVILEGE_SET PrivilegeSet; 254 ULONG PrivilegeCount = 0, Index = 0; 255 ACCESS_MASK AccessMask = 0; 256 PAGED_CODE(); 257 258 /* Check if we have a security subject context */ 259 if (SubjectContext != NULL) 260 { 261 /* Check if there is a client impersonation token */ 262 if (SubjectContext->ClientToken != NULL) 263 Token = SubjectContext->ClientToken; 264 else 265 Token = SubjectContext->PrimaryToken; 266 } 267 268 /* Check if the caller wants ACCESS_SYSTEM_SECURITY access */ 269 if (*DesiredAccess & ACCESS_SYSTEM_SECURITY) 270 { 271 /* Do the privilege check */ 272 if (SepSinglePrivilegeCheck(SeSecurityPrivilege, Token, PreviousMode)) 273 { 274 /* Remember this access flag */ 275 AccessMask |= ACCESS_SYSTEM_SECURITY; 276 PrivilegeCount++; 277 } 278 else 279 { 280 return STATUS_PRIVILEGE_NOT_HELD; 281 } 282 } 283 284 /* Check if the caller wants WRITE_OWNER access */ 285 if (*DesiredAccess & WRITE_OWNER) 286 { 287 /* Do the privilege check */ 288 if (SepSinglePrivilegeCheck(SeTakeOwnershipPrivilege, Token, PreviousMode)) 289 { 290 /* Remember this access flag */ 291 AccessMask |= WRITE_OWNER; 292 PrivilegeCount++; 293 } 294 } 295 296 /* Update the access masks */ 297 *GrantedAccess |= AccessMask; 298 *DesiredAccess &= ~AccessMask; 299 300 /* Does the caller want a privilege set? */ 301 if (OutPrivilegeSet != NULL) 302 { 303 /* Do we have any privileges to report? */ 304 if (PrivilegeCount > 0) 305 { 306 /* Calculate size and allocate the structure */ 307 PrivilegeSize = FIELD_OFFSET(PRIVILEGE_SET, Privilege[PrivilegeCount]); 308 PrivilegeSet = ExAllocatePoolWithTag(PagedPool, PrivilegeSize, TAG_PRIVILEGE_SET); 309 *OutPrivilegeSet = PrivilegeSet; 310 if (PrivilegeSet == NULL) 311 { 312 return STATUS_INSUFFICIENT_RESOURCES; 313 } 314 315 PrivilegeSet->PrivilegeCount = PrivilegeCount; 316 PrivilegeSet->Control = 0; 317 318 if (AccessMask & WRITE_OWNER) 319 { 320 PrivilegeSet->Privilege[Index].Luid = SeTakeOwnershipPrivilege; 321 PrivilegeSet->Privilege[Index].Attributes = SE_PRIVILEGE_USED_FOR_ACCESS; 322 Index++; 323 } 324 325 if (AccessMask & ACCESS_SYSTEM_SECURITY) 326 { 327 PrivilegeSet->Privilege[Index].Luid = SeSecurityPrivilege; 328 PrivilegeSet->Privilege[Index].Attributes = SE_PRIVILEGE_USED_FOR_ACCESS; 329 } 330 } 331 else 332 { 333 /* No privileges, no structure */ 334 *OutPrivilegeSet = NULL; 335 } 336 } 337 338 return STATUS_SUCCESS; 339 } 340 341 /** 342 * @brief 343 * Checks a single privilege and performs an audit 344 * against a privileged service based on a security subject 345 * context. 346 * 347 * @param[in] DesiredAccess 348 * Security subject context used for privileged service 349 * auditing. 350 * 351 * @param[in] PreviousMode 352 * Processor level access mode. 353 * 354 * @return 355 * Returns TRUE if service auditing and privilege checking 356 * tests have succeeded, FALSE otherwise. 357 */ 358 BOOLEAN 359 NTAPI 360 SeCheckAuditPrivilege( 361 _In_ PSECURITY_SUBJECT_CONTEXT SubjectContext, 362 _In_ KPROCESSOR_MODE PreviousMode) 363 { 364 PRIVILEGE_SET PrivilegeSet; 365 BOOLEAN Result; 366 PAGED_CODE(); 367 368 /* Initialize the privilege set with the single privilege */ 369 PrivilegeSet.PrivilegeCount = 1; 370 PrivilegeSet.Control = PRIVILEGE_SET_ALL_NECESSARY; 371 PrivilegeSet.Privilege[0].Luid = SeAuditPrivilege; 372 PrivilegeSet.Privilege[0].Attributes = 0; 373 374 /* Check against the primary token! */ 375 Result = SepPrivilegeCheck(SubjectContext->PrimaryToken, 376 &PrivilegeSet.Privilege[0], 377 1, 378 PRIVILEGE_SET_ALL_NECESSARY, 379 PreviousMode); 380 381 if (PreviousMode != KernelMode) 382 { 383 SePrivilegedServiceAuditAlarm(NULL, 384 SubjectContext, 385 &PrivilegeSet, 386 Result); 387 } 388 389 return Result; 390 } 391 392 /** 393 * @brief 394 * Captures a LUID with attributes structure. This function is mainly 395 * tied in the context of privileges. 396 * 397 * @param[in] Src 398 * Source of a valid LUID with attributes structure. 399 * 400 * @param[in] PrivilegeCount 401 * Count number of privileges to be captured. 402 * 403 * @param[in] PreviousMode 404 * Processor level access mode. 405 * 406 * @param[in] AllocatedMem 407 * If specified, the function will use this allocated block memory 408 * buffer for the captured LUID and attributes structure. Otherwise 409 * the function will automatically allocate some buffer for it. 410 * 411 * @param[in] AllocatedLength 412 * The length of the buffer, pointed by AllocatedMem. 413 * 414 * @param[in] PoolType 415 * Pool type of the memory allocation. 416 * 417 * @param[in] CaptureIfKernel 418 * If set to TRUE, the capturing is done in the kernel itself. 419 * FALSE if the capturing is done in a kernel mode driver instead. 420 * 421 * @param[out] Dest 422 * The captured LUID with attributes buffer. 423 * 424 * @param[in,out] Length 425 * The length of the captured privileges count. 426 * 427 * @return 428 * Returns STATUS_SUCCESS if the LUID and attributes array 429 * has been captured successfully. STATUS_INSUFFICIENT_RESOURCES is returned 430 * if memory pool allocation for the captured buffer has failed. 431 * STATUS_BUFFER_TOO_SMALL is returned if the buffer size is less than the 432 * required size. STATUS_INVALID_PARAMETER is returned if the caller has 433 * submitted a privilege count that exceeds that maximum threshold the 434 * kernel can permit, for the purpose to avoid an integer overflow. 435 */ 436 NTSTATUS 437 NTAPI 438 SeCaptureLuidAndAttributesArray( 439 _In_ PLUID_AND_ATTRIBUTES Src, 440 _In_ ULONG PrivilegeCount, 441 _In_ KPROCESSOR_MODE PreviousMode, 442 _In_opt_ PLUID_AND_ATTRIBUTES AllocatedMem, 443 _In_opt_ ULONG AllocatedLength, 444 _In_ POOL_TYPE PoolType, 445 _In_ BOOLEAN CaptureIfKernel, 446 _Out_ PLUID_AND_ATTRIBUTES *Dest, 447 _Inout_ PULONG Length) 448 { 449 ULONG BufferSize; 450 NTSTATUS Status = STATUS_SUCCESS; 451 452 PAGED_CODE(); 453 454 if (PrivilegeCount == 0) 455 { 456 *Dest = 0; 457 *Length = 0; 458 return STATUS_SUCCESS; 459 } 460 461 if (PrivilegeCount > SE_MAXIMUM_PRIVILEGE_LIMIT) 462 { 463 return STATUS_INVALID_PARAMETER; 464 } 465 466 if (PreviousMode == KernelMode && !CaptureIfKernel) 467 { 468 *Dest = Src; 469 return STATUS_SUCCESS; 470 } 471 472 BufferSize = PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES); 473 *Length = ROUND_UP(BufferSize, 4); /* round up to a 4 byte alignment */ 474 475 /* probe the buffer */ 476 if (PreviousMode != KernelMode) 477 { 478 _SEH2_TRY 479 { 480 ProbeForRead(Src, 481 BufferSize, 482 sizeof(ULONG)); 483 } 484 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 485 { 486 /* Return the exception code */ 487 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 488 } 489 _SEH2_END; 490 } 491 492 /* allocate enough memory or check if the provided buffer is 493 large enough to hold the array */ 494 if (AllocatedMem != NULL) 495 { 496 if (AllocatedLength < BufferSize) 497 { 498 return STATUS_BUFFER_TOO_SMALL; 499 } 500 501 *Dest = AllocatedMem; 502 } 503 else 504 { 505 *Dest = ExAllocatePoolWithTag(PoolType, 506 BufferSize, 507 TAG_LUID); 508 if (*Dest == NULL) 509 { 510 return STATUS_INSUFFICIENT_RESOURCES; 511 } 512 } 513 514 /* copy the array to the buffer */ 515 _SEH2_TRY 516 { 517 RtlCopyMemory(*Dest, 518 Src, 519 BufferSize); 520 } 521 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 522 { 523 Status = _SEH2_GetExceptionCode(); 524 } 525 _SEH2_END; 526 527 if (!NT_SUCCESS(Status) && AllocatedMem == NULL) 528 { 529 ExFreePoolWithTag(*Dest, TAG_LUID); 530 } 531 532 return Status; 533 } 534 535 /** 536 * @brief 537 * Releases a LUID with attributes structure. 538 * 539 * @param[in] Privilege 540 * Array of a LUID and attributes that represents a privilege. 541 * 542 * @param[in] PreviousMode 543 * Processor level access mode. 544 * 545 * @param[in] CaptureIfKernel 546 * If set to TRUE, the releasing is done in the kernel itself. 547 * FALSE if the releasing is done in a kernel mode driver instead. 548 * 549 * @return 550 * Nothing. 551 */ 552 VOID 553 NTAPI 554 SeReleaseLuidAndAttributesArray( 555 _In_ PLUID_AND_ATTRIBUTES Privilege, 556 _In_ KPROCESSOR_MODE PreviousMode, 557 _In_ BOOLEAN CaptureIfKernel) 558 { 559 PAGED_CODE(); 560 561 if (Privilege != NULL && 562 (PreviousMode != KernelMode || CaptureIfKernel)) 563 { 564 ExFreePoolWithTag(Privilege, TAG_LUID); 565 } 566 } 567 568 /* PUBLIC FUNCTIONS ***********************************************************/ 569 570 /** 571 * @brief 572 * Appends additional privileges. 573 * 574 * @param[in] AccessState 575 * Access request to append. 576 * 577 * @param[in] Privileges 578 * Set of new privileges to append. 579 * 580 * @return 581 * Returns STATUS_SUCCESS if the privileges have been successfully 582 * appended. Otherwise STATUS_INSUFFICIENT_RESOURCES is returned, 583 * indicating that pool allocation has failed for the buffer to hold 584 * the new set of privileges. 585 */ 586 NTSTATUS 587 NTAPI 588 SeAppendPrivileges( 589 _Inout_ PACCESS_STATE AccessState, 590 _In_ PPRIVILEGE_SET Privileges) 591 { 592 PAUX_ACCESS_DATA AuxData; 593 ULONG OldPrivilegeSetSize; 594 ULONG NewPrivilegeSetSize; 595 PPRIVILEGE_SET PrivilegeSet; 596 597 PAGED_CODE(); 598 599 /* Get the Auxiliary Data */ 600 AuxData = AccessState->AuxData; 601 602 /* Calculate the size of the old privilege set */ 603 OldPrivilegeSetSize = sizeof(PRIVILEGE_SET) + 604 (AuxData->PrivilegesUsed->PrivilegeCount - 1) * sizeof(LUID_AND_ATTRIBUTES); 605 606 if (AuxData->PrivilegesUsed->PrivilegeCount + 607 Privileges->PrivilegeCount > INITIAL_PRIVILEGE_COUNT) 608 { 609 /* Calculate the size of the new privilege set */ 610 NewPrivilegeSetSize = OldPrivilegeSetSize + 611 Privileges->PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES); 612 613 /* Allocate a new privilege set */ 614 PrivilegeSet = ExAllocatePoolWithTag(PagedPool, 615 NewPrivilegeSetSize, 616 TAG_PRIVILEGE_SET); 617 if (PrivilegeSet == NULL) 618 return STATUS_INSUFFICIENT_RESOURCES; 619 620 /* Copy original privileges from the acess state */ 621 RtlCopyMemory(PrivilegeSet, 622 AuxData->PrivilegesUsed, 623 OldPrivilegeSetSize); 624 625 /* Append privileges from the privilege set*/ 626 RtlCopyMemory((PVOID)((ULONG_PTR)PrivilegeSet + OldPrivilegeSetSize), 627 (PVOID)((ULONG_PTR)Privileges + sizeof(PRIVILEGE_SET) - sizeof(LUID_AND_ATTRIBUTES)), 628 Privileges->PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES)); 629 630 /* Adjust the number of privileges in the new privilege set */ 631 PrivilegeSet->PrivilegeCount += Privileges->PrivilegeCount; 632 633 /* Free the old privilege set if it was allocated */ 634 if (AccessState->PrivilegesAllocated != FALSE) 635 ExFreePoolWithTag(AuxData->PrivilegesUsed, TAG_PRIVILEGE_SET); 636 637 /* Now we are using an allocated privilege set */ 638 AccessState->PrivilegesAllocated = TRUE; 639 640 /* Assign the new privileges to the access state */ 641 AuxData->PrivilegesUsed = PrivilegeSet; 642 } 643 else 644 { 645 /* Append privileges */ 646 RtlCopyMemory((PVOID)((ULONG_PTR)AuxData->PrivilegesUsed + OldPrivilegeSetSize), 647 (PVOID)((ULONG_PTR)Privileges + sizeof(PRIVILEGE_SET) - sizeof(LUID_AND_ATTRIBUTES)), 648 Privileges->PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES)); 649 650 /* Adjust the number of privileges in the target privilege set */ 651 AuxData->PrivilegesUsed->PrivilegeCount += Privileges->PrivilegeCount; 652 } 653 654 return STATUS_SUCCESS; 655 } 656 657 /** 658 * @brief 659 * Frees a set of privileges. 660 * 661 * @param[in] Privileges 662 * Set of privileges array to be freed. 663 * 664 * @return 665 * Nothing. 666 */ 667 VOID 668 NTAPI 669 SeFreePrivileges( 670 _In_ PPRIVILEGE_SET Privileges) 671 { 672 PAGED_CODE(); 673 ExFreePoolWithTag(Privileges, TAG_PRIVILEGE_SET); 674 } 675 676 /** 677 * @brief 678 * Checks if a set of privileges exist and match within a 679 * security subject context. 680 * 681 * @param[in] Privileges 682 * A set of privileges where the check must be performed 683 * against the subject context. 684 * 685 * @param[in] SubjectContext 686 * A subject security context. 687 * 688 * @param[in] PreviousMode 689 * Processor level access mode. 690 * 691 * @return 692 * Returns TRUE if all the privileges do exist and match 693 * with the ones specified by the caller and subject 694 * context, FALSE otherwise. 695 */ 696 BOOLEAN 697 NTAPI 698 SePrivilegeCheck( 699 _In_ PPRIVILEGE_SET Privileges, 700 _In_ PSECURITY_SUBJECT_CONTEXT SubjectContext, 701 _In_ KPROCESSOR_MODE PreviousMode) 702 { 703 PACCESS_TOKEN Token = NULL; 704 705 PAGED_CODE(); 706 707 if (SubjectContext->ClientToken == NULL) 708 { 709 Token = SubjectContext->PrimaryToken; 710 } 711 else 712 { 713 Token = SubjectContext->ClientToken; 714 if (SubjectContext->ImpersonationLevel < 2) 715 { 716 return FALSE; 717 } 718 } 719 720 return SepPrivilegeCheck(Token, 721 Privileges->Privilege, 722 Privileges->PrivilegeCount, 723 Privileges->Control, 724 PreviousMode); 725 } 726 727 /** 728 * @brief 729 * Checks if a single privilege is present in the context 730 * of the calling thread. 731 * 732 * @param[in] PrivilegeValue 733 * The specific privilege to be checked. 734 * 735 * @param[in] PreviousMode 736 * Processor level access mode. 737 * 738 * @return 739 * Returns TRUE if the privilege is present, FALSE 740 * otherwise. 741 */ 742 BOOLEAN 743 NTAPI 744 SeSinglePrivilegeCheck( 745 _In_ LUID PrivilegeValue, 746 _In_ KPROCESSOR_MODE PreviousMode) 747 { 748 SECURITY_SUBJECT_CONTEXT SubjectContext; 749 PRIVILEGE_SET Priv; 750 BOOLEAN Result; 751 752 PAGED_CODE(); 753 754 SeCaptureSubjectContext(&SubjectContext); 755 756 Priv.PrivilegeCount = 1; 757 Priv.Control = PRIVILEGE_SET_ALL_NECESSARY; 758 Priv.Privilege[0].Luid = PrivilegeValue; 759 Priv.Privilege[0].Attributes = SE_PRIVILEGE_ENABLED; 760 761 Result = SePrivilegeCheck(&Priv, 762 &SubjectContext, 763 PreviousMode); 764 765 if (PreviousMode != KernelMode) 766 { 767 SePrivilegedServiceAuditAlarm(NULL, 768 &SubjectContext, 769 &Priv, 770 Result); 771 772 } 773 774 SeReleaseSubjectContext(&SubjectContext); 775 776 return Result; 777 } 778 779 /** 780 * @brief 781 * Checks a privileged object if such object has 782 * the specific privilege submitted by the caller. 783 * 784 * @param[in] PrivilegeValue 785 * A privilege to be checked against the one from 786 * the object. 787 * 788 * @param[in] ObjectHandle 789 * A handle to any kind of object. 790 * 791 * @param[in] DesiredAccess 792 * Desired access right mask requested by the caller. 793 * 794 * @param[in] PreviousMode 795 * Processor level access mode. 796 * 797 * @return 798 * Returns TRUE if the privilege is present, FALSE 799 * otherwise. 800 */ 801 BOOLEAN 802 NTAPI 803 SeCheckPrivilegedObject( 804 _In_ LUID PrivilegeValue, 805 _In_ HANDLE ObjectHandle, 806 _In_ ACCESS_MASK DesiredAccess, 807 _In_ KPROCESSOR_MODE PreviousMode) 808 { 809 SECURITY_SUBJECT_CONTEXT SubjectContext; 810 PRIVILEGE_SET Priv; 811 BOOLEAN Result; 812 813 PAGED_CODE(); 814 815 SeCaptureSubjectContext(&SubjectContext); 816 817 Priv.PrivilegeCount = 1; 818 Priv.Control = PRIVILEGE_SET_ALL_NECESSARY; 819 Priv.Privilege[0].Luid = PrivilegeValue; 820 Priv.Privilege[0].Attributes = SE_PRIVILEGE_ENABLED; 821 822 Result = SePrivilegeCheck(&Priv, &SubjectContext, PreviousMode); 823 if (PreviousMode != KernelMode) 824 { 825 #if 0 826 SePrivilegeObjectAuditAlarm(ObjectHandle, 827 &SubjectContext, 828 DesiredAccess, 829 &PrivilegeValue, 830 Result, 831 PreviousMode); 832 #endif 833 } 834 835 SeReleaseSubjectContext(&SubjectContext); 836 837 return Result; 838 } 839 840 /* SYSTEM CALLS ***************************************************************/ 841 842 /** 843 * @brief 844 * Checks a client access token if it has the required set of 845 * privileges. 846 * 847 * @param[in] ClientToken 848 * A handle to an access client token. 849 * 850 * @param[in] RequiredPrivileges 851 * A set of required privileges to be checked against the privileges 852 * of the access token. 853 * 854 * @param[out] Result 855 * The result, as a boolean value. If TRUE, the token has all the required 856 * privileges, FALSE otherwise. 857 * 858 * @return 859 * Returns STATUS_SUCCESS if the function has completed successfully. 860 * STATUS_INVALID_PARAMETER is returned if the set array of required 861 * privileges has a bogus number of privileges, that is, the array 862 * has a count of privileges that exceeds the maximum threshold 863 * (or in other words, an integer overflow). A failure NTSTATUS code 864 * is returned otherwise. 865 */ 866 NTSTATUS 867 NTAPI 868 NtPrivilegeCheck( 869 _In_ HANDLE ClientToken, 870 _In_ PPRIVILEGE_SET RequiredPrivileges, 871 _Out_ PBOOLEAN Result) 872 { 873 PLUID_AND_ATTRIBUTES Privileges; 874 PTOKEN Token; 875 ULONG PrivilegeCount = 0; 876 ULONG PrivilegeControl = 0; 877 ULONG Length; 878 BOOLEAN CheckResult; 879 KPROCESSOR_MODE PreviousMode; 880 NTSTATUS Status; 881 882 PAGED_CODE(); 883 884 PreviousMode = KeGetPreviousMode(); 885 886 /* probe the buffers */ 887 if (PreviousMode != KernelMode) 888 { 889 _SEH2_TRY 890 { 891 ProbeForWrite(RequiredPrivileges, 892 FIELD_OFFSET(PRIVILEGE_SET, 893 Privilege), 894 sizeof(ULONG)); 895 896 PrivilegeCount = RequiredPrivileges->PrivilegeCount; 897 PrivilegeControl = RequiredPrivileges->Control; 898 899 /* Check PrivilegeCount to avoid an integer overflow! */ 900 if (FIELD_OFFSET(PRIVILEGE_SET, 901 Privilege[PrivilegeCount]) / 902 sizeof(RequiredPrivileges->Privilege[0]) != PrivilegeCount) 903 { 904 _SEH2_YIELD(return STATUS_INVALID_PARAMETER); 905 } 906 907 /* probe all of the array */ 908 ProbeForWrite(RequiredPrivileges, 909 FIELD_OFFSET(PRIVILEGE_SET, 910 Privilege[PrivilegeCount]), 911 sizeof(ULONG)); 912 913 ProbeForWriteBoolean(Result); 914 } 915 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 916 { 917 /* Return the exception code */ 918 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 919 } 920 _SEH2_END; 921 } 922 else 923 { 924 PrivilegeCount = RequiredPrivileges->PrivilegeCount; 925 PrivilegeControl = RequiredPrivileges->Control; 926 } 927 928 /* reference the token and make sure we're 929 not doing an anonymous impersonation */ 930 Status = ObReferenceObjectByHandle(ClientToken, 931 TOKEN_QUERY, 932 SeTokenObjectType, 933 PreviousMode, 934 (PVOID*)&Token, 935 NULL); 936 if (!NT_SUCCESS(Status)) 937 { 938 return Status; 939 } 940 941 if (Token->TokenType == TokenImpersonation && 942 Token->ImpersonationLevel < SecurityIdentification) 943 { 944 ObDereferenceObject(Token); 945 return STATUS_BAD_IMPERSONATION_LEVEL; 946 } 947 948 /* capture the privileges */ 949 Status = SeCaptureLuidAndAttributesArray(RequiredPrivileges->Privilege, 950 PrivilegeCount, 951 PreviousMode, 952 NULL, 953 0, 954 PagedPool, 955 TRUE, 956 &Privileges, 957 &Length); 958 if (!NT_SUCCESS(Status)) 959 { 960 ObDereferenceObject (Token); 961 return Status; 962 } 963 964 CheckResult = SepPrivilegeCheck(Token, 965 Privileges, 966 PrivilegeCount, 967 PrivilegeControl, 968 PreviousMode); 969 970 ObDereferenceObject(Token); 971 972 /* return the array */ 973 _SEH2_TRY 974 { 975 RtlCopyMemory(RequiredPrivileges->Privilege, 976 Privileges, 977 PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES)); 978 *Result = CheckResult; 979 Status = STATUS_SUCCESS; 980 } 981 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 982 { 983 Status = _SEH2_GetExceptionCode(); 984 } 985 _SEH2_END; 986 987 SeReleaseLuidAndAttributesArray(Privileges, 988 PreviousMode, 989 TRUE); 990 991 return Status; 992 } 993 994 /* EOF */ 995