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){
{
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);
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);
}
/*
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));
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);