summaryrefslogtreecommitdiffstats
path: root/libcli/security
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 17:20:00 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 17:20:00 +0000
commit8daa83a594a2e98f39d764422bfbdbc62c9efd44 (patch)
tree4099e8021376c7d8c05bdf8503093d80e9c7bad0 /libcli/security
parentInitial commit. (diff)
downloadsamba-8daa83a594a2e98f39d764422bfbdbc62c9efd44.tar.xz
samba-8daa83a594a2e98f39d764422bfbdbc62c9efd44.zip
Adding upstream version 2:4.20.0+dfsg.upstream/2%4.20.0+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'libcli/security')
-rw-r--r--libcli/security/access_check.c958
-rw-r--r--libcli/security/access_check.h100
-rw-r--r--libcli/security/claims-conversions.c1216
-rw-r--r--libcli/security/claims-conversions.h60
-rw-r--r--libcli/security/conditional_ace.c2550
-rw-r--r--libcli/security/conditional_ace.h97
-rw-r--r--libcli/security/create_descriptor.c666
-rw-r--r--libcli/security/display_sec.c274
-rw-r--r--libcli/security/display_sec.h34
-rw-r--r--libcli/security/dom_sid.c582
-rw-r--r--libcli/security/dom_sid.h155
-rw-r--r--libcli/security/object_tree.c127
-rw-r--r--libcli/security/privileges.c498
-rw-r--r--libcli/security/privileges.h116
-rw-r--r--libcli/security/privileges_private.h41
-rw-r--r--libcli/security/pysecurity.c96
-rw-r--r--libcli/security/sddl.c1341
-rw-r--r--libcli/security/sddl.h47
-rw-r--r--libcli/security/sddl_conditional_ace.c3476
-rw-r--r--libcli/security/secace.c204
-rw-r--r--libcli/security/secace.h40
-rw-r--r--libcli/security/secacl.c75
-rw-r--r--libcli/security/secacl.h33
-rw-r--r--libcli/security/secdesc.c623
-rw-r--r--libcli/security/secdesc.h96
-rw-r--r--libcli/security/security.h121
-rw-r--r--libcli/security/security_descriptor.c908
-rw-r--r--libcli/security/security_descriptor.h99
-rw-r--r--libcli/security/security_token.c228
-rw-r--r--libcli/security/security_token.h74
-rw-r--r--libcli/security/session.c74
-rw-r--r--libcli/security/session.h44
-rw-r--r--libcli/security/tests/data/conditional_aces.txt83
-rw-r--r--libcli/security/tests/data/conditional_aces.txt.json1
-rw-r--r--libcli/security/tests/data/conditional_aces_case_insensitive.txt1
-rw-r--r--libcli/security/tests/data/conditional_aces_should_fail.txt14
-rw-r--r--libcli/security/tests/data/conditional_aces_windows_only.txt14
-rwxr-xr-xlibcli/security/tests/data/export-sddl-fuzz-seeds-as-json49
-rwxr-xr-xlibcli/security/tests/data/extract-sddl-seeds72
-rw-r--r--libcli/security/tests/data/ndr_dumps/fileb5iJt4bin0 -> 118 bytes
-rw-r--r--libcli/security/tests/data/ndr_dumps/fileb8cNVSbin0 -> 360 bytes
-rw-r--r--libcli/security/tests/data/ndr_dumps/filebI7h5Hbin0 -> 112 bytes
-rw-r--r--libcli/security/tests/data/ndr_dumps/filebNdBgtbin0 -> 344 bytes
-rw-r--r--libcli/security/tests/data/ndr_dumps/filebOjK4Hbin0 -> 124 bytes
-rw-r--r--libcli/security/tests/data/ndr_dumps/filebzCPTHbin0 -> 480 bytes
-rw-r--r--libcli/security/tests/data/oversize-acls.json20
-rw-r--r--libcli/security/tests/data/registry-object-rights.json1
-rw-r--r--libcli/security/tests/data/short-conditional-and-resource-aces-successes.json.gzbin0 -> 17815 bytes
-rw-r--r--libcli/security/tests/data/short-conditional-and-resource-aces-tx-int.json.gzbin0 -> 2183 bytes
-rw-r--r--libcli/security/tests/data/short-ordinary-acls-v2.json.gzbin0 -> 7223 bytes
-rw-r--r--libcli/security/tests/data/short-ordinary-acls.json.gzbin0 -> 220742 bytes
-rw-r--r--libcli/security/tests/test_claim_conversion.c171
-rw-r--r--libcli/security/tests/test_run_conditional_ace.c730
-rw-r--r--libcli/security/tests/test_sddl_conditional_ace.c1003
-rw-r--r--libcli/security/tests/windows/canonical.txt19
-rw-r--r--libcli/security/tests/windows/conditional_aces.txt.json1
-rw-r--r--libcli/security/tests/windows/non_canonical.txt50
-rw-r--r--libcli/security/tests/windows/should_fail.txt47
-rw-r--r--libcli/security/tests/windows/windows-sddl-tests.c341
-rw-r--r--libcli/security/tests/windows/windows-sddl-tests.py181
-rw-r--r--libcli/security/tests/windows/windows_is_fussy.txt1
-rw-r--r--libcli/security/tests/windows/windows_is_less_fussy.txt23
-rw-r--r--libcli/security/tests/windows/windows_is_weird.txt10
-rw-r--r--libcli/security/util_sid.c1117
-rw-r--r--libcli/security/wscript_build64
65 files changed, 19066 insertions, 0 deletions
diff --git a/libcli/security/access_check.c b/libcli/security/access_check.c
new file mode 100644
index 0000000..e3dfe3d
--- /dev/null
+++ b/libcli/security/access_check.c
@@ -0,0 +1,958 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Andrew Tridgell 2004
+ Copyright (C) Gerald Carter 2005
+ Copyright (C) Volker Lendecke 2007
+ Copyright (C) Jeremy Allison 2008
+ Copyright (C) Andrew Bartlett 2010
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "replace.h"
+#include "lib/util/debug.h"
+#include "libcli/security/security.h"
+#include "librpc/gen_ndr/conditional_ace.h"
+#include "libcli/security/conditional_ace.h"
+
+/* Map generic access rights to object specific rights. This technique is
+ used to give meaning to assigning read, write, execute and all access to
+ objects. Each type of object has its own mapping of generic to object
+ specific access rights. */
+
+void se_map_generic(uint32_t *access_mask, const struct generic_mapping *mapping)
+{
+ uint32_t old_mask = *access_mask;
+
+ if (*access_mask & GENERIC_READ_ACCESS) {
+ *access_mask &= ~GENERIC_READ_ACCESS;
+ *access_mask |= mapping->generic_read;
+ }
+
+ if (*access_mask & GENERIC_WRITE_ACCESS) {
+ *access_mask &= ~GENERIC_WRITE_ACCESS;
+ *access_mask |= mapping->generic_write;
+ }
+
+ if (*access_mask & GENERIC_EXECUTE_ACCESS) {
+ *access_mask &= ~GENERIC_EXECUTE_ACCESS;
+ *access_mask |= mapping->generic_execute;
+ }
+
+ if (*access_mask & GENERIC_ALL_ACCESS) {
+ *access_mask &= ~GENERIC_ALL_ACCESS;
+ *access_mask |= mapping->generic_all;
+ }
+
+ if (old_mask != *access_mask) {
+ DEBUG(10, ("se_map_generic(): mapped mask 0x%08x to 0x%08x\n",
+ old_mask, *access_mask));
+ }
+}
+
+/* Map generic access rights to object specific rights for all the ACE's
+ * in a security_acl.
+ */
+
+void security_acl_map_generic(struct security_acl *sa,
+ const struct generic_mapping *mapping)
+{
+ unsigned int i;
+
+ if (!sa) {
+ return;
+ }
+
+ for (i = 0; i < sa->num_aces; i++) {
+ se_map_generic(&sa->aces[i].access_mask, mapping);
+ }
+}
+
+/* Map standard access rights to object specific rights. This technique is
+ used to give meaning to assigning read, write, execute and all access to
+ objects. Each type of object has its own mapping of standard to object
+ specific access rights. */
+
+void se_map_standard(uint32_t *access_mask, const struct standard_mapping *mapping)
+{
+ uint32_t old_mask = *access_mask;
+
+ if (*access_mask & SEC_STD_READ_CONTROL) {
+ *access_mask &= ~SEC_STD_READ_CONTROL;
+ *access_mask |= mapping->std_read;
+ }
+
+ if (*access_mask & (SEC_STD_DELETE|SEC_STD_WRITE_DAC|SEC_STD_WRITE_OWNER|SEC_STD_SYNCHRONIZE)) {
+ *access_mask &= ~(SEC_STD_DELETE|SEC_STD_WRITE_DAC|SEC_STD_WRITE_OWNER|SEC_STD_SYNCHRONIZE);
+ *access_mask |= mapping->std_all;
+ }
+
+ if (old_mask != *access_mask) {
+ DEBUG(10, ("se_map_standard(): mapped mask 0x%08x to 0x%08x\n",
+ old_mask, *access_mask));
+ }
+}
+
+enum ace_callback_result {
+ ACE_CALLBACK_DENY,
+ ACE_CALLBACK_ALLOW,
+ ACE_CALLBACK_SKIP, /* do not apply this ACE */
+ ACE_CALLBACK_INVALID /* we don't want to process the conditional ACE */
+};
+
+
+static enum ace_callback_result check_callback_ace_allow(
+ const struct security_ace *ace,
+ const struct security_token *token,
+ const struct security_descriptor *sd)
+{
+ bool ok;
+ int result;
+
+ switch (token->evaluate_claims) {
+ case CLAIMS_EVALUATION_ALWAYS:
+ break;
+
+ case CLAIMS_EVALUATION_INVALID_STATE:
+ DBG_WARNING("Refusing to evaluate ACL with "
+ "conditional ACE against security "
+ "token with CLAIMS_EVALUATION_INVALID_STATE\n");
+ return ACE_CALLBACK_INVALID;
+ case CLAIMS_EVALUATION_NEVER:
+ default:
+ /*
+ * We are asked to pretend we never understood this
+ * ACE type.
+ *
+ * By returning SKIP, this ACE will not adjust any
+ * permission bits making it an effective no-op, which
+ * was the default behaviour up to Samba 4.19.
+ */
+ return ACE_CALLBACK_SKIP;
+ }
+
+ if (ace->type != SEC_ACE_TYPE_ACCESS_ALLOWED_CALLBACK &&
+ ace->type != SEC_ACE_TYPE_ACCESS_ALLOWED_CALLBACK_OBJECT) {
+ /* This indicates a programming error */
+ DBG_ERR("bad conditional allow ACE type: %u\n", ace->type);
+ return ACE_CALLBACK_INVALID;
+ }
+
+ /*
+ * Until we discover otherwise, we assume all callback ACEs
+ * are conditional ACEs.
+ */
+ ok = access_check_conditional_ace(ace, token, sd, &result);
+ if (!ok) {
+ /*
+ * An error in processing the conditional ACE is
+ * treated as UNKNOWN, which amounts to a DENY/SKIP
+ * result.
+ *
+ * This is different from the INVALID result which
+ * means we should not be thinking about conditional
+ * ACES at all, and will abort the whole access check.
+ */
+ DBG_WARNING("callback ACE was not a valid conditional ACE\n");
+ return ACE_CALLBACK_SKIP;
+ }
+ if (result == ACE_CONDITION_TRUE) {
+ return ACE_CALLBACK_ALLOW;
+ }
+ /* UNKNOWN means do not allow */
+ return ACE_CALLBACK_SKIP;
+}
+
+
+static enum ace_callback_result check_callback_ace_deny(
+ const struct security_ace *ace,
+ const struct security_token *token,
+ const struct security_descriptor *sd)
+{
+ bool ok;
+ int result;
+
+ switch (token->evaluate_claims) {
+ case CLAIMS_EVALUATION_ALWAYS:
+ break;
+
+ case CLAIMS_EVALUATION_INVALID_STATE:
+ DBG_WARNING("Refusing to evaluate ACL with "
+ "conditional ACE against security "
+ "token with CLAIMS_EVALUATION_INVALID_STATE\n");
+ return ACE_CALLBACK_INVALID;
+ case CLAIMS_EVALUATION_NEVER:
+ default:
+ /*
+ * We are asked to pretend we never understood this
+ * ACE type.
+ */
+ return ACE_CALLBACK_SKIP;
+ }
+
+ if (ace->type != SEC_ACE_TYPE_ACCESS_DENIED_CALLBACK &&
+ ace->type != SEC_ACE_TYPE_ACCESS_DENIED_CALLBACK_OBJECT) {
+ DBG_ERR("bad conditional deny ACE type: %u\n", ace->type);
+ return ACE_CALLBACK_INVALID;
+ }
+
+ /*
+ * Until we discover otherwise, we assume all callback ACEs
+ * are conditional ACEs.
+ */
+ ok = access_check_conditional_ace(ace, token, sd, &result);
+ if (!ok) {
+ /*
+ * An error in processing the conditional ACE is
+ * treated as UNKNOWN, which means DENY.
+ */
+ DBG_WARNING("callback ACE was not a valid conditional ACE\n");
+ return ACE_CALLBACK_DENY;
+ }
+ if (result != ACE_CONDITION_FALSE) {
+ /* UNKNOWN means deny */
+ return ACE_CALLBACK_DENY;
+ }
+ return ACE_CALLBACK_SKIP;
+}
+
+
+/*
+ perform a SEC_FLAG_MAXIMUM_ALLOWED access check
+*/
+static uint32_t access_check_max_allowed(const struct security_descriptor *sd,
+ const struct security_token *token,
+ enum implicit_owner_rights implicit_owner_rights)
+{
+ uint32_t denied = 0, granted = 0;
+ bool am_owner = false;
+ bool have_owner_rights_ace = false;
+ unsigned i;
+
+ if (sd->dacl == NULL) {
+ if (security_token_has_sid(token, sd->owner_sid)) {
+ switch (implicit_owner_rights) {
+ case IMPLICIT_OWNER_READ_CONTROL_AND_WRITE_DAC_RIGHTS:
+ granted |= SEC_STD_WRITE_DAC;
+ FALL_THROUGH;
+ case IMPLICIT_OWNER_READ_CONTROL_RIGHTS:
+ granted |= SEC_STD_READ_CONTROL;
+ break;
+ }
+ }
+ return granted;
+ }
+
+ if (security_token_has_sid(token, sd->owner_sid)) {
+ /*
+ * Check for explicit owner rights: if there are none, we remove
+ * the default owner right SEC_STD_WRITE_DAC|SEC_STD_READ_CONTROL
+ * from remaining_access. Otherwise we just process the
+ * explicitly granted rights when processing the ACEs.
+ */
+ am_owner = true;
+
+ for (i=0; i < sd->dacl->num_aces; i++) {
+ struct security_ace *ace = &sd->dacl->aces[i];
+
+ if (ace->flags & SEC_ACE_FLAG_INHERIT_ONLY) {
+ continue;
+ }
+
+ have_owner_rights_ace = dom_sid_equal(
+ &ace->trustee, &global_sid_Owner_Rights);
+ if (have_owner_rights_ace) {
+ break;
+ }
+ }
+ }
+
+ if (am_owner && !have_owner_rights_ace) {
+ switch (implicit_owner_rights) {
+ case IMPLICIT_OWNER_READ_CONTROL_AND_WRITE_DAC_RIGHTS:
+ granted |= SEC_STD_WRITE_DAC;
+ FALL_THROUGH;
+ case IMPLICIT_OWNER_READ_CONTROL_RIGHTS:
+ granted |= SEC_STD_READ_CONTROL;
+ break;
+ }
+ }
+
+ for (i = 0;i<sd->dacl->num_aces; i++) {
+ struct security_ace *ace = &sd->dacl->aces[i];
+ bool is_owner_rights_ace = false;
+
+ if (ace->flags & SEC_ACE_FLAG_INHERIT_ONLY) {
+ continue;
+ }
+
+ if (am_owner) {
+ is_owner_rights_ace = dom_sid_equal(
+ &ace->trustee, &global_sid_Owner_Rights);
+ }
+
+ if (!is_owner_rights_ace &&
+ !security_token_has_sid(token, &ace->trustee))
+ {
+ continue;
+ }
+
+ switch (ace->type) {
+ case SEC_ACE_TYPE_ACCESS_ALLOWED:
+ granted |= ace->access_mask;
+ break;
+ case SEC_ACE_TYPE_ACCESS_DENIED:
+ case SEC_ACE_TYPE_ACCESS_DENIED_OBJECT:
+ denied |= ~granted & ace->access_mask;
+ break;
+
+ case SEC_ACE_TYPE_ACCESS_ALLOWED_CALLBACK:
+ {
+ enum ace_callback_result allow =
+ check_callback_ace_allow(ace, token, sd);
+ if (allow == ACE_CALLBACK_INVALID) {
+ return 0;
+ }
+ if (allow == ACE_CALLBACK_ALLOW) {
+ granted |= ace->access_mask;
+ }
+ break;
+ }
+
+ case SEC_ACE_TYPE_ACCESS_DENIED_CALLBACK:
+ {
+ enum ace_callback_result deny =
+ check_callback_ace_deny(ace, token, sd);
+ if (deny == ACE_CALLBACK_INVALID) {
+ return 0;
+ }
+ if (deny == ACE_CALLBACK_DENY) {
+ denied |= ~granted & ace->access_mask;
+ }
+ break;
+ }
+
+ default: /* Other ACE types not handled/supported */
+ break;
+ }
+ }
+
+ return granted & ~denied;
+}
+
+
+
+static NTSTATUS se_access_check_implicit_owner(const struct security_descriptor *sd,
+ const struct security_token *token,
+ uint32_t access_desired,
+ uint32_t *access_granted,
+ enum implicit_owner_rights implicit_owner_rights)
+{
+ uint32_t i;
+ uint32_t bits_remaining;
+ uint32_t explicitly_denied_bits = 0;
+ bool am_owner = false;
+ bool have_owner_rights_ace = false;
+
+ switch (token->evaluate_claims) {
+ case CLAIMS_EVALUATION_INVALID_STATE:
+ if (token->num_local_claims > 0 ||
+ token->num_user_claims > 0 ||
+ token->num_device_claims > 0 ||
+ token->num_device_sids > 0) {
+ DBG_WARNING("Refusing to evaluate token with claims or device SIDs but also "
+ "with CLAIMS_EVALUATION_INVALID_STATE\n");
+ return NT_STATUS_INVALID_TOKEN;
+ }
+ break;
+ case CLAIMS_EVALUATION_ALWAYS:
+ case CLAIMS_EVALUATION_NEVER:
+ break;
+ }
+
+ *access_granted = access_desired;
+ bits_remaining = access_desired;
+
+ /* handle the maximum allowed flag */
+ if (access_desired & SEC_FLAG_MAXIMUM_ALLOWED) {
+ uint32_t orig_access_desired = access_desired;
+
+ access_desired |= access_check_max_allowed(sd, token, implicit_owner_rights);
+ access_desired &= ~SEC_FLAG_MAXIMUM_ALLOWED;
+ *access_granted = access_desired;
+ bits_remaining = access_desired;
+
+ DEBUG(10,("se_access_check: MAX desired = 0x%x, granted = 0x%x, remaining = 0x%x\n",
+ orig_access_desired,
+ *access_granted,
+ bits_remaining));
+ }
+
+ /* a NULL dacl allows access */
+ if ((sd->type & SEC_DESC_DACL_PRESENT) && sd->dacl == NULL) {
+ *access_granted = access_desired;
+ return NT_STATUS_OK;
+ }
+
+ if (sd->dacl == NULL) {
+ goto done;
+ }
+
+ if (security_token_has_sid(token, sd->owner_sid)) {
+ /*
+ * Check for explicit owner rights: if there are none, we remove
+ * the default owner right SEC_STD_WRITE_DAC|SEC_STD_READ_CONTROL
+ * from remaining_access. Otherwise we just process the
+ * explicitly granted rights when processing the ACEs.
+ */
+ am_owner = true;
+
+ for (i=0; i < sd->dacl->num_aces; i++) {
+ struct security_ace *ace = &sd->dacl->aces[i];
+
+ if (ace->flags & SEC_ACE_FLAG_INHERIT_ONLY) {
+ continue;
+ }
+
+ have_owner_rights_ace = dom_sid_equal(
+ &ace->trustee, &global_sid_Owner_Rights);
+ if (have_owner_rights_ace) {
+ break;
+ }
+ }
+ }
+ if (am_owner && !have_owner_rights_ace) {
+ switch (implicit_owner_rights) {
+ case IMPLICIT_OWNER_READ_CONTROL_AND_WRITE_DAC_RIGHTS:
+ bits_remaining &= ~SEC_STD_WRITE_DAC;
+ FALL_THROUGH;
+ case IMPLICIT_OWNER_READ_CONTROL_RIGHTS:
+ bits_remaining &= ~SEC_STD_READ_CONTROL;
+ break;
+ }
+ }
+
+ /* check each ace in turn. */
+ for (i=0; bits_remaining && i < sd->dacl->num_aces; i++) {
+ struct security_ace *ace = &sd->dacl->aces[i];
+ bool is_owner_rights_ace = false;
+
+ if (ace->flags & SEC_ACE_FLAG_INHERIT_ONLY) {
+ continue;
+ }
+
+ if (am_owner) {
+ is_owner_rights_ace = dom_sid_equal(
+ &ace->trustee, &global_sid_Owner_Rights);
+ }
+
+ if (!is_owner_rights_ace &&
+ !security_token_has_sid(token, &ace->trustee))
+ {
+ continue;
+ }
+
+ switch (ace->type) {
+ case SEC_ACE_TYPE_ACCESS_ALLOWED:
+ bits_remaining &= ~ace->access_mask;
+ break;
+ case SEC_ACE_TYPE_ACCESS_DENIED:
+ case SEC_ACE_TYPE_ACCESS_DENIED_OBJECT:
+ explicitly_denied_bits |= (bits_remaining & ace->access_mask);
+ break;
+
+ case SEC_ACE_TYPE_ACCESS_ALLOWED_CALLBACK:
+ {
+ enum ace_callback_result allow =
+ check_callback_ace_allow(ace, token, sd);
+ if (allow == ACE_CALLBACK_INVALID) {
+ return NT_STATUS_INVALID_ACE_CONDITION;
+ }
+ if (allow == ACE_CALLBACK_ALLOW) {
+ bits_remaining &= ~ace->access_mask;
+ }
+ break;
+ }
+
+ case SEC_ACE_TYPE_ACCESS_DENIED_CALLBACK:
+ case SEC_ACE_TYPE_ACCESS_DENIED_CALLBACK_OBJECT:
+ {
+ enum ace_callback_result deny =
+ check_callback_ace_deny(ace, token, sd);
+ if (deny == ACE_CALLBACK_INVALID) {
+ return NT_STATUS_INVALID_ACE_CONDITION;
+ }
+ if (deny == ACE_CALLBACK_DENY) {
+ explicitly_denied_bits |= (bits_remaining & ace->access_mask);
+ }
+ break;
+ }
+
+ default: /* Other ACE types not handled/supported */
+ break;
+ }
+ }
+
+ /* Explicitly denied bits always override */
+ bits_remaining |= explicitly_denied_bits;
+
+ /*
+ * We check privileges here because they override even DENY entries.
+ */
+
+ /* Does the user have the privilege to gain SEC_PRIV_SECURITY? */
+ if (bits_remaining & SEC_FLAG_SYSTEM_SECURITY) {
+ if (security_token_has_privilege(token, SEC_PRIV_SECURITY)) {
+ bits_remaining &= ~SEC_FLAG_SYSTEM_SECURITY;
+ } else {
+ return NT_STATUS_PRIVILEGE_NOT_HELD;
+ }
+ }
+
+ if ((bits_remaining & SEC_STD_WRITE_OWNER) &&
+ security_token_has_privilege(token, SEC_PRIV_TAKE_OWNERSHIP)) {
+ bits_remaining &= ~(SEC_STD_WRITE_OWNER);
+ }
+
+done:
+ if (bits_remaining != 0) {
+ *access_granted = bits_remaining;
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ The main entry point for access checking. If returning ACCESS_DENIED
+ this function returns the denied bits in the uint32_t pointed
+ to by the access_granted pointer.
+*/
+NTSTATUS se_access_check(const struct security_descriptor *sd,
+ const struct security_token *token,
+ uint32_t access_desired,
+ uint32_t *access_granted)
+{
+ return se_access_check_implicit_owner(sd,
+ token,
+ access_desired,
+ access_granted,
+ IMPLICIT_OWNER_READ_CONTROL_AND_WRITE_DAC_RIGHTS);
+}
+
+/*
+ The main entry point for access checking FOR THE FILE SERVER ONLY !
+ If returning ACCESS_DENIED this function returns the denied bits in
+ the uint32_t pointed to by the access_granted pointer.
+*/
+NTSTATUS se_file_access_check(const struct security_descriptor *sd,
+ const struct security_token *token,
+ bool priv_open_requested,
+ uint32_t access_desired,
+ uint32_t *access_granted)
+{
+ uint32_t bits_remaining;
+ NTSTATUS status;
+
+ if (!priv_open_requested) {
+ /* Fall back to generic se_access_check(). */
+ return se_access_check_implicit_owner(sd,
+ token,
+ access_desired,
+ access_granted,
+ IMPLICIT_OWNER_READ_CONTROL_AND_WRITE_DAC_RIGHTS);
+ }
+
+ /*
+ * We need to handle the maximum allowed flag
+ * outside of se_access_check(), as we need to
+ * add in the access allowed by the privileges
+ * as well.
+ */
+
+ if (access_desired & SEC_FLAG_MAXIMUM_ALLOWED) {
+ uint32_t orig_access_desired = access_desired;
+
+ access_desired |= access_check_max_allowed(sd, token, true);
+ access_desired &= ~SEC_FLAG_MAXIMUM_ALLOWED;
+
+ if (security_token_has_privilege(token, SEC_PRIV_BACKUP)) {
+ access_desired |= SEC_RIGHTS_PRIV_BACKUP;
+ }
+
+ if (security_token_has_privilege(token, SEC_PRIV_RESTORE)) {
+ access_desired |= SEC_RIGHTS_PRIV_RESTORE;
+ }
+
+ DEBUG(10,("se_file_access_check: MAX desired = 0x%x "
+ "mapped to 0x%x\n",
+ orig_access_desired,
+ access_desired));
+ }
+
+ status = se_access_check_implicit_owner(sd,
+ token,
+ access_desired,
+ access_granted,
+ IMPLICIT_OWNER_READ_CONTROL_AND_WRITE_DAC_RIGHTS);
+
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
+ return status;
+ }
+
+ bits_remaining = *access_granted;
+
+ /* Check if we should override with privileges. */
+ if ((bits_remaining & SEC_RIGHTS_PRIV_BACKUP) &&
+ security_token_has_privilege(token, SEC_PRIV_BACKUP)) {
+ bits_remaining &= ~(SEC_RIGHTS_PRIV_BACKUP);
+ }
+ if ((bits_remaining & SEC_RIGHTS_PRIV_RESTORE) &&
+ security_token_has_privilege(token, SEC_PRIV_RESTORE)) {
+ bits_remaining &= ~(SEC_RIGHTS_PRIV_RESTORE);
+ }
+ if (bits_remaining != 0) {
+ *access_granted = bits_remaining;
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static const struct GUID *get_ace_object_type(const struct security_ace *ace)
+{
+ if (ace->object.object.flags & SEC_ACE_OBJECT_TYPE_PRESENT) {
+ return &ace->object.object.type.type;
+ }
+
+ return NULL;
+}
+
+/**
+ * Evaluates access rights specified in a object-specific ACE for an AD object.
+ * This logic corresponds to MS-ADTS 5.1.3.3.3 Checking Object-Specific Access.
+ * @param[in] ace - the ACE being processed
+ * @param[in/out] tree - remaining_access gets updated for the tree
+ * @param[out] grant_access - set to true if the ACE grants sufficient access
+ * rights to the object/attribute
+ * @returns NT_STATUS_OK, unless access was denied
+ */
+static NTSTATUS check_object_specific_access(const struct security_ace *ace,
+ struct object_tree *tree,
+ bool *grant_access)
+{
+ struct object_tree *node = NULL;
+ const struct GUID *type = NULL;
+
+ *grant_access = false;
+
+ /* if no tree was supplied, we can't do object-specific access checks */
+ if (!tree) {
+ return NT_STATUS_OK;
+ }
+
+ /* Get the ObjectType GUID this ACE applies to */
+ type = get_ace_object_type(ace);
+
+ /*
+ * If the ACE doesn't have a type, then apply it to the whole tree, i.e.
+ * treat 'OA' ACEs as 'A' and 'OD' as 'D'
+ */
+ if (!type) {
+ node = tree;
+ } else {
+
+ /* skip it if the ACE's ObjectType GUID is not in the tree */
+ node = get_object_tree_by_GUID(tree, type);
+ if (!node) {
+ return NT_STATUS_OK;
+ }
+ }
+
+ if (ace->type == SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT ||
+ ace->type == SEC_ACE_TYPE_ACCESS_ALLOWED_CALLBACK_OBJECT) {
+ /* apply the access rights to this node, and any children */
+ object_tree_modify_access(node, ace->access_mask);
+
+ /*
+ * Currently all nodes in the tree request the same access mask,
+ * so we can use any node to check if processing this ACE now
+ * means the requested access has been granted
+ */
+ if (node->remaining_access == 0) {
+ *grant_access = true;
+ return NT_STATUS_OK;
+ }
+
+ /*
+ * As per 5.1.3.3.4 Checking Control Access Right-Based Access,
+ * if the CONTROL_ACCESS right is present, then we can grant
+ * access and stop any further access checks
+ */
+ if (ace->access_mask & SEC_ADS_CONTROL_ACCESS) {
+ *grant_access = true;
+ return NT_STATUS_OK;
+ }
+ } else {
+
+ /* this ACE denies access to the requested object/attribute */
+ if (node->remaining_access & ace->access_mask){
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ }
+ return NT_STATUS_OK;
+}
+
+
+NTSTATUS sec_access_check_ds_implicit_owner(const struct security_descriptor *sd,
+ const struct security_token *token,
+ uint32_t access_desired,
+ uint32_t *access_granted,
+ struct object_tree *tree,
+ const struct dom_sid *replace_sid,
+ enum implicit_owner_rights implicit_owner_rights)
+{
+ uint32_t i;
+ uint32_t bits_remaining;
+
+ *access_granted = access_desired;
+ bits_remaining = access_desired;
+
+ /* handle the maximum allowed flag */
+ if (access_desired & SEC_FLAG_MAXIMUM_ALLOWED) {
+ access_desired |= access_check_max_allowed(sd, token, implicit_owner_rights);
+ access_desired &= ~SEC_FLAG_MAXIMUM_ALLOWED;
+ *access_granted = access_desired;
+ bits_remaining = access_desired;
+ }
+
+ if (access_desired & SEC_FLAG_SYSTEM_SECURITY) {
+ if (security_token_has_privilege(token, SEC_PRIV_SECURITY)) {
+ bits_remaining &= ~SEC_FLAG_SYSTEM_SECURITY;
+ } else {
+ return NT_STATUS_PRIVILEGE_NOT_HELD;
+ }
+ }
+
+ /* the owner always gets SEC_STD_WRITE_DAC and SEC_STD_READ_CONTROL */
+ if ((bits_remaining & (SEC_STD_WRITE_DAC|SEC_STD_READ_CONTROL)) &&
+ security_token_has_sid(token, sd->owner_sid)) {
+ switch (implicit_owner_rights) {
+ case IMPLICIT_OWNER_READ_CONTROL_AND_WRITE_DAC_RIGHTS:
+ bits_remaining &= ~SEC_STD_WRITE_DAC;
+ FALL_THROUGH;
+ case IMPLICIT_OWNER_READ_CONTROL_RIGHTS:
+ bits_remaining &= ~SEC_STD_READ_CONTROL;
+ break;
+ }
+ }
+
+ /* SEC_PRIV_TAKE_OWNERSHIP grants SEC_STD_WRITE_OWNER */
+ if ((bits_remaining & (SEC_STD_WRITE_OWNER)) &&
+ security_token_has_privilege(token, SEC_PRIV_TAKE_OWNERSHIP)) {
+ bits_remaining &= ~(SEC_STD_WRITE_OWNER);
+ }
+
+ /* a NULL dacl allows access */
+ if ((sd->type & SEC_DESC_DACL_PRESENT) && sd->dacl == NULL) {
+ *access_granted = access_desired;
+ return NT_STATUS_OK;
+ }
+
+ if (sd->dacl == NULL) {
+ goto done;
+ }
+
+ /* check each ace in turn. */
+ for (i=0; bits_remaining && i < sd->dacl->num_aces; i++) {
+ const struct dom_sid *trustee;
+ const struct security_ace *ace = &sd->dacl->aces[i];
+ NTSTATUS status;
+ bool grant_access = false;
+
+ if (ace->flags & SEC_ACE_FLAG_INHERIT_ONLY) {
+ continue;
+ }
+
+ if (dom_sid_equal(&ace->trustee, &global_sid_Self) && replace_sid) {
+ trustee = replace_sid;
+ } else {
+ trustee = &ace->trustee;
+ }
+
+ if (!security_token_has_sid(token, trustee)) {
+ continue;
+ }
+
+ switch (ace->type) {
+ case SEC_ACE_TYPE_ACCESS_ALLOWED:
+ if (tree) {
+ object_tree_modify_access(tree, ace->access_mask);
+ }
+
+ bits_remaining &= ~ace->access_mask;
+ break;
+ case SEC_ACE_TYPE_ACCESS_DENIED:
+ if (bits_remaining & ace->access_mask) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ break;
+ case SEC_ACE_TYPE_ACCESS_ALLOWED_CALLBACK:
+ {
+ enum ace_callback_result allow =
+ check_callback_ace_allow(ace, token, sd);
+ if (allow == ACE_CALLBACK_INVALID) {
+ return NT_STATUS_INVALID_ACE_CONDITION;
+ }
+ if (allow == ACE_CALLBACK_ALLOW) {
+ bits_remaining &= ~ace->access_mask;
+ }
+ break;
+ }
+
+ case SEC_ACE_TYPE_ACCESS_DENIED_CALLBACK:
+ {
+ enum ace_callback_result deny =
+ check_callback_ace_deny(ace, token, sd);
+ if (deny == ACE_CALLBACK_INVALID) {
+ return NT_STATUS_INVALID_ACE_CONDITION;
+ }
+ if (deny == ACE_CALLBACK_DENY) {
+ if (bits_remaining & ace->access_mask) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ }
+ break;
+ }
+
+ case SEC_ACE_TYPE_ACCESS_DENIED_OBJECT:
+ case SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT:
+ status = check_object_specific_access(ace, tree,
+ &grant_access);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (grant_access) {
+ return NT_STATUS_OK;
+ }
+ break;
+ case SEC_ACE_TYPE_ACCESS_ALLOWED_CALLBACK_OBJECT:
+ {
+ /*
+ * if the callback says ALLOW, we treat this as a
+ * SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT.
+ *
+ * Otherwise we act as if this ACE does not exist.
+ */
+ enum ace_callback_result allow =
+ check_callback_ace_allow(ace, token, sd);
+ if (allow == ACE_CALLBACK_INVALID) {
+ return NT_STATUS_INVALID_ACE_CONDITION;
+ }
+ if (allow != ACE_CALLBACK_ALLOW) {
+ break;
+ }
+
+ status = check_object_specific_access(ace, tree,
+ &grant_access);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (grant_access) {
+ return NT_STATUS_OK;
+ }
+ break;
+ }
+ case SEC_ACE_TYPE_ACCESS_DENIED_CALLBACK_OBJECT:
+ {
+ /*
+ * ACCESS_DENIED_OBJECT ACEs can't grant access --
+ * they either don't match the object and slide
+ * harmlessly past or they return
+ * NT_STATUS_ACCESS_DENIED.
+ *
+ * ACCESS_DENIED_CALLBACK_OBJECT ACEs add another way
+ * of not applying, and another way of failing.
+ */
+ enum ace_callback_result deny =
+ check_callback_ace_deny(ace, token, sd);
+ if (deny == ACE_CALLBACK_INVALID) {
+ return NT_STATUS_INVALID_ACE_CONDITION;
+ }
+ if (deny != ACE_CALLBACK_DENY) {
+ break;
+ }
+ status = check_object_specific_access(ace, tree,
+ &grant_access);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ break;
+ }
+ default: /* Other ACE types not handled/supported */
+ break;
+ }
+ }
+
+done:
+ if (bits_remaining != 0) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/**
+ * @brief Perform directoryservice (DS) related access checks for a given user
+ *
+ * Perform DS access checks for the user represented by its security_token, on
+ * the provided security descriptor. If an tree associating GUID and access
+ * required is provided then object access (OA) are checked as well. *
+ * @param[in] sd The security descriptor against which the required
+ * access are requested
+ *
+ * @param[in] token The security_token associated with the user to
+ * test
+ *
+ * @param[in] access_desired A bitfield of rights that must be granted for the
+ * given user in the specified SD.
+ *
+ * If one
+ * of the entry in the tree grants all the requested rights for the given GUID
+ * FIXME
+ * tree can be null if not null it's the
+ * Lots of code duplication, it will be united in just one
+ * function eventually */
+
+NTSTATUS sec_access_check_ds(const struct security_descriptor *sd,
+ const struct security_token *token,
+ uint32_t access_desired,
+ uint32_t *access_granted,
+ struct object_tree *tree,
+ struct dom_sid *replace_sid)
+{
+ return sec_access_check_ds_implicit_owner(sd,
+ token,
+ access_desired,
+ access_granted,
+ tree,
+ replace_sid,
+ IMPLICIT_OWNER_READ_CONTROL_RIGHTS);
+}
diff --git a/libcli/security/access_check.h b/libcli/security/access_check.h
new file mode 100644
index 0000000..7c424b9
--- /dev/null
+++ b/libcli/security/access_check.h
@@ -0,0 +1,100 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Andrew Tridgell 2004
+ Copyright (C) Gerald Carter 2005
+ Copyright (C) Volker Lendecke 2007
+ Copyright (C) Jeremy Allison 2008
+ Copyright (C) Andrew Bartlett 2010
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef _ACCESS_CHECK_H_
+#define _ACCESS_CHECK_H_
+
+#include "librpc/gen_ndr/security.h"
+
+/* Map generic access rights to object specific rights. This technique is
+ used to give meaning to assigning read, write, execute and all access to
+ objects. Each type of object has its own mapping of generic to object
+ specific access rights. */
+
+void se_map_generic(uint32_t *access_mask, const struct generic_mapping *mapping);
+
+/* Map generic access rights to object specific rights for all the ACE's
+ * in a security_acl.
+ */
+void security_acl_map_generic(struct security_acl *sa,
+ const struct generic_mapping *mapping);
+
+/* Map standard access rights to object specific rights. This technique is
+ used to give meaning to assigning read, write, execute and all access to
+ objects. Each type of object has its own mapping of standard to object
+ specific access rights. */
+void se_map_standard(uint32_t *access_mask, const struct standard_mapping *mapping);
+
+/*
+ The main entry point for access checking. If returning ACCESS_DENIED
+ this function returns the denied bits in the uint32_t pointed
+ to by the access_granted pointer.
+*/
+NTSTATUS se_access_check(const struct security_descriptor *sd,
+ const struct security_token *token,
+ uint32_t access_desired,
+ uint32_t *access_granted);
+
+/*
+ The main entry point for access checking FOR THE FILE SERVER ONLY !
+ If returning ACCESS_DENIED this function returns the denied bits in
+ the uint32_t pointed to by the access_granted pointer.
+*/
+NTSTATUS se_file_access_check(const struct security_descriptor *sd,
+ const struct security_token *token,
+ bool priv_open_requested,
+ uint32_t access_desired,
+ uint32_t *access_granted);
+
+NTSTATUS sec_access_check_ds_implicit_owner(const struct security_descriptor *sd,
+ const struct security_token *token,
+ uint32_t access_desired,
+ uint32_t *access_granted,
+ struct object_tree *tree,
+ const struct dom_sid *replace_sid,
+ enum implicit_owner_rights implicit_owner_rights);
+
+/* modified access check for the purposes of DS security
+ * Lots of code duplication, it will be united in just one
+ * function eventually */
+
+NTSTATUS sec_access_check_ds(const struct security_descriptor *sd,
+ const struct security_token *token,
+ uint32_t access_desired,
+ uint32_t *access_granted,
+ struct object_tree *tree,
+ struct dom_sid *replace_sid);
+
+bool insert_in_object_tree(TALLOC_CTX *mem_ctx,
+ const struct GUID *guid,
+ uint32_t init_access,
+ struct object_tree *root,
+ struct object_tree **new_node_out);
+
+/* search by GUID */
+struct object_tree *get_object_tree_by_GUID(struct object_tree *root,
+ const struct GUID *guid);
+
+/* Change the granted access per each ACE */
+void object_tree_modify_access(struct object_tree *root,
+ uint32_t access_mask);
+#endif
diff --git a/libcli/security/claims-conversions.c b/libcli/security/claims-conversions.c
new file mode 100644
index 0000000..ccf1375
--- /dev/null
+++ b/libcli/security/claims-conversions.c
@@ -0,0 +1,1216 @@
+/*
+ * Unix SMB implementation.
+ * Utility functions for converting between claims formats.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "replace.h"
+#include "librpc/gen_ndr/ndr_security.h"
+#include "librpc/gen_ndr/ndr_conditional_ace.h"
+#include "libcli/security/claims-conversions.h"
+#include "lib/util/debug.h"
+#include "lib/util/stable_sort.h"
+
+#include "librpc/gen_ndr/conditional_ace.h"
+#include "librpc/gen_ndr/claims.h"
+
+/*
+ * We support three formats for claims, all slightly different.
+ *
+ * 1. MS-ADTS 2.2.18.* claims sets, blobs, arrays, or whatever, which
+ * are used in the PAC.
+ *
+ * 2. MS-DTYP 2.4.10.1 CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1
+ * structures, used in security tokens and resource SACL ACEs.
+ *
+ * 3. MS-DTYP 2.4.4.17 Conditional ACE tokens.
+ *
+ * The types don't map perfectly onto each other -- in particular,
+ * Conditional ACEs don't have unsigned integer or boolean types, but
+ * do have short integer types which the other forms don't.
+ *
+ * We don't support the format used by the Win32 API function
+ * AddResourceAttributeAce(), which is called CLAIM_SECURITY_ATTRIBUTE_V1.
+ * Nobody has ever used that function in public, and the format is not used
+ * on the wire.
+ */
+
+
+static bool claim_v1_string_to_ace_string(
+ TALLOC_CTX *mem_ctx,
+ const struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim,
+ size_t offset,
+ struct ace_condition_token *result)
+{
+ char *s = talloc_strdup(mem_ctx,
+ claim->values[offset].string_value);
+ if (s == NULL) {
+ return false;
+ }
+
+ result->type = CONDITIONAL_ACE_TOKEN_UNICODE;
+ result->data.unicode.value = s;
+ return true;
+}
+
+
+static bool claim_v1_octet_string_to_ace_octet_string(
+ TALLOC_CTX *mem_ctx,
+ const struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim,
+ size_t offset,
+ struct ace_condition_token *result)
+{
+ DATA_BLOB *v = NULL;
+ DATA_BLOB w = data_blob_null;
+
+ v = claim->values[offset].octet_value;
+
+ if (v->length > CONDITIONAL_ACE_MAX_LENGTH) {
+ DBG_WARNING("claim has octet string of unexpected length %zu "
+ "(expected range 1 - %u)\n",
+ v->length, CONDITIONAL_ACE_MAX_LENGTH);
+ return false;
+ }
+ if (v->length != 0) {
+ w = data_blob_talloc(mem_ctx, v->data, v->length);
+ if (w.data == NULL) {
+ return false;
+ }
+ }
+
+ result->type = CONDITIONAL_ACE_TOKEN_OCTET_STRING;
+ result->data.bytes = w;
+ return true;
+}
+
+
+static bool blob_string_sid_to_sid(DATA_BLOB *blob,
+ struct dom_sid *sid)
+{
+ /*
+ * Resource ACE claim SIDs are stored as SID strings in
+ * CLAIM_SECURITY_ATTRIBUTE_OCTET_STRING_RELATIVE blobs. These are in
+ * ACEs, which means we don't quite know who wrote them, and it is
+ * unspecified whether the blob should contain a terminating NUL byte.
+ * Therefore we accept either form, copying into a temporary buffer if
+ * there is no '\0'. Apart from this special case, we don't accept
+ * SIDs that are shorter than the blob.
+ *
+ * It doesn't seem like SDDL short SIDs ("WD") are accepted here. This
+ * isn't SDDL.
+ */
+ bool ok;
+ size_t len = blob->length;
+ char buf[DOM_SID_STR_BUFLEN + 1]; /* 191 + 1 */
+ const char *end = NULL;
+ char *str = NULL;
+
+ if (len < 5 || len >= DOM_SID_STR_BUFLEN) {
+ return false;
+ }
+ if (blob->data[len - 1] == '\0') {
+ str = (char *)blob->data;
+ len--;
+ } else {
+ memcpy(buf, blob->data, len);
+ buf[len] = 0;
+ str = buf;
+ }
+
+ ok = dom_sid_parse_endp(str, sid, &end);
+ if (!ok) {
+ return false;
+ }
+
+ if (end - str != len) {
+ return false;
+ }
+ return true;
+}
+
+
+static bool claim_v1_sid_to_ace_sid(
+ const struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim,
+ size_t offset,
+ struct ace_condition_token *result)
+{
+ /*
+ * In the _V1 struct, SIDs are stored as octet string blobs,
+ * as *SID strings*.
+ *
+ * In the conditional ACE they are stored as struct dom_sid.
+ *
+ * There are no SIDs in ADTS claims, but there can be in
+ * resource ACEs.
+ */
+ DATA_BLOB *v = NULL;
+ bool ok;
+
+ v = claim->values[offset].sid_value;
+
+ ok = blob_string_sid_to_sid(v, &result->data.sid.sid);
+ if (! ok) {
+ DBG_WARNING("claim has invalid SID string of length %zu.\n",
+ v->length);
+ return false;
+ }
+
+ result->type = CONDITIONAL_ACE_TOKEN_SID;
+ return true;
+}
+
+
+static bool claim_v1_int_to_ace_int(
+ const struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim,
+ size_t offset,
+ struct ace_condition_token *result)
+{
+ int64_t v = *claim->values[offset].int_value;
+ result->type = CONDITIONAL_ACE_TOKEN_INT64;
+ result->data.int64.base = CONDITIONAL_ACE_INT_BASE_10;
+ result->data.int64.value = v;
+
+ /*
+ * The sign flag (and the base flag above) determines how the
+ * ACE token will be displayed if converted to SDDL. These
+ * values are not likely to end up as SDDL, but we might as
+ * well get it right. A negative flag means it will be
+ * displayed with a minus sign, and a positive flag means a
+ * plus sign is shown. The none flag means no + or -.
+ */
+ if (v < 0) {
+ result->data.int64.sign = CONDITIONAL_ACE_INT_SIGN_NEGATIVE;
+ } else {
+ result->data.int64.sign = CONDITIONAL_ACE_INT_SIGN_NONE;
+ }
+
+ return true;
+}
+
+
+static bool claim_v1_unsigned_int_to_ace_int(
+ const struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim,
+ size_t offset,
+ struct ace_condition_token *result)
+{
+ uint64_t v = *claim->values[offset].uint_value;
+ if (v > INT64_MAX) {
+ /*
+ * The unsigned value can't be represented in a
+ * conditional ACE type.
+ *
+ * XXX or can it? does the positive flag make it
+ * unsigned?
+ */
+ return false;
+ }
+ result->type = CONDITIONAL_ACE_TOKEN_INT64;
+ result->data.int64.base = CONDITIONAL_ACE_INT_BASE_10;
+ result->data.int64.sign = CONDITIONAL_ACE_INT_SIGN_POSITIVE;
+ result->data.int64.value = v;
+ return true;
+}
+
+
+static bool claim_v1_bool_to_ace_int(
+ const struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim,
+ size_t offset,
+ struct ace_condition_token *result)
+{
+ uint64_t v = *claim->values[offset].uint_value;
+ result->type = CONDITIONAL_ACE_TOKEN_INT64;
+ result->data.int64.base = CONDITIONAL_ACE_INT_BASE_10;
+ result->data.int64.sign = CONDITIONAL_ACE_INT_SIGN_NONE;
+ result->data.int64.value = v ? 1 : 0;
+ return true;
+}
+
+
+static bool claim_v1_offset_to_ace_token(
+ TALLOC_CTX *mem_ctx,
+ const struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim,
+ size_t offset,
+ struct ace_condition_token *result)
+{
+ /*
+ * A claim structure has an array of claims of a certain type,
+ * and this converts a single one into a conditional ACE token.
+ *
+ * For example, if offset is 3, claim->values[3] will be
+ * turned into *result.
+ *
+ * conditional ace token will have flags to indicate that it
+ * comes from a claim attribute, and whether or not that
+ * attribute should be compared case-sensitively (only
+ * affecting unicode strings).
+ *
+ * The CLAIM_SECURITY_ATTRIBUTE_CASE_SENSITIVE (from the
+ * claim_flags enum in security.idl) is used for both.
+ */
+ uint8_t f = claim->flags & CLAIM_SECURITY_ATTRIBUTE_VALUE_CASE_SENSITIVE;
+ result->flags = f | CONDITIONAL_ACE_FLAG_TOKEN_FROM_ATTR;
+
+ if (claim->values[offset].int_value == NULL) {
+ return false;
+ }
+ switch (claim->value_type) {
+ case CLAIM_SECURITY_ATTRIBUTE_TYPE_INT64:
+ return claim_v1_int_to_ace_int(claim, offset, result);
+ case CLAIM_SECURITY_ATTRIBUTE_TYPE_UINT64:
+ return claim_v1_unsigned_int_to_ace_int(claim, offset, result);
+ case CLAIM_SECURITY_ATTRIBUTE_TYPE_STRING:
+ return claim_v1_string_to_ace_string(mem_ctx, claim, offset,
+ result);
+ case CLAIM_SECURITY_ATTRIBUTE_TYPE_SID:
+ return claim_v1_sid_to_ace_sid(claim, offset, result);
+ case CLAIM_SECURITY_ATTRIBUTE_TYPE_BOOLEAN:
+ return claim_v1_bool_to_ace_int(claim, offset, result);
+ case CLAIM_SECURITY_ATTRIBUTE_TYPE_OCTET_STRING:
+ return claim_v1_octet_string_to_ace_octet_string(mem_ctx,
+ claim,
+ offset,
+ result);
+ default:
+ return false;
+ }
+}
+
+
+static bool claim_v1_copy(
+ TALLOC_CTX *mem_ctx,
+ struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *dest,
+ const struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *src);
+
+
+
+bool claim_v1_to_ace_composite_unchecked(
+ TALLOC_CTX *mem_ctx,
+ const struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim,
+ struct ace_condition_token *result)
+{
+ /*
+ * This converts a claim object into a conditional ACE
+ * composite without checking whether it is a valid and sorted
+ * claim. It is called in two places:
+ *
+ * 1. claim_v1_to_ace_token() below (which does do those
+ * checks, and is the function you want).
+ *
+ * 2. sddl_resource_attr_from_claim() in which a resource
+ * attribute claim needs to pass through a conditional ACE
+ * composite structure on its way to becoming SDDL. In that
+ * case we don't want to check validity.
+ */
+ size_t i;
+ struct ace_condition_token *tokens = NULL;
+ bool ok;
+
+ tokens = talloc_array(mem_ctx,
+ struct ace_condition_token,
+ claim->value_count);
+ if (tokens == NULL) {
+ return false;
+ }
+
+ for (i = 0; i < claim->value_count; i++) {
+ ok = claim_v1_offset_to_ace_token(tokens,
+ claim,
+ i,
+ &tokens[i]);
+ if (! ok) {
+ TALLOC_FREE(tokens);
+ return false;
+ }
+ }
+
+ result->type = CONDITIONAL_ACE_TOKEN_COMPOSITE;
+ result->data.composite.tokens = tokens;
+ result->data.composite.n_members = claim->value_count;
+ result->flags = claim->flags & CLAIM_SECURITY_ATTRIBUTE_VALUE_CASE_SENSITIVE;
+ return true;
+}
+
+
+bool claim_v1_to_ace_token(TALLOC_CTX *mem_ctx,
+ const struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim,
+ struct ace_condition_token *result)
+{
+ struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim_copy = NULL;
+ const struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *sorted_claim = NULL;
+ NTSTATUS status;
+ bool ok;
+ bool case_sensitive = claim->flags & \
+ CLAIM_SECURITY_ATTRIBUTE_VALUE_CASE_SENSITIVE;
+
+ if (claim->value_count < 1 ||
+ claim->value_count >= CONDITIONAL_ACE_MAX_TOKENS) {
+ DBG_WARNING("rejecting claim with %"PRIu32" tokens\n",
+ claim->value_count);
+ return false;
+ }
+ /*
+ * if there is one, we return a single thing of that type; if
+ * there are many, we return a composite.
+ */
+
+ if (claim->value_count == 1) {
+ return claim_v1_offset_to_ace_token(mem_ctx,
+ claim,
+ 0,
+ result);
+ }
+
+ if (claim->flags & CLAIM_SECURITY_ATTRIBUTE_UNIQUE_AND_SORTED) {
+ /*
+ * We can avoid making a sorted copy.
+ *
+ * This is normal case for wire claims, where the
+ * sorting and duplicate checking happens earlier in
+ * token_claims_to_claims_v1().
+ */
+ sorted_claim = claim;
+ } else {
+ /*
+ * This is presumably a resource attribute ACE, which
+ * is stored in the ACE as struct
+ * CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1, and we don't
+ * really want to mutate that copy -- even if there
+ * aren't currently realistic pathways that read an
+ * ACE, trigger this, and write it back (outside of
+ * tests).
+ */
+ claim_copy = talloc(mem_ctx, struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1);
+ if (claim_copy == NULL) {
+ return false;
+ }
+
+ ok = claim_v1_copy(claim_copy, claim_copy, claim);
+ if (!ok) {
+ TALLOC_FREE(claim_copy);
+ return false;
+ }
+
+ status = claim_v1_check_and_sort(claim_copy, claim_copy,
+ case_sensitive);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_WARNING("resource attribute claim sort failed with %s\n",
+ nt_errstr(status));
+ TALLOC_FREE(claim_copy);
+ return false;
+ }
+ sorted_claim = claim_copy;
+ }
+ ok = claim_v1_to_ace_composite_unchecked(mem_ctx, sorted_claim, result);
+ if (! ok) {
+ TALLOC_FREE(claim_copy);
+ return false;
+ }
+
+ /*
+ * The multiple values will get turned into a composite
+ * literal in the conditional ACE. Each element of the
+ * composite will have flags set by
+ * claim_v1_offset_to_ace_token(), but they also need to be
+ * set here (at least the _FROM_ATTR flag) or the child values
+ * will not be reached.
+ */
+ result->flags |= (
+ CONDITIONAL_ACE_FLAG_TOKEN_FROM_ATTR |
+ CLAIM_SECURITY_ATTRIBUTE_UNIQUE_AND_SORTED);
+
+ return true;
+}
+
+
+
+static bool ace_int_to_claim_v1_int(TALLOC_CTX *mem_ctx,
+ const struct ace_condition_token *tok,
+ struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim,
+ size_t offset)
+{
+ int64_t *v = talloc(mem_ctx, int64_t);
+ if (v == NULL) {
+ return false;
+ }
+ *v = tok->data.int64.value;
+ claim->values[offset].int_value = v;
+ return true;
+}
+
+
+static bool ace_string_to_claim_v1_string(TALLOC_CTX *mem_ctx,
+ const struct ace_condition_token *tok,
+ struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim,
+ size_t offset)
+{
+ const char *s = talloc_strdup(mem_ctx,
+ tok->data.unicode.value);
+ if (s == NULL) {
+ return false;
+ }
+ claim->values[offset].string_value = s;
+ return true;
+
+}
+
+
+static bool ace_sid_to_claim_v1_sid(TALLOC_CTX *mem_ctx,
+ const struct ace_condition_token *tok,
+ struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim,
+ size_t offset)
+{
+ /* claim_v1 sid is an "S-1-*" string data blob, not struct dom_sid. */
+ char *s = NULL;
+
+ DATA_BLOB *blob = NULL;
+ blob = talloc(mem_ctx, DATA_BLOB);
+ if (blob == NULL) {
+ return false;
+ }
+ s = dom_sid_string(blob, &tok->data.sid.sid);
+ if (s == NULL) {
+ TALLOC_FREE(blob);
+ return false;
+ }
+ *blob = data_blob_string_const(s);
+ claim->values[offset].sid_value = blob;
+ return true;
+}
+
+static bool ace_octet_string_to_claim_v1_octet_string(
+ TALLOC_CTX *mem_ctx,
+ const struct ace_condition_token *tok,
+ struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim,
+ size_t offset)
+{
+ DATA_BLOB *v = talloc(mem_ctx, DATA_BLOB);
+ if (v == NULL) {
+ return false;
+ }
+
+ *v = data_blob_talloc(v,
+ tok->data.bytes.data,
+ tok->data.bytes.length);
+ if (v->data == NULL) {
+ return false;
+ }
+
+ claim->values[offset].octet_value = v;
+ return true;
+}
+
+
+
+static bool ace_token_to_claim_v1_offset(TALLOC_CTX *mem_ctx,
+ const struct ace_condition_token *tok,
+ struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim,
+ size_t offset)
+{
+ /*
+ * A claim structure has an array of claims of a certain type,
+ * and this converts a single one into a conditional ACE token.
+ *
+ * For example, if offset is 3, claim->values[3] will be
+ * turned into *result.
+ */
+ if (offset >= claim->value_count) {
+ return false;
+ }
+ switch (claim->value_type) {
+ case CLAIM_SECURITY_ATTRIBUTE_TYPE_INT64:
+ case CLAIM_SECURITY_ATTRIBUTE_TYPE_UINT64:
+ return ace_int_to_claim_v1_int(mem_ctx, tok, claim, offset);
+ case CLAIM_SECURITY_ATTRIBUTE_TYPE_STRING:
+ return ace_string_to_claim_v1_string(mem_ctx, tok, claim, offset);
+ case CLAIM_SECURITY_ATTRIBUTE_TYPE_SID:
+ return ace_sid_to_claim_v1_sid(mem_ctx, tok, claim, offset);
+ case CLAIM_SECURITY_ATTRIBUTE_TYPE_OCTET_STRING:
+ return ace_octet_string_to_claim_v1_octet_string(mem_ctx,
+ tok,
+ claim,
+ offset);
+ default:
+ /*bool unimplemented, because unreachable */
+ return false;
+ }
+}
+
+
+bool ace_token_to_claim_v1(TALLOC_CTX *mem_ctx,
+ const char *name,
+ const struct ace_condition_token *tok,
+ struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 **claim,
+ uint32_t flags)
+{
+ size_t i;
+ bool ok;
+ bool is_comp = false;
+ int claim_type = -1;
+ struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *_claim = NULL;
+ uint32_t value_count;
+
+ if (name == NULL || claim == NULL || tok == NULL) {
+ return false;
+ }
+ *claim = NULL;
+
+ if (tok->type == CONDITIONAL_ACE_TOKEN_COMPOSITE) {
+ is_comp = true;
+ /* there must be values, all of the same type */
+ if (tok->data.composite.n_members == 0) {
+ DBG_WARNING("Empty ACE composite list\n");
+ return false;
+ }
+ if (tok->data.composite.n_members > 1) {
+ for (i = 1; i < tok->data.composite.n_members; i++) {
+ if (tok->data.composite.tokens[i].type !=
+ tok->data.composite.tokens[0].type) {
+ DBG_WARNING(
+ "ACE composite list has varying "
+ "types (at least %u and %u)\n",
+ tok->data.composite.tokens[i].type,
+ tok->data.composite.tokens[0].type);
+ return false;
+ }
+ }
+ }
+ value_count = tok->data.composite.n_members;
+
+ switch (tok->data.composite.tokens[0].type) {
+ case CONDITIONAL_ACE_TOKEN_INT8:
+ case CONDITIONAL_ACE_TOKEN_INT16:
+ case CONDITIONAL_ACE_TOKEN_INT32:
+ case CONDITIONAL_ACE_TOKEN_INT64:
+ claim_type = CLAIM_SECURITY_ATTRIBUTE_TYPE_INT64;
+ break;
+ case CONDITIONAL_ACE_TOKEN_UNICODE:
+ claim_type = CLAIM_SECURITY_ATTRIBUTE_TYPE_STRING;
+ break;
+ case CONDITIONAL_ACE_TOKEN_OCTET_STRING:
+ claim_type = CLAIM_SECURITY_ATTRIBUTE_TYPE_OCTET_STRING;
+ break;
+ case CONDITIONAL_ACE_TOKEN_SID:
+ claim_type = CLAIM_SECURITY_ATTRIBUTE_TYPE_SID;
+ break;
+ default:
+ /* reject nested composites, no uint or bool. */
+ DBG_WARNING("ACE composite list has invalid type %u\n",
+ tok->data.composite.tokens[0].type);
+ return false;
+ }
+ } else {
+ value_count = 1;
+ switch(tok->type) {
+ case CONDITIONAL_ACE_TOKEN_INT8:
+ case CONDITIONAL_ACE_TOKEN_INT16:
+ case CONDITIONAL_ACE_TOKEN_INT32:
+ case CONDITIONAL_ACE_TOKEN_INT64:
+ claim_type = CLAIM_SECURITY_ATTRIBUTE_TYPE_INT64;
+ break;
+ case CONDITIONAL_ACE_TOKEN_UNICODE:
+ claim_type = CLAIM_SECURITY_ATTRIBUTE_TYPE_STRING;
+ break;
+ case CONDITIONAL_ACE_TOKEN_OCTET_STRING:
+ claim_type = CLAIM_SECURITY_ATTRIBUTE_TYPE_OCTET_STRING;
+ break;
+ case CONDITIONAL_ACE_TOKEN_SID:
+ claim_type = CLAIM_SECURITY_ATTRIBUTE_TYPE_SID;
+ break;
+ default:
+ /*
+ * no way of creating bool or uint values,
+ * composite is handled above.
+ */
+ DBG_WARNING("ACE token has invalid type %u\n",
+ tok->data.composite.tokens[0].type);
+ return false;
+ }
+ }
+
+ _claim = talloc(mem_ctx, struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1);
+ if (_claim == NULL) {
+ return false;
+ }
+
+ _claim->value_count = value_count;
+ _claim->value_type = claim_type;
+ _claim->flags = flags;
+ _claim->name = talloc_strdup(mem_ctx, name);
+ if (_claim->name == NULL) {
+ TALLOC_FREE(_claim);
+ return false;
+ }
+ /*
+ * The values array is actually an array of pointers to
+ * values, even when the values are ints or bools.
+ */
+ _claim->values = talloc_array(_claim, union claim_values, value_count);
+ if (_claim->values == NULL) {
+ TALLOC_FREE(_claim);
+ return false;
+ }
+ if (! is_comp) {
+ /* there is one value, not a list */
+ ok = ace_token_to_claim_v1_offset(_claim,
+ tok,
+ _claim,
+ 0);
+ if (! ok) {
+ TALLOC_FREE(_claim);
+ return false;
+ }
+ } else {
+ /* a composite list of values */
+ for (i = 0; i < value_count; i++) {
+ struct ace_condition_token *t = &tok->data.composite.tokens[i];
+ ok = ace_token_to_claim_v1_offset(mem_ctx,
+ t,
+ _claim,
+ i);
+ if (! ok) {
+ TALLOC_FREE(_claim);
+ return false;
+ }
+ }
+ }
+
+
+ if (_claim->value_type == CLAIM_SECURITY_ATTRIBUTE_TYPE_INT64) {
+ /*
+ * Conditional ACE tokens don't have a UINT type but
+ * claims do. Windows tends to use UINT types in
+ * claims when it can, so so do we.
+ */
+ bool could_be_uint = true;
+ for (i = 0; i < value_count; i++) {
+ if (*_claim->values[i].int_value < 0) {
+ could_be_uint = false;
+ break;
+ }
+ }
+ if (could_be_uint) {
+ _claim->value_type = CLAIM_SECURITY_ATTRIBUTE_TYPE_UINT64;
+ }
+ }
+
+ *claim = _claim;
+ return true;
+}
+
+
+
+static bool claim_v1_copy(
+ TALLOC_CTX *mem_ctx,
+ struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *dest,
+ const struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *src)
+{
+ DATA_BLOB blob = {0};
+ enum ndr_err_code ndr_err;
+
+ /*
+ * FIXME, could be more efficient! but copying these
+ * structures is fiddly, and it might be worth coming up
+ * with a better API for adding claims.
+ */
+
+ ndr_err = ndr_push_struct_blob(
+ &blob, mem_ctx, src,
+ (ndr_push_flags_fn_t)ndr_push_CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1);
+
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return false;
+ }
+
+ ndr_err = ndr_pull_struct_blob(
+ &blob, mem_ctx, dest,
+ (ndr_pull_flags_fn_t)ndr_pull_CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1);
+
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ TALLOC_FREE(blob.data);
+ return false;
+ }
+ TALLOC_FREE(blob.data);
+ return true;
+}
+
+
+
+bool add_claim_to_token(TALLOC_CTX *mem_ctx,
+ struct security_token *token,
+ const struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim,
+ const char *claim_type)
+{
+ struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *tmp = NULL;
+ NTSTATUS status;
+ uint32_t *n = NULL;
+ bool ok;
+ struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 **list = NULL;
+ if (strcmp(claim_type, "device") == 0) {
+ n = &token->num_device_claims;
+ list = &token->device_claims;
+ } else if (strcmp(claim_type, "local") == 0) {
+ n = &token->num_local_claims;
+ list = &token->local_claims;
+ } else if (strcmp(claim_type, "user") == 0) {
+ n = &token->num_user_claims;
+ list = &token->user_claims;
+ } else {
+ return false;
+ }
+ if ((*n) == UINT32_MAX) {
+ return false;
+ }
+
+ tmp = talloc_realloc(mem_ctx,
+ *list,
+ struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1,
+ (*n) + 1);
+ if (tmp == NULL) {
+ return false;
+ }
+
+ ok = claim_v1_copy(mem_ctx, &tmp[*n], claim);
+ if (! ok ) {
+ TALLOC_FREE(tmp);
+ return false;
+ }
+
+ status = claim_v1_check_and_sort(tmp, &tmp[*n],
+ claim->flags & CLAIM_SECURITY_ATTRIBUTE_VALUE_CASE_SENSITIVE);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_WARNING("resource attribute claim sort failed with %s\n",
+ nt_errstr(status));
+ TALLOC_FREE(tmp);
+ return false;
+ }
+
+ (*n)++;
+ *list = tmp;
+ return true;
+}
+
+
+static NTSTATUS claim_v1_check_and_sort_boolean(
+ TALLOC_CTX *mem_ctx,
+ struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim)
+{
+ /*
+ * There are so few valid orders in a boolean claim that we can
+ * enumerate them all.
+ */
+ switch (claim->value_count) {
+ case 0:
+ return NT_STATUS_OK;
+ case 1:
+ if (*claim->values[0].uint_value == 0 ||
+ *claim->values[0].uint_value == 1) {
+ return NT_STATUS_OK;
+ }
+ break;
+ case 2:
+ if (*claim->values[0].uint_value == 1) {
+ /* switch the order. */
+ *claim->values[0].uint_value = *claim->values[1].uint_value;
+ *claim->values[1].uint_value = 1;
+ }
+ if (*claim->values[0].uint_value == 0 &&
+ *claim->values[1].uint_value == 1) {
+ return NT_STATUS_OK;
+ }
+ break;
+ default:
+ /* 3 or more must have duplicates. */
+ break;
+ }
+ return NT_STATUS_INVALID_PARAMETER;
+}
+
+
+struct claim_sort_context {
+ uint16_t value_type;
+ bool failed;
+ bool case_sensitive;
+};
+
+static int claim_sort_cmp(const union claim_values *lhs,
+ const union claim_values *rhs,
+ struct claim_sort_context *ctx)
+{
+ /*
+ * These comparisons have to match those used in
+ * conditional_ace.c.
+ */
+ int cmp;
+
+ switch (ctx->value_type) {
+ case CLAIM_SECURITY_ATTRIBUTE_TYPE_INT64:
+ case CLAIM_SECURITY_ATTRIBUTE_TYPE_UINT64:
+ {
+ /*
+ * We sort as signed integers, even for uint64,
+ * because a) we don't actually care about the true
+ * order, just uniqueness, and b) the conditional ACEs
+ * only know of signed values.
+ */
+ int64_t a, b;
+ if (ctx->value_type == CLAIM_SECURITY_ATTRIBUTE_TYPE_INT64) {
+ a = *lhs->int_value;
+ b = *rhs->int_value;
+ } else {
+ a = (int64_t)*lhs->uint_value;
+ b = (int64_t)*rhs->uint_value;
+ }
+ if (a < b) {
+ return -1;
+ }
+ if (a == b) {
+ return 0;
+ }
+ return 1;
+ }
+ case CLAIM_SECURITY_ATTRIBUTE_TYPE_STRING:
+ {
+ const char *a = lhs->string_value;
+ const char *b = rhs->string_value;
+ if (ctx->case_sensitive) {
+ return strcmp(a, b);
+ }
+ return strcasecmp_m(a, b);
+ }
+
+ case CLAIM_SECURITY_ATTRIBUTE_TYPE_SID:
+ {
+ /*
+ * The blobs in a claim are "S-1-.." strings, not struct
+ * dom_sid as used in conditional ACEs, and to sort them the
+ * same as ACEs we need to make temporary structs.
+ *
+ * We don't accept SID claims over the wire -- these
+ * are resource attribute ACEs only.
+ */
+ struct dom_sid a, b;
+ bool lhs_ok, rhs_ok;
+
+ lhs_ok = blob_string_sid_to_sid(lhs->sid_value, &a);
+ rhs_ok = blob_string_sid_to_sid(rhs->sid_value, &b);
+ if (!(lhs_ok && rhs_ok)) {
+ ctx->failed = true;
+ return -1;
+ }
+ cmp = dom_sid_compare(&a, &b);
+ return cmp;
+ }
+ case CLAIM_SECURITY_ATTRIBUTE_TYPE_OCTET_STRING:
+ {
+ const DATA_BLOB *a = lhs->octet_value;
+ const DATA_BLOB *b = rhs->octet_value;
+ return data_blob_cmp(a, b);
+ }
+ default:
+ ctx->failed = true;
+ break;
+ }
+ return -1;
+}
+
+
+NTSTATUS claim_v1_check_and_sort(TALLOC_CTX *mem_ctx,
+ struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim,
+ bool case_sensitive)
+{
+ bool ok;
+ uint32_t i;
+ struct claim_sort_context sort_ctx = {
+ .failed = false,
+ .value_type = claim->value_type,
+ .case_sensitive = case_sensitive
+ };
+
+ /*
+ * It could be that the values array contains a NULL pointer, in which
+ * case we don't need to worry about what type it is.
+ */
+ for (i = 0; i < claim->value_count; i++) {
+ if (claim->values[i].int_value == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ }
+
+ if (claim->value_type == CLAIM_SECURITY_ATTRIBUTE_TYPE_BOOLEAN) {
+ NTSTATUS status = claim_v1_check_and_sort_boolean(mem_ctx, claim);
+ if (NT_STATUS_IS_OK(status)) {
+ claim->flags |= CLAIM_SECURITY_ATTRIBUTE_UNIQUE_AND_SORTED;
+ }
+ return status;
+ }
+
+ ok = stable_sort_talloc_r(mem_ctx,
+ claim->values,
+ claim->value_count,
+ sizeof(union claim_values),
+ (samba_compare_with_context_fn_t)claim_sort_cmp,
+ &sort_ctx);
+ if (!ok) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (sort_ctx.failed) {
+ /* this failure probably means a bad SID string */
+ DBG_WARNING("claim sort of %"PRIu32" members, type %"PRIu16" failed\n",
+ claim->value_count,
+ claim->value_type);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ for (i = 1; i < claim->value_count; i++) {
+ int cmp = claim_sort_cmp(&claim->values[i - 1],
+ &claim->values[i],
+ &sort_ctx);
+ if (cmp == 0) {
+ DBG_WARNING("duplicate values in claim\n");
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ if (cmp > 0) {
+ DBG_ERR("claim sort failed!\n");
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ }
+ if (case_sensitive) {
+ claim->flags |= CLAIM_SECURITY_ATTRIBUTE_VALUE_CASE_SENSITIVE;
+ }
+ claim->flags |= CLAIM_SECURITY_ATTRIBUTE_UNIQUE_AND_SORTED;
+ return NT_STATUS_OK;
+}
+
+
+NTSTATUS token_claims_to_claims_v1(TALLOC_CTX *mem_ctx,
+ const struct CLAIMS_SET *claims_set,
+ struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 **out_claims,
+ uint32_t *out_n_claims)
+{
+ struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claims = NULL;
+ uint32_t n_claims = 0;
+ uint32_t expected_n_claims = 0;
+ uint32_t i;
+ NTSTATUS status;
+
+ if (out_claims == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ if (out_n_claims == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ *out_claims = NULL;
+ *out_n_claims = 0;
+
+ if (claims_set == NULL) {
+ return NT_STATUS_OK;
+ }
+
+ /*
+ * The outgoing number of claims is (at most) the sum of the
+ * claims_counts of each claims_array.
+ */
+ for (i = 0; i < claims_set->claims_array_count; ++i) {
+ uint32_t count = claims_set->claims_arrays[i].claims_count;
+ expected_n_claims += count;
+ if (expected_n_claims < count) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ }
+
+ claims = talloc_array(mem_ctx,
+ struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1,
+ expected_n_claims);
+ if (claims == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i = 0; i < claims_set->claims_array_count; ++i) {
+ const struct CLAIMS_ARRAY *claims_array = &claims_set->claims_arrays[i];
+ uint32_t j;
+
+ switch (claims_array->claims_source_type) {
+ case CLAIMS_SOURCE_TYPE_AD:
+ case CLAIMS_SOURCE_TYPE_CERTIFICATE:
+ break;
+ default:
+ /* Ignore any claims of a type we don’t recognize. */
+ continue;
+ }
+
+ for (j = 0; j < claims_array->claims_count; ++j) {
+ const struct CLAIM_ENTRY *claim_entry = &claims_array->claim_entries[j];
+ const char *name = NULL;
+ union claim_values *claim_values = NULL;
+ uint32_t n_values;
+ enum security_claim_value_type value_type;
+
+ switch (claim_entry->type) {
+ case CLAIM_TYPE_INT64:
+ {
+ const struct CLAIM_INT64 *values = &claim_entry->values.claim_int64;
+ uint32_t k;
+ int64_t *claim_values_int64 = NULL;
+
+ n_values = values->value_count;
+ value_type = CLAIM_SECURITY_ATTRIBUTE_TYPE_INT64;
+
+ claim_values = talloc_array(claims,
+ union claim_values,
+ n_values);
+ if (claim_values == NULL) {
+ talloc_free(claims);
+ return NT_STATUS_NO_MEMORY;
+ }
+ claim_values_int64 = talloc_array(claims,
+ int64_t,
+ n_values);
+ if (claim_values_int64 == NULL) {
+ talloc_free(claims);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (k = 0; k < n_values; ++k) {
+ claim_values_int64[k] = values->values[k];
+ claim_values[k].int_value = &claim_values_int64[k];
+ }
+
+ break;
+ }
+ case CLAIM_TYPE_UINT64:
+ case CLAIM_TYPE_BOOLEAN:
+ {
+ const struct CLAIM_UINT64 *values = &claim_entry->values.claim_uint64;
+ uint32_t k;
+ uint64_t *claim_values_uint64 = NULL;
+
+ n_values = values->value_count;
+ value_type = (claim_entry->type == CLAIM_TYPE_UINT64)
+ ? CLAIM_SECURITY_ATTRIBUTE_TYPE_UINT64
+ : CLAIM_SECURITY_ATTRIBUTE_TYPE_BOOLEAN;
+
+ claim_values = talloc_array(claims,
+ union claim_values,
+ n_values);
+ if (claim_values == NULL) {
+ talloc_free(claims);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ claim_values_uint64 = talloc_array(claims,
+ uint64_t,
+ n_values);
+ if (claim_values_uint64 == NULL) {
+ talloc_free(claims);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (k = 0; k < n_values; ++k) {
+ claim_values_uint64[k] = values->values[k];
+ claim_values[k].uint_value = &claim_values_uint64[k];
+ }
+
+ break;
+ }
+ case CLAIM_TYPE_STRING:
+ {
+ const struct CLAIM_STRING *values = &claim_entry->values.claim_string;
+ uint32_t k, m;
+ bool seen_empty = false;
+ n_values = values->value_count;
+ value_type = CLAIM_SECURITY_ATTRIBUTE_TYPE_STRING;
+
+ claim_values = talloc_array(claims,
+ union claim_values,
+ n_values);
+ if (claim_values == NULL) {
+ talloc_free(claims);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ m = 0;
+ for (k = 0; k < n_values; ++k) {
+ const char *string_value = NULL;
+
+ if (values->values[k] != NULL) {
+ string_value = talloc_strdup(claim_values, values->values[k]);
+ if (string_value == NULL) {
+ talloc_free(claims);
+ return NT_STATUS_NO_MEMORY;
+ }
+ claim_values[m].string_value = string_value;
+ m++;
+ } else {
+ /*
+ * We allow one NULL string
+ * per claim, but not two,
+ * because two would be a
+ * duplicate, and we don't
+ * want those (duplicates in
+ * actual values are checked
+ * later).
+ */
+ if (seen_empty) {
+ talloc_free(claims);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ seen_empty = true;
+ }
+ }
+ n_values = m;
+ break;
+ }
+ default:
+ /*
+ * Other claim types are unsupported — just skip
+ * them.
+ */
+ continue;
+ }
+
+ if (claim_entry->id != NULL) {
+ name = talloc_strdup(claims, claim_entry->id);
+ if (name == NULL) {
+ talloc_free(claims);
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ claims[n_claims] = (struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1) {
+ .name = name,
+ .value_type = value_type,
+ .flags = 0,
+ .value_count = n_values,
+ .values = claim_values,
+ };
+
+ status = claim_v1_check_and_sort(claims, &claims[n_claims],
+ false);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(claims);
+ DBG_WARNING("claim sort and uniqueness test failed with %s\n",
+ nt_errstr(status));
+ return status;
+ }
+ n_claims++;
+ }
+ }
+ *out_claims = claims;
+ *out_n_claims = n_claims;
+
+ return NT_STATUS_OK;
+}
diff --git a/libcli/security/claims-conversions.h b/libcli/security/claims-conversions.h
new file mode 100644
index 0000000..78d8e91
--- /dev/null
+++ b/libcli/security/claims-conversions.h
@@ -0,0 +1,60 @@
+/*
+ * Unix SMB implementation.
+ * Utility functions for converting between claims formats.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef LIBCLI_SECURITY_CLAIMS_CONVERSIONS_H
+#define LIBCLI_SECURITY_CLAIMS_CONVERSIONS_H
+
+#include "replace.h"
+#include <talloc.h>
+#include "libcli/util/ntstatus.h"
+
+struct CLAIMS_SET;
+struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1;
+struct ace_condition_token;
+struct security_token;
+
+bool claim_v1_to_ace_token(TALLOC_CTX *mem_ctx,
+ const struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim,
+ struct ace_condition_token *result);
+
+bool ace_token_to_claim_v1(TALLOC_CTX *mem_ctx,
+ const char *name,
+ const struct ace_condition_token *tok,
+ struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 **claim,
+ uint32_t flags);
+
+bool add_claim_to_token(TALLOC_CTX *mem_ctx,
+ struct security_token *token,
+ const struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim,
+ const char *claim_type);
+
+NTSTATUS token_claims_to_claims_v1(TALLOC_CTX *mem_ctx,
+ const struct CLAIMS_SET *claims_set,
+ struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 **out_claims,
+ uint32_t *out_n_claims);
+
+bool claim_v1_to_ace_composite_unchecked(TALLOC_CTX *mem_ctx,
+ const struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim,
+ struct ace_condition_token *result);
+
+NTSTATUS claim_v1_check_and_sort(
+ TALLOC_CTX *mem_ctx,
+ struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim,
+ bool case_sensitive);
+
+#endif /* LIBCLI_SECURITY_CLAIMS_CONVERSIONS_H */
diff --git a/libcli/security/conditional_ace.c b/libcli/security/conditional_ace.c
new file mode 100644
index 0000000..158c8ec
--- /dev/null
+++ b/libcli/security/conditional_ace.c
@@ -0,0 +1,2550 @@
+/*
+ * Unix SMB implementation.
+ * Functions for understanding conditional ACEs
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "replace.h"
+#include "librpc/gen_ndr/ndr_security.h"
+#include "librpc/gen_ndr/ndr_conditional_ace.h"
+#include "librpc/gen_ndr/conditional_ace.h"
+#include "libcli/security/security.h"
+#include "libcli/security/conditional_ace.h"
+#include "libcli/security/claims-conversions.h"
+#include "lib/util/tsort.h"
+#include "lib/util/debug.h"
+#include "lib/util/bytearray.h"
+#include "lib/util/talloc_stack.h"
+#include "util/discard.h"
+#include "lib/util/stable_sort.h"
+/*
+ * Conditional ACE logic truth tables.
+ *
+ * Conditional ACES use a ternary logic, with "unknown" as well as true and
+ * false. The ultimate meaning of unknown depends on the context; in a deny
+ * ace, unknown means yes, in an allow ace, unknown means no. That is, we
+ * treat unknown results with maximum suspicion.
+ *
+ * AND true false unknown
+ * true T F ?
+ * false F F F
+ * unknown ? F ?
+ *
+ * OR true false unknown
+ * true T T T
+ * false T F ?
+ * unknown T ? ?
+ *
+ * NOT
+ * true F
+ * false T
+ * unknown ?
+ *
+ * This can be summed up by saying unknown values taint the result except in
+ * the cases where short circuit evaluation could apply (true OR anything,
+ * false AND anything, which hold their value).
+ *
+ * What counts as unknown
+ *
+ * - NULL attributes.
+ * - certain comparisons between incompatible types
+ *
+ * What counts as false
+ *
+ * - zero
+ * - empty strings
+ *
+ * An error means the entire expression is unknown.
+ */
+
+
+static bool check_integer_range(const struct ace_condition_token *tok)
+{
+ int64_t val = tok->data.int64.value;
+ switch (tok->type) {
+ case CONDITIONAL_ACE_TOKEN_INT8:
+ if (val < -128 || val > 127) {
+ return false;
+ }
+ break;
+ case CONDITIONAL_ACE_TOKEN_INT16:
+ if (val < INT16_MIN || val > INT16_MAX) {
+ return false;
+ }
+ break;
+ case CONDITIONAL_ACE_TOKEN_INT32:
+ if (val < INT32_MIN || val > INT32_MAX) {
+ return false;
+ }
+ break;
+ case CONDITIONAL_ACE_TOKEN_INT64:
+ /* val has these limits naturally */
+ break;
+ default:
+ return false;
+ }
+
+ if (tok->data.int64.base != CONDITIONAL_ACE_INT_BASE_8 &&
+ tok->data.int64.base != CONDITIONAL_ACE_INT_BASE_10 &&
+ tok->data.int64.base != CONDITIONAL_ACE_INT_BASE_16) {
+ return false;
+ }
+ if (tok->data.int64.sign != CONDITIONAL_ACE_INT_SIGN_POSITIVE &&
+ tok->data.int64.sign != CONDITIONAL_ACE_INT_SIGN_NEGATIVE &&
+ tok->data.int64.sign != CONDITIONAL_ACE_INT_SIGN_NONE) {
+ return false;
+ }
+ return true;
+}
+
+
+static ssize_t pull_integer(TALLOC_CTX *mem_ctx,
+ uint8_t *data, size_t length,
+ struct ace_condition_int *tok)
+{
+ ssize_t bytes_used;
+ enum ndr_err_code ndr_err;
+ DATA_BLOB v = data_blob_const(data, length);
+ struct ndr_pull *ndr = ndr_pull_init_blob(&v, mem_ctx);
+ if (ndr == NULL) {
+ return -1;
+ }
+ ndr_err = ndr_pull_ace_condition_int(ndr, NDR_SCALARS|NDR_BUFFERS, tok);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ TALLOC_FREE(ndr);
+ return -1;
+ }
+ bytes_used = ndr->offset;
+ TALLOC_FREE(ndr);
+ return bytes_used;
+}
+
+static ssize_t push_integer(uint8_t *data, size_t available,
+ const struct ace_condition_int *tok)
+{
+ enum ndr_err_code ndr_err;
+ DATA_BLOB v;
+ ndr_err = ndr_push_struct_blob(&v, NULL,
+ tok,
+ (ndr_push_flags_fn_t)ndr_push_ace_condition_int);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return -1;
+ }
+ if (available < v.length) {
+ talloc_free(v.data);
+ return -1;
+ }
+ memcpy(data, v.data, v.length);
+ talloc_free(v.data);
+ return v.length;
+}
+
+
+static ssize_t pull_unicode(TALLOC_CTX *mem_ctx,
+ uint8_t *data, size_t length,
+ struct ace_condition_unicode *tok)
+{
+ ssize_t bytes_used;
+ enum ndr_err_code ndr_err;
+ DATA_BLOB v = data_blob_const(data, length);
+ struct ndr_pull *ndr = ndr_pull_init_blob(&v, mem_ctx);
+ if (ndr == NULL) {
+ return -1;
+ }
+ ndr_err = ndr_pull_ace_condition_unicode(ndr, NDR_SCALARS|NDR_BUFFERS, tok);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ TALLOC_FREE(ndr);
+ return -1;
+ }
+ bytes_used = ndr->offset;
+ TALLOC_FREE(ndr);
+ return bytes_used;
+}
+
+static ssize_t push_unicode(uint8_t *data, size_t available,
+ const struct ace_condition_unicode *tok)
+{
+ enum ndr_err_code ndr_err;
+ DATA_BLOB v;
+ ndr_err = ndr_push_struct_blob(&v, NULL,
+ tok,
+ (ndr_push_flags_fn_t)ndr_push_ace_condition_unicode);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return -1;
+ }
+ if (available < v.length) {
+ talloc_free(v.data);
+ return -1;
+ }
+ memcpy(data, v.data, v.length);
+ talloc_free(v.data);
+ return v.length;
+}
+
+
+static ssize_t pull_bytes(TALLOC_CTX *mem_ctx,
+ uint8_t *data, size_t length,
+ DATA_BLOB *tok)
+{
+ ssize_t bytes_used;
+ enum ndr_err_code ndr_err;
+ DATA_BLOB v = data_blob_const(data, length);
+ struct ndr_pull *ndr = ndr_pull_init_blob(&v, mem_ctx);
+ if (ndr == NULL) {
+ return -1;
+ }
+ ndr_err = ndr_pull_DATA_BLOB(ndr, NDR_SCALARS|NDR_BUFFERS, tok);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ TALLOC_FREE(ndr);
+ return -1;
+ }
+ bytes_used = ndr->offset;
+ talloc_free(ndr);
+ return bytes_used;
+}
+
+static ssize_t push_bytes(uint8_t *data, size_t available,
+ const DATA_BLOB *tok)
+{
+ size_t offset;
+ enum ndr_err_code ndr_err;
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct ndr_push *ndr = ndr_push_init_ctx(frame);
+ if (ndr == NULL) {
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ ndr_err = ndr_push_DATA_BLOB(ndr, NDR_SCALARS|NDR_BUFFERS, *tok);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ if (available < ndr->offset) {
+ TALLOC_FREE(frame);
+ return -1;
+ }
+ memcpy(data, ndr->data, ndr->offset);
+ offset = ndr->offset;
+ TALLOC_FREE(frame);
+ return offset;
+}
+
+static ssize_t pull_sid(TALLOC_CTX *mem_ctx,
+ uint8_t *data, size_t length,
+ struct ace_condition_sid *tok)
+{
+ ssize_t bytes_used;
+ enum ndr_err_code ndr_err;
+ DATA_BLOB v = data_blob_const(data, length);
+ struct ndr_pull *ndr = ndr_pull_init_blob(&v, mem_ctx);
+ if (ndr == NULL) {
+ return -1;
+ }
+ ndr->flags |= LIBNDR_FLAG_SUBCONTEXT_NO_UNREAD_BYTES;
+
+ ndr_err = ndr_pull_ace_condition_sid(ndr, NDR_SCALARS|NDR_BUFFERS, tok);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ TALLOC_FREE(ndr);
+ return -1;
+ }
+ bytes_used = ndr->offset;
+ TALLOC_FREE(ndr);
+ return bytes_used;
+}
+
+static ssize_t push_sid(uint8_t *data, size_t available,
+ const struct ace_condition_sid *tok)
+{
+ enum ndr_err_code ndr_err;
+ DATA_BLOB v;
+ ndr_err = ndr_push_struct_blob(&v, NULL,
+ tok,
+ (ndr_push_flags_fn_t)ndr_push_ace_condition_sid);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return -1;
+ }
+ if (available < v.length) {
+ talloc_free(v.data);
+ return -1;
+ }
+ memcpy(data, v.data, v.length);
+ talloc_free(v.data);
+ return v.length;
+}
+
+
+static ssize_t pull_composite(TALLOC_CTX *mem_ctx,
+ uint8_t *data, size_t length,
+ struct ace_condition_composite *tok)
+{
+ size_t i, j;
+ size_t alloc_length;
+ size_t byte_size;
+ struct ace_condition_token *tokens = NULL;
+ if (length < 4) {
+ return -1;
+ }
+ byte_size = PULL_LE_U32(data, 0);
+ if (byte_size > length - 4) {
+ return -1;
+ }
+ /*
+ * There is a list of other literal tokens (possibly including nested
+ * composites), which we will store in an array.
+ *
+ * This array can *only* be literals.
+ */
+ alloc_length = byte_size;
+ tokens = talloc_array(mem_ctx,
+ struct ace_condition_token,
+ alloc_length);
+ if (tokens == NULL) {
+ return -1;
+ }
+ byte_size += 4;
+ i = 4;
+ j = 0;
+ while (i < byte_size) {
+ struct ace_condition_token *el = &tokens[j];
+ ssize_t consumed;
+ uint8_t *el_data = NULL;
+ size_t available;
+ bool ok;
+ *el = (struct ace_condition_token) { .type = data[i] };
+ i++;
+
+ el_data = data + i;
+ available = byte_size - i;
+
+ switch (el->type) {
+ case CONDITIONAL_ACE_TOKEN_INT8:
+ case CONDITIONAL_ACE_TOKEN_INT16:
+ case CONDITIONAL_ACE_TOKEN_INT32:
+ case CONDITIONAL_ACE_TOKEN_INT64:
+ consumed = pull_integer(mem_ctx,
+ el_data,
+ available,
+ &el->data.int64);
+ ok = check_integer_range(el);
+ if (! ok) {
+ goto error;
+ }
+ break;
+ case CONDITIONAL_ACE_TOKEN_UNICODE:
+ consumed = pull_unicode(mem_ctx,
+ el_data,
+ available,
+ &el->data.unicode);
+ break;
+
+ case CONDITIONAL_ACE_TOKEN_OCTET_STRING:
+ consumed = pull_bytes(mem_ctx,
+ el_data,
+ available,
+ &el->data.bytes);
+ break;
+
+ case CONDITIONAL_ACE_TOKEN_SID:
+ consumed = pull_sid(mem_ctx,
+ el_data,
+ available,
+ &el->data.sid);
+ break;
+
+ case CONDITIONAL_ACE_TOKEN_COMPOSITE:
+ DBG_ERR("recursive composite tokens in conditional "
+ "ACEs are not currently supported\n");
+ goto error;
+ default:
+ goto error;
+ }
+
+ if (consumed < 0 || consumed + i > length) {
+ goto error;
+ }
+ i += consumed;
+ j++;
+ if (j == UINT16_MAX) {
+ talloc_free(tokens);
+ return -1;
+ }
+ if (j == alloc_length) {
+ struct ace_condition_token *new_tokens = NULL;
+
+ alloc_length += 5;
+ new_tokens = talloc_realloc(mem_ctx,
+ tokens,
+ struct ace_condition_token,
+ alloc_length);
+
+ if (new_tokens == NULL) {
+ goto error;
+ }
+ tokens = new_tokens;
+ }
+ }
+ tok->n_members = j;
+ tok->tokens = tokens;
+ return byte_size;
+error:
+ talloc_free(tokens);
+ return -1;
+}
+
+
+static ssize_t push_composite(uint8_t *data, size_t length,
+ const struct ace_condition_composite *tok)
+{
+ size_t i;
+ uint8_t *byte_length_ptr;
+ size_t used = 0;
+ if (length < 4) {
+ return -1;
+ }
+ /*
+ * We have no idea what the eventual length will be, so we keep a
+ * pointer to write it in at the end.
+ */
+ byte_length_ptr = data;
+ PUSH_LE_U32(data, 0, 0);
+ used = 4;
+
+ for (i = 0; i < tok->n_members && used < length; i++) {
+ struct ace_condition_token *el = &tok->tokens[i];
+ ssize_t consumed;
+ uint8_t *el_data = NULL;
+ size_t available;
+ bool ok;
+ data[used] = el->type;
+ used++;
+ if (used == length) {
+ /*
+ * used == length is not expected here; the token
+ * types that only have an opcode and no data are not
+ * literals that can be in composites.
+ */
+ return -1;
+ }
+ el_data = data + used;
+ available = length - used;
+
+ switch (el->type) {
+ case CONDITIONAL_ACE_TOKEN_INT8:
+ case CONDITIONAL_ACE_TOKEN_INT16:
+ case CONDITIONAL_ACE_TOKEN_INT32:
+ case CONDITIONAL_ACE_TOKEN_INT64:
+ ok = check_integer_range(el);
+ if (! ok) {
+ return -1;
+ }
+ consumed = push_integer(el_data,
+ available,
+ &el->data.int64);
+ break;
+ case CONDITIONAL_ACE_TOKEN_UNICODE:
+ consumed = push_unicode(el_data,
+ available,
+ &el->data.unicode);
+ break;
+
+ case CONDITIONAL_ACE_TOKEN_OCTET_STRING:
+ consumed = push_bytes(el_data,
+ available,
+ &el->data.bytes);
+ break;
+
+ case CONDITIONAL_ACE_TOKEN_SID:
+ consumed = push_sid(el_data,
+ available,
+ &el->data.sid);
+ break;
+
+ case CONDITIONAL_ACE_TOKEN_COMPOSITE:
+ consumed = push_composite(el_data,
+ available,
+ &el->data.composite);
+ break;
+
+ default:
+ return -1;
+ }
+
+ if (consumed < 0) {
+ return -1;
+ }
+ used += consumed;
+ }
+ if (used > length) {
+ return -1;
+ }
+
+ PUSH_LE_U32(byte_length_ptr, 0, used - 4);
+ return used;
+}
+
+static ssize_t pull_end_padding(uint8_t *data, size_t length)
+{
+ /*
+ * We just check that we have the right kind of number of zero
+ * bytes. The blob must end on a multiple of 4. One zero byte
+ * has already been swallowed as tok->type, which sends us
+ * here, so we expect 1 or two more -- total padding is 0, 1,
+ * 2, or 3.
+ *
+ * zero is also called CONDITIONAL_ACE_TOKEN_INVALID_OR_PADDING.
+ */
+ ssize_t i;
+ if (length > 2) {
+ return -1;
+ }
+ for (i = 0; i < length; i++) {
+ if (data[i] != 0) {
+ return -1;
+ }
+ }
+ return length;
+}
+
+
+struct ace_condition_script *parse_conditional_ace(TALLOC_CTX *mem_ctx,
+ DATA_BLOB data)
+{
+ size_t i, j;
+ struct ace_condition_token *tokens = NULL;
+ size_t alloc_length;
+ struct ace_condition_script *program = NULL;
+
+ if (data.length < 4 ||
+ data.data[0] != 'a' ||
+ data.data[1] != 'r' ||
+ data.data[2] != 't' ||
+ data.data[3] != 'x') {
+ /*
+ * lacks the "artx" conditional ace identifier magic.
+ * NULL returns will deny access.
+ */
+ return NULL;
+ }
+ if (data.length > CONDITIONAL_ACE_MAX_LENGTH ||
+ (data.length & 3) != 0) {
+ /*
+ * >= 64k or non-multiples of 4 are not possible in the ACE
+ * wire format.
+ */
+ return NULL;
+ }
+
+ program = talloc(mem_ctx, struct ace_condition_script);
+ if (program == NULL) {
+ return NULL;
+ }
+
+ /*
+ * We will normally end up with fewer than data.length tokens, as
+ * values are stored in multiple bytes (all integers are 10 bytes,
+ * strings and attributes are utf16 + length, SIDs are SID-size +
+ * length, etc). But operators are one byte, so something like
+ * !(!(!(!(!(!(x)))))) -- where each '!(..)' is one byte -- will bring
+ * the number of tokens close to the number of bytes.
+ *
+ * This is all to say we're guessing a token length that hopes to
+ * avoid reallocs without wasting too much up front.
+ */
+ alloc_length = data.length / 2 + 1;
+ tokens = talloc_array(program,
+ struct ace_condition_token,
+ alloc_length);
+ if (tokens == NULL) {
+ TALLOC_FREE(program);
+ return NULL;
+ }
+
+ i = 4;
+ j = 0;
+ while(i < data.length) {
+ struct ace_condition_token *tok = &tokens[j];
+ ssize_t consumed = 0;
+ uint8_t *tok_data = NULL;
+ size_t available;
+ bool ok;
+ tok->type = data.data[i];
+ tok->flags = 0;
+ i++;
+ tok_data = data.data + i;
+ available = data.length - i;
+
+ switch (tok->type) {
+ case CONDITIONAL_ACE_TOKEN_INT8:
+ case CONDITIONAL_ACE_TOKEN_INT16:
+ case CONDITIONAL_ACE_TOKEN_INT32:
+ case CONDITIONAL_ACE_TOKEN_INT64:
+ consumed = pull_integer(program,
+ tok_data,
+ available,
+ &tok->data.int64);
+ ok = check_integer_range(tok);
+ if (! ok) {
+ goto fail;
+ }
+ break;
+ case CONDITIONAL_ACE_TOKEN_UNICODE:
+ /*
+ * The next four are pulled as unicode, but are
+ * processed as user attribute look-ups.
+ */
+ case CONDITIONAL_ACE_LOCAL_ATTRIBUTE:
+ case CONDITIONAL_ACE_USER_ATTRIBUTE:
+ case CONDITIONAL_ACE_RESOURCE_ATTRIBUTE:
+ case CONDITIONAL_ACE_DEVICE_ATTRIBUTE:
+ consumed = pull_unicode(program,
+ tok_data,
+ available,
+ &tok->data.unicode);
+ break;
+
+ case CONDITIONAL_ACE_TOKEN_OCTET_STRING:
+ consumed = pull_bytes(program,
+ tok_data,
+ available,
+ &tok->data.bytes);
+ break;
+
+ case CONDITIONAL_ACE_TOKEN_SID:
+ consumed = pull_sid(program,
+ tok_data,
+ available,
+ &tok->data.sid);
+ break;
+
+ case CONDITIONAL_ACE_TOKEN_COMPOSITE:
+ consumed = pull_composite(program,
+ tok_data,
+ available,
+ &tok->data.composite);
+ break;
+
+ case CONDITIONAL_ACE_TOKEN_MEMBER_OF:
+ case CONDITIONAL_ACE_TOKEN_DEVICE_MEMBER_OF:
+ case CONDITIONAL_ACE_TOKEN_MEMBER_OF_ANY:
+ case CONDITIONAL_ACE_TOKEN_DEVICE_MEMBER_OF_ANY:
+ case CONDITIONAL_ACE_TOKEN_NOT_MEMBER_OF:
+ case CONDITIONAL_ACE_TOKEN_NOT_DEVICE_MEMBER_OF:
+ case CONDITIONAL_ACE_TOKEN_NOT_MEMBER_OF_ANY:
+ case CONDITIONAL_ACE_TOKEN_NOT_DEVICE_MEMBER_OF_ANY:
+ /*
+ * these require a SID or composite SID list operand,
+ * and we could check that now in most cases.
+ */
+ break;
+ /* binary relational operators */
+ case CONDITIONAL_ACE_TOKEN_EQUAL:
+ case CONDITIONAL_ACE_TOKEN_NOT_EQUAL:
+ case CONDITIONAL_ACE_TOKEN_LESS_THAN:
+ case CONDITIONAL_ACE_TOKEN_LESS_OR_EQUAL:
+ case CONDITIONAL_ACE_TOKEN_GREATER_THAN:
+ case CONDITIONAL_ACE_TOKEN_GREATER_OR_EQUAL:
+ case CONDITIONAL_ACE_TOKEN_CONTAINS:
+ case CONDITIONAL_ACE_TOKEN_ANY_OF:
+ case CONDITIONAL_ACE_TOKEN_NOT_CONTAINS:
+ case CONDITIONAL_ACE_TOKEN_NOT_ANY_OF:
+ /* unary logical operators */
+ case CONDITIONAL_ACE_TOKEN_EXISTS:
+ case CONDITIONAL_ACE_TOKEN_NOT_EXISTS:
+ case CONDITIONAL_ACE_TOKEN_NOT:
+ /* binary logical operators */
+ case CONDITIONAL_ACE_TOKEN_AND:
+ case CONDITIONAL_ACE_TOKEN_OR:
+ break;
+ case CONDITIONAL_ACE_TOKEN_INVALID_OR_PADDING:
+ /* this is only valid at the end */
+ consumed = pull_end_padding(tok_data,
+ available);
+ j--; /* don't add this token */
+ break;
+ default:
+ goto fail;
+ }
+
+ if (consumed < 0) {
+ goto fail;
+ }
+ if (consumed + i < i || consumed + i > data.length) {
+ goto fail;
+ }
+ i += consumed;
+ j++;
+ if (j == alloc_length) {
+ alloc_length *= 2;
+ tokens = talloc_realloc(program,
+ tokens,
+ struct ace_condition_token,
+ alloc_length);
+ if (tokens == NULL) {
+ goto fail;
+ }
+ }
+ }
+ program->length = j;
+ program->tokens = talloc_realloc(program,
+ tokens,
+ struct ace_condition_token,
+ program->length + 1);
+ if (program->tokens == NULL) {
+ goto fail;
+ }
+ /*
+ * When interpreting the program we will need a stack, which in the
+ * very worst case can be as deep as the program is long.
+ */
+ program->stack = talloc_array(program,
+ struct ace_condition_token,
+ program->length + 1);
+ if (program->stack == NULL) {
+ goto fail;
+ }
+
+ return program;
+ fail:
+ talloc_free(program);
+ return NULL;
+ }
+
+
+static bool claim_lookup_internal(
+ TALLOC_CTX *mem_ctx,
+ struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim,
+ struct ace_condition_token *result)
+{
+ bool ok = claim_v1_to_ace_token(mem_ctx, claim, result);
+ return ok;
+}
+
+
+static bool resource_claim_lookup(
+ TALLOC_CTX *mem_ctx,
+ const struct ace_condition_token *op,
+ const struct security_descriptor *sd,
+ struct ace_condition_token *result)
+{
+ /*
+ * For a @Resource.attr, the claims come from a resource ACE
+ * in the object's SACL. That's why we need a security descriptor.
+ *
+ * If there is no matching resource ACE, a NULL result is returned,
+ * which should compare UNKNOWN to anything. The NULL will have the
+ * CONDITIONAL_ACE_FLAG_NULL_MEANS_ERROR flag set if it seems failure
+ * is not simply due to the sought claim not existing. This is useful for
+ * the Exists and Not_Exists operators.
+ */
+ size_t i;
+ struct ace_condition_unicode name;
+
+ result->type = CONDITIONAL_ACE_SAMBA_RESULT_NULL;
+
+ if (op->type != CONDITIONAL_ACE_RESOURCE_ATTRIBUTE) {
+ /* what are we even doing here? */
+ result->type = CONDITIONAL_ACE_SAMBA_RESULT_ERROR;
+ return false;
+ }
+
+ name = op->data.resource_attr;
+
+ if (sd->sacl == NULL) {
+ DBG_NOTICE("Resource attribute ACE '%s' not found, "
+ "because there is no SACL\n",
+ name.value);
+ return true;
+ }
+
+ for (i = 0; i < sd->sacl->num_aces; i++) {
+ struct security_ace *ace = &sd->sacl->aces[i];
+ bool ok;
+
+ if (ace->type != SEC_ACE_TYPE_SYSTEM_RESOURCE_ATTRIBUTE) {
+ continue;
+ }
+ if (strcasecmp_m(name.value,
+ ace->coda.claim.name) != 0) {
+ continue;
+ }
+ /* this is the one */
+ ok = claim_lookup_internal(mem_ctx, &ace->coda.claim, result);
+ if (ok) {
+ return true;
+ }
+ }
+ DBG_NOTICE("Resource attribute ACE '%s' not found.\n",
+ name.value);
+ return false;
+}
+
+
+static bool token_claim_lookup(
+ TALLOC_CTX *mem_ctx,
+ const struct security_token *token,
+ const struct ace_condition_token *op,
+ struct ace_condition_token *result)
+{
+ /*
+ * The operator has an attribute name; if there is a claim of
+ * the right type with that name, that is returned as the result.
+ *
+ * XXX what happens otherwise? NULL result?
+ */
+ struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claims = NULL;
+ size_t num_claims;
+ bool ok;
+ const struct ace_condition_unicode *name = NULL;
+ size_t i;
+
+ result->type = CONDITIONAL_ACE_SAMBA_RESULT_NULL;
+
+ switch (op->type) {
+ case CONDITIONAL_ACE_LOCAL_ATTRIBUTE:
+ claims = token->local_claims;
+ num_claims = token->num_local_claims;
+ name = &op->data.local_attr;
+ break;
+ case CONDITIONAL_ACE_USER_ATTRIBUTE:
+ claims = token->user_claims;
+ num_claims = token->num_user_claims;
+ name = &op->data.user_attr;
+ break;
+ case CONDITIONAL_ACE_DEVICE_ATTRIBUTE:
+ claims = token->device_claims;
+ num_claims = token->num_device_claims;
+ name = &op->data.device_attr;
+ break;
+ default:
+ DBG_WARNING("Conditional ACE claim lookup got bad arg type %u\n",
+ op->type);
+ result->type = CONDITIONAL_ACE_SAMBA_RESULT_ERROR;
+ return false;
+ }
+
+ if (num_claims == 0) {
+ DBG_NOTICE("There are no type %u claims\n", op->type);
+ return false;
+ }
+ if (claims == NULL) {
+ DBG_ERR("Type %u claim list unexpectedly NULL!\n", op->type);
+ result->type = CONDITIONAL_ACE_SAMBA_RESULT_ERROR;
+ return false;
+ }
+ /*
+ * Loop backwards: a later claim will override an earlier one with the
+ * same name.
+ */
+ for (i = num_claims - 1; i < num_claims; i--) {
+ if (claims[i].name == NULL) {
+ DBG_ERR("claim %zu has no name!\n", i);
+ continue;
+ }
+ if (strcasecmp_m(claims[i].name, name->value) == 0) {
+ /* this is the one */
+ ok = claim_lookup_internal(mem_ctx, &claims[i], result);
+ return ok;
+ }
+ }
+ DBG_NOTICE("Claim not found\n");
+ return false;
+}
+
+
+
+
+static bool member_lookup(
+ const struct security_token *token,
+ const struct ace_condition_token *op,
+ const struct ace_condition_token *arg,
+ struct ace_condition_token *result)
+{
+ /*
+ * We need to compare the lists of SIDs in the token with the
+ * SID[s] in the argument. There are 8 combinations of
+ * operation, depending on whether we want to match all or any
+ * of the SIDs, whether we're using the device SIDs or user
+ * SIDs, and whether the operator name starts with "Not_".
+ *
+ * _MEMBER_OF User has all operand SIDs
+ * _DEVICE_MEMBER_OF Device has all operand SIDs
+ * _MEMBER_OF_ANY User has one or more operand SIDs
+ * _DEVICE_MEMBER_OF_ANY Device has one or more operand SIDs
+ *
+ * NOT_* has the effect of !(the operator without NOT_).
+ *
+ * The operand can either be a composite of SIDs or a single SID.
+ * This adds an additional branch.
+ */
+ bool match = false;
+ bool it_is_a_not_op;
+ bool it_is_an_any_op;
+ bool it_is_a_device_op;
+ bool arg_is_a_single_sid;
+ struct dom_sid *sid_array = NULL;
+ size_t num_sids, i, j;
+ const struct dom_sid *sid = NULL;
+
+ result->type = CONDITIONAL_ACE_SAMBA_RESULT_BOOL;
+ result->data.result.value = ACE_CONDITION_UNKNOWN;
+
+ switch (arg->type) {
+ case CONDITIONAL_ACE_TOKEN_SID:
+ arg_is_a_single_sid = true;
+ break;
+ case CONDITIONAL_ACE_TOKEN_COMPOSITE:
+ arg_is_a_single_sid = false;
+ break;
+ default:
+ DBG_WARNING("Conditional ACE Member_Of got bad arg type %u\n",
+ arg->type);
+ return false;
+ }
+
+ switch (op->type) {
+ case CONDITIONAL_ACE_TOKEN_NOT_MEMBER_OF:
+ case CONDITIONAL_ACE_TOKEN_NOT_MEMBER_OF_ANY:
+ it_is_a_not_op = true;
+ it_is_a_device_op = false;
+ break;
+ case CONDITIONAL_ACE_TOKEN_NOT_DEVICE_MEMBER_OF_ANY:
+ case CONDITIONAL_ACE_TOKEN_NOT_DEVICE_MEMBER_OF:
+ it_is_a_not_op = true;
+ it_is_a_device_op = true;
+ break;
+ case CONDITIONAL_ACE_TOKEN_MEMBER_OF:
+ case CONDITIONAL_ACE_TOKEN_MEMBER_OF_ANY:
+ it_is_a_not_op = false;
+ it_is_a_device_op = false;
+ break;
+ case CONDITIONAL_ACE_TOKEN_DEVICE_MEMBER_OF_ANY:
+ case CONDITIONAL_ACE_TOKEN_DEVICE_MEMBER_OF:
+ it_is_a_not_op = false;
+ it_is_a_device_op = true;
+ break;
+ default:
+ DBG_WARNING("Conditional ACE Member_Of got bad op type %u\n",
+ op->type);
+ return false;
+ }
+
+ switch (op->type) {
+ case CONDITIONAL_ACE_TOKEN_NOT_MEMBER_OF_ANY:
+ case CONDITIONAL_ACE_TOKEN_NOT_DEVICE_MEMBER_OF_ANY:
+ case CONDITIONAL_ACE_TOKEN_MEMBER_OF_ANY:
+ case CONDITIONAL_ACE_TOKEN_DEVICE_MEMBER_OF_ANY:
+ it_is_an_any_op = true;
+ break;
+ default:
+ it_is_an_any_op = false;
+ }
+
+ if (it_is_a_device_op) {
+ sid_array = token->device_sids;
+ num_sids = token->num_device_sids;
+ } else {
+ sid_array = token->sids;
+ num_sids = token->num_sids;
+ }
+
+ if (arg_is_a_single_sid) {
+ /*
+ * In this case the any and all operations are the
+ * same.
+ */
+ sid = &arg->data.sid.sid;
+ match = false;
+ for (i = 0; i < num_sids; i++) {
+ match = dom_sid_equal(sid, &sid_array[i]);
+ if (match) {
+ break;
+ }
+ }
+ if (it_is_a_not_op) {
+ match = ! match;
+ }
+ if (match) {
+ result->data.result.value = ACE_CONDITION_TRUE;
+ } else {
+ result->data.result.value = ACE_CONDITION_FALSE;
+ }
+ return true;
+ }
+
+ /* This is a composite list (hopefully of SIDs) */
+ if (arg->data.composite.n_members == 0) {
+ DBG_WARNING("Conditional ACE Member_Of argument is empty\n");
+ return false;
+ }
+
+ for (j = 0; j < arg->data.composite.n_members; j++) {
+ const struct ace_condition_token *member =
+ &arg->data.composite.tokens[j];
+ if (member->type != CONDITIONAL_ACE_TOKEN_SID) {
+ DBG_WARNING("Conditional ACE Member_Of argument contains "
+ "non-sid element [%zu]: %u\n",
+ j, member->type);
+ return false;
+ }
+ sid = &member->data.sid.sid;
+ match = false;
+ for (i = 0; i < num_sids; i++) {
+ match = dom_sid_equal(sid, &sid_array[i]);
+ if (match) {
+ break;
+ }
+ }
+ if (it_is_an_any_op) {
+ if (match) {
+ /* we have matched one SID, which is enough */
+ goto apply_not;
+ }
+ } else { /* an all op */
+ if (! match) {
+ /* failing one is enough */
+ goto apply_not;
+ }
+ }
+ }
+ /*
+ * Reaching the end of that loop means either:
+ * 1. it was an ALL op and we never failed to find one, or
+ * 2. it was an ANY op, and we didn't find one.
+ */
+ match = !it_is_an_any_op;
+
+ apply_not:
+ if (it_is_a_not_op) {
+ match = ! match;
+ }
+ if (match) {
+ result->data.result.value = ACE_CONDITION_TRUE;
+ } else {
+ result->data.result.value = ACE_CONDITION_FALSE;
+ }
+
+ return true;
+}
+
+
+static bool ternary_value(
+ const struct ace_condition_token *arg,
+ struct ace_condition_token *result)
+{
+ /*
+ * Find the truth value of the argument, stored in the result token.
+ *
+ * A return value of false means the operation is invalid, and the
+ * result is undefined.
+ */
+ if (arg->type == CONDITIONAL_ACE_SAMBA_RESULT_BOOL) {
+ /* pass through */
+ *result = *arg;
+ return true;
+ }
+
+ result->type = CONDITIONAL_ACE_SAMBA_RESULT_BOOL;
+ result->data.result.value = ACE_CONDITION_UNKNOWN;
+
+ if (IS_INT_TOKEN(arg)) {
+ /* zero is false */
+ if (arg->data.int64.value == 0) {
+ result->data.result.value = ACE_CONDITION_FALSE;
+ } else {
+ result->data.result.value = ACE_CONDITION_TRUE;
+ }
+ return true;
+ }
+ if (arg->type == CONDITIONAL_ACE_TOKEN_UNICODE) {
+ /* empty is false */
+ if (arg->data.unicode.value[0] == '\0') {
+ result->data.result.value = ACE_CONDITION_FALSE;
+ } else {
+ result->data.result.value = ACE_CONDITION_TRUE;
+ }
+ return true;
+ }
+
+ /*
+ * everything else in UNKNOWN. This includes NULL values (i.e. an
+ * unsuccessful look-up).
+ */
+ result->data.result.value = ACE_CONDITION_UNKNOWN;
+ return true;
+}
+
+static bool not_operator(
+ const struct ace_condition_token *arg,
+ struct ace_condition_token *result)
+{
+ bool ok;
+ if (IS_LITERAL_TOKEN(arg)) {
+ /*
+ * Logic operators don't work on literals.
+ */
+ return false;
+ }
+
+ ok = ternary_value(arg, result);
+ if (! ok) {
+ return false;
+ }
+ if (result->data.result.value == ACE_CONDITION_FALSE) {
+ result->data.result.value = ACE_CONDITION_TRUE;
+ } else if (result->data.result.value == ACE_CONDITION_TRUE) {
+ result->data.result.value = ACE_CONDITION_FALSE;
+ }
+ /* unknown stays unknown */
+ return true;
+}
+
+
+static bool unary_logic_operator(
+ TALLOC_CTX *mem_ctx,
+ const struct security_token *token,
+ const struct ace_condition_token *op,
+ const struct ace_condition_token *arg,
+ const struct security_descriptor *sd,
+ struct ace_condition_token *result)
+{
+
+ bool ok;
+ bool found;
+ struct ace_condition_token claim = {
+ .type = CONDITIONAL_ACE_SAMBA_RESULT_ERROR
+ };
+ if (op->type == CONDITIONAL_ACE_TOKEN_NOT) {
+ return not_operator(arg, result);
+ }
+ result->type = CONDITIONAL_ACE_SAMBA_RESULT_BOOL;
+ result->data.result.value = ACE_CONDITION_UNKNOWN;
+
+ /*
+ * Not_Exists and Exists require the same work, except we negate the
+ * answer in one case. From [MS-DTYP] 2.4.4.17.7:
+ *
+ * If the type of the operand is "Local Attribute"
+ * If the value is non-null return TRUE
+ * Else return FALSE
+ * Else if the type of the operand is "Resource Attribute"
+ * Return TRUE if value is non-null; FALSE otherwise.
+ * Else return Error
+ */
+ switch (op->type) {
+ case CONDITIONAL_ACE_LOCAL_ATTRIBUTE:
+ ok = token_claim_lookup(mem_ctx, token, arg, &claim);
+ /*
+ * "not ok" usually means a failure to find the attribute,
+ * which is the false condition and not an error.
+ *
+ * XXX or do we need an extra flag?
+ */
+ break;
+ case CONDITIONAL_ACE_RESOURCE_ATTRIBUTE:
+ ok = resource_claim_lookup(mem_ctx, arg, sd, &claim);
+ break;
+ default:
+ return false;
+ }
+
+ /*
+ *
+ */
+
+ if (claim.type != CONDITIONAL_ACE_SAMBA_RESULT_NULL) {
+ found = true;
+ } else if (ok) {
+ found = false;
+ } else {
+ return false;
+ }
+
+
+
+ if (op->type == CONDITIONAL_ACE_TOKEN_NOT_EXISTS) {
+ found = ! found;
+ } else if (op->type != CONDITIONAL_ACE_TOKEN_EXISTS) {
+ /* should not get here */
+ return false;
+ }
+
+ result->data.result.value = found ? ACE_CONDITION_TRUE: ACE_CONDITION_FALSE;
+ return true;
+}
+
+
+
+static bool binary_logic_operator(
+ const struct security_token *token,
+ const struct ace_condition_token *op,
+ const struct ace_condition_token *lhs,
+ const struct ace_condition_token *rhs,
+ struct ace_condition_token *result)
+{
+ struct ace_condition_token at, bt;
+ int a, b;
+ bool ok;
+
+ result->type = CONDITIONAL_ACE_SAMBA_RESULT_BOOL;
+ result->data.result.value = ACE_CONDITION_UNKNOWN;
+
+ if (IS_LITERAL_TOKEN(lhs) || IS_LITERAL_TOKEN(rhs)) {
+ /*
+ * Logic operators don't work on literals.
+ */
+ return false;
+ }
+
+ ok = ternary_value(lhs, &at);
+ if (! ok) {
+ return false;
+ }
+ ok = ternary_value(rhs, &bt);
+ if (! ok) {
+ return false;
+ }
+ a = at.data.result.value;
+ b = bt.data.result.value;
+
+ if (op->type == CONDITIONAL_ACE_TOKEN_AND) {
+ /*
+ * AND true false unknown
+ * true T F ?
+ * false F F F
+ * unknown ? F ?
+ *
+ * unknown unless BOTH true or EITHER false
+ */
+ if (a == ACE_CONDITION_TRUE &&
+ b == ACE_CONDITION_TRUE) {
+ result->data.result.value = ACE_CONDITION_TRUE;
+ return true;
+ }
+ if (a == ACE_CONDITION_FALSE ||
+ b == ACE_CONDITION_FALSE) {
+ result->data.result.value = ACE_CONDITION_FALSE;
+ return true;
+ }
+ /*
+ * Neither value is False, so the result is Unknown,
+ * as set at the start of this function.
+ */
+ return true;
+ }
+ /*
+ * OR true false unknown
+ * true T T T
+ * false T F ?
+ * unknown T ? ?
+ *
+ * unknown unless EITHER true or BOTH false
+ */
+ if (a == ACE_CONDITION_TRUE ||
+ b == ACE_CONDITION_TRUE) {
+ result->data.result.value = ACE_CONDITION_TRUE;
+ return true;
+ }
+ if (a == ACE_CONDITION_FALSE &&
+ b == ACE_CONDITION_FALSE) {
+ result->data.result.value = ACE_CONDITION_FALSE;
+ return true;
+ }
+ return true;
+}
+
+
+static bool tokens_are_comparable(const struct ace_condition_token *op,
+ const struct ace_condition_token *lhs,
+ const struct ace_condition_token *rhs)
+{
+ uint64_t n;
+ /*
+ * we can't compare different types *unless* they are both
+ * integers, or one is a bool and the other is an integer 0 or
+ * 1, and the operator is == or != (or NULL, which for convenience,
+ * is treated as ==).
+ */
+ //XXX actually it says "literal integers", do we need to check flags?
+ if (lhs->type == rhs->type) {
+ return true;
+ }
+
+ if (IS_INT_TOKEN(lhs) && IS_INT_TOKEN(rhs)) {
+ /* don't block e.g. comparing an int32 to an int64 */
+ return true;
+ }
+
+ /* is it == or != */
+ if (op != NULL &&
+ op->type != CONDITIONAL_ACE_TOKEN_EQUAL &&
+ op->type != CONDITIONAL_ACE_TOKEN_NOT_EQUAL) {
+ return false;
+ }
+ /* is one a bool and the other an int? */
+ if (IS_INT_TOKEN(lhs) && IS_BOOL_TOKEN(rhs)) {
+ n = lhs->data.int64.value;
+ } else if (IS_INT_TOKEN(rhs) && IS_BOOL_TOKEN(lhs)) {
+ n = rhs->data.int64.value;
+ } else {
+ return false;
+ }
+ if (n == 0 || n == 1) {
+ return true;
+ }
+ return false;
+}
+
+
+static bool cmp_to_result(const struct ace_condition_token *op,
+ struct ace_condition_token *result,
+ int cmp)
+{
+ bool answer;
+ switch (op->type) {
+ case CONDITIONAL_ACE_TOKEN_EQUAL:
+ answer = cmp == 0;
+ break;
+ case CONDITIONAL_ACE_TOKEN_NOT_EQUAL:
+ answer = cmp != 0;
+ break;
+ case CONDITIONAL_ACE_TOKEN_LESS_THAN:
+ answer = cmp < 0;
+ break;
+ case CONDITIONAL_ACE_TOKEN_LESS_OR_EQUAL:
+ answer = cmp <= 0;
+ break;
+ case CONDITIONAL_ACE_TOKEN_GREATER_THAN:
+ answer = cmp > 0;
+ break;
+ case CONDITIONAL_ACE_TOKEN_GREATER_OR_EQUAL:
+ answer = cmp >= 0;
+ break;
+ default:
+ result->data.result.value = ACE_CONDITION_UNKNOWN;
+ return false;
+ }
+ result->data.result.value = \
+ answer ? ACE_CONDITION_TRUE : ACE_CONDITION_FALSE;
+ return true;
+}
+
+
+
+static bool compare_unicode(const struct ace_condition_token *op,
+ const struct ace_condition_token *lhs,
+ const struct ace_condition_token *rhs,
+ int *cmp)
+{
+ struct ace_condition_unicode a = lhs->data.unicode;
+ struct ace_condition_unicode b = rhs->data.unicode;
+ /*
+ * Comparison is case-insensitive UNLESS the claim structure
+ * has the case-sensitive flag, which is passed through as a
+ * flag on the token. Usually only the LHS is a claim value,
+ * but in the event that they both are, we allow either to
+ * request case-sensitivity.
+ *
+ * For greater than and less than, the sort order is utf-8 order,
+ * which is not exactly what Windows does, but we don't sort like
+ * Windows does anywhere else either.
+ */
+ uint8_t flags = lhs->flags | rhs->flags;
+ if (flags & CLAIM_SECURITY_ATTRIBUTE_VALUE_CASE_SENSITIVE) {
+ *cmp = strcmp(a.value, b.value);
+ } else {
+ *cmp = strcasecmp_m(a.value, b.value);
+ }
+ return true;
+}
+
+
+static bool compare_bytes(const struct ace_condition_token *op,
+ const struct ace_condition_token *lhs,
+ const struct ace_condition_token *rhs,
+ int *cmp)
+{
+ DATA_BLOB a = lhs->data.bytes;
+ DATA_BLOB b = rhs->data.bytes;
+ *cmp = data_blob_cmp(&a, &b);
+ return true;
+}
+
+
+static bool compare_sids(const struct ace_condition_token *op,
+ const struct ace_condition_token *lhs,
+ const struct ace_condition_token *rhs,
+ int *cmp)
+{
+ *cmp = dom_sid_compare(&lhs->data.sid.sid,
+ &rhs->data.sid.sid);
+ return true;
+}
+
+
+static bool compare_ints(const struct ace_condition_token *op,
+ const struct ace_condition_token *lhs,
+ const struct ace_condition_token *rhs,
+ int *cmp)
+{
+ int64_t a = lhs->data.int64.value;
+ int64_t b = rhs->data.int64.value;
+
+ if (a < b) {
+ *cmp = -1;
+ } else if (a == b) {
+ *cmp = 0;
+ } else {
+ *cmp = 1;
+ }
+ return true;
+}
+
+
+static bool compare_bools(const struct ace_condition_token *op,
+ const struct ace_condition_token *lhs,
+ const struct ace_condition_token *rhs,
+ int *cmp)
+{
+ bool ok;
+ struct ace_condition_token a, b;
+ *cmp = -1;
+
+ if (IS_LITERAL_TOKEN(lhs)) {
+ /*
+ * we can compare a boolean LHS to a literal RHS, but not
+ * vice versa
+ */
+ return false;
+ }
+ ok = ternary_value(lhs, &a);
+ if (! ok) {
+ return false;
+ }
+ ok = ternary_value(rhs, &b);
+ if (! ok) {
+ return false;
+ }
+ if (a.data.result.value == ACE_CONDITION_UNKNOWN ||
+ b.data.result.value == ACE_CONDITION_UNKNOWN) {
+ return false;
+ }
+
+ switch (op->type) {
+ case CONDITIONAL_ACE_TOKEN_EQUAL:
+ case CONDITIONAL_ACE_TOKEN_NOT_EQUAL:
+ *cmp = a.data.result.value - b.data.result.value;
+ break;
+ default:
+ /* we are not allowing non-equality comparisons with bools */
+ return false;
+ }
+ return true;
+}
+
+
+static bool simple_relational_operator(const struct ace_condition_token *op,
+ const struct ace_condition_token *lhs,
+ const struct ace_condition_token *rhs,
+ int *cmp);
+
+
+struct composite_sort_context {
+ bool failed;
+};
+
+static int composite_sort_cmp(const struct ace_condition_token *lhs,
+ const struct ace_condition_token *rhs,
+ struct composite_sort_context *ctx)
+{
+ bool ok;
+ int cmp = -1;
+ /*
+ * simple_relational_operator uses the operator token only to
+ * decide whether the comparison is allowed for the type. In
+ * particular, boolean result and composite arguments can only
+ * be used with equality operators. We want those to fail (we
+ * should not see them here, remembering that claim booleans
+ * become composite integers), so we use a non-equality op.
+ */
+ static const struct ace_condition_token op = {
+ .type = CONDITIONAL_ACE_TOKEN_LESS_THAN
+ };
+
+ ok = simple_relational_operator(&op, lhs, rhs, &cmp);
+ if (ok) {
+ return cmp;
+ }
+ /*
+ * This sort isn't going to work out, but the sort function
+ * will only find out at the end.
+ */
+ ctx->failed = true;
+ return cmp;
+}
+
+
+/*
+ * Return a sorted copy of the composite tokens array.
+ *
+ * The copy is shallow, so the actual string pointers are the same, which is
+ * fine for the purposes of comparison.
+ */
+
+static struct ace_condition_token *composite_sorted_copy(
+ TALLOC_CTX *mem_ctx,
+ const struct ace_condition_composite *c,
+ bool case_sensitive)
+{
+ struct ace_condition_token *copy = NULL;
+ bool ok;
+ size_t i;
+ struct composite_sort_context sort_ctx = {
+ .failed = false
+ };
+
+ /*
+ * Case sensitivity is a bit tricky. Each token can have a flag saying
+ * it should be sorted case-sensitively and when comparing two tokens,
+ * we should respect this flag on either side. The flag can only come
+ * from claims (including resource attribute ACEs), and as there is only
+ * one flag per claim, it must apply the same to all members (in fact we
+ * don't set it on the members, only the composite). So to be sure we
+ * sort in the way we want, we might need to set the flag on all the
+ * members of the copy *before* sorting it.
+ *
+ * When it comes to comparing two composites, we want to be
+ * case-sensitive if either side has the flag. This can have odd
+ * effects. Think of these RA claims:
+ *
+ * (RA;;;;;WD;("foo",TS,0,"a","A"))
+ * (RA;;;;;WD;("bar",TS,2,"a","A")) <-- 2 is the case-sensitive flag
+ * (RA;;;;;WD;("baz",TS,0,"a"))
+ *
+ * (@Resource.foo == @Resource.bar) is true
+ * (@Resource.bar == @Resource.foo) is true
+ * (@Resource.bar == @Resource.bar) is true
+ * (@Resource.foo == @Resource.foo) is an error (duplicate values on LHS)
+ * (@Resource.baz == @Resource.foo) is true (RHS case-folds down)
+ * (@Resource.baz == @Resource.bar) is false
+ * (@Resource.bar == {"A", "a"}) is true
+ * (@Resource.baz == {"A", "a"}) is true
+ * (@Resource.foo == {"A", "a"}) is an error
+ */
+ copy = talloc_array(mem_ctx, struct ace_condition_token, c->n_members);
+ if (copy == NULL) {
+ return NULL;
+ }
+ memcpy(copy, c->tokens, sizeof(struct ace_condition_token) * c->n_members);
+
+ if (case_sensitive) {
+ for (i = 0; i < c->n_members; i++) {
+ c->tokens[i].flags |= CLAIM_SECURITY_ATTRIBUTE_VALUE_CASE_SENSITIVE;
+ }
+ }
+
+ ok = stable_sort_talloc_r(mem_ctx,
+ copy,
+ c->n_members,
+ sizeof(struct ace_condition_token),
+ (samba_compare_with_context_fn_t)composite_sort_cmp,
+ &sort_ctx);
+
+ if (!ok || sort_ctx.failed) {
+ DBG_NOTICE("composite sort of %"PRIu32" members failed\n",
+ c->n_members);
+ TALLOC_FREE(copy);
+ return NULL;
+ }
+ return copy;
+}
+
+
+/*
+ * This is a helper for compare composites.
+ */
+static bool compare_composites_via_sort(const struct ace_condition_token *lhs,
+ const struct ace_condition_token *rhs,
+ int *cmp)
+{
+ const struct ace_condition_composite *lc = &lhs->data.composite;
+ const struct ace_condition_composite *rc = &rhs->data.composite;
+ size_t i;
+ TALLOC_CTX *tmp_ctx = NULL;
+ bool ok;
+ int cmp_pair;
+ bool case_sensitive, rhs_case_sensitive;
+ bool rhs_sorted;
+ struct ace_condition_token *ltok = lc->tokens;
+ struct ace_condition_token *rtok = rc->tokens;
+ static const struct ace_condition_token eq = {
+ .type = CONDITIONAL_ACE_TOKEN_EQUAL
+ };
+ *cmp = -1;
+ if (lc->n_members == 0 ||
+ rc->n_members < lc->n_members) {
+ /* we should not have got this far */
+ return false;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return false;
+ }
+
+ case_sensitive = lhs->flags & CLAIM_SECURITY_ATTRIBUTE_VALUE_CASE_SENSITIVE;
+ rhs_case_sensitive = rhs->flags & CLAIM_SECURITY_ATTRIBUTE_VALUE_CASE_SENSITIVE;
+ rhs_sorted = rhs->flags & CLAIM_SECURITY_ATTRIBUTE_UNIQUE_AND_SORTED;
+
+ if (lc->tokens[0].type != CONDITIONAL_ACE_TOKEN_UNICODE) {
+ /*
+ * All LHS tokens are the same type (because it is a
+ * claim), and that type is not one that cares about
+ * case, so nor do we.
+ */
+ case_sensitive = false;
+ } else if (case_sensitive == rhs_case_sensitive) {
+ /* phew, no extra work */
+ } else if (case_sensitive) {
+ /* trigger a sorted copy */
+ rhs_sorted = false;
+ } else if (rhs_case_sensitive) {
+ /*
+ * Do we need to rescan for uniqueness, given the new
+ * comparison function? No! The strings were already
+ * unique in the looser comparison, and now they can
+ * only be more so. The number of unique values can't
+ * change, just their order.
+ */
+ case_sensitive = true;
+ ltok = composite_sorted_copy(tmp_ctx, lc, case_sensitive);
+ if (ltok == NULL) {
+ DBG_WARNING("sort of LHS failed\n");
+ goto error;
+ }
+ }
+
+ if (! rhs_sorted) {
+ /*
+ * we need an RHS sorted copy (it's a literal, or
+ * there was a case sensitivity disagreement).
+ */
+ rtok = composite_sorted_copy(tmp_ctx, rc, case_sensitive);
+ if (rtok == NULL) {
+ DBG_WARNING("sort of RHS failed\n");
+ goto error;
+ }
+ }
+ /*
+ * Each member of LHS must match one or more members of RHS.
+ * Each member of RHS must match at least one of LHS.
+ *
+ * If they are the same length we can compare directly, so let's get
+ * rid of duplicates in RHS. This can only happen with literal
+ * composites.
+ */
+ if (rc->n_members > lc->n_members) {
+ size_t gap = 0;
+ for (i = 1; i < rc->n_members; i++) {
+ ok = simple_relational_operator(&eq,
+ &rtok[i - 1],
+ &rtok[i],
+ &cmp_pair);
+ if (! ok) {
+ goto error;
+ }
+ if (cmp_pair == 0) {
+ gap++;
+ }
+ if (gap != 0) {
+ rtok[i - gap] = rtok[i];
+ }
+ }
+ if (rc->n_members - lc->n_members != gap) {
+ /*
+ * There were too many or too few duplicates to account
+ * for the difference, and no further comparison is
+ * necessary.
+ */
+ goto not_equal;
+ }
+ }
+ /*
+ * OK, now we know LHS and RHS are the same length and sorted in the
+ * same way, so we can just iterate over them and check each pair.
+ */
+
+ for (i = 0; i < lc->n_members; i++) {
+ ok = simple_relational_operator(&eq,
+ &ltok[i],
+ &rtok[i],
+ &cmp_pair);
+ if (! ok){
+ goto error;
+ }
+ if (cmp_pair != 0) {
+ goto not_equal;
+ }
+ }
+
+ *cmp = 0;
+
+not_equal:
+ TALLOC_FREE(tmp_ctx);
+ return true;
+error:
+ TALLOC_FREE(tmp_ctx);
+ return false;
+}
+
+
+static bool composite_is_comparable(const struct ace_condition_token *tok,
+ const struct ace_condition_token *comp)
+{
+ /*
+ * Are all members of the composite comparable to the token?
+ */
+ size_t i;
+ const struct ace_condition_composite *rc = &comp->data.composite;
+ size_t n = rc->n_members;
+
+ if ((comp->flags & CLAIM_SECURITY_ATTRIBUTE_UNIQUE_AND_SORTED) &&
+ n > 1) {
+ /*
+ * all members are known to be the same type, so we
+ * can just check one.
+ */
+ n = 1;
+ }
+
+ for (i = 0; i < n; i++) {
+ if (! tokens_are_comparable(NULL,
+ tok,
+ &rc->tokens[i])) {
+ DBG_NOTICE("token type %u != composite type %u\n",
+ tok->type, rc->tokens[i].type);
+ return false;
+ }
+ }
+ return true;
+}
+
+
+static bool compare_composites(const struct ace_condition_token *op,
+ const struct ace_condition_token *lhs,
+ const struct ace_condition_token *rhs,
+ int *cmp)
+{
+ /*
+ * This is for comparing multivalued sets, which includes
+ * conditional ACE composites and claim sets. Because these
+ * are sets, there are no < and > operations, just equality or
+ * otherwise.
+ *
+ * Claims are true sets, while composites are multisets --
+ * duplicate values are allowed -- but these are reduced to
+ * sets in evaluation, and the number of duplicates has no
+ * effect in comparisons. Resource attribute ACEs live in an
+ * intermediate state -- they can contain duplicates on the
+ * wire and as ACE structures, but as soon as they are
+ * evaluated as claims their values must be unique. Windows
+ * will treat RA ACEs with duplicate values as not existing,
+ * rather than as UNKNOWN (This is significant for the Exists
+ * operator). Claims can have a case-sensitive flags set,
+ * meaning they must be compared case-sensitively.
+ *
+ * Some good news is that the LHS of a comparison must always
+ * be a claim. That means we can assume it has unique values
+ * when it comes to pairwise comparisons. Using the magic of
+ * flags, we try to check this only once per claim.
+ *
+ * Conditional ACE composites, which can have duplicates (and
+ * mixed types), can only be on the RHS.
+ *
+ * To summarise:
+ *
+ * {a, b} vs {a, b} equal
+ * { } vs { } equal
+ * {a, b} vs {b, a} equal
+ * {a, b} vs {a, c} not equal
+ * {a, b} vs {a, a, b} equal
+ * {b, a} vs {a, b, a} equal
+ * {a, b} vs {a, a, b, c} not equal
+ * {a, b, a} vs {a, b} should not happen, error
+ * {a, b, a} vs {a, b, a} should not happen, error
+ *
+ * mixed types:
+ * {1, 2} vs {1, "2"} error
+ * {1, "2"} vs {1, "2"} should not happen, error
+ *
+ * case sensitivity (*{ }* indicates case-sensitive flag):
+ *
+ * {"a", "b"} vs {"a", "B"} equal
+ * {"a", "b"} vs *{"a", "B"}* not equal
+ * *{"a", "b"}* vs {"a", "B"} not equal
+ * *{"a", "A"}* vs {"a", "A"} equal (if RHS is composite)
+ * {"a", "A"} vs *{"a", "A"}* impossible (LHS is not unique)
+ * *{"a"}* vs {"a", "A"} not equal
+ *
+ * The naive approach is of course O(n * m) with an additional O(n²)
+ * if the LHS values are not known to be unique (that is, in resource
+ * attribute claims). We want to avoid that with big sets.
+ */
+ const struct ace_condition_composite *lc = &lhs->data.composite;
+ const struct ace_condition_composite *rc = &rhs->data.composite;
+ bool ok;
+
+ if (!(lhs->flags & CLAIM_SECURITY_ATTRIBUTE_UNIQUE_AND_SORTED)) {
+ /*
+ * The LHS needs to be a claim, and it should have gone
+ * through claim_v1_check_and_sort() to get here.
+ */
+ *cmp = -1;
+ return false;
+ }
+
+ /* if one or both are empty, the answer is easy */
+ if (lc->n_members == 0) {
+ if (rc->n_members == 0) {
+ *cmp = 0;
+ return true;
+ }
+ *cmp = -1;
+ return true;
+ }
+ if (rc->n_members == 0) {
+ *cmp = -1;
+ return true;
+ }
+
+ /*
+ * LHS must be a claim, so it must be unique, so if there are
+ * fewer members on the RHS, we know they can't be equal.
+ *
+ * If you think about it too much, you might think this is
+ * affected by case sensitivity, but it isn't. One side can be
+ * infected by case-sensitivity by the other, but that can't
+ * shrink the number of elements on the RHS -- it can only
+ * make a literal {"a", "A"} have effective length 2 rather
+ * than 1.
+ *
+ * On the other hand, if the RHS is case sensitive, it must be
+ * a claim and unique in its own terms, and its finer-grained
+ * distinctions can't collapse members of the case sensitive
+ * LHS.
+ */
+ if (lc->n_members > rc->n_members) {
+ *cmp = -1;
+ return composite_is_comparable(&lc->tokens[0], rhs);
+ }
+
+ /*
+ * It *could* be that RHS is also unique and we know it. In that
+ * case we can short circuit if RHS has more members. This is
+ * the case when both sides are claims.
+ *
+ * This is also not affected by case-senstivity.
+ */
+ if (lc->n_members < rc->n_members &&
+ (rhs->flags & CLAIM_SECURITY_ATTRIBUTE_UNIQUE_AND_SORTED)) {
+ *cmp = -1;
+ return composite_is_comparable(&lc->tokens[0], rhs);
+ }
+
+ ok = compare_composites_via_sort(lhs, rhs, cmp);
+ if (! ok) {
+ return false;
+ }
+ return true;
+}
+
+
+static bool simple_relational_operator(const struct ace_condition_token *op,
+ const struct ace_condition_token *lhs,
+ const struct ace_condition_token *rhs,
+ int *cmp)
+
+{
+ if (lhs->type != rhs->type) {
+ if (! tokens_are_comparable(op, lhs, rhs)) {
+ return false;
+ }
+ }
+ switch (lhs->type) {
+ case CONDITIONAL_ACE_TOKEN_INT8:
+ case CONDITIONAL_ACE_TOKEN_INT16:
+ case CONDITIONAL_ACE_TOKEN_INT32:
+ case CONDITIONAL_ACE_TOKEN_INT64:
+ if (rhs->type == CONDITIONAL_ACE_SAMBA_RESULT_BOOL) {
+ return compare_bools(op, lhs, rhs, cmp);
+ }
+ return compare_ints(op, lhs, rhs, cmp);
+ case CONDITIONAL_ACE_SAMBA_RESULT_BOOL:
+ return compare_bools(op, lhs, rhs, cmp);
+ case CONDITIONAL_ACE_TOKEN_UNICODE:
+ return compare_unicode(op, lhs, rhs, cmp);
+ case CONDITIONAL_ACE_TOKEN_OCTET_STRING:
+ return compare_bytes(op, lhs, rhs, cmp);
+ case CONDITIONAL_ACE_TOKEN_SID:
+ return compare_sids(op, lhs, rhs, cmp);
+ case CONDITIONAL_ACE_TOKEN_COMPOSITE:
+ return compare_composites(op, lhs, rhs, cmp);
+ case CONDITIONAL_ACE_SAMBA_RESULT_NULL:
+ /* leave the result unknown */
+ return false;
+ default:
+ DBG_ERR("did not expect ace type %u\n", lhs->type);
+ return false;
+ }
+
+ return false;
+}
+
+
+static bool find_in_composite(const struct ace_condition_token *tok,
+ struct ace_condition_composite candidates,
+ bool *answer)
+{
+ size_t i;
+ int cmp;
+ bool ok;
+ const struct ace_condition_token equals = {
+ .type = CONDITIONAL_ACE_TOKEN_EQUAL
+ };
+
+ *answer = false;
+
+ for (i = 0; i < candidates.n_members; i++) {
+ ok = simple_relational_operator(&equals,
+ tok,
+ &candidates.tokens[i],
+ &cmp);
+ if (! ok) {
+ return false;
+ }
+ if (cmp == 0) {
+ *answer = true;
+ return true;
+ }
+ }
+ return true;
+}
+
+
+static bool contains_operator(const struct ace_condition_token *lhs,
+ const struct ace_condition_token *rhs,
+ bool *answer)
+{
+ size_t i;
+ bool ok;
+ int cmp;
+ const struct ace_condition_token equals = {
+ .type = CONDITIONAL_ACE_TOKEN_EQUAL
+ };
+
+ /*
+ * All the required objects must be identical to something in
+ * candidates. But what do we mean by *identical*? We'll use
+ * the equality operator to decide that.
+ *
+ * Both the lhs or rhs can be solitary objects or composites.
+ * This makes it a bit fiddlier.
+ *
+ * NOTE: this operator does not take advantage of the
+ * CLAIM_SECURITY_ATTRIBUTE_UNIQUE_AND_SORTED flag. It could, but it
+ * doesn't.
+ */
+ if (lhs->type == CONDITIONAL_ACE_TOKEN_COMPOSITE) {
+ struct ace_condition_composite candidates = lhs->data.composite;
+ struct ace_condition_composite required;
+ if (rhs->type != CONDITIONAL_ACE_TOKEN_COMPOSITE) {
+ return find_in_composite(rhs, candidates, answer);
+ }
+ required = rhs->data.composite;
+ if (required.n_members == 0) {
+ return false;
+ }
+ for (i = 0; i < required.n_members; i++) {
+ const struct ace_condition_token *t = &required.tokens[i];
+ ok = find_in_composite(t, candidates, answer);
+ if (! ok) {
+ return false;
+ }
+ if (! *answer) {
+ /*
+ * one required item was not there,
+ * *answer is false
+ */
+ return true;
+ }
+ }
+ /* all required items are there, *answer will be true */
+ return true;
+ }
+ /* LHS is a single item */
+ if (rhs->type == CONDITIONAL_ACE_TOKEN_COMPOSITE) {
+ /*
+ * There could be more than one RHS member that is
+ * equal to the single LHS value, so it doesn't help
+ * to compare lengths or anything.
+ */
+ struct ace_condition_composite required = rhs->data.composite;
+ if (required.n_members == 0) {
+ return false;
+ }
+ for (i = 0; i < required.n_members; i++) {
+ ok = simple_relational_operator(&equals,
+ lhs,
+ &required.tokens[i],
+ &cmp);
+ if (! ok) {
+ return false;
+ }
+ if (cmp != 0) {
+ /*
+ * one required item was not there,
+ * *answer is false
+ */
+ *answer = false;
+ return true;
+ }
+ }
+ *answer = true;
+ return true;
+ }
+ /* LHS and RHS are both single */
+ ok = simple_relational_operator(&equals,
+ lhs,
+ rhs,
+ &cmp);
+ if (! ok) {
+ return false;
+ }
+ *answer = (cmp == 0);
+ return true;
+}
+
+
+static bool any_of_operator(const struct ace_condition_token *lhs,
+ const struct ace_condition_token *rhs,
+ bool *answer)
+{
+ size_t i;
+ bool ok;
+ int cmp;
+ const struct ace_condition_token equals = {
+ .type = CONDITIONAL_ACE_TOKEN_EQUAL
+ };
+
+ /*
+ * There has to be *some* overlap between the LHS and RHS.
+ * Both sides can be solitary objects or composites.
+ *
+ * We can exploit this symmetry.
+ */
+ if (lhs->type != CONDITIONAL_ACE_TOKEN_COMPOSITE) {
+ const struct ace_condition_token *tmp = lhs;
+ lhs = rhs;
+ rhs = tmp;
+ }
+ if (lhs->type != CONDITIONAL_ACE_TOKEN_COMPOSITE) {
+ /* both singles */
+ ok = simple_relational_operator(&equals,
+ lhs,
+ rhs,
+ &cmp);
+ if (! ok) {
+ return false;
+ }
+ *answer = (cmp == 0);
+ return true;
+ }
+ if (rhs->type != CONDITIONAL_ACE_TOKEN_COMPOSITE) {
+ return find_in_composite(rhs, lhs->data.composite, answer);
+ }
+ /* both are composites */
+ if (lhs->data.composite.n_members == 0) {
+ return false;
+ }
+ for (i = 0; i < lhs->data.composite.n_members; i++) {
+ ok = find_in_composite(&lhs->data.composite.tokens[i],
+ rhs->data.composite,
+ answer);
+ if (! ok) {
+ return false;
+ }
+ if (*answer) {
+ /* We have found one match, which is enough. */
+ return true;
+ }
+ }
+ return true;
+}
+
+
+static bool composite_relational_operator(const struct ace_condition_token *op,
+ const struct ace_condition_token *lhs,
+ const struct ace_condition_token *rhs,
+ struct ace_condition_token *result)
+{
+ bool ok, answer;
+ switch(op->type) {
+ case CONDITIONAL_ACE_TOKEN_CONTAINS:
+ case CONDITIONAL_ACE_TOKEN_NOT_CONTAINS:
+ ok = contains_operator(lhs, rhs, &answer);
+ break;
+ case CONDITIONAL_ACE_TOKEN_ANY_OF:
+ case CONDITIONAL_ACE_TOKEN_NOT_ANY_OF:
+ ok = any_of_operator(lhs, rhs, &answer);
+ break;
+ default:
+ return false;
+ }
+ if (!ok) {
+ return false;
+ }
+
+ /* negate the NOTs */
+ if (op->type == CONDITIONAL_ACE_TOKEN_NOT_CONTAINS ||
+ op->type == CONDITIONAL_ACE_TOKEN_NOT_ANY_OF)
+ {
+ answer = !answer;
+ }
+
+ if (answer) {
+ result->data.result.value = ACE_CONDITION_TRUE;
+ } else {
+ result->data.result.value = ACE_CONDITION_FALSE;
+ }
+ return true;
+}
+
+
+static bool relational_operator(
+ const struct security_token *token,
+ const struct ace_condition_token *op,
+ const struct ace_condition_token *lhs,
+ const struct ace_condition_token *rhs,
+ struct ace_condition_token *result)
+{
+ int cmp;
+ bool ok;
+ result->type = CONDITIONAL_ACE_SAMBA_RESULT_BOOL;
+ result->data.result.value = ACE_CONDITION_UNKNOWN;
+
+ if ((lhs->flags & CONDITIONAL_ACE_FLAG_TOKEN_FROM_ATTR) == 0) {
+ /* LHS was not derived from an attribute */
+ return false;
+ }
+
+ /*
+ * This first nested switch is ensuring that >, >=, <, <= are
+ * not being tried on tokens that are not numbers, strings, or
+ * octet strings. Equality operators are available for all types.
+ */
+ switch (lhs->type) {
+ case CONDITIONAL_ACE_TOKEN_INT8:
+ case CONDITIONAL_ACE_TOKEN_INT16:
+ case CONDITIONAL_ACE_TOKEN_INT32:
+ case CONDITIONAL_ACE_TOKEN_INT64:
+ case CONDITIONAL_ACE_TOKEN_UNICODE:
+ case CONDITIONAL_ACE_TOKEN_OCTET_STRING:
+ break;
+ default:
+ switch(op->type) {
+ case CONDITIONAL_ACE_TOKEN_LESS_THAN:
+ case CONDITIONAL_ACE_TOKEN_LESS_OR_EQUAL:
+ case CONDITIONAL_ACE_TOKEN_GREATER_THAN:
+ case CONDITIONAL_ACE_TOKEN_GREATER_OR_EQUAL:
+ return false;
+ default:
+ break;
+ }
+ }
+
+ /*
+ * Dispatch according to operator type.
+ */
+ switch (op->type) {
+ case CONDITIONAL_ACE_TOKEN_EQUAL:
+ case CONDITIONAL_ACE_TOKEN_NOT_EQUAL:
+ case CONDITIONAL_ACE_TOKEN_LESS_THAN:
+ case CONDITIONAL_ACE_TOKEN_LESS_OR_EQUAL:
+ case CONDITIONAL_ACE_TOKEN_GREATER_THAN:
+ case CONDITIONAL_ACE_TOKEN_GREATER_OR_EQUAL:
+ ok = simple_relational_operator(op,
+ lhs,
+ rhs,
+ &cmp);
+ if (ok) {
+ ok = cmp_to_result(op, result, cmp);
+ }
+ return ok;
+
+ case CONDITIONAL_ACE_TOKEN_CONTAINS:
+ case CONDITIONAL_ACE_TOKEN_ANY_OF:
+ case CONDITIONAL_ACE_TOKEN_NOT_CONTAINS:
+ case CONDITIONAL_ACE_TOKEN_NOT_ANY_OF:
+ return composite_relational_operator(op,
+ lhs,
+ rhs,
+ result);
+ default:
+ return false;
+ }
+}
+
+
+int run_conditional_ace(TALLOC_CTX *mem_ctx,
+ const struct security_token *token,
+ struct ace_condition_script *program,
+ const struct security_descriptor *sd)
+{
+ size_t i;
+ size_t depth = 0;
+ struct ace_condition_token *lhs = NULL;
+ struct ace_condition_token *rhs = NULL;
+ struct ace_condition_token result = {};
+ bool ok;
+
+ for (i = 0; i < program->length; i++) {
+ struct ace_condition_token *tok = &program->tokens[i];
+ switch (tok->type) {
+ case CONDITIONAL_ACE_TOKEN_INT8:
+ case CONDITIONAL_ACE_TOKEN_INT16:
+ case CONDITIONAL_ACE_TOKEN_INT32:
+ case CONDITIONAL_ACE_TOKEN_INT64:
+ case CONDITIONAL_ACE_TOKEN_UNICODE:
+ case CONDITIONAL_ACE_TOKEN_OCTET_STRING:
+ case CONDITIONAL_ACE_TOKEN_SID:
+ case CONDITIONAL_ACE_TOKEN_COMPOSITE:
+ /* just plonk these literals on the stack */
+ program->stack[depth] = *tok;
+ depth++;
+ break;
+
+ case CONDITIONAL_ACE_LOCAL_ATTRIBUTE:
+ case CONDITIONAL_ACE_USER_ATTRIBUTE:
+ case CONDITIONAL_ACE_DEVICE_ATTRIBUTE:
+ ok = token_claim_lookup(mem_ctx, token, tok, &result);
+ if (! ok) {
+ goto error;
+ }
+ program->stack[depth] = result;
+ depth++;
+ break;
+
+ case CONDITIONAL_ACE_RESOURCE_ATTRIBUTE:
+ ok = resource_claim_lookup(mem_ctx,
+ tok,
+ sd,
+ &result);
+ if (! ok) {
+ goto error;
+ }
+ program->stack[depth] = result;
+ depth++;
+ break;
+
+ case CONDITIONAL_ACE_TOKEN_MEMBER_OF:
+ case CONDITIONAL_ACE_TOKEN_DEVICE_MEMBER_OF:
+ case CONDITIONAL_ACE_TOKEN_MEMBER_OF_ANY:
+ case CONDITIONAL_ACE_TOKEN_DEVICE_MEMBER_OF_ANY:
+ case CONDITIONAL_ACE_TOKEN_NOT_MEMBER_OF:
+ case CONDITIONAL_ACE_TOKEN_NOT_DEVICE_MEMBER_OF:
+ case CONDITIONAL_ACE_TOKEN_NOT_MEMBER_OF_ANY:
+ case CONDITIONAL_ACE_TOKEN_NOT_DEVICE_MEMBER_OF_ANY:
+ if (depth == 0) {
+ goto error;
+ }
+ depth--;
+ lhs = &program->stack[depth];
+ ok = member_lookup(token, tok, lhs, &result);
+ if (! ok) {
+ goto error;
+ }
+ program->stack[depth] = result;
+ depth++;
+ break;
+ /* binary relational operators */
+ case CONDITIONAL_ACE_TOKEN_EQUAL:
+ case CONDITIONAL_ACE_TOKEN_NOT_EQUAL:
+ case CONDITIONAL_ACE_TOKEN_LESS_THAN:
+ case CONDITIONAL_ACE_TOKEN_LESS_OR_EQUAL:
+ case CONDITIONAL_ACE_TOKEN_GREATER_THAN:
+ case CONDITIONAL_ACE_TOKEN_GREATER_OR_EQUAL:
+ case CONDITIONAL_ACE_TOKEN_CONTAINS:
+ case CONDITIONAL_ACE_TOKEN_ANY_OF:
+ case CONDITIONAL_ACE_TOKEN_NOT_CONTAINS:
+ case CONDITIONAL_ACE_TOKEN_NOT_ANY_OF:
+ if (depth < 2) {
+ goto error;
+ }
+ depth--;
+ rhs = &program->stack[depth];
+ depth--;
+ lhs = &program->stack[depth];
+ ok = relational_operator(token, tok, lhs, rhs, &result);
+ if (! ok) {
+ goto error;
+ }
+ program->stack[depth] = result;
+ depth++;
+ break;
+ /* unary logical operators */
+ case CONDITIONAL_ACE_TOKEN_EXISTS:
+ case CONDITIONAL_ACE_TOKEN_NOT_EXISTS:
+ case CONDITIONAL_ACE_TOKEN_NOT:
+ if (depth == 0) {
+ goto error;
+ }
+ depth--;
+ lhs = &program->stack[depth];
+ ok = unary_logic_operator(mem_ctx, token, tok, lhs, sd, &result);
+ if (!ok) {
+ goto error;
+ }
+ program->stack[depth] = result;
+ depth++;
+ break;
+ /* binary logical operators */
+ case CONDITIONAL_ACE_TOKEN_AND:
+ case CONDITIONAL_ACE_TOKEN_OR:
+ if (depth < 2) {
+ goto error;
+ }
+ depth--;
+ rhs = &program->stack[depth];
+ depth--;
+ lhs = &program->stack[depth];
+ ok = binary_logic_operator(token, tok, lhs, rhs, &result);
+ if (! ok) {
+ goto error;
+ }
+ program->stack[depth] = result;
+ depth++;
+ break;
+ default:
+ goto error;
+ }
+ }
+ /*
+ * The evaluation should have left a single result value (true, false,
+ * or unknown) on the stack. If not, the expression was malformed.
+ */
+ if (depth != 1) {
+ goto error;
+ }
+ result = program->stack[0];
+ if (result.type != CONDITIONAL_ACE_SAMBA_RESULT_BOOL) {
+ goto error;
+ }
+
+ return result.data.result.value;
+
+ error:
+ /*
+ * the result of an error is always UNKNOWN, which should be
+ * interpreted pessimistically, not allowing access.
+ */
+ return ACE_CONDITION_UNKNOWN;
+}
+
+
+/** access_check_conditional_ace()
+ *
+ * Run the conditional ACE from the blob form. Return false if it is
+ * not a valid conditional ACE, true if it is, even if there is some
+ * other error in running it. The *result parameter is set to
+ * ACE_CONDITION_FALSE, ACE_CONDITION_TRUE, or ACE_CONDITION_UNKNOWN.
+ *
+ * ACE_CONDITION_UNKNOWN should be treated pessimistically, as if it were
+ * TRUE for deny ACEs, and FALSE for allow ACEs.
+ *
+ * @param[in] ace - the ACE being processed.
+ * @param[in] token - the security token the ACE is processing.
+ * @param[out] result - a ternary result value.
+ *
+ * @return true if it is a valid conditional ACE.
+ */
+
+bool access_check_conditional_ace(const struct security_ace *ace,
+ const struct security_token *token,
+ const struct security_descriptor *sd,
+ int *result)
+{
+ TALLOC_CTX *tmp_ctx = talloc_new(NULL);
+ struct ace_condition_script *program = NULL;
+ program = parse_conditional_ace(tmp_ctx, ace->coda.conditions);
+ if (program == NULL) {
+ *result = ACE_CONDITION_UNKNOWN;
+ TALLOC_FREE(tmp_ctx);
+ return false;
+ }
+
+ *result = run_conditional_ace(tmp_ctx, token, program, sd);
+
+ TALLOC_FREE(tmp_ctx);
+ return true;
+}
+
+
+bool conditional_ace_encode_binary(TALLOC_CTX *mem_ctx,
+ struct ace_condition_script *program,
+ DATA_BLOB *dest)
+{
+ size_t i, j, alloc_size, required_size;
+ uint8_t *data = NULL;
+ uint8_t *new_data = NULL;
+ *dest = (DATA_BLOB){NULL, 0};
+
+ alloc_size = CONDITIONAL_ACE_MAX_LENGTH;
+ data = talloc_array(mem_ctx,
+ uint8_t,
+ alloc_size);
+ if (data == NULL) {
+ return false;
+ }
+
+ data[0] = 'a';
+ data[1] = 'r';
+ data[2] = 't';
+ data[3] = 'x';
+
+ j = 4;
+ for (i = 0; i < program->length; i++) {
+ struct ace_condition_token *tok = &program->tokens[i];
+ ssize_t consumed;
+ bool ok;
+ /*
+ * In all cases we write the token type byte.
+ */
+ data[j] = tok->type;
+ j++;
+ if (j >= alloc_size) {
+ DBG_ERR("program exceeds %zu bytes\n", alloc_size);
+ goto error;
+ }
+
+ switch (tok->type) {
+ case CONDITIONAL_ACE_TOKEN_MEMBER_OF:
+ case CONDITIONAL_ACE_TOKEN_DEVICE_MEMBER_OF:
+ case CONDITIONAL_ACE_TOKEN_MEMBER_OF_ANY:
+ case CONDITIONAL_ACE_TOKEN_DEVICE_MEMBER_OF_ANY:
+ case CONDITIONAL_ACE_TOKEN_NOT_MEMBER_OF:
+ case CONDITIONAL_ACE_TOKEN_NOT_DEVICE_MEMBER_OF:
+ case CONDITIONAL_ACE_TOKEN_NOT_MEMBER_OF_ANY:
+ case CONDITIONAL_ACE_TOKEN_NOT_DEVICE_MEMBER_OF_ANY:
+ case CONDITIONAL_ACE_TOKEN_EQUAL:
+ case CONDITIONAL_ACE_TOKEN_NOT_EQUAL:
+ case CONDITIONAL_ACE_TOKEN_LESS_THAN:
+ case CONDITIONAL_ACE_TOKEN_LESS_OR_EQUAL:
+ case CONDITIONAL_ACE_TOKEN_GREATER_THAN:
+ case CONDITIONAL_ACE_TOKEN_GREATER_OR_EQUAL:
+ case CONDITIONAL_ACE_TOKEN_CONTAINS:
+ case CONDITIONAL_ACE_TOKEN_ANY_OF:
+ case CONDITIONAL_ACE_TOKEN_NOT_CONTAINS:
+ case CONDITIONAL_ACE_TOKEN_NOT_ANY_OF:
+ case CONDITIONAL_ACE_TOKEN_EXISTS:
+ case CONDITIONAL_ACE_TOKEN_NOT_EXISTS:
+ case CONDITIONAL_ACE_TOKEN_NOT:
+ case CONDITIONAL_ACE_TOKEN_AND:
+ case CONDITIONAL_ACE_TOKEN_OR:
+ /*
+ * All of these are simple operators that operate on
+ * the stack. We have already added the tok->type and
+ * there's nothing else to do.
+ */
+ continue;
+
+ case CONDITIONAL_ACE_TOKEN_INT8:
+ case CONDITIONAL_ACE_TOKEN_INT16:
+ case CONDITIONAL_ACE_TOKEN_INT32:
+ case CONDITIONAL_ACE_TOKEN_INT64:
+ ok = check_integer_range(tok);
+ if (! ok) {
+ goto error;
+ }
+ consumed = push_integer(data + j,
+ alloc_size - j,
+ &tok->data.int64);
+ break;
+ case CONDITIONAL_ACE_LOCAL_ATTRIBUTE:
+ case CONDITIONAL_ACE_USER_ATTRIBUTE:
+ case CONDITIONAL_ACE_RESOURCE_ATTRIBUTE:
+ case CONDITIONAL_ACE_DEVICE_ATTRIBUTE:
+ case CONDITIONAL_ACE_TOKEN_UNICODE:
+ consumed = push_unicode(data + j,
+ alloc_size - j,
+ &tok->data.unicode);
+ break;
+ case CONDITIONAL_ACE_TOKEN_OCTET_STRING:
+ consumed = push_bytes(data + j,
+ alloc_size - j,
+ &tok->data.bytes);
+ break;
+ case CONDITIONAL_ACE_TOKEN_SID:
+ consumed = push_sid(data + j,
+ alloc_size - j,
+ &tok->data.sid);
+ break;
+ case CONDITIONAL_ACE_TOKEN_COMPOSITE:
+ consumed = push_composite(data + j,
+ alloc_size - j,
+ &tok->data.composite);
+ break;
+
+ default:
+ DBG_ERR("unknown token 0x%02x at position %zu\n",
+ tok->type, i);
+ goto error;
+ }
+ if (consumed == -1) {
+ DBG_ERR("program exceeds %zu bytes\n", alloc_size);
+ goto error;
+ }
+ j += consumed;
+ if (j >= alloc_size) {
+ DBG_ERR("program exceeds %zu bytes\n", alloc_size);
+ goto error;
+ }
+ }
+ /* align to a 4 byte boundary */
+ required_size = (j + 3) & ~((size_t)3);
+ if (required_size > alloc_size) {
+ DBG_ERR("program exceeds %zu bytes\n", alloc_size);
+ goto error;
+ }
+ while (j < required_size) {
+ data[j] = 0;
+ j++;
+ }
+ new_data = talloc_realloc(mem_ctx,
+ data,
+ uint8_t,
+ required_size);
+ if (new_data == NULL) {
+ goto error;
+ }
+ data = new_data;
+
+ (*dest).data = data;
+ (*dest).length = j;
+ return true;
+ error:
+ TALLOC_FREE(data);
+ return false;
+}
diff --git a/libcli/security/conditional_ace.h b/libcli/security/conditional_ace.h
new file mode 100644
index 0000000..e592056
--- /dev/null
+++ b/libcli/security/conditional_ace.h
@@ -0,0 +1,97 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba utility functions
+
+ Copyright © Catalyst
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _CONDITIONAL_ACE_H_
+#define _CONDITIONAL_ACE_H_
+
+#include <talloc.h>
+#include "lib/util/data_blob.h"
+
+#include "librpc/gen_ndr/conditional_ace.h"
+
+
+struct ace_condition_script *parse_conditional_ace(TALLOC_CTX *mem_ctx,
+ DATA_BLOB data);
+
+int run_conditional_ace(TALLOC_CTX *mem_ctx,
+ const struct security_token *token,
+ struct ace_condition_script *program,
+ const struct security_descriptor *sd);
+
+
+bool access_check_conditional_ace(const struct security_ace *ace,
+ const struct security_token *token,
+ const struct security_descriptor *sd,
+ int *result);
+
+bool conditional_ace_encode_binary(TALLOC_CTX *mem_ctx,
+ struct ace_condition_script *program,
+ DATA_BLOB *dest);
+
+struct ace_condition_script * ace_conditions_compile_sddl(TALLOC_CTX *mem_ctx,
+ const enum ace_condition_flags ace_condition_flags,
+ const char *sddl,
+ const char **message,
+ size_t *message_offset,
+ size_t *consumed_length);
+
+char *debug_conditional_ace(TALLOC_CTX *mem_ctx,
+ struct ace_condition_script *program);
+
+char *sddl_from_conditional_ace(TALLOC_CTX *mem_ctx,
+ struct ace_condition_script *program);
+
+#define IS_INT_TOKEN(x) \
+ (((x)->type) == CONDITIONAL_ACE_TOKEN_INT64 || \
+ unlikely(((x)->type) == CONDITIONAL_ACE_TOKEN_INT32 || \
+ ((x)->type) == CONDITIONAL_ACE_TOKEN_INT16 || \
+ ((x)->type) == CONDITIONAL_ACE_TOKEN_INT8) \
+ )
+
+#define IS_BOOL_TOKEN(x) \
+ (((x)->type) == CONDITIONAL_ACE_SAMBA_RESULT_BOOL)
+
+#define IS_DERIVED_TOKEN(x) \
+ ((((x)->flags) & CONDITIONAL_ACE_FLAG_TOKEN_FROM_ATTR) == 0)
+
+#define IS_LITERAL_TOKEN(x) \
+ ((IS_INT_TOKEN(x) || \
+ ((x)->type) == CONDITIONAL_ACE_TOKEN_UNICODE || \
+ ((x)->type) == CONDITIONAL_ACE_TOKEN_OCTET_STRING || \
+ ((x)->type) == CONDITIONAL_ACE_TOKEN_SID || \
+ ((x)->type) == CONDITIONAL_ACE_TOKEN_COMPOSITE) && \
+ (! IS_DERIVED_TOKEN(x)))
+
+struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *parse_sddl_literal_as_claim(
+ TALLOC_CTX *mem_ctx,
+ const char *name,
+ const char *str);
+
+struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *sddl_decode_resource_attr (
+ TALLOC_CTX *mem_ctx,
+ const char *str,
+ size_t *length);
+
+char *sddl_resource_attr_from_claim(
+ TALLOC_CTX *mem_ctx,
+ const struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim);
+
+
+#endif /*_CONDITIONAL_ACE_H_*/
diff --git a/libcli/security/create_descriptor.c b/libcli/security/create_descriptor.c
new file mode 100644
index 0000000..4db23be
--- /dev/null
+++ b/libcli/security/create_descriptor.c
@@ -0,0 +1,666 @@
+/*
+ Copyright (C) Nadezhda Ivanova 2009
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ * Name: create_descriptor
+ *
+ * Component: routines for calculating and creating security descriptors
+ * as described in MS-DTYP 2.5.3.x
+ *
+ * Description:
+ *
+ *
+ * Author: Nadezhda Ivanova
+ */
+#include "replace.h"
+#include "lib/util/debug.h"
+#include "libcli/security/security.h"
+#include "librpc/gen_ndr/ndr_security.h"
+
+/* Todos:
+ * build the security token dacl as follows:
+ * SYSTEM: GA, OWNER: GA, LOGIN_SID:GW|GE
+ * Need session id information for the login SID. Probably
+ * the best place for this is during token creation
+ *
+ * Implement SD Invariants
+ * ACE sorting rules
+ * LDAP_SERVER_SD_FLAGS_OID control
+ * ADTS 7.1.3.3 needs to be clarified
+ */
+
+/* the mapping function for generic rights for DS.(GA,GR,GW,GX)
+ * The mapping function is passed as an argument to the
+ * descriptor calculating routine and depends on the security
+ * manager that calls the calculating routine.
+ * TODO: need similar mappings for the file system and
+ * registry security managers in order to make this code
+ * generic for all security managers
+ */
+
+uint32_t map_generic_rights_ds(uint32_t access_mask)
+{
+ if (access_mask & SEC_GENERIC_ALL) {
+ access_mask |= SEC_ADS_GENERIC_ALL;
+ access_mask &= ~SEC_GENERIC_ALL;
+ }
+
+ if (access_mask & SEC_GENERIC_EXECUTE) {
+ access_mask |= SEC_ADS_GENERIC_EXECUTE;
+ access_mask &= ~SEC_GENERIC_EXECUTE;
+ }
+
+ if (access_mask & SEC_GENERIC_WRITE) {
+ access_mask |= SEC_ADS_GENERIC_WRITE;
+ access_mask &= ~SEC_GENERIC_WRITE;
+ }
+
+ if (access_mask & SEC_GENERIC_READ) {
+ access_mask |= SEC_ADS_GENERIC_READ;
+ access_mask &= ~SEC_GENERIC_READ;
+ }
+
+ return access_mask;
+}
+
+/* Not sure what this has to be,
+* and it does not seem to have any influence */
+static bool object_in_list(const struct GUID *object_list, const struct GUID *object)
+{
+ size_t i;
+
+ if (object_list == NULL) {
+ return true;
+ }
+
+ if (GUID_all_zero(object)) {
+ return true;
+ }
+
+ for (i=0; ; i++) {
+ if (GUID_all_zero(&object_list[i])) {
+ return false;
+ }
+ if (!GUID_equal(&object_list[i], object)) {
+ continue;
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+/* returns true if the ACE gontains generic information
+ * that needs to be processed additionally */
+
+static bool desc_ace_has_generic(const struct security_ace *ace)
+{
+ if (ace->access_mask & SEC_GENERIC_ALL || ace->access_mask & SEC_GENERIC_READ ||
+ ace->access_mask & SEC_GENERIC_WRITE || ace->access_mask & SEC_GENERIC_EXECUTE) {
+ return true;
+ }
+ if (dom_sid_equal(&ace->trustee, &global_sid_Creator_Owner) ||
+ dom_sid_equal(&ace->trustee, &global_sid_Creator_Group)) {
+ return true;
+ }
+ return false;
+}
+
+/* creates an ace in which the generic information is expanded */
+
+static void desc_expand_generic(struct security_ace *new_ace,
+ struct dom_sid *owner,
+ struct dom_sid *group)
+{
+ new_ace->access_mask = map_generic_rights_ds(new_ace->access_mask);
+ if (dom_sid_equal(&new_ace->trustee, &global_sid_Creator_Owner)) {
+ new_ace->trustee = *owner;
+ }
+ if (dom_sid_equal(&new_ace->trustee, &global_sid_Creator_Group)) {
+ new_ace->trustee = *group;
+ }
+ new_ace->flags = 0x0;
+}
+
+static struct security_acl *calculate_inherited_from_parent(TALLOC_CTX *mem_ctx,
+ struct security_acl *acl,
+ bool is_container,
+ struct dom_sid *owner,
+ struct dom_sid *group,
+ struct GUID *object_list)
+{
+ uint32_t i;
+ struct security_acl *tmp_acl = NULL;
+
+ if (!acl) {
+ return NULL;
+ }
+ tmp_acl = talloc_zero(mem_ctx, struct security_acl);
+ if (!tmp_acl) {
+ return NULL;
+ }
+
+ for (i=0; i < acl->num_aces; i++) {
+ const struct security_ace *ace = &acl->aces[i];
+ const struct GUID *inherited_object = NULL;
+ const struct GUID *inherited_property = NULL;
+ struct security_ace *tmp_ace = NULL;
+ bool applies = false;
+ bool inherited_only = false;
+ bool expand_ace = false;
+ bool expand_only = false;
+
+ if (is_container && (ace->flags & SEC_ACE_FLAG_CONTAINER_INHERIT)) {
+ applies = true;
+ } else if (!is_container && (ace->flags & SEC_ACE_FLAG_OBJECT_INHERIT)) {
+ applies = true;
+ }
+
+ if (!applies) {
+ /*
+ * If the ace doesn't apply to the
+ * current node, we should only keep
+ * it as SEC_ACE_FLAG_OBJECT_INHERIT
+ * on a container. We'll add
+ * SEC_ACE_FLAG_INHERITED_ACE
+ * and SEC_ACE_FLAG_INHERIT_ONLY below.
+ *
+ * Otherwise we should completely ignore it.
+ */
+ if (!(ace->flags & SEC_ACE_FLAG_OBJECT_INHERIT)) {
+ continue;
+ }
+ }
+
+ switch (ace->type) {
+ case SEC_ACE_TYPE_ACCESS_ALLOWED:
+ case SEC_ACE_TYPE_ACCESS_DENIED:
+ case SEC_ACE_TYPE_SYSTEM_AUDIT:
+ case SEC_ACE_TYPE_SYSTEM_ALARM:
+ case SEC_ACE_TYPE_ALLOWED_COMPOUND:
+ break;
+
+ case SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT:
+ case SEC_ACE_TYPE_ACCESS_DENIED_OBJECT:
+ case SEC_ACE_TYPE_SYSTEM_ALARM_OBJECT:
+ case SEC_ACE_TYPE_SYSTEM_AUDIT_OBJECT:
+ case SEC_ACE_TYPE_ACCESS_ALLOWED_CALLBACK_OBJECT:
+ case SEC_ACE_TYPE_ACCESS_DENIED_CALLBACK_OBJECT:
+ case SEC_ACE_TYPE_SYSTEM_AUDIT_CALLBACK_OBJECT:
+ if (ace->object.object.flags & SEC_ACE_OBJECT_TYPE_PRESENT) {
+ inherited_property = &ace->object.object.type.type;
+ }
+ if (ace->object.object.flags & SEC_ACE_INHERITED_OBJECT_TYPE_PRESENT) {
+ inherited_object = &ace->object.object.inherited_type.inherited_type;
+ }
+
+ if (inherited_object != NULL && !object_in_list(object_list, inherited_object)) {
+ /*
+ * An explicit object class schemaId is given,
+ * but doesn't belong to the current object.
+ */
+ applies = false;
+ }
+
+ break;
+
+ case SEC_ACE_TYPE_ACCESS_DENIED_CALLBACK:
+ case SEC_ACE_TYPE_ACCESS_ALLOWED_CALLBACK:
+ case SEC_ACE_TYPE_SYSTEM_AUDIT_CALLBACK:
+ break;
+ case SEC_ACE_TYPE_SYSTEM_RESOURCE_ATTRIBUTE:
+ break;
+ case SEC_ACE_TYPE_SYSTEM_ALARM_CALLBACK:
+ case SEC_ACE_TYPE_SYSTEM_ALARM_CALLBACK_OBJECT:
+ case SEC_ACE_TYPE_SYSTEM_MANDATORY_LABEL:
+ case SEC_ACE_TYPE_SYSTEM_SCOPED_POLICY_ID:
+ default:
+ DBG_WARNING("ACE type %d is not handled\n", ace->type);
+ TALLOC_FREE(tmp_acl);
+ return NULL;
+ }
+
+ if (ace->flags & SEC_ACE_FLAG_NO_PROPAGATE_INHERIT) {
+ if (!applies) {
+ /*
+ * If the ACE doesn't apply to
+ * the current object, we should
+ * ignore it as it should not be
+ * inherited any further
+ */
+ continue;
+ }
+ /*
+ * We should only keep the expanded version
+ * of the ACE on the current object.
+ */
+ expand_ace = true;
+ expand_only = true;
+ } else if (applies) {
+ /*
+ * We check if should also add
+ * the expanded version of the ACE
+ * in addition, in case we should
+ * expand generic access bits or
+ * special sids.
+ *
+ * In that case we need to
+ * keep the original ACE with
+ * SEC_ACE_FLAG_INHERIT_ONLY.
+ */
+ expand_ace = desc_ace_has_generic(ace);
+ if (expand_ace) {
+ inherited_only = true;
+ }
+ } else {
+ /*
+ * If the ACE doesn't apply
+ * to the current object,
+ * we need to keep it with
+ * SEC_ACE_FLAG_INHERIT_ONLY
+ * in order to apply them to
+ * grandchildren
+ */
+ inherited_only = true;
+ }
+
+ if (expand_ace) {
+ tmp_acl->aces = talloc_realloc(tmp_acl,
+ tmp_acl->aces,
+ struct security_ace,
+ tmp_acl->num_aces+1);
+ if (tmp_acl->aces == NULL) {
+ TALLOC_FREE(tmp_acl);
+ return NULL;
+ }
+
+ tmp_ace = &tmp_acl->aces[tmp_acl->num_aces];
+ tmp_acl->num_aces++;
+
+ *tmp_ace = *ace;
+
+ /*
+ * Expand generic access bits as well as special
+ * sids.
+ */
+ desc_expand_generic(tmp_ace, owner, group);
+
+ /*
+ * Expanded ACEs are marked as inherited,
+ * but never inherited any further to
+ * grandchildren.
+ */
+ tmp_ace->flags |= SEC_ACE_FLAG_INHERITED_ACE;
+ tmp_ace->flags &= ~SEC_ACE_FLAG_CONTAINER_INHERIT;
+ tmp_ace->flags &= ~SEC_ACE_FLAG_OBJECT_INHERIT;
+ tmp_ace->flags &= ~SEC_ACE_FLAG_NO_PROPAGATE_INHERIT;
+
+ /*
+ * Expanded ACEs never have an explicit
+ * object class schemaId, so clear it
+ * if present.
+ */
+ if (inherited_object != NULL) {
+ tmp_ace->object.object.flags &= ~SEC_ACE_INHERITED_OBJECT_TYPE_PRESENT;
+ }
+
+ /*
+ * If the ACE had an explicit object class
+ * schemaId, but no attribute/propertySet
+ * we need to downgrade the _OBJECT variants
+ * to the normal ones.
+ */
+ if (inherited_property == NULL) {
+ switch (tmp_ace->type) {
+ case SEC_ACE_TYPE_ACCESS_ALLOWED:
+ case SEC_ACE_TYPE_ACCESS_DENIED:
+ case SEC_ACE_TYPE_SYSTEM_AUDIT:
+ case SEC_ACE_TYPE_SYSTEM_ALARM:
+ case SEC_ACE_TYPE_ALLOWED_COMPOUND:
+ break;
+ case SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT:
+ tmp_ace->type = SEC_ACE_TYPE_ACCESS_ALLOWED;
+ break;
+ case SEC_ACE_TYPE_ACCESS_DENIED_OBJECT:
+ tmp_ace->type = SEC_ACE_TYPE_ACCESS_DENIED;
+ break;
+ case SEC_ACE_TYPE_SYSTEM_ALARM_OBJECT:
+ tmp_ace->type = SEC_ACE_TYPE_SYSTEM_ALARM;
+ break;
+ case SEC_ACE_TYPE_SYSTEM_AUDIT_OBJECT:
+ tmp_ace->type = SEC_ACE_TYPE_SYSTEM_AUDIT;
+ break;
+ case SEC_ACE_TYPE_ACCESS_ALLOWED_CALLBACK_OBJECT:
+ tmp_ace->type = SEC_ACE_TYPE_ACCESS_ALLOWED_CALLBACK;
+ break;
+ case SEC_ACE_TYPE_ACCESS_DENIED_CALLBACK_OBJECT:
+ tmp_ace->type = SEC_ACE_TYPE_ACCESS_DENIED_CALLBACK;
+ break;
+ case SEC_ACE_TYPE_SYSTEM_AUDIT_CALLBACK_OBJECT:
+ tmp_ace->type = SEC_ACE_TYPE_SYSTEM_AUDIT_CALLBACK;
+ break;
+ default:
+ /*
+ * SEC_ACE_TYPE_SYSTEM_ALARM_CALLBACK_OBJECT
+ * is reserved.
+ */
+ break;
+ }
+ }
+
+ if (expand_only) {
+ continue;
+ }
+ }
+
+ tmp_acl->aces = talloc_realloc(tmp_acl,
+ tmp_acl->aces,
+ struct security_ace,
+ tmp_acl->num_aces+1);
+ if (tmp_acl->aces == NULL) {
+ TALLOC_FREE(tmp_acl);
+ return NULL;
+ }
+
+ tmp_ace = &tmp_acl->aces[tmp_acl->num_aces];
+ tmp_acl->num_aces++;
+
+ *tmp_ace = *ace;
+ tmp_ace->flags |= SEC_ACE_FLAG_INHERITED_ACE;
+
+ if (inherited_only) {
+ tmp_ace->flags |= SEC_ACE_FLAG_INHERIT_ONLY;
+ } else {
+ tmp_ace->flags &= ~SEC_ACE_FLAG_INHERIT_ONLY;
+ }
+
+ if (ace->flags & SEC_ACE_FLAG_NO_PROPAGATE_INHERIT) {
+ tmp_ace->flags &= ~SEC_ACE_FLAG_CONTAINER_INHERIT;
+ tmp_ace->flags &= ~SEC_ACE_FLAG_OBJECT_INHERIT;
+ tmp_ace->flags &= ~SEC_ACE_FLAG_NO_PROPAGATE_INHERIT;
+ }
+ }
+ if (tmp_acl->num_aces == 0) {
+ TALLOC_FREE(tmp_acl);
+ return NULL;
+ }
+ if (acl) {
+ tmp_acl->revision = acl->revision;
+ }
+ return tmp_acl;
+}
+
+static struct security_acl *process_user_acl(TALLOC_CTX *mem_ctx,
+ struct security_acl *acl,
+ bool is_container,
+ struct dom_sid *owner,
+ struct dom_sid *group,
+ struct GUID *object_list,
+ bool is_protected)
+{
+ uint32_t i;
+ TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
+ struct security_acl *tmp_acl = talloc_zero(tmp_ctx, struct security_acl);
+ struct security_acl *new_acl;
+
+ if (!acl)
+ return NULL;
+
+ if (!tmp_acl)
+ return NULL;
+
+ tmp_acl->revision = acl->revision;
+ DBG_DEBUG("acl revision %d\n", acl->revision);
+
+ for (i=0; i < acl->num_aces; i++){
+ struct security_ace *ace = &acl->aces[i];
+ /* Remove ID flags from user-provided ACEs
+ * if we break inheritance, ignore them otherwise */
+ if (ace->flags & SEC_ACE_FLAG_INHERITED_ACE) {
+ if (is_protected) {
+ ace->flags &= ~SEC_ACE_FLAG_INHERITED_ACE;
+ } else {
+ continue;
+ }
+ }
+
+ if (ace->flags & SEC_ACE_FLAG_INHERIT_ONLY &&
+ !(ace->flags & SEC_ACE_FLAG_CONTAINER_INHERIT ||
+ ace->flags & SEC_ACE_FLAG_OBJECT_INHERIT))
+ continue;
+
+ tmp_acl->aces = talloc_realloc(tmp_acl,
+ tmp_acl->aces,
+ struct security_ace,
+ tmp_acl->num_aces+1);
+ tmp_acl->aces[tmp_acl->num_aces] = *ace;
+ tmp_acl->num_aces++;
+ if (ace->flags & SEC_ACE_FLAG_INHERIT_ONLY) {
+ continue;
+ }
+ /* if the ACE contains CO, CG, GA, GE, GR or GW, and is inheritable
+ * it has to be expanded to two aces, the original as IO,
+ * and another one where these are translated */
+ if (desc_ace_has_generic(ace)) {
+ if (!(ace->flags & SEC_ACE_FLAG_CONTAINER_INHERIT)) {
+ desc_expand_generic(&tmp_acl->aces[tmp_acl->num_aces-1],
+ owner,
+ group);
+ } else {
+ /*The original ACE becomes read only */
+ tmp_acl->aces[tmp_acl->num_aces-1].flags |= SEC_ACE_FLAG_INHERIT_ONLY;
+ tmp_acl->aces = talloc_realloc(tmp_acl, tmp_acl->aces,
+ struct security_ace,
+ tmp_acl->num_aces+1);
+ /* add a new ACE with expanded generic info */
+ tmp_acl->aces[tmp_acl->num_aces] = *ace;
+ desc_expand_generic(&tmp_acl->aces[tmp_acl->num_aces],
+ owner,
+ group);
+ tmp_acl->num_aces++;
+ }
+ }
+ }
+ new_acl = security_acl_dup(mem_ctx,tmp_acl);
+
+ if (new_acl)
+ new_acl->revision = acl->revision;
+
+ talloc_free(tmp_ctx);
+ return new_acl;
+}
+
+static void cr_descr_log_descriptor(struct security_descriptor *sd,
+ const char *message,
+ int level)
+{
+ if (sd) {
+ DEBUG(level,("%s: %s\n", message,
+ ndr_print_struct_string(0,(ndr_print_fn_t)ndr_print_security_descriptor,
+ "", sd)));
+ }
+ else {
+ DEBUG(level,("%s: NULL\n", message));
+ }
+}
+
+#if 0
+static void cr_descr_log_acl(struct security_acl *acl,
+ const char *message,
+ int level)
+{
+ if (acl) {
+ DEBUG(level,("%s: %s\n", message,
+ ndr_print_struct_string(0,(ndr_print_fn_t)ndr_print_security_acl,
+ "", acl)));
+ }
+ else {
+ DEBUG(level,("%s: NULL\n", message));
+ }
+}
+#endif
+
+static bool compute_acl(struct security_descriptor *parent_sd,
+ struct security_descriptor *creator_sd,
+ bool is_container,
+ uint32_t inherit_flags,
+ struct GUID *object_list,
+ uint32_t (*generic_map)(uint32_t access_mask),
+ struct security_token *token,
+ struct security_descriptor *new_sd) /* INOUT argument */
+{
+ struct security_acl *user_dacl, *user_sacl, *inherited_dacl, *inherited_sacl;
+ int level = 10;
+
+ if (!parent_sd || !(inherit_flags & SEC_DACL_AUTO_INHERIT)) {
+ inherited_dacl = NULL;
+ } else if (creator_sd && (creator_sd->type & SEC_DESC_DACL_PROTECTED)) {
+ inherited_dacl = NULL;
+ } else {
+ inherited_dacl = calculate_inherited_from_parent(new_sd,
+ parent_sd->dacl,
+ is_container,
+ new_sd->owner_sid,
+ new_sd->group_sid,
+ object_list);
+ }
+
+
+ if (!parent_sd || !(inherit_flags & SEC_SACL_AUTO_INHERIT)) {
+ inherited_sacl = NULL;
+ } else if (creator_sd && (creator_sd->type & SEC_DESC_SACL_PROTECTED)) {
+ inherited_sacl = NULL;
+ } else {
+ inherited_sacl = calculate_inherited_from_parent(new_sd,
+ parent_sd->sacl,
+ is_container,
+ new_sd->owner_sid,
+ new_sd->group_sid,
+ object_list);
+ }
+
+ if (!creator_sd || (inherit_flags & SEC_DEFAULT_DESCRIPTOR)) {
+ user_dacl = NULL;
+ user_sacl = NULL;
+ } else {
+ user_dacl = process_user_acl(new_sd,
+ creator_sd->dacl,
+ is_container,
+ new_sd->owner_sid,
+ new_sd->group_sid,
+ object_list,
+ creator_sd->type & SEC_DESC_DACL_PROTECTED);
+ user_sacl = process_user_acl(new_sd,
+ creator_sd->sacl,
+ is_container,
+ new_sd->owner_sid,
+ new_sd->group_sid,
+ object_list,
+ creator_sd->type & SEC_DESC_SACL_PROTECTED);
+ }
+ cr_descr_log_descriptor(parent_sd, __location__"parent_sd", level);
+ cr_descr_log_descriptor(creator_sd,__location__ "creator_sd", level);
+
+ new_sd->dacl = security_acl_concatenate(new_sd, user_dacl, inherited_dacl);
+ if (new_sd->dacl) {
+ new_sd->type |= SEC_DESC_DACL_PRESENT;
+ }
+ if (inherited_dacl) {
+ new_sd->type |= SEC_DESC_DACL_AUTO_INHERITED;
+ }
+
+ new_sd->sacl = security_acl_concatenate(new_sd, user_sacl, inherited_sacl);
+ if (new_sd->sacl) {
+ new_sd->type |= SEC_DESC_SACL_PRESENT;
+ }
+ if (inherited_sacl) {
+ new_sd->type |= SEC_DESC_SACL_AUTO_INHERITED;
+ }
+ /* This is a hack to handle the fact that
+ * apprantly any AI flag provided by the user is preserved */
+ if (creator_sd)
+ new_sd->type |= creator_sd->type;
+ cr_descr_log_descriptor(new_sd, __location__"final sd", level);
+ return true;
+}
+
+struct security_descriptor *create_security_descriptor(TALLOC_CTX *mem_ctx,
+ struct security_descriptor *parent_sd,
+ struct security_descriptor *creator_sd,
+ bool is_container,
+ struct GUID *object_list,
+ uint32_t inherit_flags,
+ struct security_token *token,
+ struct dom_sid *default_owner, /* valid only for DS, NULL for the other RSs */
+ struct dom_sid *default_group, /* valid only for DS, NULL for the other RSs */
+ uint32_t (*generic_map)(uint32_t access_mask))
+{
+ struct security_descriptor *new_sd;
+ struct dom_sid *new_owner = NULL;
+ struct dom_sid *new_group = NULL;
+
+ new_sd = security_descriptor_initialise(mem_ctx);
+ if (!new_sd) {
+ return NULL;
+ }
+
+ if (!creator_sd || !creator_sd->owner_sid) {
+ if ((inherit_flags & SEC_OWNER_FROM_PARENT) && parent_sd) {
+ new_owner = parent_sd->owner_sid;
+ } else if (!default_owner) {
+ new_owner = &token->sids[PRIMARY_USER_SID_INDEX];
+ } else {
+ new_owner = default_owner;
+ new_sd->type |= SEC_DESC_OWNER_DEFAULTED;
+ }
+ } else {
+ new_owner = creator_sd->owner_sid;
+ }
+
+ if (!creator_sd || !creator_sd->group_sid){
+ if ((inherit_flags & SEC_GROUP_FROM_PARENT) && parent_sd) {
+ new_group = parent_sd->group_sid;
+ } else if (!default_group && token->num_sids > PRIMARY_GROUP_SID_INDEX) {
+ new_group = &token->sids[PRIMARY_GROUP_SID_INDEX];
+ } else if (!default_group) {
+ /* This will happen only for anonymous, which has no other groups */
+ new_group = &token->sids[PRIMARY_USER_SID_INDEX];
+ } else {
+ new_group = default_group;
+ new_sd->type |= SEC_DESC_GROUP_DEFAULTED;
+ }
+ } else {
+ new_group = creator_sd->group_sid;
+ }
+
+ new_sd->owner_sid = talloc_memdup(new_sd, new_owner, sizeof(struct dom_sid));
+ new_sd->group_sid = talloc_memdup(new_sd, new_group, sizeof(struct dom_sid));
+ if (!new_sd->owner_sid || !new_sd->group_sid){
+ talloc_free(new_sd);
+ return NULL;
+ }
+
+ if (!compute_acl(parent_sd, creator_sd,
+ is_container, inherit_flags, object_list,
+ generic_map,token,new_sd)){
+ talloc_free(new_sd);
+ return NULL;
+ }
+
+ return new_sd;
+}
diff --git a/libcli/security/display_sec.c b/libcli/security/display_sec.c
new file mode 100644
index 0000000..be89a33
--- /dev/null
+++ b/libcli/security/display_sec.c
@@ -0,0 +1,274 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba utility functions
+ Copyright (C) Andrew Tridgell 1992-1999
+ Copyright (C) Luke Kenneth Casson Leighton 1996 - 1999
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "replace.h"
+#include "libcli/security/security.h"
+#include "librpc/ndr/libndr.h"
+#include "libcli/security/display_sec.h"
+
+/****************************************************************************
+convert a security permissions into a string
+****************************************************************************/
+
+char *get_sec_mask_str(TALLOC_CTX *ctx, uint32_t type)
+{
+ char *typestr = talloc_strdup(ctx, "");
+
+ if (type & SEC_GENERIC_ALL) {
+ talloc_asprintf_addbuf(&typestr, "Generic all access ");
+ }
+ if (type & SEC_GENERIC_EXECUTE) {
+ talloc_asprintf_addbuf(&typestr, "Generic execute access");
+ }
+ if (type & SEC_GENERIC_WRITE) {
+ talloc_asprintf_addbuf(&typestr, "Generic write access ");
+ }
+ if (type & SEC_GENERIC_READ) {
+ talloc_asprintf_addbuf(&typestr, "Generic read access ");
+ }
+ if (type & SEC_FLAG_MAXIMUM_ALLOWED) {
+ talloc_asprintf_addbuf(&typestr, "MAXIMUM_ALLOWED_ACCESS ");
+ }
+ if (type & SEC_FLAG_SYSTEM_SECURITY) {
+ talloc_asprintf_addbuf(&typestr, "SYSTEM_SECURITY_ACCESS ");
+ }
+ if (type & SEC_STD_SYNCHRONIZE) {
+ talloc_asprintf_addbuf(&typestr, "SYNCHRONIZE_ACCESS ");
+ }
+ if (type & SEC_STD_WRITE_OWNER) {
+ talloc_asprintf_addbuf(&typestr, "WRITE_OWNER_ACCESS ");
+ }
+ if (type & SEC_STD_WRITE_DAC) {
+ talloc_asprintf_addbuf(&typestr, "WRITE_DAC_ACCESS ");
+ }
+ if (type & SEC_STD_READ_CONTROL) {
+ talloc_asprintf_addbuf(&typestr, "READ_CONTROL_ACCESS ");
+ }
+ if (type & SEC_STD_DELETE) {
+ talloc_asprintf_addbuf(&typestr, "DELETE_ACCESS ");
+ }
+
+ printf("\t\tSpecific bits: 0x%lx\n", (unsigned long)type&SEC_MASK_SPECIFIC);
+
+ return typestr;
+}
+
+/****************************************************************************
+ display sec_access structure
+ ****************************************************************************/
+void display_sec_access(uint32_t *info)
+{
+ char *mask_str = get_sec_mask_str(NULL, *info);
+ printf("\t\tPermissions: 0x%x: %s\n", *info, mask_str ? mask_str : "");
+ talloc_free(mask_str);
+}
+
+/****************************************************************************
+ display sec_ace flags
+ ****************************************************************************/
+void display_sec_ace_flags(uint8_t flags)
+{
+ if (flags & SEC_ACE_FLAG_OBJECT_INHERIT)
+ printf("SEC_ACE_FLAG_OBJECT_INHERIT ");
+ if (flags & SEC_ACE_FLAG_CONTAINER_INHERIT)
+ printf(" SEC_ACE_FLAG_CONTAINER_INHERIT ");
+ if (flags & SEC_ACE_FLAG_NO_PROPAGATE_INHERIT)
+ printf("SEC_ACE_FLAG_NO_PROPAGATE_INHERIT ");
+ if (flags & SEC_ACE_FLAG_INHERIT_ONLY)
+ printf("SEC_ACE_FLAG_INHERIT_ONLY ");
+ if (flags & SEC_ACE_FLAG_INHERITED_ACE)
+ printf("SEC_ACE_FLAG_INHERITED_ACE ");
+/* if (flags & SEC_ACE_FLAG_VALID_INHERIT)
+ printf("SEC_ACE_FLAG_VALID_INHERIT "); */
+ if (flags & SEC_ACE_FLAG_SUCCESSFUL_ACCESS)
+ printf("SEC_ACE_FLAG_SUCCESSFUL_ACCESS ");
+ if (flags & SEC_ACE_FLAG_FAILED_ACCESS)
+ printf("SEC_ACE_FLAG_FAILED_ACCESS ");
+
+ printf("\n");
+}
+
+/****************************************************************************
+ display sec_ace object
+ ****************************************************************************/
+static void disp_sec_ace_object(struct security_ace_object *object)
+{
+ char *str;
+ if (object->flags & SEC_ACE_OBJECT_TYPE_PRESENT) {
+ str = GUID_string(NULL, &object->type.type);
+ if (str == NULL) return;
+ printf("Object type: SEC_ACE_OBJECT_TYPE_PRESENT\n");
+ printf("Object GUID: %s\n", str);
+ talloc_free(str);
+ }
+ if (object->flags & SEC_ACE_INHERITED_OBJECT_TYPE_PRESENT) {
+ str = GUID_string(NULL, &object->inherited_type.inherited_type);
+ if (str == NULL) return;
+ printf("Object type: SEC_ACE_INHERITED_OBJECT_TYPE_PRESENT\n");
+ printf("Object GUID: %s\n", str);
+ talloc_free(str);
+ }
+}
+
+/****************************************************************************
+ display sec_ace structure
+ ****************************************************************************/
+void display_sec_ace(struct security_ace *ace)
+{
+ struct dom_sid_buf sid_str;
+
+ printf("\tACE\n\t\ttype: ");
+ switch (ace->type) {
+ case SEC_ACE_TYPE_ACCESS_ALLOWED:
+ printf("ACCESS ALLOWED");
+ break;
+ case SEC_ACE_TYPE_ACCESS_DENIED:
+ printf("ACCESS DENIED");
+ break;
+ case SEC_ACE_TYPE_SYSTEM_AUDIT:
+ printf("SYSTEM AUDIT");
+ break;
+ case SEC_ACE_TYPE_SYSTEM_ALARM:
+ printf("SYSTEM ALARM");
+ break;
+#define ACE_CASE(x) case x: printf(#x); break
+ ACE_CASE(SEC_ACE_TYPE_ALLOWED_COMPOUND);
+ ACE_CASE(SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT);
+ ACE_CASE(SEC_ACE_TYPE_ACCESS_DENIED_OBJECT);
+ ACE_CASE(SEC_ACE_TYPE_SYSTEM_AUDIT_OBJECT);
+ ACE_CASE(SEC_ACE_TYPE_SYSTEM_ALARM_OBJECT);
+ ACE_CASE(SEC_ACE_TYPE_ACCESS_ALLOWED_CALLBACK);
+ ACE_CASE(SEC_ACE_TYPE_ACCESS_DENIED_CALLBACK);
+ ACE_CASE(SEC_ACE_TYPE_ACCESS_ALLOWED_CALLBACK_OBJECT);
+ ACE_CASE(SEC_ACE_TYPE_ACCESS_DENIED_CALLBACK_OBJECT);
+ ACE_CASE(SEC_ACE_TYPE_SYSTEM_AUDIT_CALLBACK);
+ ACE_CASE(SEC_ACE_TYPE_SYSTEM_ALARM_CALLBACK);
+ ACE_CASE(SEC_ACE_TYPE_SYSTEM_AUDIT_CALLBACK_OBJECT);
+ ACE_CASE(SEC_ACE_TYPE_SYSTEM_ALARM_CALLBACK_OBJECT);
+#undef ACE_CASE
+ default:
+ printf("????");
+ break;
+ }
+
+ printf(" (%d) flags: 0x%02x ", ace->type, ace->flags);
+ display_sec_ace_flags(ace->flags);
+ display_sec_access(&ace->access_mask);
+ printf("\t\tSID: %s\n\n", dom_sid_str_buf(&ace->trustee, &sid_str));
+
+ if (sec_ace_object(ace->type)) {
+ disp_sec_ace_object(&ace->object.object);
+ }
+
+}
+
+/****************************************************************************
+ display sec_acl structure
+ ****************************************************************************/
+void display_sec_acl(struct security_acl *sec_acl)
+{
+ uint32_t i;
+
+ printf("\tACL\tNum ACEs:\t%u\trevision:\t%x\n",
+ sec_acl->num_aces, sec_acl->revision);
+ printf("\t---\n");
+
+ if (sec_acl->size != 0 && sec_acl->num_aces != 0) {
+ for (i = 0; i < sec_acl->num_aces; i++) {
+ display_sec_ace(&sec_acl->aces[i]);
+ }
+ }
+}
+
+void display_acl_type(uint16_t type)
+{
+ printf("type: 0x%04x: ", type);
+
+ if (type & SEC_DESC_OWNER_DEFAULTED) /* 0x0001 */
+ printf("SEC_DESC_OWNER_DEFAULTED ");
+ if (type & SEC_DESC_GROUP_DEFAULTED) /* 0x0002 */
+ printf("SEC_DESC_GROUP_DEFAULTED ");
+ if (type & SEC_DESC_DACL_PRESENT) /* 0x0004 */
+ printf("SEC_DESC_DACL_PRESENT ");
+ if (type & SEC_DESC_DACL_DEFAULTED) /* 0x0008 */
+ printf("SEC_DESC_DACL_DEFAULTED ");
+ if (type & SEC_DESC_SACL_PRESENT) /* 0x0010 */
+ printf("SEC_DESC_SACL_PRESENT ");
+ if (type & SEC_DESC_SACL_DEFAULTED) /* 0x0020 */
+ printf("SEC_DESC_SACL_DEFAULTED ");
+ if (type & SEC_DESC_DACL_TRUSTED) /* 0x0040 */
+ printf("SEC_DESC_DACL_TRUSTED ");
+ if (type & SEC_DESC_SERVER_SECURITY) /* 0x0080 */
+ printf("SEC_DESC_SERVER_SECURITY ");
+ if (type & SEC_DESC_DACL_AUTO_INHERIT_REQ) /* 0x0100 */
+ printf("SEC_DESC_DACL_AUTO_INHERIT_REQ ");
+ if (type & SEC_DESC_SACL_AUTO_INHERIT_REQ) /* 0x0200 */
+ printf("SEC_DESC_SACL_AUTO_INHERIT_REQ ");
+ if (type & SEC_DESC_DACL_AUTO_INHERITED) /* 0x0400 */
+ printf("SEC_DESC_DACL_AUTO_INHERITED ");
+ if (type & SEC_DESC_SACL_AUTO_INHERITED) /* 0x0800 */
+ printf("SEC_DESC_SACL_AUTO_INHERITED ");
+ if (type & SEC_DESC_DACL_PROTECTED) /* 0x1000 */
+ printf("SEC_DESC_DACL_PROTECTED ");
+ if (type & SEC_DESC_SACL_PROTECTED) /* 0x2000 */
+ printf("SEC_DESC_SACL_PROTECTED ");
+ if (type & SEC_DESC_RM_CONTROL_VALID) /* 0x4000 */
+ printf("SEC_DESC_RM_CONTROL_VALID ");
+ if (type & SEC_DESC_SELF_RELATIVE) /* 0x8000 */
+ printf("SEC_DESC_SELF_RELATIVE ");
+
+ printf("\n");
+}
+
+/****************************************************************************
+ display sec_desc structure
+ ****************************************************************************/
+void display_sec_desc(struct security_descriptor *sec)
+{
+ struct dom_sid_buf sid_str;
+
+ if (!sec) {
+ printf("NULL\n");
+ return;
+ }
+
+ printf("revision: %d\n", sec->revision);
+ display_acl_type(sec->type);
+
+ if (sec->sacl) {
+ printf("SACL\n");
+ display_sec_acl(sec->sacl);
+ }
+
+ if (sec->dacl) {
+ printf("DACL\n");
+ display_sec_acl(sec->dacl);
+ }
+
+ if (sec->owner_sid) {
+ printf("\tOwner SID:\t%s\n",
+ dom_sid_str_buf(sec->owner_sid, &sid_str));
+ }
+
+ if (sec->group_sid) {
+ printf("\tGroup SID:\t%s\n",
+ dom_sid_str_buf(sec->group_sid, &sid_str));
+ }
+}
diff --git a/libcli/security/display_sec.h b/libcli/security/display_sec.h
new file mode 100644
index 0000000..336e04c
--- /dev/null
+++ b/libcli/security/display_sec.h
@@ -0,0 +1,34 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba utility functions
+ Copyright (C) Andrew Tridgell 1992-1999
+ Copyright (C) Luke Kenneth Casson Leighton 1996 - 1999
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _LIBCLI_SECURITY_DISPLAY_SEC_H
+#define _LIBCLI_SECURITY_DISPLAY_SEC_H
+
+/* The following definitions come from libcli/security/display_sec.c */
+
+char *get_sec_mask_str(TALLOC_CTX *ctx, uint32_t type);
+void display_sec_access(uint32_t *info);
+void display_sec_ace_flags(uint8_t flags);
+void display_sec_ace(struct security_ace *ace);
+void display_sec_acl(struct security_acl *sec_acl);
+void display_acl_type(uint16_t type);
+void display_sec_desc(struct security_descriptor *sec);
+
+#endif /* _LIBCLI_SECURITY_DISPLAY_SEC_H */
diff --git a/libcli/security/dom_sid.c b/libcli/security/dom_sid.c
new file mode 100644
index 0000000..eaece2a
--- /dev/null
+++ b/libcli/security/dom_sid.c
@@ -0,0 +1,582 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba utility functions
+
+ Copyright (C) Stefan (metze) Metzmacher 2002-2004
+ Copyright (C) Andrew Tridgell 1992-2004
+ Copyright (C) Jeremy Allison 1999
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "replace.h"
+#include "lib/util/data_blob.h"
+#include "system/locale.h"
+#include "lib/util/debug.h"
+#include "lib/util/util.h"
+#include "librpc/gen_ndr/security.h"
+#include "dom_sid.h"
+#include "lib/util/smb_strtox.h"
+
+/*****************************************************************
+ Compare the auth portion of two sids.
+*****************************************************************/
+
+int dom_sid_compare_auth(const struct dom_sid *sid1,
+ const struct dom_sid *sid2)
+{
+ int i;
+
+ if (sid1 == sid2)
+ return 0;
+ if (!sid1)
+ return -1;
+ if (!sid2)
+ return 1;
+
+ if (sid1->sid_rev_num != sid2->sid_rev_num)
+ return sid1->sid_rev_num - sid2->sid_rev_num;
+
+ for (i = 0; i < 6; i++)
+ if (sid1->id_auth[i] != sid2->id_auth[i])
+ return sid1->id_auth[i] - sid2->id_auth[i];
+
+ return 0;
+}
+
+/*****************************************************************
+ Compare two sids.
+*****************************************************************/
+
+int dom_sid_compare(const struct dom_sid *sid1, const struct dom_sid *sid2)
+{
+ int i;
+
+ if (sid1 == sid2)
+ return 0;
+ if (!sid1)
+ return -1;
+ if (!sid2)
+ return 1;
+
+ /* Compare most likely different rids, first: i.e start at end */
+ if (sid1->num_auths != sid2->num_auths)
+ return sid1->num_auths - sid2->num_auths;
+
+ for (i = sid1->num_auths-1; i >= 0; --i) {
+ if (sid1->sub_auths[i] < sid2->sub_auths[i]) {
+ return -1;
+ }
+ if (sid1->sub_auths[i] > sid2->sub_auths[i]) {
+ return 1;
+ }
+ }
+
+ return dom_sid_compare_auth(sid1, sid2);
+}
+
+/*****************************************************************
+ Compare two sids.
+*****************************************************************/
+
+bool dom_sid_equal(const struct dom_sid *sid1, const struct dom_sid *sid2)
+{
+ return dom_sid_compare(sid1, sid2) == 0;
+}
+
+/*****************************************************************
+ Add a rid to the end of a sid
+*****************************************************************/
+
+bool sid_append_rid(struct dom_sid *sid, uint32_t rid)
+{
+ if (sid->num_auths < ARRAY_SIZE(sid->sub_auths)) {
+ sid->sub_auths[sid->num_auths++] = rid;
+ return true;
+ }
+ return false;
+}
+
+/*
+ See if 2 SIDs are in the same domain
+ this just compares the leading sub-auths
+*/
+int dom_sid_compare_domain(const struct dom_sid *sid1,
+ const struct dom_sid *sid2)
+{
+ int n, i;
+
+ n = MIN(sid1->num_auths, sid2->num_auths);
+
+ for (i = n-1; i >= 0; --i) {
+ if (sid1->sub_auths[i] < sid2->sub_auths[i]) {
+ return -1;
+ }
+ if (sid1->sub_auths[i] > sid2->sub_auths[i]) {
+ return 1;
+ }
+ }
+
+ return dom_sid_compare_auth(sid1, sid2);
+}
+
+/*****************************************************************
+ Convert a string to a SID. Returns True on success, False on fail.
+ Return the first character not parsed in endp.
+*****************************************************************/
+#define AUTHORITY_MASK (~(0xffffffffffffULL))
+
+bool dom_sid_parse_endp(const char *sidstr,struct dom_sid *sidout,
+ const char **endp)
+{
+ const char *p;
+ char *q = NULL;
+ char *end = NULL;
+ uint64_t conv;
+ int error = 0;
+
+ *sidout = (struct dom_sid) {};
+
+ if ((sidstr[0] != 'S' && sidstr[0] != 's') || sidstr[1] != '-') {
+ goto format_error;
+ }
+
+ /* Get the revision number. */
+ p = sidstr + 2;
+
+ if (!isdigit((unsigned char)*p)) {
+ goto format_error;
+ }
+
+ conv = smb_strtoul(p, &q, 10, &error, SMB_STR_STANDARD);
+ if (error != 0 || (*q != '-') || conv > UINT8_MAX || q - p > 4) {
+ goto format_error;
+ }
+ sidout->sid_rev_num = (uint8_t) conv;
+ q++;
+
+ if (!isdigit((unsigned char)*q)) {
+ goto format_error;
+ }
+ while (q[0] == '0' && isdigit((unsigned char)q[1])) {
+ /*
+ * strtoull will think this is octal, which is not how SIDs
+ * work! So let's walk along until there are no leading zeros
+ * (or a single zero).
+ */
+ q++;
+ }
+
+ /* get identauth */
+ conv = smb_strtoull(q, &end, 0, &error, SMB_STR_STANDARD);
+ if (conv & AUTHORITY_MASK || error != 0) {
+ goto format_error;
+ }
+ if (conv >= (1ULL << 48) || end - q > 15) {
+ /*
+ * This identauth looks like a big number, but resolves to a
+ * small number after rounding.
+ */
+ goto format_error;
+ }
+
+ /* NOTE - the conv value is in big-endian format. */
+ sidout->id_auth[0] = (conv & 0xff0000000000ULL) >> 40;
+ sidout->id_auth[1] = (conv & 0x00ff00000000ULL) >> 32;
+ sidout->id_auth[2] = (conv & 0x0000ff000000ULL) >> 24;
+ sidout->id_auth[3] = (conv & 0x000000ff0000ULL) >> 16;
+ sidout->id_auth[4] = (conv & 0x00000000ff00ULL) >> 8;
+ sidout->id_auth[5] = (conv & 0x0000000000ffULL);
+
+ sidout->num_auths = 0;
+ q = end;
+ if (*q != '-') {
+ /* Just id_auth, no subauths */
+ goto done;
+ }
+
+ q++;
+
+ while (true) {
+ if (!isdigit((unsigned char)*q)) {
+ goto format_error;
+ }
+ while (q[0] == '0' && isdigit((unsigned char)q[1])) {
+ /*
+ * strtoull will think this is octal, which is not how
+ * SIDs work! So let's walk along until there are no
+ * leading zeros (or a single zero).
+ */
+ q++;
+ }
+ conv = smb_strtoull(q, &end, 0, &error, SMB_STR_STANDARD);
+ if (conv > UINT32_MAX || error != 0 || end - q > 12) {
+ /*
+ * This sub-auth is greater than 4294967295,
+ * and hence invalid. Windows will treat it as
+ * 4294967295, while we prefer to refuse (old
+ * versions of Samba will wrap, arriving at
+ * another number altogether).
+ */
+ DBG_NOTICE("bad sub-auth in %s\n", sidstr);
+ goto format_error;
+ }
+
+ if (!sid_append_rid(sidout, conv)) {
+ DEBUG(3, ("Too many sid auths in %s\n", sidstr));
+ return false;
+ }
+
+ q = end;
+ if (*q != '-') {
+ break;
+ }
+ q += 1;
+ }
+done:
+ if (endp != NULL) {
+ *endp = q;
+ }
+ return true;
+
+format_error:
+ DEBUG(3, ("string_to_sid: SID %s is not in a valid format\n", sidstr));
+ return false;
+}
+
+bool string_to_sid(struct dom_sid *sidout, const char *sidstr)
+{
+ return dom_sid_parse(sidstr, sidout);
+}
+
+bool dom_sid_parse(const char *sidstr, struct dom_sid *ret)
+{
+ return dom_sid_parse_endp(sidstr, ret, NULL);
+}
+
+/*
+ convert a string to a dom_sid, returning a talloc'd dom_sid
+*/
+struct dom_sid *dom_sid_parse_talloc(TALLOC_CTX *mem_ctx, const char *sidstr)
+{
+ struct dom_sid *ret;
+ ret = talloc(mem_ctx, struct dom_sid);
+ if (!ret) {
+ return NULL;
+ }
+ if (!dom_sid_parse(sidstr, ret)) {
+ talloc_free(ret);
+ return NULL;
+ }
+
+ return ret;
+}
+
+/*
+ convert a string to a dom_sid, returning a talloc'd dom_sid
+*/
+struct dom_sid *dom_sid_parse_length(TALLOC_CTX *mem_ctx, const DATA_BLOB *sid)
+{
+ char p[sid->length+1];
+ memcpy(p, sid->data, sid->length);
+ p[sid->length] = '\0';
+ return dom_sid_parse_talloc(mem_ctx, p);
+}
+
+/*
+ copy a dom_sid structure
+*/
+struct dom_sid *dom_sid_dup(TALLOC_CTX *mem_ctx, const struct dom_sid *dom_sid)
+{
+ struct dom_sid *ret;
+
+ if (!dom_sid) {
+ return NULL;
+ }
+
+ ret = talloc(mem_ctx, struct dom_sid);
+ if (!ret) {
+ return NULL;
+ }
+ sid_copy(ret, dom_sid);
+
+ return ret;
+}
+
+/*
+ add a rid to a domain dom_sid to make a full dom_sid. This function
+ returns a new sid in the supplied memory context
+*/
+struct dom_sid *dom_sid_add_rid(TALLOC_CTX *mem_ctx,
+ const struct dom_sid *domain_sid,
+ uint32_t rid)
+{
+ struct dom_sid *sid;
+
+ sid = dom_sid_dup(mem_ctx, domain_sid);
+ if (!sid) return NULL;
+
+ if (!sid_append_rid(sid, rid)) {
+ talloc_free(sid);
+ return NULL;
+ }
+
+ return sid;
+}
+
+/*
+ Split up a SID into its domain and RID part
+*/
+NTSTATUS dom_sid_split_rid(TALLOC_CTX *mem_ctx, const struct dom_sid *sid,
+ struct dom_sid **domain, uint32_t *rid)
+{
+ if (sid->num_auths == 0) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (domain) {
+ if (!(*domain = dom_sid_dup(mem_ctx, sid))) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ (*domain)->num_auths -= 1;
+ }
+
+ if (rid) {
+ *rid = sid->sub_auths[sid->num_auths - 1];
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ return true if the 2nd sid is in the domain given by the first sid
+*/
+bool dom_sid_in_domain(const struct dom_sid *domain_sid,
+ const struct dom_sid *sid)
+{
+ int i;
+
+ if (!domain_sid || !sid) {
+ return false;
+ }
+
+ if (sid->num_auths < 2) {
+ return false;
+ }
+
+ if (domain_sid->num_auths != (sid->num_auths - 1)) {
+ return false;
+ }
+
+ for (i = domain_sid->num_auths-1; i >= 0; --i) {
+ if (domain_sid->sub_auths[i] != sid->sub_auths[i]) {
+ return false;
+ }
+ }
+
+ return dom_sid_compare_auth(domain_sid, sid) == 0;
+}
+
+bool dom_sid_has_account_domain(const struct dom_sid *sid)
+{
+ if (sid == NULL) {
+ return false;
+ }
+
+ if (sid->sid_rev_num != 1) {
+ return false;
+ }
+ if (sid->num_auths != 5) {
+ return false;
+ }
+ if (sid->id_auth[5] != 5) {
+ return false;
+ }
+ if (sid->id_auth[4] != 0) {
+ return false;
+ }
+ if (sid->id_auth[3] != 0) {
+ return false;
+ }
+ if (sid->id_auth[2] != 0) {
+ return false;
+ }
+ if (sid->id_auth[1] != 0) {
+ return false;
+ }
+ if (sid->id_auth[0] != 0) {
+ return false;
+ }
+ if (sid->sub_auths[0] != 21) {
+ return false;
+ }
+
+ return true;
+}
+
+bool dom_sid_is_valid_account_domain(const struct dom_sid *sid)
+{
+ /*
+ * We expect S-1-5-21-9-8-7, but we don't
+ * allow S-1-5-21-0-0-0 as this is used
+ * for claims and compound identities.
+ *
+ * With this structure:
+ *
+ * struct dom_sid {
+ * uint8_t sid_rev_num;
+ * int8_t num_auths; [range(0,15)]
+ * uint8_t id_auth[6];
+ * uint32_t sub_auths[15];
+ * }
+ *
+ * S-1-5-21-9-8-7 looks like this:
+ * {1, 4, {0,0,0,0,0,5}, {21,9,8,7,0,0,0,0,0,0,0,0,0,0,0}};
+ */
+ if (sid == NULL) {
+ return false;
+ }
+
+ if (sid->sid_rev_num != 1) {
+ return false;
+ }
+ if (sid->num_auths != 4) {
+ return false;
+ }
+ if (sid->id_auth[5] != 5) {
+ return false;
+ }
+ if (sid->id_auth[4] != 0) {
+ return false;
+ }
+ if (sid->id_auth[3] != 0) {
+ return false;
+ }
+ if (sid->id_auth[2] != 0) {
+ return false;
+ }
+ if (sid->id_auth[1] != 0) {
+ return false;
+ }
+ if (sid->id_auth[0] != 0) {
+ return false;
+ }
+ if (sid->sub_auths[0] != 21) {
+ return false;
+ }
+ if (sid->sub_auths[1] == 0) {
+ return false;
+ }
+ if (sid->sub_auths[2] == 0) {
+ return false;
+ }
+ if (sid->sub_auths[3] == 0) {
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ Convert a dom_sid to a string, printing into a buffer. Return the
+ string length. If it overflows, return the string length that would
+ result (buflen needs to be +1 for the terminating 0).
+*/
+static int dom_sid_string_buf(const struct dom_sid *sid, char *buf, int buflen)
+{
+ int i, ofs, ret;
+ uint64_t ia;
+
+ if (!sid) {
+ return strlcpy(buf, "(NULL SID)", buflen);
+ }
+
+ ia = ((uint64_t)sid->id_auth[5]) +
+ ((uint64_t)sid->id_auth[4] << 8 ) +
+ ((uint64_t)sid->id_auth[3] << 16) +
+ ((uint64_t)sid->id_auth[2] << 24) +
+ ((uint64_t)sid->id_auth[1] << 32) +
+ ((uint64_t)sid->id_auth[0] << 40);
+
+ ret = snprintf(buf, buflen, "S-%"PRIu8"-", sid->sid_rev_num);
+ if (ret < 0) {
+ return ret;
+ }
+ ofs = ret;
+
+ if (ia >= UINT32_MAX) {
+ ret = snprintf(buf+ofs, MAX(buflen-ofs, 0), "0x%"PRIx64, ia);
+ } else {
+ ret = snprintf(buf+ofs, MAX(buflen-ofs, 0), "%"PRIu64, ia);
+ }
+ if (ret < 0) {
+ return ret;
+ }
+ ofs += ret;
+
+ for (i = 0; i < sid->num_auths; i++) {
+ ret = snprintf(
+ buf+ofs,
+ MAX(buflen-ofs, 0),
+ "-%"PRIu32,
+ sid->sub_auths[i]);
+ if (ret < 0) {
+ return ret;
+ }
+ ofs += ret;
+ }
+ return ofs;
+}
+
+/*
+ convert a dom_sid to a string
+*/
+char *dom_sid_string(TALLOC_CTX *mem_ctx, const struct dom_sid *sid)
+{
+ char buf[DOM_SID_STR_BUFLEN];
+ char *result;
+ int len;
+
+ len = dom_sid_string_buf(sid, buf, sizeof(buf));
+
+ if ((len < 0) || (len+1 > sizeof(buf))) {
+ return talloc_strdup(mem_ctx, "(SID ERR)");
+ }
+
+ /*
+ * Avoid calling strlen (via talloc_strdup), we already have
+ * the length
+ */
+ result = (char *)talloc_memdup(mem_ctx, buf, len+1);
+ if (result == NULL) {
+ return NULL;
+ }
+
+ /*
+ * beautify the talloc_report output
+ */
+ talloc_set_name_const(result, result);
+ return result;
+}
+
+char *dom_sid_str_buf(const struct dom_sid *sid, struct dom_sid_buf *dst)
+{
+ int ret;
+ ret = dom_sid_string_buf(sid, dst->buf, sizeof(dst->buf));
+ if ((ret < 0) || (ret >= sizeof(dst->buf))) {
+ strlcpy(dst->buf, "(INVALID SID)", sizeof(dst->buf));
+ }
+ return dst->buf;
+}
diff --git a/libcli/security/dom_sid.h b/libcli/security/dom_sid.h
new file mode 100644
index 0000000..343001e
--- /dev/null
+++ b/libcli/security/dom_sid.h
@@ -0,0 +1,155 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba utility functions
+
+ Copyright (C) Stefan (metze) Metzmacher 2002-2004
+ Copyright (C) Andrew Tridgell 1992-2004
+ Copyright (C) Jeremy Allison 1999
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _DOM_SID_H_
+#define _DOM_SID_H_
+
+#include "replace.h"
+#include <talloc.h>
+#include "lib/util/data_blob.h"
+#include "librpc/gen_ndr/security.h"
+
+/* Some well-known SIDs */
+extern const struct dom_sid global_sid_World_Domain;
+extern const struct dom_sid global_sid_World;
+extern const struct dom_sid global_sid_Local_Authority;
+extern const struct dom_sid global_sid_Creator_Owner_Domain;
+extern const struct dom_sid global_sid_NT_Authority;
+extern const struct dom_sid global_sid_Enterprise_DCs;
+extern const struct dom_sid global_sid_System;
+extern const struct dom_sid global_sid_NULL;
+extern const struct dom_sid global_sid_Self;
+extern const struct dom_sid global_sid_Authenticated_Users;
+extern const struct dom_sid global_sid_Network;
+extern const struct dom_sid global_sid_Asserted_Identity;
+extern const struct dom_sid global_sid_Asserted_Identity_Service;
+extern const struct dom_sid global_sid_Asserted_Identity_Authentication_Authority;
+extern const struct dom_sid global_sid_Creator_Owner;
+extern const struct dom_sid global_sid_Creator_Group;
+extern const struct dom_sid global_sid_Owner_Rights;
+extern const struct dom_sid global_sid_Anonymous;
+extern const struct dom_sid global_sid_Compounded_Authentication;
+extern const struct dom_sid global_sid_Claims_Valid;
+extern const struct dom_sid global_sid_Builtin;
+extern const struct dom_sid global_sid_Builtin_Administrators;
+extern const struct dom_sid global_sid_Builtin_Users;
+extern const struct dom_sid global_sid_Builtin_Guests;
+extern const struct dom_sid global_sid_Builtin_Power_Users;
+extern const struct dom_sid global_sid_Builtin_Account_Operators;
+extern const struct dom_sid global_sid_Builtin_Server_Operators;
+extern const struct dom_sid global_sid_Builtin_Print_Operators;
+extern const struct dom_sid global_sid_Builtin_Backup_Operators;
+extern const struct dom_sid global_sid_Builtin_Replicator;
+extern const struct dom_sid global_sid_Builtin_PreWin2kAccess;
+extern const struct dom_sid global_sid_Unix_Users;
+extern const struct dom_sid global_sid_Unix_Groups;
+extern const struct dom_sid global_sid_Unix_NFS;
+extern const struct dom_sid global_sid_Unix_NFS_Users;
+extern const struct dom_sid global_sid_Unix_NFS_Groups;
+extern const struct dom_sid global_sid_Unix_NFS_Mode;
+extern const struct dom_sid global_sid_Unix_NFS_Other;
+extern const struct dom_sid global_sid_Samba_SMB3;
+
+extern const struct dom_sid global_sid_Samba_NPA_Flags;
+#define SAMBA_NPA_FLAGS_NEED_IDLE 1
+#define SAMBA_NPA_FLAGS_WINBIND_OFF 2
+
+struct auth_SidAttr;
+enum lsa_SidType;
+
+NTSTATUS dom_sid_lookup_predefined_name(const char *name,
+ const struct dom_sid **sid,
+ enum lsa_SidType *type,
+ const struct dom_sid **authority_sid,
+ const char **authority_name);
+NTSTATUS dom_sid_lookup_predefined_sid(const struct dom_sid *sid,
+ const char **name,
+ enum lsa_SidType *type,
+ const struct dom_sid **authority_sid,
+ const char **authority_name);
+bool dom_sid_lookup_is_predefined_domain(const char *domain);
+
+int dom_sid_compare_auth(const struct dom_sid *sid1,
+ const struct dom_sid *sid2);
+int dom_sid_compare(const struct dom_sid *sid1, const struct dom_sid *sid2);
+int dom_sid_compare_domain(const struct dom_sid *sid1,
+ const struct dom_sid *sid2);
+bool dom_sid_equal(const struct dom_sid *sid1, const struct dom_sid *sid2);
+bool sid_append_rid(struct dom_sid *sid, uint32_t rid);
+bool string_to_sid(struct dom_sid *sidout, const char *sidstr);
+bool dom_sid_parse_endp(const char *sidstr,struct dom_sid *sidout,
+ const char **endp);
+bool dom_sid_parse(const char *sidstr, struct dom_sid *ret);
+struct dom_sid *dom_sid_parse_talloc(TALLOC_CTX *mem_ctx, const char *sidstr);
+struct dom_sid *dom_sid_parse_length(TALLOC_CTX *mem_ctx, const DATA_BLOB *sid);
+struct dom_sid *dom_sid_dup(TALLOC_CTX *mem_ctx, const struct dom_sid *dom_sid);
+struct dom_sid *dom_sid_add_rid(TALLOC_CTX *mem_ctx,
+ const struct dom_sid *domain_sid,
+ uint32_t rid);
+NTSTATUS dom_sid_split_rid(TALLOC_CTX *mem_ctx, const struct dom_sid *sid,
+ struct dom_sid **domain, uint32_t *rid);
+bool dom_sid_in_domain(const struct dom_sid *domain_sid,
+ const struct dom_sid *sid);
+bool dom_sid_has_account_domain(const struct dom_sid *sid);
+bool dom_sid_is_valid_account_domain(const struct dom_sid *sid);
+
+#define DOM_SID_STR_BUFLEN (15*11+25)
+char *dom_sid_string(TALLOC_CTX *mem_ctx, const struct dom_sid *sid);
+
+struct dom_sid_buf { char buf[DOM_SID_STR_BUFLEN]; };
+char *dom_sid_str_buf(const struct dom_sid *sid, struct dom_sid_buf *dst);
+
+const char *sid_type_lookup(uint32_t sid_type);
+const struct security_token *get_system_token(void);
+bool sid_compose(struct dom_sid *dst, const struct dom_sid *domain_sid, uint32_t rid);
+bool sid_split_rid(struct dom_sid *sid, uint32_t *rid);
+bool sid_peek_rid(const struct dom_sid *sid, uint32_t *rid);
+bool sid_peek_check_rid(const struct dom_sid *exp_dom_sid, const struct dom_sid *sid, uint32_t *rid);
+void sid_copy(struct dom_sid *dst, const struct dom_sid *src);
+ssize_t sid_parse(const uint8_t *inbuf, size_t len, struct dom_sid *sid);
+NTSTATUS add_sid_to_array(TALLOC_CTX *mem_ctx, const struct dom_sid *sid,
+ struct dom_sid **sids, uint32_t *num);
+NTSTATUS add_sid_to_array_unique(TALLOC_CTX *mem_ctx, const struct dom_sid *sid,
+ struct dom_sid **sids, uint32_t *num_sids);
+NTSTATUS add_sid_to_array_attrs(TALLOC_CTX *mem_ctx,
+ const struct dom_sid *sid, uint32_t attrs,
+ struct auth_SidAttr **sids, uint32_t *num);
+NTSTATUS add_sid_to_array_attrs_unique(TALLOC_CTX *mem_ctx,
+ const struct dom_sid *sid, uint32_t attrs,
+ struct auth_SidAttr **sids, uint32_t *num_sids);
+void del_sid_from_array(const struct dom_sid *sid, struct dom_sid **sids,
+ uint32_t *num);
+bool add_rid_to_array_unique(TALLOC_CTX *mem_ctx,
+ uint32_t rid, uint32_t **pp_rids, size_t *p_num);
+bool is_null_sid(const struct dom_sid *sid);
+bool sids_contains_sid(const struct dom_sid *sids,
+ const uint32_t num_sids,
+ const struct dom_sid *sid);
+bool sid_attrs_contains_sid(const struct auth_SidAttr *sids,
+ const uint32_t num_sids,
+ const struct dom_sid *sid);
+bool sids_contains_sid_attrs(const struct auth_SidAttr *sids,
+ const uint32_t num_sids,
+ const struct dom_sid *sid,
+ uint32_t attrs);
+
+#endif /*_DOM_SID_H_*/
diff --git a/libcli/security/object_tree.c b/libcli/security/object_tree.c
new file mode 100644
index 0000000..70b1ea0
--- /dev/null
+++ b/libcli/security/object_tree.c
@@ -0,0 +1,127 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ security access checking routines
+
+ Copyright (C) Nadezhda Ivanova 2009
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ * Description: Contains data handler functions for
+ * the object tree that must be constructed to perform access checks.
+ * The object tree is an unbalanced tree of depth 3, indexed by
+ * object type guid. Perhaps a different data structure
+ * should be considered later to improve performance
+ *
+ * Author: Nadezhda Ivanova
+ */
+#include "replace.h"
+#include "libcli/security/security.h"
+#include "librpc/ndr/libndr.h"
+
+/* Adds a new node to the object tree. If attributeSecurityGUID is not zero and
+ * has already been added to the tree, the new node is added as a child of that node
+ * In all other cases as a child of the root
+ */
+
+bool insert_in_object_tree(TALLOC_CTX *mem_ctx,
+ const struct GUID *guid,
+ uint32_t init_access,
+ struct object_tree *root,
+ struct object_tree **new_node_out)
+{
+ struct object_tree *new_node;
+
+ if (!guid || GUID_all_zero(guid)){
+ return true;
+ }
+
+ if (!root) {
+ root = talloc_zero(mem_ctx, struct object_tree);
+ if (!root) {
+ return false;
+ }
+ new_node = root;
+ } else {
+ int i;
+
+ for (i = 0; i < root->num_of_children; i++) {
+ if (GUID_equal(&root->children[i].guid, guid)) {
+ new_node = &root->children[i];
+ new_node->remaining_access |= init_access;
+ *new_node_out = new_node;
+ return true;
+ }
+ }
+
+ root->children = talloc_realloc(mem_ctx, root->children,
+ struct object_tree,
+ root->num_of_children + 1);
+ if (!root->children) {
+ return false;
+ }
+ new_node = &root->children[root->num_of_children];
+ root->num_of_children++;
+ }
+
+ new_node->children = NULL;
+ new_node->guid = *guid;
+ new_node->remaining_access = init_access;
+ new_node->num_of_children = 0;
+
+ *new_node_out = new_node;
+ return true;
+}
+
+/* search by GUID */
+struct object_tree *get_object_tree_by_GUID(struct object_tree *root,
+ const struct GUID *guid)
+{
+ struct object_tree *result = NULL;
+ int i;
+
+ if (!root || GUID_equal(&root->guid, guid)) {
+ result = root;
+ return result;
+ }
+ for (i = 0; i < root->num_of_children; i++) {
+ if ((result = get_object_tree_by_GUID(&root->children[i], guid)))
+ break;
+ }
+ return result;
+}
+
+/**
+ * @brief Modify the tree to mark specified access rights as granted
+ *
+ * This function will modify the root and the child of the tree pointed by
+ * root, so that for each tree element the bits set in access_mask are
+ * marked as granted.
+ *
+ * @param[in] root An object_tree structure that we want to modify
+ *
+ * @param[in] access_mask A bitfield of access right that we want to mark as
+ * granted in the whole tree.
+ */
+void object_tree_modify_access(struct object_tree *root,
+ uint32_t access_mask)
+{
+ int i;
+ root->remaining_access &= ~access_mask;
+ for (i = 0; i < root->num_of_children; i++) {
+ object_tree_modify_access(&root->children[i], access_mask);
+ }
+}
diff --git a/libcli/security/privileges.c b/libcli/security/privileges.c
new file mode 100644
index 0000000..33debdc
--- /dev/null
+++ b/libcli/security/privileges.c
@@ -0,0 +1,498 @@
+/*
+ Unix SMB/CIFS implementation.
+ Privileges handling functions
+ Copyright (C) Jean François Micouleau 1998-2001
+ Copyright (C) Simo Sorce 2002-2003
+ Copyright (C) Gerald (Jerry) Carter 2005
+ Copyright (C) Michael Adam 2007
+ Copyright (C) Andrew Bartlett 2010
+ Copyright (C) Andrew Tridgell 2004
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ * Basic privileges functions (mask-operations and conversion
+ * functions between the different formats (se_priv, privset, luid)
+ * moved here * from lib/privileges.c to minimize linker deps.
+ *
+ * generally SID- and LUID-related code is left in lib/privileges.c
+ *
+ * some extra functions to hide privs array from lib/privileges.c
+ */
+
+#include "replace.h"
+#include "libcli/security/privileges.h"
+#include "libcli/security/privileges_private.h"
+#include "librpc/gen_ndr/security.h"
+#include "lib/util/samba_util.h"
+#include "lib/util/debug.h"
+
+/* The use of strcasecmp here is safe, all the comparison strings are ASCII */
+#undef strcasecmp
+
+#define NUM_SHORT_LIST_PRIVS 9
+
+static const struct {
+ enum sec_privilege luid;
+ uint64_t privilege_mask;
+ const char *name;
+ const char *description;
+} privs[] = {
+
+ {SEC_PRIV_MACHINE_ACCOUNT, SEC_PRIV_MACHINE_ACCOUNT_BIT, "SeMachineAccountPrivilege", "Add machines to domain"},
+ {SEC_PRIV_TAKE_OWNERSHIP, SEC_PRIV_TAKE_OWNERSHIP_BIT, "SeTakeOwnershipPrivilege", "Take ownership of files or other objects"},
+ {SEC_PRIV_BACKUP, SEC_PRIV_BACKUP_BIT, "SeBackupPrivilege", "Back up files and directories"},
+ {SEC_PRIV_RESTORE, SEC_PRIV_RESTORE_BIT, "SeRestorePrivilege", "Restore files and directories"},
+ {SEC_PRIV_REMOTE_SHUTDOWN, SEC_PRIV_REMOTE_SHUTDOWN_BIT, "SeRemoteShutdownPrivilege", "Force shutdown from a remote system"},
+
+ {SEC_PRIV_PRINT_OPERATOR, SEC_PRIV_PRINT_OPERATOR_BIT, "SePrintOperatorPrivilege", "Manage printers"},
+ {SEC_PRIV_ADD_USERS, SEC_PRIV_ADD_USERS_BIT, "SeAddUsersPrivilege", "Add users and groups to the domain"},
+ {SEC_PRIV_DISK_OPERATOR, SEC_PRIV_DISK_OPERATOR_BIT, "SeDiskOperatorPrivilege", "Manage disk shares"},
+ {SEC_PRIV_SECURITY, SEC_PRIV_SECURITY_BIT, "SeSecurityPrivilege", "System security"},
+
+
+ /* The list from here on is not displayed in the code from
+ * source3, and is after index NUM_SHORT_LIST_PRIVS for that
+ * reason */
+
+ {SEC_PRIV_SYSTEMTIME,
+ SEC_PRIV_SYSTEMTIME_BIT,
+ "SeSystemtimePrivilege",
+ "Set the system clock"},
+
+ {SEC_PRIV_SHUTDOWN,
+ SEC_PRIV_SHUTDOWN_BIT,
+ "SeShutdownPrivilege",
+ "Shutdown the system"},
+
+ {SEC_PRIV_DEBUG,
+ SEC_PRIV_DEBUG_BIT,
+ "SeDebugPrivilege",
+ "Debug processes"},
+
+ {SEC_PRIV_SYSTEM_ENVIRONMENT,
+ SEC_PRIV_SYSTEM_ENVIRONMENT_BIT,
+ "SeSystemEnvironmentPrivilege",
+ "Modify system environment"},
+
+ {SEC_PRIV_SYSTEM_PROFILE,
+ SEC_PRIV_SYSTEM_PROFILE_BIT,
+ "SeSystemProfilePrivilege",
+ "Profile the system"},
+
+ {SEC_PRIV_PROFILE_SINGLE_PROCESS,
+ SEC_PRIV_PROFILE_SINGLE_PROCESS_BIT,
+ "SeProfileSingleProcessPrivilege",
+ "Profile one process"},
+
+ {SEC_PRIV_INCREASE_BASE_PRIORITY,
+ SEC_PRIV_INCREASE_BASE_PRIORITY_BIT,
+ "SeIncreaseBasePriorityPrivilege",
+ "Increase base priority"},
+
+ {SEC_PRIV_LOAD_DRIVER,
+ SEC_PRIV_LOAD_DRIVER_BIT,
+ "SeLoadDriverPrivilege",
+ "Load drivers"},
+
+ {SEC_PRIV_CREATE_PAGEFILE,
+ SEC_PRIV_CREATE_PAGEFILE_BIT,
+ "SeCreatePagefilePrivilege",
+ "Create page files"},
+
+ {SEC_PRIV_INCREASE_QUOTA,
+ SEC_PRIV_INCREASE_QUOTA_BIT,
+ "SeIncreaseQuotaPrivilege",
+ "Increase quota"},
+
+ {SEC_PRIV_CHANGE_NOTIFY,
+ SEC_PRIV_CHANGE_NOTIFY_BIT,
+ "SeChangeNotifyPrivilege",
+ "Register for change notify"},
+
+ {SEC_PRIV_UNDOCK,
+ SEC_PRIV_UNDOCK_BIT,
+ "SeUndockPrivilege",
+ "Undock devices"},
+
+ {SEC_PRIV_MANAGE_VOLUME,
+ SEC_PRIV_MANAGE_VOLUME_BIT,
+ "SeManageVolumePrivilege",
+ "Manage system volumes"},
+
+ {SEC_PRIV_IMPERSONATE,
+ SEC_PRIV_IMPERSONATE_BIT,
+ "SeImpersonatePrivilege",
+ "Impersonate users"},
+
+ {SEC_PRIV_CREATE_GLOBAL,
+ SEC_PRIV_CREATE_GLOBAL_BIT,
+ "SeCreateGlobalPrivilege",
+ "Create global"},
+
+ {SEC_PRIV_ENABLE_DELEGATION,
+ SEC_PRIV_ENABLE_DELEGATION_BIT,
+ "SeEnableDelegationPrivilege",
+ "Enable Delegation"},
+};
+
+/* These are rights, not privileges, and should not be confused. The
+ * names are very similar, and they are quite similar in behaviour,
+ * but they are not to be enumerated as a system-wide list or have an
+ * LUID value */
+static const struct {
+ uint32_t right_mask;
+ const char *name;
+ const char *description;
+} rights[] = {
+ {LSA_POLICY_MODE_INTERACTIVE,
+ "SeInteractiveLogonRight",
+ "Interactive logon"},
+
+ {LSA_POLICY_MODE_NETWORK,
+ "SeNetworkLogonRight",
+ "Network logon"},
+
+ {LSA_POLICY_MODE_REMOTE_INTERACTIVE,
+ "SeRemoteInteractiveLogonRight",
+ "Remote Interactive logon"}
+};
+
+/*
+ return a privilege mask given a privilege id
+*/
+uint64_t sec_privilege_mask(enum sec_privilege privilege)
+{
+ size_t i;
+ for (i=0;i<ARRAY_SIZE(privs);i++) {
+ if (privs[i].luid == privilege) {
+ return privs[i].privilege_mask;
+ }
+ }
+
+ return 0;
+}
+
+/***************************************************************************
+ put all valid privileges into a mask
+****************************************************************************/
+
+void se_priv_put_all_privileges(uint64_t *privilege_mask)
+{
+ size_t i;
+
+ *privilege_mask = 0;
+ for ( i=0; i<ARRAY_SIZE(privs); i++ ) {
+ *privilege_mask |= privs[i].privilege_mask;
+ }
+}
+
+/*********************************************************************
+ Lookup the uint64_t bitmask value for a privilege name
+*********************************************************************/
+
+bool se_priv_from_name( const char *name, uint64_t *privilege_mask )
+{
+ size_t i;
+ for ( i=0; i<ARRAY_SIZE(privs); i++ ) {
+ if ( strequal( privs[i].name, name ) ) {
+ *privilege_mask = privs[i].privilege_mask;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+const char* get_privilege_dispname( const char *name )
+{
+ size_t i;
+
+ if (!name) {
+ return NULL;
+ }
+
+ for ( i=0; i<ARRAY_SIZE(privs); i++ ) {
+ if ( strequal( privs[i].name, name ) ) {
+ return privs[i].description;
+ }
+ }
+
+ return NULL;
+}
+
+/*******************************************************************
+ return the number of elements in the 'short' privilege array (traditional source3 behaviour)
+*******************************************************************/
+
+int num_privileges_in_short_list( void )
+{
+ return NUM_SHORT_LIST_PRIVS;
+}
+
+/****************************************************************************
+ add a privilege to a privilege array
+ ****************************************************************************/
+
+static bool privilege_set_add(PRIVILEGE_SET *priv_set, struct lsa_LUIDAttribute set)
+{
+ struct lsa_LUIDAttribute *new_set;
+
+ /* we can allocate memory to add the new privilege */
+
+ new_set = talloc_realloc(priv_set->mem_ctx, priv_set->set, struct lsa_LUIDAttribute, priv_set->count + 1);
+ if ( !new_set ) {
+ DEBUG(0,("privilege_set_add: failed to allocate memory!\n"));
+ return false;
+ }
+
+ new_set[priv_set->count].luid.high = set.luid.high;
+ new_set[priv_set->count].luid.low = set.luid.low;
+ new_set[priv_set->count].attribute = set.attribute;
+
+ priv_set->count++;
+ priv_set->set = new_set;
+
+ return true;
+}
+
+/*******************************************************************
+*******************************************************************/
+
+bool se_priv_to_privilege_set( PRIVILEGE_SET *set, uint64_t privilege_mask )
+{
+ size_t i;
+ struct lsa_LUIDAttribute luid;
+
+ luid.attribute = 0;
+ luid.luid.high = 0;
+
+ for ( i=0; i<ARRAY_SIZE(privs); i++ ) {
+ if ((privilege_mask & privs[i].privilege_mask) == 0)
+ continue;
+
+ luid.luid.high = 0;
+ luid.luid.low = privs[i].luid;
+
+ if ( !privilege_set_add( set, luid ) )
+ return false;
+ }
+
+ return true;
+}
+
+/*******************************************************************
+*******************************************************************/
+
+bool privilege_set_to_se_priv( uint64_t *privilege_mask, struct lsa_PrivilegeSet *privset )
+{
+ uint32_t i;
+
+ ZERO_STRUCTP( privilege_mask );
+
+ for ( i=0; i<privset->count; i++ ) {
+ uint64_t r;
+
+ /* sanity check for invalid privilege. we really
+ only care about the low 32 bits */
+
+ if ( privset->set[i].luid.high != 0 )
+ return false;
+
+ r = sec_privilege_mask(privset->set[i].luid.low);
+ if (r) {
+ *privilege_mask |= r;
+ }
+ }
+
+ return true;
+}
+
+/*
+ map a privilege id to the wire string constant
+*/
+const char *sec_privilege_name(enum sec_privilege privilege)
+{
+ size_t i;
+ for (i=0;i<ARRAY_SIZE(privs);i++) {
+ if (privs[i].luid == privilege) {
+ return privs[i].name;
+ }
+ }
+ return NULL;
+}
+
+/*
+ map a privilege id to a privilege display name. Return NULL if not found
+
+ TODO: this should use language mappings
+*/
+const char *sec_privilege_display_name(enum sec_privilege privilege, uint16_t *language)
+{
+ size_t i;
+ for (i=0;i<ARRAY_SIZE(privs);i++) {
+ if (privs[i].luid == privilege) {
+ return privs[i].description;
+ }
+ }
+ return NULL;
+}
+
+/*
+ map a privilege name to a privilege id. Return SEC_PRIV_INVALID if not found
+*/
+enum sec_privilege sec_privilege_id(const char *name)
+{
+ size_t i;
+ for (i=0;i<ARRAY_SIZE(privs);i++) {
+ if (strcasecmp(privs[i].name, name) == 0) {
+ return privs[i].luid;
+ }
+ }
+ return SEC_PRIV_INVALID;
+}
+
+/*
+ map a 'right' name to it's bitmap value. Return 0 if not found
+*/
+uint32_t sec_right_bit(const char *name)
+{
+ size_t i;
+ for (i=0;i<ARRAY_SIZE(rights);i++) {
+ if (strcasecmp(rights[i].name, name) == 0) {
+ return rights[i].right_mask;
+ }
+ }
+ return 0;
+}
+
+/*
+ assist in walking the table of privileges - return the LUID (low 32 bits) by index
+*/
+enum sec_privilege sec_privilege_from_index(int idx)
+{
+ if (idx >= 0 && (unsigned)idx<ARRAY_SIZE(privs)) {
+ return privs[idx].luid;
+ }
+ return SEC_PRIV_INVALID;
+}
+
+/*
+ assist in walking the table of privileges - return the string constant by index
+*/
+const char *sec_privilege_name_from_index(int idx)
+{
+ if (idx >= 0 && (unsigned)idx<ARRAY_SIZE(privs)) {
+ return privs[idx].name;
+ }
+ return NULL;
+}
+
+
+
+/*
+ return true if a security_token has a particular privilege bit set
+*/
+bool security_token_has_privilege(const struct security_token *token, enum sec_privilege privilege)
+{
+ uint64_t mask;
+
+ if (!token) {
+ return false;
+ }
+
+ mask = sec_privilege_mask(privilege);
+ if (mask == 0) {
+ return false;
+ }
+
+ if (token->privilege_mask & mask) {
+ return true;
+ }
+ return false;
+}
+
+bool security_token_system_privilege(const struct security_token *token)
+{
+ if (token == NULL) {
+ return false;
+ }
+
+ if (token->privilege_mask == (uint64_t)~0) {
+ return true;
+ }
+
+ return false;
+}
+
+/*
+ set a bit in the privilege mask
+*/
+void security_token_set_privilege(struct security_token *token, enum sec_privilege privilege)
+{
+ /* Relies on the fact that an invalid privilege will return 0, so won't change this */
+ token->privilege_mask |= sec_privilege_mask(privilege);
+}
+
+/*
+ set a bit in the rights mask
+*/
+void security_token_set_right_bit(struct security_token *token, uint32_t right_bit)
+{
+ token->rights_mask |= right_bit;
+}
+
+char *security_token_debug_privileges(TALLOC_CTX *mem_ctx,
+ const struct security_token *token)
+{
+ char *s = NULL;
+
+ s = talloc_asprintf(mem_ctx,
+ " Privileges (0x%16" PRIX64 "):\n",
+ token->privilege_mask);
+
+ if (token->privilege_mask) {
+ size_t idx = 0;
+ size_t i = 0;
+ for (idx = 0; idx<ARRAY_SIZE(privs); idx++) {
+ if (token->privilege_mask & privs[idx].privilege_mask) {
+ talloc_asprintf_addbuf(
+ &s,
+ " Privilege[%3zu]: %s\n",
+ i++,
+ privs[idx].name);
+ }
+ }
+ }
+
+ talloc_asprintf_addbuf(&s,
+ " Rights (0x%16" PRIX32 "):\n",
+ token->rights_mask);
+
+ if (token->rights_mask) {
+ size_t idx = 0;
+ size_t i = 0;
+ for (idx = 0; idx<ARRAY_SIZE(rights); idx++) {
+ if (token->rights_mask & rights[idx].right_mask) {
+ talloc_asprintf_addbuf(&s,
+ " Right[%3zu]: %s\n",
+ i++,
+ rights[idx].name);
+ }
+ }
+ }
+
+ return s;
+}
diff --git a/libcli/security/privileges.h b/libcli/security/privileges.h
new file mode 100644
index 0000000..e9dab11
--- /dev/null
+++ b/libcli/security/privileges.h
@@ -0,0 +1,116 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB parameters and setup
+ Copyright (C) Andrew Tridgell 1992-1997
+ Copyright (C) Luke Kenneth Casson Leighton 1996-1997
+ Copyright (C) Paul Ashton 1997
+ Copyright (C) Simo Sorce 2003
+ Copyright (C) Gerald (Jerry) Carter 2005
+ Copyright (C) Andrew Bartlett 2010
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef PRIVILEGES_H
+#define PRIVILEGES_H
+
+#include "lib/util/data_blob.h"
+#include "lib/util/time.h"
+#include "../librpc/gen_ndr/lsa.h"
+#include "../librpc/gen_ndr/security.h"
+
+/* common privilege bitmask defines */
+
+#define SE_ALL_PRIVS (uint64_t)-1
+
+/*
+ * These are used in Lsa replies (srv_lsa_nt.c)
+ */
+
+typedef struct {
+ TALLOC_CTX *mem_ctx;
+ bool ext_ctx;
+ uint32_t count;
+ uint32_t control;
+ struct lsa_LUIDAttribute *set;
+} PRIVILEGE_SET;
+
+const char* get_privilege_dispname( const char *name );
+
+/*******************************************************************
+ return the number of elements in the 'short' privilege array (traditional source3 behaviour)
+*******************************************************************/
+
+int num_privileges_in_short_list( void );
+
+/*
+ map a privilege id to the wire string constant
+*/
+const char *sec_privilege_name(enum sec_privilege privilege);
+
+/*
+ map a privilege id to a privilege display name. Return NULL if not found
+
+ TODO: this should use language mappings
+*/
+const char *sec_privilege_display_name(enum sec_privilege privilege, uint16_t *language);
+
+/*
+ map a privilege name to a privilege id. Return -1 if not found
+*/
+enum sec_privilege sec_privilege_id(const char *name);
+
+/*
+ map a 'right' name to it's bitmap value. Return 0 if not found
+*/
+uint32_t sec_right_bit(const char *name);
+
+/*
+ assist in walking the table of privileges - return the LUID (low 32 bits) by index
+*/
+enum sec_privilege sec_privilege_from_index(int idx);
+
+/*
+ assist in walking the table of privileges - return the string constant by index
+*/
+const char *sec_privilege_name_from_index(int idx);
+
+/*
+ return true if a security_token has a particular privilege bit set
+*/
+bool security_token_has_privilege(const struct security_token *token, enum sec_privilege privilege);
+
+
+/**
+ * @brief Check if the security token has system privileges.
+ *
+ * @param[in] token The token to check.
+ *
+ * @return True if the token has system privileges, false if not.
+ */
+bool security_token_system_privilege(const struct security_token *token);
+
+/*
+ set a bit in the privilege mask
+*/
+void security_token_set_privilege(struct security_token *token, enum sec_privilege privilege);
+/*
+ set a bit in the rights mask
+*/
+void security_token_set_right_bit(struct security_token *token, uint32_t right_bit);
+
+char *security_token_debug_privileges(TALLOC_CTX *mem_ctx,
+ const struct security_token *token);
+
+#endif /* PRIVILEGES_H */
diff --git a/libcli/security/privileges_private.h b/libcli/security/privileges_private.h
new file mode 100644
index 0000000..eec5ba3
--- /dev/null
+++ b/libcli/security/privileges_private.h
@@ -0,0 +1,41 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB parameters and setup
+ Copyright (C) Andrew Bartlett 2010
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*********************************************************************
+ Lookup the privilege mask for a privilege name
+*********************************************************************/
+bool se_priv_from_name( const char *name, uint64_t *privilege_mask );
+
+/***************************************************************************
+ return a privilege mask given a privilege id
+****************************************************************************/
+uint64_t sec_privilege_mask(enum sec_privilege privilege);
+
+/***************************************************************************
+ put all privileges into a mask
+****************************************************************************/
+
+void se_priv_put_all_privileges(uint64_t *privilege_mask);
+
+/****************************************************************************
+ Convert PRIVILEGE_SET to a privilege bitmap and back again
+****************************************************************************/
+
+bool se_priv_to_privilege_set( PRIVILEGE_SET *set, uint64_t privilege_mask );
+bool privilege_set_to_se_priv( uint64_t *privilege_mask, struct lsa_PrivilegeSet *privset );
diff --git a/libcli/security/pysecurity.c b/libcli/security/pysecurity.c
new file mode 100644
index 0000000..037d43c
--- /dev/null
+++ b/libcli/security/pysecurity.c
@@ -0,0 +1,96 @@
+/*
+ Unix SMB/CIFS implementation.
+ Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2008
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2011
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "lib/replace/system/python.h"
+#include "python/py3compat.h"
+#include "includes.h"
+#include "python/modules.h"
+#include "libcli/util/pyerrors.h"
+#include "libcli/security/security.h"
+#include "pytalloc.h"
+
+static PyObject *py_se_access_check(PyObject *module, PyObject *args, PyObject *kwargs)
+{
+ NTSTATUS nt_status;
+ const char * const kwnames[] = { "security_descriptor", "token", "access_desired", NULL };
+ PyObject *py_sec_desc = NULL;
+ PyObject *py_security_token = NULL;
+ struct security_descriptor *security_descriptor;
+ struct security_token *security_token;
+ unsigned int access_desired; /* This is an unsigned int, not uint32_t,
+ * because that's what we need for the
+ * python PyArg_ParseTupleAndKeywords */
+ uint32_t access_granted;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OOI",
+ discard_const_p(char *, kwnames),
+ &py_sec_desc, &py_security_token, &access_desired)) {
+ return NULL;
+ }
+
+ security_descriptor = pytalloc_get_type(py_sec_desc, struct security_descriptor);
+ if (!security_descriptor) {
+ PyErr_Format(PyExc_TypeError,
+ "Expected dcerpc.security.descriptor for security_descriptor argument got %s",
+ pytalloc_get_name(py_sec_desc));
+ return NULL;
+ }
+
+ security_token = pytalloc_get_type(py_security_token, struct security_token);
+ if (!security_token) {
+ PyErr_Format(PyExc_TypeError,
+ "Expected dcerpc.security.token for token argument, got %s",
+ pytalloc_get_name(py_sec_desc));
+ return NULL;
+ }
+
+ nt_status = se_access_check(security_descriptor, security_token, access_desired, &access_granted);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ PyErr_NTSTATUS_IS_ERR_RAISE(nt_status);
+ }
+
+ return PyLong_FromLong(access_granted);
+}
+
+static PyMethodDef py_security_methods[] = {
+ { "access_check", PY_DISCARD_FUNC_SIG(PyCFunction,
+ py_se_access_check),
+ METH_VARARGS|METH_KEYWORDS,
+ "access_check(security_descriptor, token, access_desired) -> access_granted. Raises NT_STATUS on error, including on access check failure, returns access granted bitmask"},
+ {0},
+};
+
+static struct PyModuleDef moduledef = {
+ PyModuleDef_HEAD_INIT,
+ .m_name = "security",
+ .m_doc = "Security support.",
+ .m_size = -1,
+ .m_methods = py_security_methods,
+};
+
+MODULE_INIT_FUNC(security)
+{
+ PyObject *m;
+
+ m = PyModule_Create(&moduledef);
+ if (m == NULL)
+ return NULL;
+
+ return m;
+}
diff --git a/libcli/security/sddl.c b/libcli/security/sddl.c
new file mode 100644
index 0000000..d1f7707
--- /dev/null
+++ b/libcli/security/sddl.c
@@ -0,0 +1,1341 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ security descriptor description language functions
+
+ Copyright (C) Andrew Tridgell 2005
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "replace.h"
+#include "lib/util/debug.h"
+#include "libcli/security/security.h"
+#include "libcli/security/conditional_ace.h"
+#include "librpc/gen_ndr/ndr_misc.h"
+#include "lib/util/smb_strtox.h"
+#include "libcli/security/sddl.h"
+#include "system/locale.h"
+#include "lib/util/util_str_hex.h"
+
+
+struct sddl_transition_state {
+ const struct dom_sid *machine_sid;
+ const struct dom_sid *domain_sid;
+ const struct dom_sid *forest_sid;
+};
+
+struct flag_map {
+ const char *name;
+ uint32_t flag;
+};
+
+static bool sddl_map_flag(
+ const struct flag_map *map,
+ const char *str,
+ size_t *plen,
+ uint32_t *pflag)
+{
+ while (map->name != NULL) {
+ size_t len = strlen(map->name);
+ int cmp = strncmp(map->name, str, len);
+
+ if (cmp == 0) {
+ *plen = len;
+ *pflag = map->flag;
+ return true;
+ }
+ map += 1;
+ }
+ return false;
+}
+
+/*
+ map a series of letter codes into a uint32_t
+*/
+static bool sddl_map_flags(const struct flag_map *map, const char *str,
+ uint32_t *pflags, size_t *plen,
+ bool unknown_flag_is_part_of_next_thing)
+{
+ const char *str0 = str;
+ if (plen != NULL) {
+ *plen = 0;
+ }
+ *pflags = 0;
+ while (str[0] != '\0' && isupper((unsigned char)str[0])) {
+ size_t len;
+ uint32_t flags;
+ bool found;
+
+ found = sddl_map_flag(map, str, &len, &flags);
+ if (!found) {
+ break;
+ }
+
+ *pflags |= flags;
+ if (plen != NULL) {
+ *plen += len;
+ }
+ str += len;
+ }
+ /*
+ * For ACL flags, unknown_flag_is_part_of_next_thing is set,
+ * and we expect some more stuff that isn't flags.
+ *
+ * For ACE flags, unknown_flag_is_part_of_next_thing is unset,
+ * and the flags have been tokenised into their own little
+ * string. We don't expect anything here, even whitespace.
+ */
+ if (*str == '\0' || unknown_flag_is_part_of_next_thing) {
+ return true;
+ }
+ DBG_WARNING("Unknown flag - '%s' in '%s'\n", str, str0);
+ return false;
+}
+
+
+/*
+ a mapping between the 2 letter SID codes and sid strings
+*/
+static const struct {
+ const char *code;
+ const char *sid;
+ uint32_t machine_rid;
+ uint32_t domain_rid;
+ uint32_t forest_rid;
+} sid_codes[] = {
+ { .code = "WD", .sid = SID_WORLD },
+
+ { .code = "CO", .sid = SID_CREATOR_OWNER },
+ { .code = "CG", .sid = SID_CREATOR_GROUP },
+ { .code = "OW", .sid = SID_OWNER_RIGHTS },
+
+ { .code = "NU", .sid = SID_NT_NETWORK },
+ { .code = "IU", .sid = SID_NT_INTERACTIVE },
+ { .code = "SU", .sid = SID_NT_SERVICE },
+ { .code = "AN", .sid = SID_NT_ANONYMOUS },
+ { .code = "ED", .sid = SID_NT_ENTERPRISE_DCS },
+ { .code = "PS", .sid = SID_NT_SELF },
+ { .code = "AU", .sid = SID_NT_AUTHENTICATED_USERS },
+ { .code = "RC", .sid = SID_NT_RESTRICTED },
+ { .code = "SY", .sid = SID_NT_SYSTEM },
+ { .code = "LS", .sid = SID_NT_LOCAL_SERVICE },
+ { .code = "NS", .sid = SID_NT_NETWORK_SERVICE },
+ { .code = "WR", .sid = SID_SECURITY_RESTRICTED_CODE },
+
+ { .code = "BA", .sid = SID_BUILTIN_ADMINISTRATORS },
+ { .code = "BU", .sid = SID_BUILTIN_USERS },
+ { .code = "BG", .sid = SID_BUILTIN_GUESTS },
+ { .code = "PU", .sid = SID_BUILTIN_POWER_USERS },
+ { .code = "AO", .sid = SID_BUILTIN_ACCOUNT_OPERATORS },
+ { .code = "SO", .sid = SID_BUILTIN_SERVER_OPERATORS },
+ { .code = "PO", .sid = SID_BUILTIN_PRINT_OPERATORS },
+ { .code = "BO", .sid = SID_BUILTIN_BACKUP_OPERATORS },
+ { .code = "RE", .sid = SID_BUILTIN_REPLICATOR },
+ { .code = "RU", .sid = SID_BUILTIN_PREW2K },
+ { .code = "RD", .sid = SID_BUILTIN_REMOTE_DESKTOP_USERS },
+ { .code = "NO", .sid = SID_BUILTIN_NETWORK_CONF_OPERATORS },
+
+ { .code = "MU", .sid = SID_BUILTIN_PERFMON_USERS },
+ { .code = "LU", .sid = SID_BUILTIN_PERFLOG_USERS },
+ { .code = "IS", .sid = SID_BUILTIN_IUSERS },
+ { .code = "CY", .sid = SID_BUILTIN_CRYPTO_OPERATORS },
+ { .code = "ER", .sid = SID_BUILTIN_EVENT_LOG_READERS },
+ { .code = "CD", .sid = SID_BUILTIN_CERT_SERV_DCOM_ACCESS },
+ { .code = "RA", .sid = SID_BUILTIN_RDS_REMOTE_ACCESS_SERVERS },
+ { .code = "ES", .sid = SID_BUILTIN_RDS_ENDPOINT_SERVERS },
+ { .code = "MS", .sid = SID_BUILTIN_RDS_MANAGEMENT_SERVERS },
+ { .code = "HA", .sid = SID_BUILTIN_HYPER_V_ADMINS },
+ { .code = "AA", .sid = SID_BUILTIN_ACCESS_CONTROL_ASSISTANCE_OPS },
+ { .code = "RM", .sid = SID_BUILTIN_REMOTE_MANAGEMENT_USERS },
+
+ { .code = "UD", .sid = SID_USER_MODE_DRIVERS },
+
+ { .code = "AC", .sid = SID_SECURITY_BUILTIN_PACKAGE_ANY_PACKAGE },
+
+ { .code = "LW", .sid = SID_SECURITY_MANDATORY_LOW },
+ { .code = "ME", .sid = SID_SECURITY_MANDATORY_MEDIUM },
+ { .code = "MP", .sid = SID_SECURITY_MANDATORY_MEDIUM_PLUS },
+ { .code = "HI", .sid = SID_SECURITY_MANDATORY_HIGH },
+ { .code = "SI", .sid = SID_SECURITY_MANDATORY_SYSTEM },
+
+ { .code = "AS", .sid = SID_AUTHENTICATION_AUTHORITY_ASSERTED_IDENTITY },
+ { .code = "SS", .sid = SID_SERVICE_ASSERTED_IDENTITY },
+
+ { .code = "RO", .forest_rid = DOMAIN_RID_ENTERPRISE_READONLY_DCS },
+
+ { .code = "LA", .machine_rid = DOMAIN_RID_ADMINISTRATOR },
+ { .code = "LG", .machine_rid = DOMAIN_RID_GUEST },
+
+ { .code = "DA", .domain_rid = DOMAIN_RID_ADMINS },
+ { .code = "DU", .domain_rid = DOMAIN_RID_USERS },
+ { .code = "DG", .domain_rid = DOMAIN_RID_GUESTS },
+ { .code = "DC", .domain_rid = DOMAIN_RID_DOMAIN_MEMBERS },
+ { .code = "DD", .domain_rid = DOMAIN_RID_DCS },
+ { .code = "CA", .domain_rid = DOMAIN_RID_CERT_ADMINS },
+ { .code = "SA", .forest_rid = DOMAIN_RID_SCHEMA_ADMINS },
+ { .code = "EA", .forest_rid = DOMAIN_RID_ENTERPRISE_ADMINS },
+ { .code = "PA", .domain_rid = DOMAIN_RID_POLICY_ADMINS },
+
+ { .code = "CN", .domain_rid = DOMAIN_RID_CLONEABLE_CONTROLLERS },
+
+ { .code = "AP", .domain_rid = DOMAIN_RID_PROTECTED_USERS },
+ { .code = "KA", .domain_rid = DOMAIN_RID_KEY_ADMINS },
+ { .code = "EK", .forest_rid = DOMAIN_RID_ENTERPRISE_KEY_ADMINS },
+
+ { .code = "RS", .domain_rid = DOMAIN_RID_RAS_SERVERS }
+};
+
+/*
+ decode a SID
+ It can either be a special 2 letter code, or in S-* format
+*/
+static struct dom_sid *sddl_transition_decode_sid(TALLOC_CTX *mem_ctx, const char **sddlp,
+ struct sddl_transition_state *state)
+{
+ const char *sddl = (*sddlp);
+ size_t i;
+
+ /* see if its in the numeric format */
+ if (strncasecmp(sddl, "S-", 2) == 0) {
+ struct dom_sid *sid = NULL;
+ char *sid_str = NULL;
+ const char *end = NULL;
+ bool ok;
+ size_t len = strspn(sddl + 2, "-0123456789ABCDEFabcdefxX") + 2;
+ if (len < 5) { /* S-1-x */
+ return NULL;
+ }
+ if (sddl[len - 1] == 'D' && sddl[len] == ':') {
+ /*
+ * we have run into the "D:" dacl marker, mistaking it
+ * for a hex digit. There is no other way for this
+ * pair to occur at the end of a SID in SDDL.
+ */
+ len--;
+ }
+
+ sid_str = talloc_strndup(mem_ctx, sddl, len);
+ if (sid_str == NULL) {
+ return NULL;
+ }
+ if (sid_str[0] == 's') {
+ /*
+ * In SDDL, but not in the dom_sid parsers, a
+ * lowercase "s-1-1-0" is accepted.
+ */
+ sid_str[0] = 'S';
+ }
+ sid = talloc(mem_ctx, struct dom_sid);
+ if (sid == NULL) {
+ TALLOC_FREE(sid_str);
+ return NULL;
+ };
+ ok = dom_sid_parse_endp(sid_str, sid, &end);
+ if (!ok) {
+ DBG_WARNING("could not parse SID '%s'\n", sid_str);
+ TALLOC_FREE(sid_str);
+ TALLOC_FREE(sid);
+ return NULL;
+ }
+ if (end - sid_str != len) {
+ DBG_WARNING("trailing junk after SID '%s'\n", sid_str);
+ TALLOC_FREE(sid_str);
+ TALLOC_FREE(sid);
+ return NULL;
+ }
+ TALLOC_FREE(sid_str);
+ (*sddlp) += len;
+ return sid;
+ }
+
+ /* now check for one of the special codes */
+ for (i=0;i<ARRAY_SIZE(sid_codes);i++) {
+ if (strncmp(sid_codes[i].code, sddl, 2) == 0) break;
+ }
+ if (i == ARRAY_SIZE(sid_codes)) {
+ DEBUG(1,("Unknown sddl sid code '%2.2s'\n", sddl));
+ return NULL;
+ }
+
+ (*sddlp) += 2;
+
+
+ if (sid_codes[i].machine_rid != 0) {
+ return dom_sid_add_rid(mem_ctx, state->machine_sid,
+ sid_codes[i].machine_rid);
+ }
+
+ if (sid_codes[i].domain_rid != 0) {
+ return dom_sid_add_rid(mem_ctx, state->domain_sid,
+ sid_codes[i].domain_rid);
+ }
+
+ if (sid_codes[i].forest_rid != 0) {
+ return dom_sid_add_rid(mem_ctx, state->forest_sid,
+ sid_codes[i].forest_rid);
+ }
+
+ return dom_sid_parse_talloc(mem_ctx, sid_codes[i].sid);
+}
+
+struct dom_sid *sddl_decode_sid(TALLOC_CTX *mem_ctx, const char **sddlp,
+ const struct dom_sid *domain_sid)
+{
+ struct sddl_transition_state state = {
+ /*
+ * TODO: verify .machine_rid values really belong
+ * to the machine_sid on a member, once
+ * we pass machine_sid from the caller...
+ */
+ .machine_sid = domain_sid,
+ .domain_sid = domain_sid,
+ .forest_sid = domain_sid,
+ };
+ return sddl_transition_decode_sid(mem_ctx, sddlp, &state);
+}
+
+
+static const struct flag_map ace_types[] = {
+ { "AU", SEC_ACE_TYPE_SYSTEM_AUDIT },
+ { "AL", SEC_ACE_TYPE_SYSTEM_ALARM },
+ { "OA", SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT },
+ { "OD", SEC_ACE_TYPE_ACCESS_DENIED_OBJECT },
+ { "OU", SEC_ACE_TYPE_SYSTEM_AUDIT_OBJECT },
+ { "OL", SEC_ACE_TYPE_SYSTEM_ALARM_OBJECT },
+ { "A", SEC_ACE_TYPE_ACCESS_ALLOWED },
+ { "D", SEC_ACE_TYPE_ACCESS_DENIED },
+
+ { "XA", SEC_ACE_TYPE_ACCESS_ALLOWED_CALLBACK },
+ { "XD", SEC_ACE_TYPE_ACCESS_DENIED_CALLBACK },
+ { "ZA", SEC_ACE_TYPE_ACCESS_ALLOWED_CALLBACK_OBJECT },
+ /*
+ * SEC_ACE_TYPE_ACCESS_DENIED_CALLBACK_OBJECT exists but has
+ * no SDDL flag.
+ *
+ * ZA and XU are switched in [MS-DTYP] as of version 36.0,
+ * but this should be corrected in later versions.
+ */
+ { "XU", SEC_ACE_TYPE_SYSTEM_AUDIT_CALLBACK },
+
+ { "RA", SEC_ACE_TYPE_SYSTEM_RESOURCE_ATTRIBUTE },
+ { NULL, 0 }
+};
+
+static const struct flag_map ace_flags[] = {
+ { "OI", SEC_ACE_FLAG_OBJECT_INHERIT },
+ { "CI", SEC_ACE_FLAG_CONTAINER_INHERIT },
+ { "NP", SEC_ACE_FLAG_NO_PROPAGATE_INHERIT },
+ { "IO", SEC_ACE_FLAG_INHERIT_ONLY },
+ { "ID", SEC_ACE_FLAG_INHERITED_ACE },
+ { "SA", SEC_ACE_FLAG_SUCCESSFUL_ACCESS },
+ { "FA", SEC_ACE_FLAG_FAILED_ACCESS },
+ { NULL, 0 },
+};
+
+static const struct flag_map ace_access_mask[] = {
+ { "CC", SEC_ADS_CREATE_CHILD },
+ { "DC", SEC_ADS_DELETE_CHILD },
+ { "LC", SEC_ADS_LIST },
+ { "SW", SEC_ADS_SELF_WRITE },
+ { "RP", SEC_ADS_READ_PROP },
+ { "WP", SEC_ADS_WRITE_PROP },
+ { "DT", SEC_ADS_DELETE_TREE },
+ { "LO", SEC_ADS_LIST_OBJECT },
+ { "CR", SEC_ADS_CONTROL_ACCESS },
+ { "SD", SEC_STD_DELETE },
+ { "RC", SEC_STD_READ_CONTROL },
+ { "WD", SEC_STD_WRITE_DAC },
+ { "WO", SEC_STD_WRITE_OWNER },
+ { "GA", SEC_GENERIC_ALL },
+ { "GX", SEC_GENERIC_EXECUTE },
+ { "GW", SEC_GENERIC_WRITE },
+ { "GR", SEC_GENERIC_READ },
+ { NULL, 0 }
+};
+
+static const struct flag_map decode_ace_access_mask[] = {
+ { "FA", FILE_GENERIC_ALL },
+ { "FR", FILE_GENERIC_READ },
+ { "FW", FILE_GENERIC_WRITE },
+ { "FX", FILE_GENERIC_EXECUTE },
+ { NULL, 0 },
+};
+
+
+static char *sddl_match_file_rights(TALLOC_CTX *mem_ctx,
+ uint32_t flags)
+{
+ int i;
+
+ /* try to find an exact match */
+ for (i=0;decode_ace_access_mask[i].name;i++) {
+ if (decode_ace_access_mask[i].flag == flags) {
+ return talloc_strdup(mem_ctx,
+ decode_ace_access_mask[i].name);
+ }
+ }
+ return NULL;
+}
+
+static bool sddl_decode_access(const char *str, uint32_t *pmask)
+{
+ const char *str0 = str;
+ char *end = NULL;
+ uint32_t mask = 0;
+ unsigned long long numeric_mask;
+ int err;
+ /*
+ * The access mask can be a number or a series of flags.
+ *
+ * Canonically the number is expressed in hexadecimal (with 0x), but
+ * per MS-DTYP and Windows behaviour, octal and decimal numbers are
+ * also accepted.
+ *
+ * Windows has two behaviours we choose not to replicate:
+ *
+ * 1. numbers exceeding 0xffffffff are truncated at that point,
+ * turning on all access flags.
+ *
+ * 2. negative numbers are accepted, so e.g. -2 becomes 0xfffffffe.
+ */
+ numeric_mask = smb_strtoull(str, &end, 0, &err, SMB_STR_STANDARD);
+ if (err == 0) {
+ if (numeric_mask > UINT32_MAX) {
+ DBG_WARNING("Bad numeric flag value - %llu in %s\n",
+ numeric_mask, str0);
+ return false;
+ }
+ if (end - str > sizeof("037777777777")) {
+ /* here's the tricky thing: if a number is big
+ * enough to overflow the uint64, it might end
+ * up small enough to fit in the uint32, and
+ * we'd miss that it overflowed. So we count
+ * the digits -- any more than 12 (for
+ * "037777777777") is too long for 32 bits,
+ * and the shortest 64-bit wrapping string is
+ * 19 (for "0x1" + 16 zeros).
+ */
+ DBG_WARNING("Bad numeric flag value in '%s'\n", str0);
+ return false;
+ }
+ if (*end != '\0') {
+ DBG_WARNING("Bad characters in '%s'\n", str0);
+ return false;
+ }
+ *pmask = numeric_mask;
+ return true;
+ }
+ /* It's not a positive number, so we'll look for flags */
+
+ while ((str[0] != '\0') &&
+ (isupper((unsigned char)str[0]) || str[0] == ' ')) {
+ uint32_t flags = 0;
+ size_t len = 0;
+ bool found;
+ while (str[0] == ' ') {
+ /*
+ * Following Windows we accept spaces between flags
+ * but not after flags. Not tabs, though, never tabs.
+ */
+ str++;
+ if (str[0] == '\0') {
+ DBG_WARNING("trailing whitespace in flags "
+ "- '%s'\n", str0);
+ return false;
+ }
+ }
+ found = sddl_map_flag(
+ ace_access_mask, str, &len, &flags);
+ found |= sddl_map_flag(
+ decode_ace_access_mask, str, &len, &flags);
+ if (!found) {
+ DEBUG(1, ("Unknown flag - %s in %s\n", str, str0));
+ return false;
+ }
+ mask |= flags;
+ str += len;
+ }
+ if (*str != '\0') {
+ DBG_WARNING("Bad characters in '%s'\n", str0);
+ return false;
+ }
+ *pmask = mask;
+ return true;
+}
+
+
+static bool sddl_decode_guid(const char *str, struct GUID *guid)
+{
+ if (strlen(str) != 36) {
+ return false;
+ }
+ return parse_guid_string(str, guid);
+}
+
+
+
+static DATA_BLOB sddl_decode_conditions(TALLOC_CTX *mem_ctx,
+ const enum ace_condition_flags ace_condition_flags,
+ const char *conditions,
+ size_t *length,
+ const char **msg,
+ size_t *msg_offset)
+{
+ DATA_BLOB blob = {0};
+ struct ace_condition_script *script = NULL;
+ script = ace_conditions_compile_sddl(mem_ctx,
+ ace_condition_flags,
+ conditions,
+ msg,
+ msg_offset,
+ length);
+ if (script != NULL) {
+ bool ok = conditional_ace_encode_binary(mem_ctx,
+ script,
+ &blob);
+ if (! ok) {
+ DBG_ERR("could not blobify '%s'\n", conditions);
+ }
+ }
+ return blob;
+}
+
+
+/*
+ decode an ACE
+ return true on success, false on failure
+ note that this routine modifies the string
+*/
+static bool sddl_decode_ace(TALLOC_CTX *mem_ctx,
+ struct security_ace *ace,
+ const enum ace_condition_flags ace_condition_flags,
+ char **sddl_copy,
+ struct sddl_transition_state *state,
+ const char **msg, size_t *msg_offset)
+{
+ const char *tok[7];
+ const char *s;
+ uint32_t v;
+ struct dom_sid *sid;
+ bool ok;
+ size_t len;
+ size_t count = 0;
+ char *str = *sddl_copy;
+ bool has_extra_data = false;
+ ZERO_STRUCTP(ace);
+
+ *msg_offset = 1;
+ if (*str != '(') {
+ *msg = talloc_strdup(mem_ctx, "Not an ACE");
+ return false;
+ }
+ str++;
+ /*
+ * First we split apart the 6 (or 7) tokens.
+ *
+ * 0. ace type
+ * 1. ace flags
+ * 2. access mask
+ * 3. object guid
+ * 4. inherit guid
+ * 5. sid
+ *
+ * 6/extra_data rare optional extra data
+ */
+ tok[0] = str;
+ while (*str != '\0') {
+ if (*str == ';') {
+ *str = '\0';
+ str++;
+ count++;
+ tok[count] = str;
+ if (count == 6) {
+ /*
+ * this looks like a conditional ACE
+ * or resource ACE, but we can't say
+ * for sure until we look at the ACE
+ * type (tok[0]), after the loop.
+ */
+ has_extra_data = true;
+ break;
+ }
+ continue;
+ }
+ /*
+ * we are not expecting a ')' in the 6 sections of an
+ * ordinary ACE, except ending the last one.
+ */
+ if (*str == ')') {
+ count++;
+ *str = '\0';
+ str++;
+ break;
+ }
+ str++;
+ }
+ if (count != 6) {
+ /* we hit the '\0' or ')' before all of ';;;;;)' */
+ *msg = talloc_asprintf(mem_ctx,
+ "malformed ACE with only %zu ';'",
+ MIN(count - 1, count));
+ return false;
+ }
+
+ /* parse ace type */
+ ok = sddl_map_flag(ace_types, tok[0], &len, &v);
+ if (!ok) {
+ *msg = talloc_asprintf(mem_ctx,
+ "Unknown ACE type - %s", tok[0]);
+ return false;
+ }
+ if (tok[0][len] != '\0') {
+ *msg = talloc_asprintf(mem_ctx,
+ "Garbage after ACE type - %s", tok[0]);
+ return false;
+ }
+
+ ace->type = v;
+
+ /*
+ * Only callback and resource aces should have trailing data.
+ */
+ if (sec_ace_callback(ace->type)) {
+ if (! has_extra_data) {
+ *msg = talloc_strdup(
+ mem_ctx,
+ "callback ACE has no trailing data");
+ *msg_offset = str - *sddl_copy;
+ return false;
+ }
+ } else if (sec_ace_resource(ace->type)) {
+ if (! has_extra_data) {
+ *msg = talloc_strdup(
+ mem_ctx,
+ "resource attribute ACE has no trailing data");
+ *msg_offset = str - *sddl_copy;
+ return false;
+ }
+ } else if (has_extra_data) {
+ *msg = talloc_strdup(
+ mem_ctx,
+ "ACE has trailing section but is not a "
+ "callback or resource ACE");
+ *msg_offset = str - *sddl_copy;
+ return false;
+ }
+
+ /* ace flags */
+ if (!sddl_map_flags(ace_flags, tok[1], &v, NULL, false)) {
+ *msg = talloc_strdup(mem_ctx,
+ "could not parse flags");
+ *msg_offset = tok[1] - *sddl_copy;
+ return false;
+ }
+ ace->flags = v;
+
+ /* access mask */
+ ok = sddl_decode_access(tok[2], &ace->access_mask);
+ if (!ok) {
+ *msg = talloc_strdup(mem_ctx,
+ "could not parse access string");
+ *msg_offset = tok[2] - *sddl_copy;
+ return false;
+ }
+
+ /* object */
+ if (tok[3][0] != 0) {
+ ok = sddl_decode_guid(tok[3], &ace->object.object.type.type);
+ if (!ok) {
+ *msg = talloc_strdup(mem_ctx,
+ "could not parse object GUID");
+ *msg_offset = tok[3] - *sddl_copy;
+ return false;
+ }
+ ace->object.object.flags |= SEC_ACE_OBJECT_TYPE_PRESENT;
+ }
+
+ /* inherit object */
+ if (tok[4][0] != 0) {
+ ok = sddl_decode_guid(tok[4],
+ &ace->object.object.inherited_type.inherited_type);
+ if (!ok) {
+ *msg = talloc_strdup(
+ mem_ctx,
+ "could not parse inherited object GUID");
+ *msg_offset = tok[4] - *sddl_copy;
+ return false;
+ }
+ ace->object.object.flags |= SEC_ACE_INHERITED_OBJECT_TYPE_PRESENT;
+ }
+
+ /* trustee */
+ s = tok[5];
+ sid = sddl_transition_decode_sid(mem_ctx, &s, state);
+ if (sid == NULL) {
+ *msg = talloc_strdup(
+ mem_ctx,
+ "could not parse trustee SID");
+ *msg_offset = tok[5] - *sddl_copy;
+ return false;
+ }
+ ace->trustee = *sid;
+ talloc_free(sid);
+ if (*s != '\0') {
+ *msg = talloc_strdup(
+ mem_ctx,
+ "garbage after trustee SID");
+ *msg_offset = s - *sddl_copy;
+ return false;
+ }
+
+ if (sec_ace_callback(ace->type)) {
+ /*
+ * This is either a conditional ACE or some unknown
+ * type of callback ACE that will be rejected by the
+ * conditional ACE compiler.
+ */
+ size_t length;
+ DATA_BLOB conditions = {0};
+ s = tok[6];
+
+ conditions = sddl_decode_conditions(mem_ctx,
+ ace_condition_flags,
+ s,
+ &length,
+ msg,
+ msg_offset);
+ if (conditions.data == NULL) {
+ DBG_NOTICE("Conditional ACE compilation failure at %zu: %s\n",
+ *msg_offset, *msg);
+ *msg_offset += s - *sddl_copy;
+ return false;
+ }
+ ace->coda.conditions = conditions;
+
+ /*
+ * We have found the end of the conditions, and the
+ * next character should be the ')' to end the ACE.
+ */
+ if (s[length] != ')') {
+ *msg = talloc_strdup(
+ mem_ctx,
+ "Conditional ACE has trailing bytes"
+ " or lacks ')'");
+ *msg_offset = s + length - *sddl_copy;
+ return false;
+ }
+ str = discard_const_p(char, s + length + 1);
+ } else if (sec_ace_resource(ace->type)) {
+ size_t length;
+ struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim = NULL;
+
+ if (! dom_sid_equal(&ace->trustee, &global_sid_World)) {
+ /* these are just the rules */
+ *msg = talloc_strdup(
+ mem_ctx,
+ "Resource Attribute ACE trustee must be "
+ "'S-1-1-0' or 'WD'.");
+ *msg_offset = tok[5] - *sddl_copy;
+ return false;
+ }
+
+ s = tok[6];
+ claim = sddl_decode_resource_attr(mem_ctx, s, &length);
+ if (claim == NULL) {
+ *msg = talloc_strdup(
+ mem_ctx,
+ "Resource Attribute ACE parse failure");
+ *msg_offset = s - *sddl_copy;
+ return false;
+ }
+ ace->coda.claim = *claim;
+
+ /*
+ * We want a ')' to end the ACE.
+ */
+ if (s[length] != ')') {
+ *msg = talloc_strdup(
+ mem_ctx,
+ "Resource Attribute ACE has trailing bytes"
+ " or lacks ')'");
+ *msg_offset = s + length - *sddl_copy;
+ return false;
+ }
+ str = discard_const_p(char, s + length + 1);
+ }
+
+ *sddl_copy = str;
+ return true;
+}
+
+static const struct flag_map acl_flags[] = {
+ { "P", SEC_DESC_DACL_PROTECTED },
+ { "AR", SEC_DESC_DACL_AUTO_INHERIT_REQ },
+ { "AI", SEC_DESC_DACL_AUTO_INHERITED },
+ { NULL, 0 }
+};
+
+/*
+ decode an ACL
+*/
+static struct security_acl *sddl_decode_acl(struct security_descriptor *sd,
+ const enum ace_condition_flags ace_condition_flags,
+ const char **sddlp, uint32_t *flags,
+ struct sddl_transition_state *state,
+ const char **msg, size_t *msg_offset)
+{
+ const char *sddl = *sddlp;
+ char *sddl_copy = NULL;
+ char *aces_start = NULL;
+ struct security_acl *acl;
+ size_t len;
+ *flags = 0;
+
+ acl = talloc_zero(sd, struct security_acl);
+ if (acl == NULL) {
+ return NULL;
+ }
+ acl->revision = SECURITY_ACL_REVISION_ADS;
+
+ if (isupper((unsigned char)sddl[0]) && sddl[1] == ':') {
+ /* its an empty ACL */
+ return acl;
+ }
+
+ /* work out the ACL flags */
+ if (!sddl_map_flags(acl_flags, sddl, flags, &len, true)) {
+ *msg = talloc_strdup(sd, "bad ACL flags");
+ *msg_offset = 0;
+ talloc_free(acl);
+ return NULL;
+ }
+ sddl += len;
+
+ if (sddl[0] != '(') {
+ /*
+ * it is empty apart from the flags
+ * (or the flags are bad, and we will find out when
+ * we try to parse the next bit as a top-level fragment)
+ */
+ *sddlp = sddl;
+ return acl;
+ }
+
+ /*
+ * now the ACEs
+ *
+ * For this we make a copy of the rest of the SDDL, which the ACE
+ * tokeniser will mutilate by putting '\0' where it finds ';'.
+ *
+ * We need to copy the rest of the SDDL string because it is not
+ * possible in general to find where an ACL ends if there are
+ * conditional ACEs.
+ */
+
+ sddl_copy = talloc_strdup(acl, sddl);
+ if (sddl_copy == NULL) {
+ TALLOC_FREE(acl);
+ return NULL;
+ }
+ aces_start = sddl_copy;
+
+ while (*sddl_copy == '(') {
+ bool ok;
+ if (acl->num_aces > UINT16_MAX / 16) {
+ /*
+ * We can't fit this many ACEs in a wire ACL
+ * which has a 16 bit size field (and 16 is
+ * the minimal size of an ACE with no subauths).
+ */
+ talloc_free(acl);
+ return NULL;
+ }
+
+ acl->aces = talloc_realloc(acl, acl->aces, struct security_ace,
+ acl->num_aces+1);
+ if (acl->aces == NULL) {
+ talloc_free(acl);
+ return NULL;
+ }
+ ok = sddl_decode_ace(acl->aces, &acl->aces[acl->num_aces],
+ ace_condition_flags,
+ &sddl_copy, state, msg, msg_offset);
+ if (!ok) {
+ *msg_offset += sddl_copy - aces_start;
+ talloc_steal(sd, *msg);
+ talloc_free(acl);
+ return NULL;
+ }
+ acl->num_aces++;
+ }
+ sddl += sddl_copy - aces_start;
+ TALLOC_FREE(aces_start);
+ (*sddlp) = sddl;
+ return acl;
+}
+
+/*
+ * Decode a security descriptor in SDDL format, catching compilation
+ * error messages, if any.
+ *
+ * The message will be a direct talloc child of mem_ctx or NULL.
+ */
+struct security_descriptor *sddl_decode_err_msg(TALLOC_CTX *mem_ctx, const char *sddl,
+ const struct dom_sid *domain_sid,
+ const enum ace_condition_flags ace_condition_flags,
+ const char **msg, size_t *msg_offset)
+{
+ struct sddl_transition_state state = {
+ /*
+ * TODO: verify .machine_rid values really belong
+ * to the machine_sid on a member, once
+ * we pass machine_sid from the caller...
+ */
+ .machine_sid = domain_sid,
+ .domain_sid = domain_sid,
+ .forest_sid = domain_sid,
+ };
+ const char *start = sddl;
+ struct security_descriptor *sd = NULL;
+
+ if (msg == NULL || msg_offset == NULL) {
+ DBG_ERR("Programmer misbehaviour: use sddl_decode() "
+ "or provide msg pointers.\n");
+ return NULL;
+ }
+ *msg = NULL;
+ *msg_offset = 0;
+
+ sd = talloc_zero(mem_ctx, struct security_descriptor);
+ if (sd == NULL) {
+ return NULL;
+ }
+ sd->revision = SECURITY_DESCRIPTOR_REVISION_1;
+ sd->type = SEC_DESC_SELF_RELATIVE;
+
+ while (*sddl) {
+ uint32_t flags;
+ char c = sddl[0];
+ if (sddl[1] != ':') {
+ *msg = talloc_strdup(mem_ctx,
+ "expected '[OGDS]:' section start "
+ "(or the previous section ended prematurely)");
+ goto failed;
+ }
+ sddl += 2;
+ switch (c) {
+ case 'D':
+ if (sd->dacl != NULL) goto failed;
+ sd->dacl = sddl_decode_acl(sd, ace_condition_flags, &sddl, &flags, &state, msg, msg_offset);
+ if (sd->dacl == NULL) goto failed;
+ sd->type |= flags | SEC_DESC_DACL_PRESENT;
+ break;
+ case 'S':
+ if (sd->sacl != NULL) goto failed;
+ sd->sacl = sddl_decode_acl(sd, ace_condition_flags, &sddl, &flags, &state, msg, msg_offset);
+ if (sd->sacl == NULL) goto failed;
+ /* this relies on the SEC_DESC_SACL_* flags being
+ 1 bit shifted from the SEC_DESC_DACL_* flags */
+ sd->type |= (flags<<1) | SEC_DESC_SACL_PRESENT;
+ break;
+ case 'O':
+ if (sd->owner_sid != NULL) goto failed;
+ sd->owner_sid = sddl_transition_decode_sid(sd, &sddl, &state);
+ if (sd->owner_sid == NULL) goto failed;
+ break;
+ case 'G':
+ if (sd->group_sid != NULL) goto failed;
+ sd->group_sid = sddl_transition_decode_sid(sd, &sddl, &state);
+ if (sd->group_sid == NULL) goto failed;
+ break;
+ default:
+ *msg = talloc_strdup(mem_ctx, "unexpected character (expected [OGDS])");
+ goto failed;
+ }
+ }
+ return sd;
+failed:
+ if (*msg != NULL) {
+ *msg = talloc_steal(mem_ctx, *msg);
+ }
+ /*
+ * The actual message (*msg) might still be NULL, but the
+ * offset at least provides a clue.
+ */
+ *msg_offset += sddl - start;
+
+ if (*msg_offset > strlen(sddl)) {
+ /*
+ * It's not that we *don't* trust our pointer difference
+ * arithmetic, just that we *shouldn't*. Let's render it
+ * harmless, before Python tries printing 18 quadrillion
+ * spaces.
+ */
+ DBG_WARNING("sddl error message offset %zu is too big\n",
+ *msg_offset);
+ *msg_offset = 0;
+ }
+ DEBUG(2,("Badly formatted SDDL '%s'\n", sddl));
+ talloc_free(sd);
+ return NULL;
+}
+
+
+/*
+ decode a security descriptor in SDDL format
+*/
+struct security_descriptor *sddl_decode(TALLOC_CTX *mem_ctx, const char *sddl,
+ const struct dom_sid *domain_sid)
+{
+ const char *msg = NULL;
+ size_t msg_offset = 0;
+ struct security_descriptor *sd = sddl_decode_err_msg(mem_ctx,
+ sddl,
+ domain_sid,
+ ACE_CONDITION_FLAG_ALLOW_DEVICE,
+ &msg,
+ &msg_offset);
+ if (sd == NULL) {
+ DBG_NOTICE("could not decode '%s'\n", sddl);
+ if (msg != NULL) {
+ DBG_NOTICE(" %*c\n",
+ (int)msg_offset, '^');
+ DBG_NOTICE("error '%s'\n", msg);
+ talloc_free(discard_const(msg));
+ }
+ }
+ return sd;
+}
+
+/*
+ turn a set of flags into a string
+*/
+static char *sddl_flags_to_string(TALLOC_CTX *mem_ctx, const struct flag_map *map,
+ uint32_t flags, bool check_all)
+{
+ int i;
+ char *s;
+
+ /* try to find an exact match */
+ for (i=0;map[i].name;i++) {
+ if (map[i].flag == flags) {
+ return talloc_strdup(mem_ctx, map[i].name);
+ }
+ }
+
+ s = talloc_strdup(mem_ctx, "");
+
+ /* now by bits */
+ for (i=0;map[i].name;i++) {
+ if ((flags & map[i].flag) != 0) {
+ s = talloc_asprintf_append_buffer(s, "%s", map[i].name);
+ if (s == NULL) goto failed;
+ flags &= ~map[i].flag;
+ }
+ }
+
+ if (check_all && flags != 0) {
+ goto failed;
+ }
+
+ return s;
+
+failed:
+ talloc_free(s);
+ return NULL;
+}
+
+/*
+ encode a sid in SDDL format
+*/
+static char *sddl_transition_encode_sid(TALLOC_CTX *mem_ctx, const struct dom_sid *sid,
+ struct sddl_transition_state *state)
+{
+ bool in_machine = dom_sid_in_domain(state->machine_sid, sid);
+ bool in_domain = dom_sid_in_domain(state->domain_sid, sid);
+ bool in_forest = dom_sid_in_domain(state->forest_sid, sid);
+ struct dom_sid_buf buf;
+ const char *sidstr = dom_sid_str_buf(sid, &buf);
+ uint32_t rid = 0;
+ size_t i;
+
+ if (sid->num_auths > 1) {
+ rid = sid->sub_auths[sid->num_auths-1];
+ }
+
+ for (i=0;i<ARRAY_SIZE(sid_codes);i++) {
+ /* seen if its a well known sid */
+ if (sid_codes[i].sid != NULL) {
+ int cmp;
+
+ cmp = strcmp(sidstr, sid_codes[i].sid);
+ if (cmp != 0) {
+ continue;
+ }
+
+ return talloc_strdup(mem_ctx, sid_codes[i].code);
+ }
+
+ if (rid == 0) {
+ continue;
+ }
+
+ if (in_machine && sid_codes[i].machine_rid == rid) {
+ return talloc_strdup(mem_ctx, sid_codes[i].code);
+ }
+ if (in_domain && sid_codes[i].domain_rid == rid) {
+ return talloc_strdup(mem_ctx, sid_codes[i].code);
+ }
+ if (in_forest && sid_codes[i].forest_rid == rid) {
+ return talloc_strdup(mem_ctx, sid_codes[i].code);
+ }
+ }
+
+ return talloc_strdup(mem_ctx, sidstr);
+}
+
+char *sddl_encode_sid(TALLOC_CTX *mem_ctx, const struct dom_sid *sid,
+ const struct dom_sid *domain_sid)
+{
+ struct sddl_transition_state state = {
+ /*
+ * TODO: verify .machine_rid values really belong
+ * to the machine_sid on a member, once
+ * we pass machine_sid from the caller...
+ */
+ .machine_sid = domain_sid,
+ .domain_sid = domain_sid,
+ .forest_sid = domain_sid,
+ };
+ return sddl_transition_encode_sid(mem_ctx, sid, &state);
+}
+
+
+
+/*
+ encode an ACE in SDDL format
+*/
+static char *sddl_transition_encode_ace(TALLOC_CTX *mem_ctx, const struct security_ace *ace,
+ struct sddl_transition_state *state)
+{
+ char *sddl = NULL;
+ TALLOC_CTX *tmp_ctx;
+ struct GUID_txt_buf object_buf, iobject_buf;
+ const char *sddl_type="", *sddl_flags="", *sddl_mask="",
+ *sddl_object="", *sddl_iobject="", *sddl_trustee="";
+ tmp_ctx = talloc_new(mem_ctx);
+ if (tmp_ctx == NULL) {
+ DEBUG(0, ("talloc_new failed\n"));
+ return NULL;
+ }
+
+ sddl_type = sddl_flags_to_string(tmp_ctx, ace_types, ace->type, true);
+ if (sddl_type == NULL) {
+ goto failed;
+ }
+
+ sddl_flags = sddl_flags_to_string(tmp_ctx, ace_flags, ace->flags,
+ true);
+ if (sddl_flags == NULL) {
+ goto failed;
+ }
+
+ sddl_mask = sddl_flags_to_string(tmp_ctx, ace_access_mask,
+ ace->access_mask, true);
+ if (sddl_mask == NULL) {
+ sddl_mask = sddl_match_file_rights(tmp_ctx,
+ ace->access_mask);
+ if (sddl_mask == NULL) {
+ sddl_mask = talloc_asprintf(tmp_ctx, "0x%x",
+ ace->access_mask);
+ }
+ if (sddl_mask == NULL) {
+ goto failed;
+ }
+ }
+
+ if (sec_ace_object(ace->type)) {
+ const struct security_ace_object *object = &ace->object.object;
+
+ if (ace->object.object.flags & SEC_ACE_OBJECT_TYPE_PRESENT) {
+ sddl_object = GUID_buf_string(
+ &object->type.type, &object_buf);
+ }
+
+ if (ace->object.object.flags &
+ SEC_ACE_INHERITED_OBJECT_TYPE_PRESENT) {
+ sddl_iobject = GUID_buf_string(
+ &object->inherited_type.inherited_type,
+ &iobject_buf);
+ }
+ }
+ sddl_trustee = sddl_transition_encode_sid(tmp_ctx, &ace->trustee, state);
+ if (sddl_trustee == NULL) {
+ goto failed;
+ }
+
+ if (sec_ace_callback(ace->type)) {
+ /* encode the conditional part */
+ struct ace_condition_script *s = NULL;
+ const char *sddl_conditions = NULL;
+
+ s = parse_conditional_ace(tmp_ctx, ace->coda.conditions);
+
+ if (s == NULL) {
+ goto failed;
+ }
+
+ sddl_conditions = sddl_from_conditional_ace(tmp_ctx, s);
+ if (sddl_conditions == NULL) {
+ goto failed;
+ }
+
+ sddl = talloc_asprintf(mem_ctx, "%s;%s;%s;%s;%s;%s;%s",
+ sddl_type, sddl_flags, sddl_mask,
+ sddl_object, sddl_iobject,
+ sddl_trustee, sddl_conditions);
+ } else if (sec_ace_resource(ace->type)) {
+ /* encode the resource part */
+ const char *coda = NULL;
+ coda = sddl_resource_attr_from_claim(tmp_ctx,
+ &ace->coda.claim);
+
+ if (coda == NULL) {
+ DBG_WARNING("resource ACE has invalid claim\n");
+ goto failed;
+ }
+ sddl = talloc_asprintf(mem_ctx, "%s;%s;%s;%s;%s;%s;%s",
+ sddl_type, sddl_flags, sddl_mask,
+ sddl_object, sddl_iobject,
+ sddl_trustee, coda);
+ } else {
+ sddl = talloc_asprintf(mem_ctx, "%s;%s;%s;%s;%s;%s",
+ sddl_type, sddl_flags, sddl_mask,
+ sddl_object, sddl_iobject, sddl_trustee);
+ }
+failed:
+ talloc_free(tmp_ctx);
+ return sddl;
+}
+
+char *sddl_encode_ace(TALLOC_CTX *mem_ctx, const struct security_ace *ace,
+ const struct dom_sid *domain_sid)
+{
+ struct sddl_transition_state state = {
+ /*
+ * TODO: verify .machine_rid values really belong
+ * to the machine_sid on a member, once
+ * we pass machine_sid from the caller...
+ */
+ .machine_sid = domain_sid,
+ .domain_sid = domain_sid,
+ .forest_sid = domain_sid,
+ };
+ return sddl_transition_encode_ace(mem_ctx, ace, &state);
+}
+
+/*
+ encode an ACL in SDDL format
+*/
+static char *sddl_encode_acl(TALLOC_CTX *mem_ctx, const struct security_acl *acl,
+ uint32_t flags, struct sddl_transition_state *state)
+{
+ char *sddl;
+ uint32_t i;
+
+ /* add any ACL flags */
+ sddl = sddl_flags_to_string(mem_ctx, acl_flags, flags, false);
+ if (sddl == NULL) goto failed;
+
+ /* now the ACEs, encoded in braces */
+ for (i=0;i<acl->num_aces;i++) {
+ char *ace = sddl_transition_encode_ace(sddl, &acl->aces[i], state);
+ if (ace == NULL) goto failed;
+ sddl = talloc_asprintf_append_buffer(sddl, "(%s)", ace);
+ if (sddl == NULL) goto failed;
+ talloc_free(ace);
+ }
+
+ return sddl;
+
+failed:
+ talloc_free(sddl);
+ return NULL;
+}
+
+
+/*
+ encode a security descriptor to SDDL format
+*/
+char *sddl_encode(TALLOC_CTX *mem_ctx, const struct security_descriptor *sd,
+ const struct dom_sid *domain_sid)
+{
+ struct sddl_transition_state state = {
+ /*
+ * TODO: verify .machine_rid values really belong
+ * to the machine_sid on a member, once
+ * we pass machine_sid from the caller...
+ */
+ .machine_sid = domain_sid,
+ .domain_sid = domain_sid,
+ .forest_sid = domain_sid,
+ };
+ char *sddl;
+ TALLOC_CTX *tmp_ctx;
+
+ /* start with a blank string */
+ sddl = talloc_strdup(mem_ctx, "");
+ if (sddl == NULL) goto failed;
+
+ tmp_ctx = talloc_new(sddl);
+ if (tmp_ctx == NULL) {
+ goto failed;
+ }
+
+ if (sd->owner_sid != NULL) {
+ char *sid = sddl_transition_encode_sid(tmp_ctx, sd->owner_sid, &state);
+ if (sid == NULL) goto failed;
+ sddl = talloc_asprintf_append_buffer(sddl, "O:%s", sid);
+ if (sddl == NULL) goto failed;
+ }
+
+ if (sd->group_sid != NULL) {
+ char *sid = sddl_transition_encode_sid(tmp_ctx, sd->group_sid, &state);
+ if (sid == NULL) goto failed;
+ sddl = talloc_asprintf_append_buffer(sddl, "G:%s", sid);
+ if (sddl == NULL) goto failed;
+ }
+
+ if ((sd->type & SEC_DESC_DACL_PRESENT) && sd->dacl != NULL) {
+ char *acl = sddl_encode_acl(tmp_ctx, sd->dacl, sd->type, &state);
+ if (acl == NULL) goto failed;
+ sddl = talloc_asprintf_append_buffer(sddl, "D:%s", acl);
+ if (sddl == NULL) goto failed;
+ }
+
+ if ((sd->type & SEC_DESC_SACL_PRESENT) && sd->sacl != NULL) {
+ char *acl = sddl_encode_acl(tmp_ctx, sd->sacl, sd->type>>1, &state);
+ if (acl == NULL) goto failed;
+ sddl = talloc_asprintf_append_buffer(sddl, "S:%s", acl);
+ if (sddl == NULL) goto failed;
+ }
+
+ talloc_free(tmp_ctx);
+ return sddl;
+
+failed:
+ talloc_free(sddl);
+ return NULL;
+}
diff --git a/libcli/security/sddl.h b/libcli/security/sddl.h
new file mode 100644
index 0000000..03c8a27
--- /dev/null
+++ b/libcli/security/sddl.h
@@ -0,0 +1,47 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba utility functions
+
+ Copyright (C) 2009 Jelmer Vernooij <jelmer@samba.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __SDDL_H__
+#define __SDDL_H__
+
+#include <talloc.h>
+#include "lib/util/data_blob.h"
+
+#include "librpc/gen_ndr/conditional_ace.h"
+#include "librpc/gen_ndr/security.h"
+
+struct security_descriptor *sddl_decode(TALLOC_CTX *mem_ctx, const char *sddl,
+ const struct dom_sid *domain_sid);
+struct security_descriptor *sddl_decode_err_msg(TALLOC_CTX *mem_ctx, const char *sddl,
+ const struct dom_sid *domain_sid,
+ const enum ace_condition_flags ace_condition_flags,
+ const char **msg, size_t *msg_offset);
+char *sddl_encode(TALLOC_CTX *mem_ctx, const struct security_descriptor *sd,
+ const struct dom_sid *domain_sid);
+char *sddl_encode_ace(TALLOC_CTX *mem_ctx, const struct security_ace *ace,
+ const struct dom_sid *domain_sid);
+
+struct dom_sid *sddl_decode_sid(TALLOC_CTX *mem_ctx, const char **sddlp,
+ const struct dom_sid *domain_sid);
+
+char *sddl_encode_sid(TALLOC_CTX *mem_ctx, const struct dom_sid *sid,
+ const struct dom_sid *domain_sid);
+
+#endif /* __SDDL_H__ */
diff --git a/libcli/security/sddl_conditional_ace.c b/libcli/security/sddl_conditional_ace.c
new file mode 100644
index 0000000..e9d83b7
--- /dev/null
+++ b/libcli/security/sddl_conditional_ace.c
@@ -0,0 +1,3476 @@
+/*
+ * Unix SMB implementation.
+ * Functions for understanding conditional ACEs
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "librpc/gen_ndr/ndr_security.h"
+#include "librpc/gen_ndr/conditional_ace.h"
+#include "libcli/security/security.h"
+#include "libcli/security/conditional_ace.h"
+#include "libcli/security/claims-conversions.h"
+#include "lib/util/tsort.h"
+#include "lib/util/bytearray.h"
+
+
+/* We're only dealing with utf-8 here. Honestly. */
+#undef strncasecmp
+
+
+#define SDDL_FLAG_EXPECTING_UNARY_OP 1
+#define SDDL_FLAG_EXPECTING_BINARY_OP 2
+#define SDDL_FLAG_EXPECTING_BINARY_LOGIC_OP 4
+#define SDDL_FLAG_EXPECTING_LOCAL_ATTR 8
+#define SDDL_FLAG_EXPECTING_NON_LOCAL_ATTR 16
+#define SDDL_FLAG_EXPECTING_LITERAL 32
+#define SDDL_FLAG_EXPECTING_PAREN 64
+#define SDDL_FLAG_EXPECTING_PAREN_LITERAL 128
+#define SDDL_FLAG_NOT_EXPECTING_END_PAREN 256
+
+#define SDDL_FLAG_DEVICE 512
+
+#define SDDL_FLAG_IS_UNARY_OP (1 << 20)
+#define SDDL_FLAG_IS_BINARY_OP (1 << 21)
+
+
+#define SDDL_FLAGS_EXPR_START (SDDL_FLAG_EXPECTING_UNARY_OP | \
+ SDDL_FLAG_EXPECTING_LOCAL_ATTR | \
+ SDDL_FLAG_EXPECTING_NON_LOCAL_ATTR | \
+ SDDL_FLAG_EXPECTING_PAREN)
+
+#define SDDL_FLAGS_MEMBER_OP (SDDL_FLAG_EXPECTING_LITERAL | \
+ SDDL_FLAG_EXPECTING_PAREN_LITERAL | \
+ SDDL_FLAG_IS_UNARY_OP)
+
+#define SDDL_FLAGS_RELATIONAL_OP (SDDL_FLAG_EXPECTING_LITERAL | \
+ SDDL_FLAG_EXPECTING_PAREN_LITERAL | \
+ SDDL_FLAG_EXPECTING_NON_LOCAL_ATTR | \
+ SDDL_FLAG_IS_BINARY_OP)
+
+#define SDDL_FLAGS_CONTAINS_OP (SDDL_FLAG_EXPECTING_LITERAL | \
+ SDDL_FLAG_EXPECTING_NON_LOCAL_ATTR | \
+ SDDL_FLAG_IS_BINARY_OP)
+
+#define SDDL_FLAGS_EXISTS_OP (SDDL_FLAG_EXPECTING_LOCAL_ATTR | \
+ SDDL_FLAG_EXPECTING_NON_LOCAL_ATTR | \
+ SDDL_FLAG_IS_UNARY_OP)
+
+#define SDDL_FLAGS_LOGIC_OP (SDDL_FLAG_EXPECTING_LOCAL_ATTR | \
+ SDDL_FLAG_EXPECTING_NON_LOCAL_ATTR | \
+ SDDL_FLAG_EXPECTING_PAREN | \
+ SDDL_FLAG_EXPECTING_UNARY_OP | \
+ SDDL_FLAG_IS_BINARY_OP)
+
+#define SDDL_FLAGS_ATTRIBUTE (SDDL_FLAG_EXPECTING_BINARY_OP | \
+ SDDL_FLAG_EXPECTING_BINARY_LOGIC_OP)
+
+#define SDDL_FLAGS_LITERAL SDDL_FLAG_EXPECTING_BINARY_LOGIC_OP
+
+#define SDDL_FLAGS_PAREN_END (SDDL_FLAG_EXPECTING_BINARY_LOGIC_OP | \
+ SDDL_FLAG_EXPECTING_BINARY_OP)
+
+enum {
+ SDDL_NOT_AN_OP = 0,
+ SDDL_PRECEDENCE_EXISTS,
+ SDDL_PRECEDENCE_COMMON,
+ SDDL_PRECEDENCE_NOT,
+ SDDL_PRECEDENCE_AND,
+ SDDL_PRECEDENCE_OR,
+ SDDL_PRECEDENCE_PAREN_END,
+ SDDL_PRECEDENCE_PAREN_START,
+};
+
+struct ace_condition_sddl_compiler_context {
+ TALLOC_CTX *mem_ctx;
+ const uint8_t *sddl;
+ uint32_t length;
+ uint32_t offset;
+ uint32_t stack_depth;
+ uint32_t max_program_length;
+ uint32_t approx_size;
+ struct ace_condition_script *program;
+ struct ace_condition_token *stack;
+ struct ace_condition_token *target;
+ uint32_t *target_len;
+ const char *message;
+ uint32_t message_offset;
+ struct dom_sid *domain_sid;
+ uint32_t state;
+ uint8_t last_token_type;
+ bool allow_device;
+};
+
+struct sddl_data {
+ const char *name;
+ uint32_t flags;
+ uint8_t op_precedence;
+ uint8_t nargs;
+};
+
+static const struct sddl_data sddl_strings[256] = {
+ /* operators */
+ [CONDITIONAL_ACE_TOKEN_MEMBER_OF] = {
+ "Member_of",
+ SDDL_FLAGS_MEMBER_OP,
+ SDDL_PRECEDENCE_COMMON,
+ 1
+ },
+ [CONDITIONAL_ACE_TOKEN_DEVICE_MEMBER_OF] = {
+ "Device_Member_of",
+ SDDL_FLAGS_MEMBER_OP|SDDL_FLAG_DEVICE,
+ SDDL_PRECEDENCE_COMMON,
+ 1
+ },
+ [CONDITIONAL_ACE_TOKEN_MEMBER_OF_ANY] = {
+ /* [MS-DTYP] says "_Any", but windows prefers '_any' */
+ "Member_of_any",
+ SDDL_FLAGS_MEMBER_OP,
+ SDDL_PRECEDENCE_COMMON,
+ 1
+ },
+ [CONDITIONAL_ACE_TOKEN_DEVICE_MEMBER_OF_ANY] = {
+ "Device_Member_of_Any",
+ SDDL_FLAGS_MEMBER_OP|SDDL_FLAG_DEVICE,
+ SDDL_PRECEDENCE_COMMON,
+ 1
+ },
+ [CONDITIONAL_ACE_TOKEN_NOT_MEMBER_OF] = {
+ "Not_Member_of",
+ SDDL_FLAGS_MEMBER_OP,
+ SDDL_PRECEDENCE_COMMON,
+ 1
+ },
+ [CONDITIONAL_ACE_TOKEN_NOT_DEVICE_MEMBER_OF] = {
+ "Not_Device_Member_of",
+ SDDL_FLAGS_MEMBER_OP|SDDL_FLAG_DEVICE,
+ SDDL_PRECEDENCE_COMMON,
+ 1
+ },
+ [CONDITIONAL_ACE_TOKEN_NOT_MEMBER_OF_ANY] = {
+ "Not_Member_of_Any",
+ SDDL_FLAGS_MEMBER_OP,
+ SDDL_PRECEDENCE_COMMON,
+ 1
+ },
+ [CONDITIONAL_ACE_TOKEN_NOT_DEVICE_MEMBER_OF_ANY] = {
+ "Not_Device_Member_of_Any",
+ SDDL_FLAGS_MEMBER_OP|SDDL_FLAG_DEVICE,
+ SDDL_PRECEDENCE_COMMON,
+ 1
+ },
+ [CONDITIONAL_ACE_TOKEN_EQUAL] = {
+ "==",
+ SDDL_FLAGS_RELATIONAL_OP,
+ SDDL_PRECEDENCE_COMMON,
+ 2
+ },
+ [CONDITIONAL_ACE_TOKEN_NOT_EQUAL] = {
+ "!=",
+ SDDL_FLAGS_RELATIONAL_OP,
+ SDDL_PRECEDENCE_COMMON,
+ 2
+ },
+ [CONDITIONAL_ACE_TOKEN_LESS_THAN] = {
+ "<",
+ SDDL_FLAGS_RELATIONAL_OP,
+ SDDL_PRECEDENCE_COMMON,
+ 2
+ },
+ [CONDITIONAL_ACE_TOKEN_LESS_OR_EQUAL] = {
+ "<=",
+ SDDL_FLAGS_RELATIONAL_OP,
+ SDDL_PRECEDENCE_COMMON,
+ 2
+ },
+ [CONDITIONAL_ACE_TOKEN_GREATER_THAN] = {
+ ">",
+ SDDL_FLAGS_RELATIONAL_OP,
+ SDDL_PRECEDENCE_COMMON,
+ 2
+ },
+ [CONDITIONAL_ACE_TOKEN_GREATER_OR_EQUAL] = {
+ ">=",
+ SDDL_FLAGS_RELATIONAL_OP,
+ SDDL_PRECEDENCE_COMMON,
+ 2
+ },
+ [CONDITIONAL_ACE_TOKEN_CONTAINS] = {
+ "Contains",
+ SDDL_FLAGS_CONTAINS_OP,
+ SDDL_PRECEDENCE_COMMON,
+ 2
+ },
+ [CONDITIONAL_ACE_TOKEN_ANY_OF] = {
+ "Any_of",
+ SDDL_FLAGS_CONTAINS_OP,
+ SDDL_PRECEDENCE_COMMON,
+ 2
+ },
+ [CONDITIONAL_ACE_TOKEN_NOT_CONTAINS] = {
+ "Not_Contains",
+ SDDL_FLAGS_CONTAINS_OP,
+ SDDL_PRECEDENCE_COMMON,
+ 2
+ },
+ [CONDITIONAL_ACE_TOKEN_NOT_ANY_OF] = {
+ "Not_Any_of",
+ SDDL_FLAGS_CONTAINS_OP,
+ SDDL_PRECEDENCE_COMMON,
+ 2
+ },
+ [CONDITIONAL_ACE_TOKEN_AND] = {
+ "&&",
+ SDDL_FLAGS_LOGIC_OP,
+ SDDL_PRECEDENCE_AND,
+ 2
+ },
+ [CONDITIONAL_ACE_TOKEN_OR] = {
+ "||",
+ SDDL_FLAGS_LOGIC_OP,
+ SDDL_PRECEDENCE_OR,
+ 2
+ },
+ [CONDITIONAL_ACE_TOKEN_NOT] = {
+ "!",
+ (SDDL_FLAG_EXPECTING_PAREN |
+ SDDL_FLAG_EXPECTING_NON_LOCAL_ATTR |
+ SDDL_FLAG_IS_UNARY_OP),
+ SDDL_PRECEDENCE_NOT,
+ 1
+ },
+ [CONDITIONAL_ACE_TOKEN_EXISTS] = {
+ "Exists",
+ SDDL_FLAGS_EXISTS_OP,
+ SDDL_PRECEDENCE_EXISTS,
+ 1
+ },
+ [CONDITIONAL_ACE_TOKEN_NOT_EXISTS] = {
+ "Not_Exists",
+ SDDL_FLAGS_EXISTS_OP,
+ SDDL_PRECEDENCE_EXISTS,
+ 1
+ },
+ /* pseudo-operator pseudo-tokens */
+ [CONDITIONAL_ACE_SAMBA_SDDL_PAREN] = {
+ "(",
+ 0,
+ SDDL_PRECEDENCE_PAREN_START,
+ 0
+ },
+ [CONDITIONAL_ACE_SAMBA_SDDL_PAREN_END] = {
+ ")",
+ SDDL_FLAGS_PAREN_END,
+ SDDL_PRECEDENCE_PAREN_END,
+ 0
+ },
+
+ /*
+ * non-operators.
+ * The names here are only used for error messages.
+ *
+ * some of them will never actually be encountered (e.g. 8-bit
+ * integers).
+ */
+ [CONDITIONAL_ACE_TOKEN_INT8] = {
+ .name = "8-bit integer",
+ .flags = SDDL_FLAGS_LITERAL,
+ SDDL_NOT_AN_OP,
+ 0
+ },
+ [CONDITIONAL_ACE_TOKEN_INT16] = {
+ "16-bit integer",
+ SDDL_FLAGS_LITERAL,
+ SDDL_NOT_AN_OP,
+ 0
+ },
+ [CONDITIONAL_ACE_TOKEN_INT32] = {
+ "32-bit integer",
+ SDDL_FLAGS_LITERAL,
+ SDDL_NOT_AN_OP,
+ 0
+ },
+ [CONDITIONAL_ACE_TOKEN_INT64] = {
+ "64-bit integer",
+ SDDL_FLAGS_LITERAL,
+ SDDL_NOT_AN_OP,
+ 0
+ },
+
+ [CONDITIONAL_ACE_TOKEN_UNICODE] = {
+ "unicode",
+ SDDL_FLAGS_LITERAL,
+ SDDL_NOT_AN_OP,
+ 0
+ },
+ [CONDITIONAL_ACE_TOKEN_OCTET_STRING] = {
+ "byte string",
+ SDDL_FLAGS_LITERAL,
+ SDDL_NOT_AN_OP,
+ 0
+ },
+ [CONDITIONAL_ACE_TOKEN_COMPOSITE] = {
+ "composite list",
+ SDDL_FLAGS_LITERAL,
+ SDDL_NOT_AN_OP,
+ 0
+ },
+ [CONDITIONAL_ACE_TOKEN_SID] = {
+ "SID",
+ SDDL_FLAGS_LITERAL,
+ SDDL_NOT_AN_OP,
+ 0
+ },
+ [CONDITIONAL_ACE_LOCAL_ATTRIBUTE] = {
+ "local attribute",
+ SDDL_FLAGS_ATTRIBUTE,
+ SDDL_NOT_AN_OP,
+ 0
+ },
+ [CONDITIONAL_ACE_USER_ATTRIBUTE] = {
+ "user attribute",
+ SDDL_FLAGS_ATTRIBUTE,
+ SDDL_NOT_AN_OP,
+ 0
+ },
+ [CONDITIONAL_ACE_RESOURCE_ATTRIBUTE] = {
+ "resource attribute",
+ SDDL_FLAGS_ATTRIBUTE,
+ SDDL_NOT_AN_OP,
+ 0
+ },
+ [CONDITIONAL_ACE_DEVICE_ATTRIBUTE] = {
+ "device attribute",
+ SDDL_FLAGS_ATTRIBUTE|SDDL_FLAG_DEVICE,
+ SDDL_NOT_AN_OP,
+ 0
+ },
+ [CONDITIONAL_ACE_SAMBA_RESULT_BOOL] = {
+ "boolean result",
+ 0,
+ SDDL_NOT_AN_OP,
+ 0
+ },
+ [CONDITIONAL_ACE_SAMBA_RESULT_NULL] = {
+ "null result",
+ 0,
+ SDDL_NOT_AN_OP,
+ 0
+ },
+ [CONDITIONAL_ACE_SAMBA_RESULT_ERROR] = {
+ "error result",
+ 0,
+ SDDL_NOT_AN_OP,
+ 0
+ },
+};
+
+struct sddl_attr_type{
+ const char *name;
+ uint8_t code;
+};
+
+/*
+ * These are the prefixes for non-local attribute types. [MS-DTYP]
+ * styles them in title case ("@User."), but Windows itself seems to
+ * prefer all-caps, so that is how we render them.
+ */
+static const struct sddl_attr_type sddl_attr_types[] = {
+ {"USER.", CONDITIONAL_ACE_USER_ATTRIBUTE},
+ {"RESOURCE.", CONDITIONAL_ACE_RESOURCE_ATTRIBUTE},
+ {"DEVICE.", CONDITIONAL_ACE_DEVICE_ATTRIBUTE},
+};
+
+
+struct sddl_write_context {
+ TALLOC_CTX *mem_ctx;
+ char *sddl;
+ size_t len;
+ size_t alloc_len;
+};
+
+static bool sddl_write(struct sddl_write_context *ctx,
+ const char *s)
+{
+ size_t len = strlen(s);
+ if (ctx->alloc_len - ctx->len <= len ||
+ ctx->sddl == NULL) {
+ size_t old = ctx->alloc_len;
+ ctx->alloc_len = old + MAX(old / 2, len + 50);
+ if (ctx->alloc_len <= old ||
+ ctx->alloc_len - ctx->len <= len) {
+ return false;
+ }
+ ctx->sddl = talloc_realloc(ctx->mem_ctx, ctx->sddl,
+ char, ctx->alloc_len);
+
+ if (ctx->sddl == NULL) {
+ return false;
+ }
+ }
+ memcpy(ctx->sddl + ctx->len, s, len);
+ ctx->len += len;
+ ctx->sddl[ctx->len] = 0;
+ return true;
+}
+
+/*
+ * This is a helper function to create a representation of a
+ * conditional ACE. This is not SDDL, more like a disassembly,
+ * but it uses some of the same tables.
+ */
+char *debug_conditional_ace(TALLOC_CTX *mem_ctx,
+ struct ace_condition_script *program)
+{
+ size_t i;
+ size_t depth = 0;
+ char stack[] = " ";
+ char line[120];
+ struct sddl_write_context ctx = {
+ .mem_ctx = mem_ctx
+ };
+
+ for (i = 0; i < program->length; i++) {
+ struct ace_condition_token *tok = &program->tokens[i];
+ struct sddl_data s = sddl_strings[tok->type];
+ char hex[21];
+ char *utf8 = NULL;
+ int utf8_len;
+ char type;
+ char nom[40];
+ snprintf(nom, sizeof(nom), "\033[1;33m%20s\033[0m", s.name);
+ switch (tok->type) {
+ case CONDITIONAL_ACE_TOKEN_INT8:
+ case CONDITIONAL_ACE_TOKEN_INT16:
+ case CONDITIONAL_ACE_TOKEN_INT32:
+ case CONDITIONAL_ACE_TOKEN_INT64:
+ if (tok->data.int64.sign > 3 ||
+ tok->data.int64.base > 3) {
+ goto error;
+ }
+ snprintf(line, sizeof(line),
+ "%s %"PRIi64" %c%c\n",
+ nom,
+ tok->data.int64.value,
+ "?+-_"[tok->data.int64.sign],
+ "?odh"[tok->data.int64.base]
+ );
+ type = 'i';
+ break;
+
+ case CONDITIONAL_ACE_TOKEN_MEMBER_OF:
+ case CONDITIONAL_ACE_TOKEN_DEVICE_MEMBER_OF:
+ case CONDITIONAL_ACE_TOKEN_MEMBER_OF_ANY:
+ case CONDITIONAL_ACE_TOKEN_DEVICE_MEMBER_OF_ANY:
+ case CONDITIONAL_ACE_TOKEN_NOT_MEMBER_OF:
+ case CONDITIONAL_ACE_TOKEN_NOT_DEVICE_MEMBER_OF:
+ case CONDITIONAL_ACE_TOKEN_NOT_MEMBER_OF_ANY:
+ case CONDITIONAL_ACE_TOKEN_NOT_DEVICE_MEMBER_OF_ANY:
+ snprintf(line, sizeof(line),
+ "%s bool\n",
+ nom
+ );
+ type = 'b';
+ break;
+
+ case CONDITIONAL_ACE_TOKEN_EQUAL:
+ case CONDITIONAL_ACE_TOKEN_NOT_EQUAL:
+ case CONDITIONAL_ACE_TOKEN_LESS_THAN:
+ case CONDITIONAL_ACE_TOKEN_LESS_OR_EQUAL:
+ case CONDITIONAL_ACE_TOKEN_GREATER_THAN:
+ case CONDITIONAL_ACE_TOKEN_GREATER_OR_EQUAL:
+ case CONDITIONAL_ACE_TOKEN_CONTAINS:
+ case CONDITIONAL_ACE_TOKEN_ANY_OF:
+ case CONDITIONAL_ACE_TOKEN_NOT_CONTAINS:
+ case CONDITIONAL_ACE_TOKEN_NOT_ANY_OF:
+ case CONDITIONAL_ACE_TOKEN_AND:
+ case CONDITIONAL_ACE_TOKEN_OR:
+ snprintf(line, sizeof(line),
+ "%s bool\n",
+ nom
+ );
+ type = 'b';
+ break;
+
+ case CONDITIONAL_ACE_TOKEN_EXISTS:
+ case CONDITIONAL_ACE_TOKEN_NOT_EXISTS:
+ case CONDITIONAL_ACE_TOKEN_NOT:
+ snprintf(line, sizeof(line),
+ "%s bool\n",
+ nom
+ );
+ type = 'b';
+ break;
+
+ case CONDITIONAL_ACE_LOCAL_ATTRIBUTE:
+ case CONDITIONAL_ACE_USER_ATTRIBUTE:
+ case CONDITIONAL_ACE_RESOURCE_ATTRIBUTE:
+ case CONDITIONAL_ACE_DEVICE_ATTRIBUTE:
+ snprintf(line, sizeof(line),
+ "%s.%s (any type)\n",
+ nom,
+ tok->data.unicode.value
+ );
+ type = '?';
+ break;
+
+ case CONDITIONAL_ACE_TOKEN_UNICODE:
+ snprintf(line, sizeof(line),
+ "%s.%s (any type)\n",
+ nom,
+ tok->data.unicode.value
+ );
+ type = 'u';
+ break;
+
+ case CONDITIONAL_ACE_TOKEN_OCTET_STRING:
+ utf8_len = MIN(tok->data.bytes.length, 9);
+ hex_encode_buf(hex, tok->data.bytes.data, utf8_len);
+
+ snprintf(line, sizeof(line),
+ "%s %.*s (%d)\n",
+ nom, utf8_len * 2, hex, utf8_len);
+ type = 'o';
+ break;
+ case CONDITIONAL_ACE_TOKEN_SID:
+ utf8 = sddl_encode_sid(mem_ctx,
+ &tok->data.sid.sid,
+ NULL);
+ snprintf(line, sizeof(line),
+ "%s (%s)\n",
+ nom, utf8);
+ type = 'S';
+ break;
+ case CONDITIONAL_ACE_TOKEN_COMPOSITE:
+ snprintf(line, sizeof(line),
+ "%s %"PRIu32" direct members\n",
+ nom, tok->data.composite.n_members);
+ type = 'C';
+ break;
+
+ case CONDITIONAL_ACE_TOKEN_INVALID_OR_PADDING:
+ snprintf(line, sizeof(line),
+ "%s\n", nom);
+ type = '0';
+ break;
+ default:
+ snprintf(line, sizeof(line),
+ "unknown opcode %#02x\n", tok->type);
+ type = '!';
+ break;
+ }
+
+ if (s.nargs > depth) {
+ snprintf(nom, sizeof(nom),
+ "UNDER: -%zu", s.nargs - depth);
+ depth = 0;
+ sddl_write(&ctx, nom);
+ } else if (depth >= strlen(stack)) {
+ snprintf(nom, sizeof(nom),
+ "depth %zu", s.nargs - depth);
+ depth -= (s.nargs - 1);
+ sddl_write(&ctx, nom);
+ } else {
+ depth -= s.nargs;
+ stack[depth] = type;
+ depth++;
+ if (depth < strlen(stack)) {
+ stack[depth] = ' ';
+ }
+ sddl_write(&ctx, stack);
+ }
+ sddl_write(&ctx, line);
+ }
+ if (depth == 1 && stack[0] == 'b') {
+ snprintf(line, sizeof(line),
+ "\033[1;32mGOOD: finishes on a single bool\033[0m\n");
+ } else {
+ snprintf(line, sizeof(line),
+ "\033[1;31mBAD: should finish with a bool\033[0m\n");
+ }
+ sddl_write(&ctx, line);
+ return ctx.sddl;
+
+ error:
+ TALLOC_FREE(ctx.sddl);
+ return NULL;
+}
+
+
+struct sddl_node {
+ struct ace_condition_token *tok;
+ struct sddl_node *lhs;
+ struct sddl_node *rhs;
+ bool wants_parens;
+};
+
+static bool sddl_write_int(struct sddl_write_context *ctx,
+ const struct ace_condition_token *tok)
+{
+ int64_t v = tok->data.int64.value;
+ uint8_t sign = tok->data.int64.sign;
+ uint8_t base = tok->data.int64.base;
+ char buf[26]; /* oct(1<<63) + sign + \0 */
+ char sign_char;
+ if (sign > CONDITIONAL_ACE_INT_SIGN_NONE ||
+ base > CONDITIONAL_ACE_INT_BASE_16) {
+ return false;
+ }
+
+ /*
+ * we have 9 combinations of base/sign (+ some invalid combinations of
+ * actual sign vs claimed sign).
+ */
+ if (sign == CONDITIONAL_ACE_INT_SIGN_NONE) {
+ /* octal and hex will end up unsigned! */
+ if (base == CONDITIONAL_ACE_INT_BASE_8) {
+ snprintf(buf, sizeof(buf), "0%"PRIo64, v);
+ } else if (base == CONDITIONAL_ACE_INT_BASE_10) {
+ snprintf(buf, sizeof(buf), "%"PRId64, v);
+ } else {
+ snprintf(buf, sizeof(buf), "0x%"PRIx64, v);
+ }
+ return sddl_write(ctx, buf);
+ }
+ if (sign == CONDITIONAL_ACE_INT_SIGN_POSITIVE && v < 0) {
+ return false;
+ }
+ if (sign == CONDITIONAL_ACE_INT_SIGN_NEGATIVE && v > 0) {
+ /* note we allow "-0", because we will parse it. */
+ return false;
+ }
+ sign_char = (sign == CONDITIONAL_ACE_INT_SIGN_NEGATIVE) ? '-' : '+';
+ /*
+ * We can use "%+ld" for the decimal sign (except -0), but
+ * "%+lx" and "%+lo" are invalid because %o and %x are
+ * unsigned.
+ */
+ if (base == CONDITIONAL_ACE_INT_BASE_10) {
+ if (v == 0) {
+ snprintf(buf, sizeof(buf), "%c0", sign_char);
+ } else {
+ snprintf(buf, sizeof(buf), "%+"PRId64, v);
+ }
+ return sddl_write(ctx, buf);
+ }
+
+ if (v == INT64_MIN) {
+ /*
+ * llabs(INT64_MIN) will be undefined.
+ * The lengths we must go to to round trip!
+ */
+ if (base == CONDITIONAL_ACE_INT_BASE_8) {
+ return sddl_write(ctx, "-01000000000000000000000");
+ }
+ return sddl_write(ctx, "-0x8000000000000000");
+ }
+
+ if (base == CONDITIONAL_ACE_INT_BASE_8) {
+ snprintf(buf, sizeof(buf), "%c0%llo", sign_char, llabs(v));
+ } else {
+ snprintf(buf, sizeof(buf), "%c0x%llx", sign_char, llabs(v));
+ }
+ return sddl_write(ctx, buf);
+}
+
+
+static bool sddl_should_escape_utf16(uint16_t c)
+{
+ if (c <= ' ' || c > 126) {
+ return true;
+ }
+
+ switch (c) {
+ case '!':
+ case '"':
+ case '&':
+ case '(':
+ case ')':
+ case '<':
+ case '=':
+ case '>':
+ case '|':
+ case '%':
+ return true;
+ }
+
+ return false;
+}
+
+static bool sddl_encode_attr_name(TALLOC_CTX *mem_ctx,
+ const char *src,
+ char **dest,
+ size_t *dest_len)
+{
+ size_t i, j;
+ bool ok;
+ uint16_t *utf16 = NULL;
+ char *escaped = NULL;
+ size_t utf16_byte_len;
+ size_t utf16_len;
+ size_t src_len = strlen(src);
+ size_t escapees;
+ size_t required;
+ *dest = NULL;
+
+ /*
+ * Writing the string escapes can only really happen in
+ * utf-16.
+ */
+ ok = convert_string_talloc(mem_ctx,
+ CH_UTF8, CH_UTF16LE,
+ src, src_len,
+ &utf16, &utf16_byte_len);
+ if (!ok) {
+ return false;
+ }
+ utf16_len = utf16_byte_len / 2;
+
+ escapees = 0;
+ for (i = 0; i < utf16_len; i++) {
+ uint16_t c = utf16[i];
+ if (sddl_should_escape_utf16(c)) {
+ escapees++;
+ }
+ if (c == 0) {
+ /* we can't have '\0' (or "%0000") in a name. */
+ TALLOC_FREE(utf16);
+ return false;
+ }
+ }
+
+ required = src_len + escapees * 5;
+ escaped = talloc_size(mem_ctx, required + 1);
+ if (escaped == NULL) {
+ TALLOC_FREE(utf16);
+ return false;
+ }
+
+ if (escapees == 0) {
+ /* there is nothing to escape: the original string is fine */
+ memcpy(escaped, src, src_len);
+ escaped[src_len] = '\0';
+ *dest = escaped;
+ *dest_len = src_len;
+ TALLOC_FREE(utf16);
+ return true;
+ }
+
+ for (i = 0, j = 0; i < utf16_len && j < required; i++) {
+ uint16_t c = utf16[i];
+ if (sddl_should_escape_utf16(c)) {
+ if (j + 5 >= required) {
+ TALLOC_FREE(escaped);
+ TALLOC_FREE(utf16);
+ return false;
+ }
+ snprintf(escaped + j, 6, "%%%04x", c);
+ j += 5;
+ } else {
+ escaped[j] = c;
+ j++;
+ }
+ }
+ escaped[j] = '\0';
+
+ *dest = escaped;
+ *dest_len = j;
+
+ TALLOC_FREE(utf16);
+ return true;
+}
+
+static bool sddl_write_attr(struct sddl_write_context *ctx,
+ struct ace_condition_token *tok)
+{
+ char *name = NULL;
+ size_t name_len;
+ size_t i;
+ bool ok = sddl_encode_attr_name(ctx->mem_ctx,
+ tok->data.local_attr.value,
+ &name, &name_len);
+ if (!ok) {
+ return false;
+ }
+ for (i = 0; i < ARRAY_SIZE(sddl_attr_types); i++) {
+ struct sddl_attr_type x = sddl_attr_types[i];
+ if (x.code == tok->type) {
+ ok = sddl_write(ctx, "@");
+ if (! ok) {
+ return false;
+ }
+ ok = sddl_write(ctx, x.name);
+ if (! ok) {
+ return false;
+ }
+ break;
+ }
+ }
+
+ ok = sddl_write(ctx, name);
+ talloc_free(name);
+ return ok;
+}
+
+
+static bool sddl_write_unicode(struct sddl_write_context *ctx,
+ const struct ace_condition_token *tok)
+{
+ char *quoted = NULL;
+ bool ok;
+ /*
+ * We rely on tok->data.unicode.value being
+ * nul-terminated.
+ */
+ if (strchr(tok->data.unicode.value, '"') != NULL) {
+ /*
+ * There is a double quote in this string, but SDDL
+ * has no mechanism for escaping these (or anything
+ * else) in unicode strings.
+ *
+ * The only thing to do is fail.
+ *
+ * This cannot happen with an ACE created from SDDL,
+ * because the same no-escapes rule applies on the way
+ * in.
+ */
+ return false;
+ }
+
+ quoted = talloc_asprintf(ctx->mem_ctx, "\"%s\"",
+ tok->data.unicode.value);
+ if (quoted == NULL) {
+ return false;
+ }
+ ok = sddl_write(ctx, quoted);
+ TALLOC_FREE(quoted);
+ return ok;
+}
+
+static bool sddl_write_octet_string(struct sddl_write_context *ctx,
+ const struct ace_condition_token *tok)
+{
+ bool ok;
+ char *hex = hex_encode_talloc(ctx->mem_ctx,
+ tok->data.bytes.data,
+ tok->data.bytes.length);
+ ok = sddl_write(ctx, "#");
+ if (!ok) {
+ return false;
+ }
+ ok = sddl_write(ctx, hex);
+ talloc_free(hex);
+ return ok;
+}
+
+/*
+ * For octet strings, the Resource attribute ACE SDDL differs from conditional
+ * ACE SDDL, lacking the leading '#'.
+ */
+static bool sddl_write_ra_octet_string(struct sddl_write_context *ctx,
+ const struct ace_condition_token *tok)
+{
+ bool ok;
+ char *hex = hex_encode_talloc(ctx->mem_ctx,
+ tok->data.bytes.data,
+ tok->data.bytes.length);
+ ok = sddl_write(ctx, hex);
+ talloc_free(hex);
+ return ok;
+}
+
+
+static bool sddl_write_sid(struct sddl_write_context *ctx,
+ const struct ace_condition_token *tok)
+{
+ bool ok;
+ char *sddl = NULL;
+ char *sid = sddl_encode_sid(ctx->mem_ctx,
+ &tok->data.sid.sid,
+ NULL);
+ if (sid == NULL) {
+ return false;
+ }
+ sddl = talloc_asprintf(ctx->mem_ctx, "SID(%s)", sid);
+ if (sddl == NULL) {
+ talloc_free(sid);
+ return false;
+ }
+ ok = sddl_write(ctx, sddl);
+ talloc_free(sid);
+ talloc_free(sddl);
+ return ok;
+}
+
+static bool sddl_write_composite(struct sddl_write_context *ctx,
+ struct ace_condition_token *tok)
+{
+ /*
+ * Looks like {1, 2, 3, "four", {"woah, nesting", {6}}, SID(BA)}.
+ */
+ struct ace_condition_composite *c = &tok->data.composite;
+ uint32_t i;
+ bool ok;
+ ok = sddl_write(ctx, "{");
+ if (!ok) {
+ return false;
+ }
+ for (i = 0; i < c->n_members; i++) {
+ struct ace_condition_token *t = &c->tokens[i];
+ if (i > 0) {
+ ok = sddl_write(ctx, ", ");
+ if (!ok) {
+ return false;
+ }
+ }
+ switch (t->type) {
+ case CONDITIONAL_ACE_TOKEN_INT8:
+ case CONDITIONAL_ACE_TOKEN_INT16:
+ case CONDITIONAL_ACE_TOKEN_INT32:
+ case CONDITIONAL_ACE_TOKEN_INT64:
+ ok = sddl_write_int(ctx, t);
+ break;
+ case CONDITIONAL_ACE_TOKEN_UNICODE:
+ ok = sddl_write_unicode(ctx, t);
+ break;
+ case CONDITIONAL_ACE_TOKEN_OCTET_STRING:
+ ok = sddl_write_octet_string(ctx, t);
+ break;
+ case CONDITIONAL_ACE_TOKEN_SID:
+ ok = sddl_write_sid(ctx, t);
+ break;
+ case CONDITIONAL_ACE_TOKEN_COMPOSITE:
+ return false;
+ default:
+ return false;
+ }
+ if (!ok) {
+ return false;
+ }
+ }
+ ok = sddl_write(ctx, "}");
+ return ok;
+}
+
+static bool sddl_write_node(struct sddl_write_context *ctx,
+ struct sddl_node *node)
+{
+ struct ace_condition_token *tok = node->tok;
+ switch (tok->type) {
+ case CONDITIONAL_ACE_TOKEN_INT8:
+ case CONDITIONAL_ACE_TOKEN_INT16:
+ case CONDITIONAL_ACE_TOKEN_INT32:
+ case CONDITIONAL_ACE_TOKEN_INT64:
+ return sddl_write_int(ctx, tok);
+
+ case CONDITIONAL_ACE_TOKEN_MEMBER_OF:
+ case CONDITIONAL_ACE_TOKEN_DEVICE_MEMBER_OF:
+ case CONDITIONAL_ACE_TOKEN_MEMBER_OF_ANY:
+ case CONDITIONAL_ACE_TOKEN_DEVICE_MEMBER_OF_ANY:
+ case CONDITIONAL_ACE_TOKEN_NOT_MEMBER_OF:
+ case CONDITIONAL_ACE_TOKEN_NOT_DEVICE_MEMBER_OF:
+ case CONDITIONAL_ACE_TOKEN_NOT_MEMBER_OF_ANY:
+ case CONDITIONAL_ACE_TOKEN_NOT_DEVICE_MEMBER_OF_ANY:
+ case CONDITIONAL_ACE_TOKEN_EQUAL:
+ case CONDITIONAL_ACE_TOKEN_NOT_EQUAL:
+ case CONDITIONAL_ACE_TOKEN_LESS_THAN:
+ case CONDITIONAL_ACE_TOKEN_LESS_OR_EQUAL:
+ case CONDITIONAL_ACE_TOKEN_GREATER_THAN:
+ case CONDITIONAL_ACE_TOKEN_GREATER_OR_EQUAL:
+ case CONDITIONAL_ACE_TOKEN_CONTAINS:
+ case CONDITIONAL_ACE_TOKEN_ANY_OF:
+ case CONDITIONAL_ACE_TOKEN_NOT_CONTAINS:
+ case CONDITIONAL_ACE_TOKEN_NOT_ANY_OF:
+ case CONDITIONAL_ACE_TOKEN_AND:
+ case CONDITIONAL_ACE_TOKEN_OR:
+ case CONDITIONAL_ACE_TOKEN_EXISTS:
+ case CONDITIONAL_ACE_TOKEN_NOT_EXISTS:
+ case CONDITIONAL_ACE_TOKEN_NOT:
+ return sddl_write(ctx, sddl_strings[tok->type].name);
+
+ case CONDITIONAL_ACE_LOCAL_ATTRIBUTE:
+ case CONDITIONAL_ACE_USER_ATTRIBUTE:
+ case CONDITIONAL_ACE_RESOURCE_ATTRIBUTE:
+ case CONDITIONAL_ACE_DEVICE_ATTRIBUTE:
+ return sddl_write_attr(ctx, tok);
+
+ case CONDITIONAL_ACE_TOKEN_UNICODE:
+ return sddl_write_unicode(ctx, tok);
+
+ case CONDITIONAL_ACE_TOKEN_OCTET_STRING:
+ return sddl_write_octet_string(ctx, tok);
+
+ case CONDITIONAL_ACE_TOKEN_SID:
+ return sddl_write_sid(ctx, tok);
+
+ case CONDITIONAL_ACE_TOKEN_COMPOSITE:
+ return sddl_write_composite(ctx, tok);
+
+ case CONDITIONAL_ACE_TOKEN_INVALID_OR_PADDING:
+ /*
+ * This is only expected at the very end, which we
+ * can't (and don't need to) check here, but we can at
+ * least ensure it's the end of a sub-expression.
+ */
+ return (node->rhs == NULL);
+ default:
+ return false;
+ }
+ /* not expecting to get here */
+ return false;
+}
+
+
+static inline bool sddl_wants_outer_parens(struct sddl_node *node)
+{
+ /*
+ * Binary ops (having a LHS) are always parenthesised "(a == 2)"
+ *
+ * Member-of ops are too, for some reason.
+ */
+ return (node->lhs != NULL ||
+ node->tok->type == CONDITIONAL_ACE_TOKEN_MEMBER_OF ||
+ node->tok->type == CONDITIONAL_ACE_TOKEN_NOT_MEMBER_OF ||
+ node->tok->type == CONDITIONAL_ACE_TOKEN_MEMBER_OF_ANY ||
+ node->tok->type == CONDITIONAL_ACE_TOKEN_NOT_MEMBER_OF_ANY ||
+ node->tok->type == CONDITIONAL_ACE_TOKEN_DEVICE_MEMBER_OF ||
+ node->tok->type == CONDITIONAL_ACE_TOKEN_NOT_DEVICE_MEMBER_OF ||
+ node->tok->type == CONDITIONAL_ACE_TOKEN_DEVICE_MEMBER_OF_ANY ||
+ node->tok->type == CONDITIONAL_ACE_TOKEN_NOT_DEVICE_MEMBER_OF_ANY);
+}
+
+
+static inline bool sddl_wants_inner_parens(struct sddl_node *node,
+ struct sddl_node *child)
+{
+ /*
+ * logical operators are serialised with parentheses around their
+ * arguments (for NOT it is obligatory).
+ */
+ if (node->tok->type != CONDITIONAL_ACE_TOKEN_NOT &&
+ node->tok->type != CONDITIONAL_ACE_TOKEN_AND &&
+ node->tok->type != CONDITIONAL_ACE_TOKEN_OR) {
+ return false;
+ }
+ if (sddl_wants_outer_parens(child)) {
+ return false;
+ }
+ return true;
+}
+
+
+static void sddl_tree_resolve_parens(struct sddl_node *node)
+{
+ if (sddl_wants_outer_parens(node)) {
+ node->wants_parens = true;
+ }
+ if (node->lhs != NULL) {
+ bool p = sddl_wants_inner_parens(node, node->lhs);
+ node->lhs->wants_parens = p;
+ sddl_tree_resolve_parens(node->lhs);
+ }
+ if (node->rhs != NULL) {
+ bool p = sddl_wants_inner_parens(node, node->rhs);
+ node->rhs->wants_parens = p;
+ sddl_tree_resolve_parens(node->rhs);
+ }
+}
+
+static bool sddl_tree_to_sddl(struct sddl_write_context *ctx,
+ struct sddl_node *node)
+{
+ bool ok;
+ if (node->wants_parens) {
+ ok = sddl_write(ctx, "(");
+ if (! ok) {
+ return false;
+ }
+ }
+
+ if (node->lhs != NULL) {
+ ok = sddl_tree_to_sddl(ctx, node->lhs);
+ if (! ok) {
+ return false;
+ }
+ ok = sddl_write(ctx, " ");
+ if (!ok) {
+ return false;
+ }
+ }
+
+ ok = sddl_write_node(ctx, node);
+ if (!ok) {
+ return false;
+ }
+ if (node->rhs != NULL) {
+ /* NOT is a special case: "!(x)", not "! (x)" */
+ if (node->tok->type != CONDITIONAL_ACE_TOKEN_NOT) {
+ ok = sddl_write(ctx, " ");
+ if (!ok) {
+ return false;
+ }
+ }
+
+ ok = sddl_tree_to_sddl(ctx, node->rhs);
+ if (! ok) {
+ return false;
+ }
+ }
+ if (node->wants_parens) {
+ ok = sddl_write(ctx, ")");
+ if (!ok) {
+ return false;
+ }
+ }
+ return true;
+}
+
+/*
+ * Convert conditional ACE conditions into SDDL conditions.
+ *
+ * @param mem_ctx
+ * @param program
+ * @return a string or NULL on error.
+ */
+char *sddl_from_conditional_ace(TALLOC_CTX *mem_ctx,
+ struct ace_condition_script *program)
+{
+ size_t i;
+ char *sddl = NULL;
+ struct sddl_node *nodes = NULL;
+ struct sddl_node **trees = NULL;
+ size_t n_trees = 0;
+ struct ace_condition_token *tok = NULL;
+ struct sddl_data s;
+ bool ok;
+ struct sddl_write_context ctx = {
+ .mem_ctx = mem_ctx
+ };
+
+ if (program->length == 0) {
+ /*
+ * The empty program is a special case.
+ */
+ return talloc_strdup(mem_ctx, "()");
+ }
+ nodes = talloc_zero_array(mem_ctx,
+ struct sddl_node,
+ program->length);
+ if (nodes == NULL) {
+ talloc_free(sddl);
+ return NULL;
+ }
+ trees = talloc_array(mem_ctx,
+ struct sddl_node*,
+ program->length);
+ if (trees == NULL) {
+ talloc_free(sddl);
+ talloc_free(nodes);
+ return NULL;
+ }
+
+ /*
+ * This loop constructs a tree, which we then traverse to get the
+ * SDDL. Consider this transformation:
+ *
+ * {A, B, ==, C, D, ==, &&} => "((A == B) && (C == D))"
+ *
+ * We keep an array of sub-trees, and add to it in sequence. When the
+ * thing we're adding takes arguments, we pop those off the tree list.
+ * So it would go through this sequence:
+ *
+ * len items
+ * 1: A
+ * 2: A, B
+ * 1: ==(A, B)
+ * 2: ==(A, B), C
+ * 3: ==(A, B), C, D
+ * 2: ==(A, B), ==(C, D)
+ * 1 &&(==(A, B), ==(C, D))
+ *
+ * Without building a tree it would be difficult to know how many
+ * parentheses to put before A.
+ *
+ * (A == B == C) should become
+ * {A B == C ==} which should be the same as
+ * ((A == B) == C)
+ */
+
+ for (i = 0; i < program->length; i++) {
+ tok = &program->tokens[i];
+ s = sddl_strings[tok->type];
+ nodes[i].tok = tok;
+ if (s.nargs > n_trees) {
+ goto error;
+ }
+ if (s.nargs >= 1) {
+ /*
+ * Read this note if you're trying to follow
+ * [MS-DTYP]. MS-DTYP uses 'LHS' to describe the
+ * operand of unary operators even though they are
+ * always displayed on the right of the operator. It
+ * makes everything much simpler to use rhs
+ * instead.
+ */
+ n_trees--;
+ nodes[i].rhs = trees[n_trees];
+
+ if (s.nargs == 2) {
+ n_trees--;
+ nodes[i].lhs = trees[n_trees];
+ }
+ }
+ trees[n_trees] = &nodes[i];
+ n_trees++;
+ }
+
+ if (n_trees != 1) {
+ goto error;
+ }
+
+ /*
+ * First we walk the tree to work out where to put parentheses (to
+ * match the canonical Windows representation).
+ *
+ * Doing it in the same traverse as the writing would be possible but
+ * trickier to get right.
+ */
+ sddl_tree_resolve_parens(trees[0]);
+ trees[0]->wants_parens = true;
+
+ /*
+ * Clamber over the tree, writing the string.
+ */
+ ok = sddl_tree_to_sddl(&ctx, trees[0]);
+
+ if (! ok) {
+ goto error;
+ }
+
+ talloc_free(trees);
+ talloc_free(nodes);
+ return ctx.sddl;
+
+ error:
+ talloc_free(sddl);
+ talloc_free(trees);
+ talloc_free(nodes);
+ return NULL;
+}
+
+
+
+static void comp_error(struct ace_condition_sddl_compiler_context *comp,
+ const char *fmt, ...) PRINTF_ATTRIBUTE(2,3);
+
+static void comp_error(struct ace_condition_sddl_compiler_context *comp,
+ const char *fmt, ...)
+{
+ char *msg = NULL;
+ va_list ap;
+ va_start(ap, fmt);
+ msg = talloc_vasprintf(comp->mem_ctx, fmt, ap);
+ va_end(ap);
+ if (msg == NULL) {
+ goto fail;
+ }
+
+ if (comp->message == NULL) {
+ /*
+ * Previously unset message; prepend the position.
+ *
+ * This is the common case.
+ */
+ comp->message_offset = comp->offset;
+ comp->message = msg;
+ return;
+ }
+ /*
+ * There's a message already so we'll try to append.
+ * This is unlikely to happen.
+ */
+ comp->message = talloc_asprintf(comp->mem_ctx,
+ "%s AND THEN %s",
+ comp->message,
+ msg);
+ TALLOC_FREE(msg);
+ if (comp->message == NULL) {
+ goto fail;
+ }
+ DBG_NOTICE("%s\n", comp->message);
+ return;
+fail:
+ comp->message = talloc_strdup(comp->mem_ctx,
+ "failed to set error message");
+ DBG_WARNING("%s\n", comp->message);
+}
+
+
+
+
+/*
+conditional-ace = "(" conditional-ace-type ";" [ace-flag-string] ";" ace-rights
+";" [object- guid] ";" [inherit-object-guid] ";" sid-string ";" "(" cond-expr
+")" ")"
+
+wspace = 1*(%x09-0D / %x20)
+
+literal-SID = "SID(" sid-string ")"
+
+term = [wspace] (memberof-op / exists-op / rel-op / contains-op / anyof-op /
+attr-name / rel- op2) [wspace]
+
+cond-expr = term / term [wspace] ("||" / "&&" ) [wspace] cond-expr / (["!"]
+[wspace] "(" cond-expr ")")
+
+memberof-op = ( "Member_of" / "Not_Member_of" / "Member_of_Any" /
+"Not_Member_of_Any" / "Device_Member_of" / "Device_Member_of_Any" /
+"Not_Device_Member_of" / "Not_Device_Member_of_Any" ) wspace sid-array
+
+exists-op = ( "Exists" / "Not_Exists") wspace attr-name
+
+rel-op = attr-name [wspace] ("<" / "<=" / ">" / ">=") [wspace] (attr-name2 /
+value) ; only scalars
+
+rel-op2 = attr-name [wspace] ("==" / "!=") [wspace] ( attr-name2 / value-array )
+; scalar or list
+
+contains-op = attr-name wspace ("Contains" / "Not_Contains") wspace (attr-name2
+/ value- array)
+
+anyof-op = attr-name wspace ("Any_of" / "Not_Any_of") wspace (attr-name2 /
+value-array)
+
+
+attr-name1 = attr-char1 *(attr-char1 / "@")
+
+attr-char1 = 1*(ALPHA / DIGIT / ":" / "." / "/" / "_")
+
+
+
+attr-name2 = ("@user." / "@device." / "@resource.") 1*attr-char2
+; new prefixed name form
+attr-char2 = attr-char1 / lit-char
+attr-name = attr-name1 / attr-name2
+ */
+
+
+
+static inline bool is_wspace(uint8_t c)
+{
+ /* wspace := %x09-0D | %x20 */
+ return (c == ' ' || c == '\x09' || c == '\x0A' ||
+ c == '\x0B' || c == '\x0C' || c == '\x0D');
+}
+
+static inline bool is_attr_char1(uint8_t c)
+{
+ /*
+ * attr-char1 = 1*(ALPHA / DIGIT / ":" / "." / "/" / "_")
+ * (ALPHA and DIGIT being ASCII only).
+ *
+ * These are used for local attributes, which we don't really
+ * expect to see in Samba AD.
+ *
+ * One example is "WIN://SYSAPPID", which is used in conditional ACEs
+ * that seem to relate to software installers; another is
+ * "APPID://PATH", used by Windows Applocker.
+ */
+ return (((c >= 'a') && (c <= 'z')) ||
+ ((c >= 'A') && (c <= 'Z')) ||
+ ((c >= '0') && (c <= '9')) ||
+ c == ':' || c == '.' || c == '/' || c == '_');
+}
+
+
+static ssize_t read_attr2_string(
+ struct ace_condition_sddl_compiler_context *comp,
+ struct ace_condition_unicode *dest)
+{
+ /*
+ * our SDDL is utf-8, but we need to convert to utf-16 and
+ * parse the escapes, then back to utf-8, because that's how
+ * the claims will appear.
+ *
+ * attr_char2 is used for attribute names that follow "@Class."
+ * specifiers. They can consume 5 characters to specify a single code
+ * unit, using "%1234" style escapes. Certain characters must be
+ * encoded this way, while others must be literal values. Because the
+ * %1234 refers to a utf-16 code unit, we really need to do the work
+ * in that codespace.
+ */
+ bool ok;
+ uint16_t *utf16 = NULL;
+ size_t utf16_byte_len;
+ size_t utf16_chars;
+ size_t utf8_len;
+ size_t src_len;
+ ssize_t i, j;
+ ssize_t max_len = comp->length - comp->offset;
+ const uint8_t *src = comp->sddl + comp->offset;
+
+ for (i = 0; i < max_len; i++) {
+ uint8_t c = src[i];
+ /*
+ * A double‐byte that must be escaped but isn't tells us that
+ * the attribute name has ended.
+ *
+ * The exception is '%', which must also be escaped
+ * (as "%0025"), but is obviously still expected in
+ * the escaped string.
+ */
+ if (strchr("!&()><=| \"", c) != NULL || is_wspace(c)) {
+ break;
+ }
+ }
+ if (i == max_len) {
+ /* too long, because we need at least one ')' */
+ comp_error(comp, "interminable attribute name");
+ return -1;
+ }
+ if (i == 0) {
+ /* too short! like "User.>= 4" */
+ comp_error(comp, "empty attribute name");
+ return -1;
+ }
+
+ if (unlikely(i > CONDITIONAL_ACE_MAX_LENGTH)) {
+ /*
+ * This is imprecise; the limit for the whole ACL is 64k.
+ * However there could be many escapes in the SDDL name which
+ * would reduce down to single utf16 code units in the
+ * compiled string.
+ */
+ comp_error(comp, "attribute is way too long (%zu)", i);
+ return -1;
+ }
+
+ src_len = i;
+
+ ok = convert_string_talloc(comp->mem_ctx,
+ CH_UTF8, CH_UTF16LE,
+ src, src_len,
+ &utf16, &utf16_byte_len);
+ if (!ok) {
+ comp_error(comp, "could not convert to utf-16");
+ return -1;
+ }
+ /*
+ * utf16_byte_len is in bytes, we want to count uint16s.
+ */
+ utf16_chars = utf16_byte_len / 2;
+
+ /* now the escapes. */
+ for (i = 0, j = 0;
+ j < utf16_chars && i < utf16_chars;
+ j++) {
+ uint16_t c = utf16[i];
+ if (c == '%') {
+ uint16_t v = 0;
+ size_t end = i + 5;
+ /*
+ * we need to read 4 hex characters.
+ * hex_byte() won't help because that is 8-bit.
+ */
+ if (end > utf16_chars) {
+ comp_error(comp,
+ "insufficient room for %% escape");
+ talloc_free(utf16);
+ return -1;
+ }
+ for (i++; i < end; i++) {
+ v <<= 4;
+ c = utf16[i];
+ if (c >= '0' && c <= '9') {
+ v += c - '0';
+ } else if (c >= 'A' && c <= 'F') {
+ v += c - 'A' + 10;
+ } else if (c >= 'a' && c <= 'f') {
+ v += c - 'a' + 10;
+ } else {
+ comp_error(comp, "invalid %% escape");
+ talloc_free(utf16);
+ return -1;
+ }
+ }
+ /*
+ * from MS-DTYP 2.5.1.1 Syntax (text, not ABNF), some
+ * characters must be literals, not escaped.
+ */
+ if ((v >= '0' && v <= '9') ||
+ (v >= 'A' && v <= 'Z') ||
+ (v >= 'a' && v <= 'z') ||
+ (v < 127 &&
+ strchr("#$'*+-;?@[\\]^_`{}~:/.", v) != NULL)) {
+ comp_error(comp, "invalid %% escape: "
+ "'%%%04x' should be literal '%c'",
+ v, v);
+ talloc_free(utf16);
+ return -1;
+ }
+ utf16[j] = v;
+ continue;
+ }
+ /*
+ * Note the characters "!&()><=|% \"" must be escaped per
+ * [MS-DTYP], but as we found the bounds of this string using
+ * those in utf-8 at the top of this function, we are not
+ * going to find them in the utf-16 now.
+ *
+ * Also, per [MS-DTYP], un-escaped whitespace is allowed, but
+ * effectively disallowed by Samba.
+ */
+ utf16[j] = utf16[i];
+ i++;
+ }
+
+ ok = convert_string_talloc(comp->mem_ctx,
+ CH_UTF16LE, CH_UTF8,
+ utf16, j * 2,
+ &dest->value, &utf8_len);
+ TALLOC_FREE(utf16);
+ if (!ok) {
+ comp_error(comp, "could not convert to utf-16");
+ return -1;
+ }
+
+ /* returning bytes consumed, not necessarily the length of token */
+ return src_len;
+}
+
+
+
+static bool eat_whitespace(struct ace_condition_sddl_compiler_context *comp,
+ bool trailing)
+{
+ /*
+ * Advance the offset to the first non-whitespace character.
+ *
+ * If trailing is false, there has to be something before the end of
+ * the string.
+ */
+ while (comp->offset < comp->length) {
+ if (! is_wspace(comp->sddl[comp->offset])) {
+ break;
+ }
+ comp->offset++;
+ }
+ if ((!trailing) && comp->offset == comp->length) {
+ comp_error(comp, "input ends unexpectedly");
+ return false;
+ }
+ return true;
+}
+
+static bool pop_sddl_token(struct ace_condition_sddl_compiler_context *comp,
+ struct ace_condition_token *token);
+
+static bool write_sddl_token(struct ace_condition_sddl_compiler_context *comp,
+ struct ace_condition_token token);
+
+static bool pop_write_sddl_token(
+ struct ace_condition_sddl_compiler_context *comp);
+
+
+static bool flush_stack_tokens(struct ace_condition_sddl_compiler_context *comp,
+ uint8_t type)
+{
+ bool ok;
+ uint8_t precedence = sddl_strings[type].op_precedence;
+ if (precedence == SDDL_PRECEDENCE_PAREN_START) {
+ /* paren has a special role */
+ return true;
+ }
+ /*
+ * Any operators on the top of the stack that have a "higher"
+ * precedence (tighter binding) to this one get popped off and written
+ * to the output. "higher" is in quotes because it means lower enum
+ * value.
+ *
+ * This works for binary operators, for example, with "(a == b == c)"
+ * (which is equivalent to "((a == b) == c)" via the left-to-right
+ * rule), we have:
+ * TOKEN dest PROGRAM STACK
+ * (
+ * a p
+ * == s a
+ * b p a ==
+ * == s a b ==
+ * flush stack
+ * s->p a b == ==
+ * c p a b ==
+ * ) a b == c ==
+ * flush stack
+ * a b == c ==
+ *
+ * but it is not right for unary operators, as in "(!(!(Exists
+ * a)))". As it turns out though, >= works for the unary
+ * operators and syntactic rules we have.
+ */
+ while (comp->stack_depth > 0) {
+ struct ace_condition_token *op =
+ &comp->stack[comp->stack_depth - 1];
+ if(sddl_strings[op->type].op_precedence > precedence) {
+ break;
+ }
+ if(sddl_strings[op->type].op_precedence == precedence &&
+ sddl_strings[op->type].flags & SDDL_FLAG_IS_UNARY_OP) {
+ break;
+ }
+
+ ok = pop_write_sddl_token(comp);
+ if (! ok) {
+ comp_error(comp,
+ "could not flush '%s' to program",
+ sddl_strings[op->type].name);
+ return false;
+ }
+ }
+ return true;
+}
+
+static bool push_sddl_token(struct ace_condition_sddl_compiler_context *comp,
+ struct ace_condition_token token)
+{
+ if (comp->stack_depth >= CONDITIONAL_ACE_MAX_TOKENS - 1) {
+ comp_error(comp, "excessive recursion");
+ return false;
+ }
+ if (sddl_strings[token.type].op_precedence == SDDL_NOT_AN_OP) {
+ comp_error(comp,
+ "wrong kind of token for the SDDL stack: %s",
+ sddl_strings[token.type].name);
+ return false;
+ }
+ /*
+ * Any operators on the top of the stack that have a "greater" or
+ * equal precedence to this one get popped off and written to the
+ * output.
+ */
+ flush_stack_tokens(comp, token.type);
+
+ token.data.op.sddl_position = comp->offset;
+
+ comp->stack[comp->stack_depth] = token;
+ comp->stack_depth++;
+ if (token.type != CONDITIONAL_ACE_SAMBA_SDDL_PAREN) {
+ comp->last_token_type = token.type;
+ }
+ return true;
+}
+
+static bool pop_sddl_token(struct ace_condition_sddl_compiler_context *comp,
+ struct ace_condition_token *token)
+{
+ if (comp->stack_depth == 0) {
+ comp_error(comp, "misbalanced expression");
+ return false;
+ }
+ comp->stack_depth--;
+ *token = comp->stack[comp->stack_depth];
+ return true;
+}
+
+
+static bool write_sddl_token(struct ace_condition_sddl_compiler_context *comp,
+ struct ace_condition_token token)
+{
+ /*
+ * This is adding a token to the program. Normally it will be to the
+ * main program list, but if we are constructing a composite list, then
+ * will be redirected there (via comp->target).
+ *
+ * We also conservatively track the overall size, so we don't waste
+ * time compiling something that is way too big.
+ */
+ DBG_INFO("writing %"PRIu32" %x %s\n",
+ *comp->target_len,
+ token.type,
+ sddl_strings[token.type].name);
+ comp->approx_size++;
+ if (comp->approx_size > CONDITIONAL_ACE_MAX_TOKENS) {
+ comp_error(comp, "program is too long "
+ "(over %d tokens)",
+ CONDITIONAL_ACE_MAX_TOKENS);
+ return false;
+ }
+ if (token.type != CONDITIONAL_ACE_SAMBA_SDDL_PAREN) {
+ comp->last_token_type = token.type;
+ }
+ comp->target[*comp->target_len] = token;
+ (*comp->target_len)++;
+ return true;
+}
+
+static bool pop_write_sddl_token(
+ struct ace_condition_sddl_compiler_context *comp)
+{
+ bool ok;
+ struct ace_condition_token token = {};
+ ok = pop_sddl_token(comp, &token);
+ if (!ok) {
+ comp_error(comp, "could not pop from op stack");
+ return false;
+ }
+ if (comp->target != comp->program->tokens) {
+ comp_error(comp, "compiler is seriously confused");
+ return false;
+ }
+
+ ok = write_sddl_token(comp, token);
+ if (!ok) {
+ comp_error(comp,
+ "could not write '%s' to program",
+ sddl_strings[token.type].name);
+ return false;
+ }
+ DBG_INFO(" written '%s'\n", sddl_strings[token.type].name);
+ return true;
+}
+
+
+
+static bool parse_expression(struct ace_condition_sddl_compiler_context *comp);
+static bool parse_composite(struct ace_condition_sddl_compiler_context *comp);
+
+
+
+
+static bool parse_oppy_op(struct ace_condition_sddl_compiler_context *comp)
+{
+ /*
+ * These ones look like operators and are operators.
+ */
+ bool ok;
+ struct ace_condition_token token = {};
+ uint8_t c, d;
+ uint32_t flag = SDDL_FLAG_EXPECTING_BINARY_OP;
+
+ if (comp->offset + 1 >= comp->length) {
+ comp_error(comp, "syntax error");
+ return false;
+ }
+
+ token.data.sddl_op.start = comp->offset;
+
+ /*
+ * These are all one or two characters long, and we always have room
+ * to peek ahead.
+ */
+ c = comp->sddl[comp->offset];
+ d = comp->sddl[comp->offset + 1];
+
+ if (c == '!') {
+ if (d == '=') {
+ comp->offset++;
+ token.type = CONDITIONAL_ACE_TOKEN_NOT_EQUAL;
+
+ } else {
+ token.type = CONDITIONAL_ACE_TOKEN_NOT;
+ flag = SDDL_FLAG_EXPECTING_UNARY_OP;
+ }
+ } else if (c == '=' && d == '=') {
+ comp->offset++;
+ token.type = CONDITIONAL_ACE_TOKEN_EQUAL;
+ } else if (c == '>') {
+ if (d == '=') {
+ comp->offset++;
+ token.type = CONDITIONAL_ACE_TOKEN_GREATER_OR_EQUAL;
+
+ } else {
+ token.type = CONDITIONAL_ACE_TOKEN_GREATER_THAN;
+ }
+ } else if (c == '<') {
+ if (d == '=') {
+ comp->offset++;
+ token.type = CONDITIONAL_ACE_TOKEN_LESS_OR_EQUAL;
+
+ } else {
+ token.type = CONDITIONAL_ACE_TOKEN_LESS_THAN;
+ }
+ } else if (c == '&' && d == '&') {
+ comp->offset++;
+ token.type = CONDITIONAL_ACE_TOKEN_AND;
+ flag = SDDL_FLAG_EXPECTING_BINARY_LOGIC_OP;
+ } else if (c == '|' && d == '|') {
+ comp->offset++;
+ token.type = CONDITIONAL_ACE_TOKEN_OR;
+ flag = SDDL_FLAG_EXPECTING_BINARY_LOGIC_OP;
+ } else {
+ comp_error(comp, "unknown operator");
+ return false;
+ }
+
+ if ((comp->state & flag) == 0) {
+ comp_error(comp, "unexpected operator");
+ return false;
+ }
+
+ comp->offset++;
+
+ ok = push_sddl_token(comp, token);
+ if (!ok) {
+ return false;
+ }
+
+ ok = eat_whitespace(comp, true);
+ return ok;
+}
+
+static bool parse_unicode(struct ace_condition_sddl_compiler_context *comp)
+{
+ /*
+ * This looks like "hello" (including the double quotes).
+ *
+ * Fortunately (for now), there is no mechanism for escaping
+ * double quotes in conditional ace strings, so we can simply
+ * look for the second quote without worrying about things
+ * like «\\\"».
+ */
+ struct ace_condition_token token = {};
+ char *s = NULL;
+ const uint8_t *src = NULL;
+ char *utf16 = NULL;
+ size_t len, max_len;
+ bool ok;
+ if (comp->sddl[comp->offset] != '"') {
+ comp_error(comp, "was expecting '\"' for Unicode string");
+ return false;
+ }
+ comp->offset++;
+ src = comp->sddl + comp->offset;
+ max_len = comp->length - comp->offset;
+ /* strnchr */
+ for (len = 0; len < max_len; len++) {
+ if (src[len] == '"') {
+ break;
+ }
+ }
+ if (len == max_len) {
+ comp_error(comp, "unterminated unicode string");
+ return false;
+ }
+
+ /*
+ * Look, this is wasteful, but it probably doesn't matter. We want to
+ * check that the string we're putting into the descriptor is valid,
+ * or we'll see errors down the track.
+ */
+ ok = convert_string_talloc(comp->mem_ctx,
+ CH_UTF8, CH_UTF16LE,
+ src, len,
+ &utf16, NULL);
+ if (!ok) {
+ comp_error(comp, "not valid unicode");
+ return false;
+ }
+ TALLOC_FREE(utf16);
+
+ s = talloc_array_size(comp->mem_ctx, 1, len + 1);
+ if (s == NULL) {
+ comp_error(comp, "allocation error");
+ return false;
+ }
+ memcpy(s, src, len);
+ s[len] = 0;
+ comp->offset += len + 1; /* +1 for the final quote */
+ token.type = CONDITIONAL_ACE_TOKEN_UNICODE;
+ token.data.unicode.value = s;
+
+ return write_sddl_token(comp, token);
+}
+
+
+static bool parse_octet_string(struct ace_condition_sddl_compiler_context *comp)
+{
+ /*
+ * This looks like '#hhhh...', where each 'hh' is hex for a byte, with
+ * the weird and annoying complication that '#' can be used to mean
+ * '0'.
+ */
+ struct ace_condition_token token = {};
+ size_t length, i;
+
+ if (comp->sddl[comp->offset] != '#') {
+ comp_error(comp, "was expecting '#' for octet string");
+ return false;
+ }
+ comp->offset++;
+ length = strspn((const char*)(comp->sddl + comp->offset),
+ "#0123456789abcdefABCDEF");
+
+ if (length & 1) {
+ comp_error(comp, "octet string has odd number of hex digits");
+ return false;
+ }
+
+ length /= 2;
+
+ token.data.bytes = data_blob_talloc_zero(comp->mem_ctx, length);
+ token.type = CONDITIONAL_ACE_TOKEN_OCTET_STRING;
+
+ for (i = 0; i < length; i++) {
+ /*
+ * Why not just strhex_to_str()?
+ *
+ * Because we need to treat '#' as '0' in octet string values,
+ * so all of the following are the same
+ * (equaling {0x10, 0x20, 0x30, 0x0}).
+ *
+ * #10203000
+ * #10203###
+ * #1#2#3###
+ * #10203#00
+ */
+ bool ok;
+ char pair[2];
+ size_t j = comp->offset + i * 2;
+ pair[0] = (comp->sddl[j] == '#') ? '0' : comp->sddl[j];
+ pair[1] = (comp->sddl[j + 1] == '#') ? '0' : comp->sddl[j + 1];
+
+ ok = hex_byte(pair, &token.data.bytes.data[i]);
+ if (!ok) {
+ talloc_free(token.data.bytes.data);
+ comp_error(comp, "inexplicable error in octet string");
+ return false;
+ }
+ }
+ comp->offset += length * 2;
+ return write_sddl_token(comp, token);
+}
+
+
+static bool parse_ra_octet_string(struct ace_condition_sddl_compiler_context *comp)
+{
+ /*
+ * Resource attribute octet strings resemble conditional ace octet
+ * strings, but have some important differences:
+ *
+ * 1. The '#' at the start is optional, and if present is
+ * counted as a zero.
+ *
+ * 2. An odd number of characters is implicitly left-padded with a zero.
+ *
+ * That is, "abc" means "0abc", "#12" means "0012", "f##"
+ * means "0f00", and "##" means 00.
+ */
+ struct ace_condition_token token = {};
+ size_t string_length, bytes_length, i, j;
+ bool ok;
+ char pair[2];
+
+ string_length = strspn((const char*)(comp->sddl + comp->offset),
+ "#0123456789abcdefABCDEF");
+
+ bytes_length = (string_length + 1) / 2;
+
+ if (bytes_length == 0) {
+ comp_error(comp, "zero length octet bytes");
+ return false;
+ }
+
+ token.data.bytes = data_blob_talloc_zero(comp->mem_ctx, bytes_length);
+ if (token.data.bytes.data == NULL) {
+ return false;
+ }
+ token.type = CONDITIONAL_ACE_TOKEN_OCTET_STRING;
+
+ j = comp->offset;
+ i = 0;
+ if (string_length & 1) {
+ /*
+ * An odd number of characters means the first
+ * character gains an implicit 0 for the high nybble.
+ */
+ pair[0] = 0;
+ pair[1] = (comp->sddl[0] == '#') ? '0' : comp->sddl[0];
+
+ ok = hex_byte(pair, &token.data.bytes.data[i]);
+ if (!ok) {
+ goto fail;
+ }
+ j++;
+ i++;
+ }
+
+ for (; i < bytes_length; i++) {
+ /*
+ * Why not just strhex_to_str() ?
+ *
+ * Because we need to treat '#' as '0' in octet string values.
+ */
+ if (comp->length - j < 2) {
+ goto fail;
+ }
+
+ pair[0] = (comp->sddl[j] == '#') ? '0' : comp->sddl[j];
+ pair[1] = (comp->sddl[j + 1] == '#') ? '0' : comp->sddl[j + 1];
+
+ ok = hex_byte(pair, &token.data.bytes.data[i]);
+ if (!ok) {
+ goto fail;
+ }
+ j += 2;
+ }
+ comp->offset = j;
+ return write_sddl_token(comp, token);
+
+fail:
+ comp_error(comp, "inexplicable error in octet string");
+ talloc_free(token.data.bytes.data);
+ return false;
+}
+
+
+static bool parse_sid(struct ace_condition_sddl_compiler_context *comp)
+{
+ struct dom_sid *sid = NULL;
+ const uint8_t *sidstr = NULL;
+ struct ace_condition_token token = {};
+ size_t end;
+ if (comp->length - comp->offset < 7) {
+ /* minimum: "SID(AA)" */
+ comp_error(comp, "no room for a complete SID");
+ return false;
+ }
+ /* conditional ACE SID string */
+ if (comp->sddl[comp->offset ] != 'S' ||
+ comp->sddl[comp->offset + 1] != 'I' ||
+ comp->sddl[comp->offset + 2] != 'D' ||
+ comp->sddl[comp->offset + 3] != '(') {
+ comp_error(comp, "malformed SID() constructor");
+ return false;
+ } else {
+ comp->offset += 4;
+ }
+
+ sidstr = comp->sddl + comp->offset;
+
+ sid = sddl_decode_sid(comp->mem_ctx,
+ (const char **)&sidstr,
+ comp->domain_sid);
+
+ if (sid == NULL) {
+ comp_error(comp, "could not parse SID");
+ return false;
+ }
+ end = sidstr - comp->sddl;
+ if (end >= comp->length || end < comp->offset) {
+ comp_error(comp, "apparent overflow in SID parsing");
+ return false;
+ }
+ comp->offset = end;
+ /*
+ * offset is now at the end of the SID, but we need to account
+ * for the ')'.
+ */
+ if (comp->sddl[comp->offset] != ')') {
+ comp_error(comp, "expected ')' to follow SID");
+ return false;
+ }
+ comp->offset++;
+
+ token.type = CONDITIONAL_ACE_TOKEN_SID;
+ token.data.sid.sid = *sid;
+ return write_sddl_token(comp, token);
+}
+
+
+
+static bool parse_ra_sid(struct ace_condition_sddl_compiler_context *comp)
+{
+ struct dom_sid *sid = NULL;
+ const uint8_t *sidstr = NULL;
+ struct ace_condition_token token = {};
+ size_t end;
+
+ if ((comp->state & SDDL_FLAG_EXPECTING_LITERAL) == 0) {
+ comp_error(comp, "did not expect a SID here");
+ return false;
+ }
+ /*
+ * Here we are parsing a resource attribute ACE which doesn't
+ * have the SID() wrapper around the SID string (unlike a
+ * conditional ACE).
+ *
+ * The resource ACE doesn't need this because there is no
+ * ambiguity with local attribute names, besides which the
+ * type has already been specified earlier in the ACE.
+ */
+ if (comp->length - comp->offset < 2){
+ comp_error(comp, "no room for a complete SID");
+ return false;
+ }
+
+ sidstr = comp->sddl + comp->offset;
+
+ sid = sddl_decode_sid(comp->mem_ctx,
+ (const char **)&sidstr,
+ comp->domain_sid);
+
+ if (sid == NULL) {
+ comp_error(comp, "could not parse SID");
+ return false;
+ }
+ end = sidstr - comp->sddl;
+ if (end >= comp->length || end < comp->offset) {
+ comp_error(comp, "apparent overflow in SID parsing");
+ return false;
+ }
+ comp->offset = end;
+ token.type = CONDITIONAL_ACE_TOKEN_SID;
+ token.data.sid.sid = *sid;
+ return write_sddl_token(comp, token);
+}
+
+
+static bool parse_int(struct ace_condition_sddl_compiler_context *comp)
+{
+ /*
+ * This one is relatively simple. strtoll() does the work.
+ */
+ long long v;
+ struct ace_condition_token token = {};
+ const char *start = (const char *)comp->sddl + comp->offset;
+ char *end = NULL;
+ const char *first_digit = start;
+ size_t len;
+ errno = 0;
+ v = strtoll(start, &end, 0);
+ if (errno != 0) {
+ comp_error(comp, "bad integer: %s", strerror(errno));
+ return false;
+ }
+ len = end - start;
+
+ if (len == 0) {
+ comp_error(comp, "unexpected non-integer");
+ return false;
+ }
+ if (comp->offset + len > comp->length) {
+ comp_error(comp, "impossible integer length: %zu!", len);
+ return false;
+ }
+
+ comp->offset += len;
+
+ /*
+ * Record the base and sign, which are used for recreating the SDDL.
+ *
+ * 'Sign' indicates whether there is a '+' or '-' sign. Base indicates
+ * whether the number was in hex, octal, or decimal. These make no
+ * difference to the evaluation of the ACE, just the display.
+ *
+ * This would not work reliably if eat_whitespace() is not called
+ * before parse_int(), but a) we know it is, and b) we don't *really*
+ * care if we lose these display hints.
+ */
+ if (*start == '-') {
+ token.data.int64.sign = CONDITIONAL_ACE_INT_SIGN_NEGATIVE;
+ first_digit++;
+ } else if (*start == '+') {
+ token.data.int64.sign = CONDITIONAL_ACE_INT_SIGN_POSITIVE;
+ first_digit++;
+ } else {
+ token.data.int64.sign = CONDITIONAL_ACE_INT_SIGN_NONE;
+ }
+ if (*first_digit == '0' && (end - first_digit) > 1) {
+ if ((end - first_digit > 2) &&
+ (first_digit[1] == 'x' ||
+ first_digit[1] == 'X')) {
+ token.data.int64.base = CONDITIONAL_ACE_INT_BASE_16;
+ } else {
+ token.data.int64.base = CONDITIONAL_ACE_INT_BASE_8;
+ }
+ } else {
+ token.data.int64.base = CONDITIONAL_ACE_INT_BASE_10;
+ }
+
+ token.data.int64.value = v;
+ token.type = CONDITIONAL_ACE_TOKEN_INT64;
+ return write_sddl_token(comp, token);
+}
+
+
+static bool parse_uint(struct ace_condition_sddl_compiler_context *comp)
+{
+ struct ace_condition_token *tok = NULL;
+ bool ok = parse_int(comp);
+ if (ok == false) {
+ return false;
+ }
+ /*
+ * check that the token's value is positive.
+ */
+ if (comp->target_len == 0) {
+ return false;
+ }
+ tok = &comp->target[*comp->target_len - 1];
+ if (tok->type != CONDITIONAL_ACE_TOKEN_INT64) {
+ return false;
+ }
+ if (tok->data.int64.value < 0) {
+ comp_error(comp, "invalid resource ACE value for unsigned TU claim");
+ return false;
+ }
+ return true;
+}
+
+
+static bool parse_bool(struct ace_condition_sddl_compiler_context *comp)
+{
+ struct ace_condition_token *tok = NULL;
+ bool ok = parse_int(comp);
+ if (ok == false || comp->target_len == 0) {
+ return false;
+ }
+ /*
+ * check that the token is 0 or 1.
+ */
+ tok = &comp->target[*comp->target_len - 1];
+ if (tok->type != CONDITIONAL_ACE_TOKEN_INT64) {
+ return false;
+ }
+ if (tok->data.int64.value != 0 && tok->data.int64.value != 1) {
+ comp_error(comp, "invalid resource ACE Boolean value");
+ return false;
+ }
+ return true;
+}
+
+
+static bool could_be_an_int(struct ace_condition_sddl_compiler_context *comp)
+{
+ const char *start = (const char*)(comp->sddl + comp->offset);
+ char* end = NULL;
+
+ if ((comp->state & SDDL_FLAG_EXPECTING_LITERAL) == 0) {
+ return false;
+ }
+
+ errno = 0;
+ /*
+ * See, we don't care about the strtoll return value, only
+ * whether it succeeds or not and what it finds at the end. If
+ * it succeeds, parse_int() will do it again for the value.
+ *
+ * Note that an out of range int will raise ERANGE (probably
+ * 34), so it will be read as a local attribute.
+ */
+ strtoll(start, &end, 0);
+ if (errno != 0 ||
+ end == start ||
+ end >= (const char*)comp->sddl + comp->length) {
+ return false;
+ }
+ /*
+ * We know *some* characters form an int, but if we run right
+ * into other attr1 characters (basically, letters), we won't
+ * count it as an int.
+ *
+ * For example, the "17" in "17p" is not an int. The "17" in
+ * "17||" is.
+ */
+ if (is_attr_char1(*end)) {
+ return false;
+ }
+ return true;
+}
+
+
+static bool parse_word(struct ace_condition_sddl_compiler_context *comp)
+{
+ /*
+ * Sometimes a bare word must be a local attribute, while in other
+ * cases it could also be a member-of or exists operator. Sometimes it
+ * could actually be a SID, which we discover when we've read as far
+ * as "SID(". Sometimes it might be a literal integer (attribute
+ * names can also consist entirely of digits).
+ *
+ * When it is an operator name, we have the complication that a match
+ * does not necessarily end the token. Consider "Member_of_Any" which
+ * contains the operator "Member_of". According to [MS-DTYP], a space
+ * is not necessary between the operator and the next token, but it
+ * does seem to be required for Windows 2022.
+ *
+ * Also, "Member_of" et. al. *could* be valid local attributes, which
+ * would make "(Member_of == 123)" a valid expression that we will
+ * fail to parse. This is not much of an issue for Samba AD where
+ * local attributes are not used.
+ *
+ * Operators are matched case-insensitively.
+ *
+ * There's another kind of attribute that starts with a '@', which we
+ * deal with in parse_attr2(). Those ones have full unicode glory;
+ * these ones are ASCII only.
+ */
+ size_t i, j, k;
+ bool ok;
+ uint8_t candidates[8];
+ size_t n_candidates = 0;
+ struct ace_condition_token token = {};
+ bool expecting_unary = comp->state & SDDL_FLAG_EXPECTING_UNARY_OP;
+ bool expecting_binary = comp->state & SDDL_FLAG_EXPECTING_BINARY_OP;
+ bool expecting_attr = comp->state & SDDL_FLAG_EXPECTING_LOCAL_ATTR;
+ bool expecting_literal = comp->state & SDDL_FLAG_EXPECTING_LITERAL;
+ const uint8_t *start = comp->sddl + comp->offset;
+ uint8_t c = start[0];
+ char *s = NULL;
+ if (! is_attr_char1(*start)) {
+ /* we shouldn't get here, because we peeked first */
+ return false;
+ }
+
+ /*
+ * We'll look for a SID first, because it simplifies the rest.
+ */
+ if (expecting_literal &&
+ comp->offset + 4 < comp->length &&
+ start[0] == 'S' &&
+ start[1] == 'I' &&
+ start[2] == 'D' &&
+ start[3] == '(') {
+ /* actually, we are parsing a SID. */
+ return parse_sid(comp);
+ }
+
+ if (expecting_binary || expecting_unary) {
+ /*
+ * Collect up the operators that can possibly be used
+ * here, including only those that start with the
+ * current letter and have the right arity/syntax.
+ *
+ * We don't expect more than 5 (for 'N', beginning the
+ * "Not_..." unary ops), and we'll winnow them down as
+ * we progress through the word.
+ */
+ int uc = toupper(c);
+ for (i = 0; i < 256; i++) {
+ const struct sddl_data *d = &sddl_strings[i];
+ if (sddl_strings[i].op_precedence != SDDL_NOT_AN_OP &&
+ uc == toupper((unsigned char)d->name[0])) {
+ if (d->flags & SDDL_FLAG_IS_UNARY_OP) {
+ if (!expecting_unary) {
+ continue;
+ }
+ } else if (!expecting_binary) {
+ continue;
+ }
+ candidates[n_candidates] = i;
+ n_candidates++;
+ if (n_candidates == ARRAY_SIZE(candidates)) {
+ /* impossible, really. */
+ return false;
+ }
+ }
+ }
+ } else if (could_be_an_int(comp)) {
+ /*
+ * if looks like an integer, and we expect an integer, it is
+ * an integer. If we don't expect an integer, it is a local
+ * attribute with a STUPID NAME. Or an error.
+ */
+ return parse_int(comp);
+ } else if (! expecting_attr) {
+ comp_error(comp, "did not expect this word here");
+ return false;
+ }
+
+ i = 1;
+ while (comp->offset + i < comp->length) {
+ c = start[i];
+ if (! is_attr_char1(c)) {
+ break;
+ }
+ if (n_candidates != 0) {
+ /*
+ * Filter out candidate operators that no longer
+ * match.
+ */
+ int uc = toupper(c);
+ k = 0;
+ for (j = 0; j < n_candidates; j++) {
+ size_t o = candidates[j];
+ uint8_t c2 = sddl_strings[o].name[i];
+ if (uc == toupper(c2)) {
+ candidates[k] = candidates[j];
+ k++;
+ }
+ }
+ n_candidates = k;
+ }
+ i++;
+ }
+
+ /*
+ * We have finished and there is a complete word. If it could be an
+ * operator we'll assume it is one.
+ *
+ * A complication is we could have matched more than one operator, for
+ * example "Member_of" and "Member_of_Any", so we have to look through
+ * the list of candidates for the one that ends.
+ */
+ if (n_candidates != 0) {
+ for (j = 0; j < n_candidates; j++) {
+ size_t o = candidates[j];
+ if (sddl_strings[o].name[i] == '\0') {
+ /* it is this one */
+
+ if (!comp->allow_device &&
+ (sddl_strings[o].flags & SDDL_FLAG_DEVICE))
+ {
+ comp_error(
+ comp,
+ "a device‐relative expression "
+ "will never evaluate to true "
+ "in this context (did you "
+ "intend a user‐relative "
+ "expression?)");
+ return false;
+ }
+
+ token.type = o;
+ token.data.sddl_op.start = comp->offset;
+ comp->offset += i;
+ ok = push_sddl_token(comp, token);
+ return ok;
+ }
+ }
+ }
+ /*
+ * if looks like an integer, and we expect an integer, it is
+ * an integer. If we don't expect an integer, it is a local
+ * attribute with a STUPID NAME.
+ */
+ if (could_be_an_int(comp)) {
+ return parse_int(comp);
+ }
+
+ if (! expecting_attr) {
+ comp_error(comp, "word makes no sense here");
+ return false;
+ }
+ /* it's definitely an attribute name */
+ token.type = CONDITIONAL_ACE_LOCAL_ATTRIBUTE;
+ if (comp->offset + i >= comp->length) {
+ comp_error(comp, "missing trailing ')'?");
+ return false;
+ }
+
+ s = talloc_memdup(comp->mem_ctx, start, i + 1);
+ if (s == NULL) {
+ comp_error(comp, "allocation error");
+ return false;
+ }
+ s[i] = 0;
+ token.data.local_attr.value = s;
+ comp->offset += i;
+ return write_sddl_token(comp, token);
+}
+
+static bool parse_attr2(struct ace_condition_sddl_compiler_context *comp)
+{
+ /*
+ * Attributes in the form @class.attr
+ *
+ * class can be "User", "Device", or "Resource", case insensitive.
+ */
+ size_t i;
+ bool ok;
+ size_t len;
+ struct ace_condition_token token = {};
+
+ if ((comp->state & SDDL_FLAG_EXPECTING_NON_LOCAL_ATTR) == 0) {
+ comp_error(comp, "did not expect @attr here");
+ return false;
+ }
+ if (comp->sddl[comp->offset] != '@') {
+ comp_error(comp, "Expected '@'");
+ return false;
+ }
+ comp->offset++;
+
+ for (i = 0; i < ARRAY_SIZE(sddl_attr_types); i++) {
+ int ret;
+ size_t attr_len = strlen(sddl_attr_types[i].name);
+ if (attr_len >= comp->length - comp->offset) {
+ continue;
+ }
+ ret = strncasecmp(sddl_attr_types[i].name,
+ (const char *) (comp->sddl + comp->offset),
+ attr_len);
+ if (ret == 0) {
+ const uint8_t code = sddl_attr_types[i].code;
+
+ if (!comp->allow_device &&
+ (sddl_strings[code].flags & SDDL_FLAG_DEVICE))
+ {
+ comp_error(comp,
+ "a device attribute is not "
+ "applicable in this context (did "
+ "you intend a user attribute?)");
+ return false;
+ }
+
+ token.type = code;
+ comp->offset += attr_len;
+ break;
+ }
+ }
+ if (i == ARRAY_SIZE(sddl_attr_types)) {
+ comp_error(comp, "unknown attribute class");
+ return false;
+ }
+
+ /*
+ * Now we are past the class and the '.', and into the
+ * attribute name. The attribute name can be almost
+ * anything, but some characters need to be escaped.
+ */
+
+ len = read_attr2_string(comp, &token.data.unicode);
+ if (len == -1) {
+ /* read_attr2_string has set a message */
+ return false;
+ }
+ ok = write_sddl_token(comp, token);
+ if (! ok) {
+ return false;
+ }
+ comp->offset += len;
+ ok = eat_whitespace(comp, false);
+ return ok;
+}
+
+static bool parse_literal(struct ace_condition_sddl_compiler_context *comp,
+ bool in_composite)
+{
+ uint8_t c = comp->sddl[comp->offset];
+ if (!(comp->state & SDDL_FLAG_EXPECTING_LITERAL)) {
+ comp_error(comp, "did not expect to be parsing a literal now");
+ return false;
+ }
+ switch(c) {
+ case '#':
+ return parse_octet_string(comp);
+ case '"':
+ return parse_unicode(comp);
+ case 'S':
+ return parse_sid(comp);
+ case '{':
+ if (in_composite) {
+ /* nested composites are not supported */
+ return false;
+ } else {
+ return parse_composite(comp);
+ }
+ default:
+ if (strchr("1234567890-+", c) != NULL) {
+ return parse_int(comp);
+ }
+ }
+ if (c > 31 && c < 127) {
+ comp_error(comp,
+ "unexpected byte 0x%02x '%c' parsing literal", c, c);
+ } else {
+ comp_error(comp, "unexpected byte 0x%02x parsing literal", c);
+ }
+ return false;
+}
+
+
+static bool parse_composite(struct ace_condition_sddl_compiler_context *comp)
+{
+ /*
+ * This jumps into a different parser, expecting a comma separated
+ * list of literal values, which might include nested literal
+ * composites.
+ *
+ * To handle the nesting, we redirect the pointers that determine
+ * where write_sddl_token() writes.
+ */
+ bool ok;
+ bool first = true;
+ struct ace_condition_token token = {
+ .type = CONDITIONAL_ACE_TOKEN_COMPOSITE
+ };
+ uint32_t start = comp->offset;
+ size_t alloc_size;
+ struct ace_condition_token *old_target = comp->target;
+ uint32_t *old_target_len = comp->target_len;
+
+ if (comp->sddl[start] != '{') {
+ comp_error(comp, "expected '{' for composite list");
+ return false;
+ }
+ if (!(comp->state & SDDL_FLAG_EXPECTING_LITERAL)) {
+ comp_error(comp, "did not expect '{' for composite list");
+ return false;
+ }
+ comp->offset++; /* past '{' */
+
+ /*
+ * the worst case is one token for every two bytes: {1,1,1}, and we
+ * allocate for that (counting commas and finding '}' gets hard because
+ * string literals).
+ */
+ alloc_size = MIN((comp->length - start) / 2 + 1,
+ CONDITIONAL_ACE_MAX_LENGTH);
+
+ token.data.composite.tokens = talloc_array(
+ comp->mem_ctx,
+ struct ace_condition_token,
+ alloc_size);
+ if (token.data.composite.tokens == NULL) {
+ comp_error(comp, "allocation failure");
+ return false;
+ }
+
+ comp->target = token.data.composite.tokens;
+ comp->target_len = &token.data.composite.n_members;
+
+ /*
+ * in this loop we are looking for:
+ *
+ * a) possible whitespace.
+ * b) a comma (or terminating '}')
+ * c) more possible whitespace
+ * d) a literal
+ *
+ * Failures use a goto to reset comp->target, just in case we ever try
+ * continuing after error.
+ */
+ while (comp->offset < comp->length) {
+ uint8_t c;
+ ok = eat_whitespace(comp, false);
+ if (! ok) {
+ goto fail;
+ }
+ c = comp->sddl[comp->offset];
+ if (c == '}') {
+ comp->offset++;
+ break;
+ }
+ if (!first) {
+ if (c != ',') {
+ comp_error(comp,
+ "malformed composite (expected comma)");
+ goto fail;
+ }
+ comp->offset++;
+
+ ok = eat_whitespace(comp, false);
+ if (! ok) {
+ goto fail;
+ }
+ }
+ first = false;
+ if (*comp->target_len >= alloc_size) {
+ comp_error(comp,
+ "Too many tokens in composite "
+ "(>= %"PRIu32" tokens)",
+ *comp->target_len);
+ goto fail;
+ }
+ ok = parse_literal(comp, true);
+ if (!ok) {
+ goto fail;
+ }
+ }
+ comp->target = old_target;
+ comp->target_len = old_target_len;
+ write_sddl_token(comp, token);
+ return true;
+fail:
+ talloc_free(token.data.composite.tokens);
+ comp->target = old_target;
+ comp->target_len = old_target_len;
+ return false;
+}
+
+
+static bool parse_paren_literal(struct ace_condition_sddl_compiler_context *comp)
+{
+ bool ok;
+ if (comp->sddl[comp->offset] != '(') {
+ comp_error(comp, "expected '('");
+ return false;
+ }
+ comp->offset++;
+ ok = parse_literal(comp, false);
+ if (!ok) {
+ return false;
+ }
+ if (comp->sddl[comp->offset] != ')') {
+ comp_error(comp, "expected ')'");
+ return false;
+ }
+ comp->offset++;
+ return true;
+}
+
+static bool parse_expression(struct ace_condition_sddl_compiler_context *comp)
+{
+ /*
+ * This expects a parenthesised expression.
+ */
+ bool ok;
+ struct ace_condition_token token = {};
+ uint32_t start = comp->offset;
+
+ if (comp->state & SDDL_FLAG_EXPECTING_PAREN_LITERAL) {
+ /*
+ * Syntactically we allow parentheses to wrap a
+ * literal value after a Member_of or >= op, but we
+ * want to remember that it just wants a single
+ * literal, not a general expression.
+ */
+ return parse_paren_literal(comp);
+ }
+
+ if (comp->sddl[start] != '(') {
+ comp_error(comp, "expected '('");
+ return false;
+ }
+
+ if (!(comp->state & SDDL_FLAG_EXPECTING_PAREN)) {
+ comp_error(comp, "did not expect '('");
+ return false;
+ }
+
+ token.type = CONDITIONAL_ACE_SAMBA_SDDL_PAREN;
+ token.data.sddl_op.start = start;
+ ok = push_sddl_token(comp, token);
+ if (!ok) {
+ return false;
+ }
+ comp->offset++; /* over the '(' */
+ comp->state = SDDL_FLAGS_EXPR_START;
+ DBG_INFO("%3"PRIu32": (\n", comp->offset);
+
+ comp->state |= SDDL_FLAG_NOT_EXPECTING_END_PAREN;
+
+ while (comp->offset < comp->length) {
+ uint8_t c;
+ ok = eat_whitespace(comp, false);
+ if (! ok) {
+ return false;
+ }
+ c = comp->sddl[comp->offset];
+ if (c == '(') {
+ ok = parse_expression(comp);
+ } else if (c == ')') {
+ if (comp->state & (SDDL_FLAG_IS_BINARY_OP |
+ SDDL_FLAG_IS_UNARY_OP)) {
+ /*
+ * You can't have "(a ==)" or "(!)"
+ */
+ comp_error(comp,
+ "operator lacks right hand argument");
+ return false;
+ }
+ if (comp->state & SDDL_FLAG_NOT_EXPECTING_END_PAREN) {
+ /*
+ * You can't have "( )"
+ */
+ comp_error(comp, "empty expression");
+ return false;
+ }
+ break;
+ } else if (c == '@') {
+ ok = parse_attr2(comp);
+ } else if (strchr("!<>=&|", c)) {
+ ok = parse_oppy_op(comp);
+ } else if (is_attr_char1(c)) {
+ ok = parse_word(comp);
+ } else if (comp->state & SDDL_FLAG_EXPECTING_LITERAL) {
+ ok = parse_literal(comp, false);
+ } else {
+ if (c > 31 && c < 127) {
+ comp_error(comp,
+ "unexpected byte 0x%02x '%c'", c, c);
+ } else {
+ comp_error(comp, "unexpected byte 0x%02x", c);
+ }
+ ok = false;
+ }
+
+ if (! ok) {
+ return false;
+ }
+ /*
+ * what did we just find? Set what we expect accordingly.
+ */
+ comp->state = sddl_strings[comp->last_token_type].flags;
+ DBG_INFO("%3"PRIu32": %s\n",
+ comp->offset,
+ sddl_strings[comp->last_token_type].name);
+ }
+ ok = eat_whitespace(comp, false);
+ if (!ok) {
+ return false;
+ }
+
+ if (comp->sddl[comp->offset] != ')') {
+ comp_error(comp, "expected ')' to match '(' at %"PRIu32, start);
+ return false;
+ }
+ /*
+ * we won't comp->offset++ until after these other error checks, so
+ * that their messages have consistent locations.
+ */
+ ok = flush_stack_tokens(comp, CONDITIONAL_ACE_SAMBA_SDDL_PAREN_END);
+ if (!ok) {
+ return false;
+ }
+ if (comp->stack_depth == 0) {
+ comp_error(comp, "mysterious nesting error between %"
+ PRIu32" and here",
+ start);
+ return false;
+ }
+ token = comp->stack[comp->stack_depth - 1];
+ if (token.type != CONDITIONAL_ACE_SAMBA_SDDL_PAREN) {
+ comp_error(comp, "nesting error between %"PRIu32" and here",
+ start);
+ return false;
+ }
+ if (token.data.sddl_op.start != start) {
+ comp_error(comp, "')' should match '(' at %"PRIu32
+ ", not %"PRIu32,
+ token.data.sddl_op.start, start);
+ return false;
+ }
+ comp->stack_depth--;
+ DBG_INFO("%3"PRIu32": )\n", comp->offset);
+
+ comp->offset++; /* for the ')' */
+ comp->last_token_type = CONDITIONAL_ACE_SAMBA_SDDL_PAREN_END;
+ comp->state = sddl_strings[comp->last_token_type].flags;
+
+ ok = eat_whitespace(comp, true);
+ return ok;
+}
+
+
+
+static bool init_compiler_context(
+ TALLOC_CTX *mem_ctx,
+ struct ace_condition_sddl_compiler_context *comp,
+ const enum ace_condition_flags ace_condition_flags,
+ const char *sddl,
+ size_t max_length,
+ size_t max_stack)
+{
+ struct ace_condition_script *program = NULL;
+
+ comp->sddl = (const uint8_t*)sddl;
+ comp->mem_ctx = mem_ctx;
+
+ program = talloc_zero(mem_ctx, struct ace_condition_script);
+ if (program == NULL) {
+ return false;
+ }
+ /*
+ * For the moment, we allocate for the worst case up front.
+ */
+ program->tokens = talloc_array(program,
+ struct ace_condition_token,
+ max_length);
+ if (program->tokens == NULL) {
+ TALLOC_FREE(program);
+ return false;
+ }
+ program->stack = talloc_array(program,
+ struct ace_condition_token,
+ max_stack + 1);
+ if (program->stack == NULL) {
+ TALLOC_FREE(program);
+ return false;
+ }
+ comp->program = program;
+ /* we can borrow the program stack for the operator stack */
+ comp->stack = program->stack;
+ comp->target = program->tokens;
+ comp->target_len = &program->length;
+ comp->length = strlen(sddl);
+ comp->state = SDDL_FLAG_EXPECTING_PAREN;
+ comp->allow_device = ace_condition_flags & ACE_CONDITION_FLAG_ALLOW_DEVICE;
+ return true;
+}
+
+/*
+ * Compile SDDL conditional ACE conditions.
+ *
+ * @param mem_ctx
+ * @param sddl - the string to be parsed
+ * @param ace_condition_flags - flags controlling compiler behaviour
+ * @param message - on error, a pointer to a compiler message
+ * @param message_offset - where the error occurred
+ * @param consumed_length - how much of the SDDL was used
+ * @return a struct ace_condition_script (or NULL).
+ */
+struct ace_condition_script * ace_conditions_compile_sddl(
+ TALLOC_CTX *mem_ctx,
+ const enum ace_condition_flags ace_condition_flags,
+ const char *sddl,
+ const char **message,
+ size_t *message_offset,
+ size_t *consumed_length)
+{
+ bool ok;
+ struct ace_condition_sddl_compiler_context comp = {};
+
+ *message = NULL;
+ *message_offset = 0;
+
+ ok = init_compiler_context(mem_ctx,
+ &comp,
+ ace_condition_flags,
+ sddl,
+ CONDITIONAL_ACE_MAX_LENGTH,
+ CONDITIONAL_ACE_MAX_TOKENS);
+ if (!ok) {
+ return NULL;
+ }
+
+ ok = parse_expression(&comp);
+ if (!ok) {
+ goto error;
+ }
+ if (comp.stack_depth != 0) {
+ comp_error(&comp, "incomplete expression");
+ goto error;
+ }
+ if (consumed_length != NULL) {
+ *consumed_length = comp.offset;
+ }
+ *message = comp.message;
+ *message_offset = comp.message_offset;
+ return comp.program;
+ error:
+ *message = comp.message;
+ *message_offset = comp.message_offset;
+ TALLOC_FREE(comp.program);
+ return NULL;
+}
+
+
+
+static bool parse_resource_attr_list(
+ struct ace_condition_sddl_compiler_context *comp,
+ char attr_type_char)
+{
+ /*
+ * This is a bit like parse_composite() above, but with the following
+ * differences:
+ *
+ * - it doesn't want '{...}' around the list.
+ * - if there is just one value, it is not a composite
+ * - all the values must be the expected type.
+ * - there is no nesting.
+ * - SIDs are not written with SID(...) around them.
+ */
+ bool ok;
+ bool first = true;
+ struct ace_condition_token composite = {
+ .type = CONDITIONAL_ACE_TOKEN_COMPOSITE
+ };
+ uint32_t start = comp->offset;
+ size_t alloc_size;
+ struct ace_condition_token *old_target = comp->target;
+ uint32_t *old_target_len = comp->target_len;
+
+ comp->state = SDDL_FLAG_EXPECTING_LITERAL;
+
+ /*
+ * the worst case is one token for every two bytes: {1,1,1}, and we
+ * allocate for that (counting commas and finding '}' gets hard because
+ * string literals).
+ */
+ alloc_size = MIN((comp->length - start) / 2 + 1,
+ CONDITIONAL_ACE_MAX_LENGTH);
+
+ composite.data.composite.tokens = talloc_array(
+ comp->mem_ctx,
+ struct ace_condition_token,
+ alloc_size);
+ if (composite.data.composite.tokens == NULL) {
+ comp_error(comp, "allocation failure");
+ return false;
+ }
+
+ comp->target = composite.data.composite.tokens;
+ comp->target_len = &composite.data.composite.n_members;
+
+ /*
+ * in this loop we are looking for:
+ *
+ * a) possible whitespace.
+ * b) a comma (or terminating ')')
+ * c) more possible whitespace
+ * d) a literal, of the right type (checked after)
+ *
+ * Failures use a goto to reset comp->target, just in case we ever try
+ * continuing after error.
+ */
+ while (comp->offset < comp->length) {
+ uint8_t c;
+ ok = eat_whitespace(comp, false);
+ if (! ok) {
+ goto fail;
+ }
+ c = comp->sddl[comp->offset];
+ if (c == ')') {
+ break;
+ }
+ if (!first) {
+ if (c != ',') {
+ comp_error(comp,
+ "malformed resource attribute ACE "
+ "(expected comma)");
+ goto fail;
+ }
+ comp->offset++;
+
+ ok = eat_whitespace(comp, false);
+ if (! ok) {
+ goto fail;
+ }
+ }
+ first = false;
+ if (*comp->target_len >= alloc_size) {
+ comp_error(comp,
+ "Too many tokens in resource attribute ACE "
+ "(>= %"PRIu32" tokens)",
+ *comp->target_len);
+ goto fail;
+ }
+ switch(attr_type_char) {
+ case 'X':
+ ok = parse_ra_octet_string(comp);
+ break;
+ case 'S':
+ ok = parse_unicode(comp);
+ break;
+ case 'U':
+ ok = parse_uint(comp);
+ break;
+ case 'B':
+ ok = parse_bool(comp);
+ break;
+ case 'I':
+ ok = parse_int(comp);
+ break;
+ case 'D':
+ ok = parse_ra_sid(comp);
+ break;
+ default:
+ /* it's a mystery we got this far */
+ comp_error(comp,
+ "unknown attribute type T%c",
+ attr_type_char);
+ goto fail;
+ }
+ if (!ok) {
+ goto fail;
+ }
+
+ if (*comp->target_len == 0) {
+ goto fail;
+ }
+ }
+ comp->target = old_target;
+ comp->target_len = old_target_len;
+
+ /*
+ * If we only ended up collecting one token into the composite, we
+ * write that instead.
+ */
+ if (composite.data.composite.n_members == 1) {
+ ok = write_sddl_token(comp, composite.data.composite.tokens[0]);
+ talloc_free(composite.data.composite.tokens);
+ } else {
+ ok = write_sddl_token(comp, composite);
+ }
+ if (! ok) {
+ goto fail;
+ }
+
+ return true;
+fail:
+ comp->target = old_target;
+ comp->target_len = old_target_len;
+ TALLOC_FREE(composite.data.composite.tokens);
+ return false;
+}
+
+
+
+struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *sddl_decode_resource_attr (
+ TALLOC_CTX *mem_ctx,
+ const char *str,
+ size_t *length)
+{
+ /*
+ * Resource attribute ACEs define claims in object SACLs. They look like
+ *
+ * "(RA; «flags» ;;;;WD;( «attribute-data» ))"
+ *
+ * attribute-data = DQUOTE 1*attr-char2 DQUOTE "," \
+ * ( TI-attr / TU-attr / TS-attr / TD-attr / TX-attr / TB-attr )
+ * TI-attr = "TI" "," attr-flags *("," int-64)
+ * TU-attr = "TU" "," attr-flags *("," uint-64)
+ * TS-attr = "TS" "," attr-flags *("," char-string)
+ * TD-attr = "TD" "," attr-flags *("," sid-string)
+ * TX-attr = "TX" "," attr-flags *("," octet-string)
+ * TB-attr = "TB" "," attr-flags *("," ( "0" / "1" ) )
+ *
+ * and the data types are *mostly* parsed in the SDDL way,
+ * though there are significant differences for octet-strings.
+ *
+ * At this point we only have the "(«attribute-data»)".
+ *
+ * What we do is set up a conditional ACE compiler to be expecting a
+ * literal, and ask it to parse the strings between the commas. It's a
+ * hack.
+ */
+ bool ok;
+ struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim = NULL;
+ struct ace_condition_sddl_compiler_context comp = {};
+ char attr_type;
+ struct ace_condition_token *tok;
+ uint32_t flags;
+ size_t len;
+ struct ace_condition_unicode attr_name = {};
+
+ ok = init_compiler_context(mem_ctx,
+ &comp,
+ ACE_CONDITION_FLAG_ALLOW_DEVICE,
+ str,
+ 3,
+ 3);
+ if (!ok) {
+ return NULL;
+ }
+ if (comp.length < 6 || comp.length > CONDITIONAL_ACE_MAX_LENGTH) {
+ DBG_WARNING("invalid resource attribute: '%s'\n", str);
+ goto error;
+ }
+ /*
+ * Resource attribute ACEs list SIDs in a bare form "S-1-2-3", while
+ * conditional ACEs use a wrapper syntax "SID(S-1-2-3)". As almost
+ * everything is the same, we are reusing the conditional ACE parser,
+ * with a flag set to tell the SID parser which form to expect.
+ */
+
+ /* Most examples on the web have leading whitespace */
+ ok = eat_whitespace(&comp, false);
+ if (!ok) {
+ return NULL;
+ }
+ if (comp.sddl[comp.offset] != '(' ||
+ comp.sddl[comp.offset + 1] != '"') {
+ DBG_WARNING("invalid resource attribute -- expected '(\"'\n");
+ goto error;
+ }
+ comp.offset += 2;
+
+ /*
+ * Read the name. Here we are not reading a token into comp->program,
+ * just into a unicode blob.
+ */
+ len = read_attr2_string(&comp, &attr_name);
+
+ if (len == -1) {
+ DBG_WARNING("invalid resource attr name: %s\n", str);
+ goto error;
+ }
+ comp.offset += len;
+
+ ok = eat_whitespace(&comp, false);
+ if (comp.offset + 6 > comp.length) {
+ DBG_WARNING("invalid resource attribute (too short): '%s'\n",
+ str);
+ goto error;
+ }
+ /*
+ * now we have the name. Next comes '",«T[IUSDXB]»,' followed
+ * by the flags, which are a 32 bit number.
+ */
+ if (comp.sddl[comp.offset] != '"' ||
+ comp.sddl[comp.offset + 1] != ','||
+ comp.sddl[comp.offset + 2] != 'T') {
+ DBG_WARNING("expected '\",T[IUSDXB]' after attr name\n");
+ goto error;
+ }
+ attr_type = comp.sddl[comp.offset + 3];
+
+ if (comp.sddl[comp.offset + 4] != ',') {
+ DBG_WARNING("expected ',' after attr type\n");
+ goto error;
+ }
+ comp.offset += 5;
+ comp.state = SDDL_FLAG_EXPECTING_LITERAL;
+ ok = parse_literal(&comp, false);
+ if (!ok ||
+ comp.program->length != 1) {
+ DBG_WARNING("invalid attr flags: %s\n", str);
+ goto error;
+ }
+
+ tok = &comp.program->tokens[0];
+ if (tok->type != CONDITIONAL_ACE_TOKEN_INT64 ||
+ tok->data.int64.value < 0 ||
+ tok->data.int64.value > UINT32_MAX) {
+ DBG_WARNING("invalid attr flags (want 32 bit int): %s\n", str);
+ goto error;
+ }
+ flags = tok->data.int64.value;
+ if (flags & 0xff00) {
+ DBG_WARNING("invalid attr flags, "
+ "stepping on reserved 0xff00 range: %s\n",
+ str);
+ goto error;
+ }
+ if (comp.offset + 3 > comp.length) {
+ DBG_WARNING("invalid resource attribute (too short): '%s'\n",
+ str);
+ goto error;
+ }
+ if (comp.sddl[comp.offset] != ',') {
+ DBG_WARNING("invalid resource attribute ace\n");
+ goto error;
+ }
+ comp.offset++;
+
+ ok = parse_resource_attr_list(&comp, attr_type);
+ if (!ok || comp.program->length != 2) {
+ DBG_WARNING("invalid attribute type or value: T%c, %s\n",
+ attr_type, str);
+ goto error;
+ }
+ if (comp.sddl[comp.offset] != ')') {
+ DBG_WARNING("expected trailing ')'\n");
+ goto error;
+ }
+ comp.offset++;
+ *length = comp.offset;
+
+ ok = ace_token_to_claim_v1(mem_ctx,
+ attr_name.value,
+ &comp.program->tokens[1],
+ &claim,
+ flags);
+ if (!ok) {
+ goto error;
+ }
+ TALLOC_FREE(comp.program);
+ return claim;
+ error:
+ TALLOC_FREE(comp.program);
+ return NULL;
+}
+
+
+static bool write_resource_attr_from_token(struct sddl_write_context *ctx,
+ const struct ace_condition_token *tok)
+{
+ /*
+ * this is a helper for sddl_resource_attr_from_claim(),
+ * recursing into composites if necessary.
+ */
+ bool ok;
+ char *sid = NULL;
+ size_t i;
+ const struct ace_condition_composite *c = NULL;
+ switch (tok->type) {
+ case CONDITIONAL_ACE_TOKEN_INT64:
+ /*
+ * Note that this includes uint and bool claim types,
+ * but we don't check the validity of the ranges (0|1
+ * and >=0, respectively), rather we trust the claim
+ * to be self-consistent in this regard. Going the
+ * other way, string-to-claim, we do check.
+ */
+ return sddl_write_int(ctx, tok);
+
+ case CONDITIONAL_ACE_TOKEN_UNICODE:
+ return sddl_write_unicode(ctx, tok);
+
+ case CONDITIONAL_ACE_TOKEN_SID:
+ /* unlike conditional ACE, SID does not have a "SID()" wrapper. */
+ sid = sddl_encode_sid(ctx->mem_ctx, &tok->data.sid.sid, NULL);
+ if (sid == NULL) {
+ return false;
+ }
+ return sddl_write(ctx, sid);
+
+ case CONDITIONAL_ACE_TOKEN_OCTET_STRING:
+ return sddl_write_ra_octet_string(ctx, tok);
+
+ case CONDITIONAL_ACE_TOKEN_COMPOSITE:
+ /*
+ * write each token, separated by commas. If there
+ * were nested composites, this would flatten them,
+ * but that isn't really possible because the token we
+ * are dealing with came from a claim, which has no
+ * facility for nesting.
+ */
+ c = &tok->data.composite;
+ for(i = 0; i < c->n_members; i++) {
+ ok = write_resource_attr_from_token(ctx, &c->tokens[i]);
+ if (!ok) {
+ return false;
+ }
+ if (i != c->n_members - 1) {
+ ok = sddl_write(ctx, ",");
+ if (!ok) {
+ return false;
+ }
+ }
+ }
+ return true;
+ default:
+ /* We really really don't expect to get here */
+ return false;
+ }
+}
+
+char *sddl_resource_attr_from_claim(
+ TALLOC_CTX *mem_ctx,
+ const struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim)
+{
+ char *s = NULL;
+ char attr_type;
+ bool ok;
+ struct ace_condition_token tok = {};
+ struct sddl_write_context ctx = {};
+ TALLOC_CTX *tmp_ctx = NULL;
+ char *name = NULL;
+ size_t name_len;
+
+ switch(claim->value_type) {
+ case CLAIM_SECURITY_ATTRIBUTE_TYPE_INT64:
+ attr_type = 'I';
+ break;
+ case CLAIM_SECURITY_ATTRIBUTE_TYPE_UINT64:
+ attr_type = 'U';
+ break;
+ case CLAIM_SECURITY_ATTRIBUTE_TYPE_STRING:
+ attr_type = 'S';
+ break;
+ case CLAIM_SECURITY_ATTRIBUTE_TYPE_SID:
+ attr_type = 'D';
+ break;
+ case CLAIM_SECURITY_ATTRIBUTE_TYPE_BOOLEAN:
+ attr_type = 'B';
+ break;
+ case CLAIM_SECURITY_ATTRIBUTE_TYPE_OCTET_STRING:
+ attr_type = 'X';
+ break;
+ default:
+ return NULL;
+ }
+
+ tmp_ctx = talloc_new(mem_ctx);
+ if (tmp_ctx == NULL) {
+ return NULL;
+ }
+ ctx.mem_ctx = tmp_ctx;
+
+ ok = claim_v1_to_ace_composite_unchecked(tmp_ctx, claim, &tok);
+ if (!ok) {
+ TALLOC_FREE(tmp_ctx);
+ return NULL;
+ }
+
+ /* this will construct the proper string in ctx.sddl */
+ ok = write_resource_attr_from_token(&ctx, &tok);
+ if (!ok) {
+ TALLOC_FREE(tmp_ctx);
+ return NULL;
+ }
+
+ /* escape the claim name */
+ ok = sddl_encode_attr_name(tmp_ctx,
+ claim->name,
+ &name, &name_len);
+
+ if (!ok) {
+ TALLOC_FREE(tmp_ctx);
+ return NULL;
+ }
+
+ s = talloc_asprintf(mem_ctx,
+ "(\"%s\",T%c,0x%x,%s)",
+ name,
+ attr_type,
+ claim->flags,
+ ctx.sddl);
+ TALLOC_FREE(tmp_ctx);
+ return s;
+}
+
+
+struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *parse_sddl_literal_as_claim(
+ TALLOC_CTX *mem_ctx,
+ const char *name,
+ const char *str)
+{
+ /*
+ * For testing purposes (and possibly for client tools), we
+ * want to be able to create claim literals, and we might as
+ * well use the SDDL syntax. So we pretend to be parsing SDDL
+ * for one literal.
+ */
+ bool ok;
+ struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim = NULL;
+ struct ace_condition_sddl_compiler_context comp = {};
+
+ ok = init_compiler_context(mem_ctx,
+ &comp,
+ ACE_CONDITION_FLAG_ALLOW_DEVICE,
+ str,
+ 2,
+ 2);
+ if (!ok) {
+ return NULL;
+ }
+
+ comp.state = SDDL_FLAG_EXPECTING_LITERAL;
+ ok = parse_literal(&comp, false);
+
+ if (!ok) {
+ goto error;
+ }
+ if (comp.program->length != 1) {
+ goto error;
+ }
+
+ ok = ace_token_to_claim_v1(mem_ctx,
+ name,
+ &comp.program->tokens[0],
+ &claim,
+ 0);
+ if (!ok) {
+ goto error;
+ }
+ TALLOC_FREE(comp.program);
+ return claim;
+ error:
+ TALLOC_FREE(comp.program);
+ return NULL;
+}
diff --git a/libcli/security/secace.c b/libcli/security/secace.c
new file mode 100644
index 0000000..22bf217
--- /dev/null
+++ b/libcli/security/secace.c
@@ -0,0 +1,204 @@
+/*
+ * Unix SMB/Netbios implementation.
+ * struct security_ace handling functions
+ * Copyright (C) Andrew Tridgell 1992-1998,
+ * Copyright (C) Jeremy R. Allison 1995-2003.
+ * Copyright (C) Luke Kenneth Casson Leighton 1996-1998,
+ * Copyright (C) Paul Ashton 1997-1998.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "replace.h"
+#include "librpc/gen_ndr/ndr_security.h"
+#include "libcli/security/security.h"
+#include "lib/util/tsort.h"
+
+/**
+ * Check if ACE has OBJECT type.
+ */
+bool sec_ace_object(uint8_t type)
+{
+ if (type == SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT ||
+ type == SEC_ACE_TYPE_ACCESS_DENIED_OBJECT ||
+ type == SEC_ACE_TYPE_SYSTEM_AUDIT_OBJECT ||
+ type == SEC_ACE_TYPE_SYSTEM_ALARM_OBJECT ||
+ type == SEC_ACE_TYPE_ACCESS_ALLOWED_CALLBACK_OBJECT ||
+ type == SEC_ACE_TYPE_ACCESS_DENIED_CALLBACK_OBJECT ||
+ type == SEC_ACE_TYPE_SYSTEM_AUDIT_CALLBACK_OBJECT) {
+ /*
+ * MS-DTYP has a reserved value for
+ * SEC_ACE_TYPE_SYSTEM_ALARM_CALLBACK_OBJECT, but we
+ * don't assume that it will be an object ACE just
+ * because it sounds like one.
+ */
+ return true;
+ }
+ return false;
+}
+
+/**
+ * Check if ACE is a CALLBACK type, which means it will have a blob of data at
+ * the end.
+ */
+bool sec_ace_callback(uint8_t type)
+{
+ if (type == SEC_ACE_TYPE_ACCESS_ALLOWED_CALLBACK ||
+ type == SEC_ACE_TYPE_ACCESS_DENIED_CALLBACK ||
+ type == SEC_ACE_TYPE_ACCESS_ALLOWED_CALLBACK_OBJECT ||
+ type == SEC_ACE_TYPE_ACCESS_DENIED_CALLBACK_OBJECT ||
+ type == SEC_ACE_TYPE_SYSTEM_AUDIT_CALLBACK ||
+ type == SEC_ACE_TYPE_SYSTEM_AUDIT_CALLBACK_OBJECT) {
+ /*
+ * While SEC_ACE_TYPE_SYSTEM_ALARM_CALLBACK and
+ * SEC_ACE_TYPE_SYSTEM_ALARM_CALLBACK_OBJECT sound like
+ * callback types, they are reserved values in MS-DTYP,
+ * and their eventual use is not defined.
+ */
+ return true;
+ }
+ return false;
+}
+
+/**
+ * Check if an ACE type is resource attribute, which means it will
+ * have a blob of data at the end defining an attribute on the object.
+ * Resource attribute ACEs should only occur in SACLs.
+ */
+bool sec_ace_resource(uint8_t type)
+{
+ return type == SEC_ACE_TYPE_SYSTEM_RESOURCE_ATTRIBUTE;
+}
+
+bool sec_ace_has_extra_blob(uint8_t type)
+{
+ return sec_ace_callback(type) || sec_ace_resource(type);
+}
+
+
+/*******************************************************************
+ Sets up a struct security_ace structure.
+********************************************************************/
+
+void init_sec_ace(struct security_ace *t, const struct dom_sid *sid, enum security_ace_type type,
+ uint32_t mask, uint8_t flag)
+{
+ t->type = type;
+ t->flags = flag;
+ t->size = ndr_size_dom_sid(sid, 0) + 8;
+ t->access_mask = mask;
+
+ t->trustee = *sid;
+ t->coda.ignored.data = NULL;
+ t->coda.ignored.length = 0;
+}
+
+int nt_ace_inherit_comp(const struct security_ace *a1, const struct security_ace *a2)
+{
+ int a1_inh = a1->flags & SEC_ACE_FLAG_INHERITED_ACE;
+ int a2_inh = a2->flags & SEC_ACE_FLAG_INHERITED_ACE;
+
+ if (a1_inh == a2_inh)
+ return 0;
+
+ if (!a1_inh && a2_inh)
+ return -1;
+ return 1;
+}
+
+/*******************************************************************
+ Comparison function to apply the order explained below in a group.
+*******************************************************************/
+
+int nt_ace_canon_comp( const struct security_ace *a1, const struct security_ace *a2)
+{
+ if ((a1->type == SEC_ACE_TYPE_ACCESS_DENIED) &&
+ (a2->type != SEC_ACE_TYPE_ACCESS_DENIED))
+ return -1;
+
+ if ((a2->type == SEC_ACE_TYPE_ACCESS_DENIED) &&
+ (a1->type != SEC_ACE_TYPE_ACCESS_DENIED))
+ return 1;
+
+ /* Both access denied or access allowed. */
+
+ /* 1. ACEs that apply to the object itself */
+
+ if (!(a1->flags & SEC_ACE_FLAG_INHERIT_ONLY) &&
+ (a2->flags & SEC_ACE_FLAG_INHERIT_ONLY))
+ return -1;
+ else if (!(a2->flags & SEC_ACE_FLAG_INHERIT_ONLY) &&
+ (a1->flags & SEC_ACE_FLAG_INHERIT_ONLY))
+ return 1;
+
+ /* 2. ACEs that apply to a subobject of the object, such as
+ * a property set or property. */
+
+ if (a1->flags & (SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_OBJECT_INHERIT) &&
+ !(a2->flags & (SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_OBJECT_INHERIT)))
+ return -1;
+ else if (a2->flags & (SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_OBJECT_INHERIT) &&
+ !(a1->flags & (SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_OBJECT_INHERIT)))
+ return 1;
+
+ return 0;
+}
+
+/*******************************************************************
+ Functions to convert a SEC_DESC ACE DACL list into canonical order.
+ JRA.
+
+--- from http://msdn.microsoft.com/library/default.asp?url=/library/en-us/security/security/order_of_aces_in_a_dacl.asp
+
+The following describes the preferred order:
+
+ To ensure that noninherited ACEs have precedence over inherited ACEs,
+ place all noninherited ACEs in a group before any inherited ACEs.
+ This ordering ensures, for example, that a noninherited access-denied ACE
+ is enforced regardless of any inherited ACE that allows access.
+
+ Within the groups of noninherited ACEs and inherited ACEs, order ACEs according to ACE type, as the following shows:
+ 1. Access-denied ACEs that apply to the object itself
+ 2. Access-denied ACEs that apply to a subobject of the object, such as a property set or property
+ 3. Access-allowed ACEs that apply to the object itself
+ 4. Access-allowed ACEs that apply to a subobject of the object"
+
+********************************************************************/
+
+void dacl_sort_into_canonical_order(struct security_ace *srclist, unsigned int num_aces)
+{
+ unsigned int i;
+
+ if (!srclist || num_aces == 0)
+ return;
+
+ /* Sort so that non-inherited ACE's come first. */
+ TYPESAFE_QSORT(srclist, num_aces, nt_ace_inherit_comp);
+
+ /* Find the boundary between non-inherited ACEs. */
+ for (i = 0; i < num_aces; i++ ) {
+ struct security_ace *curr_ace = &srclist[i];
+
+ if (curr_ace->flags & SEC_ACE_FLAG_INHERITED_ACE)
+ break;
+ }
+
+ /* i now points at entry number of the first inherited ACE. */
+
+ /* Sort the non-inherited ACEs. */
+ TYPESAFE_QSORT(srclist, i, nt_ace_canon_comp);
+
+ /* Now sort the inherited ACEs. */
+ TYPESAFE_QSORT(&srclist[i], num_aces - i, nt_ace_canon_comp);
+}
diff --git a/libcli/security/secace.h b/libcli/security/secace.h
new file mode 100644
index 0000000..879c711
--- /dev/null
+++ b/libcli/security/secace.h
@@ -0,0 +1,40 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba utility functions
+
+ Copyright (C) 2009 Jelmer Vernooij <jelmer@samba.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _ACE_H_
+#define _ACE_H_
+
+#include "librpc/gen_ndr/security.h"
+#include "librpc/ndr/libndr.h"
+
+bool sec_ace_object(uint8_t type);
+size_t ndr_subcontext_size_of_ace_coda(const struct security_ace *ace, size_t ace_size, libndr_flags flags);
+bool sec_ace_callback(uint8_t type);
+bool sec_ace_resource(uint8_t type);
+bool sec_ace_has_extra_blob(uint8_t type);
+
+void init_sec_ace(struct security_ace *t, const struct dom_sid *sid, enum security_ace_type type,
+ uint32_t mask, uint8_t flag);
+int nt_ace_inherit_comp( const struct security_ace *a1, const struct security_ace *a2);
+int nt_ace_canon_comp( const struct security_ace *a1, const struct security_ace *a2);
+void dacl_sort_into_canonical_order(struct security_ace *srclist, unsigned int num_aces);
+
+#endif /*_ACE_H_*/
+
diff --git a/libcli/security/secacl.c b/libcli/security/secacl.c
new file mode 100644
index 0000000..6c92a2e
--- /dev/null
+++ b/libcli/security/secacl.c
@@ -0,0 +1,75 @@
+/*
+ * Unix SMB/Netbios implementation.
+ * SEC_ACL handling routines
+ * Copyright (C) Andrew Tridgell 1992-1998,
+ * Copyright (C) Jeremy R. Allison 1995-2003.
+ * Copyright (C) Luke Kenneth Casson Leighton 1996-1998,
+ * Copyright (C) Paul Ashton 1997-1998.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "replace.h"
+#include "librpc/gen_ndr/ndr_security.h"
+#include "libcli/security/secace.h"
+#include "libcli/security/secacl.h"
+
+#define SEC_ACL_HEADER_SIZE (2 * sizeof(uint16_t) + sizeof(uint32_t))
+
+/*******************************************************************
+ Create a SEC_ACL structure.
+********************************************************************/
+
+struct security_acl *make_sec_acl(
+ TALLOC_CTX *ctx,
+ enum security_acl_revision revision,
+ int num_aces,
+ const struct security_ace *ace_list)
+{
+ struct security_acl *dst;
+ int i;
+
+ dst = talloc(ctx, struct security_acl);
+ if (dst == NULL) {
+ return NULL;
+ }
+
+ dst->revision = revision;
+ dst->num_aces = num_aces;
+ dst->size = SEC_ACL_HEADER_SIZE;
+ dst->aces = NULL;
+
+ /* Now we need to return a non-NULL address for the ace list even
+ if the number of aces required is zero. This is because there
+ is a distinct difference between a NULL ace and an ace with zero
+ entries in it. This is achieved by checking that num_aces is a
+ positive number. */
+
+ if (num_aces == 0) {
+ return dst;
+ }
+
+ dst->aces = talloc_array(dst, struct security_ace, num_aces);
+ if (dst->aces == NULL) {
+ TALLOC_FREE(dst);
+ return NULL;
+ }
+
+ for (i = 0; i < num_aces; i++) {
+ dst->aces[i] = ace_list[i]; /* Structure copy. */
+ dst->size += ace_list[i].size;
+ }
+
+ return dst;
+}
diff --git a/libcli/security/secacl.h b/libcli/security/secacl.h
new file mode 100644
index 0000000..961e2b4
--- /dev/null
+++ b/libcli/security/secacl.h
@@ -0,0 +1,33 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba utility functions
+
+ Copyright (C) 2009 Jelmer Vernooij <jelmer@samba.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _SECACL_H_
+#define _SECACL_H_
+
+#include "librpc/gen_ndr/security.h"
+
+struct security_acl *make_sec_acl(
+ TALLOC_CTX *ctx,
+ enum security_acl_revision revision,
+ int num_aces,
+ const struct security_ace *ace_list);
+
+#endif /*_SECACL_H_*/
+
diff --git a/libcli/security/secdesc.c b/libcli/security/secdesc.c
new file mode 100644
index 0000000..cb8037c
--- /dev/null
+++ b/libcli/security/secdesc.c
@@ -0,0 +1,623 @@
+/*
+ * Unix SMB/Netbios implementation.
+ * SEC_DESC handling functions
+ * Copyright (C) Andrew Tridgell 1992-1998,
+ * Copyright (C) Jeremy R. Allison 1995-2003.
+ * Copyright (C) Luke Kenneth Casson Leighton 1996-1998,
+ * Copyright (C) Paul Ashton 1997-1998.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "replace.h"
+#include "lib/util/debug.h"
+#include "lib/util/fault.h"
+#include "librpc/gen_ndr/ndr_security.h"
+#include "libcli/security/security.h"
+
+/* Map generic permissions to file object specific permissions */
+
+const struct generic_mapping file_generic_mapping = {
+ FILE_GENERIC_READ,
+ FILE_GENERIC_WRITE,
+ FILE_GENERIC_EXECUTE,
+ FILE_GENERIC_ALL
+};
+
+/*******************************************************************
+ Given a security_descriptor return the sec_info.
+********************************************************************/
+
+uint32_t get_sec_info(const struct security_descriptor *sd)
+{
+ uint32_t sec_info = 0;
+
+ SMB_ASSERT(sd);
+
+ if (sd->owner_sid != NULL) {
+ sec_info |= SECINFO_OWNER;
+ }
+ if (sd->group_sid != NULL) {
+ sec_info |= SECINFO_GROUP;
+ }
+ if (sd->sacl != NULL) {
+ sec_info |= SECINFO_SACL;
+ }
+ if (sd->dacl != NULL) {
+ sec_info |= SECINFO_DACL;
+ }
+
+ if (sd->type & SEC_DESC_SACL_PROTECTED) {
+ sec_info |= SECINFO_PROTECTED_SACL;
+ } else if (sd->type & SEC_DESC_SACL_AUTO_INHERITED) {
+ sec_info |= SECINFO_UNPROTECTED_SACL;
+ }
+ if (sd->type & SEC_DESC_DACL_PROTECTED) {
+ sec_info |= SECINFO_PROTECTED_DACL;
+ } else if (sd->type & SEC_DESC_DACL_AUTO_INHERITED) {
+ sec_info |= SECINFO_UNPROTECTED_DACL;
+ }
+
+ return sec_info;
+}
+
+
+/*******************************************************************
+ Merge part of security descriptor old_sec in to the empty sections of
+ security descriptor new_sec.
+********************************************************************/
+
+struct sec_desc_buf *sec_desc_merge_buf(TALLOC_CTX *ctx, struct sec_desc_buf *new_sdb, struct sec_desc_buf *old_sdb)
+{
+ struct dom_sid *owner_sid, *group_sid;
+ struct sec_desc_buf *return_sdb;
+ struct security_acl *dacl, *sacl;
+ struct security_descriptor *psd = NULL;
+ uint16_t secdesc_type;
+ size_t secdesc_size;
+
+ /* Copy over owner and group sids. There seems to be no flag for
+ this so just check the pointer values. */
+
+ owner_sid = new_sdb->sd->owner_sid ? new_sdb->sd->owner_sid :
+ old_sdb->sd->owner_sid;
+
+ group_sid = new_sdb->sd->group_sid ? new_sdb->sd->group_sid :
+ old_sdb->sd->group_sid;
+
+ secdesc_type = new_sdb->sd->type;
+
+ /* Ignore changes to the system ACL. This has the effect of making
+ changes through the security tab audit button not sticking.
+ Perhaps in future Samba could implement these settings somehow. */
+
+ sacl = NULL;
+ secdesc_type &= ~SEC_DESC_SACL_PRESENT;
+
+ /* Copy across discretionary ACL */
+
+ if (secdesc_type & SEC_DESC_DACL_PRESENT) {
+ dacl = new_sdb->sd->dacl;
+ } else {
+ dacl = old_sdb->sd->dacl;
+ }
+
+ /* Create new security descriptor from bits */
+
+ psd = make_sec_desc(ctx, new_sdb->sd->revision, secdesc_type,
+ owner_sid, group_sid, sacl, dacl, &secdesc_size);
+
+ return_sdb = make_sec_desc_buf(ctx, secdesc_size, psd);
+
+ return(return_sdb);
+}
+
+struct security_descriptor *sec_desc_merge(TALLOC_CTX *ctx, struct security_descriptor *new_sdb, struct security_descriptor *old_sdb)
+{
+ struct dom_sid *owner_sid, *group_sid;
+ struct security_acl *dacl, *sacl;
+ struct security_descriptor *psd = NULL;
+ uint16_t secdesc_type;
+ size_t secdesc_size;
+
+ /* Copy over owner and group sids. There seems to be no flag for
+ this so just check the pointer values. */
+
+ owner_sid = new_sdb->owner_sid ? new_sdb->owner_sid :
+ old_sdb->owner_sid;
+
+ group_sid = new_sdb->group_sid ? new_sdb->group_sid :
+ old_sdb->group_sid;
+
+ secdesc_type = new_sdb->type;
+
+ /* Ignore changes to the system ACL. This has the effect of making
+ changes through the security tab audit button not sticking.
+ Perhaps in future Samba could implement these settings somehow. */
+
+ sacl = NULL;
+ secdesc_type &= ~SEC_DESC_SACL_PRESENT;
+
+ /* Copy across discretionary ACL */
+
+ if (secdesc_type & SEC_DESC_DACL_PRESENT) {
+ dacl = new_sdb->dacl;
+ } else {
+ dacl = old_sdb->dacl;
+ }
+
+ /* Create new security descriptor from bits */
+ psd = make_sec_desc(ctx, new_sdb->revision, secdesc_type,
+ owner_sid, group_sid, sacl, dacl, &secdesc_size);
+
+ return psd;
+}
+
+/*******************************************************************
+ Creates a struct security_descriptor structure
+********************************************************************/
+struct security_descriptor *make_sec_desc(TALLOC_CTX *ctx,
+ enum security_descriptor_revision revision,
+ uint16_t type,
+ const struct dom_sid *owner_sid, const struct dom_sid *grp_sid,
+ struct security_acl *sacl, struct security_acl *dacl, size_t *sd_size)
+{
+ struct security_descriptor *dst;
+
+ if (sd_size != NULL) {
+ *sd_size = 0;
+ }
+
+ dst = security_descriptor_initialise(ctx);
+ if (dst == NULL) {
+ return NULL;
+ }
+
+ dst->revision = revision;
+ dst->type = type;
+
+ if (sacl != NULL) {
+ dst->sacl = security_acl_dup(dst, sacl);
+ if (dst->sacl == NULL) {
+ goto err_sd_free;
+ }
+ dst->type |= SEC_DESC_SACL_PRESENT;
+ }
+
+ if (dacl != NULL) {
+ dst->dacl = security_acl_dup(dst, dacl);
+ if (dst->dacl == NULL) {
+ goto err_sd_free;
+ }
+ dst->type |= SEC_DESC_DACL_PRESENT;
+ }
+
+ if (owner_sid != NULL) {
+ dst->owner_sid = dom_sid_dup(dst, owner_sid);
+ if (dst->owner_sid == NULL) {
+ goto err_sd_free;
+ }
+ }
+
+ if (grp_sid != NULL) {
+ dst->group_sid = dom_sid_dup(dst, grp_sid);
+ if (dst->group_sid == NULL) {
+ goto err_sd_free;
+ }
+ }
+
+ if (sd_size != NULL) {
+ *sd_size = ndr_size_security_descriptor(dst, 0);
+ }
+
+ return dst;
+
+err_sd_free:
+ talloc_free(dst);
+ return NULL;
+}
+
+/*******************************************************************
+ Convert a secdesc into a byte stream
+********************************************************************/
+NTSTATUS marshall_sec_desc(TALLOC_CTX *mem_ctx,
+ const struct security_descriptor *secdesc,
+ uint8_t **data, size_t *len)
+{
+ DATA_BLOB blob;
+ enum ndr_err_code ndr_err;
+
+ ndr_err = ndr_push_struct_blob(
+ &blob, mem_ctx, secdesc,
+ (ndr_push_flags_fn_t)ndr_push_security_descriptor);
+
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ DEBUG(0, ("ndr_push_security_descriptor failed: %s\n",
+ ndr_errstr(ndr_err)));
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+ *data = blob.data;
+ *len = blob.length;
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ Convert a secdesc_buf into a byte stream
+********************************************************************/
+
+NTSTATUS marshall_sec_desc_buf(TALLOC_CTX *mem_ctx,
+ const struct sec_desc_buf *secdesc_buf,
+ uint8_t **data, size_t *len)
+{
+ DATA_BLOB blob;
+ enum ndr_err_code ndr_err;
+
+ ndr_err = ndr_push_struct_blob(
+ &blob, mem_ctx, secdesc_buf,
+ (ndr_push_flags_fn_t)ndr_push_sec_desc_buf);
+
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ DEBUG(0, ("ndr_push_sec_desc_buf failed: %s\n",
+ ndr_errstr(ndr_err)));
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+ *data = blob.data;
+ *len = blob.length;
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ Parse a byte stream into a secdesc
+********************************************************************/
+NTSTATUS unmarshall_sec_desc(TALLOC_CTX *mem_ctx, uint8_t *data, size_t len,
+ struct security_descriptor **psecdesc)
+{
+ DATA_BLOB blob;
+ enum ndr_err_code ndr_err;
+ struct security_descriptor *result;
+
+ if ((data == NULL) || (len == 0)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ result = talloc_zero(mem_ctx, struct security_descriptor);
+ if (result == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ blob = data_blob_const(data, len);
+
+ ndr_err = ndr_pull_struct_blob(&blob, result, result,
+ (ndr_pull_flags_fn_t)ndr_pull_security_descriptor);
+
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ DEBUG(0, ("ndr_pull_security_descriptor failed: %s\n",
+ ndr_errstr(ndr_err)));
+ TALLOC_FREE(result);
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+ *psecdesc = result;
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ Parse a byte stream into a sec_desc_buf
+********************************************************************/
+
+NTSTATUS unmarshall_sec_desc_buf(TALLOC_CTX *mem_ctx, uint8_t *data, size_t len,
+ struct sec_desc_buf **psecdesc_buf)
+{
+ DATA_BLOB blob;
+ enum ndr_err_code ndr_err;
+ struct sec_desc_buf *result;
+
+ if ((data == NULL) || (len == 0)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ result = talloc_zero(mem_ctx, struct sec_desc_buf);
+ if (result == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ blob = data_blob_const(data, len);
+
+ ndr_err = ndr_pull_struct_blob(&blob, result, result,
+ (ndr_pull_flags_fn_t)ndr_pull_sec_desc_buf);
+
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ DEBUG(0, ("ndr_pull_sec_desc_buf failed: %s\n",
+ ndr_errstr(ndr_err)));
+ TALLOC_FREE(result);
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+ *psecdesc_buf = result;
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ Creates a struct security_descriptor structure with typical defaults.
+********************************************************************/
+
+struct security_descriptor *make_standard_sec_desc(TALLOC_CTX *ctx, const struct dom_sid *owner_sid, const struct dom_sid *grp_sid,
+ struct security_acl *dacl, size_t *sd_size)
+{
+ return make_sec_desc(ctx, SECURITY_DESCRIPTOR_REVISION_1,
+ SEC_DESC_SELF_RELATIVE, owner_sid, grp_sid, NULL,
+ dacl, sd_size);
+}
+
+/*******************************************************************
+ Creates a struct sec_desc_buf structure.
+********************************************************************/
+
+struct sec_desc_buf *make_sec_desc_buf(TALLOC_CTX *ctx, size_t len, struct security_descriptor *sec_desc)
+{
+ struct sec_desc_buf *dst;
+
+ if((dst = talloc_zero(ctx, struct sec_desc_buf)) == NULL)
+ return NULL;
+
+ /* max buffer size (allocated size) */
+ dst->sd_size = (uint32_t)len;
+
+ if (sec_desc != NULL) {
+ dst->sd = security_descriptor_copy(ctx, sec_desc);
+ if (dst->sd == NULL) {
+ return NULL;
+ }
+ }
+
+ return dst;
+}
+
+/*
+ * Determine if an struct security_ace is inheritable
+ */
+
+static bool is_inheritable_ace(const struct security_ace *ace,
+ bool container)
+{
+ if (!container) {
+ return ((ace->flags & SEC_ACE_FLAG_OBJECT_INHERIT) != 0);
+ }
+
+ if (ace->flags & SEC_ACE_FLAG_CONTAINER_INHERIT) {
+ return true;
+ }
+
+ if ((ace->flags & SEC_ACE_FLAG_OBJECT_INHERIT) &&
+ !(ace->flags & SEC_ACE_FLAG_NO_PROPAGATE_INHERIT)) {
+ return true;
+ }
+
+ return false;
+}
+
+/*
+ * Does a security descriptor have any inheritable components for
+ * the newly created type ?
+ */
+
+bool sd_has_inheritable_components(const struct security_descriptor *parent_ctr, bool container)
+{
+ unsigned int i;
+ const struct security_acl *the_acl = parent_ctr->dacl;
+
+ if (the_acl == NULL) {
+ return false;
+ }
+
+ for (i = 0; i < the_acl->num_aces; i++) {
+ const struct security_ace *ace = &the_acl->aces[i];
+
+ if (is_inheritable_ace(ace, container)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/* Create a child security descriptor using another security descriptor as
+ the parent container. This child object can either be a container or
+ non-container object. */
+
+NTSTATUS se_create_child_secdesc(TALLOC_CTX *ctx,
+ struct security_descriptor **ppsd,
+ size_t *psize,
+ const struct security_descriptor *parent_ctr,
+ const struct dom_sid *owner_sid,
+ const struct dom_sid *group_sid,
+ bool container)
+{
+ struct security_acl *new_dacl = NULL, *the_acl = NULL;
+ struct security_ace *new_ace_list = NULL;
+ unsigned int new_ace_list_ndx = 0, i;
+ bool set_inherited_flags = (parent_ctr->type & SEC_DESC_DACL_AUTO_INHERITED);
+
+ *ppsd = NULL;
+ *psize = 0;
+
+ /* Currently we only process the dacl when creating the child. The
+ sacl should also be processed but this is left out as sacls are
+ not implemented in Samba at the moment.*/
+
+ the_acl = parent_ctr->dacl;
+
+ if (the_acl->num_aces) {
+ if (2*the_acl->num_aces < the_acl->num_aces) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (!(new_ace_list = talloc_array(ctx, struct security_ace,
+ 2*the_acl->num_aces))) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ } else {
+ new_ace_list = NULL;
+ }
+
+ for (i = 0; i < the_acl->num_aces; i++) {
+ const struct security_ace *ace = &the_acl->aces[i];
+ struct security_ace *new_ace = &new_ace_list[new_ace_list_ndx];
+ const struct dom_sid *ptrustee = &ace->trustee;
+ const struct dom_sid *creator = NULL;
+ uint8_t new_flags = ace->flags;
+ struct dom_sid_buf sidbuf1, sidbuf2;
+
+ if (!is_inheritable_ace(ace, container)) {
+ continue;
+ }
+
+ /* see the RAW-ACLS inheritance test for details on these rules */
+ if (!container) {
+ new_flags = 0;
+ } else {
+ /*
+ * We need to remove SEC_ACE_FLAG_INHERITED_ACE here
+ * if present because it should only be set if the
+ * parent has the AUTO_INHERITED bit set in the
+ * type/control field. If we don't it will slip through
+ * and create DACLs with incorrectly ordered ACEs
+ * when there are CREATOR_OWNER or CREATOR_GROUP
+ * ACEs.
+ */
+ new_flags &= ~(SEC_ACE_FLAG_INHERIT_ONLY
+ | SEC_ACE_FLAG_INHERITED_ACE);
+
+ if (!(new_flags & SEC_ACE_FLAG_CONTAINER_INHERIT)) {
+ new_flags |= SEC_ACE_FLAG_INHERIT_ONLY;
+ }
+ if (new_flags & SEC_ACE_FLAG_NO_PROPAGATE_INHERIT) {
+ new_flags = 0;
+ }
+ }
+
+ /* The CREATOR sids are special when inherited */
+ if (dom_sid_equal(ptrustee, &global_sid_Creator_Owner)) {
+ creator = &global_sid_Creator_Owner;
+ ptrustee = owner_sid;
+ } else if (dom_sid_equal(ptrustee, &global_sid_Creator_Group)) {
+ creator = &global_sid_Creator_Group;
+ ptrustee = group_sid;
+ }
+
+ if (creator && container &&
+ (new_flags & SEC_ACE_FLAG_CONTAINER_INHERIT)) {
+
+ /* First add the regular ACE entry. */
+ init_sec_ace(new_ace, ptrustee, ace->type,
+ ace->access_mask,
+ set_inherited_flags ? SEC_ACE_FLAG_INHERITED_ACE : 0);
+
+ DEBUG(5,("se_create_child_secdesc(): %s:%d/0x%02x/0x%08x"
+ " inherited as %s:%d/0x%02x/0x%08x\n",
+ dom_sid_str_buf(&ace->trustee, &sidbuf1),
+ ace->type, ace->flags, ace->access_mask,
+ dom_sid_str_buf(&new_ace->trustee, &sidbuf2),
+ new_ace->type, new_ace->flags,
+ new_ace->access_mask));
+
+ new_ace_list_ndx++;
+
+ /* Now add the extra creator ACE. */
+ new_ace = &new_ace_list[new_ace_list_ndx];
+
+ ptrustee = creator;
+ new_flags |= SEC_ACE_FLAG_INHERIT_ONLY;
+
+ } else if (container &&
+ !(ace->flags & SEC_ACE_FLAG_NO_PROPAGATE_INHERIT)) {
+ ptrustee = &ace->trustee;
+ }
+
+ init_sec_ace(new_ace, ptrustee, ace->type,
+ ace->access_mask, new_flags |
+ (set_inherited_flags ? SEC_ACE_FLAG_INHERITED_ACE : 0));
+
+ DEBUG(5, ("se_create_child_secdesc(): %s:%d/0x%02x/0x%08x "
+ " inherited as %s:%d/0x%02x/0x%08x\n",
+ dom_sid_str_buf(&ace->trustee, &sidbuf1),
+ ace->type, ace->flags, ace->access_mask,
+ dom_sid_str_buf(&new_ace->trustee, &sidbuf2),
+ new_ace->type, new_ace->flags,
+ new_ace->access_mask));
+
+ new_ace_list_ndx++;
+ }
+
+ /*
+ * remove duplicates
+ */
+ for (i=1; i < new_ace_list_ndx;) {
+ struct security_ace *ai = &new_ace_list[i];
+ unsigned int remaining, j;
+ bool remove_ace = false;
+
+ for (j=0; j < i; j++) {
+ struct security_ace *aj = &new_ace_list[j];
+
+ if (!security_ace_equal(ai, aj)) {
+ continue;
+ }
+
+ remove_ace = true;
+ break;
+ }
+
+ if (!remove_ace) {
+ i++;
+ continue;
+ }
+
+ new_ace_list_ndx--;
+ remaining = new_ace_list_ndx - i;
+ if (remaining == 0) {
+ ZERO_STRUCT(new_ace_list[i]);
+ continue;
+ }
+ memmove(&new_ace_list[i], &new_ace_list[i+1],
+ sizeof(new_ace_list[i]) * remaining);
+ }
+
+ /* Create child security descriptor to return */
+ if (new_ace_list_ndx) {
+ new_dacl = make_sec_acl(ctx,
+ NT4_ACL_REVISION,
+ new_ace_list_ndx,
+ new_ace_list);
+
+ if (!new_dacl) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ *ppsd = make_sec_desc(ctx,
+ SECURITY_DESCRIPTOR_REVISION_1,
+ SEC_DESC_SELF_RELATIVE|SEC_DESC_DACL_PRESENT|
+ (set_inherited_flags ? SEC_DESC_DACL_AUTO_INHERITED : 0),
+ owner_sid,
+ group_sid,
+ NULL,
+ new_dacl,
+ psize);
+ if (!*ppsd) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ return NT_STATUS_OK;
+}
diff --git a/libcli/security/secdesc.h b/libcli/security/secdesc.h
new file mode 100644
index 0000000..ca9376a
--- /dev/null
+++ b/libcli/security/secdesc.h
@@ -0,0 +1,96 @@
+/*
+ * Unix SMB/Netbios implementation.
+ * SEC_DESC handling functions
+ * Copyright (C) Andrew Tridgell 1992-1998,
+ * Copyright (C) Jeremy R. Allison 1995-2003.
+ * Copyright (C) Luke Kenneth Casson Leighton 1996-1998,
+ * Copyright (C) Paul Ashton 1997-1998.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _SECDESC_H_
+#define _SECDESC_H_
+
+/* The following definitions come from libcli/security/secdesc.c */
+#include "librpc/gen_ndr/security.h"
+
+/*******************************************************************
+ Given a security_descriptor return the sec_info.
+********************************************************************/
+uint32_t get_sec_info(const struct security_descriptor *sd);
+
+/*******************************************************************
+ Merge part of security descriptor old_sec in to the empty sections of
+ security descriptor new_sec.
+********************************************************************/
+struct sec_desc_buf *sec_desc_merge_buf(TALLOC_CTX *ctx, struct sec_desc_buf *new_sdb, struct sec_desc_buf *old_sdb);
+struct security_descriptor *sec_desc_merge(TALLOC_CTX *ctx, struct security_descriptor *new_sdb, struct security_descriptor *old_sdb);
+
+/*******************************************************************
+ Creates a struct security_descriptor structure
+********************************************************************/
+struct security_descriptor *make_sec_desc(TALLOC_CTX *ctx,
+ enum security_descriptor_revision revision,
+ uint16_t type,
+ const struct dom_sid *owner_sid, const struct dom_sid *grp_sid,
+ struct security_acl *sacl, struct security_acl *dacl, size_t *sd_size);
+
+/*******************************************************************
+ Convert a secdesc into a byte stream
+********************************************************************/
+NTSTATUS marshall_sec_desc(TALLOC_CTX *mem_ctx,
+ const struct security_descriptor *secdesc,
+ uint8_t **data, size_t *len);
+
+/*******************************************************************
+ Convert a secdesc_buf into a byte stream
+********************************************************************/
+NTSTATUS marshall_sec_desc_buf(TALLOC_CTX *mem_ctx,
+ const struct sec_desc_buf *secdesc_buf,
+ uint8_t **data, size_t *len);
+
+/*******************************************************************
+ Parse a byte stream into a secdesc
+********************************************************************/
+NTSTATUS unmarshall_sec_desc(TALLOC_CTX *mem_ctx, uint8_t *data, size_t len,
+ struct security_descriptor **psecdesc);
+
+/*******************************************************************
+ Parse a byte stream into a sec_desc_buf
+********************************************************************/
+NTSTATUS unmarshall_sec_desc_buf(TALLOC_CTX *mem_ctx, uint8_t *data, size_t len,
+ struct sec_desc_buf **psecdesc_buf);
+
+/*******************************************************************
+ Creates a struct security_descriptor structure with typical defaults.
+********************************************************************/
+struct security_descriptor *make_standard_sec_desc(TALLOC_CTX *ctx, const struct dom_sid *owner_sid, const struct dom_sid *grp_sid,
+ struct security_acl *dacl, size_t *sd_size);
+
+/*******************************************************************
+ Creates a struct sec_desc_buf structure.
+********************************************************************/
+struct sec_desc_buf *make_sec_desc_buf(TALLOC_CTX *ctx, size_t len, struct security_descriptor *sec_desc);
+
+bool sd_has_inheritable_components(const struct security_descriptor *parent_ctr, bool container);
+NTSTATUS se_create_child_secdesc(TALLOC_CTX *ctx,
+ struct security_descriptor **ppsd,
+ size_t *psize,
+ const struct security_descriptor *parent_ctr,
+ const struct dom_sid *owner_sid,
+ const struct dom_sid *group_sid,
+ bool container);
+
+#endif /* _SECDESC_H_ */
diff --git a/libcli/security/security.h b/libcli/security/security.h
new file mode 100644
index 0000000..a1c26ed
--- /dev/null
+++ b/libcli/security/security.h
@@ -0,0 +1,121 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Stefan Metzmacher 2006
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _LIBCLI_SECURITY_SECURITY_H_
+#define _LIBCLI_SECURITY_SECURITY_H_
+
+#include "lib/util/data_blob.h"
+#include "lib/util/time.h"
+
+#include "librpc/gen_ndr/security.h"
+
+/* File Specific access rights */
+#define FILE_READ_DATA SEC_FILE_READ_DATA
+#define FILE_WRITE_DATA SEC_FILE_WRITE_DATA
+#define FILE_APPEND_DATA SEC_FILE_APPEND_DATA
+#define FILE_READ_EA SEC_FILE_READ_EA /* File and directory */
+#define FILE_WRITE_EA SEC_FILE_WRITE_EA /* File and directory */
+#define FILE_EXECUTE SEC_FILE_EXECUTE
+#define FILE_READ_ATTRIBUTES SEC_FILE_READ_ATTRIBUTE
+#define FILE_WRITE_ATTRIBUTES SEC_FILE_WRITE_ATTRIBUTE
+
+#define FILE_ALL_ACCESS SEC_FILE_ALL
+
+/* Directory specific access rights */
+#define FILE_LIST_DIRECTORY SEC_DIR_LIST
+#define FILE_ADD_FILE SEC_DIR_ADD_FILE
+#define FILE_ADD_SUBDIRECTORY SEC_DIR_ADD_SUBDIR
+#define FILE_TRAVERSE SEC_DIR_TRAVERSE
+#define FILE_DELETE_CHILD SEC_DIR_DELETE_CHILD
+
+/* Generic access masks & rights. */
+#define DELETE_ACCESS SEC_STD_DELETE /* (1L<<16) */
+#define READ_CONTROL_ACCESS SEC_STD_READ_CONTROL /* (1L<<17) */
+#define WRITE_DAC_ACCESS SEC_STD_WRITE_DAC /* (1L<<18) */
+#define WRITE_OWNER_ACCESS SEC_STD_WRITE_OWNER /* (1L<<19) */
+#define SYNCHRONIZE_ACCESS SEC_STD_SYNCHRONIZE /* (1L<<20) */
+
+#define SYSTEM_SECURITY_ACCESS SEC_FLAG_SYSTEM_SECURITY /* (1L<<24) */
+#define MAXIMUM_ALLOWED_ACCESS SEC_FLAG_MAXIMUM_ALLOWED /* (1L<<25) */
+#define GENERIC_ALL_ACCESS SEC_GENERIC_ALL /* (1<<28) */
+#define GENERIC_EXECUTE_ACCESS SEC_GENERIC_EXECUTE /* (1<<29) */
+#define GENERIC_WRITE_ACCESS SEC_GENERIC_WRITE /* (1<<30) */
+#define GENERIC_READ_ACCESS ((unsigned)SEC_GENERIC_READ) /* (((unsigned)1)<<31) */
+
+/* Mapping of generic access rights for files to specific rights. */
+
+/* This maps to 0x1F01FF */
+#define FILE_GENERIC_ALL (STANDARD_RIGHTS_REQUIRED_ACCESS|\
+ SEC_STD_SYNCHRONIZE|\
+ FILE_ALL_ACCESS)
+
+/* This maps to 0x120089 */
+#define FILE_GENERIC_READ (STANDARD_RIGHTS_READ_ACCESS|\
+ FILE_READ_DATA|\
+ FILE_READ_ATTRIBUTES|\
+ FILE_READ_EA|\
+ SYNCHRONIZE_ACCESS)
+
+/* This maps to 0x120116 */
+#define FILE_GENERIC_WRITE (SEC_STD_READ_CONTROL|\
+ FILE_WRITE_DATA|\
+ FILE_WRITE_ATTRIBUTES|\
+ FILE_WRITE_EA|\
+ FILE_APPEND_DATA|\
+ SYNCHRONIZE_ACCESS)
+
+#define FILE_GENERIC_EXECUTE (STANDARD_RIGHTS_EXECUTE_ACCESS|\
+ FILE_READ_ATTRIBUTES|\
+ FILE_EXECUTE|\
+ SYNCHRONIZE_ACCESS)
+
+/* Share specific rights. */
+#define SHARE_ALL_ACCESS FILE_GENERIC_ALL
+#define SHARE_READ_ONLY (FILE_GENERIC_READ|FILE_EXECUTE)
+
+/**
+ * Remaining access is a bit mask of remaining access rights (bits) that have
+ * to be granted in order to fulfill the requested access.
+ *
+ * The GUID is optional, if specified it restricts this object tree and its
+ * children to object/attributes that inherits from this GUID.
+ * For DS access an object inherits from a GUID if one of its class has this GUID
+ * in the schemaIDGUID attribute.
+ */
+struct object_tree {
+ uint32_t remaining_access;
+ struct GUID guid;
+ int num_of_children;
+ struct object_tree *children;
+};
+
+/* Moved the dom_sid functions to the top level dir with manual proto header */
+#include "libcli/security/dom_sid.h"
+#include "libcli/security/secace.h"
+#include "libcli/security/secacl.h"
+#include "libcli/security/secdesc.h"
+#include "libcli/security/security_descriptor.h"
+#include "libcli/security/security_token.h"
+#include "libcli/security/sddl.h"
+#include "libcli/security/privileges.h"
+#include "libcli/security/access_check.h"
+#include "libcli/security/session.h"
+#include "libcli/security/display_sec.h"
+
+#endif
diff --git a/libcli/security/security_descriptor.c b/libcli/security/security_descriptor.c
new file mode 100644
index 0000000..9b9f16c
--- /dev/null
+++ b/libcli/security/security_descriptor.c
@@ -0,0 +1,908 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ security descriptor utility functions
+
+ Copyright (C) Andrew Tridgell 2004
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "replace.h"
+#include "libcli/security/security.h"
+#include "librpc/ndr/libndr.h"
+
+/*
+ return a blank security descriptor (no owners, dacl or sacl)
+*/
+struct security_descriptor *security_descriptor_initialise(TALLOC_CTX *mem_ctx)
+{
+ struct security_descriptor *sd;
+
+ sd = talloc(mem_ctx, struct security_descriptor);
+ if (!sd) {
+ return NULL;
+ }
+
+ sd->revision = SD_REVISION;
+ /* we mark as self relative, even though it isn't while it remains
+ a pointer in memory because this simplifies the ndr code later.
+ All SDs that we store/emit are in fact SELF_RELATIVE
+ */
+ sd->type = SEC_DESC_SELF_RELATIVE;
+
+ sd->owner_sid = NULL;
+ sd->group_sid = NULL;
+ sd->sacl = NULL;
+ sd->dacl = NULL;
+
+ return sd;
+}
+
+struct security_acl *security_acl_dup(TALLOC_CTX *mem_ctx,
+ const struct security_acl *oacl)
+{
+ struct security_acl *nacl;
+
+ if (oacl == NULL) {
+ return NULL;
+ }
+
+ if (oacl->aces == NULL && oacl->num_aces > 0) {
+ return NULL;
+ }
+
+ nacl = talloc (mem_ctx, struct security_acl);
+ if (nacl == NULL) {
+ return NULL;
+ }
+
+ *nacl = (struct security_acl) {
+ .revision = oacl->revision,
+ .size = oacl->size,
+ .num_aces = oacl->num_aces,
+ };
+ if (nacl->num_aces == 0) {
+ return nacl;
+ }
+
+ nacl->aces = (struct security_ace *)talloc_memdup (nacl, oacl->aces, sizeof(struct security_ace) * oacl->num_aces);
+ if (nacl->aces == NULL) {
+ goto failed;
+ }
+
+ return nacl;
+
+ failed:
+ talloc_free (nacl);
+ return NULL;
+
+}
+
+struct security_acl *security_acl_concatenate(TALLOC_CTX *mem_ctx,
+ const struct security_acl *acl1,
+ const struct security_acl *acl2)
+{
+ struct security_acl *nacl;
+ uint32_t i;
+
+ if (!acl1 && !acl2)
+ return NULL;
+
+ if (!acl1){
+ nacl = security_acl_dup(mem_ctx, acl2);
+ return nacl;
+ }
+
+ if (!acl2){
+ nacl = security_acl_dup(mem_ctx, acl1);
+ return nacl;
+ }
+
+ nacl = talloc (mem_ctx, struct security_acl);
+ if (nacl == NULL) {
+ return NULL;
+ }
+
+ nacl->revision = acl1->revision;
+ nacl->size = acl1->size + acl2->size;
+ nacl->num_aces = acl1->num_aces + acl2->num_aces;
+
+ if (nacl->num_aces == 0)
+ return nacl;
+
+ nacl->aces = (struct security_ace *)talloc_array (mem_ctx, struct security_ace, acl1->num_aces+acl2->num_aces);
+ if ((nacl->aces == NULL) && (nacl->num_aces > 0)) {
+ goto failed;
+ }
+
+ for (i = 0; i < acl1->num_aces; i++)
+ nacl->aces[i] = acl1->aces[i];
+ for (i = 0; i < acl2->num_aces; i++)
+ nacl->aces[i + acl1->num_aces] = acl2->aces[i];
+
+ return nacl;
+
+ failed:
+ talloc_free (nacl);
+ return NULL;
+
+}
+
+/*
+ talloc and copy a security descriptor
+ */
+struct security_descriptor *security_descriptor_copy(TALLOC_CTX *mem_ctx,
+ const struct security_descriptor *osd)
+{
+ struct security_descriptor *nsd;
+
+ nsd = talloc_zero(mem_ctx, struct security_descriptor);
+ if (!nsd) {
+ return NULL;
+ }
+
+ if (osd->owner_sid) {
+ nsd->owner_sid = dom_sid_dup(nsd, osd->owner_sid);
+ if (nsd->owner_sid == NULL) {
+ goto failed;
+ }
+ }
+
+ if (osd->group_sid) {
+ nsd->group_sid = dom_sid_dup(nsd, osd->group_sid);
+ if (nsd->group_sid == NULL) {
+ goto failed;
+ }
+ }
+
+ if (osd->sacl) {
+ nsd->sacl = security_acl_dup(nsd, osd->sacl);
+ if (nsd->sacl == NULL) {
+ goto failed;
+ }
+ }
+
+ if (osd->dacl) {
+ nsd->dacl = security_acl_dup(nsd, osd->dacl);
+ if (nsd->dacl == NULL) {
+ goto failed;
+ }
+ }
+
+ nsd->revision = osd->revision;
+ nsd->type = osd->type;
+
+ return nsd;
+
+ failed:
+ talloc_free(nsd);
+
+ return NULL;
+}
+
+NTSTATUS security_descriptor_for_client(TALLOC_CTX *mem_ctx,
+ const struct security_descriptor *ssd,
+ uint32_t sec_info,
+ uint32_t access_granted,
+ struct security_descriptor **_csd)
+{
+ struct security_descriptor *csd = NULL;
+ uint32_t access_required = 0;
+
+ *_csd = NULL;
+
+ if (sec_info & (SECINFO_OWNER|SECINFO_GROUP)) {
+ access_required |= SEC_STD_READ_CONTROL;
+ }
+ if (sec_info & SECINFO_DACL) {
+ access_required |= SEC_STD_READ_CONTROL;
+ }
+ if (sec_info & SECINFO_SACL) {
+ access_required |= SEC_FLAG_SYSTEM_SECURITY;
+ }
+
+ if (access_required & (~access_granted)) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ /*
+ * make a copy...
+ */
+ csd = security_descriptor_copy(mem_ctx, ssd);
+ if (csd == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /*
+ * ... and remove everything not wanted
+ */
+
+ if (!(sec_info & SECINFO_OWNER)) {
+ TALLOC_FREE(csd->owner_sid);
+ csd->type &= ~SEC_DESC_OWNER_DEFAULTED;
+ }
+ if (!(sec_info & SECINFO_GROUP)) {
+ TALLOC_FREE(csd->group_sid);
+ csd->type &= ~SEC_DESC_GROUP_DEFAULTED;
+ }
+ if (!(sec_info & SECINFO_DACL)) {
+ TALLOC_FREE(csd->dacl);
+ csd->type &= ~(
+ SEC_DESC_DACL_PRESENT |
+ SEC_DESC_DACL_DEFAULTED|
+ SEC_DESC_DACL_AUTO_INHERIT_REQ |
+ SEC_DESC_DACL_AUTO_INHERITED |
+ SEC_DESC_DACL_PROTECTED |
+ SEC_DESC_DACL_TRUSTED);
+ }
+ if (!(sec_info & SECINFO_SACL)) {
+ TALLOC_FREE(csd->sacl);
+ csd->type &= ~(
+ SEC_DESC_SACL_PRESENT |
+ SEC_DESC_SACL_DEFAULTED |
+ SEC_DESC_SACL_AUTO_INHERIT_REQ |
+ SEC_DESC_SACL_AUTO_INHERITED |
+ SEC_DESC_SACL_PROTECTED |
+ SEC_DESC_SERVER_SECURITY);
+ }
+
+ *_csd = csd;
+ return NT_STATUS_OK;
+}
+
+/*
+ add an ACE to an ACL of a security_descriptor
+*/
+
+static NTSTATUS security_descriptor_acl_add(struct security_descriptor *sd,
+ bool add_to_sacl,
+ const struct security_ace *ace,
+ ssize_t _idx)
+{
+ struct security_acl *acl = NULL;
+ ssize_t idx;
+
+ if (add_to_sacl) {
+ acl = sd->sacl;
+ } else {
+ acl = sd->dacl;
+ }
+
+ if (acl == NULL) {
+ acl = talloc(sd, struct security_acl);
+ if (acl == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ acl->revision = SECURITY_ACL_REVISION_NT4;
+ acl->size = 0;
+ acl->num_aces = 0;
+ acl->aces = NULL;
+ }
+
+ if (_idx < 0) {
+ idx = (acl->num_aces + 1) + _idx;
+ } else {
+ idx = _idx;
+ }
+
+ if (idx < 0) {
+ return NT_STATUS_ARRAY_BOUNDS_EXCEEDED;
+ } else if (idx > acl->num_aces) {
+ return NT_STATUS_ARRAY_BOUNDS_EXCEEDED;
+ }
+
+ acl->aces = talloc_realloc(acl, acl->aces,
+ struct security_ace, acl->num_aces+1);
+ if (acl->aces == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ARRAY_INSERT_ELEMENT(acl->aces, acl->num_aces, *ace, idx);
+ acl->num_aces++;
+
+ if (sec_ace_object(acl->aces[idx].type)) {
+ acl->revision = SECURITY_ACL_REVISION_ADS;
+ }
+
+ if (add_to_sacl) {
+ sd->sacl = acl;
+ sd->type |= SEC_DESC_SACL_PRESENT;
+ } else {
+ sd->dacl = acl;
+ sd->type |= SEC_DESC_DACL_PRESENT;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ add an ACE to the SACL of a security_descriptor
+*/
+
+NTSTATUS security_descriptor_sacl_add(struct security_descriptor *sd,
+ const struct security_ace *ace)
+{
+ return security_descriptor_acl_add(sd, true, ace, -1);
+}
+
+/*
+ insert an ACE at a given index to the SACL of a security_descriptor
+
+ idx can be negative, which means it's related to the new size from the
+ end, so -1 means the ace is appended at the end.
+*/
+
+NTSTATUS security_descriptor_sacl_insert(struct security_descriptor *sd,
+ const struct security_ace *ace,
+ ssize_t idx)
+{
+ return security_descriptor_acl_add(sd, true, ace, idx);
+}
+
+/*
+ add an ACE to the DACL of a security_descriptor
+*/
+
+NTSTATUS security_descriptor_dacl_add(struct security_descriptor *sd,
+ const struct security_ace *ace)
+{
+ return security_descriptor_acl_add(sd, false, ace, -1);
+}
+
+/*
+ insert an ACE at a given index to the DACL of a security_descriptor
+
+ idx can be negative, which means it's related to the new size from the
+ end, so -1 means the ace is appended at the end.
+*/
+
+NTSTATUS security_descriptor_dacl_insert(struct security_descriptor *sd,
+ const struct security_ace *ace,
+ ssize_t idx)
+{
+ return security_descriptor_acl_add(sd, false, ace, idx);
+}
+
+/*
+ delete the ACE corresponding to the given trustee in an ACL of a
+ security_descriptor
+*/
+
+static NTSTATUS security_descriptor_acl_del(struct security_descriptor *sd,
+ bool sacl_del,
+ const struct dom_sid *trustee)
+{
+ uint32_t i;
+ bool found = false;
+ struct security_acl *acl = NULL;
+
+ if (sacl_del) {
+ acl = sd->sacl;
+ } else {
+ acl = sd->dacl;
+ }
+
+ if (acl == NULL) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ /* there can be multiple ace's for one trustee */
+ for (i=0;i<acl->num_aces;i++) {
+ if (dom_sid_equal(trustee, &acl->aces[i].trustee)) {
+ ARRAY_DEL_ELEMENT(acl->aces, i, acl->num_aces);
+ acl->num_aces--;
+ if (acl->num_aces == 0) {
+ acl->aces = NULL;
+ }
+ found = true;
+ --i;
+ }
+ }
+
+ if (!found) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ acl->revision = SECURITY_ACL_REVISION_NT4;
+
+ for (i=0;i<acl->num_aces;i++) {
+ if (sec_ace_object(acl->aces[i].type)) {
+ acl->revision = SECURITY_ACL_REVISION_ADS;
+ break;
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ delete the ACE corresponding to the given trustee in the DACL of a
+ security_descriptor
+*/
+
+NTSTATUS security_descriptor_dacl_del(struct security_descriptor *sd,
+ const struct dom_sid *trustee)
+{
+ return security_descriptor_acl_del(sd, false, trustee);
+}
+
+/*
+ delete the ACE corresponding to the given trustee in the SACL of a
+ security_descriptor
+*/
+
+NTSTATUS security_descriptor_sacl_del(struct security_descriptor *sd,
+ const struct dom_sid *trustee)
+{
+ return security_descriptor_acl_del(sd, true, trustee);
+}
+
+/*
+ delete the given ACE in the SACL or DACL of a security_descriptor
+*/
+static NTSTATUS security_descriptor_acl_del_ace(struct security_descriptor *sd,
+ bool sacl_del,
+ const struct security_ace *ace)
+{
+ uint32_t i;
+ bool found = false;
+ struct security_acl *acl = NULL;
+
+ if (sacl_del) {
+ acl = sd->sacl;
+ } else {
+ acl = sd->dacl;
+ }
+
+ if (acl == NULL) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ for (i=0;i<acl->num_aces;i++) {
+ if (security_ace_equal(ace, &acl->aces[i])) {
+ ARRAY_DEL_ELEMENT(acl->aces, i, acl->num_aces);
+ acl->num_aces--;
+ if (acl->num_aces == 0) {
+ acl->aces = NULL;
+ }
+ found = true;
+ i--;
+ }
+ }
+
+ if (!found) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ acl->revision = SECURITY_ACL_REVISION_NT4;
+
+ for (i=0;i<acl->num_aces;i++) {
+ if (sec_ace_object(acl->aces[i].type)) {
+ acl->revision = SECURITY_ACL_REVISION_ADS;
+ break;
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS security_descriptor_dacl_del_ace(struct security_descriptor *sd,
+ const struct security_ace *ace)
+{
+ return security_descriptor_acl_del_ace(sd, false, ace);
+}
+
+NTSTATUS security_descriptor_sacl_del_ace(struct security_descriptor *sd,
+ const struct security_ace *ace)
+{
+ return security_descriptor_acl_del_ace(sd, true, ace);
+}
+
+static bool security_ace_object_equal(const struct security_ace_object *object1,
+ const struct security_ace_object *object2)
+{
+ if (object1 == object2) {
+ return true;
+ }
+ if ((object1 == NULL) || (object2 == NULL)) {
+ return false;
+ }
+ if (object1->flags != object2->flags) {
+ return false;
+ }
+ if (object1->flags & SEC_ACE_OBJECT_TYPE_PRESENT
+ && !GUID_equal(&object1->type.type, &object2->type.type)) {
+ return false;
+ }
+ if (object1->flags & SEC_ACE_INHERITED_OBJECT_TYPE_PRESENT
+ && !GUID_equal(&object1->inherited_type.inherited_type,
+ &object2->inherited_type.inherited_type)) {
+ return false;
+ }
+
+ return true;
+}
+
+
+static bool security_ace_claim_equal(const struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim1,
+ const struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim2)
+{
+ uint32_t i;
+
+ if (claim1 == claim2) {
+ return true;
+ }
+ if (claim1 == NULL || claim2 == NULL) {
+ return false;
+ }
+ if (claim1->name != NULL && claim2->name != NULL) {
+ if (strcasecmp_m(claim1->name, claim2->name) != 0) {
+ return false;
+ }
+ } else if (claim1->name != NULL || claim2->name != NULL) {
+ return false;
+ }
+ if (claim1->value_type != claim2->value_type) {
+ return false;
+ }
+ if (claim1->flags != claim2->flags) {
+ return false;
+ }
+ if (claim1->value_count != claim2->value_count) {
+ return false;
+ }
+ for (i = 0; i < claim1->value_count; ++i) {
+ const union claim_values *values1 = claim1->values;
+ const union claim_values *values2 = claim2->values;
+
+ switch (claim1->value_type) {
+ case CLAIM_SECURITY_ATTRIBUTE_TYPE_INT64:
+ if (values1[i].int_value != NULL && values2[i].int_value != NULL) {
+ if (*values1[i].int_value != *values2[i].int_value) {
+ return false;
+ }
+ } else if (values1[i].int_value != NULL || values2[i].int_value != NULL) {
+ return false;
+ }
+ break;
+ case CLAIM_SECURITY_ATTRIBUTE_TYPE_UINT64:
+ case CLAIM_SECURITY_ATTRIBUTE_TYPE_BOOLEAN:
+ if (values1[i].uint_value != NULL && values2[i].uint_value != NULL) {
+ if (*values1[i].uint_value != *values2[i].uint_value) {
+ return false;
+ }
+ } else if (values1[i].uint_value != NULL || values2[i].uint_value != NULL) {
+ return false;
+ }
+ break;
+ case CLAIM_SECURITY_ATTRIBUTE_TYPE_STRING:
+ if (values1[i].string_value != NULL && values2[i].string_value != NULL) {
+ if (strcasecmp_m(values1[i].string_value, values2[i].string_value) != 0) {
+ return false;
+ }
+ } else if (values1[i].string_value != NULL || values2[i].string_value != NULL) {
+ return false;
+ }
+ break;
+ case CLAIM_SECURITY_ATTRIBUTE_TYPE_SID:
+ if (values1[i].sid_value != NULL && values2[i].sid_value != NULL) {
+ if (data_blob_cmp(values1[i].sid_value, values2[i].sid_value) != 0) {
+ return false;
+ }
+ } else if (values1[i].sid_value != NULL || values2[i].sid_value != NULL) {
+ return false;
+ }
+ break;
+ case CLAIM_SECURITY_ATTRIBUTE_TYPE_OCTET_STRING:
+ if (values1[i].octet_value != NULL && values2[i].octet_value != NULL) {
+ if (data_blob_cmp(values1[i].octet_value, values2[i].octet_value) != 0) {
+ return false;
+ }
+ } else if (values1[i].octet_value != NULL || values2[i].octet_value != NULL) {
+ return false;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ return true;
+}
+
+/*
+ compare two security ace structures
+*/
+bool security_ace_equal(const struct security_ace *ace1,
+ const struct security_ace *ace2)
+{
+ if (ace1 == ace2) {
+ return true;
+ }
+ if ((ace1 == NULL) || (ace2 == NULL)) {
+ return false;
+ }
+ if (ace1->type != ace2->type) {
+ return false;
+ }
+ if (ace1->flags != ace2->flags) {
+ return false;
+ }
+ if (ace1->access_mask != ace2->access_mask) {
+ return false;
+ }
+ if (sec_ace_object(ace1->type) &&
+ !security_ace_object_equal(&ace1->object.object,
+ &ace2->object.object))
+ {
+ return false;
+ }
+ if (!dom_sid_equal(&ace1->trustee, &ace2->trustee)) {
+ return false;
+ }
+
+ if (sec_ace_callback(ace1->type)) {
+ if (data_blob_cmp(&ace1->coda.conditions, &ace2->coda.conditions) != 0) {
+ return false;
+ }
+ } else if (sec_ace_resource(ace1->type)) {
+ if (!security_ace_claim_equal(&ace1->coda.claim, &ace2->coda.claim)) {
+ return false;
+ }
+ } else {
+ /*
+ * Don’t require ace1->coda.ignored to match ace2->coda.ignored.
+ */
+ }
+
+ return true;
+}
+
+
+/*
+ compare two security acl structures
+*/
+bool security_acl_equal(const struct security_acl *acl1,
+ const struct security_acl *acl2)
+{
+ uint32_t i;
+
+ if (acl1 == acl2) return true;
+ if (!acl1 || !acl2) return false;
+ if (acl1->revision != acl2->revision) return false;
+ if (acl1->num_aces != acl2->num_aces) return false;
+
+ for (i=0;i<acl1->num_aces;i++) {
+ if (!security_ace_equal(&acl1->aces[i], &acl2->aces[i])) return false;
+ }
+ return true;
+}
+
+/*
+ compare two security descriptors.
+*/
+bool security_descriptor_equal(const struct security_descriptor *sd1,
+ const struct security_descriptor *sd2)
+{
+ if (sd1 == sd2) return true;
+ if (!sd1 || !sd2) return false;
+ if (sd1->revision != sd2->revision) return false;
+ if (sd1->type != sd2->type) return false;
+
+ if (!dom_sid_equal(sd1->owner_sid, sd2->owner_sid)) return false;
+ if (!dom_sid_equal(sd1->group_sid, sd2->group_sid)) return false;
+ if (!security_acl_equal(sd1->sacl, sd2->sacl)) return false;
+ if (!security_acl_equal(sd1->dacl, sd2->dacl)) return false;
+
+ return true;
+}
+
+/*
+ compare two security descriptors, but allow certain (missing) parts
+ to be masked out of the comparison
+*/
+bool security_descriptor_mask_equal(const struct security_descriptor *sd1,
+ const struct security_descriptor *sd2,
+ uint32_t mask)
+{
+ if (sd1 == sd2) return true;
+ if (!sd1 || !sd2) return false;
+ if (sd1->revision != sd2->revision) return false;
+ if ((sd1->type & mask) != (sd2->type & mask)) return false;
+
+ if (!dom_sid_equal(sd1->owner_sid, sd2->owner_sid)) return false;
+ if (!dom_sid_equal(sd1->group_sid, sd2->group_sid)) return false;
+ if ((mask & SEC_DESC_DACL_PRESENT) && !security_acl_equal(sd1->dacl, sd2->dacl)) return false;
+ if ((mask & SEC_DESC_SACL_PRESENT) && !security_acl_equal(sd1->sacl, sd2->sacl)) return false;
+
+ return true;
+}
+
+
+static struct security_descriptor *security_descriptor_appendv(struct security_descriptor *sd,
+ bool add_ace_to_sacl,
+ va_list ap)
+{
+ const char *sidstr;
+
+ while ((sidstr = va_arg(ap, const char *))) {
+ struct dom_sid *sid;
+ struct security_ace *ace = talloc_zero(sd, struct security_ace);
+ NTSTATUS status;
+
+ if (ace == NULL) {
+ talloc_free(sd);
+ return NULL;
+ }
+ ace->type = va_arg(ap, unsigned int);
+ ace->access_mask = va_arg(ap, unsigned int);
+ ace->flags = va_arg(ap, unsigned int);
+ sid = dom_sid_parse_talloc(ace, sidstr);
+ if (sid == NULL) {
+ talloc_free(sd);
+ return NULL;
+ }
+ ace->trustee = *sid;
+ if (add_ace_to_sacl) {
+ status = security_descriptor_sacl_add(sd, ace);
+ } else {
+ status = security_descriptor_dacl_add(sd, ace);
+ }
+ /* TODO: check: would talloc_free(ace) here be correct? */
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(sd);
+ return NULL;
+ }
+ }
+
+ return sd;
+}
+
+static struct security_descriptor *security_descriptor_createv(TALLOC_CTX *mem_ctx,
+ uint16_t sd_type,
+ const char *owner_sid,
+ const char *group_sid,
+ bool add_ace_to_sacl,
+ va_list ap)
+{
+ struct security_descriptor *sd;
+
+ sd = security_descriptor_initialise(mem_ctx);
+ if (sd == NULL) {
+ return NULL;
+ }
+
+ sd->type |= sd_type;
+
+ if (owner_sid) {
+ sd->owner_sid = dom_sid_parse_talloc(sd, owner_sid);
+ if (sd->owner_sid == NULL) {
+ talloc_free(sd);
+ return NULL;
+ }
+ }
+ if (group_sid) {
+ sd->group_sid = dom_sid_parse_talloc(sd, group_sid);
+ if (sd->group_sid == NULL) {
+ talloc_free(sd);
+ return NULL;
+ }
+ }
+
+ return security_descriptor_appendv(sd, add_ace_to_sacl, ap);
+}
+
+/*
+ create a security descriptor using string SIDs. This is used by the
+ torture code to allow the easy creation of complex ACLs
+ This is a varargs function. The list of DACL ACEs ends with a NULL sid.
+
+ Each ACE contains a set of 4 parameters:
+ SID, ACCESS_TYPE, MASK, FLAGS
+
+ a typical call would be:
+
+ sd = security_descriptor_dacl_create(mem_ctx,
+ sd_type_flags,
+ mysid,
+ mygroup,
+ SID_NT_AUTHENTICATED_USERS,
+ SEC_ACE_TYPE_ACCESS_ALLOWED,
+ SEC_FILE_ALL,
+ SEC_ACE_FLAG_OBJECT_INHERIT,
+ NULL);
+ that would create a sd with one DACL ACE
+*/
+
+struct security_descriptor *security_descriptor_dacl_create(TALLOC_CTX *mem_ctx,
+ uint16_t sd_type,
+ const char *owner_sid,
+ const char *group_sid,
+ ...)
+{
+ struct security_descriptor *sd = NULL;
+ va_list ap;
+ va_start(ap, group_sid);
+ sd = security_descriptor_createv(mem_ctx, sd_type, owner_sid,
+ group_sid, false, ap);
+ va_end(ap);
+
+ return sd;
+}
+
+struct security_descriptor *security_descriptor_sacl_create(TALLOC_CTX *mem_ctx,
+ uint16_t sd_type,
+ const char *owner_sid,
+ const char *group_sid,
+ ...)
+{
+ struct security_descriptor *sd = NULL;
+ va_list ap;
+ va_start(ap, group_sid);
+ sd = security_descriptor_createv(mem_ctx, sd_type, owner_sid,
+ group_sid, true, ap);
+ va_end(ap);
+
+ return sd;
+}
+
+struct security_ace *security_ace_create(TALLOC_CTX *mem_ctx,
+ const char *sid_str,
+ enum security_ace_type type,
+ uint32_t access_mask,
+ uint8_t flags)
+
+{
+ struct security_ace *ace;
+ bool ok;
+
+ ace = talloc_zero(mem_ctx, struct security_ace);
+ if (ace == NULL) {
+ return NULL;
+ }
+
+ ok = dom_sid_parse(sid_str, &ace->trustee);
+ if (!ok) {
+ talloc_free(ace);
+ return NULL;
+ }
+ ace->type = type;
+ ace->access_mask = access_mask;
+ ace->flags = flags;
+
+ return ace;
+}
+
+/*******************************************************************
+ Check for MS NFS ACEs in a sd
+*******************************************************************/
+bool security_descriptor_with_ms_nfs(const struct security_descriptor *psd)
+{
+ uint32_t i;
+
+ if (psd->dacl == NULL) {
+ return false;
+ }
+
+ for (i = 0; i < psd->dacl->num_aces; i++) {
+ if (dom_sid_compare_domain(
+ &global_sid_Unix_NFS,
+ &psd->dacl->aces[i].trustee) == 0) {
+ return true;
+ }
+ }
+
+ return false;
+}
diff --git a/libcli/security/security_descriptor.h b/libcli/security/security_descriptor.h
new file mode 100644
index 0000000..354bc17
--- /dev/null
+++ b/libcli/security/security_descriptor.h
@@ -0,0 +1,99 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba utility functions
+
+ Copyright (C) 2009 Jelmer Vernooij <jelmer@samba.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __SECURITY_DESCRIPTOR_H__
+#define __SECURITY_DESCRIPTOR_H__
+
+#include "librpc/gen_ndr/security.h"
+
+struct security_descriptor *security_descriptor_initialise(TALLOC_CTX *mem_ctx);
+struct security_descriptor *security_descriptor_copy(TALLOC_CTX *mem_ctx,
+ const struct security_descriptor *osd);
+NTSTATUS security_descriptor_for_client(TALLOC_CTX *mem_ctx,
+ const struct security_descriptor *ssd,
+ uint32_t sec_info,
+ uint32_t access_granted,
+ struct security_descriptor **_csd);
+NTSTATUS security_descriptor_sacl_add(struct security_descriptor *sd,
+ const struct security_ace *ace);
+NTSTATUS security_descriptor_sacl_insert(struct security_descriptor *sd,
+ const struct security_ace *ace,
+ ssize_t idx);
+NTSTATUS security_descriptor_dacl_add(struct security_descriptor *sd,
+ const struct security_ace *ace);
+NTSTATUS security_descriptor_dacl_insert(struct security_descriptor *sd,
+ const struct security_ace *ace,
+ ssize_t idx);
+NTSTATUS security_descriptor_dacl_del(struct security_descriptor *sd,
+ const struct dom_sid *trustee);
+NTSTATUS security_descriptor_sacl_del(struct security_descriptor *sd,
+ const struct dom_sid *trustee);
+NTSTATUS security_descriptor_dacl_del_ace(struct security_descriptor *sd,
+ const struct security_ace *ace);
+NTSTATUS security_descriptor_sacl_del_ace(struct security_descriptor *sd,
+ const struct security_ace *ace);
+bool security_ace_equal(const struct security_ace *ace1,
+ const struct security_ace *ace2);
+bool security_acl_equal(const struct security_acl *acl1,
+ const struct security_acl *acl2);
+bool security_descriptor_equal(const struct security_descriptor *sd1,
+ const struct security_descriptor *sd2);
+bool security_descriptor_mask_equal(const struct security_descriptor *sd1,
+ const struct security_descriptor *sd2,
+ uint32_t mask);
+struct security_descriptor *security_descriptor_dacl_create(TALLOC_CTX *mem_ctx,
+ uint16_t sd_type,
+ const char *owner_sid,
+ const char *group_sid,
+ ...);
+struct security_descriptor *security_descriptor_sacl_create(TALLOC_CTX *mem_ctx,
+ uint16_t sd_type,
+ const char *owner_sid,
+ const char *group_sid,
+ ...);
+struct security_ace *security_ace_create(TALLOC_CTX *mem_ctx,
+ const char *sid_str,
+ enum security_ace_type type,
+ uint32_t access_mask,
+ uint8_t flags);
+
+struct security_acl *security_acl_dup(TALLOC_CTX *mem_ctx,
+ const struct security_acl *oacl);
+
+struct security_acl *security_acl_concatenate(TALLOC_CTX *mem_ctx,
+ const struct security_acl *acl1,
+ const struct security_acl *acl2);
+
+uint32_t map_generic_rights_ds(uint32_t access_mask);
+
+struct security_descriptor *create_security_descriptor(TALLOC_CTX *mem_ctx,
+ struct security_descriptor *parent_sd,
+ struct security_descriptor *creator_sd,
+ bool is_container,
+ struct GUID *object_list,
+ uint32_t inherit_flags,
+ struct security_token *token,
+ struct dom_sid *default_owner, /* valid only for DS, NULL for the other RSs */
+ struct dom_sid *default_group, /* valid only for DS, NULL for the other RSs */
+ uint32_t (*generic_map)(uint32_t access_mask));
+
+bool security_descriptor_with_ms_nfs(const struct security_descriptor *psd);
+
+#endif /* __SECURITY_DESCRIPTOR_H__ */
diff --git a/libcli/security/security_token.c b/libcli/security/security_token.c
new file mode 100644
index 0000000..79de6e3
--- /dev/null
+++ b/libcli/security/security_token.c
@@ -0,0 +1,228 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ security descriptor utility functions
+
+ Copyright (C) Andrew Tridgell 2004
+ Copyright (C) Andrew Bartlett 2010
+ Copyright (C) Stefan Metzmacher 2005
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "replace.h"
+#include <talloc.h>
+#include "lib/util/talloc_stack.h"
+#include "lib/util/debug.h"
+#include "lib/util/fault.h"
+#include "libcli/security/security_token.h"
+#include "libcli/security/dom_sid.h"
+#include "libcli/security/privileges.h"
+#include "librpc/gen_ndr/ndr_security.h"
+#include "lib/util/talloc_stack.h"
+
+/*
+ return a blank security token
+*/
+struct security_token *security_token_initialise(TALLOC_CTX *mem_ctx,
+ enum claims_evaluation_control evaluate_claims)
+{
+ struct security_token *st = talloc_zero(
+ mem_ctx, struct security_token);
+ st->evaluate_claims = evaluate_claims;
+
+ return st;
+}
+
+/****************************************************************************
+ Duplicate a SID token.
+****************************************************************************/
+
+struct security_token *security_token_duplicate(TALLOC_CTX *mem_ctx, const struct security_token *src)
+{
+ TALLOC_CTX *frame = NULL;
+ struct security_token *dst = NULL;
+ DATA_BLOB blob;
+ enum ndr_err_code ndr_err;
+
+ if (src == NULL) {
+ return NULL;
+ }
+
+ frame = talloc_stackframe();
+
+ ndr_err = ndr_push_struct_blob(
+ &blob,
+ frame,
+ src,
+ (ndr_push_flags_fn_t)ndr_push_security_token);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ DBG_ERR("Failed to duplicate security_token ndr_push_security_token failed: %s\n",
+ ndr_errstr(ndr_err));
+ TALLOC_FREE(frame);
+ return NULL;
+ }
+
+ dst = talloc_zero(mem_ctx, struct security_token);
+ if (dst == NULL) {
+ DBG_ERR("talloc failed\n");
+ TALLOC_FREE(frame);
+ return NULL;
+ }
+
+ ndr_err = ndr_pull_struct_blob(
+ &blob,
+ dst,
+ dst,
+ (ndr_pull_flags_fn_t)ndr_pull_security_token);
+
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ DBG_ERR("Failed to duplicate security_token ndr_pull_security_token "
+ "failed: %s\n",
+ ndr_errstr(ndr_err));
+ TALLOC_FREE(dst);
+ TALLOC_FREE(frame);
+ return NULL;
+ }
+
+ TALLOC_FREE(frame);
+ return dst;
+}
+
+/****************************************************************************
+ prints a struct security_token to debug output.
+****************************************************************************/
+void security_token_debug(int dbg_class, int dbg_lev, const struct security_token *token)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ char *sids = NULL;
+ char *privs = NULL;
+ uint32_t i;
+
+ if (!token) {
+ DEBUGC(dbg_class, dbg_lev, ("Security token: (NULL)\n"));
+ TALLOC_FREE(frame);
+ return;
+ }
+
+ sids = talloc_asprintf(frame,
+ "Security token SIDs (%" PRIu32 "):\n",
+ token->num_sids);
+ for (i = 0; i < token->num_sids; i++) {
+ struct dom_sid_buf sidbuf;
+ talloc_asprintf_addbuf(
+ &sids,
+ " SID[%3" PRIu32 "]: %s\n",
+ i,
+ dom_sid_str_buf(&token->sids[i], &sidbuf));
+ }
+
+ privs = security_token_debug_privileges(frame, token);
+
+ DEBUGC(dbg_class,
+ dbg_lev,
+ ("%s%s", sids ? sids : "(NULL)", privs ? privs : "(NULL)"));
+
+ TALLOC_FREE(frame);
+}
+
+/* These really should be cheaper... */
+
+bool security_token_is_sid(const struct security_token *token, const struct dom_sid *sid)
+{
+ bool ret;
+
+ if (token->sids == NULL) {
+ return false;
+ }
+ ret = dom_sid_equal(&token->sids[PRIMARY_USER_SID_INDEX], sid);
+ return ret;
+}
+
+bool security_token_is_system(const struct security_token *token)
+{
+ return security_token_is_sid(token, &global_sid_System);
+}
+
+bool security_token_is_anonymous(const struct security_token *token)
+{
+ return security_token_is_sid(token, &global_sid_Anonymous);
+}
+
+bool security_token_has_sid(const struct security_token *token, const struct dom_sid *sid)
+{
+ uint32_t i;
+ for (i = 0; i < token->num_sids; i++) {
+ if (dom_sid_equal(&token->sids[i], sid)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+size_t security_token_count_flag_sids(const struct security_token *token,
+ const struct dom_sid *prefix_sid,
+ size_t num_flags,
+ const struct dom_sid **_flag_sid)
+{
+ const size_t num_auths_expected = prefix_sid->num_auths + num_flags;
+ const struct dom_sid *found = NULL;
+ size_t num = 0;
+ uint32_t i;
+
+ SMB_ASSERT(num_auths_expected <= ARRAY_SIZE(prefix_sid->sub_auths));
+
+ for (i = 0; i < token->num_sids; i++) {
+ const struct dom_sid *sid = &token->sids[i];
+ int cmp;
+
+ if (sid->num_auths != num_auths_expected) {
+ continue;
+ }
+
+ cmp = dom_sid_compare_domain(sid, prefix_sid);
+ if (cmp != 0) {
+ continue;
+ }
+
+ num += 1;
+ found = sid;
+ }
+
+ if ((num == 1) && (_flag_sid != NULL)) {
+ *_flag_sid = found;
+ }
+
+ return num;
+}
+
+bool security_token_has_builtin_guests(const struct security_token *token)
+{
+ return security_token_has_sid(token, &global_sid_Builtin_Guests);
+}
+
+bool security_token_has_builtin_administrators(const struct security_token *token)
+{
+ return security_token_has_sid(token, &global_sid_Builtin_Administrators);
+}
+
+bool security_token_has_nt_authenticated_users(const struct security_token *token)
+{
+ return security_token_has_sid(token, &global_sid_Authenticated_Users);
+}
+
+bool security_token_has_enterprise_dcs(const struct security_token *token)
+{
+ return security_token_has_sid(token, &global_sid_Enterprise_DCs);
+}
diff --git a/libcli/security/security_token.h b/libcli/security/security_token.h
new file mode 100644
index 0000000..8baf4cf
--- /dev/null
+++ b/libcli/security/security_token.h
@@ -0,0 +1,74 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ security descriptor utility functions
+
+ Copyright (C) Andrew Tridgell 2004
+ Copyright (C) Andrew Bartlett 2010
+ Copyright (C) Stefan Metzmacher 2005
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _LIBCLI_SECURITY_SECURITY_TOKEN_H_
+#define _LIBCLI_SECURITY_SECURITY_TOKEN_H_
+
+#include "replace.h"
+#include "lib/util/data_blob.h"
+#include "librpc/gen_ndr/security.h"
+
+#define PRIMARY_USER_SID_INDEX 0
+#define PRIMARY_GROUP_SID_INDEX 1
+#define PRIMARY_SIDS_COUNT 2
+#define REMAINING_SIDS_INDEX 2
+
+/*
+ return a blank security token
+*/
+struct security_token *security_token_initialise(TALLOC_CTX *mem_ctx,
+ enum claims_evaluation_control evaluate_claims);
+
+struct security_token *security_token_duplicate(TALLOC_CTX *mem_ctx, const struct security_token *src);
+
+/****************************************************************************
+ prints a struct security_token to debug output.
+****************************************************************************/
+void security_token_debug(int dbg_class, int dbg_lev, const struct security_token *token);
+
+bool security_token_is_sid(const struct security_token *token, const struct dom_sid *sid);
+
+bool security_token_is_system(const struct security_token *token);
+
+bool security_token_is_anonymous(const struct security_token *token);
+
+bool security_token_has_sid(const struct security_token *token, const struct dom_sid *sid);
+
+/*
+ * Return any of the domain sids found in the token matching "domain"
+ * in _domain_sid, makes most sense if you just found one.
+ */
+size_t security_token_count_flag_sids(const struct security_token *token,
+ const struct dom_sid *prefix_sid,
+ size_t num_flags,
+ const struct dom_sid **_flag_sid);
+
+bool security_token_has_builtin_guests(const struct security_token *token);
+
+bool security_token_has_builtin_administrators(const struct security_token *token);
+
+bool security_token_has_nt_authenticated_users(const struct security_token *token);
+
+bool security_token_has_enterprise_dcs(const struct security_token *token);
+
+#endif
diff --git a/libcli/security/session.c b/libcli/security/session.c
new file mode 100644
index 0000000..8db341d
--- /dev/null
+++ b/libcli/security/session.c
@@ -0,0 +1,74 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ session_info utility functions
+
+ Copyright (C) Andrew Bartlett 2008-2010
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "replace.h"
+#include "libcli/security/security.h"
+#include "libcli/util/werror.h"
+#include "librpc/gen_ndr/auth.h"
+
+enum security_user_level security_session_user_level(struct auth_session_info *session_info,
+ const struct dom_sid *domain_sid)
+{
+ struct security_token *token = NULL;
+ bool authenticated = false;
+ bool guest = false;
+
+ if (!session_info) {
+ return SECURITY_ANONYMOUS;
+ }
+ token = session_info->security_token;
+
+ if (security_token_is_system(token)) {
+ return SECURITY_SYSTEM;
+ }
+
+ if (security_token_is_anonymous(token)) {
+ return SECURITY_ANONYMOUS;
+ }
+
+ authenticated = security_token_has_nt_authenticated_users(token);
+ guest = security_token_has_builtin_guests(token);
+ if (!authenticated) {
+ if (guest) {
+ return SECURITY_GUEST;
+ }
+ return SECURITY_ANONYMOUS;
+ }
+
+ if (security_token_has_builtin_administrators(token)) {
+ return SECURITY_ADMINISTRATOR;
+ }
+
+ if (domain_sid) {
+ struct dom_sid rodc_dcs = { .num_auths = 0 };
+ sid_compose(&rodc_dcs, domain_sid, DOMAIN_RID_READONLY_DCS);
+
+ if (security_token_has_sid(token, &rodc_dcs)) {
+ return SECURITY_RO_DOMAIN_CONTROLLER;
+ }
+ }
+
+ if (security_token_has_enterprise_dcs(token)) {
+ return SECURITY_DOMAIN_CONTROLLER;
+ }
+
+ return SECURITY_USER;
+}
diff --git a/libcli/security/session.h b/libcli/security/session.h
new file mode 100644
index 0000000..31e950e
--- /dev/null
+++ b/libcli/security/session.h
@@ -0,0 +1,44 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ session_info utility functions
+
+ Copyright (C) Andrew Bartlett 2008-2010
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _LIBCLI_SECURITY_SESSION_H_
+#define _LIBCLI_SECURITY_SESSION_H_
+
+enum security_user_level {
+ SECURITY_ANONYMOUS = 0,
+ SECURITY_GUEST = 1,
+ SECURITY_USER = 10,
+ SECURITY_RO_DOMAIN_CONTROLLER = 20,
+ SECURITY_DOMAIN_CONTROLLER = 30,
+ SECURITY_ADMINISTRATOR = 40,
+ SECURITY_SYSTEM = 50
+};
+
+struct cli_credentials;
+struct security_token;
+struct auth_user_info;
+struct auth_user_info_torture;
+struct auth_session_info;
+
+enum security_user_level security_session_user_level(struct auth_session_info *session_info,
+ const struct dom_sid *domain_sid);
+
+#endif
diff --git a/libcli/security/tests/data/conditional_aces.txt b/libcli/security/tests/data/conditional_aces.txt
new file mode 100644
index 0000000..cf7d7a9
--- /dev/null
+++ b/libcli/security/tests/data/conditional_aces.txt
@@ -0,0 +1,83 @@
+D:(XD;;CC;;;S-1-2-3;(@User.Title == @User.Title)) -> D:(XD;;CC;;;S-1-2-3;(@USER.Title == @USER.Title))
+D:(XA;;FX;;;S-1-1-0;(@User.Title == "PM")) -> D:(XA;;FX;;;WD;(@USER.Title == "PM"))
+D:(D;OICI;GA;;;BG)(D;OICI;GA;;;AN)(A;OICI;GRGWGX;;;AU)(XA;;FX;;;S-1-1-0;(@User.title == "perambuator"))(A;OICI;GA;;;BA) -> D:(D;OICI;GA;;;BG)(D;OICI;GA;;;AN)(A;OICI;GXGWGR;;;AU)(XA;;FX;;;WD;(@USER.title == "perambuator"))(A;OICI;GA;;;BA)
+O:SYG:SYD:(XA;OICI;CR;;;WD;(@USER.ad://ext/AuthenticationSilo == "siloname")) -> O:SYG:SYD:(XA;OICI;CR;;;WD;(@USER.ad://ext/AuthenticationSilo == "siloname"))
+D:(D;OICI;GA;;;BG)(D;OICI;GA;;;AN)(A;OICI;GRGWGX;;;AU)(XA;;FX;;;S-1-1-0;(@User.Title == ""))(A;OICI;GA;;;BA) -> D:(D;OICI;GA;;;BG)(D;OICI;GA;;;AN)(A;OICI;GXGWGR;;;AU)(XA;;FX;;;WD;(@USER.Title == ""))(A;OICI;GA;;;BA)
+D:(XA;;CC;;;S-1-2-3;(@User.Title != @User.Title)) -> D:(XA;;CC;;;S-1-2-3;(@USER.Title != @USER.Title))
+D:(XD;;FX;;;S-1-1-0;(@User.Title != "PM")) -> D:(XD;;FX;;;WD;(@USER.Title != "PM"))
+D:(XD;;FX;;;S-1-1-0;(@User.Project Any_of @Resource.Project)) -> D:(XD;;FX;;;WD;(@USER.Project Any_of @RESOURCE.Project))
+D:AI(XA;OICI;FA;;;WD;(OctetStringType==##1#2#3##)) -> D:AI(XA;OICI;FA;;;WD;(OctetStringType == #01020300))
+D:AI(XA;OICI;FA;;;WD;(OctetStringType==#01020300)) -> D:AI(XA;OICI;FA;;;WD;(OctetStringType == #01020300))
+D:(XA;;FR;;;S-1-1-0;(Member_of {SID(S-1-999-777-7-7), SID(BO)} && @Device.Bitlocker)) -> D:(XA;;FR;;;WD;((Member_of {SID(S-1-999-777-7-7), SID(BO)}) && (@DEVICE.Bitlocker)))
+D:(XA;;FX;;;S-1-1-0;(@User.Title=="PM" && (@User.Division=="Finance" || @User.Division =="Sales"))) -> D:(XA;;FX;;;WD;((@USER.Title == "PM") && ((@USER.Division == "Finance") || (@USER.Division == "Sales"))))
+D:(D;OICI;GA;;;BG)(D;OICI;GA;;;AN)(A;OICI;GRGWGX;;;AU)(XA;;FX;;;S-1-1-0;(@User.Title == ""))(A;OICI;GA;;;BA) -> D:(D;OICI;GA;;;BG)(D;OICI;GA;;;AN)(A;OICI;GXGWGR;;;AU)(XA;;FX;;;WD;(@USER.Title == ""))(A;OICI;GA;;;BA)
+D:(XA;;FX;;;S-1-1-0;(@User.Project Any_of @Resource.Project)) -> D:(XA;;FX;;;WD;(@USER.Project Any_of @RESOURCE.Project))
+D:(XA;;0x1f;;;AA;(@Device.colour == {"orange", "blue"})) -> D:(XA;;CCDCLCSWRP;;;AA;(@DEVICE.colour == {"orange", "blue"}))
+D:(XA;;0x1f;;;AA;(@Device.legs >= 1)) -> D:(XA;;CCDCLCSWRP;;;AA;(@DEVICE.legs >= 1))
+D:(XA;;0x1f;;;AA;(@Device.legs == 1)) -> D:(XA;;CCDCLCSWRP;;;AA;(@DEVICE.legs == 1))
+D:(XA;;0x1f;;;AA;(Device_Member_of{SID(BA)} && Member_of{SID(WD)})) -> D:(XA;;CCDCLCSWRP;;;AA;((Device_Member_of {SID(BA)}) && (Member_of {SID(WD)})))
+D:(XA;;0x1f;;;AA;(Device_Member_of{SID(AA)} || Member_of{SID(WD)})) -> D:(XA;;CCDCLCSWRP;;;AA;((Device_Member_of {SID(AA)}) || (Member_of {SID(WD)})))
+D:(XA;;0x1f;;;AA;(Device_Member_of{SID(BG)} || Member_of{SID(WR)})) -> D:(XA;;CCDCLCSWRP;;;AA;((Device_Member_of {SID(BG)}) || (Member_of {SID(WR)})))
+D:(XA;;0x1ff;;;S-1-222-333;(Member_of_Any{SID(S-1-222-333)})) -> D:(XA;;CCDCLCSWRPWPDTLOCR;;;S-1-222-333;(Member_of_any {SID(S-1-222-333)}))
+O:S-1-1-0D:(XA;;0x1ff;;;WD;(Member_of{SID(S-1-1-0)})) -> O:WDD:(XA;;CCDCLCSWRPWPDTLOCR;;;WD;(Member_of {SID(WD)}))
+O:S-1-1-0D:(XA;;0x1ff;;;WD;(Member_of SID(S-1-1-0))) -> O:WDD:(XA;;CCDCLCSWRPWPDTLOCR;;;WD;(Member_of SID(WD)))
+O:S-1-1-0D:(XA;;0x1ff;;;WD;(Member_of(SID(S-1-1-0)))) -> O:WDD:(XA;;CCDCLCSWRPWPDTLOCR;;;WD;(Member_of SID(WD)))
+O:S-1-1-0D:(XA;;0x1ff;;;WD;(Member_of_Any SID(S-1-1-0))) -> O:WDD:(XA;;CCDCLCSWRPWPDTLOCR;;;WD;(Member_of_any SID(WD)))
+O:S-1-1-0D:(XA;;0x1;;;WD;(Member_of_Any{SID(AS),SID(WD)})) -> O:WDD:(XA;;CC;;;WD;(Member_of_any {SID(AS), SID(WD)}))
+O:S-1-1-0D:(XA;;0x1ff;;;WD;(Member_of_Any{SID(S-1-1-0), SID(S-1-222-333)})) -> O:WDD:(XA;;CCDCLCSWRPWPDTLOCR;;;WD;(Member_of_any {SID(WD), SID(S-1-222-333)}))
+O:S-1-1-0D:(XA;;0x1ff;;;WD;(Member_of_Any{SID(S-1-1-334), SID(S-1-222-333)})) -> O:WDD:(XA;;CCDCLCSWRPWPDTLOCR;;;WD;(Member_of_any {SID(S-1-1-334), SID(S-1-222-333)}))
+D:(XA;;0x1ff;;;WD;(Member_of_Any{SID(S-1-222-333)})) -> D:(XA;;CCDCLCSWRPWPDTLOCR;;;WD;(Member_of_any {SID(S-1-222-333)}))
+D:(XA;;0x1f;;;AA;(Member_of{SID(S-1-77-88-99)})) -> D:(XA;;CCDCLCSWRP;;;AA;(Member_of {SID(S-1-77-88-99)}))
+D:(XA;;0x1f;;;AA;(Device_Member_of{SID(BA)})) -> D:(XA;;CCDCLCSWRP;;;AA;(Device_Member_of {SID(BA)}))
+D:(XA;;0x1f;;;AA;(!(! (Member_of{SID(AA)})))) -> D:(XA;;CCDCLCSWRP;;;AA;(!(!(Member_of {SID(AA)}))))
+D:(XA;;0x1f;;;AA;(!(!(!(!(!(! (Member_of{SID(AA)})))))))) -> D:(XA;;CCDCLCSWRP;;;AA;(!(!(!(!(!(!(Member_of {SID(AA)}))))))))
+D:(XA;;0x1f;;;AA;(@Device.colour Contains @Resource.colour))S:(RA;;;;;WD;("colour",TS,0,"blue")) -> D:(XA;;CCDCLCSWRP;;;AA;(@DEVICE.colour Contains @RESOURCE.colour))S:(RA;;;;;WD;("colour",TS,0x0,"blue"))
+D:(XA;;0x1f;;;AA;(@Device.colour == @Resource.colour))S:(RA;;;;;WD;("colour",TS,0,"blue")) -> D:(XA;;CCDCLCSWRP;;;AA;(@DEVICE.colour == @RESOURCE.colour))S:(RA;;;;;WD;("colour",TS,0x0,"blue"))
+D:(XA;;0x1f;;;AA;(@Device.colour == "blue")) -> D:(XA;;CCDCLCSWRP;;;AA;(@DEVICE.colour == "blue"))
+D:(XA;;0x1f;;;AA;(@User.colour == @Device.colour)) -> D:(XA;;CCDCLCSWRP;;;AA;(@USER.colour == @DEVICE.colour))
+D:(XA;;0x1f;;;AA;(@Device.colour Contains @Resource.colour))S:(RA;;;;;WD;("colour",TS,0,"blue", "red")) -> D:(XA;;CCDCLCSWRP;;;AA;(@DEVICE.colour Contains @RESOURCE.colour))S:(RA;;;;;WD;("colour",TS,0x0,"blue","red"))
+O:S-1-1-0D:(XA;;0x1ff;;;WD;(member_of{SID(S-1-1-0)})) -> O:WDD:(XA;;CCDCLCSWRPWPDTLOCR;;;WD;(Member_of {SID(WD)}))
+O:S-1-1-0D:(XA;;0x1ff;;;WD;(mEMBER_of{SID(S-1-1-0)})) -> O:WDD:(XA;;CCDCLCSWRPWPDTLOCR;;;WD;(Member_of {SID(WD)}))
+O:S-1-1-0D:(XA;;0x1ff;;;WD;(Member_Of{SID(S-1-1-0)})) -> O:WDD:(XA;;CCDCLCSWRPWPDTLOCR;;;WD;(Member_of {SID(WD)}))
+O:S-1-1-0D:(XA;;0x0;;;WD;(Member_Of SID(S-1-1-0))) -> O:WDD:(XA;;;;;WD;(Member_of SID(WD)))
+O:S-1-1-0D:(XA;;0;;;WD;(Member_Of SID(S-1-1-0))) -> O:WDD:(XA;;;;;WD;(Member_of SID(WD)))
+O:S-1-1-0D:(XA;;;;;WD;(Member_Of SID(S-1-1-0))) -> O:WDD:(XA;;;;;WD;(Member_of SID(WD)))
+D:(XD;;FX;;;WD;(@USER.Project Any_of "pink"))
+D:(XD;;FX;;;WD;(@USER.Project Any_of 1))
+D:(XD;;FX;;;WD;(!(@USER.Project Not_Any_of 1)))
+D:(XA;;0x1f;;;AA;(a == 1)) -> D:(XA;;CCDCLCSWRP;;;AA;(a == 1))
+D:(XA;;CC;;;AA;(@User.a == @User.b)) -> D:(XA;;CC;;;AA;(@USER.a == @USER.b))
+D:(XA;;CC;;;AA;(a == @User.a)) -> D:(XA;;CC;;;AA;(a == @USER.a))
+
+D:(XA;;FR;;;S-1-1-0;(@USER.A && @Device.B && @USER.C)) -> D:(XA;;FR;;;WD;(((@USER.A) && (@DEVICE.B)) && (@USER.C)))
+D:(XA;;FR;;;S-1-1-0;(@USER.A && @Device.B || @USER.C)) -> D:(XA;;FR;;;WD;(((@USER.A) && (@DEVICE.B)) || (@USER.C)))
+D:(XA;;FR;;;S-1-1-0;(@USER.A || @Device.B && @USER.C)) -> D:(XA;;FR;;;WD;((@USER.A) || ((@DEVICE.B) && (@USER.C))))
+D:(XA;;FR;;;S-1-1-0;(@USER.A || @Device.B || @USER.C)) -> D:(XA;;FR;;;WD;(((@USER.A) || (@DEVICE.B)) || (@USER.C)))
+
+D:(XA;;FR;;;S-1-1-0;(@Device.Bitlocker && @Device.Bitlocker)) -> D:(XA;;FR;;;WD;((@DEVICE.Bitlocker) && (@DEVICE.Bitlocker)))
+D:(XA;;FR;;;S-1-1-0;(@Device.Bitlocker || @Device.Bitlocker)) -> D:(XA;;FR;;;WD;((@DEVICE.Bitlocker) || (@DEVICE.Bitlocker)))
+D:(XA;;FR;;;S-1-1-0;(@USER.A && @Device.B)) -> D:(XA;;FR;;;WD;((@USER.A) && (@DEVICE.B)))
+D:(XA;;FR;;;S-1-1-0;(@USER.Bitlocker || @Device.Bitlocker)) -> D:(XA;;FR;;;WD;((@USER.Bitlocker) || (@DEVICE.Bitlocker)))
+D:(XA;;;;;WD;(@Device.bb == 0x7fffffffffffffff)) -> D:(XA;;;;;WD;(@DEVICE.bb == 0x7fffffffffffffff))
+D:(XA;;;;;WD;(@Device.bb == 0xffffffff)) -> D:(XA;;;;;WD;(@DEVICE.bb == 0xffffffff))
+D:(XA;;;;;WD;(@Device.bb == 0xfffffffff)) -> D:(XA;;;;;WD;(@DEVICE.bb == 0xfffffffff))
+
+
+# Member_of is supposed to be SID only
+D:(XD;;FX;;;WD;(Member_of {1, 2, 3}))(A;;CR;;;WD)
+D:(XD;;FX;;;WD;(Member_of 3))(A;;CR;;;WD)
+
+# repeated composite values
+D:(XD;;FX;;;WD;(@USER.Project Any_of 1))(A;;CR;;;WD)
+D:(XD;;FX;;;WD;(@USER.Project Any_of {1, 1}))(A;;CR;;;WD)
+D:(XD;;FX;;;WD;(@USER.Project Any_of {"foo", "FOO"}))(A;;CR;;;WD)
+D:(XD;;FX;;;WD;(@USER.Project Any_of {"foo", "foo", "FOO"}))(A;;CR;;;WD)
+
+# composite order
+D:(XD;;FX;;;WD;(@USER.Project Any_of {1, 2, 3}))(A;;CR;;;WD)
+D:(XD;;FX;;;WD;(@USER.Project Any_of {3, 2, 1}))(A;;CR;;;WD)
+D:(XD;;FX;;;WD;(@USER.Project Any_of {1, 1, 1}))(A;;CR;;;WD)
+D:(XD;;FX;;;WD;(@USER.Project Any_of {1, 2, 3, 2, 1}))(A;;CR;;;WD)
+
+D:(XA;;0x1f;;;AA;(@Device.colour == @Resource.colour))S:(RA;;;;;WD;("colour",TS,0,"red", "blue")) -> D:(XA;;CCDCLCSWRP;;;AA;(@DEVICE.colour == @RESOURCE.colour))S:(RA;;;;;WD;("colour",TS,0x0,"red","blue"))
+D:(XA;;CCDCLCSWRP;;;AA;(@RESOURCE.a == @RESOURCE.b))S:(RA;;;;;WD;("a",TS,0x0,"1","2"))(RA;;;;;WD;("b",TS,0x0,"2","1"))
diff --git a/libcli/security/tests/data/conditional_aces.txt.json b/libcli/security/tests/data/conditional_aces.txt.json
new file mode 100644
index 0000000..4c8211c
--- /dev/null
+++ b/libcli/security/tests/data/conditional_aces.txt.json
@@ -0,0 +1 @@
+{"D:(D;OICI;GA;;;BG)(D;OICI;GA;;;AN)(A;OICI;GRGWGX;;;AU)(XA;;FX;;;S-1-1-0;(@User.Title == \"\"))(A;OICI;GA;;;BA)": [1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 144, 0, 5, 0, 0, 0, 1, 3, 24, 0, 0, 0, 0, 16, 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 34, 2, 0, 0, 1, 3, 20, 0, 0, 0, 0, 16, 1, 1, 0, 0, 0, 0, 0, 5, 7, 0, 0, 0, 0, 3, 20, 0, 0, 0, 0, 224, 1, 1, 0, 0, 0, 0, 0, 5, 11, 0, 0, 0, 9, 0, 48, 0, 160, 0, 18, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 97, 114, 116, 120, 249, 10, 0, 0, 0, 84, 0, 105, 0, 116, 0, 108, 0, 101, 0, 16, 0, 0, 0, 0, 128, 0, 0, 0, 0, 3, 24, 0, 0, 0, 0, 16, 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 32, 2, 0, 0], "D:(D;OICI;GA;;;BG)(D;OICI;GA;;;AN)(A;OICI;GRGWGX;;;AU)(XA;;FX;;;S-1-1-0;(@User.title == \"perambuator\"))(A;OICI;GA;;;BA)": [1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 164, 0, 5, 0, 0, 0, 1, 3, 24, 0, 0, 0, 0, 16, 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 34, 2, 0, 0, 1, 3, 20, 0, 0, 0, 0, 16, 1, 1, 0, 0, 0, 0, 0, 5, 7, 0, 0, 0, 0, 3, 20, 0, 0, 0, 0, 224, 1, 1, 0, 0, 0, 0, 0, 5, 11, 0, 0, 0, 9, 0, 68, 0, 160, 0, 18, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 97, 114, 116, 120, 249, 10, 0, 0, 0, 116, 0, 105, 0, 116, 0, 108, 0, 101, 0, 16, 22, 0, 0, 0, 112, 0, 101, 0, 114, 0, 97, 0, 109, 0, 98, 0, 117, 0, 97, 0, 116, 0, 111, 0, 114, 0, 128, 0, 0, 3, 24, 0, 0, 0, 0, 16, 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 32, 2, 0, 0], "D:(XA;;0x1f;;;AA;(!(! (Member_of{SID(AA)}))))": [1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 68, 0, 1, 0, 0, 0, 9, 0, 60, 0, 31, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 67, 2, 0, 0, 97, 114, 116, 120, 80, 21, 0, 0, 0, 81, 16, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 67, 2, 0, 0, 137, 162, 162, 0, 0, 0], "D:(XA;;0x1f;;;AA;(!(!(!(!(!(! (Member_of{SID(AA)}))))))))": [1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 72, 0, 1, 0, 0, 0, 9, 0, 64, 0, 31, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 67, 2, 0, 0, 97, 114, 116, 120, 80, 21, 0, 0, 0, 81, 16, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 67, 2, 0, 0, 137, 162, 162, 162, 162, 162, 162, 0, 0, 0], "D:(XA;;0x1f;;;AA;(@Device.colour == \"blue\"))": [1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 68, 0, 1, 0, 0, 0, 9, 0, 60, 0, 31, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 67, 2, 0, 0, 97, 114, 116, 120, 251, 12, 0, 0, 0, 99, 0, 111, 0, 108, 0, 111, 0, 117, 0, 114, 0, 16, 8, 0, 0, 0, 98, 0, 108, 0, 117, 0, 101, 0, 128, 0], "D:(XA;;0x1f;;;AA;(@Device.colour == @Resource.colour))S:(RA;;;;;WD;(\"colour\",TS,0,\"blue\"))": [1, 0, 20, 128, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 92, 0, 0, 0, 2, 0, 72, 0, 1, 0, 0, 0, 18, 0, 64, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 20, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 34, 0, 0, 0, 99, 0, 111, 0, 108, 0, 111, 0, 117, 0, 114, 0, 0, 0, 98, 0, 108, 0, 117, 0, 101, 0, 0, 0, 2, 0, 72, 0, 1, 0, 0, 0, 9, 0, 64, 0, 31, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 67, 2, 0, 0, 97, 114, 116, 120, 251, 12, 0, 0, 0, 99, 0, 111, 0, 108, 0, 111, 0, 117, 0, 114, 0, 250, 12, 0, 0, 0, 99, 0, 111, 0, 108, 0, 111, 0, 117, 0, 114, 0, 128, 0], "D:(XA;;0x1f;;;AA;(@Device.colour == {\"orange\", \"blue\"}))": [1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 92, 0, 1, 0, 0, 0, 9, 0, 84, 0, 31, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 67, 2, 0, 0, 97, 114, 116, 120, 251, 12, 0, 0, 0, 99, 0, 111, 0, 108, 0, 111, 0, 117, 0, 114, 0, 80, 30, 0, 0, 0, 16, 12, 0, 0, 0, 111, 0, 114, 0, 97, 0, 110, 0, 103, 0, 101, 0, 16, 8, 0, 0, 0, 98, 0, 108, 0, 117, 0, 101, 0, 128, 0, 0, 0], "D:(XA;;0x1f;;;AA;(@Device.colour Contains @Resource.colour))S:(RA;;;;;WD;(\"colour\",TS,0,\"blue\"))": [1, 0, 20, 128, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 92, 0, 0, 0, 2, 0, 72, 0, 1, 0, 0, 0, 18, 0, 64, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 20, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 34, 0, 0, 0, 99, 0, 111, 0, 108, 0, 111, 0, 117, 0, 114, 0, 0, 0, 98, 0, 108, 0, 117, 0, 101, 0, 0, 0, 2, 0, 72, 0, 1, 0, 0, 0, 9, 0, 64, 0, 31, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 67, 2, 0, 0, 97, 114, 116, 120, 251, 12, 0, 0, 0, 99, 0, 111, 0, 108, 0, 111, 0, 117, 0, 114, 0, 250, 12, 0, 0, 0, 99, 0, 111, 0, 108, 0, 111, 0, 117, 0, 114, 0, 134, 0], "D:(XA;;0x1f;;;AA;(@Device.colour Contains @Resource.colour))S:(RA;;;;;WD;(\"colour\",TS,0,\"blue\", \"red\"))": [1, 0, 20, 128, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 104, 0, 0, 0, 2, 0, 84, 0, 1, 0, 0, 0, 18, 0, 76, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 24, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 38, 0, 0, 0, 48, 0, 0, 0, 99, 0, 111, 0, 108, 0, 111, 0, 117, 0, 114, 0, 0, 0, 98, 0, 108, 0, 117, 0, 101, 0, 0, 0, 114, 0, 101, 0, 100, 0, 0, 0, 2, 0, 72, 0, 1, 0, 0, 0, 9, 0, 64, 0, 31, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 67, 2, 0, 0, 97, 114, 116, 120, 251, 12, 0, 0, 0, 99, 0, 111, 0, 108, 0, 111, 0, 117, 0, 114, 0, 250, 12, 0, 0, 0, 99, 0, 111, 0, 108, 0, 111, 0, 117, 0, 114, 0, 134, 0], "D:(XA;;0x1f;;;AA;(@Device.legs == 1))": [1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 64, 0, 1, 0, 0, 0, 9, 0, 56, 0, 31, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 67, 2, 0, 0, 97, 114, 116, 120, 251, 8, 0, 0, 0, 108, 0, 101, 0, 103, 0, 115, 0, 4, 1, 0, 0, 0, 0, 0, 0, 0, 3, 2, 128, 0, 0, 0], "D:(XA;;0x1f;;;AA;(@Device.legs >= 1))": [1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 64, 0, 1, 0, 0, 0, 9, 0, 56, 0, 31, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 67, 2, 0, 0, 97, 114, 116, 120, 251, 8, 0, 0, 0, 108, 0, 101, 0, 103, 0, 115, 0, 4, 1, 0, 0, 0, 0, 0, 0, 0, 3, 2, 133, 0, 0, 0], "D:(XA;;0x1f;;;AA;(@User.colour == @Device.colour))": [1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 72, 0, 1, 0, 0, 0, 9, 0, 64, 0, 31, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 67, 2, 0, 0, 97, 114, 116, 120, 249, 12, 0, 0, 0, 99, 0, 111, 0, 108, 0, 111, 0, 117, 0, 114, 0, 251, 12, 0, 0, 0, 99, 0, 111, 0, 108, 0, 111, 0, 117, 0, 114, 0, 128, 0], "D:(XA;;0x1f;;;AA;(Device_Member_of{SID(AA)} || Member_of{SID(WD)}))": [1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 88, 0, 1, 0, 0, 0, 9, 0, 80, 0, 31, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 67, 2, 0, 0, 97, 114, 116, 120, 80, 21, 0, 0, 0, 81, 16, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 67, 2, 0, 0, 138, 80, 17, 0, 0, 0, 81, 12, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 137, 161, 0], "D:(XA;;0x1f;;;AA;(Device_Member_of{SID(BA)} && Member_of{SID(WD)}))": [1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 88, 0, 1, 0, 0, 0, 9, 0, 80, 0, 31, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 67, 2, 0, 0, 97, 114, 116, 120, 80, 21, 0, 0, 0, 81, 16, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 32, 2, 0, 0, 138, 80, 17, 0, 0, 0, 81, 12, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 137, 160, 0], "D:(XA;;0x1f;;;AA;(Device_Member_of{SID(BA)}))": [1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 64, 0, 1, 0, 0, 0, 9, 0, 56, 0, 31, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 67, 2, 0, 0, 97, 114, 116, 120, 80, 21, 0, 0, 0, 81, 16, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 32, 2, 0, 0, 138, 0], "D:(XA;;0x1f;;;AA;(Device_Member_of{SID(BG)} || Member_of{SID(WR)}))": [1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 88, 0, 1, 0, 0, 0, 9, 0, 80, 0, 31, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 67, 2, 0, 0, 97, 114, 116, 120, 80, 21, 0, 0, 0, 81, 16, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 34, 2, 0, 0, 138, 80, 17, 0, 0, 0, 81, 12, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 5, 33, 0, 0, 0, 137, 161, 0], "D:(XA;;0x1f;;;AA;(Member_of{SID(S-1-77-88-99)}))": [1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 64, 0, 1, 0, 0, 0, 9, 0, 56, 0, 31, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 67, 2, 0, 0, 97, 114, 116, 120, 80, 21, 0, 0, 0, 81, 16, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 77, 88, 0, 0, 0, 99, 0, 0, 0, 137, 0], "D:(XA;;0x1f;;;AA;(a == 1))": [1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 56, 0, 1, 0, 0, 0, 9, 0, 48, 0, 31, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 67, 2, 0, 0, 97, 114, 116, 120, 248, 2, 0, 0, 0, 97, 0, 4, 1, 0, 0, 0, 0, 0, 0, 0, 3, 2, 128, 0], "D:(XA;;0x1ff;;;S-1-222-333;(Member_of_Any{SID(S-1-222-333)}))": [1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 56, 0, 1, 0, 0, 0, 9, 0, 48, 0, 255, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 222, 77, 1, 0, 0, 97, 114, 116, 120, 80, 17, 0, 0, 0, 81, 12, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 222, 77, 1, 0, 0, 139, 0], "D:(XA;;0x1ff;;;WD;(Member_of_Any{SID(S-1-222-333)}))": [1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 56, 0, 1, 0, 0, 0, 9, 0, 48, 0, 255, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 97, 114, 116, 120, 80, 17, 0, 0, 0, 81, 12, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 222, 77, 1, 0, 0, 139, 0], "D:(XA;;;;;WD;(@Device.bb == 0x7fffffffffffffff))": [1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 56, 0, 1, 0, 0, 0, 9, 0, 48, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 97, 114, 116, 120, 251, 4, 0, 0, 0, 98, 0, 98, 0, 4, 255, 255, 255, 255, 255, 255, 255, 127, 3, 3, 128, 0, 0, 0], "D:(XA;;;;;WD;(@Device.bb == 0xffffffff))": [1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 56, 0, 1, 0, 0, 0, 9, 0, 48, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 97, 114, 116, 120, 251, 4, 0, 0, 0, 98, 0, 98, 0, 4, 255, 255, 255, 255, 0, 0, 0, 0, 3, 3, 128, 0, 0, 0], "D:(XA;;;;;WD;(@Device.bb == 0xfffffffff))": [1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 56, 0, 1, 0, 0, 0, 9, 0, 48, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 97, 114, 116, 120, 251, 4, 0, 0, 0, 98, 0, 98, 0, 4, 255, 255, 255, 255, 15, 0, 0, 0, 3, 3, 128, 0, 0, 0], "D:(XA;;CC;;;AA;(@User.a == @User.b))": [1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 52, 0, 1, 0, 0, 0, 9, 0, 44, 0, 1, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 67, 2, 0, 0, 97, 114, 116, 120, 249, 2, 0, 0, 0, 97, 0, 249, 2, 0, 0, 0, 98, 0, 128, 0], "D:(XA;;CC;;;AA;(a == @User.a))": [1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 52, 0, 1, 0, 0, 0, 9, 0, 44, 0, 1, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 67, 2, 0, 0, 97, 114, 116, 120, 248, 2, 0, 0, 0, 97, 0, 249, 2, 0, 0, 0, 97, 0, 128, 0], "D:(XA;;CC;;;S-1-2-3;(@User.Title != @User.Title))": [1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 64, 0, 1, 0, 0, 0, 9, 0, 56, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 2, 3, 0, 0, 0, 97, 114, 116, 120, 249, 10, 0, 0, 0, 84, 0, 105, 0, 116, 0, 108, 0, 101, 0, 249, 10, 0, 0, 0, 84, 0, 105, 0, 116, 0, 108, 0, 101, 0, 129, 0], "D:(XA;;FR;;;S-1-1-0;(@Device.Bitlocker && @Device.Bitlocker))": [1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 80, 0, 1, 0, 0, 0, 9, 0, 72, 0, 137, 0, 18, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 97, 114, 116, 120, 251, 18, 0, 0, 0, 66, 0, 105, 0, 116, 0, 108, 0, 111, 0, 99, 0, 107, 0, 101, 0, 114, 0, 251, 18, 0, 0, 0, 66, 0, 105, 0, 116, 0, 108, 0, 111, 0, 99, 0, 107, 0, 101, 0, 114, 0, 160, 0], "D:(XA;;FR;;;S-1-1-0;(@Device.Bitlocker || @Device.Bitlocker))": [1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 80, 0, 1, 0, 0, 0, 9, 0, 72, 0, 137, 0, 18, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 97, 114, 116, 120, 251, 18, 0, 0, 0, 66, 0, 105, 0, 116, 0, 108, 0, 111, 0, 99, 0, 107, 0, 101, 0, 114, 0, 251, 18, 0, 0, 0, 66, 0, 105, 0, 116, 0, 108, 0, 111, 0, 99, 0, 107, 0, 101, 0, 114, 0, 161, 0], "D:(XA;;FR;;;S-1-1-0;(@USER.A && @Device.B && @USER.C))": [1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 56, 0, 1, 0, 0, 0, 9, 0, 48, 0, 137, 0, 18, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 97, 114, 116, 120, 249, 2, 0, 0, 0, 65, 0, 251, 2, 0, 0, 0, 66, 0, 160, 249, 2, 0, 0, 0, 67, 0, 160, 0], "D:(XA;;FR;;;S-1-1-0;(@USER.A && @Device.B || @USER.C))": [1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 56, 0, 1, 0, 0, 0, 9, 0, 48, 0, 137, 0, 18, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 97, 114, 116, 120, 249, 2, 0, 0, 0, 65, 0, 251, 2, 0, 0, 0, 66, 0, 160, 249, 2, 0, 0, 0, 67, 0, 161, 0], "D:(XA;;FR;;;S-1-1-0;(@USER.A && @Device.B))": [1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 48, 0, 1, 0, 0, 0, 9, 0, 40, 0, 137, 0, 18, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 97, 114, 116, 120, 249, 2, 0, 0, 0, 65, 0, 251, 2, 0, 0, 0, 66, 0, 160, 0], "D:(XA;;FR;;;S-1-1-0;(@USER.A || @Device.B && @USER.C))": [1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 56, 0, 1, 0, 0, 0, 9, 0, 48, 0, 137, 0, 18, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 97, 114, 116, 120, 249, 2, 0, 0, 0, 65, 0, 251, 2, 0, 0, 0, 66, 0, 249, 2, 0, 0, 0, 67, 0, 160, 161, 0], "D:(XA;;FR;;;S-1-1-0;(@USER.A || @Device.B || @USER.C))": [1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 56, 0, 1, 0, 0, 0, 9, 0, 48, 0, 137, 0, 18, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 97, 114, 116, 120, 249, 2, 0, 0, 0, 65, 0, 251, 2, 0, 0, 0, 66, 0, 161, 249, 2, 0, 0, 0, 67, 0, 161, 0], "D:(XA;;FR;;;S-1-1-0;(@USER.Bitlocker || @Device.Bitlocker))": [1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 80, 0, 1, 0, 0, 0, 9, 0, 72, 0, 137, 0, 18, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 97, 114, 116, 120, 249, 18, 0, 0, 0, 66, 0, 105, 0, 116, 0, 108, 0, 111, 0, 99, 0, 107, 0, 101, 0, 114, 0, 251, 18, 0, 0, 0, 66, 0, 105, 0, 116, 0, 108, 0, 111, 0, 99, 0, 107, 0, 101, 0, 114, 0, 161, 0], "D:(XA;;FR;;;S-1-1-0;(Member_of {SID(S-1-999-777-7-7), SID(BO)} && @Device.Bitlocker))": [1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 108, 0, 1, 0, 0, 0, 9, 0, 100, 0, 137, 0, 18, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 97, 114, 116, 120, 80, 46, 0, 0, 0, 81, 20, 0, 0, 0, 1, 3, 0, 0, 0, 0, 3, 231, 9, 3, 0, 0, 7, 0, 0, 0, 7, 0, 0, 0, 81, 16, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 39, 2, 0, 0, 137, 251, 18, 0, 0, 0, 66, 0, 105, 0, 116, 0, 108, 0, 111, 0, 99, 0, 107, 0, 101, 0, 114, 0, 160], "D:(XA;;FX;;;S-1-1-0;(@User.Project Any_of @Resource.Project))": [1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 72, 0, 1, 0, 0, 0, 9, 0, 64, 0, 160, 0, 18, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 97, 114, 116, 120, 249, 14, 0, 0, 0, 80, 0, 114, 0, 111, 0, 106, 0, 101, 0, 99, 0, 116, 0, 250, 14, 0, 0, 0, 80, 0, 114, 0, 111, 0, 106, 0, 101, 0, 99, 0, 116, 0, 136, 0], "D:(XA;;FX;;;S-1-1-0;(@User.Title == \"PM\"))": [1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 60, 0, 1, 0, 0, 0, 9, 0, 52, 0, 160, 0, 18, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 97, 114, 116, 120, 249, 10, 0, 0, 0, 84, 0, 105, 0, 116, 0, 108, 0, 101, 0, 16, 4, 0, 0, 0, 80, 0, 77, 0, 128, 0, 0, 0], "D:(XA;;FX;;;S-1-1-0;(@User.Title==\"PM\" && (@User.Division==\"Finance\" || @User.Division ==\"Sales\")))": [1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 140, 0, 1, 0, 0, 0, 9, 0, 132, 0, 160, 0, 18, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 97, 114, 116, 120, 249, 10, 0, 0, 0, 84, 0, 105, 0, 116, 0, 108, 0, 101, 0, 16, 4, 0, 0, 0, 80, 0, 77, 0, 128, 249, 16, 0, 0, 0, 68, 0, 105, 0, 118, 0, 105, 0, 115, 0, 105, 0, 111, 0, 110, 0, 16, 14, 0, 0, 0, 70, 0, 105, 0, 110, 0, 97, 0, 110, 0, 99, 0, 101, 0, 128, 249, 16, 0, 0, 0, 68, 0, 105, 0, 118, 0, 105, 0, 115, 0, 105, 0, 111, 0, 110, 0, 16, 10, 0, 0, 0, 83, 0, 97, 0, 108, 0, 101, 0, 115, 0, 128, 161, 160, 0, 0, 0], "D:(XD;;CC;;;S-1-2-3;(@User.Title == @User.Title))": [1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 64, 0, 1, 0, 0, 0, 10, 0, 56, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 2, 3, 0, 0, 0, 97, 114, 116, 120, 249, 10, 0, 0, 0, 84, 0, 105, 0, 116, 0, 108, 0, 101, 0, 249, 10, 0, 0, 0, 84, 0, 105, 0, 116, 0, 108, 0, 101, 0, 128, 0], "D:(XD;;FX;;;S-1-1-0;(@User.Project Any_of @Resource.Project))": [1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 72, 0, 1, 0, 0, 0, 10, 0, 64, 0, 160, 0, 18, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 97, 114, 116, 120, 249, 14, 0, 0, 0, 80, 0, 114, 0, 111, 0, 106, 0, 101, 0, 99, 0, 116, 0, 250, 14, 0, 0, 0, 80, 0, 114, 0, 111, 0, 106, 0, 101, 0, 99, 0, 116, 0, 136, 0], "D:(XD;;FX;;;S-1-1-0;(@User.Title != \"PM\"))": [1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 60, 0, 1, 0, 0, 0, 10, 0, 52, 0, 160, 0, 18, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 97, 114, 116, 120, 249, 10, 0, 0, 0, 84, 0, 105, 0, 116, 0, 108, 0, 101, 0, 16, 4, 0, 0, 0, 80, 0, 77, 0, 129, 0, 0, 0], "D:(XD;;FX;;;WD;(!(@USER.Project Not_Any_of 1)))": [1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 64, 0, 1, 0, 0, 0, 10, 0, 56, 0, 160, 0, 18, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 97, 114, 116, 120, 249, 14, 0, 0, 0, 80, 0, 114, 0, 111, 0, 106, 0, 101, 0, 99, 0, 116, 0, 4, 1, 0, 0, 0, 0, 0, 0, 0, 3, 2, 143, 162], "D:(XD;;FX;;;WD;(@USER.Project Any_of \"pink\"))": [1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 68, 0, 1, 0, 0, 0, 10, 0, 60, 0, 160, 0, 18, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 97, 114, 116, 120, 249, 14, 0, 0, 0, 80, 0, 114, 0, 111, 0, 106, 0, 101, 0, 99, 0, 116, 0, 16, 8, 0, 0, 0, 112, 0, 105, 0, 110, 0, 107, 0, 136, 0, 0, 0], "D:(XD;;FX;;;WD;(@USER.Project Any_of 1))": [1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 64, 0, 1, 0, 0, 0, 10, 0, 56, 0, 160, 0, 18, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 97, 114, 116, 120, 249, 14, 0, 0, 0, 80, 0, 114, 0, 111, 0, 106, 0, 101, 0, 99, 0, 116, 0, 4, 1, 0, 0, 0, 0, 0, 0, 0, 3, 2, 136, 0], "D:AI(XA;OICI;FA;;;WD;(OctetStringType==##1#2#3##))": [1, 0, 4, 132, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 80, 0, 1, 0, 0, 0, 9, 3, 72, 0, 255, 1, 31, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 97, 114, 116, 120, 248, 30, 0, 0, 0, 79, 0, 99, 0, 116, 0, 101, 0, 116, 0, 83, 0, 116, 0, 114, 0, 105, 0, 110, 0, 103, 0, 84, 0, 121, 0, 112, 0, 101, 0, 24, 4, 0, 0, 0, 1, 2, 3, 0, 128, 0, 0, 0], "D:AI(XA;OICI;FA;;;WD;(OctetStringType==#01020300))": [1, 0, 4, 132, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 80, 0, 1, 0, 0, 0, 9, 3, 72, 0, 255, 1, 31, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 97, 114, 116, 120, 248, 30, 0, 0, 0, 79, 0, 99, 0, 116, 0, 101, 0, 116, 0, 83, 0, 116, 0, 114, 0, 105, 0, 110, 0, 103, 0, 84, 0, 121, 0, 112, 0, 101, 0, 24, 4, 0, 0, 0, 1, 2, 3, 0, 128, 0, 0, 0], "O:S-1-1-0D:(XA;;0;;;WD;(Member_Of SID(S-1-1-0)))": [1, 0, 4, 128, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 52, 0, 1, 0, 0, 0, 9, 0, 44, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 97, 114, 116, 120, 81, 12, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 137, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0], "O:S-1-1-0D:(XA;;0x0;;;WD;(Member_Of SID(S-1-1-0)))": [1, 0, 4, 128, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 52, 0, 1, 0, 0, 0, 9, 0, 44, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 97, 114, 116, 120, 81, 12, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 137, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0], "O:S-1-1-0D:(XA;;0x1;;;WD;(Member_of_Any{SID(AS),SID(WD)}))": [1, 0, 4, 128, 92, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 72, 0, 1, 0, 0, 0, 9, 0, 64, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 97, 114, 116, 120, 80, 34, 0, 0, 0, 81, 12, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 18, 1, 0, 0, 0, 81, 12, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 139, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0], "O:S-1-1-0D:(XA;;0x1ff;;;WD;(Member_Of{SID(S-1-1-0)}))": [1, 0, 4, 128, 76, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 56, 0, 1, 0, 0, 0, 9, 0, 48, 0, 255, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 97, 114, 116, 120, 80, 17, 0, 0, 0, 81, 12, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 137, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0], "O:S-1-1-0D:(XA;;0x1ff;;;WD;(Member_of SID(S-1-1-0)))": [1, 0, 4, 128, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 52, 0, 1, 0, 0, 0, 9, 0, 44, 0, 255, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 97, 114, 116, 120, 81, 12, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 137, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0], "O:S-1-1-0D:(XA;;0x1ff;;;WD;(Member_of(SID(S-1-1-0))))": [1, 0, 4, 128, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 52, 0, 1, 0, 0, 0, 9, 0, 44, 0, 255, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 97, 114, 116, 120, 81, 12, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 137, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0], "O:S-1-1-0D:(XA;;0x1ff;;;WD;(Member_of_Any SID(S-1-1-0)))": [1, 0, 4, 128, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 52, 0, 1, 0, 0, 0, 9, 0, 44, 0, 255, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 97, 114, 116, 120, 81, 12, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 139, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0], "O:S-1-1-0D:(XA;;0x1ff;;;WD;(Member_of_Any{SID(S-1-1-0), SID(S-1-222-333)}))": [1, 0, 4, 128, 92, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 72, 0, 1, 0, 0, 0, 9, 0, 64, 0, 255, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 97, 114, 116, 120, 80, 34, 0, 0, 0, 81, 12, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 81, 12, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 222, 77, 1, 0, 0, 139, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0], "O:S-1-1-0D:(XA;;0x1ff;;;WD;(Member_of_Any{SID(S-1-1-334), SID(S-1-222-333)}))": [1, 0, 4, 128, 92, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 72, 0, 1, 0, 0, 0, 9, 0, 64, 0, 255, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 97, 114, 116, 120, 80, 34, 0, 0, 0, 81, 12, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 78, 1, 0, 0, 81, 12, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 222, 77, 1, 0, 0, 139, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0], "O:S-1-1-0D:(XA;;0x1ff;;;WD;(Member_of{SID(S-1-1-0)}))": [1, 0, 4, 128, 76, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 56, 0, 1, 0, 0, 0, 9, 0, 48, 0, 255, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 97, 114, 116, 120, 80, 17, 0, 0, 0, 81, 12, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 137, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0], "O:S-1-1-0D:(XA;;0x1ff;;;WD;(mEMBER_of{SID(S-1-1-0)}))": [1, 0, 4, 128, 76, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 56, 0, 1, 0, 0, 0, 9, 0, 48, 0, 255, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 97, 114, 116, 120, 80, 17, 0, 0, 0, 81, 12, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 137, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0], "O:S-1-1-0D:(XA;;0x1ff;;;WD;(member_of{SID(S-1-1-0)}))": [1, 0, 4, 128, 76, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 56, 0, 1, 0, 0, 0, 9, 0, 48, 0, 255, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 97, 114, 116, 120, 80, 17, 0, 0, 0, 81, 12, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 137, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0], "O:S-1-1-0D:(XA;;;;;WD;(Member_Of SID(S-1-1-0)))": [1, 0, 4, 128, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 52, 0, 1, 0, 0, 0, 9, 0, 44, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 97, 114, 116, 120, 81, 12, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 137, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0], "O:SYG:SYD:(XA;OICI;CR;;;WD;(@USER.ad://ext/AuthenticationSilo == \"siloname\"))": [1, 0, 4, 128, 136, 0, 0, 0, 148, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 116, 0, 1, 0, 0, 0, 9, 3, 108, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 97, 114, 116, 120, 249, 54, 0, 0, 0, 97, 0, 100, 0, 58, 0, 47, 0, 47, 0, 101, 0, 120, 0, 116, 0, 47, 0, 65, 0, 117, 0, 116, 0, 104, 0, 101, 0, 110, 0, 116, 0, 105, 0, 99, 0, 97, 0, 116, 0, 105, 0, 111, 0, 110, 0, 83, 0, 105, 0, 108, 0, 111, 0, 16, 16, 0, 0, 0, 115, 0, 105, 0, 108, 0, 111, 0, 110, 0, 97, 0, 109, 0, 101, 0, 128, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 5, 18, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 5, 18, 0, 0, 0]} \ No newline at end of file
diff --git a/libcli/security/tests/data/conditional_aces_case_insensitive.txt b/libcli/security/tests/data/conditional_aces_case_insensitive.txt
new file mode 100644
index 0000000..ee2500d
--- /dev/null
+++ b/libcli/security/tests/data/conditional_aces_case_insensitive.txt
@@ -0,0 +1 @@
+D:AI(XA;OICI;FA;;;WD;(OctetStringType==#abcdef)) -> D:AI(XA;OICI;FA;;;WD;(OctetStringType == #abcdef))
diff --git a/libcli/security/tests/data/conditional_aces_should_fail.txt b/libcli/security/tests/data/conditional_aces_should_fail.txt
new file mode 100644
index 0000000..23eadcf
--- /dev/null
+++ b/libcli/security/tests/data/conditional_aces_should_fail.txt
@@ -0,0 +1,14 @@
+# Lines starting with # are ignored.
+# These SDDL strings are expected to fail.
+D:(XA;;FR;;;S-1-1-0; (Member_of {SID(ernie), SID(BO)} && @Device.Bitlocker)) -> D:(XA;;FR;;;S-1-1-0; (Member_of {SID(ernie), SID(BO)} && @Device.Bitlocker))
+D:(XA;;0x1f;;;AA;(!!! !!! !!! Member_of{SID(BA)})) -> D:(XA;;0x1f;;;AA;(!!! !!! !!! Member_of{SID(BA)}))
+D:(XA;;0x1f;;;AA;(!!! !!! !!! Not_Member_of{SID(AA)})) -> D:(XA;;0x1f;;;AA;(!!! !!! !!! Not_Member_of{SID(AA)}))
+O:S-1-1-0D:(XA;;0x1ff;;;WD;(Member_of_AnySID(S-1-1-0))) -> O:S-1-1-0D:(XA;;0x1ff;;;WD;(Member_of_AnySID(S-1-1-0)))
+D:(XA;;CC;;;S-1-2-3;(@User.Title == !(@User.Title))) -> x
+D:(XA;;0x1f;;;AA;(! Member_of{SID(BA)})) -> x
+# local attributes on the RHS fail (ok on the LHS)
+D:(XA;;0x1f;;;AA;(a == a))
+D:(XA;;;;;WD;(@Device.bb == 055555624677746777766777767))
+D:(XA;;;;;WD;(@Device.bb == 0x624677746777766777767))
+D:(XA;;;;;WD;(@Device.bb == 624677746777766777767))
+D:(XA;;;;;WD;(@Device.bb == 0x10000000000000000))
diff --git a/libcli/security/tests/data/conditional_aces_windows_only.txt b/libcli/security/tests/data/conditional_aces_windows_only.txt
new file mode 100644
index 0000000..182d412
--- /dev/null
+++ b/libcli/security/tests/data/conditional_aces_windows_only.txt
@@ -0,0 +1,14 @@
+# Windows is far less fussy about case in general SDDL
+O:S-1-1-0D:(xd;;;;;WD;(Member_Of SID(S-1-1-0))) -> O:WDD:(XD;;;;;WD;(Member_of SID(WD)))
+O:s-1-1-0D:(xa;;;;;wd;(Member_Of SID(S-1-1-0))) -> O:WDD:(XA;;;;;WD;(Member_of SID(WD)))
+O:s-1-1-0D:(xa;;;;;wd;(member_of sid(s-1-1-0))) -> O:WDD:(XA;;;;;WD;(Member_of SID(WD)))
+O:s-1-1-0D:(xa;;;;;wd;(member_of(sid(s-1-1-0)))) -> O:WDD:(XA;;;;;WD;(Member_of SID(WD)))
+O:s-1-1-0D:(xa;;;;;wd;(member_of((sid(s-1-1-0))))) -> O:WDD:(XA;;;;;WD;(Member_of SID(WD)))
+# spaces in general SDDL
+D:(D;OICI;GA;;;BG)(D;OICI;GA;;;AN)(A; OICI; GRGWGX;;;AU)(XA;;FX;;;S-1-1-0;(@User.TEETH == "5"))(A;OICI;GA;;;BA) -> D:(D;OICI;GA;;;BG)(D;OICI;GA;;;AN)(A;OICI;GXGWGR;;;AU)(XA;;FX;;;WD;(@USER.TEETH == "5"))(A;OICI;GA;;;BA)
+D:(D;OICI;GA;;;BG)(D;OICI;GA;;;AN)(A; OICI; GRGWGX;;;AU)(XA;;FX;;;S-1-1-0;(@User.title == "perambuator"))(A;OICI;GA;;;BA) -> D:(D;OICI;GA;;;BG)(D;OICI;GA;;;AN)(A;OICI;GXGWGR;;;AU)(XA;;FX;;;WD;(@USER.title == "perambuator"))(A;OICI;GA;;;BA)
+D:(XA;;FR;;;S-1-1-0; (Member_of {SID(S-1-1-0), SID(BO)} && @Device.Bitlocker)) -> D:(XA;;FR;;;WD;((Member_of {SID(WD), SID(BO)}) && (@DEVICE.Bitlocker)))
+D:(XD;;FX;;;S-1-1-0; (@User.Project Any_of @Resource.Project)) -> D:(XD;;FX;;;WD;(@USER.Project Any_of @RESOURCE.Project))
+# note the odd number of characters in this octet string; implies a leading '0'
+D:AI(XA;OICI;FA;;;WD;(OctetStringType==#1#2#3##)) -> D:AI(XA;OICI;FA;;;WD;(OctetStringType == #01020300))
+D:(XA;;;;;WD;(@Device.bb == 0xffffffffffffffff)) -> D:(XA;;;;;WD;(@DEVICE.bb == 0xffffffffffffffff))
diff --git a/libcli/security/tests/data/export-sddl-fuzz-seeds-as-json b/libcli/security/tests/data/export-sddl-fuzz-seeds-as-json
new file mode 100755
index 0000000..cbff661
--- /dev/null
+++ b/libcli/security/tests/data/export-sddl-fuzz-seeds-as-json
@@ -0,0 +1,49 @@
+#!/usr/bin/python3
+"""USAGE: $ ./export-sddl-fuzz-seeds-as-json DIR [DIR[...]] > x.json
+
+Some of our fuzzers generate SDDL strings with trailing garbage.
+
+This script converts them into the JSON format used by
+windows-sddl-tests.py, though it doesn't parse the SDDL, mapping all
+strings to an empty list. The idea is you can feed this through
+windows-sddl-tests.py or something else to get the correct bytes.
+
+Valid and invalid strings are treated alike, so long as they are
+utf-8. The JSON is un-indented, but structurally equivalent to this:
+
+{
+ "D:P" : [],
+ "yertle" : [],
+ "ł\n¼" : [],
+}
+"""
+from pathlib import Path
+import sys
+import json
+
+
+def main():
+ if {'-h', '--help'}.intersection(sys.argv) or len(sys.argv) < 2:
+ print(__doc__)
+ sys.exit(len(sys.argv) < 2)
+
+ bytes_json = {}
+ for arg in sys.argv[1:]:
+ d = Path(arg)
+ for fn in d.iterdir():
+ with fn.open("rb") as f:
+ b = f.read()
+ # the SDDL string is the nul-terminated portion.
+ if 0 in b:
+ b = b[:b.index(0)]
+ try:
+ s = b.decode()
+ except UnicodeDecodeError:
+ continue
+ bytes_json[s] = []
+
+ out = json.dumps(bytes_json)
+ print(out)
+
+
+main()
diff --git a/libcli/security/tests/data/extract-sddl-seeds b/libcli/security/tests/data/extract-sddl-seeds
new file mode 100755
index 0000000..27ca407
--- /dev/null
+++ b/libcli/security/tests/data/extract-sddl-seeds
@@ -0,0 +1,72 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) Catalyst IT Ltd. 2023
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+"""USAGE: extract-sddl-seeds SRCDIR SDDLDIR
+
+SRCDIR should have fuzz_security_token_vs_descriptor seeds.
+
+SDDLDIR will end up with SDDL strings representing the security
+descriptors in the seeds, along with 4 trailing bytes representing an
+access mask. This is the format used by the SDDL fuzzers.
+"""
+
+
+import sys
+sys.path.insert(0, "bin/python")
+
+from pathlib import Path
+from hashlib import md5
+from samba.ndr import ndr_unpack, ndr_pack
+from samba.dcerpc.security import token_descriptor_fuzzing_pair
+
+
+def usage(ret):
+ print(__doc__)
+ exit(ret)
+
+
+def main():
+ if {'-h', '--help'}.intersection(sys.argv):
+ usage(0)
+ if len(sys.argv) != 3:
+ usage(1)
+
+ src, dest = sys.argv[1:]
+ sp = Path(src)
+ dp = Path(dest)
+
+ raw_strings = set()
+ sddl_strings = set()
+
+ for filename in sp.iterdir():
+ with open(filename, 'rb') as f:
+ raw_strings.add(f.read())
+
+ for s in raw_strings:
+ pair = ndr_unpack(s)
+ sd = pair.sd.as_sddl()
+ mask = pair.access_desired
+ b = sd.encode() + mask.to_bytes(4, 'little')
+ sddl_strings.add(b)
+
+ for s in sddl_strings:
+ name = md5(s).hexdigest()
+ with open(dp / name, "wb") as f:
+ f.write(s)
+
+
+main()
diff --git a/libcli/security/tests/data/ndr_dumps/fileb5iJt4 b/libcli/security/tests/data/ndr_dumps/fileb5iJt4
new file mode 100644
index 0000000..c0de4da
--- /dev/null
+++ b/libcli/security/tests/data/ndr_dumps/fileb5iJt4
Binary files differ
diff --git a/libcli/security/tests/data/ndr_dumps/fileb8cNVS b/libcli/security/tests/data/ndr_dumps/fileb8cNVS
new file mode 100644
index 0000000..bee598e
--- /dev/null
+++ b/libcli/security/tests/data/ndr_dumps/fileb8cNVS
Binary files differ
diff --git a/libcli/security/tests/data/ndr_dumps/filebI7h5H b/libcli/security/tests/data/ndr_dumps/filebI7h5H
new file mode 100644
index 0000000..c98fe38
--- /dev/null
+++ b/libcli/security/tests/data/ndr_dumps/filebI7h5H
Binary files differ
diff --git a/libcli/security/tests/data/ndr_dumps/filebNdBgt b/libcli/security/tests/data/ndr_dumps/filebNdBgt
new file mode 100644
index 0000000..62e37ae
--- /dev/null
+++ b/libcli/security/tests/data/ndr_dumps/filebNdBgt
Binary files differ
diff --git a/libcli/security/tests/data/ndr_dumps/filebOjK4H b/libcli/security/tests/data/ndr_dumps/filebOjK4H
new file mode 100644
index 0000000..9a040c1
--- /dev/null
+++ b/libcli/security/tests/data/ndr_dumps/filebOjK4H
Binary files differ
diff --git a/libcli/security/tests/data/ndr_dumps/filebzCPTH b/libcli/security/tests/data/ndr_dumps/filebzCPTH
new file mode 100644
index 0000000..ba52884
--- /dev/null
+++ b/libcli/security/tests/data/ndr_dumps/filebzCPTH
Binary files differ
diff --git a/libcli/security/tests/data/oversize-acls.json b/libcli/security/tests/data/oversize-acls.json
new file mode 100644
index 0000000..a4559f3
--- /dev/null
+++ b/libcli/security/tests/data/oversize-acls.json
@@ -0,0 +1,20 @@
+{
+ "D:(A;OICI;FA;;;S-1-5-21-1927343755-967950539-965328874-512)(A;OICI;FA;;;S-1-5-21-1927343755-967950539-965328874-519)(A;;FA;;;BA)(A;OICIIO;FA;;;CO)(A;OICI;FA;;;SY)(A;OICI;0x1200a9;;;AU)(A;OICI;;;;AU)(A;OICI;0x1200a9;;;ED)":
+ [1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 4, 0, 208, 0, 8, 0, 0, 0, 0, 3, 36, 0, 255, 1, 31, 0, 1, 5, 0, 0, 0, 0, 0, 5, 21, 0, 0, 0, 139, 238, 224, 114, 203, 192, 177, 57, 234, 191, 137, 57, 0, 2, 0, 0, 0, 3, 36, 0, 255, 1, 31, 0, 1, 5, 0, 0, 0, 0, 0, 5, 21, 0, 0, 0, 139, 238, 224, 114, 203, 192, 177, 57, 234, 191, 137, 57, 7, 2, 0, 0, 0, 0, 24, 0, 255, 1, 31, 0, 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 32, 2, 0, 0, 0, 11, 20, 0, 255, 1, 31, 0, 1, 1, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 3, 20, 0, 255, 1, 31, 0, 1, 1, 0, 0, 0, 0, 0, 5, 18, 0, 0, 0, 0, 3, 20, 0, 169, 0, 18, 0, 1, 1, 0, 0, 0, 0, 0, 5, 11, 0, 0, 0, 0, 3, 20, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 5, 11, 0, 0, 0, 0, 3, 20, 0, 169, 0, 18, 0, 1, 1, 0, 0, 0, 0, 0, 5, 9, 0, 0, 0, 0, 0, 0, 0],
+ "D:(A;OICI;FA;;;S-1-5-21-3372605546-132586199-2553092274-512)(A;OICI;FA;;;S-1-5-21-3372605546-132586199-2553092274-519)(A;;FA;;;BA)(A;OICIIO;FA;;;CO)(A;OICI;FA;;;SY)(A;OICI;0x1200a9;;;AU)(A;OICI;;;;AU)(A;OICI;0x1200a9;;;ED)":
+ [1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 4, 0, 208, 0, 8, 0, 0, 0, 0, 3, 36, 0, 255, 1, 31, 0, 1, 5, 0, 0, 0, 0, 0, 5, 21, 0, 0, 0, 106, 224, 5, 201, 215, 26, 231, 7, 178, 24, 45, 152, 0, 2, 0, 0, 0, 3, 36, 0, 255, 1, 31, 0, 1, 5, 0, 0, 0, 0, 0, 5, 21, 0, 0, 0, 106, 224, 5, 201, 215, 26, 231, 7, 178, 24, 45, 152, 7, 2, 0, 0, 0, 0, 24, 0, 255, 1, 31, 0, 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 32, 2, 0, 0, 0, 11, 20, 0, 255, 1, 31, 0, 1, 1, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 3, 20, 0, 255, 1, 31, 0, 1, 1, 0, 0, 0, 0, 0, 5, 18, 0, 0, 0, 0, 3, 20, 0, 169, 0, 18, 0, 1, 1, 0, 0, 0, 0, 0, 5, 11, 0, 0, 0, 0, 3, 20, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 5, 11, 0, 0, 0, 0, 3, 20, 0, 169, 0, 18, 0, 1, 1, 0, 0, 0, 0, 0, 5, 9, 0, 0, 0, 0, 0, 0, 0],
+ "D:(A;OICI;FA;;;S-1-5-21-446349270-2432516025-2131592620-512)(A;OICI;FA;;;S-1-5-21-446349270-2432516025-2131592620-519)(A;;FA;;;BA)(A;OICIIO;FA;;;CO)(A;OICI;FA;;;SY)(A;OICI;0x1200a9;;;AU)(A;OICI;;;;AU)(A;OICI;0x1200a9;;;ED)":
+ [1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 4, 0, 208, 0, 8, 0, 0, 0, 0, 3, 36, 0, 255, 1, 31, 0, 1, 5, 0, 0, 0, 0, 0, 5, 21, 0, 0, 0, 214, 191, 154, 26, 185, 63, 253, 144, 172, 133, 13, 127, 0, 2, 0, 0, 0, 3, 36, 0, 255, 1, 31, 0, 1, 5, 0, 0, 0, 0, 0, 5, 21, 0, 0, 0, 214, 191, 154, 26, 185, 63, 253, 144, 172, 133, 13, 127, 7, 2, 0, 0, 0, 0, 24, 0, 255, 1, 31, 0, 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 32, 2, 0, 0, 0, 11, 20, 0, 255, 1, 31, 0, 1, 1, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 3, 20, 0, 255, 1, 31, 0, 1, 1, 0, 0, 0, 0, 0, 5, 18, 0, 0, 0, 0, 3, 20, 0, 169, 0, 18, 0, 1, 1, 0, 0, 0, 0, 0, 5, 11, 0, 0, 0, 0, 3, 20, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 5, 11, 0, 0, 0, 0, 3, 20, 0, 169, 0, 18, 0, 1, 1, 0, 0, 0, 0, 0, 5, 9, 0, 0, 0, 0, 0, 0, 0],
+ "D:(A;OICI;FA;;;S-1-5-21-926620776-2075325327-1127912823-512)(A;OICI;FA;;;S-1-5-21-926620776-2075325327-1127912823-519)(A;;FA;;;BA)(A;OICIIO;FA;;;CO)(A;OICI;FA;;;SY)(A;OICI;0x1200a9;;;AU)(A;OICI;;;;AU)(A;OICI;0x1200a9;;;ED)":
+ [1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 4, 0, 208, 0, 8, 0, 0, 0, 0, 3, 36, 0, 255, 1, 31, 0, 1, 5, 0, 0, 0, 0, 0, 5, 21, 0, 0, 0, 104, 28, 59, 55, 143, 243, 178, 123, 119, 149, 58, 67, 0, 2, 0, 0, 0, 3, 36, 0, 255, 1, 31, 0, 1, 5, 0, 0, 0, 0, 0, 5, 21, 0, 0, 0, 104, 28, 59, 55, 143, 243, 178, 123, 119, 149, 58, 67, 7, 2, 0, 0, 0, 0, 24, 0, 255, 1, 31, 0, 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 32, 2, 0, 0, 0, 11, 20, 0, 255, 1, 31, 0, 1, 1, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 3, 20, 0, 255, 1, 31, 0, 1, 1, 0, 0, 0, 0, 0, 5, 18, 0, 0, 0, 0, 3, 20, 0, 169, 0, 18, 0, 1, 1, 0, 0, 0, 0, 0, 5, 11, 0, 0, 0, 0, 3, 20, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 5, 11, 0, 0, 0, 0, 3, 20, 0, 169, 0, 18, 0, 1, 1, 0, 0, 0, 0, 0, 5, 9, 0, 0, 0, 0, 0, 0, 0],
+ "D:P(D;;;;;MP)(D;;;;;MP)":
+ [1, 0, 4, 144, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 4, 0, 56, 0, 2, 0, 0, 0, 1, 0, 20, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 16, 0, 33, 0, 0, 1, 0, 20, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 16, 0, 33, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
+ "D:P(D;;;;;MP)(D;;;;;MP)(D;;;;;MP)":
+ [1, 0, 4, 144, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 4, 0, 80, 0, 3, 0, 0, 0, 1, 0, 20, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 16, 0, 33, 0, 0, 1, 0, 20, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 16, 0, 33, 0, 0, 1, 0, 20, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 16, 0, 33, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
+ "D:P(D;;;;;MP)(D;;;;;MP)(D;;;;;MP)(D;;;;;MP)":
+ [1, 0, 4, 144, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 4, 0, 104, 0, 4, 0, 0, 0, 1, 0, 20, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 16, 0, 33, 0, 0, 1, 0, 20, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 16, 0, 33, 0, 0, 1, 0, 20, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 16, 0, 33, 0, 0, 1, 0, 20, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 16, 0, 33, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
+ "D:P(D;;;;;MP)(D;;;;;MP)(D;;;;;MP)(D;;;;;MP)(D;;;;;MP)(D;;;;;MP)(D;;;;;MP)":
+ [1, 0, 4, 144, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 4, 0, 176, 0, 7, 0, 0, 0, 1, 0, 20, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 16, 0, 33, 0, 0, 1, 0, 20, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 16, 0, 33, 0, 0, 1, 0, 20, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 16, 0, 33, 0, 0, 1, 0, 20, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 16, 0, 33, 0, 0, 1, 0, 20, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 16, 0, 33, 0, 0, 1, 0, 20, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 16, 0, 33, 0, 0, 1, 0, 20, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 16, 0, 33, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
+ "D:P(D;;;;;MP)(D;;;;;MP)(D;;;;;MP)(D;;;;;MP)(D;;;;;MP)(D;;;;;MP)(D;;;;;MP)(D;;;;;MP)(D;;;;;MP)(D;;;;;MP)(D;;;;;MP)":
+ [1, 0, 4, 144, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 4, 0, 16, 1, 11, 0, 0, 0, 1, 0, 20, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 16, 0, 33, 0, 0, 1, 0, 20, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 16, 0, 33, 0, 0, 1, 0, 20, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 16, 0, 33, 0, 0, 1, 0, 20, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 16, 0, 33, 0, 0, 1, 0, 20, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 16, 0, 33, 0, 0, 1, 0, 20, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 16, 0, 33, 0, 0, 1, 0, 20, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 16, 0, 33, 0, 0, 1, 0, 20, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 16, 0, 33, 0, 0, 1, 0, 20, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 16, 0, 33, 0, 0, 1, 0, 20, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 16, 0, 33, 0, 0, 1, 0, 20, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 16, 0, 33, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
+}
diff --git a/libcli/security/tests/data/registry-object-rights.json b/libcli/security/tests/data/registry-object-rights.json
new file mode 100644
index 0000000..97a64ea
--- /dev/null
+++ b/libcli/security/tests/data/registry-object-rights.json
@@ -0,0 +1 @@
+{"D:(A;;CCLCRPRC;;;WD)(A;;KA;;;BA)": [1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 52, 0, 2, 0, 0, 0, 0, 0, 20, 0, 21, 0, 2, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 24, 0, 63, 0, 15, 0, 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 32, 2, 0, 0], "D:(A;;CCRPWPRC;;;WD)(A;;KA;;;BA)(A;;KA;;;AO)": [1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 76, 0, 3, 0, 0, 0, 0, 0, 20, 0, 49, 0, 2, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 24, 0, 63, 0, 15, 0, 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 32, 2, 0, 0, 0, 0, 24, 0, 63, 0, 15, 0, 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 36, 2, 0, 0], "D:(A;;CCRPWPRC;;;WD)(A;;KA;;;BA)(A;;KA;;;AO)(A;;KA;;;S-1-5-21-1069531106-184984463-4116541046-512)": [1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 112, 0, 4, 0, 0, 0, 0, 0, 20, 0, 49, 0, 2, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 24, 0, 63, 0, 15, 0, 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 32, 2, 0, 0, 0, 0, 24, 0, 63, 0, 15, 0, 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 36, 2, 0, 0, 0, 0, 36, 0, 63, 0, 15, 0, 1, 5, 0, 0, 0, 0, 0, 5, 21, 0, 0, 0, 226, 191, 191, 63, 143, 163, 6, 11, 118, 110, 93, 245, 0, 2, 0, 0], "D:(A;;CCRPWPRC;;;WD)(A;;KA;;;BA)(A;;KA;;;AO)(A;;KA;;;S-1-5-21-1378461354-3939386343-493233828-512)": [1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 112, 0, 4, 0, 0, 0, 0, 0, 20, 0, 49, 0, 2, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 24, 0, 63, 0, 15, 0, 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 32, 2, 0, 0, 0, 0, 24, 0, 63, 0, 15, 0, 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 36, 2, 0, 0, 0, 0, 36, 0, 63, 0, 15, 0, 1, 5, 0, 0, 0, 0, 0, 5, 21, 0, 0, 0, 170, 166, 41, 82, 231, 67, 206, 234, 164, 38, 102, 29, 0, 2, 0, 0], "D:(A;;CCRPWPRC;;;WD)(A;;KA;;;BA)(A;;KA;;;AO)(A;;KA;;;S-1-5-21-3587273675-3237974979-2131186439-512)": [1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 112, 0, 4, 0, 0, 0, 0, 0, 20, 0, 49, 0, 2, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 24, 0, 63, 0, 15, 0, 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 32, 2, 0, 0, 0, 0, 24, 0, 63, 0, 15, 0, 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 36, 2, 0, 0, 0, 0, 36, 0, 63, 0, 15, 0, 1, 5, 0, 0, 0, 0, 0, 5, 21, 0, 0, 0, 203, 115, 209, 213, 195, 147, 255, 192, 7, 83, 7, 127, 0, 2, 0, 0], "D:(A;;CCRPWPRC;;;WD)(A;;KA;;;BA)(A;;KA;;;AO)(A;;KA;;;S-1-5-21-3984653172-1380167674-707033525-512)": [1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 112, 0, 4, 0, 0, 0, 0, 0, 20, 0, 49, 0, 2, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 24, 0, 63, 0, 15, 0, 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 32, 2, 0, 0, 0, 0, 24, 0, 63, 0, 15, 0, 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 36, 2, 0, 0, 0, 0, 36, 0, 63, 0, 15, 0, 1, 5, 0, 0, 0, 0, 0, 5, 21, 0, 0, 0, 116, 251, 128, 237, 250, 175, 67, 82, 181, 121, 36, 42, 0, 2, 0, 0], "D:(A;;CCRPWPRC;;;WD)(A;;KA;;;BA)(A;;KA;;;AO)(A;;KA;;;S-1-5-21-4154349010-984067676-209295477-512)": [1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 112, 0, 4, 0, 0, 0, 0, 0, 20, 0, 49, 0, 2, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 24, 0, 63, 0, 15, 0, 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 32, 2, 0, 0, 0, 0, 24, 0, 63, 0, 15, 0, 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 36, 2, 0, 0, 0, 0, 36, 0, 63, 0, 15, 0, 1, 5, 0, 0, 0, 0, 0, 5, 21, 0, 0, 0, 210, 85, 158, 247, 92, 174, 167, 58, 117, 152, 121, 12, 0, 2, 0, 0], "D:(A;;CCRPWPRC;;;WD)(A;;KA;;;BA)(A;;KA;;;AO)(A;;KA;;;S-1-5-21-536441700-3718478525-2547843259-512)": [1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 112, 0, 4, 0, 0, 0, 0, 0, 20, 0, 49, 0, 2, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 24, 0, 63, 0, 15, 0, 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 32, 2, 0, 0, 0, 0, 24, 0, 63, 0, 15, 0, 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 36, 2, 0, 0, 0, 0, 36, 0, 63, 0, 15, 0, 1, 5, 0, 0, 0, 0, 0, 5, 21, 0, 0, 0, 100, 115, 249, 31, 189, 122, 163, 221, 187, 0, 221, 151, 0, 2, 0, 0], "O:BAG:SYD:(A;;KR;;;WD)(A;;KA;;;BA)(A;;KA;;;SY)": [1, 0, 4, 128, 92, 0, 0, 0, 108, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 72, 0, 3, 0, 0, 0, 0, 0, 20, 0, 25, 0, 2, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 24, 0, 63, 0, 15, 0, 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 32, 2, 0, 0, 0, 0, 20, 0, 63, 0, 15, 0, 1, 1, 0, 0, 0, 0, 0, 5, 18, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 32, 2, 0, 0, 1, 1, 0, 0, 0, 0, 0, 5, 18, 0, 0, 0], "O:S-1-5-21-3984653172-1380167674-707033525-1000G:S-1-22-2-50133D:(A;;0x1f019f;;;S-1-5-21-3984653172-1380167674-707033525-1000)(A;;0x1f019f;;;S-1-22-2-50133)(A;;0x1f019f;;;WD)(A;;KA;;;SY)": [1, 0, 4, 128, 128, 0, 0, 0, 156, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 108, 0, 4, 0, 0, 0, 0, 0, 36, 0, 159, 1, 31, 0, 1, 5, 0, 0, 0, 0, 0, 5, 21, 0, 0, 0, 116, 251, 128, 237, 250, 175, 67, 82, 181, 121, 36, 42, 232, 3, 0, 0, 0, 0, 24, 0, 159, 1, 31, 0, 1, 2, 0, 0, 0, 0, 0, 22, 2, 0, 0, 0, 213, 195, 0, 0, 0, 0, 20, 0, 159, 1, 31, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 20, 0, 63, 0, 15, 0, 1, 1, 0, 0, 0, 0, 0, 5, 18, 0, 0, 0, 1, 5, 0, 0, 0, 0, 0, 5, 21, 0, 0, 0, 116, 251, 128, 237, 250, 175, 67, 82, 181, 121, 36, 42, 232, 3, 0, 0, 1, 2, 0, 0, 0, 0, 0, 22, 2, 0, 0, 0, 213, 195, 0, 0], "O:S-1-5-21-536441700-3718478525-2547843259-1000G:S-1-22-2-50133D:(A;;0x1f019f;;;S-1-5-21-536441700-3718478525-2547843259-1000)(A;;0x1f019f;;;S-1-22-2-50133)(A;;0x1f019f;;;WD)(A;;KA;;;SY)": [1, 0, 4, 128, 128, 0, 0, 0, 156, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 108, 0, 4, 0, 0, 0, 0, 0, 36, 0, 159, 1, 31, 0, 1, 5, 0, 0, 0, 0, 0, 5, 21, 0, 0, 0, 100, 115, 249, 31, 189, 122, 163, 221, 187, 0, 221, 151, 232, 3, 0, 0, 0, 0, 24, 0, 159, 1, 31, 0, 1, 2, 0, 0, 0, 0, 0, 22, 2, 0, 0, 0, 213, 195, 0, 0, 0, 0, 20, 0, 159, 1, 31, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 20, 0, 63, 0, 15, 0, 1, 1, 0, 0, 0, 0, 0, 5, 18, 0, 0, 0, 1, 5, 0, 0, 0, 0, 0, 5, 21, 0, 0, 0, 100, 115, 249, 31, 189, 122, 163, 221, 187, 0, 221, 151, 232, 3, 0, 0, 1, 2, 0, 0, 0, 0, 0, 22, 2, 0, 0, 0, 213, 195, 0, 0]} \ No newline at end of file
diff --git a/libcli/security/tests/data/short-conditional-and-resource-aces-successes.json.gz b/libcli/security/tests/data/short-conditional-and-resource-aces-successes.json.gz
new file mode 100644
index 0000000..e7f8024
--- /dev/null
+++ b/libcli/security/tests/data/short-conditional-and-resource-aces-successes.json.gz
Binary files differ
diff --git a/libcli/security/tests/data/short-conditional-and-resource-aces-tx-int.json.gz b/libcli/security/tests/data/short-conditional-and-resource-aces-tx-int.json.gz
new file mode 100644
index 0000000..e1b6157
--- /dev/null
+++ b/libcli/security/tests/data/short-conditional-and-resource-aces-tx-int.json.gz
Binary files differ
diff --git a/libcli/security/tests/data/short-ordinary-acls-v2.json.gz b/libcli/security/tests/data/short-ordinary-acls-v2.json.gz
new file mode 100644
index 0000000..1f4ef20
--- /dev/null
+++ b/libcli/security/tests/data/short-ordinary-acls-v2.json.gz
Binary files differ
diff --git a/libcli/security/tests/data/short-ordinary-acls.json.gz b/libcli/security/tests/data/short-ordinary-acls.json.gz
new file mode 100644
index 0000000..8554b7c
--- /dev/null
+++ b/libcli/security/tests/data/short-ordinary-acls.json.gz
Binary files differ
diff --git a/libcli/security/tests/test_claim_conversion.c b/libcli/security/tests/test_claim_conversion.c
new file mode 100644
index 0000000..aeb172f
--- /dev/null
+++ b/libcli/security/tests/test_claim_conversion.c
@@ -0,0 +1,171 @@
+/*
+ * Unit tests for conditional ACE SDDL.
+ *
+ * Copyright (C) Catalyst.NET Ltd 2023
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <setjmp.h>
+#include "cmocka.h"
+
+#include "lib/util/attr.h"
+#include "includes.h"
+#include "librpc/gen_ndr/ndr_security.h"
+#include "libcli/security/security.h"
+#include "libcli/security/conditional_ace.h"
+#include "librpc/gen_ndr/conditional_ace.h"
+#include "libcli/security/claims-conversions.h"
+#include "librpc/gen_ndr/ndr_claims.h"
+
+#define debug_message(...) print_message(__VA_ARGS__)
+
+#define debug_fail(x, ...) print_message("\033[1;31m" x "\033[0m", __VA_ARGS__)
+#define debug_ok(x, ...) print_message("\033[1;32m" x "\033[0m", __VA_ARGS__)
+
+#define assert_ntstatus_equal(got, expected, comment) \
+ do { NTSTATUS __got = got, __expected = expected; \
+ if (!NT_STATUS_EQUAL(__got, __expected)) { \
+ print_message(": "#got" was %s, expected %s: %s", \
+ nt_errstr(__got), \
+ nt_errstr(__expected), comment); \
+ fail(); \
+ } \
+ } while(0)
+
+
+
+static DATA_BLOB datablob_from_file(TALLOC_CTX *mem_ctx,
+ const char *filename)
+{
+ DATA_BLOB b = {0};
+ FILE *fh = fopen(filename, "rb");
+ int ret;
+ struct stat s;
+ size_t len;
+ if (fh == NULL) {
+ debug_message("could not open '%s'\n", filename);
+ return b;
+ }
+ ret = fstat(fileno(fh), &s);
+ if (ret != 0) {
+ fclose(fh);
+ return b;
+ }
+ b.data = talloc_array(mem_ctx, uint8_t, s.st_size);
+ if (b.data == NULL) {
+ fclose(fh);
+ return b;
+ }
+ len = fread(b.data, 1, s.st_size, fh);
+ if (ferror(fh) || len != s.st_size) {
+ TALLOC_FREE(b.data);
+ } else {
+ b.length = len;
+ }
+ fclose(fh);
+ return b;
+}
+
+
+static void _test_one_ndr_dump(void **state, const char *name)
+{
+ TALLOC_CTX *tmp_ctx = talloc_new(NULL);
+ struct CLAIMS_SET claims_set;
+ DATA_BLOB blob;
+ NTSTATUS status;
+ struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *out_claims = NULL;
+ uint32_t out_n_claims = 0;
+ enum ndr_err_code ndr_err;
+ char filename[200];
+ snprintf(filename, sizeof(filename),
+ "libcli/security/tests/data/ndr_dumps/%s", name);
+
+ blob = datablob_from_file(tmp_ctx, filename);
+ ndr_err = ndr_pull_struct_blob(
+ &blob, tmp_ctx, &claims_set,
+ (ndr_pull_flags_fn_t)ndr_pull_CLAIMS_SET);
+ assert_int_equal(ndr_err, NDR_ERR_SUCCESS);
+
+ status = token_claims_to_claims_v1(tmp_ctx,
+ &claims_set,
+ &out_claims,
+ &out_n_claims);
+ assert_ntstatus_equal(status, NT_STATUS_OK, "sigh\n");
+}
+
+
+
+static void test_fileb5iJt4(void **state)
+{
+ _test_one_ndr_dump(state, "fileb5iJt4");
+}
+
+static void test_fileb8cNVS(void **state)
+{
+ _test_one_ndr_dump(state, "fileb8cNVS");
+}
+
+static void test_filebI7h5H(void **state)
+{
+ _test_one_ndr_dump(state, "filebI7h5H");
+}
+
+static void test_filebNdBgt(void **state)
+{
+ _test_one_ndr_dump(state, "filebNdBgt");
+}
+
+static void test_filebOjK4H(void **state)
+{
+ _test_one_ndr_dump(state, "filebOjK4H");
+}
+
+static void test_filebzCPTH(void **state)
+{
+ _test_one_ndr_dump(state, "filebzCPTH");
+}
+
+
+
+
+int main(_UNUSED_ int argc, _UNUSED_ const char **argv)
+{
+ const struct CMUnitTest tests[] = {
+ cmocka_unit_test(test_fileb5iJt4),
+ cmocka_unit_test(test_fileb8cNVS),
+ cmocka_unit_test(test_filebI7h5H),
+ cmocka_unit_test(test_filebNdBgt),
+ cmocka_unit_test(test_filebOjK4H),
+ cmocka_unit_test(test_filebzCPTH),
+ };
+ if (isatty(1)) {
+ /*
+ * interactive testers can set debug level
+ * -- just give it a number.
+ */
+ int debug_level = DBGLVL_WARNING;
+ if (argc > 1) {
+ debug_level = atoi(argv[1]);
+ }
+ debuglevel_set(debug_level);
+
+ } else {
+ cmocka_set_message_output(CM_OUTPUT_SUBUNIT);
+ }
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/libcli/security/tests/test_run_conditional_ace.c b/libcli/security/tests/test_run_conditional_ace.c
new file mode 100644
index 0000000..dc02e33
--- /dev/null
+++ b/libcli/security/tests/test_run_conditional_ace.c
@@ -0,0 +1,730 @@
+/*
+ * Unit tests for conditional ACE SDDL.
+ *
+ * Copyright (C) Catalyst.NET Ltd 2023
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <setjmp.h>
+#include "cmocka.h"
+
+#include "lib/util/attr.h"
+#include "includes.h"
+#include "librpc/gen_ndr/ndr_security.h"
+#include "libcli/security/security.h"
+#include "libcli/security/conditional_ace.h"
+#include "librpc/gen_ndr/conditional_ace.h"
+#include "libcli/security/claims-conversions.h"
+
+#define debug_message(...) print_message(__VA_ARGS__)
+
+#define debug_fail(x, ...) print_message("\033[1;31m" x "\033[0m", __VA_ARGS__)
+#define debug_ok(x, ...) print_message("\033[1;32m" x "\033[0m", __VA_ARGS__)
+
+#define assert_ntstatus_equal(got, expected, comment) \
+ do { NTSTATUS __got = got, __expected = expected; \
+ if (!NT_STATUS_EQUAL(__got, __expected)) { \
+ print_message(": "#got" was %s, expected %s: %s", \
+ nt_errstr(__got), \
+ nt_errstr(__expected), comment); \
+ fail(); \
+ } \
+ } while(0)
+
+
+
+
+/*
+static void print_error_message(const char *sddl,
+ const char *message,
+ size_t message_offset)
+{
+ print_message("%s\n\033[1;33m %*c\033[0m\n", sddl,
+ (int)message_offset, '^');
+ print_message("%s\n", message);
+}
+*/
+static bool fill_token_claims(TALLOC_CTX *mem_ctx,
+ struct security_token *token,
+ const char *claim_type,
+ const char *name,
+ ...)
+{
+ va_list args;
+ va_start(args, name);
+ while (true) {
+ struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim = NULL;
+ const char *str = va_arg(args, const char *);
+ if (str == NULL) {
+ break;
+ }
+ claim = parse_sddl_literal_as_claim(mem_ctx,
+ name,
+ str);
+ if (claim == NULL) {
+ va_end(args);
+ debug_fail("bad claim: %s\n", str);
+ return false;
+ }
+ add_claim_to_token(mem_ctx, token, claim, claim_type);
+ }
+ va_end(args);
+ return true;
+}
+
+
+static bool fill_token_sids(TALLOC_CTX *mem_ctx,
+ struct security_token *token,
+ const char *owner,
+ ...)
+{
+ uint32_t *n = &token->num_sids;
+ struct dom_sid **list = NULL;
+ va_list args;
+ if (strcmp(owner, "device") == 0) {
+ n = &token->num_device_sids;
+ list = &token->device_sids;
+ } else if (strcmp(owner, "user") == 0) {
+ n = &token->num_sids;
+ list = &token->sids;
+ } else {
+ return false;
+ }
+
+ *n = 0;
+ va_start(args, owner);
+ while (true) {
+ struct dom_sid *sid = NULL;
+ const char *str = va_arg(args, const char *);
+ if (str == NULL) {
+ break;
+ }
+
+ sid = sddl_decode_sid(mem_ctx, &str, NULL);
+ if (sid == NULL) {
+ debug_fail("bad SID: %s\n", str);
+ va_end(args);
+ return false;
+ }
+ add_sid_to_array(mem_ctx, sid, list, n);
+ }
+ va_end(args);
+ return true;
+}
+
+
+static void test_device_claims_composite(void **state)
+{
+ TALLOC_CTX *mem_ctx = talloc_new(NULL);
+ struct security_token token = {
+ .evaluate_claims = CLAIMS_EVALUATION_ALWAYS
+ };
+ bool ok;
+ NTSTATUS status;
+ uint32_t access_granted = 0;
+ struct security_descriptor *sd = NULL;
+ const char *sddl = \
+ "D:(XA;;0x1f;;;AA;(@Device.colour == {\"orange\", \"blue\"}))";
+ ok = fill_token_sids(mem_ctx, &token,
+ "user",
+ "WD", "AA", NULL);
+ assert_true(ok);
+ ok = fill_token_claims(mem_ctx, &token,
+ "device", "colour",
+ "{\"orange\", \"blue\"}",
+ NULL);
+ assert_true(ok);
+ sd = sddl_decode(mem_ctx, sddl, NULL);
+ assert_non_null(sd);
+ status = se_access_check(sd, &token, 0x10, &access_granted);
+ assert_ntstatus_equal(status, NT_STATUS_OK, "access check failed\n");
+}
+
+
+static bool fill_sd(TALLOC_CTX *mem_ctx,
+ struct security_descriptor **sd,
+ const char *sddl)
+{
+ *sd = sddl_decode(mem_ctx, sddl, NULL);
+ return *sd != NULL;
+}
+
+#define USER_SIDS(...) \
+ assert_true(fill_token_sids(mem_ctx, &token, "user", __VA_ARGS__, NULL))
+
+#define DEVICE_SIDS(...) \
+ assert_true( \
+ fill_token_sids(mem_ctx, &token, "device", __VA_ARGS__, NULL))
+
+#define USER_CLAIMS(...) \
+ assert_true( \
+ fill_token_claims(mem_ctx, &token, "user", __VA_ARGS__, NULL))
+
+#define LOCAL_CLAIMS(...) \
+ assert_true(fill_token_claims(mem_ctx, \
+ &token, \
+ "local", \
+ __VA_ARGS__, \
+ NULL))
+
+#define DEVICE_CLAIMS(...) \
+ assert_true(fill_token_claims(mem_ctx, \
+ &token, \
+ "device", \
+ __VA_ARGS__, \
+ NULL))
+
+
+#define SD(sddl) assert_true(fill_sd(mem_ctx, &sd, sddl))
+#define SD_FAIL(sddl) assert_false(fill_sd(mem_ctx, &sd, sddl))
+
+#define ALLOW_CHECK(requested) \
+ do { \
+ NTSTATUS status; \
+ uint32_t access_granted = 0; \
+ status = se_access_check(sd, \
+ &token, \
+ requested, \
+ &access_granted); \
+ assert_ntstatus_equal(status, \
+ NT_STATUS_OK, \
+ "access not granted\n"); \
+ } while (0)
+
+
+#define DENY_CHECK(requested) \
+ do { \
+ NTSTATUS status; \
+ uint32_t access_granted = 0; \
+ status = se_access_check(sd, \
+ &token, \
+ requested, \
+ &access_granted); \
+ assert_ntstatus_equal(status, \
+ NT_STATUS_ACCESS_DENIED, \
+ "not denied\n"); \
+ } while (0)
+
+
+#define INIT() \
+ TALLOC_CTX *mem_ctx = talloc_new(NULL); \
+ struct security_token token = { \
+ .evaluate_claims = CLAIMS_EVALUATION_ALWAYS \
+ }; \
+ struct security_descriptor *sd = NULL;
+
+
+
+static void test_composite_different_order(void **state)
+{
+ INIT()
+ SD("D:(XA;;0x1f;;;AA;(@Device.colour == {\"orange\", \"blue\"}))");
+ USER_SIDS("WD", "AA");
+ DEVICE_CLAIMS("colour", "{\"blue\", \"orange\"}");
+ /*
+ * Claim arrays are sets, so we assume conditional ACE ones are too.
+ */
+ ALLOW_CHECK(0x10);
+}
+
+static void test_composite_different_order_with_dupes(void **state)
+{
+ INIT()
+ SD("D:(XA;;0x1f;;;AA;(@Device.colour == {\"orange\", \"blue\", \"orange\"}))");
+ USER_SIDS("WD", "AA");
+ DEVICE_CLAIMS("colour", "{\"orange\", \"blue\", \"orange\"}");
+ DENY_CHECK(0x10);
+}
+
+static void test_composite_different_order_with_dupes_in_composite(void **state)
+{
+ INIT()
+ SD("D:(XA;;0x1f;;;AA;(@Device.colour == {\"orange\", \"blue\", \"orange\"}))");
+ USER_SIDS("WD", "AA");
+ DEVICE_CLAIMS("colour", "{\"orange\", \"blue\"}");
+ ALLOW_CHECK(0x10);
+}
+
+static void test_composite_different_order_with_SID_dupes(void **state)
+{
+ INIT()
+ SD("D:(XA;;0x1f;;;AA;(@Device.colour == {SID(WD), SID(AA), SID(WD)}))");
+ USER_SIDS("WD", "AA");
+ DEVICE_CLAIMS("colour", "{SID(AA), SID(AA), SID(WD)}");
+ DENY_CHECK(0x10);
+}
+
+static void test_composite_different_order_with_SID_dupes_in_composite(void **state)
+{
+ INIT()
+ SD("D:(XA;;0x1f;;;AA;(@Device.colour == {SID(WD), SID(AA), SID(WD)}))");
+ USER_SIDS("WD", "AA");
+ DEVICE_CLAIMS("colour", "{SID(AA), SID(WD)}");
+ ALLOW_CHECK(0x10);
+}
+
+static void test_composite_mixed_types(void **state)
+{
+ /*
+ * If the conditional ACE composite has mixed types, it can
+ * never equal a claim, which only has one type.
+ */
+ INIT()
+ SD("D:(XA;;0x1f;;;AA;(@Device.colour == {2, SID(WD), SID(AA), SID(WD)}))");
+ USER_SIDS("WD", "AA");
+ DEVICE_CLAIMS("colour", "{SID(AA), SID(WD)}");
+ DENY_CHECK(0x10);
+}
+
+static void test_composite_mixed_types_different_last(void **state)
+{
+ /*
+ * If the conditional ACE composite has mixed types, it can
+ * never equal a claim, which only has one type.
+ */
+ INIT()
+ SD("D:(XA;;0x1f;;;AA;(@Device.colour == {SID(WD), SID(AA), 2}))");
+ USER_SIDS("WD", "AA");
+ DEVICE_CLAIMS("colour", "{SID(AA), SID(WD)}");
+ DENY_CHECK(0x10);
+}
+
+static void test_composite_mixed_types_deny(void **state)
+{
+ /*
+ * If the conditional ACE composite has mixed types, it can
+ * never equal a claim, which only has one type.
+ */
+ INIT()
+ SD("D:(XD;;0x1f;;;AA;(@Device.colour == {2, SID(WD), SID(AA), SID(WD)}))"
+ "(D;;;;;WD)");
+ USER_SIDS("WD", "AA");
+ DEVICE_CLAIMS("colour", "{SID(AA), SID(WD)}");
+ DENY_CHECK(0x10);
+}
+
+static void test_different_case(void **state)
+{
+ INIT()
+ SD("D:(XA;;0x1f;;;AA;(@Device.colour == {\"OraNgE\", \"BLuE\"}))");
+ USER_SIDS("WD", "AA");
+ DEVICE_CLAIMS("colour", "{\"orange\", \"blue\"}");
+ ALLOW_CHECK(0x10);
+}
+
+static void test_different_case_with_case_sensitive_flag(void **state)
+{
+ INIT()
+ SD("D:(XA;;0x1f;;;AA;(@Device.colour == {\"OraNgE\", \"BLuE\"}))");
+ USER_SIDS("WD", "AA");
+ DEVICE_CLAIMS("colour", "{\"orange\", \"blue\"}");
+ /* set the flag bit */
+ token.device_claims[0].flags = CLAIM_SECURITY_ATTRIBUTE_VALUE_CASE_SENSITIVE;
+ DENY_CHECK(0x10);
+}
+
+
+static void test_claim_name_different_case(void **state)
+{
+ INIT()
+ SD("D:(XA;;0x1f;;;AA;(@Device.Colour == {\"orange\", \"blue\"}))");
+ USER_SIDS("WD", "AA");
+ DEVICE_CLAIMS("colour", "{\"orange\", \"blue\"}");
+ ALLOW_CHECK(0x10);
+}
+
+static void test_claim_name_different_case_case_flag(void **state)
+{
+ INIT()
+ SD("D:(XA;;0x1f;;;AA;(@Device.Colour == {\"orange\", \"blue\"}))");
+ USER_SIDS("WD", "AA");
+ DEVICE_CLAIMS("colour", "{\"orange\", \"blue\"}");
+ /*
+ * The CASE_SENSITIVE flag is for the values, not the names.
+ */
+ token.device_claims[0].flags = CLAIM_SECURITY_ATTRIBUTE_VALUE_CASE_SENSITIVE;
+ ALLOW_CHECK(0x10);
+}
+
+static void test_more_values_not_equal(void **state)
+{
+ INIT()
+ SD("D:(XA;;0x1f;;;AA;(@Device.colour != {\"orange\", \"blue\", \"green\"}))");
+ USER_SIDS("WD", "AA");
+ DEVICE_CLAIMS("colour", "{\"orange\", \"blue\"}");
+ ALLOW_CHECK(0x10);
+}
+
+static void test_contains(void **state)
+{
+ INIT()
+ SD("D:(XA;;0x1f;;;AA;(@Device.colour Contains {\"orange\", \"blue\"}))");
+ USER_SIDS("WD", "AA");
+ DEVICE_CLAIMS("colour", "{\"orange\", \"blue\"}");
+ ALLOW_CHECK(0x10);
+}
+
+static void test_contains_incomplete(void **state)
+{
+ INIT()
+ SD("D:(XA;;0x1f;;;AA;(@Device.colour Contains {\"orange\", \"blue\", \"red\"}))");
+ USER_SIDS("WD", "AA");
+ DEVICE_CLAIMS("colour", "{\"orange\", \"blue\"}");
+ DENY_CHECK(0x10);
+}
+
+static void test_any_of(void **state)
+{
+ INIT()
+ SD("D:(XA;;0x1f;;;AA;(@Device.colour Any_of {\"orange\", \"blue\", \"red\"}))");
+ USER_SIDS("WD", "AA");
+ DEVICE_CLAIMS("colour", "{\"orange\", \"blue\"}");
+ ALLOW_CHECK(0x10);
+}
+
+static void test_any_of_match_last(void **state)
+{
+ INIT()
+ SD("D:(XA;;0x1f;;;AA;(@Device.colour Any_of {\"a\", \"b\", \"blue\"}))");
+ USER_SIDS("WD", "AA");
+ DEVICE_CLAIMS("colour", "{\"orange\", \"blue\"}");
+ ALLOW_CHECK(0x10);
+}
+
+static void test_any_of_1(void **state)
+{
+ INIT()
+ SD("D:(XA;;0x1f;;;AA;(@Device.colour Any_of\"blue\"))");
+ USER_SIDS("WD", "AA");
+ DEVICE_CLAIMS("colour", "{\"orange\", \"blue\"}");
+ ALLOW_CHECK(0x10);
+}
+
+static void test_contains_1(void **state)
+{
+ INIT()
+ SD("D:(XA;;0x1f;;;AA;(@Device.colour Contains \"blue\"))");
+ USER_SIDS("WD", "AA");
+ DEVICE_CLAIMS("colour", "{\"orange\", \"blue\"}");
+ ALLOW_CHECK(0x10);
+}
+
+static void test_contains_1_fail(void **state)
+{
+ INIT()
+ SD("D:(XA;;0x1f;;;AA;(@Device.colour Contains \"pink\"))");
+ USER_SIDS("WD", "AA");
+ DEVICE_CLAIMS("colour", "{\"orange\", \"blue\"}");
+ DENY_CHECK(0x10);
+}
+
+static void test_any_of_1_fail(void **state)
+{
+ INIT()
+ SD("D:(XA;;0x1f;;;AA;(@Device.colour Any_of \"pink\"))");
+ USER_SIDS("WD", "AA");
+ DEVICE_CLAIMS("colour", "{\"orange\", \"blue\"}");
+ DENY_CHECK(0x10);
+}
+
+
+static void test_not_any_of_1_fail(void **state)
+{
+ INIT()
+ SD("D:(XA;;0x1f;;;AA;(@Device.colour Not_Any_of\"blue\"))");
+ USER_SIDS("WD", "AA");
+ DEVICE_CLAIMS("colour", "{\"orange\", \"blue\"}");
+ DENY_CHECK(0x10);
+}
+
+static void test_not_any_of_composite_1(void **state)
+{
+ INIT()
+ SD("D:(XA;;0x1f;;;AA;(@Device.colour Not_Any_of{\"blue\"}))");
+ USER_SIDS("WD", "AA");
+ DEVICE_CLAIMS("colour", "{\"orange\", \"blue\"}");
+ DENY_CHECK(0x10);
+}
+
+static void test_not_contains_1_fail(void **state)
+{
+ INIT()
+ SD("D:(XA;;0x1f;;;AA;(@Device.colour Not_Contains \"blue\"))");
+ USER_SIDS("WD", "AA");
+ DEVICE_CLAIMS("colour", "{\"orange\", \"blue\"}");
+ DENY_CHECK(0x10);
+}
+
+static void test_not_contains_1(void **state)
+{
+ INIT()
+ SD("D:(XA;;0x1f;;;AA;(@Device.colour Not_Contains \"pink\"))");
+ USER_SIDS("WD", "AA");
+ DEVICE_CLAIMS("colour", "{\"orange\", \"blue\"}");
+ ALLOW_CHECK(0x10);
+}
+
+static void test_not_any_of_1(void **state)
+{
+ INIT()
+ SD("D:(XA;;0x1f;;;AA;(@Device.colour Not_Any_of \"pink\"))");
+ USER_SIDS("WD", "AA");
+ DEVICE_CLAIMS("colour", "{\"orange\", \"blue\"}");
+ ALLOW_CHECK(0x10);
+}
+
+static void test_not_Not_Any_of_1(void **state)
+{
+ INIT()
+ SD("D:(XA;;0x1f;;;AA;(!(@Device.colour Not_Any_of \"pink\")))");
+ USER_SIDS("WD", "AA");
+ DEVICE_CLAIMS("colour", "{\"orange\", \"blue\"}");
+ DENY_CHECK(0x10);
+}
+
+static void test_not_Not_Contains_1(void **state)
+{
+ INIT()
+ SD("D:(XA;;0x1f;;;AA;(! (@Device.colour Not_Contains \"blue\")))");
+ USER_SIDS("WD", "AA");
+ DEVICE_CLAIMS("colour", "{\"orange\", \"blue\"}");
+ ALLOW_CHECK(0x10);
+}
+
+
+static void test_not_not_Not_Member_of(void **state)
+{
+ INIT();
+ SD("D:(XA;;0x1f;;;AA;(!(!(Not_Member_of{SID(BA)}))))");
+ USER_SIDS("WD", "AA");
+ DEVICE_SIDS("BA", "BG");
+ ALLOW_CHECK(0x10);
+}
+
+static void test_not_not_Not_Member_of_fail(void **state)
+{
+ INIT();
+ SD("D:(XA;;0x1f;;;AA;(!(!(Not_Member_of{SID(AA)}))))");
+ USER_SIDS("WD", "AA");
+ DEVICE_SIDS("BA", "BG");
+ DENY_CHECK(0x10);
+}
+
+static void test_not_not_not_not_not_not_not_not_not_not_Not_Member_of(void **state)
+{
+ INIT();
+ SD("D:(XA;;0x1f;;;AA;(!(!(!( !(!(!( !(!(!( "
+ "Not_Member_of{SID(AA)})))))))))))");
+ USER_SIDS("WD", "AA");
+ DEVICE_SIDS("BA", "BG");
+ ALLOW_CHECK(0x10);
+}
+
+
+static void test_Device_Member_of_and_Member_of(void **state)
+{
+ INIT();
+ USER_SIDS("WD", "AA");
+ DEVICE_SIDS("BA", "BG");
+ SD("D:(XA;;0x1f;;;AA;"
+ "(Device_Member_of{SID(BA)} && Member_of{SID(WD)}))");
+ ALLOW_CHECK(0x10);
+}
+
+
+static void test_Device_claim_contains_Resource_claim(void **state)
+{
+ INIT();
+ USER_SIDS("WD", "AA");
+ DEVICE_CLAIMS("colour", "\"blue\"");
+ SD("D:(XA;;0x1f;;;AA;(@Device.colour Contains @Resource.colour))"
+ "S:(RA;;;;;WD;(\"colour\",TS,0,\"blue\"))");
+ ALLOW_CHECK(0x10);
+}
+
+
+static void test_device_claim_contains_resource_claim(void **state)
+{
+ INIT();
+ USER_SIDS("WD", "AA");
+ DEVICE_CLAIMS("colour", "\"blue\"");
+ SD("D:(XA;;0x1f;;;AA;(@Device.colour Contains @Resource.colour))"
+ "S:(RA;;;;;WD;(\"colour\",TS,0,\"blue\"))");
+ ALLOW_CHECK(0x10);
+}
+
+static void test_device_claim_eq_resource_claim(void **state)
+{
+ INIT();
+ USER_SIDS("WD", "AA");
+ DEVICE_CLAIMS("colour", "\"blue\"");
+ SD("D:(XA;;0x1f;;;AA;(@Device.colour == @Resource.colour))"
+ "S:(RA;;;;;WD;(\"colour\",TS,0,\"blue\"))");
+ ALLOW_CHECK(0x10);
+}
+
+static void test_user_claim_eq_device_claim(void **state)
+{
+ INIT();
+ USER_SIDS("WD", "AA");
+ USER_CLAIMS("colour", "\"blue\"");
+ DEVICE_CLAIMS("colour", "\"blue\"");
+ SD("D:(XA;;0x1f;;;AA;(@User.colour == @Device.colour))");
+ ALLOW_CHECK(0x10);
+}
+
+static void test_device_claim_eq_resource_claim_2(void **state)
+{
+ INIT();
+ USER_SIDS("WD", "AA");
+ DEVICE_CLAIMS("colour", "{\"orange\", \"blue\"}");
+ SD("D:(XA;;0x1f;;;AA;(@Device.colour == {\"orange\", \"blue\"}))");
+ ALLOW_CHECK(0x10);
+}
+
+static void test_resource_ace_multi(void **state)
+{
+ INIT();
+ USER_SIDS("WD", "AA");
+ DEVICE_CLAIMS("colour", "{\"blue\", \"red\"}");
+ SD("D:(XA;;0x1f;;;AA;(@Device.colour Contains @Resource.colour))"
+ "S:(RA;;;;;WD;(\"colour\",TS,0,\"blue\", \"red\"))");
+ ALLOW_CHECK(0x10);
+}
+
+static void test_resource_ace_multi_any_of(void **state)
+{
+ INIT();
+ USER_SIDS("WD", "AA");
+ DEVICE_CLAIMS("colour", "\"blue\"");
+ SD("D:(XA;;0x1f;;;AA;(@Device.colour Any_of @Resource.colour))"
+ "S:(RA;;;;;WD;(\"colour\",TS,0,\"grue\", \"blue\", \"red\"))");
+ ALLOW_CHECK(0x10);
+}
+
+static void test_horrible_fuzz_derived_test_3(void **state)
+{
+ INIT();
+ USER_SIDS("WD", "AA", "IS");
+ SD_FAIL("S:PPD:(XA;OI;0x1;;;IS;(q>))");
+}
+
+static void test_resource_ace_single(void **state)
+{
+ INIT();
+ USER_SIDS("WD", "AA");
+ DEVICE_CLAIMS("colour", "\"blue\"");
+ SD("D:(XA;;0x1f;;;AA;(@Device.colour Contains @Resource.colour))"
+ "S:(RA;;;;;WD;(\"colour\",TS,0,\"blue\"))");
+ ALLOW_CHECK(0x10);
+}
+
+
+static void test_user_attr_any_of_missing_resource_and_user_attr(void **state)
+{
+ INIT();
+ USER_SIDS("WD", "AA");
+ DEVICE_CLAIMS("colour", "\"blue\"");
+ SD("D:(XD;;FX;;;S-1-1-0;(@User.Project Any_of @Resource.Project))");
+ DENY_CHECK(0x10);
+}
+
+static void test_user_attr_any_of_missing_resource_attr(void **state)
+{
+ INIT();
+ USER_SIDS("WD", "AA");
+ USER_CLAIMS("Project", "3");
+ SD("D:(XD;;FX;;;S-1-1-0;(@User.Project Any_of @Resource.Project))");
+ DENY_CHECK(0x10);
+}
+
+static void test_user_attr_any_of_missing_user_attr(void **state)
+{
+ INIT();
+ USER_SIDS("WD", "AA");
+ SD("D:(XD;;FX;;;S-1-1-0;(@User.Project Any_of @Resource.Project))"
+ "S:(RA;;;;;WD;(\"Project\",TX,0,1234))");
+ DENY_CHECK(0x10);
+}
+
+
+int main(_UNUSED_ int argc, _UNUSED_ const char **argv)
+{
+ const struct CMUnitTest tests[] = {
+ cmocka_unit_test(test_user_attr_any_of_missing_resource_and_user_attr),
+ cmocka_unit_test(test_user_attr_any_of_missing_resource_attr),
+ cmocka_unit_test(test_user_attr_any_of_missing_user_attr),
+ cmocka_unit_test(test_composite_mixed_types),
+ cmocka_unit_test(test_composite_mixed_types_different_last),
+ cmocka_unit_test(test_composite_mixed_types_deny),
+ cmocka_unit_test(test_composite_different_order_with_SID_dupes),
+ cmocka_unit_test(test_composite_different_order_with_SID_dupes_in_composite),
+ cmocka_unit_test(test_device_claim_eq_resource_claim_2),
+ cmocka_unit_test(test_not_Not_Any_of_1),
+ cmocka_unit_test(test_not_any_of_composite_1),
+ cmocka_unit_test(test_resource_ace_single),
+ cmocka_unit_test(test_horrible_fuzz_derived_test_3),
+ cmocka_unit_test(test_Device_Member_of_and_Member_of),
+ cmocka_unit_test(test_resource_ace_multi),
+ cmocka_unit_test(test_resource_ace_multi_any_of),
+ cmocka_unit_test(test_user_claim_eq_device_claim),
+ cmocka_unit_test(test_device_claim_contains_resource_claim),
+ cmocka_unit_test(test_device_claim_eq_resource_claim),
+ cmocka_unit_test(test_Device_claim_contains_Resource_claim),
+ cmocka_unit_test(test_not_Not_Contains_1),
+ cmocka_unit_test(test_not_not_Not_Member_of_fail),
+ cmocka_unit_test(test_not_not_Not_Member_of),
+ cmocka_unit_test(test_not_not_not_not_not_not_not_not_not_not_Not_Member_of),
+ cmocka_unit_test(test_not_any_of_1_fail),
+ cmocka_unit_test(test_not_any_of_1),
+ cmocka_unit_test(test_not_contains_1),
+ cmocka_unit_test(test_not_contains_1_fail),
+ cmocka_unit_test(test_any_of_1_fail),
+ cmocka_unit_test(test_any_of_1),
+ cmocka_unit_test(test_any_of),
+ cmocka_unit_test(test_any_of_match_last),
+ cmocka_unit_test(test_contains_incomplete),
+ cmocka_unit_test(test_contains),
+ cmocka_unit_test(test_contains_1),
+ cmocka_unit_test(test_contains_1_fail),
+ cmocka_unit_test(test_device_claims_composite),
+ cmocka_unit_test(test_claim_name_different_case),
+ cmocka_unit_test(test_claim_name_different_case_case_flag),
+ cmocka_unit_test(test_different_case_with_case_sensitive_flag),
+ cmocka_unit_test(test_composite_different_order),
+ cmocka_unit_test(test_different_case),
+ cmocka_unit_test(test_composite_different_order_with_dupes),
+ cmocka_unit_test(test_composite_different_order_with_dupes_in_composite),
+ cmocka_unit_test(test_more_values_not_equal),
+ };
+ if (isatty(1)) {
+ /*
+ * interactive testers can set debug level
+ * -- just give it a number.
+ */
+ int debug_level = DBGLVL_WARNING;
+ if (argc > 1) {
+ debug_level = atoi(argv[1]);
+ }
+ debuglevel_set(debug_level);
+
+ } else {
+ cmocka_set_message_output(CM_OUTPUT_SUBUNIT);
+ }
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/libcli/security/tests/test_sddl_conditional_ace.c b/libcli/security/tests/test_sddl_conditional_ace.c
new file mode 100644
index 0000000..fc9281d
--- /dev/null
+++ b/libcli/security/tests/test_sddl_conditional_ace.c
@@ -0,0 +1,1003 @@
+/*
+ * Unit tests for conditional ACE SDDL.
+ *
+ * Copyright (C) Catalyst.NET Ltd 2023
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <setjmp.h>
+#include "cmocka.h"
+
+#include "lib/util/attr.h"
+#include "includes.h"
+#include "librpc/gen_ndr/ndr_security.h"
+#include "libcli/security/security.h"
+#include "libcli/security/conditional_ace.h"
+#include "librpc/gen_ndr/conditional_ace.h"
+
+/*
+ * Some of the test strings break subunit, so we only print those if
+ * stdout is a terminal.
+ */
+#define debug_message(...) do { \
+ if (isatty(1)) { \
+ print_message(__VA_ARGS__); \
+ } \
+ } while(0)
+
+#define debug_fail(x, ...) debug_message("\033[1;31m" x "\033[0m", __VA_ARGS__)
+#define debug_ok(x, ...) debug_message("\033[1;32m" x "\033[0m", __VA_ARGS__)
+
+#define ACEINT64(x, b, s) CONDITIONAL_ACE_TOKEN_INT64, \
+ (x & 0xff), ((x >> 8) & 0xff), ((x >> 16) & 0xff), \
+ ((x >> 24) & 0xff), (((uint64_t)x >> 32) & 0xff), (((uint64_t)x >> 40) & 0xff), \
+ (((uint64_t)x >> 48) & 0xff), (((uint64_t)x >> 56) & 0xff), b, s
+
+
+static void print_error_message(const char *sddl,
+ const char *message,
+ size_t message_offset)
+{
+ print_message("%s\n\033[1;33m %*c\033[0m\n", sddl,
+ (int)message_offset, '^');
+ print_message("%s\n", message);
+}
+
+static void test_sddl_compile(void **state)
+{
+ /*
+ * Example codes:
+ *
+ * CONDITIONAL_ACE_LOCAL_ATTRIBUTE, 2,0,0,0, 'x',0,
+ * ^attr byte code ^ ^
+ * 32 bit little-endian length |
+ * utf-16, little endian
+ *
+ * CONDITIONAL_ACE_TOKEN_EQUAL
+ * ^ op byte code with no following data
+ */
+ static const char *sddl = "(x==41 &&(x >@device.x ) )";
+ static const uint8_t ace[] = {
+ 'a', 'r', 't', 'x',
+ CONDITIONAL_ACE_LOCAL_ATTRIBUTE, 2, 0, 0, 0, 'x', 0,
+ ACEINT64(41,
+ CONDITIONAL_ACE_INT_SIGN_NONE,
+ CONDITIONAL_ACE_INT_BASE_10),
+ CONDITIONAL_ACE_TOKEN_EQUAL,
+ CONDITIONAL_ACE_LOCAL_ATTRIBUTE, 2, 0, 0, 0, 'x', 0,
+ CONDITIONAL_ACE_DEVICE_ATTRIBUTE, 2, 0, 0, 0, 'x', 0,
+ CONDITIONAL_ACE_TOKEN_GREATER_THAN,
+ CONDITIONAL_ACE_TOKEN_AND, 0,0,0,0,
+ };
+
+ size_t i;
+ TALLOC_CTX *mem_ctx = talloc_new(NULL);
+ struct ace_condition_script *s = NULL;
+ const char *message = NULL;
+ size_t message_offset;
+ bool ok;
+ DATA_BLOB compiled;
+ size_t length;
+
+ s = ace_conditions_compile_sddl(mem_ctx,
+ ACE_CONDITION_FLAG_ALLOW_DEVICE,
+ sddl,
+ &message,
+ &message_offset,
+ &length);
+ if (message != NULL) {
+ print_error_message(sddl, message, message_offset);
+ }
+ if (s == NULL) {
+ debug_fail("%s\n", sddl);
+ fail();
+ }
+
+ ok = conditional_ace_encode_binary(mem_ctx, s, &compiled);
+ assert_true(ok);
+
+ assert_true(compiled.length <= ARRAY_SIZE(ace));
+ for (i = 0; i < compiled.length; i++) {
+ assert_int_equal(compiled.data[i], ace[i]);
+ }
+}
+
+static void test_sddl_compile2(void **state)
+{
+ /* this one is from Windows, not hand-calculated */
+ static const char *sddl = "(@USER.Project Any_of 1))";
+ static const uint8_t ace[] = ("artx\xf9\x0e\x00\x00\x00P\x00r"
+ "\x00o\x00j\x00""e\x00""c\x00t\x00"
+ "\x04\x01\x00\x00\x00\x00\x00\x00"
+ "\x00\x03\x02\x88\x00");
+ size_t i;
+ TALLOC_CTX *mem_ctx = talloc_new(NULL);
+ struct ace_condition_script *s = NULL;
+ const char *message = NULL;
+ size_t message_offset;
+ bool ok;
+ DATA_BLOB compiled;
+ size_t length;
+
+ s = ace_conditions_compile_sddl(mem_ctx,
+ ACE_CONDITION_FLAG_ALLOW_DEVICE,
+ sddl,
+ &message,
+ &message_offset,
+ &length);
+ if (message != NULL) {
+ print_error_message(sddl, message, message_offset);
+ }
+ if (s == NULL) {
+ debug_fail("%s\n", sddl);
+ fail();
+ }
+
+ ok = conditional_ace_encode_binary(mem_ctx, s, &compiled);
+ assert_true(ok);
+
+ assert_true(compiled.length <= ARRAY_SIZE(ace));
+ for (i = 0; i < compiled.length; i++) {
+ assert_int_equal(compiled.data[i], ace[i]);
+ }
+}
+
+static void test_full_sddl_compile(void **state)
+{
+ /*
+ * This one is from Windows, and annotated by hand.
+ *
+ * We have the bytes of a full security descriptor, in
+ * "relative" form, which is the same as the its NDR
+ * representation.
+ *
+ * *In general* we can't necessarily assert that Samba's NDR
+ * will be the same as Windows, because they could e.g. put
+ * the two ACLs in the reverse order which is also legitimate
+ * (there are hints this may vary on Windows). But in this
+ * particular case Samba and the Windows 2022 sample agree, so
+ * we can compare the bytes here.
+ *
+ * We can assert that unpacking these bytes as a security
+ * descriptor should succeed and give us exactly the same
+ * descriptor as parsing the SDDL.
+ */
+ TALLOC_CTX *mem_ctx = talloc_new(NULL);
+ struct security_descriptor sec_desc_windows = {};
+ struct security_descriptor *sec_desc_samba = NULL;
+ DATA_BLOB sd_ndr = {};
+ DATA_BLOB sd_win_push = {};
+ DATA_BLOB sd_samba_push = {};
+ bool ok;
+ enum ndr_err_code ndr_err;
+ const char *sddl = "D:(XA;;CCDCLCSWRPWP;;;MP;"\
+ "(@RESOURCE.c))S:(RA;;;;;WD;(\"colOIr\",TU,0xe,29925))";
+
+ uint8_t sd_bytes[] = {
+ 1, /* 0 version */
+ 0, /* 1 reserved */
+ 20, 128, /* 2 control */
+ 0, 0, 0, 0, /* 4 owner (null relative pointer == no owner) */
+ 0, 0, 0, 0, /* 8 group */
+ 20, 0, 0, 0,/* 12 SACL */
+ 92, 0, 0, 0,/* 16 DACL, i.e. pointer to 92 below */
+
+ /* 20 SACL (from pointer above) */
+ 4, /* 20 revision (ADS) */
+ 0, /* 21 reserved */
+ 72, 0, /* 22 size --> takes us to 92 */
+ 1, 0, /* 24 ace count */
+ 0, 0, /* 26 reserved */
+
+ /* now come SACL aces, of which there should be one */
+ 18, /* 28 ace type (SEC_ACE_TYPE_SYSTEM_RESOURCE_ATTRIBUTE) */
+ 0, /* 29 ace flags */
+ 64, 0, /* 30 ace size (from start of ACE, again adds to ending at 92) */
+ 0, 0, 0, 0, /* 32 mask */
+
+ /* here's the ACE SID */
+ 1, /* 36 revision */
+ 1, /* 37 sub-auth count */
+ 0, 0, 0, 0, 0, 1, /* 38 big endian ident auth */
+ 0, 0, 0, 0, /* 44 the sub-auth (so SID is S-1-1-0 (everyone), mandatory with RA ace) */
+
+ /* here starts the actual claim, at 48 */
+ 20, 0, 0, 0, /* 48 pointer to name (relative to claim, at 68) */
+ 2, 0, /* 52 value type (uint64) */
+ 0, 0, /* 54 reserved */
+ 14, 0, 0, 0, /* 56 flags (case-sensitive|deny-only|disabled-by-default -- the "0xe" in the SDDL) */
+ 1, 0, 0, 0, /* 60 value count */
+ 34, 0, 0, 0, /* 64 array of pointers, 1-long, points to 48 + 34 == 82 */
+ /* 68 utf-16 letters "colOIr\0", indicated by name pointer at 48 */
+ 'c', 0,
+ 'o', 0,
+ 'l', 0,
+ 'O', 0, /* unlike conditional ACE strings, this is nul-terminated. */
+ 'I', 0, /* where does the next thing start: */
+ 'r', 0, /* 6 letters + '\0' * 2 = 14. 68 + 14 = 82 */
+ 0, 0,
+ /* 82 is the value pointed to at 64 above (LE uint64) */
+ 229, 116, 0, 0, 0, 0, 0, 0, /* this equals 229 + 116 * 256 == 29925, as we see in the SDDL. */
+
+ /* 88 the claim has ended. the ace has NEARLY ended, but we need to round up: */
+
+ 0, 0, /* 90 two bytes of padding to get to a multiple of 4. */
+ /* The ace and SACL have ended */
+
+ /* 92 the DACL starts. */
+ 2, /* 92 version (NT) */
+ 0, /* 93 reserved */
+ 40, 0, /* 94 size */
+ 1, 0, /* 96 ace count */
+ 0, 0, /* 98 reserved */
+ /* 100 the DACL aces start */
+ 9, /* 100 ace type (SEC_ACE_TYPE_ACCESS_ALLOWED_CALLBACK) */
+ 0, /* 101 flags */
+ 32, 0, /* 102 ace size (ending at 132) */
+ 63, 0, 0, 0, /* 104 mask (let's assume CCDCLCSWRPWP as in sddl, not checked, but it's the right number of bits) */
+ /* 108 the ACE sid */
+ 1, /* 108 version */
+ 1, /* 109 sub-auths */
+ 0, 0, 0, 0, 0, 16,/* 110 bigendian 16 identauth */
+ 0, 33, 0, 0, /* 116 sub-auth 1, 33 << 8 == 8448; "S-1-16-8448" == "ML_MEDIUM_PLUS" == "MP" */
+ /* 120 here starts the callback */
+ 97, 114, 116, 120, /* 120 'artx' */
+ 250, /* 124 0xfa CONDITIONAL_ACE_RESOURCE_ATTRIBUTE token */
+ 2, 0, 0, 0, /* 125 length 2 (bytes) */
+ 'c', 0, /* 129 utf-16 "c" -- NOT nul-terminated */
+ 0 /* 131 padding to bring length to a multiple of 4 (132) */
+ };
+ sd_ndr.length = 132;
+ sd_ndr.data = sd_bytes;
+
+ sec_desc_samba = sddl_decode(mem_ctx, sddl, NULL);
+ assert_non_null(sec_desc_samba);
+ ndr_err = ndr_pull_struct_blob(
+ &sd_ndr, mem_ctx, &sec_desc_windows,
+ (ndr_pull_flags_fn_t)ndr_pull_security_descriptor);
+
+ assert_true(NDR_ERR_CODE_IS_SUCCESS(ndr_err));
+
+ /*
+ * look, we munge the DACL version byte before comparing,
+ * because Samba currently always does version 4.
+ */
+ sec_desc_windows.dacl->revision = SECURITY_ACL_REVISION_ADS;
+ sd_bytes[92] = SECURITY_ACL_REVISION_ADS;
+
+ /* push the structures back into blobs for 3-way comparisons. */
+ ndr_err = ndr_push_struct_blob(
+ &sd_win_push, mem_ctx,
+ &sec_desc_windows,
+ (ndr_push_flags_fn_t)ndr_push_security_descriptor);
+ assert_true(NDR_ERR_CODE_IS_SUCCESS(ndr_err));
+
+ ndr_err = ndr_push_struct_blob(
+ &sd_samba_push, mem_ctx,
+ sec_desc_samba,
+ (ndr_push_flags_fn_t)ndr_push_security_descriptor);
+ assert_true(NDR_ERR_CODE_IS_SUCCESS(ndr_err));
+
+ assert_int_equal(sd_samba_push.length, sd_win_push.length);
+ assert_int_equal(sd_samba_push.length, sd_ndr.length);
+ assert_memory_equal(sd_samba_push.data,
+ sd_win_push.data,
+ sd_win_push.length);
+ assert_memory_equal(sd_win_push.data,
+ sd_ndr.data,
+ sd_ndr.length);
+
+ ok = security_descriptor_equal(sec_desc_samba, &sec_desc_windows);
+ assert_true(ok);
+ talloc_free(mem_ctx);
+}
+
+
+static void debug_conditional_ace_stderr(TALLOC_CTX *mem_ctx,
+ struct ace_condition_script *program)
+{
+ char * debug_string = debug_conditional_ace(mem_ctx, program);
+
+ if (debug_string != NULL) {
+ fputs(debug_string, stderr);
+ TALLOC_FREE(debug_string);
+ } else {
+ print_message("failed to debug!\n");
+ }
+}
+
+
+static void test_full_sddl_ra_encode(void **state)
+{
+ /*
+ * This is an example from Windows that Samba once had trouble
+ * with.
+ */
+ bool ok;
+ enum ndr_err_code ndr_err;
+ char *sddl = NULL;
+ struct dom_sid domain_sid;
+ uint8_t win_bytes[] = {
+ 0x01, 0x00, 0x14, 0x80, /* descriptor header */
+ 0x00, 0x00, 0x00, 0x00, /* NULL owner pointer */
+ 0x00, 0x00, 0x00, 0x00, /* NULL group pointer */
+ 0x14, 0x00, 0x00, 0x00, /* SACL at 0x14 (20) */
+ 0x58, 0x01, 0x00, 0x00, /* DACL at 0x158 (344) */
+ /* SACL starts here (20) */
+ 0x02, 0x00, /* rev 2, NT */
+ 0x44, 0x01, /* size 0x0144 (324) -- ends at 344 */
+ 0x01, 0x00, /* ace count */
+ 0x00, 0x00, /* reserved */
+ /* ace starts here, 28 */
+ 0x12, 0x00, /* ace type, flags: 0x12(18) is resource attribute */
+ 0x3c, 0x01, /* ACE size 0x13c == 316, from ACE start, end at 344 */
+ 0x00, 0x00, 0x00, 0x00, /*ACE mask */
+ 0x01, 0x01, /* SID S-1-<identauth>-<1 subauth>) */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, /* -1- indent auth */
+ 0x00, 0x00, 0x00, 0x00, /* -0 -> S-1-1-0, world */
+ /* claim starts here, 48 */
+ 0x28, 0x00, 0x00, 0x00, /* pointer to name 40 (from claim start 48) = 88 */
+ 0x10, 0x00, /* type octet string */
+ 0x00, 0x00, /* empty */
+ 0x00, 0x00, 0x00, 0x00, /* zero flags */
+ 0x06, 0x00, 0x00, 0x00, /* value count */
+ /* array of 6 value pointers (at claim + 16, 64) */
+ 0xf2, 0x00, 0x00, 0x00, /* value 0xf2 = 242 from claim (48) == 290 */
+ 0xf8, 0x00, 0x00, 0x00, /* 0xf8, 248 */
+ 0x0d, 0x01, 0x00, 0x00, /* 0x10d, 269 */
+ 0x14, 0x01, 0x00, 0x00, /* 0x114, 276 */
+ 0x1a, 0x01, 0x00, 0x00, /* 0x11a, 282 */
+ 0x21, 0x01, 0x00, 0x00, /* 0x121, 289 */
+ /* here's the name, at 88 */
+ 'c', 0x00,
+ 'o', 0x00,
+ 'l', 0x00,
+ 'O', 0x00,
+ 'I', 0x00,
+ 'r', 0x00, /* the following lines are all \x16 */
+ /* 100 */
+ 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00,
+ 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00,
+ 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00,
+ 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00,
+ 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00,
+ /* 150 */
+ 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00,
+ 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00,
+ 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00,
+ 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00,
+ 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00,
+ /* 200 */
+ 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00,
+ 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00,
+ 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00,
+ 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00,
+ 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00,
+ /* 250 */
+ 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00,
+ 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00,
+ 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00,
+ /* 280 */
+ 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, /* 286 */
+ 'r', 0x00,
+ 0x00, 0x00, /* name is nul-terminated */
+ /* 290, first octet string blob */
+ 0x02, 0x00, 0x00, 0x00, /* length 2 */
+ 0x00, 0x77, /* 2 blob bytes */
+ /* second blob @ 48 + 248 == 296 */
+ 0x11, 0x00, 0x00, 0x00, /* length 0x11 = 17 */
+ 0x00, 0x77, 0x77, 0x71, 0x83, 0x68, 0x96, 0x62, 0x95, 0x93,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07,
+ /* third blob at 269 + 48 == 317 */
+ 0x03, 0x00, 0x00, 0x00,
+ 0x00, 0x77, 0x77,
+ /* fourth blob, 276 + 48 == 324 */
+ 0x02, 0x00, 0x00, 0x00,
+ 0x00, 0x77,
+ /* fifth blob, 282 + 48 == 330 */
+ 0x03, 0x00, 0x00, 0x00,
+ 0x00, 0x77, 0x77,
+ /* last blob 289 + 48 == 337 */
+ 0x03, 0x00, 0x00, 0x00,
+ 0x00, 0x77, 0x77,
+ /* claim ends */
+ /* 344 DACL starts */
+ 0x02, 0x00, /* rev 2 (NT) */
+ 0x28, 0x00, /* size 40, ending at 384 */
+ 0x01, 0x00, /* ace count */
+ 0x00, 0x00,
+ /* ACE starts here, 352 */
+ 0x09, 0x00, /* type 9, access allowed callback */
+ 0x20, 0x00, /* size 32 */
+ 0x3f, 0x00, 0x00, 0x00, /*mask */
+ 0x01, 0x01, /* S-1-... (1 subauth) */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, /*...-16-...*/
+ 0x00, 0x21, 0x00, 0x00, /* -5356. S-1-16-5376 */
+ 'a', 'r', 't', 'x',
+ 0xfa, /* resource attr */
+ 0x02, 0x00, 0x00, 0x00, /*name is 2 bytes long (i.e. 1 UTF-16) */
+ 'c', 0x00, /* name is "c" */
+ /* here we're at 383, but need to round to a multiple of 4 with zeros: */
+ 0x00
+ };
+ DATA_BLOB win_blob = {
+ .data = win_bytes,
+ .length = sizeof(win_bytes)
+ };
+
+ TALLOC_CTX *mem_ctx = talloc_new(NULL);
+ struct security_descriptor sec_desc_windows = {};
+ struct security_descriptor *sec_desc_samba = NULL;
+
+ ndr_err = ndr_pull_struct_blob(
+ &win_blob, mem_ctx, &sec_desc_windows,
+ (ndr_pull_flags_fn_t)ndr_pull_security_descriptor);
+ assert_true(NDR_ERR_CODE_IS_SUCCESS(ndr_err));
+
+ string_to_sid(&domain_sid, "S-1-2-3");
+ sddl = sddl_encode(mem_ctx, &sec_desc_windows, &domain_sid);
+ assert_non_null(sddl);
+ sec_desc_samba = sddl_decode(mem_ctx, sddl, &domain_sid);
+
+ /* hack the acl revision numbers */
+ sec_desc_windows.dacl->revision = SECURITY_ACL_REVISION_ADS;
+ sec_desc_windows.sacl->revision = SECURITY_ACL_REVISION_ADS;
+ ok = security_descriptor_equal(sec_desc_samba, &sec_desc_windows);
+ assert_true(ok);
+ talloc_free(mem_ctx);
+}
+
+
+static void test_full_sddl_ra_escapes(void **state)
+{
+ /*
+ * This is the security descriptor described in
+ * test_full_sddl_ra_encode(), with SDDL.
+ */
+ enum ndr_err_code ndr_err;
+ const char *sddl = (
+ "D:(XA;;CCDCLCSWRPWP;;;MP;(@RESOURCE.c))S:(RA;;;;;WD;(\""
+ "colOIr%0016%0016%0016%0016%0016%0016%0016%0016%0016%0016"
+ "%0016%0016%0016%0016%0016%0016%0016%0016%0016%0016%0016"
+ "%0016%0016%0016%0016%0016%0016%0016%0016%0016%0016%0016"
+ "%0016%0016%0016%0016%0016%0016%0016%0016%0016%0016%0016"
+ "%0016%0016%0016%0016%0016%0016%0016%0016%0016%0016%0016"
+ "%0016%0016%0016%0016%0016%0016%0016%0016%0016%0016%0016"
+ "%0016%0016%0016%0016%0016%0016%0016%0016%0016%0016%0016"
+ "%0016%0016%0016%0016%0016%0016%0016%0016%0016%0016%0016"
+ "%0016%0016%0016%0016%0016%0016%0016%0016%0016%0016%0016"
+ "%0016%0016%0016%0016%0016%0016%0016%0016%0016%0016%0016"
+ "%0016%0016%0016%0016%0016%0016r\","
+ "TX,0x0,"
+ "0077,00,0077,00,0077,00,00,00,0077,00,0077,"
+ "00,0077,007777,007777,0077,007777,0077,007777,"
+ "007770,0077,00,0077,00,00,00,0077,00,0077,00,"
+ "0077,007777,007777,0077,007777,0077,007777,007777))");
+ uint8_t win_bytes[] = {
+ 0x01, 0x00, 0x14, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0xb0, 0x02, 0x00, 0x00,
+ 0x02, 0x00, 0x9c, 0x02, 0x01, 0x00, 0x00, 0x00, 0x12, 0x00,
+ 0x94, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xa8, 0x00,
+ 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x26, 0x00, 0x00, 0x00, 0x9e, 0x01, 0x00, 0x00, 0xa4, 0x01,
+ 0x00, 0x00, 0xa9, 0x01, 0x00, 0x00, 0xaf, 0x01, 0x00, 0x00,
+ 0xb4, 0x01, 0x00, 0x00, 0xba, 0x01, 0x00, 0x00, 0xbf, 0x01,
+ 0x00, 0x00, 0xc4, 0x01, 0x00, 0x00, 0xc9, 0x01, 0x00, 0x00,
+ 0xcf, 0x01, 0x00, 0x00, 0xd4, 0x01, 0x00, 0x00, 0xda, 0x01,
+ 0x00, 0x00, 0xdf, 0x01, 0x00, 0x00, 0xe5, 0x01, 0x00, 0x00,
+ 0xec, 0x01, 0x00, 0x00, 0xf3, 0x01, 0x00, 0x00, 0xf9, 0x01,
+ 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x06, 0x02, 0x00, 0x00,
+ 0x0d, 0x02, 0x00, 0x00, 0x14, 0x02, 0x00, 0x00, 0x1a, 0x02,
+ 0x00, 0x00, 0x1f, 0x02, 0x00, 0x00, 0x25, 0x02, 0x00, 0x00,
+ 0x2a, 0x02, 0x00, 0x00, 0x2f, 0x02, 0x00, 0x00, 0x34, 0x02,
+ 0x00, 0x00, 0x3a, 0x02, 0x00, 0x00, 0x3f, 0x02, 0x00, 0x00,
+ 0x45, 0x02, 0x00, 0x00, 0x4a, 0x02, 0x00, 0x00, 0x50, 0x02,
+ 0x00, 0x00, 0x57, 0x02, 0x00, 0x00, 0x5e, 0x02, 0x00, 0x00,
+ 0x64, 0x02, 0x00, 0x00, 0x6b, 0x02, 0x00, 0x00, 0x71, 0x02,
+ 0x00, 0x00, 0x78, 0x02, 0x00, 0x00, 0x63, 0x00, 0x6f, 0x00,
+ 0x6c, 0x00, 0x4f, 0x00, 0x49, 0x00, 0x72, 0x00, 0x16, 0x00,
+ 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00,
+ 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00,
+ 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00,
+ 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00,
+ 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00,
+ 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00,
+ 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00,
+ 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00,
+ 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00,
+ 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00,
+ 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00,
+ 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00,
+ 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00,
+ 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00,
+ 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00,
+ 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00,
+ 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00,
+ 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00,
+ 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00,
+ 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00,
+ 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00,
+ 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00,
+ 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x72, 0x00,
+ 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x77, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x77, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x77,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
+ 0x77, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x00, 0x77, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00,
+ 0x00, 0x00, 0x77, 0x03, 0x00, 0x00, 0x00, 0x00, 0x77, 0x77,
+ 0x03, 0x00, 0x00, 0x00, 0x00, 0x77, 0x77, 0x02, 0x00, 0x00,
+ 0x00, 0x00, 0x77, 0x03, 0x00, 0x00, 0x00, 0x00, 0x77, 0x77,
+ 0x02, 0x00, 0x00, 0x00, 0x00, 0x77, 0x03, 0x00, 0x00, 0x00,
+ 0x00, 0x77, 0x77, 0x03, 0x00, 0x00, 0x00, 0x00, 0x77, 0x70,
+ 0x02, 0x00, 0x00, 0x00, 0x00, 0x77, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x77, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x77, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x77, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x77,
+ 0x03, 0x00, 0x00, 0x00, 0x00, 0x77, 0x77, 0x03, 0x00, 0x00,
+ 0x00, 0x00, 0x77, 0x77, 0x02, 0x00, 0x00, 0x00, 0x00, 0x77,
+ 0x03, 0x00, 0x00, 0x00, 0x00, 0x77, 0x77, 0x02, 0x00, 0x00,
+ 0x00, 0x00, 0x77, 0x03, 0x00, 0x00, 0x00, 0x00, 0x77, 0x77,
+ 0x03, 0x00, 0x00, 0x00, 0x00, 0x77, 0x77, 0x00, 0x02, 0x00,
+ 0x28, 0x00, 0x01, 0x00, 0x00, 0x00, 0x09, 0x00, 0x20, 0x00,
+ 0x3f, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x00, 0x21, 0x00, 0x00, 0x61, 0x72, 0x74, 0x78,
+ 0xfa, 0x02, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00};
+ DATA_BLOB win_blob = {
+ .data = win_bytes,
+ .length = sizeof(win_bytes)
+ };
+
+ TALLOC_CTX *mem_ctx = talloc_new(NULL);
+ struct security_descriptor sec_desc_windows = {};
+ struct security_descriptor *sec_desc_samba = sddl_decode(mem_ctx, sddl,
+ NULL);
+ assert_non_null(sec_desc_samba);
+ ndr_err = ndr_pull_struct_blob(
+ &win_blob, mem_ctx, &sec_desc_windows,
+ (ndr_pull_flags_fn_t)ndr_pull_security_descriptor);
+
+ assert_true(NDR_ERR_CODE_IS_SUCCESS(ndr_err));
+}
+
+static void test_round_trips(void **state)
+{
+ /*
+ * These expressions should parse into proper conditional
+ * ACEs, which then encode into an equivalent SDDL string,
+ * which then parses again into the same conditional ACE.
+ */
+ static const char *sddl[] = {
+ "(0>-0)",
+ "(0>+0)",
+ ("(Member_of{SID(AA)})"),
+ ("(a Contains @USER.b == @device.c)"),
+ ("(a == @user.b == @resource.c)"),
+ ("(@Device.bb <= -00624677746777766777767)"),
+ ("(@Device.bb == 0624677746777766777767)"),
+ ("(@Device.%025cɜ == 3)"),
+ ("(17pq == 3||2a==@USER.7)"),
+ ("(x==1 && x >= 2 && @User.Title == @User.shoes || "
+ "Member_of{SID(CD)} && !(Member_of_Any{ 3 }) || "
+ "Device_Member_of{SID(BA), 7, 1, 3} "
+ "|| Exists hooly)"),
+ ("(!(!(!(!(!((!(x==1))))))))"),
+ ("(@User.a == {})"),
+ ("(Member_of{})"),
+ ("(Member_of {SID(S-1-33-5), "
+ "SID(BO)} && @Device.Bitlocker)"),
+ "(@USER.ad://ext/AuthenticationSilo == \"siloname\")",
+ "(@User.Division==\"Finance\" || @User.Division ==\"Sales\")",
+ "(@User.Title == @User.Title)",
+ "(@User.Title == \"PM\")",
+ "(OctetStringType==#01020300)",
+ "(@User.Project Any_of @Resource.Project)",
+ "(@user.x==1 &&(@user.x >@user.x ) )",
+ "(x==1) ",
+ "( x Contains 3)",
+ "( x < 3)",
+ "(x Any_of 3)",
+ "( x == SID(BA))",
+ "((x) == SID(BA))",
+ "(OctetStringType==#1#2#3###))",
+ "(@user.x == 00)",
+ "(@user.x == 01)",
+ "(@user.x == -00)",
+ "(@user.x == -01)",
+ "(@user.x == 0x0)",
+ "(@user.x == 0x1)",
+ "(@user.x == -0x0)",
+ "(@user.x == -0x1)",
+ };
+ size_t i, length;
+ TALLOC_CTX *mem_ctx = talloc_new(NULL);
+ bool failed = false;
+ bool ok;
+ for (i = 0; i < ARRAY_SIZE(sddl); i++) {
+ struct ace_condition_script *s1 = NULL;
+ struct ace_condition_script *s2 = NULL;
+ struct ace_condition_script *s3 = NULL;
+ const char *message = NULL;
+ size_t message_offset;
+ const char *resddl1 = NULL;
+ const char *resddl2 = NULL;
+ DATA_BLOB e1, e2, e3;
+ fputs("=======================\n", stderr);
+ s1 = ace_conditions_compile_sddl(mem_ctx,
+ ACE_CONDITION_FLAG_ALLOW_DEVICE,
+ sddl[i],
+ &message,
+ &message_offset,
+ &length);
+ if (s1 == NULL) {
+ debug_fail("%s\n", sddl[i]);
+ failed = true;
+ print_error_message(sddl[i], message, message_offset);
+ continue;
+ }
+ if (false) {
+ debug_conditional_ace_stderr(mem_ctx, s1);
+ }
+ ok = conditional_ace_encode_binary(mem_ctx, s1, &e1);
+ if (! ok) {
+ failed = true;
+ debug_fail("%s could not encode\n", sddl[i]);
+ continue;
+ }
+
+ s2 = parse_conditional_ace(mem_ctx, e1);
+ if (s2 == NULL) {
+ debug_fail("%s failed to decode ace\n", sddl[i]);
+ failed = true;
+ continue;
+ }
+
+ ok = conditional_ace_encode_binary(mem_ctx, s2, &e2);
+ if (! ok) {
+ failed = true;
+ debug_fail("%s could not re-encode\n", sddl[i]);
+ continue;
+ }
+ if (data_blob_cmp(&e1, &e2) != 0) {
+ failed = true;
+ }
+
+ resddl1 = sddl_from_conditional_ace(mem_ctx, s1);
+ if (resddl1 == NULL) {
+ failed = true;
+ debug_fail("could not re-make SDDL of %s\n", sddl[i]);
+ continue;
+ }
+ resddl2 = sddl_from_conditional_ace(mem_ctx, s2);
+ if (resddl2 == NULL) {
+ failed = true;
+ debug_fail("could not re-make SDDL of %s\n", sddl[i]);
+ continue;
+ }
+ if (strcmp(resddl1, resddl2) != 0) {
+ print_message("SDDL 2: %s\n", resddl2);
+ failed = true;
+ }
+ print_message("SDDL: '%s' -> '%s'\n", sddl[i], resddl1);
+ s3 = ace_conditions_compile_sddl(mem_ctx,
+ ACE_CONDITION_FLAG_ALLOW_DEVICE,
+ resddl1,
+ &message,
+ &message_offset,
+ &length);
+ if (s3 == NULL) {
+ debug_fail("resddl: %s\n", resddl1);
+ failed = true;
+ print_error_message(resddl1, message, message_offset);
+ continue;
+ }
+ ok = conditional_ace_encode_binary(mem_ctx, s3, &e3);
+ if (! ok) {
+ failed = true;
+ debug_fail("%s could not encode\n", resddl1);
+ continue;
+ }
+ if (data_blob_cmp(&e1, &e3) != 0) {
+ debug_fail("'%s' and '%s' compiled differently\n", sddl[i], resddl1);
+ failed = true;
+ }
+ }
+ assert_false(failed);
+}
+
+static void test_a_number_of_valid_strings(void **state)
+{
+ /*
+ * These expressions should parse into proper conditional ACEs.
+ */
+ static const char *sddl[] = {
+ "(@User.TEETH == \"5\")",
+ "(x==1) ",
+ "( x Contains 3)",
+ "( x < 3)",
+ "(x Any_of 3)",
+ "( x == SID(BA))",
+ "(x ANY_Of 3)",
+ "((x) == SID(BA))",
+ "(x==1 && x >= 2)", /* logical consistency not required */
+ };
+ size_t i, length;
+ TALLOC_CTX *mem_ctx = talloc_new(NULL);
+ bool failed = false;
+ for (i = 0; i < ARRAY_SIZE(sddl); i++) {
+ struct ace_condition_script *s = NULL;
+ const char *message = NULL;
+ size_t message_offset;
+
+ s = ace_conditions_compile_sddl(mem_ctx,
+ ACE_CONDITION_FLAG_ALLOW_DEVICE,
+ sddl[i],
+ &message,
+ &message_offset,
+ &length);
+ if (s == NULL) {
+ debug_fail("%s\n", sddl[i]);
+ failed = true;
+ } else if (length != strlen(sddl[i])) {
+ debug_fail("%s failed to consume whole string\n",
+ sddl[i]);
+ failed = true;
+ }
+ if (message != NULL) {
+ print_error_message(sddl[i], message, message_offset);
+ } else if (s == NULL) {
+ print_message("failed without message\n");
+ }
+ }
+ assert_false(failed);
+}
+
+
+static void test_a_number_of_invalid_strings(void **state)
+{
+ /*
+ * These expressions should fail to parse.
+ */
+ static const char *sddl[] = {
+ /* '!' is only allowed before parens or @attr */
+ "(!!! !!! !!! Not_Member_of{SID(AA)}))",
+ /* overflowing numbers can't be sensibly interpreted */
+ ("(@Device.bb == 055555624677746777766777767)"),
+ ("(@Device.bb == 0x624677746777766777767)"),
+ ("(@Device.bb == 624677746777766777767)"),
+ /* insufficient arguments */
+ "(!)",
+ "(x >)",
+ "(> 3)",
+ /* keyword as local attribute name */
+ "( Member_of Contains 3)",
+ /* no parens */
+ " x < 3",
+ /* wants '==' */
+ "( x = SID(BA))",
+ /* invalid SID strings */
+ "( x == SID(ZZ))",
+ "( x == SID(S-1-))",
+ "( x == SID())",
+ /* literal on LHS */
+ "(\"x\" == \"x\")",
+ /* odd number of digits following '#' */
+ "(OctetStringType==#1#2#3##))",
+ /* empty expression */
+ "()",
+ /* relational op with with complex RHS */
+ "(@Device.bb == (@USER.x < 62))",
+ /* hex‐escapes that should be literals */
+ ("(@Device.%002e == 3)"),
+ ("(@Device.%002f == 3)"),
+ ("(@Device.%003a == 3)"),
+ /* trailing comma in composite */
+ "(Member_of{SID(AA),})",
+ /* missing comma between elements of a composite */
+ "(Member_of{SID(AA) SID(AC)})",
+ /* unexpected comma in composite */
+ "(Member_of{,})",
+ };
+ size_t i, length;
+ TALLOC_CTX *mem_ctx = talloc_new(NULL);
+ bool failed_to_fail = false;
+ for (i = 0; i < ARRAY_SIZE(sddl); i++) {
+ struct ace_condition_script *s = NULL;
+ const char *message = NULL;
+ size_t message_offset;
+ s = ace_conditions_compile_sddl(mem_ctx,
+ ACE_CONDITION_FLAG_ALLOW_DEVICE,
+ sddl[i],
+ &message,
+ &message_offset,
+ &length);
+ if (s != NULL) {
+ print_message("unexpected success: ");
+ debug_fail("%s\n", sddl[i]);
+ failed_to_fail = true;
+ }
+ if (message != NULL) {
+ print_error_message(sddl[i], message, message_offset);
+ } else if (s == NULL) {
+ print_message("failed without message\n");
+ }
+ }
+ assert_false(failed_to_fail);
+}
+
+
+static void test_a_number_of_invalid_full_sddl_strings(void **state)
+{
+ /*
+ * These ones are complete SDDL sentences and should fail to parse,
+ * with specific message snippets.
+ */
+ static struct {
+ const char *sddl;
+ const char *snippet;
+ ssize_t offset;
+ } cases[] = {
+ {
+ "O:SYG:SYD:(A;;;;ZZ)(XA;OICI;CR;;;WD;(Member_of {WD}))",
+ "malformed ACE with only 4 ';'",
+ 11
+ },
+ {
+ "O:SYG:SYD:QQ(A;;;;ZZ)(XA;OICI;CR;;;WD;(Member_of {WD}))",
+ "expected '[OGDS]:' section start",
+ 10
+ }
+ };
+ size_t i;
+ TALLOC_CTX *mem_ctx = talloc_new(NULL);
+ bool failed_to_fail = false;
+ bool message_wrong = false;
+ enum ace_condition_flags ace_condition_flags = \
+ ACE_CONDITION_FLAG_ALLOW_DEVICE;
+ struct dom_sid domain_sid;
+ string_to_sid(&domain_sid, "S-1-2-3");
+
+ for (i = 0; i < ARRAY_SIZE(cases); i++) {
+ struct security_descriptor *sd = NULL;
+ const char *message = NULL;
+ size_t message_offset;
+ sd = sddl_decode_err_msg(mem_ctx,
+ cases[i].sddl,
+ &domain_sid,
+ ace_condition_flags,
+ &message,
+ &message_offset);
+ if (sd != NULL) {
+ print_message("unexpected success: ");
+ debug_fail("%s\n", cases[i].sddl);
+ failed_to_fail = true;
+ }
+ if (cases[i].snippet != NULL) {
+ if (message != NULL) {
+ char *c = strstr(message, cases[i].snippet);
+ print_error_message(cases[i].sddl,
+ message,
+ message_offset);
+ if (c == NULL) {
+ message_wrong = true;
+ print_message("expected '%s'\n",
+ cases[i].snippet);
+ }
+ } else {
+ message_wrong = true;
+ print_error_message(cases[i].sddl,
+ "NO MESSAGE!",
+ message_offset);
+ print_message("expected '%s', got no message!\n",
+ cases[i].snippet);
+ }
+ } else {
+ print_message("no assertion about message, got '%s'\n",
+ message);
+ }
+ if (cases[i].offset >= 0) {
+ if (cases[i].offset != message_offset) {
+ message_wrong = true;
+ print_message("expected offset %zd, got %zu\n",
+ cases[i].offset,
+ message_offset);
+ }
+ } else {
+ print_message("no assertion about offset, got '%zu\n",
+ message_offset);
+ }
+ }
+ assert_false(failed_to_fail);
+ assert_false(message_wrong);
+}
+
+
+static void test_valid_strings_with_trailing_crap(void **state)
+{
+ /*
+ * These expressions should parse even though they have
+ * trailing bytes that look bad.
+ *
+ * ace_conditions_compile_sddl() will return when it has
+ * found a complete expression, and tell us how much it used.
+ */
+ static struct {
+ const char *sddl;
+ size_t length;
+ } pairs[] = {
+ {"(x==1 &&(x < 5 )) )", 18},
+ {"(x==1) &&", 7},
+ {"(x)) ", 3},
+ };
+ size_t i, length;
+ TALLOC_CTX *mem_ctx = talloc_new(NULL);
+ bool failed = false;
+ for (i = 0; i < ARRAY_SIZE(pairs); i++) {
+ struct ace_condition_script *s = NULL;
+ const char *message = NULL;
+ size_t message_offset;
+ s = ace_conditions_compile_sddl(mem_ctx,
+ ACE_CONDITION_FLAG_ALLOW_DEVICE,
+ pairs[i].sddl,
+ &message,
+ &message_offset,
+ &length);
+
+ if (s == NULL) {
+ debug_fail("%s\n", pairs[i].sddl);
+ failed = true;
+ } else if (pairs[i].length == length) {
+ debug_ok("%s\n", pairs[i].sddl);
+ } else {
+ debug_fail("expected to consume %zu bytes, actual %zu\n",
+ pairs[i].length, length);
+ failed = true;
+ }
+ if (message != NULL) {
+ print_error_message(pairs[i].sddl, message, message_offset);
+ } else if (s == NULL) {
+ print_message("failed without message\n");
+ }
+ }
+ assert_false(failed);
+}
+
+
+int main(_UNUSED_ int argc, _UNUSED_ const char **argv)
+{
+ const struct CMUnitTest tests[] = {
+ cmocka_unit_test(test_a_number_of_invalid_full_sddl_strings),
+ cmocka_unit_test(test_full_sddl_ra_encode),
+ cmocka_unit_test(test_full_sddl_ra_escapes),
+ cmocka_unit_test(test_full_sddl_compile),
+ cmocka_unit_test(test_round_trips),
+ cmocka_unit_test(test_a_number_of_invalid_strings),
+ cmocka_unit_test(test_a_number_of_valid_strings),
+ cmocka_unit_test(test_valid_strings_with_trailing_crap),
+ cmocka_unit_test(test_sddl_compile),
+ cmocka_unit_test(test_sddl_compile2),
+ };
+ if (!isatty(1)) {
+ cmocka_set_message_output(CM_OUTPUT_SUBUNIT);
+ }
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/libcli/security/tests/windows/canonical.txt b/libcli/security/tests/windows/canonical.txt
new file mode 100644
index 0000000..edeae63
--- /dev/null
+++ b/libcli/security/tests/windows/canonical.txt
@@ -0,0 +1,19 @@
+O:S-1-5-21-1225132014-296224811-2507946102-512G:S-1-5-21-1225132014-296224811-2507946102-512D:P -> O:S-1-5-21-1225132014-296224811-2507946102-512G:S-1-5-21-1225132014-296224811-2507946102-512D:P
+D:(A;;GA;;;SY) -> D:(A;;GA;;;SY)
+D:(A;;GA;;;RU) -> D:(A;;GA;;;RU)
+D:(A;;GA;;;LG) -> D:(A;;GA;;;LG)
+D:(A;;0x401200a0;;;LG) -> D:(A;;0x401200a0;;;LG)
+D:S: -> D:S:
+D:PS: -> D:PS:
+D:(A;;GA;;;RD) -> D:(A;;GA;;;RD)
+S:(AU;SA;CR;;;WD)(AU;SA;CR;;;WD) -> S:(AU;SA;CR;;;WD)(AU;SA;CR;;;WD)
+S:(OU;CISA;WP;f30e3bbe-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)(OU;CISA;WP;f30e3bbf-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD) -> S:(OU;CISA;WP;f30e3bbe-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)(OU;CISA;WP;f30e3bbf-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)
+D:(A;;GA;;;S-1-3-4294967295-3-4) -> D:(A;;GA;;;S-1-3-4294967295-3-4)
+D:(A;;GA;;;S-1-5-21-1-2-3-513) -> D:(A;;GA;;;S-1-5-21-1-2-3-513)
+D:(A;;GA;;;S-1-5-21-2447931902-1787058256-3961074038-1201) -> D:(A;;GA;;;S-1-5-21-2447931902-1787058256-3961074038-1201)
+O:S-1-2-512D: -> O:S-1-2-512D:
+D:PARAI(A;;GA;;;SY) -> D:PARAI(A;;GA;;;SY)
+D:P(A;;GA;;;LG)(A;;GX;;;AA) -> D:P(A;;GA;;;LG)(A;;GX;;;AA)
+D:(A;;FA;;;WD) -> D:(A;;FA;;;WD)
+D:(A;;CCDCLCSWRPWPDTLOCR;;;WD) -> D:(A;;CCDCLCSWRPWPDTLOCR;;;WD)
+D:(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;BA) -> D:(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;BA)
diff --git a/libcli/security/tests/windows/conditional_aces.txt.json b/libcli/security/tests/windows/conditional_aces.txt.json
new file mode 100644
index 0000000..4c8211c
--- /dev/null
+++ b/libcli/security/tests/windows/conditional_aces.txt.json
@@ -0,0 +1 @@
+{"D:(D;OICI;GA;;;BG)(D;OICI;GA;;;AN)(A;OICI;GRGWGX;;;AU)(XA;;FX;;;S-1-1-0;(@User.Title == \"\"))(A;OICI;GA;;;BA)": [1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 144, 0, 5, 0, 0, 0, 1, 3, 24, 0, 0, 0, 0, 16, 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 34, 2, 0, 0, 1, 3, 20, 0, 0, 0, 0, 16, 1, 1, 0, 0, 0, 0, 0, 5, 7, 0, 0, 0, 0, 3, 20, 0, 0, 0, 0, 224, 1, 1, 0, 0, 0, 0, 0, 5, 11, 0, 0, 0, 9, 0, 48, 0, 160, 0, 18, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 97, 114, 116, 120, 249, 10, 0, 0, 0, 84, 0, 105, 0, 116, 0, 108, 0, 101, 0, 16, 0, 0, 0, 0, 128, 0, 0, 0, 0, 3, 24, 0, 0, 0, 0, 16, 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 32, 2, 0, 0], "D:(D;OICI;GA;;;BG)(D;OICI;GA;;;AN)(A;OICI;GRGWGX;;;AU)(XA;;FX;;;S-1-1-0;(@User.title == \"perambuator\"))(A;OICI;GA;;;BA)": [1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 164, 0, 5, 0, 0, 0, 1, 3, 24, 0, 0, 0, 0, 16, 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 34, 2, 0, 0, 1, 3, 20, 0, 0, 0, 0, 16, 1, 1, 0, 0, 0, 0, 0, 5, 7, 0, 0, 0, 0, 3, 20, 0, 0, 0, 0, 224, 1, 1, 0, 0, 0, 0, 0, 5, 11, 0, 0, 0, 9, 0, 68, 0, 160, 0, 18, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 97, 114, 116, 120, 249, 10, 0, 0, 0, 116, 0, 105, 0, 116, 0, 108, 0, 101, 0, 16, 22, 0, 0, 0, 112, 0, 101, 0, 114, 0, 97, 0, 109, 0, 98, 0, 117, 0, 97, 0, 116, 0, 111, 0, 114, 0, 128, 0, 0, 3, 24, 0, 0, 0, 0, 16, 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 32, 2, 0, 0], "D:(XA;;0x1f;;;AA;(!(! (Member_of{SID(AA)}))))": [1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 68, 0, 1, 0, 0, 0, 9, 0, 60, 0, 31, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 67, 2, 0, 0, 97, 114, 116, 120, 80, 21, 0, 0, 0, 81, 16, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 67, 2, 0, 0, 137, 162, 162, 0, 0, 0], "D:(XA;;0x1f;;;AA;(!(!(!(!(!(! (Member_of{SID(AA)}))))))))": [1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 72, 0, 1, 0, 0, 0, 9, 0, 64, 0, 31, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 67, 2, 0, 0, 97, 114, 116, 120, 80, 21, 0, 0, 0, 81, 16, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 67, 2, 0, 0, 137, 162, 162, 162, 162, 162, 162, 0, 0, 0], "D:(XA;;0x1f;;;AA;(@Device.colour == \"blue\"))": [1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 68, 0, 1, 0, 0, 0, 9, 0, 60, 0, 31, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 67, 2, 0, 0, 97, 114, 116, 120, 251, 12, 0, 0, 0, 99, 0, 111, 0, 108, 0, 111, 0, 117, 0, 114, 0, 16, 8, 0, 0, 0, 98, 0, 108, 0, 117, 0, 101, 0, 128, 0], "D:(XA;;0x1f;;;AA;(@Device.colour == @Resource.colour))S:(RA;;;;;WD;(\"colour\",TS,0,\"blue\"))": [1, 0, 20, 128, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 92, 0, 0, 0, 2, 0, 72, 0, 1, 0, 0, 0, 18, 0, 64, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 20, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 34, 0, 0, 0, 99, 0, 111, 0, 108, 0, 111, 0, 117, 0, 114, 0, 0, 0, 98, 0, 108, 0, 117, 0, 101, 0, 0, 0, 2, 0, 72, 0, 1, 0, 0, 0, 9, 0, 64, 0, 31, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 67, 2, 0, 0, 97, 114, 116, 120, 251, 12, 0, 0, 0, 99, 0, 111, 0, 108, 0, 111, 0, 117, 0, 114, 0, 250, 12, 0, 0, 0, 99, 0, 111, 0, 108, 0, 111, 0, 117, 0, 114, 0, 128, 0], "D:(XA;;0x1f;;;AA;(@Device.colour == {\"orange\", \"blue\"}))": [1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 92, 0, 1, 0, 0, 0, 9, 0, 84, 0, 31, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 67, 2, 0, 0, 97, 114, 116, 120, 251, 12, 0, 0, 0, 99, 0, 111, 0, 108, 0, 111, 0, 117, 0, 114, 0, 80, 30, 0, 0, 0, 16, 12, 0, 0, 0, 111, 0, 114, 0, 97, 0, 110, 0, 103, 0, 101, 0, 16, 8, 0, 0, 0, 98, 0, 108, 0, 117, 0, 101, 0, 128, 0, 0, 0], "D:(XA;;0x1f;;;AA;(@Device.colour Contains @Resource.colour))S:(RA;;;;;WD;(\"colour\",TS,0,\"blue\"))": [1, 0, 20, 128, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 92, 0, 0, 0, 2, 0, 72, 0, 1, 0, 0, 0, 18, 0, 64, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 20, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 34, 0, 0, 0, 99, 0, 111, 0, 108, 0, 111, 0, 117, 0, 114, 0, 0, 0, 98, 0, 108, 0, 117, 0, 101, 0, 0, 0, 2, 0, 72, 0, 1, 0, 0, 0, 9, 0, 64, 0, 31, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 67, 2, 0, 0, 97, 114, 116, 120, 251, 12, 0, 0, 0, 99, 0, 111, 0, 108, 0, 111, 0, 117, 0, 114, 0, 250, 12, 0, 0, 0, 99, 0, 111, 0, 108, 0, 111, 0, 117, 0, 114, 0, 134, 0], "D:(XA;;0x1f;;;AA;(@Device.colour Contains @Resource.colour))S:(RA;;;;;WD;(\"colour\",TS,0,\"blue\", \"red\"))": [1, 0, 20, 128, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 104, 0, 0, 0, 2, 0, 84, 0, 1, 0, 0, 0, 18, 0, 76, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 24, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 38, 0, 0, 0, 48, 0, 0, 0, 99, 0, 111, 0, 108, 0, 111, 0, 117, 0, 114, 0, 0, 0, 98, 0, 108, 0, 117, 0, 101, 0, 0, 0, 114, 0, 101, 0, 100, 0, 0, 0, 2, 0, 72, 0, 1, 0, 0, 0, 9, 0, 64, 0, 31, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 67, 2, 0, 0, 97, 114, 116, 120, 251, 12, 0, 0, 0, 99, 0, 111, 0, 108, 0, 111, 0, 117, 0, 114, 0, 250, 12, 0, 0, 0, 99, 0, 111, 0, 108, 0, 111, 0, 117, 0, 114, 0, 134, 0], "D:(XA;;0x1f;;;AA;(@Device.legs == 1))": [1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 64, 0, 1, 0, 0, 0, 9, 0, 56, 0, 31, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 67, 2, 0, 0, 97, 114, 116, 120, 251, 8, 0, 0, 0, 108, 0, 101, 0, 103, 0, 115, 0, 4, 1, 0, 0, 0, 0, 0, 0, 0, 3, 2, 128, 0, 0, 0], "D:(XA;;0x1f;;;AA;(@Device.legs >= 1))": [1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 64, 0, 1, 0, 0, 0, 9, 0, 56, 0, 31, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 67, 2, 0, 0, 97, 114, 116, 120, 251, 8, 0, 0, 0, 108, 0, 101, 0, 103, 0, 115, 0, 4, 1, 0, 0, 0, 0, 0, 0, 0, 3, 2, 133, 0, 0, 0], "D:(XA;;0x1f;;;AA;(@User.colour == @Device.colour))": [1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 72, 0, 1, 0, 0, 0, 9, 0, 64, 0, 31, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 67, 2, 0, 0, 97, 114, 116, 120, 249, 12, 0, 0, 0, 99, 0, 111, 0, 108, 0, 111, 0, 117, 0, 114, 0, 251, 12, 0, 0, 0, 99, 0, 111, 0, 108, 0, 111, 0, 117, 0, 114, 0, 128, 0], "D:(XA;;0x1f;;;AA;(Device_Member_of{SID(AA)} || Member_of{SID(WD)}))": [1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 88, 0, 1, 0, 0, 0, 9, 0, 80, 0, 31, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 67, 2, 0, 0, 97, 114, 116, 120, 80, 21, 0, 0, 0, 81, 16, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 67, 2, 0, 0, 138, 80, 17, 0, 0, 0, 81, 12, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 137, 161, 0], "D:(XA;;0x1f;;;AA;(Device_Member_of{SID(BA)} && Member_of{SID(WD)}))": [1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 88, 0, 1, 0, 0, 0, 9, 0, 80, 0, 31, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 67, 2, 0, 0, 97, 114, 116, 120, 80, 21, 0, 0, 0, 81, 16, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 32, 2, 0, 0, 138, 80, 17, 0, 0, 0, 81, 12, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 137, 160, 0], "D:(XA;;0x1f;;;AA;(Device_Member_of{SID(BA)}))": [1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 64, 0, 1, 0, 0, 0, 9, 0, 56, 0, 31, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 67, 2, 0, 0, 97, 114, 116, 120, 80, 21, 0, 0, 0, 81, 16, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 32, 2, 0, 0, 138, 0], "D:(XA;;0x1f;;;AA;(Device_Member_of{SID(BG)} || Member_of{SID(WR)}))": [1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 88, 0, 1, 0, 0, 0, 9, 0, 80, 0, 31, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 67, 2, 0, 0, 97, 114, 116, 120, 80, 21, 0, 0, 0, 81, 16, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 34, 2, 0, 0, 138, 80, 17, 0, 0, 0, 81, 12, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 5, 33, 0, 0, 0, 137, 161, 0], "D:(XA;;0x1f;;;AA;(Member_of{SID(S-1-77-88-99)}))": [1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 64, 0, 1, 0, 0, 0, 9, 0, 56, 0, 31, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 67, 2, 0, 0, 97, 114, 116, 120, 80, 21, 0, 0, 0, 81, 16, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 77, 88, 0, 0, 0, 99, 0, 0, 0, 137, 0], "D:(XA;;0x1f;;;AA;(a == 1))": [1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 56, 0, 1, 0, 0, 0, 9, 0, 48, 0, 31, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 67, 2, 0, 0, 97, 114, 116, 120, 248, 2, 0, 0, 0, 97, 0, 4, 1, 0, 0, 0, 0, 0, 0, 0, 3, 2, 128, 0], "D:(XA;;0x1ff;;;S-1-222-333;(Member_of_Any{SID(S-1-222-333)}))": [1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 56, 0, 1, 0, 0, 0, 9, 0, 48, 0, 255, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 222, 77, 1, 0, 0, 97, 114, 116, 120, 80, 17, 0, 0, 0, 81, 12, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 222, 77, 1, 0, 0, 139, 0], "D:(XA;;0x1ff;;;WD;(Member_of_Any{SID(S-1-222-333)}))": [1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 56, 0, 1, 0, 0, 0, 9, 0, 48, 0, 255, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 97, 114, 116, 120, 80, 17, 0, 0, 0, 81, 12, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 222, 77, 1, 0, 0, 139, 0], "D:(XA;;;;;WD;(@Device.bb == 0x7fffffffffffffff))": [1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 56, 0, 1, 0, 0, 0, 9, 0, 48, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 97, 114, 116, 120, 251, 4, 0, 0, 0, 98, 0, 98, 0, 4, 255, 255, 255, 255, 255, 255, 255, 127, 3, 3, 128, 0, 0, 0], "D:(XA;;;;;WD;(@Device.bb == 0xffffffff))": [1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 56, 0, 1, 0, 0, 0, 9, 0, 48, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 97, 114, 116, 120, 251, 4, 0, 0, 0, 98, 0, 98, 0, 4, 255, 255, 255, 255, 0, 0, 0, 0, 3, 3, 128, 0, 0, 0], "D:(XA;;;;;WD;(@Device.bb == 0xfffffffff))": [1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 56, 0, 1, 0, 0, 0, 9, 0, 48, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 97, 114, 116, 120, 251, 4, 0, 0, 0, 98, 0, 98, 0, 4, 255, 255, 255, 255, 15, 0, 0, 0, 3, 3, 128, 0, 0, 0], "D:(XA;;CC;;;AA;(@User.a == @User.b))": [1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 52, 0, 1, 0, 0, 0, 9, 0, 44, 0, 1, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 67, 2, 0, 0, 97, 114, 116, 120, 249, 2, 0, 0, 0, 97, 0, 249, 2, 0, 0, 0, 98, 0, 128, 0], "D:(XA;;CC;;;AA;(a == @User.a))": [1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 52, 0, 1, 0, 0, 0, 9, 0, 44, 0, 1, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 67, 2, 0, 0, 97, 114, 116, 120, 248, 2, 0, 0, 0, 97, 0, 249, 2, 0, 0, 0, 97, 0, 128, 0], "D:(XA;;CC;;;S-1-2-3;(@User.Title != @User.Title))": [1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 64, 0, 1, 0, 0, 0, 9, 0, 56, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 2, 3, 0, 0, 0, 97, 114, 116, 120, 249, 10, 0, 0, 0, 84, 0, 105, 0, 116, 0, 108, 0, 101, 0, 249, 10, 0, 0, 0, 84, 0, 105, 0, 116, 0, 108, 0, 101, 0, 129, 0], "D:(XA;;FR;;;S-1-1-0;(@Device.Bitlocker && @Device.Bitlocker))": [1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 80, 0, 1, 0, 0, 0, 9, 0, 72, 0, 137, 0, 18, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 97, 114, 116, 120, 251, 18, 0, 0, 0, 66, 0, 105, 0, 116, 0, 108, 0, 111, 0, 99, 0, 107, 0, 101, 0, 114, 0, 251, 18, 0, 0, 0, 66, 0, 105, 0, 116, 0, 108, 0, 111, 0, 99, 0, 107, 0, 101, 0, 114, 0, 160, 0], "D:(XA;;FR;;;S-1-1-0;(@Device.Bitlocker || @Device.Bitlocker))": [1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 80, 0, 1, 0, 0, 0, 9, 0, 72, 0, 137, 0, 18, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 97, 114, 116, 120, 251, 18, 0, 0, 0, 66, 0, 105, 0, 116, 0, 108, 0, 111, 0, 99, 0, 107, 0, 101, 0, 114, 0, 251, 18, 0, 0, 0, 66, 0, 105, 0, 116, 0, 108, 0, 111, 0, 99, 0, 107, 0, 101, 0, 114, 0, 161, 0], "D:(XA;;FR;;;S-1-1-0;(@USER.A && @Device.B && @USER.C))": [1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 56, 0, 1, 0, 0, 0, 9, 0, 48, 0, 137, 0, 18, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 97, 114, 116, 120, 249, 2, 0, 0, 0, 65, 0, 251, 2, 0, 0, 0, 66, 0, 160, 249, 2, 0, 0, 0, 67, 0, 160, 0], "D:(XA;;FR;;;S-1-1-0;(@USER.A && @Device.B || @USER.C))": [1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 56, 0, 1, 0, 0, 0, 9, 0, 48, 0, 137, 0, 18, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 97, 114, 116, 120, 249, 2, 0, 0, 0, 65, 0, 251, 2, 0, 0, 0, 66, 0, 160, 249, 2, 0, 0, 0, 67, 0, 161, 0], "D:(XA;;FR;;;S-1-1-0;(@USER.A && @Device.B))": [1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 48, 0, 1, 0, 0, 0, 9, 0, 40, 0, 137, 0, 18, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 97, 114, 116, 120, 249, 2, 0, 0, 0, 65, 0, 251, 2, 0, 0, 0, 66, 0, 160, 0], "D:(XA;;FR;;;S-1-1-0;(@USER.A || @Device.B && @USER.C))": [1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 56, 0, 1, 0, 0, 0, 9, 0, 48, 0, 137, 0, 18, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 97, 114, 116, 120, 249, 2, 0, 0, 0, 65, 0, 251, 2, 0, 0, 0, 66, 0, 249, 2, 0, 0, 0, 67, 0, 160, 161, 0], "D:(XA;;FR;;;S-1-1-0;(@USER.A || @Device.B || @USER.C))": [1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 56, 0, 1, 0, 0, 0, 9, 0, 48, 0, 137, 0, 18, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 97, 114, 116, 120, 249, 2, 0, 0, 0, 65, 0, 251, 2, 0, 0, 0, 66, 0, 161, 249, 2, 0, 0, 0, 67, 0, 161, 0], "D:(XA;;FR;;;S-1-1-0;(@USER.Bitlocker || @Device.Bitlocker))": [1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 80, 0, 1, 0, 0, 0, 9, 0, 72, 0, 137, 0, 18, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 97, 114, 116, 120, 249, 18, 0, 0, 0, 66, 0, 105, 0, 116, 0, 108, 0, 111, 0, 99, 0, 107, 0, 101, 0, 114, 0, 251, 18, 0, 0, 0, 66, 0, 105, 0, 116, 0, 108, 0, 111, 0, 99, 0, 107, 0, 101, 0, 114, 0, 161, 0], "D:(XA;;FR;;;S-1-1-0;(Member_of {SID(S-1-999-777-7-7), SID(BO)} && @Device.Bitlocker))": [1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 108, 0, 1, 0, 0, 0, 9, 0, 100, 0, 137, 0, 18, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 97, 114, 116, 120, 80, 46, 0, 0, 0, 81, 20, 0, 0, 0, 1, 3, 0, 0, 0, 0, 3, 231, 9, 3, 0, 0, 7, 0, 0, 0, 7, 0, 0, 0, 81, 16, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 39, 2, 0, 0, 137, 251, 18, 0, 0, 0, 66, 0, 105, 0, 116, 0, 108, 0, 111, 0, 99, 0, 107, 0, 101, 0, 114, 0, 160], "D:(XA;;FX;;;S-1-1-0;(@User.Project Any_of @Resource.Project))": [1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 72, 0, 1, 0, 0, 0, 9, 0, 64, 0, 160, 0, 18, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 97, 114, 116, 120, 249, 14, 0, 0, 0, 80, 0, 114, 0, 111, 0, 106, 0, 101, 0, 99, 0, 116, 0, 250, 14, 0, 0, 0, 80, 0, 114, 0, 111, 0, 106, 0, 101, 0, 99, 0, 116, 0, 136, 0], "D:(XA;;FX;;;S-1-1-0;(@User.Title == \"PM\"))": [1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 60, 0, 1, 0, 0, 0, 9, 0, 52, 0, 160, 0, 18, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 97, 114, 116, 120, 249, 10, 0, 0, 0, 84, 0, 105, 0, 116, 0, 108, 0, 101, 0, 16, 4, 0, 0, 0, 80, 0, 77, 0, 128, 0, 0, 0], "D:(XA;;FX;;;S-1-1-0;(@User.Title==\"PM\" && (@User.Division==\"Finance\" || @User.Division ==\"Sales\")))": [1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 140, 0, 1, 0, 0, 0, 9, 0, 132, 0, 160, 0, 18, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 97, 114, 116, 120, 249, 10, 0, 0, 0, 84, 0, 105, 0, 116, 0, 108, 0, 101, 0, 16, 4, 0, 0, 0, 80, 0, 77, 0, 128, 249, 16, 0, 0, 0, 68, 0, 105, 0, 118, 0, 105, 0, 115, 0, 105, 0, 111, 0, 110, 0, 16, 14, 0, 0, 0, 70, 0, 105, 0, 110, 0, 97, 0, 110, 0, 99, 0, 101, 0, 128, 249, 16, 0, 0, 0, 68, 0, 105, 0, 118, 0, 105, 0, 115, 0, 105, 0, 111, 0, 110, 0, 16, 10, 0, 0, 0, 83, 0, 97, 0, 108, 0, 101, 0, 115, 0, 128, 161, 160, 0, 0, 0], "D:(XD;;CC;;;S-1-2-3;(@User.Title == @User.Title))": [1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 64, 0, 1, 0, 0, 0, 10, 0, 56, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 2, 3, 0, 0, 0, 97, 114, 116, 120, 249, 10, 0, 0, 0, 84, 0, 105, 0, 116, 0, 108, 0, 101, 0, 249, 10, 0, 0, 0, 84, 0, 105, 0, 116, 0, 108, 0, 101, 0, 128, 0], "D:(XD;;FX;;;S-1-1-0;(@User.Project Any_of @Resource.Project))": [1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 72, 0, 1, 0, 0, 0, 10, 0, 64, 0, 160, 0, 18, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 97, 114, 116, 120, 249, 14, 0, 0, 0, 80, 0, 114, 0, 111, 0, 106, 0, 101, 0, 99, 0, 116, 0, 250, 14, 0, 0, 0, 80, 0, 114, 0, 111, 0, 106, 0, 101, 0, 99, 0, 116, 0, 136, 0], "D:(XD;;FX;;;S-1-1-0;(@User.Title != \"PM\"))": [1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 60, 0, 1, 0, 0, 0, 10, 0, 52, 0, 160, 0, 18, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 97, 114, 116, 120, 249, 10, 0, 0, 0, 84, 0, 105, 0, 116, 0, 108, 0, 101, 0, 16, 4, 0, 0, 0, 80, 0, 77, 0, 129, 0, 0, 0], "D:(XD;;FX;;;WD;(!(@USER.Project Not_Any_of 1)))": [1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 64, 0, 1, 0, 0, 0, 10, 0, 56, 0, 160, 0, 18, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 97, 114, 116, 120, 249, 14, 0, 0, 0, 80, 0, 114, 0, 111, 0, 106, 0, 101, 0, 99, 0, 116, 0, 4, 1, 0, 0, 0, 0, 0, 0, 0, 3, 2, 143, 162], "D:(XD;;FX;;;WD;(@USER.Project Any_of \"pink\"))": [1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 68, 0, 1, 0, 0, 0, 10, 0, 60, 0, 160, 0, 18, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 97, 114, 116, 120, 249, 14, 0, 0, 0, 80, 0, 114, 0, 111, 0, 106, 0, 101, 0, 99, 0, 116, 0, 16, 8, 0, 0, 0, 112, 0, 105, 0, 110, 0, 107, 0, 136, 0, 0, 0], "D:(XD;;FX;;;WD;(@USER.Project Any_of 1))": [1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 64, 0, 1, 0, 0, 0, 10, 0, 56, 0, 160, 0, 18, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 97, 114, 116, 120, 249, 14, 0, 0, 0, 80, 0, 114, 0, 111, 0, 106, 0, 101, 0, 99, 0, 116, 0, 4, 1, 0, 0, 0, 0, 0, 0, 0, 3, 2, 136, 0], "D:AI(XA;OICI;FA;;;WD;(OctetStringType==##1#2#3##))": [1, 0, 4, 132, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 80, 0, 1, 0, 0, 0, 9, 3, 72, 0, 255, 1, 31, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 97, 114, 116, 120, 248, 30, 0, 0, 0, 79, 0, 99, 0, 116, 0, 101, 0, 116, 0, 83, 0, 116, 0, 114, 0, 105, 0, 110, 0, 103, 0, 84, 0, 121, 0, 112, 0, 101, 0, 24, 4, 0, 0, 0, 1, 2, 3, 0, 128, 0, 0, 0], "D:AI(XA;OICI;FA;;;WD;(OctetStringType==#01020300))": [1, 0, 4, 132, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 80, 0, 1, 0, 0, 0, 9, 3, 72, 0, 255, 1, 31, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 97, 114, 116, 120, 248, 30, 0, 0, 0, 79, 0, 99, 0, 116, 0, 101, 0, 116, 0, 83, 0, 116, 0, 114, 0, 105, 0, 110, 0, 103, 0, 84, 0, 121, 0, 112, 0, 101, 0, 24, 4, 0, 0, 0, 1, 2, 3, 0, 128, 0, 0, 0], "O:S-1-1-0D:(XA;;0;;;WD;(Member_Of SID(S-1-1-0)))": [1, 0, 4, 128, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 52, 0, 1, 0, 0, 0, 9, 0, 44, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 97, 114, 116, 120, 81, 12, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 137, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0], "O:S-1-1-0D:(XA;;0x0;;;WD;(Member_Of SID(S-1-1-0)))": [1, 0, 4, 128, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 52, 0, 1, 0, 0, 0, 9, 0, 44, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 97, 114, 116, 120, 81, 12, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 137, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0], "O:S-1-1-0D:(XA;;0x1;;;WD;(Member_of_Any{SID(AS),SID(WD)}))": [1, 0, 4, 128, 92, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 72, 0, 1, 0, 0, 0, 9, 0, 64, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 97, 114, 116, 120, 80, 34, 0, 0, 0, 81, 12, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 18, 1, 0, 0, 0, 81, 12, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 139, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0], "O:S-1-1-0D:(XA;;0x1ff;;;WD;(Member_Of{SID(S-1-1-0)}))": [1, 0, 4, 128, 76, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 56, 0, 1, 0, 0, 0, 9, 0, 48, 0, 255, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 97, 114, 116, 120, 80, 17, 0, 0, 0, 81, 12, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 137, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0], "O:S-1-1-0D:(XA;;0x1ff;;;WD;(Member_of SID(S-1-1-0)))": [1, 0, 4, 128, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 52, 0, 1, 0, 0, 0, 9, 0, 44, 0, 255, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 97, 114, 116, 120, 81, 12, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 137, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0], "O:S-1-1-0D:(XA;;0x1ff;;;WD;(Member_of(SID(S-1-1-0))))": [1, 0, 4, 128, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 52, 0, 1, 0, 0, 0, 9, 0, 44, 0, 255, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 97, 114, 116, 120, 81, 12, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 137, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0], "O:S-1-1-0D:(XA;;0x1ff;;;WD;(Member_of_Any SID(S-1-1-0)))": [1, 0, 4, 128, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 52, 0, 1, 0, 0, 0, 9, 0, 44, 0, 255, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 97, 114, 116, 120, 81, 12, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 139, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0], "O:S-1-1-0D:(XA;;0x1ff;;;WD;(Member_of_Any{SID(S-1-1-0), SID(S-1-222-333)}))": [1, 0, 4, 128, 92, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 72, 0, 1, 0, 0, 0, 9, 0, 64, 0, 255, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 97, 114, 116, 120, 80, 34, 0, 0, 0, 81, 12, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 81, 12, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 222, 77, 1, 0, 0, 139, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0], "O:S-1-1-0D:(XA;;0x1ff;;;WD;(Member_of_Any{SID(S-1-1-334), SID(S-1-222-333)}))": [1, 0, 4, 128, 92, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 72, 0, 1, 0, 0, 0, 9, 0, 64, 0, 255, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 97, 114, 116, 120, 80, 34, 0, 0, 0, 81, 12, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 78, 1, 0, 0, 81, 12, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 222, 77, 1, 0, 0, 139, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0], "O:S-1-1-0D:(XA;;0x1ff;;;WD;(Member_of{SID(S-1-1-0)}))": [1, 0, 4, 128, 76, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 56, 0, 1, 0, 0, 0, 9, 0, 48, 0, 255, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 97, 114, 116, 120, 80, 17, 0, 0, 0, 81, 12, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 137, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0], "O:S-1-1-0D:(XA;;0x1ff;;;WD;(mEMBER_of{SID(S-1-1-0)}))": [1, 0, 4, 128, 76, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 56, 0, 1, 0, 0, 0, 9, 0, 48, 0, 255, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 97, 114, 116, 120, 80, 17, 0, 0, 0, 81, 12, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 137, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0], "O:S-1-1-0D:(XA;;0x1ff;;;WD;(member_of{SID(S-1-1-0)}))": [1, 0, 4, 128, 76, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 56, 0, 1, 0, 0, 0, 9, 0, 48, 0, 255, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 97, 114, 116, 120, 80, 17, 0, 0, 0, 81, 12, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 137, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0], "O:S-1-1-0D:(XA;;;;;WD;(Member_Of SID(S-1-1-0)))": [1, 0, 4, 128, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 52, 0, 1, 0, 0, 0, 9, 0, 44, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 97, 114, 116, 120, 81, 12, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 137, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0], "O:SYG:SYD:(XA;OICI;CR;;;WD;(@USER.ad://ext/AuthenticationSilo == \"siloname\"))": [1, 0, 4, 128, 136, 0, 0, 0, 148, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 116, 0, 1, 0, 0, 0, 9, 3, 108, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 97, 114, 116, 120, 249, 54, 0, 0, 0, 97, 0, 100, 0, 58, 0, 47, 0, 47, 0, 101, 0, 120, 0, 116, 0, 47, 0, 65, 0, 117, 0, 116, 0, 104, 0, 101, 0, 110, 0, 116, 0, 105, 0, 99, 0, 97, 0, 116, 0, 105, 0, 111, 0, 110, 0, 83, 0, 105, 0, 108, 0, 111, 0, 16, 16, 0, 0, 0, 115, 0, 105, 0, 108, 0, 111, 0, 110, 0, 97, 0, 109, 0, 101, 0, 128, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 5, 18, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 5, 18, 0, 0, 0]} \ No newline at end of file
diff --git a/libcli/security/tests/windows/non_canonical.txt b/libcli/security/tests/windows/non_canonical.txt
new file mode 100644
index 0000000..5506d9b
--- /dev/null
+++ b/libcli/security/tests/windows/non_canonical.txt
@@ -0,0 +1,50 @@
+D:(A;;CC;;;BA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU) -> D:(A;;CC;;;BA)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;SY)(A;;LCRPLORC;;;AU)
+D:(A;;RP;;;WD)(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)(A;;RPLCLORC;;;AU)(A;;RPWPCRLCLOCCRCWDWOSW;;;BO)(A;CI;RPWPCRLCLOCCRCWDWOSDSW;;;BA)(A;;RPWPCRLCLOCCDCRCWDWOSDDTSW;;;SY)(A;CI;RPWPCRLCLOCCDCRCWDWOSDDTSW;;;ES)(A;CI;LC;;;RU)(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;bf967aba-0de6-11d0-a285-00aa003049e2;RU)(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;bf967aba-0de6-11d0-a285-00aa003049e2;RU)(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)(OA;;RP;c7407360-20bf-11d0-a768-00aa006e0529;;RU)(OA;CIIO;RPLCLORC;;bf967a9c-0de6-11d0-a285-00aa003049e2;RU)(A;;RPRC;;;RU)(OA;CIIO;RPLCLORC;;bf967aba-0de6-11d0-a285-00aa003049e2;RU)(A;;LCRPLORC;;;ED)(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;4828CC14-1437-45bc-9B07-AD6F015E5F28;RU)(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;4828CC14-1437-45bc-9B07-AD6F015E5F28;RU)(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;4828CC14-1437-45bc-9B07-AD6F015E5F28;RU)(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;4828CC14-1437-45bc-9B07-AD6F015E5F28;RU)(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;4828CC14-1437-45bc-9B07-AD6F015E5F28;RU)(OA;CIIO;RPLCLORC;;4828CC14-1437-45bc-9B07-AD6F015E5F28;RU)(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;RU)(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;AU)(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967aba-0de6-11d0-a285-00aa003049e2;ED)(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a9c-0de6-11d0-a285-00aa003049e2;ED)(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a86-0de6-11d0-a285-00aa003049e2;ED)(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;NO)(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)(OA;;CR;e2a36dc9-ae17-47c3-b58b-be34c55ba633;;SU)(OA;;CR;280f369c-67c7-438e-ae98-1d46f3c6f541;;AU)(OA;;CR;ccc2dc7d-a6ad-4a7a-8846-c04e3cc53501;;AU)(OA;;CR;05c74c5e-4deb-43b4-bd9f-86664c2a7fd5;;AU)S:(AU;SA;WDWOWP;;;WD) -> D:(A;;RP;;;WD)(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)(A;;LCRPLORC;;;AU)(A;;CCLCSWRPWPLOCRRCWDWO;;;BO)(A;CI;CCLCSWRPWPLOCRSDRCWDWO;;;BA)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;SY)(A;CI;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;ES)(A;CI;LC;;;RU)(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;bf967aba-0de6-11d0-a285-00aa003049e2;RU)(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;bf967aba-0de6-11d0-a285-00aa003049e2;RU)(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)(OA;;RP;c7407360-20bf-11d0-a768-00aa006e0529;;RU)(OA;CIIO;LCRPLORC;;bf967a9c-0de6-11d0-a285-00aa003049e2;RU)(A;;RPRC;;;RU)(OA;CIIO;LCRPLORC;;bf967aba-0de6-11d0-a285-00aa003049e2;RU)(A;;LCRPLORC;;;ED)(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)(OA;CIIO;LCRPLORC;;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;RU)(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;AU)(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967aba-0de6-11d0-a285-00aa003049e2;ED)(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a9c-0de6-11d0-a285-00aa003049e2;ED)(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a86-0de6-11d0-a285-00aa003049e2;ED)(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;NO)(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)(OA;;CR;e2a36dc9-ae17-47c3-b58b-be34c55ba633;;SU)(OA;;CR;280f369c-67c7-438e-ae98-1d46f3c6f541;;AU)(OA;;CR;ccc2dc7d-a6ad-4a7a-8846-c04e3cc53501;;AU)(OA;;CR;05c74c5e-4deb-43b4-bd9f-86664c2a7fd5;;AU)S:(AU;SA;WPWDWO;;;WD)
+D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;BO)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;AO)(A;;WPRPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPCRLCLORCSDDT;;;CO)(OA;;WP;4c164200-20c0-11d0-a768-00aa006e0529;;CO)(A;;RPLCLORC;;;AU)(OA;;CR;ab721a53-1e2f-11d0-9819-00aa0040529b;;WD)(A;;CCDC;;;PS)(OA;;CCDC;bf967aa8-0de6-11d0-a285-00aa003049e2;;PO)(OA;;RPWP;bf967a7f-0de6-11d0-a285-00aa003049e2;;SY)(OA;;SW;f3a64788-5306-11d1-a9c5-0000f80367c1;;PS)(OA;;RPWP;77B5B886-944A-11d1-AEBD-0000F80367C1;;PS)(OA;;SW;72e39547-7b18-11d1-adef-00c04fd8d5cd;;PS)(OA;;SW;72e39547-7b18-11d1-adef-00c04fd8d5cd;;CO)(OA;;SW;f3a64788-5306-11d1-a9c5-0000f80367c1;;CO)(OA;;WP;3e0abfd0-126a-11d0-a060-00aa006c33ed;bf967a86-0de6-11d0-a285-00aa003049e2;CO)(OA;;WP;5f202010-79a5-11d0-9020-00c04fc2d4cf;bf967a86-0de6-11d0-a285-00aa003049e2;CO)(OA;;WP;bf967950-0de6-11d0-a285-00aa003049e2;bf967a86-0de6-11d0-a285-00aa003049e2;CO)(OA;;WP;bf967953-0de6-11d0-a285-00aa003049e2;bf967a86-0de6-11d0-a285-00aa003049e2;CO)(OA;;RP;46a9b11d-60ae-405a-b7e8-ff8a58d456d2;;S-1-5-32-560) -> D:(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;BO)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;AO)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;SY)(A;;LCRPDTLOCRSDRC;;;CO)(OA;;WP;4c164200-20c0-11d0-a768-00aa006e0529;;CO)(A;;LCRPLORC;;;AU)(OA;;CR;ab721a53-1e2f-11d0-9819-00aa0040529b;;WD)(A;;CCDC;;;PS)(OA;;CCDC;bf967aa8-0de6-11d0-a285-00aa003049e2;;PO)(OA;;RPWP;bf967a7f-0de6-11d0-a285-00aa003049e2;;SY)(OA;;SW;f3a64788-5306-11d1-a9c5-0000f80367c1;;PS)(OA;;RPWP;77b5b886-944a-11d1-aebd-0000f80367c1;;PS)(OA;;SW;72e39547-7b18-11d1-adef-00c04fd8d5cd;;PS)(OA;;SW;72e39547-7b18-11d1-adef-00c04fd8d5cd;;CO)(OA;;SW;f3a64788-5306-11d1-a9c5-0000f80367c1;;CO)(OA;;WP;3e0abfd0-126a-11d0-a060-00aa006c33ed;bf967a86-0de6-11d0-a285-00aa003049e2;CO)(OA;;WP;5f202010-79a5-11d0-9020-00c04fc2d4cf;bf967a86-0de6-11d0-a285-00aa003049e2;CO)(OA;;WP;bf967950-0de6-11d0-a285-00aa003049e2;bf967a86-0de6-11d0-a285-00aa003049e2;CO)(OA;;WP;bf967953-0de6-11d0-a285-00aa003049e2;bf967a86-0de6-11d0-a285-00aa003049e2;CO)(OA;;RP;46a9b11d-60ae-405a-b7e8-ff8a58d456d2;;S-1-5-32-560)
+D:(A;;RPLCLORC;;;BO)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU) -> D:(A;;LCRPLORC;;;BO)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;SY)(A;;LCRPLORC;;;AU)
+D:(A;;WPCRCCDCLCLORCWOWDSDDTSWRP;;;BO)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;AO)(A;;RPWPCRCCDCLCLORCWOWDSDSWDT;;;SY)(A;;RPCRLCLORCSDDT;;;CO)(OA;;WP;4c164200-20c0-11d0-a768-00aa006e0529;;CO)(A;;RPLCLORC;;;AU)(OA;;CR;ab721a53-1e2f-11d0-9819-00aa0040529b;;WD)(A;;CCDC;;;PS)(OA;;CCDC;bf967aa8-0de6-11d0-a285-00aa003049e2;;PO)(OA;;RPWP;bf967a7f-0de6-11d0-a285-00aa003049e2;;SY)(OA;;SW;f3a64788-5306-11d1-a9c5-0000f80367c1;;PS)(OA;;RPWP;77B5B886-944A-11d1-AEBD-0000F80367C1;;PS)(OA;;SW;72e39547-7b18-11d1-adef-00c04fd8d5cd;;PS)(OA;;SW;72e39547-7b18-11d1-adef-00c04fd8d5cd;;CO)(OA;;SW;f3a64788-5306-11d1-a9c5-0000f80367c1;;CO)(OA;;WP;3e0abfd0-126a-11d0-a060-00aa006c33ed;bf967a86-0de6-11d0-a285-00aa003049e2;CO)(OA;;WP;5f202010-79a5-11d0-9020-00c04fc2d4cf;bf967a86-0de6-11d0-a285-00aa003049e2;CO)(OA;;WP;bf967950-0de6-11d0-a285-00aa003049e2;bf967a86-0de6-11d0-a285-00aa003049e2;CO)(OA;;WP;bf967953-0de6-11d0-a285-00aa003049e2;bf967a86-0de6-11d0-a285-00aa003049e2;CO)(OA;;RP;46a9b11d-60ae-405a-b7e8-ff8a58d456d2;;SU) -> D:(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;BO)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;AO)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;SY)(A;;LCRPDTLOCRSDRC;;;CO)(OA;;WP;4c164200-20c0-11d0-a768-00aa006e0529;;CO)(A;;LCRPLORC;;;AU)(OA;;CR;ab721a53-1e2f-11d0-9819-00aa0040529b;;WD)(A;;CCDC;;;PS)(OA;;CCDC;bf967aa8-0de6-11d0-a285-00aa003049e2;;PO)(OA;;RPWP;bf967a7f-0de6-11d0-a285-00aa003049e2;;SY)(OA;;SW;f3a64788-5306-11d1-a9c5-0000f80367c1;;PS)(OA;;RPWP;77b5b886-944a-11d1-aebd-0000f80367c1;;PS)(OA;;SW;72e39547-7b18-11d1-adef-00c04fd8d5cd;;PS)(OA;;SW;72e39547-7b18-11d1-adef-00c04fd8d5cd;;CO)(OA;;SW;f3a64788-5306-11d1-a9c5-0000f80367c1;;CO)(OA;;WP;3e0abfd0-126a-11d0-a060-00aa006c33ed;bf967a86-0de6-11d0-a285-00aa003049e2;CO)(OA;;WP;5f202010-79a5-11d0-9020-00c04fc2d4cf;bf967a86-0de6-11d0-a285-00aa003049e2;CO)(OA;;WP;bf967950-0de6-11d0-a285-00aa003049e2;bf967a86-0de6-11d0-a285-00aa003049e2;CO)(OA;;WP;bf967953-0de6-11d0-a285-00aa003049e2;bf967a86-0de6-11d0-a285-00aa003049e2;CO)(OA;;RP;46a9b11d-60ae-405a-b7e8-ff8a58d456d2;;SU)
+D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;BO)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU) -> D:(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;BO)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;SY)(A;;LCRPLORC;;;AU)
+D:(A;;;;;BO)(A;;;;;AO)(A;;;;;SY)(A;;RPCRLCLORCSDDT;;;CO)(OA;;WP;4c164200-20c0-11d0-a768-00aa006e0529;;CO)(A;;RPLCLORC;;;AU)(OA;;CR;ab721a53-1e2f-11d0-9819-00aa0040529b;;WD)(A;;CCDC;;;PS)(OA;;CCDC;bf967aa8-0de6-11d0-a285-00aa003049e2;;PO)(OA;;RPWP;bf967a7f-0de6-11d0-a285-00aa003049e2;;SY)(OA;;SW;f3a64788-5306-11d1-a9c5-0000f80367c1;;PS)(OA;;RPWP;77B5B886-944A-11d1-AEBD-0000F80367C1;;PS)(OA;;SW;72e39547-7b18-11d1-adef-00c04fd8d5cd;;PS)(OA;;SW;72e39547-7b18-11d1-adef-00c04fd8d5cd;;CO)(OA;;SW;f3a64788-5306-11d1-a9c5-0000f80367c1;;CO)(OA;;WP;3e0abfd0-126a-11d0-a060-00aa006c33ed;bf967a86-0de6-11d0-a285-00aa003049e2;CO)(OA;;WP;5f202010-79a5-11d0-9020-00c04fc2d4cf;bf967a86-0de6-11d0-a285-00aa003049e2;CO)(OA;;WP;bf967950-0de6-11d0-a285-00aa003049e2;bf967a86-0de6-11d0-a285-00aa003049e2;CO)(OA;;WP;bf967953-0de6-11d0-a285-00aa003049e2;bf967a86-0de6-11d0-a285-00aa003049e2;CO)(OA;;RP;46a9b11d-60ae-405a-b7e8-ff8a58d456d2;;SU) -> D:(A;;;;;BO)(A;;;;;AO)(A;;;;;SY)(A;;LCRPDTLOCRSDRC;;;CO)(OA;;WP;4c164200-20c0-11d0-a768-00aa006e0529;;CO)(A;;LCRPLORC;;;AU)(OA;;CR;ab721a53-1e2f-11d0-9819-00aa0040529b;;WD)(A;;CCDC;;;PS)(OA;;CCDC;bf967aa8-0de6-11d0-a285-00aa003049e2;;PO)(OA;;RPWP;bf967a7f-0de6-11d0-a285-00aa003049e2;;SY)(OA;;SW;f3a64788-5306-11d1-a9c5-0000f80367c1;;PS)(OA;;RPWP;77b5b886-944a-11d1-aebd-0000f80367c1;;PS)(OA;;SW;72e39547-7b18-11d1-adef-00c04fd8d5cd;;PS)(OA;;SW;72e39547-7b18-11d1-adef-00c04fd8d5cd;;CO)(OA;;SW;f3a64788-5306-11d1-a9c5-0000f80367c1;;CO)(OA;;WP;3e0abfd0-126a-11d0-a060-00aa006c33ed;bf967a86-0de6-11d0-a285-00aa003049e2;CO)(OA;;WP;5f202010-79a5-11d0-9020-00c04fc2d4cf;bf967a86-0de6-11d0-a285-00aa003049e2;CO)(OA;;WP;bf967950-0de6-11d0-a285-00aa003049e2;bf967a86-0de6-11d0-a285-00aa003049e2;CO)(OA;;WP;bf967953-0de6-11d0-a285-00aa003049e2;bf967a86-0de6-11d0-a285-00aa003049e2;CO)(OA;;RP;46a9b11d-60ae-405a-b7e8-ff8a58d456d2;;SU)
+D:(A;;RPLCLORC;;;AU) -> D:(A;;LCRPLORC;;;AU)
+D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;BO)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;AO)(A;;RPLCLORC;;;PS)(OA;;CR;ab721a55-1e2f-11d0-9819-00aa0040529b;;AU)(OA;;RP;46a9b11d-60ae-405a-b7e8-ff8a58d456d2;;SU) -> D:(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;BO)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;SY)(A;;LCRPLORC;;;AU)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;AO)(A;;LCRPLORC;;;PS)(OA;;CR;ab721a55-1e2f-11d0-9819-00aa0040529b;;AU)(OA;;RP;46a9b11d-60ae-405a-b7e8-ff8a58d456d2;;SU)
+D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;BO)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;CO) -> D:(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;BO)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;SY)(A;;LCRPLORC;;;AU)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;CO)
+D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;BO)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)S:(AU;SA;CRWP;;;WD) -> D:(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;BO)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;SY)(A;;LCRPLORC;;;AU)S:(AU;SA;WPCR;;;WD)
+D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSWRP;;;BO)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;AO)(A;;RPLCLORC;;;PS)(OA;;CR;ab721a53-1e2f-11d0-9819-00aa0040529b;;PS)(OA;;CR;ab721a54-1e2f-11d0-9819-00aa0040529b;;PS)(OA;;CR;ab721a56-1e2f-11d0-9819-00aa0040529b;;PS)(OA;;RPWP;77B5B886-944A-11d1-AEBD-0000F80367C1;;PS)(OA;;RPWP;E45795B2-9455-11d1-AEBD-0000F80367C1;;PS)(OA;;RPWP;E45795B3-9455-11d1-AEBD-0000F80367C1;;PS)(OA;;RP;037088f8-0ae1-11d2-b422-00a0c968f939;;RD)(OA;;RP;4c164200-20c0-11d0-a768-00aa006e0529;;RD)(OA;;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;;RD)(A;;RC;;;AU)(OA;;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;;AU)(OA;;RP;77B5B886-944A-11d1-AEBD-0000F80367C1;;AU)(OA;;RP;E45795B3-9455-11d1-AEBD-0000F80367C1;;AU)(OA;;RP;e48d0154-bcf8-11d1-8702-00c04fb96050;;AU)(OA;;CR;ab721a53-1e2f-11d0-9819-00aa0040529b;;WD)(OA;;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;;RD)(OA;;RPWP;bf967a7f-0de6-11d0-a285-00aa003049e2;;SY)(OA;;RP;46a9b11d-60ae-405a-b7e8-ff8a58d456d2;;SU)(OA;;WPRP;6db69a1c-9422-11d1-aebd-0000f80367c1;;SU) -> D:(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;BO)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;SY)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;AO)(A;;LCRPLORC;;;PS)(OA;;CR;ab721a53-1e2f-11d0-9819-00aa0040529b;;PS)(OA;;CR;ab721a54-1e2f-11d0-9819-00aa0040529b;;PS)(OA;;CR;ab721a56-1e2f-11d0-9819-00aa0040529b;;PS)(OA;;RPWP;77b5b886-944a-11d1-aebd-0000f80367c1;;PS)(OA;;RPWP;e45795b2-9455-11d1-aebd-0000f80367c1;;PS)(OA;;RPWP;e45795b3-9455-11d1-aebd-0000f80367c1;;PS)(OA;;RP;037088f8-0ae1-11d2-b422-00a0c968f939;;RD)(OA;;RP;4c164200-20c0-11d0-a768-00aa006e0529;;RD)(OA;;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;;RD)(A;;RC;;;AU)(OA;;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;;AU)(OA;;RP;77b5b886-944a-11d1-aebd-0000f80367c1;;AU)(OA;;RP;e45795b3-9455-11d1-aebd-0000f80367c1;;AU)(OA;;RP;e48d0154-bcf8-11d1-8702-00c04fb96050;;AU)(OA;;CR;ab721a53-1e2f-11d0-9819-00aa0040529b;;WD)(OA;;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;;RD)(OA;;RPWP;bf967a7f-0de6-11d0-a285-00aa003049e2;;SY)(OA;;RP;46a9b11d-60ae-405a-b7e8-ff8a58d456d2;;SU)(OA;;RPWP;6db69a1c-9422-11d1-aebd-0000f80367c1;;SU)
+D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY) -> D:(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;SY)
+D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)(A;;LCRPLORC;;;ED) -> D:(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;SY)(A;;LCRPLORC;;;AU)(A;;LCRPLORC;;;ED)
+D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;BO)(OA;;CCDC;bf967a86-0de6-11d0-a285-00aa003049e2;;AO)(OA;;CCDC;bf967aba-0de6-11d0-a285-00aa003049e2;;AO)(OA;;CCDC;bf967a9c-0de6-11d0-a285-00aa003049e2;;AO)(OA;;CCDC;bf967aa8-0de6-11d0-a285-00aa003049e2;;PO)(A;;RPLCLORC;;;AU)(A;;LCRPLORC;;;ED)(OA;;CCDC;4828CC14-1437-45bc-9B07-AD6F015E5F28;;AO) -> D:(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;SY)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;BO)(OA;;CCDC;bf967a86-0de6-11d0-a285-00aa003049e2;;AO)(OA;;CCDC;bf967aba-0de6-11d0-a285-00aa003049e2;;AO)(OA;;CCDC;bf967a9c-0de6-11d0-a285-00aa003049e2;;AO)(OA;;CCDC;bf967aa8-0de6-11d0-a285-00aa003049e2;;PO)(A;;LCRPLORC;;;AU)(A;;LCRPLORC;;;ED)(OA;;CCDC;4828cc14-1437-45bc-9b07-ad6f015e5f28;;AO)
+D:(A;;RPWPCRCCDCLCLORCWOWDSW;;;BO)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU) -> D:(A;;CCDCLCSWRPWPLOCRRCWDWO;;;BO)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;SY)(A;;LCRPLORC;;;AU)
+D:(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;BO)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU) -> D:(A;CI;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;BO)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;SY)(A;;LCRPLORC;;;AU)
+S:D:P -> D:PS:
+S:D: -> D:S:
+D:(A;;123456789;;;LG) -> D:(A;;0x75bcd15;;;LG)
+D:(A;;01234567;;;LG) -> D:(A;;0x53977;;;LG)
+D:(A;;16;;;LG) -> D:(A;;RP;;;LG)
+D:(A;;17;;;LG) -> D:(A;;CCRP;;;LG)
+D:(A;;0xff;;;LG) -> D:(A;;CCDCLCSWRPWPDTLO;;;LG)
+D:(A;;0xf01ff;;;LG) -> D:(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;LG)
+D:(A;;0xe00f0000;;;LG) -> D:(A;;SDRCWDWOGXGWGR;;;LG)
+D:ARPAI(A;;GA;;;SY) -> D:PARAI(A;;GA;;;SY)
+D:AIPAR(A;;GA;;;SY) -> D:PARAI(A;;GA;;;SY)
+D:PARP(A;;GA;;;SY) -> D:PAR(A;;GA;;;SY)
+D:PPPPPPPPPPPP(A;;GA;;;SY) -> D:P(A;;GA;;;SY)
+D:(A;;CC;;;S-1-21474836480-32-579) -> D:(A;;CC;;;S-1-0x500000000-32-579)
+D:(A;;GA;;;S-1-5000000000-30-40) -> D:(A;;GA;;;S-1-0x12A05F200-30-40)
+D:(A;;GA;;;S-1-0x2-3-4) -> D:(A;;GA;;;S-1-2-3-4)
+D:(A;;GA;;;S-1-0x20-3-4) -> D:(A;;GA;;;S-1-32-3-4)
+D:(A;;GA;;;S-1-3-0x00000002-3-4) -> D:(A;;GA;;;S-1-3-2-3-4)
+D:(A;;GA;;;S-1-3-0xffffffff-3-4) -> D:(A;;GA;;;S-1-3-4294967295-3-4)
+D:(A;;GA;;;S-1-5-21-0x1-0x2-0x3-513) -> D:(A;;GA;;;S-1-5-21-1-2-3-513)
+D:(A;;GA;;;S-1-5-21-2447931902-1787058256-3961074038-0x4b1) -> D:(A;;GA;;;S-1-5-21-2447931902-1787058256-3961074038-1201)
+O:S-1-2-0x200D: -> O:S-1-2-512D:
+O:S-1-2-0x2D:(A;;GA;;;LG) -> O:S-1-2-2D:(A;;GA;;;LG)
+D:(A;;0x00654321;;;WD)(A;;0x00abc101;;;S-1-5-21-11111111-22222222-33333333-101)(A;;0x00abc102;;;S-1-5-21-11111111-22222222-33333333-102)(A;;0x00abc103;;;S-1-5-21-11111111-22222222-33333333-103)(A;;0x00abc104;;;S-1-5-21-11111111-22222222-33333333-104)(A;;0x00abc105;;;S-1-5-21-11111111-22222222-33333333-105)(A;;0x00abc106;;;S-1-5-21-11111111-22222222-33333333-106)(A;;0x00abc107;;;S-1-5-21-11111111-22222222-33333333-107)(A;;0x00abc108;;;S-1-5-21-11111111-22222222-33333333-108)(A;;0x00abc109;;;S-1-5-21-11111111-22222222-33333333-109)(A;;0x00abc110;;;S-1-5-21-11111111-22222222-33333333-110)(A;;0x00abc111;;;S-1-5-21-11111111-22222222-33333333-111)(A;;0x00abc112;;;S-1-5-21-11111111-22222222-33333333-112)(A;;0x00abc113;;;S-1-5-21-11111111-22222222-33333333-113)(A;;0x00abc114;;;S-1-5-21-11111111-22222222-33333333-114)(A;;0x00abc115;;;S-1-5-21-11111111-22222222-33333333-115)(A;;0x00abc116;;;S-1-5-21-11111111-22222222-33333333-116)(A;;0x00abc117;;;S-1-5-21-11111111-22222222-33333333-117)(A;;0x00abc118;;;S-1-5-21-11111111-22222222-33333333-118)(A;;0x00abc119;;;S-1-5-21-11111111-22222222-33333333-119)(A;;0x00abc120;;;S-1-5-21-11111111-22222222-33333333-120)(A;;0x00abc121;;;S-1-5-21-11111111-22222222-33333333-121)(A;;0x00abc122;;;S-1-5-21-11111111-22222222-33333333-122)(A;;0x00abc123;;;S-1-5-21-11111111-22222222-33333333-123)(A;;0x00abc124;;;S-1-5-21-11111111-22222222-33333333-124)(A;;0x00abc125;;;S-1-5-21-11111111-22222222-33333333-125)(A;;0x00abc126;;;S-1-5-21-11111111-22222222-33333333-126)(A;;0x00abc127;;;S-1-5-21-11111111-22222222-33333333-127)(A;;0x00abc128;;;S-1-5-21-11111111-22222222-33333333-128)(A;;0x00abc129;;;S-1-5-21-11111111-22222222-33333333-129)(A;;0x00abc130;;;S-1-5-21-11111111-22222222-33333333-130)(A;;0x00abc131;;;S-1-5-21-11111111-22222222-33333333-131)(A;;0x00abc132;;;S-1-5-21-11111111-22222222-33333333-132)(A;;0x00abc133;;;S-1-5-21-11111111-22222222-33333333-133)(A;;0x00abc134;;;S-1-5-21-11111111-22222222-33333333-134)(A;;0x00abc135;;;S-1-5-21-11111111-22222222-33333333-135)(A;;0x00abc136;;;S-1-5-21-11111111-22222222-33333333-136)(A;;0x00abc137;;;S-1-5-21-11111111-22222222-33333333-137)(A;;0x00abc138;;;S-1-5-21-11111111-22222222-33333333-138)(A;;0x00abc139;;;S-1-5-21-11111111-22222222-33333333-139)(A;;0x00abc140;;;S-1-5-21-11111111-22222222-33333333-140)(A;;0x00abc141;;;S-1-5-21-11111111-22222222-33333333-141)(A;;0x00abc142;;;S-1-5-21-11111111-22222222-33333333-142)(A;;0x00abc143;;;S-1-5-21-11111111-22222222-33333333-143)(A;;0x00abc144;;;S-1-5-21-11111111-22222222-33333333-144)(A;;0x00abc145;;;S-1-5-21-11111111-22222222-33333333-145)(A;;0x00abc146;;;S-1-5-21-11111111-22222222-33333333-146)(A;;0x00abc147;;;S-1-5-21-11111111-22222222-33333333-147)(A;;0x00abc148;;;S-1-5-21-11111111-22222222-33333333-148)(A;;0x00abc149;;;S-1-5-21-11111111-22222222-33333333-149)(A;;0x00abc150;;;S-1-5-21-11111111-22222222-33333333-150)(A;;0x00abc151;;;S-1-5-21-11111111-22222222-33333333-151)(A;;0x00abc152;;;S-1-5-21-11111111-22222222-33333333-152)(A;;0x00abc153;;;S-1-5-21-11111111-22222222-33333333-153)(A;;0x00abc154;;;S-1-5-21-11111111-22222222-33333333-154)(A;;0x00abc155;;;S-1-5-21-11111111-22222222-33333333-155)(A;;0x00abc156;;;S-1-5-21-11111111-22222222-33333333-156)(A;;0x00abc157;;;S-1-5-21-11111111-22222222-33333333-157)(A;;0x00abc158;;;S-1-5-21-11111111-22222222-33333333-158)(A;;0x00abc159;;;S-1-5-21-11111111-22222222-33333333-159)(A;;0x00abc160;;;S-1-5-21-11111111-22222222-33333333-160)(A;;0x00abc161;;;S-1-5-21-11111111-22222222-33333333-161)(A;;0x00abc162;;;S-1-5-21-11111111-22222222-33333333-162)(A;;0x00abc163;;;S-1-5-21-11111111-22222222-33333333-163)(A;;0x00abc164;;;S-1-5-21-11111111-22222222-33333333-164)(A;;0x00abc165;;;S-1-5-21-11111111-22222222-33333333-165)(A;;0x00abc166;;;S-1-5-21-11111111-22222222-33333333-166)(A;;0x00abc167;;;S-1-5-21-11111111-22222222-33333333-167)(A;;0x00abc168;;;S-1-5-21-11111111-22222222-33333333-168)(A;;0x00abc169;;;S-1-5-21-11111111-22222222-33333333-169)(A;;0x00abc170;;;S-1-5-21-11111111-22222222-33333333-170)(A;;0x00abc171;;;S-1-5-21-11111111-22222222-33333333-171)(A;;0x00abc172;;;S-1-5-21-11111111-22222222-33333333-172)(A;;0x00abc173;;;S-1-5-21-11111111-22222222-33333333-173)(A;;0x00abc174;;;S-1-5-21-11111111-22222222-33333333-174)(A;;0x00abc175;;;S-1-5-21-11111111-22222222-33333333-175)(A;;0x00abc176;;;S-1-5-21-11111111-22222222-33333333-176)(A;;0x00abc177;;;S-1-5-21-11111111-22222222-33333333-177)(A;;0x00abc178;;;S-1-5-21-11111111-22222222-33333333-178)(A;;0x00abc179;;;S-1-5-21-11111111-22222222-33333333-179)(A;;0x00abc180;;;S-1-5-21-11111111-22222222-33333333-180)(A;;0x00abc181;;;S-1-5-21-11111111-22222222-33333333-181)(A;;0x00abc182;;;S-1-5-21-11111111-22222222-33333333-182)(A;;0x00abc183;;;S-1-5-21-11111111-22222222-33333333-183)(A;;0x00abc184;;;S-1-5-21-11111111-22222222-33333333-184)(A;;0x00abc185;;;S-1-5-21-11111111-22222222-33333333-185)(A;;0x00abc186;;;S-1-5-21-11111111-22222222-33333333-186)(A;;0x00abc187;;;S-1-5-21-11111111-22222222-33333333-187)(A;;0x00abc188;;;S-1-5-21-11111111-22222222-33333333-188)(A;;0x00abc189;;;S-1-5-21-11111111-22222222-33333333-189)(A;;0x00abc190;;;S-1-5-21-11111111-22222222-33333333-190)(A;;0x00abc191;;;S-1-5-21-11111111-22222222-33333333-191)(A;;0x00abc192;;;S-1-5-21-11111111-22222222-33333333-192)(A;;0x00abc193;;;S-1-5-21-11111111-22222222-33333333-193)(A;;0x00abc194;;;S-1-5-21-11111111-22222222-33333333-194)(A;;0x00abc195;;;S-1-5-21-11111111-22222222-33333333-195)(A;;0x00abc196;;;S-1-5-21-11111111-22222222-33333333-196)(A;;0x00abc197;;;S-1-5-21-11111111-22222222-33333333-197)(A;;0x00abc198;;;S-1-5-21-11111111-22222222-33333333-198)(A;;0x00abc199;;;S-1-5-21-11111111-22222222-33333333-199)(A;;0x00abc200;;;S-1-5-21-11111111-22222222-33333333-200)(A;;0x00abc201;;;S-1-5-21-11111111-22222222-33333333-201)(A;;0x00abc202;;;S-1-5-21-11111111-22222222-33333333-202)(A;;0x00abc203;;;S-1-5-21-11111111-22222222-33333333-203)(A;;0x00abc204;;;S-1-5-21-11111111-22222222-33333333-204)(A;;0x00abc205;;;S-1-5-21-11111111-22222222-33333333-205)(A;;0x00abc206;;;S-1-5-21-11111111-22222222-33333333-206)(A;;0x00abc207;;;S-1-5-21-11111111-22222222-33333333-207)(A;;0x00abc208;;;S-1-5-21-11111111-22222222-33333333-208)(A;;0x00abc209;;;S-1-5-21-11111111-22222222-33333333-209)(A;;0x00abc210;;;S-1-5-21-11111111-22222222-33333333-210)(A;;0x00abc211;;;S-1-5-21-11111111-22222222-33333333-211)(A;;0x00abc212;;;S-1-5-21-11111111-22222222-33333333-212)(A;;0x00abc213;;;S-1-5-21-11111111-22222222-33333333-213)(A;;0x00abc214;;;S-1-5-21-11111111-22222222-33333333-214)(A;;0x00abc215;;;S-1-5-21-11111111-22222222-33333333-215)(A;;0x00abc216;;;S-1-5-21-11111111-22222222-33333333-216)(A;;0x00abc217;;;S-1-5-21-11111111-22222222-33333333-217)(A;;0x00abc218;;;S-1-5-21-11111111-22222222-33333333-218)(A;;0x00abc219;;;S-1-5-21-11111111-22222222-33333333-219)(A;;0x00abc220;;;S-1-5-21-11111111-22222222-33333333-220)(A;;0x00abc221;;;S-1-5-21-11111111-22222222-33333333-221)(A;;0x00abc222;;;S-1-5-21-11111111-22222222-33333333-222)(A;;0x00abc223;;;S-1-5-21-11111111-22222222-33333333-223)(A;;0x00abc224;;;S-1-5-21-11111111-22222222-33333333-224)(A;;0x00abc225;;;S-1-5-21-11111111-22222222-33333333-225)(A;;0x00abc226;;;S-1-5-21-11111111-22222222-33333333-226)(A;;0x00abc227;;;S-1-5-21-11111111-22222222-33333333-227)(A;;0x00abc228;;;S-1-5-21-11111111-22222222-33333333-228)(A;;0x00abc229;;;S-1-5-21-11111111-22222222-33333333-229)(A;;0x00abc230;;;S-1-5-21-11111111-22222222-33333333-230)(A;;0x00abc231;;;S-1-5-21-11111111-22222222-33333333-231)(A;;0x00abc232;;;S-1-5-21-11111111-22222222-33333333-232)(A;;0x00abc233;;;S-1-5-21-11111111-22222222-33333333-233)(A;;0x00abc234;;;S-1-5-21-11111111-22222222-33333333-234)(A;;0x00abc235;;;S-1-5-21-11111111-22222222-33333333-235)(A;;0x00abc236;;;S-1-5-21-11111111-22222222-33333333-236)(A;;0x00abc237;;;S-1-5-21-11111111-22222222-33333333-237)(A;;0x00abc238;;;S-1-5-21-11111111-22222222-33333333-238)(A;;0x00abc239;;;S-1-5-21-11111111-22222222-33333333-239)(A;;0x00abc240;;;S-1-5-21-11111111-22222222-33333333-240)(A;;0x00abc241;;;S-1-5-21-11111111-22222222-33333333-241)(A;;0x00abc242;;;S-1-5-21-11111111-22222222-33333333-242)(A;;0x00abc243;;;S-1-5-21-11111111-22222222-33333333-243)(A;;0x00abc244;;;S-1-5-21-11111111-22222222-33333333-244)(A;;0x00abc245;;;S-1-5-21-11111111-22222222-33333333-245)(A;;0x00abc246;;;S-1-5-21-11111111-22222222-33333333-246)(A;;0x00abc247;;;S-1-5-21-11111111-22222222-33333333-247)(A;;0x00abc248;;;S-1-5-21-11111111-22222222-33333333-248)(A;;0x00abc249;;;S-1-5-21-11111111-22222222-33333333-249)(A;;0x00abc250;;;S-1-5-21-11111111-22222222-33333333-250)(A;;0x00abc251;;;S-1-5-21-11111111-22222222-33333333-251)(A;;0x00abc252;;;S-1-5-21-11111111-22222222-33333333-252)(A;;0x00abc253;;;S-1-5-21-11111111-22222222-33333333-253)(A;;0x00abc254;;;S-1-5-21-11111111-22222222-33333333-254)(A;;0x00abc255;;;S-1-5-21-11111111-22222222-33333333-255)(A;;0x00abc256;;;S-1-5-21-11111111-22222222-33333333-256)(A;;0x00abc257;;;S-1-5-21-11111111-22222222-33333333-257)(A;;0x00abc258;;;S-1-5-21-11111111-22222222-33333333-258)(A;;0x00abc259;;;S-1-5-21-11111111-22222222-33333333-259)(A;;0x00abc260;;;S-1-5-21-11111111-22222222-33333333-260)(A;;0x00abc261;;;S-1-5-21-11111111-22222222-33333333-261)(A;;0x00abc262;;;S-1-5-21-11111111-22222222-33333333-262)(A;;0x00abc263;;;S-1-5-21-11111111-22222222-33333333-263)(A;;0x00abc264;;;S-1-5-21-11111111-22222222-33333333-264)(A;;0x00abc265;;;S-1-5-21-11111111-22222222-33333333-265)(A;;0x00abc266;;;S-1-5-21-11111111-22222222-33333333-266)(A;;0x00abc267;;;S-1-5-21-11111111-22222222-33333333-267)(A;;0x00abc268;;;S-1-5-21-11111111-22222222-33333333-268)(A;;0x00abc269;;;S-1-5-21-11111111-22222222-33333333-269)(A;;0x00abc270;;;S-1-5-21-11111111-22222222-33333333-270)(A;;0x00abc271;;;S-1-5-21-11111111-22222222-33333333-271)(A;;0x00abc272;;;S-1-5-21-11111111-22222222-33333333-272)(A;;0x00abc273;;;S-1-5-21-11111111-22222222-33333333-273)(A;;0x00abc274;;;S-1-5-21-11111111-22222222-33333333-274)(A;;0x00abc275;;;S-1-5-21-11111111-22222222-33333333-275)(A;;0x00abc276;;;S-1-5-21-11111111-22222222-33333333-276)(A;;0x00abc277;;;S-1-5-21-11111111-22222222-33333333-277)(A;;0x00abc278;;;S-1-5-21-11111111-22222222-33333333-278)(A;;0x00abc279;;;S-1-5-21-11111111-22222222-33333333-279)(A;;0x00abc280;;;S-1-5-21-11111111-22222222-33333333-280)(A;;0x00abc281;;;S-1-5-21-11111111-22222222-33333333-281)(A;;0x00abc282;;;S-1-5-21-11111111-22222222-33333333-282)(A;;0x00abc283;;;S-1-5-21-11111111-22222222-33333333-283)(A;;0x00abc284;;;S-1-5-21-11111111-22222222-33333333-284)(A;;0x00abc285;;;S-1-5-21-11111111-22222222-33333333-285)(A;;0x00abc286;;;S-1-5-21-11111111-22222222-33333333-286)(A;;0x00abc287;;;S-1-5-21-11111111-22222222-33333333-287)(A;;0x00abc288;;;S-1-5-21-11111111-22222222-33333333-288)(A;;0x00abc289;;;S-1-5-21-11111111-22222222-33333333-289)(A;;0x00abc290;;;S-1-5-21-11111111-22222222-33333333-290)(A;;0x00abc291;;;S-1-5-21-11111111-22222222-33333333-291)(A;;0x00abc292;;;S-1-5-21-11111111-22222222-33333333-292)(A;;0x00abc293;;;S-1-5-21-11111111-22222222-33333333-293)(A;;0x00abc294;;;S-1-5-21-11111111-22222222-33333333-294)(A;;0x00abc295;;;S-1-5-21-11111111-22222222-33333333-295)(A;;0x00abc296;;;S-1-5-21-11111111-22222222-33333333-296)(A;;0x00abc297;;;S-1-5-21-11111111-22222222-33333333-297)(A;;0x00abc298;;;S-1-5-21-11111111-22222222-33333333-298)(A;;0x00abc299;;;S-1-5-21-11111111-22222222-33333333-299)(A;;0x00abc300;;;S-1-5-21-11111111-22222222-33333333-300)(A;;0x00abc301;;;S-1-5-21-11111111-22222222-33333333-301)(A;;0x00abc302;;;S-1-5-21-11111111-22222222-33333333-302)(A;;0x00abc303;;;S-1-5-21-11111111-22222222-33333333-303)(A;;0x00abc304;;;S-1-5-21-11111111-22222222-33333333-304)(A;;0x00abc305;;;S-1-5-21-11111111-22222222-33333333-305)(A;;0x00abc306;;;S-1-5-21-11111111-22222222-33333333-306)(A;;0x00abc307;;;S-1-5-21-11111111-22222222-33333333-307)(A;;0x00abc308;;;S-1-5-21-11111111-22222222-33333333-308)(A;;0x00abc309;;;S-1-5-21-11111111-22222222-33333333-309)(A;;0x00abc310;;;S-1-5-21-11111111-22222222-33333333-310)(A;;0x00abc311;;;S-1-5-21-11111111-22222222-33333333-311)(A;;0x00abc312;;;S-1-5-21-11111111-22222222-33333333-312)(A;;0x00abc313;;;S-1-5-21-11111111-22222222-33333333-313)(A;;0x00abc314;;;S-1-5-21-11111111-22222222-33333333-314)(A;;0x00abc315;;;S-1-5-21-11111111-22222222-33333333-315)(A;;0x00abc316;;;S-1-5-21-11111111-22222222-33333333-316)(A;;0x00abc317;;;S-1-5-21-11111111-22222222-33333333-317)(A;;0x00abc318;;;S-1-5-21-11111111-22222222-33333333-318)(A;;0x00abc319;;;S-1-5-21-11111111-22222222-33333333-319)(A;;0x00abc320;;;S-1-5-21-11111111-22222222-33333333-320)(A;;0x00abc321;;;S-1-5-21-11111111-22222222-33333333-321)(A;;0x00abc322;;;S-1-5-21-11111111-22222222-33333333-322)(A;;0x00abc323;;;S-1-5-21-11111111-22222222-33333333-323)(A;;0x00abc324;;;S-1-5-21-11111111-22222222-33333333-324)(A;;0x00abc325;;;S-1-5-21-11111111-22222222-33333333-325)(A;;0x00abc326;;;S-1-5-21-11111111-22222222-33333333-326)(A;;0x00abc327;;;S-1-5-21-11111111-22222222-33333333-327)(A;;0x00abc328;;;S-1-5-21-11111111-22222222-33333333-328)(A;;0x00abc329;;;S-1-5-21-11111111-22222222-33333333-329)(A;;0x00abc330;;;S-1-5-21-11111111-22222222-33333333-330)(A;;0x00abc331;;;S-1-5-21-11111111-22222222-33333333-331)(A;;0x00abc332;;;S-1-5-21-11111111-22222222-33333333-332)(A;;0x00abc333;;;S-1-5-21-11111111-22222222-33333333-333)(A;;0x00abc334;;;S-1-5-21-11111111-22222222-33333333-334)(A;;0x00abc335;;;S-1-5-21-11111111-22222222-33333333-335)(A;;0x00abc336;;;S-1-5-21-11111111-22222222-33333333-336)(A;;0x00abc337;;;S-1-5-21-11111111-22222222-33333333-337)(A;;0x00abc338;;;S-1-5-21-11111111-22222222-33333333-338)(A;;0x00abc339;;;S-1-5-21-11111111-22222222-33333333-339)(A;;0x00abc340;;;S-1-5-21-11111111-22222222-33333333-340)(A;;0x00abc341;;;S-1-5-21-11111111-22222222-33333333-341)(A;;0x00abc342;;;S-1-5-21-11111111-22222222-33333333-342)(A;;0x00abc343;;;S-1-5-21-11111111-22222222-33333333-343)(A;;0x00abc344;;;S-1-5-21-11111111-22222222-33333333-344)(A;;0x00abc345;;;S-1-5-21-11111111-22222222-33333333-345)(A;;0x00abc346;;;S-1-5-21-11111111-22222222-33333333-346)(A;;0x00abc347;;;S-1-5-21-11111111-22222222-33333333-347)(A;;0x00abc348;;;S-1-5-21-11111111-22222222-33333333-348)(A;;0x00abc349;;;S-1-5-21-11111111-22222222-33333333-349)(A;;0x00abc350;;;S-1-5-21-11111111-22222222-33333333-350)(A;;0x00abc351;;;S-1-5-21-11111111-22222222-33333333-351)(A;;0x00abc352;;;S-1-5-21-11111111-22222222-33333333-352)(A;;0x00abc353;;;S-1-5-21-11111111-22222222-33333333-353)(A;;0x00abc354;;;S-1-5-21-11111111-22222222-33333333-354)(A;;0x00abc355;;;S-1-5-21-11111111-22222222-33333333-355)(A;;0x00abc356;;;S-1-5-21-11111111-22222222-33333333-356)(A;;0x00abc357;;;S-1-5-21-11111111-22222222-33333333-357)(A;;0x00abc358;;;S-1-5-21-11111111-22222222-33333333-358)(A;;0x00abc359;;;S-1-5-21-11111111-22222222-33333333-359)(A;;0x00abc360;;;S-1-5-21-11111111-22222222-33333333-360)(A;;0x00abc361;;;S-1-5-21-11111111-22222222-33333333-361)(A;;0x00abc362;;;S-1-5-21-11111111-22222222-33333333-362)(A;;0x00abc363;;;S-1-5-21-11111111-22222222-33333333-363)(A;;0x00abc364;;;S-1-5-21-11111111-22222222-33333333-364)(A;;0x00abc365;;;S-1-5-21-11111111-22222222-33333333-365)(A;;0x00abc366;;;S-1-5-21-11111111-22222222-33333333-366)(A;;0x00abc367;;;S-1-5-21-11111111-22222222-33333333-367)(A;;0x00abc368;;;S-1-5-21-11111111-22222222-33333333-368)(A;;0x00abc369;;;S-1-5-21-11111111-22222222-33333333-369)(A;;0x00abc370;;;S-1-5-21-11111111-22222222-33333333-370)(A;;0x00abc371;;;S-1-5-21-11111111-22222222-33333333-371)(A;;0x00abc372;;;S-1-5-21-11111111-22222222-33333333-372)(A;;0x00abc373;;;S-1-5-21-11111111-22222222-33333333-373)(A;;0x00abc374;;;S-1-5-21-11111111-22222222-33333333-374)(A;;0x00abc375;;;S-1-5-21-11111111-22222222-33333333-375)(A;;0x00abc376;;;S-1-5-21-11111111-22222222-33333333-376)(A;;0x00abc377;;;S-1-5-21-11111111-22222222-33333333-377)(A;;0x00abc378;;;S-1-5-21-11111111-22222222-33333333-378)(A;;0x00abc379;;;S-1-5-21-11111111-22222222-33333333-379)(A;;0x00abc380;;;S-1-5-21-11111111-22222222-33333333-380)(A;;0x00abc381;;;S-1-5-21-11111111-22222222-33333333-381)(A;;0x00abc382;;;S-1-5-21-11111111-22222222-33333333-382)(A;;0x00abc383;;;S-1-5-21-11111111-22222222-33333333-383)(A;;0x00abc384;;;S-1-5-21-11111111-22222222-33333333-384)(A;;0x00abc385;;;S-1-5-21-11111111-22222222-33333333-385)(A;;0x00abc386;;;S-1-5-21-11111111-22222222-33333333-386)(A;;0x00abc387;;;S-1-5-21-11111111-22222222-33333333-387)(A;;0x00abc388;;;S-1-5-21-11111111-22222222-33333333-388)(A;;0x00abc389;;;S-1-5-21-11111111-22222222-33333333-389)(A;;0x00abc390;;;S-1-5-21-11111111-22222222-33333333-390)(A;;0x00abc391;;;S-1-5-21-11111111-22222222-33333333-391)(A;;0x00abc392;;;S-1-5-21-11111111-22222222-33333333-392)(A;;0x00abc393;;;S-1-5-21-11111111-22222222-33333333-393)(A;;0x00abc394;;;S-1-5-21-11111111-22222222-33333333-394)(A;;0x00abc395;;;S-1-5-21-11111111-22222222-33333333-395)(A;;0x00abc396;;;S-1-5-21-11111111-22222222-33333333-396)(A;;0x00abc397;;;S-1-5-21-11111111-22222222-33333333-397)(A;;0x00abc398;;;S-1-5-21-11111111-22222222-33333333-398)(A;;0x00abc399;;;S-1-5-21-11111111-22222222-33333333-399)(A;;0x00abc400;;;S-1-5-21-11111111-22222222-33333333-400)(A;;0x00abc401;;;S-1-5-21-11111111-22222222-33333333-401)(A;;0x00abc402;;;S-1-5-21-11111111-22222222-33333333-402)(A;;0x00abc403;;;S-1-5-21-11111111-22222222-33333333-403)(A;;0x00abc404;;;S-1-5-21-11111111-22222222-33333333-404)(A;;0x00abc405;;;S-1-5-21-11111111-22222222-33333333-405)(A;;0x00abc406;;;S-1-5-21-11111111-22222222-33333333-406)(A;;0x00abc407;;;S-1-5-21-11111111-22222222-33333333-407)(A;;0x00abc408;;;S-1-5-21-11111111-22222222-33333333-408)(A;;0x00abc409;;;S-1-5-21-11111111-22222222-33333333-409)(A;;0x00abc410;;;S-1-5-21-11111111-22222222-33333333-410)(A;;0x00abc411;;;S-1-5-21-11111111-22222222-33333333-411)(A;;0x00abc412;;;S-1-5-21-11111111-22222222-33333333-412)(A;;0x00abc413;;;S-1-5-21-11111111-22222222-33333333-413)(A;;0x00abc414;;;S-1-5-21-11111111-22222222-33333333-414)(A;;0x00abc415;;;S-1-5-21-11111111-22222222-33333333-415)(A;;0x00abc416;;;S-1-5-21-11111111-22222222-33333333-416)(A;;0x00abc417;;;S-1-5-21-11111111-22222222-33333333-417)(A;;0x00abc418;;;S-1-5-21-11111111-22222222-33333333-418)(A;;0x00abc419;;;S-1-5-21-11111111-22222222-33333333-419)(A;;0x00abc420;;;S-1-5-21-11111111-22222222-33333333-420)(A;;0x00abc421;;;S-1-5-21-11111111-22222222-33333333-421)(A;;0x00abc422;;;S-1-5-21-11111111-22222222-33333333-422)(A;;0x00abc423;;;S-1-5-21-11111111-22222222-33333333-423)(A;;0x00abc424;;;S-1-5-21-11111111-22222222-33333333-424)(A;;0x00abc425;;;S-1-5-21-11111111-22222222-33333333-425)(A;;0x00abc426;;;S-1-5-21-11111111-22222222-33333333-426)(A;;0x00abc427;;;S-1-5-21-11111111-22222222-33333333-427)(A;;0x00abc428;;;S-1-5-21-11111111-22222222-33333333-428)(A;;0x00abc429;;;S-1-5-21-11111111-22222222-33333333-429)(A;;0x00abc430;;;S-1-5-21-11111111-22222222-33333333-430)(A;;0x00abc431;;;S-1-5-21-11111111-22222222-33333333-431)(A;;0x00abc432;;;S-1-5-21-11111111-22222222-33333333-432)(A;;0x00abc433;;;S-1-5-21-11111111-22222222-33333333-433)(A;;0x00abc434;;;S-1-5-21-11111111-22222222-33333333-434)(A;;0x00abc435;;;S-1-5-21-11111111-22222222-33333333-435)(A;;0x00abc436;;;S-1-5-21-11111111-22222222-33333333-436)(A;;0x00abc437;;;S-1-5-21-11111111-22222222-33333333-437)(A;;0x00abc438;;;S-1-5-21-11111111-22222222-33333333-438)(A;;0x00abc439;;;S-1-5-21-11111111-22222222-33333333-439)(A;;0x00abc440;;;S-1-5-21-11111111-22222222-33333333-440)(A;;0x00abc441;;;S-1-5-21-11111111-22222222-33333333-441)(A;;0x00abc442;;;S-1-5-21-11111111-22222222-33333333-442)(A;;0x00abc443;;;S-1-5-21-11111111-22222222-33333333-443)(A;;0x00abc444;;;S-1-5-21-11111111-22222222-33333333-444)(A;;0x00abc445;;;S-1-5-21-11111111-22222222-33333333-445)(A;;0x00abc446;;;S-1-5-21-11111111-22222222-33333333-446)(A;;0x00abc447;;;S-1-5-21-11111111-22222222-33333333-447)(A;;0x00abc448;;;S-1-5-21-11111111-22222222-33333333-448)(A;;0x00abc449;;;S-1-5-21-11111111-22222222-33333333-449)(A;;0x00abc450;;;S-1-5-21-11111111-22222222-33333333-450)(A;;0x00abc451;;;S-1-5-21-11111111-22222222-33333333-451)(A;;0x00abc452;;;S-1-5-21-11111111-22222222-33333333-452)(A;;0x00abc453;;;S-1-5-21-11111111-22222222-33333333-453)(A;;0x00abc454;;;S-1-5-21-11111111-22222222-33333333-454)(A;;0x00abc455;;;S-1-5-21-11111111-22222222-33333333-455)(A;;0x00abc456;;;S-1-5-21-11111111-22222222-33333333-456)(A;;0x00abc457;;;S-1-5-21-11111111-22222222-33333333-457)(A;;0x00abc458;;;S-1-5-21-11111111-22222222-33333333-458)(A;;0x00abc459;;;S-1-5-21-11111111-22222222-33333333-459)(A;;0x00abc460;;;S-1-5-21-11111111-22222222-33333333-460)(A;;0x00abc461;;;S-1-5-21-11111111-22222222-33333333-461)(A;;0x00abc462;;;S-1-5-21-11111111-22222222-33333333-462)(A;;0x00abc463;;;S-1-5-21-11111111-22222222-33333333-463)(A;;0x00abc464;;;S-1-5-21-11111111-22222222-33333333-464)(A;;0x00abc465;;;S-1-5-21-11111111-22222222-33333333-465)(A;;0x00abc466;;;S-1-5-21-11111111-22222222-33333333-466)(A;;0x00abc467;;;S-1-5-21-11111111-22222222-33333333-467)(A;;0x00abc468;;;S-1-5-21-11111111-22222222-33333333-468)(A;;0x00abc469;;;S-1-5-21-11111111-22222222-33333333-469)(A;;0x00abc470;;;S-1-5-21-11111111-22222222-33333333-470)(A;;0x00abc471;;;S-1-5-21-11111111-22222222-33333333-471)(A;;0x00abc472;;;S-1-5-21-11111111-22222222-33333333-472)(A;;0x00abc473;;;S-1-5-21-11111111-22222222-33333333-473)(A;;0x00abc474;;;S-1-5-21-11111111-22222222-33333333-474)(A;;0x00abc475;;;S-1-5-21-11111111-22222222-33333333-475)(A;;0x00abc476;;;S-1-5-21-11111111-22222222-33333333-476)(A;;0x00abc477;;;S-1-5-21-11111111-22222222-33333333-477)(A;;0x00abc478;;;S-1-5-21-11111111-22222222-33333333-478)(A;;0x00abc479;;;S-1-5-21-11111111-22222222-33333333-479)(A;;0x00abc480;;;S-1-5-21-11111111-22222222-33333333-480)(A;;0x00abc481;;;S-1-5-21-11111111-22222222-33333333-481)(A;;0x00abc482;;;S-1-5-21-11111111-22222222-33333333-482)(A;;0x00abc483;;;S-1-5-21-11111111-22222222-33333333-483)(A;;0x00abc484;;;S-1-5-21-11111111-22222222-33333333-484)(A;;0x00abc485;;;S-1-5-21-11111111-22222222-33333333-485)(A;;0x00abc486;;;S-1-5-21-11111111-22222222-33333333-486)(A;;0x00abc487;;;S-1-5-21-11111111-22222222-33333333-487)(A;;0x00abc488;;;S-1-5-21-11111111-22222222-33333333-488)(A;;0x00abc489;;;S-1-5-21-11111111-22222222-33333333-489)(A;;0x00abc490;;;S-1-5-21-11111111-22222222-33333333-490)(A;;0x00abc491;;;S-1-5-21-11111111-22222222-33333333-491)(A;;0x00abc492;;;S-1-5-21-11111111-22222222-33333333-492)(A;;0x00abc493;;;S-1-5-21-11111111-22222222-33333333-493)(A;;0x00abc494;;;S-1-5-21-11111111-22222222-33333333-494)(A;;0x00abc495;;;S-1-5-21-11111111-22222222-33333333-495)(A;;0x00abc496;;;S-1-5-21-11111111-22222222-33333333-496)(A;;0x00abc497;;;S-1-5-21-11111111-22222222-33333333-497)(A;;0x00abc498;;;S-1-5-21-11111111-22222222-33333333-498)(A;;0x00abc499;;;S-1-5-21-11111111-22222222-33333333-499)(A;;0x00abc500;;;S-1-5-21-11111111-22222222-33333333-500)(A;;0x00abc501;;;S-1-5-21-11111111-22222222-33333333-501)(A;;0x00abc502;;;S-1-5-21-11111111-22222222-33333333-502)(A;;0x00abc503;;;S-1-5-21-11111111-22222222-33333333-503)(A;;0x00abc504;;;S-1-5-21-11111111-22222222-33333333-504)(A;;0x00abc505;;;S-1-5-21-11111111-22222222-33333333-505)(A;;0x00abc506;;;S-1-5-21-11111111-22222222-33333333-506)(A;;0x00abc507;;;S-1-5-21-11111111-22222222-33333333-507)(A;;0x00abc508;;;S-1-5-21-11111111-22222222-33333333-508)(A;;0x00abc509;;;S-1-5-21-11111111-22222222-33333333-509)(A;;0x00abc510;;;S-1-5-21-11111111-22222222-33333333-510)(A;;0x00abc511;;;S-1-5-21-11111111-22222222-33333333-511)(A;;0x00abc512;;;S-1-5-21-11111111-22222222-33333333-512)(A;;0x00abc513;;;S-1-5-21-11111111-22222222-33333333-513)(A;;0x00abc514;;;S-1-5-21-11111111-22222222-33333333-514)(A;;0x00abc515;;;S-1-5-21-11111111-22222222-33333333-515)(A;;0x00abc516;;;S-1-5-21-11111111-22222222-33333333-516)(A;;0x00abc517;;;S-1-5-21-11111111-22222222-33333333-517)(A;;0x00abc518;;;S-1-5-21-11111111-22222222-33333333-518)(A;;0x00abc519;;;S-1-5-21-11111111-22222222-33333333-519)(A;;0x00abc520;;;S-1-5-21-11111111-22222222-33333333-520)(A;;0x00abc521;;;S-1-5-21-11111111-22222222-33333333-521)(A;;0x00abc522;;;S-1-5-21-11111111-22222222-33333333-522)(A;;0x00abc523;;;S-1-5-21-11111111-22222222-33333333-523)(A;;0x00abc524;;;S-1-5-21-11111111-22222222-33333333-524)(A;;0x00abc525;;;S-1-5-21-11111111-22222222-33333333-525)(A;;0x00abc526;;;S-1-5-21-11111111-22222222-33333333-526)(A;;0x00abc527;;;S-1-5-21-11111111-22222222-33333333-527)(A;;0x00abc528;;;S-1-5-21-11111111-22222222-33333333-528)(A;;0x00abc529;;;S-1-5-21-11111111-22222222-33333333-529)(A;;0x00abc530;;;S-1-5-21-11111111-22222222-33333333-530)(A;;0x00abc531;;;S-1-5-21-11111111-22222222-33333333-531)(A;;0x00abc532;;;S-1-5-21-11111111-22222222-33333333-532)(A;;0x00abc533;;;S-1-5-21-11111111-22222222-33333333-533)(A;;0x00abc534;;;S-1-5-21-11111111-22222222-33333333-534)(A;;0x00abc535;;;S-1-5-21-11111111-22222222-33333333-535)(A;;0x00abc536;;;S-1-5-21-11111111-22222222-33333333-536)(A;;0x00abc537;;;S-1-5-21-11111111-22222222-33333333-537)(A;;0x00abc538;;;S-1-5-21-11111111-22222222-33333333-538)(A;;0x00abc539;;;S-1-5-21-11111111-22222222-33333333-539)(A;;0x00abc540;;;S-1-5-21-11111111-22222222-33333333-540)(A;;0x00abc541;;;S-1-5-21-11111111-22222222-33333333-541)(A;;0x00abc542;;;S-1-5-21-11111111-22222222-33333333-542)(A;;0x00abc543;;;S-1-5-21-11111111-22222222-33333333-543)(A;;0x00abc544;;;S-1-5-21-11111111-22222222-33333333-544)(A;;0x00abc545;;;S-1-5-21-11111111-22222222-33333333-545)(A;;0x00abc546;;;S-1-5-21-11111111-22222222-33333333-546)(A;;0x00abc547;;;S-1-5-21-11111111-22222222-33333333-547)(A;;0x00abc548;;;S-1-5-21-11111111-22222222-33333333-548)(A;;0x00abc549;;;S-1-5-21-11111111-22222222-33333333-549)(A;;0x00abc550;;;S-1-5-21-11111111-22222222-33333333-550)(A;;0x00abc551;;;S-1-5-21-11111111-22222222-33333333-551)(A;;0x00abc552;;;S-1-5-21-11111111-22222222-33333333-552)(A;;0x00abc553;;;S-1-5-21-11111111-22222222-33333333-553)(A;;0x00abc554;;;S-1-5-21-11111111-22222222-33333333-554)(A;;0x00abc555;;;S-1-5-21-11111111-22222222-33333333-555)(A;;0x00abc556;;;S-1-5-21-11111111-22222222-33333333-556)(A;;0x00abc557;;;S-1-5-21-11111111-22222222-33333333-557)(A;;0x00abc558;;;S-1-5-21-11111111-22222222-33333333-558)(A;;0x00abc559;;;S-1-5-21-11111111-22222222-33333333-559)(A;;0x00abc560;;;S-1-5-21-11111111-22222222-33333333-560)(A;;0x00abc561;;;S-1-5-21-11111111-22222222-33333333-561)(A;;0x00abc562;;;S-1-5-21-11111111-22222222-33333333-562)(A;;0x00abc563;;;S-1-5-21-11111111-22222222-33333333-563)(A;;0x00abc564;;;S-1-5-21-11111111-22222222-33333333-564)(A;;0x00abc565;;;S-1-5-21-11111111-22222222-33333333-565)(A;;0x00abc566;;;S-1-5-21-11111111-22222222-33333333-566)(A;;0x00abc567;;;S-1-5-21-11111111-22222222-33333333-567)(A;;0x00abc568;;;S-1-5-21-11111111-22222222-33333333-568)(A;;0x00abc569;;;S-1-5-21-11111111-22222222-33333333-569)(A;;0x00abc570;;;S-1-5-21-11111111-22222222-33333333-570)(A;;0x00abc571;;;S-1-5-21-11111111-22222222-33333333-571)(A;;0x00abc572;;;S-1-5-21-11111111-22222222-33333333-572)(A;;0x00abc573;;;S-1-5-21-11111111-22222222-33333333-573)(A;;0x00abc574;;;S-1-5-21-11111111-22222222-33333333-574)(A;;0x00abc575;;;S-1-5-21-11111111-22222222-33333333-575)(A;;0x00abc576;;;S-1-5-21-11111111-22222222-33333333-576)(A;;0x00abc577;;;S-1-5-21-11111111-22222222-33333333-577)(A;;0x00abc578;;;S-1-5-21-11111111-22222222-33333333-578)(A;;0x00abc579;;;S-1-5-21-11111111-22222222-33333333-579)(A;;0x00abc580;;;S-1-5-21-11111111-22222222-33333333-580)(A;;0x00abc581;;;S-1-5-21-11111111-22222222-33333333-581)(A;;0x00abc582;;;S-1-5-21-11111111-22222222-33333333-582)(A;;0x00abc583;;;S-1-5-21-11111111-22222222-33333333-583)(A;;0x00abc584;;;S-1-5-21-11111111-22222222-33333333-584)(A;;0x00abc585;;;S-1-5-21-11111111-22222222-33333333-585)(A;;0x00abc586;;;S-1-5-21-11111111-22222222-33333333-586)(A;;0x00abc587;;;S-1-5-21-11111111-22222222-33333333-587)(A;;0x00abc588;;;S-1-5-21-11111111-22222222-33333333-588)(A;;0x00abc589;;;S-1-5-21-11111111-22222222-33333333-589)(A;;0x00abc590;;;S-1-5-21-11111111-22222222-33333333-590)(A;;0x00abc591;;;S-1-5-21-11111111-22222222-33333333-591)(A;;0x00abc592;;;S-1-5-21-11111111-22222222-33333333-592)(A;;0x00abc593;;;S-1-5-21-11111111-22222222-33333333-593)(A;;0x00abc594;;;S-1-5-21-11111111-22222222-33333333-594)(A;;0x00abc595;;;S-1-5-21-11111111-22222222-33333333-595)(A;;0x00abc596;;;S-1-5-21-11111111-22222222-33333333-596)(A;;0x00abc597;;;S-1-5-21-11111111-22222222-33333333-597)(A;;0x00abc598;;;S-1-5-21-11111111-22222222-33333333-598)(A;;0x00abc599;;;S-1-5-21-11111111-22222222-33333333-599)(A;;0x00abc600;;;S-1-5-21-11111111-22222222-33333333-600) -> D:(A;;0x654321;;;WD)(A;;0xabc101;;;S-1-5-21-11111111-22222222-33333333-101)(A;;0xabc102;;;S-1-5-21-11111111-22222222-33333333-102)(A;;0xabc103;;;S-1-5-21-11111111-22222222-33333333-103)(A;;0xabc104;;;S-1-5-21-11111111-22222222-33333333-104)(A;;0xabc105;;;S-1-5-21-11111111-22222222-33333333-105)(A;;0xabc106;;;S-1-5-21-11111111-22222222-33333333-106)(A;;0xabc107;;;S-1-5-21-11111111-22222222-33333333-107)(A;;0xabc108;;;S-1-5-21-11111111-22222222-33333333-108)(A;;0xabc109;;;S-1-5-21-11111111-22222222-33333333-109)(A;;0xabc110;;;S-1-5-21-11111111-22222222-33333333-110)(A;;0xabc111;;;S-1-5-21-11111111-22222222-33333333-111)(A;;0xabc112;;;S-1-5-21-11111111-22222222-33333333-112)(A;;0xabc113;;;S-1-5-21-11111111-22222222-33333333-113)(A;;0xabc114;;;S-1-5-21-11111111-22222222-33333333-114)(A;;0xabc115;;;S-1-5-21-11111111-22222222-33333333-115)(A;;0xabc116;;;S-1-5-21-11111111-22222222-33333333-116)(A;;0xabc117;;;S-1-5-21-11111111-22222222-33333333-117)(A;;0xabc118;;;S-1-5-21-11111111-22222222-33333333-118)(A;;0xabc119;;;S-1-5-21-11111111-22222222-33333333-119)(A;;0xabc120;;;S-1-5-21-11111111-22222222-33333333-120)(A;;0xabc121;;;S-1-5-21-11111111-22222222-33333333-121)(A;;0xabc122;;;S-1-5-21-11111111-22222222-33333333-122)(A;;0xabc123;;;S-1-5-21-11111111-22222222-33333333-123)(A;;0xabc124;;;S-1-5-21-11111111-22222222-33333333-124)(A;;0xabc125;;;S-1-5-21-11111111-22222222-33333333-125)(A;;0xabc126;;;S-1-5-21-11111111-22222222-33333333-126)(A;;0xabc127;;;S-1-5-21-11111111-22222222-33333333-127)(A;;0xabc128;;;S-1-5-21-11111111-22222222-33333333-128)(A;;0xabc129;;;S-1-5-21-11111111-22222222-33333333-129)(A;;0xabc130;;;S-1-5-21-11111111-22222222-33333333-130)(A;;0xabc131;;;S-1-5-21-11111111-22222222-33333333-131)(A;;0xabc132;;;S-1-5-21-11111111-22222222-33333333-132)(A;;0xabc133;;;S-1-5-21-11111111-22222222-33333333-133)(A;;0xabc134;;;S-1-5-21-11111111-22222222-33333333-134)(A;;0xabc135;;;S-1-5-21-11111111-22222222-33333333-135)(A;;0xabc136;;;S-1-5-21-11111111-22222222-33333333-136)(A;;0xabc137;;;S-1-5-21-11111111-22222222-33333333-137)(A;;0xabc138;;;S-1-5-21-11111111-22222222-33333333-138)(A;;0xabc139;;;S-1-5-21-11111111-22222222-33333333-139)(A;;0xabc140;;;S-1-5-21-11111111-22222222-33333333-140)(A;;0xabc141;;;S-1-5-21-11111111-22222222-33333333-141)(A;;0xabc142;;;S-1-5-21-11111111-22222222-33333333-142)(A;;0xabc143;;;S-1-5-21-11111111-22222222-33333333-143)(A;;0xabc144;;;S-1-5-21-11111111-22222222-33333333-144)(A;;0xabc145;;;S-1-5-21-11111111-22222222-33333333-145)(A;;0xabc146;;;S-1-5-21-11111111-22222222-33333333-146)(A;;0xabc147;;;S-1-5-21-11111111-22222222-33333333-147)(A;;0xabc148;;;S-1-5-21-11111111-22222222-33333333-148)(A;;0xabc149;;;S-1-5-21-11111111-22222222-33333333-149)(A;;0xabc150;;;S-1-5-21-11111111-22222222-33333333-150)(A;;0xabc151;;;S-1-5-21-11111111-22222222-33333333-151)(A;;0xabc152;;;S-1-5-21-11111111-22222222-33333333-152)(A;;0xabc153;;;S-1-5-21-11111111-22222222-33333333-153)(A;;0xabc154;;;S-1-5-21-11111111-22222222-33333333-154)(A;;0xabc155;;;S-1-5-21-11111111-22222222-33333333-155)(A;;0xabc156;;;S-1-5-21-11111111-22222222-33333333-156)(A;;0xabc157;;;S-1-5-21-11111111-22222222-33333333-157)(A;;0xabc158;;;S-1-5-21-11111111-22222222-33333333-158)(A;;0xabc159;;;S-1-5-21-11111111-22222222-33333333-159)(A;;0xabc160;;;S-1-5-21-11111111-22222222-33333333-160)(A;;0xabc161;;;S-1-5-21-11111111-22222222-33333333-161)(A;;0xabc162;;;S-1-5-21-11111111-22222222-33333333-162)(A;;0xabc163;;;S-1-5-21-11111111-22222222-33333333-163)(A;;0xabc164;;;S-1-5-21-11111111-22222222-33333333-164)(A;;0xabc165;;;S-1-5-21-11111111-22222222-33333333-165)(A;;0xabc166;;;S-1-5-21-11111111-22222222-33333333-166)(A;;0xabc167;;;S-1-5-21-11111111-22222222-33333333-167)(A;;0xabc168;;;S-1-5-21-11111111-22222222-33333333-168)(A;;0xabc169;;;S-1-5-21-11111111-22222222-33333333-169)(A;;0xabc170;;;S-1-5-21-11111111-22222222-33333333-170)(A;;0xabc171;;;S-1-5-21-11111111-22222222-33333333-171)(A;;0xabc172;;;S-1-5-21-11111111-22222222-33333333-172)(A;;0xabc173;;;S-1-5-21-11111111-22222222-33333333-173)(A;;0xabc174;;;S-1-5-21-11111111-22222222-33333333-174)(A;;0xabc175;;;S-1-5-21-11111111-22222222-33333333-175)(A;;0xabc176;;;S-1-5-21-11111111-22222222-33333333-176)(A;;0xabc177;;;S-1-5-21-11111111-22222222-33333333-177)(A;;0xabc178;;;S-1-5-21-11111111-22222222-33333333-178)(A;;0xabc179;;;S-1-5-21-11111111-22222222-33333333-179)(A;;0xabc180;;;S-1-5-21-11111111-22222222-33333333-180)(A;;0xabc181;;;S-1-5-21-11111111-22222222-33333333-181)(A;;0xabc182;;;S-1-5-21-11111111-22222222-33333333-182)(A;;0xabc183;;;S-1-5-21-11111111-22222222-33333333-183)(A;;0xabc184;;;S-1-5-21-11111111-22222222-33333333-184)(A;;0xabc185;;;S-1-5-21-11111111-22222222-33333333-185)(A;;0xabc186;;;S-1-5-21-11111111-22222222-33333333-186)(A;;0xabc187;;;S-1-5-21-11111111-22222222-33333333-187)(A;;0xabc188;;;S-1-5-21-11111111-22222222-33333333-188)(A;;0xabc189;;;S-1-5-21-11111111-22222222-33333333-189)(A;;0xabc190;;;S-1-5-21-11111111-22222222-33333333-190)(A;;0xabc191;;;S-1-5-21-11111111-22222222-33333333-191)(A;;0xabc192;;;S-1-5-21-11111111-22222222-33333333-192)(A;;0xabc193;;;S-1-5-21-11111111-22222222-33333333-193)(A;;0xabc194;;;S-1-5-21-11111111-22222222-33333333-194)(A;;0xabc195;;;S-1-5-21-11111111-22222222-33333333-195)(A;;0xabc196;;;S-1-5-21-11111111-22222222-33333333-196)(A;;0xabc197;;;S-1-5-21-11111111-22222222-33333333-197)(A;;0xabc198;;;S-1-5-21-11111111-22222222-33333333-198)(A;;0xabc199;;;S-1-5-21-11111111-22222222-33333333-199)(A;;0xabc200;;;S-1-5-21-11111111-22222222-33333333-200)(A;;0xabc201;;;S-1-5-21-11111111-22222222-33333333-201)(A;;0xabc202;;;S-1-5-21-11111111-22222222-33333333-202)(A;;0xabc203;;;S-1-5-21-11111111-22222222-33333333-203)(A;;0xabc204;;;S-1-5-21-11111111-22222222-33333333-204)(A;;0xabc205;;;S-1-5-21-11111111-22222222-33333333-205)(A;;0xabc206;;;S-1-5-21-11111111-22222222-33333333-206)(A;;0xabc207;;;S-1-5-21-11111111-22222222-33333333-207)(A;;0xabc208;;;S-1-5-21-11111111-22222222-33333333-208)(A;;0xabc209;;;S-1-5-21-11111111-22222222-33333333-209)(A;;0xabc210;;;S-1-5-21-11111111-22222222-33333333-210)(A;;0xabc211;;;S-1-5-21-11111111-22222222-33333333-211)(A;;0xabc212;;;S-1-5-21-11111111-22222222-33333333-212)(A;;0xabc213;;;S-1-5-21-11111111-22222222-33333333-213)(A;;0xabc214;;;S-1-5-21-11111111-22222222-33333333-214)(A;;0xabc215;;;S-1-5-21-11111111-22222222-33333333-215)(A;;0xabc216;;;S-1-5-21-11111111-22222222-33333333-216)(A;;0xabc217;;;S-1-5-21-11111111-22222222-33333333-217)(A;;0xabc218;;;S-1-5-21-11111111-22222222-33333333-218)(A;;0xabc219;;;S-1-5-21-11111111-22222222-33333333-219)(A;;0xabc220;;;S-1-5-21-11111111-22222222-33333333-220)(A;;0xabc221;;;S-1-5-21-11111111-22222222-33333333-221)(A;;0xabc222;;;S-1-5-21-11111111-22222222-33333333-222)(A;;0xabc223;;;S-1-5-21-11111111-22222222-33333333-223)(A;;0xabc224;;;S-1-5-21-11111111-22222222-33333333-224)(A;;0xabc225;;;S-1-5-21-11111111-22222222-33333333-225)(A;;0xabc226;;;S-1-5-21-11111111-22222222-33333333-226)(A;;0xabc227;;;S-1-5-21-11111111-22222222-33333333-227)(A;;0xabc228;;;S-1-5-21-11111111-22222222-33333333-228)(A;;0xabc229;;;S-1-5-21-11111111-22222222-33333333-229)(A;;0xabc230;;;S-1-5-21-11111111-22222222-33333333-230)(A;;0xabc231;;;S-1-5-21-11111111-22222222-33333333-231)(A;;0xabc232;;;S-1-5-21-11111111-22222222-33333333-232)(A;;0xabc233;;;S-1-5-21-11111111-22222222-33333333-233)(A;;0xabc234;;;S-1-5-21-11111111-22222222-33333333-234)(A;;0xabc235;;;S-1-5-21-11111111-22222222-33333333-235)(A;;0xabc236;;;S-1-5-21-11111111-22222222-33333333-236)(A;;0xabc237;;;S-1-5-21-11111111-22222222-33333333-237)(A;;0xabc238;;;S-1-5-21-11111111-22222222-33333333-238)(A;;0xabc239;;;S-1-5-21-11111111-22222222-33333333-239)(A;;0xabc240;;;S-1-5-21-11111111-22222222-33333333-240)(A;;0xabc241;;;S-1-5-21-11111111-22222222-33333333-241)(A;;0xabc242;;;S-1-5-21-11111111-22222222-33333333-242)(A;;0xabc243;;;S-1-5-21-11111111-22222222-33333333-243)(A;;0xabc244;;;S-1-5-21-11111111-22222222-33333333-244)(A;;0xabc245;;;S-1-5-21-11111111-22222222-33333333-245)(A;;0xabc246;;;S-1-5-21-11111111-22222222-33333333-246)(A;;0xabc247;;;S-1-5-21-11111111-22222222-33333333-247)(A;;0xabc248;;;S-1-5-21-11111111-22222222-33333333-248)(A;;0xabc249;;;S-1-5-21-11111111-22222222-33333333-249)(A;;0xabc250;;;S-1-5-21-11111111-22222222-33333333-250)(A;;0xabc251;;;S-1-5-21-11111111-22222222-33333333-251)(A;;0xabc252;;;S-1-5-21-11111111-22222222-33333333-252)(A;;0xabc253;;;S-1-5-21-11111111-22222222-33333333-253)(A;;0xabc254;;;S-1-5-21-11111111-22222222-33333333-254)(A;;0xabc255;;;S-1-5-21-11111111-22222222-33333333-255)(A;;0xabc256;;;S-1-5-21-11111111-22222222-33333333-256)(A;;0xabc257;;;S-1-5-21-11111111-22222222-33333333-257)(A;;0xabc258;;;S-1-5-21-11111111-22222222-33333333-258)(A;;0xabc259;;;S-1-5-21-11111111-22222222-33333333-259)(A;;0xabc260;;;S-1-5-21-11111111-22222222-33333333-260)(A;;0xabc261;;;S-1-5-21-11111111-22222222-33333333-261)(A;;0xabc262;;;S-1-5-21-11111111-22222222-33333333-262)(A;;0xabc263;;;S-1-5-21-11111111-22222222-33333333-263)(A;;0xabc264;;;S-1-5-21-11111111-22222222-33333333-264)(A;;0xabc265;;;S-1-5-21-11111111-22222222-33333333-265)(A;;0xabc266;;;S-1-5-21-11111111-22222222-33333333-266)(A;;0xabc267;;;S-1-5-21-11111111-22222222-33333333-267)(A;;0xabc268;;;S-1-5-21-11111111-22222222-33333333-268)(A;;0xabc269;;;S-1-5-21-11111111-22222222-33333333-269)(A;;0xabc270;;;S-1-5-21-11111111-22222222-33333333-270)(A;;0xabc271;;;S-1-5-21-11111111-22222222-33333333-271)(A;;0xabc272;;;S-1-5-21-11111111-22222222-33333333-272)(A;;0xabc273;;;S-1-5-21-11111111-22222222-33333333-273)(A;;0xabc274;;;S-1-5-21-11111111-22222222-33333333-274)(A;;0xabc275;;;S-1-5-21-11111111-22222222-33333333-275)(A;;0xabc276;;;S-1-5-21-11111111-22222222-33333333-276)(A;;0xabc277;;;S-1-5-21-11111111-22222222-33333333-277)(A;;0xabc278;;;S-1-5-21-11111111-22222222-33333333-278)(A;;0xabc279;;;S-1-5-21-11111111-22222222-33333333-279)(A;;0xabc280;;;S-1-5-21-11111111-22222222-33333333-280)(A;;0xabc281;;;S-1-5-21-11111111-22222222-33333333-281)(A;;0xabc282;;;S-1-5-21-11111111-22222222-33333333-282)(A;;0xabc283;;;S-1-5-21-11111111-22222222-33333333-283)(A;;0xabc284;;;S-1-5-21-11111111-22222222-33333333-284)(A;;0xabc285;;;S-1-5-21-11111111-22222222-33333333-285)(A;;0xabc286;;;S-1-5-21-11111111-22222222-33333333-286)(A;;0xabc287;;;S-1-5-21-11111111-22222222-33333333-287)(A;;0xabc288;;;S-1-5-21-11111111-22222222-33333333-288)(A;;0xabc289;;;S-1-5-21-11111111-22222222-33333333-289)(A;;0xabc290;;;S-1-5-21-11111111-22222222-33333333-290)(A;;0xabc291;;;S-1-5-21-11111111-22222222-33333333-291)(A;;0xabc292;;;S-1-5-21-11111111-22222222-33333333-292)(A;;0xabc293;;;S-1-5-21-11111111-22222222-33333333-293)(A;;0xabc294;;;S-1-5-21-11111111-22222222-33333333-294)(A;;0xabc295;;;S-1-5-21-11111111-22222222-33333333-295)(A;;0xabc296;;;S-1-5-21-11111111-22222222-33333333-296)(A;;0xabc297;;;S-1-5-21-11111111-22222222-33333333-297)(A;;0xabc298;;;S-1-5-21-11111111-22222222-33333333-298)(A;;0xabc299;;;S-1-5-21-11111111-22222222-33333333-299)(A;;0xabc300;;;S-1-5-21-11111111-22222222-33333333-300)(A;;0xabc301;;;S-1-5-21-11111111-22222222-33333333-301)(A;;0xabc302;;;S-1-5-21-11111111-22222222-33333333-302)(A;;0xabc303;;;S-1-5-21-11111111-22222222-33333333-303)(A;;0xabc304;;;S-1-5-21-11111111-22222222-33333333-304)(A;;0xabc305;;;S-1-5-21-11111111-22222222-33333333-305)(A;;0xabc306;;;S-1-5-21-11111111-22222222-33333333-306)(A;;0xabc307;;;S-1-5-21-11111111-22222222-33333333-307)(A;;0xabc308;;;S-1-5-21-11111111-22222222-33333333-308)(A;;0xabc309;;;S-1-5-21-11111111-22222222-33333333-309)(A;;0xabc310;;;S-1-5-21-11111111-22222222-33333333-310)(A;;0xabc311;;;S-1-5-21-11111111-22222222-33333333-311)(A;;0xabc312;;;S-1-5-21-11111111-22222222-33333333-312)(A;;0xabc313;;;S-1-5-21-11111111-22222222-33333333-313)(A;;0xabc314;;;S-1-5-21-11111111-22222222-33333333-314)(A;;0xabc315;;;S-1-5-21-11111111-22222222-33333333-315)(A;;0xabc316;;;S-1-5-21-11111111-22222222-33333333-316)(A;;0xabc317;;;S-1-5-21-11111111-22222222-33333333-317)(A;;0xabc318;;;S-1-5-21-11111111-22222222-33333333-318)(A;;0xabc319;;;S-1-5-21-11111111-22222222-33333333-319)(A;;0xabc320;;;S-1-5-21-11111111-22222222-33333333-320)(A;;0xabc321;;;S-1-5-21-11111111-22222222-33333333-321)(A;;0xabc322;;;S-1-5-21-11111111-22222222-33333333-322)(A;;0xabc323;;;S-1-5-21-11111111-22222222-33333333-323)(A;;0xabc324;;;S-1-5-21-11111111-22222222-33333333-324)(A;;0xabc325;;;S-1-5-21-11111111-22222222-33333333-325)(A;;0xabc326;;;S-1-5-21-11111111-22222222-33333333-326)(A;;0xabc327;;;S-1-5-21-11111111-22222222-33333333-327)(A;;0xabc328;;;S-1-5-21-11111111-22222222-33333333-328)(A;;0xabc329;;;S-1-5-21-11111111-22222222-33333333-329)(A;;0xabc330;;;S-1-5-21-11111111-22222222-33333333-330)(A;;0xabc331;;;S-1-5-21-11111111-22222222-33333333-331)(A;;0xabc332;;;S-1-5-21-11111111-22222222-33333333-332)(A;;0xabc333;;;S-1-5-21-11111111-22222222-33333333-333)(A;;0xabc334;;;S-1-5-21-11111111-22222222-33333333-334)(A;;0xabc335;;;S-1-5-21-11111111-22222222-33333333-335)(A;;0xabc336;;;S-1-5-21-11111111-22222222-33333333-336)(A;;0xabc337;;;S-1-5-21-11111111-22222222-33333333-337)(A;;0xabc338;;;S-1-5-21-11111111-22222222-33333333-338)(A;;0xabc339;;;S-1-5-21-11111111-22222222-33333333-339)(A;;0xabc340;;;S-1-5-21-11111111-22222222-33333333-340)(A;;0xabc341;;;S-1-5-21-11111111-22222222-33333333-341)(A;;0xabc342;;;S-1-5-21-11111111-22222222-33333333-342)(A;;0xabc343;;;S-1-5-21-11111111-22222222-33333333-343)(A;;0xabc344;;;S-1-5-21-11111111-22222222-33333333-344)(A;;0xabc345;;;S-1-5-21-11111111-22222222-33333333-345)(A;;0xabc346;;;S-1-5-21-11111111-22222222-33333333-346)(A;;0xabc347;;;S-1-5-21-11111111-22222222-33333333-347)(A;;0xabc348;;;S-1-5-21-11111111-22222222-33333333-348)(A;;0xabc349;;;S-1-5-21-11111111-22222222-33333333-349)(A;;0xabc350;;;S-1-5-21-11111111-22222222-33333333-350)(A;;0xabc351;;;S-1-5-21-11111111-22222222-33333333-351)(A;;0xabc352;;;S-1-5-21-11111111-22222222-33333333-352)(A;;0xabc353;;;S-1-5-21-11111111-22222222-33333333-353)(A;;0xabc354;;;S-1-5-21-11111111-22222222-33333333-354)(A;;0xabc355;;;S-1-5-21-11111111-22222222-33333333-355)(A;;0xabc356;;;S-1-5-21-11111111-22222222-33333333-356)(A;;0xabc357;;;S-1-5-21-11111111-22222222-33333333-357)(A;;0xabc358;;;S-1-5-21-11111111-22222222-33333333-358)(A;;0xabc359;;;S-1-5-21-11111111-22222222-33333333-359)(A;;0xabc360;;;S-1-5-21-11111111-22222222-33333333-360)(A;;0xabc361;;;S-1-5-21-11111111-22222222-33333333-361)(A;;0xabc362;;;S-1-5-21-11111111-22222222-33333333-362)(A;;0xabc363;;;S-1-5-21-11111111-22222222-33333333-363)(A;;0xabc364;;;S-1-5-21-11111111-22222222-33333333-364)(A;;0xabc365;;;S-1-5-21-11111111-22222222-33333333-365)(A;;0xabc366;;;S-1-5-21-11111111-22222222-33333333-366)(A;;0xabc367;;;S-1-5-21-11111111-22222222-33333333-367)(A;;0xabc368;;;S-1-5-21-11111111-22222222-33333333-368)(A;;0xabc369;;;S-1-5-21-11111111-22222222-33333333-369)(A;;0xabc370;;;S-1-5-21-11111111-22222222-33333333-370)(A;;0xabc371;;;S-1-5-21-11111111-22222222-33333333-371)(A;;0xabc372;;;S-1-5-21-11111111-22222222-33333333-372)(A;;0xabc373;;;S-1-5-21-11111111-22222222-33333333-373)(A;;0xabc374;;;S-1-5-21-11111111-22222222-33333333-374)(A;;0xabc375;;;S-1-5-21-11111111-22222222-33333333-375)(A;;0xabc376;;;S-1-5-21-11111111-22222222-33333333-376)(A;;0xabc377;;;S-1-5-21-11111111-22222222-33333333-377)(A;;0xabc378;;;S-1-5-21-11111111-22222222-33333333-378)(A;;0xabc379;;;S-1-5-21-11111111-22222222-33333333-379)(A;;0xabc380;;;S-1-5-21-11111111-22222222-33333333-380)(A;;0xabc381;;;S-1-5-21-11111111-22222222-33333333-381)(A;;0xabc382;;;S-1-5-21-11111111-22222222-33333333-382)(A;;0xabc383;;;S-1-5-21-11111111-22222222-33333333-383)(A;;0xabc384;;;S-1-5-21-11111111-22222222-33333333-384)(A;;0xabc385;;;S-1-5-21-11111111-22222222-33333333-385)(A;;0xabc386;;;S-1-5-21-11111111-22222222-33333333-386)(A;;0xabc387;;;S-1-5-21-11111111-22222222-33333333-387)(A;;0xabc388;;;S-1-5-21-11111111-22222222-33333333-388)(A;;0xabc389;;;S-1-5-21-11111111-22222222-33333333-389)(A;;0xabc390;;;S-1-5-21-11111111-22222222-33333333-390)(A;;0xabc391;;;S-1-5-21-11111111-22222222-33333333-391)(A;;0xabc392;;;S-1-5-21-11111111-22222222-33333333-392)(A;;0xabc393;;;S-1-5-21-11111111-22222222-33333333-393)(A;;0xabc394;;;S-1-5-21-11111111-22222222-33333333-394)(A;;0xabc395;;;S-1-5-21-11111111-22222222-33333333-395)(A;;0xabc396;;;S-1-5-21-11111111-22222222-33333333-396)(A;;0xabc397;;;S-1-5-21-11111111-22222222-33333333-397)(A;;0xabc398;;;S-1-5-21-11111111-22222222-33333333-398)(A;;0xabc399;;;S-1-5-21-11111111-22222222-33333333-399)(A;;0xabc400;;;S-1-5-21-11111111-22222222-33333333-400)(A;;0xabc401;;;S-1-5-21-11111111-22222222-33333333-401)(A;;0xabc402;;;S-1-5-21-11111111-22222222-33333333-402)(A;;0xabc403;;;S-1-5-21-11111111-22222222-33333333-403)(A;;0xabc404;;;S-1-5-21-11111111-22222222-33333333-404)(A;;0xabc405;;;S-1-5-21-11111111-22222222-33333333-405)(A;;0xabc406;;;S-1-5-21-11111111-22222222-33333333-406)(A;;0xabc407;;;S-1-5-21-11111111-22222222-33333333-407)(A;;0xabc408;;;S-1-5-21-11111111-22222222-33333333-408)(A;;0xabc409;;;S-1-5-21-11111111-22222222-33333333-409)(A;;0xabc410;;;S-1-5-21-11111111-22222222-33333333-410)(A;;0xabc411;;;S-1-5-21-11111111-22222222-33333333-411)(A;;0xabc412;;;S-1-5-21-11111111-22222222-33333333-412)(A;;0xabc413;;;S-1-5-21-11111111-22222222-33333333-413)(A;;0xabc414;;;S-1-5-21-11111111-22222222-33333333-414)(A;;0xabc415;;;S-1-5-21-11111111-22222222-33333333-415)(A;;0xabc416;;;S-1-5-21-11111111-22222222-33333333-416)(A;;0xabc417;;;S-1-5-21-11111111-22222222-33333333-417)(A;;0xabc418;;;S-1-5-21-11111111-22222222-33333333-418)(A;;0xabc419;;;S-1-5-21-11111111-22222222-33333333-419)(A;;0xabc420;;;S-1-5-21-11111111-22222222-33333333-420)(A;;0xabc421;;;S-1-5-21-11111111-22222222-33333333-421)(A;;0xabc422;;;S-1-5-21-11111111-22222222-33333333-422)(A;;0xabc423;;;S-1-5-21-11111111-22222222-33333333-423)(A;;0xabc424;;;S-1-5-21-11111111-22222222-33333333-424)(A;;0xabc425;;;S-1-5-21-11111111-22222222-33333333-425)(A;;0xabc426;;;S-1-5-21-11111111-22222222-33333333-426)(A;;0xabc427;;;S-1-5-21-11111111-22222222-33333333-427)(A;;0xabc428;;;S-1-5-21-11111111-22222222-33333333-428)(A;;0xabc429;;;S-1-5-21-11111111-22222222-33333333-429)(A;;0xabc430;;;S-1-5-21-11111111-22222222-33333333-430)(A;;0xabc431;;;S-1-5-21-11111111-22222222-33333333-431)(A;;0xabc432;;;S-1-5-21-11111111-22222222-33333333-432)(A;;0xabc433;;;S-1-5-21-11111111-22222222-33333333-433)(A;;0xabc434;;;S-1-5-21-11111111-22222222-33333333-434)(A;;0xabc435;;;S-1-5-21-11111111-22222222-33333333-435)(A;;0xabc436;;;S-1-5-21-11111111-22222222-33333333-436)(A;;0xabc437;;;S-1-5-21-11111111-22222222-33333333-437)(A;;0xabc438;;;S-1-5-21-11111111-22222222-33333333-438)(A;;0xabc439;;;S-1-5-21-11111111-22222222-33333333-439)(A;;0xabc440;;;S-1-5-21-11111111-22222222-33333333-440)(A;;0xabc441;;;S-1-5-21-11111111-22222222-33333333-441)(A;;0xabc442;;;S-1-5-21-11111111-22222222-33333333-442)(A;;0xabc443;;;S-1-5-21-11111111-22222222-33333333-443)(A;;0xabc444;;;S-1-5-21-11111111-22222222-33333333-444)(A;;0xabc445;;;S-1-5-21-11111111-22222222-33333333-445)(A;;0xabc446;;;S-1-5-21-11111111-22222222-33333333-446)(A;;0xabc447;;;S-1-5-21-11111111-22222222-33333333-447)(A;;0xabc448;;;S-1-5-21-11111111-22222222-33333333-448)(A;;0xabc449;;;S-1-5-21-11111111-22222222-33333333-449)(A;;0xabc450;;;S-1-5-21-11111111-22222222-33333333-450)(A;;0xabc451;;;S-1-5-21-11111111-22222222-33333333-451)(A;;0xabc452;;;S-1-5-21-11111111-22222222-33333333-452)(A;;0xabc453;;;S-1-5-21-11111111-22222222-33333333-453)(A;;0xabc454;;;S-1-5-21-11111111-22222222-33333333-454)(A;;0xabc455;;;S-1-5-21-11111111-22222222-33333333-455)(A;;0xabc456;;;S-1-5-21-11111111-22222222-33333333-456)(A;;0xabc457;;;S-1-5-21-11111111-22222222-33333333-457)(A;;0xabc458;;;S-1-5-21-11111111-22222222-33333333-458)(A;;0xabc459;;;S-1-5-21-11111111-22222222-33333333-459)(A;;0xabc460;;;S-1-5-21-11111111-22222222-33333333-460)(A;;0xabc461;;;S-1-5-21-11111111-22222222-33333333-461)(A;;0xabc462;;;S-1-5-21-11111111-22222222-33333333-462)(A;;0xabc463;;;S-1-5-21-11111111-22222222-33333333-463)(A;;0xabc464;;;S-1-5-21-11111111-22222222-33333333-464)(A;;0xabc465;;;S-1-5-21-11111111-22222222-33333333-465)(A;;0xabc466;;;S-1-5-21-11111111-22222222-33333333-466)(A;;0xabc467;;;S-1-5-21-11111111-22222222-33333333-467)(A;;0xabc468;;;S-1-5-21-11111111-22222222-33333333-468)(A;;0xabc469;;;S-1-5-21-11111111-22222222-33333333-469)(A;;0xabc470;;;S-1-5-21-11111111-22222222-33333333-470)(A;;0xabc471;;;S-1-5-21-11111111-22222222-33333333-471)(A;;0xabc472;;;S-1-5-21-11111111-22222222-33333333-472)(A;;0xabc473;;;S-1-5-21-11111111-22222222-33333333-473)(A;;0xabc474;;;S-1-5-21-11111111-22222222-33333333-474)(A;;0xabc475;;;S-1-5-21-11111111-22222222-33333333-475)(A;;0xabc476;;;S-1-5-21-11111111-22222222-33333333-476)(A;;0xabc477;;;S-1-5-21-11111111-22222222-33333333-477)(A;;0xabc478;;;S-1-5-21-11111111-22222222-33333333-478)(A;;0xabc479;;;S-1-5-21-11111111-22222222-33333333-479)(A;;0xabc480;;;S-1-5-21-11111111-22222222-33333333-480)(A;;0xabc481;;;S-1-5-21-11111111-22222222-33333333-481)(A;;0xabc482;;;S-1-5-21-11111111-22222222-33333333-482)(A;;0xabc483;;;S-1-5-21-11111111-22222222-33333333-483)(A;;0xabc484;;;S-1-5-21-11111111-22222222-33333333-484)(A;;0xabc485;;;S-1-5-21-11111111-22222222-33333333-485)(A;;0xabc486;;;S-1-5-21-11111111-22222222-33333333-486)(A;;0xabc487;;;S-1-5-21-11111111-22222222-33333333-487)(A;;0xabc488;;;S-1-5-21-11111111-22222222-33333333-488)(A;;0xabc489;;;S-1-5-21-11111111-22222222-33333333-489)(A;;0xabc490;;;S-1-5-21-11111111-22222222-33333333-490)(A;;0xabc491;;;S-1-5-21-11111111-22222222-33333333-491)(A;;0xabc492;;;S-1-5-21-11111111-22222222-33333333-492)(A;;0xabc493;;;S-1-5-21-11111111-22222222-33333333-493)(A;;0xabc494;;;S-1-5-21-11111111-22222222-33333333-494)(A;;0xabc495;;;S-1-5-21-11111111-22222222-33333333-495)(A;;0xabc496;;;S-1-5-21-11111111-22222222-33333333-496)(A;;0xabc497;;;S-1-5-21-11111111-22222222-33333333-497)(A;;0xabc498;;;S-1-5-21-11111111-22222222-33333333-498)(A;;0xabc499;;;S-1-5-21-11111111-22222222-33333333-499)(A;;0xabc500;;;S-1-5-21-11111111-22222222-33333333-500)(A;;0xabc501;;;S-1-5-21-11111111-22222222-33333333-501)(A;;0xabc502;;;S-1-5-21-11111111-22222222-33333333-502)(A;;0xabc503;;;S-1-5-21-11111111-22222222-33333333-503)(A;;0xabc504;;;S-1-5-21-11111111-22222222-33333333-504)(A;;0xabc505;;;S-1-5-21-11111111-22222222-33333333-505)(A;;0xabc506;;;S-1-5-21-11111111-22222222-33333333-506)(A;;0xabc507;;;S-1-5-21-11111111-22222222-33333333-507)(A;;0xabc508;;;S-1-5-21-11111111-22222222-33333333-508)(A;;0xabc509;;;S-1-5-21-11111111-22222222-33333333-509)(A;;0xabc510;;;S-1-5-21-11111111-22222222-33333333-510)(A;;0xabc511;;;S-1-5-21-11111111-22222222-33333333-511)(A;;0xabc512;;;S-1-5-21-11111111-22222222-33333333-512)(A;;0xabc513;;;S-1-5-21-11111111-22222222-33333333-513)(A;;0xabc514;;;S-1-5-21-11111111-22222222-33333333-514)(A;;0xabc515;;;S-1-5-21-11111111-22222222-33333333-515)(A;;0xabc516;;;S-1-5-21-11111111-22222222-33333333-516)(A;;0xabc517;;;S-1-5-21-11111111-22222222-33333333-517)(A;;0xabc518;;;S-1-5-21-11111111-22222222-33333333-518)(A;;0xabc519;;;S-1-5-21-11111111-22222222-33333333-519)(A;;0xabc520;;;S-1-5-21-11111111-22222222-33333333-520)(A;;0xabc521;;;S-1-5-21-11111111-22222222-33333333-521)(A;;0xabc522;;;S-1-5-21-11111111-22222222-33333333-522)(A;;0xabc523;;;S-1-5-21-11111111-22222222-33333333-523)(A;;0xabc524;;;S-1-5-21-11111111-22222222-33333333-524)(A;;0xabc525;;;S-1-5-21-11111111-22222222-33333333-525)(A;;0xabc526;;;S-1-5-21-11111111-22222222-33333333-526)(A;;0xabc527;;;S-1-5-21-11111111-22222222-33333333-527)(A;;0xabc528;;;S-1-5-21-11111111-22222222-33333333-528)(A;;0xabc529;;;S-1-5-21-11111111-22222222-33333333-529)(A;;0xabc530;;;S-1-5-21-11111111-22222222-33333333-530)(A;;0xabc531;;;S-1-5-21-11111111-22222222-33333333-531)(A;;0xabc532;;;S-1-5-21-11111111-22222222-33333333-532)(A;;0xabc533;;;S-1-5-21-11111111-22222222-33333333-533)(A;;0xabc534;;;S-1-5-21-11111111-22222222-33333333-534)(A;;0xabc535;;;S-1-5-21-11111111-22222222-33333333-535)(A;;0xabc536;;;S-1-5-21-11111111-22222222-33333333-536)(A;;0xabc537;;;S-1-5-21-11111111-22222222-33333333-537)(A;;0xabc538;;;S-1-5-21-11111111-22222222-33333333-538)(A;;0xabc539;;;S-1-5-21-11111111-22222222-33333333-539)(A;;0xabc540;;;S-1-5-21-11111111-22222222-33333333-540)(A;;0xabc541;;;S-1-5-21-11111111-22222222-33333333-541)(A;;0xabc542;;;S-1-5-21-11111111-22222222-33333333-542)(A;;0xabc543;;;S-1-5-21-11111111-22222222-33333333-543)(A;;0xabc544;;;S-1-5-21-11111111-22222222-33333333-544)(A;;0xabc545;;;S-1-5-21-11111111-22222222-33333333-545)(A;;0xabc546;;;S-1-5-21-11111111-22222222-33333333-546)(A;;0xabc547;;;S-1-5-21-11111111-22222222-33333333-547)(A;;0xabc548;;;S-1-5-21-11111111-22222222-33333333-548)(A;;0xabc549;;;S-1-5-21-11111111-22222222-33333333-549)(A;;0xabc550;;;S-1-5-21-11111111-22222222-33333333-550)(A;;0xabc551;;;S-1-5-21-11111111-22222222-33333333-551)(A;;0xabc552;;;S-1-5-21-11111111-22222222-33333333-552)(A;;0xabc553;;;S-1-5-21-11111111-22222222-33333333-553)(A;;0xabc554;;;S-1-5-21-11111111-22222222-33333333-554)(A;;0xabc555;;;S-1-5-21-11111111-22222222-33333333-555)(A;;0xabc556;;;S-1-5-21-11111111-22222222-33333333-556)(A;;0xabc557;;;S-1-5-21-11111111-22222222-33333333-557)(A;;0xabc558;;;S-1-5-21-11111111-22222222-33333333-558)(A;;0xabc559;;;S-1-5-21-11111111-22222222-33333333-559)(A;;0xabc560;;;S-1-5-21-11111111-22222222-33333333-560)(A;;0xabc561;;;S-1-5-21-11111111-22222222-33333333-561)(A;;0xabc562;;;S-1-5-21-11111111-22222222-33333333-562)(A;;0xabc563;;;S-1-5-21-11111111-22222222-33333333-563)(A;;0xabc564;;;S-1-5-21-11111111-22222222-33333333-564)(A;;0xabc565;;;S-1-5-21-11111111-22222222-33333333-565)(A;;0xabc566;;;S-1-5-21-11111111-22222222-33333333-566)(A;;0xabc567;;;S-1-5-21-11111111-22222222-33333333-567)(A;;0xabc568;;;S-1-5-21-11111111-22222222-33333333-568)(A;;0xabc569;;;S-1-5-21-11111111-22222222-33333333-569)(A;;0xabc570;;;S-1-5-21-11111111-22222222-33333333-570)(A;;0xabc571;;;S-1-5-21-11111111-22222222-33333333-571)(A;;0xabc572;;;S-1-5-21-11111111-22222222-33333333-572)(A;;0xabc573;;;S-1-5-21-11111111-22222222-33333333-573)(A;;0xabc574;;;S-1-5-21-11111111-22222222-33333333-574)(A;;0xabc575;;;S-1-5-21-11111111-22222222-33333333-575)(A;;0xabc576;;;S-1-5-21-11111111-22222222-33333333-576)(A;;0xabc577;;;S-1-5-21-11111111-22222222-33333333-577)(A;;0xabc578;;;S-1-5-21-11111111-22222222-33333333-578)(A;;0xabc579;;;S-1-5-21-11111111-22222222-33333333-579)(A;;0xabc580;;;S-1-5-21-11111111-22222222-33333333-580)(A;;0xabc581;;;S-1-5-21-11111111-22222222-33333333-581)(A;;0xabc582;;;S-1-5-21-11111111-22222222-33333333-582)(A;;0xabc583;;;S-1-5-21-11111111-22222222-33333333-583)(A;;0xabc584;;;S-1-5-21-11111111-22222222-33333333-584)(A;;0xabc585;;;S-1-5-21-11111111-22222222-33333333-585)(A;;0xabc586;;;S-1-5-21-11111111-22222222-33333333-586)(A;;0xabc587;;;S-1-5-21-11111111-22222222-33333333-587)(A;;0xabc588;;;S-1-5-21-11111111-22222222-33333333-588)(A;;0xabc589;;;S-1-5-21-11111111-22222222-33333333-589)(A;;0xabc590;;;S-1-5-21-11111111-22222222-33333333-590)(A;;0xabc591;;;S-1-5-21-11111111-22222222-33333333-591)(A;;0xabc592;;;S-1-5-21-11111111-22222222-33333333-592)(A;;0xabc593;;;S-1-5-21-11111111-22222222-33333333-593)(A;;0xabc594;;;S-1-5-21-11111111-22222222-33333333-594)(A;;0xabc595;;;S-1-5-21-11111111-22222222-33333333-595)(A;;0xabc596;;;S-1-5-21-11111111-22222222-33333333-596)(A;;0xabc597;;;S-1-5-21-11111111-22222222-33333333-597)(A;;0xabc598;;;S-1-5-21-11111111-22222222-33333333-598)(A;;0xabc599;;;S-1-5-21-11111111-22222222-33333333-599)(A;;0xabc600;;;S-1-5-21-11111111-22222222-33333333-600)
+D:AI(A;CI;RP LCLORC;;;AU) -> D:AI(A;CI;LCRPLORC;;;AU)
+D:AI(A;CI;RP LCLO RC;;;AU) -> D:AI(A;CI;LCRPLORC;;;AU)
+D:(A;; GA;;;LG) -> D:(A;;GA;;;LG)
+D:(A;; 0x75bcd15;;;LG) -> D:(A;;0x75bcd15;;;LG)
+D:(A;;0x001f01ff;;;WD)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1001)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1002)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1003)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1004)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1005)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1006)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1007)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1008)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1009)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1010)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1011)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1012)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1013)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1014)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1015)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1016)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1017)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1018)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1019)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1020)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1021)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1022)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1023)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1024)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1025)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1026)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1027)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1028)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1029)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1030)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1031)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1032)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1033)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1034)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1035)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1036)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1037)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1038)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1039)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1040)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1041)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1042)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1043)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1044)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1045)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1046)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1047)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1048)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1049)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1050)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1051)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1052)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1053)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1054)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1055)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1056)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1057)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1058)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1059)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1060)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1061)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1062)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1063)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1064)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1065)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1066)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1067)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1068)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1069)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1070)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1071)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1072)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1073)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1074)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1075)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1076)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1077)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1078)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1079)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1080)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1081)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1082)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1083)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1084)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1085)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1086)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1087)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1088)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1089)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1090)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1091)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1092)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1093)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1094)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1095)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1096)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1097)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1098)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1099)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1100)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1101)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1102)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1103)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1104)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1105)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1106)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1107)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1108)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1109)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1110)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1111)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1112)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1113)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1114)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1115)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1116)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1117)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1118)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1119)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1120)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1121)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1122)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1123)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1124)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1125)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1126)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1127)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1128)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1129)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1130)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1131)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1132)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1133)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1134)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1135)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1136)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1137)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1138)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1139)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1140)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1141)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1142)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1143)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1144)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1145)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1146)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1147)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1148)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1149)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1150)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1151)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1152)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1153)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1154)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1155)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1156)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1157)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1158)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1159)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1160)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1161)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1162)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1163)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1164)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1165)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1166)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1167)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1168)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1169)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1170)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1171)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1172)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1173)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1174)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1175)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1176)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1177)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1178)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1179)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1180)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1181)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1182)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1183)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1184)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1185)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1186)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1187)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1188)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1189)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1190)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1191)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1192)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1193)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1194)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1195)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1196)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1197)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1198)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1199)(A;;0x001f01ff;;;S-1-5-21-11111111-22222222-33333333-1200) -> D:(A;;FA;;;WD)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1001)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1002)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1003)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1004)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1005)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1006)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1007)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1008)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1009)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1010)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1011)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1012)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1013)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1014)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1015)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1016)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1017)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1018)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1019)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1020)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1021)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1022)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1023)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1024)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1025)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1026)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1027)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1028)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1029)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1030)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1031)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1032)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1033)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1034)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1035)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1036)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1037)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1038)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1039)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1040)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1041)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1042)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1043)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1044)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1045)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1046)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1047)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1048)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1049)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1050)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1051)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1052)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1053)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1054)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1055)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1056)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1057)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1058)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1059)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1060)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1061)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1062)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1063)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1064)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1065)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1066)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1067)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1068)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1069)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1070)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1071)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1072)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1073)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1074)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1075)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1076)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1077)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1078)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1079)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1080)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1081)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1082)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1083)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1084)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1085)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1086)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1087)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1088)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1089)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1090)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1091)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1092)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1093)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1094)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1095)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1096)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1097)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1098)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1099)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1100)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1101)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1102)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1103)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1104)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1105)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1106)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1107)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1108)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1109)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1110)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1111)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1112)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1113)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1114)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1115)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1116)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1117)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1118)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1119)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1120)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1121)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1122)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1123)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1124)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1125)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1126)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1127)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1128)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1129)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1130)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1131)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1132)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1133)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1134)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1135)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1136)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1137)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1138)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1139)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1140)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1141)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1142)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1143)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1144)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1145)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1146)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1147)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1148)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1149)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1150)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1151)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1152)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1153)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1154)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1155)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1156)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1157)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1158)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1159)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1160)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1161)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1162)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1163)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1164)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1165)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1166)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1167)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1168)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1169)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1170)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1171)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1172)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1173)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1174)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1175)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1176)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1177)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1178)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1179)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1180)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1181)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1182)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1183)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1184)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1185)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1186)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1187)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1188)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1189)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1190)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1191)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1192)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1193)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1194)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1195)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1196)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1197)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1198)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1199)(A;;FA;;;S-1-5-21-11111111-22222222-33333333-1200)
+O:S-1-5-21-2212615479-2695158682-2101375468-512G:S-1-5-21-2212615479-2695158682-2101375468-513D:P(A;OICI;0x001f01ff;;;S-1-5-21-2212615479-2695158682-2101375468-512)(A;OICI;0x001f01ff;;;S-1-5-21-2212615479-2695158682-2101375468-519)(A;OICIIO;0x001f01ff;;;CO)(A;OICI;0x001f01ff;;;S-1-5-21-2212615479-2695158682-2101375468-512)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)(A;OICI;0x001200a9;;;ED)S:AI(OU;CIIDSA;WP;f30e3bbe-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)(OU;CIIDSA;WP;f30e3bbf-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD) -> O:S-1-5-21-2212615479-2695158682-2101375468-512G:S-1-5-21-2212615479-2695158682-2101375468-513D:P(A;OICI;FA;;;S-1-5-21-2212615479-2695158682-2101375468-512)(A;OICI;FA;;;S-1-5-21-2212615479-2695158682-2101375468-519)(A;OICIIO;FA;;;CO)(A;OICI;FA;;;S-1-5-21-2212615479-2695158682-2101375468-512)(A;OICI;FA;;;SY)(A;OICI;0x1200a9;;;AU)(A;OICI;0x1200a9;;;ED)S:AI(OU;CIIDSA;WP;f30e3bbe-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)(OU;CIIDSA;WP;f30e3bbf-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)
+O:LAG:BAD:P(A;OICI;0x1f01ff;;;BA) -> O:LAG:BAD:P(A;OICI;FA;;;BA)
+O:LAG:BAD:(A;;0x1ff;;;WD) -> O:LAG:BAD:(A;;CCDCLCSWRPWPDTLOCR;;;WD)
+D:(A;;FAGX;;;SY) -> D:(A;;0x201f01ff;;;SY)
diff --git a/libcli/security/tests/windows/should_fail.txt b/libcli/security/tests/windows/should_fail.txt
new file mode 100644
index 0000000..35a813d
--- /dev/null
+++ b/libcli/security/tests/windows/should_fail.txt
@@ -0,0 +1,47 @@
+Z:(A;;GA;;;SY) -> Z:(A;;GA;;;SY)
+D:(Antlers;;GA;;;SY) -> D:(Antlers;;GA;;;SY)
+Q:(A;;GA;;;RU) -> Q:(A;;GA;;;RU)
+d:(A;;GA;;;LG) -> d:(A;;GA;;;LG)
+D:((A;;GA;;;LG)) -> D:((A;;GA;;;LG))
+D:(A;;GA;;) -> D:(A;;GA;;)
+D :S: -> D :S:
+S:(AU;SA;CROOO;;;WD)(AU;SA;CR;;;WD) -> S:(AU;SA;CROOO;;;WD)(AU;SA;CR;;;WD)
+D:(A;;GA;;;S-1-0x1313131313131-513) -> D:(A;;GA;;;S-1-0x1313131313131-513)
+D:(A;;GA;a;;S-1-5-21-2447931902-1787058256-0x3961074038-1201) -> D:(A;;GA;a;;S-1-5-21-2447931902-1787058256-0x3961074038-1201)
+D:(A;;GA;a;;S-1-5-21-2447931902-1787058256-0xec193176-1201) -> D:(A;;GA;a;;S-1-5-21-2447931902-1787058256-0xec193176-1201)
+S:(OOU;CISA;WP;f30e3bbe-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)(OU;CISA;WP;f30e3bbf-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD) -> S:(OOU;CISA;WP;f30e3bbe-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)(OU;CISA;WP;f30e3bbf-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)
+S:(OU;CISA;WP;f30e3bbe-9ff0-11d1-b603-00potato7c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)(OU;CISA;WP;f30e3bbf-9ff0-11d1-b603-00chips7c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD) -> S:(OU;CISA;WP;f30e3bbe-9ff0-11d1-b603-00potato7c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)(OU;CISA;WP;f30e3bbf-9ff0-11d1-b603-00chips7c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)
+D:P:S: -> D:P:S:
+D:(Ā;;GA;;;LG) -> D:(Ā;;GA;;;LG)
+D:(A;;123456789 ;;;LG) -> D:(A;;123456789 ;;;LG)
+D:(A;;0x75bcd15 ;;;LG) -> D:(A;;0x75bcd15 ;;;LG)
+D:(A;; 0x75bcd15;;;LG -> D:(A;; 0x75bcd15;;;LG
+D:(A;;0x 75bcd15;;;LG) -> D:(A;;0x 75bcd15;;;LG)
+D:(A;;GA ;;;LG) -> D:(A;;GA ;;;LG)
+D:(A;;RP ;;;LG) -> D:(A;;RP ;;;LG)
+D:(A;;GA;;;LG;) -> D:(A;;GA;;;LG;)
+D:(A;;GA;;;LG;;) -> D:(A;;GA;;;LG;;)
+D:(A;;GA) -> D:(A;;GA)
+D:(A;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;) -> D:(A;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;)
+D:(A;;GA;;;S-1-3-4 ) -> D:(A;;GA;;;S-1-3-4 )
+D:(A;;GA; f30e3bbf-9ff0-11d1-b603-0000f80367c1;;WD) -> D:(A;;GA; f30e3bbf-9ff0-11d1-b603-0000f80367c1;;WD)
+D:(A;;GA;f30e3bbf-9ff0-11d1-b603-0000f80367c1 ;;WD) -> D:(A;;GA;f30e3bbf-9ff0-11d1-b603-0000f80367c1 ;;WD)
+D:(A;;GA;; f30e3bbf-9ff0-11d1-b603-0000f80367c1;WD) -> D:(A;;GA;; f30e3bbf-9ff0-11d1-b603-0000f80367c1;WD)
+D:(A;;GA;;f30e3bbf-9ff0-11d1-b603-0000f80367c1 ;WD) -> D:(A;;GA;;f30e3bbf-9ff0-11d1-b603-0000f80367c1 ;WD)
+D:(A;;GA;;{f30e3bbf-9ff0-11d1-b603-0000f80367c1};WD) -> D:(A;;GA;;{f30e3bbf-9ff0-11d1-b603-0000f80367c1};WD)
+D:(A;;GA;;0123456789abcdef;WD) -> D:(A;;GA;;0123456789abcdef;WD)
+D:(A;;GA;;0123456789abcdef0123456789abcdef;WD) -> D:(A;;GA;;0123456789abcdef0123456789abcdef;WD)
+D:AI(A;CI;RP LCLOR C;;;AU) -> D:AI(A;CI;RP LCLOR C;;;AU)
+D:AI(A;CI;RP LC LORC;;;AU) -> D:AI(A;CI;RP LC LORC;;;AU)
+D:AI(A;CI;RP LC LORC;;;AU) -> D:AI(A;CI;RP LC LORC;;;AU)
+O:S -> O:S
+O:S- -> O:S-
+O:S-1 -> O:S-1
+O:S-10 -> O:S-10
+O:S-0 -> O:S-0
+O:S-1- -> O:S-1-
+O:S-0x1 -> O:S-0x1
+O:S-0x1- -> O:S-0x1-
+O: -> O:
+O:XX -> O:XX
+D:(D:()D:())D:(A;;0x75bcd15;;;LG)) -> D:(D:()D:())D:(A;;0x75bcd15;;;LG))
diff --git a/libcli/security/tests/windows/windows-sddl-tests.c b/libcli/security/tests/windows/windows-sddl-tests.c
new file mode 100644
index 0000000..3857aef
--- /dev/null
+++ b/libcli/security/tests/windows/windows-sddl-tests.c
@@ -0,0 +1,341 @@
+/*
+ * Test Windows SDDL handling.
+ *
+ * Copyright (c) 2023 Douglas Bagnall <dbagnall@samba.org>
+ *
+ * GPLv3+.
+ *
+ * This can be compiled on Windows under Cygwin, like this:
+ *
+ *
+ * gcc -o windows-sddl-tests windows-sddl-tests.c \
+ * C:/Windows/System32/advapi32.dll -ladvapi32
+ *
+ *
+ * then run like this:
+ *
+ * ./windows-sddl-tests.exe
+ *
+ *
+ * That will show you a mix of success and failure.
+ *
+ * To run the tests in python/samba/tests/sddl.py, edit the method
+ * _test_write_test_strings(), removing the leading underscore so it starts
+ * with "test_". Then running
+ *
+ * make test TESTS='sddl\\b'
+ *
+ * will write some files into /tmp, containing lines like this:
+ *
+ * D:(A;;GA;;;RU) -> D:(A;;GA;;;RU)
+ *
+ * Copy these files to Windows. Then in Cygwin, run this:
+ *
+ * ./windows-sddl-tests.exe -i non_canonical.txt canonical.txt [...]
+ *
+ * and the part of each line before the " -> " will be fed into the SDDL
+ * parser, and back through the serialiser, which should result in the string
+ * after the " -> ". These are the tests that sddl.py does.
+ */
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <windows.h>
+#include <sddl.h>
+
+#define RED "\033[1;31m"
+#define GREEN "\033[1;32m"
+#define AMBER "\033[33m"
+#define CYAN "\033[1;36m"
+#define C_NORMAL "\033[0m"
+
+/*
+ * Note that the SIDs SA, CA, RS, EA, PA, RO, and CN cannot be set by
+ * an ordinary local Administrator (error 1337, invalid SID). For this
+ * reason we use other SIDs instead/as well, so the list differs from
+ * the python/samba/tests/sddl.py list, which it is otherwise based on.
+ */
+const char *strings[] = {
+ "D:(A;;CC;;;BA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)",
+
+ "D:(A;;GA;;;RU)",
+
+ "D:(A;;GA;;;LG)",
+
+ ("D:(A;;RP;;;WD)"
+ "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)"
+ "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)"
+ "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)"
+ "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)"
+ "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)"
+ "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)"
+ "(A;;RPLCLORC;;;AU)"
+ "(A;;RPWPCRLCLOCCRCWDWOSW;;;BO)"
+ "(A;CI;RPWPCRLCLOCCRCWDWOSDSW;;;BA)"
+ "(A;;RPWPCRLCLOCCDCRCWDWOSDDTSW;;;SY)"
+ "(A;CI;RPWPCRLCLOCCDCRCWDWOSDDTSW;;;ES)"
+ "(A;CI;LC;;;RU)"
+ "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;bf967aba-0de6-11d0-a285-00aa003049e2;RU)"
+ "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)"
+ "(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)"
+ "(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;bf967aba-0de6-11d0-a285-00aa003049e2;RU)"
+ "(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)"
+ "(OA;;RP;c7407360-20bf-11d0-a768-00aa006e0529;;RU)"
+ "(OA;CIIO;RPLCLORC;;bf967a9c-0de6-11d0-a285-00aa003049e2;RU)"
+ "(A;;RPRC;;;RU)"
+ "(OA;CIIO;RPLCLORC;;bf967aba-0de6-11d0-a285-00aa003049e2;RU)"
+ "(A;;LCRPLORC;;;ED)"
+ "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;4828CC14-1437-45bc-9B07-AD6F015E5F28;RU)"
+ "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;4828CC14-1437-45bc-9B07-AD6F015E5F28;RU)"
+ "(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;4828CC14-1437-45bc-9B07-AD6F015E5F28;RU)"
+ "(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;4828CC14-1437-45bc-9B07-AD6F015E5F28;RU)"
+ "(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;4828CC14-1437-45bc-9B07-AD6F015E5F28;RU)"
+ "(OA;CIIO;RPLCLORC;;4828CC14-1437-45bc-9B07-AD6F015E5F28;RU)"
+ "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;RU)"
+ "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;AU)"
+ "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967aba-0de6-11d0-a285-00aa003049e2;ED)"
+ "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a9c-0de6-11d0-a285-00aa003049e2;ED)"
+ "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a86-0de6-11d0-a285-00aa003049e2;ED)"
+ "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;NO)"
+ "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)"
+ "(OA;;CR;e2a36dc9-ae17-47c3-b58b-be34c55ba633;;SU)"
+ "(OA;;CR;280f369c-67c7-438e-ae98-1d46f3c6f541;;AU)"
+ "(OA;;CR;ccc2dc7d-a6ad-4a7a-8846-c04e3cc53501;;AU)"
+ "(OA;;CR;05c74c5e-4deb-43b4-bd9f-86664c2a7fd5;;AU)S:(AU;SA;WDWOWP;;;WD)"),
+
+ ("S:(AU;SA;CR;;;WD)"
+ "(AU;SA;CR;;;WD)"),
+
+ ("S:""(OU;CISA;WP;f30e3bbe-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)"
+ "(OU;CISA;WP;f30e3bbf-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)"),
+
+ ("D:(A;;RPLCLORC;;;BO)"
+ "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)"
+ "(A;;RPLCLORC;;;AU)"),
+
+ ("D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;BO)"
+ "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;AO)"
+ "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)"
+ "(A;;RPCRLCLORCSDDT;;;CO)"
+ "(OA;;WP;4c164200-20c0-11d0-a768-00aa006e0529;;CO)"
+ "(A;;RPLCLORC;;;AU)"
+ "(OA;;CR;ab721a53-1e2f-11d0-9819-00aa0040529b;;WD)"
+ "(A;;CCDC;;;PS)"
+ "(OA;;CCDC;bf967aa8-0de6-11d0-a285-00aa003049e2;;PO)"
+ "(OA;;RPWP;bf967a7f-0de6-11d0-a285-00aa003049e2;;SY)"
+ "(OA;;SW;f3a64788-5306-11d1-a9c5-0000f80367c1;;PS)"
+ "(OA;;RPWP;77B5B886-944A-11d1-AEBD-0000F80367C1;;PS)"
+ "(OA;;SW;72e39547-7b18-11d1-adef-00c04fd8d5cd;;PS)"
+ "(OA;;SW;72e39547-7b18-11d1-adef-00c04fd8d5cd;;CO)"
+ "(OA;;SW;f3a64788-5306-11d1-a9c5-0000f80367c1;;CO)"
+ "(OA;;WP;3e0abfd0-126a-11d0-a060-00aa006c33ed;bf967a86-0de6-11d0-a285-00aa003049e2;CO)"
+ "(OA;;WP;5f202010-79a5-11d0-9020-00c04fc2d4cf;bf967a86-0de6-11d0-a285-00aa003049e2;CO)"
+ "(OA;;WP;bf967950-0de6-11d0-a285-00aa003049e2;bf967a86-0de6-11d0-a285-00aa003049e2;CO)"
+ "(OA;;WP;bf967953-0de6-11d0-a285-00aa003049e2;bf967a86-0de6-11d0-a285-00aa003049e2;CO)"
+ "(OA;;RP;46a9b11d-60ae-405a-b7e8-ff8a58d456d2;;SU)"),
+
+ ("D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;BO)"
+ "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)"
+ "(A;;RPLCLORC;;;AU)"),
+
+ ("D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;BO)"
+ "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)"
+ "(A;;RPLCLORC;;;AU)"
+ "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;AO)"
+ "(A;;RPLCLORC;;;PS)"
+ "(OA;;CR;ab721a55-1e2f-11d0-9819-00aa0040529b;;AU)"
+ "(OA;;RP;46a9b11d-60ae-405a-b7e8-ff8a58d456d2;;SU)"),
+
+ ("D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;BO)"
+ "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)"
+ "(A;;RPLCLORC;;;AU)"
+ "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;CO)"),
+
+ ("D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;BO)"
+ "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)"
+ "(A;;RPLCLORC;;;AU)S:(AU;SA;CRWP;;;WD)"),
+
+ ("D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;BO)"
+ "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)"
+ "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;AO)"
+ "(A;;RPLCLORC;;;PS)"
+ "(OA;;CR;ab721a53-1e2f-11d0-9819-00aa0040529b;;PS)"
+ "(OA;;CR;ab721a54-1e2f-11d0-9819-00aa0040529b;;PS)"
+ "(OA;;CR;ab721a56-1e2f-11d0-9819-00aa0040529b;;PS)"
+ "(OA;;RPWP;77B5B886-944A-11d1-AEBD-0000F80367C1;;PS)"
+ "(OA;;RPWP;E45795B2-9455-11d1-AEBD-0000F80367C1;;PS)"
+ "(OA;;RPWP;E45795B3-9455-11d1-AEBD-0000F80367C1;;PS)"
+ "(OA;;RP;037088f8-0ae1-11d2-b422-00a0c968f939;;RD)"
+ "(OA;;RP;4c164200-20c0-11d0-a768-00aa006e0529;;RD)"
+ "(OA;;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;;RD)"
+ "(A;;RC;;;AU)"
+ "(OA;;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;;AU)"
+ "(OA;;RP;77B5B886-944A-11d1-AEBD-0000F80367C1;;AU)"
+ "(OA;;RP;E45795B3-9455-11d1-AEBD-0000F80367C1;;AU)"
+ "(OA;;RP;e48d0154-bcf8-11d1-8702-00c04fb96050;;AU)"
+ "(OA;;CR;ab721a53-1e2f-11d0-9819-00aa0040529b;;WD)"
+ "(OA;;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;;RD)"
+ "(OA;;RPWP;bf967a7f-0de6-11d0-a285-00aa003049e2;;SY)"
+ "(OA;;RP;46a9b11d-60ae-405a-b7e8-ff8a58d456d2;;SU)"
+ "(OA;;WPRP;6db69a1c-9422-11d1-aebd-0000f80367c1;;SU)"),
+
+ "D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)",
+
+ ("D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)"
+ "(A;;RPLCLORC;;;AU)"
+ "(A;;LCRPLORC;;;ED)"),
+
+ ("D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)"
+ "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;BO)"
+ "(OA;;CCDC;bf967a86-0de6-11d0-a285-00aa003049e2;;AO)"
+ "(OA;;CCDC;bf967aba-0de6-11d0-a285-00aa003049e2;;AO)"
+ "(OA;;CCDC;bf967a9c-0de6-11d0-a285-00aa003049e2;;AO)"
+ "(OA;;CCDC;bf967aa8-0de6-11d0-a285-00aa003049e2;;PO)"
+ "(A;;RPLCLORC;;;AU)"
+ "(A;;LCRPLORC;;;ED)"
+ "(OA;;CCDC;4828CC14-1437-45bc-9B07-AD6F015E5F28;;AO)"),
+
+ ("D:(A;;RPWPCRCCDCLCLORCWOWDSW;;;BO)"
+ "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)"
+ "(A;;RPLCLORC;;;AU)"),
+
+ ("D:(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;BO)"
+ "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)"
+ "(A;;RPLCLORC;;;AU)"),
+
+ "D:S:",
+ "D:PS:",
+ NULL
+};
+
+
+static int test_pair(const char *s, const char *canonical)
+{
+ PSECURITY_DESCRIPTOR sd = NULL;
+ ULONG len;
+ char *return_string = NULL;
+ ULONG return_len;
+ int ok = ConvertStringSecurityDescriptorToSecurityDescriptorA(s,
+ 1,
+ &sd,
+ &len);
+ if (!ok) {
+ int e = GetLastError();
+ const char *ename = NULL;
+ switch(e) {
+ case 1337:
+ ename = " invalid sid";
+ break;
+ case 1336:
+ ename = " insufficient privs/SACL vs DACL/something something";
+ break;
+ case 1804:
+ ename = " invalid datatype";
+ break;
+ default:
+ ename = "";
+ }
+
+ printf(RED "not ok:" AMBER " %d%s" C_NORMAL ": %s\n",
+ e, ename, s);
+ return e;
+ }
+ if (sd == NULL) {
+ printf(RED "NULL sd" C_NORMAL": %s\n", s);
+ return -1;
+ }
+
+ ok = ConvertSecurityDescriptorToStringSecurityDescriptorA(
+ sd,
+ 1,
+ ~BACKUP_SECURITY_INFORMATION,
+ &return_string,
+ &return_len);
+ if (strncmp(return_string, canonical, return_len) != 0) {
+ printf(RED "return differs:" AMBER " %u vs %u" C_NORMAL "\n",
+ len, return_len);
+ printf(RED "original:" C_NORMAL ": %s\n", s);
+ printf(RED "returned:" C_NORMAL ": %s\n", return_string);
+ return -2;
+ }
+ printf(GREEN "GOOD" C_NORMAL ": %s\n", s);
+ if (strncmp(return_string, s, return_len) != 0) {
+ printf(CYAN "original:" C_NORMAL ": %s\n", s);
+ printf(CYAN "returned:" C_NORMAL ": %s\n", return_string);
+ return -2;
+ }
+ return 0;
+}
+
+
+int test_from_files(int argc, const char *argv[])
+{
+ size_t i, j;
+ static char buf[100000];
+
+ for (i = 0; i < argc; i++) {
+ char *orig = NULL;
+ char *canon = NULL;
+ size_t len;
+ FILE *f = fopen(argv[i], "r");
+ if (f == NULL) {
+ printf(RED "bad filename? %s\n" C_NORMAL,
+ argv[i]);
+ }
+ len = fread(buf, 1, sizeof(buf), f);
+
+ if (len >= sizeof(buf) - 1 || len == 0) {
+ printf(RED "couldn't read %s\n" C_NORMAL, argv[i]);
+ continue;
+ }
+ printf(CYAN "%s\n" C_NORMAL, argv[i]);
+ buf[len] = 0;
+ orig = buf;
+ for (j = 0; j < len; j++) {
+ char c = buf[j];
+ if (c == '\n') {
+ buf[j] = 0;
+ if (j != 0 && buf[j - 1] == '\r') {
+ buf[j - 1] = 0;
+ }
+ if (orig && canon) {
+ test_pair(orig, canon);
+ canon = NULL;
+ } else {
+ printf(RED "bad pair %s -> %s\n" C_NORMAL,
+ orig, canon);
+ }
+ orig = buf + j + 1;
+ } else if (c == ' ' && j + 4 < len &&
+ buf[j + 1] == '-' &&
+ buf[j + 2] == '>' &&
+ buf[j + 3] == ' ') {
+ buf[j] = 0;
+ canon = buf + j + 4;
+ }
+ }
+ }
+}
+
+int main(int argc, const char *argv[])
+{
+ uint32_t i;
+ if (argc < 2) {
+ for (i = 0; strings[i] != NULL; i++) {
+ test_pair(strings[i], strings[i]);
+ }
+ } else if (strncmp("-i", argv[1], 2) == 0) {
+ return test_from_files(argc - 2, argv + 2);
+ } else {
+ for (i = 1; i < argc; i++) {
+ test_pair(argv[i], argv[i]);
+ }
+ }
+ return 0;
+}
diff --git a/libcli/security/tests/windows/windows-sddl-tests.py b/libcli/security/tests/windows/windows-sddl-tests.py
new file mode 100644
index 0000000..38acb44
--- /dev/null
+++ b/libcli/security/tests/windows/windows-sddl-tests.py
@@ -0,0 +1,181 @@
+# Test SDDL strings on Windows
+#
+#
+# Copyright (c) 2023 Catalyst IT
+#
+# GPLv3+.
+#
+# This uses the Python win32 module to access
+# ConvertStringSecurityDescriptorToSecurityDescriptor and the like. To
+# install this, you need to go
+#
+# pip install pywin32
+#
+# or something like that.
+
+import argparse
+from difflib import SequenceMatcher
+from collections import defaultdict
+import sys
+import json
+
+try:
+ import win32security as w
+except ImportError:
+ print("This test script is meant to be run on Windows using the pywin32 module.")
+ print("To install this module, try:\n")
+ print("pip install pywin32")
+ sys.exit(1)
+
+
+# This is necessary for ANSI colour escapes to work in Powershell.
+import os
+os.system('')
+
+RED = "\033[1;31m"
+GREEN = "\033[1;32m"
+DARK_YELLOW = "\033[0;33m"
+C_NORMAL = "\033[0m"
+
+def c_RED(s):
+ return f"{RED}{s}{C_NORMAL}"
+def c_GREEN(s):
+ return f"{GREEN}{s}{C_NORMAL}"
+def c_DY(s):
+ return f"{DARK_YELLOW}{s}{C_NORMAL}"
+
+
+def read_strings(files):
+ """Try to read as JSON a JSON dictionary first, then secondly in the bespoke
+ sddl-in -> sddl-out
+ format used by other Samba SDDL test programs on Windows.
+ """
+ pairs = []
+ for filename in files:
+ with open(filename) as f:
+ try:
+ data = json.load(f)
+ print(f"loading {filename} as JSON")
+ for k, v in data.items():
+ if not v or not isinstance(v, str):
+ v = k
+ pairs.append((k, v))
+ continue
+ except json.JSONDecodeError:
+ pass
+
+ print(f"loading {filename} as 'a -> b' style")
+ f.seek(0)
+ for line in f:
+ line = line.rstrip()
+ if line.startswith('#') or line == '':
+ continue
+ # note: if the line does not have ' -> ', we expect a
+ # perfect round trip.
+ o, _, c = line.partition(' -> ')
+ if c == '':
+ c = o
+ pairs.append((o, c))
+
+ return pairs
+
+
+def colourdiff(a, b):
+ out = []
+ a = a.replace(' ', '␠')
+ b = b.replace(' ', '␠')
+
+ s = SequenceMatcher(None, a, b)
+ for op, al, ar, bl, br in s.get_opcodes():
+ if op == 'equal':
+ out.append(a[al: ar])
+ elif op == 'delete':
+ out.append(c_RED(a[al: ar]))
+ elif op == 'insert':
+ out.append(c_GREEN(b[bl: br]))
+ elif op == 'replace':
+ out.append(c_RED(a[al: ar]))
+ out.append(c_GREEN(b[bl: br]))
+ else:
+ print(f'unknown op {op}!')
+
+ return ''.join(out)
+
+
+def no_print(*args, **kwargs):
+ pass
+
+
+def main():
+ parser = argparse.ArgumentParser()
+ parser.add_argument('--export-bytes', const='sddl_bytes.json', nargs='?',
+ help='write JSON file containing SD bytes')
+ parser.add_argument('--quiet', action='store_true',
+ help='avoid printing to stdout')
+ parser.add_argument('files', nargs='+', help='read these files')
+
+ args = parser.parse_args()
+
+ if args.quiet:
+ global print
+ print = no_print
+
+ cases = read_strings(args.files)
+ parseable_cases = []
+ unparseable_cases = []
+ unserializeable_cases = []
+ round_trip_failures = []
+ exceptions = defaultdict(list)
+ bytes_json = {}
+
+ print(f"{len(set(cases))}/{len(cases)} unique pairs, "
+ f"{len(set(x[0] for x in cases))}/{len(cases)} unique strings")
+
+ for a, b in sorted(set(cases)):
+ try:
+ sd = w.ConvertStringSecurityDescriptorToSecurityDescriptor(a, 1)
+ except Exception as e:
+ print(a)
+ exceptions[f"{e} parse"].append(a)
+ print(c_RED(e))
+ unparseable_cases.append(a)
+ continue
+
+ parseable_cases.append(a)
+
+ try:
+ # maybe 0xffff is an incorrect guess -- it gives use v2 (NT), not v4 (AD)
+ c = w.ConvertSecurityDescriptorToStringSecurityDescriptor(sd, 1, 0xffff)
+ except Exception as e:
+ print(f"could not serialize '{sd}': {e}")
+ print(f" derived from '{a}'")
+ exceptions[f"{e} serialize"].append(a)
+ unserializeable_cases.append(a)
+ continue
+
+ if args.export_bytes:
+ bytes_json[c] = list(bytes(sd))
+
+ if c != b:
+ round_trip_failures.append((a, b, c))
+ exceptions["mismatch"].append(a)
+ #print(f"{c_GREEN(a)} -> {c_DY(c)}")
+ print(colourdiff(b, c))
+ print(c_DY(f"{b} -> {c}"))
+
+ for k, v in exceptions.items():
+ print(f"{k}: {len(v)}")
+
+ print(f"{len(unparseable_cases)} failed to parse")
+ print(f"{len(parseable_cases)} successfully parsed")
+ print(f"{len(unserializeable_cases)} of these failed to re-serialize")
+ print(f"{len(round_trip_failures)} of these failed to round trip")
+ #for p in parseable_cases:
+ # print(f"«{c_GREEN(p)}»")
+
+ if args.export_bytes:
+ with open(args.export_bytes, 'w') as f:
+ json.dump(bytes_json, f)
+ print(f"wrote bytes to {args.export_bytes}")
+
+main()
diff --git a/libcli/security/tests/windows/windows_is_fussy.txt b/libcli/security/tests/windows/windows_is_fussy.txt
new file mode 100644
index 0000000..b058a67
--- /dev/null
+++ b/libcli/security/tests/windows/windows_is_fussy.txt
@@ -0,0 +1 @@
+D:(A;;RP;;;WD)(AU;SA;CR;;;BA)(AU;SA;CR;;;DU) -> D:(A;;RP;;;WD)(AU;SA;CR;;;BA)(AU;SA;CR;;;DU)
diff --git a/libcli/security/tests/windows/windows_is_less_fussy.txt b/libcli/security/tests/windows/windows_is_less_fussy.txt
new file mode 100644
index 0000000..17e2e5b
--- /dev/null
+++ b/libcli/security/tests/windows/windows_is_less_fussy.txt
@@ -0,0 +1,23 @@
+D:(A;;GA;;; LG) -> D:(A;;GA;;;LG)
+D: (A;;GA;;;LG) -> D:(A;;GA;;;LG)
+D: AI(A;;GA;;;LG) -> D:AI(A;;GA;;;LG)
+D:(a;;GA;;;LG) -> D:(A;;GA;;;LG)
+D:(A;;GA;;;lg) -> D:(A;;GA;;;LG)
+D:(A;;ga;;;LG) -> D:(A;;GA;;;LG)
+D: S: -> D:S:
+D: P(A;;GA;;;LG) -> D:P(A;;GA;;;LG)
+D:P (A;;GA;;;LG) -> D:P(A;;GA;;;LG)
+D:P(A;;GA;;;LG) (A;;GX;;;AA) -> D:P(A;;GA;;;LG)(A;;GX;;;AA)
+D:(A; ;GA;;;LG) -> D:(A;;GA;;;LG)
+D:AI (A;;GA;;;LG) -> D:AI(A;;GA;;;LG)
+D:(A;;GA;;; WD) -> D:(A;;GA;;;WD)
+D:(A;;GA;;;WD ) -> D:(A;;GA;;;WD)
+D:(A;;GA;;; S-1-3-4) -> D:(A;;GA;;;OW)
+D:(A;;GA;; ;S-1-3-4) -> D:(A;;GA;;;OW)
+D:(A;;GA; ;;S-1-3-4) -> D:(A;;GA;;;OW)
+D:(A;;GA;;; S-1-333-4) -> D:(A;;GA;;;S-1-333-4)
+D:(A;;GA; ;;S-1-333-4) -> D:(A;;GA;;;S-1-333-4)
+ O:AA -> O:AA
+ O:AA -> O:AA
+ O:AA G:WD -> O:AAG:WD
+O:S- 1- 2-3 -> O:S-1-2-3
diff --git a/libcli/security/tests/windows/windows_is_weird.txt b/libcli/security/tests/windows/windows_is_weird.txt
new file mode 100644
index 0000000..7c9d265
--- /dev/null
+++ b/libcli/security/tests/windows/windows_is_weird.txt
@@ -0,0 +1,10 @@
+D:(A;;0x123456789;;;LG) -> D:(A;;0xffffffff;;;LG)
+D:(A;;CC;;;S-0x1-0-0-579) -> D:(A;;CC;;;S-1-0-0-1401)
+O:S-0x1-20-0-579 -> O:S-1-32-0-1401
+D:(A;;GA;;;S-1-3-4294967296-3-4) -> D:(A;;GA;;;S-1-3-4294967295-3-4)
+D:(A;;GA;;;S-1-3-0x100000000-3-4) -> D:(A;;GA;;;S-1-3-4294967295-3-4)
+D:(A;;GA;;;S-1-5-21-0x1313131313131-513) -> D:(A;;GA;;;S-1-5-21-4294967295-513)
+D:(A;;-99;;;LG) -> D:(A;;0xffffff9d;;;LG)
+D:(A;;-0xffffff55;;;LG) -> D:(A;;CCDCSWWPLO;;;LG)
+D:(A;;-9876543210;;;LG) -> D:(A;;CC;;;LG)
+D:(A;;100000000000000000000000;;;LG) -> D:(A;;0xffffffff;;;LG)
diff --git a/libcli/security/util_sid.c b/libcli/security/util_sid.c
new file mode 100644
index 0000000..54a2fc3
--- /dev/null
+++ b/libcli/security/util_sid.c
@@ -0,0 +1,1117 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba utility functions
+ Copyright (C) Andrew Tridgell 1992-1998
+ Copyright (C) Luke Kenneth Caseson Leighton 1998-1999
+ Copyright (C) Jeremy Allison 1999
+ Copyright (C) Stefan (metze) Metzmacher 2002
+ Copyright (C) Simo Sorce 2002
+ Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2005
+ Copyright (C) Andrew Bartlett 2010
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "replace.h"
+#include "lib/util/samba_util.h"
+#include "../librpc/gen_ndr/ndr_security.h"
+#include "../librpc/gen_ndr/netlogon.h"
+#include "../libcli/security/security.h"
+#include "auth/auth.h"
+
+
+#undef strcasecmp
+#undef strncasecmp
+
+/*
+ * Some useful sids, more well known sids can be found at
+ * http://support.microsoft.com/kb/243330/EN-US/
+ */
+
+
+/* S-1-1 */
+const struct dom_sid global_sid_World_Domain = /* Everyone domain */
+{ 1, 0, {0,0,0,0,0,1}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}};
+/* S-1-1-0 */
+const struct dom_sid global_sid_World = /* Everyone */
+{ 1, 1, {0,0,0,0,0,1}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}};
+/* S-1-2 */
+const struct dom_sid global_sid_Local_Authority = /* Local Authority */
+{ 1, 0, {0,0,0,0,0,2}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}};
+/* S-1-3 */
+const struct dom_sid global_sid_Creator_Owner_Domain = /* Creator Owner domain */
+{ 1, 0, {0,0,0,0,0,3}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}};
+/* S-1-5 */
+const struct dom_sid global_sid_NT_Authority = /* NT Authority */
+{ 1, 0, {0,0,0,0,0,5}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}};
+/* S-1-5-18 */
+const struct dom_sid global_sid_System = /* System */
+{ 1, 1, {0,0,0,0,0,5}, {18,0,0,0,0,0,0,0,0,0,0,0,0,0,0}};
+/* S-1-0-0 */
+const struct dom_sid global_sid_NULL = /* NULL sid */
+{ 1, 1, {0,0,0,0,0,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}};
+/* S-1-5-10 */
+const struct dom_sid global_sid_Self = /* SELF */
+{ 1, 1, {0,0,0,0,0,5}, {10,0,0,0,0,0,0,0,0,0,0,0,0,0,0}};
+/* S-1-5-11 */
+const struct dom_sid global_sid_Authenticated_Users = /* All authenticated rids */
+{ 1, 1, {0,0,0,0,0,5}, {11,0,0,0,0,0,0,0,0,0,0,0,0,0,0}};
+#if 0
+/* for documentation S-1-5-12 */
+const struct dom_sid global_sid_Restricted = /* Restricted Code */
+{ 1, 1, {0,0,0,0,0,5}, {12,0,0,0,0,0,0,0,0,0,0,0,0,0,0}};
+#endif
+
+/* S-1-18 */
+const struct dom_sid global_sid_Asserted_Identity = /* Asserted Identity */
+{ 1, 0, {0,0,0,0,0,18}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}};
+/* S-1-18-1 */
+const struct dom_sid global_sid_Asserted_Identity_Authentication_Authority = /* Asserted Identity Authentication Authority */
+{ 1, 1, {0,0,0,0,0,18}, {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0}};
+/* S-1-18-2 */
+const struct dom_sid global_sid_Asserted_Identity_Service = /* Asserted Identity Service */
+{ 1, 1, {0,0,0,0,0,18}, {2,0,0,0,0,0,0,0,0,0,0,0,0,0,0}};
+
+/* S-1-5-2 */
+const struct dom_sid global_sid_Network = /* Network rids */
+{ 1, 1, {0,0,0,0,0,5}, {2,0,0,0,0,0,0,0,0,0,0,0,0,0,0}};
+
+/* S-1-3 */
+const struct dom_sid global_sid_Creator_Owner = /* Creator Owner */
+{ 1, 1, {0,0,0,0,0,3}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}};
+/* S-1-3-1 */
+const struct dom_sid global_sid_Creator_Group = /* Creator Group */
+{ 1, 1, {0,0,0,0,0,3}, {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0}};
+/* S-1-3-4 */
+const struct dom_sid global_sid_Owner_Rights = /* Owner Rights */
+{ 1, 1, {0,0,0,0,0,3}, {4,0,0,0,0,0,0,0,0,0,0,0,0,0,0}};
+/* S-1-5-7 */
+const struct dom_sid global_sid_Anonymous = /* Anonymous login */
+{ 1, 1, {0,0,0,0,0,5}, {7,0,0,0,0,0,0,0,0,0,0,0,0,0,0}};
+/* S-1-5-9 */
+const struct dom_sid global_sid_Enterprise_DCs = /* Enterprise DCs */
+{ 1, 1, {0,0,0,0,0,5}, {9,0,0,0,0,0,0,0,0,0,0,0,0,0,0}};
+/* S-1-5-21-0-0-0-496 */
+const struct dom_sid global_sid_Compounded_Authentication = /* Compounded Authentication */
+{1, 5, {0,0,0,0,0,5}, {21,0,0,0,496,0,0,0,0,0,0,0,0,0,0}};
+/* S-1-5-21-0-0-0-497 */
+const struct dom_sid global_sid_Claims_Valid = /* Claims Valid */
+{1, 5, {0,0,0,0,0,5}, {21,0,0,0,497,0,0,0,0,0,0,0,0,0,0}};
+/* S-1-5-32 */
+const struct dom_sid global_sid_Builtin = /* Local well-known domain */
+{ 1, 1, {0,0,0,0,0,5}, {32,0,0,0,0,0,0,0,0,0,0,0,0,0,0}};
+/* S-1-5-32-544 */
+const struct dom_sid global_sid_Builtin_Administrators = /* Builtin administrators */
+{ 1, 2, {0,0,0,0,0,5}, {32,544,0,0,0,0,0,0,0,0,0,0,0,0,0}};
+/* S-1-5-32-545 */
+const struct dom_sid global_sid_Builtin_Users = /* Builtin users */
+{ 1, 2, {0,0,0,0,0,5}, {32,545,0,0,0,0,0,0,0,0,0,0,0,0,0}};
+/* S-1-5-32-546 */
+const struct dom_sid global_sid_Builtin_Guests = /* Builtin guest users */
+{ 1, 2, {0,0,0,0,0,5}, {32,546,0,0,0,0,0,0,0,0,0,0,0,0,0}};
+/* S-1-5-32-547 */
+const struct dom_sid global_sid_Builtin_Power_Users = /* Builtin power users */
+{ 1, 2, {0,0,0,0,0,5}, {32,547,0,0,0,0,0,0,0,0,0,0,0,0,0}};
+/* S-1-5-32-548 */
+const struct dom_sid global_sid_Builtin_Account_Operators = /* Builtin account operators */
+{ 1, 2, {0,0,0,0,0,5}, {32,548,0,0,0,0,0,0,0,0,0,0,0,0,0}};
+/* S-1-5-32-549 */
+const struct dom_sid global_sid_Builtin_Server_Operators = /* Builtin server operators */
+{ 1, 2, {0,0,0,0,0,5}, {32,549,0,0,0,0,0,0,0,0,0,0,0,0,0}};
+/* S-1-5-32-550 */
+const struct dom_sid global_sid_Builtin_Print_Operators = /* Builtin print operators */
+{ 1, 2, {0,0,0,0,0,5}, {32,550,0,0,0,0,0,0,0,0,0,0,0,0,0}};
+/* S-1-5-32-551 */
+const struct dom_sid global_sid_Builtin_Backup_Operators = /* Builtin backup operators */
+{ 1, 2, {0,0,0,0,0,5}, {32,551,0,0,0,0,0,0,0,0,0,0,0,0,0}};
+/* S-1-5-32-552 */
+const struct dom_sid global_sid_Builtin_Replicator = /* Builtin replicator */
+{ 1, 2, {0,0,0,0,0,5}, {32,552,0,0,0,0,0,0,0,0,0,0,0,0,0}};
+/* S-1-5-32-554 */
+const struct dom_sid global_sid_Builtin_PreWin2kAccess = /* Builtin pre win2k access */
+{ 1, 2, {0,0,0,0,0,5}, {32,554,0,0,0,0,0,0,0,0,0,0,0,0,0}};
+
+/* S-1-22-1 */
+const struct dom_sid global_sid_Unix_Users = /* Unmapped Unix users */
+{ 1, 1, {0,0,0,0,0,22}, {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0}};
+/* S-1-22-2 */
+const struct dom_sid global_sid_Unix_Groups = /* Unmapped Unix groups */
+{ 1, 1, {0,0,0,0,0,22}, {2,0,0,0,0,0,0,0,0,0,0,0,0,0,0}};
+
+/*
+ * http://technet.microsoft.com/en-us/library/hh509017(v=ws.10).aspx
+ */
+/* S-1-5-88 */
+const struct dom_sid global_sid_Unix_NFS = /* MS NFS and Apple style */
+{ 1, 1, {0,0,0,0,0,5}, {88,0,0,0,0,0,0,0,0,0,0,0,0,0,0}};
+/* S-1-5-88-1 */
+const struct dom_sid global_sid_Unix_NFS_Users = /* Unix uid, MS NFS and Apple style */
+{ 1, 2, {0,0,0,0,0,5}, {88,1,0,0,0,0,0,0,0,0,0,0,0,0,0}};
+/* S-1-5-88-2 */
+const struct dom_sid global_sid_Unix_NFS_Groups = /* Unix gid, MS NFS and Apple style */
+{ 1, 2, {0,0,0,0,0,5}, {88,2,0,0,0,0,0,0,0,0,0,0,0,0,0}};
+/* S-1-5-88-3 */
+const struct dom_sid global_sid_Unix_NFS_Mode = /* Unix mode */
+{ 1, 2, {0,0,0,0,0,5}, {88,3,0,0,0,0,0,0,0,0,0,0,0,0,0}};
+/* Unused, left here for documentary purposes */
+#if 0
+const struct dom_sid global_sid_Unix_NFS_Other = /* Unix other, MS NFS and Apple style */
+{ 1, 2, {0,0,0,0,0,5}, {88,4,0,0,0,0,0,0,0,0,0,0,0,0,0}};
+#endif
+
+/* Information passing via security token */
+const struct dom_sid global_sid_Samba_SMB3 =
+{1, 1, {0,0,0,0,0,22}, {1397571891, }};
+
+const struct dom_sid global_sid_Samba_NPA_Flags = {1,
+ 1,
+ {0, 0, 0, 0, 0, 22},
+ {
+ 2041152804,
+ }};
+
+/* Unused, left here for documentary purposes */
+#if 0
+#define SECURITY_NULL_SID_AUTHORITY 0
+#define SECURITY_WORLD_SID_AUTHORITY 1
+#define SECURITY_LOCAL_SID_AUTHORITY 2
+#define SECURITY_CREATOR_SID_AUTHORITY 3
+#define SECURITY_NT_AUTHORITY 5
+#endif
+
+static struct dom_sid system_sid_array[1] =
+{ { 1, 1, {0,0,0,0,0,5}, {18,0,0,0,0,0,0,0,0,0,0,0,0,0,0}} };
+static const struct security_token system_token = {
+ .num_sids = ARRAY_SIZE(system_sid_array),
+ .sids = system_sid_array,
+ .privilege_mask = SE_ALL_PRIVS
+};
+
+/****************************************************************************
+ Lookup string names for SID types.
+****************************************************************************/
+
+const char *sid_type_lookup(uint32_t sid_type)
+{
+ switch (sid_type) {
+ case SID_NAME_USE_NONE:
+ return "None";
+ break;
+ case SID_NAME_USER:
+ return "User";
+ break;
+ case SID_NAME_DOM_GRP:
+ return "Domain Group";
+ break;
+ case SID_NAME_DOMAIN:
+ return "Domain";
+ break;
+ case SID_NAME_ALIAS:
+ return "Local Group";
+ break;
+ case SID_NAME_WKN_GRP:
+ return "Well-known Group";
+ break;
+ case SID_NAME_DELETED:
+ return "Deleted Account";
+ break;
+ case SID_NAME_INVALID:
+ return "Invalid Account";
+ break;
+ case SID_NAME_UNKNOWN:
+ return "UNKNOWN";
+ break;
+ case SID_NAME_COMPUTER:
+ return "Computer";
+ break;
+ case SID_NAME_LABEL:
+ return "Mandatory Label";
+ break;
+ }
+
+ /* Default return */
+ return "SID *TYPE* is INVALID";
+}
+
+/**************************************************************************
+ Create the SYSTEM token.
+***************************************************************************/
+
+const struct security_token *get_system_token(void)
+{
+ return &system_token;
+}
+
+bool sid_compose(struct dom_sid *dst, const struct dom_sid *domain_sid, uint32_t rid)
+{
+ sid_copy(dst, domain_sid);
+ return sid_append_rid(dst, rid);
+}
+
+/*****************************************************************
+ Removes the last rid from the end of a sid
+*****************************************************************/
+
+bool sid_split_rid(struct dom_sid *sid, uint32_t *rid)
+{
+ if (sid->num_auths > 0) {
+ sid->num_auths--;
+ if (rid != NULL) {
+ *rid = sid->sub_auths[sid->num_auths];
+ }
+ return true;
+ }
+ return false;
+}
+
+/*****************************************************************
+ Return the last rid from the end of a sid
+*****************************************************************/
+
+bool sid_peek_rid(const struct dom_sid *sid, uint32_t *rid)
+{
+ if (!sid || !rid)
+ return false;
+
+ if (sid->num_auths > 0) {
+ *rid = sid->sub_auths[sid->num_auths - 1];
+ return true;
+ }
+ return false;
+}
+
+/*****************************************************************
+ Return the last rid from the end of a sid
+ and check the sid against the exp_dom_sid
+*****************************************************************/
+
+bool sid_peek_check_rid(const struct dom_sid *exp_dom_sid, const struct dom_sid *sid, uint32_t *rid)
+{
+ if (!exp_dom_sid || !sid || !rid)
+ return false;
+
+ if (sid->num_auths != (exp_dom_sid->num_auths+1)) {
+ return false;
+ }
+
+ if (dom_sid_compare_domain(exp_dom_sid, sid)!=0){
+ *rid=(-1);
+ return false;
+ }
+
+ return sid_peek_rid(sid, rid);
+}
+
+/*****************************************************************
+ Copies a sid
+*****************************************************************/
+
+void sid_copy(struct dom_sid *dst, const struct dom_sid *src)
+{
+ int i;
+
+ *dst = (struct dom_sid) {
+ .sid_rev_num = src->sid_rev_num,
+ .num_auths = src->num_auths,
+ };
+
+ memcpy(&dst->id_auth[0], &src->id_auth[0], sizeof(src->id_auth));
+
+ for (i = 0; i < src->num_auths; i++)
+ dst->sub_auths[i] = src->sub_auths[i];
+}
+
+/*****************************************************************
+ Parse a on-the-wire SID to a struct dom_sid.
+*****************************************************************/
+
+ssize_t sid_parse(const uint8_t *inbuf, size_t len, struct dom_sid *sid)
+{
+ DATA_BLOB in = data_blob_const(inbuf, len);
+ enum ndr_err_code ndr_err;
+
+ ndr_err = ndr_pull_struct_blob_all(
+ &in, NULL, sid, (ndr_pull_flags_fn_t)ndr_pull_dom_sid);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return -1;
+ }
+ return ndr_size_dom_sid(sid, 0);
+}
+
+/********************************************************************
+ Add SID to an array of SIDs
+********************************************************************/
+
+NTSTATUS add_sid_to_array(TALLOC_CTX *mem_ctx, const struct dom_sid *sid,
+ struct dom_sid **sids, uint32_t *num)
+{
+ struct dom_sid *tmp;
+
+ if ((*num) == UINT32_MAX) {
+ return NT_STATUS_INTEGER_OVERFLOW;
+ }
+
+ tmp = talloc_realloc(mem_ctx, *sids, struct dom_sid, (*num)+1);
+ if (tmp == NULL) {
+ *num = 0;
+ return NT_STATUS_NO_MEMORY;
+ }
+ *sids = tmp;
+
+ sid_copy(&((*sids)[*num]), sid);
+ *num += 1;
+
+ return NT_STATUS_OK;
+}
+
+
+/********************************************************************
+ Add SID to an array of SIDs ensuring that it is not already there
+********************************************************************/
+
+NTSTATUS add_sid_to_array_unique(TALLOC_CTX *mem_ctx, const struct dom_sid *sid,
+ struct dom_sid **sids, uint32_t *num_sids)
+{
+ bool contains;
+
+ contains = sids_contains_sid(*sids, *num_sids, sid);
+ if (contains) {
+ return NT_STATUS_OK;
+ }
+
+ return add_sid_to_array(mem_ctx, sid, sids, num_sids);
+}
+
+/**
+ * Appends a SID and attribute to an array of auth_SidAttr.
+ *
+ * @param [in] mem_ctx Talloc memory context on which to allocate the array.
+ * @param [in] sid The SID to append.
+ * @param [in] attrs SE_GROUP_* flags to go with the SID.
+ * @param [inout] sids A pointer to the auth_SidAttr array.
+ * @param [inout] num A pointer to the size of the auth_SidArray array.
+ * @returns NT_STATUS_OK on success.
+ */
+NTSTATUS add_sid_to_array_attrs(TALLOC_CTX *mem_ctx,
+ const struct dom_sid *sid, uint32_t attrs,
+ struct auth_SidAttr **sids, uint32_t *num)
+{
+ struct auth_SidAttr *tmp = NULL;
+
+ if ((*num) == UINT32_MAX) {
+ return NT_STATUS_INTEGER_OVERFLOW;
+ }
+
+ tmp = talloc_realloc(mem_ctx, *sids, struct auth_SidAttr, (*num)+1);
+ if (tmp == NULL) {
+ *num = 0;
+ return NT_STATUS_NO_MEMORY;
+ }
+ *sids = tmp;
+
+ sid_copy(&((*sids)[*num].sid), sid);
+ (*sids)[*num].attrs = attrs;
+ *num += 1;
+
+ return NT_STATUS_OK;
+}
+
+
+/**
+ * Appends a SID and attribute to an array of auth_SidAttr,
+ * ensuring that it is not already there.
+ *
+ * @param [in] mem_ctx Talloc memory context on which to allocate the array.
+ * @param [in] sid The SID to append.
+ * @param [in] attrs SE_GROUP_* flags to go with the SID.
+ * @param [inout] sids A pointer to the auth_SidAttr array.
+ * @param [inout] num_sids A pointer to the size of the auth_SidArray array.
+ * @returns NT_STATUS_OK on success.
+ */
+NTSTATUS add_sid_to_array_attrs_unique(TALLOC_CTX *mem_ctx,
+ const struct dom_sid *sid, uint32_t attrs,
+ struct auth_SidAttr **sids, uint32_t *num_sids)
+{
+ bool contains;
+
+ contains = sids_contains_sid_attrs(*sids, *num_sids, sid, attrs);
+ if (contains) {
+ return NT_STATUS_OK;
+ }
+
+ return add_sid_to_array_attrs(mem_ctx, sid, attrs, sids, num_sids);
+}
+
+/********************************************************************
+ Remove SID from an array
+********************************************************************/
+
+void del_sid_from_array(const struct dom_sid *sid, struct dom_sid **sids,
+ uint32_t *num)
+{
+ struct dom_sid *sid_list = *sids;
+ uint32_t i;
+
+ for ( i=0; i<*num; i++ ) {
+
+ /* if we find the SID, then decrement the count
+ and break out of the loop */
+
+ if (dom_sid_equal(sid, &sid_list[i])) {
+ *num -= 1;
+ break;
+ }
+ }
+
+ /* This loop will copy the remainder of the array
+ if i < num of sids in the array */
+
+ for ( ; i<*num; i++ ) {
+ sid_copy( &sid_list[i], &sid_list[i+1] );
+ }
+}
+
+bool add_rid_to_array_unique(TALLOC_CTX *mem_ctx,
+ uint32_t rid, uint32_t **pp_rids, size_t *p_num)
+{
+ size_t i;
+
+ for (i=0; i<*p_num; i++) {
+ if ((*pp_rids)[i] == rid)
+ return true;
+ }
+
+ *pp_rids = talloc_realloc(mem_ctx, *pp_rids, uint32_t, *p_num+1);
+
+ if (*pp_rids == NULL) {
+ *p_num = 0;
+ return false;
+ }
+
+ (*pp_rids)[*p_num] = rid;
+ *p_num += 1;
+ return true;
+}
+
+bool is_null_sid(const struct dom_sid *sid)
+{
+ static const struct dom_sid null_sid = {0};
+ return dom_sid_equal(sid, &null_sid);
+}
+
+/**
+ * Return true if an array of SIDs contains a certain SID.
+ *
+ * @param [in] sids The SID array.
+ * @param [in] num_sids The size of the SID array.
+ * @param [in] sid The SID in question.
+ * @returns true if the array contains the SID.
+ */
+bool sids_contains_sid(const struct dom_sid *sids,
+ const uint32_t num_sids,
+ const struct dom_sid *sid)
+{
+ uint32_t i;
+
+ for (i = 0; i < num_sids; i++) {
+ if (dom_sid_equal(&sids[i], sid)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+ * Return true if an array of auth_SidAttr contains a certain SID.
+ *
+ * @param [in] sids The auth_SidAttr array.
+ * @param [in] num_sids The size of the auth_SidArray array.
+ * @param [in] sid The SID in question.
+ * @returns true if the array contains the SID.
+ */
+bool sid_attrs_contains_sid(const struct auth_SidAttr *sids,
+ const uint32_t num_sids,
+ const struct dom_sid *sid)
+{
+ uint32_t i;
+
+ for (i = 0; i < num_sids; i++) {
+ if (dom_sid_equal(&sids[i].sid, sid)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+ * Return true if an array of auth_SidAttr contains a certain SID with certain
+ * attributes.
+ *
+ * @param [in] sids The auth_SidAttr array.
+ * @param [in] num_sids The size of the auth_SidArray array.
+ * @param [in] sid The SID in question.
+ * @param [in] attrs The attributes of the SID.
+ * @returns true if the array contains the SID.
+ */
+bool sids_contains_sid_attrs(const struct auth_SidAttr *sids,
+ const uint32_t num_sids,
+ const struct dom_sid *sid,
+ uint32_t attrs)
+{
+ uint32_t i;
+
+ for (i = 0; i < num_sids; i++) {
+ if (attrs != sids[i].attrs) {
+ continue;
+ }
+ if (!dom_sid_equal(&sids[i].sid, sid)) {
+ continue;
+ }
+
+ return true;
+ }
+ return false;
+}
+
+/*
+ * See [MS-LSAT] 3.1.1.1.1 Predefined Translation Database and Corresponding View
+ */
+struct predefined_name_mapping {
+ const char *name;
+ enum lsa_SidType type;
+ struct dom_sid sid;
+};
+
+struct predefined_domain_mapping {
+ const char *domain;
+ struct dom_sid sid;
+ size_t num_names;
+ const struct predefined_name_mapping *names;
+};
+
+/* S-1-${AUTHORITY} */
+#define _SID0(authority) \
+ { 1, 0, {0,0,0,0,0,authority}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}}
+/* S-1-${AUTHORITY}-${SUB1} */
+#define _SID1(authority,sub1) \
+ { 1, 1, {0,0,0,0,0,authority}, {sub1,0,0,0,0,0,0,0,0,0,0,0,0,0,0}}
+/* S-1-${AUTHORITY}-${SUB1}-${SUB2} */
+#define _SID2(authority,sub1,sub2) \
+ { 1, 2, {0,0,0,0,0,authority}, {sub1,sub2,0,0,0,0,0,0,0,0,0,0,0,0,0}}
+
+/*
+ * S-1-0
+ */
+static const struct predefined_name_mapping predefined_names_S_1_0[] = {
+ {
+ .name = "NULL SID",
+ .type = SID_NAME_WKN_GRP,
+ .sid = _SID1(0, 0), /* S-1-0-0 */
+ },
+};
+
+/*
+ * S-1-1
+ */
+static const struct predefined_name_mapping predefined_names_S_1_1[] = {
+ {
+ .name = "Everyone",
+ .type = SID_NAME_WKN_GRP,
+ .sid = _SID1(1, 0), /* S-1-1-0 */
+ },
+};
+
+/*
+ * S-1-2
+ */
+static const struct predefined_name_mapping predefined_names_S_1_2[] = {
+ {
+ .name = "LOCAL",
+ .type = SID_NAME_WKN_GRP,
+ .sid = _SID1(2, 0), /* S-1-2-0 */
+ },
+};
+
+/*
+ * S-1-3
+ */
+static const struct predefined_name_mapping predefined_names_S_1_3[] = {
+ {
+ .name = "CREATOR OWNER",
+ .type = SID_NAME_WKN_GRP,
+ .sid = _SID1(3, 0), /* S-1-3-0 */
+ },
+ {
+ .name = "CREATOR GROUP",
+ .type = SID_NAME_WKN_GRP,
+ .sid = _SID1(3, 1), /* S-1-3-1 */
+ },
+ {
+ .name = "CREATOR OWNER SERVER",
+ .type = SID_NAME_WKN_GRP,
+ .sid = _SID1(3, 0), /* S-1-3-2 */
+ },
+ {
+ .name = "CREATOR GROUP SERVER",
+ .type = SID_NAME_WKN_GRP,
+ .sid = _SID1(3, 1), /* S-1-3-3 */
+ },
+ {
+ .name = "OWNER RIGHTS",
+ .type = SID_NAME_WKN_GRP,
+ .sid = _SID1(3, 4), /* S-1-3-4 */
+ },
+};
+
+/*
+ * S-1-5 only 'NT Pseudo Domain'
+ */
+static const struct predefined_name_mapping predefined_names_S_1_5p[] = {
+ {
+ .name = "NT Pseudo Domain",
+ .type = SID_NAME_DOMAIN,
+ .sid = _SID0(5), /* S-1-5 */
+ },
+};
+
+/*
+ * S-1-5 'NT AUTHORITY'
+ */
+static const struct predefined_name_mapping predefined_names_S_1_5a[] = {
+ {
+ .name = "DIALUP",
+ .type = SID_NAME_WKN_GRP,
+ .sid = _SID1(5, 1), /* S-1-5-1 */
+ },
+ {
+ .name = "NETWORK",
+ .type = SID_NAME_WKN_GRP,
+ .sid = _SID1(5, 2), /* S-1-5-2 */
+ },
+ {
+ .name = "BATCH",
+ .type = SID_NAME_WKN_GRP,
+ .sid = _SID1(5, 3), /* S-1-5-3 */
+ },
+ {
+ .name = "INTERACTIVE",
+ .type = SID_NAME_WKN_GRP,
+ .sid = _SID1(5, 4), /* S-1-5-4 */
+ },
+ {
+ .name = "SERVICE",
+ .type = SID_NAME_WKN_GRP,
+ .sid = _SID1(5, 6), /* S-1-5-6 */
+ },
+ {
+ .name = "ANONYMOUS LOGON",
+ .type = SID_NAME_WKN_GRP,
+ .sid = _SID1(5, 7), /* S-1-5-7 */
+ },
+ {
+ .name = "PROXY",
+ .type = SID_NAME_WKN_GRP,
+ .sid = _SID1(5, 8), /* S-1-5-8 */
+ },
+ {
+ .name = "ENTERPRISE DOMAIN CONTROLLERS",
+ .type = SID_NAME_WKN_GRP,
+ .sid = _SID1(5, 9), /* S-1-5-9 */
+ },
+ {
+ .name = "SELF",
+ .type = SID_NAME_WKN_GRP,
+ .sid = _SID1(5, 10), /* S-1-5-10 */
+ },
+ {
+ .name = "Authenticated Users",
+ .type = SID_NAME_WKN_GRP,
+ .sid = _SID1(5, 11), /* S-1-5-11 */
+ },
+ {
+ .name = "RESTRICTED",
+ .type = SID_NAME_WKN_GRP,
+ .sid = _SID1(5, 12), /* S-1-5-12 */
+ },
+ {
+ .name = "TERMINAL SERVER USER",
+ .type = SID_NAME_WKN_GRP,
+ .sid = _SID1(5, 13), /* S-1-5-13 */
+ },
+ {
+ .name = "REMOTE INTERACTIVE LOGON",
+ .type = SID_NAME_WKN_GRP,
+ .sid = _SID1(5, 14), /* S-1-5-14 */
+ },
+ {
+ .name = "This Organization",
+ .type = SID_NAME_WKN_GRP,
+ .sid = _SID1(5, 15), /* S-1-5-15 */
+ },
+ {
+ .name = "IUSR",
+ .type = SID_NAME_WKN_GRP,
+ .sid = _SID1(5, 17), /* S-1-5-17 */
+ },
+ {
+ .name = "SYSTEM",
+ .type = SID_NAME_WKN_GRP,
+ .sid = _SID1(5, 18), /* S-1-5-18 */
+ },
+ {
+ .name = "LOCAL SERVICE",
+ .type = SID_NAME_WKN_GRP,
+ .sid = _SID1(5, 19), /* S-1-5-19 */
+ },
+ {
+ .name = "NETWORK SERVICE",
+ .type = SID_NAME_WKN_GRP,
+ .sid = _SID1(5, 20), /* S-1-5-20 */
+ },
+ {
+ .name = "WRITE RESTRICTED",
+ .type = SID_NAME_WKN_GRP,
+ .sid = _SID1(5, 33), /* S-1-5-33 */
+ },
+ {
+ .name = "Other Organization",
+ .type = SID_NAME_WKN_GRP,
+ .sid = _SID1(5, 1000), /* S-1-5-1000 */
+ },
+};
+
+/*
+ * S-1-5-32
+ */
+static const struct predefined_name_mapping predefined_names_S_1_5_32[] = {
+ {
+ .name = "BUILTIN",
+ .type = SID_NAME_DOMAIN,
+ .sid = _SID1(5, 32), /* S-1-5-32 */
+ },
+};
+
+/*
+ * S-1-5-64
+ */
+static const struct predefined_name_mapping predefined_names_S_1_5_64[] = {
+ {
+ .name = "NTLM Authentication",
+ .type = SID_NAME_WKN_GRP,
+ .sid = _SID2(5, 64, 10), /* S-1-5-64-10 */
+ },
+ {
+ .name = "SChannel Authentication",
+ .type = SID_NAME_WKN_GRP,
+ .sid = _SID2(5, 64, 14), /* S-1-5-64-14 */
+ },
+ {
+ .name = "Digest Authentication",
+ .type = SID_NAME_WKN_GRP,
+ .sid = _SID2(5, 64, 21), /* S-1-5-64-21 */
+ },
+};
+
+/*
+ * S-1-7
+ */
+static const struct predefined_name_mapping predefined_names_S_1_7[] = {
+ {
+ .name = "Internet$",
+ .type = SID_NAME_DOMAIN,
+ .sid = _SID0(7), /* S-1-7 */
+ },
+};
+
+/*
+ * S-1-16
+ */
+static const struct predefined_name_mapping predefined_names_S_1_16[] = {
+ {
+ .name = "Mandatory Label",
+ .type = SID_NAME_DOMAIN,
+ .sid = _SID0(16), /* S-1-16 */
+ },
+ {
+ .name = "Untrusted Mandatory Level",
+ .type = SID_NAME_LABEL,
+ .sid = _SID1(16, 0), /* S-1-16-0 */
+ },
+ {
+ .name = "Low Mandatory Level",
+ .type = SID_NAME_LABEL,
+ .sid = _SID1(16, 4096), /* S-1-16-4096 */
+ },
+ {
+ .name = "Medium Mandatory Level",
+ .type = SID_NAME_LABEL,
+ .sid = _SID1(16, 8192), /* S-1-16-8192 */
+ },
+ {
+ .name = "High Mandatory Level",
+ .type = SID_NAME_LABEL,
+ .sid = _SID1(16, 12288), /* S-1-16-12288 */
+ },
+ {
+ .name = "System Mandatory Level",
+ .type = SID_NAME_LABEL,
+ .sid = _SID1(16, 16384), /* S-1-16-16384 */
+ },
+ {
+ .name = "Protected Process Mandatory Level",
+ .type = SID_NAME_LABEL,
+ .sid = _SID1(16, 20480), /* S-1-16-20480 */
+ },
+};
+
+static const struct predefined_domain_mapping predefined_domains[] = {
+ {
+ .domain = "",
+ .sid = _SID0(0), /* S-1-0 */
+ .num_names = ARRAY_SIZE(predefined_names_S_1_0),
+ .names = predefined_names_S_1_0,
+ },
+ {
+ .domain = "",
+ .sid = _SID0(1), /* S-1-1 */
+ .num_names = ARRAY_SIZE(predefined_names_S_1_1),
+ .names = predefined_names_S_1_1,
+ },
+ {
+ .domain = "",
+ .sid = _SID0(2), /* S-1-2 */
+ .num_names = ARRAY_SIZE(predefined_names_S_1_2),
+ .names = predefined_names_S_1_2,
+ },
+ {
+ .domain = "",
+ .sid = _SID0(3), /* S-1-3 */
+ .num_names = ARRAY_SIZE(predefined_names_S_1_3),
+ .names = predefined_names_S_1_3,
+ },
+ {
+ .domain = "",
+ .sid = _SID0(3), /* S-1-3 */
+ .num_names = ARRAY_SIZE(predefined_names_S_1_3),
+ .names = predefined_names_S_1_3,
+ },
+ /*
+ * S-1-5 is split here
+ *
+ * 'NT Pseudo Domain' has precedence before 'NT AUTHORITY'.
+ *
+ * In a LookupSids with multiple sids e.g. S-1-5 and S-1-5-7
+ * the domain section (struct lsa_DomainInfo) gets
+ * 'NT Pseudo Domain' with S-1-5. If asked in reversed order
+ * S-1-5-7 and then S-1-5, you get struct lsa_DomainInfo
+ * with 'NT AUTHORITY' and S-1-5.
+ */
+ {
+ .domain = "NT Pseudo Domain",
+ .sid = _SID0(5), /* S-1-5 */
+ .num_names = ARRAY_SIZE(predefined_names_S_1_5p),
+ .names = predefined_names_S_1_5p,
+ },
+ {
+ .domain = "NT AUTHORITY",
+ .sid = _SID0(5), /* S-1-5 */
+ .num_names = ARRAY_SIZE(predefined_names_S_1_5a),
+ .names = predefined_names_S_1_5a,
+ },
+ {
+ .domain = "BUILTIN",
+ .sid = _SID1(5, 32), /* S-1-5-32 */
+ .num_names = ARRAY_SIZE(predefined_names_S_1_5_32),
+ .names = predefined_names_S_1_5_32,
+ },
+ /*
+ * 'NT AUTHORITY' again with S-1-5-64 this time
+ */
+ {
+ .domain = "NT AUTHORITY",
+ .sid = _SID1(5, 64), /* S-1-5-64 */
+ .num_names = ARRAY_SIZE(predefined_names_S_1_5_64),
+ .names = predefined_names_S_1_5_64,
+ },
+ {
+ .domain = "Internet$",
+ .sid = _SID0(7), /* S-1-7 */
+ .num_names = ARRAY_SIZE(predefined_names_S_1_7),
+ .names = predefined_names_S_1_7,
+ },
+ {
+ .domain = "Mandatory Label",
+ .sid = _SID0(16), /* S-1-16 */
+ .num_names = ARRAY_SIZE(predefined_names_S_1_16),
+ .names = predefined_names_S_1_16,
+ },
+};
+
+NTSTATUS dom_sid_lookup_predefined_name(const char *name,
+ const struct dom_sid **sid,
+ enum lsa_SidType *type,
+ const struct dom_sid **authority_sid,
+ const char **authority_name)
+{
+ size_t di;
+ const char *domain = "";
+ size_t domain_len = 0;
+ const char *p;
+ bool match;
+
+ *sid = NULL;
+ *type = SID_NAME_UNKNOWN;
+ *authority_sid = NULL;
+ *authority_name = NULL;
+
+ if (name == NULL) {
+ name = "";
+ }
+
+ p = strchr(name, '\\');
+ if (p != NULL) {
+ domain = name;
+ domain_len = PTR_DIFF(p, domain);
+ name = p + 1;
+ }
+
+ match = strequal(name, "");
+ if (match) {
+ /*
+ * Strange, but that's what W2012R2 does.
+ */
+ name = "BUILTIN";
+ }
+
+ for (di = 0; di < ARRAY_SIZE(predefined_domains); di++) {
+ const struct predefined_domain_mapping *d =
+ &predefined_domains[di];
+ size_t ni;
+
+ if (domain_len != 0) {
+ int cmp;
+
+ cmp = strncasecmp(d->domain, domain, domain_len);
+ if (cmp != 0) {
+ continue;
+ }
+ }
+
+ for (ni = 0; ni < d->num_names; ni++) {
+ const struct predefined_name_mapping *n =
+ &d->names[ni];
+
+ match = strequal(n->name, name);
+ if (!match) {
+ continue;
+ }
+
+ *sid = &n->sid;
+ *type = n->type;
+ *authority_sid = &d->sid;
+ *authority_name = d->domain;
+ return NT_STATUS_OK;
+ }
+ }
+
+ return NT_STATUS_NONE_MAPPED;
+}
+
+bool dom_sid_lookup_is_predefined_domain(const char *domain)
+{
+ size_t di;
+ bool match;
+
+ if (domain == NULL) {
+ domain = "";
+ }
+
+ match = strequal(domain, "");
+ if (match) {
+ /*
+ * Strange, but that's what W2012R2 does.
+ */
+ domain = "BUILTIN";
+ }
+
+ for (di = 0; di < ARRAY_SIZE(predefined_domains); di++) {
+ const struct predefined_domain_mapping *d =
+ &predefined_domains[di];
+ int cmp;
+
+ cmp = strcasecmp(d->domain, domain);
+ if (cmp != 0) {
+ continue;
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+NTSTATUS dom_sid_lookup_predefined_sid(const struct dom_sid *sid,
+ const char **name,
+ enum lsa_SidType *type,
+ const struct dom_sid **authority_sid,
+ const char **authority_name)
+{
+ size_t di;
+ bool match_domain = false;
+
+ *name = NULL;
+ *type = SID_NAME_UNKNOWN;
+ *authority_sid = NULL;
+ *authority_name = NULL;
+
+ if (sid == NULL) {
+ return NT_STATUS_INVALID_SID;
+ }
+
+ for (di = 0; di < ARRAY_SIZE(predefined_domains); di++) {
+ const struct predefined_domain_mapping *d =
+ &predefined_domains[di];
+ size_t ni;
+ int cmp;
+
+ cmp = dom_sid_compare_auth(&d->sid, sid);
+ if (cmp != 0) {
+ continue;
+ }
+
+ match_domain = true;
+
+ for (ni = 0; ni < d->num_names; ni++) {
+ const struct predefined_name_mapping *n =
+ &d->names[ni];
+
+ cmp = dom_sid_compare(&n->sid, sid);
+ if (cmp != 0) {
+ continue;
+ }
+
+ *name = n->name;
+ *type = n->type;
+ *authority_sid = &d->sid;
+ *authority_name = d->domain;
+ return NT_STATUS_OK;
+ }
+ }
+
+ if (!match_domain) {
+ return NT_STATUS_INVALID_SID;
+ }
+
+ return NT_STATUS_NONE_MAPPED;
+}
diff --git a/libcli/security/wscript_build b/libcli/security/wscript_build
new file mode 100644
index 0000000..db8a9b9
--- /dev/null
+++ b/libcli/security/wscript_build
@@ -0,0 +1,64 @@
+#!/usr/bin/env python
+
+
+bld.SAMBA_LIBRARY('samba-security',
+ source=['dom_sid.c',
+ 'display_sec.c', 'secace.c', 'secacl.c',
+ 'security_descriptor.c', 'sddl.c', 'privileges.c',
+ 'security_token.c', 'access_check.c',
+ 'object_tree.c', 'create_descriptor.c',
+ 'util_sid.c', 'session.c', 'secdesc.c',
+ 'conditional_ace.c', 'sddl_conditional_ace.c',
+ 'claims-conversions.c'],
+ private_library=True,
+ deps='stable_sort talloc ndr NDR_SECURITY NDR_CONDITIONAL_ACE')
+
+pytalloc_util = bld.pyembed_libname('pytalloc-util')
+bld.SAMBA_PYTHON('pysecurity',
+ source='pysecurity.c',
+ deps='samba-security %s' % pytalloc_util,
+ realname='samba/security.so'
+ )
+
+bld.SAMBA_BINARY(
+ 'test_sddl_conditional_ace',
+ source='tests/test_sddl_conditional_ace.c',
+ deps='''
+ cmocka
+ talloc
+ samba-util
+ asn1util
+ NDR_SECURITY
+ samba-security
+ ''',
+ for_selftest=True
+)
+
+bld.SAMBA_BINARY(
+ 'test_run_conditional_ace',
+ source='tests/test_run_conditional_ace.c',
+ deps='''
+ cmocka
+ talloc
+ samba-util
+ asn1util
+ NDR_SECURITY
+ samba-security
+ ''',
+ for_selftest=True
+)
+
+bld.SAMBA_BINARY(
+ 'test_claim_conversion',
+ source='tests/test_claim_conversion.c',
+ deps='''
+ cmocka
+ talloc
+ samba-util
+ asn1util
+ NDR_SECURITY
+ NDR_CLAIMS
+ samba-security
+ ''',
+ for_selftest=True
+)