diff options
Diffstat (limited to '')
29 files changed, 7321 insertions, 0 deletions
diff --git a/libcli/security/access_check.c b/libcli/security/access_check.c new file mode 100644 index 0000000..7dd3798 --- /dev/null +++ b/libcli/security/access_check.c @@ -0,0 +1,615 @@ +/* + 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 "includes.h" +#include "libcli/security/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) +{ + 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)); + } +} + +/* + 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) +{ + 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)) { + granted |= SEC_STD_WRITE_DAC | SEC_STD_READ_CONTROL; + } + 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) { + granted |= SEC_STD_WRITE_DAC|SEC_STD_READ_CONTROL; + } + + 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; + default: /* Other ACE types not handled/supported */ + break; + } + } + + return granted & ~denied; +} + +/* + 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) +{ + uint32_t i; + uint32_t bits_remaining; + uint32_t explicitly_denied_bits = 0; + bool am_owner = false; + bool have_owner_rights_ace = false; + + *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); + 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) { + bits_remaining &= ~(SEC_STD_WRITE_DAC | SEC_STD_READ_CONTROL); + } + + /* 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; + 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 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(sd, + token, + access_desired, + access_granted); + } + + /* + * 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); + 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(sd, + token, + access_desired, + access_granted); + + 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) { + + /* 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; +} + +/** + * @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 descritor 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, + const struct dom_sid *replace_sid) +{ + uint32_t i; + uint32_t bits_remaining; + struct dom_sid self_sid; + + dom_sid_parse(SID_NT_SELF, &self_sid); + + *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); + 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)) { + bits_remaining &= ~(SEC_STD_WRITE_DAC|SEC_STD_READ_CONTROL); + } + + /* 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, &self_sid) && 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_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; + default: /* Other ACE types not handled/supported */ + break; + } + } + +done: + if (bits_remaining != 0) { + return NT_STATUS_ACCESS_DENIED; + } + + return NT_STATUS_OK; +} diff --git a/libcli/security/access_check.h b/libcli/security/access_check.h new file mode 100644 index 0000000..37ca078 --- /dev/null +++ b/libcli/security/access_check.h @@ -0,0 +1,92 @@ +/* + 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); + +/* 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, + const 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/create_descriptor.c b/libcli/security/create_descriptor.c new file mode 100644 index 0000000..947d6c1 --- /dev/null +++ b/libcli/security/create_descriptor.c @@ -0,0 +1,631 @@ +/* + 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 "includes.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; + TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx); + struct security_acl *tmp_acl = talloc_zero(mem_ctx, struct security_acl); + if (!tmp_acl) { + return NULL; + } + + if (!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: + 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; + } + + 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_ctx); + 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 downgrate 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; + } + } + + 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_ctx); + 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) { + 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..d75b890 --- /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 "includes.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; + case SEC_ACE_TYPE_ALLOWED_COMPOUND: + printf("SEC_ACE_TYPE_ALLOWED_COMPOUND"); + break; + case SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT: + printf("SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT"); + break; + case SEC_ACE_TYPE_ACCESS_DENIED_OBJECT: + printf("SEC_ACE_TYPE_ACCESS_DENIED_OBJECT"); + break; + case SEC_ACE_TYPE_SYSTEM_AUDIT_OBJECT: + printf("SEC_ACE_TYPE_SYSTEM_AUDIT_OBJECT"); + break; + case SEC_ACE_TYPE_SYSTEM_ALARM_OBJECT: + printf("SEC_ACE_TYPE_SYSTEM_ALARM_OBJECT"); + break; + 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..32bc3e1 --- /dev/null +++ b/libcli/security/dom_sid.c @@ -0,0 +1,520 @@ +/* + 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 sid1->sub_auths[i] - sid2->sub_auths[i]; + + 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 sid1->sub_auths[i] - sid2->sub_auths[i]; + + 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; + /* BIG NOTE: this function only does SIDS where the identauth is not >= 2^32 */ + uint64_t conv; + int error = 0; + + ZERO_STRUCTP(sidout); + + if ((sidstr[0] != 'S' && sidstr[0] != 's') || sidstr[1] != '-') { + goto format_error; + } + + /* Get the revision number. */ + p = sidstr + 2; + + if (!isdigit(*p)) { + goto format_error; + } + + conv = smb_strtoul(p, &q, 10, &error, SMB_STR_STANDARD); + if (error != 0 || (*q != '-') || conv > UINT8_MAX) { + goto format_error; + } + sidout->sid_rev_num = (uint8_t) conv; + q++; + + if (!isdigit(*q)) { + goto format_error; + } + + /* get identauth */ + conv = smb_strtoull(q, &q, 0, &error, SMB_STR_STANDARD); + if (conv & AUTHORITY_MASK || error != 0) { + goto format_error; + } + + /* When identauth >= UINT32_MAX, it's in hex with a leading 0x */ + /* 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; + if (*q != '-') { + /* Just id_auth, no subauths */ + goto done; + } + + q++; + + while (true) { + char *end; + + if (!isdigit(*q)) { + goto format_error; + } + + conv = smb_strtoull(q, &end, 10, &error, SMB_STR_STANDARD); + if (conv > UINT32_MAX || error != 0) { + 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; + int i; + + if (!dom_sid) { + return NULL; + } + + ret = talloc(mem_ctx, struct dom_sid); + if (!ret) { + return NULL; + } + + ret->sid_rev_num = dom_sid->sid_rev_num; + ret->id_auth[0] = dom_sid->id_auth[0]; + ret->id_auth[1] = dom_sid->id_auth[1]; + ret->id_auth[2] = dom_sid->id_auth[2]; + ret->id_auth[3] = dom_sid->id_auth[3]; + ret->id_auth[4] = dom_sid->id_auth[4]; + ret->id_auth[5] = dom_sid->id_auth[5]; + ret->num_auths = dom_sid->num_auths; + + for (i=0;i<dom_sid->num_auths;i++) { + ret->sub_auths[i] = dom_sid->sub_auths[i]; + } + + 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_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..c362fa6 --- /dev/null +++ b/libcli/security/dom_sid.h @@ -0,0 +1,136 @@ +/* + 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_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_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 + +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_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); +int sid_compare_domain(const struct dom_sid *sid1, const struct dom_sid *sid2); +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); +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); + +#endif /*_DOM_SID_H_*/ + diff --git a/libcli/security/object_tree.c b/libcli/security/object_tree.c new file mode 100644 index 0000000..fd00068 --- /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 concidered later to improve performance + * + * Author: Nadezhda Ivanova + */ +#include "includes.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..7910001 --- /dev/null +++ b/libcli/security/privileges.c @@ -0,0 +1,487 @@ +/* + 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' privlege 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 privilage 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; +} + +void security_token_debug_privileges(int dbg_class, int dbg_lev, const struct security_token *token) +{ + DEBUGADDC(dbg_class, dbg_lev, (" Privileges (0x%16llX):\n", + (unsigned long long) token->privilege_mask)); + + if (token->privilege_mask) { + size_t idx = 0; + int i = 0; + for (idx = 0; idx<ARRAY_SIZE(privs); idx++) { + if (token->privilege_mask & privs[idx].privilege_mask) { + DEBUGADDC(dbg_class, dbg_lev, + (" Privilege[%3lu]: %s\n", (unsigned long)i++, + privs[idx].name)); + } + } + } + DEBUGADDC(dbg_class, dbg_lev, (" Rights (0x%16lX):\n", + (unsigned long) token->rights_mask)); + + if (token->rights_mask) { + size_t idx = 0; + int i = 0; + for (idx = 0; idx<ARRAY_SIZE(rights); idx++) { + if (token->rights_mask & rights[idx].right_mask) { + DEBUGADDC(dbg_class, dbg_lev, + (" Right[%3lu]: %s\n", (unsigned long)i++, + rights[idx].name)); + } + } + } +} diff --git a/libcli/security/privileges.h b/libcli/security/privileges.h new file mode 100644 index 0000000..c8a8dad --- /dev/null +++ b/libcli/security/privileges.h @@ -0,0 +1,115 @@ +/* + 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' privlege 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); + +void security_token_debug_privileges(int dbg_class, int dbg_lev, 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..8073048 --- /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 <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 = Py_None; + PyObject *py_security_token = Py_None; + 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..5bb65dd --- /dev/null +++ b/libcli/security/sddl.c @@ -0,0 +1,736 @@ +/* + 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 "includes.h" +#include "libcli/security/security.h" +#include "librpc/gen_ndr/ndr_misc.h" +#include "system/locale.h" + +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) +{ + const char *str0 = str; + if (plen != NULL) { + *plen = 0; + } + *pflags = 0; + while (str[0] && isupper(str[0])) { + size_t len; + uint32_t flags; + bool found; + + found = sddl_map_flag(map, str, &len, &flags); + if (!found) { + DEBUG(1, ("Unknown flag - %s in %s\n", str, str0)); + return false; + } + + *pflags |= flags; + if (plen != NULL) { + *plen += len; + } + str += len; + } + return true; +} + +/* + a mapping between the 2 letter SID codes and sid strings +*/ +static const struct { + const char *code; + const char *sid; + uint32_t 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", .sid = NULL, .rid = DOMAIN_RID_ENTERPRISE_READONLY_DCS }, + + { .code = "LA", .sid = NULL, .rid = DOMAIN_RID_ADMINISTRATOR }, + { .code = "LG", .sid = NULL, .rid = DOMAIN_RID_GUEST }, + + { .code = "DA", .sid = NULL, .rid = DOMAIN_RID_ADMINS }, + { .code = "DU", .sid = NULL, .rid = DOMAIN_RID_USERS }, + { .code = "DG", .sid = NULL, .rid = DOMAIN_RID_GUESTS }, + { .code = "DC", .sid = NULL, .rid = DOMAIN_RID_DOMAIN_MEMBERS }, + { .code = "DD", .sid = NULL, .rid = DOMAIN_RID_DCS }, + { .code = "CA", .sid = NULL, .rid = DOMAIN_RID_CERT_ADMINS }, + { .code = "SA", .sid = NULL, .rid = DOMAIN_RID_SCHEMA_ADMINS }, + { .code = "EA", .sid = NULL, .rid = DOMAIN_RID_ENTERPRISE_ADMINS }, + { .code = "PA", .sid = NULL, .rid = DOMAIN_RID_POLICY_ADMINS }, + + { .code = "CN", .sid = NULL, .rid = DOMAIN_RID_CLONEABLE_CONTROLLERS }, + + { .code = "AP", .sid = NULL, .rid = DOMAIN_RID_PROTECTED_USERS }, + { .code = "KA", .sid = NULL, .rid = DOMAIN_RID_KEY_ADMINS }, + { .code = "EK", .sid = NULL, .rid = DOMAIN_RID_ENTERPRISE_KEY_ADMINS }, + + { .code = "RS", .sid = NULL, .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_decode_sid(TALLOC_CTX *mem_ctx, const char **sddlp, + const struct dom_sid *domain_sid) +{ + const char *sddl = (*sddlp); + size_t i; + + /* see if its in the numeric format */ + if (strncmp(sddl, "S-", 2) == 0) { + struct dom_sid *sid; + char *sid_str; + size_t len = strspn(sddl+2, "-0123456789"); + sid_str = talloc_strndup(mem_ctx, sddl, len+2); + if (!sid_str) { + return NULL; + } + (*sddlp) += len+2; + sid = dom_sid_parse_talloc(mem_ctx, sid_str); + talloc_free(sid_str); + 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].sid == NULL) { + return dom_sid_add_rid(mem_ctx, domain_sid, sid_codes[i].rid); + } + + return dom_sid_parse_talloc(mem_ctx, sid_codes[i].sid); +} + +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 }, + { 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[] = { + { "RP", SEC_ADS_READ_PROP }, + { "WP", SEC_ADS_WRITE_PROP }, + { "CR", SEC_ADS_CONTROL_ACCESS }, + { "CC", SEC_ADS_CREATE_CHILD }, + { "DC", SEC_ADS_DELETE_CHILD }, + { "LC", SEC_ADS_LIST }, + { "LO", SEC_ADS_LIST_OBJECT }, + { "RC", SEC_STD_READ_CONTROL }, + { "WO", SEC_STD_WRITE_OWNER }, + { "WD", SEC_STD_WRITE_DAC }, + { "SD", SEC_STD_DELETE }, + { "DT", SEC_ADS_DELETE_TREE }, + { "SW", SEC_ADS_SELF_WRITE }, + { "GA", SEC_GENERIC_ALL }, + { "GR", SEC_GENERIC_READ }, + { "GW", SEC_GENERIC_WRITE }, + { "GX", SEC_GENERIC_EXECUTE }, + { NULL, 0 } +}; + +static const struct flag_map decode_ace_access_mask[] = { + { "FA", FILE_ALL_ACCESS }, + { "FR", FILE_GENERIC_READ }, + { "FW", FILE_GENERIC_WRITE }, + { "FX", FILE_GENERIC_EXECUTE }, + { NULL, 0 }, +}; + +static bool sddl_decode_access(const char *str, uint32_t *pmask) +{ + const char *str0 = str; + uint32_t mask = 0; + int cmp; + + cmp = strncmp(str, "0x", 2); + if (cmp == 0) { + *pmask = strtol(str, NULL, 16); + return true; + } + + while ((str[0] != '\0') && isupper(str[0])) { + uint32_t flags = 0; + size_t len = 0; + bool found; + + 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; + } + + *pmask = mask; + return true; +} + +/* + 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, char *str, + const struct dom_sid *domain_sid) +{ + const char *tok[6]; + const char *s; + int i; + uint32_t v; + struct dom_sid *sid; + bool ok; + + ZERO_STRUCTP(ace); + + /* parse out the 6 tokens */ + tok[0] = str; + for (i=0;i<5;i++) { + char *ptr = strchr(str, ';'); + if (ptr == NULL) return false; + *ptr = 0; + str = ptr+1; + tok[i+1] = str; + } + + /* parse ace type */ + if (!sddl_map_flags(ace_types, tok[0], &v, NULL)) { + return false; + } + ace->type = v; + + /* ace flags */ + if (!sddl_map_flags(ace_flags, tok[1], &v, NULL)) { + return false; + } + ace->flags = v; + + /* access mask */ + ok = sddl_decode_access(tok[2], &ace->access_mask); + if (!ok) { + return false; + } + + /* object */ + if (tok[3][0] != 0) { + NTSTATUS status = GUID_from_string(tok[3], + &ace->object.object.type.type); + if (!NT_STATUS_IS_OK(status)) { + return false; + } + ace->object.object.flags |= SEC_ACE_OBJECT_TYPE_PRESENT; + } + + /* inherit object */ + if (tok[4][0] != 0) { + NTSTATUS status = GUID_from_string(tok[4], + &ace->object.object.inherited_type.inherited_type); + if (!NT_STATUS_IS_OK(status)) { + return false; + } + ace->object.object.flags |= SEC_ACE_INHERITED_OBJECT_TYPE_PRESENT; + } + + /* trustee */ + s = tok[5]; + sid = sddl_decode_sid(mem_ctx, &s, domain_sid); + if (sid == NULL) { + return false; + } + ace->trustee = *sid; + talloc_free(sid); + + 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 char **sddlp, uint32_t *flags, + const struct dom_sid *domain_sid) +{ + const char *sddl = *sddlp; + 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(sddl[0]) && sddl[1] == ':') { + /* its an empty ACL */ + return acl; + } + + /* work out the ACL flags */ + if (!sddl_map_flags(acl_flags, sddl, flags, &len)) { + talloc_free(acl); + return NULL; + } + sddl += len; + + /* now the ACEs */ + while (*sddl == '(') { + char *astr; + len = strcspn(sddl+1, ")"); + astr = talloc_strndup(acl, sddl+1, len); + if (astr == NULL || sddl[len+1] != ')') { + 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; + } + if (!sddl_decode_ace(acl->aces, &acl->aces[acl->num_aces], + astr, domain_sid)) { + talloc_free(acl); + return NULL; + } + switch (acl->aces[acl->num_aces].type) { + case SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT: + case SEC_ACE_TYPE_ACCESS_DENIED_OBJECT: + case SEC_ACE_TYPE_SYSTEM_AUDIT_OBJECT: + case SEC_ACE_TYPE_SYSTEM_ALARM_OBJECT: + acl->revision = SECURITY_ACL_REVISION_ADS; + break; + default: + break; + } + talloc_free(astr); + sddl += len+2; + acl->num_aces++; + } + + (*sddlp) = sddl; + return acl; +} + +/* + 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) +{ + struct security_descriptor *sd; + sd = talloc_zero(mem_ctx, struct security_descriptor); + + sd->revision = SECURITY_DESCRIPTOR_REVISION_1; + sd->type = SEC_DESC_SELF_RELATIVE; + + while (*sddl) { + uint32_t flags; + char c = sddl[0]; + if (sddl[1] != ':') goto failed; + + sddl += 2; + switch (c) { + case 'D': + if (sd->dacl != NULL) goto failed; + sd->dacl = sddl_decode_acl(sd, &sddl, &flags, domain_sid); + 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, &sddl, &flags, domain_sid); + 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_decode_sid(sd, &sddl, domain_sid); + if (sd->owner_sid == NULL) goto failed; + break; + case 'G': + if (sd->group_sid != NULL) goto failed; + sd->group_sid = sddl_decode_sid(sd, &sddl, domain_sid); + if (sd->group_sid == NULL) goto failed; + break; + } + } + + return sd; + +failed: + DEBUG(2,("Badly formatted SDDL '%s'\n", sddl)); + talloc_free(sd); + return NULL; +} + +/* + 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_encode_sid(TALLOC_CTX *mem_ctx, const struct dom_sid *sid, + const struct dom_sid *domain_sid) +{ + size_t i; + char *sidstr; + + sidstr = dom_sid_string(mem_ctx, sid); + if (sidstr == NULL) return NULL; + + /* seen if its a well known sid */ + for (i=0;sid_codes[i].sid;i++) { + if (strcmp(sidstr, sid_codes[i].sid) == 0) { + talloc_free(sidstr); + return talloc_strdup(mem_ctx, sid_codes[i].code); + } + } + + /* or a well known rid in our domain */ + if (dom_sid_in_domain(domain_sid, sid)) { + uint32_t rid = sid->sub_auths[sid->num_auths-1]; + for (;i<ARRAY_SIZE(sid_codes);i++) { + if (rid == sid_codes[i].rid) { + talloc_free(sidstr); + return talloc_strdup(mem_ctx, sid_codes[i].code); + } + } + } + + talloc_free(sidstr); + + /* TODO: encode well known sids as two letter codes */ + return dom_sid_string(mem_ctx, sid); +} + + +/* + encode an ACE in SDDL format +*/ +static char *sddl_encode_ace(TALLOC_CTX *mem_ctx, const struct security_ace *ace, + const struct dom_sid *domain_sid) +{ + 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 = talloc_asprintf(tmp_ctx, "0x%08x", + ace->access_mask); + if (sddl_mask == NULL) { + goto failed; + } + } + + if (ace->type == SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT || + ace->type == SEC_ACE_TYPE_ACCESS_DENIED_OBJECT || + ace->type == SEC_ACE_TYPE_SYSTEM_AUDIT_OBJECT || + ace->type == SEC_ACE_TYPE_SYSTEM_ALARM_OBJECT) { + 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_encode_sid(tmp_ctx, &ace->trustee, domain_sid); + if (sddl_trustee == NULL) { + goto failed; + } + + 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; +} + +/* + encode an ACL in SDDL format +*/ +static char *sddl_encode_acl(TALLOC_CTX *mem_ctx, const struct security_acl *acl, + uint32_t flags, const struct dom_sid *domain_sid) +{ + 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_encode_ace(sddl, &acl->aces[i], domain_sid); + 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) +{ + 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(mem_ctx); + + if (sd->owner_sid != NULL) { + char *sid = sddl_encode_sid(tmp_ctx, sd->owner_sid, domain_sid); + 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_encode_sid(tmp_ctx, sd->group_sid, domain_sid); + 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, domain_sid); + 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, domain_sid); + 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..e8bc25a --- /dev/null +++ b/libcli/security/sddl.h @@ -0,0 +1,32 @@ +/* + 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 "librpc/gen_ndr/security.h" + +struct security_descriptor *sddl_decode(TALLOC_CTX *mem_ctx, const char *sddl, + const struct dom_sid *domain_sid); +char *sddl_encode(TALLOC_CTX *mem_ctx, const struct security_descriptor *sd, + const struct dom_sid *domain_sid); + + +#endif /* __SDDL_H__ */ diff --git a/libcli/security/secace.c b/libcli/security/secace.c new file mode 100644 index 0000000..26c366a --- /dev/null +++ b/libcli/security/secace.c @@ -0,0 +1,171 @@ +/* + * 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 "includes.h" +#include "librpc/gen_ndr/ndr_security.h" +#include "libcli/security/security.h" +#include "lib/util/tsort.h" + +#define SEC_ACE_HEADER_SIZE (2 * sizeof(uint8_t) + sizeof(uint16_t) + sizeof(uint32_t)) + +/** + * 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) { + return true; + } + return false; +} + +/** + * copy a struct security_ace structure. + */ +void sec_ace_copy(struct security_ace *ace_dest, const struct security_ace *ace_src) +{ + ace_dest->type = ace_src->type; + ace_dest->flags = ace_src->flags; + ace_dest->size = ace_src->size; + ace_dest->access_mask = ace_src->access_mask; + ace_dest->object = ace_src->object; + ace_dest->trustee = ace_src->trustee; +} + +/******************************************************************* + 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; +} + +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..16c495d --- /dev/null +++ b/libcli/security/secace.h @@ -0,0 +1,35 @@ +/* + 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" + +bool sec_ace_object(uint8_t type); +void sec_ace_copy(struct security_ace *ace_dest, const struct security_ace *ace_src); +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..a367ab6 --- /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 "includes.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..cd94430 --- /dev/null +++ b/libcli/security/secdesc.c @@ -0,0 +1,627 @@ +/* + * 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 "includes.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); + + TALLOC_CTX *frame; + + *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; + } + + frame = talloc_stackframe(); + + 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++; + } + + talloc_free(frame); + + /* + * 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..4df18eb --- /dev/null +++ b/libcli/security/security.h @@ -0,0 +1,124 @@ +/* + 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" + +#define PRIMARY_USER_SID_INDEX 0 +#define PRIMARY_GROUP_SID_INDEX 1 + +/* 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 + * childs 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..08f2cf1 --- /dev/null +++ b/libcli/security/security_descriptor.c @@ -0,0 +1,792 @@ +/* + Unix SMB/CIFS implementation. + + security descriptror 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 "includes.h" +#include "libcli/security/security.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 everthing 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++; + + switch (acl->aces[idx].type) { + case SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT: + case SEC_ACE_TYPE_ACCESS_DENIED_OBJECT: + case SEC_ACE_TYPE_SYSTEM_AUDIT_OBJECT: + case SEC_ACE_TYPE_SYSTEM_ALARM_OBJECT: + acl->revision = SECURITY_ACL_REVISION_ADS; + break; + default: + break; + } + + 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; + } + } + + if (!found) { + return NT_STATUS_OBJECT_NAME_NOT_FOUND; + } + + acl->revision = SECURITY_ACL_REVISION_NT4; + + for (i=0;i<acl->num_aces;i++) { + switch (acl->aces[i].type) { + case SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT: + case SEC_ACE_TYPE_ACCESS_DENIED_OBJECT: + case SEC_ACE_TYPE_SYSTEM_AUDIT_OBJECT: + case SEC_ACE_TYPE_SYSTEM_ALARM_OBJECT: + acl->revision = SECURITY_ACL_REVISION_ADS; + return NT_STATUS_OK; + default: + break; /* only for the switch statement */ + } + } + + 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++) { + switch (acl->aces[i].type) { + case SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT: + case SEC_ACE_TYPE_ACCESS_DENIED_OBJECT: + case SEC_ACE_TYPE_SYSTEM_AUDIT_OBJECT: + case SEC_ACE_TYPE_SYSTEM_ALARM_OBJECT: + acl->revision = SECURITY_ACL_REVISION_ADS; + return NT_STATUS_OK; + default: + break; /* only for the switch statement */ + } + } + + 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); +} +/* + 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 (!dom_sid_equal(&ace1->trustee, &ace2->trustee)) { + return false; + } + + 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..f788540 --- /dev/null +++ b/libcli/security/security_token.c @@ -0,0 +1,152 @@ +/* + 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 "includes.h" +#include "libcli/security/security_token.h" +#include "libcli/security/dom_sid.h" +#include "libcli/security/privileges.h" + +/* + return a blank security token +*/ +struct security_token *security_token_initialise(TALLOC_CTX *mem_ctx) +{ + struct security_token *st = talloc_zero( + mem_ctx, struct security_token); + return st; +} + +/**************************************************************************** + prints a struct security_token to debug output. +****************************************************************************/ +void security_token_debug(int dbg_class, int dbg_lev, const struct security_token *token) +{ + uint32_t i; + + if (!token) { + DEBUGC(dbg_class, dbg_lev, ("Security token: (NULL)\n")); + return; + } + + DEBUGC(dbg_class, dbg_lev, ("Security token SIDs (%lu):\n", + (unsigned long)token->num_sids)); + for (i = 0; i < token->num_sids; i++) { + struct dom_sid_buf sidbuf; + DEBUGADDC(dbg_class, + dbg_lev, + (" SID[%3lu]: %s\n", (unsigned long)i, + dom_sid_str_buf(&token->sids[i], &sidbuf))); + } + + security_token_debug_privileges(dbg_class, dbg_lev, token); +} + +/* These really should be cheaper... */ + +bool security_token_is_sid(const struct security_token *token, const struct dom_sid *sid) +{ + if (token->sids == NULL) { + return false; + } + if (dom_sid_equal(&token->sids[PRIMARY_USER_SID_INDEX], sid)) { + return true; + } + return false; +} + +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 ((size_t)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..c689885 --- /dev/null +++ b/libcli/security/security_token.h @@ -0,0 +1,67 @@ +/* + 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 "librpc/gen_ndr/security.h" + +#define PRIMARY_USER_SID_INDEX 0 +#define PRIMARY_GROUP_SID_INDEX 1 + +/* + return a blank security token +*/ +struct security_token *security_token_initialise(TALLOC_CTX *mem_ctx); + +/**************************************************************************** + 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..7657451 --- /dev/null +++ b/libcli/security/session.c @@ -0,0 +1,73 @@ +/* + 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 "includes.h" +#include "libcli/security/security.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/util_sid.c b/libcli/security/util_sid.c new file mode 100644 index 0000000..d7adef3 --- /dev/null +++ b/libcli/security/util_sid.c @@ -0,0 +1,982 @@ +/* + 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 "includes.h" +#include "../librpc/gen_ndr/ndr_security.h" +#include "../librpc/gen_ndr/netlogon.h" +#include "../libcli/security/security.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-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_Restriced = /* Restriced 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_Service = /* Asserted Identity Service */ +{ 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_Authentication_Authority = /* Asserted Identity Authentication Authority */ +{ 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-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. +****************************************************************************/ + +static const struct { + enum lsa_SidType sid_type; + const char *string; +} sid_name_type[] = { + {SID_NAME_USE_NONE, "None"}, + {SID_NAME_USER, "User"}, + {SID_NAME_DOM_GRP, "Domain Group"}, + {SID_NAME_DOMAIN, "Domain"}, + {SID_NAME_ALIAS, "Local Group"}, + {SID_NAME_WKN_GRP, "Well-known Group"}, + {SID_NAME_DELETED, "Deleted Account"}, + {SID_NAME_INVALID, "Invalid Account"}, + {SID_NAME_UNKNOWN, "UNKNOWN"}, + {SID_NAME_COMPUTER, "Computer"}, + {SID_NAME_LABEL, "Mandatory Label"} +}; + +const char *sid_type_lookup(uint32_t sid_type) +{ + size_t i; + + /* Look through list */ + for (i=0; i < ARRAY_SIZE(sid_name_type); i++) { + if (sid_name_type[i].sid_type == sid_type) { + return sid_name_type[i].string; + } + } + + /* 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 (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); +} + +/***************************************************************** + See if 2 SIDs are in the same domain + this just compares the leading sub-auths +*****************************************************************/ + +int 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 sid1->sub_auths[i] - sid2->sub_auths[i]; + + return dom_sid_compare_auth(sid1, sid2); +} + +/******************************************************************** + Add SID to an array 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 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) +{ + uint32_t i; + + for (i=0; i<(*num_sids); i++) { + if (dom_sid_equal(sid, &(*sids)[i])) { + return NT_STATUS_OK; + } + } + + return add_sid_to_array(mem_ctx, sid, 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] ); + } + + return; +} + +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) +{ + const struct dom_sid null_sid = {0}; + return dom_sid_equal(sid, &null_sid); +} + +/* + * 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..ebbf79a --- /dev/null +++ b/libcli/security/wscript_build @@ -0,0 +1,15 @@ +#!/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', + private_library=True, + deps='talloc ndr NDR_SECURITY' + ) + +pytalloc_util = bld.pyembed_libname('pytalloc-util') +bld.SAMBA_PYTHON('pysecurity', + source='pysecurity.c', + deps='samba-security %s' % pytalloc_util, + realname='samba/security.so' + ) |