s4-dsdb: added samdb_ldb_val_case_cmp()
[ira/wip.git] / source4 / dsdb / common / util.c
index 512230f63c0ec84eecea8e355596e2913ac6960a..139ea4dc6b4ab2d2e8911abe6c50397a86c3e124 100644 (file)
@@ -396,22 +396,15 @@ struct dom_sid *samdb_result_dom_sid(TALLOC_CTX *mem_ctx, const struct ldb_messa
 struct GUID samdb_result_guid(const struct ldb_message *msg, const char *attr)
 {
        const struct ldb_val *v;
-       enum ndr_err_code ndr_err;
        struct GUID guid;
-       TALLOC_CTX *mem_ctx;
-
-       ZERO_STRUCT(guid);
+       NTSTATUS status;
 
        v = ldb_msg_find_ldb_val(msg, attr);
-       if (!v) return guid;
+       if (!v) return GUID_zero();
 
-       mem_ctx = talloc_named_const(NULL, 0, "samdb_result_guid");
-       if (!mem_ctx) return guid;
-       ndr_err = ndr_pull_struct_blob(v, mem_ctx, NULL, &guid,
-                                      (ndr_pull_flags_fn_t)ndr_pull_GUID);
-       talloc_free(mem_ctx);
-       if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
-               return guid;
+       status = GUID_from_ndr_blob(v, &guid);
+       if (!NT_STATUS_IS_OK(status)) {
+               return GUID_zero();
        }
 
        return guid;
@@ -433,7 +426,8 @@ struct dom_sid *samdb_result_sid_prefix(TALLOC_CTX *mem_ctx, const struct ldb_me
 /*
   pull a NTTIME in a result set. 
 */
-NTTIME samdb_result_nttime(struct ldb_message *msg, const char *attr, NTTIME default_value)
+NTTIME samdb_result_nttime(const struct ldb_message *msg, const char *attr,
+                          NTTIME default_value)
 {
        return ldb_msg_find_attr_as_uint64(msg, attr, default_value);
 }
@@ -444,7 +438,7 @@ NTTIME samdb_result_nttime(struct ldb_message *msg, const char *attr, NTTIME def
  * it returns 0x7FFFFFFFFFFFFFFF, not returning this value in this case
  * cause windows 2008 and newer version to fail for SMB requests
  */
-NTTIME samdb_result_last_logoff(struct ldb_message *msg)
+NTTIME samdb_result_last_logoff(const struct ldb_message *msg)
 {
        NTTIME ret = ldb_msg_find_attr_as_uint64(msg, "lastLogoff",0);
 
@@ -466,7 +460,7 @@ NTTIME samdb_result_last_logoff(struct ldb_message *msg)
  * Consolidate that logic here to allow clearer logic for account expiry in
  * the rest of the code.
  */
-NTTIME samdb_result_account_expires(struct ldb_message *msg)
+NTTIME samdb_result_account_expires(const struct ldb_message *msg)
 {
        NTTIME ret = ldb_msg_find_attr_as_uint64(msg, "accountExpires",
                                                 0);
@@ -480,7 +474,8 @@ NTTIME samdb_result_account_expires(struct ldb_message *msg)
 /*
   pull a uint64_t from a result set. 
 */
-uint64_t samdb_result_uint64(struct ldb_message *msg, const char *attr, uint64_t default_value)
+uint64_t samdb_result_uint64(const struct ldb_message *msg, const char *attr,
+                            uint64_t default_value)
 {
        return ldb_msg_find_attr_as_uint64(msg, attr, default_value);
 }
@@ -1001,6 +996,81 @@ int samdb_replace(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_m
        return ldb_modify(sam_ldb, msg);
 }
 
+/*
+ * Handle ldb_request in transaction
+ */
+static int dsdb_autotransaction_request(struct ldb_context *sam_ldb,
+                                struct ldb_request *req)
+{
+       int ret;
+
+       ret = ldb_transaction_start(sam_ldb);
+       if (ret != LDB_SUCCESS) {
+               return ret;
+       }
+
+       ret = ldb_request(sam_ldb, req);
+       if (ret == LDB_SUCCESS) {
+               ret = ldb_wait(req->handle, LDB_WAIT_ALL);
+       }
+
+       if (ret == LDB_SUCCESS) {
+               return ldb_transaction_commit(sam_ldb);
+       }
+       ldb_transaction_cancel(sam_ldb);
+
+       return ret;
+}
+
+/*
+ * replace elements in a record using LDB_CONTROL_AS_SYSTEM
+ * used to skip access checks on operations
+ * that are performed by the system
+ */
+int samdb_replace_as_system(struct ldb_context *sam_ldb,
+                           TALLOC_CTX *mem_ctx,
+                           struct ldb_message *msg)
+{
+       int i;
+       int ldb_ret;
+       struct ldb_request *req = NULL;
+
+       /* mark all the message elements as LDB_FLAG_MOD_REPLACE */
+       for (i=0;i<msg->num_elements;i++) {
+               msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
+       }
+
+
+       ldb_ret = ldb_msg_sanity_check(sam_ldb, msg);
+       if (ldb_ret != LDB_SUCCESS) {
+               return ldb_ret;
+       }
+
+       ldb_ret = ldb_build_mod_req(&req, sam_ldb, mem_ctx,
+                                   msg,
+                                   NULL,
+                                   NULL,
+                                   ldb_op_default_callback,
+                                   NULL);
+
+       if (ldb_ret != LDB_SUCCESS) {
+               talloc_free(req);
+               return ldb_ret;
+       }
+
+       ldb_ret = ldb_request_add_control(req, LDB_CONTROL_AS_SYSTEM_OID, false, NULL);
+       if (ldb_ret != LDB_SUCCESS) {
+               talloc_free(req);
+               return ldb_ret;
+       }
+
+       /* do request and auto start a transaction */
+       ldb_ret = dsdb_autotransaction_request(sam_ldb, req);
+
+       talloc_free(req);
+       return ldb_ret;
+}
+
 /*
   return a default security descriptor
 */
@@ -1451,6 +1521,86 @@ struct ldb_dn *samdb_server_site_dn(struct ldb_context *ldb, TALLOC_CTX *mem_ctx
        return server_site_dn;
 }
 
+/*
+  find a 'reference' DN that points at another object
+  (eg. serverReference, rIDManagerReference etc)
+ */
+int samdb_reference_dn(struct ldb_context *ldb, TALLOC_CTX *mem_ctx, struct ldb_dn *base,
+                      const char *attribute, struct ldb_dn **dn)
+{
+       const char *attrs[2];
+       struct ldb_result *res;
+       int ret;
+
+       attrs[0] = attribute;
+       attrs[1] = NULL;
+
+       ret = ldb_search(ldb, mem_ctx, &res, base, LDB_SCOPE_BASE, attrs, NULL);
+       if (ret != LDB_SUCCESS) {
+               return ret;
+       }
+       if (res->count != 1) {
+               talloc_free(res);
+               return LDB_ERR_NO_SUCH_OBJECT;
+       }
+
+       *dn = ldb_msg_find_attr_as_dn(ldb, mem_ctx, res->msgs[0], attribute);
+       if (!*dn) {
+               talloc_free(res);
+               return LDB_ERR_NO_SUCH_ATTRIBUTE;
+       }
+
+       talloc_free(res);
+       return LDB_SUCCESS;
+}
+
+/*
+  find our machine account via the serverReference attribute in the
+  server DN
+ */
+int samdb_server_reference_dn(struct ldb_context *ldb, TALLOC_CTX *mem_ctx, struct ldb_dn **dn)
+{
+       struct ldb_dn *server_dn;
+       int ret;
+
+       server_dn = samdb_server_dn(ldb, mem_ctx);
+       if (server_dn == NULL) {
+               return LDB_ERR_NO_SUCH_OBJECT;
+       }
+
+       ret = samdb_reference_dn(ldb, mem_ctx, server_dn, "serverReference", dn);
+       talloc_free(server_dn);
+
+       return ret;
+}
+
+/*
+  find the RID Manager$ DN via the rIDManagerReference attribute in the
+  base DN
+ */
+int samdb_rid_manager_dn(struct ldb_context *ldb, TALLOC_CTX *mem_ctx, struct ldb_dn **dn)
+{
+       return samdb_reference_dn(ldb, mem_ctx, samdb_base_dn(ldb), "rIDManagerReference", dn);
+}
+
+/*
+  find the RID Set DN via the rIDSetReferences attribute in our
+  machine account DN
+ */
+int samdb_rid_set_dn(struct ldb_context *ldb, TALLOC_CTX *mem_ctx, struct ldb_dn **dn)
+{
+       struct ldb_dn *server_ref_dn;
+       int ret;
+
+       ret = samdb_server_reference_dn(ldb, mem_ctx, &server_ref_dn);
+       if (ret != LDB_SUCCESS) {
+               return ret;
+       }
+       ret = samdb_reference_dn(ldb, mem_ctx, server_ref_dn, "rIDSetReferences", dn);
+       talloc_free(server_ref_dn);
+       return ret;
+}
+
 const char *samdb_server_site_name(struct ldb_context *ldb, TALLOC_CTX *mem_ctx)
 {
        const struct ldb_val *val = ldb_dn_get_rdn_val(samdb_server_site_dn(ldb, mem_ctx));
@@ -1868,20 +2018,15 @@ NTSTATUS samdb_set_password(struct ldb_context *ctx, TALLOC_CTX *mem_ctx,
                 * hashes */
                CHECK_RET(ldb_msg_add_value(mod, "clearTextPassword", new_password, NULL));
        } else {
-               /* We don't have the cleartext, so delete the old one
-                * and set what we have of the hashes */
-               CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "clearTextPassword"));
+               /* we don't have the cleartext, so set what we have of the
+                * hashes */
 
                if (lmNewHash) {
                        CHECK_RET(samdb_msg_add_hash(ctx, mem_ctx, mod, "dBCSPwd", lmNewHash));
-               } else {
-                       CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "dBCSPwd"));
                }
 
                if (ntNewHash) {
                        CHECK_RET(samdb_msg_add_hash(ctx, mem_ctx, mod, "unicodePwd", ntNewHash));
-               } else {
-                       CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "unicodePwd"));
                }
        }
 
@@ -1941,12 +2086,15 @@ NTSTATUS samdb_set_password_sid(struct ldb_context *ldb, TALLOC_CTX *mem_ctx,
        msg = ldb_msg_new(mem_ctx);
        if (msg == NULL) {
                ldb_transaction_cancel(ldb);
+               talloc_free(user_dn);
                return NT_STATUS_NO_MEMORY;
        }
 
        msg->dn = ldb_dn_copy(msg, user_dn);
        if (!msg->dn) {
                ldb_transaction_cancel(ldb);
+               talloc_free(user_dn);
+               talloc_free(msg);
                return NT_STATUS_NO_MEMORY;
        }
 
@@ -1954,10 +2102,12 @@ NTSTATUS samdb_set_password_sid(struct ldb_context *ldb, TALLOC_CTX *mem_ctx,
                                       user_dn, NULL,
                                       msg, new_password,
                                       lmNewHash, ntNewHash,
-                                      user_change, /* This is a password set, not change */
+                                      user_change,
                                       reject_reason, _dominfo);
        if (!NT_STATUS_IS_OK(nt_status)) {
                ldb_transaction_cancel(ldb);
+               talloc_free(user_dn);
+               talloc_free(msg);
                return nt_status;
        }
 
@@ -1965,16 +2115,23 @@ NTSTATUS samdb_set_password_sid(struct ldb_context *ldb, TALLOC_CTX *mem_ctx,
        ret = samdb_replace(ldb, mem_ctx, msg);
        if (ret != LDB_SUCCESS) {
                ldb_transaction_cancel(ldb);
+               talloc_free(user_dn);
+               talloc_free(msg);
                return NT_STATUS_ACCESS_DENIED;
        }
 
+       talloc_free(msg);
+
        ret = ldb_transaction_commit(ldb);
        if (ret != LDB_SUCCESS) {
                DEBUG(0,("Failed to commit transaction to change password on %s: %s\n",
-                        ldb_dn_get_linearized(msg->dn),
+                        ldb_dn_get_linearized(user_dn),
                         ldb_errstring(ldb)));
+               talloc_free(user_dn);
                return NT_STATUS_TRANSACTION_ABORTED;
        }
+
+       talloc_free(user_dn);
        return NT_STATUS_OK;
 }
 
@@ -1984,7 +2141,7 @@ NTSTATUS samdb_create_foreign_security_principal(struct ldb_context *sam_ctx, TA
 {
        struct ldb_message *msg;
        struct ldb_dn *basedn;
-       const char *sidstr;
+       char *sidstr;
        int ret;
 
        sidstr = dom_sid_string(mem_ctx, sid);
@@ -1993,45 +2150,47 @@ NTSTATUS samdb_create_foreign_security_principal(struct ldb_context *sam_ctx, TA
        /* We might have to create a ForeignSecurityPrincipal, even if this user
         * is in our own domain */
 
-       msg = ldb_msg_new(mem_ctx);
+       msg = ldb_msg_new(sidstr);
        if (msg == NULL) {
+               talloc_free(sidstr);
                return NT_STATUS_NO_MEMORY;
        }
 
-       /* TODO: Hmmm. This feels wrong. How do I find the base dn to
-        * put the ForeignSecurityPrincipals? d_state->domain_dn does
-        * not work, this is wrong for the Builtin domain, there's no
-        * cn=For...,cn=Builtin,dc={BASEDN}.  -- vl
-        */
-
-       basedn = samdb_search_dn(sam_ctx, mem_ctx, NULL,
-                                "(&(objectClass=container)(cn=ForeignSecurityPrincipals))");
-
-       if (basedn == NULL) {
+       ret = dsdb_wellknown_dn(sam_ctx, sidstr, samdb_base_dn(sam_ctx),
+                               DS_GUID_FOREIGNSECURITYPRINCIPALS_CONTAINER,
+                               &basedn);
+       if (ret != LDB_SUCCESS) {
                DEBUG(0, ("Failed to find DN for "
-                         "ForeignSecurityPrincipal container\n"));
+                         "ForeignSecurityPrincipal container - %s\n", ldb_errstring(sam_ctx)));
+               talloc_free(sidstr);
                return NT_STATUS_INTERNAL_DB_CORRUPTION;
        }
 
        /* add core elements to the ldb_message for the alias */
-       msg->dn = ldb_dn_copy(mem_ctx, basedn);
-       if ( ! ldb_dn_add_child_fmt(msg->dn, "CN=%s", sidstr))
+       msg->dn = basedn;
+       if ( ! ldb_dn_add_child_fmt(msg->dn, "CN=%s", sidstr)) {
+               talloc_free(sidstr);
                return NT_STATUS_NO_MEMORY;
+       }
 
-       samdb_msg_add_string(sam_ctx, mem_ctx, msg,
+       samdb_msg_add_string(sam_ctx, msg, msg,
                             "objectClass",
                             "foreignSecurityPrincipal");
 
        /* create the alias */
        ret = ldb_add(sam_ctx, msg);
-       if (ret != 0) {
+       if (ret != LDB_SUCCESS) {
                DEBUG(0,("Failed to create foreignSecurityPrincipal "
                         "record %s: %s\n", 
                         ldb_dn_get_linearized(msg->dn),
                         ldb_errstring(sam_ctx)));
+               talloc_free(sidstr);
                return NT_STATUS_INTERNAL_DB_CORRUPTION;
        }
-       *ret_dn = msg->dn;
+
+       *ret_dn = talloc_steal(mem_ctx, msg->dn);
+       talloc_free(sidstr);
+
        return NT_STATUS_OK;
 }
 
@@ -2070,14 +2229,16 @@ struct ldb_dn *samdb_dns_domain_to_dn(struct ldb_context *ldb, TALLOC_CTX *mem_c
        if (!ldb_dn_validate(dn)) {
                DEBUG(2, ("Failed to validated DN %s\n",
                          ldb_dn_get_linearized(dn)));
+               talloc_free(tmp_ctx);
                return NULL;
        }
+       talloc_free(tmp_ctx);
        return dn;
 }
+
 /*
   Find the DN of a domain, be it the netbios or DNS name 
 */
-
 struct ldb_dn *samdb_domain_to_dn(struct ldb_context *ldb, TALLOC_CTX *mem_ctx, 
                                  const char *domain_name) 
 {
@@ -2149,13 +2310,14 @@ int dsdb_find_dn_by_guid(struct ldb_context *ldb,
                return LDB_ERR_OPERATIONS_ERROR;
        }
 
-       res = talloc_zero(mem_ctx, struct ldb_result);
+       res = talloc_zero(expression, struct ldb_result);
        if (!res) {
                DEBUG(0, (__location__ ": out of memory\n"));
+               talloc_free(expression);
                return LDB_ERR_OPERATIONS_ERROR;
        }
 
-       ret = ldb_build_search_req(&search_req, ldb, mem_ctx,
+       ret = ldb_build_search_req(&search_req, ldb, expression,
                                   ldb_get_default_basedn(ldb),
                                   LDB_SCOPE_SUBTREE,
                                   expression, attrs,
@@ -2163,6 +2325,7 @@ int dsdb_find_dn_by_guid(struct ldb_context *ldb,
                                   res, ldb_search_default_callback,
                                   NULL);
        if (ret != LDB_SUCCESS) {
+               talloc_free(expression);
                return ret;
        }
 
@@ -2171,12 +2334,14 @@ int dsdb_find_dn_by_guid(struct ldb_context *ldb,
        options = talloc(search_req, struct ldb_search_options_control);
        if (options == NULL) {
                DEBUG(0, (__location__ ": out of memory\n"));
+               talloc_free(expression);
                return LDB_ERR_OPERATIONS_ERROR;
        }
        options->search_options = LDB_SEARCH_OPTION_PHANTOM_ROOT;
 
        ret = ldb_request_add_control(search_req, LDB_CONTROL_EXTENDED_DN_OID, true, NULL);
        if (ret != LDB_SUCCESS) {
+               talloc_free(expression);
                return ret;
        }
 
@@ -2184,16 +2349,19 @@ int dsdb_find_dn_by_guid(struct ldb_context *ldb,
                                      LDB_CONTROL_SEARCH_OPTIONS_OID,
                                      true, options);
        if (ret != LDB_SUCCESS) {
+               talloc_free(expression);
                return ret;
        }
 
        ret = ldb_request(ldb, search_req);
        if (ret != LDB_SUCCESS) {
+               talloc_free(expression);
                return ret;
        }
 
        ret = ldb_wait(search_req->handle, LDB_WAIT_ALL);
        if (ret != LDB_SUCCESS) {
+               talloc_free(expression);
                return ret;
        }
 
@@ -2201,10 +2369,12 @@ int dsdb_find_dn_by_guid(struct ldb_context *ldb,
           partitions module that can return two here with the
           search_options control set */
        if (res->count < 1) {
+               talloc_free(expression);
                return LDB_ERR_NO_SUCH_OBJECT;
        }
 
-       *dn = res->msgs[0]->dn;
+       *dn = talloc_steal(mem_ctx, res->msgs[0]->dn);
+       talloc_free(expression);
 
        return LDB_SUCCESS;
 }
@@ -2227,6 +2397,7 @@ int dsdb_search_dn_with_deleted(struct ldb_context *ldb,
 
        res = talloc_zero(tmp_ctx, struct ldb_result);
        if (!res) {
+               talloc_free(tmp_ctx);
                return LDB_ERR_OPERATIONS_ERROR;
        }
 
@@ -2246,6 +2417,7 @@ int dsdb_search_dn_with_deleted(struct ldb_context *ldb,
 
        ret = ldb_request_add_control(req, LDB_CONTROL_SHOW_DELETED_OID, true, NULL);
        if (ret != LDB_SUCCESS) {
+               talloc_free(tmp_ctx);
                return ret;
        }
 
@@ -2254,23 +2426,27 @@ int dsdb_search_dn_with_deleted(struct ldb_context *ldb,
                ret = ldb_wait(req->handle, LDB_WAIT_ALL);
        }
 
-       talloc_free(req);
        *_res = talloc_steal(mem_ctx, res);
+       talloc_free(tmp_ctx);
        return ret;
 }
 
 
 /*
-  use a DN to find a GUID
+  use a DN to find a GUID with a given attribute name
  */
-int dsdb_find_guid_by_dn(struct ldb_context *ldb, 
-                        struct ldb_dn *dn, struct GUID *guid)
+int dsdb_find_guid_attr_by_dn(struct ldb_context *ldb,
+                             struct ldb_dn *dn, const char *attribute,
+                             struct GUID *guid)
 {
        int ret;
        struct ldb_result *res;
-       const char *attrs[] = { "objectGUID", NULL };
+       const char *attrs[2];
        TALLOC_CTX *tmp_ctx = talloc_new(ldb);
 
+       attrs[0] = attribute;
+       attrs[1] = NULL;
+
        ret = dsdb_search_dn_with_deleted(ldb, tmp_ctx, &res, dn, attrs);
        if (ret != LDB_SUCCESS) {
                talloc_free(tmp_ctx);
@@ -2280,11 +2456,20 @@ int dsdb_find_guid_by_dn(struct ldb_context *ldb,
                talloc_free(tmp_ctx);
                return LDB_ERR_NO_SUCH_OBJECT;
        }
-       *guid = samdb_result_guid(res->msgs[0], "objectGUID");
+       *guid = samdb_result_guid(res->msgs[0], attribute);
        talloc_free(tmp_ctx);
        return LDB_SUCCESS;
 }
 
+/*
+  use a DN to find a GUID
+ */
+int dsdb_find_guid_by_dn(struct ldb_context *ldb,
+                        struct ldb_dn *dn, struct GUID *guid)
+{
+       return dsdb_find_guid_attr_by_dn(ldb, dn, "objectGUID", guid);
+}
+
 
 
 /*
@@ -2296,16 +2481,12 @@ int dsdb_msg_add_guid(struct ldb_message *msg,
                const char *attr_name)
 {
        int ret;
-       enum ndr_err_code ndr_err;
        struct ldb_val v;
-
+       NTSTATUS status;
        TALLOC_CTX *tmp_ctx =  talloc_init("dsdb_msg_add_guid");
 
-       ndr_err = ndr_push_struct_blob(&v, tmp_ctx, NULL,
-                                      guid,
-                                      (ndr_push_flags_fn_t)ndr_push_GUID);
-
-       if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+       status = GUID_to_ndr_blob(guid, tmp_ctx, &v);
+       if (!NT_STATUS_IS_OK(status)) {
                ret = LDB_ERR_OPERATIONS_ERROR;
                goto done;
        }
@@ -2629,6 +2810,12 @@ int drsuapi_DsReplicaCursor2_compare(const struct drsuapi_DsReplicaCursor2 *c1,
        return GUID_compare(&c1->source_dsa_invocation_id, &c2->source_dsa_invocation_id);
 }
 
+int drsuapi_DsReplicaCursor_compare(const struct drsuapi_DsReplicaCursor *c1,
+                                   const struct drsuapi_DsReplicaCursor *c2)
+{
+       return GUID_compare(&c1->source_dsa_invocation_id, &c2->source_dsa_invocation_id);
+}
+
 /*
   see if we are a RODC
 
@@ -2721,3 +2908,309 @@ int dsdb_functional_level(struct ldb_context *ldb)
        return *domainFunctionality;
 }
 
+/*
+  set a GUID in an extended DN structure
+ */
+int dsdb_set_extended_dn_guid(struct ldb_dn *dn, const struct GUID *guid, const char *component_name)
+{
+       struct ldb_val v;
+       NTSTATUS status;
+       int ret;
+
+       status = GUID_to_ndr_blob(guid, dn, &v);
+       if (!NT_STATUS_IS_OK(status)) {
+               return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
+       }
+
+       ret = ldb_dn_set_extended_component(dn, component_name, &v);
+       data_blob_free(&v);
+       return ret;
+}
+
+/*
+  return a GUID from a extended DN structure
+ */
+NTSTATUS dsdb_get_extended_dn_guid(struct ldb_dn *dn, struct GUID *guid, const char *component_name)
+{
+       const struct ldb_val *v;
+
+       v = ldb_dn_get_extended_component(dn, component_name);
+       if (v == NULL) {
+               return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+       }
+
+       return GUID_from_ndr_blob(v, guid);
+}
+
+/*
+  return a uint64_t from a extended DN structure
+ */
+NTSTATUS dsdb_get_extended_dn_uint64(struct ldb_dn *dn, uint64_t *val, const char *component_name)
+{
+       const struct ldb_val *v;
+       char *s;
+
+       v = ldb_dn_get_extended_component(dn, component_name);
+       if (v == NULL) {
+               return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+       }
+       s = talloc_strndup(dn, (const char *)v->data, v->length);
+       NT_STATUS_HAVE_NO_MEMORY(s);
+
+       *val = strtoull(s, NULL, 0);
+
+       talloc_free(s);
+       return NT_STATUS_OK;
+}
+
+/*
+  return a NTTIME from a extended DN structure
+ */
+NTSTATUS dsdb_get_extended_dn_nttime(struct ldb_dn *dn, NTTIME *nttime, const char *component_name)
+{
+       return dsdb_get_extended_dn_uint64(dn, nttime, component_name);
+}
+
+/*
+  return a uint32_t from a extended DN structure
+ */
+NTSTATUS dsdb_get_extended_dn_uint32(struct ldb_dn *dn, uint32_t *val, const char *component_name)
+{
+       const struct ldb_val *v;
+       char *s;
+
+       v = ldb_dn_get_extended_component(dn, component_name);
+       if (v == NULL) {
+               return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+       }
+
+       s = talloc_strndup(dn, (const char *)v->data, v->length);
+       NT_STATUS_HAVE_NO_MEMORY(s);
+
+       *val = strtoul(s, NULL, 0);
+
+       talloc_free(s);
+       return NT_STATUS_OK;
+}
+
+/*
+  return RMD_FLAGS directly from a ldb_dn
+  returns 0 if not found
+ */
+uint32_t dsdb_dn_rmd_flags(struct ldb_dn *dn)
+{
+       const struct ldb_val *v;
+       char buf[32];
+       v = ldb_dn_get_extended_component(dn, "RMD_FLAGS");
+       if (!v || v->length > sizeof(buf)-1) return 0;
+       strncpy(buf, (const char *)v->data, v->length);
+       buf[v->length] = 0;
+       return strtoul(buf, NULL, 10);
+}
+
+/*
+  return RMD_FLAGS directly from a ldb_val for a DN
+  returns 0 if RMD_FLAGS is not found
+ */
+uint32_t dsdb_dn_val_rmd_flags(struct ldb_val *val)
+{
+       const char *p;
+       uint32_t flags;
+       char *end;
+
+       if (val->length < 13) {
+               return 0;
+       }
+       p = memmem(val->data, val->length-2, "<RMD_FLAGS=", 11);
+       if (!p) {
+               return 0;
+       }
+       flags = strtoul(p+11, &end, 10);
+       if (!end || *end != '>') {
+               /* it must end in a > */
+               return 0;
+       }
+       return flags;
+}
+
+/*
+  return true if a ldb_val containing a DN in storage form is deleted
+ */
+bool dsdb_dn_is_deleted_val(struct ldb_val *val)
+{
+       return (dsdb_dn_val_rmd_flags(val) & DSDB_RMD_FLAG_DELETED) != 0;
+}
+
+/*
+  return true if a ldb_val containing a DN in storage form is
+  in the upgraded w2k3 linked attribute format
+ */
+bool dsdb_dn_is_upgraded_link_val(struct ldb_val *val)
+{
+       return memmem(val->data, val->length, "<RMD_ADDTIME=", 13) != NULL;
+}
+
+/*
+  return a DN for a wellknown GUID
+ */
+int dsdb_wellknown_dn(struct ldb_context *samdb, TALLOC_CTX *mem_ctx,
+                     struct ldb_dn *nc_root, const char *wk_guid,
+                     struct ldb_dn **wkguid_dn)
+{
+       TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
+       const char *attrs[] = { NULL };
+       int ret;
+       struct ldb_dn *dn;
+       struct ldb_result *res;
+
+       /* construct the magic WKGUID DN */
+       dn = ldb_dn_new_fmt(tmp_ctx, samdb, "<WKGUID=%s,%s>",
+                           wk_guid, ldb_dn_get_linearized(nc_root));
+       if (!wkguid_dn) {
+               talloc_free(tmp_ctx);
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       ret = dsdb_search_dn_with_deleted(samdb, tmp_ctx, &res, dn, attrs);
+       if (ret != LDB_SUCCESS) {
+               talloc_free(tmp_ctx);
+               return ret;
+       }
+
+       (*wkguid_dn) = talloc_steal(mem_ctx, res->msgs[0]->dn);
+       talloc_free(tmp_ctx);
+       return LDB_SUCCESS;
+}
+
+
+static int dsdb_dn_compare_ptrs(struct ldb_dn **dn1, struct ldb_dn **dn2)
+{
+       return ldb_dn_compare(*dn1, *dn2);
+}
+
+/*
+  find a NC root given a DN within the NC
+ */
+int dsdb_find_nc_root(struct ldb_context *samdb, TALLOC_CTX *mem_ctx, struct ldb_dn *dn,
+                     struct ldb_dn **nc_root)
+{
+       const char *root_attrs[] = { "namingContexts", NULL };
+       TALLOC_CTX *tmp_ctx;
+       int ret;
+       struct ldb_message_element *el;
+       struct ldb_result *root_res;
+       int i;
+       struct ldb_dn **nc_dns;
+
+       tmp_ctx = talloc_new(samdb);
+       if (tmp_ctx == NULL) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       ret = ldb_search(samdb, tmp_ctx, &root_res,
+                        ldb_dn_new(tmp_ctx, samdb, ""), LDB_SCOPE_BASE, root_attrs, NULL);
+       if (ret) {
+               DEBUG(1,("Searching for namingContexts in rootDSE failed: %s\n", ldb_errstring(samdb)));
+               talloc_free(tmp_ctx);
+               return ret;
+       }
+
+       el = ldb_msg_find_element(root_res->msgs[0], "namingContexts");
+       if (!el) {
+               DEBUG(1,("Finding namingContexts element in root_res failed: %s\n",
+                       ldb_errstring(samdb)));
+              talloc_free(tmp_ctx);
+              return LDB_ERR_NO_SUCH_ATTRIBUTE;
+       }
+
+       nc_dns = talloc_array(tmp_ctx, struct ldb_dn *, el->num_values);
+       if (!nc_dns) {
+              talloc_free(tmp_ctx);
+              return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       for (i=0; i<el->num_values; i++) {
+              nc_dns[i] = ldb_dn_from_ldb_val(nc_dns, samdb, &el->values[i]);
+              if (nc_dns[i] == NULL) {
+                      talloc_free(tmp_ctx);
+                      return LDB_ERR_OPERATIONS_ERROR;
+              }
+       }
+
+       qsort(nc_dns, el->num_values, sizeof(nc_dns[0]), (comparison_fn_t)dsdb_dn_compare_ptrs);
+
+       for (i=0; i<el->num_values; i++) {
+               if (ldb_dn_compare_base(nc_dns[i], dn) == 0) {
+                      (*nc_root) = talloc_steal(mem_ctx, nc_dns[i]);
+                       talloc_free(tmp_ctx);
+                       return LDB_SUCCESS;
+               }
+       }
+
+       talloc_free(tmp_ctx);
+       return LDB_ERR_NO_SUCH_OBJECT;
+}
+
+
+/*
+  find the deleted objects DN for any object, by looking for the NC
+  root, then looking up the wellknown GUID
+ */
+int dsdb_get_deleted_objects_dn(struct ldb_context *ldb,
+                               TALLOC_CTX *mem_ctx, struct ldb_dn *obj_dn,
+                               struct ldb_dn **do_dn)
+{
+       struct ldb_dn *nc_root;
+       int ret;
+
+       ret = dsdb_find_nc_root(ldb, mem_ctx, obj_dn, &nc_root);
+       if (ret != LDB_SUCCESS) {
+               return ret;
+       }
+
+       ret = dsdb_wellknown_dn(ldb, mem_ctx, nc_root, DS_GUID_DELETED_OBJECTS_CONTAINER, do_dn);
+       talloc_free(nc_root);
+       return ret;
+}
+
+/*
+  return the tombstoneLifetime, in days
+ */
+int dsdb_tombstone_lifetime(struct ldb_context *ldb, uint32_t *lifetime)
+{
+       struct ldb_dn *dn;
+       dn = samdb_config_dn(ldb);
+       if (!dn) {
+               return LDB_ERR_NO_SUCH_OBJECT;
+       }
+       dn = ldb_dn_copy(ldb, dn);
+       if (!dn) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+       /* see MS-ADTS section 7.1.1.2.4.1.1. There doesn't appear to
+        be a wellknown GUID for this */
+       if (!ldb_dn_add_child_fmt(dn, "CN=Directory Service,CN=Windows NT")) {
+               talloc_free(dn);
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       *lifetime = samdb_search_uint(ldb, dn, 180, dn, "tombstoneLifetime", "objectClass=nTDSService");
+       talloc_free(dn);
+       return LDB_SUCCESS;
+}
+
+/*
+  compare a ldb_val to a string case insensitively
+ */
+int samdb_ldb_val_case_cmp(const char *s, struct ldb_val *v)
+{
+       size_t len = strlen(s);
+       int ret;
+       if (len > v->length) return 1;
+       ret = strncasecmp(s, (const char *)v->data, v->length);
+       if (ret != 0) return ret;
+       if (v->length > len && v->data[len] != 0) {
+               return -1;
+       }
+       return 0;
+}