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