X-Git-Url: http://git.samba.org/samba.git/?a=blobdiff_plain;f=source4%2Fdsdb%2Fsamdb%2Fldb_modules%2Frepl_meta_data.c;h=efd1932a9ec23d6cc7e21e014e8c41d4503d4d6c;hb=b7b229a424cfe6a2bbe7b091c93a6f09b2132975;hp=c5dcf2140f1045aa87539b6d906a674998edccec;hpb=98c961eb82194cd0738dbc58e4ae9f81771b409e;p=nivanova%2Fsamba-autobuild%2F.git diff --git a/source4/dsdb/samdb/ldb_modules/repl_meta_data.c b/source4/dsdb/samdb/ldb_modules/repl_meta_data.c index c5dcf2140f1..efd1932a9ec 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 { @@ -79,6 +80,7 @@ struct replmd_replicated_request { struct ldb_request *req; const struct dsdb_schema *schema; + struct GUID our_invocation_id; /* the controls we pass down */ struct ldb_control **controls; @@ -89,6 +91,7 @@ struct replmd_replicated_request { struct dsdb_extended_replicated_objects *objs; struct ldb_message *search_msg; + struct GUID local_parent_guid; uint64_t seq_num; bool is_urgent; @@ -240,6 +243,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); } @@ -560,6 +565,23 @@ static int replmd_notify_store(struct ldb_module *module, struct ldb_request *pa ldb_dn_get_linearized(modified_partition->dn))); return ret; } + + if (ldb_dn_compare(modified_partition->dn, + replmd_private->schema_dn) == 0) { + struct ldb_result *ext_res; + ret = dsdb_module_extended(module, + replmd_private->schema_dn, + &ext_res, + DSDB_EXTENDED_SCHEMA_UPDATE_NOW_OID, + ext_res, + DSDB_FLAG_NEXT_MODULE, + parent); + if (ret != LDB_SUCCESS) { + return ret; + } + talloc_free(ext_res); + } + DLIST_REMOVE(replmd_private->ncs, modified_partition); talloc_free(modified_partition); } @@ -576,6 +598,7 @@ static struct replmd_replicated_request *replmd_ctx_init(struct ldb_module *modu { struct ldb_context *ldb; struct replmd_replicated_request *ac; + const struct GUID *our_invocation_id; ldb = ldb_module_get_ctx(module); @@ -593,8 +616,19 @@ static struct replmd_replicated_request *replmd_ctx_init(struct ldb_module *modu ldb_debug_set(ldb, LDB_DEBUG_FATAL, "replmd_modify: no dsdb_schema loaded"); DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb))); + talloc_free(ac); + return NULL; + } + + /* get our invocationId */ + our_invocation_id = samdb_ntds_invocation_id(ldb); + if (!our_invocation_id) { + ldb_debug_set(ldb, LDB_DEBUG_FATAL, + "replmd_add: unable to find invocationId\n"); + talloc_free(ac); return NULL; } + ac->our_invocation_id = *our_invocation_id; return ac; } @@ -660,58 +694,59 @@ static int replmd_replPropertyMetaData1_attid_sort(const struct replPropertyMeta const struct replPropertyMetaData1 *m2, const uint32_t *rdn_attid) { - if (m1->attid == m2->attid) { - return 0; - } - /* - * the rdn attribute should be at the end! - * so we need to return a value greater than zero - * which means m1 is greater than m2 + * 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) */ - if (m1->attid == *rdn_attid) { - return 1; + uint32_t attid_1 = m1->attid; + uint32_t attid_2 = m2->attid; + + if (attid_1 == attid_2) { + return 0; } /* - * the rdn attribute should be at the end! - * so we need to return a value less than zero - * which means m2 is greater than m1 + * 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). */ - if (m2->attid == *rdn_attid) { - return -1; - } - - return m1->attid > m2->attid ? 1 : -1; + 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, + 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; + 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; } - 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; + /* 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; } - 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; } +static int replmd_replPropertyMetaDataCtr1_sort_and_verify(struct ldb_context *ldb, + struct replPropertyMetaDataCtr1 *ctr1, + struct ldb_dn *dn) +{ + /* Note this is O(n^2) for the almost-sorted case, which this is */ + LDB_TYPESAFE_QSORT(ctr1->array, ctr1->count, NULL, + replmd_replPropertyMetaData1_attid_sort); + return replmd_replPropertyMetaDataCtr1_verify(ldb, ctr1, dn); +} + static int replmd_ldb_message_element_attid_sort(const struct ldb_message_element *e1, const struct ldb_message_element *e2, const struct dsdb_schema *schema) @@ -757,7 +792,7 @@ static int replmd_build_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct ds components, and creating backlinks to the object */ static int replmd_add_fix_la(struct ldb_module *module, struct ldb_message_element *el, - uint64_t seq_num, const struct GUID *invocationId, time_t t, + uint64_t seq_num, const struct GUID *invocationId, NTTIME now, struct GUID *guid, const struct dsdb_attribute *sa, struct ldb_request *parent) { unsigned int i; @@ -766,9 +801,6 @@ static int replmd_add_fix_la(struct ldb_module *module, struct ldb_message_eleme /* We will take a reference to the schema in replmd_add_backlink */ const struct dsdb_schema *schema = dsdb_get_schema(ldb, NULL); - NTTIME now; - - unix_to_nt_time(&now, t); for (i=0; inum_values; i++) { struct ldb_val *v = &el->values[i]; @@ -817,7 +849,6 @@ static int replmd_add_fix_la(struct ldb_module *module, struct ldb_message_eleme */ static int replmd_add(struct ldb_module *module, struct ldb_request *req) { - struct samldb_msds_intid_persistant *msds_intid_struct; struct ldb_context *ldb; struct ldb_control *control; struct replmd_replicated_request *ac; @@ -828,7 +859,12 @@ static int replmd_add(struct ldb_module *module, struct ldb_request *req) struct GUID guid; struct replPropertyMetaDataBlob nmd; struct ldb_val nmd_value; - const struct GUID *our_invocation_id; + + /* + * The use of a time_t here seems odd, but as the NTTIME + * elements are actually declared as NTTIME_1sec in the IDL, + * getting a higher resolution timestamp is not required. + */ time_t t = time(NULL); NTTIME now; char *time_str; @@ -839,7 +875,10 @@ static int replmd_add(struct ldb_module *module, struct ldb_request *req) bool allow_add_guid = false; bool remove_current_guid = false; bool is_urgent = false; + bool is_schema_nc = 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); @@ -893,15 +932,6 @@ static int replmd_add(struct ldb_module *module, struct ldb_request *req) return ret; } - /* get our invocationId */ - our_invocation_id = samdb_ntds_invocation_id(ldb); - if (!our_invocation_id) { - ldb_debug_set(ldb, LDB_DEBUG_ERROR, - "replmd_add: unable to find invocationId\n"); - talloc_free(ac); - return LDB_ERR_OPERATIONS_ERROR; - } - /* we have to copy the message as the caller might have it as a const */ msg = ldb_msg_copy_shallow(ac, req->op.add.message); if (msg == NULL) { @@ -954,12 +984,17 @@ static int replmd_add(struct ldb_module *module, struct ldb_request *req) return LDB_ERR_OPERATIONS_ERROR; } - for (i=0; i < msg->num_elements; i++) { + is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0; + + for (i=0; i < msg->num_elements;) { struct ldb_message_element *e = &msg->elements[i]; struct replPropertyMetaData1 *m = &nmd.ctr.ctr1.array[ni]; const struct dsdb_attribute *sa; - if (e->name[0] == '@') continue; + if (e->name[0] == '@') { + i++; + continue; + } sa = dsdb_attribute_by_lDAPDisplayName(ac->schema, e->name); if (!sa) { @@ -974,23 +1009,27 @@ static int replmd_add(struct ldb_module *module, struct ldb_request *req) /* if the attribute is not replicated (0x00000001) * or constructed (0x00000004) it has no metadata */ + i++; continue; } if (sa->linkID != 0 && functional_level > DS_DOMAIN_FUNCTION_2000) { - ret = replmd_add_fix_la(module, e, ac->seq_num, our_invocation_id, t, &guid, sa, req); + ret = replmd_add_fix_la(module, e, ac->seq_num, + &ac->our_invocation_id, now, + &guid, sa, req); if (ret != LDB_SUCCESS) { talloc_free(ac); return ret; } /* linked attributes are not stored in replPropertyMetaData in FL above w2k */ + i++; continue; } - m->attid = sa->attributeID_id; - m->version = 1; - if (m->attid == 0x20030) { + m->attid = dsdb_attribute_get_attid(sa, is_schema_nc); + m->version = 1; + if (m->attid == DRSUAPI_ATTID_isDeleted) { const struct ldb_val *rdn_val = ldb_dn_get_rdn_val(msg->dn); const char* rdn; @@ -1013,20 +1052,35 @@ static int replmd_add(struct ldb_module *module, struct ldb_request *req) } else { m->originating_change_time = now; } - m->originating_invocation_id = *our_invocation_id; + m->originating_invocation_id = ac->our_invocation_id; m->originating_usn = ac->seq_num; m->local_usn = ac->seq_num; ni++; + + if (!(e->flags & DSDB_FLAG_INTERNAL_FORCE_META_DATA)) { + i++; + continue; + } + + e->flags &= ~DSDB_FLAG_INTERNAL_FORCE_META_DATA; + + if (e->num_values != 0) { + i++; + continue; + } + + ldb_msg_remove_element(msg, e); } /* fix meta data count */ nmd.ctr.ctr1.count = ni; /* - * sort meta data array, and move the rdn attribute entry to the end + * sort meta data array */ - ret = replmd_replPropertyMetaDataCtr1_sort(&nmd.ctr.ctr1, ac->schema, msg->dn); + ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &nmd.ctr.ctr1, 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,14 +1184,6 @@ 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) { - - /* 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) { - msds_intid_struct->usn = ac->seq_num; - } - } /* go on with the call chain */ return ldb_next_request(module, down_req); } @@ -1145,12 +1201,14 @@ static int replmd_update_rpmd_element(struct ldb_context *ldb, uint64_t *seq_num, const struct GUID *our_invocation_id, NTTIME now, + bool is_schema_nc, struct ldb_request *req) { uint32_t i; const struct dsdb_attribute *a; struct replPropertyMetaData1 *md1; bool may_skip = false; + uint32_t attid; a = dsdb_attribute_by_lDAPDisplayName(schema, el->name); if (a == NULL) { @@ -1165,6 +1223,8 @@ static int replmd_update_rpmd_element(struct ldb_context *ldb, return LDB_ERR_OPERATIONS_ERROR; } + attid = dsdb_attribute_get_attid(a, is_schema_nc); + if ((a->systemFlags & DS_FLAG_ATTR_NOT_REPLICATED) || (a->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED)) { return LDB_SUCCESS; } @@ -1196,6 +1256,11 @@ static int replmd_update_rpmd_element(struct ldb_context *ldb, } } + if (el->flags & DSDB_FLAG_INTERNAL_FORCE_META_DATA) { + may_skip = false; + el->flags &= ~DSDB_FLAG_INTERNAL_FORCE_META_DATA; + } + if (may_skip) { if (strcmp(el->name, "interSiteTopologyGenerator") != 0 && !ldb_request_get_control(req, LDB_CONTROL_PROVISION_OID)) { @@ -1208,7 +1273,22 @@ static int replmd_update_rpmd_element(struct ldb_context *ldb, } for (i=0; ictr.ctr1.count; i++) { - if (a->attributeID_id == omd->ctr.ctr1.array[i].attid) break; + /* + * First check if we find it under the msDS-IntID, + * then check if we find it under the OID and + * prefixMap ID. + * + * This allows the administrator to simply re-write + * the attributes and so restore replication, which is + * likely what they will try to do. + */ + if (attid == omd->ctr.ctr1.array[i].attid) { + break; + } + + if (a->attributeID_id == omd->ctr.ctr1.array[i].attid) { + break; + } } if (a->linkID != 0 && dsdb_functional_level(ldb) > DS_DOMAIN_FUNCTION_2000) { @@ -1247,8 +1327,8 @@ static int replmd_update_rpmd_element(struct ldb_context *ldb, md1 = &omd->ctr.ctr1.array[i]; md1->version++; - md1->attid = a->attributeID_id; - if (md1->attid == 0x20030) { + md1->attid = attid; + if (md1->attid == DRSUAPI_ATTID_isDeleted) { const struct ldb_val *rdn_val = ldb_dn_get_rdn_val(msg->dn); const char* rdn; @@ -1277,6 +1357,52 @@ static int replmd_update_rpmd_element(struct ldb_context *ldb, return LDB_SUCCESS; } +/* + * Bump the replPropertyMetaData version on an attribute, and if it + * has changed (or forced by leaving rdn_old NULL), update the value + * in the entry. + * + * This is important, as calling a modify operation may not change the + * version number if the values appear unchanged, but a rename between + * parents bumps this value. + * + */ +static int replmd_update_rpmd_rdn_attr(struct ldb_context *ldb, + struct ldb_message *msg, + const struct ldb_val *rdn_new, + const struct ldb_val *rdn_old, + struct replPropertyMetaDataBlob *omd, + struct replmd_replicated_request *ar, + NTTIME now, + bool is_schema_nc) +{ + struct ldb_message_element new_el = { + .flags = LDB_FLAG_MOD_REPLACE, + .name = ldb_dn_get_rdn_name(msg->dn), + .num_values = 1, + .values = discard_const_p(struct ldb_val, rdn_new) + }; + struct ldb_message_element old_el = { + .flags = LDB_FLAG_MOD_REPLACE, + .name = ldb_dn_get_rdn_name(msg->dn), + .num_values = rdn_old ? 1 : 0, + .values = discard_const_p(struct ldb_val, rdn_old) + }; + + if (ldb_msg_element_equal_ordered(&new_el, &old_el) == false) { + int ret = ldb_msg_add(msg, &new_el, LDB_FLAG_MOD_REPLACE); + if (ret != LDB_SUCCESS) { + return ldb_oom(ldb); + } + } + + 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); + +} + static uint64_t find_max_local_usn(struct replPropertyMetaDataBlob omd) { uint32_t count = omd.ctr.ctr1.count; @@ -1301,7 +1427,7 @@ static int replmd_update_rpmd(struct ldb_module *module, struct ldb_request *req, const char * const *rename_attrs, struct ldb_message *msg, uint64_t *seq_num, - time_t t, + time_t t, bool is_schema_nc, bool *is_urgent, bool *rodc) { const struct ldb_val *omd_value; @@ -1312,18 +1438,26 @@ static int replmd_update_rpmd(struct ldb_module *module, const struct GUID *our_invocation_id; int ret; const char * const *attrs = NULL; - const char * const attrs1[] = { "replPropertyMetaData", "*", NULL }; const char * const attrs2[] = { "uSNChanged", "objectClass", "instanceType", NULL }; struct ldb_result *res; struct ldb_context *ldb; struct ldb_message_element *objectclass_el; enum urgent_situation situation; bool rmd_is_provided; - + bool rmd_is_just_resorted = false; + const char *not_rename_attrs[4 + msg->num_elements]; + if (rename_attrs) { attrs = rename_attrs; } else { - attrs = attrs1; + for (i = 0; i < msg->num_elements; i++) { + not_rename_attrs[i] = msg->elements[i].name; + } + not_rename_attrs[i] = "replPropertyMetaData"; + not_rename_attrs[i+1] = "objectClass"; + not_rename_attrs[i+2] = "instanceType"; + not_rename_attrs[i+3] = NULL; + attrs = not_rename_attrs; } ldb = ldb_module_get_ctx(module); @@ -1340,6 +1474,9 @@ static int replmd_update_rpmd(struct ldb_module *module, if (ldb_request_get_control(req, DSDB_CONTROL_CHANGEREPLMETADATA_OID)) { rmd_is_provided = true; + if (ldb_request_get_control(req, DSDB_CONTROL_CHANGEREPLMETADATA_RESORT_OID)) { + rmd_is_just_resorted = true; + } } else { rmd_is_provided = false; } @@ -1358,7 +1495,7 @@ static int replmd_update_rpmd(struct ldb_module *module, /* In this case the change_replmetadata control was supplied */ /* We check that it's the only attribute that is provided * (it's a rare case so it's better to keep the code simplier) - * We also check that the highest local_usn is bigger than + * We also check that the highest local_usn is bigger or the same as * uSNChanged. */ uint64_t db_seq; if( msg->num_elements != 1 || @@ -1385,7 +1522,6 @@ static int replmd_update_rpmd(struct ldb_module *module, ldb_dn_get_linearized(msg->dn))); return LDB_ERR_OPERATIONS_ERROR; } - *seq_num = find_max_local_usn(omd); ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attrs2, DSDB_FLAG_NEXT_MODULE | @@ -1398,18 +1534,22 @@ 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; - } + if (rmd_is_just_resorted == false) { + *seq_num = find_max_local_usn(omd); - 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)"\ - " is less or equal to uSNChanged (max = %lld uSNChanged = %lld)\n", - (long long)*seq_num, (long long)db_seq)); - return LDB_ERR_OPERATIONS_ERROR; + db_seq = ldb_msg_find_attr_as_uint64(res->msgs[0], "uSNChanged", 0); + + /* + * The test here now allows for a new + * replPropertyMetaData with no change, if was + * just dbcheck re-sorting the values. + */ + if (*seq_num <= db_seq) { + DEBUG(0,(__location__ ": changereplmetada control provided but max(local_usn)" \ + " is less than uSNChanged (max = %lld uSNChanged = %lld)\n", + (long long)*seq_num, (long long)db_seq)); + return LDB_ERR_OPERATIONS_ERROR; + } } } else { @@ -1428,12 +1568,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", @@ -1455,26 +1589,65 @@ static int replmd_update_rpmd(struct ldb_module *module, return LDB_ERR_OPERATIONS_ERROR; } - for (i=0; inum_elements; i++) { + for (i=0; inum_elements;) { + struct ldb_message_element *el = &msg->elements[i]; struct ldb_message_element *old_el; - old_el = ldb_msg_find_element(res->msgs[0], msg->elements[i].name); - ret = replmd_update_rpmd_element(ldb, msg, &msg->elements[i], old_el, &omd, schema, seq_num, - our_invocation_id, now, req); + + old_el = ldb_msg_find_element(res->msgs[0], el->name); + ret = replmd_update_rpmd_element(ldb, msg, el, old_el, + &omd, schema, seq_num, + our_invocation_id, + now, is_schema_nc, + req); if (ret != LDB_SUCCESS) { return ret; } - if (is_urgent && !*is_urgent && (situation == REPL_URGENT_ON_UPDATE)) { - *is_urgent = replmd_check_urgent_attribute(&msg->elements[i]); + if (!*is_urgent && (situation == REPL_URGENT_ON_UPDATE)) { + *is_urgent = replmd_check_urgent_attribute(el); + } + + if (!(el->flags & DSDB_FLAG_INTERNAL_FORCE_META_DATA)) { + i++; + continue; + } + + el->flags &= ~DSDB_FLAG_INTERNAL_FORCE_META_DATA; + + if (el->num_values != 0) { + i++; + continue; } + ldb_msg_remove_element(msg, el); + } + } + + /* + * 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 */ - if (*seq_num != 0) { + if (*seq_num != 0 || rmd_is_just_resorted == true) { struct ldb_val *md_value; struct ldb_message_element *el; @@ -1504,8 +1677,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, msg->dn); if (ret != LDB_SUCCESS) { + ldb_asprintf_errstring(ldb, "%s: %s", __func__, ldb_errstring(ldb)); return ret; } @@ -1533,13 +1707,18 @@ static int replmd_update_rpmd(struct ldb_module *module, struct parsed_dn { struct dsdb_dn *dsdb_dn; - struct GUID *guid; + struct GUID guid; struct ldb_val *v; }; static int parsed_dn_compare(struct parsed_dn *pdn1, struct parsed_dn *pdn2) { - return GUID_compare(pdn1->guid, pdn2->guid); + return GUID_compare(&pdn1->guid, &pdn2->guid); +} + +static int GUID_compare_struct(struct GUID *g1, struct GUID g2) +{ + return GUID_compare(g1, &g2); } static struct parsed_dn *parsed_dn_find(struct parsed_dn *pdn, @@ -1559,7 +1738,7 @@ static struct parsed_dn *parsed_dn_find(struct parsed_dn *pdn, } return NULL; } - BINARY_ARRAY_SEARCH(pdn, count, guid, guid, GUID_compare, ret); + BINARY_ARRAY_SEARCH(pdn, count, guid, guid, GUID_compare_struct, ret); return ret; } @@ -1600,16 +1779,10 @@ static int get_parsed_dns(struct ldb_module *module, TALLOC_CTX *mem_ctx, dn = p->dsdb_dn->dn; - p->guid = talloc(*pdn, struct GUID); - if (p->guid == NULL) { - ldb_module_oom(module); - return LDB_ERR_OPERATIONS_ERROR; - } - - status = dsdb_get_extended_dn_guid(dn, p->guid, "GUID"); + status = dsdb_get_extended_dn_guid(dn, &p->guid, "GUID"); if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) { /* we got a DN without a GUID - go find the GUID */ - int ret = dsdb_module_guid_by_dn(module, dn, p->guid, parent); + int ret = dsdb_module_guid_by_dn(module, dn, &p->guid, parent); if (ret != LDB_SUCCESS) { ldb_asprintf_errstring(ldb, "Unable to find GUID for DN %s\n", ldb_dn_get_linearized(dn)); @@ -1620,7 +1793,7 @@ static int get_parsed_dns(struct ldb_module *module, TALLOC_CTX *mem_ctx, } return ret; } - ret = dsdb_set_extended_dn_guid(dn, p->guid, "GUID"); + ret = dsdb_set_extended_dn_guid(dn, &p->guid, "GUID"); if (ret != LDB_SUCCESS) { return ret; } @@ -1763,7 +1936,7 @@ static int replmd_check_upgrade_links(struct parsed_dn *dns, uint32_t count, str */ static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn, struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id, - uint64_t seq_num, uint64_t local_usn, NTTIME nttime, + uint64_t usn, uint64_t local_usn, NTTIME nttime, uint32_t version, bool deleted) { struct ldb_dn *dn = dsdb_dn->dn; @@ -1786,7 +1959,7 @@ static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct d } tval = data_blob_string_const(tstring); - usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)seq_num); + usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)usn); if (!usn_string) { return LDB_ERR_OPERATIONS_ERROR; } @@ -1910,7 +2083,7 @@ static int replmd_modify_la_add(struct ldb_module *module, /* for each new value, see if it exists already with the same GUID */ for (i=0; inum_values; i++) { - struct parsed_dn *p = parsed_dn_find(old_dns, old_num_values, dns[i].guid, NULL); + struct parsed_dn *p = parsed_dn_find(old_dns, old_num_values, &dns[i].guid, NULL); if (p == NULL) { /* this is a new linked attribute value */ new_values = talloc_realloc(tmp_ctx, new_values, struct ldb_val, num_new_values+1); @@ -1932,8 +2105,9 @@ static int replmd_modify_la_add(struct ldb_module *module, uint32_t rmd_flags = dsdb_dn_rmd_flags(p->dsdb_dn->dn); if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) { + struct GUID_txt_buf guid_str; ldb_asprintf_errstring(ldb, "Attribute %s already exists for target GUID %s", - el->name, GUID_string(tmp_ctx, p->guid)); + el->name, GUID_buf_string(&p->guid, &guid_str)); talloc_free(tmp_ctx); /* error codes for 'member' need to be special cased */ @@ -1951,7 +2125,7 @@ static int replmd_modify_la_add(struct ldb_module *module, } } - ret = replmd_add_backlink(module, schema, msg_guid, dns[i].guid, true, schema_attr, true); + ret = replmd_add_backlink(module, schema, msg_guid, &dns[i].guid, true, schema_attr, true); if (ret != LDB_SUCCESS) { talloc_free(tmp_ctx); return ret; @@ -1999,7 +2173,7 @@ static int replmd_modify_la_delete(struct ldb_module *module, { unsigned int i; struct parsed_dn *dns, *old_dns; - TALLOC_CTX *tmp_ctx = talloc_new(msg); + TALLOC_CTX *tmp_ctx = NULL; int ret; const struct GUID *invocation_id; struct ldb_context *ldb = ldb_module_get_ctx(module); @@ -2017,6 +2191,11 @@ static int replmd_modify_la_delete(struct ldb_module *module, return LDB_ERR_NO_SUCH_ATTRIBUTE; } + tmp_ctx = talloc_new(msg); + if (tmp_ctx == NULL) { + return LDB_ERR_OPERATIONS_ERROR; + } + ret = get_parsed_dns(module, tmp_ctx, el, &dns, schema_attr->syntax->ldap_oid, parent); if (ret != LDB_SUCCESS) { talloc_free(tmp_ctx); @@ -2031,6 +2210,7 @@ static int replmd_modify_la_delete(struct ldb_module *module, invocation_id = samdb_ntds_invocation_id(ldb); if (!invocation_id) { + talloc_free(tmp_ctx); return LDB_ERR_OPERATIONS_ERROR; } @@ -2049,23 +2229,29 @@ static int replmd_modify_la_delete(struct ldb_module *module, struct parsed_dn *p2; uint32_t rmd_flags; - p2 = parsed_dn_find(old_dns, old_el->num_values, p->guid, NULL); + p2 = parsed_dn_find(old_dns, old_el->num_values, &p->guid, NULL); if (!p2) { + struct GUID_txt_buf buf; ldb_asprintf_errstring(ldb, "Attribute %s doesn't exist for target GUID %s", - el->name, GUID_string(tmp_ctx, p->guid)); + el->name, GUID_buf_string(&p->guid, &buf)); if (ldb_attr_cmp(el->name, "member") == 0) { + talloc_free(tmp_ctx); return LDB_ERR_UNWILLING_TO_PERFORM; } else { + talloc_free(tmp_ctx); return LDB_ERR_NO_SUCH_ATTRIBUTE; } } rmd_flags = dsdb_dn_rmd_flags(p2->dsdb_dn->dn); if (rmd_flags & DSDB_RMD_FLAG_DELETED) { + struct GUID_txt_buf buf; ldb_asprintf_errstring(ldb, "Attribute %s already deleted for target GUID %s", - el->name, GUID_string(tmp_ctx, p->guid)); + el->name, GUID_buf_string(&p->guid, &buf)); if (ldb_attr_cmp(el->name, "member") == 0) { + talloc_free(tmp_ctx); return LDB_ERR_UNWILLING_TO_PERFORM; } else { + talloc_free(tmp_ctx); return LDB_ERR_NO_SUCH_ATTRIBUTE; } } @@ -2078,7 +2264,7 @@ static int replmd_modify_la_delete(struct ldb_module *module, struct parsed_dn *p = &old_dns[i]; uint32_t rmd_flags; - if (el->num_values && parsed_dn_find(dns, el->num_values, p->guid, NULL) == NULL) { + if (el->num_values && parsed_dn_find(dns, el->num_values, &p->guid, NULL) == NULL) { continue; } @@ -2092,7 +2278,7 @@ static int replmd_modify_la_delete(struct ldb_module *module, return ret; } - ret = replmd_add_backlink(module, schema, msg_guid, old_dns[i].guid, false, schema_attr, true); + ret = replmd_add_backlink(module, schema, msg_guid, &old_dns[i].guid, false, schema_attr, true); if (ret != LDB_SUCCESS) { talloc_free(tmp_ctx); return ret; @@ -2175,13 +2361,13 @@ static int replmd_modify_la_replace(struct ldb_module *module, if (rmd_flags & DSDB_RMD_FLAG_DELETED) continue; - ret = replmd_add_backlink(module, schema, msg_guid, old_dns[i].guid, false, schema_attr, false); + ret = replmd_add_backlink(module, schema, msg_guid, &old_dns[i].guid, false, schema_attr, false); if (ret != LDB_SUCCESS) { talloc_free(tmp_ctx); return ret; } - p = parsed_dn_find(dns, el->num_values, old_p->guid, NULL); + p = parsed_dn_find(dns, el->num_values, &old_p->guid, NULL); if (p) { /* we don't delete it if we are re-adding it */ continue; @@ -2203,7 +2389,7 @@ static int replmd_modify_la_replace(struct ldb_module *module, if (old_dns && (old_p = parsed_dn_find(old_dns, - old_num_values, p->guid, NULL)) != NULL) { + old_num_values, &p->guid, NULL)) != NULL) { /* update in place */ ret = replmd_update_la_val(old_el->values, old_p->v, p->dsdb_dn, old_p->dsdb_dn, invocation_id, @@ -2230,7 +2416,7 @@ 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, false); + ret = replmd_add_backlink(module, schema, msg_guid, &dns[i].guid, true, schema_attr, false); if (ret != LDB_SUCCESS) { talloc_free(tmp_ctx); return ret; @@ -2391,7 +2577,6 @@ static int replmd_modify_handle_linked_attribs(struct ldb_module *module, static int replmd_modify(struct ldb_module *module, struct ldb_request *req) { - struct samldb_msds_intid_persistant *msds_intid_struct; struct ldb_context *ldb; struct replmd_replicated_request *ac; struct ldb_request *down_req; @@ -2399,9 +2584,12 @@ static int replmd_modify(struct ldb_module *module, struct ldb_request *req) time_t t = time(NULL); int ret; bool is_urgent = false, rodc = false; + bool is_schema_nc = 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 +2615,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; @@ -2452,8 +2640,11 @@ static int replmd_modify(struct ldb_module *module, struct ldb_request *req) ldb_msg_remove_attr(msg, "whenChanged"); ldb_msg_remove_attr(msg, "uSNChanged"); + is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0; + ret = replmd_update_rpmd(module, ac->schema, req, NULL, - msg, &ac->seq_num, t, &is_urgent, &rodc); + msg, &ac->seq_num, t, is_schema_nc, + &is_urgent, &rodc); if (rodc && (ret == LDB_ERR_REFERRAL)) { struct loadparm_context *lp_ctx; char *referral; @@ -2540,14 +2731,6 @@ static int replmd_modify(struct ldb_module *module, struct ldb_request *req) } } - if (!ldb_dn_compare_base(ac->schema->base_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) { - msds_intid_struct->usn = ac->seq_num; - } - } - /* go on with the call chain */ return ldb_next_request(module, down_req); } @@ -2601,7 +2784,6 @@ static int replmd_rename(struct ldb_module *module, struct ldb_request *req) static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares) { struct ldb_context *ldb; - struct replmd_replicated_request *ac; struct ldb_request *down_req; struct ldb_message *msg; const struct dsdb_attribute *rdn_attr; @@ -2611,8 +2793,13 @@ static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *are time_t t = time(NULL); int ret; bool is_urgent = false, rodc = false; + bool is_schema_nc; + struct replmd_replicated_request *ac = + talloc_get_type(req->context, struct replmd_replicated_request); + struct replmd_private *replmd_private = + talloc_get_type(ldb_module_get_private(ac->module), + struct replmd_private); - ac = talloc_get_type(req->context, struct replmd_replicated_request); ldb = ldb_module_get_ctx(ac->module); if (ares->error != LDB_SUCCESS) { @@ -2640,6 +2827,8 @@ static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *are msg->dn = ac->req->op.rename.newdn; + is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0; + rdn_name = ldb_dn_get_rdn_name(msg->dn); if (rdn_name == NULL) { talloc_free(ares); @@ -2725,7 +2914,8 @@ static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *are attrs[4] = NULL; ret = replmd_update_rpmd(ac->module, ac->schema, req, attrs, - msg, &ac->seq_num, t, &is_urgent, &rodc); + msg, &ac->seq_num, t, + is_schema_nc, &is_urgent, &rodc); if (rodc && (ret == LDB_ERR_REFERRAL)) { struct ldb_dn *olddn = ac->req->op.rename.olddn; struct loadparm_context *lp_ctx; @@ -2904,17 +3094,57 @@ static int replmd_delete_internals(struct ldb_module *module, struct ldb_request struct ldb_message_element *el; TALLOC_CTX *tmp_ctx; struct ldb_result *res, *parent_res; - const char *preserved_attrs[] = { + static const char * const preserved_attrs[] = { /* yes, this really is a hard coded list. See MS-ADTS section 3.1.1.5.5.1.1 */ - "nTSecurityDescriptor", "attributeID", "attributeSyntax", "dNReferenceUpdate", "dNSHostName", - "flatName", "governsID", "groupType", "instanceType", "lDAPDisplayName", "legacyExchangeDN", - "isDeleted", "isRecycled", "lastKnownParent", "msDS-LastKnownRDN", "mS-DS-CreatorSID", - "mSMQOwnerID", "nCName", "objectClass", "distinguishedName", "objectGUID", "objectSid", - "oMSyntax", "proxiedObjectName", "name", "replPropertyMetaData", "sAMAccountName", - "securityIdentifier", "sIDHistory", "subClassOf", "systemFlags", "trustPartner", "trustDirection", - "trustType", "trustAttributes", "userAccountControl", "uSNChanged", "uSNCreated", "whenCreated", - "whenChanged", NULL}; + "attributeID", + "attributeSyntax", + "dNReferenceUpdate", + "dNSHostName", + "flatName", + "governsID", + "groupType", + "instanceType", + "lDAPDisplayName", + "legacyExchangeDN", + "isDeleted", + "isRecycled", + "lastKnownParent", + "msDS-LastKnownRDN", + "msDS-PortLDAP", + "mS-DS-CreatorSID", + "mSMQOwnerID", + "nCName", + "objectClass", + "distinguishedName", + "objectGUID", + "objectSid", + "oMSyntax", + "proxiedObjectName", + "name", + "nTSecurityDescriptor", + "replPropertyMetaData", + "sAMAccountName", + "securityIdentifier", + "sIDHistory", + "subClassOf", + "systemFlags", + "trustPartner", + "trustDirection", + "trustType", + "trustAttributes", + "userAccountControl", + "uSNChanged", + "uSNCreated", + "whenCreated", + "whenChanged", + NULL + }; + static const char * const all_attrs[] = { + DSDB_SECRET_ATTRIBUTES, + "*", + NULL + }; unsigned int i, el_count = 0; enum deletion_state deletion_state, next_deletion_state; @@ -2922,6 +3152,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); @@ -2938,7 +3182,7 @@ static int replmd_delete_internals(struct ldb_module *module, struct ldb_request /* we need the complete msg off disk, so we can work out which attributes need to be removed */ - ret = dsdb_module_search_dn(module, tmp_ctx, &res, old_dn, NULL, + ret = dsdb_module_search_dn(module, tmp_ctx, &res, old_dn, all_attrs, DSDB_FLAG_NEXT_MODULE | DSDB_SEARCH_SHOW_RECYCLED | DSDB_SEARCH_REVEAL_INTERNALS | @@ -2965,17 +3209,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); @@ -3048,16 +3300,17 @@ static int replmd_delete_internals(struct ldb_module *module, struct ldb_request ldb_dn_escape_value(tmp_ctx, *rdn_value), GUID_string(tmp_ctx, &guid)); if (!retb) { - DEBUG(0,(__location__ ": Unable to add a formatted child to dn: %s", - ldb_dn_get_linearized(new_dn))); + ldb_asprintf_errstring(ldb, __location__ + ": Unable to add a formatted child to dn: %s", + ldb_dn_get_linearized(new_dn)); talloc_free(tmp_ctx); return LDB_ERR_OPERATIONS_ERROR; } ret = ldb_msg_add_string(msg, "isDeleted", "TRUE"); if (ret != LDB_SUCCESS) { - DEBUG(0,(__location__ ": Failed to add isDeleted string to the msg\n")); - ldb_module_oom(module); + ldb_asprintf_errstring(ldb, __location__ + ": Failed to add isDeleted string to the msg"); talloc_free(tmp_ctx); return ret; } @@ -3071,8 +3324,9 @@ static int replmd_delete_internals(struct ldb_module *module, struct ldb_request struct ldb_dn *rdn = ldb_dn_copy(tmp_ctx, old_dn); retb = ldb_dn_remove_base_components(rdn, ldb_dn_get_comp_num(rdn) - 1); if (!retb) { - DEBUG(0,(__location__ ": Unable to add a prepare rdn of %s", - ldb_dn_get_linearized(rdn))); + ldb_asprintf_errstring(ldb, __location__ + ": Unable to add a prepare rdn of %s", + ldb_dn_get_linearized(rdn)); talloc_free(tmp_ctx); return LDB_ERR_OPERATIONS_ERROR; } @@ -3080,9 +3334,10 @@ static int replmd_delete_internals(struct ldb_module *module, struct ldb_request retb = ldb_dn_add_child(new_dn, rdn); if (!retb) { - DEBUG(0,(__location__ ": Unable to add rdn %s to base dn: %s", - ldb_dn_get_linearized(rdn), - ldb_dn_get_linearized(new_dn))); + ldb_asprintf_errstring(ldb, __location__ + ": Unable to add rdn %s to base dn: %s", + ldb_dn_get_linearized(rdn), + ldb_dn_get_linearized(new_dn)); talloc_free(tmp_ctx); return LDB_ERR_OPERATIONS_ERROR; } @@ -3106,29 +3361,48 @@ static int replmd_delete_internals(struct ldb_module *module, struct ldb_request */ if (deletion_state == OBJECT_NOT_DELETED) { + struct ldb_dn *parent_dn = ldb_dn_get_parent(tmp_ctx, old_dn); + char *parent_dn_str = NULL; + /* we need the storage form of the parent GUID */ ret = dsdb_module_search_dn(module, tmp_ctx, &parent_res, - ldb_dn_get_parent(tmp_ctx, old_dn), NULL, + parent_dn, NULL, DSDB_FLAG_NEXT_MODULE | DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT | DSDB_SEARCH_REVEAL_INTERNALS| DSDB_SEARCH_SHOW_RECYCLED, req); if (ret != LDB_SUCCESS) { ldb_asprintf_errstring(ldb_module_get_ctx(module), - "repmd_delete: Failed to %s %s, because we failed to find it's parent (%s): %s", + "repmd_delete: Failed to %s %s, " + "because we failed to find it's parent (%s): %s", re_delete ? "re-delete" : "delete", ldb_dn_get_linearized(old_dn), - ldb_dn_get_linearized(ldb_dn_get_parent(tmp_ctx, old_dn)), + ldb_dn_get_linearized(parent_dn), ldb_errstring(ldb_module_get_ctx(module))); talloc_free(tmp_ctx); return ret; } + /* + * Now we can use the DB version, + * it will have the extended DN info in it + */ + parent_dn = parent_res->msgs[0]->dn; + parent_dn_str = ldb_dn_get_extended_linearized(tmp_ctx, + parent_dn, + 1); + if (parent_dn_str == NULL) { + talloc_free(tmp_ctx); + return ldb_module_oom(module); + } + ret = ldb_msg_add_steal_string(msg, "lastKnownParent", - ldb_dn_get_extended_linearized(tmp_ctx, parent_res->msgs[0]->dn, 1)); + parent_dn_str); if (ret != LDB_SUCCESS) { - DEBUG(0,(__location__ ": Failed to add lastKnownParent string to the msg\n")); - ldb_module_oom(module); + ldb_asprintf_errstring(ldb, __location__ + ": Failed to add lastKnownParent " + "string when deleting %s", + ldb_dn_get_linearized(old_dn)); talloc_free(tmp_ctx); return ret; } @@ -3137,8 +3411,10 @@ static int replmd_delete_internals(struct ldb_module *module, struct ldb_request if (next_deletion_state == OBJECT_DELETED) { ret = ldb_msg_add_value(msg, "msDS-LastKnownRDN", rdn_value, NULL); if (ret != LDB_SUCCESS) { - DEBUG(0,(__location__ ": Failed to add msDS-LastKnownRDN string to the msg\n")); - ldb_module_oom(module); + ldb_asprintf_errstring(ldb, __location__ + ": Failed to add msDS-LastKnownRDN " + "string when deleting %s", + ldb_dn_get_linearized(old_dn)); talloc_free(tmp_ctx); return ret; } @@ -3205,6 +3481,14 @@ static int replmd_delete_internals(struct ldb_module *module, struct ldb_request */ ret = replmd_delete_remove_link(module, schema, old_dn, el, sa, req); if (ret != LDB_SUCCESS) { + const char *old_dn_str + = ldb_dn_get_linearized(old_dn); + ldb_asprintf_errstring(ldb, + __location__ + ": Failed to remove backlink of " + "%s when deleting %s", + el->name, + old_dn_str); talloc_free(tmp_ctx); return LDB_ERR_OPERATIONS_ERROR; } @@ -3230,17 +3514,6 @@ static int replmd_delete_internals(struct ldb_module *module, struct ldb_request } } - /* Duplicate with the below - we remove the - * samAccountType as an originating update, in case it - * somehow came back. The objectCategory will have - * gone in the above */ - ret = ldb_msg_add_empty(msg, "sAMAccountType", LDB_FLAG_MOD_REPLACE, NULL); - if (ret != LDB_SUCCESS) { - talloc_free(tmp_ctx); - ldb_module_oom(module); - return ret; - } - break; case OBJECT_DELETED: @@ -3311,14 +3584,6 @@ static int replmd_delete_internals(struct ldb_module *module, struct ldb_request * */ - ret = dsdb_module_modify(module, msg, DSDB_FLAG_OWN_MODULE, req); - if (ret != LDB_SUCCESS) { - ldb_asprintf_errstring(ldb, "replmd_delete: Failed to modify object %s in delete - %s", - ldb_dn_get_linearized(old_dn), ldb_errstring(ldb)); - talloc_free(tmp_ctx); - return ret; - } - /* * No matter what has happned with other renames, try again to * get this to be under the deleted DN. @@ -3334,6 +3599,15 @@ static int replmd_delete_internals(struct ldb_module *module, struct ldb_request talloc_free(tmp_ctx); return ret; } + msg->dn = new_dn; + } + + ret = dsdb_module_modify(module, msg, DSDB_FLAG_OWN_MODULE, req); + if (ret != LDB_SUCCESS) { + ldb_asprintf_errstring(ldb, "replmd_delete: Failed to modify object %s in delete - %s", + ldb_dn_get_linearized(old_dn), ldb_errstring(ldb)); + talloc_free(tmp_ctx); + return ret; } talloc_free(tmp_ctx); @@ -3356,6 +3630,10 @@ static int replmd_replicated_request_werror(struct replmd_replicated_request *ar { int ret = LDB_ERR_OTHER; /* TODO: do some error mapping */ + + /* Let the caller know the full WERROR */ + ar->objs->error = status; + return ret; } @@ -3407,6 +3685,47 @@ static bool replmd_replPropertyMetaData1_is_newer(struct replPropertyMetaData1 * new_m->originating_change_time); } +static bool replmd_replPropertyMetaData1_new_should_be_taken(uint32_t dsdb_repl_flags, + struct replPropertyMetaData1 *cur_m, + struct replPropertyMetaData1 *new_m) +{ + bool cmp; + + /* + * If the new replPropertyMetaData entry for this attribute is + * not provided (this happens in the case where we look for + * ATTID_name, but the name was not changed), then the local + * state is clearly still current, as the remote + * server didn't send it due to being older the high watermark + * USN we sent. + */ + if (new_m == NULL) { + return false; + } + + if (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. + * + * This call is a bit tricky, what we + * are doing it turning the 'is_newer' + * call into a 'not is older' by + * swapping cur_m and new_m, and negating the + * outcome. + */ + cmp = !replmd_replPropertyMetaData1_is_newer(new_m, + cur_m); + } else { + cmp = replmd_replPropertyMetaData1_is_newer(cur_m, + new_m); + } + return cmp; +} + /* form a conflict DN @@ -3526,7 +3845,7 @@ static int replmd_op_name_modify_callback(struct ldb_request *req, struct ldb_re { struct replmd_replicated_request *ar = talloc_get_type_abort(req->context, struct replmd_replicated_request); - struct ldb_dn *conflict_dn; + struct ldb_dn *conflict_dn = NULL; int ret; if (ares->error != LDB_SUCCESS) { @@ -3584,7 +3903,7 @@ static int replmd_op_name_modify_callback(struct ldb_request *req, struct ldb_re } /* - callback for replmd_replicated_apply_add() and replmd_replicated_handle_rename() + callback for replmd_replicated_apply_add() This copes with the creation of conflict records in the case where the DN exists, but with a different objectGUID */ @@ -3602,36 +3921,37 @@ static int replmd_op_possible_conflict_callback(struct ldb_request *req, struct bool rename_incoming_record, rodc; struct replPropertyMetaData1 *rmd_name, *omd_name; struct ldb_message *msg; + struct ldb_request *down_req = NULL; - req->callback = callback; - - if (ares->error != LDB_ERR_ENTRY_ALREADY_EXISTS) { - /* call the normal callback for everything except - conflicts */ - return ldb_module_done(req, ares->controls, ares->response, ares->error); + /* call the normal callback for success */ + if (ares->error == LDB_SUCCESS) { + return callback(req, ares); } - ret = samdb_rodc(ldb_module_get_ctx(ar->module), &rodc); - if (ret != LDB_SUCCESS) { - 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; + conflict_dn = msg->dn; + + /* For failures other than conflicts, fail the whole operation here */ + if (ares->error != LDB_ERR_ENTRY_ALREADY_EXISTS) { + ldb_asprintf_errstring(ldb_module_get_ctx(ar->module), "Failed to locally apply remote add of %s: %s", + ldb_dn_get_linearized(conflict_dn), + ldb_errstring(ldb_module_get_ctx(ar->module))); + + return ldb_module_done(ar->req, NULL, NULL, + LDB_ERR_OPERATIONS_ERROR); + } + + ret = samdb_rodc(ldb_module_get_ctx(ar->module), &rodc); + if (ret != LDB_SUCCESS) { + 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(ar->req, NULL, NULL, + LDB_ERR_OPERATIONS_ERROR); - switch (req->operation) { - case LDB_ADD: - conflict_dn = msg->dn; - break; - case LDB_RENAME: - conflict_dn = req->op.rename.newdn; - break; - default: - return ldb_module_done(req, ares->controls, ares->response, ldb_module_operr(ar->module)); } if (rodc) { @@ -3650,7 +3970,7 @@ static int replmd_op_possible_conflict_callback(struct ldb_request *req, struct /* * first we need the replPropertyMetaData attribute from the - * old record + * local, conflicting record */ ret = dsdb_module_search_dn(ar->module, req, &res, conflict_dn, attrs, @@ -3680,40 +4000,36 @@ static int replmd_op_possible_conflict_callback(struct ldb_request *req, struct 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); + /* + * we decide which is newer based on the RPMD on the name + * attribute. See [MS-DRSR] ResolveNameConflict. + * + * We expect omd_name to be present, as this is from a local + * search, but while rmd_name should have been given to us by + * the remote server, if it is missing we just prefer the + * local name in + * replmd_replPropertyMetaData1_new_should_be_taken() + */ + 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", + if (!omd_name) { + DEBUG(0,(__location__ ": Failed to find name attribute in local LDB replPropertyMetaData for %s\n", ldb_dn_get_linearized(conflict_dn))); goto failed; } - rename_incoming_record = !(ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING) && - !replmd_replPropertyMetaData1_is_newer(omd_name, rmd_name); + /* + * Should we preserve the current record, and so rename the + * incoming record to be a conflict? + */ + rename_incoming_record + = !replmd_replPropertyMetaData1_new_should_be_taken(ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING, + omd_name, rmd_name); if (rename_incoming_record) { struct GUID guid; struct ldb_dn *new_dn; - /* - * 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) { - 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(msg, "objectGUID"); if (GUID_all_zero(&guid)) { DEBUG(0,(__location__ ": Failed to find objectGUID for conflicting incoming record %s\n", @@ -3730,12 +4046,9 @@ static int replmd_op_possible_conflict_callback(struct ldb_request *req, struct DEBUG(2,(__location__ ": Resolving conflict record via incoming rename '%s' -> '%s'\n", ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn))); - /* re-submit the request, but with a different - callback, so we don't loop forever. */ + /* re-submit the request, but with the new DN */ + callback = replmd_op_name_modify_callback; msg->dn = new_dn; - req->callback = replmd_op_name_modify_callback; - - return ldb_next_request(ar->module, req); } else { /* we are renaming the existing record */ struct GUID guid; @@ -3755,7 +4068,7 @@ static int replmd_op_possible_conflict_callback(struct ldb_request *req, struct goto failed; } - DEBUG(2,(__location__ ": Resolving conflict record via existing rename '%s' -> '%s'\n", + DEBUG(2,(__location__ ": Resolving conflict record via existing-record rename '%s' -> '%s'\n", ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn))); ret = dsdb_module_rename(ar->module, conflict_dn, new_dn, @@ -3777,15 +4090,55 @@ static int replmd_op_possible_conflict_callback(struct ldb_request *req, struct goto failed; } - return ldb_next_request(ar->module, req); + DEBUG(2,(__location__ ": With conflicting record renamed, re-apply replicated creation of '%s'\n", + ldb_dn_get_linearized(req->op.add.message->dn))); + } + + ret = ldb_build_add_req(&down_req, + ldb_module_get_ctx(ar->module), + req, + msg, + ar->controls, + ar, + callback, + req); + if (ret != LDB_SUCCESS) { + goto failed; + } + LDB_REQ_SET_LOCATION(down_req); + + /* current partition control needed by "repmd_op_callback" */ + ret = ldb_request_add_control(down_req, + DSDB_CONTROL_CURRENT_PARTITION_OID, + false, NULL); + if (ret != LDB_SUCCESS) { + return replmd_replicated_request_error(ar, ret); + } + + if (ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PARTIAL_REPLICA) { + /* this tells the partition module to make it a + partial replica if creating an NC */ + ret = ldb_request_add_control(down_req, + DSDB_CONTROL_PARTIAL_REPLICA, + false, NULL); + if (ret != LDB_SUCCESS) { + return replmd_replicated_request_error(ar, ret); + } } + /* + * Finally we re-run the add, otherwise the new record won't + * exist, as we are here because of that exact failure! + */ + return ldb_next_request(ar->module, down_req); failed: - /* on failure do the original callback. This means replication - * will stop with an error, but there is not much else we can - * do + + /* on failure make the caller get the error. This means + * replication will stop with an error, but there is not much + * else we can do. */ - return ldb_module_done(req, ares->controls, ares->response, ares->error); + return ldb_module_done(ar->req, NULL, NULL, + ret); } /* @@ -3807,16 +4160,6 @@ static int replmd_op_add_callback(struct ldb_request *req, struct ldb_reply *are return replmd_op_possible_conflict_callback(req, ares, replmd_op_callback); } -/* - callback for replmd_replicated_handle_rename() - This copes with the creation of conflict records in the case where - the DN exists, but with a different objectGUID - */ -static int replmd_op_rename_callback(struct ldb_request *req, struct ldb_reply *ares) -{ - return replmd_op_possible_conflict_callback(req, ares, ldb_modify_default_callback); -} - /* this is called when a new object comes in over DRS */ @@ -3831,17 +4174,28 @@ static int replmd_replicated_apply_add(struct replmd_replicated_request *ar) unsigned int i; int ret; bool remote_isDeleted = false; + bool is_schema_nc; + NTTIME now; + time_t t = time(NULL); + const struct ldb_val *rdn_val; + struct replmd_private *replmd_private = + talloc_get_type(ldb_module_get_private(ar->module), + struct replmd_private); + unix_to_nt_time(&now, t); ldb = ldb_module_get_ctx(ar->module); msg = ar->objs->objects[ar->index_current].msg; md = ar->objs->objects[ar->index_current].meta_data; + is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0; ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num); if (ret != LDB_SUCCESS) { return replmd_replicated_request_error(ar, ret); } - ret = ldb_msg_add_value(msg, "objectGUID", &ar->objs->objects[ar->index_current].guid_value, NULL); + ret = dsdb_msg_add_guid(msg, + &ar->objs->objects[ar->index_current].object_guid, + "objectGUID"); if (ret != LDB_SUCCESS) { return replmd_replicated_request_error(ar, ret); } @@ -3866,6 +4220,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))); @@ -3875,12 +4236,39 @@ static int replmd_replicated_apply_add(struct replmd_replicated_request *ar) } } + if (DEBUGLVL(4)) { + 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", + GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid, &guid_txt), + 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 + * the meta data array is already sorted by the caller, except + * for the RDN, which needs to be added. */ + + + 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); + 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); + } + + ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &md->ctr.ctr1, 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; } @@ -3908,12 +4296,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, @@ -3954,12 +4336,14 @@ static int replmd_replicated_apply_search_for_parent_callback(struct ldb_request return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR); } - if (ares->error != LDB_SUCCESS && - ares->error != LDB_ERR_NO_SUCH_OBJECT) { - /* - * TODO: deal with the above error that the parent object doesn't exist - */ + /* + * The error NO_SUCH_OBJECT is not expected, unless the search + * base is the partition DN, and that case doesn't happen here + * because then we wouldn't get a parent_guid_value in any + * case. + */ + if (ares->error != LDB_SUCCESS) { return ldb_module_done(ar->req, ares->controls, ares->response, ares->error); } @@ -4017,9 +4401,13 @@ static int replmd_replicated_apply_search_for_parent_callback(struct ldb_request } 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; + parent_dn + = talloc_steal(ar->objs->objects[ar->index_current].msg, parent_msg->dn); + } + ar->objs->objects[ar->index_current].local_parent_dn = parent_dn; comp_num = ldb_dn_get_comp_num(msg->dn); if (comp_num > 1) { @@ -4039,6 +4427,32 @@ static int replmd_replicated_apply_search_for_parent_callback(struct ldb_request break; case LDB_REPLY_DONE: + + if (ar->objs->objects[ar->index_current].local_parent_dn == NULL) { + struct GUID_txt_buf str_buf; + if (ar->search_msg != NULL) { + ldb_asprintf_errstring(ldb_module_get_ctx(ar->module), + "No parent with GUID %s found for object locally known as %s", + GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid, &str_buf), + ldb_dn_get_linearized(ar->search_msg->dn)); + } else { + ldb_asprintf_errstring(ldb_module_get_ctx(ar->module), + "No parent with GUID %s found for object remotely known as %s", + GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid, &str_buf), + ldb_dn_get_linearized(ar->objs->objects[ar->index_current].msg->dn)); + } + + /* + * This error code is really important, as it + * is the flag back to the callers to retry + * this with DRSUAPI_DRS_GET_ANC, and so get + * the parent objects before the child + * objects + */ + return ldb_module_done(ar->req, NULL, NULL, + replmd_replicated_request_werror(ar, WERR_DS_DRA_MISSING_PARENT)); + } + if (ar->search_msg != NULL) { ret = replmd_replicated_apply_merge(ar); } else { @@ -4068,10 +4482,11 @@ static int replmd_replicated_apply_search_for_parent(struct replmd_replicated_re char *filter; struct ldb_request *search_req; static const char *attrs[] = {"isDeleted", NULL}; + struct GUID_txt_buf guid_str_buf; ldb = ldb_module_get_ctx(ar->module); - if (!ar->objs->objects[ar->index_current].parent_guid_value.data) { + if (ar->objs->objects[ar->index_current].parent_guid == NULL) { if (ar->search_msg != NULL) { return replmd_replicated_apply_merge(ar); } else { @@ -4079,12 +4494,11 @@ static int replmd_replicated_apply_search_for_parent(struct replmd_replicated_re } } - tmp_str = ldb_binary_encode(ar, ar->objs->objects[ar->index_current].parent_guid_value); - if (!tmp_str) return replmd_replicated_request_werror(ar, WERR_NOMEM); + tmp_str = GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid, + &guid_str_buf); filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str); if (!filter) return replmd_replicated_request_werror(ar, WERR_NOMEM); - talloc_free(tmp_str); ret = ldb_build_search_req(&search_req, ldb, @@ -4115,50 +4529,222 @@ static int replmd_replicated_apply_search_for_parent(struct replmd_replicated_re */ static int replmd_replicated_handle_rename(struct replmd_replicated_request *ar, struct ldb_message *msg, - struct ldb_request *parent) + struct ldb_request *parent, + bool *renamed) { - struct ldb_request *req; int ret; TALLOC_CTX *tmp_ctx = talloc_new(msg); struct ldb_result *res; + struct ldb_dn *conflict_dn; + const char *attrs[] = { "replPropertyMetaData", "objectGUID", NULL }; + 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_dn *new_dn; + struct GUID guid; DEBUG(4,("replmd_replicated_request rename %s => %s\n", ldb_dn_get_linearized(ar->search_msg->dn), ldb_dn_get_linearized(msg->dn))); - res = talloc_zero(tmp_ctx, struct ldb_result); - if (!res) { + ret = dsdb_module_rename(ar->module, ar->search_msg->dn, msg->dn, + DSDB_FLAG_NEXT_MODULE, ar->req); + if (ret == LDB_SUCCESS) { talloc_free(tmp_ctx); - return ldb_oom(ldb_module_get_ctx(ar->module)); + *renamed = true; + return ret; } - /* pass rename to the next module - * so it doesn't appear as an originating update */ - ret = ldb_build_rename_req(&req, ldb_module_get_ctx(ar->module), tmp_ctx, - ar->search_msg->dn, msg->dn, - NULL, - ar, - replmd_op_rename_callback, - parent); - LDB_REQ_SET_LOCATION(req); - if (ret != LDB_SUCCESS) { + if (ret != LDB_ERR_ENTRY_ALREADY_EXISTS) { talloc_free(tmp_ctx); + ldb_asprintf_errstring(ldb_module_get_ctx(ar->module), "Failed to locally apply remote rename from %s to %s: %s", + ldb_dn_get_linearized(ar->search_msg->dn), + ldb_dn_get_linearized(msg->dn), + ldb_errstring(ldb_module_get_ctx(ar->module))); return ret; } - ret = dsdb_request_add_controls(req, DSDB_MODIFY_RELAX); + ret = samdb_rodc(ldb_module_get_ctx(ar->module), &rodc); if (ret != LDB_SUCCESS) { - talloc_free(tmp_ctx); - 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_ERR_OPERATIONS_ERROR; } + /* + * we have a conflict, and need to decide if we will keep the + * new record or the old record + */ - ret = ldb_next_request(ar->module, req); + conflict_dn = msg->dn; - if (ret == LDB_SUCCESS) { - ret = ldb_wait(req->handle, LDB_WAIT_ALL); + if (rodc) { + /* + * We are on an RODC, or were a GC for this + * partition, so we have to fail this until + * someone who owns the partition sorts it + * out + */ + ldb_asprintf_errstring(ldb_module_get_ctx(ar->module), + "Conflict adding object '%s' from incoming replication but we are read only for the partition. \n" + " - We must fail the operation until a master for this partition resolves the conflict", + ldb_dn_get_linearized(conflict_dn)); + goto failed; + } + + /* + * first we need the replPropertyMetaData attribute from the + * old record + */ + ret = dsdb_module_search_dn(ar->module, tmp_ctx, &res, conflict_dn, + attrs, + DSDB_FLAG_NEXT_MODULE | + DSDB_SEARCH_SHOW_DELETED | + DSDB_SEARCH_SHOW_RECYCLED, ar->req); + if (ret != LDB_SUCCESS) { + DEBUG(0,(__location__ ": Unable to find object for conflicting record '%s'\n", + ldb_dn_get_linearized(conflict_dn))); + goto failed; } + omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData"); + if (omd_value == NULL) { + DEBUG(0,(__location__ ": Unable to find replPropertyMetaData for conflicting record '%s'\n", + ldb_dn_get_linearized(conflict_dn))); + goto failed; + } + + ndr_err = ndr_pull_struct_blob(omd_value, res->msgs[0], &omd, + (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + DEBUG(0,(__location__ ": Failed to parse old 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. + * + * We expect omd_name to be present, as this is from a local + * search, but while rmd_name should have been given to us by + * the remote server, if it is missing we just prefer the + * local name in + * replmd_replPropertyMetaData1_new_should_be_taken() + */ + rmd_name = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name); + omd_name = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name); + if (!omd_name) { + DEBUG(0,(__location__ ": Failed to find name attribute in local LDB replPropertyMetaData for %s\n", + ldb_dn_get_linearized(conflict_dn))); + goto failed; + } + + /* + * Should we preserve the current record, and so rename the + * incoming record to be a conflict? + */ + rename_incoming_record = + !replmd_replPropertyMetaData1_new_should_be_taken( + ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING, + omd_name, rmd_name); + + if (rename_incoming_record) { + + new_dn = replmd_conflict_dn(msg, msg->dn, + &ar->objs->objects[ar->index_current].object_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(conflict_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); + } + + msg->dn = new_dn; + *renamed = true; + return LDB_SUCCESS; + } + + /* we are renaming the existing record */ + + guid = samdb_result_guid(res->msgs[0], "objectGUID"); + if (GUID_all_zero(&guid)) { + DEBUG(0,(__location__ ": Failed to find objectGUID for existing conflict record %s\n", + ldb_dn_get_linearized(conflict_dn))); + goto failed; + } + + new_dn = replmd_conflict_dn(tmp_ctx, conflict_dn, &guid); + if (new_dn == NULL) { + DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n", + ldb_dn_get_linearized(conflict_dn))); + goto failed; + } + + DEBUG(2,(__location__ ": Resolving conflict record via existing-record rename '%s' -> '%s'\n", + ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn))); + + ret = dsdb_module_rename(ar->module, conflict_dn, new_dn, + DSDB_FLAG_OWN_MODULE, ar->req); + if (ret != LDB_SUCCESS) { + DEBUG(0,(__location__ ": Failed to rename conflict dn '%s' to '%s' - %s\n", + ldb_dn_get_linearized(conflict_dn), + ldb_dn_get_linearized(new_dn), + ldb_errstring(ldb_module_get_ctx(ar->module)))); + goto failed; + } + + /* + * now we need to ensure that the rename is seen as an + * originating update. We do that with a modify. + */ + ret = replmd_name_modify(ar, ar->req, new_dn); + if (ret != LDB_SUCCESS) { + goto failed; + } + + DEBUG(2,(__location__ ": With conflicting record renamed, re-apply replicated rename '%s' -> '%s'\n", + ldb_dn_get_linearized(ar->search_msg->dn), + ldb_dn_get_linearized(msg->dn))); + + + ret = dsdb_module_rename(ar->module, ar->search_msg->dn, msg->dn, + DSDB_FLAG_NEXT_MODULE, ar->req); + if (ret != LDB_SUCCESS) { + DEBUG(0,(__location__ ": After conflict resolution, failed to rename dn '%s' to '%s' - %s\n", + ldb_dn_get_linearized(ar->search_msg->dn), + ldb_dn_get_linearized(msg->dn), + ldb_errstring(ldb_module_get_ctx(ar->module)))); + goto failed; + } +failed: + + /* + * On failure make the caller get the error + * This means replication will stop with an error, + * but there is not much else we can do. In the + * LDB_ERR_ENTRY_ALREADY_EXISTS case this is exactly what is + * needed. + */ + talloc_free(tmp_ctx); return ret; } @@ -4175,6 +4761,7 @@ static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar) const struct ldb_val *omd_value; struct replPropertyMetaDataBlob nmd; struct ldb_val nmd_value; + struct GUID remote_parent_guid; unsigned int i; uint32_t j,ni=0; unsigned int removed_attrs = 0; @@ -4186,10 +4773,21 @@ static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar) bool take_remote_isDeleted = false; bool sd_updated = false; bool renamed = false; + bool is_schema_nc = false; + NTSTATUS nt_status; + const struct ldb_val *old_rdn, *new_rdn; + struct replmd_private *replmd_private = + talloc_get_type(ldb_module_get_private(ar->module), + struct replmd_private); + NTTIME now; + time_t t = time(NULL); + unix_to_nt_time(&now, t); ldb = ldb_module_get_ctx(ar->module); msg = ar->objs->objects[ar->index_current].msg; + is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0; + rmd = ar->objs->objects[ar->index_current].meta_data; ZERO_STRUCT(omd); omd.version = 1; @@ -4200,7 +4798,7 @@ static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar) ndr_err = ndr_pull_struct_blob(omd_value, ar, &omd, (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob); if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { - NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err); + nt_status = ndr_map_error2ntstatus(ndr_err); return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status)); } @@ -4209,12 +4807,52 @@ static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar) } } + if (DEBUGLVL(5)) { + 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" + "%s\n" + "%s\n", + GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid, &guid_txt), + s, + ndr_print_struct_string(s, + (ndr_print_fn_t)ndr_print_replPropertyMetaDataBlob, + "existing replPropertyMetaData", + &omd), + ndr_print_struct_string(s, + (ndr_print_fn_t)ndr_print_replPropertyMetaDataBlob, + "incoming replPropertyMetaData", + rmd))); + talloc_free(s); + } + local_isDeleted = ldb_msg_find_attr_as_bool(ar->search_msg, "isDeleted", false); remote_isDeleted = ldb_msg_find_attr_as_bool(msg, "isDeleted", false); - if (strcmp(ldb_dn_get_linearized(msg->dn), ldb_dn_get_linearized(ar->search_msg->dn)) == 0) { + /* + * Fill in the remote_parent_guid with the GUID or an all-zero + * GUID. + */ + if (ar->objs->objects[ar->index_current].parent_guid != NULL) { + remote_parent_guid = *ar->objs->objects[ar->index_current].parent_guid; + } else { + remote_parent_guid = GUID_zero(); + } + + /* + * To ensure we follow a complex rename chain around, we have + * to confirm that the DN is the same (mostly to confirm the + * RDN) and the parentGUID is the same. + * + * This ensures we keep things under the correct parent, which + * replmd_replicated_handle_rename() will do. + */ + + if (strcmp(ldb_dn_get_linearized(msg->dn), ldb_dn_get_linearized(ar->search_msg->dn)) == 0 + && GUID_equal(&remote_parent_guid, &ar->local_parent_guid)) { ret = LDB_SUCCESS; } else { /* @@ -4227,49 +4865,10 @@ static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar) * the peer has an older name to what we have (see * replmd_replicated_apply_search_callback()) */ - renamed = true; - ret = replmd_replicated_handle_rename(ar, msg, ar->req); + ret = replmd_replicated_handle_rename(ar, msg, ar->req, &renamed); } - /* - * 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; - renamed = true; - } else if (ret != LDB_SUCCESS) { + 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), @@ -4278,6 +4877,14 @@ static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar) return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR); } + if (renamed == true) { + /* + * Set the callback to one that will fix up the name + * metadata on the new conflict DN + */ + callback = replmd_op_name_modify_callback; + } + ZERO_STRUCT(nmd); nmd.version = 1; nmd.ctr.ctr1.count = omd.ctr.ctr1.count + rmd->ctr.ctr1.count; @@ -4304,18 +4911,10 @@ static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar) continue; } - 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 */ - cmp = !replmd_replPropertyMetaData1_is_newer(&rmd->ctr.ctr1.array[i], - &nmd.ctr.ctr1.array[j]); - } else { - cmp = replmd_replPropertyMetaData1_is_newer(&nmd.ctr.ctr1.array[j], - &rmd->ctr.ctr1.array[i]); - } + cmp = replmd_replPropertyMetaData1_new_should_be_taken( + ar->objs->dsdb_repl_flags, + &nmd.ctr.ctr1.array[j], + &rmd->ctr.ctr1.array[i]); if (cmp) { /* replace the entry */ nmd.ctr.ctr1.array[j] = rmd->ctr.ctr1.array[i]; @@ -4383,15 +4982,23 @@ static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar) */ nmd.ctr.ctr1.count = ni; + new_rdn = ldb_dn_get_rdn_val(msg->dn); + old_rdn = ldb_dn_get_rdn_val(ar->search_msg->dn); + + if (renamed) { + ret = replmd_update_rpmd_rdn_attr(ldb, msg, new_rdn, old_rdn, + &nmd, ar, now, is_schema_nc); + 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); + } + } /* - * the rdn attribute (the alias for the name attribute), - * 'cn' for most objects is the last entry in the meta data array - * we have stored - * * 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, msg->dn); if (ret != LDB_SUCCESS) { + ldb_asprintf_errstring(ldb, "%s: error during DRS repl merge: %s", __func__, ldb_errstring(ldb)); return ret; } @@ -4439,7 +5046,7 @@ static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar) ndr_err = ndr_push_struct_blob(&nmd_value, msg, &nmd, (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob); if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { - NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err); + nt_status = ndr_map_error2ntstatus(ndr_err); return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status)); } @@ -4465,11 +5072,23 @@ 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)) { + struct GUID_txt_buf guid_txt; + char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_MODIFY, msg); - DEBUG(4, ("DRS replication modify message:\n%s\n", s)); + 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), + s)); talloc_free(s); } @@ -4528,7 +5147,8 @@ static int replmd_replicated_apply_search_callback(struct ldb_request *req, const struct ldb_val *omd_value; struct replPropertyMetaDataBlob *rmd; struct ldb_message *msg; - + int instanceType; + ar->objs->objects[ar->index_current].local_parent_dn = NULL; ar->objs->objects[ar->index_current].last_known_parent = NULL; /* @@ -4570,33 +5190,100 @@ static int replmd_replicated_apply_search_callback(struct ldb_request *req, } } + ar->local_parent_guid = samdb_result_guid(ar->search_msg, "parentGUID"); + + instanceType = ldb_msg_find_attr_as_int(ar->search_msg, "instanceType", 0); + if (((instanceType & INSTANCE_TYPE_IS_NC_HEAD) == 0) + && GUID_all_zero(&ar->local_parent_guid)) { + DEBUG(0, ("Refusing to replicate new version of %s " + "as local object has an all-zero parentGUID attribute, " + "despite not being an NC root\n", + ldb_dn_get_linearized(ar->search_msg->dn))); + return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR); + } + /* * now we need to check for double renames. We could have a * local rename pending which our replication partner hasn't * received yet. We choose which one wins by looking at the - * attribute stamps on the two objects, the newer one wins + * attribute stamps on the two objects, the newer one wins. + * + * This also simply applies the correct algorithms for + * determining if a change was made to name at all, or + * if the object has just been renamed under the same + * parent. */ md_remote = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name); - md_local = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name); - /* if there is no name attribute then we have to assume the - object we've received is in fact newer */ - if (ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING || - !md_remote || !md_local || - replmd_replPropertyMetaData1_is_newer(md_local, md_remote)) { + md_local = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name); + if (!md_local) { + DEBUG(0,(__location__ ": Failed to find name attribute in local LDB replPropertyMetaData for %s\n", + ldb_dn_get_linearized(ar->search_msg->dn))); + return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR); + } + + /* + * if there is no name attribute given then we have to assume the + * object we've received has the older name + */ + if (replmd_replPropertyMetaData1_new_should_be_taken( + ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING, + md_local, md_remote)) { + struct GUID_txt_buf p_guid_local; + struct GUID_txt_buf p_guid_remote; + msg = ar->objs->objects[ar->index_current].msg; + + /* Merge on the existing object, with rename */ + + DEBUG(4,(__location__ ": Looking for new parent for object %s currently under %s " + "as incoming object changing to %s under %s\n", + ldb_dn_get_linearized(ar->search_msg->dn), + GUID_buf_string(&ar->local_parent_guid, &p_guid_local), + ldb_dn_get_linearized(msg->dn), + GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid, + &p_guid_remote))); ret = replmd_replicated_apply_search_for_parent(ar); } else { + struct GUID_txt_buf p_guid_local; + struct GUID_txt_buf p_guid_remote; msg = ar->objs->objects[ar->index_current].msg; - /* Otherwise, just merge on the existing object, force no rename */ - DEBUG(4,(__location__ ": Keeping object %s and rejecting older rename to %s\n", - ldb_dn_get_linearized(ar->search_msg->dn), - ldb_dn_get_linearized(msg->dn))); + /* + * Merge on the existing object, force no + * rename (code below just to explain why in + * the DEBUG() logs) + */ + if (strcmp(ldb_dn_get_linearized(ar->search_msg->dn), + ldb_dn_get_linearized(msg->dn)) == 0) { + if (ar->objs->objects[ar->index_current].parent_guid != NULL && + GUID_equal(&ar->local_parent_guid, + ar->objs->objects[ar->index_current].parent_guid) + == false) { + DEBUG(4,(__location__ ": Keeping object %s at under %s " + "despite incoming object changing parent to %s\n", + ldb_dn_get_linearized(ar->search_msg->dn), + GUID_buf_string(&ar->local_parent_guid, &p_guid_local), + GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid, + &p_guid_remote))); + } + } else { + DEBUG(4,(__location__ ": Keeping object %s at under %s " + " and rejecting older rename to %s under %s\n", + ldb_dn_get_linearized(ar->search_msg->dn), + GUID_buf_string(&ar->local_parent_guid, &p_guid_local), + ldb_dn_get_linearized(msg->dn), + GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid, + &p_guid_remote))); + } /* * This assignment ensures that the strcmp() - * in replmd_replicated_apply_merge() avoids - * the rename call + * and GUID_equal() calls in + * replmd_replicated_apply_merge() avoids the + * rename call */ + ar->objs->objects[ar->index_current].parent_guid = + &ar->local_parent_guid; + msg->dn = ar->search_msg->dn; ret = replmd_replicated_apply_merge(ar); } @@ -4619,6 +5306,11 @@ static int replmd_replicated_apply_next(struct replmd_replicated_request *ar) char *tmp_str; char *filter; struct ldb_request *search_req; + static const char *attrs[] = { "repsFrom", "replUpToDateVector", + "parentGUID", "instanceType", + "replPropertyMetaData", "nTSecurityDescriptor", + "isDeleted", NULL }; + struct GUID_txt_buf guid_str_buf; if (ar->index_current >= ar->objs->num_objects) { /* done with it, go to next stage */ @@ -4629,27 +5321,26 @@ static int replmd_replicated_apply_next(struct replmd_replicated_request *ar) ar->search_msg = NULL; ar->isDeleted = false; - tmp_str = ldb_binary_encode(ar, ar->objs->objects[ar->index_current].guid_value); - if (!tmp_str) return replmd_replicated_request_werror(ar, WERR_NOMEM); + tmp_str = GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid, + &guid_str_buf); filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str); if (!filter) return replmd_replicated_request_werror(ar, WERR_NOMEM); - talloc_free(tmp_str); ret = ldb_build_search_req(&search_req, ldb, ar, - NULL, + ar->objs->partition_dn, LDB_SCOPE_SUBTREE, filter, - NULL, + attrs, NULL, ar, replmd_replicated_apply_search_callback, ar->req); LDB_REQ_SET_LOCATION(search_req); - ret = dsdb_request_add_controls(search_req, DSDB_SEARCH_SEARCH_ALL_PARTITIONS|DSDB_SEARCH_SHOW_RECYCLED); + ret = dsdb_request_add_controls(search_req, DSDB_SEARCH_SHOW_RECYCLED); if (ret != LDB_SUCCESS) { return ret; @@ -4775,7 +5466,6 @@ static int replmd_replicated_uptodate_modify(struct replmd_replicated_request *a struct replUpToDateVectorBlob nuv; struct ldb_val nuv_value; struct ldb_message_element *nuv_el = NULL; - const struct GUID *our_invocation_id; struct ldb_message_element *orf_el = NULL; struct repsFromToBlob nrf; struct ldb_val *nrf_value = NULL; @@ -4849,20 +5539,12 @@ static int replmd_replicated_uptodate_modify(struct replmd_replicated_request *a ni++; } - /* get our invocation_id if we have one already attached to the ldb */ - our_invocation_id = samdb_ntds_invocation_id(ldb); - if (our_invocation_id == NULL) { - DEBUG(0, ("repl_meta_data: Could not find our own server's invocationID!\n")); - return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR); - } - /* merge in the source_dsa vector is available */ for (i=0; (ruv && i < ruv->count); i++) { found = false; - if (our_invocation_id && - GUID_equal(&ruv->cursors[i].source_dsa_invocation_id, - our_invocation_id)) { + if (GUID_equal(&ruv->cursors[i].source_dsa_invocation_id, + &ar->our_invocation_id)) { continue; } @@ -5282,8 +5964,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 @@ -5484,7 +6166,7 @@ linked_attributes[0]: if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) { /* remove the existing backlink */ - ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid, false, attr, false); + ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid, false, attr, true); if (ret != LDB_SUCCESS) { talloc_free(tmp_ctx); return ret; @@ -5504,7 +6186,7 @@ linked_attributes[0]: if (active) { /* add the new backlink */ - ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid, true, attr, false); + ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid, true, attr, true); if (ret != LDB_SUCCESS) { talloc_free(tmp_ctx); return ret; @@ -5531,7 +6213,7 @@ linked_attributes[0]: la->meta_data.originating_usn, seq_num, la->meta_data.originating_change_time, la->meta_data.version, - (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)?false:true); + !active); if (ret != LDB_SUCCESS) { talloc_free(tmp_ctx); return ret; @@ -5539,7 +6221,7 @@ linked_attributes[0]: if (active) { ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid, - true, attr, false); + true, attr, true); if (ret != LDB_SUCCESS) { talloc_free(tmp_ctx); return ret;