X-Git-Url: http://git.samba.org/?a=blobdiff_plain;f=source4%2Fdsdb%2Fsamdb%2Fldb_modules%2Facl.c;h=2db4de58b2486312cf2d1cf1f877114dea128b6d;hb=87f31510475c6debd56ff874130f4f5d48bef9a5;hp=250d62677b90c55a1781a7623c9d1fed180b4f10;hpb=5549190b37a2bfc01f5cbff89da47b7ca4bd84fa;p=samba.git diff --git a/source4/dsdb/samdb/ldb_modules/acl.c b/source4/dsdb/samdb/ldb_modules/acl.c index 250d62677b9..2db4de58b24 100644 --- a/source4/dsdb/samdb/ldb_modules/acl.c +++ b/source4/dsdb/samdb/ldb_modules/acl.c @@ -39,7 +39,10 @@ #include "librpc/gen_ndr/ndr_security.h" #include "param/param.h" #include "dsdb/samdb/ldb_modules/util.h" +#include "dsdb/samdb/ldb_modules/schema.h" #include "lib/util/tsort.h" +#include "system/kerberos.h" +#include "auth/kerberos/kerberos.h" struct extended_access_check_attribute { const char *oa_name; @@ -60,34 +63,18 @@ struct acl_context { bool allowedChildClasses; bool allowedChildClassesEffective; bool sDRightsEffective; + bool userPassword; const char * const *attrs; + struct dsdb_schema *schema; }; -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 struct security_token *acl_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 acl_module_init(struct ldb_module *module) { struct ldb_context *ldb; struct acl_private *data; - int ret, i; - TALLOC_CTX *mem_ctx = talloc_new(module); + int ret; + unsigned int i; + TALLOC_CTX *mem_ctx; static const char *attrs[] = { "passwordAttribute", NULL }; struct ldb_result *res; struct ldb_message *msg; @@ -99,28 +86,28 @@ static int acl_module_init(struct ldb_module *module) 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 ldb_operr(ldb); } data = talloc(module, struct acl_private); if (data == NULL) { - ldb_oom(ldb); - return LDB_ERR_OPERATIONS_ERROR; + return ldb_oom(ldb); } data->password_attrs = NULL; - data->acl_perform = lp_parm_bool(ldb_get_opaque(ldb, "loadparm"), + data->acl_perform = lpcfg_parm_bool(ldb_get_opaque(ldb, "loadparm"), NULL, "acl", "perform", false); ldb_module_set_private(module, data); + mem_ctx = talloc_new(module); if (!mem_ctx) { - ldb_oom(ldb); - return LDB_ERR_OPERATIONS_ERROR; + return ldb_oom(ldb); } - ret = ldb_search(ldb, mem_ctx, &res, - ldb_dn_new(mem_ctx, ldb, "@KLUDGEACL"), - LDB_SCOPE_BASE, attrs, NULL); + ret = dsdb_module_search_dn(module, mem_ctx, &res, + ldb_dn_new(mem_ctx, ldb, "@KLUDGEACL"), + attrs, + DSDB_FLAG_NEXT_MODULE, NULL); if (ret != LDB_SUCCESS) { goto done; } @@ -142,8 +129,7 @@ static int acl_module_init(struct ldb_module *module) data->password_attrs = talloc_array(data, const char *, password_attributes->num_values + 1); if (!data->password_attrs) { talloc_free(mem_ctx); - ldb_oom(ldb); - return LDB_ERR_OPERATIONS_ERROR; + return ldb_oom(ldb); } for (i=0; i < password_attributes->num_values; i++) { data->password_attrs[i] = (const char *)password_attributes->values[i].data; @@ -156,262 +142,22 @@ done: return ldb_next_init(module); } -static int get_sd_from_ldb_message(TALLOC_CTX *mem_ctx, - struct ldb_message *acl_res, - struct security_descriptor **sd) -{ - struct ldb_message_element *sd_element; - enum ndr_err_code ndr_err; - - sd_element = ldb_msg_find_element(acl_res, "nTSecurityDescriptor"); - if (!sd_element) { - *sd = NULL; - return LDB_SUCCESS; - } - *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_ERR_OPERATIONS_ERROR; - } - - return LDB_SUCCESS; -} - -static const struct GUID *get_oc_guid_from_message(struct ldb_module *module, - struct ldb_message *msg) -{ - struct ldb_message_element *oc_el; - struct ldb_context *ldb = ldb_module_get_ctx(module); - - oc_el = ldb_msg_find_element(msg, "objectClass"); - if (!oc_el) { - return NULL; - } - - return class_schemaid_guid_by_lDAPDisplayName(dsdb_get_schema(ldb), - (char *)oc_el->values[oc_el->num_values-1].data); -} - -static int get_dom_sid_from_ldb_message(TALLOC_CTX *mem_ctx, - struct ldb_message *acl_res, - struct dom_sid **sid) -{ - struct ldb_message_element *sid_element; - enum ndr_err_code ndr_err; - - sid_element = ldb_msg_find_element(acl_res, "objectSid"); - if (!sid_element) { - *sid = NULL; - return LDB_SUCCESS; - } - *sid = talloc(mem_ctx, struct dom_sid); - if(!*sid) { - return LDB_ERR_OPERATIONS_ERROR; - } - ndr_err = ndr_pull_struct_blob(&sid_element->values[0], *sid, NULL, *sid, - (ndr_pull_flags_fn_t)ndr_pull_dom_sid); - - if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { - return LDB_ERR_OPERATIONS_ERROR; - } - - return LDB_SUCCESS; -} - - -static void acl_debug(struct security_descriptor *sd, - struct security_token *token, - struct ldb_dn *dn, - bool denied, - int level) -{ - 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))); - } - - 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 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; - struct ldb_context *ldb = ldb_module_get_ctx(module); - struct ldb_result *acl_res; - struct security_descriptor *sd = NULL; - struct dom_sid *sid = NULL; - NTSTATUS status; - uint32_t access_granted; - static const char *acl_attrs[] = { - "nTSecurityDescriptor", - "objectSid", - 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 = get_sd_from_ldb_message(mem_ctx, acl_res->msgs[0], &sd); - 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; - } - ret = get_dom_sid_from_ldb_message(mem_ctx, acl_res->msgs[0], &sid); - if (ret != LDB_SUCCESS) { - return LDB_ERR_OPERATIONS_ERROR; - } - - status = sec_access_check_ds(sd, acl_user_token(module), - access, - &access_granted, - tree, - sid); - 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_check_access_on_attribute(struct ldb_module *module, - TALLOC_CTX *mem_ctx, - struct security_descriptor *sd, - struct dom_sid *rp_sid, - uint32_t access, - const struct dsdb_attribute *attr) -{ - int ret; - NTSTATUS status; - uint32_t access_granted; - struct object_tree *root = NULL; - struct object_tree *new_node = NULL; - TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx); - struct security_token *token = acl_user_token(module); - if (attr) { - if (!GUID_all_zero(&attr->attributeSecurityGUID)) { - if (!insert_in_object_tree(tmp_ctx, - &attr->attributeSecurityGUID, access, - &root, &new_node)) { - DEBUG(10, ("acl_search: cannot add to object tree securityGUID\n")); - goto fail; - } - - if (!insert_in_object_tree(tmp_ctx, - &attr->schemaIDGUID, access, &new_node, &new_node)) { - DEBUG(10, ("acl_search: cannot add to object tree attributeGUID\n")); - goto fail; - } - } - else { - if (!insert_in_object_tree(tmp_ctx, - &attr->schemaIDGUID, access, &root, &new_node)) { - DEBUG(10, ("acl_search: cannot add to object tree attributeGUID\n")); - goto fail; - } - } - } - status = sec_access_check_ds(sd, token, - access, - &access_granted, - root, - rp_sid); - if (!NT_STATUS_IS_OK(status)) { - ret = LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS; - } - else { - ret = LDB_SUCCESS; - } - return ret; -fail: - return LDB_ERR_OPERATIONS_ERROR; -} - -static int acl_check_access_on_class(struct ldb_module *module, - TALLOC_CTX *mem_ctx, - struct security_descriptor *sd, - struct dom_sid *rp_sid, - uint32_t access, - const char *class_name) -{ - int ret; - struct ldb_context *ldb = ldb_module_get_ctx(module); - NTSTATUS status; - uint32_t access_granted; - struct object_tree *root = NULL; - struct object_tree *new_node = NULL; - const struct GUID *guid; - const struct dsdb_schema *schema = dsdb_get_schema(ldb); - TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx); - struct security_token *token = acl_user_token(module); - if (class_name) { - guid = class_schemaid_guid_by_lDAPDisplayName(schema, class_name); - if (!guid) { - DEBUG(10, ("acl_search: cannot find class %s\n", - class_name)); - goto fail; - } - if (!insert_in_object_tree(tmp_ctx, - guid, access, - &root, &new_node)) { - DEBUG(10, ("acl_search: cannot add to object tree guid\n")); - goto fail; - } - } - status = sec_access_check_ds(sd, token, - access, - &access_granted, - root, - rp_sid); - if (!NT_STATUS_IS_OK(status)) { - ret = LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS; - } - else { - ret = LDB_SUCCESS; - } - return ret; -fail: - return LDB_ERR_OPERATIONS_ERROR; -} - static int acl_allowedAttributes(struct ldb_module *module, + const struct dsdb_schema *schema, struct ldb_message *sd_msg, struct ldb_message *msg, struct acl_context *ac) { struct ldb_message_element *oc_el; struct ldb_context *ldb = ldb_module_get_ctx(module); - const struct dsdb_schema *schema = dsdb_get_schema(ldb); TALLOC_CTX *mem_ctx; const char **attr_list; int i, ret; /* If we don't have a schema yet, we can't do anything... */ if (schema == NULL) { - return LDB_SUCCESS; + ldb_asprintf_errstring(ldb, "cannot add allowedAttributes to %s because no schema is loaded", ldb_dn_get_linearized(msg->dn)); + return LDB_ERR_OPERATIONS_ERROR; } /* Must remove any existing attribute */ @@ -421,8 +167,7 @@ static int acl_allowedAttributes(struct ldb_module *module, mem_ctx = talloc_new(msg); if (!mem_ctx) { - ldb_oom(ldb); - return LDB_ERR_OPERATIONS_ERROR; + return ldb_oom(ldb); } oc_el = ldb_msg_find_element(sd_msg, "objectClass"); @@ -455,21 +200,18 @@ static int acl_allowedAttributes(struct ldb_module *module, return LDB_SUCCESS; } - ret = get_sd_from_ldb_message(mem_ctx, sd_msg, &sd); + ret = dsdb_get_sd_from_ldb_message(ldb_module_get_ctx(module), mem_ctx, sd_msg, &sd); if (ret != LDB_SUCCESS) { return ret; } - ret = get_dom_sid_from_ldb_message(mem_ctx, sd_msg, &sid); - if (ret != LDB_SUCCESS) { - return ret; - } + sid = samdb_result_dom_sid(mem_ctx, sd_msg, "objectSid"); for (i=0; attr_list && attr_list[i]; i++) { const struct dsdb_attribute *attr = dsdb_attribute_by_lDAPDisplayName(schema, attr_list[i]); if (!attr) { - return LDB_ERR_OPERATIONS_ERROR; + return ldb_operr(ldb); } /* remove constructed attributes */ if (attr->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED @@ -492,20 +234,21 @@ static int acl_allowedAttributes(struct ldb_module *module, } static int acl_childClasses(struct ldb_module *module, + const struct dsdb_schema *schema, struct ldb_message *sd_msg, struct ldb_message *msg, const char *attrName) { struct ldb_message_element *oc_el; struct ldb_message_element *allowedClasses; - struct ldb_context *ldb = ldb_module_get_ctx(module); - const struct dsdb_schema *schema = dsdb_get_schema(ldb); const struct dsdb_class *sclass; - int i, j, ret; + unsigned int i, j; + int ret; /* If we don't have a schema yet, we can't do anything... */ if (schema == NULL) { - return LDB_SUCCESS; + ldb_asprintf_errstring(ldb_module_get_ctx(module), "cannot add childClassesEffective to %s because no schema is loaded", ldb_dn_get_linearized(msg->dn)); + return LDB_ERR_OPERATIONS_ERROR; } /* Must remove any existing attribute, or else confusion reins */ @@ -545,47 +288,45 @@ static int acl_childClasses(struct ldb_module *module, } static int acl_childClassesEffective(struct ldb_module *module, + const struct dsdb_schema *schema, struct ldb_message *sd_msg, struct ldb_message *msg, struct acl_context *ac) { struct ldb_message_element *oc_el; struct ldb_message_element *allowedClasses = NULL; - struct ldb_context *ldb = ldb_module_get_ctx(module); - const struct dsdb_schema *schema = dsdb_get_schema(ldb); const struct dsdb_class *sclass; struct security_descriptor *sd; struct ldb_control *as_system = ldb_request_get_control(ac->req, LDB_CONTROL_AS_SYSTEM_OID); struct dom_sid *sid = NULL; - int i, j, ret; + unsigned int i, j; + int ret; if (as_system != NULL) { as_system->critical = 0; } if (ac->am_system || as_system) { - return acl_childClasses(module, sd_msg, msg, "allowedChildClassesEffective"); + return acl_childClasses(module, schema, sd_msg, msg, "allowedChildClassesEffective"); } /* If we don't have a schema yet, we can't do anything... */ if (schema == NULL) { - return LDB_SUCCESS; + ldb_asprintf_errstring(ldb_module_get_ctx(module), "cannot add allowedChildClassesEffective to %s because no schema is loaded", ldb_dn_get_linearized(msg->dn)); + return LDB_ERR_OPERATIONS_ERROR; } /* Must remove any existing attribute, or else confusion reins */ ldb_msg_remove_attr(msg, "allowedChildClassesEffective"); oc_el = ldb_msg_find_element(sd_msg, "objectClass"); - ret = get_sd_from_ldb_message(msg, sd_msg, &sd); + ret = dsdb_get_sd_from_ldb_message(ldb_module_get_ctx(module), msg, sd_msg, &sd); if (ret != LDB_SUCCESS) { return ret; } - ret = get_dom_sid_from_ldb_message(msg, sd_msg, &sid); - if (ret != LDB_SUCCESS) { - return ret; - } + sid = samdb_result_dom_sid(msg, sd_msg, "objectSid"); for (i=0; oc_el && i < oc_el->num_values; i++) { sclass = dsdb_class_by_lDAPDisplayName_ldb_val(schema, &oc_el->values[i]); if (!sclass) { @@ -595,6 +336,7 @@ static int acl_childClassesEffective(struct ldb_module *module, for (j=0; sclass->possibleInferiors && sclass->possibleInferiors[j]; j++) { ret = acl_check_access_on_class(module, + schema, msg, sd, sid, @@ -654,15 +396,11 @@ static int acl_sDRightsEffective(struct ldb_module *module, } else { /* Get the security descriptor from the message */ - ret = get_sd_from_ldb_message(msg, sd_msg, &sd); - if (ret != LDB_SUCCESS) { - return ret; - } - ret = get_dom_sid_from_ldb_message(msg, sd_msg, &sid); - + ret = dsdb_get_sd_from_ldb_message(ldb_module_get_ctx(module), msg, sd_msg, &sd); if (ret != LDB_SUCCESS) { return ret; } + sid = samdb_result_dom_sid(msg, sd_msg, "objectSid"); ret = acl_check_access_on_attribute(module, msg, sd, @@ -691,7 +429,211 @@ static int acl_sDRightsEffective(struct ldb_module *module, flags |= SECINFO_SACL; } } - ldb_msg_add_fmt(msg, "sDRightsEffective", "%u", flags); + return samdb_msg_add_uint(ldb_module_get_ctx(module), msg, msg, + "sDRightsEffective", flags); +} + +static int acl_validate_spn_value(TALLOC_CTX *mem_ctx, + struct ldb_context *ldb, + const char *spn_value, + int userAccountControl, + const char *samAccountName, + const char *dnsHostName, + const char *netbios_name, + const char *ntds_guid) +{ + int ret; + krb5_context krb_ctx; + krb5_error_code kerr; + krb5_principal principal; + char *instanceName; + char *serviceType; + char *serviceName; + const char *realm; + const char *guid_str; + const char *forest_name = samdb_forest_name(ldb, mem_ctx); + const char *base_domain = samdb_default_domain_name(ldb, mem_ctx); + struct loadparm_context *lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"), + struct loadparm_context); + bool is_dc = (userAccountControl & UF_SERVER_TRUST_ACCOUNT) || + (userAccountControl & UF_PARTIAL_SECRETS_ACCOUNT); + + kerr = smb_krb5_init_context_basic(mem_ctx, + lp_ctx, + &krb_ctx); + if (kerr != 0) { + return ldb_error(ldb, LDB_ERR_OPERATIONS_ERROR, + "Could not initialize kerberos context."); + } + + ret = krb5_parse_name(krb_ctx, spn_value, &principal); + if (ret) { + krb5_free_context(krb_ctx); + return LDB_ERR_CONSTRAINT_VIOLATION; + } + + instanceName = principal->name.name_string.val[1]; + serviceType = principal->name.name_string.val[0]; + realm = krb5_principal_get_realm(krb_ctx, principal); + guid_str = talloc_asprintf(mem_ctx,"%s._msdcs.%s", + ntds_guid, + forest_name); + if (principal->name.name_string.len == 3) { + serviceName = principal->name.name_string.val[2]; + } else { + serviceName = NULL; + } + + if (serviceName) { + if (!is_dc) { + goto fail; + } + if (strcasecmp(serviceType, "ldap") == 0) { + if (strcasecmp(serviceName, netbios_name) != 0 && + strcasecmp(serviceName, forest_name) != 0) { + goto fail; + } + + } else if (strcasecmp(serviceType, "gc") == 0) { + if (strcasecmp(serviceName, forest_name) != 0) { + goto fail; + } + } else { + if (strcasecmp(serviceName, base_domain) != 0 && + strcasecmp(serviceName, netbios_name) != 0) { + goto fail; + } + } + } + /* instanceName can be samAccountName without $ or dnsHostName + * or "ntds_guid._msdcs.forest_domain for DC objects */ + if (strncasecmp(instanceName, samAccountName, strlen(samAccountName - 1)) == 0) { + goto success; + } else if (strcasecmp(instanceName, dnsHostName) == 0) { + goto success; + } else if (is_dc) { + if (strcasecmp(instanceName, guid_str) == 0) { + goto success; + } + } else { + goto fail; + } +fail: + krb5_free_principal(krb_ctx, principal); + krb5_free_context(krb_ctx); + return LDB_ERR_CONSTRAINT_VIOLATION; + +success: + krb5_free_principal(krb_ctx, principal); + krb5_free_context(krb_ctx); + return LDB_SUCCESS; +} + +static int acl_check_spn(TALLOC_CTX *mem_ctx, + struct ldb_module *module, + struct ldb_request *req, + struct security_descriptor *sd, + struct dom_sid *sid, + const struct GUID *oc_guid, + const struct dsdb_attribute *attr) +{ + int ret; + unsigned int i; + TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx); + struct ldb_context *ldb = ldb_module_get_ctx(module); + struct ldb_result *acl_res; + struct ldb_result *netbios_res; + struct ldb_message_element *el; + struct ldb_dn *partitions_dn = samdb_partitions_dn(ldb, tmp_ctx); + int userAccountControl; + const char *samAccountName; + const char *dnsHostName; + const char *netbios_name; + const struct GUID *ntds = samdb_ntds_objectGUID(ldb); + const char *ntds_guid = GUID_string(tmp_ctx, ntds); + + static const char *acl_attrs[] = { + "samAccountName", + "dnsHostName", + "userAccountControl", + NULL + }; + static const char *netbios_attrs[] = { + "nETBIOSName", + NULL + }; + /* if we have wp, we can do whatever we like */ + if (acl_check_access_on_attribute(module, + tmp_ctx, + sd, + sid, + SEC_ADS_WRITE_PROP, + attr) == LDB_SUCCESS) { + talloc_free(tmp_ctx); + return LDB_SUCCESS; + } + + ret = acl_check_extended_right(tmp_ctx, sd, acl_user_token(module), + GUID_DRS_VALIDATE_SPN, + SEC_ADS_SELF_WRITE, + sid); + + if (ret == LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS) { + dsdb_acl_debug(sd, acl_user_token(module), + req->op.mod.message->dn, + true, + 10); + talloc_free(tmp_ctx); + return ret; + } + + ret = dsdb_module_search_dn(module, tmp_ctx, + &acl_res, req->op.mod.message->dn, + acl_attrs, + DSDB_FLAG_NEXT_MODULE | + DSDB_SEARCH_SHOW_DELETED, req); + if (ret != LDB_SUCCESS) { + talloc_free(tmp_ctx); + return ret; + } + + userAccountControl = ldb_msg_find_attr_as_int(acl_res->msgs[0], "userAccountControl", 0); + dnsHostName = ldb_msg_find_attr_as_string(acl_res->msgs[0], "dnsHostName", NULL); + samAccountName = ldb_msg_find_attr_as_string(acl_res->msgs[0], "samAccountName", NULL); + + ret = dsdb_module_search(module, tmp_ctx, + &netbios_res, partitions_dn, + LDB_SCOPE_ONELEVEL, + netbios_attrs, + DSDB_FLAG_NEXT_MODULE, + req, + "(ncName=%s)", + ldb_dn_get_linearized(ldb_get_default_basedn(ldb))); + + netbios_name = ldb_msg_find_attr_as_string(netbios_res->msgs[0], "nETBIOSName", NULL); + + el = ldb_msg_find_element(req->op.mod.message, "servicePrincipalName"); + if (!el) { + talloc_free(tmp_ctx); + return ldb_error(ldb, LDB_ERR_OPERATIONS_ERROR, + "Error finding element for servicePrincipalName."); + } + + for (i=0; i < el->num_values; i++) { + ret = acl_validate_spn_value(tmp_ctx, + ldb, + (char *)el->values[i].data, + userAccountControl, + samAccountName, + dnsHostName, + netbios_name, + ntds_guid); + if (ret != LDB_SUCCESS) { + talloc_free(tmp_ctx); + return ret; + } + } + talloc_free(tmp_ctx); return LDB_SUCCESS; } @@ -700,10 +642,10 @@ static int acl_add(struct ldb_module *module, struct ldb_request *req) int ret; struct ldb_dn *parent = ldb_dn_get_parent(req, req->op.add.message->dn); struct ldb_context *ldb; + const struct dsdb_schema *schema; struct ldb_message_element *oc_el; const struct GUID *guid; - struct object_tree *root = NULL; - struct object_tree *new_node = NULL; + struct ldb_dn *nc_root; struct ldb_control *as_system = ldb_request_get_control(req, LDB_CONTROL_AS_SYSTEM_OID); if (as_system != NULL) { @@ -713,18 +655,29 @@ static int acl_add(struct ldb_module *module, struct ldb_request *req) if (dsdb_module_am_system(module) || as_system) { return ldb_next_request(module, req); } - if (ldb_dn_is_special(req->op.add.message->dn)) { return ldb_next_request(module, req); } + 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)) { + + ret = dsdb_find_nc_root(ldb, req, req->op.add.message->dn, &nc_root); + if (ret != LDB_SUCCESS) { + return ret; + } + if (ldb_dn_compare(nc_root, req->op.add.message->dn) == 0) { + talloc_free(nc_root); return ldb_next_request(module, req); } + talloc_free(nc_root); + + schema = dsdb_get_schema(ldb, req); + if (!schema) { + return ldb_operr(ldb); + } oc_el = ldb_msg_find_element(req->op.add.message, "objectClass"); if (!oc_el || oc_el->num_values == 0) { @@ -732,28 +685,163 @@ static int acl_add(struct ldb_module *module, struct ldb_request *req) return ldb_module_done(req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR); } - guid = class_schemaid_guid_by_lDAPDisplayName(dsdb_get_schema(ldb), + guid = class_schemaid_guid_by_lDAPDisplayName(schema, (char *)oc_el->values[oc_el->num_values-1].data); - - if (!insert_in_object_tree(req, guid, SEC_ADS_CREATE_CHILD, &root, &new_node)) { - return LDB_ERR_OPERATIONS_ERROR; + ret = dsdb_module_check_access_on_dn(module, req, parent, SEC_ADS_CREATE_CHILD, guid, req); + if (ret != LDB_SUCCESS) { + return ret; } + return ldb_next_request(module, req); +} - ret = check_access_on_dn(module, req, parent, SEC_ADS_CREATE_CHILD, root); +/* ckecks if modifications are allowed on "Member" attribute */ +static int acl_check_self_membership(TALLOC_CTX *mem_ctx, + struct ldb_module *module, + struct ldb_request *req, + struct security_descriptor *sd, + struct dom_sid *sid, + const struct GUID *oc_guid, + const struct dsdb_attribute *attr) +{ + int ret; + unsigned int i; + struct ldb_context *ldb = ldb_module_get_ctx(module); + struct ldb_dn *user_dn; + struct ldb_message_element *member_el; + /* if we have wp, we can do whatever we like */ + if (acl_check_access_on_attribute(module, + mem_ctx, + sd, + sid, + SEC_ADS_WRITE_PROP, + attr) == LDB_SUCCESS) { + return LDB_SUCCESS; + } + /* if we are adding/deleting ourselves, check for self membership */ + ret = dsdb_find_dn_by_sid(ldb, mem_ctx, + &acl_user_token(module)->sids[PRIMARY_USER_SID_INDEX], + &user_dn); if (ret != LDB_SUCCESS) { return ret; } + member_el = ldb_msg_find_element(req->op.mod.message, "member"); + if (!member_el) { + return ldb_operr(ldb); + } + /* user can only remove oneself */ + if (member_el->num_values == 0) { + return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS; + } + for (i = 0; i < member_el->num_values; i++) { + if (strcasecmp((const char *)member_el->values[i].data, + ldb_dn_get_extended_linearized(mem_ctx, user_dn, 1)) != 0) { + return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS; + } + } + ret = acl_check_extended_right(mem_ctx, sd, acl_user_token(module), + GUID_DRS_SELF_MEMBERSHIP, + SEC_ADS_SELF_WRITE, + sid); + if (ret == LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS) { + dsdb_acl_debug(sd, acl_user_token(module), + req->op.mod.message->dn, + true, + 10); + } + return ret; +} - return ldb_next_request(module, req); +static int acl_check_password_rights(TALLOC_CTX *mem_ctx, + struct ldb_module *module, + struct ldb_request *req, + struct security_descriptor *sd, + struct dom_sid *sid, + const struct GUID *oc_guid, + bool userPassword) +{ + int ret = LDB_SUCCESS; + unsigned int del_attr_cnt = 0, add_attr_cnt = 0, rep_attr_cnt = 0; + struct ldb_message_element *el; + struct ldb_message *msg; + const char *passwordAttrs[] = { "userPassword", "clearTextPassword", + "unicodePwd", "dBCSPwd", NULL }, **l; + TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx); + + msg = ldb_msg_copy_shallow(tmp_ctx, req->op.mod.message); + if (msg == NULL) { + return ldb_module_oom(module); + } + for (l = passwordAttrs; *l != NULL; l++) { + if ((!userPassword) && (ldb_attr_cmp(*l, "userPassword") == 0)) { + continue; + } + + while ((el = ldb_msg_find_element(msg, *l)) != NULL) { + if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_DELETE) { + ++del_attr_cnt; + } + if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_ADD) { + ++add_attr_cnt; + } + if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_REPLACE) { + ++rep_attr_cnt; + } + ldb_msg_remove_element(msg, el); + } + } + + /* single deletes will be handled by the "password_hash" LDB module + * later in the stack, so we let it though here */ + if ((del_attr_cnt > 0) && (add_attr_cnt == 0) && (rep_attr_cnt == 0)) { + talloc_free(tmp_ctx); + return LDB_SUCCESS; + } + + if (ldb_request_get_control(req, + DSDB_CONTROL_PASSWORD_CHANGE_OID) != NULL) { + /* The "DSDB_CONTROL_PASSWORD_CHANGE_OID" control means that we + * have a user password change and not a set as the message + * looks like. In it's value blob it contains the NT and/or LM + * hash of the old password specified by the user. + * This control is used by the SAMR and "kpasswd" password + * change mechanisms. */ + ret = acl_check_extended_right(tmp_ctx, sd, acl_user_token(module), + GUID_DRS_USER_CHANGE_PASSWORD, + SEC_ADS_CONTROL_ACCESS, + sid); + } + else if (rep_attr_cnt > 0 || (add_attr_cnt != del_attr_cnt)) { + ret = acl_check_extended_right(tmp_ctx, sd, acl_user_token(module), + GUID_DRS_FORCE_CHANGE_PASSWORD, + SEC_ADS_CONTROL_ACCESS, + sid); + } + else if (add_attr_cnt == 1 && del_attr_cnt == 1) { + ret = acl_check_extended_right(tmp_ctx, sd, acl_user_token(module), + GUID_DRS_USER_CHANGE_PASSWORD, + SEC_ADS_CONTROL_ACCESS, + sid); + /* Very strange, but we get constraint violation in this case */ + if (ret == LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS) { + ret = LDB_ERR_CONSTRAINT_VIOLATION; + } + } + if (ret != LDB_SUCCESS) { + dsdb_acl_debug(sd, acl_user_token(module), + req->op.mod.message->dn, + true, + 10); + } + talloc_free(tmp_ctx); + return ret; } static int acl_modify(struct ldb_module *module, struct ldb_request *req) { int ret; struct ldb_context *ldb = ldb_module_get_ctx(module); - const struct dsdb_schema *schema = dsdb_get_schema(ldb); - int i; - bool modify_sd = false; + const struct dsdb_schema *schema; + unsigned int i; const struct GUID *guid; uint32_t access_granted; struct object_tree *root = NULL; @@ -763,6 +851,7 @@ static int acl_modify(struct ldb_module *module, struct ldb_request *req) struct security_descriptor *sd; struct dom_sid *sid = NULL; struct ldb_control *as_system = ldb_request_get_control(req, LDB_CONTROL_AS_SYSTEM_OID); + bool userPassword = dsdb_user_password_support(module, req, req); TALLOC_CTX *tmp_ctx = talloc_new(req); static const char *acl_attrs[] = { "nTSecurityDescriptor", @@ -786,34 +875,36 @@ static int acl_modify(struct ldb_module *module, struct ldb_request *req) if (ldb_dn_is_special(req->op.mod.message->dn)) { return ldb_next_request(module, req); } - ret = ldb_search(ldb, req, &acl_res, req->op.mod.message->dn, - LDB_SCOPE_BASE, acl_attrs, NULL); + ret = dsdb_module_search_dn(module, tmp_ctx, &acl_res, req->op.mod.message->dn, + acl_attrs, + DSDB_FLAG_NEXT_MODULE, req); if (ret != LDB_SUCCESS) { - return ret; + goto fail; } - ret = get_sd_from_ldb_message(req, acl_res->msgs[0], &sd); + schema = dsdb_get_schema(ldb, tmp_ctx); + if (!schema) { + ret = LDB_ERR_OPERATIONS_ERROR; + goto fail; + } + + ret = dsdb_get_sd_from_ldb_message(ldb, tmp_ctx, acl_res->msgs[0], &sd); if (ret != LDB_SUCCESS) { DEBUG(10, ("acl_modify: cannot get descriptor\n")); - return ret; + goto fail; } /* Theoretically we pass the check if the object has no sd */ if (!sd) { - return LDB_SUCCESS; + goto success; } - guid = get_oc_guid_from_message(module,acl_res->msgs[0]); + guid = get_oc_guid_from_message(module, schema, acl_res->msgs[0]); if (!guid) { DEBUG(10, ("acl_modify: cannot get guid\n")); goto fail; } - - ret = get_dom_sid_from_ldb_message(req, acl_res->msgs[0], &sid); - if (ret != LDB_SUCCESS) { - return LDB_ERR_OPERATIONS_ERROR; - } - + sid = samdb_result_dom_sid(req, acl_res->msgs[0], "objectSid"); 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")); @@ -821,32 +912,96 @@ static int acl_modify(struct ldb_module *module, struct ldb_request *req) } 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, + attr = dsdb_attribute_by_lDAPDisplayName(schema, req->op.mod.message->elements[i].name); + + if (ldb_attr_cmp("nTSecurityDescriptor", req->op.mod.message->elements[i].name) == 0) { + status = sec_access_check_ds(sd, acl_user_token(module), + SEC_STD_WRITE_DAC, + &access_granted, + NULL, + sid); + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(10, ("Object %s has no write dacl access\n", + ldb_dn_get_linearized(req->op.mod.message->dn))); + dsdb_acl_debug(sd, + acl_user_token(module), + req->op.mod.message->dn, + true, + 10); + ret = LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS; + goto fail; + } } - if (strcmp("nTSecurityDescriptor", req->op.mod.message->elements[i].name) == 0) { - modify_sd = true; + else if (ldb_attr_cmp("member", req->op.mod.message->elements[i].name) == 0) { + ret = acl_check_self_membership(tmp_ctx, + module, + req, + sd, + sid, + guid, + attr); + if (ret != LDB_SUCCESS) { + goto fail; + } + } + else if (ldb_attr_cmp("dBCSPwd", req->op.mod.message->elements[i].name) == 0) { + /* this one is not affected by any rights, we should let it through + so that passwords_hash returns the correct error */ + continue; + } + else if (ldb_attr_cmp("unicodePwd", req->op.mod.message->elements[i].name) == 0 || + (userPassword && ldb_attr_cmp("userPassword", req->op.mod.message->elements[i].name) == 0) || + ldb_attr_cmp("clearTextPassword", req->op.mod.message->elements[i].name) == 0) { + ret = acl_check_password_rights(tmp_ctx, + module, + req, + sd, + sid, + guid, + userPassword); + if (ret != LDB_SUCCESS) { + goto fail; + } + } else if (ldb_attr_cmp("servicePrincipalName", req->op.mod.message->elements[i].name) == 0) { + ret = acl_check_spn(tmp_ctx, + module, + req, + sd, + sid, + guid, + attr); + if (ret != LDB_SUCCESS) { + goto fail; + } } else { + /* This basic attribute existence check with the right errorcode + * is needed since this module is the first one which requests + * schema attribute informations. + * The complete attribute checking is done in the + * "objectclass_attrs" module behind this one. + */ if (!attr) { - DEBUG(10, ("acl_modify: cannot find attribute %s\n", - req->op.mod.message->elements[i].name)); + ldb_asprintf_errstring(ldb, "acl_modify: attribute '%s' on entry '%s' was not found in the schema!", + req->op.mod.message->elements[i].name, + ldb_dn_get_linearized(req->op.mod.message->dn)); + ret = LDB_ERR_NO_SUCH_ATTRIBUTE; 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")); + ret = LDB_ERR_OPERATIONS_ERROR; goto fail; } 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")); + ret = LDB_ERR_OPERATIONS_ERROR; goto fail; } } @@ -860,51 +1015,34 @@ static int acl_modify(struct ldb_module *module, struct ldb_request *req) sid); 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; - } - } - if (modify_sd) { - status = sec_access_check_ds(sd, acl_user_token(module), - SEC_STD_WRITE_DAC, - &access_granted, - NULL, - sid); - - if (!NT_STATUS_IS_OK(status)) { - DEBUG(10, ("Object %s nas no write dacl access\n", + DEBUG(10, ("Object %s has no write property access\n", ldb_dn_get_linearized(req->op.mod.message->dn))); - acl_debug(sd, + dsdb_acl_debug(sd, acl_user_token(module), req->op.mod.message->dn, true, 10); - talloc_free(tmp_ctx); - return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS; + ret = LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS; + goto fail; } } +success: talloc_free(tmp_ctx); return ldb_next_request(module, req); fail: talloc_free(tmp_ctx); - return LDB_ERR_OPERATIONS_ERROR; + return ret; } /* similar to the modify for the time being. - * We need to concider the special delete tree case, though - TODO */ + * We need to consider the special delete tree case, though - TODO */ static int acl_delete(struct ldb_module *module, struct ldb_request *req) { int ret; struct ldb_dn *parent = ldb_dn_get_parent(req, req->op.del.dn); struct ldb_context *ldb; + struct ldb_dn *nc_root; struct ldb_control *as_system = ldb_request_get_control(req, LDB_CONTROL_AS_SYSTEM_OID); if (as_system != NULL) { @@ -915,30 +1053,42 @@ static int acl_delete(struct ldb_module *module, struct ldb_request *req) if (dsdb_module_am_system(module) || as_system) { return ldb_next_request(module, req); } - if (ldb_dn_is_special(req->op.del.dn)) { return ldb_next_request(module, req); } + ldb = ldb_module_get_ctx(module); - /* 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); + + /* Make sure we aren't deleting a NC */ + + ret = dsdb_find_nc_root(ldb, req, req->op.del.dn, &nc_root); + if (ret != LDB_SUCCESS) { + return ret; } + if (ldb_dn_compare(nc_root, req->op.del.dn) == 0) { + talloc_free(nc_root); + DEBUG(10,("acl:deleting a NC\n")); + /* Windows returns "ERR_UNWILLING_TO_PERFORM */ + return ldb_module_done(req, NULL, NULL, + LDB_ERR_UNWILLING_TO_PERFORM); + } + talloc_free(nc_root); - /* 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); + /* First check if we have delete object right */ + ret = dsdb_module_check_access_on_dn(module, req, req->op.del.dn, + SEC_STD_DELETE, NULL, req); + if (ret == LDB_SUCCESS) { + return ldb_next_request(module, req); } - ret = check_access_on_dn(module, req, parent, SEC_ADS_DELETE_CHILD, NULL); + /* Nope, we don't have delete object. Lets check if we have delete + * child on the parent */ + ret = dsdb_module_check_access_on_dn(module, req, parent, + SEC_ADS_DELETE_CHILD, NULL, req); if (ret != LDB_SUCCESS) { return ret; } + return ldb_next_request(module, req); } @@ -947,17 +1097,20 @@ 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); + const struct dsdb_schema *schema; struct ldb_context *ldb; struct security_descriptor *sd = NULL; struct dom_sid *sid = NULL; struct ldb_result *acl_res; const struct GUID *guid; + struct ldb_dn *nc_root; struct object_tree *root = NULL; struct object_tree *new_node = NULL; struct ldb_control *as_system = ldb_request_get_control(req, LDB_CONTROL_AS_SYSTEM_OID); TALLOC_CTX *tmp_ctx = talloc_new(req); NTSTATUS status; uint32_t access_granted; + const char *rdn_name; static const char *acl_attrs[] = { "nTSecurityDescriptor", "objectClass", @@ -976,45 +1129,84 @@ static int acl_rename(struct ldb_module *module, struct ldb_request *req) if (ldb_dn_is_special(req->op.rename.olddn)) { return ldb_next_request(module, req); } + ldb = ldb_module_get_ctx(module); - /* TODO search to include deleted objects */ - ret = ldb_search(ldb, req, &acl_res, req->op.rename.olddn, - LDB_SCOPE_BASE, acl_attrs, NULL); + /* Make sure we aren't renaming/moving a NC */ + + ret = dsdb_find_nc_root(ldb, req, req->op.rename.olddn, &nc_root); + if (ret != LDB_SUCCESS) { + return ret; + } + if (ldb_dn_compare(nc_root, req->op.rename.olddn) == 0) { + talloc_free(nc_root); + DEBUG(10,("acl:renaming/moving a NC\n")); + /* Windows returns "ERR_UNWILLING_TO_PERFORM */ + return ldb_module_done(req, NULL, NULL, + LDB_ERR_UNWILLING_TO_PERFORM); + } + talloc_free(nc_root); + + /* Look for the parent */ + + ret = dsdb_module_search_dn(module, tmp_ctx, &acl_res, + req->op.rename.olddn, acl_attrs, + DSDB_FLAG_NEXT_MODULE | + DSDB_SEARCH_SHOW_RECYCLED, req); /* 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))); + talloc_free(tmp_ctx); return ret; } - guid = get_oc_guid_from_message(module,acl_res->msgs[0]); + schema = dsdb_get_schema(ldb, acl_res); + if (!schema) { + talloc_free(tmp_ctx); + return ldb_operr(ldb); + } + + guid = get_oc_guid_from_message(module, schema, acl_res->msgs[0]); if (!insert_in_object_tree(tmp_ctx, guid, SEC_ADS_WRITE_PROP, &root, &new_node)) { - return LDB_ERR_OPERATIONS_ERROR; + talloc_free(tmp_ctx); + return ldb_operr(ldb); }; - guid = attribute_schemaid_guid_by_lDAPDisplayName(dsdb_get_schema(ldb), + guid = attribute_schemaid_guid_by_lDAPDisplayName(schema, "name"); if (!insert_in_object_tree(tmp_ctx, guid, SEC_ADS_WRITE_PROP, &new_node, &new_node)) { - return LDB_ERR_OPERATIONS_ERROR; + talloc_free(tmp_ctx); + return ldb_operr(ldb); + }; + + rdn_name = ldb_dn_get_rdn_name(req->op.rename.olddn); + if (rdn_name == NULL) { + talloc_free(tmp_ctx); + return ldb_operr(ldb); + } + guid = attribute_schemaid_guid_by_lDAPDisplayName(schema, + rdn_name); + if (!insert_in_object_tree(tmp_ctx, guid, SEC_ADS_WRITE_PROP, + &new_node, &new_node)) { + talloc_free(tmp_ctx); + return ldb_operr(ldb); }; - ret = get_sd_from_ldb_message(req, acl_res->msgs[0], &sd); + ret = dsdb_get_sd_from_ldb_message(ldb, req, acl_res->msgs[0], &sd); if (ret != LDB_SUCCESS) { - return LDB_ERR_OPERATIONS_ERROR; + talloc_free(tmp_ctx); + return ldb_operr(ldb); } /* Theoretically we pass the check if the object has no sd */ if (!sd) { + talloc_free(tmp_ctx); return LDB_SUCCESS; } - ret = get_dom_sid_from_ldb_message(req, acl_res->msgs[0], &sid); - if (ret != LDB_SUCCESS) { - return LDB_ERR_OPERATIONS_ERROR; - } - + sid = samdb_result_dom_sid(req, acl_res->msgs[0], "objectSid"); status = sec_access_check_ds(sd, acl_user_token(module), SEC_ADS_WRITE_PROP, &access_granted, @@ -1022,45 +1214,37 @@ static int acl_rename(struct ldb_module *module, struct ldb_request *req) sid); if (!NT_STATUS_IS_OK(status)) { - DEBUG(10, ("Object %s nas no wp on name\n", + DEBUG(10, ("Object %s has no wp on name\n", ldb_dn_get_linearized(req->op.rename.olddn))); - acl_debug(sd, + dsdb_acl_debug(sd, acl_user_token(module), req->op.rename.olddn, true, 10); + talloc_free(tmp_ctx); return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS; } if (ldb_dn_compare(oldparent, newparent) == 0) { /* regular rename, not move, nothing more to do */ + talloc_free(tmp_ctx); return ldb_next_request(module, req); } - /* 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_message(module,acl_res->msgs[0]); + guid = get_oc_guid_from_message(module, schema, acl_res->msgs[0]); if (!guid) { DEBUG(10,("acl:renamed object has no object class\n")); + talloc_free(tmp_ctx); 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); + + ret = dsdb_module_check_access_on_dn(module, req, newparent, SEC_ADS_CREATE_CHILD, guid, req); if (ret != LDB_SUCCESS) { DEBUG(10,("acl:access_denied renaming %s", ldb_dn_get_linearized(req->op.rename.olddn))); + talloc_free(tmp_ctx); return ret; } /* do we have delete object on the object? */ @@ -1072,14 +1256,19 @@ static int acl_rename(struct ldb_module *module, struct ldb_request *req) sid); if (NT_STATUS_IS_OK(status)) { + talloc_free(tmp_ctx); return ldb_next_request(module, req); } /* what about delete child on the current parent */ - ret = check_access_on_dn(module, req, oldparent, SEC_ADS_DELETE_CHILD, NULL); + ret = dsdb_module_check_access_on_dn(module, req, oldparent, SEC_ADS_DELETE_CHILD, NULL, req); if (ret != LDB_SUCCESS) { DEBUG(10,("acl:access_denied renaming %s", ldb_dn_get_linearized(req->op.rename.olddn))); + talloc_free(tmp_ctx); return ldb_module_done(req, NULL, NULL, ret); } + + talloc_free(tmp_ctx); + return ldb_next_request(module, req); } @@ -1095,7 +1284,8 @@ static int acl_search_callback(struct ldb_request *req, struct ldb_reply *ares) "objectSid", NULL }; - int ret, i; + int ret; + unsigned int i; ac = talloc_get_type(req->context, struct acl_context); data = talloc_get_type(ldb_module_get_private(ac->module), struct acl_private); @@ -1117,32 +1307,34 @@ static int acl_search_callback(struct ldb_request *req, struct ldb_reply *ares) || ac->allowedChildClassesEffective || ac->allowedAttributesEffective || ac->sDRightsEffective) { - ret = ldb_search(ldb, ac, &acl_res, ares->message->dn, LDB_SCOPE_BASE, acl_attrs, NULL); + ret = dsdb_module_search_dn(ac->module, ac, &acl_res, ares->message->dn, + acl_attrs, + DSDB_FLAG_NEXT_MODULE, req); if (ret != LDB_SUCCESS) { return ldb_module_done(ac->req, NULL, NULL, ret); } if (ac->allowedAttributes || ac->allowedAttributesEffective) { - ret = acl_allowedAttributes(ac->module, acl_res->msgs[0], ares->message, ac); + ret = acl_allowedAttributes(ac->module, ac->schema, acl_res->msgs[0], ares->message, ac); if (ret != LDB_SUCCESS) { return ldb_module_done(ac->req, NULL, NULL, ret); } } if (ac->allowedChildClasses) { - ret = acl_childClasses(ac->module, acl_res->msgs[0], + ret = acl_childClasses(ac->module, ac->schema, acl_res->msgs[0], ares->message, "allowedChildClasses"); if (ret != LDB_SUCCESS) { return ldb_module_done(ac->req, NULL, NULL, ret); } } if (ac->allowedChildClassesEffective) { - ret = acl_childClassesEffective(ac->module, + ret = acl_childClassesEffective(ac->module, ac->schema, acl_res->msgs[0], ares->message, ac); if (ret != LDB_SUCCESS) { return ldb_module_done(ac->req, NULL, NULL, ret); } } if (ac->sDRightsEffective) { - ret = acl_sDRightsEffective(ac->module, + ret = acl_sDRightsEffective(ac->module, acl_res->msgs[0], ares->message, ac); if (ret != LDB_SUCCESS) { return ldb_module_done(ac->req, NULL, NULL, ret); @@ -1152,6 +1344,11 @@ static int acl_search_callback(struct ldb_request *req, struct ldb_reply *ares) if (data && data->password_attrs) { if (!ac->am_system) { for (i = 0; data->password_attrs[i]; i++) { + if ((!ac->userPassword) && + (ldb_attr_cmp(data->password_attrs[i], + "userPassword") == 0)) + continue; + ldb_msg_remove_attr(ares->message, data->password_attrs[i]); } } @@ -1175,14 +1372,14 @@ static int acl_search(struct ldb_module *module, struct ldb_request *req) struct acl_context *ac; struct ldb_request *down_req; struct acl_private *data; - int ret, i; + int ret; + unsigned int i; ldb = ldb_module_get_ctx(module); ac = talloc_zero(req, struct acl_context); if (ac == NULL) { - ldb_oom(ldb); - return LDB_ERR_OPERATIONS_ERROR; + return ldb_oom(ldb); } data = talloc_get_type(ldb_module_get_private(module), struct acl_private); @@ -1194,6 +1391,8 @@ static int acl_search(struct ldb_module *module, struct ldb_request *req) ac->allowedChildClasses = ldb_attr_in_list(req->op.search.attrs, "allowedChildClasses"); ac->allowedChildClassesEffective = ldb_attr_in_list(req->op.search.attrs, "allowedChildClassesEffective"); ac->sDRightsEffective = ldb_attr_in_list(req->op.search.attrs, "sDRightsEffective"); + ac->userPassword = dsdb_user_password_support(module, ac, req); + ac->schema = dsdb_get_schema(ldb, ac); /* replace any attributes in the parse tree that are private, so we don't allow a search for 'userPassword=penguin', @@ -1203,6 +1402,11 @@ static int acl_search(struct ldb_module *module, struct ldb_request *req) /* remove password attributes */ if (data && data->password_attrs) { for (i = 0; data->password_attrs[i]; i++) { + if ((!ac->userPassword) && + (ldb_attr_cmp(data->password_attrs[i], + "userPassword") == 0)) + continue; + ldb_parse_tree_attr_replace(req->op.search.tree, data->password_attrs[i], "kludgeACLredactedattribute"); @@ -1218,6 +1422,7 @@ static int acl_search(struct ldb_module *module, struct ldb_request *req) req->controls, ac, acl_search_callback, req); + LDB_REQ_SET_LOCATION(down_req); if (ret != LDB_SUCCESS) { return ret; } @@ -1225,12 +1430,43 @@ static int acl_search(struct ldb_module *module, struct ldb_request *req) return ldb_next_request(module, down_req); } -_PUBLIC_ const struct ldb_module_ops ldb_acl_module_ops = { +static int acl_extended(struct ldb_module *module, struct ldb_request *req) +{ + struct ldb_context *ldb = ldb_module_get_ctx(module); + struct ldb_control *as_system = ldb_request_get_control(req, LDB_CONTROL_AS_SYSTEM_OID); + + /* allow everybody to read the sequence number */ + if (strcmp(req->op.extended.oid, + LDB_EXTENDED_SEQUENCE_NUMBER) == 0) { + return ldb_next_request(module, req); + } + + if (dsdb_module_am_system(module) || + dsdb_module_am_administrator(module) || as_system) { + return ldb_next_request(module, req); + } else { + ldb_asprintf_errstring(ldb, + "acl_extended: " + "attempted database modify not permitted. " + "User %s is not SYSTEM or an administrator", + acl_user_name(req, module)); + return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS; + } +} + +static 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 }; + +int ldb_acl_module_init(const char *version) +{ + LDB_MODULE_CHECK_VERSION(version); + return ldb_register_module(&ldb_acl_module_ops); +}