https://git.reactos.org/?p=reactos.git;a=commitdiff;h=badd97043fbce3bbfe60aa0470749a0cf4678b34

commit badd97043fbce3bbfe60aa0470749a0cf4678b34
Author:     Ratin Gao <[email protected]>
AuthorDate: Wed Oct 5 20:31:39 2022 +0800
Commit:     GitHub <[email protected]>
CommitDate: Wed Oct 5 14:31:39 2022 +0200

    [RTL][NTDLL_APITEST] Implement RtlRemovePrivileges (#4614)
    
    Vista+ API, compile-time guarded.
    Add tests for it.
---
 dll/ntdll/def/ntdll.spec                           |   2 +-
 modules/rostests/apitests/ntdll/CMakeLists.txt     |   1 +
 .../rostests/apitests/ntdll/RtlRemovePrivileges.c  | 111 +++++++++++++++++++++
 modules/rostests/apitests/ntdll/testlist.c         |   2 +
 sdk/include/ndk/rtlfuncs.h                         |  14 +++
 sdk/lib/rtl/priv.c                                 | 102 +++++++++++++++++++
 6 files changed, 231 insertions(+), 1 deletion(-)

diff --git a/dll/ntdll/def/ntdll.spec b/dll/ntdll/def/ntdll.spec
index 80d27637413..3cd201aa3d4 100644
--- a/dll/ntdll/def/ntdll.spec
+++ b/dll/ntdll/def/ntdll.spec
@@ -1111,7 +1111,7 @@
 @ stdcall -stub -version=0x600+ RtlReleaseSRWLockExclusive(ptr)
 @ stdcall -stub -version=0x600+ RtlReleaseSRWLockShared(ptr)
 @ stdcall RtlRemoteCall(ptr ptr ptr long ptr long long)
-@ stub -version=0x600+ RtlRemovePrivileges
+@ stdcall -version=0x600+ RtlRemovePrivileges(ptr ptr long)
 @ stdcall RtlRemoveVectoredContinueHandler(ptr)
 @ stdcall RtlRemoveVectoredExceptionHandler(ptr)
 @ stub -version=0x600+ RtlReportException
diff --git a/modules/rostests/apitests/ntdll/CMakeLists.txt 
b/modules/rostests/apitests/ntdll/CMakeLists.txt
index e1076a3a584..c2ec474a5a0 100644
--- a/modules/rostests/apitests/ntdll/CMakeLists.txt
+++ b/modules/rostests/apitests/ntdll/CMakeLists.txt
@@ -85,6 +85,7 @@ list(APPEND SOURCE
     RtlpEnsureBufferSize.c
     RtlQueryTimeZoneInfo.c
     RtlReAllocateHeap.c
+    RtlRemovePrivileges.c
     RtlUnicodeStringToAnsiString.c
     RtlUnicodeStringToCountedOemString.c
     RtlUnicodeToOemN.c
diff --git a/modules/rostests/apitests/ntdll/RtlRemovePrivileges.c 
b/modules/rostests/apitests/ntdll/RtlRemovePrivileges.c
new file mode 100644
index 00000000000..861dba1a8a8
--- /dev/null
+++ b/modules/rostests/apitests/ntdll/RtlRemovePrivileges.c
@@ -0,0 +1,111 @@
+/*
+ * PROJECT:         ReactOS api tests
+ * LICENSE:         See COPYING in the top level directory
+ * PURPOSE:         Test for RtlRemovePrivileges
+ * PROGRAMMER:      Ratin Gao <[email protected]>
+ */
+
+#include "precomp.h"
+
+START_TEST(RtlRemovePrivileges)
+{
+#if (NTDDI_VERSION >= NTDDI_VISTA)
+    NTSTATUS Status;
+    HANDLE TokenHandle, TestTokenHandle;
+    ULONG ReturnLength;
+    UCHAR Buffer
+        [sizeof(TOKEN_PRIVILEGES) +
+         sizeof(LUID_AND_ATTRIBUTES) * (SE_MAX_WELL_KNOWN_PRIVILEGE - 
SE_MIN_WELL_KNOWN_PRIVILEGE)];
+    PTOKEN_PRIVILEGES Privileges;
+    ULONG PrivilegesToKeep[2];
+
+    /* Duplicate current process token to run this test */
+    Status = NtOpenProcessToken(GetCurrentProcess(), TOKEN_DUPLICATE, 
&TokenHandle);
+    if (!NT_SUCCESS(Status))
+    {
+        ok(0, "Failed to open current process token with TOKEN_DUPLICATE 
access (Status code %lx)!\n", Status);
+        return;
+    }
+
+    Status = NtDuplicateToken(TokenHandle, TOKEN_ALL_ACCESS, NULL, FALSE, 
TokenPrimary, &TestTokenHandle);
+    NtClose(TokenHandle);
+    if (!NT_SUCCESS(Status))
+    {
+        ok(0, "Failed to duplicate current process token (Status code 
%lx)!\n", Status);
+        return;
+    }
+
+    /* Retrieve token privileges, we need at least 3 privileges to run 
following tests */
+    Status = NtQueryInformationToken(TestTokenHandle, TokenPrivileges, Buffer, 
sizeof(Buffer), &ReturnLength);
+    if (!NT_SUCCESS(Status))
+    {
+        NtClose(TestTokenHandle);
+        ok(0, "Failed to retrieve token privileges (Status code %lx)!\n", 
Status);
+        return;
+    }
+    Privileges = (PTOKEN_PRIVILEGES)Buffer;
+    if (Privileges->PrivilegeCount < 3)
+    {
+        NtClose(TestTokenHandle);
+        ok(0, "No enough privileges to run the test (Number of privilege: 
%lu)!\n", Privileges->PrivilegeCount);
+        return;
+    }
+
+    /* Remove all privileges except 2nd and 3rd privileges, this should 
succeed */
+    PrivilegesToKeep[0] = Privileges->Privileges[1].Luid.LowPart;
+    PrivilegesToKeep[1] = Privileges->Privileges[2].Luid.LowPart;
+    Status = RtlRemovePrivileges(TestTokenHandle, PrivilegesToKeep, 
ARRAYSIZE(PrivilegesToKeep));
+
+    /* Do not use NT_SUCCESS, RtlRemovePrivileges may returns 
STATUS_NOT_ALL_ASSIGNED */
+    if (Status != STATUS_SUCCESS)
+    {
+        NtClose(TestTokenHandle);
+        ok_ntstatus(Status, STATUS_SUCCESS);
+        return;
+    }
+
+    /* Now, only two privileges we kept should be present */
+    Status = NtQueryInformationToken(TestTokenHandle, TokenPrivileges, Buffer, 
sizeof(Buffer), &ReturnLength);
+    if (!NT_SUCCESS(Status))
+    {
+        NtClose(TestTokenHandle);
+        ok(0, "Failed to retrieve token privileges (Status code %lx)!\n", 
Status);
+        return;
+    }
+    ok(Privileges->PrivilegeCount == ARRAYSIZE(PrivilegesToKeep),
+       "Number of privileges after RtlRemovePrivileges is %lu, expected %u\n", 
Privileges->PrivilegeCount,
+       ARRAYSIZE(PrivilegesToKeep));
+    ok(PrivilegesToKeep[0] + PrivilegesToKeep[1] ==
+           Privileges->Privileges[0].Luid.LowPart + 
Privileges->Privileges[1].Luid.LowPart,
+       "Incorrect privileges kept by RtlRemovePrivileges: %lu and %lu, 
expected %lu and %lu",
+       Privileges->Privileges[0].Luid.LowPart, 
Privileges->Privileges[1].Luid.LowPart, PrivilegesToKeep[0],
+       PrivilegesToKeep[1]);
+
+    /* Remove all privileges, this should succeed */
+    Status = RtlRemovePrivileges(TestTokenHandle, NULL, 0);
+
+    /* Do not use NT_SUCCESS, RtlRemovePrivileges may returns 
STATUS_NOT_ALL_ASSIGNED */
+    if (Status != STATUS_SUCCESS)
+    {
+        NtClose(TestTokenHandle);
+        ok_ntstatus(Status, STATUS_SUCCESS);
+        return;
+    }
+
+    /* Now, no privilege should be present */
+    Status = NtQueryInformationToken(TestTokenHandle, TokenPrivileges, Buffer, 
sizeof(Buffer), &ReturnLength);
+    if (!NT_SUCCESS(Status))
+    {
+        NtClose(TestTokenHandle);
+        ok(0, "Failed to retrieve token privileges (Status code %lx)!\n", 
Status);
+        return;
+    }
+    ok(Privileges->PrivilegeCount == 0, "There are %lu privileges still exist 
after RtlRemovePrivileges\n",
+       Privileges->PrivilegeCount);
+
+    NtClose(TestTokenHandle);
+    return;
+#else
+    skip("RtlRemovePrivileges available on NT6.0+ (NTDDI_VERSION >= 
NTDDI_VISTA)");
+#endif /* (NTDDI_VERSION >= NTDDI_VISTA) */
+}
diff --git a/modules/rostests/apitests/ntdll/testlist.c 
b/modules/rostests/apitests/ntdll/testlist.c
index f5b265f0305..29b3ae4ba21 100644
--- a/modules/rostests/apitests/ntdll/testlist.c
+++ b/modules/rostests/apitests/ntdll/testlist.c
@@ -81,6 +81,7 @@ extern void func_RtlpApplyLengthFunction(void);
 extern void func_RtlpEnsureBufferSize(void);
 extern void func_RtlQueryTimeZoneInformation(void);
 extern void func_RtlReAllocateHeap(void);
+extern void func_RtlRemovePrivileges(void);
 extern void func_RtlUnicodeStringToAnsiString(void);
 extern void func_RtlUnicodeStringToCountedOemString(void);
 extern void func_RtlUnicodeToOemN(void);
@@ -172,6 +173,7 @@ const struct test winetest_testlist[] =
     { "RtlpEnsureBufferSize",           func_RtlpEnsureBufferSize },
     { "RtlQueryTimeZoneInformation",    func_RtlQueryTimeZoneInformation },
     { "RtlReAllocateHeap",              func_RtlReAllocateHeap },
+    { "RtlRemovePrivileges",            func_RtlRemovePrivileges },
     { "RtlUnicodeStringToAnsiSize",     func_RtlxUnicodeStringToAnsiSize }, /* 
For some reason, starting test name with Rtlx hides it */
     { "RtlUnicodeStringToAnsiString",   func_RtlUnicodeStringToAnsiString },
     { "RtlUnicodeStringToCountedOemString", 
func_RtlUnicodeStringToCountedOemString },
diff --git a/sdk/include/ndk/rtlfuncs.h b/sdk/include/ndk/rtlfuncs.h
index c6bdcfcdd19..05dc37686ba 100644
--- a/sdk/include/ndk/rtlfuncs.h
+++ b/sdk/include/ndk/rtlfuncs.h
@@ -1568,6 +1568,20 @@ RtlReleasePrivilege(
     _In_ PVOID ReturnedState
 );
 
+#if (NTDDI_VERSION >= NTDDI_VISTA)
+
+NTSYSAPI
+NTSTATUS
+NTAPI
+RtlRemovePrivileges(
+    _In_ HANDLE TokenHandle,
+    _In_reads_opt_(PrivilegeCount) _When_(PrivilegeCount != 0, _Notnull_)
+         PULONG PrivilegesToKeep,
+    _In_ ULONG PrivilegeCount
+);
+
+#endif /* (NTDDI_VERSION >= NTDDI_VISTA) */
+
 _IRQL_requires_max_(APC_LEVEL)
 NTSYSAPI
 NTSTATUS
diff --git a/sdk/lib/rtl/priv.c b/sdk/lib/rtl/priv.c
index 164bd80f75f..0070140103f 100644
--- a/sdk/lib/rtl/priv.c
+++ b/sdk/lib/rtl/priv.c
@@ -486,3 +486,105 @@ RtlAdjustPrivilege(IN ULONG Privilege,
 
     return STATUS_SUCCESS;
 }
+
+#if (NTDDI_VERSION >= NTDDI_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.
+ */
+NTSTATUS
+NTAPI
+RtlRemovePrivileges(
+    _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) */

Reply via email to