1 /* 2 * COPYRIGHT: See COPYING in the top level directory 3 * PROJECT: ReactOS system libraries 4 * FILE: lib/rtl/priv.c 5 * PURPOSE: Security related functions and Security Objects 6 * PROGRAMMER: Eric Kohl 7 * Pierre Schweitzer (pierre@reactos.org) 8 */ 9 10 /* INCLUDES *****************************************************************/ 11 12 #include <rtl.h> 13 14 #define NDEBUG 15 #include <debug.h> 16 17 /* FUNCTIONS ***************************************************************/ 18 19 /* 20 * @implemented 21 */ 22 NTSTATUS 23 NTAPI 24 RtlpOpenThreadToken(IN ACCESS_MASK DesiredAccess, 25 OUT PHANDLE TokenHandle) 26 { 27 NTSTATUS Status; 28 29 Status = ZwOpenThreadToken(NtCurrentThread(), DesiredAccess, 30 TRUE, TokenHandle); 31 if (!NT_SUCCESS(Status)) 32 { 33 Status = ZwOpenThreadToken(NtCurrentThread(), DesiredAccess, 34 FALSE, TokenHandle); 35 } 36 37 return Status; 38 } 39 40 /* 41 * @implemented 42 */ 43 NTSTATUS 44 NTAPI 45 RtlImpersonateSelf(IN SECURITY_IMPERSONATION_LEVEL ImpersonationLevel) 46 { 47 HANDLE ProcessToken; 48 HANDLE ImpersonationToken; 49 NTSTATUS Status; 50 OBJECT_ATTRIBUTES ObjAttr; 51 SECURITY_QUALITY_OF_SERVICE Sqos; 52 53 PAGED_CODE_RTL(); 54 55 Status = ZwOpenProcessToken(NtCurrentProcess(), 56 TOKEN_DUPLICATE, 57 &ProcessToken); 58 if (!NT_SUCCESS(Status)) 59 { 60 DPRINT1("NtOpenProcessToken() failed (Status %lx)\n", Status); 61 return Status; 62 } 63 64 Sqos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE); 65 Sqos.ImpersonationLevel = ImpersonationLevel; 66 Sqos.ContextTrackingMode = 0; 67 Sqos.EffectiveOnly = FALSE; 68 69 InitializeObjectAttributes(&ObjAttr, 70 NULL, 71 0, 72 NULL, 73 NULL); 74 75 ObjAttr.SecurityQualityOfService = &Sqos; 76 77 Status = ZwDuplicateToken(ProcessToken, 78 TOKEN_IMPERSONATE, 79 &ObjAttr, 80 Sqos.EffectiveOnly, /* why both here _and_ in Sqos? */ 81 TokenImpersonation, 82 &ImpersonationToken); 83 if (!NT_SUCCESS(Status)) 84 { 85 DPRINT1("NtDuplicateToken() failed (Status %lx)\n", Status); 86 NtClose(ProcessToken); 87 return Status; 88 } 89 90 Status = ZwSetInformationThread(NtCurrentThread(), 91 ThreadImpersonationToken, 92 &ImpersonationToken, 93 sizeof(HANDLE)); 94 if (!NT_SUCCESS(Status)) 95 { 96 DPRINT1("NtSetInformationThread() failed (Status %lx)\n", Status); 97 } 98 99 ZwClose(ImpersonationToken); 100 ZwClose(ProcessToken); 101 102 return Status; 103 } 104 105 /* 106 * @implemented 107 */ 108 NTSTATUS 109 NTAPI 110 RtlAcquirePrivilege(IN PULONG Privilege, 111 IN ULONG NumPriv, 112 IN ULONG Flags, 113 OUT PVOID *ReturnedState) 114 { 115 PRTL_ACQUIRE_STATE State; 116 NTSTATUS Status, IntStatus; 117 ULONG ReturnLength, i, OldSize; 118 SECURITY_QUALITY_OF_SERVICE Sqos; 119 OBJECT_ATTRIBUTES ObjectAttributes; 120 HANDLE ImpersonationToken = 0, ProcessToken; 121 122 DPRINT("RtlAcquirePrivilege(%p, %u, %u, %p)\n", Privilege, NumPriv, Flags, ReturnedState); 123 124 /* Validate flags */ 125 if (Flags & ~(RTL_ACQUIRE_PRIVILEGE_PROCESS | RTL_ACQUIRE_PRIVILEGE_IMPERSONATE)) 126 { 127 return STATUS_INVALID_PARAMETER; 128 } 129 130 /* If user wants to acquire privileges for the process, we have to impersonate him */ 131 if (Flags & RTL_ACQUIRE_PRIVILEGE_PROCESS) 132 { 133 Flags |= RTL_ACQUIRE_PRIVILEGE_IMPERSONATE; 134 } 135 136 /* Allocate enough memory to hold: old privileges (fixed buffer size, might not be enough) 137 * new privileges (big enough, after old privileges memory area) 138 */ 139 State = RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(RTL_ACQUIRE_STATE) + sizeof(TOKEN_PRIVILEGES) + 140 (NumPriv - ANYSIZE_ARRAY) * sizeof(LUID_AND_ATTRIBUTES)); 141 if (!State) 142 { 143 return STATUS_NO_MEMORY; 144 } 145 146 /* Only zero a bit of the memory (will be faster that way) */ 147 State->Token = 0; 148 State->OldImpersonationToken = 0; 149 State->Flags = 0; 150 State->OldPrivileges = NULL; 151 152 /* Check whether we have already an active impersonation */ 153 if (NtCurrentTeb()->IsImpersonating) 154 { 155 /* Check whether we want to impersonate */ 156 if (Flags & RTL_ACQUIRE_PRIVILEGE_IMPERSONATE) 157 { 158 /* That's all fine, just get the token. 159 * We need access for: adjust (obvious...) but also 160 * query, to be able to query old privileges 161 */ 162 Status = RtlpOpenThreadToken(TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &State->Token); 163 if (!NT_SUCCESS(Status)) 164 { 165 RtlFreeHeap(RtlGetProcessHeap(), 0, State); 166 return Status; 167 } 168 } 169 else 170 { 171 /* Otherwise, we have to temporary disable active impersonation. 172 * Get previous impersonation token to save it 173 */ 174 Status = RtlpOpenThreadToken(TOKEN_IMPERSONATE, &State->OldImpersonationToken); 175 if (!NT_SUCCESS(Status)) 176 { 177 RtlFreeHeap(RtlGetProcessHeap(), 0, State); 178 return Status; 179 } 180 181 /* Remember the fact we had an active impersonation */ 182 State->Flags |= RTL_ACQUIRE_PRIVILEGE_IMPERSONATE; 183 184 /* Revert impersonation (ie, give 0 as handle) */ 185 Status = ZwSetInformationThread(NtCurrentThread(), 186 ThreadImpersonationToken, 187 &ImpersonationToken, 188 sizeof(HANDLE)); 189 } 190 } 191 192 /* If we have no token yet (which is likely) */ 193 if (!State->Token) 194 { 195 /* If we are asked to use process, then do */ 196 if (Flags & RTL_ACQUIRE_PRIVILEGE_PROCESS) 197 { 198 Status = ZwOpenProcessToken(NtCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, 199 &State->Token); 200 if (!NT_SUCCESS(Status)) 201 { 202 goto Cleanup; 203 } 204 } 205 else 206 { 207 /* Otherwise, we have to impersonate. 208 * Open token for duplication 209 */ 210 Status = ZwOpenProcessToken(NtCurrentProcess(), TOKEN_DUPLICATE, &ProcessToken); 211 212 InitializeObjectAttributes(&ObjectAttributes, 213 NULL, 214 0, 215 NULL, 216 NULL); 217 218 ObjectAttributes.SecurityQualityOfService = &Sqos; 219 Sqos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE); 220 Sqos.ImpersonationLevel = SecurityDelegation; 221 Sqos.ContextTrackingMode = 1; 222 Sqos.EffectiveOnly = FALSE; 223 224 /* Duplicate */ 225 Status = ZwDuplicateToken(ProcessToken, 226 TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY | TOKEN_IMPERSONATE, 227 &ObjectAttributes, 228 FALSE, 229 TokenImpersonation, 230 &ImpersonationToken); 231 if (!NT_SUCCESS(Status)) 232 { 233 ZwClose(ProcessToken); 234 goto Cleanup; 235 } 236 237 /* Assign our duplicated token to current thread */ 238 Status = ZwSetInformationThread(NtCurrentThread(), 239 ThreadImpersonationToken, 240 &ImpersonationToken, 241 sizeof(HANDLE)); 242 if (!NT_SUCCESS(Status)) 243 { 244 ZwClose(ImpersonationToken); 245 ZwClose(ProcessToken); 246 goto Cleanup; 247 } 248 249 /* Save said token and the fact we have impersonated */ 250 State->Token = ImpersonationToken; 251 State->Flags |= RTL_ACQUIRE_PRIVILEGE_IMPERSONATE; 252 253 ZwClose(ProcessToken); 254 } 255 } 256 257 /* Properly set the privileges pointers: 258 * OldPrivileges points to the static memory in struct (= OldPrivBuffer) 259 * NewPrivileges points to the dynamic memory after OldPrivBuffer 260 * There's NO overflow risks (OldPrivileges is always used with its size) 261 */ 262 State->OldPrivileges = (PTOKEN_PRIVILEGES)State->OldPrivBuffer; 263 State->NewPrivileges = (PTOKEN_PRIVILEGES)(State->OldPrivBuffer + (sizeof(State->OldPrivBuffer) / sizeof(State->OldPrivBuffer[0]))); 264 265 /* Assign all the privileges to be acquired */ 266 State->NewPrivileges->PrivilegeCount = NumPriv; 267 for (i = 0; i < NumPriv; ++i) 268 { 269 State->NewPrivileges->Privileges[i].Luid.LowPart = Privilege[i]; 270 State->NewPrivileges->Privileges[i].Luid.HighPart = 0; 271 State->NewPrivileges->Privileges[i].Attributes = SE_PRIVILEGE_ENABLED; 272 } 273 274 /* Start privileges adjustements */ 275 OldSize = sizeof(State->OldPrivBuffer); 276 do 277 { 278 ReturnLength = sizeof(State->OldPrivBuffer); 279 Status = ZwAdjustPrivilegesToken(State->Token, FALSE, State->NewPrivileges, 280 OldSize, State->OldPrivileges, &ReturnLength); 281 /* This is returned when OldPrivileges buffer is too small */ 282 if (Status == STATUS_BUFFER_TOO_SMALL) 283 { 284 /* Try to allocate a new one, big enough to hold data */ 285 State->OldPrivileges = RtlAllocateHeap(RtlGetProcessHeap(), 0, ReturnLength); 286 if (State->OldPrivileges) 287 { 288 DPRINT("Allocated old privileges: %p\n", State->OldPrivileges); 289 OldSize = ReturnLength; 290 continue; 291 } 292 else 293 { 294 /* If we failed, properly set status: we failed because of the lack of memory */ 295 Status = STATUS_NO_MEMORY; 296 } 297 } 298 299 /* If we failed to assign at least one privilege */ 300 if (Status == STATUS_NOT_ALL_ASSIGNED) 301 { 302 /* If there was actually only one privilege to acquire, use more accurate status */ 303 if (NumPriv == 1) 304 { 305 Status = STATUS_PRIVILEGE_NOT_HELD; 306 } 307 } 308 309 /* Fail if needed, otherwise return our state to caller */ 310 if (!NT_SUCCESS(Status)) 311 { 312 goto Cleanup; 313 } 314 else 315 { 316 *ReturnedState = State; 317 break; 318 } 319 } while (TRUE); 320 321 DPRINT("RtlAcquirePrivilege succeed!\n"); 322 323 return Status; 324 325 Cleanup: 326 /* If we allocated our own buffer for old privileges, release it */ 327 if (State->OldPrivileges && (PVOID)State->OldPrivBuffer != (PVOID)State->OldPrivileges) 328 { 329 RtlFreeHeap(RtlGetProcessHeap(), 0, State->OldPrivileges); 330 } 331 332 /* Do we have to restore previously active impersonation? */ 333 if (State->Flags & RTL_ACQUIRE_PRIVILEGE_IMPERSONATE) 334 { 335 IntStatus = ZwSetInformationThread(NtCurrentThread(), ThreadImpersonationToken, 336 &State->OldImpersonationToken, sizeof(HANDLE)); 337 /* If this ever happens, we're in a really bad situation... */ 338 if (!NT_SUCCESS(IntStatus)) 339 { 340 RtlRaiseStatus(IntStatus); 341 } 342 } 343 344 /* Release token */ 345 if (State->Token) 346 { 347 ZwClose(State->Token); 348 } 349 350 /* And free our state buffer */ 351 RtlFreeHeap(RtlGetProcessHeap(), 0, State); 352 353 DPRINT("RtlAcquirePrivilege() failed with status: %lx\n", Status); 354 355 return Status; 356 } 357 358 /* 359 * @implemented 360 */ 361 VOID 362 NTAPI 363 RtlReleasePrivilege(IN PVOID ReturnedState) 364 { 365 NTSTATUS Status; 366 PRTL_ACQUIRE_STATE State = (PRTL_ACQUIRE_STATE)ReturnedState; 367 368 DPRINT("RtlReleasePrivilege(%p)\n", ReturnedState); 369 370 /* If we had an active impersonation before we acquired privileges 371 * Or if we have impersonated, quit it 372 */ 373 if (State->Flags & RTL_ACQUIRE_PRIVILEGE_IMPERSONATE) 374 { 375 /* Restore it for the current thread */ 376 Status = ZwSetInformationThread(NtCurrentThread(), ThreadImpersonationToken, 377 &State->OldImpersonationToken, sizeof(HANDLE)); 378 if (!NT_SUCCESS(Status)) 379 { 380 RtlRaiseStatus(Status); 381 } 382 383 /* And close the token if needed */ 384 if (State->OldImpersonationToken) 385 ZwClose(State->OldImpersonationToken); 386 } 387 else 388 { 389 /* Otherwise, restore old state */ 390 Status = ZwAdjustPrivilegesToken(State->Token, FALSE, 391 State->OldPrivileges, 0, NULL, NULL); 392 if (!NT_SUCCESS(Status)) 393 { 394 RtlRaiseStatus(Status); 395 } 396 } 397 398 /* If we used a different buffer for old privileges, just free it */ 399 if ((PVOID)State->OldPrivBuffer != (PVOID)State->OldPrivileges) 400 { 401 DPRINT("Releasing old privileges: %p\n", State->OldPrivileges); 402 RtlFreeHeap(RtlGetProcessHeap(), 0, State->OldPrivileges); 403 } 404 405 /* Release token and free state */ 406 ZwClose(State->Token); 407 RtlFreeHeap(RtlGetProcessHeap(), 0, State); 408 } 409 410 /* 411 * @implemented 412 */ 413 NTSTATUS 414 NTAPI 415 RtlAdjustPrivilege(IN ULONG Privilege, 416 IN BOOLEAN Enable, 417 IN BOOLEAN CurrentThread, 418 OUT PBOOLEAN Enabled) 419 { 420 TOKEN_PRIVILEGES NewState; 421 TOKEN_PRIVILEGES OldState; 422 ULONG ReturnLength; 423 HANDLE TokenHandle; 424 NTSTATUS Status; 425 426 PAGED_CODE_RTL(); 427 428 DPRINT("RtlAdjustPrivilege() called\n"); 429 430 if (CurrentThread) 431 { 432 Status = ZwOpenThreadToken(NtCurrentThread(), 433 TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, 434 FALSE, 435 &TokenHandle); 436 } 437 else 438 { 439 Status = ZwOpenProcessToken(NtCurrentProcess(), 440 TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, 441 &TokenHandle); 442 } 443 444 if (!NT_SUCCESS (Status)) 445 { 446 DPRINT1("Retrieving token handle failed (Status %lx)\n", Status); 447 return Status; 448 } 449 450 OldState.PrivilegeCount = 1; 451 452 NewState.PrivilegeCount = 1; 453 NewState.Privileges[0].Luid.LowPart = Privilege; 454 NewState.Privileges[0].Luid.HighPart = 0; 455 NewState.Privileges[0].Attributes = (Enable) ? SE_PRIVILEGE_ENABLED : 0; 456 457 Status = ZwAdjustPrivilegesToken(TokenHandle, 458 FALSE, 459 &NewState, 460 sizeof(TOKEN_PRIVILEGES), 461 &OldState, 462 &ReturnLength); 463 ZwClose (TokenHandle); 464 if (Status == STATUS_NOT_ALL_ASSIGNED) 465 { 466 DPRINT1("Failed to assign all privileges\n"); 467 return STATUS_PRIVILEGE_NOT_HELD; 468 } 469 470 if (!NT_SUCCESS(Status)) 471 { 472 DPRINT1("NtAdjustPrivilegesToken() failed (Status %lx)\n", Status); 473 return Status; 474 } 475 476 if (OldState.PrivilegeCount == 0) 477 { 478 *Enabled = Enable; 479 } 480 else 481 { 482 *Enabled = (OldState.Privileges[0].Attributes & SE_PRIVILEGE_ENABLED); 483 } 484 485 DPRINT("RtlAdjustPrivilege() done\n"); 486 487 return STATUS_SUCCESS; 488 } 489 490 #if (NTDDI_VERSION >= NTDDI_VISTA) || (DLL_EXPORT_VERSION >= _WIN32_WINNT_VISTA) 491 492 /** 493 * @brief 494 * Removes all privileges in the specified access token. 495 * 496 * @param[in] TokenHandle 497 * A handle to the access token that contains the privileges to be removed. 498 * 499 * @param[in] PrivilegesToKeep 500 * A pointer to an array of privilege values (defined as SE_XXX_PRIVILEGE) that specify 501 * the privileges to keep in the token. 502 * 503 * @param[in] PrivilegeCount 504 * Specifies the number of entries in the PrivilegesToKeep array. 505 * 506 * @return 507 * Returns STATUS_SUCCESS if privileges removed successfully. 508 * STATUS_INVALID_PARAMETER is returned if input privilege value greater than 509 * SE_MAX_WELL_KNOWN_PRIVILEGE. STATUS_NOT_ALL_ASSIGNED is returned if The token does 510 * not have one or more of the privileges specified in the PrivilegesToKeep parameter, 511 * and no privileges were removed. A failure NTSTATUS code is returned otherwise. 512 */ 513 NTSTATUS 514 NTAPI 515 RtlRemovePrivileges( 516 _In_ HANDLE TokenHandle, 517 _In_reads_opt_(PrivilegeCount) _When_(PrivilegeCount != 0, _Notnull_) 518 PULONG PrivilegesToKeep, 519 _In_ ULONG PrivilegeCount) 520 { 521 NTSTATUS Status; 522 UINT64 PrivilegesToKeepBitmap; 523 ULONG i, ReturnLength; 524 UCHAR Buffer[sizeof(TOKEN_PRIVILEGES) + 525 sizeof(LUID_AND_ATTRIBUTES) * (SE_MAX_WELL_KNOWN_PRIVILEGE - SE_MIN_WELL_KNOWN_PRIVILEGE)]; 526 PTOKEN_PRIVILEGES Privileges; 527 528 C_ASSERT(SE_MAX_WELL_KNOWN_PRIVILEGE < 64); 529 530 DPRINT("RtlRemovePrivileges(%p, %p, %u)\n", TokenHandle, PrivilegesToKeep, PrivilegeCount); 531 532 /* Save privileges that should be keep */ 533 PrivilegesToKeepBitmap = 0; 534 if (PrivilegeCount) 535 { 536 for (i = 0; i < PrivilegeCount; i++) 537 { 538 if (PrivilegesToKeep[i] > SE_MAX_WELL_KNOWN_PRIVILEGE) 539 { 540 return STATUS_INVALID_PARAMETER; 541 } 542 PrivilegesToKeepBitmap |= (1ULL << PrivilegesToKeep[i]); 543 } 544 } 545 546 /* Get token privileges information */ 547 Status = ZwQueryInformationToken(TokenHandle, 548 TokenPrivileges, 549 Buffer, 550 sizeof(Buffer), 551 &ReturnLength); 552 if (!NT_SUCCESS(Status)) 553 { 554 return Status; 555 } 556 557 /* Remove all privileges that we don't need to keep */ 558 Privileges = (PTOKEN_PRIVILEGES)Buffer; 559 for (i = 0; i < Privileges->PrivilegeCount; i++) 560 { 561 LARGE_INTEGER Privilege = *(LARGE_INTEGER*)&Privileges->Privileges[i].Luid; 562 ASSERT(Privilege.QuadPart <= SE_MAX_WELL_KNOWN_PRIVILEGE); 563 if (PrivilegesToKeepBitmap & (1ULL << Privilege.QuadPart)) 564 { 565 PrivilegesToKeepBitmap &= ~(1ULL << Privilege.QuadPart); 566 } 567 else 568 { 569 Privileges->Privileges[i].Attributes = SE_PRIVILEGE_REMOVED; 570 } 571 } 572 573 if (PrivilegesToKeepBitmap) 574 { 575 Status = STATUS_NOT_ALL_ASSIGNED; 576 } 577 else 578 { 579 Status = ZwAdjustPrivilegesToken(TokenHandle, 580 FALSE, 581 (PTOKEN_PRIVILEGES)Buffer, 582 sizeof(Buffer), 583 NULL, 584 NULL); 585 } 586 587 return Status; 588 } 589 590 #endif /* (NTDDI_VERSION >= NTDDI_VISTA) */ 591