#include "dsdb/samdb/ldb_modules/util.h"
#include "lib/util/tsort.h"
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_DRS_REPL
+
/*
* It's 29/12/9999 at 23:59:59 UTC as specified in MS-ADTS 7.1.1.4.2
* Deleted Objects Container
struct la_entry {
struct la_entry *next, *prev;
struct drsuapi_DsReplicaLinkedAttribute *la;
+ uint32_t dsdb_repl_flags;
};
struct replmd_replicated_request {
struct parsed_dn *dns, uint32_t count,
struct ldb_message_element *el,
const char *ldap_oid);
+static int replmd_verify_linked_attribute(struct replmd_replicated_request *ar,
+ struct la_entry *la);
enum urgent_situation {
REPL_URGENT_ON_CREATE = 1,
ret = dsdb_module_dn_by_guid(module, frame, &bl->target_guid, &target_dn, parent);
if (ret != LDB_SUCCESS) {
struct GUID_txt_buf guid_str;
- DEBUG(2,(__location__ ": WARNING: Failed to find target DN for linked attribute with GUID %s\n",
- GUID_buf_string(&bl->target_guid, &guid_str)));
+ DBG_WARNING("Failed to find target DN for linked attribute with GUID %s\n",
+ GUID_buf_string(&bl->target_guid, &guid_str));
+ DBG_WARNING("Please run 'samba-tool dbcheck' to resolve any missing backlinks.\n");
talloc_free(frame);
return LDB_SUCCESS;
}
}
if (ares->error != LDB_SUCCESS) {
- DEBUG(5,("%s failure. Error is: %s\n", __FUNCTION__, ldb_strerror(ares->error)));
+ struct GUID_txt_buf guid_txt;
+ struct ldb_message *msg = NULL;
+ char *s = NULL;
+
+ if (ac->apply_mode == false) {
+ DBG_NOTICE("Originating update failure. Error is: %s\n",
+ ldb_strerror(ares->error));
+ return ldb_module_done(ac->req, controls,
+ ares->response, ares->error);
+ }
+
+ msg = ac->objs->objects[ac->index_current].msg;
+ /*
+ * Set at DBG_NOTICE as once these start to happe, they
+ * will happen a lot until resolved, due to repeated
+ * replication. The caller will probably print the
+ * ldb error string anyway.
+ */
+ DBG_NOTICE("DRS replication apply failure for %s. Error is: %s\n",
+ ldb_dn_get_linearized(msg->dn),
+ ldb_strerror(ares->error));
+
+ s = ldb_ldif_message_redacted_string(ldb_module_get_ctx(ac->module),
+ ac,
+ LDB_CHANGETYPE_ADD,
+ msg);
+
+ DBG_INFO("Failing DRS %s replication message was %s:\n%s\n",
+ ac->search_msg == NULL ? "ADD" : "MODIFY",
+ GUID_buf_string(&ac->objs->objects[ac->index_current].object_guid,
+ &guid_txt),
+ s);
+ talloc_free(s);
return ldb_module_done(ac->req, controls,
- ares->response, ares->error);
+ ares->response, ares->error);
}
if (ares->type != LDB_REPLY_DONE) {
const struct GUID *invocation_id, uint64_t seq_num,
uint64_t local_usn, NTTIME nttime, uint32_t version, bool deleted);
+static int parsed_dn_compare(struct parsed_dn *pdn1, struct parsed_dn *pdn2);
+
static int get_parsed_dns(struct ldb_module *module, TALLOC_CTX *mem_ctx,
struct ldb_message_element *el, struct parsed_dn **pdn,
const char *ldap_oid, struct ldb_request *parent);
/* We will take a reference to the schema in replmd_add_backlink */
const struct dsdb_schema *schema = dsdb_get_schema(ldb, NULL);
struct ldb_val *new_values = NULL;
+ int ret;
+
+ if (dsdb_check_single_valued_link(sa, el) == LDB_SUCCESS) {
+ el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
+ } else {
+ ldb_asprintf_errstring(ldb,
+ "Attribute %s is single valued but "
+ "more than one value has been supplied",
+ el->name);
+ talloc_free(tmp_ctx);
+ return LDB_ERR_CONSTRAINT_VIOLATION;
+ }
- int ret = get_parsed_dns(module, tmp_ctx, el, &pdn,
- sa->syntax->ldap_oid, parent);
+ ret = get_parsed_dns(module, tmp_ctx, el, &pdn,
+ sa->syntax->ldap_oid, parent);
if (ret != LDB_SUCCESS) {
talloc_free(tmp_ctx);
return ret;
for (i = 0; i < el->num_values; i++) {
struct parsed_dn *p = &pdn[i];
+ if (i > 0 && parsed_dn_compare(p, &pdn[i - 1]) == 0) {
+ ldb_asprintf_errstring(ldb,
+ "Linked attribute %s has "
+ "multiple identical values", el->name);
+ talloc_free(tmp_ctx);
+ if (ldb_attr_cmp(el->name, "member") == 0) {
+ return LDB_ERR_ENTRY_ALREADY_EXISTS;
+ } else {
+ return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
+ }
+ }
ret = replmd_build_la_val(el->values, p->v, p->dsdb_dn,
&ac->our_invocation_id,
ac->seq_num, ac->seq_num, now, 0, false);
const struct GUID *our_invocation_id,
NTTIME now,
bool is_schema_nc,
+ bool is_forced_rodc,
struct ldb_request *req)
{
uint32_t i;
md1->originating_usn = *seq_num;
md1->local_usn = *seq_num;
- if (ldb_request_get_control(req, DSDB_CONTROL_FORCE_RODC_LOCAL_CHANGE) != NULL) {
+ if (is_forced_rodc) {
/* Force version to 0 to be overriden later via replication */
- bool am_rodc = false;
- int ret = samdb_rodc(ldb, &am_rodc);
- if (ret == LDB_SUCCESS && am_rodc) {
- md1->version = 0;
- }
+ md1->version = 0;
}
return LDB_SUCCESS;
struct replPropertyMetaDataBlob *omd,
struct replmd_replicated_request *ar,
NTTIME now,
- bool is_schema_nc)
+ bool is_schema_nc,
+ bool is_forced_rodc)
{
const char *rdn_name = ldb_dn_get_rdn_name(msg->dn);
const struct dsdb_attribute *rdn_attr =
return replmd_update_rpmd_element(ldb, msg, &new_el, NULL,
omd, ar->schema, &ar->seq_num,
&ar->our_invocation_id,
- now, is_schema_nc, ar->req);
+ now, is_schema_nc, is_forced_rodc,
+ ar->req);
}
bool rmd_is_provided;
bool rmd_is_just_resorted = false;
const char *not_rename_attrs[4 + msg->num_elements];
+ bool is_forced_rodc = false;
if (rename_attrs) {
attrs = rename_attrs;
ldb = ldb_module_get_ctx(module);
+ ret = samdb_rodc(ldb, rodc);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(4, (__location__ ": unable to tell if we are an RODC\n"));
+ *rodc = false;
+ }
+
+ if (*rodc &&
+ ldb_request_get_control(req, DSDB_CONTROL_FORCE_RODC_LOCAL_CHANGE)) {
+ is_forced_rodc = true;
+ }
+
our_invocation_id = samdb_ntds_invocation_id(ldb);
if (!our_invocation_id) {
/* this happens during an initial vampire while
&omd, schema, seq_num,
our_invocation_id,
now, is_schema_nc,
+ is_forced_rodc,
req);
if (ret != LDB_SUCCESS) {
return ret;
/*if we are RODC and this is a DRSR update then its ok*/
if (!ldb_request_get_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID)
&& !ldb_request_get_control(req, DSDB_CONTROL_DBCHECK_MODIFY_RO_REPLICA)
- && !ldb_request_get_control(req, DSDB_CONTROL_FORCE_RODC_LOCAL_CHANGE)) {
+ && !is_forced_rodc) {
unsigned instanceType;
- ret = samdb_rodc(ldb, rodc);
- if (ret != LDB_SUCCESS) {
- DEBUG(4, (__location__ ": unable to tell if we are an RODC\n"));
- } else if (*rodc) {
+ if (*rodc) {
ldb_set_errstring(ldb, "RODC modify is forbidden!");
return LDB_ERR_REFERRAL;
}
for (i=0; i<msg->num_elements; i++) {
struct ldb_message_element *el = &msg->elements[i];
struct ldb_message_element *old_el, *new_el;
+ unsigned int mod_type = LDB_FLAG_MOD_TYPE(el->flags);
const struct dsdb_attribute *schema_attr
= dsdb_attribute_by_lDAPDisplayName(schema, el->name);
if (!schema_attr) {
return LDB_ERR_UNWILLING_TO_PERFORM;
}
old_el = ldb_msg_find_element(old_msg, el->name);
- switch (el->flags & LDB_FLAG_MOD_MASK) {
+ switch (mod_type) {
case LDB_FLAG_MOD_REPLACE:
ret = replmd_modify_la_replace(module, replmd_private,
schema, msg, el, old_el,
ldb_asprintf_errstring(ldb,
"Attribute %s is single valued but more than one value has been supplied",
el->name);
- return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
+ /* Return codes as found on Windows 2012r2 */
+ if (mod_type == LDB_FLAG_MOD_REPLACE) {
+ return LDB_ERR_CONSTRAINT_VIOLATION;
+ } else {
+ return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
+ }
} else {
el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
}
-
-
if (ret != LDB_SUCCESS) {
return ret;
}
goto failed;
}
- ret = dsdb_module_modify(ar->module, msg, DSDB_FLAG_OWN_MODULE, req);
+ /*
+ * We have to mark this as a replicated update otherwise
+ * schema_data may reject a rename in the schema partition
+ */
+
+ ret = dsdb_module_modify(ar->module, msg,
+ DSDB_FLAG_OWN_MODULE|DSDB_FLAG_REPLICATED_UPDATE,
+ req);
if (ret != LDB_SUCCESS) {
- DEBUG(0,(__location__ ": Failed to modify rDN/name of conflict DN '%s' - %s",
+ DEBUG(0,(__location__ ": Failed to modify rDN/name of DN being DRS renamed '%s' - %s",
ldb_dn_get_linearized(dn),
ldb_errstring(ldb_module_get_ctx(ar->module))));
return ret;
failed:
talloc_free(msg);
- DEBUG(0,(__location__ ": Failed to setup modify rDN/name of conflict DN '%s'",
+ DEBUG(0,(__location__ ": Failed to setup modify rDN/name of DN being DRS renamed '%s'",
ldb_dn_get_linearized(dn)));
return LDB_ERR_OPERATIONS_ERROR;
}
}
}
- if (DEBUGLVL(4)) {
+ if (DEBUGLVL(8)) {
struct GUID_txt_buf guid_txt;
- char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_ADD, msg);
- DEBUG(4, ("DRS replication add message of %s:\n%s\n",
+ char *s = ldb_ldif_message_redacted_string(ldb, ar,
+ LDB_CHANGETYPE_ADD,
+ msg);
+ DEBUG(8, ("DRS replication add message of %s:\n%s\n",
GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid, &guid_txt),
s));
talloc_free(s);
+ } else if (DEBUGLVL(4)) {
+ struct GUID_txt_buf guid_txt;
+ DEBUG(4, ("DRS replication add DN of %s is %s\n",
+ GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid, &guid_txt),
+ ldb_dn_get_linearized(msg->dn)));
}
-
remote_isDeleted = ldb_msg_find_attr_as_bool(msg,
"isDeleted", false);
rdn_val = ldb_dn_get_rdn_val(msg->dn);
ret = replmd_update_rpmd_rdn_attr(ldb, msg, rdn_val, NULL,
- md, ar, now, is_schema_nc);
+ md, ar, now, is_schema_nc,
+ false);
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);
}
}
- if (DEBUGLVL(5)) {
+ if (DEBUGLVL(8)) {
struct GUID_txt_buf guid_txt;
- char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_MODIFY, msg);
- DEBUG(5, ("Initial DRS replication modify message of %s is:\n%s\n"
+ char *s = ldb_ldif_message_redacted_string(ldb, ar,
+ LDB_CHANGETYPE_MODIFY, msg);
+ DEBUG(8, ("Initial DRS replication modify message of %s is:\n%s\n"
"%s\n"
"%s\n",
GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid, &guid_txt),
"incoming replPropertyMetaData",
rmd)));
talloc_free(s);
- }
+ } else if (DEBUGLVL(4)) {
+ struct GUID_txt_buf guid_txt;
+ DEBUG(4, ("Initial DRS replication modify DN of %s is: %s\n",
+ GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid,
+ &guid_txt),
+ ldb_dn_get_linearized(msg->dn)));
+ }
+
local_isDeleted = ldb_msg_find_attr_as_bool(ar->search_msg,
"isDeleted", false);
remote_isDeleted = ldb_msg_find_attr_as_bool(msg,
if (renamed) {
ret = replmd_update_rpmd_rdn_attr(ldb, msg, new_rdn, old_rdn,
- &nmd, ar, now, is_schema_nc);
+ &nmd, ar, now, is_schema_nc,
+ false);
if (ret != LDB_SUCCESS) {
ldb_asprintf_errstring(ldb, "%s: error during DRS repl merge: %s", __func__, ldb_errstring(ldb));
return replmd_replicated_request_error(ar, ret);
}
}
- if (DEBUGLVL(4)) {
+ if (DEBUGLVL(8)) {
struct GUID_txt_buf guid_txt;
- char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_MODIFY, msg);
- DEBUG(4, ("Final DRS replication modify message of %s:\n%s\n",
- GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid, &guid_txt),
+ char *s = ldb_ldif_message_redacted_string(ldb, ar,
+ LDB_CHANGETYPE_MODIFY,
+ msg);
+ DEBUG(8, ("Final DRS replication modify message of %s:\n%s\n",
+ GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid,
+ &guid_txt),
s));
talloc_free(s);
+ } else if (DEBUGLVL(4)) {
+ struct GUID_txt_buf guid_txt;
+
+ DEBUG(4, ("Final DRS replication modify DN of %s is %s\n",
+ GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid,
+ &guid_txt),
+ ldb_dn_get_linearized(msg->dn)));
}
ret = ldb_build_mod_req(&change_req,
return LDB_SUCCESS;
}
+/**
+ * Stores the linked attributes received in the replication chunk - these get
+ * applied at the end of the transaction. We also check that each linked
+ * attribute is valid, i.e. source and target objects are known.
+ */
+static int replmd_store_linked_attributes(struct replmd_replicated_request *ar)
+{
+ int ret = LDB_SUCCESS;
+ uint32_t i;
+ struct ldb_module *module = ar->module;
+ struct replmd_private *replmd_private =
+ talloc_get_type(ldb_module_get_private(module), struct replmd_private);
+ struct ldb_context *ldb;
+
+ ldb = ldb_module_get_ctx(module);
+
+ DEBUG(4,("linked_attributes_count=%u\n", ar->objs->linked_attributes_count));
+
+ /* save away the linked attributes for the end of the transaction */
+ for (i = 0; i < ar->objs->linked_attributes_count; i++) {
+ struct la_entry *la_entry;
+
+ if (replmd_private->la_ctx == NULL) {
+ replmd_private->la_ctx = talloc_new(replmd_private);
+ }
+ la_entry = talloc(replmd_private->la_ctx, struct la_entry);
+ if (la_entry == NULL) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ la_entry->la = talloc(la_entry, struct drsuapi_DsReplicaLinkedAttribute);
+ if (la_entry->la == NULL) {
+ talloc_free(la_entry);
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ *la_entry->la = ar->objs->linked_attributes[i];
+ la_entry->dsdb_repl_flags = ar->objs->dsdb_repl_flags;
+
+ /* we need to steal the non-scalars so they stay
+ around until the end of the transaction */
+ talloc_steal(la_entry->la, la_entry->la->identifier);
+ talloc_steal(la_entry->la, la_entry->la->value.blob);
+
+ ret = replmd_verify_linked_attribute(ar, la_entry);
+
+ if (ret != LDB_SUCCESS) {
+ break;
+ }
+
+ DLIST_ADD(replmd_private->la_list, la_entry);
+ }
+
+ return ret;
+}
+
static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar);
static int replmd_replicated_apply_next(struct replmd_replicated_request *ar)
struct GUID_txt_buf guid_str_buf;
if (ar->index_current >= ar->objs->num_objects) {
- /* done with it, go to next stage */
+
+ /*
+ * Now that we've applied all the objects, check the new linked
+ * attributes and store them (we apply them in .prepare_commit)
+ */
+ ret = replmd_store_linked_attributes(ar);
+
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ /* done applying objects, move on to the next stage */
return replmd_replicated_uptodate_vector(ar);
}
nrf_el->flags = LDB_FLAG_MOD_REPLACE;
if (CHECK_DEBUGLVL(4)) {
- char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_MODIFY, msg);
+ char *s = ldb_ldif_message_redacted_string(ldb, ar,
+ LDB_CHANGETYPE_MODIFY,
+ msg);
DEBUG(4, ("DRS replication uptodate modify message:\n%s\n", s));
talloc_free(s);
}
struct replmd_replicated_request *ar;
struct ldb_control **ctrls;
int ret;
- uint32_t i;
- struct replmd_private *replmd_private =
- talloc_get_type(ldb_module_get_private(module), struct replmd_private);
ldb = ldb_module_get_ctx(module);
ar->controls = req->controls;
req->controls = ctrls;
- DEBUG(4,("linked_attributes_count=%u\n", objs->linked_attributes_count));
+ return replmd_replicated_apply_next(ar);
+}
- /* save away the linked attributes for the end of the
- transaction */
- for (i=0; i<ar->objs->linked_attributes_count; i++) {
- struct la_entry *la_entry;
+/**
+ * Checks how to handle an missing target - either we need to fail the
+ * replication and retry with GET_TGT, ignore the link and continue, or try to
+ * add a partial link to an unknown target.
+ */
+static int replmd_allow_missing_target(struct ldb_module *module,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_dn *target_dn,
+ struct ldb_dn *source_dn,
+ struct GUID *guid,
+ uint32_t dsdb_repl_flags,
+ bool *ignore_link,
+ const char * missing_str)
+{
+ struct ldb_context *ldb = ldb_module_get_ctx(module);
+ bool is_in_same_nc;
- if (replmd_private->la_ctx == NULL) {
- replmd_private->la_ctx = talloc_new(replmd_private);
- }
- la_entry = talloc(replmd_private->la_ctx, struct la_entry);
- if (la_entry == NULL) {
- ldb_oom(ldb);
- return LDB_ERR_OPERATIONS_ERROR;
- }
- la_entry->la = talloc(la_entry, struct drsuapi_DsReplicaLinkedAttribute);
- if (la_entry->la == NULL) {
- talloc_free(la_entry);
- ldb_oom(ldb);
- return LDB_ERR_OPERATIONS_ERROR;
- }
- *la_entry->la = ar->objs->linked_attributes[i];
+ /*
+ * we may not be able to resolve link targets properly when
+ * dealing with subsets of objects, e.g. the source is a
+ * critical object and the target isn't
+ *
+ * TODO:
+ * When we implement Trusted Domains we need to consider
+ * whether they get treated as an incomplete replica here or not
+ */
+ if (dsdb_repl_flags & DSDB_REPL_FLAG_OBJECT_SUBSET) {
- /* we need to steal the non-scalars so they stay
- around until the end of the transaction */
- talloc_steal(la_entry->la, la_entry->la->identifier);
- talloc_steal(la_entry->la, la_entry->la->value.blob);
+ /*
+ * Ignore the link. We don't increase the highwater-mark in
+ * the object subset cases, so subsequent replications should
+ * resolve any missing links
+ */
+ DEBUG(2, ("%s target %s linked from %s\n", missing_str,
+ ldb_dn_get_linearized(target_dn),
+ ldb_dn_get_linearized(source_dn)));
+ *ignore_link = true;
+ return LDB_SUCCESS;
+ }
- DLIST_ADD(replmd_private->la_list, la_entry);
+ if (dsdb_repl_flags & DSDB_REPL_FLAG_TARGETS_UPTODATE) {
+
+ /*
+ * target should already be up-to-date so there's no point in
+ * retrying. This could be due to bad timing, or if a target
+ * on a one-way link was deleted. We ignore the link rather
+ * than failing the replication cycle completely
+ */
+ *ignore_link = true;
+ DBG_WARNING("%s is %s but up to date. Ignoring link from %s\n",
+ ldb_dn_get_linearized(target_dn), missing_str,
+ ldb_dn_get_linearized(source_dn));
+ return LDB_SUCCESS;
+ }
+
+ is_in_same_nc = dsdb_objects_have_same_nc(ldb,
+ mem_ctx,
+ source_dn,
+ target_dn);
+ if (is_in_same_nc) {
+ /* fail the replication and retry with GET_TGT */
+ ldb_asprintf_errstring(ldb, "%s target %s GUID %s linked from %s\n",
+ missing_str,
+ ldb_dn_get_linearized(target_dn),
+ GUID_string(mem_ctx, guid),
+ ldb_dn_get_linearized(source_dn));
+ return LDB_ERR_NO_SUCH_OBJECT;
}
+
+ /*
+ * The target of the cross-partition link is missing. Continue
+ * and try to at least add the forward-link. This isn't great,
+ * but a partial link can be fixed by dbcheck, so it's better
+ * than dropping the link completely.
+ */
+ DBG_WARNING("%s cross-partition target %s linked from %s\n",
+ missing_str, ldb_dn_get_linearized(target_dn),
+ ldb_dn_get_linearized(source_dn));
+ *ignore_link = false;
- return replmd_replicated_apply_next(ar);
+ return LDB_SUCCESS;
}
-/*
- process one linked attribute structure
+/**
+ * Checks that the target object for a linked attribute exists.
+ * @param guid returns the target object's GUID (is returned)if it exists)
+ * @param ignore_link set to true if the linked attribute should be ignored
+ * (i.e. the target doesn't exist, but that it's OK to skip the link)
*/
-static int replmd_process_linked_attribute(struct ldb_module *module,
- struct replmd_private *replmd_private,
- struct la_entry *la_entry,
- struct ldb_request *parent)
+static int replmd_check_target_exists(struct ldb_module *module,
+ struct dsdb_dn *dsdb_dn,
+ struct la_entry *la_entry,
+ struct ldb_dn *source_dn,
+ struct GUID *guid,
+ bool *ignore_link)
{
struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
struct ldb_context *ldb = ldb_module_get_ctx(module);
- struct ldb_message *msg;
- struct ldb_message *target_msg = NULL;
+ struct ldb_result *target_res;
TALLOC_CTX *tmp_ctx = talloc_new(la_entry);
- const struct dsdb_schema *schema = dsdb_get_schema(ldb, tmp_ctx);
+ const char *attrs[] = { "isDeleted", "isRecycled", NULL };
+ NTSTATUS ntstatus;
+ int ret;
+ enum deletion_state target_deletion_state = OBJECT_REMOVED;
+ bool active = (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE) ? true : false;
+
+ *ignore_link = false;
+ ntstatus = dsdb_get_extended_dn_guid(dsdb_dn->dn, guid, "GUID");
+
+ if (!NT_STATUS_IS_OK(ntstatus) && !active) {
+
+ /*
+ * This strange behaviour (allowing a NULL/missing
+ * GUID) originally comes from:
+ *
+ * commit e3054ce0fe0f8f62d2f5b2a77893e7a1479128bd
+ * Author: Andrew Tridgell <tridge@samba.org>
+ * Date: Mon Dec 21 21:21:55 2009 +1100
+ *
+ * s4-drs: cope better with NULL GUIDS from DRS
+ *
+ * It is valid to get a NULL GUID over DRS for a deleted forward link. We
+ * need to match by DN if possible when seeing if we should update an
+ * existing link.
+ *
+ * Pair-Programmed-With: Andrew Bartlett <abartlet@samba.org>
+ */
+ ret = dsdb_module_search_dn(module, tmp_ctx, &target_res,
+ dsdb_dn->dn, attrs,
+ DSDB_FLAG_NEXT_MODULE |
+ DSDB_SEARCH_SHOW_RECYCLED |
+ DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
+ DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
+ NULL);
+ } else if (!NT_STATUS_IS_OK(ntstatus)) {
+ ldb_asprintf_errstring(ldb, "Failed to find GUID in linked attribute 0x%x blob for %s from %s",
+ la->attid,
+ ldb_dn_get_linearized(dsdb_dn->dn),
+ ldb_dn_get_linearized(source_dn));
+ talloc_free(tmp_ctx);
+ return LDB_ERR_OPERATIONS_ERROR;
+ } else {
+ ret = dsdb_module_search(module, tmp_ctx, &target_res,
+ NULL, LDB_SCOPE_SUBTREE,
+ attrs,
+ DSDB_FLAG_NEXT_MODULE |
+ DSDB_SEARCH_SHOW_RECYCLED |
+ DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
+ DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
+ NULL,
+ "objectGUID=%s",
+ GUID_string(tmp_ctx, guid));
+ }
+
+ if (ret != LDB_SUCCESS) {
+ ldb_asprintf_errstring(ldb, "Failed to re-resolve GUID %s: %s\n",
+ GUID_string(tmp_ctx, guid),
+ ldb_errstring(ldb));
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+
+ if (target_res->count == 0) {
+
+ /*
+ * target object is unknown. Check whether to ignore the link,
+ * fail the replication, or add a partial link
+ */
+ ret = replmd_allow_missing_target(module, tmp_ctx, dsdb_dn->dn,
+ source_dn, guid,
+ la_entry->dsdb_repl_flags,
+ ignore_link, "Unknown");
+
+ } else if (target_res->count != 1) {
+ ldb_asprintf_errstring(ldb, "More than one object found matching objectGUID %s\n",
+ GUID_string(tmp_ctx, guid));
+ ret = LDB_ERR_OPERATIONS_ERROR;
+ } else {
+ struct ldb_message *target_msg = target_res->msgs[0];
+
+ dsdb_dn->dn = talloc_steal(dsdb_dn, target_msg->dn);
+
+ /* Get the object's state (i.e. Not Deleted, Tombstone, etc) */
+ replmd_deletion_state(module, target_msg,
+ &target_deletion_state, NULL);
+
+ /*
+ * Check for deleted objects as per MS-DRSR 4.1.10.6.14
+ * ProcessLinkValue(). Link updates should not be sent for
+ * recycled and tombstone objects (deleting the links should
+ * happen when we delete the object). This probably means our
+ * copy of the target object isn't up to date.
+ */
+ if (target_deletion_state >= OBJECT_RECYCLED) {
+
+ /*
+ * target object is deleted. Check whether to ignore the
+ * link, fail the replication, or add a partial link
+ */
+ ret = replmd_allow_missing_target(module, tmp_ctx,
+ dsdb_dn->dn,
+ source_dn, guid,
+ la_entry->dsdb_repl_flags,
+ ignore_link, "Deleted");
+ }
+ }
+
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+/**
+ * Extracts the key details about the source/target object for a
+ * linked-attribute entry.
+ * This returns the following details:
+ * @param ret_attr the schema details for the linked attribute
+ * @param source_msg the search result for the source object
+ * @param target_dsdb_dn the unpacked DN info for the target object
+ */
+static int replmd_extract_la_entry_details(struct ldb_module *module,
+ struct la_entry *la_entry,
+ TALLOC_CTX *mem_ctx,
+ const struct dsdb_attribute **ret_attr,
+ struct ldb_message **source_msg,
+ struct dsdb_dn **target_dsdb_dn)
+{
+ struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
+ struct ldb_context *ldb = ldb_module_get_ctx(module);
+ const struct dsdb_schema *schema = dsdb_get_schema(ldb, mem_ctx);
int ret;
const struct dsdb_attribute *attr;
- struct dsdb_dn *dsdb_dn;
- uint64_t seq_num = 0;
- struct ldb_message_element *old_el;
WERROR status;
- time_t t = time(NULL);
struct ldb_result *res;
- struct ldb_result *target_res;
const char *attrs[4];
- const char *attrs2[] = { "isDeleted", "isRecycled", NULL };
- struct parsed_dn *pdn_list, *pdn, *next;
- struct GUID guid = GUID_zero();
- NTSTATUS ntstatus;
- bool active = (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)?true:false;
-
- enum deletion_state deletion_state = OBJECT_NOT_DELETED;
- enum deletion_state target_deletion_state = OBJECT_NOT_DELETED;
/*
linked_attributes[0]:
la->attid,
GUID_buf_string(&la->identifier->guid,
&guid_str));
- talloc_free(tmp_ctx);
return LDB_ERR_OPERATIONS_ERROR;
}
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
- use this msg as the basis for a modify call */
- ret = dsdb_module_search(module, tmp_ctx, &res, NULL, LDB_SCOPE_SUBTREE, attrs,
+ /*
+ * get the existing message from the db for the object with
+ * this GUID, returning attribute being modified. We will then
+ * use this msg as the basis for a modify call
+ */
+ ret = dsdb_module_search(module, mem_ctx, &res, NULL, LDB_SCOPE_SUBTREE, attrs,
DSDB_FLAG_NEXT_MODULE |
DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
DSDB_SEARCH_SHOW_RECYCLED |
DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
DSDB_SEARCH_REVEAL_INTERNALS,
- parent,
- "objectGUID=%s", GUID_string(tmp_ctx, &la->identifier->guid));
+ NULL,
+ "objectGUID=%s", GUID_string(mem_ctx, &la->identifier->guid));
if (ret != LDB_SUCCESS) {
- talloc_free(tmp_ctx);
return ret;
}
if (res->count != 1) {
ldb_asprintf_errstring(ldb, "DRS linked attribute for GUID %s - DN not found",
- GUID_string(tmp_ctx, &la->identifier->guid));
- talloc_free(tmp_ctx);
+ GUID_string(mem_ctx, &la->identifier->guid));
return LDB_ERR_NO_SUCH_OBJECT;
}
- msg = res->msgs[0];
+
+ *source_msg = res->msgs[0];
+
+ /* the value blob for the attribute holds the target object DN */
+ status = dsdb_dn_la_from_blob(ldb, attr, schema, mem_ctx, la->value.blob, target_dsdb_dn);
+ if (!W_ERROR_IS_OK(status)) {
+ ldb_asprintf_errstring(ldb, "Failed to parsed linked attribute blob for %s on %s - %s\n",
+ attr->lDAPDisplayName,
+ ldb_dn_get_linearized(res->msgs[0]->dn),
+ win_errstr(status));
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ *ret_attr = attr;
+
+ return LDB_SUCCESS;
+}
+
+/**
+ * Verifies the source and target objects are known for a linked attribute
+ */
+static int replmd_verify_linked_attribute(struct replmd_replicated_request *ar,
+ struct la_entry *la)
+{
+ int ret = LDB_SUCCESS;
+ TALLOC_CTX *tmp_ctx = talloc_new(la);
+ struct ldb_module *module = ar->module;
+ struct ldb_message *src_msg;
+ const struct dsdb_attribute *attr;
+ struct dsdb_dn *tgt_dsdb_dn;
+ struct GUID guid = GUID_zero();
+ bool dummy;
+
+ ret = replmd_extract_la_entry_details(module, la, tmp_ctx, &attr,
+ &src_msg, &tgt_dsdb_dn);
+
+ /*
+ * When we fail to find the source object, the error code we pass
+ * back here is really important. It flags back to the callers to
+ * retry this request with DRSUAPI_DRS_GET_ANC. This case should
+ * never happen if we're replicating from a Samba DC, but it is
+ * needed to talk to a Windows DC
+ */
+ if (ret == LDB_ERR_NO_SUCH_OBJECT) {
+ ret = replmd_replicated_request_werror(ar, WERR_DS_DRA_MISSING_PARENT);
+ }
+
+ if (ret != LDB_SUCCESS) {
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+
+ ret = replmd_check_target_exists(module, tgt_dsdb_dn, la,
+ src_msg->dn, &guid, &dummy);
+
+ /*
+ * When we fail to find the target object, the error code we pass
+ * back here is really important. It flags back to the callers to
+ * retry this request with DRSUAPI_DRS_GET_TGT
+ */
+ if (ret == LDB_ERR_NO_SUCH_OBJECT) {
+ ret = replmd_replicated_request_werror(ar, WERR_DS_DRA_RECYCLED_TARGET);
+ }
+
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+/*
+ process one linked attribute structure
+ */
+static int replmd_process_linked_attribute(struct ldb_module *module,
+ struct replmd_private *replmd_private,
+ struct la_entry *la_entry,
+ struct ldb_request *parent)
+{
+ struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
+ struct ldb_context *ldb = ldb_module_get_ctx(module);
+ struct ldb_message *msg;
+ TALLOC_CTX *tmp_ctx = talloc_new(la_entry);
+ const struct dsdb_schema *schema = dsdb_get_schema(ldb, tmp_ctx);
+ int ret;
+ const struct dsdb_attribute *attr;
+ struct dsdb_dn *dsdb_dn;
+ uint64_t seq_num = 0;
+ struct ldb_message_element *old_el;
+ time_t t = time(NULL);
+ struct parsed_dn *pdn_list, *pdn, *next;
+ struct GUID guid = GUID_zero();
+ bool active = (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)?true:false;
+ bool ignore_link;
+ enum deletion_state deletion_state = OBJECT_NOT_DELETED;
+
+ /*
+ * get the attribute being modified, the search result for the source object,
+ * and the target object's DN details
+ */
+ ret = replmd_extract_la_entry_details(module, la_entry, tmp_ctx, &attr,
+ &msg, &dsdb_dn);
+
+ if (ret != LDB_SUCCESS) {
+ talloc_free(tmp_ctx);
+ return ret;
+ }
/*
- * Check for deleted objects per MS-DRSR 4.1.10.6.13
+ * Check for deleted objects per MS-DRSR 4.1.10.6.14
* ProcessLinkValue, because link updates are not applied to
* recycled and tombstone objects. We don't have to delete
* any existing link, that should have happened when the
* object deletion was replicated or initiated.
*/
-
replmd_deletion_state(module, msg, &deletion_state, NULL);
if (deletion_state >= OBJECT_RECYCLED) {
return ret;
}
- status = dsdb_dn_la_from_blob(ldb, attr, schema, tmp_ctx, la->value.blob, &dsdb_dn);
- if (!W_ERROR_IS_OK(status)) {
- ldb_asprintf_errstring(ldb, "Failed to parsed linked attribute blob for %s on %s - %s\n",
- old_el->name, ldb_dn_get_linearized(msg->dn), win_errstr(status));
- talloc_free(tmp_ctx);
- return LDB_ERR_OPERATIONS_ERROR;
- }
-
- ntstatus = dsdb_get_extended_dn_guid(dsdb_dn->dn, &guid, "GUID");
- if (!NT_STATUS_IS_OK(ntstatus) && !active) {
- /*
- * This strange behaviour (allowing a NULL/missing
- * GUID) originally comes from:
- *
- * commit e3054ce0fe0f8f62d2f5b2a77893e7a1479128bd
- * Author: Andrew Tridgell <tridge@samba.org>
- * Date: Mon Dec 21 21:21:55 2009 +1100
- *
- * s4-drs: cope better with NULL GUIDS from DRS
- *
- * It is valid to get a NULL GUID over DRS for a deleted forward link. We
- * need to match by DN if possible when seeing if we should update an
- * existing link.
- *
- * Pair-Programmed-With: Andrew Bartlett <abartlet@samba.org>
- */
-
- ret = dsdb_module_search_dn(module, tmp_ctx, &target_res,
- dsdb_dn->dn, attrs2,
- DSDB_FLAG_NEXT_MODULE |
- DSDB_SEARCH_SHOW_RECYCLED |
- DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
- DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
- parent);
- } else if (!NT_STATUS_IS_OK(ntstatus)) {
- ldb_asprintf_errstring(ldb, "Failed to find GUID in linked attribute blob for %s on %s from %s",
- old_el->name,
- ldb_dn_get_linearized(dsdb_dn->dn),
- ldb_dn_get_linearized(msg->dn));
- talloc_free(tmp_ctx);
- return LDB_ERR_OPERATIONS_ERROR;
- } else {
- ret = dsdb_module_search(module, tmp_ctx, &target_res,
- NULL, LDB_SCOPE_SUBTREE,
- attrs2,
- DSDB_FLAG_NEXT_MODULE |
- DSDB_SEARCH_SHOW_RECYCLED |
- DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
- DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
- parent,
- "objectGUID=%s",
- GUID_string(tmp_ctx, &guid));
- }
+ ret = replmd_check_target_exists(module, dsdb_dn, la_entry, msg->dn,
+ &guid, &ignore_link);
if (ret != LDB_SUCCESS) {
- ldb_asprintf_errstring(ldb_module_get_ctx(module), "Failed to re-resolve GUID %s: %s\n",
- GUID_string(tmp_ctx, &guid),
- ldb_errstring(ldb_module_get_ctx(module)));
talloc_free(tmp_ctx);
return ret;
}
- if (target_res->count == 0) {
- DEBUG(2,(__location__ ": WARNING: Failed to re-resolve GUID %s - using %s\n",
- GUID_string(tmp_ctx, &guid),
- ldb_dn_get_linearized(dsdb_dn->dn)));
- } else if (target_res->count != 1) {
- ldb_asprintf_errstring(ldb_module_get_ctx(module), "More than one object found matching objectGUID %s\n",
- GUID_string(tmp_ctx, &guid));
- talloc_free(tmp_ctx);
- return LDB_ERR_OPERATIONS_ERROR;
- } else {
- target_msg = target_res->msgs[0];
- dsdb_dn->dn = talloc_steal(dsdb_dn, target_msg->dn);
- }
-
/*
- * Check for deleted objects per MS-DRSR 4.1.10.6.13
- * ProcessLinkValue, because link updates are not applied to
- * recycled and tombstone objects. We don't have to delete
- * any existing link, that should have happened when the
- * object deletion was replicated or initiated.
+ * there are some cases where the target object doesn't exist, but it's
+ * OK to ignore the linked attribute
*/
- replmd_deletion_state(module, target_msg,
- &target_deletion_state, NULL);
-
- if (target_deletion_state >= OBJECT_RECYCLED) {
+ if (ignore_link) {
talloc_free(tmp_ctx);
- return LDB_SUCCESS;
+ return ret;
}
/* see if this link already exists */
if (ret != LDB_SUCCESS) {
ldb_debug(ldb, LDB_DEBUG_WARNING, "Failed to apply linked attribute change '%s'\n%s\n",
ldb_errstring(ldb),
- ldb_ldif_message_string(ldb, tmp_ctx, LDB_CHANGETYPE_MODIFY, msg));
+ ldb_ldif_message_redacted_string(ldb,
+ tmp_ctx,
+ LDB_CHANGETYPE_MODIFY,
+ msg));
talloc_free(tmp_ctx);
return ret;
}