s4-dsdb: ensure rIDSetReferences is stored as an extended DN
[amitay/samba.git] / source4 / dsdb / samdb / ldb_modules / util.c
index ba0c2d9166adf9132010fb2a427f686a306c382a..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
@@ -36,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;
@@ -48,7 +50,7 @@ int dsdb_module_search_dn(struct ldb_module *module,
        res = talloc_zero(tmp_ctx, struct ldb_result);
        if (!res) {
                talloc_free(tmp_ctx);
-               return LDB_ERR_OPERATIONS_ERROR;
+               return ldb_oom(ldb_module_get_ctx(module));
        }
 
        ret = ldb_build_search_req(&req, ldb_module_get_ctx(module), tmp_ctx,
@@ -59,7 +61,8 @@ 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);
                return ret;
@@ -71,7 +74,20 @@ int dsdb_module_search_dn(struct ldb_module *module,
                return ret;
        }
 
-       ret = ldb_next_request(module, req);
+       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->search(module, req);
+       }
        if (ret == LDB_SUCCESS) {
                ret = ldb_wait(req->handle, LDB_WAIT_ALL);
        }
@@ -94,54 +110,40 @@ 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_ERR_OPERATIONS_ERROR;
-               }
-       } else {
-               expression = NULL;
-       }
 
        res = talloc_zero(tmp_ctx, struct ldb_result);
        if (!res) {
                talloc_free(tmp_ctx);
-               return LDB_ERR_OPERATIONS_ERROR;
+               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);
                return ret;
@@ -153,13 +155,18 @@ int dsdb_module_search(struct ldb_module *module,
                return ret;
        }
 
-       if (dsdb_flags & DSDB_FLAG_OWN_MODULE) {
-               const struct ldb_module_ops *ops = ldb_module_get_ops(module);
-               ret = ops->search(module, req);
+       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) {
                ret = ldb_request(ldb_module_get_ctx(module), req);
        } else {
-               ret = ldb_next_request(module, req);
+               const struct ldb_module_ops *ops = ldb_module_get_ops(module);
+               SMB_ASSERT(dsdb_flags & DSDB_FLAG_OWN_MODULE);
+               ret = ops->search(module, req);
        }
        if (ret == LDB_SUCCESS) {
                ret = ldb_wait(req->handle, LDB_WAIT_ALL);
@@ -173,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 };
@@ -187,9 +250,10 @@ int dsdb_module_dn_by_guid(struct ldb_module *module, TALLOC_CTX *mem_ctx,
        ret = dsdb_module_search(module, tmp_ctx, &res, NULL, LDB_SCOPE_SUBTREE,
                                 attrs,
                                 DSDB_FLAG_NEXT_MODULE |
-                                DSDB_SEARCH_SHOW_DELETED |
+                                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);
@@ -215,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;
@@ -224,8 +289,10 @@ int dsdb_module_guid_by_dn(struct ldb_module *module, struct ldb_dn *dn, struct
        NTSTATUS status;
 
        ret = dsdb_module_search_dn(module, tmp_ctx, &res, dn, attrs,
-                                   DSDB_SEARCH_SHOW_DELETED|
-                                   DSDB_SEARCH_SHOW_EXTENDED_DN);
+                                   DSDB_FLAG_NEXT_MODULE |
+                                   DSDB_SEARCH_SHOW_RECYCLED |
+                                   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));
@@ -236,20 +303,82 @@ int dsdb_module_guid_by_dn(struct ldb_module *module, struct ldb_dn *dn, struct
        status = dsdb_get_extended_dn_guid(res->msgs[0]->dn, guid, "GUID");
        if (!NT_STATUS_IS_OK(status)) {
                talloc_free(tmp_ctx);
-               return LDB_ERR_OPERATIONS_ERROR;
+               return ldb_operr(ldb_module_get_ctx(module));
        }
 
        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;
@@ -260,7 +389,7 @@ int dsdb_module_modify(struct ldb_module *module,
        res = talloc_zero(tmp_ctx, struct ldb_result);
        if (!res) {
                talloc_free(tmp_ctx);
-               return LDB_ERR_OPERATIONS_ERROR;
+               return ldb_oom(ldb_module_get_ctx(module));
        }
 
        ret = ldb_build_mod_req(&mod_req, ldb, tmp_ctx,
@@ -268,7 +397,8 @@ 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);
                return ret;
@@ -280,14 +410,19 @@ 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_OWN_MODULE) {
-               const struct ldb_module_ops *ops = ldb_module_get_ops(module);
-               ret = ops->modify(module, mod_req);
+       if (dsdb_flags & DSDB_FLAG_NEXT_MODULE) {
+               ret = ldb_next_request(module, mod_req);
        } else if (dsdb_flags & DSDB_FLAG_TOP_MODULE) {
                ret = ldb_request(ldb_module_get_ctx(module), mod_req);
        } else {
-               ret = ldb_next_request(module, mod_req);
+               const struct ldb_module_ops *ops = ldb_module_get_ops(module);
+               SMB_ASSERT(dsdb_flags & DSDB_FLAG_OWN_MODULE);
+               ret = ops->modify(module, mod_req);
        }
        if (ret == LDB_SUCCESS) {
                ret = ldb_wait(mod_req->handle, LDB_WAIT_ALL);
@@ -304,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;
@@ -316,7 +452,7 @@ int dsdb_module_rename(struct ldb_module *module,
        res = talloc_zero(tmp_ctx, struct ldb_result);
        if (!res) {
                talloc_free(tmp_ctx);
-               return LDB_ERR_OPERATIONS_ERROR;
+               return ldb_oom(ldb_module_get_ctx(module));
        }
 
        ret = ldb_build_rename_req(&req, ldb, tmp_ctx,
@@ -325,7 +461,8 @@ 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);
                return ret;
@@ -337,14 +474,19 @@ 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_OWN_MODULE) {
-               const struct ldb_module_ops *ops = ldb_module_get_ops(module);
-               ret = ops->rename(module, req);
+       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 {
-               ret = ldb_next_request(module, req);
+               const struct ldb_module_ops *ops = ldb_module_get_ops(module);
+               SMB_ASSERT(dsdb_flags & DSDB_FLAG_OWN_MODULE);
+               ret = ops->rename(module, req);
        }
        if (ret == LDB_SUCCESS) {
                ret = ldb_wait(req->handle, LDB_WAIT_ALL);
@@ -360,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;
@@ -371,7 +514,7 @@ int dsdb_module_add(struct ldb_module *module,
        res = talloc_zero(tmp_ctx, struct ldb_result);
        if (!res) {
                talloc_free(tmp_ctx);
-               return LDB_ERR_OPERATIONS_ERROR;
+               return ldb_oom(ldb_module_get_ctx(module));
        }
 
        ret = ldb_build_add_req(&req, ldb, tmp_ctx,
@@ -379,7 +522,8 @@ 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);
                return ret;
@@ -391,14 +535,19 @@ 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_OWN_MODULE) {
-               const struct ldb_module_ops *ops = ldb_module_get_ops(module);
-               ret = ops->add(module, req);
+       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 {
-               ret = ldb_next_request(module, req);
+               const struct ldb_module_ops *ops = ldb_module_get_ops(module);
+               SMB_ASSERT(dsdb_flags & DSDB_FLAG_OWN_MODULE);
+               ret = ops->add(module, req);
        }
        if (ret == LDB_SUCCESS) {
                ret = ldb_wait(req->handle, LDB_WAIT_ALL);
@@ -414,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;
@@ -425,7 +575,7 @@ int dsdb_module_del(struct ldb_module *module,
        res = talloc_zero(tmp_ctx, struct ldb_result);
        if (!res) {
                talloc_free(tmp_ctx);
-               return LDB_ERR_OPERATIONS_ERROR;
+               return ldb_oom(ldb);
        }
 
        ret = ldb_build_del_req(&req, ldb, tmp_ctx,
@@ -433,7 +583,8 @@ 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);
                return ret;
@@ -445,14 +596,19 @@ 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_OWN_MODULE) {
-               const struct ldb_module_ops *ops = ldb_module_get_ops(module);
-               ret = ops->del(module, req);
+       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 {
-               ret = ldb_next_request(module, req);
+               const struct ldb_module_ops *ops = ldb_module_get_ops(module);
+               SMB_ASSERT(dsdb_flags & DSDB_FLAG_OWN_MODULE);
+               ret = ops->del(module, req);
        }
        if (ret == LDB_SUCCESS) {
                ret = ldb_wait(req->handle, LDB_WAIT_ALL);
@@ -462,33 +618,6 @@ int dsdb_module_del(struct ldb_module *module,
        return ret;
 }
 
-const struct dsdb_class * get_last_structural_class(const struct dsdb_schema *schema,const struct ldb_message_element *element)
-{
-       const struct dsdb_class *last_class = NULL;
-       unsigned int i;
-
-       for (i = 0; i < element->num_values; i++){
-               const struct dsdb_class *tmp_class = dsdb_class_by_lDAPDisplayName_ldb_val(schema, &element->values[i]);
-
-               if(tmp_class == NULL) {
-                       continue;
-               }
-
-               if(tmp_class->objectClassCategory > 1) {
-                       continue;
-               }
-
-               if (!last_class) {
-                       last_class = tmp_class;
-               } else {
-                       if (tmp_class->subClass_order > last_class->subClass_order)
-                               last_class = tmp_class;
-               }
-       }
-
-       return last_class;
-}
-
 /*
   check if a single valued link has multiple non-deleted values
 
@@ -577,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;
@@ -591,7 +750,8 @@ int dsdb_module_reference_dn(struct ldb_module *module, TALLOC_CTX *mem_ctx, str
        attrs[0] = attribute;
        attrs[1] = NULL;
 
-       ret = dsdb_module_search_dn(module, mem_ctx, &res, base, attrs, 0);
+       ret = dsdb_module_search_dn(module, mem_ctx, &res, base, attrs,
+                                   DSDB_FLAG_NEXT_MODULE | DSDB_SEARCH_SHOW_EXTENDED_DN, parent);
        if (ret != LDB_SUCCESS) {
                return ret;
        }
@@ -599,6 +759,7 @@ int dsdb_module_reference_dn(struct ldb_module *module, TALLOC_CTX *mem_ctx, str
        *dn = ldb_msg_find_attr_as_dn(ldb_module_get_ctx(module),
                                      mem_ctx, res->msgs[0], attribute);
        if (!*dn) {
+               ldb_reset_err_string(ldb_module_get_ctx(module));
                talloc_free(res);
                return LDB_ERR_NO_SUCH_ATTRIBUTE;
        }
@@ -611,62 +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) {
-               ldb_module_oom(module);
-               talloc_free(msg);
-               return LDB_ERR_OPERATIONS_ERROR;
-       }
-       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) {
-               ldb_module_oom(module);
-               talloc_free(msg);
-               return LDB_ERR_OPERATIONS_ERROR;
-       }
-       v2 = data_blob_string_const(vstring);
-
-       ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE);
-       talloc_free(msg);
-       return ret;
+                                       "rIDManagerReference", dn, parent);
 }
 
 /*
@@ -680,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;
@@ -722,7 +808,7 @@ int dsdb_module_load_partition_usn(struct ldb_module *module, struct ldb_dn *dn,
        res = talloc_zero(tmp_ctx, struct ldb_result);
        if (!res) {
                talloc_free(tmp_ctx);
-               return LDB_ERR_OPERATIONS_ERROR;
+               return ldb_module_oom(module);
        }
 
        ret = ldb_build_search_req(&req, ldb, tmp_ctx,
@@ -731,7 +817,8 @@ 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);
                return ret;
@@ -740,7 +827,7 @@ int dsdb_module_load_partition_usn(struct ldb_module *module, struct ldb_dn *dn,
        p_ctrl = talloc(req, struct dsdb_control_current_partition);
        if (p_ctrl == NULL) {
                talloc_free(tmp_ctx);
-               return LDB_ERR_OPERATIONS_ERROR;
+               return ldb_module_oom(module);
        }
        p_ctrl->version = DSDB_CONTROL_CURRENT_PARTITION_VERSION;
        p_ctrl->dn = dn;
@@ -766,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;
        }
 
@@ -774,7 +862,7 @@ int dsdb_module_load_partition_usn(struct ldb_module *module, struct ldb_dn *dn,
                return ret;
        }
 
-       if (res->count < 1) {
+       if (res->count != 1) {
                *uSN = 0;
                if (urgent_uSN) {
                        *urgent_uSN = 0;
@@ -796,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;
@@ -807,22 +896,22 @@ int dsdb_module_save_partition_usn(struct ldb_module *module, struct ldb_dn *dn,
 
        msg = ldb_msg_new(module);
        if (msg == NULL) {
-               return LDB_ERR_OPERATIONS_ERROR;
+               return ldb_module_oom(module);
        }
 
        msg->dn = ldb_dn_new(msg, ldb, "@REPLCHANGED");
        if (msg->dn == NULL) {
                talloc_free(msg);
-               return LDB_ERR_OPERATIONS_ERROR;
+               return ldb_operr(ldb_module_get_ctx(module));
        }
 
        res = talloc_zero(msg, struct ldb_result);
        if (!res) {
                talloc_free(msg);
-               return LDB_ERR_OPERATIONS_ERROR;
+               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;
@@ -831,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;
@@ -843,7 +933,7 @@ int dsdb_module_save_partition_usn(struct ldb_module *module, struct ldb_dn *dn,
        p_ctrl = talloc(msg, struct dsdb_control_current_partition);
        if (p_ctrl == NULL) {
                talloc_free(msg);
-               return LDB_ERR_OPERATIONS_ERROR;
+               return ldb_oom(ldb);
        }
        p_ctrl->version = DSDB_CONTROL_CURRENT_PARTITION_VERSION;
        p_ctrl->dn = dn;
@@ -852,7 +942,8 @@ 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) {
                talloc_free(msg);
@@ -879,7 +970,8 @@ again:
                                        NULL,
                                        res,
                                        ldb_modify_default_callback,
-                                       NULL);
+                                       parent);
+               LDB_REQ_SET_LOCATION(req);
                goto again;
        }
 
@@ -892,10 +984,18 @@ bool dsdb_module_am_system(struct ldb_module *module)
 {
        struct ldb_context *ldb = ldb_module_get_ctx(module);
        struct auth_session_info *session_info
-               = (struct auth_session_info *)ldb_get_opaque(ldb, "sessionInfo");
+               = talloc_get_type(ldb_get_opaque(ldb, "sessionInfo"), struct auth_session_info);
        return security_session_user_level(session_info, NULL) == SECURITY_SYSTEM;
 }
 
+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);
+       return security_session_user_level(session_info, NULL) == SECURITY_ADMINISTRATOR;
+}
+
 /*
   check if the recyclebin is enabled
  */
@@ -921,14 +1021,317 @@ int dsdb_recyclebin_enabled(struct ldb_module *module, bool *enabled)
        return LDB_SUCCESS;
 }
 
-bool is_attr_in_list(const char * const * attrs, const char *attr)
+int dsdb_msg_constrainted_update_int32(struct ldb_module *module,
+                                      struct ldb_message *msg,
+                                      const char *attr,
+                                      const int32_t *old_val,
+                                      const int32_t *new_val)
+{
+       struct ldb_message_element *el;
+       int ret;
+       char *vstring;
+
+       if (old_val) {
+               ret = ldb_msg_add_empty(msg, attr, LDB_FLAG_MOD_DELETE, &el);
+               if (ret != LDB_SUCCESS) {
+                       return ret;
+               }
+               el->num_values = 1;
+               el->values = talloc_array(msg, struct ldb_val, el->num_values);
+               if (!el->values) {
+                       return ldb_module_oom(module);
+               }
+               vstring = talloc_asprintf(el->values, "%ld", (long)*old_val);
+               if (!vstring) {
+                       return ldb_module_oom(module);
+               }
+               *el->values = data_blob_string_const(vstring);
+       }
+
+       if (new_val) {
+               ret = ldb_msg_add_empty(msg, attr, LDB_FLAG_MOD_ADD, &el);
+               if (ret != LDB_SUCCESS) {
+                       return ret;
+               }
+               el->num_values = 1;
+               el->values = talloc_array(msg, struct ldb_val, el->num_values);
+               if (!el->values) {
+                       return ldb_module_oom(module);
+               }
+               vstring = talloc_asprintf(el->values, "%ld", (long)*new_val);
+               if (!vstring) {
+                       return ldb_module_oom(module);
+               }
+               *el->values = data_blob_string_const(vstring);
+       }
+
+       return LDB_SUCCESS;
+}
+
+int dsdb_msg_constrainted_update_uint32(struct ldb_module *module,
+                                       struct ldb_message *msg,
+                                       const char *attr,
+                                       const uint32_t *old_val,
+                                       const uint32_t *new_val)
+{
+       return dsdb_msg_constrainted_update_int32(module, msg, attr,
+                                                 (const int32_t *)old_val,
+                                                 (const int32_t *)new_val);
+}
+
+int dsdb_msg_constrainted_update_int64(struct ldb_module *module,
+                                      struct ldb_message *msg,
+                                      const char *attr,
+                                      const int64_t *old_val,
+                                      const int64_t *new_val)
+{
+       struct ldb_message_element *el;
+       int ret;
+       char *vstring;
+
+       if (old_val) {
+               ret = ldb_msg_add_empty(msg, attr, LDB_FLAG_MOD_DELETE, &el);
+               if (ret != LDB_SUCCESS) {
+                       return ret;
+               }
+               el->num_values = 1;
+               el->values = talloc_array(msg, struct ldb_val, el->num_values);
+               if (!el->values) {
+                       return ldb_module_oom(module);
+               }
+               vstring = talloc_asprintf(el->values, "%lld", (long long)*old_val);
+               if (!vstring) {
+                       return ldb_module_oom(module);
+               }
+               *el->values = data_blob_string_const(vstring);
+       }
+
+       if (new_val) {
+               ret = ldb_msg_add_empty(msg, attr, LDB_FLAG_MOD_ADD, &el);
+               if (ret != LDB_SUCCESS) {
+                       return ret;
+               }
+               el->num_values = 1;
+               el->values = talloc_array(msg, struct ldb_val, el->num_values);
+               if (!el->values) {
+                       return ldb_module_oom(module);
+               }
+               vstring = talloc_asprintf(el->values, "%lld", (long long)*new_val);
+               if (!vstring) {
+                       return ldb_module_oom(module);
+               }
+               *el->values = data_blob_string_const(vstring);
+       }
+
+       return LDB_SUCCESS;
+}
+
+int dsdb_msg_constrainted_update_uint64(struct ldb_module *module,
+                                       struct ldb_message *msg,
+                                       const char *attr,
+                                       const uint64_t *old_val,
+                                       const uint64_t *new_val)
+{
+       return dsdb_msg_constrainted_update_int64(module, msg, attr,
+                                                 (const int64_t *)old_val,
+                                                 (const int64_t *)new_val);
+}
+
+/*
+  update an int32 attribute safely via a constrained delete/add
+ */
+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,
+                                         struct ldb_request *parent)
 {
+       struct ldb_message *msg;
+       int ret;
+
+       msg = ldb_msg_new(module);
+       msg->dn = dn;
+
+       ret = dsdb_msg_constrainted_update_int32(module,
+                                                msg, attr,
+                                                old_val,
+                                                new_val);
+       if (ret != LDB_SUCCESS) {
+               talloc_free(msg);
+               return ret;
+       }
+
+       ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE, parent);
+       talloc_free(msg);
+       return ret;
+}
+
+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,
+                                          struct ldb_request *parent)
+{
+       return dsdb_module_constrainted_update_int32(module, dn, attr,
+                                                    (const int32_t *)old_val,
+                                                    (const int32_t *)new_val, parent);
+}
+
+/*
+  update an int64 attribute safely via a constrained delete/add
+ */
+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,
+                                         struct ldb_request *parent)
+{
+       struct ldb_message *msg;
+       int ret;
+
+       msg = ldb_msg_new(module);
+       msg->dn = dn;
+
+       ret = dsdb_msg_constrainted_update_int64(module,
+                                                msg, attr,
+                                                old_val,
+                                                new_val);
+       if (ret != LDB_SUCCESS) {
+               talloc_free(msg);
+               return ret;
+       }
+
+       ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE, parent);
+       talloc_free(msg);
+       return ret;
+}
+
+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,
+                                          struct ldb_request *parent)
+{
+       return dsdb_module_constrainted_update_int64(module, dn, attr,
+                                                    (const int64_t *)old_val,
+                                                    (const int64_t *)new_val,
+                                                    parent);
+}
+
+
+const struct ldb_val *dsdb_module_find_dsheuristics(struct ldb_module *module,
+                                                   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 };
+       struct ldb_result *res;
+
+       new_dn = ldb_dn_copy(mem_ctx, ldb_get_config_basedn(ldb));
+       if (!ldb_dn_add_child_fmt(new_dn,
+                                  "CN=Directory Service,CN=Windows NT,CN=Services")) {
+               talloc_free(new_dn);
+               return NULL;
+       }
+       ret = dsdb_module_search_dn(module, mem_ctx, &res,
+                                   new_dn,
+                                   attrs,
+                                   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");
+       }
+       talloc_free(new_dn);
+       return NULL;
+}
+
+bool dsdb_block_anonymous_ops(struct ldb_module *module, struct ldb_request *parent)
+{
+       TALLOC_CTX *tmp_ctx = talloc_new(module);
+       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_BLOCK_ANONYMOUS_OPS) {
+               result = true;
+       } else if (hr_val->data[DS_HR_BLOCK_ANONYMOUS_OPS -1] == '2') {
+               result = false;
+       } else {
+               result = true;
+       }
+
+       talloc_free(tmp_ctx);
+       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)
+{
+       char *s = ldb_module_call_chain(req, req);
+       DEBUG(level, ("%s\n", s));
+       talloc_free(s);
+}
+
+/*
+ * 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;
 
-       for (i = 0; attrs[i]; i++) {
-               if (ldb_attr_cmp(attrs[i], attr) == 0)
-                       return true;
+       /* 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 false;
+       return el;
 }