const struct replPropertyMetaData1 *m2,
const uint32_t *rdn_attid)
{
- if (m1->attid == m2->attid) {
+ /*
+ * 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)
+ */
+ uint32_t attid_1 = m1->attid;
+ uint32_t attid_2 = m2->attid;
+
+ if (attid_1 == attid_2) {
return 0;
}
* so we need to return a value greater than zero
* which means m1 is greater than m2
*/
- if (m1->attid == *rdn_attid) {
+ if (attid_1 == *rdn_attid) {
return 1;
}
* so we need to return a value less than zero
* which means m2 is greater than m1
*/
- if (m2->attid == *rdn_attid) {
+ if (attid_2 == *rdn_attid) {
return -1;
}
- return m1->attid > m2->attid ? 1 : -1;
+ /*
+ * 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).
+ */
+ 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,
+ const struct dsdb_attribute *rdn_sa,
+ struct ldb_dn *dn)
+{
+ 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;
+ }
+ if (ctr1->array[ctr1->count - 1].attid != rdn_sa->attributeID_id) {
+ ldb_debug_set(ldb, LDB_DEBUG_FATAL,
+ "No rDN found in replPropertyMetaData for %s!\n",
+ ldb_dn_get_linearized(dn));
+ return LDB_ERR_CONSTRAINT_VIOLATION;
+ }
+
+ /* 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;
+ }
+
+ return LDB_SUCCESS;
+}
+
+static int replmd_replPropertyMetaDataCtr1_sort_and_verify(struct ldb_context *ldb,
+ struct replPropertyMetaDataCtr1 *ctr1,
+ const struct dsdb_schema *schema,
+ 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;
+ ldb_debug_set(ldb, LDB_DEBUG_FATAL,
+ __location__ ": No rDN for %s?\n",
+ ldb_dn_get_linearized(dn));
+ return LDB_ERR_INVALID_DN_SYNTAX;
}
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;
+ ldb_debug_set(ldb, LDB_DEBUG_FATAL,
+ __location__ ": No sa found for rDN %s for %s\n",
+ rdn_name, ldb_dn_get_linearized(dn));
+ return LDB_ERR_UNDEFINED_ATTRIBUTE_TYPE;
}
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;
+ LDB_TYPESAFE_QSORT(ctr1->array, ctr1->count, &rdn_sa->attributeID_id,
+ replmd_replPropertyMetaData1_attid_sort);
+ return replmd_replPropertyMetaDataCtr1_verify(ldb, ctr1, rdn_sa, dn);
}
static int replmd_ldb_message_element_attid_sort(const struct ldb_message_element *e1,
/*
* sort meta data array, and move the rdn attribute entry to the end
*/
- ret = replmd_replPropertyMetaDataCtr1_sort(&nmd.ctr.ctr1, ac->schema, msg->dn);
+ ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &nmd.ctr.ctr1, ac->schema, 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;
}
*/
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);
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;
- }
-
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)"\
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",
return ret;
}
- if (is_urgent && !*is_urgent && (situation == REPL_URGENT_ON_UPDATE)) {
+ if (!*is_urgent && (situation == REPL_URGENT_ON_UPDATE)) {
*is_urgent = replmd_check_urgent_attribute(&msg->elements[i]);
}
}
}
+
+ /*
+ * 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
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, schema, msg->dn);
if (ret != LDB_SUCCESS) {
+ ldb_asprintf_errstring(ldb, "%s: %s", __func__, ldb_errstring(ldb));
return ret;
}
static struct parsed_dn *parsed_dn_find(struct parsed_dn *pdn,
unsigned int count, struct GUID *guid,
- struct ldb_dn *dn)
+ struct ldb_dn *dn,
+ DATA_BLOB extra)
{
struct parsed_dn *ret;
unsigned int i;
return NULL;
}
BINARY_ARRAY_SEARCH(pdn, count, guid, guid, GUID_compare, ret);
+ if (extra.length && ret) {
+ /*
+ * Check the content of the extra it's a match only
+ * if the dn match but also the extra.
+ */
+ if (ret->dsdb_dn->extra_part.length != extra.length) {
+ return NULL;
+ }
+ if (memcmp(ret->dsdb_dn->extra_part.data, extra.data, extra.length) != 0) {
+ return NULL;
+ }
+ }
return ret;
}
/* for each new value, see if it exists already with the same GUID */
for (i=0; i<el->num_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,
+ dns[i].dsdb_dn->extra_part);
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);
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,
+ p->dsdb_dn->extra_part);
if (!p2) {
ldb_asprintf_errstring(ldb, "Attribute %s doesn't exist for target GUID %s",
el->name, GUID_string(tmp_ctx, p->guid));
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,
+ p->dsdb_dn->extra_part) == NULL) {
continue;
}
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,
+ old_p->dsdb_dn->extra_part);
if (p) {
/* we don't delete it if we are re-adding it */
continue;
if (old_dns &&
(old_p = parsed_dn_find(old_dns,
- old_num_values, p->guid, NULL)) != NULL) {
+ old_num_values, p->guid, NULL,
+ p->dsdb_dn->extra_part)) != NULL) {
/* update in place */
ret = replmd_update_la_val(old_el->values, old_p->v, p->dsdb_dn,
old_p->dsdb_dn, invocation_id,
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);
}
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);
*/
continue;
}
- if (!sa->linkID && ldb_attr_in_list(preserved_attrs, el->name)) {
- continue;
+ if (!sa->linkID) {
+ if (ldb_attr_in_list(preserved_attrs, el->name)) {
+ continue;
+ }
+ if (sa->searchFlags & SEARCH_FLAG_PRESERVEONDELETE) {
+ continue;
+ }
}
ret = ldb_msg_add_empty(msg, el->name, LDB_FLAG_MOD_DELETE, &el);
if (ret != LDB_SUCCESS) {
unsigned int i;
int ret;
bool remote_isDeleted = false;
+ const struct dsdb_attribute *rdn_sa;
+ const char *rdn_name;
ldb = ldb_module_get_ctx(ar->module);
msg = ar->objs->objects[ar->index_current].msg;
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)));
}
}
+ 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);
+ }
+
remote_isDeleted = ldb_msg_find_attr_as_bool(msg,
"isDeleted", false);
/*
* the meta data array is already sorted by the caller
*/
+
+ rdn_name = ldb_dn_get_rdn_name(msg->dn);
+ if (rdn_name == NULL) {
+ ldb_asprintf_errstring(ldb, __location__ ": No rDN for %s?\n", ldb_dn_get_linearized(msg->dn));
+ return replmd_replicated_request_error(ar, LDB_ERR_INVALID_DN_SYNTAX);
+ }
+
+ rdn_sa = dsdb_attribute_by_lDAPDisplayName(ar->schema, rdn_name);
+ if (rdn_sa == NULL) {
+ ldb_asprintf_errstring(ldb, ": No schema attribute found for rDN %s for %s\n",
+ rdn_name, ldb_dn_get_linearized(msg->dn));
+ return replmd_replicated_request_error(ar, LDB_ERR_UNDEFINED_ATTRIBUTE_TYPE);
+ }
+
+ ret = replmd_replPropertyMetaDataCtr1_verify(ldb, &md->ctr.ctr1, rdn_sa, 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;
}
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,
}
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 */
+ /*
+ * 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 i and j, and negating the
+ * outcome.
+ */
cmp = !replmd_replPropertyMetaData1_is_newer(&rmd->ctr.ctr1.array[i],
&nmd.ctr.ctr1.array[j]);
} else {
*
* 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, ar->schema, msg->dn);
if (ret != LDB_SUCCESS) {
+ ldb_asprintf_errstring(ldb, "%s: error during DRS repl merge: %s", __func__, ldb_errstring(ldb));
return ret;
}
/* 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)) {
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
}
/* see if this link already exists */
- pdn = parsed_dn_find(pdn_list, old_el->num_values, &guid, dsdb_dn->dn);
+ pdn = parsed_dn_find(pdn_list, old_el->num_values, &guid, dsdb_dn->dn,
+ dsdb_dn->extra_part);
if (pdn != NULL) {
/* see if this update is newer than what we have already */
struct GUID invocation_id = GUID_zero();