]> git.ipfire.org Git - thirdparty/samba.git/commitdiff
CVE-2020-25720: s4-acl: Change behavior of Create Children check
authorNadezhda Ivanova <nivanova@symas.com>
Mon, 25 Oct 2021 10:10:56 +0000 (13:10 +0300)
committerAndrew Bartlett <abartlet@samba.org>
Fri, 16 Sep 2022 02:32:36 +0000 (02:32 +0000)
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>
selftest/knownfail.d/bug-14810
source4/dsdb/samdb/ldb_modules/acl.c
source4/dsdb/samdb/ldb_modules/descriptor.c
source4/dsdb/samdb/samdb.h
source4/dsdb/tests/python/sec_descriptor.py
source4/dsdb/tests/python/user_account_control.py
source4/libcli/ldap/ldap_controls.c

index a4aebe7964b653346e8cb009063da3d54a33d9ed..74e832fe6ffd99e0a6c60b9c73b388bc67a8350c 100644 (file)
@@ -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\(.*\)
index 038eb1dad2fb10b17a9f43fe5cef74ef8a659c22..38aa465a8d52c9d924838392f6bb3c91844ea760 100644 (file)
@@ -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);
 }
 
index aef2e284dc74eada8cb62ed3205a2beac4d91146..1b28781a4f9fb31938d94c28213ef4ac443992ea 100644 (file)
@@ -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;
index 3db7704307fd3884eeb437f51257845505d27803..f2f738121f97a860337495e8200269462d6acd30 100644 (file)
@@ -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;
index 5211634ab2f534cf8c94a9af7697adced4f4f617..b22b0c0f10cf4c941ef4a245106c012d32295f9c 100755 (executable)
@@ -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)
index b427aaf1ea3277ff74523510ffb15181612c9740..192b382a6a416df50c4f883f00ed0535242bffa9 100755 (executable)
@@ -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,
index 9193971c91fb5444152d1558808a0d1a573edfba..517243d13adcdc6a5eb6d9ee5949063ed78ecf2c 100644 (file)
@@ -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 }
 };