diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 17:20:00 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 17:20:00 +0000 |
commit | 8daa83a594a2e98f39d764422bfbdbc62c9efd44 (patch) | |
tree | 4099e8021376c7d8c05bdf8503093d80e9c7bad0 /libcli/security | |
parent | Initial commit. (diff) | |
download | samba-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')
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, + <ok[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 Binary files differnew file mode 100644 index 0000000..c0de4da --- /dev/null +++ b/libcli/security/tests/data/ndr_dumps/fileb5iJt4 diff --git a/libcli/security/tests/data/ndr_dumps/fileb8cNVS b/libcli/security/tests/data/ndr_dumps/fileb8cNVS Binary files differnew file mode 100644 index 0000000..bee598e --- /dev/null +++ b/libcli/security/tests/data/ndr_dumps/fileb8cNVS diff --git a/libcli/security/tests/data/ndr_dumps/filebI7h5H b/libcli/security/tests/data/ndr_dumps/filebI7h5H Binary files differnew file mode 100644 index 0000000..c98fe38 --- /dev/null +++ b/libcli/security/tests/data/ndr_dumps/filebI7h5H diff --git a/libcli/security/tests/data/ndr_dumps/filebNdBgt b/libcli/security/tests/data/ndr_dumps/filebNdBgt Binary files differnew file mode 100644 index 0000000..62e37ae --- /dev/null +++ b/libcli/security/tests/data/ndr_dumps/filebNdBgt diff --git a/libcli/security/tests/data/ndr_dumps/filebOjK4H b/libcli/security/tests/data/ndr_dumps/filebOjK4H Binary files differnew file mode 100644 index 0000000..9a040c1 --- /dev/null +++ b/libcli/security/tests/data/ndr_dumps/filebOjK4H diff --git a/libcli/security/tests/data/ndr_dumps/filebzCPTH b/libcli/security/tests/data/ndr_dumps/filebzCPTH Binary files differnew file mode 100644 index 0000000..ba52884 --- /dev/null +++ b/libcli/security/tests/data/ndr_dumps/filebzCPTH 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 Binary files differnew file mode 100644 index 0000000..e7f8024 --- /dev/null +++ b/libcli/security/tests/data/short-conditional-and-resource-aces-successes.json.gz 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 Binary files differnew file mode 100644 index 0000000..e1b6157 --- /dev/null +++ b/libcli/security/tests/data/short-conditional-and-resource-aces-tx-int.json.gz diff --git a/libcli/security/tests/data/short-ordinary-acls-v2.json.gz b/libcli/security/tests/data/short-ordinary-acls-v2.json.gz Binary files differnew file mode 100644 index 0000000..1f4ef20 --- /dev/null +++ b/libcli/security/tests/data/short-ordinary-acls-v2.json.gz diff --git a/libcli/security/tests/data/short-ordinary-acls.json.gz b/libcli/security/tests/data/short-ordinary-acls.json.gz Binary files differnew file mode 100644 index 0000000..8554b7c --- /dev/null +++ b/libcli/security/tests/data/short-ordinary-acls.json.gz 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 +) |