summaryrefslogtreecommitdiff
path: root/source4/dsdb/samdb/ldb_modules
diff options
context:
space:
mode:
Diffstat (limited to 'source4/dsdb/samdb/ldb_modules')
-rw-r--r--source4/dsdb/samdb/ldb_modules/descriptor.c2
-rw-r--r--source4/dsdb/samdb/ldb_modules/dirsync.c7
-rw-r--r--source4/dsdb/samdb/ldb_modules/extended_dn_in.c37
-rw-r--r--source4/dsdb/samdb/ldb_modules/instancetype.c3
-rw-r--r--source4/dsdb/samdb/ldb_modules/objectclass.c54
-rw-r--r--source4/dsdb/samdb/ldb_modules/operational.c2
-rw-r--r--source4/dsdb/samdb/ldb_modules/partition_metadata.c34
-rw-r--r--source4/dsdb/samdb/ldb_modules/password_hash.c1
-rw-r--r--source4/dsdb/samdb/ldb_modules/repl_meta_data.c267
-rw-r--r--source4/dsdb/samdb/ldb_modules/rootdse.c110
-rw-r--r--source4/dsdb/samdb/ldb_modules/samldb.c29
-rw-r--r--source4/dsdb/samdb/ldb_modules/schema_data.c16
-rw-r--r--source4/dsdb/samdb/ldb_modules/schema_load.c207
-rw-r--r--source4/dsdb/samdb/ldb_modules/secrets_tdb_sync.c6
-rw-r--r--source4/dsdb/samdb/ldb_modules/simple_ldap_map.c2
-rw-r--r--source4/dsdb/samdb/ldb_modules/update_keytab.c11
-rw-r--r--source4/dsdb/samdb/ldb_modules/util.c13
17 files changed, 547 insertions, 254 deletions
diff --git a/source4/dsdb/samdb/ldb_modules/descriptor.c b/source4/dsdb/samdb/ldb_modules/descriptor.c
index ceac8db7de2..cc0a9c25ebb 100644
--- a/source4/dsdb/samdb/ldb_modules/descriptor.c
+++ b/source4/dsdb/samdb/ldb_modules/descriptor.c
@@ -501,7 +501,7 @@ static int descriptor_search_callback(struct ldb_request *req, struct ldb_reply
struct ldb_val *sd_val = NULL;
struct ldb_message_element *sd_el;
DATA_BLOB *show_sd;
- int ret;
+ int ret = LDB_SUCCESS;
ac = talloc_get_type(req->context, struct descriptor_context);
diff --git a/source4/dsdb/samdb/ldb_modules/dirsync.c b/source4/dsdb/samdb/ldb_modules/dirsync.c
index e7699485083..c93189ef716 100644
--- a/source4/dsdb/samdb/ldb_modules/dirsync.c
+++ b/source4/dsdb/samdb/ldb_modules/dirsync.c
@@ -70,7 +70,7 @@ static int dirsync_filter_entry(struct ldb_request *req,
bool referral)
{
struct ldb_context *ldb;
- uint64_t val;
+ uint64_t val = 0;
enum ndr_err_code ndr_err;
uint32_t n;
int i;
@@ -336,10 +336,13 @@ skip:
* if not we remove the attribute.
*/
for (i = msg->num_elements - 1; i >= 0; i--) {
+ const char *ldapattrname;
+
el = &(msg->elements[i]);
+ ldapattrname = el->name;
+
attr = dsdb_attribute_by_lDAPDisplayName(dsc->schema,
el->name);
- const char *ldapattrname = el->name;
keep = false;
if (attr->linkID & 1) {
diff --git a/source4/dsdb/samdb/ldb_modules/extended_dn_in.c b/source4/dsdb/samdb/ldb_modules/extended_dn_in.c
index 034d22a2ca3..213b2c2c87b 100644
--- a/source4/dsdb/samdb/ldb_modules/extended_dn_in.c
+++ b/source4/dsdb/samdb/ldb_modules/extended_dn_in.c
@@ -315,6 +315,7 @@ struct extended_dn_filter_ctx {
struct ldb_module *module;
struct ldb_request *req;
struct dsdb_schema *schema;
+ uint32_t dsdb_flags;
};
/*
@@ -411,10 +412,7 @@ static int extended_dn_filter_callback(struct ldb_parse_tree *tree, void *privat
return LDB_SUCCESS;
}
- dsdb_flags = DSDB_FLAG_NEXT_MODULE |
- DSDB_FLAG_AS_SYSTEM |
- DSDB_SEARCH_SHOW_RECYCLED |
- DSDB_SEARCH_SHOW_EXTENDED_DN;
+ dsdb_flags = filter_ctx->dsdb_flags | DSDB_FLAG_NEXT_MODULE;
if (guid_val) {
expression = talloc_asprintf(filter_ctx, "objectGUID=%s", ldb_binary_encode(filter_ctx, *guid_val));
@@ -475,7 +473,9 @@ static int extended_dn_filter_callback(struct ldb_parse_tree *tree, void *privat
fix the parse tree to change any extended DN components to their
caconical form
*/
-static int extended_dn_fix_filter(struct ldb_module *module, struct ldb_request *req)
+static int extended_dn_fix_filter(struct ldb_module *module,
+ struct ldb_request *req,
+ uint32_t default_dsdb_flags)
{
struct extended_dn_filter_ctx *filter_ctx;
int ret;
@@ -493,6 +493,7 @@ static int extended_dn_fix_filter(struct ldb_module *module, struct ldb_request
filter_ctx->module = module;
filter_ctx->req = req;
filter_ctx->schema = dsdb_get_schema(ldb_module_get_ctx(module), filter_ctx);
+ filter_ctx->dsdb_flags= default_dsdb_flags;
ret = ldb_parse_tree_walk(req->op.search.tree, extended_dn_filter_callback, filter_ctx);
if (ret != LDB_SUCCESS) {
@@ -541,10 +542,20 @@ static int extended_dn_in_fix(struct ldb_module *module, struct ldb_request *req
static const char *no_attr[] = {
NULL
};
- bool all_partitions = false;
+ uint32_t dsdb_flags = DSDB_FLAG_AS_SYSTEM | DSDB_SEARCH_SHOW_EXTENDED_DN;
+
+ if (ldb_request_get_control(req, LDB_CONTROL_SHOW_DELETED_OID)) {
+ dsdb_flags |= DSDB_SEARCH_SHOW_DELETED;
+ }
+ if (ldb_request_get_control(req, LDB_CONTROL_SHOW_RECYCLED_OID)) {
+ dsdb_flags |= DSDB_SEARCH_SHOW_RECYCLED;
+ }
+ if (ldb_request_get_control(req, DSDB_CONTROL_DBCHECK)) {
+ dsdb_flags |= DSDB_SEARCH_SHOW_RECYCLED;
+ }
if (req->operation == LDB_SEARCH) {
- ret = extended_dn_fix_filter(module, req);
+ ret = extended_dn_fix_filter(module, req, dsdb_flags);
if (ret != LDB_SUCCESS) {
return ret;
}
@@ -556,7 +567,6 @@ static int extended_dn_in_fix(struct ldb_module *module, struct ldb_request *req
} else {
/* It looks like we need to map the DN */
const struct ldb_val *sid_val, *guid_val, *wkguid_val;
- uint32_t dsdb_flags = 0;
if (!ldb_dn_match_allowed(dn, req)) {
return ldb_error(ldb_module_get_ctx(module),
@@ -573,7 +583,7 @@ static int extended_dn_in_fix(struct ldb_module *module, struct ldb_request *req
ForeignSecurityPrinciples due to provision errors
*/
if (guid_val) {
- all_partitions = true;
+ dsdb_flags |= DSDB_SEARCH_SEARCH_ALL_PARTITIONS;
base_dn = NULL;
base_dn_filter = talloc_asprintf(req, "(objectGUID=%s)",
ldb_binary_encode(req, *guid_val));
@@ -584,7 +594,7 @@ static int extended_dn_in_fix(struct ldb_module *module, struct ldb_request *req
base_dn_attrs = no_attr;
} else if (sid_val) {
- all_partitions = true;
+ dsdb_flags |= DSDB_SEARCH_SEARCH_ALL_PARTITIONS;
base_dn = NULL;
base_dn_filter = talloc_asprintf(req, "(objectSid=%s)",
ldb_binary_encode(req, *sid_val));
@@ -661,13 +671,6 @@ static int extended_dn_in_fix(struct ldb_module *module, struct ldb_request *req
return ldb_operr(ldb_module_get_ctx(module));
}
- dsdb_flags = DSDB_FLAG_AS_SYSTEM |
- DSDB_SEARCH_SHOW_RECYCLED |
- DSDB_SEARCH_SHOW_EXTENDED_DN;
- if (all_partitions) {
- dsdb_flags |= DSDB_SEARCH_SEARCH_ALL_PARTITIONS;
- }
-
ret = dsdb_request_add_controls(down_req, dsdb_flags);
if (ret != LDB_SUCCESS) {
return ret;
diff --git a/source4/dsdb/samdb/ldb_modules/instancetype.c b/source4/dsdb/samdb/ldb_modules/instancetype.c
index 7bf95f3180c..c35f4b6a262 100644
--- a/source4/dsdb/samdb/ldb_modules/instancetype.c
+++ b/source4/dsdb/samdb/ldb_modules/instancetype.c
@@ -80,8 +80,7 @@ static int instancetype_add(struct ldb_module *module, struct ldb_request *req)
* "TYPE_WRITE" flag in order to succeed,
* unless this NC is not instantiated
*/
- if (!(instanceType & INSTANCE_TYPE_UNINSTANT) &&
- !(instanceType & INSTANCE_TYPE_WRITE)) {
+ if (!(instanceType & INSTANCE_TYPE_WRITE)) {
ldb_set_errstring(ldb, "instancetype: if TYPE_IS_NC_HEAD was set, then also TYPE_WRITE is requested!");
return LDB_ERR_UNWILLING_TO_PERFORM;
}
diff --git a/source4/dsdb/samdb/ldb_modules/objectclass.c b/source4/dsdb/samdb/ldb_modules/objectclass.c
index f6f7338d3cf..8c361e97231 100644
--- a/source4/dsdb/samdb/ldb_modules/objectclass.c
+++ b/source4/dsdb/samdb/ldb_modules/objectclass.c
@@ -186,36 +186,6 @@ static int get_search_callback(struct ldb_request *req, struct ldb_reply *ares)
return LDB_SUCCESS;
}
-static int oc_op_callback(struct ldb_request *req, struct ldb_reply *ares)
-{
- struct oc_context *ac;
-
- ac = talloc_get_type(req->context, struct oc_context);
-
- if (!ares) {
- return ldb_module_done(ac->req, NULL, NULL,
- LDB_ERR_OPERATIONS_ERROR);
- }
-
- if (ares->type == LDB_REPLY_REFERRAL) {
- return ldb_module_send_referral(ac->req, ares->referral);
- }
-
- if (ares->error != LDB_SUCCESS) {
- return ldb_module_done(ac->req, ares->controls,
- ares->response, ares->error);
- }
-
- if (ares->type != LDB_REPLY_DONE) {
- talloc_free(ares);
- return ldb_module_done(ac->req, NULL, NULL,
- LDB_ERR_OPERATIONS_ERROR);
- }
-
- return ldb_module_done(ac->req, ares->controls,
- ares->response, ares->error);
-}
-
/* Fix up the DN to be in the standard form, taking particular care to match the parent DN
This should mean that if the parent is:
@@ -659,7 +629,7 @@ static int objectclass_do_add(struct oc_context *ac)
ret = ldb_build_add_req(&add_req, ldb, ac,
msg,
ac->req->controls,
- ac, oc_op_callback,
+ ac->req, dsdb_next_callback,
ac->req);
LDB_REQ_SET_LOCATION(add_req);
if (ret != LDB_SUCCESS) {
@@ -745,11 +715,19 @@ static int objectclass_modify(struct ldb_module *module, struct ldb_request *req
talloc_free(nc_root);
}
- ret = ldb_build_mod_req(&down_req, ldb, ac,
- msg,
- req->controls, ac,
- oc_changes ? oc_modify_callback : oc_op_callback,
- req);
+ if (oc_changes) {
+ ret = ldb_build_mod_req(&down_req, ldb, ac,
+ msg,
+ req->controls, ac,
+ oc_modify_callback,
+ req);
+ } else {
+ ret = ldb_build_mod_req(&down_req, ldb, ac,
+ msg,
+ req->controls, req,
+ dsdb_next_callback,
+ req);
+ }
LDB_REQ_SET_LOCATION(down_req);
if (ret != LDB_SUCCESS) {
return ret;
@@ -978,7 +956,7 @@ static int objectclass_do_mod(struct oc_context *ac)
ret = ldb_build_mod_req(&mod_req, ldb, ac,
msg,
ac->req->controls,
- ac, oc_op_callback,
+ ac->req, dsdb_next_callback,
ac->req);
LDB_REQ_SET_LOCATION(mod_req);
if (ret != LDB_SUCCESS) {
@@ -1216,7 +1194,7 @@ static int objectclass_do_rename2(struct oc_context *ac)
ret = ldb_build_rename_req(&rename_req, ldb, ac,
ac->req->op.rename.olddn, fixed_dn,
ac->req->controls,
- ac, oc_op_callback,
+ ac->req, dsdb_next_callback,
ac->req);
LDB_REQ_SET_LOCATION(rename_req);
if (ret != LDB_SUCCESS) {
diff --git a/source4/dsdb/samdb/ldb_modules/operational.c b/source4/dsdb/samdb/ldb_modules/operational.c
index 9337faacc4c..2ebefac1713 100644
--- a/source4/dsdb/samdb/ldb_modules/operational.c
+++ b/source4/dsdb/samdb/ldb_modules/operational.c
@@ -131,7 +131,7 @@ static int construct_token_groups(struct ldb_module *module,
struct ldb_message *msg, enum ldb_scope scope,
struct ldb_request *parent)
{
- struct ldb_context *ldb = ldb_module_get_ctx(module);;
+ struct ldb_context *ldb = ldb_module_get_ctx(module);
TALLOC_CTX *tmp_ctx = talloc_new(msg);
unsigned int i;
int ret;
diff --git a/source4/dsdb/samdb/ldb_modules/partition_metadata.c b/source4/dsdb/samdb/ldb_modules/partition_metadata.c
index 5826ac2ee11..b3b57447ecd 100644
--- a/source4/dsdb/samdb/ldb_modules/partition_metadata.c
+++ b/source4/dsdb/samdb/ldb_modules/partition_metadata.c
@@ -129,9 +129,13 @@ static int partition_metadata_set_uint64(struct ldb_module *module,
}
if (tdb_store(tdb, tdb_key, tdb_data, tdb_flag) != 0) {
+ int ret;
+ char *error_string = talloc_asprintf(tmp_ctx, "%s: tdb_store of key %s failed: %s",
+ tdb_name(tdb), key, tdb_errorstr(tdb));
+ ret = ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
+ error_string);
talloc_free(tmp_ctx);
- return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
- tdb_errorstr(tdb));
+ return ret;
}
talloc_free(tmp_ctx);
@@ -199,13 +203,13 @@ static int partition_metadata_open(struct ldb_module *module, bool create)
}
sam_name = (const char *)ldb_get_opaque(ldb, "ldb_url");
- if (strncmp("tdb://", sam_name, 6) == 0) {
- sam_name += 6;
- }
if (!sam_name) {
talloc_free(tmp_ctx);
return ldb_operr(ldb);
}
+ if (strncmp("tdb://", sam_name, 6) == 0) {
+ sam_name += 6;
+ }
filename = talloc_asprintf(tmp_ctx, "%s.d/metadata.tdb", sam_name);
if (!filename) {
talloc_free(tmp_ctx);
@@ -242,9 +246,14 @@ static int partition_metadata_open(struct ldb_module *module, bool create)
if (data->metadata->db == NULL) {
talloc_free(tmp_ctx);
if (create) {
- ldb_debug(ldb, LDB_DEBUG_ERROR,
- "partition_metadata: Unable to create %s",
- filename);
+ ldb_asprintf_errstring(ldb, "partition_metadata: Unable to create %s: %s",
+ filename, strerror(errno));
+ } else {
+ ldb_asprintf_errstring(ldb, "partition_metadata: Unable to open %s: %s",
+ filename, strerror(errno));
+ }
+ if (errno == EACCES || errno == EPERM) {
+ return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
}
return LDB_ERR_OPERATIONS_ERROR;
}
@@ -295,9 +304,16 @@ int partition_metadata_init(struct ldb_module *module)
}
/* metadata.tdb does not exist, create it */
- DEBUG(2, ("partition_metadata: Migrating partition metadata\n"));
+ DEBUG(2, ("partition_metadata: Migrating partition metadata: "
+ "open of metadata.tdb gave: %s\n",
+ ldb_errstring(ldb_module_get_ctx(module))));
ret = partition_metadata_open(module, true);
if (ret != LDB_SUCCESS) {
+ ldb_asprintf_errstring(ldb_module_get_ctx(module),
+ "partition_metadata: "
+ "Migrating partition metadata: "
+ "create of metadata.tdb gave: %s\n",
+ ldb_errstring(ldb_module_get_ctx(module)));
talloc_free(data->metadata);
data->metadata = NULL;
goto end;
diff --git a/source4/dsdb/samdb/ldb_modules/password_hash.c b/source4/dsdb/samdb/ldb_modules/password_hash.c
index 79741694990..78311c6c13e 100644
--- a/source4/dsdb/samdb/ldb_modules/password_hash.c
+++ b/source4/dsdb/samdb/ldb_modules/password_hash.c
@@ -2159,7 +2159,6 @@ static int setup_io(struct ph_context *ac,
/* Some operations below require kerberos contexts */
if (smb_krb5_init_context(ac,
- ldb_get_event_context(ldb),
(struct loadparm_context *)ldb_get_opaque(ldb, "loadparm"),
&io->smb_krb5_context) != 0) {
return ldb_operr(ldb);
diff --git a/source4/dsdb/samdb/ldb_modules/repl_meta_data.c b/source4/dsdb/samdb/ldb_modules/repl_meta_data.c
index 91a5d9233c5..4c5ced42309 100644
--- a/source4/dsdb/samdb/ldb_modules/repl_meta_data.c
+++ b/source4/dsdb/samdb/ldb_modules/repl_meta_data.c
@@ -67,6 +67,7 @@ struct replmd_private {
uint64_t mod_usn;
uint64_t mod_usn_urgent;
} *ncs;
+ struct ldb_dn *schema_dn;
};
struct la_entry {
@@ -240,6 +241,8 @@ static int replmd_init(struct ldb_module *module)
}
ldb_module_set_private(module, replmd_private);
+ replmd_private->schema_dn = ldb_get_schema_basedn(ldb);
+
return ldb_next_init(module);
}
@@ -660,7 +663,15 @@ static int replmd_replPropertyMetaData1_attid_sort(const struct replPropertyMeta
const struct replPropertyMetaData1 *m2,
const uint32_t *rdn_attid)
{
- if (m1->attid == m2->attid) {
+ /*
+ * This assignment seems inoccous, but it is critical for the
+ * system, as we need to do the comparisons as a unsigned
+ * quantity, not signed (enums are signed integers)
+ */
+ uint32_t attid_1 = m1->attid;
+ uint32_t attid_2 = m2->attid;
+
+ if (attid_1 == attid_2) {
return 0;
}
@@ -669,7 +680,7 @@ static int replmd_replPropertyMetaData1_attid_sort(const struct replPropertyMeta
* so we need to return a value greater than zero
* which means m1 is greater than m2
*/
- if (m1->attid == *rdn_attid) {
+ if (attid_1 == *rdn_attid) {
return 1;
}
@@ -678,38 +689,78 @@ static int replmd_replPropertyMetaData1_attid_sort(const struct replPropertyMeta
* so we need to return a value less than zero
* which means m2 is greater than m1
*/
- if (m2->attid == *rdn_attid) {
+ if (attid_2 == *rdn_attid) {
return -1;
}
- return m1->attid > m2->attid ? 1 : -1;
+ /*
+ * See above regarding this being an unsigned comparison.
+ * Otherwise when the high bit is set on non-standard
+ * attributes, they would end up first, before objectClass
+ * (0).
+ */
+ return attid_1 > attid_2 ? 1 : -1;
}
-static int replmd_replPropertyMetaDataCtr1_sort(struct replPropertyMetaDataCtr1 *ctr1,
- const struct dsdb_schema *schema,
- struct ldb_dn *dn)
+static int replmd_replPropertyMetaDataCtr1_verify(struct ldb_context *ldb,
+ struct replPropertyMetaDataCtr1 *ctr1,
+ const struct dsdb_attribute *rdn_sa,
+ struct ldb_dn *dn)
+{
+ if (ctr1->count == 0) {
+ ldb_debug_set(ldb, LDB_DEBUG_FATAL,
+ "No elements found in replPropertyMetaData for %s!\n",
+ ldb_dn_get_linearized(dn));
+ return LDB_ERR_CONSTRAINT_VIOLATION;
+ }
+ if (ctr1->array[ctr1->count - 1].attid != rdn_sa->attributeID_id) {
+ ldb_debug_set(ldb, LDB_DEBUG_FATAL,
+ "No rDN found in replPropertyMetaData for %s!\n",
+ ldb_dn_get_linearized(dn));
+ return LDB_ERR_CONSTRAINT_VIOLATION;
+ }
+
+ /* the objectClass attribute is value 0x00000000, so must be first */
+ if (ctr1->array[0].attid != DRSUAPI_ATTID_objectClass) {
+ ldb_debug_set(ldb, LDB_DEBUG_FATAL,
+ "No objectClass found in replPropertyMetaData for %s!\n",
+ ldb_dn_get_linearized(dn));
+ return LDB_ERR_OBJECT_CLASS_VIOLATION;
+ }
+
+ return LDB_SUCCESS;
+}
+
+static int replmd_replPropertyMetaDataCtr1_sort_and_verify(struct ldb_context *ldb,
+ struct replPropertyMetaDataCtr1 *ctr1,
+ const struct dsdb_schema *schema,
+ struct ldb_dn *dn)
{
const char *rdn_name;
const struct dsdb_attribute *rdn_sa;
rdn_name = ldb_dn_get_rdn_name(dn);
if (!rdn_name) {
- DEBUG(0,(__location__ ": No rDN for %s?\n", ldb_dn_get_linearized(dn)));
- return LDB_ERR_OPERATIONS_ERROR;
+ ldb_debug_set(ldb, LDB_DEBUG_FATAL,
+ __location__ ": No rDN for %s?\n",
+ ldb_dn_get_linearized(dn));
+ return LDB_ERR_INVALID_DN_SYNTAX;
}
rdn_sa = dsdb_attribute_by_lDAPDisplayName(schema, rdn_name);
if (rdn_sa == NULL) {
- DEBUG(0,(__location__ ": No sa found for rDN %s for %s\n", rdn_name, ldb_dn_get_linearized(dn)));
- return LDB_ERR_OPERATIONS_ERROR;
+ ldb_debug_set(ldb, LDB_DEBUG_FATAL,
+ __location__ ": No sa found for rDN %s for %s\n",
+ rdn_name, ldb_dn_get_linearized(dn));
+ return LDB_ERR_UNDEFINED_ATTRIBUTE_TYPE;
}
DEBUG(6,("Sorting rpmd with attid exception %u rDN=%s DN=%s\n",
rdn_sa->attributeID_id, rdn_name, ldb_dn_get_linearized(dn)));
- LDB_TYPESAFE_QSORT(ctr1->array, ctr1->count, &rdn_sa->attributeID_id, replmd_replPropertyMetaData1_attid_sort);
-
- return LDB_SUCCESS;
+ LDB_TYPESAFE_QSORT(ctr1->array, ctr1->count, &rdn_sa->attributeID_id,
+ replmd_replPropertyMetaData1_attid_sort);
+ return replmd_replPropertyMetaDataCtr1_verify(ldb, ctr1, rdn_sa, dn);
}
static int replmd_ldb_message_element_attid_sort(const struct ldb_message_element *e1,
@@ -840,6 +891,8 @@ static int replmd_add(struct ldb_module *module, struct ldb_request *req)
bool remove_current_guid = false;
bool is_urgent = false;
struct ldb_message_element *objectclass_el;
+ struct replmd_private *replmd_private =
+ talloc_get_type_abort(ldb_module_get_private(module), struct replmd_private);
/* check if there's a show relax control (used by provision to say 'I know what I'm doing') */
control = ldb_request_get_control(req, LDB_CONTROL_RELAX_OID);
@@ -1025,8 +1078,9 @@ static int replmd_add(struct ldb_module *module, struct ldb_request *req)
/*
* sort meta data array, and move the rdn attribute entry to the end
*/
- ret = replmd_replPropertyMetaDataCtr1_sort(&nmd.ctr.ctr1, ac->schema, msg->dn);
+ ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &nmd.ctr.ctr1, ac->schema, msg->dn);
if (ret != LDB_SUCCESS) {
+ ldb_asprintf_errstring(ldb, "%s: error during direct ADD: %s", __func__, ldb_errstring(ldb));
talloc_free(ac);
return ret;
}
@@ -1080,7 +1134,17 @@ static int replmd_add(struct ldb_module *module, struct ldb_request *req)
*/
replmd_ldb_message_sort(msg, ac->schema);
+ /*
+ * Assert that we do have an objectClass
+ */
objectclass_el = ldb_msg_find_element(msg, "objectClass");
+ if (objectclass_el == NULL) {
+ ldb_asprintf_errstring(ldb, __location__
+ ": objectClass missing on %s\n",
+ ldb_dn_get_linearized(msg->dn));
+ talloc_free(ac);
+ return LDB_ERR_OBJECT_CLASS_VIOLATION;
+ }
is_urgent = replmd_check_urgent_objectclass(objectclass_el,
REPL_URGENT_ON_CREATE);
@@ -1120,7 +1184,7 @@ static int replmd_add(struct ldb_module *module, struct ldb_request *req)
if (control) {
control->critical = 0;
}
- if (ldb_dn_compare_base(ac->schema->base_dn, req->op.add.message->dn) != 0) {
+ if (ldb_dn_compare_base(replmd_private->schema_dn, req->op.add.message->dn) != 0) {
/* Update the usn in the SAMLDB_MSDS_INTID_OPAQUE opaque */
msds_intid_struct = (struct samldb_msds_intid_persistant *) ldb_get_opaque(ldb, SAMLDB_MSDS_INTID_OPAQUE);
@@ -1398,12 +1462,6 @@ static int replmd_update_rpmd(struct ldb_module *module,
return ret;
}
- objectclass_el = ldb_msg_find_element(res->msgs[0], "objectClass");
- if (is_urgent && replmd_check_urgent_objectclass(objectclass_el,
- situation)) {
- *is_urgent = true;
- }
-
db_seq = ldb_msg_find_attr_as_uint64(res->msgs[0], "uSNChanged", 0);
if (*seq_num <= db_seq) {
DEBUG(0,(__location__ ": changereplmetada control provided but max(local_usn)"\
@@ -1428,12 +1486,6 @@ static int replmd_update_rpmd(struct ldb_module *module,
return ret;
}
- objectclass_el = ldb_msg_find_element(res->msgs[0], "objectClass");
- if (is_urgent && replmd_check_urgent_objectclass(objectclass_el,
- situation)) {
- *is_urgent = true;
- }
-
omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
if (!omd_value) {
DEBUG(0,(__location__ ": Object %s does not have a replPropertyMetaData attribute\n",
@@ -1464,12 +1516,33 @@ static int replmd_update_rpmd(struct ldb_module *module,
return ret;
}
- if (is_urgent && !*is_urgent && (situation == REPL_URGENT_ON_UPDATE)) {
+ if (!*is_urgent && (situation == REPL_URGENT_ON_UPDATE)) {
*is_urgent = replmd_check_urgent_attribute(&msg->elements[i]);
}
}
}
+
+ /*
+ * Assert that we have an objectClass attribute - this is major
+ * corruption if we don't have this!
+ */
+ objectclass_el = ldb_msg_find_element(res->msgs[0], "objectClass");
+ if (objectclass_el != NULL) {
+ /*
+ * Now check if this objectClass means we need to do urgent replication
+ */
+ if (!*is_urgent && replmd_check_urgent_objectclass(objectclass_el,
+ situation)) {
+ *is_urgent = true;
+ }
+ } else if (!ldb_request_get_control(req, DSDB_CONTROL_DBCHECK)) {
+ ldb_asprintf_errstring(ldb, __location__
+ ": objectClass missing on %s\n",
+ ldb_dn_get_linearized(msg->dn));
+ return LDB_ERR_OBJECT_CLASS_VIOLATION;
+ }
+
/*
* replmd_update_rpmd_element has done an update if the
* seq_num is set
@@ -1504,8 +1577,9 @@ static int replmd_update_rpmd(struct ldb_module *module,
return LDB_ERR_OPERATIONS_ERROR;
}
- ret = replmd_replPropertyMetaDataCtr1_sort(&omd.ctr.ctr1, schema, msg->dn);
+ ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &omd.ctr.ctr1, schema, msg->dn);
if (ret != LDB_SUCCESS) {
+ ldb_asprintf_errstring(ldb, "%s: %s", __func__, ldb_errstring(ldb));
return ret;
}
@@ -2400,8 +2474,10 @@ static int replmd_modify(struct ldb_module *module, struct ldb_request *req)
int ret;
bool is_urgent = false, rodc = false;
unsigned int functional_level;
- const DATA_BLOB *guid_blob;
+ const struct ldb_message_element *guid_el = NULL;
struct ldb_control *sd_propagation_control;
+ struct replmd_private *replmd_private =
+ talloc_get_type(ldb_module_get_private(module), struct replmd_private);
/* do not manipulate our control entries */
if (ldb_dn_is_special(req->op.mod.message->dn)) {
@@ -2427,8 +2503,8 @@ static int replmd_modify(struct ldb_module *module, struct ldb_request *req)
ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_modify\n");
- guid_blob = ldb_msg_find_ldb_val(req->op.mod.message, "objectGUID");
- if ( guid_blob != NULL ) {
+ guid_el = ldb_msg_find_element(req->op.mod.message, "objectGUID");
+ if (guid_el != NULL) {
ldb_set_errstring(ldb,
"replmd_modify: it's not allowed to change the objectGUID!");
return LDB_ERR_CONSTRAINT_VIOLATION;
@@ -2540,7 +2616,7 @@ static int replmd_modify(struct ldb_module *module, struct ldb_request *req)
}
}
- if (!ldb_dn_compare_base(ac->schema->base_dn, msg->dn)) {
+ if (!ldb_dn_compare_base(replmd_private->schema_dn, msg->dn)) {
/* Update the usn in the SAMLDB_MSDS_INTID_OPAQUE opaque */
msds_intid_struct = (struct samldb_msds_intid_persistant *) ldb_get_opaque(ldb, SAMLDB_MSDS_INTID_OPAQUE);
if (msds_intid_struct) {
@@ -2922,6 +2998,20 @@ static int replmd_delete_internals(struct ldb_module *module, struct ldb_request
return ldb_next_request(module, req);
}
+ /*
+ * We have to allow dbcheck to remove an object that
+ * is beyond repair, and to do so totally. This could
+ * mean we we can get a partial object from the other
+ * DC, causing havoc, so dbcheck suggests
+ * re-replication first. dbcheck sets both DBCHECK
+ * and RELAX in this situation.
+ */
+ if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)
+ && ldb_request_get_control(req, DSDB_CONTROL_DBCHECK)) {
+ /* really, really remove it */
+ return ldb_next_request(module, req);
+ }
+
tmp_ctx = talloc_new(ldb);
if (!tmp_ctx) {
ldb_oom(ldb);
@@ -2965,17 +3055,25 @@ static int replmd_delete_internals(struct ldb_module *module, struct ldb_request
}
if (next_deletion_state == OBJECT_REMOVED) {
- struct auth_session_info *session_info =
- (struct auth_session_info *)ldb_get_opaque(ldb, "sessionInfo");
- if (security_session_user_level(session_info, NULL) != SECURITY_SYSTEM) {
- ldb_asprintf_errstring(ldb, "Refusing to delete deleted object %s",
- ldb_dn_get_linearized(old_msg->dn));
- return LDB_ERR_UNWILLING_TO_PERFORM;
+ /*
+ * We have to prevent objects being deleted, even if
+ * the administrator really wants them gone, as
+ * without the tombstone, we can get a partial object
+ * from the other DC, causing havoc.
+ *
+ * The only other valid case is when the 180 day
+ * timeout has expired, when relax is specified.
+ */
+ if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)) {
+ /* it is already deleted - really remove it this time */
+ talloc_free(tmp_ctx);
+ return ldb_next_request(module, req);
}
- /* it is already deleted - really remove it this time */
- talloc_free(tmp_ctx);
- return ldb_next_request(module, req);
+ ldb_asprintf_errstring(ldb, "Refusing to delete tombstone object %s. "
+ "This check is to prevent corruption of the replicated state.",
+ ldb_dn_get_linearized(old_msg->dn));
+ return LDB_ERR_UNWILLING_TO_PERFORM;
}
rdn_name = ldb_dn_get_rdn_name(old_dn);
@@ -3214,8 +3312,13 @@ static int replmd_delete_internals(struct ldb_module *module, struct ldb_request
*/
continue;
}
- if (!sa->linkID && ldb_attr_in_list(preserved_attrs, el->name)) {
- continue;
+ if (!sa->linkID) {
+ if (ldb_attr_in_list(preserved_attrs, el->name)) {
+ continue;
+ }
+ if (sa->searchFlags & SEARCH_FLAG_PRESERVEONDELETE) {
+ continue;
+ }
}
ret = ldb_msg_add_empty(msg, el->name, LDB_FLAG_MOD_DELETE, &el);
if (ret != LDB_SUCCESS) {
@@ -3826,6 +3929,8 @@ static int replmd_replicated_apply_add(struct replmd_replicated_request *ar)
unsigned int i;
int ret;
bool remote_isDeleted = false;
+ const struct dsdb_attribute *rdn_sa;
+ const char *rdn_name;
ldb = ldb_module_get_ctx(ar->module);
msg = ar->objs->objects[ar->index_current].msg;
@@ -3861,6 +3966,13 @@ static int replmd_replicated_apply_add(struct replmd_replicated_request *ar)
struct ldb_message_element *el = &msg->elements[i];
if (el->num_values == 0) {
+ if (ldb_attr_cmp(msg->elements[i].name, "objectClass") == 0) {
+ ldb_asprintf_errstring(ldb, __location__
+ ": empty objectClass sent on %s, aborting replication\n",
+ ldb_dn_get_linearized(msg->dn));
+ return replmd_replicated_request_error(ar, LDB_ERR_OBJECT_CLASS_VIOLATION);
+ }
+
DEBUG(4,(__location__ ": Removing attribute %s with num_values==0\n",
el->name));
memmove(el, el+1, sizeof(*el)*(msg->num_elements - (i+1)));
@@ -3870,12 +3982,38 @@ static int replmd_replicated_apply_add(struct replmd_replicated_request *ar)
}
}
+ if (DEBUGLVL(4)) {
+ char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_ADD, msg);
+ DEBUG(4, ("DRS replication add message:\n%s\n", s));
+ talloc_free(s);
+ }
+
remote_isDeleted = ldb_msg_find_attr_as_bool(msg,
"isDeleted", false);
/*
* the meta data array is already sorted by the caller
*/
+
+ rdn_name = ldb_dn_get_rdn_name(msg->dn);
+ if (rdn_name == NULL) {
+ ldb_asprintf_errstring(ldb, __location__ ": No rDN for %s?\n", ldb_dn_get_linearized(msg->dn));
+ return replmd_replicated_request_error(ar, LDB_ERR_INVALID_DN_SYNTAX);
+ }
+
+ rdn_sa = dsdb_attribute_by_lDAPDisplayName(ar->schema, rdn_name);
+ if (rdn_sa == NULL) {
+ ldb_asprintf_errstring(ldb, ": No schema attribute found for rDN %s for %s\n",
+ rdn_name, ldb_dn_get_linearized(msg->dn));
+ return replmd_replicated_request_error(ar, LDB_ERR_UNDEFINED_ATTRIBUTE_TYPE);
+ }
+
+ ret = replmd_replPropertyMetaDataCtr1_verify(ldb, &md->ctr.ctr1, rdn_sa, msg->dn);
+ if (ret != LDB_SUCCESS) {
+ ldb_asprintf_errstring(ldb, "%s: error during DRS repl ADD: %s", __func__, ldb_errstring(ldb));
+ return replmd_replicated_request_error(ar, ret);
+ }
+
for (i=0; i < md->ctr.ctr1.count; i++) {
md->ctr.ctr1.array[i].local_usn = ar->seq_num;
}
@@ -3903,12 +4041,6 @@ static int replmd_replicated_apply_add(struct replmd_replicated_request *ar)
ar->isDeleted = remote_isDeleted;
- if (DEBUGLVL(4)) {
- char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_ADD, msg);
- DEBUG(4, ("DRS replication add message:\n%s\n", s));
- talloc_free(s);
- }
-
ret = ldb_build_add_req(&change_req,
ldb,
ar,
@@ -4300,11 +4432,19 @@ static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar)
}
if (ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING) {
- /* if we compare equal then do an
- update. This is used when a client
- asks for a FULL_SYNC, and can be
- used to recover a corrupt
- replica */
+ /*
+ * if we compare equal then do an
+ * update. This is used when a client
+ * asks for a FULL_SYNC, and can be
+ * used to recover a corrupt
+ * replica.
+ *
+ * This call is a bit tricky, what we
+ * are doing it turning the 'is_newer'
+ * call into a 'not is older' by
+ * swapping i and j, and negating the
+ * outcome.
+ */
cmp = !replmd_replPropertyMetaData1_is_newer(&rmd->ctr.ctr1.array[i],
&nmd.ctr.ctr1.array[j]);
} else {
@@ -4385,8 +4525,9 @@ static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar)
*
* sort the new meta data array
*/
- ret = replmd_replPropertyMetaDataCtr1_sort(&nmd.ctr.ctr1, ar->schema, msg->dn);
+ ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &nmd.ctr.ctr1, ar->schema, msg->dn);
if (ret != LDB_SUCCESS) {
+ ldb_asprintf_errstring(ldb, "%s: error during DRS repl merge: %s", __func__, ldb_errstring(ldb));
return ret;
}
@@ -4460,6 +4601,14 @@ static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar)
/* we want to replace the old values */
for (i=0; i < msg->num_elements; i++) {
msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
+ if (ldb_attr_cmp(msg->elements[i].name, "objectClass") == 0) {
+ if (msg->elements[i].num_values == 0) {
+ ldb_asprintf_errstring(ldb, __location__
+ ": objectClass removed on %s, aborting replication\n",
+ ldb_dn_get_linearized(msg->dn));
+ return replmd_replicated_request_error(ar, LDB_ERR_OBJECT_CLASS_VIOLATION);
+ }
+ }
}
if (DEBUGLVL(4)) {
@@ -5277,8 +5426,8 @@ linked_attributes[0]:
attrs[0] = attr->lDAPDisplayName;
attrs[1] = "isDeleted";
- attrs[1] = "isRecycled";
- attrs[2] = NULL;
+ attrs[2] = "isRecycled";
+ attrs[3] = NULL;
/* get the existing message from the db for the object with
this GUID, returning attribute being modified. We will then
diff --git a/source4/dsdb/samdb/ldb_modules/rootdse.c b/source4/dsdb/samdb/ldb_modules/rootdse.c
index f905aa24230..b13dc9e5c59 100644
--- a/source4/dsdb/samdb/ldb_modules/rootdse.c
+++ b/source4/dsdb/samdb/ldb_modules/rootdse.c
@@ -36,13 +36,16 @@
#include "librpc/gen_ndr/ndr_irpc_c.h"
#include "lib/tsocket/tsocket.h"
#include "cldap_server/cldap_server.h"
+#include "lib/events/events.h"
-struct private_data {
+struct rootdse_private_data {
unsigned int num_controls;
char **controls;
unsigned int num_partitions;
struct ldb_dn **partitions;
bool block_anonymous;
+ struct tevent_context *saved_ev;
+ struct tevent_context *private_ev;
};
struct rootdse_context {
@@ -227,7 +230,7 @@ static int dsdb_module_we_are_master(struct ldb_module *module, struct ldb_dn *d
static int rootdse_add_dynamic(struct rootdse_context *ac, struct ldb_message *msg)
{
struct ldb_context *ldb;
- struct private_data *priv = talloc_get_type(ldb_module_get_private(ac->module), struct private_data);
+ struct rootdse_private_data *priv = talloc_get_type(ldb_module_get_private(ac->module), struct rootdse_private_data);
const char * const *attrs = ac->req->op.search.attrs;
char **server_sasl;
const struct dsdb_schema *schema;
@@ -654,7 +657,7 @@ static int rootdse_callback(struct ldb_request *req, struct ldb_reply *ares)
static int rootdse_filter_controls(struct ldb_module *module, struct ldb_request *req)
{
unsigned int i, j;
- struct private_data *priv = talloc_get_type(ldb_module_get_private(module), struct private_data);
+ struct rootdse_private_data *priv = talloc_get_type(ldb_module_get_private(module), struct rootdse_private_data);
bool is_untrusted;
if (!req->controls) {
@@ -717,7 +720,7 @@ static int rootdse_filter_controls(struct ldb_module *module, struct ldb_request
static int rootdse_filter_operations(struct ldb_module *module, struct ldb_request *req)
{
struct auth_session_info *session_info;
- struct private_data *priv = talloc_get_type(ldb_module_get_private(module), struct private_data);
+ struct rootdse_private_data *priv = talloc_get_type(ldb_module_get_private(module), struct rootdse_private_data);
bool is_untrusted = ldb_req_is_untrusted(req);
bool is_anonymous = true;
if (is_untrusted == false) {
@@ -855,7 +858,7 @@ static int rootdse_search(struct ldb_module *module, struct ldb_request *req)
static int rootdse_register_control(struct ldb_module *module, struct ldb_request *req)
{
- struct private_data *priv = talloc_get_type(ldb_module_get_private(module), struct private_data);
+ struct rootdse_private_data *priv = talloc_get_type(ldb_module_get_private(module), struct rootdse_private_data);
char **list;
list = talloc_realloc(priv, priv->controls, char *, priv->num_controls + 1);
@@ -876,7 +879,7 @@ static int rootdse_register_control(struct ldb_module *module, struct ldb_reques
static int rootdse_register_partition(struct ldb_module *module, struct ldb_request *req)
{
- struct private_data *priv = talloc_get_type(ldb_module_get_private(module), struct private_data);
+ struct rootdse_private_data *priv = talloc_get_type(ldb_module_get_private(module), struct rootdse_private_data);
struct ldb_dn **list;
list = talloc_realloc(priv, priv->partitions, struct ldb_dn *, priv->num_partitions + 1);
@@ -916,14 +919,14 @@ static int rootdse_init(struct ldb_module *module)
int ret;
struct ldb_context *ldb;
struct ldb_result *res;
- struct private_data *data;
+ struct rootdse_private_data *data;
const char *attrs[] = { "msDS-Behavior-Version", NULL };
const char *ds_attrs[] = { "dsServiceName", NULL };
TALLOC_CTX *mem_ctx;
ldb = ldb_module_get_ctx(module);
- data = talloc_zero(module, struct private_data);
+ data = talloc_zero(module, struct rootdse_private_data);
if (data == NULL) {
return ldb_oom(ldb);
}
@@ -1356,6 +1359,67 @@ static int rootdse_add(struct ldb_module *module, struct ldb_request *req)
return LDB_ERR_NAMING_VIOLATION;
}
+static int rootdse_start_trans(struct ldb_module *module)
+{
+ int ret;
+ struct ldb_context *ldb = ldb_module_get_ctx(module);
+ struct rootdse_private_data *data = talloc_get_type_abort(ldb_module_get_private(module),
+ struct rootdse_private_data);
+ ret = ldb_next_start_trans(module);
+ if (ret == LDB_SUCCESS) {
+ if (data->private_ev != NULL) {
+ return ldb_operr(ldb);
+ }
+ data->private_ev = s4_event_context_init(data);
+ if (data->private_ev == NULL) {
+ return ldb_operr(ldb);
+ }
+ data->saved_ev = ldb_get_event_context(ldb);
+ ldb_set_event_context(ldb, data->private_ev);
+ }
+ return ret;
+}
+
+static int rootdse_end_trans(struct ldb_module *module)
+{
+ int ret;
+ struct ldb_context *ldb = ldb_module_get_ctx(module);
+ struct rootdse_private_data *data = talloc_get_type_abort(ldb_module_get_private(module),
+ struct rootdse_private_data);
+ ret = ldb_next_end_trans(module);
+ if (data->saved_ev == NULL) {
+ return ldb_operr(ldb);
+ }
+
+ if (data->private_ev != ldb_get_event_context(ldb)) {
+ return ldb_operr(ldb);
+ }
+ ldb_set_event_context(ldb, data->saved_ev);
+ data->saved_ev = NULL;
+ TALLOC_FREE(data->private_ev);
+ return ret;
+}
+
+static int rootdse_del_trans(struct ldb_module *module)
+{
+ int ret;
+ struct ldb_context *ldb = ldb_module_get_ctx(module);
+ struct rootdse_private_data *data = talloc_get_type_abort(ldb_module_get_private(module),
+ struct rootdse_private_data);
+ ret = ldb_next_del_trans(module);
+ if (data->saved_ev == NULL) {
+ return ldb_operr(ldb);
+ }
+
+ if (data->private_ev != ldb_get_event_context(ldb)) {
+ return ldb_operr(ldb);
+ }
+ ldb_set_event_context(ldb, data->saved_ev);
+ data->saved_ev = NULL;
+ TALLOC_FREE(data->private_ev);
+ return ret;
+}
+
struct fsmo_transfer_state {
struct ldb_context *ldb;
struct ldb_request *req;
@@ -1373,6 +1437,7 @@ static void rootdse_fsmo_transfer_callback(struct tevent_req *treq)
int ret;
struct ldb_request *req = fsmo->req;
struct ldb_context *ldb = fsmo->ldb;
+ struct ldb_module *module = fsmo->module;
status = dcerpc_drepl_takeFSMORole_recv(treq, fsmo, &werr);
talloc_free(fsmo);
@@ -1382,7 +1447,7 @@ static void rootdse_fsmo_transfer_callback(struct tevent_req *treq)
* Now that it is failed, start the transaction up
* again so the wrappers can close it without additional error
*/
- ldb_next_start_trans(fsmo->module);
+ rootdse_start_trans(module);
ldb_module_done(req, NULL, NULL, LDB_ERR_UNAVAILABLE);
return;
}
@@ -1392,7 +1457,7 @@ static void rootdse_fsmo_transfer_callback(struct tevent_req *treq)
* Now that it is failed, start the transaction up
* again so the wrappers can close it without additional error
*/
- ldb_next_start_trans(fsmo->module);
+ rootdse_start_trans(module);
ldb_module_done(req, NULL, NULL, LDB_ERR_UNAVAILABLE);
return;
}
@@ -1401,7 +1466,7 @@ static void rootdse_fsmo_transfer_callback(struct tevent_req *treq)
* Now that it is done, start the transaction up again so the
* wrappers can close it without error
*/
- ret = ldb_next_start_trans(fsmo->module);
+ ret = rootdse_start_trans(module);
ldb_module_done(req, NULL, NULL, ret);
}
@@ -1442,7 +1507,7 @@ static int rootdse_become_master(struct ldb_module *module,
* this gives the least supprise to this supprising action (as
* we will never record anything done to this point
*/
- ldb_next_del_trans(module);
+ rootdse_del_trans(module);
msg = imessaging_client_init(tmp_ctx, lp_ctx,
ldb_get_event_context(ldb));
@@ -1611,15 +1676,18 @@ static int rootdse_extended(struct ldb_module *module, struct ldb_request *req)
}
static const struct ldb_module_ops ldb_rootdse_module_ops = {
- .name = "rootdse",
- .init_context = rootdse_init,
- .search = rootdse_search,
- .request = rootdse_request,
- .add = rootdse_add,
- .modify = rootdse_modify,
- .rename = rootdse_rename,
- .extended = rootdse_extended,
- .del = rootdse_delete
+ .name = "rootdse",
+ .init_context = rootdse_init,
+ .search = rootdse_search,
+ .request = rootdse_request,
+ .add = rootdse_add,
+ .modify = rootdse_modify,
+ .rename = rootdse_rename,
+ .extended = rootdse_extended,
+ .del = rootdse_delete,
+ .start_transaction = rootdse_start_trans,
+ .end_transaction = rootdse_end_trans,
+ .del_transaction = rootdse_del_trans
};
int ldb_rootdse_module_init(const char *version)
diff --git a/source4/dsdb/samdb/ldb_modules/samldb.c b/source4/dsdb/samdb/ldb_modules/samldb.c
index a63a44ea8e1..2f8c5728fe4 100644
--- a/source4/dsdb/samdb/ldb_modules/samldb.c
+++ b/source4/dsdb/samdb/ldb_modules/samldb.c
@@ -509,7 +509,8 @@ static int samldb_add_handle_msDS_IntId(struct samldb_ctx *ac)
continue;
}
- ret = dsdb_module_load_partition_usn(ac->module, schema->base_dn, &current_usn, NULL, NULL);
+ ret = dsdb_module_load_partition_usn(ac->module, schema_dn,
+ &current_usn, NULL, NULL);
if (ret != LDB_SUCCESS) {
ldb_debug_set(ldb, LDB_DEBUG_ERROR,
__location__": Searching for schema USN failed: %s\n",
@@ -2002,7 +2003,7 @@ static int samldb_service_principal_names_change(struct samldb_ctx *ac)
/* Create a temporary message for fetching the "sAMAccountName" */
if (el2 != NULL) {
- char *tempstr, *tempstr2;
+ char *tempstr, *tempstr2 = NULL;
const char *acct_attrs[] = { "sAMAccountName", NULL };
msg = ldb_msg_new(ac->msg);
@@ -2267,6 +2268,15 @@ static int samldb_add(struct ldb_module *module, struct ldb_request *req)
return ldb_next_request(module, req);
}
+ el = ldb_msg_find_element(req->op.add.message, "userParameters");
+ if (el != NULL && ldb_req_is_untrusted(req)) {
+ const char *reason = "samldb_add: "
+ "setting userParameters is not supported over LDAP, "
+ "see https://bugzilla.samba.org/show_bug.cgi?id=8077";
+ ldb_debug(ldb, LDB_DEBUG_WARNING, "%s", reason);
+ return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION, reason);
+ }
+
ac = samldb_ctx_init(module, req);
if (ac == NULL) {
return ldb_operr(ldb);
@@ -2406,6 +2416,15 @@ static int samldb_modify(struct ldb_module *module, struct ldb_request *req)
}
}
+ el = ldb_msg_find_element(req->op.mod.message, "userParameters");
+ if (el != NULL && ldb_req_is_untrusted(req)) {
+ const char *reason = "samldb: "
+ "setting userParameters is not supported over LDAP, "
+ "see https://bugzilla.samba.org/show_bug.cgi?id=8077";
+ ldb_debug(ldb, LDB_DEBUG_WARNING, "%s", reason);
+ return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION, reason);
+ }
+
ac = samldb_ctx_init(module, req);
if (ac == NULL) {
return ldb_operr(ldb);
@@ -2552,6 +2571,11 @@ static int samldb_prim_group_users_check(struct samldb_ctx *ac)
/* Special object (security principal?) */
return LDB_SUCCESS;
}
+ /* do not allow deletion of well-known sids */
+ if (rid < DSDB_SAMDB_MINIMUM_ALLOWED_RID &&
+ (ldb_request_get_control(ac->req, LDB_CONTROL_RELAX_OID) == NULL)) {
+ return LDB_ERR_OTHER;
+ }
/* Deny delete requests from groups which are primary ones */
ret = dsdb_module_search(ac->module, ac, &res,
@@ -2757,7 +2781,6 @@ static int check_rename_constraints(struct ldb_message *msg,
static int samldb_rename_search_base_callback(struct ldb_request *req,
struct ldb_reply *ares)
{
- struct ldb_request *rename_req;
struct samldb_ctx *ac;
int ret;
diff --git a/source4/dsdb/samdb/ldb_modules/schema_data.c b/source4/dsdb/samdb/ldb_modules/schema_data.c
index 3ce7ef9935c..996b1f22386 100644
--- a/source4/dsdb/samdb/ldb_modules/schema_data.c
+++ b/source4/dsdb/samdb/ldb_modules/schema_data.c
@@ -144,6 +144,8 @@ static int schema_data_add(struct ldb_module *module, struct ldb_request *req)
WERROR status;
bool rodc = false;
int ret;
+ struct schema_data_private_data *mc;
+ mc = talloc_get_type(ldb_module_get_private(module), struct schema_data_private_data);
ldb = ldb_module_get_ctx(module);
@@ -162,12 +164,6 @@ static int schema_data_add(struct ldb_module *module, struct ldb_request *req)
return ldb_next_request(module, req);
}
- if (schema->base_dn == NULL) {
- ldb_debug_set(ldb, LDB_DEBUG_FATAL,
- "schema_data_add: base_dn NULL\n");
- return LDB_ERR_OPERATIONS_ERROR;
- }
-
ret = samdb_rodc(ldb, &rodc);
if (ret != LDB_SUCCESS) {
DEBUG(4, (__location__ ": unable to tell if we are an RODC \n"));
@@ -190,7 +186,7 @@ static int schema_data_add(struct ldb_module *module, struct ldb_request *req)
* the provision code needs to create
* the schema root object.
*/
- cmp = ldb_dn_compare(req->op.add.message->dn, schema->base_dn);
+ cmp = ldb_dn_compare(req->op.add.message->dn, mc->schema_dn);
if (cmp == 0) {
return ldb_next_request(module, req);
}
@@ -201,7 +197,7 @@ static int schema_data_add(struct ldb_module *module, struct ldb_request *req)
return ldb_oom(ldb);
}
- cmp = ldb_dn_compare(parent_dn, schema->base_dn);
+ cmp = ldb_dn_compare(parent_dn, mc->schema_dn);
if (cmp != 0) {
ldb_debug_set(ldb, LDB_DEBUG_ERROR,
"schema_data_add: no direct child :%s\n",
@@ -257,6 +253,8 @@ static int schema_data_modify(struct ldb_module *module, struct ldb_request *req
bool rodc = false;
int ret;
struct ldb_control *sd_propagation_control;
+ struct schema_data_private_data *mc;
+ mc = talloc_get_type(ldb_module_get_private(module), struct schema_data_private_data);
ldb = ldb_module_get_ctx(module);
@@ -295,7 +293,7 @@ static int schema_data_modify(struct ldb_module *module, struct ldb_request *req
return ldb_next_request(module, req);
}
- cmp = ldb_dn_compare(req->op.mod.message->dn, schema->base_dn);
+ cmp = ldb_dn_compare(req->op.mod.message->dn, mc->schema_dn);
if (cmp == 0) {
static const char * const constrained_attrs[] = {
"schemaInfo",
diff --git a/source4/dsdb/samdb/ldb_modules/schema_load.c b/source4/dsdb/samdb/ldb_modules/schema_load.c
index faaf3f2071c..17c20338b81 100644
--- a/source4/dsdb/samdb/ldb_modules/schema_load.c
+++ b/source4/dsdb/samdb/ldb_modules/schema_load.c
@@ -39,7 +39,10 @@ struct schema_load_private_data {
struct tdb_wrap *metadata;
};
-static int dsdb_schema_from_db(struct ldb_module *module, struct ldb_dn *schema_dn, uint64_t current_usn,
+static int dsdb_schema_from_db(struct ldb_module *module,
+ TALLOC_CTX *mem_ctx,
+ uint64_t current_usn,
+ uint64_t schema_seq_num,
struct dsdb_schema **schema);
/*
@@ -68,13 +71,13 @@ static int schema_metadata_open(struct ldb_module *module)
}
sam_name = (const char *)ldb_get_opaque(ldb, "ldb_url");
- if (strncmp("tdb://", sam_name, 6) == 0) {
- sam_name += 6;
- }
if (!sam_name) {
talloc_free(tmp_ctx);
return ldb_operr(ldb);
}
+ if (strncmp("tdb://", sam_name, 6) == 0) {
+ sam_name += 6;
+ }
filename = talloc_asprintf(tmp_ctx, "%s.d/metadata.tdb", sam_name);
if (!filename) {
talloc_free(tmp_ctx);
@@ -113,8 +116,8 @@ static int schema_metadata_get_uint64(struct ldb_module *module,
TALLOC_CTX *tmp_ctx;
if (!data || !data->metadata) {
- return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
- "schema: metadata tdb not initialized");
+ *value = default_value;
+ return LDB_SUCCESS;
}
tmp_ctx = talloc_new(NULL);
@@ -155,12 +158,15 @@ static int schema_metadata_get_uint64(struct ldb_module *module,
return LDB_SUCCESS;
}
-static struct dsdb_schema *dsdb_schema_refresh(struct ldb_module *module, struct dsdb_schema *schema, bool is_global_schema)
+static struct dsdb_schema *dsdb_schema_refresh(struct ldb_module *module, struct tevent_context *ev,
+ struct dsdb_schema *schema, bool is_global_schema)
{
- uint64_t current_usn, value;
+ TALLOC_CTX *mem_ctx;
+ uint64_t current_usn, schema_seq_num = 0;
int ret;
struct ldb_context *ldb = ldb_module_get_ctx(module);
struct dsdb_schema *new_schema;
+ struct ldb_dn *schema_dn = ldb_get_schema_basedn(ldb);
time_t ts, lastts;
struct schema_load_private_data *private_data = talloc_get_type(ldb_module_get_private(module), struct schema_load_private_data);
@@ -174,11 +180,11 @@ static struct dsdb_schema *dsdb_schema_refresh(struct ldb_module *module, struct
return schema;
}
- lastts = schema->last_refresh;
- ts = time(NULL);
- if (lastts > (ts - schema->refresh_interval)) {
- DEBUG(11, ("Less than %d seconds since last reload, returning cached version ts = %d\n", (int)schema->refresh_interval, (int)lastts));
- return schema;
+ SMB_ASSERT(ev == ldb_get_event_context(ldb));
+
+ mem_ctx = talloc_new(module);
+ if (mem_ctx == NULL) {
+ return NULL;
}
/*
@@ -189,29 +195,59 @@ static struct dsdb_schema *dsdb_schema_refresh(struct ldb_module *module, struct
* continue to hit the database to get the highest USN.
*/
- ret = schema_metadata_get_uint64(module, DSDB_METADATA_SCHEMA_SEQ_NUM, &value, 0);
- if (ret == LDB_SUCCESS) {
- schema->metadata_usn = value;
- } else {
- /* From an old provision it can happen that the tdb didn't exists yet */
- DEBUG(0, ("Error while searching for the schema usn in the metadata\n"));
- schema->metadata_usn = 0;
+ ret = schema_metadata_get_uint64(module, DSDB_METADATA_SCHEMA_SEQ_NUM, &schema_seq_num, 0);
+
+ if (schema != NULL) {
+ lastts = schema->last_refresh;
+ ts = time(NULL);
+ if (lastts > (ts - schema->refresh_interval)) {
+ DEBUG(11, ("Less than %d seconds since last reload, "
+ "returning cached version ts = %d\n",
+ (int)schema->refresh_interval,
+ (int)lastts));
+ TALLOC_FREE(mem_ctx);
+ return schema;
+ }
+
+ if (ret == LDB_SUCCESS) {
+ schema->metadata_usn = schema_seq_num;
+ } else {
+ /* From an old provision it can happen that the tdb didn't exists yet */
+ DEBUG(0, ("Error while searching for the schema usn in the metadata ignoring: %d:%s:%s\n",
+ ret, ldb_strerror(ret), ldb_errstring(ldb)));
+ schema->metadata_usn = 0;
+ }
+ schema->last_refresh = ts;
+
}
- schema->last_refresh = ts;
- ret = dsdb_module_load_partition_usn(module, schema->base_dn, &current_usn, NULL, NULL);
- if (ret != LDB_SUCCESS || current_usn == schema->loaded_usn) {
+ ret = dsdb_module_load_partition_usn(module, schema_dn, &current_usn, NULL, NULL);
+ if (ret != LDB_SUCCESS || (schema && (current_usn == schema->loaded_usn))) {
+ TALLOC_FREE(mem_ctx);
return schema;
}
- ret = dsdb_schema_from_db(module, schema->base_dn, current_usn, &new_schema);
+ ret = dsdb_schema_from_db(module, mem_ctx, current_usn, schema_seq_num, &new_schema);
if (ret != LDB_SUCCESS) {
+ ldb_debug_set(ldb, LDB_DEBUG_FATAL,
+ "dsdb_schema_from_db() failed: %d:%s: %s",
+ ret, ldb_strerror(ret), ldb_errstring(ldb));
+ TALLOC_FREE(mem_ctx);
return schema;
}
+ ret = dsdb_set_schema(ldb, new_schema);
+ if (ret != LDB_SUCCESS) {
+ ldb_debug_set(ldb, LDB_DEBUG_FATAL,
+ "dsdb_set_schema() failed: %d:%s: %s",
+ ret, ldb_strerror(ret), ldb_errstring(ldb));
+ TALLOC_FREE(mem_ctx);
+ return schema;
+ }
if (is_global_schema) {
dsdb_make_schema_global(ldb, new_schema);
}
+ TALLOC_FREE(mem_ctx);
return new_schema;
}
@@ -220,13 +256,17 @@ static struct dsdb_schema *dsdb_schema_refresh(struct ldb_module *module, struct
Given an LDB module (pointing at the schema DB), and the DN, set the populated schema
*/
-static int dsdb_schema_from_db(struct ldb_module *module, struct ldb_dn *schema_dn, uint64_t current_usn,
+static int dsdb_schema_from_db(struct ldb_module *module,
+ TALLOC_CTX *mem_ctx,
+ uint64_t current_usn,
+ uint64_t schema_seq_num,
struct dsdb_schema **schema)
{
struct ldb_context *ldb = ldb_module_get_ctx(module);
TALLOC_CTX *tmp_ctx;
char *error_string;
int ret;
+ struct ldb_dn *schema_dn = ldb_get_schema_basedn(ldb);
struct ldb_result *schema_res;
struct ldb_result *res;
static const char *schema_attrs[] = {
@@ -289,37 +329,11 @@ static int dsdb_schema_from_db(struct ldb_module *module, struct ldb_dn *schema_
goto failed;
}
- (*schema)->refresh_in_progress = true;
-
- /* If we have the readOnlySchema opaque, then don't check for
- * runtime schema updates, as they are not permitted (we would
- * have to update the backend server schema too */
- if (!ldb_get_opaque(ldb, "readOnlySchema")) {
- (*schema)->refresh_fn = dsdb_schema_refresh;
- (*schema)->loaded_from_module = module;
- (*schema)->loaded_usn = current_usn;
- }
-
- /* "dsdb_set_schema()" steals schema into the ldb_context */
- ret = dsdb_set_schema(ldb, (*schema));
-
- (*schema)->refresh_in_progress = false;
+ (*schema)->loaded_usn = current_usn;
+ (*schema)->metadata_usn = schema_seq_num;
(*schema)->last_refresh = time(NULL);
- if (ret != LDB_SUCCESS) {
- ldb_debug_set(ldb, LDB_DEBUG_FATAL,
- "schema_load_init: dsdb_set_schema() failed: %d:%s: %s",
- ret, ldb_strerror(ret), ldb_errstring(ldb));
- goto failed;
- }
-
- /* Ensure this module won't go away before the callback. This
- * causes every schema to have the LDB that originally loaded
- * the first schema as a talloc child. */
- if (talloc_reference(*schema, ldb) == NULL) {
- ldb_oom(ldb);
- ret = LDB_ERR_OPERATIONS_ERROR;
- }
+ talloc_steal(mem_ctx, *schema);
failed:
if (flags & LDB_FLG_ENABLE_TRACING) {
@@ -334,11 +348,10 @@ failed:
static int schema_load_init(struct ldb_module *module)
{
struct schema_load_private_data *private_data;
- struct dsdb_schema *schema;
struct ldb_context *ldb = ldb_module_get_ctx(module);
+ struct dsdb_schema *schema;
+ void *readOnlySchema;
int ret;
- uint64_t current_usn;
- struct ldb_dn *schema_dn;
private_data = talloc_zero(module, struct schema_load_private_data);
if (private_data == NULL) {
@@ -352,39 +365,69 @@ static int schema_load_init(struct ldb_module *module)
return ret;
}
- if (dsdb_get_schema(ldb, NULL)) {
- return LDB_SUCCESS;
- }
+ schema = dsdb_get_schema(ldb, NULL);
+
+ /* We might already have a schema */
+ if (schema != NULL) {
+ /* Hook up the refresh function */
+ if (dsdb_uses_global_schema(ldb)) {
+ ret = dsdb_set_schema_refresh_function(ldb, dsdb_schema_refresh, module);
+
+ if (ret != LDB_SUCCESS) {
+ ldb_debug_set(ldb, LDB_DEBUG_FATAL,
+ "schema_load_init: dsdb_set_schema_refresh_fns() failed: %d:%s: %s",
+ ret, ldb_strerror(ret), ldb_errstring(ldb));
+ return ret;
+ }
+ }
- schema_dn = ldb_get_schema_basedn(ldb);
- if (!schema_dn) {
- ldb_reset_err_string(ldb);
- ldb_debug(ldb, LDB_DEBUG_WARNING,
- "schema_load_init: no schema dn present: (skip schema loading)\n");
return LDB_SUCCESS;
}
- ret = dsdb_module_load_partition_usn(module, schema_dn, &current_usn, NULL, NULL);
- if (ret != LDB_SUCCESS) {
- /* Ignore the error and just reload the DB more often */
- current_usn = 0;
- }
+ readOnlySchema = ldb_get_opaque(ldb, "readOnlySchema");
- ret = dsdb_schema_from_db(module, schema_dn, current_usn, &schema);
- /* We don't care too much on the result of this action
- * the most probable reason for this to fail is that the tdb didn't
- * exists yet and this will be corrected by the partition module.
- */
- if (ret == LDB_SUCCESS && schema_metadata_open(module) == LDB_SUCCESS) {
- uint64_t value;
+ /* If we have the readOnlySchema opaque, then don't check for
+ * runtime schema updates, as they are not permitted (we would
+ * have to update the backend server schema too */
+ if (readOnlySchema != NULL) {
+ struct dsdb_schema *new_schema;
+ ret = dsdb_schema_from_db(module, private_data, 0, 0, &new_schema);
+ if (ret != LDB_SUCCESS) {
+ ldb_debug_set(ldb, LDB_DEBUG_FATAL,
+ "schema_load_init: dsdb_schema_from_db() failed: %d:%s: %s",
+ ret, ldb_strerror(ret), ldb_errstring(ldb));
+ return ret;
+ }
- ret = schema_metadata_get_uint64(module, DSDB_METADATA_SCHEMA_SEQ_NUM, &value, 0);
- if (ret == LDB_SUCCESS) {
- schema->metadata_usn = value;
- } else {
- schema->metadata_usn = 0;
+ /* "dsdb_set_schema()" steals schema into the ldb_context */
+ ret = dsdb_set_schema(ldb, new_schema);
+ if (ret != LDB_SUCCESS) {
+ ldb_debug_set(ldb, LDB_DEBUG_FATAL,
+ "schema_load_init: dsdb_set_schema() failed: %d:%s: %s",
+ ret, ldb_strerror(ret), ldb_errstring(ldb));
+ return ret;
+ }
+
+ } else {
+ ret = dsdb_set_schema_refresh_function(ldb, dsdb_schema_refresh, module);
+
+ if (ret != LDB_SUCCESS) {
+ ldb_debug_set(ldb, LDB_DEBUG_FATAL,
+ "schema_load_init: dsdb_set_schema_refresh_fns() failed: %d:%s: %s",
+ ret, ldb_strerror(ret), ldb_errstring(ldb));
+ return ret;
}
}
+
+ schema = dsdb_get_schema(ldb, NULL);
+
+ /* We do this, invoking the refresh handler, so we know that it works */
+ if (schema == NULL) {
+ ldb_debug_set(ldb, LDB_DEBUG_FATAL,
+ "schema_load_init: dsdb_get_schema failed");
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
return ret;
}
diff --git a/source4/dsdb/samdb/ldb_modules/secrets_tdb_sync.c b/source4/dsdb/samdb/ldb_modules/secrets_tdb_sync.c
index e3d8485c611..284aa1b6e2d 100644
--- a/source4/dsdb/samdb/ldb_modules/secrets_tdb_sync.c
+++ b/source4/dsdb/samdb/ldb_modules/secrets_tdb_sync.c
@@ -489,12 +489,12 @@ static int secrets_tdb_sync_init(struct ldb_module *module)
ldb_module_set_private(module, data);
secrets_ldb = (const char *)ldb_get_opaque(ldb, "ldb_url");
- if (strncmp("tdb://", secrets_ldb, 6) == 0) {
- secrets_ldb += 6;
- }
if (!secrets_ldb) {
return ldb_operr(ldb);
}
+ if (strncmp("tdb://", secrets_ldb, 6) == 0) {
+ secrets_ldb += 6;
+ }
private_dir = talloc_strdup(data, secrets_ldb);
p = strrchr(private_dir, '/');
if (p) {
diff --git a/source4/dsdb/samdb/ldb_modules/simple_ldap_map.c b/source4/dsdb/samdb/ldb_modules/simple_ldap_map.c
index 451c7a1c666..87e3c65ef83 100644
--- a/source4/dsdb/samdb/ldb_modules/simple_ldap_map.c
+++ b/source4/dsdb/samdb/ldb_modules/simple_ldap_map.c
@@ -362,7 +362,7 @@ static const struct ldb_map_attribute entryuuid_attributes[] =
},
{
.local_name = "distinguishedName",
- .type = LDB_MAP_RENAME,
+ .type = LDB_MAP_RENDROP,
.u = {
.rename = {
.remote_name = "entryDN"
diff --git a/source4/dsdb/samdb/ldb_modules/update_keytab.c b/source4/dsdb/samdb/ldb_modules/update_keytab.c
index bec4a83abfc..ebf77dc9a82 100644
--- a/source4/dsdb/samdb/ldb_modules/update_keytab.c
+++ b/source4/dsdb/samdb/ldb_modules/update_keytab.c
@@ -377,7 +377,8 @@ static int update_kt_prepare_commit(struct ldb_module *module)
struct update_kt_private *data = talloc_get_type(ldb_module_get_private(module), struct update_kt_private);
struct dn_list *p;
struct smb_krb5_context *smb_krb5_context;
- int krb5_ret = smb_krb5_init_context(data, ldb_get_event_context(ldb), ldb_get_opaque(ldb, "loadparm"),
+ int krb5_ret = smb_krb5_init_context(data,
+ ldb_get_opaque(ldb, "loadparm"),
&smb_krb5_context);
TALLOC_CTX *tmp_ctx;
@@ -397,7 +398,7 @@ static int update_kt_prepare_commit(struct ldb_module *module)
const char *realm;
char *upper_realm;
struct ldb_message_element *spn_el = ldb_msg_find_element(p->msg, "servicePrincipalName");
- char **SPNs = NULL;
+ const char **SPNs = NULL;
int num_SPNs = 0;
int i;
@@ -411,13 +412,13 @@ static int update_kt_prepare_commit(struct ldb_module *module)
}
num_SPNs = spn_el->num_values;
- SPNs = talloc_array(tmp_ctx, char *, num_SPNs);
+ SPNs = talloc_array(tmp_ctx, const char *, num_SPNs);
if (!SPNs) {
ldb_oom(ldb);
goto fail;
}
for (i = 0; i < num_SPNs; i++) {
- SPNs[i] = talloc_asprintf(tmp_ctx, "%*.*s@%s",
+ SPNs[i] = talloc_asprintf(SPNs, "%*.*s@%s",
(int)spn_el->values[i].length,
(int)spn_el->values[i].length,
(const char *)spn_el->values[i].data,
@@ -432,7 +433,7 @@ static int update_kt_prepare_commit(struct ldb_module *module)
krb5_ret = smb_krb5_update_keytab(tmp_ctx, smb_krb5_context->krb5_context,
keytab_name_from_msg(tmp_ctx, ldb, p->msg),
ldb_msg_find_attr_as_string(p->msg, "samAccountName", NULL),
- realm, (const char **)SPNs, num_SPNs,
+ realm, SPNs, num_SPNs,
ldb_msg_find_attr_as_string(p->msg, "saltPrincipal", NULL),
ldb_msg_find_attr_as_string(p->msg, "secret", NULL),
ldb_msg_find_attr_as_string(p->msg, "priorSecret", NULL),
diff --git a/source4/dsdb/samdb/ldb_modules/util.c b/source4/dsdb/samdb/ldb_modules/util.c
index f7803e56cb1..8d587a4cb38 100644
--- a/source4/dsdb/samdb/ldb_modules/util.c
+++ b/source4/dsdb/samdb/ldb_modules/util.c
@@ -174,6 +174,19 @@ int dsdb_module_search_tree(struct ldb_module *module,
ret = ldb_wait(req->handle, LDB_WAIT_ALL);
}
+ if (dsdb_flags & DSDB_SEARCH_ONE_ONLY) {
+ if (res->count == 0) {
+ talloc_free(tmp_ctx);
+ ldb_reset_err_string(ldb_module_get_ctx(module));
+ return LDB_ERR_NO_SUCH_OBJECT;
+ }
+ if (res->count != 1) {
+ talloc_free(tmp_ctx);
+ ldb_reset_err_string(ldb_module_get_ctx(module));
+ return LDB_ERR_CONSTRAINT_VIOLATION;
+ }
+ }
+
talloc_free(req);
if (ret == LDB_SUCCESS) {
*_res = talloc_steal(mem_ctx, res);