const struct ldb_val *sam_account_name_val = NULL;
struct GUID ntds;
char *ntds_guid = NULL;
+ const struct ldb_message *msg = NULL;
+ const struct ldb_message *search_res = NULL;
static const char *acl_attrs[] = {
"samAccountName",
NULL
};
+ if (req->operation == LDB_MODIFY) {
+ msg = req->op.mod.message;
+ } else if (req->operation == LDB_ADD) {
+ msg = req->op.add.message;
+ }
+
if (implicit_validated_write_control != NULL) {
/*
* The validated write control dispenses with ACL
if (ret != LDB_SUCCESS) {
dsdb_acl_debug(sd, acl_user_token(module),
- req->op.mod.message->dn,
+ msg->dn,
true,
10);
talloc_free(tmp_ctx);
talloc_free(tmp_ctx);
return LDB_SUCCESS;
}
- }
- ret = dsdb_module_search_dn(module, tmp_ctx,
- &acl_res, req->op.mod.message->dn,
- acl_attrs,
- DSDB_FLAG_NEXT_MODULE |
- DSDB_FLAG_AS_SYSTEM |
- DSDB_SEARCH_SHOW_RECYCLED,
- req);
- if (ret != LDB_SUCCESS) {
+ ret = dsdb_module_search_dn(module, tmp_ctx,
+ &acl_res, msg->dn,
+ acl_attrs,
+ DSDB_FLAG_NEXT_MODULE |
+ DSDB_FLAG_AS_SYSTEM |
+ DSDB_SEARCH_SHOW_RECYCLED,
+ req);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+
+ search_res = acl_res->msgs[0];
+ } else if (req->operation == LDB_ADD) {
+ search_res = msg;
+ } else {
talloc_free(tmp_ctx);
- return ret;
+ return LDB_ERR_OPERATIONS_ERROR;
}
- dns_host_name_val = ldb_msg_find_ldb_val(acl_res->msgs[0], "dNSHostName");
+ if (req->operation == LDB_MODIFY) {
+ dns_host_name_val = ldb_msg_find_ldb_val(search_res, "dNSHostName");
+ }
- ret = dsdb_msg_get_single_value(req->op.mod.message,
+ ret = dsdb_msg_get_single_value(msg,
"dNSHostName",
dns_host_name_val,
&dns_host_name_val,
return ret;
}
- userAccountControl = ldb_msg_find_attr_as_uint(acl_res->msgs[0], "userAccountControl", 0);
+ userAccountControl = ldb_msg_find_attr_as_uint(search_res, "userAccountControl", 0);
- sam_account_name_val = ldb_msg_find_ldb_val(acl_res->msgs[0], "sAMAccountName");
+ if (req->operation == LDB_MODIFY) {
+ sam_account_name_val = ldb_msg_find_ldb_val(search_res, "sAMAccountName");
+ }
- ret = dsdb_msg_get_single_value(req->op.mod.message,
+ ret = dsdb_msg_get_single_value(msg,
"sAMAccountName",
sam_account_name_val,
&sam_account_name_val,
netbios_name = ldb_msg_find_attr_as_string(netbios_res->msgs[0], "nETBIOSName", NULL);
- /* NTDSDSA objectGuid of object we are checking SPN for */
+ /*
+ * NTDSDSA objectGuid of object we are checking SPN for
+ *
+ * Note - do we have the necessary attributes for this during an add operation?
+ * How should we test this?
+ */
if (userAccountControl & (UF_SERVER_TRUST_ACCOUNT | UF_PARTIAL_SECRETS_ACCOUNT)) {
ret = dsdb_module_find_ntdsguid_for_computer(module, tmp_ctx,
- req->op.mod.message->dn, &ntds, req);
+ msg->dn, &ntds, req);
if (ret != LDB_SUCCESS) {
ldb_asprintf_errstring(ldb, "Failed to find NTDSDSA objectGuid for %s: %s",
- ldb_dn_get_linearized(req->op.mod.message->dn),
+ ldb_dn_get_linearized(msg->dn),
ldb_strerror(ret));
talloc_free(tmp_ctx);
return LDB_ERR_OPERATIONS_ERROR;
return ldb_oom(ldb);
}
+ if (req->operation == LDB_MODIFY) {
+ msg = req->op.mod.message;
+ } else if (req->operation == LDB_ADD) {
+ msg = req->op.add.message;
+ }
+
if (implicit_validated_write_control != NULL) {
/*
* The validated write control dispenses with ACL
if (ret != LDB_SUCCESS) {
dsdb_acl_debug(sd, acl_user_token(module),
- req->op.mod.message->dn,
+ msg->dn,
true,
10);
talloc_free(tmp_ctx);
NULL
};
- msg = req->op.mod.message;
-
/*
* If not add or replace (eg delete),
* return success
search_res = acl_res->msgs[0];
} else if (req->operation == LDB_ADD) {
- msg = req->op.add.message;
search_res = msg;
} else {
talloc_free(tmp_ctx);
}
}
- samAccountName = ldb_msg_find_ldb_val(search_res, "sAMAccountName");
+ if (req->operation == LDB_MODIFY) {
+ samAccountName = ldb_msg_find_ldb_val(search_res, "sAMAccountName");
+ }
ret = dsdb_msg_get_single_value(msg,
"sAMAccountName",
struct ldb_context *ldb;
const struct dsdb_schema *schema;
const struct dsdb_class *objectclass;
+ const struct dsdb_class *computer_objectclass = NULL;
+ const struct ldb_message_element *oc_el = NULL;
+ struct ldb_message_element sorted_oc_el;
struct ldb_control *as_system;
+ struct ldb_control *sd_ctrl = NULL;
struct ldb_message_element *el;
unsigned int instanceType = 0;
+ struct dsdb_control_calculated_default_sd *control_sd = NULL;
+ const struct dsdb_attribute *attr = NULL;
+ const char **must_contain = NULL;
+ const struct ldb_message *msg = req->op.add.message;
+ const struct dom_sid *domain_sid = NULL;
+ int i = 0;
+ bool attribute_authorization;
+ bool is_subclass;
- if (ldb_dn_is_special(req->op.add.message->dn)) {
+ if (ldb_dn_is_special(msg->dn)) {
return ldb_next_request(module, req);
}
}
ldb = ldb_module_get_ctx(module);
+ domain_sid = samdb_domain_sid(ldb);
- parent = ldb_dn_get_parent(req, req->op.add.message->dn);
+ parent = ldb_dn_get_parent(req, msg->dn);
if (parent == NULL) {
return ldb_oom(ldb);
}
return ldb_operr(ldb);
}
- objectclass = dsdb_get_structural_oc_from_msg(schema, req->op.add.message);
- if (!objectclass) {
+ /* Find the objectclass of the new account. */
+
+ oc_el = ldb_msg_find_element(msg, "objectclass");
+ if (oc_el == NULL) {
ldb_asprintf_errstring(ldb_module_get_ctx(module),
"acl: unable to find or validate structural objectClass on %s\n",
- ldb_dn_get_linearized(req->op.add.message->dn));
+ ldb_dn_get_linearized(msg->dn));
return ldb_module_done(req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
}
- el = ldb_msg_find_element(req->op.add.message, "instanceType");
+ schema = dsdb_get_schema(ldb, req);
+ if (schema == NULL) {
+ return ldb_operr(ldb);
+ }
+
+ ret = dsdb_sort_objectClass_attr(ldb, schema, oc_el, req, &sorted_oc_el);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ objectclass = dsdb_get_last_structural_class(schema, &sorted_oc_el);
+ if (objectclass == NULL) {
+ return ldb_operr(ldb);
+ }
+
+ el = ldb_msg_find_element(msg, "instanceType");
if ((el != NULL) && (el->num_values != 1)) {
ldb_set_errstring(ldb, "acl: the 'instanceType' attribute is single-valued!");
return LDB_ERR_UNWILLING_TO_PERFORM;
}
- instanceType = ldb_msg_find_attr_as_uint(req->op.add.message,
+ instanceType = ldb_msg_find_attr_as_uint(msg,
"instanceType", 0);
if (instanceType & INSTANCE_TYPE_IS_NC_HEAD) {
static const char *no_attrs[] = { NULL };
DSDB_SEARCH_SHOW_RECYCLED,
req,
"(&(nCName=%s)(objectClass=crossRef))",
- ldb_dn_get_linearized(req->op.add.message->dn));
+ ldb_dn_get_linearized(msg->dn));
if (ret == LDB_SUCCESS) {
/* Check that we can write to the crossRef object MS-ADTS 3.1.1.5.2.8.2 */
* the naming master etc need to be handled
* in the instanceType module
*/
+ /* Note - do we need per-attribute checks? */
return ldb_next_request(module, req);
}
* master and adding the crossRef object need to be
* handled in the instanceType module
*/
- return ldb_next_request(module, req);
+ } else {
+ ret = dsdb_module_check_access_on_dn(module, req, parent,
+ SEC_ADS_CREATE_CHILD,
+ &objectclass->schemaIDGUID, req);
+ if (ret != LDB_SUCCESS) {
+ ldb_asprintf_errstring(ldb_module_get_ctx(module),
+ "acl: unable to get access to %s\n",
+ ldb_dn_get_linearized(msg->dn));
+ return ret;
+ }
}
- ret = dsdb_module_check_access_on_dn(module, req, parent,
- SEC_ADS_CREATE_CHILD,
- &objectclass->schemaIDGUID, req);
- if (ret != LDB_SUCCESS) {
- ldb_asprintf_errstring(ldb_module_get_ctx(module),
- "acl: unable to get access to %s\n",
- ldb_dn_get_linearized(req->op.add.message->dn));
- return ret;
+ attribute_authorization = dsdb_attribute_authz_on_ldap_add(module,
+ req,
+ req);
+ if (!attribute_authorization) {
+ /* Skip the remaining checks */
+ goto success;
+ }
+
+ /* Check if we have computer objectclass. */
+ computer_objectclass = dsdb_class_by_lDAPDisplayName(schema, "computer");
+ if (computer_objectclass == NULL) {
+ return ldb_operr(ldb);
+ }
+
+ is_subclass = dsdb_is_subclass_of(schema, objectclass, computer_objectclass);
+ if (!is_subclass) {
+ /*
+ * This object is not a computer (or derived from computer), so
+ * skip the remaining checks.
+ */
+ goto success;
+ }
+
+ /*
+ * we have established we have CC right, now check per-attribute
+ * access based on the default SD
+ */
+
+ sd_ctrl = ldb_request_get_control(req,
+ DSDB_CONTROL_CALCULATED_DEFAULT_SD_OID);
+ if (sd_ctrl == NULL) {
+ goto success;
+ }
+
+ {
+ TALLOC_CTX *tmp_ctx = talloc_new(req);
+ control_sd = (struct dsdb_control_calculated_default_sd *) sd_ctrl->data;
+ DBG_DEBUG("Received cookie descriptor %s\n\n",
+ sddl_encode(tmp_ctx, control_sd->default_sd, domain_sid));
+ TALLOC_FREE(tmp_ctx);
+ /* Mark the "change" control as uncritical (done) */
+ sd_ctrl->critical = false;
+ }
+
+ /*
+ * At this point we do not yet have the object's SID, so we
+ * leave it empty. It is irrelevant, as it is used to expand
+ * Principal-Self, and rights granted to PS will have no effect
+ * in this case
+ */
+ /* check if we have WD, no need to perform other attribute checks if we do */
+ attr = dsdb_attribute_by_lDAPDisplayName(schema, "nTSecurityDescriptor");
+ if (attr == NULL) {
+ return ldb_operr(ldb);
}
+
+ if (control_sd->specified_sacl) {
+ const struct security_token *token = acl_user_token(module);
+ bool has_priv = security_token_has_privilege(token, SEC_PRIV_SECURITY);
+ if (!has_priv) {
+ return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
+ }
+ }
+
+ ret = acl_check_access_on_attribute(module,
+ req,
+ control_sd->default_sd,
+ NULL,
+ SEC_STD_WRITE_DAC,
+ attr,
+ objectclass);
+ if (ret == LDB_SUCCESS) {
+ goto success;
+ }
+
+ if (control_sd->specified_sd) {
+ bool block_owner_rights = dsdb_block_owner_implicit_rights(module,
+ req,
+ req);
+ if (block_owner_rights) {
+ ldb_asprintf_errstring(ldb_module_get_ctx(module),
+ "Object %s has no SD modification rights",
+ ldb_dn_get_linearized(msg->dn));
+ dsdb_acl_debug(control_sd->default_sd,
+ acl_user_token(module),
+ msg->dn,
+ true,
+ 10);
+ ret = LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
+ return ret;
+ }
+ }
+
+ must_contain = dsdb_full_attribute_list(req, schema, &sorted_oc_el,
+ DSDB_SCHEMA_ALL_MUST);
+ for (i=0; i < msg->num_elements; i++) {
+ el = &msg->elements[i];
+
+ attr = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
+ if (attr == NULL && ldb_attr_cmp("clearTextPassword", el->name) != 0) {
+ ldb_asprintf_errstring(ldb, "acl_add: attribute '%s' "
+ "on entry '%s' was not found in the schema!",
+ el->name,
+ ldb_dn_get_linearized(msg->dn));
+ ret = LDB_ERR_NO_SUCH_ATTRIBUTE;
+ return ret;
+ }
+
+ if (attr != NULL) {
+ bool found = str_list_check(must_contain, attr->lDAPDisplayName);
+ /* do not check the mandatory attributes */
+ if (found) {
+ continue;
+ }
+ }
+
+ if (ldb_attr_cmp("dBCSPwd", el->name) == 0 ||
+ ldb_attr_cmp("unicodePwd", el->name) == 0 ||
+ ldb_attr_cmp("userPassword", el->name) == 0 ||
+ ldb_attr_cmp("clearTextPassword", el->name) == 0) {
+ continue;
+ } else if (ldb_attr_cmp("member", el->name) == 0) {
+ ret = acl_check_self_membership(req,
+ module,
+ req,
+ control_sd->default_sd,
+ NULL,
+ attr,
+ objectclass);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ } else if (ldb_attr_cmp("servicePrincipalName", el->name) == 0) {
+ ret = acl_check_spn(req,
+ module,
+ req,
+ el,
+ control_sd->default_sd,
+ NULL,
+ attr,
+ objectclass,
+ NULL);
+ if (ret != LDB_SUCCESS) {
+ ldb_asprintf_errstring(ldb_module_get_ctx(module),
+ "Object %s cannot be created with spn",
+ ldb_dn_get_linearized(msg->dn));
+ dsdb_acl_debug(control_sd->default_sd,
+ acl_user_token(module),
+ msg->dn,
+ true,
+ 10);
+ return ret;
+ }
+ } else if (ldb_attr_cmp("dnsHostName", el->name) == 0) {
+ ret = acl_check_dns_host_name(req,
+ module,
+ req,
+ el,
+ control_sd->default_sd,
+ NULL,
+ attr,
+ objectclass,
+ NULL);
+ if (ret != LDB_SUCCESS) {
+ ldb_asprintf_errstring(ldb_module_get_ctx(module),
+ "Object %s cannot be created with dnsHostName",
+ ldb_dn_get_linearized(msg->dn));
+ dsdb_acl_debug(control_sd->default_sd,
+ acl_user_token(module),
+ msg->dn,
+ true,
+ 10);
+ return ret;
+ }
+ } else {
+ ret = acl_check_access_on_attribute(module,
+ req,
+ control_sd->default_sd,
+ NULL,
+ SEC_ADS_WRITE_PROP,
+ attr,
+ objectclass);
+ if (ret != LDB_SUCCESS) {
+ ldb_asprintf_errstring(ldb_module_get_ctx(module),
+ "Object %s has no write property access",
+ ldb_dn_get_linearized(msg->dn));
+ dsdb_acl_debug(control_sd->default_sd,
+ acl_user_token(module),
+ msg->dn,
+ true,
+ 10);
+ ret = LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
+ return ret;
+ }
+ }
+ }
+success:
return ldb_next_request(module, req);
}
return final_sd;
}
-static DATA_BLOB *get_new_descriptor(struct ldb_module *module,
- struct ldb_dn *dn,
- TALLOC_CTX *mem_ctx,
- const struct dsdb_class *objectclass,
- const struct ldb_val *parent,
- const struct ldb_val *object,
- const struct ldb_val *old_sd,
- uint32_t sd_flags)
+static struct security_descriptor *get_new_descriptor_nonlinear(struct ldb_module *module,
+ struct ldb_dn *dn,
+ TALLOC_CTX *mem_ctx,
+ const struct dsdb_class *objectclass,
+ const struct ldb_val *parent,
+ const struct ldb_val *object,
+ const struct ldb_val *old_sd,
+ uint32_t sd_flags)
{
struct security_descriptor *user_descriptor = NULL, *parent_descriptor = NULL;
struct security_descriptor *old_descriptor = NULL;
struct security_descriptor *new_sd, *final_sd;
- DATA_BLOB *linear_sd;
enum ndr_err_code ndr_err;
struct ldb_context *ldb = ldb_module_get_ctx(module);
struct auth_session_info *session_info
TALLOC_FREE(tmp_ctx);
}
- linear_sd = talloc(mem_ctx, DATA_BLOB);
+ return final_sd;
+}
+
+static DATA_BLOB *get_new_descriptor(struct ldb_module *module,
+ struct ldb_dn *dn,
+ TALLOC_CTX *mem_ctx,
+ const struct dsdb_class *objectclass,
+ const struct ldb_val *parent,
+ const struct ldb_val *object,
+ const struct ldb_val *old_sd,
+ uint32_t sd_flags)
+{
+ struct security_descriptor *final_sd = NULL;
+ enum ndr_err_code ndr_err;
+ DATA_BLOB *linear_sd = talloc(mem_ctx, DATA_BLOB);
+
if (!linear_sd) {
return NULL;
}
+ final_sd = get_new_descriptor_nonlinear(module,
+ dn,
+ mem_ctx,
+ objectclass,
+ parent,
+ object,
+ old_sd,
+ sd_flags);
+ if (final_sd == NULL) {
+ return NULL;
+ }
+
ndr_err = ndr_push_struct_blob(linear_sd, mem_ctx,
final_sd,
(ndr_push_flags_fn_t)ndr_push_security_descriptor);
struct ldb_message *msg;
struct ldb_result *parent_res;
const struct ldb_val *parent_sd = NULL;
- const struct ldb_val *user_sd;
+ const struct ldb_val *user_sd = NULL;
struct ldb_dn *dn = req->op.add.message->dn;
struct ldb_dn *parent_dn, *nc_root;
struct ldb_message_element *objectclass_element, *sd_element;
static const char * const parent_attrs[] = { "nTSecurityDescriptor", NULL };
uint32_t instanceType;
bool isNC = false;
+ enum ndr_err_code ndr_err;
+ struct dsdb_control_calculated_default_sd *control_sd = NULL;
uint32_t sd_flags = dsdb_request_sd_flags(req, NULL);
+ struct security_descriptor *user_descriptor = NULL;
/* do not manipulate our control entries */
if (ldb_dn_is_special(dn)) {
*/
sd_flags = SECINFO_OWNER|SECINFO_GROUP|SECINFO_SACL|SECINFO_DACL;
+ control_sd = talloc(req, struct dsdb_control_calculated_default_sd);
+ if (control_sd == NULL) {
+ return ldb_operr(ldb);
+ }
+ control_sd->specified_sd = false;
+ control_sd->specified_sacl = false;
+ if (user_sd != NULL) {
+ user_descriptor = talloc(req, struct security_descriptor);
+ if (user_descriptor == NULL) {
+ return ldb_operr(ldb);
+ }
+ ndr_err = ndr_pull_struct_blob(user_sd, user_descriptor,
+ user_descriptor,
+ (ndr_pull_flags_fn_t)ndr_pull_security_descriptor);
+
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ talloc_free(user_descriptor);
+ return ldb_operr(ldb);
+ }
+ /*
+ * calculate the permissions needed, since in acl we no longer have
+ * access to the original user descriptor
+ */
+ control_sd->specified_sd = true;
+ control_sd->specified_sacl = user_descriptor->sacl != NULL;
+ }
+
sd = get_new_descriptor(module, dn, req,
objectclass, parent_sd,
user_sd, NULL, sd_flags);
if (sd == NULL) {
return ldb_operr(ldb);
}
+
+ control_sd->default_sd = get_new_descriptor_nonlinear(module,
+ dn,
+ req,
+ objectclass,
+ parent_sd,
+ NULL,
+ NULL,
+ sd_flags);
+ if (control_sd->default_sd == NULL) {
+ return ldb_operr(ldb);
+ }
+
msg = ldb_msg_copy_shallow(req, req->op.add.message);
if (msg == NULL) {
return ldb_oom(ldb);
req->controls,
req, dsdb_next_callback,
req);
+
LDB_REQ_SET_LOCATION(add_req);
if (ret != LDB_SUCCESS) {
return ldb_error(ldb, ret,
"descriptor_add: Error creating new add request.");
}
+ dom_sid_parse("S-1-0-0", control_sd->default_sd->owner_sid);
+ ret = ldb_request_add_control(add_req,
+ DSDB_CONTROL_CALCULATED_DEFAULT_SD_OID,
+ false, (void *)control_sd);
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_operr(module);
+ }
return ldb_next_request(module, add_req);
}
struct ldb_result *current_res, *parent_res;
const struct ldb_val *old_sd = NULL;
const struct ldb_val *parent_sd = NULL;
- const struct ldb_val *user_sd;
+ const struct ldb_val *user_sd = NULL;
struct ldb_dn *dn = req->op.mod.message->dn;
struct ldb_dn *parent_dn;
struct ldb_message_element *objectclass_element, *sd_element;