+
+
+/*
+ use a GUID to find a DN
+ */
+int dsdb_find_dn_by_guid(struct ldb_context *ldb,
+ TALLOC_CTX *mem_ctx,
+ const char *guid_str, struct ldb_dn **dn)
+{
+ int ret;
+ struct ldb_result *res;
+ const char *attrs[] = { NULL };
+ struct ldb_request *search_req;
+ char *expression;
+ struct ldb_search_options_control *options;
+
+ expression = talloc_asprintf(mem_ctx, "objectGUID=%s", guid_str);
+ if (!expression) {
+ DEBUG(0, (__location__ ": out of memory\n"));
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ 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, expression,
+ ldb_get_default_basedn(ldb),
+ LDB_SCOPE_SUBTREE,
+ expression, attrs,
+ NULL,
+ res, ldb_search_default_callback,
+ NULL);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(expression);
+ return ret;
+ }
+
+ /* we need to cope with cross-partition links, so search for
+ the GUID over all partitions */
+ 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;
+ }
+
+ ret = ldb_request_add_control(search_req,
+ 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;
+ }
+
+ /* this really should be exactly 1, but there is a bug in the
+ 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 = talloc_steal(mem_ctx, res->msgs[0]->dn);
+ talloc_free(expression);
+
+ return LDB_SUCCESS;
+}
+
+/*
+ search for attrs on one DN, allowing for deleted objects
+ */
+int dsdb_search_dn_with_deleted(struct ldb_context *ldb,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_result **_res,
+ struct ldb_dn *basedn,
+ const char * const *attrs)
+{
+ int ret;
+ struct ldb_request *req;
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_result *res;
+
+ tmp_ctx = talloc_new(mem_ctx);
+
+ res = talloc_zero(tmp_ctx, struct ldb_result);
+ if (!res) {
+ talloc_free(tmp_ctx);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ret = ldb_build_search_req(&req, ldb, tmp_ctx,
+ basedn,
+ LDB_SCOPE_BASE,
+ NULL,
+ attrs,
+ NULL,
+ res,
+ ldb_search_default_callback,
+ NULL);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+
+ ret = ldb_request_add_control(req, LDB_CONTROL_SHOW_DELETED_OID, true, NULL);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+
+ ret = ldb_request(ldb, req);
+ if (ret == LDB_SUCCESS) {
+ ret = ldb_wait(req->handle, LDB_WAIT_ALL);
+ }
+
+ *_res = talloc_steal(mem_ctx, res);
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+
+/*
+ use a DN to find a GUID with a given attribute name
+ */
+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[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);
+ return ret;
+ }
+ if (res->count < 1) {
+ talloc_free(tmp_ctx);
+ return LDB_ERR_NO_SUCH_OBJECT;
+ }
+ *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);
+}
+
+
+
+/*
+ adds the given GUID to the given ldb_message. This value is added
+ for the given attr_name (may be either "objectGUID" or "parentGUID").
+ */
+int dsdb_msg_add_guid(struct ldb_message *msg,
+ struct GUID *guid,
+ const char *attr_name)
+{
+ int ret;
+ struct ldb_val v;
+ NTSTATUS status;
+ TALLOC_CTX *tmp_ctx = talloc_init("dsdb_msg_add_guid");
+
+ status = GUID_to_ndr_blob(guid, tmp_ctx, &v);
+ if (!NT_STATUS_IS_OK(status)) {
+ ret = LDB_ERR_OPERATIONS_ERROR;
+ goto done;
+ }
+
+ ret = ldb_msg_add_steal_value(msg, attr_name, &v);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(4,(__location__ ": Failed to add %s to the message\n",
+ attr_name));
+ goto done;
+ }
+
+ ret = LDB_SUCCESS;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+
+}
+
+
+/*
+ use a DN to find a SID
+ */
+int dsdb_find_sid_by_dn(struct ldb_context *ldb,
+ struct ldb_dn *dn, struct dom_sid *sid)
+{
+ int ret;
+ struct ldb_result *res;
+ const char *attrs[] = { "objectSID", NULL };
+ TALLOC_CTX *tmp_ctx = talloc_new(ldb);
+ struct dom_sid *s;
+
+ ZERO_STRUCTP(sid);
+
+ ret = dsdb_search_dn_with_deleted(ldb, tmp_ctx, &res, dn, attrs);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+ if (res->count < 1) {
+ talloc_free(tmp_ctx);
+ return LDB_ERR_NO_SUCH_OBJECT;
+ }
+ s = samdb_result_dom_sid(tmp_ctx, res->msgs[0], "objectSID");
+ if (s == NULL) {
+ talloc_free(tmp_ctx);
+ return LDB_ERR_NO_SUCH_OBJECT;
+ }
+ *sid = *s;
+ talloc_free(tmp_ctx);
+ return LDB_SUCCESS;
+}
+
+
+
+/*
+ load a repsFromTo blob list for a given partition GUID
+ attr must be "repsFrom" or "repsTo"
+ */
+WERROR dsdb_loadreps(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx, struct ldb_dn *dn,
+ const char *attr, struct repsFromToBlob **r, uint32_t *count)
+{
+ const char *attrs[] = { attr, NULL };
+ struct ldb_result *res = NULL;
+ TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
+ int i;
+ struct ldb_message_element *el;
+
+ *r = NULL;
+ *count = 0;
+
+ if (ldb_search(sam_ctx, tmp_ctx, &res, dn, LDB_SCOPE_BASE, attrs, NULL) != LDB_SUCCESS ||
+ res->count < 1) {
+ DEBUG(0,("dsdb_loadreps: failed to read partition object\n"));
+ talloc_free(tmp_ctx);
+ return WERR_DS_DRA_INTERNAL_ERROR;
+ }
+
+ el = ldb_msg_find_element(res->msgs[0], attr);
+ if (el == NULL) {
+ /* it's OK to be empty */
+ talloc_free(tmp_ctx);
+ return WERR_OK;
+ }
+
+ *count = el->num_values;
+ *r = talloc_array(mem_ctx, struct repsFromToBlob, *count);
+ if (*r == NULL) {
+ talloc_free(tmp_ctx);
+ return WERR_DS_DRA_INTERNAL_ERROR;
+ }
+
+ for (i=0; i<(*count); i++) {
+ enum ndr_err_code ndr_err;
+ ndr_err = ndr_pull_struct_blob(&el->values[i],
+ mem_ctx, lp_iconv_convenience(ldb_get_opaque(sam_ctx, "loadparm")),
+ &(*r)[i],
+ (ndr_pull_flags_fn_t)ndr_pull_repsFromToBlob);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ talloc_free(tmp_ctx);
+ return WERR_DS_DRA_INTERNAL_ERROR;
+ }
+ }
+
+ talloc_free(tmp_ctx);
+
+ return WERR_OK;
+}
+
+/*
+ save the repsFromTo blob list for a given partition GUID
+ attr must be "repsFrom" or "repsTo"
+ */
+WERROR dsdb_savereps(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx, struct ldb_dn *dn,
+ const char *attr, struct repsFromToBlob *r, uint32_t count)
+{
+ TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
+ struct ldb_message *msg;
+ struct ldb_message_element *el;
+ int i;
+
+ msg = ldb_msg_new(tmp_ctx);
+ msg->dn = dn;
+ if (ldb_msg_add_empty(msg, attr, LDB_FLAG_MOD_REPLACE, &el) != LDB_SUCCESS) {
+ goto failed;
+ }
+
+ el->values = talloc_array(msg, struct ldb_val, count);
+ if (!el->values) {
+ goto failed;
+ }
+
+ for (i=0; i<count; i++) {
+ struct ldb_val v;
+ enum ndr_err_code ndr_err;
+
+ ndr_err = ndr_push_struct_blob(&v, tmp_ctx, lp_iconv_convenience(ldb_get_opaque(sam_ctx, "loadparm")),
+ &r[i],
+ (ndr_push_flags_fn_t)ndr_push_repsFromToBlob);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ goto failed;
+ }
+
+ el->num_values++;
+ el->values[i] = v;
+ }
+
+ if (ldb_modify(sam_ctx, msg) != LDB_SUCCESS) {
+ DEBUG(0,("Failed to store %s - %s\n", attr, ldb_errstring(sam_ctx)));
+ goto failed;
+ }
+
+ talloc_free(tmp_ctx);
+
+ return WERR_OK;
+
+failed:
+ talloc_free(tmp_ctx);
+ return WERR_DS_DRA_INTERNAL_ERROR;
+}
+
+
+/*
+ load the uSNHighest attribute from the @REPLCHANGED object for a
+ partition
+ */
+int dsdb_load_partition_usn(struct ldb_context *ldb, struct ldb_dn *dn, uint64_t *uSN)
+{
+ struct ldb_request *req;
+ int ret;
+ TALLOC_CTX *tmp_ctx = talloc_new(ldb);
+ struct dsdb_control_current_partition *p_ctrl;
+ struct ldb_result *res;
+
+ res = talloc_zero(tmp_ctx, struct ldb_result);
+ if (!res) {
+ talloc_free(tmp_ctx);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ret = ldb_build_search_req(&req, ldb, tmp_ctx,
+ ldb_dn_new(tmp_ctx, ldb, "@REPLCHANGED"),
+ LDB_SCOPE_BASE,
+ NULL, NULL,
+ NULL,
+ res, ldb_search_default_callback,
+ NULL);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+
+ p_ctrl = talloc(req, struct dsdb_control_current_partition);
+ if (p_ctrl == NULL) {
+ talloc_free(res);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ p_ctrl->version = DSDB_CONTROL_CURRENT_PARTITION_VERSION;
+ p_ctrl->dn = dn;
+
+
+ ret = ldb_request_add_control(req,
+ DSDB_CONTROL_CURRENT_PARTITION_OID,
+ false, p_ctrl);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+
+ /* Run the new request */
+ ret = ldb_request(ldb, req);
+
+ if (ret == LDB_SUCCESS) {
+ ret = ldb_wait(req->handle, LDB_WAIT_ALL);
+ }
+
+ if (ret == LDB_ERR_NO_SUCH_OBJECT) {
+ /* it hasn't been created yet, which means
+ an implicit value of zero */
+ *uSN = 0;
+ talloc_free(tmp_ctx);
+ return LDB_SUCCESS;
+ }
+
+ if (ret != LDB_SUCCESS) {
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+
+ if (res->count < 1) {
+ *uSN = 0;
+ } else {
+ *uSN = ldb_msg_find_attr_as_uint64(res->msgs[0], "uSNHighest", 0);
+ }
+
+ talloc_free(tmp_ctx);
+
+ return LDB_SUCCESS;
+}
+
+/*
+ save the uSNHighest attribute in the @REPLCHANGED object for a
+ partition
+ */
+int dsdb_save_partition_usn(struct ldb_context *ldb, struct ldb_dn *dn, uint64_t uSN)
+{
+ struct ldb_request *req;
+ struct ldb_message *msg;
+ struct dsdb_control_current_partition *p_ctrl;
+ int ret;
+
+ msg = ldb_msg_new(ldb);
+ if (msg == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ msg->dn = ldb_dn_new(msg, ldb, "@REPLCHANGED");
+ if (msg->dn == NULL) {
+ talloc_free(msg);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ret = ldb_msg_add_fmt(msg, "uSNHighest", "%llu", (unsigned long long)uSN);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(msg);
+ return ret;
+ }
+ msg->elements[0].flags = LDB_FLAG_MOD_REPLACE;
+
+
+ p_ctrl = talloc(msg, struct dsdb_control_current_partition);
+ if (p_ctrl == NULL) {
+ talloc_free(msg);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ p_ctrl->version = DSDB_CONTROL_CURRENT_PARTITION_VERSION;
+ p_ctrl->dn = dn;
+
+ ret = ldb_build_mod_req(&req, ldb, msg,
+ msg,
+ NULL,
+ NULL, ldb_op_default_callback,
+ NULL);
+again:
+ if (ret != LDB_SUCCESS) {
+ talloc_free(msg);
+ return ret;
+ }
+
+ ret = ldb_request_add_control(req,
+ DSDB_CONTROL_CURRENT_PARTITION_OID,
+ false, p_ctrl);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(msg);
+ return ret;
+ }
+
+ /* Run the new request */
+ ret = ldb_request(ldb, req);
+
+ if (ret == LDB_SUCCESS) {
+ ret = ldb_wait(req->handle, LDB_WAIT_ALL);
+ }
+ if (ret == LDB_ERR_NO_SUCH_OBJECT) {
+ ret = ldb_build_add_req(&req, ldb, msg,
+ msg,
+ NULL,
+ NULL, ldb_op_default_callback,
+ NULL);
+ goto again;
+ }
+
+ talloc_free(msg);
+
+ return ret;
+}
+
+int drsuapi_DsReplicaCursor2_compare(const struct drsuapi_DsReplicaCursor2 *c1,
+ const struct drsuapi_DsReplicaCursor2 *c2)
+{
+ 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
+
+ TODO: This should take a sam_ctx, and lookup the right object (with
+ a cache)
+*/
+bool samdb_rodc(struct loadparm_context *lp_ctx)
+{
+ return lp_parm_bool(lp_ctx, NULL, "repl", "RODC", false);
+}
+
+
+/*
+ return NTDS options flags. See MS-ADTS 7.1.1.2.2.1.2.1.1
+
+ flags are DS_NTDS_OPTION_*
+*/
+int samdb_ntds_options(struct ldb_context *ldb, uint32_t *options)
+{
+ TALLOC_CTX *tmp_ctx;
+ const char *attrs[] = { "options", NULL };
+ int ret;
+ struct ldb_result *res;
+
+ tmp_ctx = talloc_new(ldb);
+ if (tmp_ctx == NULL) {
+ goto failed;
+ }
+
+ ret = ldb_search(ldb, tmp_ctx, &res, samdb_ntds_settings_dn(ldb), LDB_SCOPE_BASE, attrs, NULL);
+ if (ret) {
+ goto failed;
+ }
+
+ if (res->count != 1) {
+ goto failed;
+ }
+
+ *options = samdb_result_uint(res->msgs[0], "options", 0);
+
+ talloc_free(tmp_ctx);
+
+ return LDB_SUCCESS;
+
+failed:
+ DEBUG(1,("Failed to find our own NTDS Settings objectGUID in the ldb!\n"));
+ talloc_free(tmp_ctx);
+ return LDB_ERR_NO_SUCH_OBJECT;
+}
+
+/*
+ * Function which generates a "lDAPDisplayName" attribute from a "CN" one.
+ * Algorithm implemented according to MS-ADTS 3.1.1.2.3.4
+ */
+const char *samdb_cn_to_lDAPDisplayName(TALLOC_CTX *mem_ctx, const char *cn)
+{
+ char **tokens, *ret;
+ size_t i;
+
+ tokens = str_list_make(mem_ctx, cn, " -_");
+ if (tokens == NULL)
+ return NULL;
+
+ /* "tolower()" and "toupper()" should also work properly on 0x00 */
+ tokens[0][0] = tolower(tokens[0][0]);
+ for (i = 1; i < str_list_length((const char **)tokens); i++)
+ tokens[i][0] = toupper(tokens[i][0]);
+
+ ret = talloc_strdup(mem_ctx, tokens[0]);
+ for (i = 1; i < str_list_length((const char **)tokens); i++)
+ ret = talloc_asprintf_append_buffer(ret, "%s", tokens[i]);
+
+ talloc_free(tokens);
+
+ return ret;
+}
+
+/*
+ return domain functional level
+ returns DS_DOMAIN_FUNCTION_*
+ */
+int dsdb_functional_level(struct ldb_context *ldb)
+{
+ int *domainFunctionality =
+ talloc_get_type(ldb_get_opaque(ldb, "domainFunctionality"), int);
+ if (!domainFunctionality) {
+ DEBUG(0,(__location__ ": WARNING: domainFunctionality not setup\n"));
+ return DS_DOMAIN_FUNCTION_2000;
+ }
+ 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;
+}