libcli/security: SDDL parse tests to run on Windows
authorDouglas Bagnall <douglas.bagnall@catalyst.net.nz>
Wed, 22 Mar 2023 02:49:26 +0000 (15:49 +1300)
committerAndrew Bartlett <abartlet@samba.org>
Fri, 28 Apr 2023 02:15:36 +0000 (02:15 +0000)
The C version tests the public SDDL API on Windows which seems to follow
Active Directory closely, though case in hex numbers is reversed vis-a-vis
defaultSecurityDescriptor.

The python version is less refined and tests powershell functions.

Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
libcli/security/tests/windows-sddl-test.py [new file with mode: 0644]
libcli/security/tests/windows/windows-sddl-tests.c [new file with mode: 0644]

diff --git a/libcli/security/tests/windows-sddl-test.py b/libcli/security/tests/windows-sddl-test.py
new file mode 100644 (file)
index 0000000..9c7d6c0
--- /dev/null
@@ -0,0 +1,258 @@
+# Test SDDL strings on Windows
+#
+# Copyright (c) 2023 Catalyst IT
+#
+# GPLv3+.
+#
+# This uses the Python ctypes module to access the sddl.h functions.
+
+from ctypes import WINFUNCTYPE
+from ctypes import create_string_buffer, byref, windll, c_void_p, pointer
+from ctypes.wintypes import LPCSTR, PULONG, LPVOID, DWORD, BOOL, ULONG
+
+f = windll.advapi32.ConvertStringSecurityDescriptorToSecurityDescriptorA
+#f.restype = BOOL
+f.argtypes = (LPCSTR, DWORD, LPVOID, PULONG)
+
+err = windll.kernel32.GetLastError
+set_err = windll.kernel32.SetLastError
+
+def check_sddl(sddl):
+    out_size = PULONG()
+    out_bytes = LPVOID()
+    _sddl = sddl.encode('utf8')
+    #print(_sddl, DWORD(1), out_bytes, out_size)
+    ok = f(_sddl, 1, out_bytes, out_size)
+    if not ok:
+        #breakpoint()
+        e = err()
+        if e != 87:
+            print(e)
+        set_err(0)
+    return ok
+
+
+def check_sddl_powershell(sddl):
+    import subprocess
+    p = subprocess.run(['powershell.exe',
+                        #'-windowstyle', 'hidden',
+                        #'-executionpolicy', 'bypass',
+                        #'-noninteractive',
+                         'ConvertFrom-SddlString',
+                         '-Sddl',
+                         '"' + sddl.replace(';', '`;') + '"',
+                        '-type', 'ActiveDirectoryRights',
+                        ],
+                       capture_output=True
+                       )
+    print()
+    stderr = p.stderr.decode()
+    stdout = p.stdout.decode()
+    if 'Exception' in stderr:
+        print(sddl)
+        if 'security descriptor object is invalid'  not in stderr:
+            print(stdout)
+            print(stderr)
+            return None # probably ok
+
+        return False
+
+    #print(stdout)
+    return True
+
+
+def main():
+    cases = [
+        "awoivhewo42u",
+        "D:(A;OICI;GA;;;WD)",
+        "",
+        "O:BAG:BAD:",
+        "O:BAG:DAD:",
+        "O:BAG:baD:",
+        "O:baG:BAD:",
+        "O:BAG:BUS:",
+        "O:BAD:BAG:",
+        "O:BAG:MUD:",
+        "G:BAO:BUS:",
+        "O:BAG:BUS:",
+        "D:(A;;CC;;;BA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)",
+
+        "D:(A;;GA;;;SY)",
+
+        "D:(A;;GA;;;RS)",
+
+        "D:(A;;RP;;;WD)",
+        "D:(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)",
+        "D:(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)",
+        "D:(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)",
+        "D:(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)",
+        "D:(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)",
+        "D:(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)",
+        "D:(A;;RPLCLORC;;;AU)",
+        "D:(A;;RPWPCRLCLOCCRCWDWOSW;;;DA)",
+        "D:(A;CI;RPWPCRLCLOCCRCWDWOSDSW;;;BA)",
+        "D:(A;;RPWPCRLCLOCCDCRCWDWOSDDTSW;;;SY)",
+        "D:(A;CI;RPWPCRLCLOCCDCRCWDWOSDDTSW;;;EA)",
+        "D:(A;CI;LC;;;RU)",
+        "D:(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;bf967aba-0de6-11d0-a285-00aa003049e2;RU)",
+        "D:(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)",
+        "D:(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)",
+        "D:(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;bf967aba-0de6-11d0-a285-00aa003049e2;RU)",
+        "D:(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)",
+        "D:(OA;;RP;c7407360-20bf-11d0-a768-00aa006e0529;;RU)",
+        "D:(OA;CIIO;RPLCLORC;;bf967a9c-0de6-11d0-a285-00aa003049e2;RU)",
+        "D:(A;;RPRC;;;RU)",
+        "D:(OA;CIIO;RPLCLORC;;bf967aba-0de6-11d0-a285-00aa003049e2;RU)",
+        "D:(A;;LCRPLORC;;;ED)",
+        "D:(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;4828CC14-1437-45bc-9B07-AD6F015E5F28;RU)",
+        "D:(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;4828CC14-1437-45bc-9B07-AD6F015E5F28;RU)",
+        "D:(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;4828CC14-1437-45bc-9B07-AD6F015E5F28;RU)",
+        "D:(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;4828CC14-1437-45bc-9B07-AD6F015E5F28;RU)",
+        "D:(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;4828CC14-1437-45bc-9B07-AD6F015E5F28;RU)",
+        "D:(OA;CIIO;RPLCLORC;;4828CC14-1437-45bc-9B07-AD6F015E5F28;RU)",
+        "D:(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;RU)",
+        "D:(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;AU)",
+        "D:(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967aba-0de6-11d0-a285-00aa003049e2;ED)",
+        "S:(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a9c-0de6-11d0-a285-00aa003049e2;ED)",
+        "S:(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a86-0de6-11d0-a285-00aa003049e2;ED)",
+        "S:(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;DD)",
+        "S:(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)",
+        "S:(OA;;CR;e2a36dc9-ae17-47c3-b58b-be34c55ba633;;S-1-5-32-557)",
+        "S:(OA;;CR;280f369c-67c7-438e-ae98-1d46f3c6f541;;AU)",
+        "S:(OA;;CR;ccc2dc7d-a6ad-4a7a-8846-c04e3cc53501;;AU)",
+        "S:(OA;;CR;05c74c5e-4deb-43b4-bd9f-86664c2a7fd5;;AU)S:(AU;SA;WDWOWP;;;WD)",
+        "S:(AU;SA;CR;;;BA)",
+        "S:(AU;SA;CR;;;DU)",
+        "S:(OU;CISA;WP;f30e3bbe-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)",
+        "S:(OU;CISA;WP;f30e3bbf-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)",
+        "D:(A;;RPLCLORC;;;DA)",
+        "S:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)",
+        "S:(A;;RPLCLORC;;;AU)",
+
+        "D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)",
+        "S:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;AO)",
+        "S:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)",
+        "S:(A;;RPCRLCLORCSDDT;;;CO)",
+        "S:(OA;;WP;4c164200-20c0-11d0-a768-00aa006e0529;;CO)",
+        "S:(A;;RPLCLORC;;;AU)",
+        "S:(OA;;CR;ab721a53-1e2f-11d0-9819-00aa0040529b;;WD)",
+        "S:(A;;CCDC;;;PS)",
+        "S:(OA;;CCDC;bf967aa8-0de6-11d0-a285-00aa003049e2;;PO)",
+        "S:(OA;;RPWP;bf967a7f-0de6-11d0-a285-00aa003049e2;;CA)",
+        "S:(OA;;SW;f3a64788-5306-11d1-a9c5-0000f80367c1;;PS)",
+        "S:(OA;;RPWP;77B5B886-944A-11d1-AEBD-0000F80367C1;;PS)",
+        "S:(OA;;SW;72e39547-7b18-11d1-adef-00c04fd8d5cd;;PS)",
+        "S:(OA;;SW;72e39547-7b18-11d1-adef-00c04fd8d5cd;;CO)",
+        "D:(OA;;SW;f3a64788-5306-11d1-a9c5-0000f80367c1;;CO)",
+        "D:(OA;;WP;3e0abfd0-126a-11d0-a060-00aa006c33ed;bf967a86-0de6-11d0-a285-00aa003049e2;CO)",
+        "D:(OA;;WP;5f202010-79a5-11d0-9020-00c04fc2d4cf;bf967a86-0de6-11d0-a285-00aa003049e2;CO)",
+        "D:(OA;;WP;bf967950-0de6-11d0-a285-00aa003049e2;bf967a86-0de6-11d0-a285-00aa003049e2;CO)",
+        "D:(OA;;WP;bf967953-0de6-11d0-a285-00aa003049e2;bf967a86-0de6-11d0-a285-00aa003049e2;CO)",
+        "D:(OA;;RP;46a9b11d-60ae-405a-b7e8-ff8a58d456d2;;S-1-5-32-560)",
+
+        "D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)",
+        "D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)",
+        "D:(A;;RPLCLORC;;;AU)",
+        "D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)",
+        "D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)",
+        "D:(A;;RPLCLORC;;;AU)",
+        "D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;AO)",
+        "D:(A;;RPLCLORC;;;PS)",
+        "D:(OA;;CR;ab721a55-1e2f-11d0-9819-00aa0040529b;;AU)",
+        "D:(OA;;RP;46a9b11d-60ae-405a-b7e8-ff8a58d456d2;;S-1-5-32-560)",
+        "D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)",
+        "D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)",
+        "D:(A;;RPLCLORC;;;AU)",
+        "D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;CO)",
+
+        "D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)",
+        "D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)",
+        "D:(A;;RPLCLORC;;;AU)S:(AU;SA;CRWP;;;WD)",
+
+        "D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)",
+        "D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)",
+        "D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;AO)",
+        "D:(A;;RPLCLORC;;;PS)",
+        "D:(OA;;CR;ab721a53-1e2f-11d0-9819-00aa0040529b;;PS)",
+        "D:(OA;;CR;ab721a54-1e2f-11d0-9819-00aa0040529b;;PS)",
+        "D:(OA;;CR;ab721a56-1e2f-11d0-9819-00aa0040529b;;PS)",
+        "D:(OA;;RPWP;77B5B886-944A-11d1-AEBD-0000F80367C1;;PS)",
+        "D:(OA;;RPWP;E45795B2-9455-11d1-AEBD-0000F80367C1;;PS)",
+        "D:(OA;;RPWP;E45795B3-9455-11d1-AEBD-0000F80367C1;;PS)",
+        "D:(OA;;RP;037088f8-0ae1-11d2-b422-00a0c968f939;;RS)",
+        "D:(OA;;RP;4c164200-20c0-11d0-a768-00aa006e0529;;RS)",
+        "D:(OA;;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;;RS)",
+        "D:(A;;RC;;;AU)",
+        "D:(OA;;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;;AU)",
+        "D:(OA;;RP;77B5B886-944A-11d1-AEBD-0000F80367C1;;AU)",
+        "D:(OA;;RP;E45795B3-9455-11d1-AEBD-0000F80367C1;;AU)",
+        "D:(OA;;RP;e48d0154-bcf8-11d1-8702-00c04fb96050;;AU)",
+        "D:(OA;;CR;ab721a53-1e2f-11d0-9819-00aa0040529b;;WD)",
+        "D:(OA;;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;;RS)",
+        "D:(OA;;RPWP;bf967a7f-0de6-11d0-a285-00aa003049e2;;CA)",
+        "D:(OA;;RP;46a9b11d-60ae-405a-b7e8-ff8a58d456d2;;S-1-5-32-560)",
+        "D:(OA;;WPRP;6db69a1c-9422-11d1-aebd-0000f80367c1;;S-1-5-32-561)",
+
+        "D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)",
+
+        "D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)",
+        "D:(A;;RPLCLORC;;;AU)",
+        "D:(A;;LCRPLORC;;;ED)",
+
+        "D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)",
+        "D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)",
+        "D:(OA;;CCDC;bf967a86-0de6-11d0-a285-00aa003049e2;;AO)",
+        "D:(OA;;CCDC;bf967aba-0de6-11d0-a285-00aa003049e2;;AO)",
+        "D:(OA;;CCDC;bf967a9c-0de6-11d0-a285-00aa003049e2;;AO)",
+        "D:(OA;;CCDC;bf967aa8-0de6-11d0-a285-00aa003049e2;;PO)",
+        "D:(A;;RPLCLORC;;;AU)",
+        "D:(A;;LCRPLORC;;;ED)",
+        "D:(OA;;CCDC;4828CC14-1437-45bc-9B07-AD6F015E5F28;;AO)",
+        "D:(A;;RPWPCRCCDCLCLORCWOWDSW;;;DA)",
+        "D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)",
+        "D:(A;;RPLCLORC;;;AU)",
+
+        "D:(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)",
+        "D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)",
+        "D:(A;;RPLCLORC;;;AU)",
+
+        "D:S:",
+        "D:PS:",
+
+        "D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)",
+        "D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)",
+        "D:(A;;RPLCLORC;;;AU)",
+
+        "S:D:P",
+    ]
+    good_cases = []
+    bad_cases = []
+    uncertain_cases = []
+    print(len(cases))
+    print(len(set(cases)))
+    for case in set(cases):
+        res = check_sddl_powershell(case)
+        #res = check_sddl(case)
+        if res:
+            print(f"good: {case}")
+            good_cases.append(case)
+        elif res is None:
+            print(f"unknown: {case}")
+            uncertain_cases.append(case)
+        else:
+            bad_cases.append(case)
+
+    print(f"{len(bad_cases)} bad")
+    for c in bad_cases:
+        print(f"BAD: {c}")
+
+    print(f"{len(uncertain_cases)} uncertain")
+    for c in uncertain_cases:
+        print(f"MAYBE: {c}")
+
+    print(f"{len(good_cases)} good")
+    for c in good_cases:
+        print(f"GOOD: {c}")
+
+
+main()
diff --git a/libcli/security/tests/windows/windows-sddl-tests.c b/libcli/security/tests/windows/windows-sddl-tests.c
new file mode 100644 (file)
index 0000000..3857aef
--- /dev/null
@@ -0,0 +1,341 @@
+/*\r
+ * Test Windows SDDL handling.\r
+ *\r
+ * Copyright (c) 2023 Douglas Bagnall <dbagnall@samba.org>\r
+ *\r
+ * GPLv3+.\r
+ *\r
+ * This can be compiled on Windows under Cygwin, like this:\r
+ *\r
+ *\r
+ * gcc -o windows-sddl-tests  windows-sddl-tests.c \\r
+ *           C:/Windows/System32/advapi32.dll  -ladvapi32\r
+ *\r
+ *\r
+ * then run like this:\r
+ *\r
+ * ./windows-sddl-tests.exe\r
+ *\r
+ *\r
+ * That will show you a mix of success and failure.\r
+ *\r
+ * To run the tests in python/samba/tests/sddl.py, edit the method\r
+ * _test_write_test_strings(), removing the leading underscore so it starts\r
+ * with "test_". Then running\r
+ *\r
+ * make test TESTS='sddl\\b'\r
+ *\r
+ * will write some files into /tmp, containing lines like this:\r
+ *\r
+ * D:(A;;GA;;;RU) -> D:(A;;GA;;;RU)\r
+ *\r
+ * Copy these files to Windows. Then in Cygwin, run this:\r
+ *\r
+ * ./windows-sddl-tests.exe -i non_canonical.txt canonical.txt [...]\r
+ *\r
+ * and the part of each line before the " -> " will be fed into the SDDL\r
+ * parser, and back through the serialiser, which should result in the string\r
+ * after the " -> ". These are the tests that sddl.py does.\r
+ */\r
+\r
+#include <stddef.h>\r
+#include <stdint.h>\r
+#include <stdlib.h>\r
+#include <stdbool.h>\r
+#include <stdio.h>\r
+#include <sys/types.h>\r
+#include <sys/stat.h>\r
+#include <unistd.h>\r
+#include <windows.h>\r
+#include <sddl.h>\r
+\r
+#define RED "\033[1;31m"\r
+#define GREEN "\033[1;32m"\r
+#define AMBER "\033[33m"\r
+#define CYAN "\033[1;36m"\r
+#define C_NORMAL "\033[0m"\r
+\r
+/*\r
+ * Note that the SIDs SA, CA, RS, EA, PA, RO, and CN cannot be set by\r
+ * an ordinary local Administrator (error 1337, invalid SID). For this\r
+ * reason we use other SIDs instead/as well, so the list differs from\r
+ * the python/samba/tests/sddl.py list, which it is otherwise based on.\r
+ */\r
+const char *strings[] = {\r
+       "D:(A;;CC;;;BA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)",\r
+\r
+       "D:(A;;GA;;;RU)",\r
+\r
+       "D:(A;;GA;;;LG)",\r
+\r
+       ("D:(A;;RP;;;WD)"\r
+        "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)"\r
+        "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)"\r
+        "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)"\r
+        "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)"\r
+        "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)"\r
+        "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)"\r
+        "(A;;RPLCLORC;;;AU)"\r
+        "(A;;RPWPCRLCLOCCRCWDWOSW;;;BO)"\r
+        "(A;CI;RPWPCRLCLOCCRCWDWOSDSW;;;BA)"\r
+        "(A;;RPWPCRLCLOCCDCRCWDWOSDDTSW;;;SY)"\r
+        "(A;CI;RPWPCRLCLOCCDCRCWDWOSDDTSW;;;ES)"\r
+        "(A;CI;LC;;;RU)"\r
+        "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;bf967aba-0de6-11d0-a285-00aa003049e2;RU)"\r
+        "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)"\r
+        "(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)"\r
+        "(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;bf967aba-0de6-11d0-a285-00aa003049e2;RU)"\r
+        "(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)"\r
+        "(OA;;RP;c7407360-20bf-11d0-a768-00aa006e0529;;RU)"\r
+        "(OA;CIIO;RPLCLORC;;bf967a9c-0de6-11d0-a285-00aa003049e2;RU)"\r
+        "(A;;RPRC;;;RU)"\r
+        "(OA;CIIO;RPLCLORC;;bf967aba-0de6-11d0-a285-00aa003049e2;RU)"\r
+        "(A;;LCRPLORC;;;ED)"\r
+        "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;4828CC14-1437-45bc-9B07-AD6F015E5F28;RU)"\r
+        "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;4828CC14-1437-45bc-9B07-AD6F015E5F28;RU)"\r
+        "(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;4828CC14-1437-45bc-9B07-AD6F015E5F28;RU)"\r
+        "(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;4828CC14-1437-45bc-9B07-AD6F015E5F28;RU)"\r
+        "(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;4828CC14-1437-45bc-9B07-AD6F015E5F28;RU)"\r
+        "(OA;CIIO;RPLCLORC;;4828CC14-1437-45bc-9B07-AD6F015E5F28;RU)"\r
+        "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;RU)"\r
+        "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;AU)"\r
+        "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967aba-0de6-11d0-a285-00aa003049e2;ED)"\r
+        "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a9c-0de6-11d0-a285-00aa003049e2;ED)"\r
+        "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a86-0de6-11d0-a285-00aa003049e2;ED)"\r
+        "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;NO)"\r
+        "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)"\r
+        "(OA;;CR;e2a36dc9-ae17-47c3-b58b-be34c55ba633;;SU)"\r
+        "(OA;;CR;280f369c-67c7-438e-ae98-1d46f3c6f541;;AU)"\r
+        "(OA;;CR;ccc2dc7d-a6ad-4a7a-8846-c04e3cc53501;;AU)"\r
+        "(OA;;CR;05c74c5e-4deb-43b4-bd9f-86664c2a7fd5;;AU)S:(AU;SA;WDWOWP;;;WD)"),\r
+\r
+       ("S:(AU;SA;CR;;;WD)"\r
+        "(AU;SA;CR;;;WD)"),\r
+\r
+       ("S:""(OU;CISA;WP;f30e3bbe-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)"\r
+        "(OU;CISA;WP;f30e3bbf-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)"),\r
+\r
+       ("D:(A;;RPLCLORC;;;BO)"\r
+        "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)"\r
+        "(A;;RPLCLORC;;;AU)"),\r
+\r
+       ("D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;BO)"\r
+        "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;AO)"\r
+        "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)"\r
+        "(A;;RPCRLCLORCSDDT;;;CO)"\r
+        "(OA;;WP;4c164200-20c0-11d0-a768-00aa006e0529;;CO)"\r
+        "(A;;RPLCLORC;;;AU)"\r
+        "(OA;;CR;ab721a53-1e2f-11d0-9819-00aa0040529b;;WD)"\r
+        "(A;;CCDC;;;PS)"\r
+        "(OA;;CCDC;bf967aa8-0de6-11d0-a285-00aa003049e2;;PO)"\r
+        "(OA;;RPWP;bf967a7f-0de6-11d0-a285-00aa003049e2;;SY)"\r
+        "(OA;;SW;f3a64788-5306-11d1-a9c5-0000f80367c1;;PS)"\r
+        "(OA;;RPWP;77B5B886-944A-11d1-AEBD-0000F80367C1;;PS)"\r
+        "(OA;;SW;72e39547-7b18-11d1-adef-00c04fd8d5cd;;PS)"\r
+        "(OA;;SW;72e39547-7b18-11d1-adef-00c04fd8d5cd;;CO)"\r
+        "(OA;;SW;f3a64788-5306-11d1-a9c5-0000f80367c1;;CO)"\r
+        "(OA;;WP;3e0abfd0-126a-11d0-a060-00aa006c33ed;bf967a86-0de6-11d0-a285-00aa003049e2;CO)"\r
+        "(OA;;WP;5f202010-79a5-11d0-9020-00c04fc2d4cf;bf967a86-0de6-11d0-a285-00aa003049e2;CO)"\r
+        "(OA;;WP;bf967950-0de6-11d0-a285-00aa003049e2;bf967a86-0de6-11d0-a285-00aa003049e2;CO)"\r
+        "(OA;;WP;bf967953-0de6-11d0-a285-00aa003049e2;bf967a86-0de6-11d0-a285-00aa003049e2;CO)"\r
+        "(OA;;RP;46a9b11d-60ae-405a-b7e8-ff8a58d456d2;;SU)"),\r
+\r
+       ("D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;BO)"\r
+        "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)"\r
+        "(A;;RPLCLORC;;;AU)"),\r
+\r
+       ("D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;BO)"\r
+        "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)"\r
+        "(A;;RPLCLORC;;;AU)"\r
+        "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;AO)"\r
+        "(A;;RPLCLORC;;;PS)"\r
+        "(OA;;CR;ab721a55-1e2f-11d0-9819-00aa0040529b;;AU)"\r
+        "(OA;;RP;46a9b11d-60ae-405a-b7e8-ff8a58d456d2;;SU)"),\r
+\r
+       ("D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;BO)"\r
+        "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)"\r
+        "(A;;RPLCLORC;;;AU)"\r
+        "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;CO)"),\r
+\r
+       ("D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;BO)"\r
+        "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)"\r
+        "(A;;RPLCLORC;;;AU)S:(AU;SA;CRWP;;;WD)"),\r
+\r
+       ("D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;BO)"\r
+        "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)"\r
+        "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;AO)"\r
+        "(A;;RPLCLORC;;;PS)"\r
+        "(OA;;CR;ab721a53-1e2f-11d0-9819-00aa0040529b;;PS)"\r
+        "(OA;;CR;ab721a54-1e2f-11d0-9819-00aa0040529b;;PS)"\r
+        "(OA;;CR;ab721a56-1e2f-11d0-9819-00aa0040529b;;PS)"\r
+        "(OA;;RPWP;77B5B886-944A-11d1-AEBD-0000F80367C1;;PS)"\r
+        "(OA;;RPWP;E45795B2-9455-11d1-AEBD-0000F80367C1;;PS)"\r
+        "(OA;;RPWP;E45795B3-9455-11d1-AEBD-0000F80367C1;;PS)"\r
+        "(OA;;RP;037088f8-0ae1-11d2-b422-00a0c968f939;;RD)"\r
+        "(OA;;RP;4c164200-20c0-11d0-a768-00aa006e0529;;RD)"\r
+        "(OA;;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;;RD)"\r
+        "(A;;RC;;;AU)"\r
+        "(OA;;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;;AU)"\r
+        "(OA;;RP;77B5B886-944A-11d1-AEBD-0000F80367C1;;AU)"\r
+        "(OA;;RP;E45795B3-9455-11d1-AEBD-0000F80367C1;;AU)"\r
+        "(OA;;RP;e48d0154-bcf8-11d1-8702-00c04fb96050;;AU)"\r
+        "(OA;;CR;ab721a53-1e2f-11d0-9819-00aa0040529b;;WD)"\r
+        "(OA;;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;;RD)"\r
+        "(OA;;RPWP;bf967a7f-0de6-11d0-a285-00aa003049e2;;SY)"\r
+        "(OA;;RP;46a9b11d-60ae-405a-b7e8-ff8a58d456d2;;SU)"\r
+        "(OA;;WPRP;6db69a1c-9422-11d1-aebd-0000f80367c1;;SU)"),\r
+\r
+       "D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)",\r
+\r
+       ("D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)"\r
+        "(A;;RPLCLORC;;;AU)"\r
+        "(A;;LCRPLORC;;;ED)"),\r
+\r
+       ("D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)"\r
+        "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;BO)"\r
+        "(OA;;CCDC;bf967a86-0de6-11d0-a285-00aa003049e2;;AO)"\r
+        "(OA;;CCDC;bf967aba-0de6-11d0-a285-00aa003049e2;;AO)"\r
+        "(OA;;CCDC;bf967a9c-0de6-11d0-a285-00aa003049e2;;AO)"\r
+        "(OA;;CCDC;bf967aa8-0de6-11d0-a285-00aa003049e2;;PO)"\r
+        "(A;;RPLCLORC;;;AU)"\r
+        "(A;;LCRPLORC;;;ED)"\r
+        "(OA;;CCDC;4828CC14-1437-45bc-9B07-AD6F015E5F28;;AO)"),\r
+\r
+       ("D:(A;;RPWPCRCCDCLCLORCWOWDSW;;;BO)"\r
+        "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)"\r
+        "(A;;RPLCLORC;;;AU)"),\r
+\r
+       ("D:(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;BO)"\r
+        "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)"\r
+        "(A;;RPLCLORC;;;AU)"),\r
+\r
+       "D:S:",\r
+       "D:PS:",\r
+       NULL\r
+};\r
+\r
+\r
+static int test_pair(const char *s, const char *canonical)\r
+{\r
+       PSECURITY_DESCRIPTOR sd = NULL;\r
+       ULONG len;\r
+       char *return_string = NULL;\r
+       ULONG return_len;\r
+       int ok = ConvertStringSecurityDescriptorToSecurityDescriptorA(s,\r
+                                                                     1,\r
+                                                                     &sd,\r
+                                                                     &len);\r
+       if (!ok) {\r
+               int e = GetLastError();\r
+               const char *ename = NULL;\r
+               switch(e) {\r
+               case 1337:\r
+                       ename = " invalid sid";\r
+                       break;\r
+               case 1336:\r
+                       ename = " insufficient privs/SACL vs DACL/something something";\r
+                       break;\r
+               case 1804:\r
+                       ename = " invalid datatype";\r
+                       break;\r
+               default:\r
+                       ename = "";\r
+               }\r
+\r
+               printf(RED "not ok:" AMBER " %d%s" C_NORMAL ": %s\n",\r
+                      e, ename, s);\r
+               return e;\r
+       }\r
+       if (sd == NULL) {\r
+               printf(RED "NULL sd" C_NORMAL": %s\n", s);\r
+               return -1;\r
+       }\r
+\r
+       ok = ConvertSecurityDescriptorToStringSecurityDescriptorA(\r
+               sd,\r
+               1,\r
+               ~BACKUP_SECURITY_INFORMATION,\r
+               &return_string,\r
+               &return_len);\r
+       if (strncmp(return_string, canonical, return_len) != 0) {\r
+               printf(RED "return differs:" AMBER " %u vs %u" C_NORMAL "\n",\r
+                      len, return_len);\r
+               printf(RED "original:" C_NORMAL ": %s\n", s);\r
+               printf(RED "returned:" C_NORMAL ": %s\n", return_string);\r
+               return -2;\r
+       }\r
+       printf(GREEN "GOOD" C_NORMAL ": %s\n", s);\r
+       if (strncmp(return_string, s, return_len) != 0) {\r
+               printf(CYAN "original:" C_NORMAL ": %s\n", s);\r
+               printf(CYAN "returned:" C_NORMAL ": %s\n", return_string);\r
+               return -2;\r
+       }\r
+       return 0;\r
+}\r
+\r
+\r
+int test_from_files(int argc, const char *argv[])\r
+{\r
+       size_t i, j;\r
+       static char buf[100000];\r
+\r
+       for (i = 0; i < argc; i++) {\r
+               char *orig = NULL;\r
+               char *canon = NULL;\r
+               size_t len;\r
+               FILE *f = fopen(argv[i], "r");\r
+               if (f == NULL) {\r
+                       printf(RED "bad filename? %s\n" C_NORMAL,\r
+                              argv[i]);\r
+               }\r
+               len = fread(buf, 1, sizeof(buf), f);\r
+\r
+               if (len >= sizeof(buf) - 1 || len == 0) {\r
+                       printf(RED "couldn't read %s\n" C_NORMAL, argv[i]);\r
+                       continue;\r
+               }\r
+               printf(CYAN "%s\n" C_NORMAL, argv[i]);\r
+               buf[len] = 0;\r
+               orig = buf;\r
+               for (j = 0; j < len; j++) {\r
+                       char c = buf[j];\r
+                       if (c == '\n') {\r
+                               buf[j] = 0;\r
+                               if (j != 0 && buf[j - 1] == '\r') {\r
+                                       buf[j - 1] = 0;\r
+                               }\r
+                               if (orig && canon) {\r
+                                       test_pair(orig, canon);\r
+                                       canon = NULL;\r
+                               } else {\r
+                                       printf(RED "bad pair %s -> %s\n" C_NORMAL,\r
+                                              orig, canon);\r
+                               }\r
+                               orig = buf + j + 1;\r
+                       } else if (c == ' ' && j + 4 < len &&\r
+                                  buf[j + 1] == '-' &&\r
+                                  buf[j + 2] == '>' &&\r
+                                  buf[j + 3] == ' ') {\r
+                               buf[j] = 0;\r
+                               canon = buf + j + 4;\r
+                       }\r
+               }\r
+       }\r
+}\r
+\r
+int main(int argc, const char *argv[])\r
+{\r
+       uint32_t i;\r
+       if (argc < 2) {\r
+               for (i = 0; strings[i] != NULL; i++) {\r
+                       test_pair(strings[i], strings[i]);\r
+               }\r
+       } else if (strncmp("-i", argv[1], 2) == 0) {\r
+               return test_from_files(argc - 2, argv + 2);\r
+       } else {\r
+               for (i = 1; i < argc; i++) {\r
+                       test_pair(argv[i], argv[i]);\r
+               }\r
+       }\r
+       return 0;\r
+}\r