Something went wrong. Try again.
Reactos
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591/* * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS system libraries * FILE: lib/rtl/priv.c * PURPOSE: Security related functions and Security Objects * PROGRAMMER: Eric Kohl * Pierre Schweitzer (pierre@reactos.org) */
/* INCLUDES *****************************************************************/
#include <rtl.h>
#define NDEBUG#include <debug.h>
/* FUNCTIONS ***************************************************************/
/* * @implemented */NTSTATUSNTAPIRtlpOpenThreadToken(IN ACCESS_MASK DesiredAccess, OUT PHANDLE TokenHandle){ NTSTATUS Status;
Status = ZwOpenThreadToken(NtCurrentThread(), DesiredAccess, TRUE, TokenHandle); if (!NT_SUCCESS(Status)) { Status = ZwOpenThreadToken(NtCurrentThread(), DesiredAccess, FALSE, TokenHandle); }
return Status;}
/* * @implemented */NTSTATUSNTAPIRtlImpersonateSelf(IN SECURITY_IMPERSONATION_LEVEL ImpersonationLevel){ HANDLE ProcessToken; HANDLE ImpersonationToken; NTSTATUS Status; OBJECT_ATTRIBUTES ObjAttr; SECURITY_QUALITY_OF_SERVICE Sqos;
PAGED_CODE_RTL();
Status = ZwOpenProcessToken(NtCurrentProcess(), TOKEN_DUPLICATE, &ProcessToken); if (!NT_SUCCESS(Status)) { DPRINT1("NtOpenProcessToken() failed (Status %lx)\n", Status); return Status; }
Sqos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE); Sqos.ImpersonationLevel = ImpersonationLevel; Sqos.ContextTrackingMode = 0; Sqos.EffectiveOnly = FALSE;
InitializeObjectAttributes(&ObjAttr, NULL, 0, NULL, NULL);
ObjAttr.SecurityQualityOfService = &Sqos;
Status = ZwDuplicateToken(ProcessToken, TOKEN_IMPERSONATE, &ObjAttr, Sqos.EffectiveOnly, /* why both here _and_ in Sqos? */ TokenImpersonation, &ImpersonationToken); if (!NT_SUCCESS(Status)) { DPRINT1("NtDuplicateToken() failed (Status %lx)\n", Status); NtClose(ProcessToken); return Status; }
Status = ZwSetInformationThread(NtCurrentThread(), ThreadImpersonationToken, &ImpersonationToken, sizeof(HANDLE)); if (!NT_SUCCESS(Status)) { DPRINT1("NtSetInformationThread() failed (Status %lx)\n", Status); }
ZwClose(ImpersonationToken); ZwClose(ProcessToken);
return Status;}
/* * @implemented */NTSTATUSNTAPIRtlAcquirePrivilege(IN PULONG Privilege, IN ULONG NumPriv, IN ULONG Flags, OUT PVOID *ReturnedState){ PRTL_ACQUIRE_STATE State; NTSTATUS Status, IntStatus; ULONG ReturnLength, i, OldSize; SECURITY_QUALITY_OF_SERVICE Sqos; OBJECT_ATTRIBUTES ObjectAttributes; HANDLE ImpersonationToken = 0, ProcessToken;
DPRINT("RtlAcquirePrivilege(%p, %u, %u, %p)\n", Privilege, NumPriv, Flags, ReturnedState);
/* Validate flags */ if (Flags & ~(RTL_ACQUIRE_PRIVILEGE_PROCESS | RTL_ACQUIRE_PRIVILEGE_IMPERSONATE)) { return STATUS_INVALID_PARAMETER; }
/* If user wants to acquire privileges for the process, we have to impersonate him */ if (Flags & RTL_ACQUIRE_PRIVILEGE_PROCESS) { Flags |= RTL_ACQUIRE_PRIVILEGE_IMPERSONATE; }
/* Allocate enough memory to hold: old privileges (fixed buffer size, might not be enough) * new privileges (big enough, after old privileges memory area) */ State = RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(RTL_ACQUIRE_STATE) + sizeof(TOKEN_PRIVILEGES) + (NumPriv - ANYSIZE_ARRAY) * sizeof(LUID_AND_ATTRIBUTES)); if (!State) { return STATUS_NO_MEMORY; }
/* Only zero a bit of the memory (will be faster that way) */ State->Token = 0; State->OldImpersonationToken = 0; State->Flags = 0; State->OldPrivileges = NULL;
/* Check whether we have already an active impersonation */ if (NtCurrentTeb()->IsImpersonating) { /* Check whether we want to impersonate */ if (Flags & RTL_ACQUIRE_PRIVILEGE_IMPERSONATE) { /* That's all fine, just get the token. * We need access for: adjust (obvious...) but also * query, to be able to query old privileges */ Status = RtlpOpenThreadToken(TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &State->Token); if (!NT_SUCCESS(Status)) { RtlFreeHeap(RtlGetProcessHeap(), 0, State); return Status; } } else { /* Otherwise, we have to temporary disable active impersonation. * Get previous impersonation token to save it */ Status = RtlpOpenThreadToken(TOKEN_IMPERSONATE, &State->OldImpersonationToken); if (!NT_SUCCESS(Status)) { RtlFreeHeap(RtlGetProcessHeap(), 0, State); return Status; }
/* Remember the fact we had an active impersonation */ State->Flags |= RTL_ACQUIRE_PRIVILEGE_IMPERSONATE;
/* Revert impersonation (ie, give 0 as handle) */ Status = ZwSetInformationThread(NtCurrentThread(), ThreadImpersonationToken, &ImpersonationToken, sizeof(HANDLE)); } }
/* If we have no token yet (which is likely) */ if (!State->Token) { /* If we are asked to use process, then do */ if (Flags & RTL_ACQUIRE_PRIVILEGE_PROCESS) { Status = ZwOpenProcessToken(NtCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &State->Token); if (!NT_SUCCESS(Status)) { goto Cleanup; } } else { /* Otherwise, we have to impersonate. * Open token for duplication */ Status = ZwOpenProcessToken(NtCurrentProcess(), TOKEN_DUPLICATE, &ProcessToken);
InitializeObjectAttributes(&ObjectAttributes, NULL, 0, NULL, NULL);
ObjectAttributes.SecurityQualityOfService = &Sqos; Sqos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE); Sqos.ImpersonationLevel = SecurityDelegation; Sqos.ContextTrackingMode = 1; Sqos.EffectiveOnly = FALSE;
/* Duplicate */ Status = ZwDuplicateToken(ProcessToken, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY | TOKEN_IMPERSONATE, &ObjectAttributes, FALSE, TokenImpersonation, &ImpersonationToken); if (!NT_SUCCESS(Status)) { ZwClose(ProcessToken); goto Cleanup; }
/* Assign our duplicated token to current thread */ Status = ZwSetInformationThread(NtCurrentThread(), ThreadImpersonationToken, &ImpersonationToken, sizeof(HANDLE)); if (!NT_SUCCESS(Status)) { ZwClose(ImpersonationToken); ZwClose(ProcessToken); goto Cleanup; }
/* Save said token and the fact we have impersonated */ State->Token = ImpersonationToken; State->Flags |= RTL_ACQUIRE_PRIVILEGE_IMPERSONATE;
ZwClose(ProcessToken); } }
/* Properly set the privileges pointers: * OldPrivileges points to the static memory in struct (= OldPrivBuffer) * NewPrivileges points to the dynamic memory after OldPrivBuffer * There's NO overflow risks (OldPrivileges is always used with its size) */ State->OldPrivileges = (PTOKEN_PRIVILEGES)State->OldPrivBuffer; State->NewPrivileges = (PTOKEN_PRIVILEGES)(State->OldPrivBuffer + (sizeof(State->OldPrivBuffer) / sizeof(State->OldPrivBuffer[0])));
/* Assign all the privileges to be acquired */ State->NewPrivileges->PrivilegeCount = NumPriv; for (i = 0; i < NumPriv; ++i) { State->NewPrivileges->Privileges[i].Luid.LowPart = Privilege[i]; State->NewPrivileges->Privileges[i].Luid.HighPart = 0; State->NewPrivileges->Privileges[i].Attributes = SE_PRIVILEGE_ENABLED; }
/* Start privileges adjustements */ OldSize = sizeof(State->OldPrivBuffer); do { ReturnLength = sizeof(State->OldPrivBuffer); Status = ZwAdjustPrivilegesToken(State->Token, FALSE, State->NewPrivileges, OldSize, State->OldPrivileges, &ReturnLength); /* This is returned when OldPrivileges buffer is too small */ if (Status == STATUS_BUFFER_TOO_SMALL) { /* Try to allocate a new one, big enough to hold data */ State->OldPrivileges = RtlAllocateHeap(RtlGetProcessHeap(), 0, ReturnLength); if (State->OldPrivileges) { DPRINT("Allocated old privileges: %p\n", State->OldPrivileges); OldSize = ReturnLength; continue; } else { /* If we failed, properly set status: we failed because of the lack of memory */ Status = STATUS_NO_MEMORY; } }
/* If we failed to assign at least one privilege */ if (Status == STATUS_NOT_ALL_ASSIGNED) { /* If there was actually only one privilege to acquire, use more accurate status */ if (NumPriv == 1) { Status = STATUS_PRIVILEGE_NOT_HELD; } }
/* Fail if needed, otherwise return our state to caller */ if (!NT_SUCCESS(Status)) { goto Cleanup; } else { *ReturnedState = State; break; } } while (TRUE);
DPRINT("RtlAcquirePrivilege succeed!\n");
return Status;
Cleanup: /* If we allocated our own buffer for old privileges, release it */ if (State->OldPrivileges && (PVOID)State->OldPrivBuffer != (PVOID)State->OldPrivileges) { RtlFreeHeap(RtlGetProcessHeap(), 0, State->OldPrivileges); }
/* Do we have to restore previously active impersonation? */ if (State->Flags & RTL_ACQUIRE_PRIVILEGE_IMPERSONATE) { IntStatus = ZwSetInformationThread(NtCurrentThread(), ThreadImpersonationToken, &State->OldImpersonationToken, sizeof(HANDLE)); /* If this ever happens, we're in a really bad situation... */ if (!NT_SUCCESS(IntStatus)) { RtlRaiseStatus(IntStatus); } }
/* Release token */ if (State->Token) { ZwClose(State->Token); }
/* And free our state buffer */ RtlFreeHeap(RtlGetProcessHeap(), 0, State);
DPRINT("RtlAcquirePrivilege() failed with status: %lx\n", Status);
return Status;}
/* * @implemented */VOIDNTAPIRtlReleasePrivilege(IN PVOID ReturnedState){ NTSTATUS Status; PRTL_ACQUIRE_STATE State = (PRTL_ACQUIRE_STATE)ReturnedState;
DPRINT("RtlReleasePrivilege(%p)\n", ReturnedState);
/* If we had an active impersonation before we acquired privileges * Or if we have impersonated, quit it */ if (State->Flags & RTL_ACQUIRE_PRIVILEGE_IMPERSONATE) { /* Restore it for the current thread */ Status = ZwSetInformationThread(NtCurrentThread(), ThreadImpersonationToken, &State->OldImpersonationToken, sizeof(HANDLE)); if (!NT_SUCCESS(Status)) { RtlRaiseStatus(Status); }
/* And close the token if needed */ if (State->OldImpersonationToken) ZwClose(State->OldImpersonationToken); } else { /* Otherwise, restore old state */ Status = ZwAdjustPrivilegesToken(State->Token, FALSE, State->OldPrivileges, 0, NULL, NULL); if (!NT_SUCCESS(Status)) { RtlRaiseStatus(Status); } }
/* If we used a different buffer for old privileges, just free it */ if ((PVOID)State->OldPrivBuffer != (PVOID)State->OldPrivileges) { DPRINT("Releasing old privileges: %p\n", State->OldPrivileges); RtlFreeHeap(RtlGetProcessHeap(), 0, State->OldPrivileges); }
/* Release token and free state */ ZwClose(State->Token); RtlFreeHeap(RtlGetProcessHeap(), 0, State);}
/* * @implemented */NTSTATUSNTAPIRtlAdjustPrivilege(IN ULONG Privilege, IN BOOLEAN Enable, IN BOOLEAN CurrentThread, OUT PBOOLEAN Enabled){ TOKEN_PRIVILEGES NewState; TOKEN_PRIVILEGES OldState; ULONG ReturnLength; HANDLE TokenHandle; NTSTATUS Status;
PAGED_CODE_RTL();
DPRINT("RtlAdjustPrivilege() called\n");
if (CurrentThread) { Status = ZwOpenThreadToken(NtCurrentThread(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, FALSE, &TokenHandle); } else { Status = ZwOpenProcessToken(NtCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &TokenHandle); }
if (!NT_SUCCESS (Status)) { DPRINT1("Retrieving token handle failed (Status %lx)\n", Status); return Status; }
OldState.PrivilegeCount = 1;
NewState.PrivilegeCount = 1; NewState.Privileges[0].Luid.LowPart = Privilege; NewState.Privileges[0].Luid.HighPart = 0; NewState.Privileges[0].Attributes = (Enable) ? SE_PRIVILEGE_ENABLED : 0;
Status = ZwAdjustPrivilegesToken(TokenHandle, FALSE, &NewState, sizeof(TOKEN_PRIVILEGES), &OldState, &ReturnLength); ZwClose (TokenHandle); if (Status == STATUS_NOT_ALL_ASSIGNED) { DPRINT1("Failed to assign all privileges\n"); return STATUS_PRIVILEGE_NOT_HELD; }
if (!NT_SUCCESS(Status)) { DPRINT1("NtAdjustPrivilegesToken() failed (Status %lx)\n", Status); return Status; }
if (OldState.PrivilegeCount == 0) { *Enabled = Enable; } else { *Enabled = (OldState.Privileges[0].Attributes & SE_PRIVILEGE_ENABLED); }
DPRINT("RtlAdjustPrivilege() done\n");
return STATUS_SUCCESS;}
#if (NTDDI_VERSION >= NTDDI_VISTA) || (DLL_EXPORT_VERSION >= _WIN32_WINNT_VISTA)
/** * @brief * Removes all privileges in the specified access token. * * @param[in] TokenHandle * A handle to the access token that contains the privileges to be removed. * * @param[in] PrivilegesToKeep * A pointer to an array of privilege values (defined as SE_XXX_PRIVILEGE) that specify * the privileges to keep in the token. * * @param[in] PrivilegeCount * Specifies the number of entries in the PrivilegesToKeep array. * * @return * Returns STATUS_SUCCESS if privileges removed successfully. * STATUS_INVALID_PARAMETER is returned if input privilege value greater than * SE_MAX_WELL_KNOWN_PRIVILEGE. STATUS_NOT_ALL_ASSIGNED is returned if The token does * not have one or more of the privileges specified in the PrivilegesToKeep parameter, * and no privileges were removed. A failure NTSTATUS code is returned otherwise. */NTSTATUSNTAPIRtlRemovePrivileges( _In_ HANDLE TokenHandle, _In_reads_opt_(PrivilegeCount) _When_(PrivilegeCount != 0, _Notnull_) PULONG PrivilegesToKeep, _In_ ULONG PrivilegeCount){ NTSTATUS Status; UINT64 PrivilegesToKeepBitmap; ULONG i, ReturnLength; UCHAR Buffer[sizeof(TOKEN_PRIVILEGES) + sizeof(LUID_AND_ATTRIBUTES) * (SE_MAX_WELL_KNOWN_PRIVILEGE - SE_MIN_WELL_KNOWN_PRIVILEGE)]; PTOKEN_PRIVILEGES Privileges;
C_ASSERT(SE_MAX_WELL_KNOWN_PRIVILEGE < 64);
DPRINT("RtlRemovePrivileges(%p, %p, %u)\n", TokenHandle, PrivilegesToKeep, PrivilegeCount);
/* Save privileges that should be keep */ PrivilegesToKeepBitmap = 0; if (PrivilegeCount) { for (i = 0; i < PrivilegeCount; i++) { if (PrivilegesToKeep[i] > SE_MAX_WELL_KNOWN_PRIVILEGE) { return STATUS_INVALID_PARAMETER; } PrivilegesToKeepBitmap |= (1ULL << PrivilegesToKeep[i]); } }
/* Get token privileges information */ Status = ZwQueryInformationToken(TokenHandle, TokenPrivileges, Buffer, sizeof(Buffer), &ReturnLength); if (!NT_SUCCESS(Status)) { return Status; }
/* Remove all privileges that we don't need to keep */ Privileges = (PTOKEN_PRIVILEGES)Buffer; for (i = 0; i < Privileges->PrivilegeCount; i++) { LARGE_INTEGER Privilege = *(LARGE_INTEGER*)&Privileges->Privileges[i].Luid; ASSERT(Privilege.QuadPart <= SE_MAX_WELL_KNOWN_PRIVILEGE); if (PrivilegesToKeepBitmap & (1ULL << Privilege.QuadPart)) { PrivilegesToKeepBitmap &= ~(1ULL << Privilege.QuadPart); } else { Privileges->Privileges[i].Attributes = SE_PRIVILEGE_REMOVED; } }
if (PrivilegesToKeepBitmap) { Status = STATUS_NOT_ALL_ASSIGNED; } else { Status = ZwAdjustPrivilegesToken(TokenHandle, FALSE, (PTOKEN_PRIVILEGES)Buffer, sizeof(Buffer), NULL, NULL); }
return Status;}
#endif /* (NTDDI_VERSION >= NTDDI_VISTA) */