[PATCH 2/2] ntdll/tests: Add test for RtlDefaultNpAcl
Patrick Hibbs
hibbsncc1701 at gmail.com
Thu Jun 24 15:06:45 CDT 2021
Signed-off-by: Patrick Hibbs <hibbsncc1701 at gmail.com>
---
dlls/ntdll/tests/Makefile.in | 1 +
dlls/ntdll/tests/sec.c | 321 +++++++++++++++++++++++++++++++++++
2 files changed, 322 insertions(+)
create mode 100644 dlls/ntdll/tests/sec.c
diff --git a/dlls/ntdll/tests/Makefile.in b/dlls/ntdll/tests/Makefile.in
index ed15c51339f..1668441e41e 100644
--- a/dlls/ntdll/tests/Makefile.in
+++ b/dlls/ntdll/tests/Makefile.in
@@ -20,6 +20,7 @@ C_SRCS = \
rtl.c \
rtlbitmap.c \
rtlstr.c \
+ sec.c \
string.c \
threadpool.c \
time.c \
diff --git a/dlls/ntdll/tests/sec.c b/dlls/ntdll/tests/sec.c
new file mode 100644
index 00000000000..c804c78365f
--- /dev/null
+++ b/dlls/ntdll/tests/sec.c
@@ -0,0 +1,321 @@
+/*
+ * Unit test suite for ntdll sec functions
+ *
+ * Copyright 2021 Patrick Hibbs
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include "ntdll_test.h"
+
+static NTSTATUS (WINAPI *pRtlDefaultNpAcl)( PACL *pAcl );
+static BOOL (WINAPI *pIsValidAcl)( PACL pAcl );
+static BOOL (WINAPI *pGetAce)( PACL pAcl, DWORD index, LPVOID * pAce);
+static BOOL (WINAPI *pEqualSid)( PSID pSid1, PSID pSid2 );
+static BOOL (WINAPI *pCopySid)( DWORD destLength, PSID destSid, PSID srcSid );
+static BOOL (WINAPI *pIsValidSid)( PSID sid );
+static BOOL (WINAPI *pConvertSidToStringSid)( PSID sid, LPSTR * out);
+
+/* Copied from dlls/kernelbase/security.c */
+typedef struct _MAX_SID
+{
+ /* same fields as struct _SID */
+ BYTE Revision;
+ BYTE SubAuthorityCount;
+ SID_IDENTIFIER_AUTHORITY IdentifierAuthority;
+ DWORD SubAuthority[SID_MAX_SUB_AUTHORITIES];
+} MAX_SID;
+
+typedef struct sid_access_t {
+ MAX_SID sid;
+ ACCESS_MASK access;
+} sid_access;
+
+/*
+ ACEs_RtlDefaultNpAcl
+
+ Defines the sids and rights we expect to be returned from RtlDefaultNpAcl.
+
+ Note: That there should be one more sid corrosponding to the process / thread
+ token's owner with Full Access rights in addition to the rights below.
+ */
+static sid_access ACEs_RtlDefaultNpAcl[] = {
+ // Local System: Full Access
+ { { SID_REVISION, 1, { SECURITY_NT_AUTHORITY }, { SECURITY_LOCAL_SYSTEM_RID } }, GENERIC_ALL },
+ // Administrators: Full Access
+ { { SID_REVISION, 2, { SECURITY_NT_AUTHORITY }, { SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS } }, GENERIC_ALL},
+ // World: Read Only
+ { { SID_REVISION, 1, { SECURITY_WORLD_SID_AUTHORITY }, { SECURITY_WORLD_RID } } , GENERIC_READ},
+ // Anonymous: Read Only
+ { { SID_REVISION, 1, { SECURITY_NT_AUTHORITY }, { SECURITY_ANONYMOUS_LOGON_RID } }, GENERIC_READ }
+};
+
+static NTSTATUS get_expected_owner_token_sid(PSID * sid)
+{
+ NTSTATUS ret;
+ PSID new_sid = NULL;
+ ULONG owner_sid_length = 0;
+ PTOKEN_OWNER owner = NULL;
+ HANDLE token = NULL;
+
+ if (sid)
+ {
+ *sid = NULL;
+
+ ret = NtOpenThreadToken(GetCurrentThread(),
+ TOKEN_QUERY,
+ TRUE,
+ &token);
+ if (ret == STATUS_NO_TOKEN)
+ {
+ ret = NtOpenProcessToken(GetCurrentProcess(),
+ TOKEN_QUERY,
+ &token);
+ if (!NT_SUCCESS(ret))
+ return ret;
+ }
+
+ /* Get the length of the owner's SID. */
+ ret = NtQueryInformationToken(token,
+ TokenOwner,
+ NULL,
+ 0,
+ &owner_sid_length);
+ if (ret == STATUS_BUFFER_TOO_SMALL)
+ {
+ owner = RtlAllocateHeap(GetProcessHeap(), 0, owner_sid_length);
+ if (owner)
+ {
+ /* Actually get the owner's SID. */
+ ret = NtQueryInformationToken(token,
+ TokenOwner,
+ owner,
+ owner_sid_length,
+ &owner_sid_length);
+ if (NT_SUCCESS(ret))
+ {
+ ok(pIsValidSid(owner->Owner), "Invalid SID from NtQueryInformationToken.\n");
+ new_sid = RtlAllocateHeap(GetProcessHeap(), 0, owner_sid_length);
+ if (new_sid)
+ {
+ pCopySid(owner_sid_length, new_sid, owner->Owner);
+ ok((ret = pIsValidSid(new_sid)), "CopySid failed to copy the SID from NtQueryInformationToken.\n");
+ if (ret)
+ {
+ *sid = new_sid;
+ ret = STATUS_SUCCESS;
+ }
+ else
+ {
+ ret = STATUS_NO_MEMORY;
+ }
+ if (ret != STATUS_SUCCESS)
+ RtlFreeHeap(GetProcessHeap(), 0, new_sid);
+ }
+ else
+ {
+ ret = STATUS_NO_MEMORY;
+ }
+ }
+
+ RtlFreeHeap(GetProcessHeap(), 0, owner);
+ }
+ }
+ }
+ else
+ ret = STATUS_INVALID_PARAMETER;
+
+ return ret;
+}
+
+static void test_RtlDefaultNpAcl(void)
+{
+ NTSTATUS ret;
+ PACL test = NULL;
+ BOOL valid_result = FALSE;
+ WORD x = 0;
+ size_t y = 0;
+ PACE_HEADER ace_head = NULL;
+ PSID check_sid = NULL;
+ ACCESS_MASK check_access;
+ PSTR sid_human = NULL;
+ PSID owner_sid = NULL;
+
+ /* Check NULL argument. */
+ /* ret = pRtlDefaultNpAcl(NULL); This generates a EXCEPTION_ACCESS_VIOLATION on Win7. */
+
+ /* Get the expected owner SID. */
+ get_expected_owner_token_sid(&owner_sid);
+
+ ok( (valid_result = (owner_sid != NULL)),
+ "Could not get owner SID for the thread / process. A portion of the tests will be skipped.\n");
+ if (valid_result)
+ {
+ pConvertSidToStringSid(owner_sid, &sid_human);
+ trace("owner sid: %s\n", sid_human);
+ LocalFree(sid_human);
+ }
+
+ /* Actually get the default Acl. */
+ ret = pRtlDefaultNpAcl(&test);
+ ok(((ret == STATUS_SUCCESS) && (test)), "RtlDefaultNpAcl failed. Invalid result for valid pointer argument. Got 0x%x.\n", ret);
+ ok((valid_result = pIsValidAcl(test)), "RtlDefaultNpAcl failed. IsValidAcl says the returned ACL is invalid.\n");
+ if (valid_result)
+ {
+ /* Total number of ACEs should be the length of the sid_access array + 1.
+ (Due to the owner acl check.) */
+ ok( (test->AceCount == ((sizeof(ACEs_RtlDefaultNpAcl) / sizeof(sid_access)) + 1)),
+ "RtlDefaultNpAcl changed. Was expecting %u ACEs, got %i.\n",
+ ((sizeof(ACEs_RtlDefaultNpAcl) / sizeof(sid_access)) + 1),
+ test->AceCount);
+
+ for (x = 0; x < test->AceCount; x++)
+ {
+ ace_head = NULL;
+ ok(((pGetAce(test, x, (LPVOID *)&ace_head)) && (ace_head)), "Could not get ACE at index %i.\n", x);
+ if (ace_head)
+ {
+ ok((ace_head->AceType == ACCESS_ALLOWED_ACE_TYPE),
+ "ACE at index %i: Was expecting an ACCESS_ALLOWED_ACE_TYPE, got %c instead.\n",
+ x,
+ ace_head->AceType);
+ if (ace_head->AceType == ACCESS_ALLOWED_ACE_TYPE)
+ {
+ check_sid = ((PSID *)&(((ACCESS_ALLOWED_ACE*)ace_head)->SidStart));
+ check_access = (((ACCESS_ALLOWED_ACE*)ace_head)->Mask);
+
+ valid_result = pIsValidSid(check_sid);
+ ok(valid_result, "ACE at index %i has an invalid SID.\n", x);
+ if (valid_result)
+ {
+ if (!pConvertSidToStringSid(check_sid, &sid_human))
+ sid_human = NULL;
+
+ valid_result = FALSE;
+ for (y = 0; (!valid_result) && (y < (sizeof(ACEs_RtlDefaultNpAcl) / sizeof(sid_access))); y++)
+ {
+ if (pEqualSid(&(ACEs_RtlDefaultNpAcl[y].sid), check_sid))
+ {
+ ok ( (valid_result = (check_access & ACEs_RtlDefaultNpAcl[y].access)),
+ "ACE at index %i for SID %s was wrong. Expected access mask 0x%x, got 0x%x.\n",
+ x,
+ ((sid_human != NULL) ? (sid_human) : ("[UNABLE TO CONVERT SID]")),
+ ACEs_RtlDefaultNpAcl[y].access,
+ check_access
+ );
+ }
+ else
+ {
+ if ((y + 1) >= ((sizeof(ACEs_RtlDefaultNpAcl) / sizeof(sid_access))))
+ {
+ /* Check owner SID. */
+ if (owner_sid != NULL)
+ {
+ valid_result = pEqualSid( owner_sid, check_sid );
+
+ ok( valid_result,
+ "ACE at index %i for an UNKNOWN SID %s with access mask 0x%x.\n",
+ x,
+ ((sid_human != NULL) ? (sid_human) : ("[UNABLE TO CONVERT SID]")),
+ check_access);
+
+ if (valid_result)
+ {
+ ok( ( valid_result = (check_access & GENERIC_ALL)),
+ "ACE at index %i for SID %s was wrong. Expected access mask 0x%x, got 0x%x.\n",
+ x,
+ ((sid_human != NULL) ? (sid_human) : ("[UNABLE TO CONVERT SID]")),
+ GENERIC_ALL,
+ check_access);
+ }
+ }
+ else
+ skip( "Test for ACE at index %i for SID %s with access mask 0x%x is being skipped due to unknown owner SID.\n",
+ x,
+ ((sid_human != NULL) ? (sid_human) : ("[UNABLE TO CONVERT SID]")),
+ check_access);
+ }
+ }
+ }
+
+ if (sid_human)
+ LocalFree(sid_human);
+ }
+ }
+ }
+ }
+ }
+
+ if (test)
+ RtlFreeHeap(GetProcessHeap(), 0, test);
+
+ if (owner_sid)
+ RtlFreeHeap(GetProcessHeap(), 0, owner_sid);
+}
+
+START_TEST(sec)
+{
+ HMODULE adv = GetModuleHandleA("advapi32.dll");
+ HMODULE mod = GetModuleHandleA("ntdll.dll");
+
+ pRtlDefaultNpAcl = (void *)GetProcAddress(mod, "RtlDefaultNpAcl");
+ pIsValidAcl = (void *)GetProcAddress(adv, "IsValidAcl");
+ pGetAce = (void *)GetProcAddress(adv, "GetAce");
+ pEqualSid = (void *)GetProcAddress(adv, "EqualSid");
+ pCopySid = (void *)GetProcAddress(mod, "RtlCopySid");
+ pIsValidSid = (void *)GetProcAddress(adv, "IsValidSid");
+ pConvertSidToStringSid = (void *)GetProcAddress(adv, "ConvertSidToStringSid");
+ if (!pConvertSidToStringSid)
+ {
+ /* Win7 seems to not define the generic name. */
+ #ifdef UNICODE
+ pConvertSidToStringSid = (void *)GetProcAddress(adv, "ConvertSidToStringSidW");
+ #else
+ pConvertSidToStringSid = (void *)GetProcAddress(adv, "ConvertSidToStringSidA");
+ #endif
+ }
+ if (pRtlDefaultNpAcl)
+ {
+ if (pIsValidAcl)
+ {
+ if (pGetAce)
+ {
+ if (pEqualSid)
+ {
+ if (pCopySid)
+ {
+ if (pConvertSidToStringSid)
+ if (pIsValidSid)
+ test_RtlDefaultNpAcl();
+ else
+ win_skip("IsValidSid function is not available.\n");
+ else
+ win_skip("ConvertSidToStringSid function is not available.\n");
+ }
+ else
+ win_skip("CopySid function is not available.\n");
+ }
+ else
+ win_skip("EqualSid function is not available.\n");
+ }
+ else
+ win_skip("GetAce function is not available.\n");
+ }
+ else
+ win_skip("IsValidAcl function is not available.\n");
+ }
+ else
+ win_skip("RtlDefaultNpAcl function is not available.\n");
+}
--
2.32.0
More information about the wine-devel
mailing list