mirror of
https://github.com/reactos/reactos.git
synced 2024-10-08 02:13:56 +00:00
[RTL]
Implement RtlAcquirePrivilege and RtlReleasePrivilege. Reviewed by Aleksey svn path=/trunk/; revision=58937
This commit is contained in:
parent
6743a1174e
commit
d4c38c2fe4
|
@ -4,6 +4,7 @@
|
|||
* FILE: lib/rtl/priv.c
|
||||
* PURPOSE: Security related functions and Security Objects
|
||||
* PROGRAMMER: Eric Kohl
|
||||
* Pierre Schweitzer (pierre@reactos.org)
|
||||
*/
|
||||
|
||||
/* INCLUDES *****************************************************************/
|
||||
|
@ -15,6 +16,27 @@
|
|||
|
||||
/* FUNCTIONS ***************************************************************/
|
||||
|
||||
/*
|
||||
* @implemented
|
||||
*/
|
||||
NTSTATUS
|
||||
NTAPI
|
||||
RtlpOpenThreadToken(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
|
||||
*/
|
||||
|
@ -81,7 +103,7 @@ RtlImpersonateSelf(IN SECURITY_IMPERSONATION_LEVEL ImpersonationLevel)
|
|||
}
|
||||
|
||||
/*
|
||||
* @unimplemented
|
||||
* @implemented
|
||||
*/
|
||||
NTSTATUS
|
||||
NTAPI
|
||||
|
@ -90,18 +112,282 @@ RtlAcquirePrivilege(IN PULONG Privilege,
|
|||
IN ULONG Flags,
|
||||
OUT PVOID *ReturnedState)
|
||||
{
|
||||
UNIMPLEMENTED;
|
||||
return STATUS_NOT_IMPLEMENTED;
|
||||
NTSTATUS Status;
|
||||
PRTL_ACQUIRE_STATE State;
|
||||
ULONG ReturnLength, i, OldSize;
|
||||
SECURITY_QUALITY_OF_SERVICE Sqos;
|
||||
OBJECT_ATTRIBUTES ObjectAttributes;
|
||||
HANDLE ImpersonationToken = 0, ProcessToken;
|
||||
|
||||
/* 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 * 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 */
|
||||
State->Token = ImpersonationToken;
|
||||
|
||||
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 + 1024);
|
||||
|
||||
/* 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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
} while (FALSE);
|
||||
|
||||
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)
|
||||
{
|
||||
Status = ZwSetInformationThread(NtCurrentThread(), ThreadImpersonationToken,
|
||||
&State->OldImpersonationToken, sizeof(HANDLE));
|
||||
/* If this ever happens, we're in a really bad situation... */
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
RtlRaiseStatus(Status);
|
||||
}
|
||||
}
|
||||
|
||||
/* Release token */
|
||||
if (State->Token)
|
||||
{
|
||||
ZwClose(State->Token);
|
||||
}
|
||||
|
||||
/* And free our state buffer */
|
||||
RtlFreeHeap(RtlGetProcessHeap(), 0, State);
|
||||
|
||||
return Status;
|
||||
}
|
||||
|
||||
/*
|
||||
* @unimplemented
|
||||
* @implemented
|
||||
*/
|
||||
VOID
|
||||
NTAPI
|
||||
RtlReleasePrivilege(IN PVOID ReturnedState)
|
||||
{
|
||||
UNIMPLEMENTED;
|
||||
NTSTATUS Status;
|
||||
PRTL_ACQUIRE_STATE State = (PRTL_ACQUIRE_STATE)ReturnedState;
|
||||
|
||||
/* If we had an active impersonation before we acquired privileges */
|
||||
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 */
|
||||
ZwAdjustPrivilegesToken(State->Token, FALSE,
|
||||
State->OldPrivileges, 0, NULL, NULL);
|
||||
|
||||
}
|
||||
|
||||
/* If we used a different buffer for old privileges, just free it */
|
||||
if ((PVOID)State->OldPrivBuffer != (PVOID)State->OldPrivileges)
|
||||
{
|
||||
RtlFreeHeap(RtlGetProcessHeap(), 0, State->OldPrivileges);
|
||||
}
|
||||
|
||||
/* Release token and free state */
|
||||
ZwClose(State->Token);
|
||||
RtlFreeHeap(RtlGetProcessHeap(), 0, State);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
Loading…
Reference in a new issue