Implement RtlAcquirePrivilege and RtlReleasePrivilege.
Reviewed by Aleksey

svn path=/trunk/; revision=58937
This commit is contained in:
Pierre Schweitzer 2013-05-05 09:30:12 +00:00
parent 6743a1174e
commit d4c38c2fe4

View file

@ -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);
}
/*