From 1fc47e1228b7f32ad0d6636d09d63d3b1c124aaa Mon Sep 17 00:00:00 2001 From: Nadezhda Ivanova Date: Thu, 5 Nov 2009 17:34:12 +0200 Subject: Version 1.0 of the directory service acls module. At this point, support for checks on LDAP add, delete, rename and modify. Old kludge_acl is still there to handle the searches. This module is synchronous as the async version was impossible to debug, will be converted to async after some user testing. --- source4/dsdb/samdb/ldb_modules/acl.c | 1305 +++++++-------------------- source4/dsdb/samdb/ldb_modules/kludge_acl.c | 4 +- source4/dsdb/schema/schema_query.c | 14 +- source4/libcli/security/object_tree.c | 101 ++- source4/libcli/security/security.h | 6 +- source4/scripting/python/samba/provision.py | 2 +- 6 files changed, 419 insertions(+), 1013 deletions(-) diff --git a/source4/dsdb/samdb/ldb_modules/acl.c b/source4/dsdb/samdb/ldb_modules/acl.c index 2f123145db..f96d4294c5 100644 --- a/source4/dsdb/samdb/ldb_modules/acl.c +++ b/source4/dsdb/samdb/ldb_modules/acl.c @@ -40,30 +40,6 @@ #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; @@ -73,199 +49,6 @@ 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; @@ -274,559 +57,306 @@ bool is_root_base_dn(struct ldb_context *ldb, struct ldb_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) +static enum security_user_level what_is_user(struct ldb_module *module) { - 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; + 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 int acl_access_check_modify(struct ldb_reply *ares, struct acl_context *ac, - struct security_descriptor *sd) +static struct security_token *acl_user_token(struct ldb_module *module) { - 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; + 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; } - 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; + return session_info->security_token; } -static int acl_access_check_delete(struct ldb_reply *ares, struct acl_context *ac, - struct security_descriptor *sd) +static int acl_module_init(struct ldb_module *module) { - 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; -} + struct ldb_context *ldb; + struct acl_private *data; + int ret; -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; + ldb = ldb_module_get_ctx(module); - if (ac->user_type == SECURITY_SYSTEM || ac->user_type == SECURITY_ANONYMOUS) { - return LDB_SUCCESS;/*FIXME: we have anonymous access*/ + 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; } - 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; - } - } + 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 ac->sec_result; + return ldb_next_init(module); } -static int acl_perform_access_check(struct ldb_request *req, struct ldb_reply *ares, - struct acl_context *ac) +static int get_sd_from_result(TALLOC_CTX *mem_ctx, + struct ldb_result *acl_res, + struct security_descriptor **sd) { - struct ldb_message_element *oc_el; - struct security_descriptor *sd; + struct ldb_message_element *sd_element; enum ndr_err_code ndr_err; - oc_el = ldb_msg_find_element(ares->message, "ntSecurityDescriptor"); - if (!oc_el || oc_el->num_values == 0) + sd_element = ldb_msg_find_element(acl_res->msgs[0], "ntSecurityDescriptor"); + if (!sd_element) { 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, + *sd = talloc(mem_ctx, struct security_descriptor); + if(!*sd) { + return LDB_ERR_OPERATIONS_ERROR; + } + ndr_err = ndr_pull_struct_blob(&sd_element->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); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + return LDB_ERR_OPERATIONS_ERROR; } + return LDB_SUCCESS; } -static int acl_forward_add(struct ldb_reply *ares, - struct acl_context *ac) +static const struct GUID *get_oc_guid_from_result(struct ldb_module *module, + struct ldb_result *acl_res) { - 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); -} + struct ldb_message_element *oc_el; + struct ldb_context *ldb = ldb_module_get_ctx(module); -static int acl_forward_modify(struct ldb_reply *ares, - struct acl_context *ac) -{ - struct ldb_request *newreq; - struct ldb_context *ldb; - int ret; + oc_el = ldb_msg_find_element(acl_res->msgs[0], "objectClass"); + if (!oc_el) { + return NULL; + } - 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); + return class_schemaid_guid_by_lDAPDisplayName(dsdb_get_schema(ldb), + (char *)oc_el->values[oc_el->num_values-1].data); } -static int acl_forward_delete(struct ldb_reply *ares, - struct acl_context *ac) +static void acl_debug(struct security_descriptor *sd, + struct security_token *token, + struct ldb_dn *dn, + bool denied, + int level) { - 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); -} + if (denied) { + DEBUG(level, ("Access on %s denied", ldb_dn_get_linearized(dn))); + } else { + DEBUG(level, ("Access on %s granted", ldb_dn_get_linearized(dn))); + } -static int acl_forward_rename(struct ldb_reply *ares, - struct acl_context *ac) -{ - return LDB_SUCCESS; + DEBUG(level,("Security context: %s\n", + ndr_print_struct_string(0,(ndr_print_fn_t)ndr_print_security_token,"", token))); + DEBUG(level,("Security descriptor: %s\n", + ndr_print_struct_string(0,(ndr_print_fn_t)ndr_print_security_descriptor,"", sd))); } -static int acl_forward_search(struct acl_context *ac) +static int check_access_on_dn(struct ldb_module *module, + TALLOC_CTX *mem_ctx, + struct ldb_dn *dn, + uint32_t access, + struct object_tree *tree) { 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; - } + struct ldb_context *ldb = ldb_module_get_ctx(module); + struct ldb_result *acl_res; + struct security_descriptor *sd = NULL; + NTSTATUS status; + uint32_t access_granted; + static const char *acl_attrs[] = { + "nTSecurityDescriptor", + NULL + }; + + ret = ldb_search(ldb, mem_ctx, &acl_res, dn, LDB_SCOPE_BASE, acl_attrs, NULL); + /* we sould be able to find the parent */ + if (ret != LDB_SUCCESS) { + DEBUG(10,("acl: failed to find object %s\n", ldb_dn_get_linearized(dn))); + return ret; } - 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); + + ret = get_sd_from_result(mem_ctx, acl_res, &sd); if (ret != LDB_SUCCESS) { - return LDB_ERR_OPERATIONS_ERROR; + 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; - } + /* Theoretically we pass the check if the object has no sd */ + if (!sd) { + return LDB_SUCCESS; } - 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); + status = sec_access_check_ds(sd, acl_user_token(module), + access, + &access_granted, + tree); + if (!NT_STATUS_IS_OK(status)) { + acl_debug(sd, + acl_user_token(module), + dn, + true, + 10); + return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS; } return LDB_SUCCESS; } -static int acl_visible_callback(struct ldb_request *req, struct ldb_reply *ares) +static int acl_add(struct ldb_module *module, struct ldb_request *req) { - struct acl_context *ac; - - ac = talloc_get_type(req->context, struct acl_context); + int ret; + struct ldb_dn *parent = ldb_dn_get_parent(req, req->op.add.message->dn); + struct ldb_context *ldb; + struct ldb_message_element *oc_el; + const struct GUID *guid; + struct object_tree *root = NULL; + struct object_tree *new_node = NULL; - if (!ares) { - return ldb_module_done(ac->req, NULL, NULL, - LDB_ERR_OPERATIONS_ERROR); + if (what_is_user(module) == SECURITY_SYSTEM) { + return ldb_next_request(module, req); } - if (ares->error != LDB_SUCCESS) { - return ldb_module_done(ac->req, ares->controls, - ares->response, ares->error); + if (ldb_dn_is_special(req->op.add.message->dn)) { + return ldb_next_request(module, req); } - - 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; + ldb = ldb_module_get_ctx(module); + /* Creating an NC. There is probably something we should do here, + * but we will establish that later */ + if ((ldb_dn_compare(req->op.add.message->dn, (ldb_get_schema_basedn(ldb))) == 0) || + (ldb_dn_compare(req->op.add.message->dn, (ldb_get_config_basedn(ldb))) == 0) || + (ldb_dn_compare(req->op.add.message->dn, (ldb_get_root_basedn(ldb))) == 0)) { + return ldb_next_request(module, req); } - 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; + oc_el = ldb_msg_find_element(req->op.add.message, "objectClass"); + if (!oc_el || oc_el->num_values == 0) { + DEBUG(10,("acl:operation error %s\n", ldb_dn_get_linearized(req->op.add.message->dn))); + return ldb_module_done(req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR); } - 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); + guid = class_schemaid_guid_by_lDAPDisplayName(dsdb_get_schema(ldb), + (char *)oc_el->values[oc_el->num_values-1].data); - 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)"; + if (!insert_in_object_tree(req, guid, SEC_ADS_CREATE_CHILD, &root, &new_node)) { + return LDB_ERR_OPERATIONS_ERROR; } - 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); + ret = check_access_on_dn(module, req, parent, SEC_ADS_CREATE_CHILD, root); 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; + return ret; } - 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); + return ldb_next_request(module, req); } -static int acl_add(struct ldb_module *module, struct ldb_request *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.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) + struct ldb_context *ldb = ldb_module_get_ctx(module); + const struct dsdb_schema *schema = dsdb_get_schema(ldb); + int i; + const struct GUID *guid; + uint32_t access_granted; + struct object_tree *root = NULL; + struct object_tree *new_node = NULL; + NTSTATUS status; + struct ldb_result *acl_res; + struct security_descriptor *sd; + TALLOC_CTX *tmp_ctx = talloc_new(req); + static const char *acl_attrs[] = { + "nTSecurityDescriptor", + "objectClass", + NULL + }; + + DEBUG(10, ("ldb:acl_modify: %s\n", req->op.mod.message->elements[0].name)); + if (what_is_user(module) == SECURITY_SYSTEM) { 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) + if (ldb_dn_is_special(req->op.mod.message->dn)) { 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); -} + ret = ldb_search(ldb, req, &acl_res, req->op.mod.message->dn, + LDB_SCOPE_BASE, acl_attrs, NULL); -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; + if (ret != LDB_SUCCESS) { + return ret; + } - ldb = ldb_module_get_ctx(module); - data = talloc_get_type(ldb_module_get_private(module), struct acl_private); + ret = get_sd_from_result(req, acl_res, &sd); + if (ret != LDB_SUCCESS) { + DEBUG(10, ("acl_modify: cannot get descriptor\n")); + return ret; + } + /* Theoretically we pass the check if the object has no sd */ + if (!sd) { + return LDB_SUCCESS; + } - if (!data->perform_check) - return ldb_next_request(module, req); + guid = get_oc_guid_from_result(module,acl_res); + if (!guid) { + DEBUG(10, ("acl_modify: cannot get guid\n")); + goto fail; + } - ac = talloc(req, struct acl_context); - if (ac == NULL) { - return LDB_ERR_OPERATIONS_ERROR; + if (!insert_in_object_tree(tmp_ctx, guid, SEC_ADS_WRITE_PROP, + &root, &new_node)) { + DEBUG(10, ("acl_modify: cannot add to object tree\n")); + goto fail; } + for (i=0; i < req->op.mod.message->num_elements; i++){ + const struct dsdb_attribute *attr; + /* clearTextPassword is not in schema */ + if (strcmp("clearTextPassword", req->op.mod.message->elements[i].name) == 0) { + attr = dsdb_attribute_by_lDAPDisplayName(schema, "unicodePwd"); + } else { + attr = dsdb_attribute_by_lDAPDisplayName(schema, + req->op.mod.message->elements[i].name); + } -/* TODO Is this really right? */ -/* if (what_is_user(module) == SECURITY_SYSTEM) */ - return ldb_next_request(module, req); + if (!attr) { + DEBUG(10, ("acl_modify: cannot find attribute %s\n", + req->op.mod.message->elements[i].name)); + goto fail; + } + if (!insert_in_object_tree(tmp_ctx, + &attr->attributeSecurityGUID, SEC_ADS_WRITE_PROP, + &new_node, &new_node)) { + DEBUG(10, ("acl_modify: cannot add to object tree securityGUID\n")); + goto fail; + } - 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; + if (!insert_in_object_tree(tmp_ctx, + &attr->schemaIDGUID, SEC_ADS_WRITE_PROP, &new_node, &new_node)) { + DEBUG(10, ("acl_modify: cannot add to object tree attributeGUID\n")); + goto fail; } - return ldb_next_request(module, ac->down_req); } + + status = sec_access_check_ds(sd, acl_user_token(module), + SEC_ADS_WRITE_PROP, + &access_granted, + root); + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(10, ("Object %s nas no write property access\n", + ldb_dn_get_linearized(req->op.mod.message->dn))); + acl_debug(sd, + acl_user_token(module), + req->op.mod.message->dn, + true, + 10); + talloc_free(tmp_ctx); + return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS; + } + + talloc_free(tmp_ctx); return ldb_next_request(module, req); +fail: + talloc_free(tmp_ctx); + return LDB_ERR_OPERATIONS_ERROR; } /* similar to the modify for the time being. @@ -834,319 +364,172 @@ static int acl_modify(struct ldb_module *module, struct ldb_request *req) 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_dn *parent = ldb_dn_get_parent(req, req->op.del.dn); 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) + DEBUG(10, ("ldb:acl_delete: %s\n", ldb_dn_get_linearized(req->op.del.dn))); + if (what_is_user(module) == SECURITY_SYSTEM) { 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) + if (ldb_dn_is_special(req->op.del.dn)) { 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) + /* first check if we have delete object right */ + ret = check_access_on_dn(module, req, req->op.del.dn, SEC_STD_DELETE, NULL); + if (ret == LDB_SUCCESS) { 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); + /* Nope, we don't have delete object. Lets check if we have delete child on the parent */ + /* No parent, so check fails */ + if ((ldb_dn_compare(req->op.del.dn, (ldb_get_schema_basedn(ldb))) == 0) || + (ldb_dn_compare(req->op.del.dn, (ldb_get_config_basedn(ldb))) == 0) || + (ldb_dn_compare(req->op.del.dn, (ldb_get_root_basedn(ldb))) == 0)) { + DEBUG(10,("acl:deleting an NC\n")); + return ldb_module_done(req, NULL, NULL, LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS); } - 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){ + ret = check_access_on_dn(module, req, parent, SEC_ADS_DELETE_CHILD, NULL); + if (ret != LDB_SUCCESS) { return ret; - } - return ldb_next_request(module, ac->down_req); + } + return ldb_next_request(module, req); } -static int acl_search_callback(struct ldb_request *req, struct ldb_reply *ares) +static int acl_rename(struct ldb_module *module, struct ldb_request *req) { + int ret; + struct ldb_dn *oldparent = ldb_dn_get_parent(req, req->op.rename.olddn); + struct ldb_dn *newparent = ldb_dn_get_parent(req, req->op.rename.newdn); 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; + struct security_descriptor *sd = NULL; + struct ldb_result *acl_res; + const struct GUID *guid; + struct object_tree *root = NULL; + struct object_tree *new_node = NULL; + TALLOC_CTX *tmp_ctx = talloc_new(req); 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); + uint32_t access_granted; + static const char *acl_attrs[] = { + "nTSecurityDescriptor", + "objectClass", + NULL + }; + + DEBUG(10, ("ldb:acl_rename: %s\n", ldb_dn_get_linearized(req->op.rename.olddn))); + if (what_is_user(module) == SECURITY_SYSTEM) { + return ldb_next_request(module, req); } - if (ares->error != LDB_SUCCESS) { - return ldb_module_done(ac->req, ares->controls, ares->response, ares->error); + if (ldb_dn_is_special(req->op.rename.olddn)) { + return ldb_next_request(module, req); } + ldb = ldb_module_get_ctx(module); - 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; imessage->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); + /* search to include deleted objects */ + ret = ldb_search(ldb, req, &acl_res, req->op.rename.olddn, + LDB_SCOPE_BASE, acl_attrs, NULL); + /* we sould be able to find the parent */ + if (ret != LDB_SUCCESS) { + DEBUG(10,("acl: failed to find object %s\n", + ldb_dn_get_linearized(req->op.rename.olddn))); + return ret; } - return LDB_SUCCESS; -} + guid = get_oc_guid_from_result(module,acl_res); + if (!insert_in_object_tree(tmp_ctx, guid, SEC_ADS_WRITE_PROP, + &root, &new_node)) { + return LDB_ERR_OPERATIONS_ERROR; + }; -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; + guid = attribute_schemaid_guid_by_lDAPDisplayName(dsdb_get_schema(ldb), + "name"); + if (!insert_in_object_tree(tmp_ctx, guid, SEC_ADS_WRITE_PROP, + &new_node, &new_node)) { + return LDB_ERR_OPERATIONS_ERROR; + }; - ldb = ldb_module_get_ctx(module); - data = talloc_get_type(ldb_module_get_private(module), struct acl_private); + ret = get_sd_from_result(req, acl_res, &sd); - if (!data || !data->perform_check) - return ldb_next_request(module, req); + if (ret != LDB_SUCCESS) { + return LDB_ERR_OPERATIONS_ERROR; + } + /* Theoretically we pass the check if the object has no sd */ + if (!sd) { + return LDB_SUCCESS; + } + status = sec_access_check_ds(sd, acl_user_token(module), + SEC_ADS_WRITE_PROP, + &access_granted, + root); + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(10, ("Object %s nas no wp on name\n", + ldb_dn_get_linearized(req->op.rename.olddn))); + acl_debug(sd, + acl_user_token(module), + req->op.rename.olddn, + true, + 10); + return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS; + } - if (what_is_user(module) == SECURITY_SYSTEM) + if (ldb_dn_compare(oldparent, newparent) == 0) { + /* regular rename, not move, nothing more to do */ 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); + /* What exactly to do in this case? It would fail anyway.. */ + if ((ldb_dn_compare(req->op.rename.newdn, (ldb_get_schema_basedn(ldb))) == 0) || + (ldb_dn_compare(req->op.rename.newdn, (ldb_get_config_basedn(ldb))) == 0) || + (ldb_dn_compare(req->op.rename.newdn, (ldb_get_root_basedn(ldb))) == 0)) { + DEBUG(10,("acl:moving as an NC\n")); + return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS; } + /* new parent should have create child */ + talloc_free(tmp_ctx); + tmp_ctx = talloc_new(req); + root = NULL; + new_node = NULL; + guid = get_oc_guid_from_result(module,acl_res); + if (!guid) { + DEBUG(10,("acl:renamed object has no object class\n")); + return ldb_module_done(req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR); + } + if (!insert_in_object_tree(tmp_ctx, guid, SEC_ADS_CREATE_CHILD, + &root, &new_node)) { + return LDB_ERR_OPERATIONS_ERROR; } + ret = check_access_on_dn(module, req, newparent, SEC_ADS_CREATE_CHILD, root); + if (ret != LDB_SUCCESS) { + DEBUG(10,("acl:access_denied renaming %s", ldb_dn_get_linearized(req->op.rename.olddn))); + return ret; + } + /* do we have delete object on the object? */ - 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); + status = sec_access_check_ds(sd, acl_user_token(module), + SEC_STD_DELETE, + &access_granted, + NULL); - /* allow everybody to read the sequence number */ - if (strcmp(req->op.extended.oid, LDB_EXTENDED_SEQUENCE_NUMBER) == 0) { + if (NT_STATUS_IS_OK(status)) { 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; + /* what about delete child on the current parent */ + ret = check_access_on_dn(module, req, oldparent, SEC_ADS_DELETE_CHILD, NULL); + if (ret != LDB_SUCCESS) { + DEBUG(10,("acl:access_denied renaming %s", ldb_dn_get_linearized(req->op.rename.olddn))); + return ldb_module_done(req, NULL, NULL, ret); } + return ldb_next_request(module, req); } _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/kludge_acl.c b/source4/dsdb/samdb/ldb_modules/kludge_acl.c index 79309e82bf..0cec95b1c2 100644 --- a/source4/dsdb/samdb/ldb_modules/kludge_acl.c +++ b/source4/dsdb/samdb/ldb_modules/kludge_acl.c @@ -527,10 +527,10 @@ done: _PUBLIC_ const struct ldb_module_ops ldb_kludge_acl_module_ops = { .name = "kludge_acl", .search = kludge_acl_search, - .add = kludge_acl_change, +/* .add = kludge_acl_change, .modify = kludge_acl_change, .del = kludge_acl_change, - .rename = kludge_acl_change, + .rename = kludge_acl_change, */ .extended = kludge_acl_extended, .init_context = kludge_acl_init }; diff --git a/source4/dsdb/schema/schema_query.c b/source4/dsdb/schema/schema_query.c index a2d9792a24..3d46cfb3b1 100644 --- a/source4/dsdb/schema/schema_query.c +++ b/source4/dsdb/schema/schema_query.c @@ -419,8 +419,8 @@ const char **dsdb_full_attribute_list(TALLOC_CTX *mem_ctx, /* Return the schemaIDGUID of a class */ -const struct GUID * class_schemaid_guid_by_lDAPDisplayName(const struct dsdb_schema *schema, - const char *name) +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) @@ -428,3 +428,13 @@ const struct GUID * class_schemaid_guid_by_lDAPDisplayName(const struct dsdb_sch return &object_class->schemaIDGUID; } + +const struct GUID *attribute_schemaid_guid_by_lDAPDisplayName(const struct dsdb_schema *schema, + const char *name) +{ + const struct dsdb_attribute *attr = dsdb_attribute_by_lDAPDisplayName(schema, name); + if (!attr) + return NULL; + + return &attr->schemaIDGUID; +} diff --git a/source4/libcli/security/object_tree.c b/source4/libcli/security/object_tree.c index 85b407913c..7c7d644543 100644 --- a/source4/libcli/security/object_tree.c +++ b/source4/libcli/security/object_tree.c @@ -30,7 +30,6 @@ */ #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 @@ -38,69 +37,85 @@ * 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) +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) { - 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 (!guid || GUID_all_zero(guid)){ + return true; } - if (attributeSecurityGUID && !GUID_all_zero(attributeSecurityGUID)){ - parent = get_object_tree_by_GUID(root, attributeSecurityGUID); - memcpy(&new_node->guid, attributeSecurityGUID, sizeof(struct GUID)); + if (!*root){ + *root = talloc_zero(mem_ctx, struct object_tree); + if (!*root) { + return false; + } + (*root)->guid = *guid; + *new_node = *root; + return true; } - 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; + if (!(*root)->children) { + (*root)->children = talloc_array(mem_ctx, struct object_tree, 1); + (*root)->children[0].guid = *guid; + (*root)->children[0].num_of_children = 0; + (*root)->children[0].children = NULL; + (*root)->num_of_children++; + (*root)->children[0].remaining_access = init_access; + *new_node = &((*root)->children[0]); + return true; + } + 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]); + return true; + } + } + (*root)->children = talloc_realloc(mem_ctx, (*root)->children, struct object_tree, + (*root)->num_of_children +1); + (*root)->children[(*root)->num_of_children].guid = *guid; + (*root)->children[(*root)->num_of_children].remaining_access = init_access; + *new_node = &((*root)->children[(*root)->num_of_children]); + (*root)->num_of_children++; + return true; + } + return true; } /* search by GUID */ -struct object_tree * get_object_tree_by_GUID(struct object_tree *root, +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; + int i; - if (!root || GUID_equal(&root->guid, guid)) + 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))) + return result; + } + else if (root->num_of_children > 0) { + for (i = 0; i < root->num_of_children; i++) { + if ((result = get_object_tree_by_GUID(&root->children[i], guid))) break; + } } - return result; } /* Change the granted access per each ACE */ void object_tree_modify_access(struct object_tree *root, - uint32_t access_mask) + uint32_t access) { - struct object_tree *p; - if (root){ - root->remaining_access &= ~access_mask; + root->remaining_access &= ~access; + if (root->num_of_children > 0) { + int i; + for (i = 0; i < root->num_of_children; i++) { + object_tree_modify_access(&root->children[i], access); + } } - - for (p = root->children; p != NULL; p = p->next) - object_tree_modify_access(p, access_mask); } diff --git a/source4/libcli/security/security.h b/source4/libcli/security/security.h index 18f6c820d1..bdf473bcf1 100644 --- a/source4/libcli/security/security.h +++ b/source4/libcli/security/security.h @@ -32,10 +32,8 @@ 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; + int num_of_children; + struct object_tree *children; }; /* Moved the dom_sid functions to the top level dir with manual proto header */ diff --git a/source4/scripting/python/samba/provision.py b/source4/scripting/python/samba/provision.py index 188c5909f6..ffafa2c223 100644 --- a/source4/scripting/python/samba/provision.py +++ b/source4/scripting/python/samba/provision.py @@ -593,7 +593,6 @@ def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info, modules_list = ["resolve_oids", "rootdse", "lazy_commit", - "acl", "paged_results", "ranged_results", "anr", @@ -604,6 +603,7 @@ def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info, "rdn_name", "objectclass", "descriptor", + "acl", "samldb", "password_hash", "operational", -- cgit