summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source4/dsdb/samdb/ldb_modules/repl_meta_data.c244
1 files changed, 224 insertions, 20 deletions
diff --git a/source4/dsdb/samdb/ldb_modules/repl_meta_data.c b/source4/dsdb/samdb/ldb_modules/repl_meta_data.c
index 249dd61a4a..7488a1bee9 100644
--- a/source4/dsdb/samdb/ldb_modules/repl_meta_data.c
+++ b/source4/dsdb/samdb/ldb_modules/repl_meta_data.c
@@ -53,6 +53,8 @@
struct replmd_private {
TALLOC_CTX *la_ctx;
struct la_entry *la_list;
+ TALLOC_CTX *bl_ctx;
+ struct la_backlink *la_backlinks;
struct nc_entry {
struct nc_entry *prev, *next;
struct ldb_dn *dn;
@@ -87,6 +89,7 @@ struct replmd_replicated_request {
static int replmd_replicated_apply_next(struct replmd_replicated_request *ar);
+
/*
initialise the module
allocate the private structure and build the list
@@ -107,6 +110,169 @@ static int replmd_init(struct ldb_module *module)
return ldb_next_init(module);
}
+/*
+ cleanup our per-transaction contexts
+ */
+static void replmd_txn_cleanup(struct replmd_private *replmd_private)
+{
+ talloc_free(replmd_private->la_ctx);
+ replmd_private->la_list = NULL;
+ replmd_private->la_ctx = NULL;
+
+ talloc_free(replmd_private->bl_ctx);
+ replmd_private->la_backlinks = NULL;
+ replmd_private->bl_ctx = NULL;
+}
+
+
+struct la_backlink {
+ struct la_backlink *next, *prev;
+ const char *attr_name;
+ struct GUID forward_guid, target_guid;
+ bool active;
+};
+
+/*
+ add a backlink to the list of backlinks to add/delete in the prepare
+ commit
+ */
+static int replmd_add_backlink(struct ldb_module *module, const struct dsdb_schema *schema,
+ struct GUID *forward_guid, struct GUID *target_guid,
+ bool active, const struct dsdb_attribute *schema_attr)
+{
+ const struct dsdb_attribute *target_attr;
+ struct la_backlink *bl;
+ struct replmd_private *replmd_private =
+ talloc_get_type_abort(ldb_module_get_private(module), struct replmd_private);
+
+ target_attr = dsdb_attribute_by_linkID(schema, schema_attr->linkID + 1);
+ if (!target_attr) {
+ /*
+ * windows 2003 has a broken schema where the
+ * definition of msDS-IsDomainFor is missing (which is
+ * supposed to be the backlink of the
+ * msDS-HasDomainNCs attribute
+ */
+ return LDB_SUCCESS;
+ }
+
+ /* see if its already in the list */
+ for (bl=replmd_private->la_backlinks; bl; bl=bl->next) {
+ if (GUID_equal(forward_guid, &bl->forward_guid) &&
+ GUID_equal(target_guid, &bl->target_guid) &&
+ (target_attr->lDAPDisplayName == bl->attr_name ||
+ strcmp(target_attr->lDAPDisplayName, bl->attr_name) == 0)) {
+ break;
+ }
+ }
+
+ if (bl) {
+ /* we found an existing one */
+ if (bl->active == active) {
+ return LDB_SUCCESS;
+ }
+ DLIST_REMOVE(replmd_private->la_backlinks, bl);
+ talloc_free(bl);
+ return LDB_SUCCESS;
+ }
+
+ if (replmd_private->bl_ctx == NULL) {
+ replmd_private->bl_ctx = talloc_new(replmd_private);
+ if (replmd_private->bl_ctx == NULL) {
+ ldb_module_oom(module);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ }
+
+ /* its a new one */
+ bl = talloc(replmd_private->bl_ctx, struct la_backlink);
+ if (bl == NULL) {
+ ldb_module_oom(module);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ bl->attr_name = target_attr->lDAPDisplayName;
+ bl->forward_guid = *forward_guid;
+ bl->target_guid = *target_guid;
+ bl->active = active;
+
+ DLIST_ADD(replmd_private->la_backlinks, bl);
+
+ return LDB_SUCCESS;
+}
+
+/*
+ process the list of backlinks we accumulated during
+ a transaction, adding and deleting the backlinks
+ from the target objects
+ */
+static int replmd_process_backlink(struct ldb_module *module, struct la_backlink *bl)
+{
+ struct ldb_dn *target_dn, *source_dn;
+ int ret;
+ struct ldb_context *ldb = ldb_module_get_ctx(module);
+ struct ldb_message *msg;
+ TALLOC_CTX *tmp_ctx = talloc_new(bl);
+ char *dn_string;
+
+ /*
+ - find DN of target
+ - find DN of source
+ - construct ldb_message
+ - either an add or a delete
+ */
+ ret = dsdb_module_dn_by_guid(module, tmp_ctx, &bl->target_guid, &target_dn);
+ if (ret != LDB_SUCCESS) {
+ ldb_asprintf_errstring(ldb, "Failed to find target DN for linked attribute with GUID %s\n",
+ GUID_string(bl, &bl->target_guid));
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+
+ ret = dsdb_module_dn_by_guid(module, tmp_ctx, &bl->forward_guid, &source_dn);
+ if (ret != LDB_SUCCESS) {
+ ldb_asprintf_errstring(ldb, "Failed to find source DN for linked attribute with GUID %s\n",
+ GUID_string(bl, &bl->forward_guid));
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+
+ msg = ldb_msg_new(tmp_ctx);
+ if (msg == NULL) {
+ ldb_module_oom(module);
+ talloc_free(tmp_ctx);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* construct a ldb_message for adding/deleting the backlink */
+ msg->dn = target_dn;
+ dn_string = ldb_dn_get_extended_linearized(tmp_ctx, source_dn, 1);
+ if (!dn_string) {
+ ldb_module_oom(module);
+ talloc_free(tmp_ctx);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ ret = ldb_msg_add_steal_string(msg, bl->attr_name, dn_string);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+ msg->elements[0].flags = bl->active?LDB_FLAG_MOD_ADD:LDB_FLAG_MOD_DELETE;
+
+ ret = dsdb_module_modify(module, msg, 0);
+ if (ret != LDB_SUCCESS) {
+ ldb_asprintf_errstring(ldb, "Failed to %s backlink from %s to %s - %s",
+ bl->active?"add":"remove",
+ ldb_dn_get_linearized(source_dn),
+ ldb_dn_get_linearized(target_dn),
+ ldb_errstring(ldb));
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
/*
* Callback for most write operations in this module:
@@ -1097,12 +1263,14 @@ static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct p
handle adding a linked attribute
*/
static int replmd_modify_la_add(struct ldb_module *module,
+ struct dsdb_schema *schema,
struct ldb_message *msg,
struct ldb_message_element *el,
struct ldb_message_element *old_el,
const struct dsdb_attribute *schema_attr,
uint64_t seq_num,
- time_t t)
+ time_t t,
+ struct GUID *msg_guid)
{
int i;
struct parsed_dn *dns, *old_dns;
@@ -1166,6 +1334,12 @@ static int replmd_modify_la_add(struct ldb_module *module,
return ret;
}
}
+
+ ret = replmd_add_backlink(module, schema, msg_guid, dns[i].guid, true, schema_attr);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(tmp_ctx);
+ return ret;
+ }
}
/* add the new ones on to the end of the old values, constructing a new el->values */
@@ -1197,12 +1371,14 @@ static int replmd_modify_la_add(struct ldb_module *module,
handle deleting all active linked attributes
*/
static int replmd_modify_la_delete(struct ldb_module *module,
+ struct dsdb_schema *schema,
struct ldb_message *msg,
struct ldb_message_element *el,
struct ldb_message_element *old_el,
const struct dsdb_attribute *schema_attr,
uint64_t seq_num,
- time_t t)
+ time_t t,
+ struct GUID *msg_guid)
{
int i;
struct parsed_dn *dns, *old_dns;
@@ -1280,6 +1456,12 @@ static int replmd_modify_la_delete(struct ldb_module *module,
talloc_free(tmp_ctx);
return ret;
}
+
+ ret = replmd_add_backlink(module, schema, msg_guid, old_dns[i].guid, false, schema_attr);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(tmp_ctx);
+ return ret;
+ }
}
el->values = talloc_steal(msg->elements, old_el->values);
@@ -1298,12 +1480,14 @@ static int replmd_modify_la_delete(struct ldb_module *module,
handle replacing a linked attribute
*/
static int replmd_modify_la_replace(struct ldb_module *module,
+ struct dsdb_schema *schema,
struct ldb_message *msg,
struct ldb_message_element *el,
struct ldb_message_element *old_el,
const struct dsdb_attribute *schema_attr,
uint64_t seq_num,
- time_t t)
+ time_t t,
+ struct GUID *msg_guid)
{
int i;
struct parsed_dn *dns, *old_dns;
@@ -1344,13 +1528,20 @@ static int replmd_modify_la_replace(struct ldb_module *module,
struct parsed_dn *p;
const struct ldb_val *v;
+ v = ldb_dn_get_extended_component(old_p->dsdb_dn->dn, "DELETED");
+ if (v) continue;
+
+ ret = replmd_add_backlink(module, schema, msg_guid, old_dns[i].guid, false, schema_attr);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+
p = parsed_dn_find(dns, el->num_values, old_p->guid);
if (p) {
/* we don't delete it if we are re-adding it */
continue;
}
- v = ldb_dn_get_extended_component(old_p->dsdb_dn->dn, "DELETED");
- if (v) continue;
ret = replmd_update_la_val(old_el->values, old_p->v, old_p, old_p,
invocation_id, seq_num, t, true);
@@ -1394,6 +1585,12 @@ static int replmd_modify_la_replace(struct ldb_module *module,
}
num_new_values++;
}
+
+ ret = replmd_add_backlink(module, schema, msg_guid, dns[i].guid, true, schema_attr);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(tmp_ctx);
+ return ret;
+ }
}
/* add the new values to the end of old_el */
@@ -1436,6 +1633,7 @@ static int replmd_modify_handle_linked_attribs(struct ldb_module *module,
struct ldb_context *ldb = ldb_module_get_ctx(module);
struct ldb_message *old_msg;
struct dsdb_schema *schema = dsdb_get_schema(ldb);
+ struct GUID old_guid;
if (seq_num == 0) {
/* there the replmd_update_rpmd code has already
@@ -1462,6 +1660,8 @@ static int replmd_modify_handle_linked_attribs(struct ldb_module *module,
}
old_msg = res->msgs[0];
+ old_guid = samdb_result_guid(old_msg, "objectGUID");
+
for (i=0; i<msg->num_elements; i++) {
struct ldb_message_element *el = &msg->elements[i];
struct ldb_message_element *old_el, *new_el;
@@ -1484,13 +1684,13 @@ static int replmd_modify_handle_linked_attribs(struct ldb_module *module,
old_el = ldb_msg_find_element(old_msg, el->name);
switch (el->flags & LDB_FLAG_MOD_MASK) {
case LDB_FLAG_MOD_REPLACE:
- ret = replmd_modify_la_replace(module, msg, el, old_el, schema_attr, seq_num, t);
+ ret = replmd_modify_la_replace(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid);
break;
case LDB_FLAG_MOD_DELETE:
- ret = replmd_modify_la_delete(module, msg, el, old_el, schema_attr, seq_num, t);
+ ret = replmd_modify_la_delete(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid);
break;
case LDB_FLAG_MOD_ADD:
- ret = replmd_modify_la_add(module, msg, el, old_el, schema_attr, seq_num, t);
+ ret = replmd_modify_la_add(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid);
break;
default:
ldb_asprintf_errstring(ldb,
@@ -2877,9 +3077,7 @@ static int replmd_start_transaction(struct ldb_module *module)
/* create our private structure for this transaction */
struct replmd_private *replmd_private = talloc_get_type(ldb_module_get_private(module),
struct replmd_private);
- talloc_free(replmd_private->la_ctx);
- replmd_private->la_list = NULL;
- replmd_private->la_ctx = NULL;
+ replmd_txn_cleanup(replmd_private);
/* free any leftover mod_usn records from cancelled
transactions */
@@ -2901,6 +3099,7 @@ static int replmd_prepare_commit(struct ldb_module *module)
struct replmd_private *replmd_private =
talloc_get_type(ldb_module_get_private(module), struct replmd_private);
struct la_entry *la, *prev;
+ struct la_backlink *bl;
int ret;
/* walk the list backwards, to do the first entry first, as we
@@ -2913,16 +3112,22 @@ static int replmd_prepare_commit(struct ldb_module *module)
DLIST_REMOVE(replmd_private->la_list, la);
ret = replmd_process_linked_attribute(module, la);
if (ret != LDB_SUCCESS) {
- talloc_free(replmd_private->la_ctx);
- replmd_private->la_list = NULL;
- replmd_private->la_ctx = NULL;
+ replmd_txn_cleanup(replmd_private);
return ret;
}
}
- talloc_free(replmd_private->la_ctx);
- replmd_private->la_list = NULL;
- replmd_private->la_ctx = NULL;
+ /* process our backlink list, creating and deleting backlinks
+ as necessary */
+ for (bl=replmd_private->la_backlinks; bl; bl=bl->next) {
+ ret = replmd_process_backlink(module, bl);
+ if (ret != LDB_SUCCESS) {
+ replmd_txn_cleanup(replmd_private);
+ return ret;
+ }
+ }
+
+ replmd_txn_cleanup(replmd_private);
/* possibly change @REPLCHANGED */
ret = replmd_notify_store(module);
@@ -2937,9 +3142,8 @@ static int replmd_del_transaction(struct ldb_module *module)
{
struct replmd_private *replmd_private =
talloc_get_type(ldb_module_get_private(module), struct replmd_private);
- talloc_free(replmd_private->la_ctx);
- replmd_private->la_list = NULL;
- replmd_private->la_ctx = NULL;
+ replmd_txn_cleanup(replmd_private);
+
return ldb_next_del_trans(module);
}