Author: pschweitzer
Date: Sun May 5 09:30:12 2013
New Revision: 58937
URL:
http://svn.reactos.org/svn/reactos?rev=58937&view=rev
Log:
[RTL]
Implement RtlAcquirePrivilege and RtlReleasePrivilege.
Reviewed by Aleksey
Modified:
trunk/reactos/lib/rtl/priv.c
Modified: trunk/reactos/lib/rtl/priv.c
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/lib/rtl/priv.c?rev=58937&a…
==============================================================================
--- trunk/reactos/lib/rtl/priv.c [iso-8859-1] (original)
+++ trunk/reactos/lib/rtl/priv.c [iso-8859-1] Sun May 5 09:30:12 2013
@@ -4,6 +4,7 @@
* FILE: lib/rtl/priv.c
* PURPOSE: Security related functions and Security Objects
* PROGRAMMER: Eric Kohl
+ * Pierre Schweitzer (pierre(a)reactos.org)
*/
/* INCLUDES *****************************************************************/
@@ -14,6 +15,27 @@
#include <debug.h>
/* 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 @@
}
/*
- * @unimplemented
+ * @implemented
*/
NTSTATUS
NTAPI
@@ -90,18 +112,282 @@
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);
}
/*