summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Bartlett <abartlet@samba.org>2022-12-05 22:21:29 +1300
committerJule Anger <janger@samba.org>2023-02-03 09:35:08 +0000
commit4413c277ef09dfba6d9d69bfa2886402bcc5c369 (patch)
tree559720d565da3cd80bfd1688624db8cf6a66bd9d
parent24adeb3ad11c693aefbbea25570527cc87cd977d (diff)
downloadsamba-4413c277ef09dfba6d9d69bfa2886402bcc5c369.tar.gz
s4-dsdb: Make dsdb_find_nc_root() first try and use DSDB_CONTROL_CURRENT_PARTITION_OID
This allows lookup of a DN with a GUID only or GUID and string, possibly not yet in the database, yet still getting the correct result. BUG: https://bugzilla.samba.org/show_bug.cgi?id=10635 Signed-off-by: Andrew Bartlett <abartlet@samba.org> Reviewed-by: Stefan Metzmacher <metze@samba.org> (cherry picked from commit d0444be4b74bdad6a731bc5fcf86da6142b03539)
-rw-r--r--selftest/knownfail.d/dsdb_get_nc_root10
-rw-r--r--source4/dsdb/common/util.c237
2 files changed, 234 insertions, 13 deletions
diff --git a/selftest/knownfail.d/dsdb_get_nc_root b/selftest/knownfail.d/dsdb_get_nc_root
deleted file mode 100644
index 0a18996aa70..00000000000
--- a/selftest/knownfail.d/dsdb_get_nc_root
+++ /dev/null
@@ -1,10 +0,0 @@
-^samba.tests.dsdb.samba.tests.dsdb.DsdbNCRootTests.test_dsdb_dn_nc_root_admin_sid
-^samba.tests.dsdb.samba.tests.dsdb.DsdbNCRootTests.test_dsdb_dn_nc_root_guid
-^samba.tests.dsdb.samba.tests.dsdb.DsdbNCRootTests.test_dsdb_dn_nc_root_misleading_to_existing_guid
-^samba.tests.dsdb.samba.tests.dsdb.DsdbNCRootTests.test_dsdb_dn_nc_root_misleading_to_noexisting_guid
-^samba.tests.dsdb.samba.tests.dsdb.DsdbNCRootTests.test_dsdb_dn_nc_root_sid
-^samba.tests.dsdb.samba.tests.dsdb.DsdbRemoteNCRootTests.test_dsdb_dn_nc_root_admin_sid
-^samba.tests.dsdb.samba.tests.dsdb.DsdbRemoteNCRootTests.test_dsdb_dn_nc_root_guid
-^samba.tests.dsdb.samba.tests.dsdb.DsdbRemoteNCRootTests.test_dsdb_dn_nc_root_misleading_to_existing_guid
-^samba.tests.dsdb.samba.tests.dsdb.DsdbRemoteNCRootTests.test_dsdb_dn_nc_root_misleading_to_noexisting_guid
-^samba.tests.dsdb.samba.tests.dsdb.DsdbRemoteNCRootTests.test_dsdb_dn_nc_root_sid
diff --git a/source4/dsdb/common/util.c b/source4/dsdb/common/util.c
index be0a2cd4a33..97444fc1d3e 100644
--- a/source4/dsdb/common/util.c
+++ b/source4/dsdb/common/util.c
@@ -4124,10 +4124,12 @@ static int dsdb_dn_compare_ptrs(struct ldb_dn **dn1, struct ldb_dn **dn2)
}
/*
- find a NC root given a DN within the NC
+ find a NC root given a DN within the NC by reading the rootDSE namingContexts
*/
-int dsdb_find_nc_root(struct ldb_context *samdb, TALLOC_CTX *mem_ctx, struct ldb_dn *dn,
- struct ldb_dn **nc_root)
+static int dsdb_find_nc_root_string_based(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;
@@ -4212,6 +4214,235 @@ int dsdb_find_nc_root(struct ldb_context *samdb, TALLOC_CTX *mem_ctx, struct ldb
return ldb_error(samdb, LDB_ERR_NO_SUCH_OBJECT, __func__);
}
+struct dsdb_get_partition_and_dn {
+ TALLOC_CTX *mem_ctx;
+ unsigned int count;
+ struct ldb_dn *dn;
+ struct ldb_dn *partition_dn;
+ bool want_partition_dn;
+};
+
+static int dsdb_get_partition_and_dn(struct ldb_request *req,
+ struct ldb_reply *ares)
+{
+ int ret;
+ struct dsdb_get_partition_and_dn *context = req->context;
+ struct ldb_control *partition_ctrl = NULL;
+ struct dsdb_control_current_partition *partition = NULL;
+
+ if (!ares) {
+ return ldb_request_done(req, LDB_ERR_OPERATIONS_ERROR);
+ }
+ if (ares->error != LDB_SUCCESS
+ && ares->error != LDB_ERR_NO_SUCH_OBJECT) {
+ return ldb_request_done(req, ares->error);
+ }
+
+ switch (ares->type) {
+ case LDB_REPLY_ENTRY:
+ if (context->count != 0) {
+ return ldb_request_done(req,
+ LDB_ERR_CONSTRAINT_VIOLATION);
+ }
+ context->count++;
+
+ context->dn = talloc_steal(context->mem_ctx,
+ ares->message->dn);
+ break;
+
+ case LDB_REPLY_REFERRAL:
+ talloc_free(ares);
+ return ldb_request_done(req, LDB_SUCCESS);
+
+ case LDB_REPLY_DONE:
+ partition_ctrl
+ = ldb_reply_get_control(ares,
+ DSDB_CONTROL_CURRENT_PARTITION_OID);
+ if (!context->want_partition_dn ||
+ partition_ctrl == NULL) {
+ ret = ares->error;
+ talloc_free(ares);
+
+ return ldb_request_done(req, ret);
+ }
+
+ partition
+ = talloc_get_type_abort(partition_ctrl->data,
+ struct dsdb_control_current_partition);
+ context->partition_dn
+ = ldb_dn_copy(context->mem_ctx, partition->dn);
+ if (context->partition_dn == NULL) {
+ return ldb_request_done(req,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+
+ ret = ares->error;
+ talloc_free(ares);
+
+ return ldb_request_done(req, ret);
+ }
+
+ talloc_free(ares);
+ return LDB_SUCCESS;
+}
+
+/*
+ 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)
+{
+ TALLOC_CTX *tmp_ctx;
+ int ret;
+ struct ldb_request *req;
+ struct ldb_result *res;
+ struct ldb_dn *search_dn = dn;
+ static const char * attrs[] = { NULL };
+ bool has_extended = ldb_dn_has_extended(dn);
+ bool has_normal_components = ldb_dn_get_comp_num(dn) >= 1;
+ struct dsdb_get_partition_and_dn context = {
+ .mem_ctx = mem_ctx,
+ .want_partition_dn = nc_root != NULL
+ };
+
+ if (!has_extended && !has_normal_components) {
+ return ldb_error(samdb, LDB_ERR_NO_SUCH_OBJECT,
+ "Request for NC root for rootDSE (\"\") deined.");
+ }
+
+ tmp_ctx = talloc_new(samdb);
+ if (tmp_ctx == NULL) {
+ return ldb_oom(samdb);
+ }
+
+ res = talloc_zero(tmp_ctx, struct ldb_result);
+ if (res == NULL) {
+ talloc_free(tmp_ctx);
+ return ldb_oom(samdb);
+ }
+
+ if (has_extended && has_normal_components) {
+ bool minimise_ok;
+ search_dn = ldb_dn_copy(tmp_ctx, dn);
+ if (search_dn == NULL) {
+ talloc_free(tmp_ctx);
+ return ldb_oom(samdb);
+ }
+ minimise_ok = ldb_dn_minimise(search_dn);
+ if (!minimise_ok) {
+ talloc_free(tmp_ctx);
+ return ldb_operr(samdb);
+ }
+ }
+
+ ret = ldb_build_search_req(&req, samdb, tmp_ctx,
+ search_dn,
+ LDB_SCOPE_BASE,
+ NULL,
+ attrs,
+ NULL,
+ &context,
+ dsdb_get_partition_and_dn,
+ NULL);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+
+ ret = ldb_request_add_control(req,
+ DSDB_CONTROL_CURRENT_PARTITION_OID,
+ false, NULL);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+
+ ret = dsdb_request_add_controls(req,
+ DSDB_SEARCH_SHOW_RECYCLED|
+ DSDB_SEARCH_SHOW_DELETED|
+ DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+
+ ret = ldb_request(samdb, req);
+ if (ret == LDB_SUCCESS) {
+ ret = ldb_wait(req->handle, LDB_WAIT_ALL);
+ }
+
+ /*
+ * This could be a new DN, not in the DB, which is OK. If we
+ * don't need the normalised DN, we can continue.
+ *
+ * We may be told the partition it would be in in the search
+ * reply control, or if not we can do a string-based match.
+ */
+
+ if (ret == LDB_ERR_NO_SUCH_OBJECT) {
+ ret = LDB_SUCCESS;
+ ldb_reset_err_string(samdb);
+ } else if (ret != LDB_SUCCESS) {
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+
+
+ /*
+ * If the user did not need to find the nc_root,
+ * we are done
+ */
+ if (nc_root == NULL) {
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+
+ /*
+ * When we are working locally, both for the case were
+ * we find the DN, and the case where we fail, we get
+ * back via controls the partition it was in or should
+ * have been in, to return to the client
+ */
+ if (context.partition_dn != NULL) {
+ (*nc_root) = context.partition_dn;
+
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+
+ /*
+ * This is a remote operation, which is a little harder as we
+ * have a work out the nc_root from the list of NCs. If we did
+ * at least resolve the DN to a string, get that now, it makes
+ * the string-based match below possible for a GUID-based
+ * input over remote LDAP.
+ */
+ if (context.dn) {
+ dn = context.dn;
+ } else if (has_extended && !has_normal_components) {
+ ldb_asprintf_errstring(samdb,
+ "Cannot determine NC root "
+ "for a not-found bare extended DN %s.",
+ ldb_dn_get_extended_linearized(tmp_ctx, dn, 1));
+ talloc_free(tmp_ctx);
+ return LDB_ERR_NO_SUCH_OBJECT;
+ }
+
+ /*
+ * Either we are working aginast a remote LDAP
+ * server or the object doesn't exist locally.
+ *
+ * This means any GUID that was present in the DN
+ * therefore could not be evaluated, so do a
+ * string-based match instead.
+ */
+ talloc_free(tmp_ctx);
+ return dsdb_find_nc_root_string_based(samdb,
+ mem_ctx,
+ dn,
+ nc_root);
+}
+
/*
find the deleted objects DN for any object, by looking for the NC