lib:ldb: Use correct integer types for sizes
[vlendec/samba-autobuild/.git] / source4 / dsdb / samdb / ldb_modules / util.c
index 48600e718ba9851cb8f9240fa7b930d27195ade4..b343828d5084c9ec3cb7dc8643bac41b788135da 100644 (file)
@@ -1,4 +1,4 @@
-/* 
+/*
    Unix SMB/CIFS implementation.
    Samba utility functions
 
 #include "librpc/ndr/libndr.h"
 #include "dsdb/samdb/ldb_modules/util.h"
 #include "dsdb/samdb/samdb.h"
-#include "util.h"
+#include "dsdb/common/util.h"
 #include "libcli/security/security.h"
 
+#undef strcasecmp
+
 /*
   search for attrs on one DN, in the modules below
  */
@@ -46,6 +48,9 @@ int dsdb_module_search_dn(struct ldb_module *module,
        struct ldb_result *res;
 
        tmp_ctx = talloc_new(mem_ctx);
+       if (tmp_ctx == NULL) {
+               return ldb_oom(ldb_module_get_ctx(module));
+       }
 
        res = talloc_zero(tmp_ctx, struct ldb_result);
        if (!res) {
@@ -100,8 +105,8 @@ int dsdb_module_search_dn(struct ldb_module *module,
        if (res->count != 1) {
                /* we may be reading a DB that does not have the 'check base on search' option... */
                ret = LDB_ERR_NO_SUCH_OBJECT;
-               ldb_asprintf_errstring(ldb_module_get_ctx(module), 
-                                      "dsdb_module_search_dn: did not find base dn %s (%d results)", 
+               ldb_asprintf_errstring(ldb_module_get_ctx(module),
+                                      "dsdb_module_search_dn: did not find base dn %s (%d results)",
                                       ldb_dn_get_linearized(basedn), res->count);
        } else {
                *_res = talloc_steal(mem_ctx, res);
@@ -126,6 +131,9 @@ int dsdb_module_search_tree(struct ldb_module *module,
        struct ldb_result *res;
 
        tmp_ctx = talloc_new(mem_ctx);
+       if (tmp_ctx == NULL) {
+               return ldb_oom(ldb_module_get_ctx(module));
+       }
 
        /* cross-partitions searches with a basedn break multi-domain support */
        SMB_ASSERT(basedn == NULL || (dsdb_flags & DSDB_SEARCH_SEARCH_ALL_PARTITIONS) == 0);
@@ -174,6 +182,18 @@ int dsdb_module_search_tree(struct ldb_module *module,
                ret = ldb_wait(req->handle, LDB_WAIT_ALL);
        }
 
+       if (dsdb_flags & DSDB_SEARCH_ONE_ONLY) {
+               if (res->count == 0) {
+                       talloc_free(tmp_ctx);
+                       return ldb_error(ldb_module_get_ctx(module), LDB_ERR_NO_SUCH_OBJECT, __func__);
+               }
+               if (res->count != 1) {
+                       talloc_free(tmp_ctx);
+                       ldb_reset_err_string(ldb_module_get_ctx(module));
+                       return LDB_ERR_CONSTRAINT_VIOLATION;
+               }
+       }
+
        talloc_free(req);
        if (ret == LDB_SUCCESS) {
                *_res = talloc_steal(mem_ctx, res);
@@ -204,6 +224,9 @@ int dsdb_module_search(struct ldb_module *module,
        SMB_ASSERT(basedn == NULL || (dsdb_flags & DSDB_SEARCH_SEARCH_ALL_PARTITIONS) == 0);
 
        tmp_ctx = talloc_new(mem_ctx);
+       if (tmp_ctx == NULL) {
+               return ldb_oom(ldb_module_get_ctx(module));
+       }
 
        if (format) {
                va_start(ap, format);
@@ -241,14 +264,16 @@ int dsdb_module_search(struct ldb_module *module,
 }
 
 /*
-  find a DN given a GUID. This searches across all partitions
+  find an object given a GUID. This searches across all partitions
  */
-int dsdb_module_dn_by_guid(struct ldb_module *module, TALLOC_CTX *mem_ctx,
-                          const struct GUID *guid, struct ldb_dn **dn,
-                          struct ldb_request *parent)
+int dsdb_module_obj_by_guid(struct ldb_module *module,
+                           TALLOC_CTX *mem_ctx,
+                           struct ldb_message **_msg,
+                           const struct GUID *guid,
+                           const char * const *attrs,
+                           struct ldb_request *parent)
 {
        struct ldb_result *res;
-       const char *attrs[] = { NULL };
        TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
        int ret;
 
@@ -266,7 +291,7 @@ int dsdb_module_dn_by_guid(struct ldb_module *module, TALLOC_CTX *mem_ctx,
        }
        if (res->count == 0) {
                talloc_free(tmp_ctx);
-               return LDB_ERR_NO_SUCH_OBJECT;
+               return ldb_error(ldb_module_get_ctx(module), LDB_ERR_NO_SUCH_OBJECT, __func__);
        }
        if (res->count != 1) {
                ldb_asprintf_errstring(ldb_module_get_ctx(module), "More than one object found matching objectGUID %s\n",
@@ -275,7 +300,36 @@ int dsdb_module_dn_by_guid(struct ldb_module *module, TALLOC_CTX *mem_ctx,
                return LDB_ERR_OPERATIONS_ERROR;
        }
 
-       *dn = talloc_steal(mem_ctx, res->msgs[0]->dn);
+       *_msg = talloc_steal(mem_ctx, res->msgs[0]);
+
+       talloc_free(tmp_ctx);
+       return LDB_SUCCESS;
+}
+
+/*
+  find a DN given a GUID. This searches across all partitions
+ */
+int dsdb_module_dn_by_guid(struct ldb_module *module, TALLOC_CTX *mem_ctx,
+                          const struct GUID *guid, struct ldb_dn **dn,
+                          struct ldb_request *parent)
+{
+       struct ldb_message *msg = NULL;
+       static const char * const attrs[] = { NULL };
+       TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
+       int ret;
+
+       ret = dsdb_module_obj_by_guid(module,
+                                     tmp_ctx,
+                                     &msg,
+                                     guid,
+                                     attrs,
+                                     parent);
+       if (ret != LDB_SUCCESS) {
+               talloc_free(tmp_ctx);
+               return ret;
+       }
+
+       *dn = talloc_steal(mem_ctx, msg->dn);
 
        talloc_free(tmp_ctx);
        return LDB_SUCCESS;
@@ -287,7 +341,7 @@ int dsdb_module_dn_by_guid(struct ldb_module *module, TALLOC_CTX *mem_ctx,
 int dsdb_module_guid_by_dn(struct ldb_module *module, struct ldb_dn *dn, struct GUID *guid,
                           struct ldb_request *parent)
 {
-       const char *attrs[] = { NULL };
+       static const char * const attrs[] = { NULL };
        struct ldb_result *res;
        TALLOC_CTX *tmp_ctx = talloc_new(module);
        int ret;
@@ -314,14 +368,21 @@ int dsdb_module_guid_by_dn(struct ldb_module *module, struct ldb_dn *dn, struct
        talloc_free(tmp_ctx);
        return LDB_SUCCESS;
 }
+
+
 /*
   a ldb_extended request operating on modules below the
   current module
+
+  Note that this does not automatically start a transaction. If you
+  need a transaction the caller needs to start it as needed.
  */
 int dsdb_module_extended(struct ldb_module *module,
-                      const char* oid, void* data,
-                      uint32_t dsdb_flags,
-                      struct ldb_request *parent)
+                        TALLOC_CTX *mem_ctx,
+                        struct ldb_result **_res,
+                        const char* oid, void* data,
+                        uint32_t dsdb_flags,
+                        struct ldb_request *parent)
 {
        struct ldb_request *req;
        int ret;
@@ -329,6 +390,10 @@ int dsdb_module_extended(struct ldb_module *module,
        TALLOC_CTX *tmp_ctx = talloc_new(module);
        struct ldb_result *res;
 
+       if (_res != NULL) {
+               (*_res) = NULL;
+       }
+
        res = talloc_zero(tmp_ctx, struct ldb_result);
        if (!res) {
                talloc_free(tmp_ctx);
@@ -373,9 +438,15 @@ int dsdb_module_extended(struct ldb_module *module,
                ret = ldb_wait(req->handle, LDB_WAIT_ALL);
        }
 
+       if (_res != NULL && ret == LDB_SUCCESS) {
+               (*_res) = talloc_steal(mem_ctx, res);
+       }
+
        talloc_free(tmp_ctx);
        return ret;
 }
+
+
 /*
   a ldb_modify request operating on modules below the
   current module
@@ -652,45 +723,103 @@ int dsdb_check_single_valued_link(const struct dsdb_attribute *attr,
        return LDB_SUCCESS;
 }
 
-int dsdb_check_optional_feature(struct ldb_module *module, struct ldb_dn *scope,
-                                       struct GUID op_feature_guid, bool *feature_enabled)
+
+int dsdb_check_samba_compatible_feature(struct ldb_module *module,
+                                       const char *feature,
+                                       bool *found)
+{
+       struct ldb_context *ldb = ldb_module_get_ctx(module);
+       struct ldb_result *res;
+       static const char * const samba_dsdb_attrs[] = {
+               SAMBA_COMPATIBLE_FEATURES_ATTR,
+               NULL
+       };
+       int ret;
+       struct ldb_dn *samba_dsdb_dn = NULL;
+       TALLOC_CTX *tmp_ctx = talloc_new(ldb);
+       if (tmp_ctx == NULL) {
+               *found = false;
+               return ldb_oom(ldb);
+       }
+       *found = false;
+
+       samba_dsdb_dn = ldb_dn_new(tmp_ctx, ldb, "@SAMBA_DSDB");
+       if (samba_dsdb_dn == NULL) {
+               TALLOC_FREE(tmp_ctx);
+               return ldb_oom(ldb);
+       }
+
+       ret = dsdb_module_search_dn(module,
+                                   tmp_ctx,
+                                   &res,
+                                   samba_dsdb_dn,
+                                   samba_dsdb_attrs,
+                                   DSDB_FLAG_NEXT_MODULE,
+                                   NULL);
+       if (ret == LDB_SUCCESS) {
+               *found = ldb_msg_check_string_attribute(
+                       res->msgs[0],
+                       SAMBA_COMPATIBLE_FEATURES_ATTR,
+                       feature);
+       } else if (ret == LDB_ERR_NO_SUCH_OBJECT) {
+               /* it is not an error not to find it */
+               ret = LDB_SUCCESS;
+       }
+       TALLOC_FREE(tmp_ctx);
+       return ret;
+}
+
+
+/*
+  check if an optional feature is enabled on our own NTDS DN
+
+  Note that features can be marked as enabled in more than one
+  place. For example, the recyclebin feature is marked as enabled both
+  on the CN=Partitions,CN=Configuration object and on the NTDS DN of
+  each DC in the forest. It seems likely that it is the job of the KCC
+  to propagate between the two
+ */
+int dsdb_check_optional_feature(struct ldb_module *module, struct GUID op_feature_guid, bool *feature_enabled)
 {
        TALLOC_CTX *tmp_ctx;
        struct ldb_context *ldb = ldb_module_get_ctx(module);
        struct ldb_result *res;
        struct ldb_dn *search_dn;
        struct GUID search_guid;
-       const char *attrs[] = {"msDS-EnabledFeature", NULL};
+       static const char * const attrs[] = {"msDS-EnabledFeature", NULL};
        int ret;
        unsigned int i;
        struct ldb_message_element *el;
-
-       *feature_enabled = false;
+       struct ldb_dn *feature_dn;
 
        tmp_ctx = talloc_new(ldb);
 
-       ret = ldb_search(ldb, tmp_ctx, &res,
-                                       scope, LDB_SCOPE_BASE, attrs,
-                                       NULL);
+       feature_dn = samdb_ntds_settings_dn(ldb_module_get_ctx(module), tmp_ctx);
+       if (feature_dn == NULL) {
+               talloc_free(tmp_ctx);
+               return ldb_operr(ldb_module_get_ctx(module));
+       }
+
+       *feature_enabled = false;
+
+       ret = dsdb_module_search_dn(module, tmp_ctx, &res, feature_dn, attrs, DSDB_FLAG_NEXT_MODULE, NULL);
        if (ret != LDB_SUCCESS) {
                ldb_asprintf_errstring(ldb,
-                               "Could no find the scope object - dn: %s\n",
-                               ldb_dn_get_linearized(scope));
+                               "Could not find the feature object - dn: %s\n",
+                               ldb_dn_get_linearized(feature_dn));
                talloc_free(tmp_ctx);
-               return LDB_ERR_OPERATIONS_ERROR;
+               return LDB_ERR_NO_SUCH_OBJECT;
        }
        if (res->msgs[0]->num_elements > 0) {
+               static const char * const attrs2[] = {"msDS-OptionalFeatureGUID", NULL};
 
                el = ldb_msg_find_element(res->msgs[0],"msDS-EnabledFeature");
 
-               attrs[0] = "msDS-OptionalFeatureGUID";
-
                for (i=0; i<el->num_values; i++) {
                        search_dn = ldb_dn_from_ldb_val(tmp_ctx, ldb, &el->values[i]);
 
-                       ret = ldb_search(ldb, tmp_ctx, &res,
-                                                       search_dn, LDB_SCOPE_BASE, attrs,
-                                                       NULL);
+                       ret = dsdb_module_search_dn(module, tmp_ctx, &res,
+                                                   search_dn, attrs2, DSDB_FLAG_NEXT_MODULE, NULL);
                        if (ret != LDB_SUCCESS) {
                                ldb_asprintf_errstring(ldb,
                                                "Could no find object dn: %s\n",
@@ -701,7 +830,7 @@ int dsdb_check_optional_feature(struct ldb_module *module, struct ldb_dn *scope,
 
                        search_guid = samdb_result_guid(res->msgs[0], "msDS-OptionalFeatureGUID");
 
-                       if (GUID_compare(&search_guid, &op_feature_guid) == 0){
+                       if (GUID_equal(&search_guid, &op_feature_guid)) {
                                *feature_enabled = true;
                                break;
                        }
@@ -792,8 +921,29 @@ int dsdb_next_callback(struct ldb_request *req, struct ldb_reply *ares)
 {
        struct ldb_request *up_req = talloc_get_type(req->context, struct ldb_request);
 
-       talloc_steal(up_req, req);
-       return up_req->callback(up_req, ares);
+       if (!ares) {
+               return ldb_module_done(up_req, NULL, NULL,
+                                      LDB_ERR_OPERATIONS_ERROR);
+       }
+
+       if (ares->error != LDB_SUCCESS || ares->type == LDB_REPLY_DONE) {
+               return ldb_module_done(up_req, ares->controls,
+                                      ares->response, ares->error);
+       }
+
+       /* Otherwise pass on the callback */
+       switch (ares->type) {
+       case LDB_REPLY_ENTRY:
+               return ldb_module_send_entry(up_req, ares->message,
+                                            ares->controls);
+
+       case LDB_REPLY_REFERRAL:
+               return ldb_module_send_referral(up_req,
+                                               ares->referral);
+       default:
+               /* Can't happen */
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
 }
 
 /*
@@ -989,7 +1139,9 @@ bool dsdb_module_am_system(struct ldb_module *module)
 {
        struct ldb_context *ldb = ldb_module_get_ctx(module);
        struct auth_session_info *session_info
-               = talloc_get_type(ldb_get_opaque(ldb, "sessionInfo"), struct auth_session_info);
+               = talloc_get_type(
+                       ldb_get_opaque(ldb, DSDB_SESSION_INFO),
+                       struct auth_session_info);
        return security_session_user_level(session_info, NULL) == SECURITY_SYSTEM;
 }
 
@@ -997,32 +1149,56 @@ bool dsdb_module_am_administrator(struct ldb_module *module)
 {
        struct ldb_context *ldb = ldb_module_get_ctx(module);
        struct auth_session_info *session_info
-               = talloc_get_type(ldb_get_opaque(ldb, "sessionInfo"), struct auth_session_info);
+               = talloc_get_type(
+                       ldb_get_opaque(ldb, DSDB_SESSION_INFO),
+                       struct auth_session_info);
        return security_session_user_level(session_info, NULL) == SECURITY_ADMINISTRATOR;
 }
 
+/*
+ * Return â€˜true’ if the caller has system access. The â€˜acl’ module passes
+ * SYSTEM_CONTROL_STRIP_CRITICAL when it wants to strip the critical flag.
+ */
+bool dsdb_have_system_access(
+       struct ldb_module *module,
+       struct ldb_request *req,
+       const enum system_control_strip_critical strip_critical)
+{
+       struct ldb_control *as_system = NULL;
+
+       as_system = ldb_request_get_control(req, LDB_CONTROL_AS_SYSTEM_OID);
+       if (as_system != NULL) {
+               switch (strip_critical) {
+               case SYSTEM_CONTROL_KEEP_CRITICAL:
+                       break;
+               case SYSTEM_CONTROL_STRIP_CRITICAL:
+                       as_system->critical = 0;
+                       break;
+               }
+
+               return true;
+       }
+
+       return dsdb_module_am_system(module);
+}
+
 /*
   check if the recyclebin is enabled
  */
 int dsdb_recyclebin_enabled(struct ldb_module *module, bool *enabled)
 {
        struct ldb_context *ldb = ldb_module_get_ctx(module);
-       struct ldb_dn *partitions_dn;
        struct GUID recyclebin_guid;
        int ret;
 
-       partitions_dn = samdb_partitions_dn(ldb, module);
-
        GUID_from_string(DS_GUID_FEATURE_RECYCLE_BIN, &recyclebin_guid);
 
-       ret = dsdb_check_optional_feature(module, partitions_dn, recyclebin_guid, enabled);
+       ret = dsdb_check_optional_feature(module, recyclebin_guid, enabled);
        if (ret != LDB_SUCCESS) {
                ldb_asprintf_errstring(ldb, "Could not verify if Recycle Bin is enabled \n");
-               talloc_free(partitions_dn);
-               return LDB_ERR_UNWILLING_TO_PERFORM;
+               return ret;
        }
 
-       talloc_free(partitions_dn);
        return LDB_SUCCESS;
 }
 
@@ -1156,6 +1332,9 @@ int dsdb_module_constrainted_update_int32(struct ldb_module *module,
        int ret;
 
        msg = ldb_msg_new(module);
+       if (msg == NULL) {
+               return ldb_module_oom(module);
+       }
        msg->dn = dn;
 
        ret = dsdb_msg_constrainted_update_int32(module,
@@ -1198,6 +1377,9 @@ int dsdb_module_constrainted_update_int64(struct ldb_module *module,
        int ret;
 
        msg = ldb_msg_new(module);
+       if (msg == NULL) {
+               return ldb_module_oom(module);
+       }
        msg->dn = dn;
 
        ret = dsdb_msg_constrainted_update_int64(module,
@@ -1234,7 +1416,7 @@ const struct ldb_val *dsdb_module_find_dsheuristics(struct ldb_module *module,
        int ret;
        struct ldb_dn *new_dn;
        struct ldb_context *ldb = ldb_module_get_ctx(module);
-       static const char *attrs[] = { "dSHeuristics", NULL };
+       static const char * const attrs[] = { "dSHeuristics", NULL };
        struct ldb_result *res;
 
        new_dn = ldb_dn_copy(mem_ctx, ldb_get_config_basedn(ldb));
@@ -1297,6 +1479,67 @@ bool dsdb_user_password_support(struct ldb_module *module,
        return result;
 }
 
+bool dsdb_do_list_object(struct ldb_module *module,
+                        TALLOC_CTX *mem_ctx,
+                        struct ldb_request *parent)
+{
+       TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
+       bool result;
+       const struct ldb_val *hr_val = dsdb_module_find_dsheuristics(module,
+                                                                    tmp_ctx,
+                                                                    parent);
+       if (hr_val == NULL || hr_val->length < DS_HR_DOLISTOBJECT) {
+               result = false;
+       } else if (hr_val->data[DS_HR_DOLISTOBJECT -1] == '1') {
+               result = true;
+       } else {
+               result = false;
+       }
+
+       talloc_free(tmp_ctx);
+       return result;
+}
+
+bool dsdb_attribute_authz_on_ldap_add(struct ldb_module *module,
+                                     TALLOC_CTX *mem_ctx,
+                                     struct ldb_request *parent)
+{
+       TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
+       bool result = false;
+       const struct ldb_val *hr_val = dsdb_module_find_dsheuristics(module,
+                                                                    tmp_ctx,
+                                                                    parent);
+       if (hr_val != NULL && hr_val->length >= DS_HR_ATTR_AUTHZ_ON_LDAP_ADD) {
+               uint8_t val = hr_val->data[DS_HR_ATTR_AUTHZ_ON_LDAP_ADD - 1];
+               if (val != '0' && val != '2') {
+                       result = true;
+               }
+       }
+
+       talloc_free(tmp_ctx);
+       return result;
+}
+
+bool dsdb_block_owner_implicit_rights(struct ldb_module *module,
+                                     TALLOC_CTX *mem_ctx,
+                                     struct ldb_request *parent)
+{
+       TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
+       bool result = false;
+       const struct ldb_val *hr_val = dsdb_module_find_dsheuristics(module,
+                                                                    tmp_ctx,
+                                                                    parent);
+       if (hr_val != NULL && hr_val->length >= DS_HR_BLOCK_OWNER_IMPLICIT_RIGHTS) {
+               uint8_t val = hr_val->data[DS_HR_BLOCK_OWNER_IMPLICIT_RIGHTS - 1];
+               if (val != '0' && val != '2') {
+                       result = true;
+               }
+       }
+
+       talloc_free(tmp_ctx);
+       return result;
+}
+
 /*
   show the chain of requests, useful for debugging async requests
  */
@@ -1308,35 +1551,425 @@ void dsdb_req_chain_debug(struct ldb_request *req, int level)
 }
 
 /*
- * Gets back a single-valued attribute by the rules of the DSDB triggers when
- * performing a modify operation.
+ * Get all the values that *might* be added by an ldb message, as a composite
+ * ldb element.
+ *
+ * This is useful when we need to check all the possible values against some
+ * criteria.
  *
- * In order that the constraint checking by the "objectclass_attrs" LDB module
- * does work properly, the change request should remain similar or only be
- * enhanced (no other modifications as deletions, variations).
+ * In cases where a modify message mixes multiple ADDs, DELETEs, and REPLACES,
+ * the returned element might contain more values than would actually end up
+ * in the database if the message was run to its conclusion.
+ *
+ * If the operation is not LDB_ADD or LDB_MODIFY, an operations error is
+ * returned.
+ *
+ * The returned element might not be new, and should not be modified or freed
+ * before the message is finished.
  */
-struct ldb_message_element *dsdb_get_single_valued_attr(const struct ldb_message *msg,
-                                                       const char *attr_name,
-                                                       enum ldb_request_type operation)
+
+int dsdb_get_expected_new_values(TALLOC_CTX *mem_ctx,
+                                const struct ldb_message *msg,
+                                const char *attr_name,
+                                struct ldb_message_element **el,
+                                enum ldb_request_type operation)
 {
-       struct ldb_message_element *el = NULL;
        unsigned int i;
+       unsigned int el_count = 0;
+       unsigned int val_count = 0;
+       struct ldb_val *v = NULL;
+       struct ldb_message_element *_el = NULL;
+       *el = NULL;
+
+       if (operation != LDB_ADD && operation != LDB_MODIFY) {
+               DBG_ERR("inapplicable operation type: %d\n", operation);
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
 
-       /* We've to walk over all modification entries and consider the last
-        * non-delete one which belongs to "attr_name".
-        *
-        * If "el" is NULL afterwards then that means there was no interesting
-        * change entry. */
+       /* count the adding or replacing elements */
        for (i = 0; i < msg->num_elements; i++) {
                if (ldb_attr_cmp(msg->elements[i].name, attr_name) == 0) {
+                       unsigned int tmp;
                        if ((operation == LDB_MODIFY) &&
                            (LDB_FLAG_MOD_TYPE(msg->elements[i].flags)
                                                == LDB_FLAG_MOD_DELETE)) {
                                continue;
                        }
-                       el = &msg->elements[i];
+                       el_count++;
+                       tmp = val_count + msg->elements[i].num_values;
+                       if (unlikely(tmp < val_count)) {
+                               DBG_ERR("too many values for one element!\n");
+                               return LDB_ERR_OPERATIONS_ERROR;
+                       }
+                       val_count = tmp;
+               }
+       }
+       if (el_count == 0) {
+               /* nothing to see here */
+               return LDB_SUCCESS;
+       }
+
+       if (el_count == 1 || val_count == 0) {
+               /*
+                * There is one effective element, which we can return as-is,
+                * OR there are only elements with zero values -- any of which
+                * will do.
+                */
+               for (i = 0; i < msg->num_elements; i++) {
+                       if (ldb_attr_cmp(msg->elements[i].name, attr_name) == 0) {
+                               if ((operation == LDB_MODIFY) &&
+                                   (LDB_FLAG_MOD_TYPE(msg->elements[i].flags)
+                                    == LDB_FLAG_MOD_DELETE)) {
+                                       continue;
+                               }
+                               *el = &msg->elements[i];
+                               return LDB_SUCCESS;
+                       }
+               }
+       }
+
+       _el = talloc_zero(mem_ctx, struct ldb_message_element);
+       if (_el == NULL) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+       _el->name = attr_name;
+
+       if (val_count == 0) {
+               /*
+                * Seems unlikely, but sometimes we might be adding zero
+                * values in multiple separate elements. The talloc zero has
+                * already set the expected values = NULL, num_values = 0.
+                */
+               *el = _el;
+               return LDB_SUCCESS;
+       }
+
+       _el->values = talloc_array(_el, struct ldb_val, val_count);
+       if (_el->values == NULL) {
+               talloc_free(_el);
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+       _el->num_values = val_count;
+
+       v = _el->values;
+
+       for (i = 0; i < msg->num_elements; i++) {
+               if (ldb_attr_cmp(msg->elements[i].name, attr_name) == 0) {
+                       const struct ldb_message_element *tmp_el = &msg->elements[i];
+                       if ((operation == LDB_MODIFY) &&
+                           (LDB_FLAG_MOD_TYPE(tmp_el->flags)
+                                               == LDB_FLAG_MOD_DELETE)) {
+                               continue;
+                       }
+                       if (tmp_el->values == NULL || tmp_el->num_values == 0) {
+                               continue;
+                       }
+                       memcpy(v,
+                              tmp_el->values,
+                              tmp_el->num_values * sizeof(*v));
+                       v += tmp_el->num_values;
+               }
+       }
+
+       *el = _el;
+       return LDB_SUCCESS;
+}
+
+
+/*
+ * Get the value of a single-valued attribute from an ADDed message. 'val' will only live as
+ * long as 'msg' and 'original_val' do, and must not be freed.
+ */
+int dsdb_msg_add_get_single_value(const struct ldb_message *msg,
+                                  const char *attr_name,
+                                  const struct ldb_val **val)
+{
+       const struct ldb_message_element *el = NULL;
+
+       /*
+        * The ldb_msg_normalize() call in ldb_request() ensures that
+        * there is at most one message element for each
+        * attribute. Thus, we don't need a loop to deal with an
+        * LDB_ADD.
+        */
+       el = ldb_msg_find_element(msg, attr_name);
+       if (el == NULL) {
+               *val = NULL;
+               return LDB_SUCCESS;
+       }
+       if (el->num_values != 1) {
+               return LDB_ERR_CONSTRAINT_VIOLATION;
+       }
+
+       *val = &el->values[0];
+       return LDB_SUCCESS;
+}
+
+/*
+ * Get the value of a single-valued attribute after processing a
+ * message. 'operation' is either LDB_ADD or LDB_MODIFY. 'val' will only live as
+ * long as 'msg' and 'original_val' do, and must not be freed.
+ */
+int dsdb_msg_get_single_value(const struct ldb_message *msg,
+                             const char *attr_name,
+                             const struct ldb_val *original_val,
+                             const struct ldb_val **val,
+                             enum ldb_request_type operation)
+{
+       unsigned idx;
+
+       *val = NULL;
+
+       if (operation == LDB_ADD) {
+               if (original_val != NULL) {
+                       /* This is an error on the caller's part. */
+                       return LDB_ERR_CONSTRAINT_VIOLATION;
+               }
+               return dsdb_msg_add_get_single_value(msg, attr_name, val);
+       }
+
+       SMB_ASSERT(operation == LDB_MODIFY);
+
+       *val = original_val;
+
+       for (idx = 0; idx < msg->num_elements; ++idx) {
+               const struct ldb_message_element *el = &msg->elements[idx];
+
+               if (ldb_attr_cmp(el->name, attr_name) != 0) {
+                       continue;
+               }
+
+               switch (el->flags & LDB_FLAG_MOD_MASK) {
+               case LDB_FLAG_MOD_ADD:
+                       if (el->num_values != 1) {
+                               return LDB_ERR_CONSTRAINT_VIOLATION;
+                       }
+                       if (*val != NULL) {
+                               return LDB_ERR_CONSTRAINT_VIOLATION;
+                       }
+
+                       *val = &el->values[0];
+
+                       break;
+
+               case LDB_FLAG_MOD_REPLACE:
+                       if (el->num_values > 1) {
+                               return LDB_ERR_CONSTRAINT_VIOLATION;
+                       }
+
+                       *val = el->num_values ? &el->values[0] : NULL;
+
+                       break;
+
+               case LDB_FLAG_MOD_DELETE:
+                       if (el->num_values > 1) {
+                               return LDB_ERR_CONSTRAINT_VIOLATION;
+                       }
+
+                       /*
+                        * If a value was specified for the delete, we don't
+                        * bother checking it matches the value we currently
+                        * have. Any mismatch will be caught later (e.g. in
+                        * ldb_kv_modify_internal).
+                        */
+
+                       *val = NULL;
+
+                       break;
                }
        }
 
-       return el;
+       return LDB_SUCCESS;
+}
+
+/*
+ * This function determines the (last) structural or 88 object class of a passed
+ * "objectClass" attribute - per MS-ADTS 3.1.1.1.4 this is the last value.
+ * Without schema this does not work and hence NULL is returned.
+ */
+const struct dsdb_class *dsdb_get_last_structural_class(const struct dsdb_schema *schema,
+                                                       const struct ldb_message_element *element)
+{
+       const struct dsdb_class *last_class;
+
+       if (schema == NULL) {
+               return NULL;
+       }
+
+       if (element->num_values == 0) {
+               return NULL;
+       }
+
+       last_class = dsdb_class_by_lDAPDisplayName_ldb_val(schema,
+                                                          &element->values[element->num_values-1]);
+       if (last_class == NULL) {
+               return NULL;
+       }
+       if (last_class->objectClassCategory > 1) {
+               return NULL;
+       }
+
+       return last_class;
+}
+
+const struct dsdb_class *dsdb_get_structural_oc_from_msg(const struct dsdb_schema *schema,
+                                                        const struct ldb_message *msg)
+{
+       struct ldb_message_element *oc_el;
+
+       oc_el = ldb_msg_find_element(msg, "objectClass");
+       if (!oc_el) {
+               return NULL;
+       }
+
+       return dsdb_get_last_structural_class(schema, oc_el);
+}
+
+/*
+  Get the parent class of an objectclass, or NULL if none exists.
+ */
+const struct dsdb_class *dsdb_get_parent_class(const struct dsdb_schema *schema,
+                                              const struct dsdb_class *objectclass)
+{
+       if (ldb_attr_cmp(objectclass->lDAPDisplayName, "top") == 0) {
+               return NULL;
+       }
+
+       if (objectclass->subClassOf == NULL) {
+               return NULL;
+       }
+
+       return dsdb_class_by_lDAPDisplayName(schema, objectclass->subClassOf);
+}
+
+/*
+  Return true if 'struct_objectclass' is a subclass of 'other_objectclass'. The
+  two objectclasses must originate from the same schema, to allow for
+  pointer-based identity comparison.
+ */
+bool dsdb_is_subclass_of(const struct dsdb_schema *schema,
+                        const struct dsdb_class *struct_objectclass,
+                        const struct dsdb_class *other_objectclass)
+{
+       while (struct_objectclass != NULL) {
+               /* Pointer comparison can be used due to the same schema str. */
+               if (struct_objectclass == other_objectclass) {
+                       return true;
+               }
+
+               struct_objectclass = dsdb_get_parent_class(schema, struct_objectclass);
+       }
+
+       return false;
+}
+
+/* Fix the DN so that the relative attribute names are in upper case so that the DN:
+   cn=Administrator,cn=users,dc=samba,dc=example,dc=com becomes
+   CN=Administrator,CN=users,DC=samba,DC=example,DC=com
+*/
+int dsdb_fix_dn_rdncase(struct ldb_context *ldb, struct ldb_dn *dn)
+{
+       int i, ret;
+       char *upper_rdn_attr;
+
+       for (i=0; i < ldb_dn_get_comp_num(dn); i++) {
+               /* We need the attribute name in upper case */
+               upper_rdn_attr = strupper_talloc(dn,
+                                                ldb_dn_get_component_name(dn, i));
+               if (!upper_rdn_attr) {
+                       return ldb_oom(ldb);
+               }
+               ret = ldb_dn_set_component(dn, i, upper_rdn_attr,
+                                          *ldb_dn_get_component_val(dn, i));
+               talloc_free(upper_rdn_attr);
+               if (ret != LDB_SUCCESS) {
+                       return ret;
+               }
+       }
+       return LDB_SUCCESS;
+}
+
+/**
+ * Make most specific objectCategory for the objectClass of passed object
+ * NOTE: In this implementation we count that it is called on already
+ * verified objectClass attribute value. See objectclass.c thorough
+ * implementation for all the magic that involves
+ *
+ * @param ldb  ldb context
+ * @param schema cached schema for ldb. We may get it, but it is very time consuming.
+ *                     Hence leave the responsibility to the caller.
+ * @param obj  AD object to determine objectCategory for
+ * @param mem_ctx Memory context - usually it is obj actually
+ * @param pobjectcategory location to store found objectCategory
+ *
+ * @return LDB_SUCCESS or error including out of memory error
+ */
+int dsdb_make_object_category(struct ldb_context *ldb, const struct dsdb_schema *schema,
+                             const struct ldb_message *obj,
+                             TALLOC_CTX *mem_ctx, const char **pobjectcategory)
+{
+       const struct dsdb_class                 *objectclass;
+       struct ldb_message_element              *objectclass_element;
+       struct dsdb_extended_dn_store_format    *dn_format;
+
+       objectclass_element = ldb_msg_find_element(obj, "objectClass");
+       if (!objectclass_element) {
+               ldb_asprintf_errstring(ldb, "dsdb: Cannot add %s, no objectclass specified!",
+                                      ldb_dn_get_linearized(obj->dn));
+               return LDB_ERR_OBJECT_CLASS_VIOLATION;
+       }
+       if (objectclass_element->num_values == 0) {
+               ldb_asprintf_errstring(ldb, "dsdb: Cannot add %s, at least one (structural) objectclass has to be specified!",
+                                      ldb_dn_get_linearized(obj->dn));
+               return LDB_ERR_CONSTRAINT_VIOLATION;
+       }
+
+       /*
+        * Get the new top-most structural object class and check for
+        * unrelated structural classes
+        */
+       objectclass = dsdb_get_last_structural_class(schema,
+                                                    objectclass_element);
+       if (objectclass == NULL) {
+               ldb_asprintf_errstring(ldb,
+                                      "Failed to find a structural class for %s",
+                                      ldb_dn_get_linearized(obj->dn));
+               return LDB_ERR_UNWILLING_TO_PERFORM;
+       }
+
+       dn_format = talloc_get_type(ldb_get_opaque(ldb, DSDB_EXTENDED_DN_STORE_FORMAT_OPAQUE_NAME),
+                                   struct dsdb_extended_dn_store_format);
+       if (dn_format && dn_format->store_extended_dn_in_ldb == false) {
+               /* Strip off extended components */
+               struct ldb_dn *dn = ldb_dn_new(mem_ctx, ldb,
+                                              objectclass->defaultObjectCategory);
+               *pobjectcategory = ldb_dn_alloc_linearized(mem_ctx, dn);
+               talloc_free(dn);
+       } else {
+               *pobjectcategory = talloc_strdup(mem_ctx, objectclass->defaultObjectCategory);
+       }
+
+       if (*pobjectcategory == NULL) {
+               return ldb_oom(ldb);
+       }
+
+       return LDB_SUCCESS;
+}
+
+/*
+ * Remove all password related attributes.
+ */
+void dsdb_remove_password_related_attrs(struct ldb_message *msg,
+                                       bool userPassword)
+{
+       if (userPassword) {
+               ldb_msg_remove_attr(msg, "userPassword");
+       }
+       ldb_msg_remove_attr(msg, "clearTextPassword");
+       ldb_msg_remove_attr(msg, "unicodePwd");
+       ldb_msg_remove_attr(msg, "ntPwdHistory");
+       ldb_msg_remove_attr(msg, "dBCSPwd");
+       ldb_msg_remove_attr(msg, "lmPwdHistory");
+       ldb_msg_remove_attr(msg, "supplementalCredentials");
+       ldb_msg_remove_attr(msg, "pwdLastSet");
 }