diff options
-rw-r--r-- | libcli/security/security_descriptor.c | 50 | ||||
-rw-r--r-- | libcli/security/security_descriptor.h | 4 | ||||
-rw-r--r-- | source4/dsdb/samdb/ldb_modules/acl.c | 1151 | ||||
-rw-r--r-- | source4/dsdb/samdb/ldb_modules/config.mk | 12 | ||||
-rw-r--r-- | source4/dsdb/samdb/ldb_modules/kludge_acl.c | 13 | ||||
-rw-r--r-- | source4/dsdb/schema/schema_query.c | 12 | ||||
-rwxr-xr-x | source4/lib/ldb/tests/python/sec_descriptor.py | 13 | ||||
-rw-r--r-- | source4/libcli/security/access_check.c | 135 | ||||
-rw-r--r-- | source4/libcli/security/config.mk | 3 | ||||
-rw-r--r-- | source4/libcli/security/create_descriptor.c | 352 | ||||
-rw-r--r-- | source4/libcli/security/object_tree.c | 106 | ||||
-rw-r--r-- | source4/libcli/security/security.h | 9 | ||||
-rw-r--r-- | source4/scripting/python/samba/provision.py | 49 | ||||
-rw-r--r-- | source4/selftest/knownfail | 3 | ||||
-rw-r--r-- | source4/setup/provision_configuration_basedn.ldif | 1 | ||||
-rw-r--r-- | source4/setup/provision_schema_basedn.ldif | 1 |
16 files changed, 1899 insertions, 15 deletions
diff --git a/libcli/security/security_descriptor.c b/libcli/security/security_descriptor.c index dbe11604fd..b77a281852 100644 --- a/libcli/security/security_descriptor.c +++ b/libcli/security/security_descriptor.c @@ -77,6 +77,56 @@ struct security_acl *security_acl_dup(TALLOC_CTX *mem_ctx, } +struct security_acl *security_acl_concatenate(TALLOC_CTX *mem_ctx, + const struct security_acl *acl1, + const struct security_acl *acl2) +{ + struct security_acl *nacl; + int 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 */ diff --git a/libcli/security/security_descriptor.h b/libcli/security/security_descriptor.h index a377ef59ce..bc5761ab6f 100644 --- a/libcli/security/security_descriptor.h +++ b/libcli/security/security_descriptor.h @@ -64,4 +64,8 @@ struct security_ace *security_ace_create(TALLOC_CTX *mem_ctx, 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); + #endif /* __SECURITY_DESCRIPTOR_H__ */ diff --git a/source4/dsdb/samdb/ldb_modules/acl.c b/source4/dsdb/samdb/ldb_modules/acl.c new file mode 100644 index 0000000000..1b02abcb8e --- /dev/null +++ b/source4/dsdb/samdb/ldb_modules/acl.c @@ -0,0 +1,1151 @@ +/* + ldb database library + + Copyright (C) Simo Sorce 2006-2008 + Copyright (C) Nadezhda Ivanova 2009 + Copyright (C) Anatoliy Atanasov 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: ldb + * + * Component: ldb ACL module + * + * Description: Module that performs authorisation access checks based on the + * account's security context and the DACL of the object being polled. + * Only DACL checks implemented at this point + * + * Authors: Nadezhda Ivanova, Anatoliy Atanasov + */ + +#include "includes.h" +#include "ldb_module.h" +#include "auth/auth.h" +#include "libcli/security/security.h" +#include "librpc/gen_ndr/ndr_security.h" +#include "dsdb/samdb/samdb.h" +#include "librpc/gen_ndr/ndr_security.h" +#include "param/param.h" + +/* acl_search helper */ +struct acl_context { + + struct ldb_module *module; + struct ldb_request *req; + struct ldb_request *down_req; + + /*needed if we have to identify if this is SYSTEM_USER*/ + enum security_user_level user_type; + + uint32_t access_needed; + struct ldb_dn * dn_to_check; + + /* set to true when we need to process the request as a SYSTEM_USER, regardless + * of the user's actual rights - for example when we need to retrieve the + * ntSecurityDescriptor */ + bool ignore_security; + struct security_token *token; + /*needed to identify if we have requested these attributes*/ + bool nTSecurityDescriptor; + bool objectClass; + int sec_result; +}; + +struct extended_access_check_attribute { + const char *oa_name; + const uint32_t requires_rights; +}; + +struct acl_private{ + bool perform_check; +}; + +static int acl_search_callback(struct ldb_request *req, struct ldb_reply *ares); + +/*FIXME: Perhaps this should go in the .idl file*/ +#define SEC_GENERIC_ACCESS_NEVER_GRANTED ( 0xFFFFFFFF ) + +/*Contains a part of the attributes - the ones that have predefined required rights*/ +static const struct extended_access_check_attribute extended_access_checks_table[] = +{ + { + .oa_name = "nTSecurityDescriptor", + .requires_rights = SEC_FLAG_SYSTEM_SECURITY & SEC_STD_READ_CONTROL, + }, + { + .oa_name = "pekList", + .requires_rights = SEC_GENERIC_ACCESS_NEVER_GRANTED, + }, + { + .oa_name = "currentValue", + .requires_rights = SEC_GENERIC_ACCESS_NEVER_GRANTED, + }, + { + .oa_name = "dBCSPwd", + .requires_rights = SEC_GENERIC_ACCESS_NEVER_GRANTED, + }, + { + .oa_name = "unicodePwd", + .requires_rights = SEC_GENERIC_ACCESS_NEVER_GRANTED, + }, + { + .oa_name = "ntPwdHistory", + .requires_rights = SEC_GENERIC_ACCESS_NEVER_GRANTED, + }, + { + .oa_name = "priorValue", + .requires_rights = SEC_GENERIC_ACCESS_NEVER_GRANTED, + }, + { + .oa_name = "supplementalCredentials", + .requires_rights = SEC_GENERIC_ACCESS_NEVER_GRANTED, + }, + { + .oa_name = "trustAuthIncoming", + .requires_rights = SEC_GENERIC_ACCESS_NEVER_GRANTED, + }, + { + .oa_name = "trustAuthOutgoing", + .requires_rights = SEC_GENERIC_ACCESS_NEVER_GRANTED, + }, + { + .oa_name = "ImPwdHistory", + .requires_rights = SEC_GENERIC_ACCESS_NEVER_GRANTED, + }, + { + .oa_name = "initialAuthIncoming", + .requires_rights = SEC_GENERIC_ACCESS_NEVER_GRANTED, + }, + { + .oa_name = "initialAuthOutgoing", + .requires_rights = SEC_GENERIC_ACCESS_NEVER_GRANTED, + }, + { + .oa_name = "msDS-ExecuteScriptPassword", + .requires_rights = SEC_GENERIC_ACCESS_NEVER_GRANTED, + }, +}; + +static NTSTATUS extended_access_check(const char *attribute_name, const int access_rights, uint32_t searchFlags) +{ + int i = 0; + if (access_rights == SEC_GENERIC_ACCESS_NEVER_GRANTED) { + return NT_STATUS_ACCESS_DENIED; + } + + /*Check if the attribute is in the table first*/ + for ( i = 0; extended_access_checks_table[i].oa_name; i++ ) { + if (ldb_attr_cmp(extended_access_checks_table[i].oa_name, attribute_name) == 0) { + if ((access_rights & extended_access_checks_table[i].requires_rights) == access_rights) { + return NT_STATUS_OK; + } else { + return NT_STATUS_ACCESS_DENIED; + } + } + } + + /*Check for attribute whose attributeSchema has 0x80 set in searchFlags*/ + if ((searchFlags & SEARCH_FLAG_CONFIDENTIAL) == SEARCH_FLAG_CONFIDENTIAL) { + if (((SEC_ADS_READ_PROP & SEC_ADS_CONTROL_ACCESS) & access_rights) == access_rights) { + return NT_STATUS_OK; + } else { + return NT_STATUS_ACCESS_DENIED; + } + } + + /*Check attributes with *special* behaviour*/ + if (ldb_attr_cmp("msDS-QuotaEffective", attribute_name) == 0 || ldb_attr_cmp("msDS-QuotaUsed", attribute_name) == 0){ + /*Rights required: + * + *(RIGHT_DS_READ_PROPERTY on the Quotas container or + *((the client is querying the quota for the security principal it is authenticated as) and + *(DS-Query-Self-Quota control access right on the Quotas container)) + */ + } + + if (ldb_attr_cmp("userPassword", attribute_name) == 0) { + /*When the dSHeuristics.fUserPwdSupport flag is false, the requester must be granted RIGHT_DS_READ_PROPERTY. + *When the dSHeuristics.fUserPwdSupport flag is true, access is never granted. + */ + } + + if (ldb_attr_cmp("sDRightsEffective", attribute_name) == 0) { + /*FIXME:3.1.1.4.5.4 in MS-ADTS*/ + } + + if (ldb_attr_cmp("allowedChildClassesEffective", attribute_name) == 0) { + /*FIXME:3.1.1.4.5.5 in MS-ADTS*/ + } + + if (ldb_attr_cmp("allowedAttributesEffective", attribute_name) == 0) { + /*FIXME:3.1.1.4.5.7 in MS-ADTS*/ + } + + if (ldb_attr_cmp("msDS-Approx-Immed-Subordinates", attribute_name) == 0) { + /*FIXME:3.1.1.4.5.15 in MS-ADTS*/ + } + + if (ldb_attr_cmp("msDS-QuotaEffective", attribute_name) == 0) { + /*FIXME:3.1.1.4.5.22 in MS-ADTS*/ + } + + if (ldb_attr_cmp("msDS-ReplAttributeMetaData", attribute_name) == 0 || ldb_attr_cmp("msDS-ReplAttributeMetaData", attribute_name) == 0) { + /*The security context of the requester must be granted the following rights on the replPropertyMetaData attribute: + *(RIGHT_DS_READ_PROPERTY)or (DS-Replication-Manage-Topology by ON!nTSecurityDescriptor) + */ + } + + if (ldb_attr_cmp("msDS-NCReplInboundNeighbors", attribute_name) == 0) { + /*The security context of the requester must be granted the following rights on repsFrom: + *(RIGHT_DS_READ_PROPERTY) or (DS-Replication-Manage-Topology) or (DS-Replication-Monitor-Topology) + */ + } + + if (ldb_attr_cmp("msDS-NCReplOutboundNeighbors", attribute_name) == 0) { + /*The security context of the requester must be granted the following rights on repsTo: + *(RIGHT_DS_READ_PROPERTY) or (DS-Replication-Manage-Topology) or (DS-Replication-Monitor-Topology) + */ + } + + if (ldb_attr_cmp("msDS-NCReplCursors", attribute_name) == 0) { + /*The security context of the requester must be granted the following rights on replUpToDateVector: (RIGHT_DS_READ_PROPERTY) + *or (DS-Replication-Manage-Topology) or (DS-Replication-Monitor-Topology) + */ + } + + if (ldb_attr_cmp("msDS-IsUserCachableAtRodc", attribute_name) == 0) { + /*The security context of the requester must be granted + *the DS-Replication-Secrets-Synchronize control access right on the root of the default NC. + */ + } + + return NT_STATUS_OK; +} + +/* Builds an object tree for object specific access checks */ +static struct object_tree * build_object_tree_form_attr_list(TALLOC_CTX *mem_ctx, /* Todo this context or separate? */ + struct ldb_context *ldb, + const char ** attr_names, + int num_attrs, + const char * object_class, + uint32_t init_access) +{ + const struct dsdb_schema *schema = dsdb_get_schema(ldb); + const struct GUID *oc_guid = class_schemaid_guid_by_lDAPDisplayName(schema, object_class); + struct object_tree *tree; + int i; + + if (!oc_guid) + return NULL; + + tree = insert_in_object_tree(mem_ctx, oc_guid, NULL, init_access, NULL); + if (attr_names){ + for (i=0; i < num_attrs; i++){ + const struct dsdb_attribute *attribute = dsdb_attribute_by_lDAPDisplayName(schema,attr_names[i]); + if (attribute) + insert_in_object_tree(mem_ctx, + &attribute->schemaIDGUID, + &attribute->attributeSecurityGUID, + init_access, + tree); + } + } + return tree; +} + +bool is_root_base_dn(struct ldb_context *ldb, struct ldb_dn *dn_to_check) +{ + int result; + struct ldb_dn *root_base_dn = ldb_get_root_basedn(ldb); + result = ldb_dn_compare(root_base_dn,dn_to_check); + return (result==0); +} + +static int acl_op_callback(struct ldb_request *req, struct ldb_reply *ares) +{ + struct acl_context *ac; + + ac = talloc_get_type(req->context, struct acl_context); + + if (!ares) { + return ldb_module_done(ac->req, NULL, NULL, + LDB_ERR_OPERATIONS_ERROR); + } + if (ares->error != LDB_SUCCESS) { + return ldb_module_done(ac->req, ares->controls, + ares->response, ares->error); + } + + if (ares->type != LDB_REPLY_DONE) { + talloc_free(ares); + return ldb_module_done(ac->req, NULL, NULL, + LDB_ERR_OPERATIONS_ERROR); + } + + return ldb_module_done(ac->req, ares->controls, + ares->response, ares->error); +} + + +static int acl_access_check_add(struct ldb_reply *ares, + struct acl_context *ac, + struct security_descriptor *sd) +{ + uint32_t access_granted = 0; + NTSTATUS status; + struct ldb_dn *parent; + struct ldb_dn *grandparent; + struct object_tree *tree = NULL; + + parent = ldb_dn_get_parent(ac->req, ac->req->op.add.message->dn); + grandparent = ldb_dn_get_parent(ac->req, parent); + if (ldb_dn_compare(ares->message->dn, grandparent) == 0) + status = sec_access_check_ds(sd, ac->token, + SEC_ADS_LIST, + &access_granted, + NULL); + else if (ldb_dn_compare(ares->message->dn, parent) == 0){ + struct ldb_message_element *oc_el; + struct ldb_context *ldb = ldb_module_get_ctx(ac->module); + const struct dsdb_schema *schema = dsdb_get_schema(ldb); + int i; + + oc_el = ldb_msg_find_element(ares->message, "objectClass"); + if (!oc_el || oc_el->num_values == 0) + return LDB_SUCCESS; + for (i = 0; i < oc_el->num_values; i++){ + const struct GUID *guid = class_schemaid_guid_by_lDAPDisplayName(schema, + oc_el->values[i].data); + ac->sec_result = LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS; + tree = insert_in_object_tree(ac->req, guid, NULL, SEC_ADS_CREATE_CHILD, + tree); + status = sec_access_check_ds(sd, ac->token, SEC_ADS_CREATE_CHILD,&access_granted, tree); + if (NT_STATUS_IS_OK(status)) + ac->sec_result = LDB_SUCCESS; + } + } + else + return LDB_SUCCESS; + + return ac->sec_result; +} + +static int acl_access_check_modify(struct ldb_reply *ares, struct acl_context *ac, + struct security_descriptor *sd) +{ + uint32_t access_granted = 0; + NTSTATUS status; + struct ldb_dn *parent; + struct object_tree *tree = NULL; + + parent = ldb_dn_get_parent(ac->req, ac->req->op.add.message->dn); + if (ldb_dn_compare(ares->message->dn, parent) == 0) + status = sec_access_check_ds(sd, ac->token, SEC_ADS_LIST,&access_granted, NULL); + else if (ldb_dn_compare(ares->message->dn, ac->req->op.add.message->dn) == 0){ + struct ldb_message_element *oc_el; + struct ldb_context *ldb = ldb_module_get_ctx(ac->module); + const struct dsdb_schema *schema = dsdb_get_schema(ldb); + int i; + struct GUID *guid; + oc_el = ldb_msg_find_element(ares->message, "objectClass"); + if (!oc_el || oc_el->num_values == 0) + return LDB_SUCCESS; + + guid = class_schemaid_guid_by_lDAPDisplayName(schema, + oc_el->values[oc_el->num_values-1].data); + tree = insert_in_object_tree(ac->req, guid, NULL, SEC_ADS_WRITE_PROP, + tree); + for (i=0; i < ac->req->op.mod.message->num_elements; i++){ + const struct dsdb_attribute *attr = dsdb_attribute_by_lDAPDisplayName(schema, + ac->req->op.mod.message->elements[i].name); + if (!attr) + return LDB_ERR_OPERATIONS_ERROR; /* What should we actually return here? */ + insert_in_object_tree(ac, &attr->schemaIDGUID, + &attr->attributeSecurityGUID, ac->access_needed, tree); + } + status = sec_access_check_ds(sd, ac->token, SEC_ADS_WRITE_PROP ,&access_granted, tree); + if (!NT_STATUS_IS_OK(status)) + ac->sec_result = LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS; + } + else + return LDB_SUCCESS; + return ac->sec_result; +} +/*TODO*/ +static int acl_access_check_rename(struct ldb_reply *ares, struct acl_context *ac, + struct security_descriptor *sd) +{ + return ac->sec_result; +} + +static int acl_access_check_delete(struct ldb_reply *ares, struct acl_context *ac, + struct security_descriptor *sd) +{ + uint32_t access_granted = 0; + NTSTATUS status; + struct ldb_dn *parent; + struct object_tree *tree = NULL; + + parent = ldb_dn_get_parent(ac->req, ac->req->op.del.dn); + if (ldb_dn_compare(ares->message->dn, parent) == 0){ + status = sec_access_check_ds(sd, ac->token, SEC_ADS_LIST,&access_granted, NULL); + if (!NT_STATUS_IS_OK(status)){ + ac->sec_result = LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS; + return ac->sec_result; + } + status = sec_access_check_ds(sd, ac->token, SEC_ADS_DELETE_CHILD,&access_granted, NULL); + if (NT_STATUS_IS_OK(status)){ + ac->sec_result = LDB_SUCCESS; + return ac->sec_result; + } + } + else if (ldb_dn_compare(ares->message->dn, ac->req->op.del.dn) == 0){ + status = sec_access_check_ds(sd, ac->token, SEC_STD_DELETE, &access_granted, NULL); + if (!NT_STATUS_IS_OK(status)) + ac->sec_result = LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS; + } + return ac->sec_result; +} + +static int acl_access_check_search(struct ldb_reply *ares, struct acl_context *ac, + struct security_descriptor *sd) +{ + uint32_t access_granted; + NTSTATUS status; + struct ldb_dn *parent; + + if (ac->user_type == SECURITY_SYSTEM || ac->user_type == SECURITY_ANONYMOUS) { + return LDB_SUCCESS;/*FIXME: we have anonymous access*/ + } + + parent = ldb_dn_get_parent(ac->req, ac->dn_to_check); + ac->sec_result = LDB_SUCCESS; + if (ldb_dn_compare(ares->message->dn, parent) == 0) { + status = sec_access_check_ds(sd, ac->token, SEC_ADS_LIST,&access_granted, NULL); + if (!NT_STATUS_IS_OK(status)) { + ac->sec_result = LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS; + } + } + + return ac->sec_result; +} + +static int acl_perform_access_check(struct ldb_request *req, struct ldb_reply *ares, + struct acl_context *ac) +{ + struct ldb_message_element *oc_el; + struct security_descriptor *sd; + enum ndr_err_code ndr_err; + + oc_el = ldb_msg_find_element(ares->message, "ntSecurityDescriptor"); + if (!oc_el || oc_el->num_values == 0) + return LDB_SUCCESS; + + sd = talloc(ac, struct security_descriptor); + if(!sd) { + return ldb_module_done(ac->req, ares->controls, + ares->response, LDB_ERR_OPERATIONS_ERROR); + } + ndr_err = ndr_pull_struct_blob(&oc_el->values[0], sd, NULL, sd, + (ndr_pull_flags_fn_t)ndr_pull_security_descriptor); + + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) + return ldb_module_done(ac->req, ares->controls, + ares->response, LDB_ERR_OPERATIONS_ERROR); + switch (ac->req->operation) { + case LDB_SEARCH: + return acl_access_check_search(ares, ac, sd); + case LDB_ADD: + return acl_access_check_add(ares, ac, sd); + case LDB_MODIFY: + return acl_access_check_modify(ares, ac, sd); + case LDB_DELETE: + return acl_access_check_delete(ares, ac, sd); + case LDB_RENAME: + return acl_access_check_rename(ares, ac, sd); + default: + return ldb_module_done(ac->req, ares->controls, + ares->response, LDB_ERR_OPERATIONS_ERROR); + } + return LDB_SUCCESS; +} + +static int acl_forward_add(struct ldb_reply *ares, + struct acl_context *ac) +{ + struct ldb_request *newreq; + struct ldb_context *ldb; + int ret; + + ldb = ldb_module_get_ctx(ac->module); + ret = ldb_build_add_req(&newreq,ldb, + ac, + ac->req->op.add.message, + ac->req->controls, + ac, + acl_op_callback, + ac->req); + if (ret != LDB_SUCCESS) + return ldb_module_done(ac->req, ares->controls, + ares->response, LDB_ERR_OPERATIONS_ERROR); + return ldb_next_request(ac->module, newreq); +} + +static int acl_forward_modify(struct ldb_reply *ares, + struct acl_context *ac) +{ + struct ldb_request *newreq; + struct ldb_context *ldb; + int ret; + + ldb = ldb_module_get_ctx(ac->module); + ret = ldb_build_mod_req(&newreq,ldb, + ac, + ac->req->op.mod.message, + ac->req->controls, + ac, + acl_op_callback, + ac->req); + if (ret != LDB_SUCCESS) + return ldb_module_done(ac->req, ares->controls, + ares->response, LDB_ERR_OPERATIONS_ERROR); + return ldb_next_request(ac->module, newreq); +} + +static int acl_forward_delete(struct ldb_reply *ares, + struct acl_context *ac) +{ + struct ldb_request *newreq; + struct ldb_context *ldb; + int ret; + + ldb = ldb_module_get_ctx(ac->module); + ret = ldb_build_del_req(&newreq, ldb, + ac, + ac->req->op.del.dn, + ac->req->controls, + ac, + acl_op_callback, + ac->req); + if (ret != LDB_SUCCESS) + return ldb_module_done(ac->req, ares->controls, + ares->response, LDB_ERR_OPERATIONS_ERROR); + return ldb_next_request(ac->module, newreq); +} + +static int acl_forward_rename(struct ldb_reply *ares, + struct acl_context *ac) +{ + return LDB_SUCCESS; +} + +static int acl_forward_search(struct acl_context *ac) +{ + int ret; + const char * const *attrs; + struct ldb_control *sd_control; + struct ldb_control **sd_saved_controls; + struct ldb_context *ldb; + struct ldb_request *newreq; + + ldb = ldb_module_get_ctx(ac->module); + attrs = ac->req->op.search.attrs; + if (attrs) { + ac->nTSecurityDescriptor = false; + ac->objectClass = false; + if (!ldb_attr_in_list(ac->req->op.search.attrs, "nTSecurityDescriptor")) { + attrs = ldb_attr_list_copy_add(ac, attrs, "nTSecurityDescriptor"); + ac->nTSecurityDescriptor = true; + } + if (!ldb_attr_in_list(ac->req->op.search.attrs, "objectClass")) { + attrs = ldb_attr_list_copy_add(ac, attrs, "objectClass"); + ac->objectClass = true; + } + } + ret = ldb_build_search_req_ex(&newreq,ldb, + ac, + ac->req->op.search.base, + ac->req->op.search.scope, + ac->req->op.search.tree, + attrs, + ac->req->controls, + ac, acl_search_callback, + ac->req); + if (ret != LDB_SUCCESS) { + return LDB_ERR_OPERATIONS_ERROR; + } + /* check if there's an SD_FLAGS control */ + sd_control = ldb_request_get_control(newreq, LDB_CONTROL_SD_FLAGS_OID); + if (sd_control) { + /* save it locally and remove it from the list */ + /* we do not need to replace them later as we + * are keeping the original req intact */ + if (!save_controls(sd_control, newreq, &sd_saved_controls)) { + return LDB_ERR_OPERATIONS_ERROR; + } + } + return ldb_next_request(ac->module, newreq); +} + +static int acl_forward_request(struct ldb_reply *ares, + struct acl_context *ac) +{ + switch (ac->req->operation) { + case LDB_SEARCH: + return acl_forward_search(ac); + case LDB_ADD: + return acl_forward_add(ares,ac); + case LDB_MODIFY: + return acl_forward_modify(ares,ac); + case LDB_DELETE: + return acl_forward_delete(ares,ac); + case LDB_RENAME: + return acl_forward_rename(ares,ac); + default: + return ldb_module_done(ac->req, ares->controls, + ares->response, LDB_ERR_OPERATIONS_ERROR); + } + return LDB_SUCCESS; +} + +static int acl_visible_callback(struct ldb_request *req, struct ldb_reply *ares) +{ + struct acl_context *ac; + + ac = talloc_get_type(req->context, struct acl_context); + + if (!ares) { + return ldb_module_done(ac->req, NULL, NULL, + LDB_ERR_OPERATIONS_ERROR); + } + + if (ares->error != LDB_SUCCESS) { + return ldb_module_done(ac->req, ares->controls, + ares->response, ares->error); + } + + switch (ares->type) { + case LDB_REPLY_ENTRY: + return acl_perform_access_check(req, ares, ac); + case LDB_REPLY_REFERRAL: + return ldb_module_send_referral(ac->req, ares->referral); /* what to do here actually? */ + case LDB_REPLY_DONE: + if (ac->sec_result != LDB_SUCCESS) { + return ldb_module_done(ac->req, ares->controls, + ares->response, ac->sec_result); + } + return acl_forward_request(ares,ac); + default: + break; + } + return LDB_SUCCESS; +} + +static enum security_user_level what_is_user(struct ldb_module *module) +{ + struct ldb_context *ldb = ldb_module_get_ctx(module); + struct auth_session_info *session_info + = (struct auth_session_info *)ldb_get_opaque(ldb, "sessionInfo"); + return security_session_user_level(session_info); +} + +static struct security_token * user_token(struct ldb_module *module) +{ + struct ldb_context *ldb = ldb_module_get_ctx(module); + struct auth_session_info *session_info + = (struct auth_session_info *)ldb_get_opaque(ldb, "sessionInfo"); + if(!session_info) { + return NULL; + } + return session_info->security_token; +} + + +static int make_req_access_check(struct ldb_module *module, struct ldb_request *req, + struct acl_context *ac, const char *filter) +{ + struct ldb_context *ldb; + int ret; + const char **attrs = talloc_array(ac, const char *, 3); + struct ldb_parse_tree *tree = ldb_parse_tree(req, filter); + + attrs[0] = talloc_strdup(attrs, "ntSecurityDescriptor"); + attrs[1] = talloc_strdup(attrs, "objectClass"); + attrs[2] = NULL; + + ldb = ldb_module_get_ctx(module); + ret = ldb_build_search_req_ex(&ac->down_req, + ldb, ac, + ac->dn_to_check, + LDB_SCOPE_SUBTREE, + tree, + attrs, + NULL, + ac, acl_visible_callback, + req); + return ret; +} + +static const char *user_name(TALLOC_CTX *mem_ctx, struct ldb_module *module) +{ + struct ldb_context *ldb = ldb_module_get_ctx(module); + struct auth_session_info *session_info + = (struct auth_session_info *)ldb_get_opaque(ldb, "sessionInfo"); + if (!session_info) { + return "UNKNOWN (NULL)"; + } + + return talloc_asprintf(mem_ctx, "%s\\%s", + session_info->server_info->domain_name, + session_info->server_info->account_name); +} + +static int acl_module_init(struct ldb_module *module) +{ + struct ldb_context *ldb; + struct acl_private *data; + int ret; + + ldb = ldb_module_get_ctx(module); + + ret = ldb_mod_register_control(module, LDB_CONTROL_SD_FLAGS_OID); + if (ret != LDB_SUCCESS) { + ldb_debug(ldb, LDB_DEBUG_ERROR, + "acl_module_init: Unable to register control with rootdse!\n"); + return LDB_ERR_OPERATIONS_ERROR; + } + + data = talloc(module, struct acl_private); + data->perform_check = lp_parm_bool(ldb_get_opaque(ldb, "loadparm"), + NULL, "acl", "perform", false); + ldb_module_set_private(module, data); + + return ldb_next_init(module); +} + +static int acl_add(struct ldb_module *module, struct ldb_request *req) +{ + int ret; + struct acl_context *ac; + struct ldb_dn * parent = ldb_dn_get_parent(req, req->op.add.message->dn); + char * filter; + struct ldb_context *ldb; + struct acl_private *data; + + ldb = ldb_module_get_ctx(module); + data = talloc_get_type(ldb_module_get_private(module), struct acl_private); + + if (!data->perform_check) + return ldb_next_request(module, req); + + ac = talloc(req, struct acl_context); + if (ac == NULL) { + return LDB_ERR_OPERATIONS_ERROR; + } + + if (what_is_user(module) == SECURITY_SYSTEM) + return ldb_next_request(module, req); + + ac->module = module; + ac->req = req; + ac->ignore_security = true; + ac->dn_to_check = ldb_dn_get_parent(req, parent); + ac->token = user_token(module); + ac->user_type = what_is_user(module); + ac->sec_result = LDB_SUCCESS; + if (!is_root_base_dn(ldb, req->op.add.message->dn) && parent && !is_root_base_dn(ldb, parent)){ + filter = talloc_asprintf(req,"(&(objectClass=*)(|(%s=%s)(%s=%s))))", + ldb_dn_get_component_name(parent,0), + ldb_dn_get_component_val(parent,0)->data, + ldb_dn_get_component_name(ac->dn_to_check,0), + ldb_dn_get_component_val(ac->dn_to_check,0)->data); + + ret = make_req_access_check(module, req, ac, filter); + if (ret != LDB_SUCCESS){ + return ret; + } + return ldb_next_request(module, ac->down_req); + } + return ldb_next_request(module, req); +} + +static int acl_modify(struct ldb_module *module, struct ldb_request *req) +{ + int ret; + struct acl_context *ac; + struct ldb_dn * parent = ldb_dn_get_parent(req, req->op.mod.message->dn); + char * filter; + struct ldb_context *ldb; + struct acl_private *data; + + ldb = ldb_module_get_ctx(module); + data = talloc_get_type(ldb_module_get_private(module), struct acl_private); + + if (!data->perform_check) + return ldb_next_request(module, req); + + ac = talloc(req, struct acl_context); + if (ac == NULL) { + return LDB_ERR_OPERATIONS_ERROR; + } + +/* if (what_is_user(module) == SECURITY_SYSTEM) */ + return ldb_next_request(module, req); + + ac->module = module; + ac->req = req; + ac->ignore_security = true; + ac->dn_to_check = req->op.mod.message->dn; + ac->token = user_token(module); + ac->user_type = what_is_user(module); + ac->sec_result = LDB_SUCCESS; + if (!is_root_base_dn(ldb, req->op.mod.message->dn) && parent && !is_root_base_dn(ldb, parent)){ + filter = talloc_asprintf(req,"(&(objectClass=*)(|(%s=%s)(%s=%s))))", + ldb_dn_get_component_name(parent,0), + ldb_dn_get_component_val(parent,0)->data, + ldb_dn_get_component_name(req->op.mod.message->dn,0), + ldb_dn_get_component_val(req->op.mod.message->dn,0)->data); + + ret = make_req_access_check(module, req, ac, filter); + if (ret != LDB_SUCCESS){ + return ret; + } + return ldb_next_request(module, ac->down_req); + } + return ldb_next_request(module, req); +} + +/* similar to the modify for the time being. + * We need to concider the special delete tree case, though - TODO */ +static int acl_delete(struct ldb_module *module, struct ldb_request *req) +{ + int ret; + struct acl_context *ac; + struct ldb_dn * parent = ldb_dn_get_parent(req, req->op.del.dn); + char * filter; + struct ldb_context *ldb; + struct acl_private *data; + + ldb = ldb_module_get_ctx(module); + data = talloc_get_type(ldb_module_get_private(module), struct acl_private); + + if (!data->perform_check) + return ldb_next_request(module, req); + + ac = talloc(req, struct acl_context); + if (ac == NULL) { + return LDB_ERR_OPERATIONS_ERROR; + } + + if (ac->user_type == SECURITY_SYSTEM) + return ldb_next_request(module, req); + + ac->module = module; + ac->req = req; + ac->ignore_security = true; + ac->dn_to_check = req->op.del.dn; + ac->token = user_token(module); + ac->user_type = what_is_user(module); + ac->sec_result = LDB_SUCCESS; + if (parent) { + filter = talloc_asprintf(req,"(&(objectClass=*)(|(%s=%s)(%s=%s))))", + ldb_dn_get_component_name(parent,0), + ldb_dn_get_component_val(parent,0)->data, + ldb_dn_get_component_name(req->op.del.dn,0), + ldb_dn_get_component_val(req->op.del.dn,0)->data); + ret = make_req_access_check(module, req, ac, filter); + + if (ret != LDB_SUCCESS){ + return ret; + } + return ldb_next_request(module, ac->down_req); + } + + return ldb_next_request(module, req); +} + +static int acl_rename(struct ldb_module *module, struct ldb_request *req) +{ + struct ldb_dn *source_parent; + struct ldb_dn *dest_parent; + int ret; + struct acl_context *ac; + char * filter; + struct ldb_context *ldb; + struct acl_private *data; + + ldb = ldb_module_get_ctx(module); + data = talloc_get_type(ldb_module_get_private(module), struct acl_private); + + if (!data->perform_check) + return ldb_next_request(module, req); + + ac = talloc(req, struct acl_context); + if (ac == NULL) { + return LDB_ERR_OPERATIONS_ERROR; + } + + if (ac->user_type == SECURITY_SYSTEM) + return ldb_next_request(module, req); + + ac->module = module; + ac->req = req; + ac->ignore_security = true; + ac->token = user_token(module); + ac->user_type = what_is_user(module); + ac->sec_result = LDB_SUCCESS; + + /* We need to know if it is a simple rename or a move operation */ + source_parent = ldb_dn_get_parent(req, req->op.rename.olddn); + dest_parent = ldb_dn_get_parent(req, req->op.rename.newdn); + + if (ldb_dn_compare(source_parent, dest_parent) == 0){ + /*Not a move, just rename*/ + filter = talloc_asprintf(req,"(&(objectClass=*)(|(%s=%s)(%s=%s))))", + ldb_dn_get_component_name(dest_parent,0), + ldb_dn_get_component_val(dest_parent,0)->data, + ldb_dn_get_component_name(req->op.rename.olddn,0), + ldb_dn_get_component_val(req->op.rename.olddn,0)->data); + } + else{ + filter = talloc_asprintf(req,"(&(objectClass=*)(|(%s=%s)(%s=%s))))", + ldb_dn_get_component_name(dest_parent,0), + ldb_dn_get_component_val(dest_parent,0)->data, + ldb_dn_get_component_name(source_parent,0), + ldb_dn_get_component_val(source_parent,0)->data); + } + + ret = make_req_access_check(module, req, ac, filter); + + if (ret != LDB_SUCCESS){ + return ret; + } + return ldb_next_request(module, ac->down_req); +} + +static int acl_search_callback(struct ldb_request *req, struct ldb_reply *ares) +{ + struct ldb_context *ldb; + struct acl_context *ac; + struct security_descriptor *sd; + uint32_t searchFlags; + uint32_t access_mask; + struct object_tree *ot; + int i, ret; + NTSTATUS status; + struct ldb_message_element *element_security_descriptor; + struct ldb_message_element *element_object_class; + const struct dsdb_attribute *attr; + const struct dsdb_schema *schema; + struct GUID *oc_guid; + + ac = talloc_get_type(req->context, struct acl_context); + ldb = ldb_module_get_ctx(ac->module); + schema = dsdb_get_schema(ldb); + if (!ares) { + return ldb_module_done(ac->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR); + } + if (ares->error != LDB_SUCCESS) { + return ldb_module_done(ac->req, ares->controls, ares->response, ares->error); + } + + switch (ares->type) { + case LDB_REPLY_ENTRY: + switch (ac->user_type) { + case SECURITY_SYSTEM: + case SECURITY_ANONYMOUS:/*FIXME: should we let anonymous have system access*/ + break; + default: + /* Access checks + * + * 0. If we do not have nTSecurityDescriptor, we do not have an object in the response, + * so check the parent dn. + * 1. Call sec_access_check on empty tree + * 2. For each attribute call extended_access_check + * 3. For each attribute call build_object_tree_form_attr_list and then check with sec_access_check + * + */ + element_security_descriptor = ldb_msg_find_element(ares->message, "nTSecurityDescriptor"); + element_object_class = ldb_msg_find_element(ares->message, "objectClass"); + if (!element_security_descriptor || !element_object_class) + break; + + sd = talloc(ldb, struct security_descriptor); + if(!sd) { + return ldb_module_done(ac->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR); + } + if(!NDR_ERR_CODE_IS_SUCCESS(ndr_pull_struct_blob(&element_security_descriptor->values[0], + ldb, + NULL, + sd, + (ndr_pull_flags_fn_t)ndr_pull_security_descriptor))) { + DEBUG(0, ("acl_search_callback: Error parsing security descriptor\n")); + return ldb_module_done(ac->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR); + } + + oc_guid = class_schemaid_guid_by_lDAPDisplayName(schema, element_object_class->values[0].data); + for (i=0; i<ares->message->num_elements; i++) { + attr = dsdb_attribute_by_lDAPDisplayName(schema, ares->message->elements[i].name); + if (attr) { + searchFlags = attr->searchFlags; + } else { + searchFlags = 0x0; + } + + /*status = extended_access_check(ares->message->elements[i].name, access_mask, searchFlags); */ /* Todo FIXME */ + ac->access_needed = SEC_ADS_READ_PROP; + if (NT_STATUS_IS_OK(status)) { + ot = insert_in_object_tree(req, oc_guid, NULL, ac->access_needed, NULL); + + insert_in_object_tree(req, + &attr->schemaIDGUID, + &attr->attributeSecurityGUID, + ac->access_needed, + ot); + + status = sec_access_check_ds(sd, + ac->token, + ac->access_needed, + &access_mask, + ot); + + if (NT_STATUS_IS_OK(status)) { + continue; + } + } + ldb_msg_remove_attr(ares->message, ares->message->elements[i].name); + } + break; + } + if (ac->nTSecurityDescriptor) { + ldb_msg_remove_attr(ares->message, "nTSecurityDescriptor"); + } else if (ac->objectClass) { + ldb_msg_remove_attr(ares->message, "objectClass"); + } + + return ldb_module_send_entry(ac->req, ares->message, ares->controls); + case LDB_REPLY_REFERRAL: + return ldb_module_send_referral(ac->req, ares->referral); + + case LDB_REPLY_DONE: + return ldb_module_done(ac->req, ares->controls,ares->response, LDB_SUCCESS); + } + + return LDB_SUCCESS; +} + +static int acl_search(struct ldb_module *module, struct ldb_request *req) +{ + int ret; + struct ldb_context *ldb; + struct acl_context *ac; + const char **attrs; + struct ldb_control *sd_control; + struct ldb_control **sd_saved_controls; + struct ldb_dn * parent; + struct acl_private *data; + + ldb = ldb_module_get_ctx(module); + data = talloc_get_type(ldb_module_get_private(module), struct acl_private); + + if (!data || !data->perform_check) + return ldb_next_request(module, req); + + if (what_is_user(module) == SECURITY_SYSTEM) + return ldb_next_request(module, req); + + ac = talloc_get_type(req->context, struct acl_context); + if ( ac == NULL ) { + ac = talloc(req, struct acl_context); + if (ac == NULL) { + ldb_oom(ldb); + return LDB_ERR_OPERATIONS_ERROR; + } + ac->module = module; + ac->req = req; + ac->ignore_security = false; + ac->user_type = what_is_user(module); + ac->token = user_token(module); + ac->dn_to_check = req->op.search.base; + ac->sec_result = LDB_SUCCESS; + + attrs = talloc_array(ac, const char*, 2); + attrs[0] = talloc_strdup(attrs, "nTSecurityDescriptor"); + attrs[1] = NULL; + parent = ldb_dn_get_parent(req, ac->dn_to_check); + if (!is_root_base_dn(ldb, req->op.search.base) && parent && !is_root_base_dn(ldb, parent)) { + /*we have parent so check for visibility*/ + ret = ldb_build_search_req(&ac->down_req, + ldb, ac, + parent, + LDB_SCOPE_BASE, + "(objectClass=*)", + attrs, + req->controls, + ac, acl_visible_callback, + req); + if (ret != LDB_SUCCESS) { + return ret; + } + return ldb_next_request(module, ac->down_req); + } else { + return acl_forward_search(ac); + } + } + + return ldb_next_request(module, req); +} + +static int acl_extended(struct ldb_module *module, struct ldb_request *req) +{ + struct ldb_context *ldb = ldb_module_get_ctx(module); + enum security_user_level user_type; + struct acl_private *data; + + data = talloc_get_type(ldb_module_get_private(module), struct acl_private); + + if (!data->perform_check) + return ldb_next_request(module, req); + + /* allow everybody to read the sequence number */ + if (strcmp(req->op.extended.oid, LDB_EXTENDED_SEQUENCE_NUMBER) == 0) { + return ldb_next_request(module, req); + } + + user_type = what_is_user(module); + switch (user_type) { + case SECURITY_SYSTEM: + case SECURITY_ADMINISTRATOR: + return ldb_next_request(module, req); + default: + ldb_asprintf_errstring(ldb, + "acl_extended: attempted database modify not permitted." + "User %s is not SYSTEM or an Administrator", + user_name(req, module)); + return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS; + } +} + +_PUBLIC_ const struct ldb_module_ops ldb_acl_module_ops = { + .name = "acl", + .search = acl_search, + .add = acl_add, + .modify = acl_modify, + .del = acl_delete, + .rename = acl_rename, + .extended = acl_extended, + .init_context = acl_module_init +}; diff --git a/source4/dsdb/samdb/ldb_modules/config.mk b/source4/dsdb/samdb/ldb_modules/config.mk index 9384d062a4..a49b238591 100644 --- a/source4/dsdb/samdb/ldb_modules/config.mk +++ b/source4/dsdb/samdb/ldb_modules/config.mk @@ -357,3 +357,15 @@ INIT_FUNCTION = LDB_MODULE(resolve_oids) ################################################ ldb_resolve_oids_OBJ_FILES = $(dsdbsrcdir)/samdb/ldb_modules/resolve_oids.o + +################################################ +# Start MODULE ldb_acl +[MODULE::ldb_acl] +PRIVATE_DEPENDENCIES = LIBTALLOC LIBEVENTS LIBSECURITY SAMDB +SUBSYSTEM = LIBLDB +INIT_FUNCTION = LDB_MODULE(acl) + +# End MODULE ldb_acl +################################################ + +ldb_acl_OBJ_FILES = $(dsdbsrcdir)/samdb/ldb_modules/acl.o diff --git a/source4/dsdb/samdb/ldb_modules/kludge_acl.c b/source4/dsdb/samdb/ldb_modules/kludge_acl.c index 15db491171..34f848de8a 100644 --- a/source4/dsdb/samdb/ldb_modules/kludge_acl.c +++ b/source4/dsdb/samdb/ldb_modules/kludge_acl.c @@ -35,6 +35,7 @@ #include "auth/auth.h" #include "libcli/security/security.h" #include "dsdb/samdb/samdb.h" +#include "param/param.h" /* Kludge ACL rules: * @@ -46,6 +47,7 @@ struct kludge_private_data { const char **password_attrs; + bool acl_perform; }; static enum security_user_level what_is_user(struct ldb_module *module) @@ -325,6 +327,9 @@ static int kludge_acl_search(struct ldb_module *module, struct ldb_request *req) data = talloc_get_type(ldb_module_get_private(module), struct kludge_private_data); + if (data && data->acl_perform) + return ldb_next_request(module, req); + ac->module = module; ac->req = req; ac->user_type = what_is_user(module); @@ -397,6 +402,12 @@ static int kludge_acl_change(struct ldb_module *module, struct ldb_request *req) { struct ldb_context *ldb = ldb_module_get_ctx(module); enum security_user_level user_type = what_is_user(module); + struct kludge_private_data *data = talloc_get_type(ldb_module_get_private(module), + struct kludge_private_data); + + if (data->acl_perform) + return ldb_next_request(module, req); + switch (user_type) { case SECURITY_SYSTEM: case SECURITY_ADMINISTRATOR: @@ -459,6 +470,8 @@ static int kludge_acl_init(struct ldb_module *module) } data->password_attrs = NULL; + data->acl_perform = lp_parm_bool(ldb_get_opaque(ldb, "loadparm"), + NULL, "acl", "perform", false); ldb_module_set_private(module, data); if (!mem_ctx) { diff --git a/source4/dsdb/schema/schema_query.c b/source4/dsdb/schema/schema_query.c index 7d696e877e..a2d9792a24 100644 --- a/source4/dsdb/schema/schema_query.c +++ b/source4/dsdb/schema/schema_query.c @@ -416,3 +416,15 @@ const char **dsdb_full_attribute_list(TALLOC_CTX *mem_ctx, const char **attr_list = dsdb_full_attribute_list_internal_el(mem_ctx, schema, class_list, query); return dedup_attr_list(attr_list); } + +/* Return the schemaIDGUID of a class */ + +const struct GUID * class_schemaid_guid_by_lDAPDisplayName(const struct dsdb_schema *schema, + const char *name) +{ + const struct dsdb_class *object_class = dsdb_class_by_lDAPDisplayName(schema, name); + if (!object_class) + return NULL; + + return &object_class->schemaIDGUID; +} diff --git a/source4/lib/ldb/tests/python/sec_descriptor.py b/source4/lib/ldb/tests/python/sec_descriptor.py index 01df86e909..155b65f4ab 100755 --- a/source4/lib/ldb/tests/python/sec_descriptor.py +++ b/source4/lib/ldb/tests/python/sec_descriptor.py @@ -249,7 +249,10 @@ userAccountControl: %s""" % userAccountControl desc_sddl = desc.as_sddl( self.domain_sid ) if ace in desc_sddl: return - desc_sddl = desc_sddl[0:desc_sddl.index("(")] + ace + desc_sddl[desc_sddl.index("("):] + if desc_sddl.find("(") >= 0: + desc_sddl = desc_sddl[0:desc_sddl.index("(")] + ace + desc_sddl[desc_sddl.index("("):] + else: + desc_sddl = desc_sddl + ace self.modify_desc(object_dn, desc_sddl) def get_desc_sddl(self, object_dn): @@ -809,13 +812,11 @@ member: """ + user_dn #mod = "" self.dacl_add_ace(object_dn, mod) desc_sddl = self.get_desc_sddl(object_dn) - #print desc_sddl # Create additional object into the first one object_dn = "OU=test_domain_ou2," + object_dn self.delete_force(self.ldb_admin, object_dn) self.create_domain_ou(self.ldb_admin, object_dn) desc_sddl = self.get_desc_sddl(object_dn) - #print desc_sddl ## Tests for SCHEMA @@ -1397,6 +1398,10 @@ class DaclDescriptorTests(DescriptorTests): # Add flag 'protected' in both DACL and SACL so no inherit ACEs # can propagate from above desc_sddl = desc_sddl.replace(":AI", ":AIP") + # colon at the end breaks ldif parsing, fix it + res = re.findall(".*?S:", desc_sddl) + if res: + desc_sddl = desc_sddl.replace("S:", "") self.modify_desc(object_dn, desc_sddl) # Verify all inheritable ACEs are gone desc_sddl = self.get_desc_sddl(object_dn) @@ -1429,6 +1434,7 @@ class DaclDescriptorTests(DescriptorTests): self.create_domain_group(self.ldb_admin, group_dn, sddl) # Make sure created group descriptor has NO additional ACEs desc_sddl = self.get_desc_sddl(group_dn) + print "group descriptor: " + desc_sddl self.assertEqual(desc_sddl, sddl) def test_202(self): @@ -1590,7 +1596,6 @@ class DaclDescriptorTests(DescriptorTests): # Make sure created group object contains only the above inherited ACE(s) # that we've added manually desc_sddl = self.get_desc_sddl(group_dn) - #print desc_sddl self.assertTrue("(D;ID;WP;;;AU)" in desc_sddl) self.assertTrue("(D;CIIOID;WP;;;CO)" in desc_sddl) diff --git a/source4/libcli/security/access_check.c b/source4/libcli/security/access_check.c index af6a3d6fb3..543b0f74c5 100644 --- a/source4/libcli/security/access_check.c +++ b/source4/libcli/security/access_check.c @@ -69,6 +69,21 @@ static uint32_t access_check_max_allowed(const struct security_descriptor *sd, return granted & ~denied; } +static const struct GUID *get_ace_object_type(struct security_ace *ace) +{ + struct GUID *type; + + if (ace->object.object.flags & SEC_ACE_OBJECT_TYPE_PRESENT) + type = &ace->object.object.type.type; + else if (ace->object.object.flags & SEC_ACE_INHERITED_OBJECT_TYPE_PRESENT) + type = &ace->object.object.inherited_type.inherited_type; /* This doesn't look right. Is something wrong with the IDL? */ + else + type = NULL; + + return type; + +} + /* the main entry point for access checking. */ @@ -153,3 +168,123 @@ done: return NT_STATUS_OK; } + +/* modified access check for the purposes of DS security + * Lots of code duplication, it will ve 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) +{ + int i; + uint32_t bits_remaining; + struct object_tree *node; + struct GUID *type; + + *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 & ~SEC_STD_DELETE; + } + + 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; + } + } + + /* a NULL dacl allows access */ + if ((sd->type & SEC_DESC_DACL_PRESENT) && sd->dacl == NULL) { + *access_granted = access_desired; + return NT_STATUS_OK; + } + + /* the owner always gets SEC_STD_WRITE_DAC, SEC_STD_READ_CONTROL and SEC_STD_DELETE */ + if ((bits_remaining & (SEC_STD_WRITE_DAC|SEC_STD_READ_CONTROL|SEC_STD_DELETE)) && + security_token_has_sid(token, sd->owner_sid)) { + bits_remaining &= ~(SEC_STD_WRITE_DAC|SEC_STD_READ_CONTROL|SEC_STD_DELETE); + } + if ((bits_remaining & SEC_STD_DELETE) && + security_token_has_privilege(token, SEC_PRIV_RESTORE)) { + bits_remaining &= ~SEC_STD_DELETE; + } + + if (sd->dacl == NULL) { + goto done; + } + + /* check each ace in turn. */ + for (i=0; bits_remaining && i < sd->dacl->num_aces; i++) { + struct security_ace *ace = &sd->dacl->aces[i]; + + if (ace->flags & SEC_ACE_FLAG_INHERIT_ONLY) { + continue; + } + + if (!security_token_has_sid(token, &ace->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: + /* check only in case we have provided a tree, + * the ACE has an object type and that type + * is in the tree */ + type = get_ace_object_type(ace); + + if (!tree) + continue; + + if (!type) + node = tree; + else + if (!(node = get_object_tree_by_GUID(tree, type))) + continue; + + if (ace->type == SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT){ + object_tree_modify_access(node, ace->access_mask); + } + else { + if (node->remaining_access & ace->access_mask){ + return NT_STATUS_ACCESS_DENIED; + } + } + 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/source4/libcli/security/config.mk b/source4/libcli/security/config.mk index ca545f817f..f1ca20a2e8 100644 --- a/source4/libcli/security/config.mk +++ b/source4/libcli/security/config.mk @@ -2,6 +2,7 @@ PUBLIC_DEPENDENCIES = LIBNDR LIBSECURITY_COMMON LIBSECURITY_OBJ_FILES = $(addprefix $(libclisrcdir)/security/, \ - security_token.o access_check.o privilege.o sddl.o create_descriptor.o) \ + security_token.o access_check.o privilege.o sddl.o \ + create_descriptor.o object_tree.o) $(eval $(call proto_header_template,$(libclisrcdir)/security/proto.h,$(LIBSECURITY_OBJ_FILES:.o=.c))) diff --git a/source4/libcli/security/create_descriptor.c b/source4/libcli/security/create_descriptor.c index 6a928273b7..1054479f28 100644 --- a/source4/libcli/security/create_descriptor.c +++ b/source4/libcli/security/create_descriptor.c @@ -29,6 +29,18 @@ #include "includes.h" #include "libcli/security/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 @@ -63,6 +75,328 @@ uint32_t map_generic_rights_ds(uint32_t access_mask) return access_mask; } +/* Not sure what this has to be, +* and it does not seem to have any influence */ +static bool object_in_list(struct GUID *object_list, struct GUID *object) +{ + return true; +} + + +static bool contains_inheritable_aces(struct security_acl *acl) +{ + int i; + if (!acl) + return false; + + for (i=0; i < acl->num_aces; i++) { + struct security_ace *ace = &acl->aces[i]; + if ((ace->flags & SEC_ACE_FLAG_CONTAINER_INHERIT) || + (ace->flags & SEC_ACE_FLAG_OBJECT_INHERIT)) + return true; + } + + return false; +} + +static struct security_acl *preprocess_creator_acl(TALLOC_CTX *mem, struct security_acl *acl) +{ + int i; + struct security_acl *new_acl = talloc_zero(mem, struct security_acl); + + new_acl->revision = acl->revision; + for (i=0; i < acl->num_aces; i++) { + struct security_ace *ace = &acl->aces[i]; + if (!(ace->flags & SEC_ACE_FLAG_INHERITED_ACE)){ + new_acl->aces = talloc_realloc(new_acl, new_acl->aces, struct security_ace, + new_acl->num_aces+1); + if (new_acl->aces == NULL) { + talloc_free(new_acl); + return NULL; + } + new_acl->aces[new_acl->num_aces] = *ace; + /*memcpy(&new_acl->aces[new_acl->num_aces], ace, + sizeof(struct security_ace)); */ + new_acl->num_aces++; + } + } + if (new_acl) + new_acl->revision = acl->revision; + /* Todo what to do if all were inherited and this is empty */ + return new_acl; +} + +/* This is not exactly as described in the docs. The original seemed to return + * only a list of the inherited or flagless ones... */ + +static bool postprocess_acl(struct security_acl *acl, + struct dom_sid *owner, + struct dom_sid *group, + uint32_t (*generic_map)(uint32_t access_mask)) +{ + int i; + struct dom_sid *co, *cg; + TALLOC_CTX *tmp_ctx = talloc_new(acl); + if (!generic_map){ + return false; + } + co = dom_sid_parse_talloc(tmp_ctx, SID_CREATOR_OWNER); + cg = dom_sid_parse_talloc(tmp_ctx, SID_CREATOR_GROUP); + for (i=0; i < acl->num_aces; i++){ + struct security_ace *ace = &acl->aces[i]; + if (!(ace->flags == 0 || ace->flags & SEC_ACE_FLAG_INHERITED_ACE)) + continue; + if (ace->flags & SEC_ACE_FLAG_INHERIT_ONLY) + continue; + if (dom_sid_equal(&ace->trustee, co)){ + ace->trustee = *owner; + /* perhaps this should be done somewhere else? */ + ace->flags &= ~SEC_ACE_FLAG_CONTAINER_INHERIT; + } + if (dom_sid_equal(&ace->trustee, cg)){ + ace->trustee = *group; + ace->flags &= ~SEC_ACE_FLAG_CONTAINER_INHERIT; + } + ace->access_mask = generic_map(ace->access_mask); + } + + talloc_free(tmp_ctx); + return true; +} + +static struct security_acl *calculate_inherited_from_parent(TALLOC_CTX *mem_ctx, + struct security_acl *acl, + bool is_container, + struct GUID *object_list) +{ + int i; + TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx); + struct security_acl *tmp_acl = talloc_zero(tmp_ctx, struct security_acl); + struct security_acl *inh_acl = talloc_zero(tmp_ctx, struct security_acl); + struct security_acl *new_acl; + struct dom_sid *co, *cg; + if (!tmp_acl || !inh_acl) + return NULL; + + co = dom_sid_parse_talloc(tmp_ctx, SID_CREATOR_OWNER); + cg = dom_sid_parse_talloc(tmp_ctx, SID_CREATOR_GROUP); + + for (i=0; i < acl->num_aces; i++){ + struct security_ace *ace = &acl->aces[i]; + if (ace->flags & SEC_ACE_FLAG_INHERIT_ONLY) + continue; + + if ((ace->flags & SEC_ACE_FLAG_CONTAINER_INHERIT) || + (ace->flags & SEC_ACE_FLAG_OBJECT_INHERIT)){ + 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_acl->aces[tmp_acl->num_aces] = *ace; + tmp_acl->aces[tmp_acl->num_aces].flags |= SEC_ACE_FLAG_INHERITED_ACE; + + if (is_container && (ace->flags & SEC_ACE_FLAG_OBJECT_INHERIT)) + tmp_acl->aces[tmp_acl->num_aces].flags |= SEC_ACE_FLAG_INHERIT_ONLY; + + if (ace->type == SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT || + ace->type == SEC_ACE_TYPE_ACCESS_DENIED_OBJECT){ + if (!object_in_list(object_list, &ace->object.object.type.type)){ + tmp_acl->aces[tmp_acl->num_aces].flags |= SEC_ACE_FLAG_INHERIT_ONLY; + } + + } + tmp_acl->num_aces++; + } + } + + if (is_container){ + for (i=0; i < acl->num_aces; i++){ + struct security_ace *ace = &acl->aces[i]; + + if (ace->flags & SEC_ACE_FLAG_NO_PROPAGATE_INHERIT) + continue; + if (!dom_sid_equal(&ace->trustee, co) && !dom_sid_equal(&ace->trustee, cg)) + continue; + + if ((ace->flags & SEC_ACE_FLAG_CONTAINER_INHERIT) || + (ace->flags & SEC_ACE_FLAG_OBJECT_INHERIT)){ + inh_acl->aces = talloc_realloc(inh_acl, inh_acl->aces, struct security_ace, + inh_acl->num_aces+1); + if (inh_acl->aces == NULL){ + talloc_free(tmp_ctx); + return NULL; + } + inh_acl->aces[inh_acl->num_aces] = *ace; + inh_acl->aces[inh_acl->num_aces].flags |= SEC_ACE_FLAG_INHERIT_ONLY; + inh_acl->aces[inh_acl->num_aces].flags |= SEC_ACE_FLAG_INHERITED_ACE; + inh_acl->num_aces++; + } + } + } + new_acl = security_acl_concatenate(mem_ctx,tmp_acl, inh_acl); + if (new_acl) + new_acl->revision = acl->revision; + talloc_free(tmp_ctx); + return new_acl; +} + +/* In the docs this looks == calculate_inherited_from_parent. However, + * It shouldn't return the inherited, rather filter them out.... + */ +static struct security_acl *calculate_inherited_from_creator(TALLOC_CTX *mem_ctx, + struct security_acl *acl, + bool is_container, + struct GUID *object_list) +{ + int i; + TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx); + struct security_acl *tmp_acl = talloc_zero(tmp_ctx, struct security_acl); +/* struct security_acl *inh_acl = talloc_zero(tmp_ctx, struct security_acl); */ + struct security_acl *new_acl; + struct dom_sid *co, *cg; + + if (!tmp_acl) + return NULL; + + co = dom_sid_parse_talloc(tmp_ctx, SID_CREATOR_OWNER); + cg = dom_sid_parse_talloc(tmp_ctx, SID_CREATOR_GROUP); + + for (i=0; i < acl->num_aces; i++){ + struct security_ace *ace = &acl->aces[i]; + if (ace->flags & SEC_ACE_FLAG_INHERITED_ACE) + 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->aces[tmp_acl->num_aces].flags = 0; + tmp_acl->num_aces++; + + if (!dom_sid_equal(&ace->trustee, co) && !dom_sid_equal(&ace->trustee, cg)) + 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->aces[tmp_acl->num_aces].flags |= SEC_ACE_FLAG_INHERIT_ONLY; + tmp_acl->num_aces++; + } + new_acl = security_acl_dup(mem_ctx,tmp_acl); + + talloc_free(tmp_ctx); + return new_acl; +} + +static bool compute_acl(int acl_type, + 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 *p_acl = NULL, *c_acl = NULL, **new_acl; + if (acl_type == SEC_DESC_DACL_PRESENT){ + if (parent_sd) + p_acl = parent_sd->dacl; + if (creator_sd) + c_acl = creator_sd->dacl; + new_acl = &new_sd->dacl; + } + else{ + if (parent_sd) + p_acl = parent_sd->sacl; + if (creator_sd) + c_acl = creator_sd->sacl; + new_acl = &new_sd->sacl; + } + if (contains_inheritable_aces(p_acl)){ + if (!c_acl || (c_acl && inherit_flags & SEC_DEFAULT_DESCRIPTOR)){ + *new_acl = calculate_inherited_from_parent(new_sd, + p_acl, + is_container, + object_list); + if (*new_acl == NULL) + goto final; + if (!postprocess_acl(*new_acl, new_sd->owner_sid, + new_sd->group_sid, generic_map)) + return false; + else + goto final; + } + if (c_acl && !(inherit_flags & SEC_DEFAULT_DESCRIPTOR)){ + struct security_acl *pr_acl, *tmp_acl, *tpr_acl; + tpr_acl = preprocess_creator_acl(new_sd, c_acl); + tmp_acl = calculate_inherited_from_creator(new_sd, + tpr_acl, + is_container, + object_list); + /* Todo some refactoring here! */ + if (acl_type == SEC_DESC_DACL_PRESENT && + !(creator_sd->type & SECINFO_PROTECTED_DACL) && + (inherit_flags & SEC_DACL_AUTO_INHERIT)){ + pr_acl = calculate_inherited_from_parent(new_sd, + p_acl, + is_container, + object_list); + + *new_acl = security_acl_concatenate(new_sd, tmp_acl, pr_acl); + new_sd->type |= SEC_DESC_DACL_AUTO_INHERITED; + } + else if (acl_type == SEC_DESC_SACL_PRESENT && + !(creator_sd->type & SECINFO_PROTECTED_SACL) && + (inherit_flags & SEC_SACL_AUTO_INHERIT)){ + pr_acl = calculate_inherited_from_parent(new_sd, + p_acl, + is_container, + object_list); + + *new_acl = security_acl_concatenate(new_sd, tmp_acl, pr_acl); + new_sd->type |= SEC_DESC_SACL_AUTO_INHERITED; + } + } + if (*new_acl == NULL) + goto final; + if (!postprocess_acl(*new_acl, new_sd->owner_sid, + new_sd->group_sid,generic_map)) + return false; + else + goto final; + } + else{ + if (!c_acl){ + if (acl_type == SEC_DESC_DACL_PRESENT && token->default_dacl) + *new_acl = security_acl_dup(new_sd, token->default_dacl); + } + else{ + *new_acl = preprocess_creator_acl(new_sd,c_acl); + if (*new_acl == NULL) + goto final; + if (!postprocess_acl(*new_acl, new_sd->owner_sid, + new_sd->group_sid,generic_map)) + return false; + else + goto final; + } + } +final: + if (acl_type == SEC_DESC_DACL_PRESENT && new_sd->dacl) + new_sd->type |= SEC_DESC_DACL_PRESENT; + + if (acl_type == SEC_DESC_SACL_PRESENT && new_sd->sacl) + new_sd->type |= SEC_DESC_SACL_PRESENT; + /* 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; + return true; +} + struct security_descriptor *create_security_descriptor(TALLOC_CTX *mem_ctx, struct security_descriptor *parent_sd, struct security_descriptor *creator_sd, @@ -108,10 +442,20 @@ struct security_descriptor *create_security_descriptor(TALLOC_CTX *mem_ctx, talloc_free(new_sd); return NULL; } - /* Todo remove */ - if (creator_sd && creator_sd->type & SEC_DESC_DACL_PRESENT){ - new_sd->dacl = security_acl_dup(new_sd, creator_sd->dacl); - new_sd->type |= SEC_DESC_DACL_PRESENT; + + if (!compute_acl(SEC_DESC_DACL_PRESENT, parent_sd, creator_sd, + is_container, inherit_flags, object_list, + generic_map,token,new_sd)){ + talloc_free(new_sd); + return NULL; } + + if (!compute_acl(SEC_DESC_SACL_PRESENT, 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/source4/libcli/security/object_tree.c b/source4/libcli/security/object_tree.c new file mode 100644 index 0000000000..8a90019a59 --- /dev/null +++ b/source4/libcli/security/object_tree.c @@ -0,0 +1,106 @@ +/* + 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 "lib/util/dlinklist.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 + */ + +struct object_tree * insert_in_object_tree(TALLOC_CTX *mem_ctx, + const struct GUID *schemaGUIDID, + const struct GUID *attributeSecurityGUID, + uint32_t init_access, + struct object_tree *root) +{ + struct object_tree * parent = NULL; + struct object_tree * new_node; + + new_node = talloc(mem_ctx, struct object_tree); + if (!new_node) + return NULL; + memset(new_node, 0, sizeof(struct object_tree)); + new_node->remaining_access = init_access; + + if (!root){ + memcpy(&new_node->guid, schemaGUIDID, sizeof(struct GUID)); + return new_node; + } + + if (attributeSecurityGUID && !GUID_all_zero(attributeSecurityGUID)){ + parent = get_object_tree_by_GUID(root, attributeSecurityGUID); + memcpy(&new_node->guid, attributeSecurityGUID, sizeof(struct GUID)); + } + else + memcpy(&new_node->guid, schemaGUIDID, sizeof(struct GUID)); + + if (!parent) + parent = root; + + new_node->remaining_access = init_access; + DLIST_ADD(parent, new_node); + return new_node; +} + +/* search by GUID */ +struct object_tree * get_object_tree_by_GUID(struct object_tree *root, + const struct GUID *guid) +{ + struct object_tree *p; + struct object_tree *result = NULL; + + if (!root || GUID_equal(&root->guid, guid)) + result = root; + else{ + for (p = root->children; p != NULL; p = p->next) + if ((result = get_object_tree_by_GUID(p, guid))) + break; + } + + return result; +} + +/* Change the granted access per each ACE */ + +void object_tree_modify_access(struct object_tree *root, + uint32_t access) +{ + struct object_tree *p; + if (root){ + root->remaining_access &= ~access; + } + + for (p = root->children; p != NULL; p = p->next) + object_tree_modify_access(p, access); +} diff --git a/source4/libcli/security/security.h b/source4/libcli/security/security.h index 3cfa484816..18f6c820d1 100644 --- a/source4/libcli/security/security.h +++ b/source4/libcli/security/security.h @@ -29,6 +29,15 @@ enum security_user_level { struct auth_session_info; +struct object_tree { + uint32_t remaining_access; + struct GUID guid; + /* linked list of children */ + struct object_tree * children; + struct object_tree * prev; + struct object_tree * next; +}; + /* 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" diff --git a/source4/scripting/python/samba/provision.py b/source4/scripting/python/samba/provision.py index 9a41709830..64491c2b18 100644 --- a/source4/scripting/python/samba/provision.py +++ b/source4/scripting/python/samba/provision.py @@ -73,6 +73,39 @@ def find_setup_dir(): return ret raise Exception("Unable to find setup directory.") +def get_schema_descriptor(domain_sid): + sddl = "O:SAG:SAD:(A;CI;RPLCLORC;;;AU)(A;CI;RPWPCRCCLCLORCWOWDSW;;;SA)" \ + "(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)" \ + "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \ + "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \ + "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \ + "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \ + "S:(AU;SA;WPCCDCWOWDSDDTSW;;;WD)" \ + "(AU;CISA;WP;;;WD)(AU;SA;CR;;;BA)" \ + "(AU;SA;CR;;;DU)(OU;SA;CR;e12b56b6-0a95-11d1-adbb-00c04fd8d5cd;;WD)" \ + "(OU;SA;CR;45ec5156-db7e-47bb-b53f-dbeb2d03c40f;;WD)" + sec = security.descriptor.from_sddl(sddl, domain_sid) + return b64encode(ndr_pack(sec)) + +def get_config_descriptor(domain_sid): + sddl = "O:EAG:EAD:(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \ + "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \ + "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \ + "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \ + "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \ + "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \ + "(A;;RPLCLORC;;;AU)(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \ + "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;CIIO;RPWPCRCCLCLORCWOWDSDSW;;;DA)" \ + "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \ + "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \ + "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \ + "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \ + "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;S-1-5-21-3191434175-1265308384-3577286990-498)" \ + "S:(AU;SA;WPWOWD;;;WD)(AU;SA;CR;;;BA)(AU;SA;CR;;;DU)" \ + "(OU;SA;CR;45ec5156-db7e-47bb-b53f-dbeb2d03c40f;;WD)" + sec = security.descriptor.from_sddl(sddl, domain_sid) + return b64encode(ndr_pack(sec)) + DEFAULTSITE = "Default-First-Site-Name" @@ -144,7 +177,7 @@ class ProvisionResult(object): self.samdb = None class Schema(object): - def __init__(self, setup_path, schemadn=None, + def __init__(self, setup_path, domain_sid, schemadn=None, serverdn=None, sambadn=None, ldap_backend_type=None): """Load schema for the SamDB from the AD schema files and samba4_schema.ldif @@ -167,8 +200,11 @@ class Schema(object): {"SCHEMADN": schemadn, "SERVERDN": serverdn, }) + + descr = get_schema_descriptor(domain_sid) self.schema_dn_add = read_and_sub_file(setup_path("provision_schema_basedn.ldif"), - {"SCHEMADN": schemadn + {"SCHEMADN": schemadn, + "DESCRIPTOR": descr }) prefixmap = open(setup_path("prefixMap.txt"), 'r').read() @@ -578,6 +614,7 @@ def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info, # - each partition has its own module list then modules_list = ["resolve_oids", "rootdse", + "acl", "paged_results", "ranged_results", "anr", @@ -907,7 +944,7 @@ def setup_samdb(path, setup_path, session_info, credentials, lp, ldap_backend=ldap_backend, serverrole=serverrole) if (schema == None): - schema = Schema(setup_path, schemadn=names.schemadn, serverdn=names.serverdn, + schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn, sambadn=names.sambadn, ldap_backend_type=ldap_backend.ldap_backend_type) # Load the database, but importantly, use Ldb not SamDB as we don't want to load the global schema @@ -988,8 +1025,10 @@ def setup_samdb(path, setup_path, session_info, credentials, lp, }) message("Adding configuration container") + descr = get_config_descriptor(domainsid); setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), { "CONFIGDN": names.configdn, + "DESCRIPTOR": descr, }) message("Modifying configuration container") setup_modify_ldif(samdb, setup_path("provision_configuration_basedn_modify.ldif"), { @@ -1109,7 +1148,7 @@ def provision(setup_dir, message, session_info, """ def setup_path(file): - return os.path.join(setup_dir, file) + return os.path.join(setup_dir, file) if domainsid is None: domainsid = security.random_sid() @@ -1192,7 +1231,7 @@ def provision(setup_dir, message, session_info, ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="") - schema = Schema(setup_path, schemadn=names.schemadn, serverdn=names.serverdn, + schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn, sambadn=names.sambadn, ldap_backend_type=ldap_backend_type) secrets_credentials = credentials diff --git a/source4/selftest/knownfail b/source4/selftest/knownfail index d4ad716d6a..fcb16c9814 100644 --- a/source4/selftest/knownfail +++ b/source4/selftest/knownfail @@ -56,4 +56,5 @@ samba4.winbind.struct.*.LOOKUP_NAME_SID # Not yet working in winbind ^samba4.*base.delaywrite.*update of write time using SET_END_OF_FILE$ ^samba4.*base.delaywrite.*update of write time using SET_ALLOCATION_SIZE$ ^samba4.ldap.python \(dc\).Test add_ldif\(\) with BASE64 security descriptor input using WRONG domain SID$ -^samba4.ldap.secdesc.python
\ No newline at end of file +^samba4.ldap.python \(dc\).Testing ldb.add_ldif\(\) for nTSecurityDescriptor +^samba4.ldap.secdesc.python diff --git a/source4/setup/provision_configuration_basedn.ldif b/source4/setup/provision_configuration_basedn.ldif index f009113f10..b385359a61 100644 --- a/source4/setup/provision_configuration_basedn.ldif +++ b/source4/setup/provision_configuration_basedn.ldif @@ -5,3 +5,4 @@ dn: ${CONFIGDN} objectClass: top objectClass: configuration cn: Configuration +nTSecurityDescriptor:: ${DESCRIPTOR} diff --git a/source4/setup/provision_schema_basedn.ldif b/source4/setup/provision_schema_basedn.ldif index 8c7ce88bac..5301a11965 100644 --- a/source4/setup/provision_schema_basedn.ldif +++ b/source4/setup/provision_schema_basedn.ldif @@ -5,3 +5,4 @@ dn: ${SCHEMADN} objectClass: top objectClass: dMD cn: Schema +nTSecurityDescriptor:: ${DESCRIPTOR} |