1 /* 2 * COPYRIGHT: See COPYING in the top level directory 3 * PROJECT: ReactOS kernel 4 * FILE: ntoskrnl/se/access.c 5 * PURPOSE: Access state functions 6 * 7 * PROGRAMMERS: Alex Ionescu (alex@relsoft.net) - 8 * Based on patch by Javier M. Mellid 9 */ 10 11 /* INCLUDES *******************************************************************/ 12 13 #include <ntoskrnl.h> 14 #define NDEBUG 15 #include <debug.h> 16 17 /* GLOBALS ********************************************************************/ 18 19 ERESOURCE SepSubjectContextLock; 20 21 /* PRIVATE FUNCTIONS **********************************************************/ 22 23 BOOLEAN 24 NTAPI 25 SepSidInTokenEx(IN PACCESS_TOKEN _Token, 26 IN PSID PrincipalSelfSid, 27 IN PSID _Sid, 28 IN BOOLEAN Deny, 29 IN BOOLEAN Restricted) 30 { 31 ULONG i; 32 PTOKEN Token = (PTOKEN)_Token; 33 PISID TokenSid, Sid = (PISID)_Sid; 34 PSID_AND_ATTRIBUTES SidAndAttributes; 35 ULONG SidCount, SidLength; 36 USHORT SidMetadata; 37 PAGED_CODE(); 38 39 /* Not yet supported */ 40 ASSERT(PrincipalSelfSid == NULL); 41 ASSERT(Restricted == FALSE); 42 43 /* Check if a principal SID was given, and this is our current SID already */ 44 if ((PrincipalSelfSid) && (RtlEqualSid(SePrincipalSelfSid, Sid))) 45 { 46 /* Just use the principal SID in this case */ 47 Sid = PrincipalSelfSid; 48 } 49 50 /* Check if this is a restricted token or not */ 51 if (Restricted) 52 { 53 /* Use the restricted SIDs and count */ 54 SidAndAttributes = Token->RestrictedSids; 55 SidCount = Token->RestrictedSidCount; 56 } 57 else 58 { 59 /* Use the normal SIDs and count */ 60 SidAndAttributes = Token->UserAndGroups; 61 SidCount = Token->UserAndGroupCount; 62 } 63 64 /* Do checks here by hand instead of the usual 4 function calls */ 65 SidLength = FIELD_OFFSET(SID, 66 SubAuthority[Sid->SubAuthorityCount]); 67 SidMetadata = *(PUSHORT)&Sid->Revision; 68 69 /* Loop every SID */ 70 for (i = 0; i < SidCount; i++) 71 { 72 TokenSid = (PISID)SidAndAttributes->Sid; 73 #if SE_SID_DEBUG 74 UNICODE_STRING sidString; 75 RtlConvertSidToUnicodeString(&sidString, TokenSid, TRUE); 76 DPRINT1("SID in Token: %wZ\n", &sidString); 77 RtlFreeUnicodeString(&sidString); 78 #endif 79 /* Check if the SID metadata matches */ 80 if (*(PUSHORT)&TokenSid->Revision == SidMetadata) 81 { 82 /* Check if the SID data matches */ 83 if (RtlEqualMemory(Sid, TokenSid, SidLength)) 84 { 85 /* Check if the group is enabled, or used for deny only */ 86 if ((!(i) && !(SidAndAttributes->Attributes & SE_GROUP_USE_FOR_DENY_ONLY)) || 87 (SidAndAttributes->Attributes & SE_GROUP_ENABLED) || 88 ((Deny) && (SidAndAttributes->Attributes & SE_GROUP_USE_FOR_DENY_ONLY))) 89 { 90 /* SID is present */ 91 return TRUE; 92 } 93 else 94 { 95 /* SID is not present */ 96 return FALSE; 97 } 98 } 99 } 100 101 /* Move to the next SID */ 102 SidAndAttributes++; 103 } 104 105 /* SID is not present */ 106 return FALSE; 107 } 108 109 BOOLEAN 110 NTAPI 111 SepSidInToken(IN PACCESS_TOKEN _Token, 112 IN PSID Sid) 113 { 114 /* Call extended API */ 115 return SepSidInTokenEx(_Token, NULL, Sid, FALSE, FALSE); 116 } 117 118 BOOLEAN 119 NTAPI 120 SepTokenIsOwner(IN PACCESS_TOKEN _Token, 121 IN PSECURITY_DESCRIPTOR SecurityDescriptor, 122 IN BOOLEAN TokenLocked) 123 { 124 PSID Sid; 125 BOOLEAN Result; 126 PTOKEN Token = _Token; 127 128 /* Get the owner SID */ 129 Sid = SepGetOwnerFromDescriptor(SecurityDescriptor); 130 ASSERT(Sid != NULL); 131 132 /* Lock the token if needed */ 133 if (!TokenLocked) SepAcquireTokenLockShared(Token); 134 135 /* Check if the owner SID is found, handling restricted case as well */ 136 Result = SepSidInToken(Token, Sid); 137 if ((Result) && (Token->TokenFlags & TOKEN_IS_RESTRICTED)) 138 { 139 Result = SepSidInTokenEx(Token, NULL, Sid, FALSE, TRUE); 140 } 141 142 /* Release the lock if we had acquired it */ 143 if (!TokenLocked) SepReleaseTokenLock(Token); 144 145 /* Return the result */ 146 return Result; 147 } 148 149 VOID 150 NTAPI 151 SeGetTokenControlInformation(IN PACCESS_TOKEN _Token, 152 OUT PTOKEN_CONTROL TokenControl) 153 { 154 PTOKEN Token = _Token; 155 PAGED_CODE(); 156 157 /* Capture the main fields */ 158 TokenControl->AuthenticationId = Token->AuthenticationId; 159 TokenControl->TokenId = Token->TokenId; 160 TokenControl->TokenSource = Token->TokenSource; 161 162 /* Lock the token */ 163 SepAcquireTokenLockShared(Token); 164 165 /* Capture the modified ID */ 166 TokenControl->ModifiedId = Token->ModifiedId; 167 168 /* Unlock it */ 169 SepReleaseTokenLock(Token); 170 } 171 172 NTSTATUS 173 NTAPI 174 SepCreateClientSecurity(IN PACCESS_TOKEN Token, 175 IN PSECURITY_QUALITY_OF_SERVICE ClientSecurityQos, 176 IN BOOLEAN ServerIsRemote, 177 IN TOKEN_TYPE TokenType, 178 IN BOOLEAN ThreadEffectiveOnly, 179 IN SECURITY_IMPERSONATION_LEVEL ImpersonationLevel, 180 OUT PSECURITY_CLIENT_CONTEXT ClientContext) 181 { 182 NTSTATUS Status; 183 PACCESS_TOKEN NewToken; 184 PAGED_CODE(); 185 186 /* Check for bogus impersonation level */ 187 if (!VALID_IMPERSONATION_LEVEL(ClientSecurityQos->ImpersonationLevel)) 188 { 189 /* Fail the call */ 190 return STATUS_INVALID_PARAMETER; 191 } 192 193 /* Check what kind of token this is */ 194 if (TokenType != TokenImpersonation) 195 { 196 /* On a primary token, if we do direct access, copy the flag from the QOS */ 197 ClientContext->DirectAccessEffectiveOnly = ClientSecurityQos->EffectiveOnly; 198 } 199 else 200 { 201 /* This is an impersonation token, is the level ok? */ 202 if (ClientSecurityQos->ImpersonationLevel > ImpersonationLevel) 203 { 204 /* Nope, fail */ 205 return STATUS_BAD_IMPERSONATION_LEVEL; 206 } 207 208 /* Is the level too low, or are we doing something other than delegation remotely */ 209 if ((ImpersonationLevel == SecurityAnonymous) || 210 (ImpersonationLevel == SecurityIdentification) || 211 ((ServerIsRemote) && (ImpersonationLevel != SecurityDelegation))) 212 { 213 /* Fail the call */ 214 return STATUS_BAD_IMPERSONATION_LEVEL; 215 } 216 217 /* Pick either the thread setting or the QOS setting */ 218 ClientContext->DirectAccessEffectiveOnly = 219 ((ThreadEffectiveOnly) || (ClientSecurityQos->EffectiveOnly)) ? TRUE : FALSE; 220 } 221 222 /* Is this static tracking */ 223 if (ClientSecurityQos->ContextTrackingMode == SECURITY_STATIC_TRACKING) 224 { 225 /* Do not use direct access and make a copy */ 226 ClientContext->DirectlyAccessClientToken = FALSE; 227 Status = SeCopyClientToken(Token, 228 ClientSecurityQos->ImpersonationLevel, 229 KernelMode, 230 &NewToken); 231 if (!NT_SUCCESS(Status)) 232 return Status; 233 } 234 else 235 { 236 /* Use direct access and check if this is local */ 237 ClientContext->DirectlyAccessClientToken = TRUE; 238 if (ServerIsRemote) 239 { 240 /* We are doing delegation, so make a copy of the control data */ 241 SeGetTokenControlInformation(Token, 242 &ClientContext->ClientTokenControl); 243 } 244 245 /* Keep the same token */ 246 NewToken = Token; 247 } 248 249 /* Fill out the context and return success */ 250 ClientContext->SecurityQos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE); 251 ClientContext->SecurityQos.ImpersonationLevel = ClientSecurityQos->ImpersonationLevel; 252 ClientContext->SecurityQos.ContextTrackingMode = ClientSecurityQos->ContextTrackingMode; 253 ClientContext->SecurityQos.EffectiveOnly = ClientSecurityQos->EffectiveOnly; 254 ClientContext->ServerIsRemote = ServerIsRemote; 255 ClientContext->ClientToken = NewToken; 256 return STATUS_SUCCESS; 257 } 258 259 /* PUBLIC FUNCTIONS ***********************************************************/ 260 261 /* 262 * @implemented 263 */ 264 VOID 265 NTAPI 266 SeCaptureSubjectContextEx(IN PETHREAD Thread, 267 IN PEPROCESS Process, 268 OUT PSECURITY_SUBJECT_CONTEXT SubjectContext) 269 { 270 BOOLEAN CopyOnOpen, EffectiveOnly; 271 272 PAGED_CODE(); 273 274 /* Save the unique ID */ 275 SubjectContext->ProcessAuditId = Process->UniqueProcessId; 276 277 /* Check if we have a thread */ 278 if (!Thread) 279 { 280 /* We don't, so no token */ 281 SubjectContext->ClientToken = NULL; 282 } 283 else 284 { 285 /* Get the impersonation token */ 286 SubjectContext->ClientToken = PsReferenceImpersonationToken(Thread, 287 &CopyOnOpen, 288 &EffectiveOnly, 289 &SubjectContext->ImpersonationLevel); 290 } 291 292 /* Get the primary token */ 293 SubjectContext->PrimaryToken = PsReferencePrimaryToken(Process); 294 } 295 296 /* 297 * @implemented 298 */ 299 VOID 300 NTAPI 301 SeCaptureSubjectContext(OUT PSECURITY_SUBJECT_CONTEXT SubjectContext) 302 { 303 /* Call the extended API */ 304 SeCaptureSubjectContextEx(PsGetCurrentThread(), 305 PsGetCurrentProcess(), 306 SubjectContext); 307 } 308 309 /* 310 * @implemented 311 */ 312 VOID 313 NTAPI 314 SeLockSubjectContext(IN PSECURITY_SUBJECT_CONTEXT SubjectContext) 315 { 316 PTOKEN PrimaryToken, ClientToken; 317 PAGED_CODE(); 318 319 /* Read both tokens */ 320 PrimaryToken = SubjectContext->PrimaryToken; 321 ClientToken = SubjectContext->ClientToken; 322 323 /* Always lock the primary */ 324 SepAcquireTokenLockShared(PrimaryToken); 325 326 /* Lock the impersonation one if it's there */ 327 if (!ClientToken) return; 328 SepAcquireTokenLockShared(ClientToken); 329 } 330 331 /* 332 * @implemented 333 */ 334 VOID 335 NTAPI 336 SeUnlockSubjectContext(IN PSECURITY_SUBJECT_CONTEXT SubjectContext) 337 { 338 PTOKEN PrimaryToken, ClientToken; 339 PAGED_CODE(); 340 341 /* Read both tokens */ 342 PrimaryToken = SubjectContext->PrimaryToken; 343 ClientToken = SubjectContext->ClientToken; 344 345 /* Unlock the impersonation one if it's there */ 346 if (ClientToken) 347 { 348 SepReleaseTokenLock(ClientToken); 349 } 350 351 /* Always unlock the primary one */ 352 SepReleaseTokenLock(PrimaryToken); 353 } 354 355 /* 356 * @implemented 357 */ 358 VOID 359 NTAPI 360 SeReleaseSubjectContext(IN PSECURITY_SUBJECT_CONTEXT SubjectContext) 361 { 362 PAGED_CODE(); 363 364 /* Drop reference on the primary */ 365 ObFastDereferenceObject(&PsGetCurrentProcess()->Token, SubjectContext->PrimaryToken); 366 SubjectContext->PrimaryToken = NULL; 367 368 /* Drop reference on the impersonation, if there was one */ 369 PsDereferenceImpersonationToken(SubjectContext->ClientToken); 370 SubjectContext->ClientToken = NULL; 371 } 372 373 /* 374 * @implemented 375 */ 376 NTSTATUS 377 NTAPI 378 SeCreateAccessStateEx(IN PETHREAD Thread, 379 IN PEPROCESS Process, 380 IN OUT PACCESS_STATE AccessState, 381 IN PAUX_ACCESS_DATA AuxData, 382 IN ACCESS_MASK Access, 383 IN PGENERIC_MAPPING GenericMapping) 384 { 385 ACCESS_MASK AccessMask = Access; 386 PTOKEN Token; 387 PAGED_CODE(); 388 389 /* Map the Generic Acess to Specific Access if we have a Mapping */ 390 if ((Access & GENERIC_ACCESS) && (GenericMapping)) 391 { 392 RtlMapGenericMask(&AccessMask, GenericMapping); 393 } 394 395 /* Initialize the Access State */ 396 RtlZeroMemory(AccessState, sizeof(ACCESS_STATE)); 397 ASSERT(AccessState->SecurityDescriptor == NULL); 398 ASSERT(AccessState->PrivilegesAllocated == FALSE); 399 400 /* Initialize and save aux data */ 401 RtlZeroMemory(AuxData, sizeof(AUX_ACCESS_DATA)); 402 AccessState->AuxData = AuxData; 403 404 /* Capture the Subject Context */ 405 SeCaptureSubjectContextEx(Thread, 406 Process, 407 &AccessState->SubjectSecurityContext); 408 409 /* Set Access State Data */ 410 AccessState->RemainingDesiredAccess = AccessMask; 411 AccessState->OriginalDesiredAccess = AccessMask; 412 ExAllocateLocallyUniqueId(&AccessState->OperationID); 413 414 /* Get the Token to use */ 415 Token = SeQuerySubjectContextToken(&AccessState->SubjectSecurityContext); 416 417 /* Check for Travers Privilege */ 418 if (Token->TokenFlags & TOKEN_HAS_TRAVERSE_PRIVILEGE) 419 { 420 /* Preserve the Traverse Privilege */ 421 AccessState->Flags = TOKEN_HAS_TRAVERSE_PRIVILEGE; 422 } 423 424 /* Set the Auxiliary Data */ 425 AuxData->PrivilegeSet = (PPRIVILEGE_SET)((ULONG_PTR)AccessState + 426 FIELD_OFFSET(ACCESS_STATE, 427 Privileges)); 428 if (GenericMapping) AuxData->GenericMapping = *GenericMapping; 429 430 /* Return Sucess */ 431 return STATUS_SUCCESS; 432 } 433 434 /* 435 * @implemented 436 */ 437 NTSTATUS 438 NTAPI 439 SeCreateAccessState(IN OUT PACCESS_STATE AccessState, 440 IN PAUX_ACCESS_DATA AuxData, 441 IN ACCESS_MASK Access, 442 IN PGENERIC_MAPPING GenericMapping) 443 { 444 PAGED_CODE(); 445 446 /* Call the extended API */ 447 return SeCreateAccessStateEx(PsGetCurrentThread(), 448 PsGetCurrentProcess(), 449 AccessState, 450 AuxData, 451 Access, 452 GenericMapping); 453 } 454 455 /* 456 * @implemented 457 */ 458 VOID 459 NTAPI 460 SeDeleteAccessState(IN PACCESS_STATE AccessState) 461 { 462 PAUX_ACCESS_DATA AuxData; 463 PAGED_CODE(); 464 465 /* Get the Auxiliary Data */ 466 AuxData = AccessState->AuxData; 467 468 /* Deallocate Privileges */ 469 if (AccessState->PrivilegesAllocated) 470 ExFreePoolWithTag(AuxData->PrivilegeSet, TAG_PRIVILEGE_SET); 471 472 /* Deallocate Name and Type Name */ 473 if (AccessState->ObjectName.Buffer) 474 { 475 ExFreePool(AccessState->ObjectName.Buffer); 476 } 477 478 if (AccessState->ObjectTypeName.Buffer) 479 { 480 ExFreePool(AccessState->ObjectTypeName.Buffer); 481 } 482 483 /* Release the Subject Context */ 484 SeReleaseSubjectContext(&AccessState->SubjectSecurityContext); 485 } 486 487 /* 488 * @implemented 489 */ 490 VOID 491 NTAPI 492 SeSetAccessStateGenericMapping(IN PACCESS_STATE AccessState, 493 IN PGENERIC_MAPPING GenericMapping) 494 { 495 PAGED_CODE(); 496 497 /* Set the Generic Mapping */ 498 ((PAUX_ACCESS_DATA)AccessState->AuxData)->GenericMapping = *GenericMapping; 499 } 500 501 /* 502 * @implemented 503 */ 504 NTSTATUS 505 NTAPI 506 SeCreateClientSecurity(IN PETHREAD Thread, 507 IN PSECURITY_QUALITY_OF_SERVICE Qos, 508 IN BOOLEAN RemoteClient, 509 OUT PSECURITY_CLIENT_CONTEXT ClientContext) 510 { 511 TOKEN_TYPE TokenType; 512 BOOLEAN ThreadEffectiveOnly; 513 SECURITY_IMPERSONATION_LEVEL ImpersonationLevel; 514 PACCESS_TOKEN Token; 515 NTSTATUS Status; 516 PAGED_CODE(); 517 518 /* Reference the correct token */ 519 Token = PsReferenceEffectiveToken(Thread, 520 &TokenType, 521 &ThreadEffectiveOnly, 522 &ImpersonationLevel); 523 524 /* Create client security from it */ 525 Status = SepCreateClientSecurity(Token, 526 Qos, 527 RemoteClient, 528 TokenType, 529 ThreadEffectiveOnly, 530 ImpersonationLevel, 531 ClientContext); 532 533 /* Check if we failed or static tracking was used */ 534 if (!(NT_SUCCESS(Status)) || (Qos->ContextTrackingMode == SECURITY_STATIC_TRACKING)) 535 { 536 /* Dereference our copy since it's not being used */ 537 ObDereferenceObject(Token); 538 } 539 540 /* Return status */ 541 return Status; 542 } 543 544 /* 545 * @implemented 546 */ 547 NTSTATUS 548 NTAPI 549 SeCreateClientSecurityFromSubjectContext(IN PSECURITY_SUBJECT_CONTEXT SubjectContext, 550 IN PSECURITY_QUALITY_OF_SERVICE ClientSecurityQos, 551 IN BOOLEAN ServerIsRemote, 552 OUT PSECURITY_CLIENT_CONTEXT ClientContext) 553 { 554 PACCESS_TOKEN Token; 555 NTSTATUS Status; 556 PAGED_CODE(); 557 558 /* Get the right token and reference it */ 559 Token = SeQuerySubjectContextToken(SubjectContext); 560 ObReferenceObject(Token); 561 562 /* Create the context */ 563 Status = SepCreateClientSecurity(Token, 564 ClientSecurityQos, 565 ServerIsRemote, 566 SubjectContext->ClientToken ? 567 TokenImpersonation : TokenPrimary, 568 FALSE, 569 SubjectContext->ImpersonationLevel, 570 ClientContext); 571 572 /* Check if we failed or static tracking was used */ 573 if (!(NT_SUCCESS(Status)) || 574 (ClientSecurityQos->ContextTrackingMode == SECURITY_STATIC_TRACKING)) 575 { 576 /* Dereference our copy since it's not being used */ 577 ObDereferenceObject(Token); 578 } 579 580 /* Return status */ 581 return Status; 582 } 583 584 /* 585 * @implemented 586 */ 587 NTSTATUS 588 NTAPI 589 SeImpersonateClientEx(IN PSECURITY_CLIENT_CONTEXT ClientContext, 590 IN PETHREAD ServerThread OPTIONAL) 591 { 592 BOOLEAN EffectiveOnly; 593 PAGED_CODE(); 594 595 /* Check if direct access is requested */ 596 if (!ClientContext->DirectlyAccessClientToken) 597 { 598 /* No, so get the flag from QOS */ 599 EffectiveOnly = ClientContext->SecurityQos.EffectiveOnly; 600 } 601 else 602 { 603 /* Yes, so see if direct access should be effective only */ 604 EffectiveOnly = ClientContext->DirectAccessEffectiveOnly; 605 } 606 607 /* Use the current thread if one was not passed */ 608 if (!ServerThread) ServerThread = PsGetCurrentThread(); 609 610 /* Call the lower layer routine */ 611 return PsImpersonateClient(ServerThread, 612 ClientContext->ClientToken, 613 TRUE, 614 EffectiveOnly, 615 ClientContext->SecurityQos.ImpersonationLevel); 616 } 617 618 /* 619 * @implemented 620 */ 621 VOID 622 NTAPI 623 SeImpersonateClient(IN PSECURITY_CLIENT_CONTEXT ClientContext, 624 IN PETHREAD ServerThread OPTIONAL) 625 { 626 PAGED_CODE(); 627 628 /* Call the new API */ 629 SeImpersonateClientEx(ClientContext, ServerThread); 630 } 631 632 /* EOF */ 633