diff options
author | Nadezhda Ivanova <nadezhda.ivanova@postpath.com> | 2009-09-21 17:27:50 -0700 |
---|---|---|
committer | Nadezhda Ivanova <nadezhda.ivanova@postpath.com> | 2009-09-21 17:27:50 -0700 |
commit | 10c6f3f71a4fe3e36e2a0476dc0077187371fafb (patch) | |
tree | 927a846bae4922c8eb6dea848479ddcd54814a21 | |
parent | 13b979b03d86f3ae43dc5fd539fa5d3f22f579a0 (diff) | |
download | samba-10c6f3f71a4fe3e36e2a0476dc0077187371fafb.tar.gz samba-10c6f3f71a4fe3e36e2a0476dc0077187371fafb.tar.bz2 samba-10c6f3f71a4fe3e36e2a0476dc0077187371fafb.zip |
Initial Implementation of the DS objects access checks.
Currently disabled. The search will be greatly modified,
also the object tree stuff will be simplified.
-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 | ||||
-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/object_tree.c | 106 | ||||
-rw-r--r-- | source4/libcli/security/security.h | 9 | ||||
-rw-r--r-- | source4/scripting/python/samba/provision.py | 1 |
9 files changed, 1441 insertions, 1 deletions
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/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/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 2d3e04eac1..64491c2b18 100644 --- a/source4/scripting/python/samba/provision.py +++ b/source4/scripting/python/samba/provision.py @@ -614,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", |