s4-dsdb: ensure rIDSetReferences is stored as an extended DN
[amitay/samba.git] / source4 / dsdb / samdb / ldb_modules / util.c
index d7bf807c24e65188188e02314c0ee1c0ab36c868..dfb860018788b31701e07f91c482f40ab2096d53 100644 (file)
@@ -4,6 +4,7 @@
 
    Copyright (C) Andrew Tridgell 2009
    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2009
+   Copyright (C) Matthieu Patou <mat@matws.net> 2011
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -27,7 +28,6 @@
 #include "dsdb/samdb/samdb.h"
 #include "util.h"
 #include "libcli/security/security.h"
-#include "lib/ldb/include/ldb_private.h"
 
 /*
   search for attrs on one DN, in the modules below
@@ -37,7 +37,8 @@ int dsdb_module_search_dn(struct ldb_module *module,
                          struct ldb_result **_res,
                          struct ldb_dn *basedn,
                          const char * const *attrs,
-                         uint32_t dsdb_flags)
+                         uint32_t dsdb_flags,
+                         struct ldb_request *parent)
 {
        int ret;
        struct ldb_request *req;
@@ -60,7 +61,7 @@ int dsdb_module_search_dn(struct ldb_module *module,
                                   NULL,
                                   res,
                                   ldb_search_default_callback,
-                                  NULL);
+                                  parent);
        LDB_REQ_SET_LOCATION(req);
        if (ret != LDB_SUCCESS) {
                talloc_free(tmp_ctx);
@@ -73,6 +74,10 @@ int dsdb_module_search_dn(struct ldb_module *module,
                return ret;
        }
 
+       if (dsdb_flags & DSDB_FLAG_TRUSTED) {
+               ldb_req_mark_trusted(req);
+       }
+
        /* Run the new request */
        if (dsdb_flags & DSDB_FLAG_NEXT_MODULE) {
                ret = ldb_next_request(module, req);
@@ -105,38 +110,23 @@ int dsdb_module_search_dn(struct ldb_module *module,
        return ret;
 }
 
-/*
-  search for attrs in the modules below
- */
-int dsdb_module_search(struct ldb_module *module,
+int dsdb_module_search_tree(struct ldb_module *module,
                       TALLOC_CTX *mem_ctx,
                       struct ldb_result **_res,
-                      struct ldb_dn *basedn, enum ldb_scope scope, 
+                      struct ldb_dn *basedn,
+                      enum ldb_scope scope,
+                      struct ldb_parse_tree *tree,
                       const char * const *attrs,
-                      int dsdb_flags, 
-                      const char *format, ...) _PRINTF_ATTRIBUTE(8, 9)
+                      int dsdb_flags,
+                      struct ldb_request *parent)
 {
        int ret;
        struct ldb_request *req;
        TALLOC_CTX *tmp_ctx;
        struct ldb_result *res;
-       va_list ap;
-       char *expression;
 
        tmp_ctx = talloc_new(mem_ctx);
 
-       if (format) {
-               va_start(ap, format);
-               expression = talloc_vasprintf(tmp_ctx, format, ap);
-               va_end(ap);
-
-               if (!expression) {
-                       talloc_free(tmp_ctx);
-                       return ldb_oom(ldb_module_get_ctx(module));
-               }
-       } else {
-               expression = NULL;
-       }
 
        res = talloc_zero(tmp_ctx, struct ldb_result);
        if (!res) {
@@ -144,15 +134,15 @@ int dsdb_module_search(struct ldb_module *module,
                return ldb_oom(ldb_module_get_ctx(module));
        }
 
-       ret = ldb_build_search_req(&req, ldb_module_get_ctx(module), tmp_ctx,
+       ret = ldb_build_search_req_ex(&req, ldb_module_get_ctx(module), tmp_ctx,
                                   basedn,
                                   scope,
-                                  expression,
+                                  tree,
                                   attrs,
                                   NULL,
                                   res,
                                   ldb_search_default_callback,
-                                  NULL);
+                                  parent);
        LDB_REQ_SET_LOCATION(req);
        if (ret != LDB_SUCCESS) {
                talloc_free(tmp_ctx);
@@ -165,6 +155,10 @@ int dsdb_module_search(struct ldb_module *module,
                return ret;
        }
 
+       if (dsdb_flags & DSDB_FLAG_TRUSTED) {
+               ldb_req_mark_trusted(req);
+       }
+
        if (dsdb_flags & DSDB_FLAG_NEXT_MODULE) {
                ret = ldb_next_request(module, req);
        } else if (dsdb_flags & DSDB_FLAG_TOP_MODULE) {
@@ -186,11 +180,67 @@ int dsdb_module_search(struct ldb_module *module,
        return ret;
 }
 
+/*
+  search for attrs in the modules below
+ */
+int dsdb_module_search(struct ldb_module *module,
+                      TALLOC_CTX *mem_ctx,
+                      struct ldb_result **_res,
+                      struct ldb_dn *basedn, enum ldb_scope scope,
+                      const char * const *attrs,
+                      int dsdb_flags,
+                      struct ldb_request *parent,
+                      const char *format, ...) _PRINTF_ATTRIBUTE(9, 10)
+{
+       int ret;
+       TALLOC_CTX *tmp_ctx;
+       va_list ap;
+       char *expression;
+       struct ldb_parse_tree *tree;
+
+       tmp_ctx = talloc_new(mem_ctx);
+
+       if (format) {
+               va_start(ap, format);
+               expression = talloc_vasprintf(tmp_ctx, format, ap);
+               va_end(ap);
+
+               if (!expression) {
+                       talloc_free(tmp_ctx);
+                       return ldb_oom(ldb_module_get_ctx(module));
+               }
+       } else {
+               expression = NULL;
+       }
+
+       tree = ldb_parse_tree(tmp_ctx, expression);
+       if (tree == NULL) {
+               talloc_free(tmp_ctx);
+               ldb_set_errstring(ldb_module_get_ctx(module),
+                               "Unable to parse search expression");
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       ret = dsdb_module_search_tree(module,
+                      mem_ctx,
+                      _res,
+                      basedn,
+                      scope,
+                      tree,
+                      attrs,
+                      dsdb_flags,
+                      parent);
+
+       talloc_free(tmp_ctx);
+       return ret;
+}
+
 /*
   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)
+                          const struct GUID *guid, struct ldb_dn **dn,
+                          struct ldb_request *parent)
 {
        struct ldb_result *res;
        const char *attrs[] = { NULL };
@@ -203,6 +253,7 @@ int dsdb_module_dn_by_guid(struct ldb_module *module, TALLOC_CTX *mem_ctx,
                                 DSDB_SEARCH_SHOW_RECYCLED |
                                 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
                                 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
+                                parent,
                                 "objectGUID=%s", GUID_string(tmp_ctx, guid));
        if (ret != LDB_SUCCESS) {
                talloc_free(tmp_ctx);
@@ -228,7 +279,8 @@ int dsdb_module_dn_by_guid(struct ldb_module *module, TALLOC_CTX *mem_ctx,
 /*
   find a GUID given a DN.
  */
-int dsdb_module_guid_by_dn(struct ldb_module *module, struct ldb_dn *dn, struct GUID *guid)
+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 };
        struct ldb_result *res;
@@ -239,7 +291,8 @@ int dsdb_module_guid_by_dn(struct ldb_module *module, struct ldb_dn *dn, struct
        ret = dsdb_module_search_dn(module, tmp_ctx, &res, dn, attrs,
                                    DSDB_FLAG_NEXT_MODULE |
                                    DSDB_SEARCH_SHOW_RECYCLED |
-                                   DSDB_SEARCH_SHOW_EXTENDED_DN);
+                                   DSDB_SEARCH_SHOW_EXTENDED_DN,
+                                   parent);
        if (ret != LDB_SUCCESS) {
                ldb_asprintf_errstring(ldb_module_get_ctx(module), "Failed to find GUID for %s",
                                       ldb_dn_get_linearized(dn));
@@ -256,14 +309,76 @@ 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
+ */
+int dsdb_module_extended(struct ldb_module *module,
+                      const char* oid, void* data,
+                      uint32_t dsdb_flags,
+                      struct ldb_request *parent)
+{
+       struct ldb_request *req;
+       int ret;
+       struct ldb_context *ldb = ldb_module_get_ctx(module);
+       TALLOC_CTX *tmp_ctx = talloc_new(module);
+       struct ldb_result *res;
+
+       res = talloc_zero(tmp_ctx, struct ldb_result);
+       if (!res) {
+               talloc_free(tmp_ctx);
+               return ldb_oom(ldb_module_get_ctx(module));
+       }
+
+       ret = ldb_build_extended_req(&req, ldb,
+                       tmp_ctx,
+                       oid,
+                       data,
+                       NULL,
+                       res, ldb_extended_default_callback,
+                       parent);
+
+       LDB_REQ_SET_LOCATION(req);
+       if (ret != LDB_SUCCESS) {
+               talloc_free(tmp_ctx);
+               return ret;
+       }
+
+       ret = dsdb_request_add_controls(req, dsdb_flags);
+       if (ret != LDB_SUCCESS) {
+               talloc_free(tmp_ctx);
+               return ret;
+       }
+
+       if (dsdb_flags & DSDB_FLAG_TRUSTED) {
+               ldb_req_mark_trusted(req);
+       }
+
+       /* Run the new request */
+       if (dsdb_flags & DSDB_FLAG_NEXT_MODULE) {
+               ret = ldb_next_request(module, req);
+       } else if (dsdb_flags & DSDB_FLAG_TOP_MODULE) {
+               ret = ldb_request(ldb_module_get_ctx(module), req);
+       } else {
+               const struct ldb_module_ops *ops = ldb_module_get_ops(module);
+               SMB_ASSERT(dsdb_flags & DSDB_FLAG_OWN_MODULE);
+               ret = ops->extended(module, req);
+       }
+       if (ret == LDB_SUCCESS) {
+               ret = ldb_wait(req->handle, LDB_WAIT_ALL);
+       }
 
+       talloc_free(tmp_ctx);
+       return ret;
+}
 /*
   a ldb_modify request operating on modules below the
   current module
  */
 int dsdb_module_modify(struct ldb_module *module,
                       const struct ldb_message *message,
-                      uint32_t dsdb_flags)
+                      uint32_t dsdb_flags,
+                      struct ldb_request *parent)
 {
        struct ldb_request *mod_req;
        int ret;
@@ -282,7 +397,7 @@ int dsdb_module_modify(struct ldb_module *module,
                                NULL,
                                res,
                                ldb_modify_default_callback,
-                               NULL);
+                               parent);
        LDB_REQ_SET_LOCATION(mod_req);
        if (ret != LDB_SUCCESS) {
                talloc_free(tmp_ctx);
@@ -295,6 +410,10 @@ int dsdb_module_modify(struct ldb_module *module,
                return ret;
        }
 
+       if (dsdb_flags & DSDB_FLAG_TRUSTED) {
+               ldb_req_mark_trusted(mod_req);
+       }
+
        /* Run the new request */
        if (dsdb_flags & DSDB_FLAG_NEXT_MODULE) {
                ret = ldb_next_request(module, mod_req);
@@ -320,8 +439,9 @@ int dsdb_module_modify(struct ldb_module *module,
   current module
  */
 int dsdb_module_rename(struct ldb_module *module,
-                      struct ldb_dn *olddn, struct ldb_dn *newdn,
-                      uint32_t dsdb_flags)
+                      struct ldb_dn *olddn, struct ldb_dn *newdn,
+                      uint32_t dsdb_flags,
+                      struct ldb_request *parent)
 {
        struct ldb_request *req;
        int ret;
@@ -341,7 +461,7 @@ int dsdb_module_rename(struct ldb_module *module,
                                   NULL,
                                   res,
                                   ldb_modify_default_callback,
-                                  NULL);
+                                  parent);
        LDB_REQ_SET_LOCATION(req);
        if (ret != LDB_SUCCESS) {
                talloc_free(tmp_ctx);
@@ -354,6 +474,10 @@ int dsdb_module_rename(struct ldb_module *module,
                return ret;
        }
 
+       if (dsdb_flags & DSDB_FLAG_TRUSTED) {
+               ldb_req_mark_trusted(req);
+       }
+
        /* Run the new request */
        if (dsdb_flags & DSDB_FLAG_NEXT_MODULE) {
                ret = ldb_next_request(module, req);
@@ -378,7 +502,8 @@ int dsdb_module_rename(struct ldb_module *module,
  */
 int dsdb_module_add(struct ldb_module *module,
                    const struct ldb_message *message,
-                   uint32_t dsdb_flags)
+                   uint32_t dsdb_flags,
+                   struct ldb_request *parent)
 {
        struct ldb_request *req;
        int ret;
@@ -397,7 +522,7 @@ int dsdb_module_add(struct ldb_module *module,
                                NULL,
                                res,
                                ldb_modify_default_callback,
-                               NULL);
+                               parent);
        LDB_REQ_SET_LOCATION(req);
        if (ret != LDB_SUCCESS) {
                talloc_free(tmp_ctx);
@@ -410,6 +535,10 @@ int dsdb_module_add(struct ldb_module *module,
                return ret;
        }
 
+       if (dsdb_flags & DSDB_FLAG_TRUSTED) {
+               ldb_req_mark_trusted(req);
+       }
+
        /* Run the new request */
        if (dsdb_flags & DSDB_FLAG_NEXT_MODULE) {
                ret = ldb_next_request(module, req);
@@ -434,7 +563,8 @@ int dsdb_module_add(struct ldb_module *module,
  */
 int dsdb_module_del(struct ldb_module *module,
                    struct ldb_dn *dn,
-                   uint32_t dsdb_flags)
+                   uint32_t dsdb_flags,
+                   struct ldb_request *parent)
 {
        struct ldb_request *req;
        int ret;
@@ -453,7 +583,7 @@ int dsdb_module_del(struct ldb_module *module,
                                NULL,
                                res,
                                ldb_modify_default_callback,
-                               NULL);
+                               parent);
        LDB_REQ_SET_LOCATION(req);
        if (ret != LDB_SUCCESS) {
                talloc_free(tmp_ctx);
@@ -466,6 +596,10 @@ int dsdb_module_del(struct ldb_module *module,
                return ret;
        }
 
+       if (dsdb_flags & DSDB_FLAG_TRUSTED) {
+               ldb_req_mark_trusted(req);
+       }
+
        /* Run the new request */
        if (dsdb_flags & DSDB_FLAG_NEXT_MODULE) {
                ret = ldb_next_request(module, req);
@@ -572,12 +706,42 @@ int dsdb_check_optional_feature(struct ldb_module *module, struct ldb_dn *scope,
        return LDB_SUCCESS;
 }
 
+/*
+  find the NTDS GUID from a computers DN record
+ */
+int dsdb_module_find_ntdsguid_for_computer(struct ldb_module *module,
+                                          TALLOC_CTX *mem_ctx,
+                                          struct ldb_dn *computer_dn,
+                                          struct GUID *ntds_guid,
+                                          struct ldb_request *parent)
+{
+       int ret;
+       struct ldb_dn *dn;
+
+       *ntds_guid = GUID_zero();
+
+       ret = dsdb_module_reference_dn(module, mem_ctx, computer_dn,
+                                      "serverReferenceBL", &dn, parent);
+       if (ret != LDB_SUCCESS) {
+               return ret;
+       }
+
+       if (!ldb_dn_add_child_fmt(dn, "CN=NTDS Settings")) {
+               talloc_free(dn);
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       ret = dsdb_module_guid_by_dn(module, dn, ntds_guid, parent);
+       talloc_free(dn);
+       return ret;
+}
+
 /*
   find a 'reference' DN that points at another object
   (eg. serverReference, rIDManagerReference etc)
  */
 int dsdb_module_reference_dn(struct ldb_module *module, TALLOC_CTX *mem_ctx, struct ldb_dn *base,
-                            const char *attribute, struct ldb_dn **dn)
+                            const char *attribute, struct ldb_dn **dn, struct ldb_request *parent)
 {
        const char *attrs[2];
        struct ldb_result *res;
@@ -587,7 +751,7 @@ int dsdb_module_reference_dn(struct ldb_module *module, TALLOC_CTX *mem_ctx, str
        attrs[1] = NULL;
 
        ret = dsdb_module_search_dn(module, mem_ctx, &res, base, attrs,
-                                   DSDB_FLAG_NEXT_MODULE);
+                                   DSDB_FLAG_NEXT_MODULE | DSDB_SEARCH_SHOW_EXTENDED_DN, parent);
        if (ret != LDB_SUCCESS) {
                return ret;
        }
@@ -608,60 +772,12 @@ int dsdb_module_reference_dn(struct ldb_module *module, TALLOC_CTX *mem_ctx, str
   find the RID Manager$ DN via the rIDManagerReference attribute in the
   base DN
  */
-int dsdb_module_rid_manager_dn(struct ldb_module *module, TALLOC_CTX *mem_ctx, struct ldb_dn **dn)
+int dsdb_module_rid_manager_dn(struct ldb_module *module, TALLOC_CTX *mem_ctx, struct ldb_dn **dn,
+                              struct ldb_request *parent)
 {
        return dsdb_module_reference_dn(module, mem_ctx,
                                        ldb_get_default_basedn(ldb_module_get_ctx(module)),
-                                       "rIDManagerReference", dn);
-}
-
-
-/*
-  update an integer attribute safely via a constrained delete/add
- */
-int dsdb_module_constrainted_update_integer(struct ldb_module *module, struct ldb_dn *dn,
-                                           const char *attr, uint64_t old_val, uint64_t new_val)
-{
-       struct ldb_message *msg;
-       struct ldb_message_element *el;
-       struct ldb_val v1, v2;
-       int ret;
-       char *vstring;
-
-       msg = ldb_msg_new(module);
-       msg->dn = dn;
-
-       ret = ldb_msg_add_empty(msg, attr, LDB_FLAG_MOD_DELETE, &el);
-       if (ret != LDB_SUCCESS) {
-               talloc_free(msg);
-               return ret;
-       }
-       el->num_values = 1;
-       el->values = &v1;
-       vstring = talloc_asprintf(msg, "%llu", (unsigned long long)old_val);
-       if (!vstring) {
-               talloc_free(msg);
-               return ldb_module_oom(module);
-       }
-       v1 = data_blob_string_const(vstring);
-
-       ret = ldb_msg_add_empty(msg, attr, LDB_FLAG_MOD_ADD, &el);
-       if (ret != LDB_SUCCESS) {
-               talloc_free(msg);
-               return ret;
-       }
-       el->num_values = 1;
-       el->values = &v2;
-       vstring = talloc_asprintf(msg, "%llu", (unsigned long long)new_val);
-       if (!vstring) {
-               talloc_free(msg);
-               return ldb_module_oom(module);
-       }
-       v2 = data_blob_string_const(vstring);
-
-       ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE);
-       talloc_free(msg);
-       return ret;
+                                       "rIDManagerReference", dn, parent);
 }
 
 /*
@@ -675,37 +791,12 @@ int dsdb_next_callback(struct ldb_request *req, struct ldb_reply *ares)
        return up_req->callback(up_req, ares);
 }
 
-
-/*
-  set an integer attribute
- */
-int dsdb_module_set_integer(struct ldb_module *module, struct ldb_dn *dn,
-                           const char *attr, uint64_t new_val)
-{
-       struct ldb_message *msg;
-       int ret;
-
-       msg = ldb_msg_new(module);
-       msg->dn = dn;
-
-       ret = ldb_msg_add_fmt(msg, attr, "%llu", (unsigned long long)new_val);
-       if (ret != LDB_SUCCESS) {
-               talloc_free(msg);
-               return ret;
-       }
-       msg->elements[0].flags = LDB_FLAG_MOD_REPLACE;
-
-       ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE);
-       talloc_free(msg);
-       return ret;
-}
-
 /*
   load the uSNHighest and the uSNUrgent attributes from the @REPLCHANGED
   object for a partition
  */
 int dsdb_module_load_partition_usn(struct ldb_module *module, struct ldb_dn *dn,
-                                 uint64_t *uSN, uint64_t *urgent_uSN)
+                                  uint64_t *uSN, uint64_t *urgent_uSN, struct ldb_request *parent)
 {
        struct ldb_context *ldb = ldb_module_get_ctx(module);
        struct ldb_request *req;
@@ -726,7 +817,7 @@ int dsdb_module_load_partition_usn(struct ldb_module *module, struct ldb_dn *dn,
                                   NULL, NULL,
                                   NULL,
                                   res, ldb_search_default_callback,
-                                  NULL);
+                                  parent);
        LDB_REQ_SET_LOCATION(req);
        if (ret != LDB_SUCCESS) {
                talloc_free(tmp_ctx);
@@ -762,6 +853,7 @@ int dsdb_module_load_partition_usn(struct ldb_module *module, struct ldb_dn *dn,
                   an implicit value of zero */
                *uSN = 0;
                talloc_free(tmp_ctx);
+               ldb_reset_err_string(ldb);
                return LDB_SUCCESS;
        }
 
@@ -792,7 +884,8 @@ int dsdb_module_load_partition_usn(struct ldb_module *module, struct ldb_dn *dn,
   partition
  */
 int dsdb_module_save_partition_usn(struct ldb_module *module, struct ldb_dn *dn,
-                                  uint64_t uSN, uint64_t urgent_uSN)
+                                  uint64_t uSN, uint64_t urgent_uSN,
+                                  struct ldb_request *parent)
 {
        struct ldb_context *ldb = ldb_module_get_ctx(module);
        struct ldb_request *req;
@@ -818,7 +911,7 @@ int dsdb_module_save_partition_usn(struct ldb_module *module, struct ldb_dn *dn,
                return ldb_module_oom(module);
        }
 
-       ret = ldb_msg_add_fmt(msg, "uSNHighest", "%llu", (unsigned long long)uSN);
+       ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNHighest", uSN);
        if (ret != LDB_SUCCESS) {
                talloc_free(msg);
                return ret;
@@ -827,7 +920,8 @@ int dsdb_module_save_partition_usn(struct ldb_module *module, struct ldb_dn *dn,
 
        /* urgent_uSN is optional so may not be stored */
        if (urgent_uSN) {
-               ret = ldb_msg_add_fmt(msg, "uSNUrgent", "%llu", (unsigned long long)urgent_uSN);
+               ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNUrgent",
+                                          urgent_uSN);
                if (ret != LDB_SUCCESS) {
                        talloc_free(msg);
                        return ret;
@@ -848,7 +942,7 @@ int dsdb_module_save_partition_usn(struct ldb_module *module, struct ldb_dn *dn,
                                NULL,
                                res,
                                ldb_modify_default_callback,
-                               NULL);
+                               parent);
        LDB_REQ_SET_LOCATION(req);
 again:
        if (ret != LDB_SUCCESS) {
@@ -876,7 +970,7 @@ again:
                                        NULL,
                                        res,
                                        ldb_modify_default_callback,
-                                       NULL);
+                                       parent);
                LDB_REQ_SET_LOCATION(req);
                goto again;
        }
@@ -1050,7 +1144,8 @@ int dsdb_module_constrainted_update_int32(struct ldb_module *module,
                                          struct ldb_dn *dn,
                                          const char *attr,
                                          const int32_t *old_val,
-                                         const int32_t *new_val)
+                                         const int32_t *new_val,
+                                         struct ldb_request *parent)
 {
        struct ldb_message *msg;
        int ret;
@@ -1067,7 +1162,7 @@ int dsdb_module_constrainted_update_int32(struct ldb_module *module,
                return ret;
        }
 
-       ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE);
+       ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE, parent);
        talloc_free(msg);
        return ret;
 }
@@ -1076,11 +1171,12 @@ int dsdb_module_constrainted_update_uint32(struct ldb_module *module,
                                           struct ldb_dn *dn,
                                           const char *attr,
                                           const uint32_t *old_val,
-                                          const uint32_t *new_val)
+                                          const uint32_t *new_val,
+                                          struct ldb_request *parent)
 {
        return dsdb_module_constrainted_update_int32(module, dn, attr,
                                                     (const int32_t *)old_val,
-                                                    (const int32_t *)new_val);
+                                                    (const int32_t *)new_val, parent);
 }
 
 /*
@@ -1090,7 +1186,8 @@ int dsdb_module_constrainted_update_int64(struct ldb_module *module,
                                          struct ldb_dn *dn,
                                          const char *attr,
                                          const int64_t *old_val,
-                                         const int64_t *new_val)
+                                         const int64_t *new_val,
+                                         struct ldb_request *parent)
 {
        struct ldb_message *msg;
        int ret;
@@ -1107,7 +1204,7 @@ int dsdb_module_constrainted_update_int64(struct ldb_module *module,
                return ret;
        }
 
-       ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE);
+       ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE, parent);
        talloc_free(msg);
        return ret;
 }
@@ -1116,21 +1213,23 @@ int dsdb_module_constrainted_update_uint64(struct ldb_module *module,
                                           struct ldb_dn *dn,
                                           const char *attr,
                                           const uint64_t *old_val,
-                                          const uint64_t *new_val)
+                                          const uint64_t *new_val,
+                                          struct ldb_request *parent)
 {
        return dsdb_module_constrainted_update_int64(module, dn, attr,
                                                     (const int64_t *)old_val,
-                                                    (const int64_t *)new_val);
+                                                    (const int64_t *)new_val,
+                                                    parent);
 }
 
 
 const struct ldb_val *dsdb_module_find_dsheuristics(struct ldb_module *module,
-                                                   TALLOC_CTX *mem_ctx)
+                                                   TALLOC_CTX *mem_ctx, struct ldb_request *parent)
 {
        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 *attrs[] = { "dSHeuristics", NULL };
        struct ldb_result *res;
 
        new_dn = ldb_dn_copy(mem_ctx, ldb_get_config_basedn(ldb));
@@ -1142,23 +1241,23 @@ const struct ldb_val *dsdb_module_find_dsheuristics(struct ldb_module *module,
        ret = dsdb_module_search_dn(module, mem_ctx, &res,
                                    new_dn,
                                    attrs,
-                                   DSDB_FLAG_NEXT_MODULE);
+                                   DSDB_FLAG_NEXT_MODULE,
+                                   parent);
        if (ret == LDB_SUCCESS && res->count == 1) {
                talloc_free(new_dn);
                return ldb_msg_find_ldb_val(res->msgs[0],
-                                           "dsHeuristics");
+                                           "dSHeuristics");
        }
        talloc_free(new_dn);
        return NULL;
 }
 
-bool dsdb_block_anonymous_ops(struct ldb_module *module,
-                             TALLOC_CTX *mem_ctx)
+bool dsdb_block_anonymous_ops(struct ldb_module *module, struct ldb_request *parent)
 {
-       TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
+       TALLOC_CTX *tmp_ctx = talloc_new(module);
        bool result;
        const struct ldb_val *hr_val = dsdb_module_find_dsheuristics(module,
-                                                                    tmp_ctx);
+                                                                    tmp_ctx, parent);
        if (hr_val == NULL || hr_val->length < DS_HR_BLOCK_ANONYMOUS_OPS) {
                result = true;
        } else if (hr_val->data[DS_HR_BLOCK_ANONYMOUS_OPS -1] == '2') {
@@ -1171,15 +1270,68 @@ bool dsdb_block_anonymous_ops(struct ldb_module *module,
        return result;
 }
 
+bool dsdb_user_password_support(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_USER_PASSWORD_SUPPORT) {
+               result = false;
+       } else if ((hr_val->data[DS_HR_USER_PASSWORD_SUPPORT -1] == '2') ||
+                  (hr_val->data[DS_HR_USER_PASSWORD_SUPPORT -1] == '0')) {
+               result = false;
+       } else {
+               result = true;
+       }
+
+       talloc_free(tmp_ctx);
+       return result;
+}
+
 /*
   show the chain of requests, useful for debugging async requests
  */
 void dsdb_req_chain_debug(struct ldb_request *req, int level)
 {
-       int i=0;
+       char *s = ldb_module_call_chain(req, req);
+       DEBUG(level, ("%s\n", s));
+       talloc_free(s);
+}
 
-       while (req && req->handle) {
-               DEBUG(level,("req[%u] %p  : %s\n", i++, req, ldb_req_location(req)));
-               req = req->handle->parent;
+/*
+ * Gets back a single-valued attribute by the rules of the DSDB triggers when
+ * performing a modify operation.
+ *
+ * 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).
+ */
+struct ldb_message_element *dsdb_get_single_valued_attr(const struct ldb_message *msg,
+                                                       const char *attr_name,
+                                                       enum ldb_request_type operation)
+{
+       struct ldb_message_element *el = NULL;
+       unsigned int i;
+
+       /* 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. */
+       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 el;
 }