mirror of
https://github.com/samba-team/samba.git
synced 2025-01-03 01:18:10 +03:00
CVE-2020-25720: s4-acl: Change behavior of Create Children check
Up to now, the rights to modify an attribute were not checked during an LDAP add operation. This means that even if a user has no right to modify an attribute, they can still specify any value during object creation, and the validated writes were not checked. This patch changes this behavior. During an add operation, a security descriptor is created that does not include the one provided by the user, and is used to verify that the user has the right to modify the supplied attributes. Exception is made for an object's mandatory attributes, and if the user has Write DACL right, further checks are skipped. BUG: https://bugzilla.samba.org/show_bug.cgi?id=14810 Pair-Programmed-With: Joseph Sutton <josephsutton@catalyst.net.nz> Signed-off-by: Nadezhda Ivanova <nivanova@symas.com> Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abartlet@samba.org>
This commit is contained in:
parent
0e1d8929f8
commit
08187833fe
@ -1,23 +1,4 @@
|
||||
^samba4.ldap.acl.python\(.*\).__main__.AclAddTests.test_add_c1\(.*\)
|
||||
^samba4.ldap.acl.python\(.*\).__main__.AclAddTests.test_add_c2\(.*\)
|
||||
^samba4.ldap.acl.python\(.*\).__main__.AclAddTests.test_add_c5\(.*\)
|
||||
^samba4.ldap.acl.python\(.*\).__main__.AclAddTests.test_add_computer1\(.*\)
|
||||
^samba4.ldap.acl.python\(.*\).__main__.AclAddTests.test_add_derived_computer\(.*\)
|
||||
^samba4.ldap.acl.python\(.*\).__main__.AclAddTests.test_add_disallowed_attr\(.*\)
|
||||
^samba4.ldap.acl.python\(.*\).__main__.AclAddTests.test_add_optional_attr\(.*\)
|
||||
^samba4.ldap.acl.python\(.*\).__main__.AclAddTests.test_add_security_descriptor_dacl\(.*\)
|
||||
^samba4.ldap.acl.python\(.*\).__main__.AclAddTests.test_add_security_descriptor_dacl_implicit\(.*\)
|
||||
^samba4.ldap.acl.python\(.*\).__main__.AclAddTests.test_add_security_descriptor_empty\(.*\)
|
||||
^samba4.ldap.acl.python\(.*\).__main__.AclAddTests.test_add_security_descriptor_explicit_right_owner_not_us\(.*\)
|
||||
^samba4.ldap.acl.python\(.*\).__main__.AclAddTests.test_add_security_descriptor_explicit_right_sacl\(.*\)
|
||||
^samba4.ldap.acl.python\(.*\).__main__.AclAddTests.test_add_security_descriptor_group\(.*\)
|
||||
^samba4.ldap.acl.python\(.*\).__main__.AclAddTests.test_add_security_descriptor_group_explicit_right\(.*\)
|
||||
^samba4.ldap.acl.python\(.*\).__main__.AclAddTests.test_add_security_descriptor_group_implicit\(.*\)
|
||||
^samba4.ldap.acl.python\(.*\).__main__.AclAddTests.test_add_security_descriptor_implicit_right_optional_attr\(.*\)
|
||||
^samba4.ldap.acl.python\(.*\).__main__.AclAddTests.test_add_security_descriptor_owner\(.*\)
|
||||
^samba4.ldap.acl.python\(.*\).__main__.AclAddTests.test_add_security_descriptor_owner_explicit_right\(.*\)
|
||||
^samba4.ldap.acl.python\(.*\).__main__.AclAddTests.test_add_security_descriptor_owner_implicit\(.*\)
|
||||
^samba4.ldap.acl.python\(.*\).__main__.AclAddTests.test_add_security_descriptor_sacl\(.*\)
|
||||
^samba4.ldap.acl.python\(.*\).__main__.AclModifyTests.test_modify_dacl_explicit_computer\(.*\)
|
||||
^samba4.ldap.acl.python\(.*\).__main__.AclModifyTests.test_modify_dacl_owner_computer_implicit_right_allowed\(.*\)
|
||||
^samba4.ldap.acl.python\(.*\).__main__.AclModifyTests.test_modify_dacl_owner_computer_implicit_right_blocked\(.*\)
|
||||
|
@ -714,6 +714,8 @@ static int acl_check_spn(TALLOC_CTX *mem_ctx,
|
||||
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",
|
||||
@ -726,6 +728,12 @@ static int acl_check_spn(TALLOC_CTX *mem_ctx,
|
||||
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
|
||||
@ -758,7 +766,7 @@ static int acl_check_spn(TALLOC_CTX *mem_ctx,
|
||||
|
||||
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);
|
||||
@ -782,23 +790,32 @@ static int acl_check_spn(TALLOC_CTX *mem_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,
|
||||
@ -808,11 +825,13 @@ static int acl_check_spn(TALLOC_CTX *mem_ctx,
|
||||
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,
|
||||
@ -834,13 +853,18 @@ static int acl_check_spn(TALLOC_CTX *mem_ctx,
|
||||
|
||||
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;
|
||||
@ -910,6 +934,12 @@ static int acl_check_dns_host_name(TALLOC_CTX *mem_ctx,
|
||||
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
|
||||
@ -943,7 +973,7 @@ static int acl_check_dns_host_name(TALLOC_CTX *mem_ctx,
|
||||
|
||||
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);
|
||||
@ -964,8 +994,6 @@ static int acl_check_dns_host_name(TALLOC_CTX *mem_ctx,
|
||||
NULL
|
||||
};
|
||||
|
||||
msg = req->op.mod.message;
|
||||
|
||||
/*
|
||||
* If not add or replace (eg delete),
|
||||
* return success
|
||||
@ -991,7 +1019,6 @@ static int acl_check_dns_host_name(TALLOC_CTX *mem_ctx,
|
||||
|
||||
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);
|
||||
@ -1032,7 +1059,9 @@ static int acl_check_dns_host_name(TALLOC_CTX *mem_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",
|
||||
@ -1227,11 +1256,23 @@ static int acl_add(struct ldb_module *module, struct ldb_request *req)
|
||||
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);
|
||||
}
|
||||
|
||||
@ -1245,8 +1286,9 @@ static int acl_add(struct ldb_module *module, struct ldb_request *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);
|
||||
}
|
||||
@ -1256,21 +1298,38 @@ static int acl_add(struct ldb_module *module, struct ldb_request *req)
|
||||
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 };
|
||||
@ -1292,7 +1351,7 @@ static int acl_add(struct ldb_module *module, struct ldb_request *req)
|
||||
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 */
|
||||
@ -1312,6 +1371,7 @@ static int acl_add(struct ldb_module *module, struct ldb_request *req)
|
||||
* 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);
|
||||
}
|
||||
|
||||
@ -1337,18 +1397,215 @@ static int acl_add(struct ldb_module *module, struct ldb_request *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);
|
||||
}
|
||||
|
||||
|
@ -281,19 +281,18 @@ static struct security_descriptor *descr_handle_sd_flags(TALLOC_CTX *mem_ctx,
|
||||
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
|
||||
@ -463,11 +462,38 @@ static DATA_BLOB *get_new_descriptor(struct ldb_module *module,
|
||||
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);
|
||||
@ -601,7 +627,7 @@ static int descriptor_add(struct ldb_module *module, struct ldb_request *req)
|
||||
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;
|
||||
@ -612,7 +638,10 @@ static int descriptor_add(struct ldb_module *module, struct ldb_request *req)
|
||||
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)) {
|
||||
@ -698,12 +727,52 @@ static int descriptor_add(struct ldb_module *module, struct ldb_request *req)
|
||||
*/
|
||||
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);
|
||||
@ -724,12 +793,20 @@ static int descriptor_add(struct ldb_module *module, struct ldb_request *req)
|
||||
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);
|
||||
}
|
||||
|
||||
@ -741,7 +818,7 @@ static int descriptor_modify(struct ldb_module *module, struct ldb_request *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;
|
||||
|
@ -232,6 +232,18 @@ struct dsdb_control_transaction_identifier {
|
||||
*/
|
||||
#define DSDB_CONTROL_FORCE_ALLOW_VALIDATED_DNS_HOSTNAME_SPN_WRITE_OID "1.3.6.1.4.1.7165.4.3.35"
|
||||
|
||||
/*
|
||||
* Used by descriptor module to pass a special SD to acl module,
|
||||
* one without the user-provided descriptor taken into account
|
||||
*/
|
||||
|
||||
#define DSDB_CONTROL_CALCULATED_DEFAULT_SD_OID "1.3.6.1.4.1.7165.4.3.36"
|
||||
struct dsdb_control_calculated_default_sd {
|
||||
struct security_descriptor *default_sd;
|
||||
bool specified_sd:1;
|
||||
bool specified_sacl:1;
|
||||
};
|
||||
|
||||
#define DSDB_EXTENDED_REPLICATED_OBJECTS_OID "1.3.6.1.4.1.7165.4.4.1"
|
||||
struct dsdb_extended_replicated_object {
|
||||
struct ldb_message *msg;
|
||||
|
@ -696,7 +696,10 @@ class OwnerGroupDescriptorTests(DescriptorTests):
|
||||
mod = "(A;;WDCC;;;AU)"
|
||||
self.sd_utils.dacl_add_ace(self.schema_dn, mod)
|
||||
# Create example Schema class
|
||||
class_dn = self.create_schema_class(_ldb)
|
||||
try:
|
||||
class_dn = self.create_schema_class(_ldb)
|
||||
except LdbError as e3:
|
||||
self.fail()
|
||||
desc_sddl = self.sd_utils.get_sd_as_sddl(class_dn)
|
||||
res = re.search("(O:.*G:.*?)D:", desc_sddl).group(1)
|
||||
self.assertEqual(self.results[self.DS_BEHAVIOR][self._testMethodName[5:]], res)
|
||||
@ -985,7 +988,10 @@ class OwnerGroupDescriptorTests(DescriptorTests):
|
||||
# Create child object with user's credentials
|
||||
object_dn = "CN=test-specifier1," + object_dn
|
||||
delete_force(self.ldb_admin, object_dn)
|
||||
self.create_configuration_specifier(_ldb, object_dn)
|
||||
try:
|
||||
self.create_configuration_specifier(_ldb, object_dn)
|
||||
except LdbError as e3:
|
||||
self.fail()
|
||||
desc_sddl = self.sd_utils.get_sd_as_sddl(object_dn)
|
||||
res = re.search("(O:.*G:.*?)D:", desc_sddl).group(1)
|
||||
self.assertEqual(self.results[self.DS_BEHAVIOR][self._testMethodName[5:]] % str(user_sid), res)
|
||||
@ -1124,7 +1130,10 @@ class OwnerGroupDescriptorTests(DescriptorTests):
|
||||
# Create a custom security descriptor
|
||||
# NB! Problematic owner part won't accept DA only <User Sid> !!!
|
||||
desc_sddl = "O:%sG:DAD:(A;;RP;;;DU)" % str(user_sid)
|
||||
self.create_configuration_specifier(_ldb, object_dn, desc_sddl)
|
||||
try:
|
||||
self.create_configuration_specifier(_ldb, object_dn, desc_sddl)
|
||||
except LdbError as e3:
|
||||
self.fail()
|
||||
desc_sddl = self.sd_utils.get_sd_as_sddl(object_dn)
|
||||
res = re.search("(O:.*G:.*?)D:", desc_sddl).group(1)
|
||||
self.assertEqual(self.results[self.DS_BEHAVIOR][self._testMethodName[5:]] % str(user_sid), res)
|
||||
@ -1147,7 +1156,10 @@ class OwnerGroupDescriptorTests(DescriptorTests):
|
||||
# Create a custom security descriptor
|
||||
# NB! Problematic owner part won't accept DA only <User Sid> !!!
|
||||
desc_sddl = "O:%sG:DAD:(A;;RP;;;DU)" % str(user_sid)
|
||||
self.create_configuration_specifier(_ldb, object_dn, desc_sddl)
|
||||
try:
|
||||
self.create_configuration_specifier(_ldb, object_dn, desc_sddl)
|
||||
except LdbError as e3:
|
||||
self.fail()
|
||||
desc_sddl = self.sd_utils.get_sd_as_sddl(object_dn)
|
||||
res = re.search("(O:.*G:.*?)D:", desc_sddl).group(1)
|
||||
self.assertEqual(self.results[self.DS_BEHAVIOR][self._testMethodName[5:]] % str(user_sid), res)
|
||||
|
@ -322,8 +322,11 @@ class UserAccountControlTests(samba.tests.TestCase):
|
||||
sd = ldb.MessageElement((ndr_pack(self.sd_reference_modify)),
|
||||
ldb.FLAG_MOD_ADD,
|
||||
"nTSecurityDescriptor")
|
||||
self.add_computer_ldap(computername,
|
||||
others={"nTSecurityDescriptor": sd})
|
||||
try:
|
||||
self.add_computer_ldap(computername,
|
||||
others={"nTSecurityDescriptor": sd})
|
||||
except LdbError as e:
|
||||
self.fail(str(e))
|
||||
|
||||
res = self.admin_samdb.search("%s" % self.base_dn,
|
||||
expression="(&(objectClass=computer)(samAccountName=%s$))" % computername,
|
||||
@ -457,9 +460,11 @@ class UserAccountControlTests(samba.tests.TestCase):
|
||||
sd = ldb.MessageElement((ndr_pack(self.sd_reference_modify)),
|
||||
ldb.FLAG_MOD_ADD,
|
||||
"nTSecurityDescriptor")
|
||||
self.add_computer_ldap(computername,
|
||||
others={"nTSecurityDescriptor": sd})
|
||||
|
||||
try:
|
||||
self.add_computer_ldap(computername,
|
||||
others={"nTSecurityDescriptor": sd})
|
||||
except LdbError as e:
|
||||
self.fail(str(e))
|
||||
res = self.admin_samdb.search("%s" % self.base_dn,
|
||||
expression="(&(objectClass=computer)(samAccountName=%s$))" % computername,
|
||||
scope=SCOPE_SUBTREE,
|
||||
|
@ -1273,6 +1273,7 @@ static const struct ldap_control_handler ldap_known_controls[] = {
|
||||
{ DSDB_CONTROL_NO_GLOBAL_CATALOG, NULL, NULL },
|
||||
{ DSDB_EXTENDED_SCHEMA_UPGRADE_IN_PROGRESS_OID, NULL, NULL },
|
||||
{ DSDB_CONTROL_TRANSACTION_IDENTIFIER_OID, NULL, NULL},
|
||||
{ DSDB_CONTROL_CALCULATED_DEFAULT_SD_OID, NULL, NULL },
|
||||
{ NULL, NULL, NULL }
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user