a1 = dsdb_attribute_by_lDAPDisplayName(schema, e1->name);
a2 = dsdb_attribute_by_lDAPDisplayName(schema, e2->name);
- /*
- * TODO: remove this check, we should rely on e1 and e2 having valid attribute names
- * in the schema
- */
- if (!a1 || !a2) {
- return strcasecmp(e1->name, e2->name);
- }
if (a1->attributeID_id == a2->attributeID_id) {
return 0;
}
bool is_urgent = false, rodc = false;
unsigned int functional_level;
const DATA_BLOB *guid_blob;
+ struct ldb_control *sd_propagation_control;
/* do not manipulate our control entries */
if (ldb_dn_is_special(req->op.mod.message->dn)) {
return ldb_next_request(module, req);
}
+ sd_propagation_control = ldb_request_get_control(req,
+ DSDB_CONTROL_SEC_DESC_PROPAGATION_OID);
+ if (sd_propagation_control != NULL) {
+ if (req->op.mod.message->num_elements != 1) {
+ return ldb_module_operr(module);
+ }
+ ret = strcmp(req->op.mod.message->elements[0].name,
+ "nTSecurityDescriptor");
+ if (ret != 0) {
+ return ldb_module_operr(module);
+ }
+
+ return ldb_next_request(module, req);
+ }
+
ldb = ldb_module_get_ctx(module);
ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_modify\n");
talloc_free(tmp_ctx);
return ret;
}
- msg->elements[el_count++].flags = LDB_FLAG_MOD_ADD;
+ msg->elements[el_count++].flags = LDB_FLAG_MOD_REPLACE;
}
switch (next_deletion_state){
callback for conflict DN handling where we have renamed the incoming
record. After renaming it, we need to ensure the change of name and
rDN for the incoming record is seen as an originating update by this DC.
+
+ This also handles updating lastKnownParent for entries sent to lostAndFound
*/
static int replmd_op_name_modify_callback(struct ldb_request *req, struct ldb_reply *ares)
{
struct replmd_replicated_request *ar =
talloc_get_type_abort(req->context, struct replmd_replicated_request);
+ struct ldb_dn *conflict_dn;
int ret;
if (ares->error != LDB_SUCCESS) {
return replmd_op_callback(req, ares);
}
+ switch (req->operation) {
+ case LDB_ADD:
+ conflict_dn = req->op.add.message->dn;
+ break;
+ case LDB_MODIFY:
+ conflict_dn = req->op.mod.message->dn;
+ break;
+ default:
+ smb_panic("replmd_op_name_modify_callback called in unknown circumstances");
+ }
+
/* perform a modify of the rDN and name of the record */
- ret = replmd_name_modify(ar, req, req->op.add.message->dn);
+ ret = replmd_name_modify(ar, req, conflict_dn);
if (ret != LDB_SUCCESS) {
ares->error = ret;
return replmd_op_callback(req, ares);
}
+ if (ar->objs->objects[ar->index_current].last_known_parent) {
+ struct ldb_message *msg = ldb_msg_new(req);
+ if (msg == NULL) {
+ ldb_module_oom(ar->module);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ msg->dn = req->op.add.message->dn;
+
+ ret = ldb_msg_add_steal_string(msg, "lastKnownParent",
+ ldb_dn_get_extended_linearized(msg, ar->objs->objects[ar->index_current].last_known_parent, 1));
+ if (ret != LDB_SUCCESS) {
+ DEBUG(0,(__location__ ": Failed to add lastKnownParent string to the msg\n"));
+ ldb_module_oom(ar->module);
+ return ret;
+ }
+ msg->elements[0].flags = LDB_FLAG_MOD_REPLACE;
+
+ ret = dsdb_module_modify(ar->module, msg, DSDB_FLAG_OWN_MODULE, req);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(0,(__location__ ": Failed to modify lastKnownParent of lostAndFound DN '%s' - %s",
+ ldb_dn_get_linearized(msg->dn),
+ ldb_errstring(ldb_module_get_ctx(ar->module))));
+ return ret;
+ }
+ TALLOC_FREE(msg);
+ }
+
return replmd_op_callback(req, ares);
}
struct ldb_result *res;
const char *attrs[] = { "replPropertyMetaData", "objectGUID", NULL };
int ret;
- const struct ldb_val *rmd_value, *omd_value;
- struct replPropertyMetaDataBlob omd, rmd;
+ const struct ldb_val *omd_value;
+ struct replPropertyMetaDataBlob omd, *rmd;
enum ndr_err_code ndr_err;
bool rename_incoming_record, rodc;
struct replPropertyMetaData1 *rmd_name, *omd_name;
+ struct ldb_message *msg;
+
+ req->callback = callback;
if (ares->error != LDB_ERR_ENTRY_ALREADY_EXISTS) {
/* call the normal callback for everything except
conflicts */
- return callback(req, ares);
+ return ldb_module_done(req, ares->controls, ares->response, ares->error);
}
ret = samdb_rodc(ldb_module_get_ctx(ar->module), &rodc);
if (ret != LDB_SUCCESS) {
- return ret;
+ ldb_asprintf_errstring(ldb_module_get_ctx(ar->module), "Failed to determine if we are an RODC when attempting to form conflict DN: %s", ldb_errstring(ldb_module_get_ctx(ar->module)));
+ return ldb_module_done(req, ares->controls, ares->response, LDB_ERR_OPERATIONS_ERROR);
}
- /*
+ /*
* we have a conflict, and need to decide if we will keep the
* new record or the old record
*/
+ msg = ar->objs->objects[ar->index_current].msg;
+
switch (req->operation) {
case LDB_ADD:
- conflict_dn = req->op.add.message->dn;
+ conflict_dn = msg->dn;
break;
case LDB_RENAME:
conflict_dn = req->op.rename.newdn;
break;
default:
- return ldb_module_operr(ar->module);
+ return ldb_module_done(req, ares->controls, ares->response, ldb_module_operr(ar->module));
}
if (rodc) {
goto failed;
}
- /*
- * and the replPropertyMetaData attribute from the
- * new record
- */
- rmd_value = ldb_msg_find_ldb_val(req->op.add.message, "replPropertyMetaData");
- if (rmd_value == NULL) {
- DEBUG(0,(__location__ ": Unable to find replPropertyMetaData for new record '%s'\n",
- ldb_dn_get_linearized(conflict_dn)));
- goto failed;
- }
-
- ndr_err = ndr_pull_struct_blob(rmd_value, req, &rmd,
- (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
- if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
- DEBUG(0,(__location__ ": Failed to parse new replPropertyMetaData for %s\n",
- ldb_dn_get_linearized(conflict_dn)));
- goto failed;
- }
+ rmd = ar->objs->objects[ar->index_current].meta_data;
/* we decide which is newer based on the RPMD on the name
attribute. See [MS-DRSR] ResolveNameConflict */
- rmd_name = replmd_replPropertyMetaData1_find_attid(&rmd, DRSUAPI_ATTID_name);
+ rmd_name = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
omd_name = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
if (!rmd_name || !omd_name) {
DEBUG(0,(__location__ ": Failed to find name attribute in replPropertyMetaData for %s\n",
struct ldb_dn *new_dn;
struct ldb_message *new_msg;
+ /*
+ * We want to run the original callback here, which
+ * will return LDB_ERR_ENTRY_ALREADY_EXISTS to the
+ * caller, which will in turn know to rename the
+ * incoming record. The error string is set in case
+ * this isn't handled properly at some point in the
+ * future.
+ */
if (req->operation == LDB_RENAME) {
- DEBUG(0,(__location__ ": Unable to handle incoming renames where this would "
- "create a conflict. Incoming record is %s\n",
- ldb_dn_get_extended_linearized(req, conflict_dn, 1)));
+ ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
+ "Unable to handle incoming renames where this would "
+ "create a conflict. Incoming record is %s (caller to handle)\n",
+ ldb_dn_get_extended_linearized(req, conflict_dn, 1));
+
goto failed;
}
- guid = samdb_result_guid(req->op.add.message, "objectGUID");
+ guid = samdb_result_guid(msg, "objectGUID");
if (GUID_all_zero(&guid)) {
DEBUG(0,(__location__ ": Failed to find objectGUID for conflicting incoming record %s\n",
ldb_dn_get_linearized(conflict_dn)));
DEBUG(1,(__location__ ": Resolving conflict record via incoming rename '%s' -> '%s'\n",
ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
- switch (req->operation) {
- case LDB_ADD:
- /* re-submit the request, but with a different
- callback, so we don't loop forever. */
- new_msg = ldb_msg_copy_shallow(req, req->op.add.message);
- if (!new_msg) {
- goto failed;
- DEBUG(0,(__location__ ": Failed to copy conflict DN message for %s\n",
- ldb_dn_get_linearized(conflict_dn)));
- }
- new_msg->dn = new_dn;
- req->op.add.message = new_msg;
- req->callback = replmd_op_name_modify_callback;
- break;
- case LDB_RENAME:
- req->op.rename.newdn = new_dn;
- req->callback = callback;
- break;
- default:
- return ldb_module_operr(ar->module);
+ /* re-submit the request, but with a different
+ callback, so we don't loop forever. */
+ new_msg = ldb_msg_copy_shallow(req, msg);
+ if (!new_msg) {
+ goto failed;
+ DEBUG(0,(__location__ ": Failed to copy conflict DN message for %s\n",
+ ldb_dn_get_linearized(conflict_dn)));
}
+ new_msg->dn = new_dn;
+ req->op.add.message = new_msg;
+ req->callback = replmd_op_name_modify_callback;
return ldb_next_request(ar->module, req);
} else {
goto failed;
}
- req->callback = callback;
-
return ldb_next_request(ar->module, req);
}
* will stop with an error, but there is not much else we can
* do
*/
- return callback(req, ares);
+ return ldb_module_done(req, ares->controls, ares->response, ares->error);
}
/*
*/
static int replmd_op_add_callback(struct ldb_request *req, struct ldb_reply *ares)
{
+ struct replmd_replicated_request *ar =
+ talloc_get_type_abort(req->context, struct replmd_replicated_request);
+
+ if (ar->objs->objects[ar->index_current].last_known_parent) {
+ /* This is like a conflict DN, where we put the object in LostAndFound
+ see MS-DRSR 4.1.10.6.10 FindBestParentObject */
+ return replmd_op_possible_conflict_callback(req, ares, replmd_op_name_modify_callback);
+ }
+
return replmd_op_possible_conflict_callback(req, ares, replmd_op_callback);
}
struct ldb_val md_value;
unsigned int i;
int ret;
+ bool remote_isDeleted = false;
/*
* TODO: check if the parent object exist
}
}
+ remote_isDeleted = ldb_msg_find_attr_as_bool(msg,
+ "isDeleted", false);
+
/*
* the meta data array is already sorted by the caller
*/
replmd_ldb_message_sort(msg, ar->schema);
+ if (!remote_isDeleted) {
+ ret = dsdb_module_schedule_sd_propagation(ar->module,
+ msg->dn, true);
+ if (ret != LDB_SUCCESS) {
+ return replmd_replicated_request_error(ar, ret);
+ }
+ }
+
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));
ldb_dn_get_linearized(parent_msg->dn));
return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
}
+ ar->objs->objects[ar->index_current].last_known_parent
+ = talloc_steal(ar->objs->objects[ar->index_current].msg, parent_msg->dn);
} else {
parent_dn = parent_msg->dn;
}
ar->req);
LDB_REQ_SET_LOCATION(search_req);
- ret = ldb_request_add_control(search_req, LDB_CONTROL_SHOW_RECYCLED_OID,
- true, NULL);
+ ret = dsdb_request_add_controls(search_req,
+ DSDB_SEARCH_SHOW_RECYCLED|
+ DSDB_SEARCH_SHOW_DELETED|
+ DSDB_SEARCH_SHOW_EXTENDED_DN);
if (ret != LDB_SUCCESS) {
return ret;
}
uint32_t j,ni=0;
unsigned int removed_attrs = 0;
int ret;
+ int (*callback)(struct ldb_request *req, struct ldb_reply *ares) = replmd_op_callback;
+ bool isDeleted = false;
+ bool local_isDeleted = false;
+ bool remote_isDeleted = false;
+ bool take_remote_isDeleted = false;
+ bool sd_updated = false;
ldb = ldb_module_get_ctx(ar->module);
msg = ar->objs->objects[ar->index_current].msg;
+
rmd = ar->objs->objects[ar->index_current].meta_data;
ZERO_STRUCT(omd);
omd.version = 1;
}
}
+ local_isDeleted = ldb_msg_find_attr_as_bool(ar->search_msg,
+ "isDeleted", false);
+ remote_isDeleted = ldb_msg_find_attr_as_bool(msg,
+ "isDeleted", false);
+
/* handle renames that come in over DRS */
ret = replmd_replicated_handle_rename(ar, msg, rmd, &omd, ar->req);
- if (ret != LDB_SUCCESS) {
+
+ /*
+ * This particular error code means that we already tried the
+ * conflict algrorithm, and the existing record name was newer, so we
+ * need to rename the incoming record
+ */
+ if (ret == LDB_ERR_ENTRY_ALREADY_EXISTS) {
+ struct GUID guid;
+ NTSTATUS status;
+ struct ldb_dn *new_dn;
+ status = GUID_from_ndr_blob(&ar->objs->objects[ar->index_current].guid_value, &guid);
+ /* This really, really can't fail */
+ SMB_ASSERT(NT_STATUS_IS_OK(status));
+
+ new_dn = replmd_conflict_dn(msg, msg->dn, &guid);
+ if (new_dn == NULL) {
+ ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
+ "Failed to form conflict DN for %s\n",
+ ldb_dn_get_linearized(msg->dn));
+
+ return replmd_replicated_request_werror(ar, WERR_NOMEM);
+ }
+
+ ret = dsdb_module_rename(ar->module, ar->search_msg->dn, new_dn,
+ DSDB_FLAG_NEXT_MODULE, ar->req);
+ if (ret != LDB_SUCCESS) {
+ ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
+ "Failed to rename incoming conflicting dn '%s' (was '%s') to '%s' - %s\n",
+ ldb_dn_get_linearized(msg->dn),
+ ldb_dn_get_linearized(ar->search_msg->dn),
+ ldb_dn_get_linearized(new_dn),
+ ldb_errstring(ldb_module_get_ctx(ar->module)));
+ return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
+ }
+
+ /* Set the callback to one that will fix up the name to be a conflict DN */
+ callback = replmd_op_name_modify_callback;
+ msg->dn = new_dn;
+ } else if (ret != LDB_SUCCESS) {
ldb_debug(ldb, LDB_DEBUG_FATAL,
"replmd_replicated_request rename %s => %s failed - %s\n",
ldb_dn_get_linearized(ar->search_msg->dn),
}
}
nmd.ctr.ctr1.array[j].local_usn = ar->seq_num;
+ switch (nmd.ctr.ctr1.array[j].attid) {
+ case DRSUAPI_ATTID_ntSecurityDescriptor:
+ sd_updated = true;
+ break;
+ case DRSUAPI_ATTID_isDeleted:
+ take_remote_isDeleted = true;
+ break;
+ default:
+ break;
+ }
found = true;
break;
}
}
}
nmd.ctr.ctr1.array[ni].local_usn = ar->seq_num;
+ switch (nmd.ctr.ctr1.array[ni].attid) {
+ case DRSUAPI_ATTID_ntSecurityDescriptor:
+ sd_updated = true;
+ break;
+ case DRSUAPI_ATTID_isDeleted:
+ take_remote_isDeleted = true;
+ break;
+ default:
+ break;
+ }
ni++;
}
ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: replace %u attributes\n",
ar->index_current, msg->num_elements);
+ if (take_remote_isDeleted) {
+ isDeleted = remote_isDeleted;
+ } else {
+ isDeleted = local_isDeleted;
+ }
+
+ if (sd_updated && !isDeleted) {
+ ret = dsdb_module_schedule_sd_propagation(ar->module,
+ msg->dn, true);
+ if (ret != LDB_SUCCESS) {
+ return ldb_operr(ldb);
+ }
+ }
+
/* create the meta data value */
ndr_err = ndr_push_struct_blob(&nmd_value, msg, &nmd,
(ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
msg,
ar->controls,
ar,
- replmd_op_callback,
+ callback,
ar->req);
LDB_REQ_SET_LOCATION(change_req);
if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
break;
case LDB_REPLY_DONE:
+ ar->objs->objects[ar->index_current].last_known_parent = NULL;
+
if (ar->search_msg != NULL) {
ret = replmd_replicated_apply_merge(ar);
} else {