4 Copyright (C) Simo Sorce 2004-2008
5 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
6 Copyright (C) Andrew Tridgell 2005
7 Copyright (C) Stefan Metzmacher <metze@samba.org> 2007
8 Copyright (C) Matthieu Patou <mat@samba.org> 2010
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 3 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>.
27 * Component: ldb repl_meta_data module
29 * Description: - add a unique objectGUID onto every new record,
30 * - handle whenCreated, whenChanged timestamps
31 * - handle uSNCreated, uSNChanged numbers
32 * - handle replPropertyMetaData attribute
35 * Author: Stefan Metzmacher
39 #include "ldb_module.h"
40 #include "dsdb/samdb/samdb.h"
41 #include "dsdb/common/proto.h"
42 #include "../libds/common/flags.h"
43 #include "librpc/gen_ndr/ndr_misc.h"
44 #include "librpc/gen_ndr/ndr_drsuapi.h"
45 #include "librpc/gen_ndr/ndr_drsblobs.h"
46 #include "param/param.h"
47 #include "libcli/security/security.h"
48 #include "lib/util/dlinklist.h"
49 #include "dsdb/samdb/ldb_modules/util.h"
50 #include "lib/util/binsearch.h"
51 #include "lib/util/tsort.h"
53 static const uint64_t DELETED_OBJECT_CONTAINER_CHANGE_TIME = 253402127999L;
54 struct replmd_private {
56 struct la_entry *la_list;
58 struct la_backlink *la_backlinks;
60 struct nc_entry *prev, *next;
63 uint64_t mod_usn_urgent;
68 struct la_entry *next, *prev;
69 struct drsuapi_DsReplicaLinkedAttribute *la;
72 struct replmd_replicated_request {
73 struct ldb_module *module;
74 struct ldb_request *req;
76 const struct dsdb_schema *schema;
78 /* the controls we pass down */
79 struct ldb_control **controls;
81 /* details for the mode where we apply a bunch of inbound replication meessages */
83 uint32_t index_current;
84 struct dsdb_extended_replicated_objects *objs;
86 struct ldb_message *search_msg;
90 const struct dsdb_attribute *isRecycled;
91 const struct dsdb_attribute *lastKnownRDN;
92 const struct dsdb_attribute *isDeleted;
93 bool is_recycled_tested;
96 enum urgent_situation {
97 REPL_URGENT_ON_CREATE = 1,
98 REPL_URGENT_ON_UPDATE = 2,
99 REPL_URGENT_ON_DELETE = 4
103 static const struct {
104 const char *update_name;
105 enum urgent_situation repl_situation;
106 } urgent_objects[] = {
107 {"nTDSDSA", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE)},
108 {"crossRef", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE)},
109 {"attributeSchema", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
110 {"classSchema", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
111 {"secret", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
112 {"rIDManager", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
116 /* Attributes looked for when updating or deleting, to check for a urgent replication needed */
117 static const char *urgent_attrs[] = {
120 "userAccountControl",
125 static bool replmd_check_urgent_objectclass(const struct ldb_message_element *objectclass_el,
126 enum urgent_situation situation)
129 for (i=0; urgent_objects[i].update_name; i++) {
131 if ((situation & urgent_objects[i].repl_situation) == 0) {
135 for (j=0; j<objectclass_el->num_values; j++) {
136 const struct ldb_val *v = &objectclass_el->values[j];
137 if (ldb_attr_cmp((const char *)v->data, urgent_objects[i].update_name) == 0) {
145 static bool replmd_check_urgent_attribute(const struct ldb_message_element *el)
147 if (ldb_attr_in_list(urgent_attrs, el->name)) {
154 static int replmd_replicated_apply_next(struct replmd_replicated_request *ar);
157 initialise the module
158 allocate the private structure and build the list
159 of partition DNs for use by replmd_notify()
161 static int replmd_init(struct ldb_module *module)
163 struct replmd_private *replmd_private;
164 struct ldb_context *ldb = ldb_module_get_ctx(module);
166 replmd_private = talloc_zero(module, struct replmd_private);
167 if (replmd_private == NULL) {
169 return LDB_ERR_OPERATIONS_ERROR;
171 ldb_module_set_private(module, replmd_private);
173 return ldb_next_init(module);
177 cleanup our per-transaction contexts
179 static void replmd_txn_cleanup(struct replmd_private *replmd_private)
181 talloc_free(replmd_private->la_ctx);
182 replmd_private->la_list = NULL;
183 replmd_private->la_ctx = NULL;
185 talloc_free(replmd_private->bl_ctx);
186 replmd_private->la_backlinks = NULL;
187 replmd_private->bl_ctx = NULL;
192 struct la_backlink *next, *prev;
193 const char *attr_name;
194 struct GUID forward_guid, target_guid;
199 process a backlinks we accumulated during a transaction, adding and
200 deleting the backlinks from the target objects
202 static int replmd_process_backlink(struct ldb_module *module, struct la_backlink *bl, struct ldb_request *parent)
204 struct ldb_dn *target_dn, *source_dn;
206 struct ldb_context *ldb = ldb_module_get_ctx(module);
207 struct ldb_message *msg;
208 TALLOC_CTX *tmp_ctx = talloc_new(bl);
214 - construct ldb_message
215 - either an add or a delete
217 ret = dsdb_module_dn_by_guid(module, tmp_ctx, &bl->target_guid, &target_dn, parent);
218 if (ret != LDB_SUCCESS) {
219 DEBUG(2,(__location__ ": WARNING: Failed to find target DN for linked attribute with GUID %s\n",
220 GUID_string(bl, &bl->target_guid)));
224 ret = dsdb_module_dn_by_guid(module, tmp_ctx, &bl->forward_guid, &source_dn, parent);
225 if (ret != LDB_SUCCESS) {
226 ldb_asprintf_errstring(ldb, "Failed to find source DN for linked attribute with GUID %s\n",
227 GUID_string(bl, &bl->forward_guid));
228 talloc_free(tmp_ctx);
232 msg = ldb_msg_new(tmp_ctx);
234 ldb_module_oom(module);
235 talloc_free(tmp_ctx);
236 return LDB_ERR_OPERATIONS_ERROR;
239 /* construct a ldb_message for adding/deleting the backlink */
241 dn_string = ldb_dn_get_extended_linearized(tmp_ctx, source_dn, 1);
243 ldb_module_oom(module);
244 talloc_free(tmp_ctx);
245 return LDB_ERR_OPERATIONS_ERROR;
247 ret = ldb_msg_add_steal_string(msg, bl->attr_name, dn_string);
248 if (ret != LDB_SUCCESS) {
249 talloc_free(tmp_ctx);
252 msg->elements[0].flags = bl->active?LDB_FLAG_MOD_ADD:LDB_FLAG_MOD_DELETE;
254 /* a backlink should never be single valued. Unfortunately the
255 exchange schema has a attribute
256 msExchBridgeheadedLocalConnectorsDNBL which is single
257 valued and a backlink. We need to cope with that by
258 ignoring the single value flag */
259 msg->elements[0].flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
261 ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE, parent);
262 if (ret == LDB_ERR_NO_SUCH_ATTRIBUTE && !bl->active) {
263 /* we allow LDB_ERR_NO_SUCH_ATTRIBUTE as success to
264 cope with possible corruption where the backlink has
265 already been removed */
266 DEBUG(3,("WARNING: backlink from %s already removed from %s - %s\n",
267 ldb_dn_get_linearized(target_dn),
268 ldb_dn_get_linearized(source_dn),
269 ldb_errstring(ldb)));
271 } else if (ret != LDB_SUCCESS) {
272 ldb_asprintf_errstring(ldb, "Failed to %s backlink from %s to %s - %s",
273 bl->active?"add":"remove",
274 ldb_dn_get_linearized(source_dn),
275 ldb_dn_get_linearized(target_dn),
277 talloc_free(tmp_ctx);
280 talloc_free(tmp_ctx);
285 add a backlink to the list of backlinks to add/delete in the prepare
288 static int replmd_add_backlink(struct ldb_module *module, const struct dsdb_schema *schema,
289 struct GUID *forward_guid, struct GUID *target_guid,
290 bool active, const struct dsdb_attribute *schema_attr, bool immediate)
292 const struct dsdb_attribute *target_attr;
293 struct la_backlink *bl;
294 struct replmd_private *replmd_private =
295 talloc_get_type_abort(ldb_module_get_private(module), struct replmd_private);
297 target_attr = dsdb_attribute_by_linkID(schema, schema_attr->linkID ^ 1);
300 * windows 2003 has a broken schema where the
301 * definition of msDS-IsDomainFor is missing (which is
302 * supposed to be the backlink of the
303 * msDS-HasDomainNCs attribute
308 /* see if its already in the list */
309 for (bl=replmd_private->la_backlinks; bl; bl=bl->next) {
310 if (GUID_equal(forward_guid, &bl->forward_guid) &&
311 GUID_equal(target_guid, &bl->target_guid) &&
312 (target_attr->lDAPDisplayName == bl->attr_name ||
313 strcmp(target_attr->lDAPDisplayName, bl->attr_name) == 0)) {
319 /* we found an existing one */
320 if (bl->active == active) {
323 DLIST_REMOVE(replmd_private->la_backlinks, bl);
328 if (replmd_private->bl_ctx == NULL) {
329 replmd_private->bl_ctx = talloc_new(replmd_private);
330 if (replmd_private->bl_ctx == NULL) {
331 ldb_module_oom(module);
332 return LDB_ERR_OPERATIONS_ERROR;
337 bl = talloc(replmd_private->bl_ctx, struct la_backlink);
339 ldb_module_oom(module);
340 return LDB_ERR_OPERATIONS_ERROR;
343 /* Ensure the schema does not go away before the bl->attr_name is used */
344 if (!talloc_reference(bl, schema)) {
346 ldb_module_oom(module);
347 return LDB_ERR_OPERATIONS_ERROR;
350 bl->attr_name = target_attr->lDAPDisplayName;
351 bl->forward_guid = *forward_guid;
352 bl->target_guid = *target_guid;
355 /* the caller may ask for this backlink to be processed
358 int ret = replmd_process_backlink(module, bl, NULL);
363 DLIST_ADD(replmd_private->la_backlinks, bl);
370 * Callback for most write operations in this module:
372 * notify the repl task that a object has changed. The notifies are
373 * gathered up in the replmd_private structure then written to the
374 * @REPLCHANGED object in each partition during the prepare_commit
376 static int replmd_op_callback(struct ldb_request *req, struct ldb_reply *ares)
379 struct replmd_replicated_request *ac =
380 talloc_get_type_abort(req->context, struct replmd_replicated_request);
381 struct replmd_private *replmd_private =
382 talloc_get_type_abort(ldb_module_get_private(ac->module), struct replmd_private);
383 struct nc_entry *modified_partition;
384 struct ldb_control *partition_ctrl;
385 const struct dsdb_control_current_partition *partition;
387 struct ldb_control **controls;
389 partition_ctrl = ldb_reply_get_control(ares, DSDB_CONTROL_CURRENT_PARTITION_OID);
391 controls = ares->controls;
392 if (ldb_request_get_control(ac->req,
393 DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
395 * Remove the current partition control from what we pass up
396 * the chain if it hasn't been requested manually.
398 controls = ldb_controls_except_specified(ares->controls, ares,
402 if (ares->error != LDB_SUCCESS) {
403 DEBUG(5,("%s failure. Error is: %s\n", __FUNCTION__, ldb_strerror(ares->error)));
404 return ldb_module_done(ac->req, controls,
405 ares->response, ares->error);
408 if (ares->type != LDB_REPLY_DONE) {
409 ldb_set_errstring(ldb_module_get_ctx(ac->module), "Invalid reply type for notify\n!");
410 return ldb_module_done(ac->req, NULL,
411 NULL, LDB_ERR_OPERATIONS_ERROR);
414 if (!partition_ctrl) {
415 ldb_set_errstring(ldb_module_get_ctx(ac->module),"No partition control on reply");
416 return ldb_module_done(ac->req, NULL,
417 NULL, LDB_ERR_OPERATIONS_ERROR);
420 partition = talloc_get_type_abort(partition_ctrl->data,
421 struct dsdb_control_current_partition);
423 if (ac->seq_num > 0) {
424 for (modified_partition = replmd_private->ncs; modified_partition;
425 modified_partition = modified_partition->next) {
426 if (ldb_dn_compare(modified_partition->dn, partition->dn) == 0) {
431 if (modified_partition == NULL) {
432 modified_partition = talloc_zero(replmd_private, struct nc_entry);
433 if (!modified_partition) {
434 ldb_oom(ldb_module_get_ctx(ac->module));
435 return ldb_module_done(ac->req, NULL,
436 NULL, LDB_ERR_OPERATIONS_ERROR);
438 modified_partition->dn = ldb_dn_copy(modified_partition, partition->dn);
439 if (!modified_partition->dn) {
440 ldb_oom(ldb_module_get_ctx(ac->module));
441 return ldb_module_done(ac->req, NULL,
442 NULL, LDB_ERR_OPERATIONS_ERROR);
444 DLIST_ADD(replmd_private->ncs, modified_partition);
447 if (ac->seq_num > modified_partition->mod_usn) {
448 modified_partition->mod_usn = ac->seq_num;
450 modified_partition->mod_usn_urgent = ac->seq_num;
455 if (ac->apply_mode) {
459 ret = replmd_replicated_apply_next(ac);
460 if (ret != LDB_SUCCESS) {
461 return ldb_module_done(ac->req, NULL, NULL, ret);
465 /* free the partition control container here, for the
466 * common path. Other cases will have it cleaned up
467 * eventually with the ares */
468 talloc_free(partition_ctrl);
469 return ldb_module_done(ac->req, controls,
470 ares->response, LDB_SUCCESS);
476 * update a @REPLCHANGED record in each partition if there have been
477 * any writes of replicated data in the partition
479 static int replmd_notify_store(struct ldb_module *module, struct ldb_request *parent)
481 struct replmd_private *replmd_private =
482 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
484 while (replmd_private->ncs) {
486 struct nc_entry *modified_partition = replmd_private->ncs;
488 ret = dsdb_module_save_partition_usn(module, modified_partition->dn,
489 modified_partition->mod_usn,
490 modified_partition->mod_usn_urgent, parent);
491 if (ret != LDB_SUCCESS) {
492 DEBUG(0,(__location__ ": Failed to save partition uSN for %s\n",
493 ldb_dn_get_linearized(modified_partition->dn)));
496 DLIST_REMOVE(replmd_private->ncs, modified_partition);
497 talloc_free(modified_partition);
505 created a replmd_replicated_request context
507 static struct replmd_replicated_request *replmd_ctx_init(struct ldb_module *module,
508 struct ldb_request *req)
510 struct ldb_context *ldb;
511 struct replmd_replicated_request *ac;
513 ldb = ldb_module_get_ctx(module);
515 ac = talloc_zero(req, struct replmd_replicated_request);
524 ac->schema = dsdb_get_schema(ldb, ac);
526 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
527 "replmd_modify: no dsdb_schema loaded");
528 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
536 add a time element to a record
538 static int add_time_element(struct ldb_message *msg, const char *attr, time_t t)
540 struct ldb_message_element *el;
544 if (ldb_msg_find_element(msg, attr) != NULL) {
548 s = ldb_timestring(msg, t);
550 return LDB_ERR_OPERATIONS_ERROR;
553 ret = ldb_msg_add_string(msg, attr, s);
554 if (ret != LDB_SUCCESS) {
558 el = ldb_msg_find_element(msg, attr);
559 /* always set as replace. This works because on add ops, the flag
561 el->flags = LDB_FLAG_MOD_REPLACE;
567 add a uint64_t element to a record
569 static int add_uint64_element(struct ldb_context *ldb, struct ldb_message *msg,
570 const char *attr, uint64_t v)
572 struct ldb_message_element *el;
575 if (ldb_msg_find_element(msg, attr) != NULL) {
579 ret = samdb_msg_add_uint64(ldb, msg, msg, attr, v);
580 if (ret != LDB_SUCCESS) {
584 el = ldb_msg_find_element(msg, attr);
585 /* always set as replace. This works because on add ops, the flag
587 el->flags = LDB_FLAG_MOD_REPLACE;
592 static int replmd_replPropertyMetaData1_attid_sort(const struct replPropertyMetaData1 *m1,
593 const struct replPropertyMetaData1 *m2,
594 const uint32_t *rdn_attid)
596 if (m1->attid == m2->attid) {
601 * the rdn attribute should be at the end!
602 * so we need to return a value greater than zero
603 * which means m1 is greater than m2
605 if (m1->attid == *rdn_attid) {
610 * the rdn attribute should be at the end!
611 * so we need to return a value less than zero
612 * which means m2 is greater than m1
614 if (m2->attid == *rdn_attid) {
618 return m1->attid > m2->attid ? 1 : -1;
621 static int replmd_replPropertyMetaDataCtr1_sort(struct replPropertyMetaDataCtr1 *ctr1,
622 const struct dsdb_schema *schema,
625 const char *rdn_name;
626 const struct dsdb_attribute *rdn_sa;
628 rdn_name = ldb_dn_get_rdn_name(dn);
630 DEBUG(0,(__location__ ": No rDN for %s?\n", ldb_dn_get_linearized(dn)));
631 return LDB_ERR_OPERATIONS_ERROR;
634 rdn_sa = dsdb_attribute_by_lDAPDisplayName(schema, rdn_name);
635 if (rdn_sa == NULL) {
636 DEBUG(0,(__location__ ": No sa found for rDN %s for %s\n", rdn_name, ldb_dn_get_linearized(dn)));
637 return LDB_ERR_OPERATIONS_ERROR;
640 DEBUG(6,("Sorting rpmd with attid exception %u rDN=%s DN=%s\n",
641 rdn_sa->attributeID_id, rdn_name, ldb_dn_get_linearized(dn)));
643 LDB_TYPESAFE_QSORT(ctr1->array, ctr1->count, &rdn_sa->attributeID_id, replmd_replPropertyMetaData1_attid_sort);
648 static int replmd_ldb_message_element_attid_sort(const struct ldb_message_element *e1,
649 const struct ldb_message_element *e2,
650 const struct dsdb_schema *schema)
652 const struct dsdb_attribute *a1;
653 const struct dsdb_attribute *a2;
656 * TODO: make this faster by caching the dsdb_attribute pointer
657 * on the ldb_messag_element
660 a1 = dsdb_attribute_by_lDAPDisplayName(schema, e1->name);
661 a2 = dsdb_attribute_by_lDAPDisplayName(schema, e2->name);
664 * TODO: remove this check, we should rely on e1 and e2 having valid attribute names
668 return strcasecmp(e1->name, e2->name);
670 if (a1->attributeID_id == a2->attributeID_id) {
673 return a1->attributeID_id > a2->attributeID_id ? 1 : -1;
676 static void replmd_ldb_message_sort(struct ldb_message *msg,
677 const struct dsdb_schema *schema)
679 LDB_TYPESAFE_QSORT(msg->elements, msg->num_elements, schema, replmd_ldb_message_element_attid_sort);
682 static int replmd_build_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
683 const struct GUID *invocation_id, uint64_t seq_num,
684 uint64_t local_usn, NTTIME nttime, uint32_t version, bool deleted);
688 fix up linked attributes in replmd_add.
689 This involves setting up the right meta-data in extended DN
690 components, and creating backlinks to the object
692 static int replmd_add_fix_la(struct ldb_module *module, struct ldb_message_element *el,
693 uint64_t seq_num, const struct GUID *invocationId, time_t t,
694 struct GUID *guid, const struct dsdb_attribute *sa, struct ldb_request *parent)
697 TALLOC_CTX *tmp_ctx = talloc_new(el->values);
698 struct ldb_context *ldb = ldb_module_get_ctx(module);
700 /* We will take a reference to the schema in replmd_add_backlink */
701 const struct dsdb_schema *schema = dsdb_get_schema(ldb, NULL);
704 unix_to_nt_time(&now, t);
706 for (i=0; i<el->num_values; i++) {
707 struct ldb_val *v = &el->values[i];
708 struct dsdb_dn *dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, v, sa->syntax->ldap_oid);
709 struct GUID target_guid;
713 /* note that the DN already has the extended
714 components from the extended_dn_store module */
715 status = dsdb_get_extended_dn_guid(dsdb_dn->dn, &target_guid, "GUID");
716 if (!NT_STATUS_IS_OK(status) || GUID_all_zero(&target_guid)) {
717 ret = dsdb_module_guid_by_dn(module, dsdb_dn->dn, &target_guid, parent);
718 if (ret != LDB_SUCCESS) {
719 talloc_free(tmp_ctx);
722 ret = dsdb_set_extended_dn_guid(dsdb_dn->dn, &target_guid, "GUID");
723 if (ret != LDB_SUCCESS) {
724 talloc_free(tmp_ctx);
729 ret = replmd_build_la_val(el->values, v, dsdb_dn, invocationId,
730 seq_num, seq_num, now, 0, false);
731 if (ret != LDB_SUCCESS) {
732 talloc_free(tmp_ctx);
736 ret = replmd_add_backlink(module, schema, guid, &target_guid, true, sa, false);
737 if (ret != LDB_SUCCESS) {
738 talloc_free(tmp_ctx);
743 talloc_free(tmp_ctx);
749 intercept add requests
751 static int replmd_add(struct ldb_module *module, struct ldb_request *req)
753 struct ldb_context *ldb;
754 struct ldb_control *control;
755 struct replmd_replicated_request *ac;
756 enum ndr_err_code ndr_err;
757 struct ldb_request *down_req;
758 struct ldb_message *msg;
759 const DATA_BLOB *guid_blob;
761 struct replPropertyMetaDataBlob nmd;
762 struct ldb_val nmd_value;
763 const struct GUID *our_invocation_id;
764 time_t t = time(NULL);
769 unsigned int functional_level;
771 bool allow_add_guid = false;
772 bool remove_current_guid = false;
773 bool is_urgent = false;
774 struct ldb_message_element *objectclass_el;
776 /* check if there's a show relax control (used by provision to say 'I know what I'm doing') */
777 control = ldb_request_get_control(req, LDB_CONTROL_RELAX_OID);
779 allow_add_guid = true;
782 /* do not manipulate our control entries */
783 if (ldb_dn_is_special(req->op.add.message->dn)) {
784 return ldb_next_request(module, req);
787 ldb = ldb_module_get_ctx(module);
789 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_add\n");
791 guid_blob = ldb_msg_find_ldb_val(req->op.add.message, "objectGUID");
792 if (guid_blob != NULL) {
793 if (!allow_add_guid) {
794 ldb_set_errstring(ldb,
795 "replmd_add: it's not allowed to add an object with objectGUID!");
796 return LDB_ERR_UNWILLING_TO_PERFORM;
798 NTSTATUS status = GUID_from_data_blob(guid_blob,&guid);
799 if (!NT_STATUS_IS_OK(status)) {
800 ldb_set_errstring(ldb,
801 "replmd_add: Unable to parse the 'objectGUID' as a GUID!");
802 return LDB_ERR_UNWILLING_TO_PERFORM;
804 /* we remove this attribute as it can be a string and
805 * will not be treated correctly and then we will re-add
806 * it later on in the good format */
807 remove_current_guid = true;
811 guid = GUID_random();
814 ac = replmd_ctx_init(module, req);
816 return ldb_module_oom(module);
819 functional_level = dsdb_functional_level(ldb);
821 /* Get a sequence number from the backend */
822 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ac->seq_num);
823 if (ret != LDB_SUCCESS) {
828 /* get our invocationId */
829 our_invocation_id = samdb_ntds_invocation_id(ldb);
830 if (!our_invocation_id) {
831 ldb_debug_set(ldb, LDB_DEBUG_ERROR,
832 "replmd_add: unable to find invocationId\n");
834 return LDB_ERR_OPERATIONS_ERROR;
837 /* we have to copy the message as the caller might have it as a const */
838 msg = ldb_msg_copy_shallow(ac, req->op.add.message);
842 return LDB_ERR_OPERATIONS_ERROR;
845 /* generated times */
846 unix_to_nt_time(&now, t);
847 time_str = ldb_timestring(msg, t);
851 return LDB_ERR_OPERATIONS_ERROR;
853 if (remove_current_guid) {
854 ldb_msg_remove_attr(msg,"objectGUID");
858 * remove autogenerated attributes
860 ldb_msg_remove_attr(msg, "whenCreated");
861 ldb_msg_remove_attr(msg, "whenChanged");
862 ldb_msg_remove_attr(msg, "uSNCreated");
863 ldb_msg_remove_attr(msg, "uSNChanged");
864 ldb_msg_remove_attr(msg, "replPropertyMetaData");
867 * readd replicated attributes
869 ret = ldb_msg_add_string(msg, "whenCreated", time_str);
870 if (ret != LDB_SUCCESS) {
876 /* build the replication meta_data */
879 nmd.ctr.ctr1.count = msg->num_elements;
880 nmd.ctr.ctr1.array = talloc_array(msg,
881 struct replPropertyMetaData1,
883 if (!nmd.ctr.ctr1.array) {
886 return LDB_ERR_OPERATIONS_ERROR;
889 for (i=0; i < msg->num_elements; i++) {
890 struct ldb_message_element *e = &msg->elements[i];
891 struct replPropertyMetaData1 *m = &nmd.ctr.ctr1.array[ni];
892 const struct dsdb_attribute *sa;
894 if (e->name[0] == '@') continue;
896 sa = dsdb_attribute_by_lDAPDisplayName(ac->schema, e->name);
898 ldb_debug_set(ldb, LDB_DEBUG_ERROR,
899 "replmd_add: attribute '%s' not defined in schema\n",
902 return LDB_ERR_NO_SUCH_ATTRIBUTE;
905 if ((sa->systemFlags & DS_FLAG_ATTR_NOT_REPLICATED) || (sa->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED)) {
906 /* if the attribute is not replicated (0x00000001)
907 * or constructed (0x00000004) it has no metadata
912 if (sa->linkID != 0 && functional_level > DS_DOMAIN_FUNCTION_2000) {
913 ret = replmd_add_fix_la(module, e, ac->seq_num, our_invocation_id, t, &guid, sa, req);
914 if (ret != LDB_SUCCESS) {
918 /* linked attributes are not stored in
919 replPropertyMetaData in FL above w2k */
923 m->attid = sa->attributeID_id;
925 if (m->attid == 0x20030) {
926 const struct ldb_val *rdn_val = ldb_dn_get_rdn_val(msg->dn);
929 if (rdn_val == NULL) {
932 return LDB_ERR_OPERATIONS_ERROR;
935 rdn = (const char*)rdn_val->data;
936 if (strcmp(rdn, "Deleted Objects") == 0) {
938 * Set the originating_change_time to 29/12/9999 at 23:59:59
939 * as specified in MS-ADTS 7.1.1.4.2 Deleted Objects Container
941 NTTIME deleted_obj_ts;
943 unix_to_nt_time(&deleted_obj_ts, DELETED_OBJECT_CONTAINER_CHANGE_TIME);
944 m->originating_change_time = deleted_obj_ts;
946 m->originating_change_time = now;
949 m->originating_change_time = now;
951 m->originating_invocation_id = *our_invocation_id;
952 m->originating_usn = ac->seq_num;
953 m->local_usn = ac->seq_num;
957 /* fix meta data count */
958 nmd.ctr.ctr1.count = ni;
961 * sort meta data array, and move the rdn attribute entry to the end
963 ret = replmd_replPropertyMetaDataCtr1_sort(&nmd.ctr.ctr1, ac->schema, msg->dn);
964 if (ret != LDB_SUCCESS) {
969 /* generated NDR encoded values */
970 ndr_err = ndr_push_struct_blob(&nmd_value, msg,
972 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
973 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
976 return LDB_ERR_OPERATIONS_ERROR;
980 * add the autogenerated values
982 ret = dsdb_msg_add_guid(msg, &guid, "objectGUID");
983 if (ret != LDB_SUCCESS) {
988 ret = ldb_msg_add_string(msg, "whenChanged", time_str);
989 if (ret != LDB_SUCCESS) {
994 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", ac->seq_num);
995 if (ret != LDB_SUCCESS) {
1000 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ac->seq_num);
1001 if (ret != LDB_SUCCESS) {
1006 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
1007 if (ret != LDB_SUCCESS) {
1014 * sort the attributes by attid before storing the object
1016 replmd_ldb_message_sort(msg, ac->schema);
1018 objectclass_el = ldb_msg_find_element(msg, "objectClass");
1019 is_urgent = replmd_check_urgent_objectclass(objectclass_el,
1020 REPL_URGENT_ON_CREATE);
1022 ac->is_urgent = is_urgent;
1023 ret = ldb_build_add_req(&down_req, ldb, ac,
1026 ac, replmd_op_callback,
1029 LDB_REQ_SET_LOCATION(down_req);
1030 if (ret != LDB_SUCCESS) {
1035 /* current partition control is needed by "replmd_op_callback" */
1036 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
1037 ret = ldb_request_add_control(down_req,
1038 DSDB_CONTROL_CURRENT_PARTITION_OID,
1040 if (ret != LDB_SUCCESS) {
1046 if (functional_level == DS_DOMAIN_FUNCTION_2000) {
1047 ret = ldb_request_add_control(down_req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
1048 if (ret != LDB_SUCCESS) {
1054 /* mark the control done */
1056 control->critical = 0;
1059 /* go on with the call chain */
1060 return ldb_next_request(module, down_req);
1065 * update the replPropertyMetaData for one element
1067 static int replmd_update_rpmd_element(struct ldb_context *ldb,
1068 struct ldb_message *msg,
1069 struct ldb_message_element *el,
1070 struct ldb_message_element *old_el,
1071 struct replPropertyMetaDataBlob *omd,
1072 const struct dsdb_schema *schema,
1074 const struct GUID *our_invocation_id,
1076 struct ldb_request *req)
1079 const struct dsdb_attribute *a;
1080 struct replPropertyMetaData1 *md1;
1082 a = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
1084 if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)) {
1085 /* allow this to make it possible for dbcheck
1086 to remove bad attributes */
1090 DEBUG(0,(__location__ ": Unable to find attribute %s in schema\n",
1092 return LDB_ERR_OPERATIONS_ERROR;
1095 if ((a->systemFlags & DS_FLAG_ATTR_NOT_REPLICATED) || (a->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED)) {
1099 /* if the attribute's value haven't changed then return LDB_SUCCESS
1100 * Unless we have the provision control or if the attribute is
1101 * interSiteTopologyGenerator as this page explain: http://support.microsoft.com/kb/224815
1102 * this attribute is periodicaly written by the DC responsible for the intersite generation
1105 if (old_el != NULL && ldb_msg_element_compare(el, old_el) == 0) {
1106 if (strcmp(el->name, "interSiteTopologyGenerator") != 0 &&
1107 !ldb_request_get_control(req, LDB_CONTROL_PROVISION_OID)) {
1109 * allow this to make it possible for dbcheck
1110 * to rebuild broken metadata
1116 for (i=0; i<omd->ctr.ctr1.count; i++) {
1117 if (a->attributeID_id == omd->ctr.ctr1.array[i].attid) break;
1120 if (a->linkID != 0 && dsdb_functional_level(ldb) > DS_DOMAIN_FUNCTION_2000) {
1121 /* linked attributes are not stored in
1122 replPropertyMetaData in FL above w2k, but we do
1123 raise the seqnum for the object */
1124 if (*seq_num == 0 &&
1125 ldb_sequence_number(ldb, LDB_SEQ_NEXT, seq_num) != LDB_SUCCESS) {
1126 return LDB_ERR_OPERATIONS_ERROR;
1131 if (i == omd->ctr.ctr1.count) {
1132 /* we need to add a new one */
1133 omd->ctr.ctr1.array = talloc_realloc(msg, omd->ctr.ctr1.array,
1134 struct replPropertyMetaData1, omd->ctr.ctr1.count+1);
1135 if (omd->ctr.ctr1.array == NULL) {
1137 return LDB_ERR_OPERATIONS_ERROR;
1139 omd->ctr.ctr1.count++;
1140 ZERO_STRUCT(omd->ctr.ctr1.array[i]);
1143 /* Get a new sequence number from the backend. We only do this
1144 * if we have a change that requires a new
1145 * replPropertyMetaData element
1147 if (*seq_num == 0) {
1148 int ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, seq_num);
1149 if (ret != LDB_SUCCESS) {
1150 return LDB_ERR_OPERATIONS_ERROR;
1154 md1 = &omd->ctr.ctr1.array[i];
1156 md1->attid = a->attributeID_id;
1157 if (md1->attid == 0x20030) {
1158 const struct ldb_val *rdn_val = ldb_dn_get_rdn_val(msg->dn);
1161 if (rdn_val == NULL) {
1163 return LDB_ERR_OPERATIONS_ERROR;
1166 rdn = (const char*)rdn_val->data;
1167 if (strcmp(rdn, "Deleted Objects") == 0) {
1169 * Set the originating_change_time to 29/12/9999 at 23:59:59
1170 * as specified in MS-ADTS 7.1.1.4.2 Deleted Objects Container
1172 NTTIME deleted_obj_ts;
1174 unix_to_nt_time(&deleted_obj_ts, DELETED_OBJECT_CONTAINER_CHANGE_TIME);
1175 md1->originating_change_time = deleted_obj_ts;
1177 md1->originating_change_time = now;
1180 md1->originating_change_time = now;
1182 md1->originating_invocation_id = *our_invocation_id;
1183 md1->originating_usn = *seq_num;
1184 md1->local_usn = *seq_num;
1189 static uint64_t find_max_local_usn(struct replPropertyMetaDataBlob omd)
1191 uint32_t count = omd.ctr.ctr1.count;
1194 for (i=0; i < count; i++) {
1195 struct replPropertyMetaData1 m = omd.ctr.ctr1.array[i];
1196 if (max < m.local_usn) {
1204 * update the replPropertyMetaData object each time we modify an
1205 * object. This is needed for DRS replication, as the merge on the
1206 * client is based on this object
1208 static int replmd_update_rpmd(struct ldb_module *module,
1209 const struct dsdb_schema *schema,
1210 struct ldb_request *req,
1211 const char * const *rename_attrs,
1212 struct ldb_message *msg, uint64_t *seq_num,
1214 bool *is_urgent, bool *rodc)
1216 const struct ldb_val *omd_value;
1217 enum ndr_err_code ndr_err;
1218 struct replPropertyMetaDataBlob omd;
1221 const struct GUID *our_invocation_id;
1223 const char * const *attrs = NULL;
1224 const char * const attrs1[] = { "replPropertyMetaData", "*", NULL };
1225 const char * const attrs2[] = { "uSNChanged", "objectClass", "instanceType", NULL };
1226 struct ldb_result *res;
1227 struct ldb_context *ldb;
1228 struct ldb_message_element *objectclass_el;
1229 enum urgent_situation situation;
1230 bool rmd_is_provided;
1233 attrs = rename_attrs;
1238 ldb = ldb_module_get_ctx(module);
1240 our_invocation_id = samdb_ntds_invocation_id(ldb);
1241 if (!our_invocation_id) {
1242 /* this happens during an initial vampire while
1243 updating the schema */
1244 DEBUG(5,("No invocationID - skipping replPropertyMetaData update\n"));
1248 unix_to_nt_time(&now, t);
1250 if (ldb_request_get_control(req, DSDB_CONTROL_CHANGEREPLMETADATA_OID)) {
1251 rmd_is_provided = true;
1253 rmd_is_provided = false;
1256 /* if isDeleted is present and is TRUE, then we consider we are deleting,
1257 * otherwise we consider we are updating */
1258 if (ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")) {
1259 situation = REPL_URGENT_ON_DELETE;
1260 } else if (rename_attrs) {
1261 situation = REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE;
1263 situation = REPL_URGENT_ON_UPDATE;
1266 if (rmd_is_provided) {
1267 /* In this case the change_replmetadata control was supplied */
1268 /* We check that it's the only attribute that is provided
1269 * (it's a rare case so it's better to keep the code simplier)
1270 * We also check that the highest local_usn is bigger than
1273 if( msg->num_elements != 1 ||
1274 strncmp(msg->elements[0].name,
1275 "replPropertyMetaData", 20) ) {
1276 DEBUG(0,(__location__ ": changereplmetada control called without "\
1277 "a specified replPropertyMetaData attribute or with others\n"));
1278 return LDB_ERR_OPERATIONS_ERROR;
1280 if (situation != REPL_URGENT_ON_UPDATE) {
1281 DEBUG(0,(__location__ ": changereplmetada control can't be called when deleting an object\n"));
1282 return LDB_ERR_OPERATIONS_ERROR;
1284 omd_value = ldb_msg_find_ldb_val(msg, "replPropertyMetaData");
1286 DEBUG(0,(__location__ ": replPropertyMetaData was not specified for Object %s\n",
1287 ldb_dn_get_linearized(msg->dn)));
1288 return LDB_ERR_OPERATIONS_ERROR;
1290 ndr_err = ndr_pull_struct_blob(omd_value, msg, &omd,
1291 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
1292 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1293 DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s\n",
1294 ldb_dn_get_linearized(msg->dn)));
1295 return LDB_ERR_OPERATIONS_ERROR;
1297 *seq_num = find_max_local_usn(omd);
1299 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attrs2,
1300 DSDB_FLAG_NEXT_MODULE |
1301 DSDB_SEARCH_SHOW_RECYCLED |
1302 DSDB_SEARCH_SHOW_EXTENDED_DN |
1303 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
1304 DSDB_SEARCH_REVEAL_INTERNALS, req);
1306 if (ret != LDB_SUCCESS) {
1310 objectclass_el = ldb_msg_find_element(res->msgs[0], "objectClass");
1311 if (is_urgent && replmd_check_urgent_objectclass(objectclass_el,
1316 db_seq = ldb_msg_find_attr_as_uint64(res->msgs[0], "uSNChanged", 0);
1317 if (*seq_num <= db_seq) {
1318 DEBUG(0,(__location__ ": changereplmetada control provided but max(local_usn)"\
1319 " is less or equal to uSNChanged (max = %lld uSNChanged = %lld)\n",
1320 (long long)*seq_num, (long long)db_seq));
1321 return LDB_ERR_OPERATIONS_ERROR;
1325 /* search for the existing replPropertyMetaDataBlob. We need
1326 * to use REVEAL and ask for DNs in storage format to support
1327 * the check for values being the same in
1328 * replmd_update_rpmd_element()
1330 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attrs,
1331 DSDB_FLAG_NEXT_MODULE |
1332 DSDB_SEARCH_SHOW_RECYCLED |
1333 DSDB_SEARCH_SHOW_EXTENDED_DN |
1334 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
1335 DSDB_SEARCH_REVEAL_INTERNALS, req);
1336 if (ret != LDB_SUCCESS) {
1340 objectclass_el = ldb_msg_find_element(res->msgs[0], "objectClass");
1341 if (is_urgent && replmd_check_urgent_objectclass(objectclass_el,
1346 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
1348 DEBUG(0,(__location__ ": Object %s does not have a replPropertyMetaData attribute\n",
1349 ldb_dn_get_linearized(msg->dn)));
1350 return LDB_ERR_OPERATIONS_ERROR;
1353 ndr_err = ndr_pull_struct_blob(omd_value, msg, &omd,
1354 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
1355 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1356 DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s\n",
1357 ldb_dn_get_linearized(msg->dn)));
1358 return LDB_ERR_OPERATIONS_ERROR;
1361 if (omd.version != 1) {
1362 DEBUG(0,(__location__ ": bad version %u in replPropertyMetaData for %s\n",
1363 omd.version, ldb_dn_get_linearized(msg->dn)));
1364 return LDB_ERR_OPERATIONS_ERROR;
1367 for (i=0; i<msg->num_elements; i++) {
1368 struct ldb_message_element *old_el;
1369 old_el = ldb_msg_find_element(res->msgs[0], msg->elements[i].name);
1370 ret = replmd_update_rpmd_element(ldb, msg, &msg->elements[i], old_el, &omd, schema, seq_num,
1371 our_invocation_id, now, req);
1372 if (ret != LDB_SUCCESS) {
1376 if (is_urgent && !*is_urgent && (situation == REPL_URGENT_ON_UPDATE)) {
1377 *is_urgent = replmd_check_urgent_attribute(&msg->elements[i]);
1383 * replmd_update_rpmd_element has done an update if the
1386 if (*seq_num != 0) {
1387 struct ldb_val *md_value;
1388 struct ldb_message_element *el;
1390 /*if we are RODC and this is a DRSR update then its ok*/
1391 if (!ldb_request_get_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID)) {
1392 unsigned instanceType;
1394 ret = samdb_rodc(ldb, rodc);
1395 if (ret != LDB_SUCCESS) {
1396 DEBUG(4, (__location__ ": unable to tell if we are an RODC\n"));
1398 ldb_set_errstring(ldb, "RODC modify is forbidden!");
1399 return LDB_ERR_REFERRAL;
1402 instanceType = ldb_msg_find_attr_as_uint(res->msgs[0], "instanceType", INSTANCE_TYPE_WRITE);
1403 if (!(instanceType & INSTANCE_TYPE_WRITE)) {
1404 return ldb_error(ldb, LDB_ERR_UNWILLING_TO_PERFORM,
1405 "cannot change replicated attribute on partial replica");
1409 md_value = talloc(msg, struct ldb_val);
1410 if (md_value == NULL) {
1412 return LDB_ERR_OPERATIONS_ERROR;
1415 ret = replmd_replPropertyMetaDataCtr1_sort(&omd.ctr.ctr1, schema, msg->dn);
1416 if (ret != LDB_SUCCESS) {
1420 ndr_err = ndr_push_struct_blob(md_value, msg, &omd,
1421 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
1422 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1423 DEBUG(0,(__location__ ": Failed to marshall replPropertyMetaData for %s\n",
1424 ldb_dn_get_linearized(msg->dn)));
1425 return LDB_ERR_OPERATIONS_ERROR;
1428 ret = ldb_msg_add_empty(msg, "replPropertyMetaData", LDB_FLAG_MOD_REPLACE, &el);
1429 if (ret != LDB_SUCCESS) {
1430 DEBUG(0,(__location__ ": Failed to add updated replPropertyMetaData %s\n",
1431 ldb_dn_get_linearized(msg->dn)));
1436 el->values = md_value;
1443 struct dsdb_dn *dsdb_dn;
1448 static int parsed_dn_compare(struct parsed_dn *pdn1, struct parsed_dn *pdn2)
1450 return GUID_compare(pdn1->guid, pdn2->guid);
1453 static struct parsed_dn *parsed_dn_find(struct parsed_dn *pdn,
1454 unsigned int count, struct GUID *guid,
1457 struct parsed_dn *ret;
1459 if (dn && GUID_all_zero(guid)) {
1460 /* when updating a link using DRS, we sometimes get a
1461 NULL GUID. We then need to try and match by DN */
1462 for (i=0; i<count; i++) {
1463 if (ldb_dn_compare(pdn[i].dsdb_dn->dn, dn) == 0) {
1464 dsdb_get_extended_dn_guid(pdn[i].dsdb_dn->dn, guid, "GUID");
1470 BINARY_ARRAY_SEARCH(pdn, count, guid, guid, GUID_compare, ret);
1475 get a series of message element values as an array of DNs and GUIDs
1476 the result is sorted by GUID
1478 static int get_parsed_dns(struct ldb_module *module, TALLOC_CTX *mem_ctx,
1479 struct ldb_message_element *el, struct parsed_dn **pdn,
1480 const char *ldap_oid, struct ldb_request *parent)
1483 struct ldb_context *ldb = ldb_module_get_ctx(module);
1490 (*pdn) = talloc_array(mem_ctx, struct parsed_dn, el->num_values);
1492 ldb_module_oom(module);
1493 return LDB_ERR_OPERATIONS_ERROR;
1496 for (i=0; i<el->num_values; i++) {
1497 struct ldb_val *v = &el->values[i];
1500 struct parsed_dn *p;
1504 p->dsdb_dn = dsdb_dn_parse(*pdn, ldb, v, ldap_oid);
1505 if (p->dsdb_dn == NULL) {
1506 return LDB_ERR_INVALID_DN_SYNTAX;
1509 dn = p->dsdb_dn->dn;
1511 p->guid = talloc(*pdn, struct GUID);
1512 if (p->guid == NULL) {
1513 ldb_module_oom(module);
1514 return LDB_ERR_OPERATIONS_ERROR;
1517 status = dsdb_get_extended_dn_guid(dn, p->guid, "GUID");
1518 if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
1519 /* we got a DN without a GUID - go find the GUID */
1520 int ret = dsdb_module_guid_by_dn(module, dn, p->guid, parent);
1521 if (ret != LDB_SUCCESS) {
1522 ldb_asprintf_errstring(ldb, "Unable to find GUID for DN %s\n",
1523 ldb_dn_get_linearized(dn));
1524 if (ret == LDB_ERR_NO_SUCH_OBJECT &&
1525 LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_DELETE &&
1526 ldb_attr_cmp(el->name, "member") == 0) {
1527 return LDB_ERR_UNWILLING_TO_PERFORM;
1531 ret = dsdb_set_extended_dn_guid(dn, p->guid, "GUID");
1532 if (ret != LDB_SUCCESS) {
1535 } else if (!NT_STATUS_IS_OK(status)) {
1536 return LDB_ERR_OPERATIONS_ERROR;
1539 /* keep a pointer to the original ldb_val */
1543 TYPESAFE_QSORT(*pdn, el->num_values, parsed_dn_compare);
1549 build a new extended DN, including all meta data fields
1551 RMD_FLAGS = DSDB_RMD_FLAG_* bits
1552 RMD_ADDTIME = originating_add_time
1553 RMD_INVOCID = originating_invocation_id
1554 RMD_CHANGETIME = originating_change_time
1555 RMD_ORIGINATING_USN = originating_usn
1556 RMD_LOCAL_USN = local_usn
1557 RMD_VERSION = version
1559 static int replmd_build_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
1560 const struct GUID *invocation_id, uint64_t seq_num,
1561 uint64_t local_usn, NTTIME nttime, uint32_t version, bool deleted)
1563 struct ldb_dn *dn = dsdb_dn->dn;
1564 const char *tstring, *usn_string, *flags_string;
1565 struct ldb_val tval;
1567 struct ldb_val usnv, local_usnv;
1568 struct ldb_val vers, flagsv;
1571 const char *dnstring;
1573 uint32_t rmd_flags = deleted?DSDB_RMD_FLAG_DELETED:0;
1575 tstring = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)nttime);
1577 return LDB_ERR_OPERATIONS_ERROR;
1579 tval = data_blob_string_const(tstring);
1581 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)seq_num);
1583 return LDB_ERR_OPERATIONS_ERROR;
1585 usnv = data_blob_string_const(usn_string);
1587 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)local_usn);
1589 return LDB_ERR_OPERATIONS_ERROR;
1591 local_usnv = data_blob_string_const(usn_string);
1593 vstring = talloc_asprintf(mem_ctx, "%lu", (unsigned long)version);
1595 return LDB_ERR_OPERATIONS_ERROR;
1597 vers = data_blob_string_const(vstring);
1599 status = GUID_to_ndr_blob(invocation_id, dn, &iid);
1600 if (!NT_STATUS_IS_OK(status)) {
1601 return LDB_ERR_OPERATIONS_ERROR;
1604 flags_string = talloc_asprintf(mem_ctx, "%u", rmd_flags);
1605 if (!flags_string) {
1606 return LDB_ERR_OPERATIONS_ERROR;
1608 flagsv = data_blob_string_const(flags_string);
1610 ret = ldb_dn_set_extended_component(dn, "RMD_FLAGS", &flagsv);
1611 if (ret != LDB_SUCCESS) return ret;
1612 ret = ldb_dn_set_extended_component(dn, "RMD_ADDTIME", &tval);
1613 if (ret != LDB_SUCCESS) return ret;
1614 ret = ldb_dn_set_extended_component(dn, "RMD_INVOCID", &iid);
1615 if (ret != LDB_SUCCESS) return ret;
1616 ret = ldb_dn_set_extended_component(dn, "RMD_CHANGETIME", &tval);
1617 if (ret != LDB_SUCCESS) return ret;
1618 ret = ldb_dn_set_extended_component(dn, "RMD_LOCAL_USN", &local_usnv);
1619 if (ret != LDB_SUCCESS) return ret;
1620 ret = ldb_dn_set_extended_component(dn, "RMD_ORIGINATING_USN", &usnv);
1621 if (ret != LDB_SUCCESS) return ret;
1622 ret = ldb_dn_set_extended_component(dn, "RMD_VERSION", &vers);
1623 if (ret != LDB_SUCCESS) return ret;
1625 dnstring = dsdb_dn_get_extended_linearized(mem_ctx, dsdb_dn, 1);
1626 if (dnstring == NULL) {
1627 return LDB_ERR_OPERATIONS_ERROR;
1629 *v = data_blob_string_const(dnstring);
1634 static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
1635 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
1636 uint64_t seq_num, uint64_t local_usn, NTTIME nttime,
1637 uint32_t version, bool deleted);
1640 check if any links need upgrading from w2k format
1642 The parent_ctx is the ldb_message_element which contains the values array that dns[i].v points at, and which should be used for allocating any new value.
1644 static int replmd_check_upgrade_links(struct parsed_dn *dns, uint32_t count, struct ldb_message_element *parent_ctx, const struct GUID *invocation_id)
1647 for (i=0; i<count; i++) {
1652 status = dsdb_get_extended_dn_uint32(dns[i].dsdb_dn->dn, &version, "RMD_VERSION");
1653 if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
1657 /* it's an old one that needs upgrading */
1658 ret = replmd_update_la_val(parent_ctx->values, dns[i].v, dns[i].dsdb_dn, dns[i].dsdb_dn, invocation_id,
1660 if (ret != LDB_SUCCESS) {
1668 update an extended DN, including all meta data fields
1670 see replmd_build_la_val for value names
1672 static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
1673 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
1674 uint64_t seq_num, uint64_t local_usn, NTTIME nttime,
1675 uint32_t version, bool deleted)
1677 struct ldb_dn *dn = dsdb_dn->dn;
1678 const char *tstring, *usn_string, *flags_string;
1679 struct ldb_val tval;
1681 struct ldb_val usnv, local_usnv;
1682 struct ldb_val vers, flagsv;
1683 const struct ldb_val *old_addtime;
1684 uint32_t old_version;
1687 const char *dnstring;
1689 uint32_t rmd_flags = deleted?DSDB_RMD_FLAG_DELETED:0;
1691 tstring = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)nttime);
1693 return LDB_ERR_OPERATIONS_ERROR;
1695 tval = data_blob_string_const(tstring);
1697 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)seq_num);
1699 return LDB_ERR_OPERATIONS_ERROR;
1701 usnv = data_blob_string_const(usn_string);
1703 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)local_usn);
1705 return LDB_ERR_OPERATIONS_ERROR;
1707 local_usnv = data_blob_string_const(usn_string);
1709 status = GUID_to_ndr_blob(invocation_id, dn, &iid);
1710 if (!NT_STATUS_IS_OK(status)) {
1711 return LDB_ERR_OPERATIONS_ERROR;
1714 flags_string = talloc_asprintf(mem_ctx, "%u", rmd_flags);
1715 if (!flags_string) {
1716 return LDB_ERR_OPERATIONS_ERROR;
1718 flagsv = data_blob_string_const(flags_string);
1720 ret = ldb_dn_set_extended_component(dn, "RMD_FLAGS", &flagsv);
1721 if (ret != LDB_SUCCESS) return ret;
1723 /* get the ADDTIME from the original */
1724 old_addtime = ldb_dn_get_extended_component(old_dsdb_dn->dn, "RMD_ADDTIME");
1725 if (old_addtime == NULL) {
1726 old_addtime = &tval;
1728 if (dsdb_dn != old_dsdb_dn ||
1729 ldb_dn_get_extended_component(dn, "RMD_ADDTIME") == NULL) {
1730 ret = ldb_dn_set_extended_component(dn, "RMD_ADDTIME", old_addtime);
1731 if (ret != LDB_SUCCESS) return ret;
1734 /* use our invocation id */
1735 ret = ldb_dn_set_extended_component(dn, "RMD_INVOCID", &iid);
1736 if (ret != LDB_SUCCESS) return ret;
1738 /* changetime is the current time */
1739 ret = ldb_dn_set_extended_component(dn, "RMD_CHANGETIME", &tval);
1740 if (ret != LDB_SUCCESS) return ret;
1742 /* update the USN */
1743 ret = ldb_dn_set_extended_component(dn, "RMD_ORIGINATING_USN", &usnv);
1744 if (ret != LDB_SUCCESS) return ret;
1746 ret = ldb_dn_set_extended_component(dn, "RMD_LOCAL_USN", &local_usnv);
1747 if (ret != LDB_SUCCESS) return ret;
1749 /* increase the version by 1 */
1750 status = dsdb_get_extended_dn_uint32(old_dsdb_dn->dn, &old_version, "RMD_VERSION");
1751 if (NT_STATUS_IS_OK(status) && old_version >= version) {
1752 version = old_version+1;
1754 vstring = talloc_asprintf(dn, "%lu", (unsigned long)version);
1755 vers = data_blob_string_const(vstring);
1756 ret = ldb_dn_set_extended_component(dn, "RMD_VERSION", &vers);
1757 if (ret != LDB_SUCCESS) return ret;
1759 dnstring = dsdb_dn_get_extended_linearized(mem_ctx, dsdb_dn, 1);
1760 if (dnstring == NULL) {
1761 return LDB_ERR_OPERATIONS_ERROR;
1763 *v = data_blob_string_const(dnstring);
1769 handle adding a linked attribute
1771 static int replmd_modify_la_add(struct ldb_module *module,
1772 const struct dsdb_schema *schema,
1773 struct ldb_message *msg,
1774 struct ldb_message_element *el,
1775 struct ldb_message_element *old_el,
1776 const struct dsdb_attribute *schema_attr,
1779 struct GUID *msg_guid,
1780 struct ldb_request *parent)
1783 struct parsed_dn *dns, *old_dns;
1784 TALLOC_CTX *tmp_ctx = talloc_new(msg);
1786 struct ldb_val *new_values = NULL;
1787 unsigned int num_new_values = 0;
1788 unsigned old_num_values = old_el?old_el->num_values:0;
1789 const struct GUID *invocation_id;
1790 struct ldb_context *ldb = ldb_module_get_ctx(module);
1793 unix_to_nt_time(&now, t);
1795 ret = get_parsed_dns(module, tmp_ctx, el, &dns, schema_attr->syntax->ldap_oid, parent);
1796 if (ret != LDB_SUCCESS) {
1797 talloc_free(tmp_ctx);
1801 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns, schema_attr->syntax->ldap_oid, parent);
1802 if (ret != LDB_SUCCESS) {
1803 talloc_free(tmp_ctx);
1807 invocation_id = samdb_ntds_invocation_id(ldb);
1808 if (!invocation_id) {
1809 talloc_free(tmp_ctx);
1810 return LDB_ERR_OPERATIONS_ERROR;
1813 ret = replmd_check_upgrade_links(old_dns, old_num_values, old_el, invocation_id);
1814 if (ret != LDB_SUCCESS) {
1815 talloc_free(tmp_ctx);
1819 /* for each new value, see if it exists already with the same GUID */
1820 for (i=0; i<el->num_values; i++) {
1821 struct parsed_dn *p = parsed_dn_find(old_dns, old_num_values, dns[i].guid, NULL);
1823 /* this is a new linked attribute value */
1824 new_values = talloc_realloc(tmp_ctx, new_values, struct ldb_val, num_new_values+1);
1825 if (new_values == NULL) {
1826 ldb_module_oom(module);
1827 talloc_free(tmp_ctx);
1828 return LDB_ERR_OPERATIONS_ERROR;
1830 ret = replmd_build_la_val(new_values, &new_values[num_new_values], dns[i].dsdb_dn,
1831 invocation_id, seq_num, seq_num, now, 0, false);
1832 if (ret != LDB_SUCCESS) {
1833 talloc_free(tmp_ctx);
1838 /* this is only allowed if the GUID was
1839 previously deleted. */
1840 uint32_t rmd_flags = dsdb_dn_rmd_flags(p->dsdb_dn->dn);
1842 if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
1843 ldb_asprintf_errstring(ldb, "Attribute %s already exists for target GUID %s",
1844 el->name, GUID_string(tmp_ctx, p->guid));
1845 talloc_free(tmp_ctx);
1846 /* error codes for 'member' need to be
1848 if (ldb_attr_cmp(el->name, "member") == 0) {
1849 return LDB_ERR_ENTRY_ALREADY_EXISTS;
1851 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
1854 ret = replmd_update_la_val(old_el->values, p->v, dns[i].dsdb_dn, p->dsdb_dn,
1855 invocation_id, seq_num, seq_num, now, 0, false);
1856 if (ret != LDB_SUCCESS) {
1857 talloc_free(tmp_ctx);
1862 ret = replmd_add_backlink(module, schema, msg_guid, dns[i].guid, true, schema_attr, true);
1863 if (ret != LDB_SUCCESS) {
1864 talloc_free(tmp_ctx);
1869 /* add the new ones on to the end of the old values, constructing a new el->values */
1870 el->values = talloc_realloc(msg->elements, old_el?old_el->values:NULL,
1872 old_num_values+num_new_values);
1873 if (el->values == NULL) {
1874 ldb_module_oom(module);
1875 return LDB_ERR_OPERATIONS_ERROR;
1878 memcpy(&el->values[old_num_values], new_values, num_new_values*sizeof(struct ldb_val));
1879 el->num_values = old_num_values + num_new_values;
1881 talloc_steal(msg->elements, el->values);
1882 talloc_steal(el->values, new_values);
1884 talloc_free(tmp_ctx);
1886 /* we now tell the backend to replace all existing values
1887 with the one we have constructed */
1888 el->flags = LDB_FLAG_MOD_REPLACE;
1895 handle deleting all active linked attributes
1897 static int replmd_modify_la_delete(struct ldb_module *module,
1898 const struct dsdb_schema *schema,
1899 struct ldb_message *msg,
1900 struct ldb_message_element *el,
1901 struct ldb_message_element *old_el,
1902 const struct dsdb_attribute *schema_attr,
1905 struct GUID *msg_guid,
1906 struct ldb_request *parent)
1909 struct parsed_dn *dns, *old_dns;
1910 TALLOC_CTX *tmp_ctx = talloc_new(msg);
1912 const struct GUID *invocation_id;
1913 struct ldb_context *ldb = ldb_module_get_ctx(module);
1916 unix_to_nt_time(&now, t);
1918 /* check if there is nothing to delete */
1919 if ((!old_el || old_el->num_values == 0) &&
1920 el->num_values == 0) {
1924 if (!old_el || old_el->num_values == 0) {
1925 return LDB_ERR_NO_SUCH_ATTRIBUTE;
1928 ret = get_parsed_dns(module, tmp_ctx, el, &dns, schema_attr->syntax->ldap_oid, parent);
1929 if (ret != LDB_SUCCESS) {
1930 talloc_free(tmp_ctx);
1934 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns, schema_attr->syntax->ldap_oid, parent);
1935 if (ret != LDB_SUCCESS) {
1936 talloc_free(tmp_ctx);
1940 invocation_id = samdb_ntds_invocation_id(ldb);
1941 if (!invocation_id) {
1942 return LDB_ERR_OPERATIONS_ERROR;
1945 ret = replmd_check_upgrade_links(old_dns, old_el->num_values, old_el, invocation_id);
1946 if (ret != LDB_SUCCESS) {
1947 talloc_free(tmp_ctx);
1953 /* see if we are being asked to delete any links that
1954 don't exist or are already deleted */
1955 for (i=0; i<el->num_values; i++) {
1956 struct parsed_dn *p = &dns[i];
1957 struct parsed_dn *p2;
1960 p2 = parsed_dn_find(old_dns, old_el->num_values, p->guid, NULL);
1962 ldb_asprintf_errstring(ldb, "Attribute %s doesn't exist for target GUID %s",
1963 el->name, GUID_string(tmp_ctx, p->guid));
1964 if (ldb_attr_cmp(el->name, "member") == 0) {
1965 return LDB_ERR_UNWILLING_TO_PERFORM;
1967 return LDB_ERR_NO_SUCH_ATTRIBUTE;
1970 rmd_flags = dsdb_dn_rmd_flags(p2->dsdb_dn->dn);
1971 if (rmd_flags & DSDB_RMD_FLAG_DELETED) {
1972 ldb_asprintf_errstring(ldb, "Attribute %s already deleted for target GUID %s",
1973 el->name, GUID_string(tmp_ctx, p->guid));
1974 if (ldb_attr_cmp(el->name, "member") == 0) {
1975 return LDB_ERR_UNWILLING_TO_PERFORM;
1977 return LDB_ERR_NO_SUCH_ATTRIBUTE;
1982 /* for each new value, see if it exists already with the same GUID
1983 if it is not already deleted and matches the delete list then delete it
1985 for (i=0; i<old_el->num_values; i++) {
1986 struct parsed_dn *p = &old_dns[i];
1989 if (el->num_values && parsed_dn_find(dns, el->num_values, p->guid, NULL) == NULL) {
1993 rmd_flags = dsdb_dn_rmd_flags(p->dsdb_dn->dn);
1994 if (rmd_flags & DSDB_RMD_FLAG_DELETED) continue;
1996 ret = replmd_update_la_val(old_el->values, p->v, p->dsdb_dn, p->dsdb_dn,
1997 invocation_id, seq_num, seq_num, now, 0, true);
1998 if (ret != LDB_SUCCESS) {
1999 talloc_free(tmp_ctx);
2003 ret = replmd_add_backlink(module, schema, msg_guid, old_dns[i].guid, false, schema_attr, true);
2004 if (ret != LDB_SUCCESS) {
2005 talloc_free(tmp_ctx);
2010 el->values = talloc_steal(msg->elements, old_el->values);
2011 el->num_values = old_el->num_values;
2013 talloc_free(tmp_ctx);
2015 /* we now tell the backend to replace all existing values
2016 with the one we have constructed */
2017 el->flags = LDB_FLAG_MOD_REPLACE;
2023 handle replacing a linked attribute
2025 static int replmd_modify_la_replace(struct ldb_module *module,
2026 const struct dsdb_schema *schema,
2027 struct ldb_message *msg,
2028 struct ldb_message_element *el,
2029 struct ldb_message_element *old_el,
2030 const struct dsdb_attribute *schema_attr,
2033 struct GUID *msg_guid,
2034 struct ldb_request *parent)
2037 struct parsed_dn *dns, *old_dns;
2038 TALLOC_CTX *tmp_ctx = talloc_new(msg);
2040 const struct GUID *invocation_id;
2041 struct ldb_context *ldb = ldb_module_get_ctx(module);
2042 struct ldb_val *new_values = NULL;
2043 unsigned int num_new_values = 0;
2044 unsigned int old_num_values = old_el?old_el->num_values:0;
2047 unix_to_nt_time(&now, t);
2049 /* check if there is nothing to replace */
2050 if ((!old_el || old_el->num_values == 0) &&
2051 el->num_values == 0) {
2055 ret = get_parsed_dns(module, tmp_ctx, el, &dns, schema_attr->syntax->ldap_oid, parent);
2056 if (ret != LDB_SUCCESS) {
2057 talloc_free(tmp_ctx);
2061 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns, schema_attr->syntax->ldap_oid, parent);
2062 if (ret != LDB_SUCCESS) {
2063 talloc_free(tmp_ctx);
2067 invocation_id = samdb_ntds_invocation_id(ldb);
2068 if (!invocation_id) {
2069 return LDB_ERR_OPERATIONS_ERROR;
2072 ret = replmd_check_upgrade_links(old_dns, old_num_values, old_el, invocation_id);
2073 if (ret != LDB_SUCCESS) {
2074 talloc_free(tmp_ctx);
2078 /* mark all the old ones as deleted */
2079 for (i=0; i<old_num_values; i++) {
2080 struct parsed_dn *old_p = &old_dns[i];
2081 struct parsed_dn *p;
2082 uint32_t rmd_flags = dsdb_dn_rmd_flags(old_p->dsdb_dn->dn);
2084 if (rmd_flags & DSDB_RMD_FLAG_DELETED) continue;
2086 ret = replmd_add_backlink(module, schema, msg_guid, old_dns[i].guid, false, schema_attr, false);
2087 if (ret != LDB_SUCCESS) {
2088 talloc_free(tmp_ctx);
2092 p = parsed_dn_find(dns, el->num_values, old_p->guid, NULL);
2094 /* we don't delete it if we are re-adding it */
2098 ret = replmd_update_la_val(old_el->values, old_p->v, old_p->dsdb_dn, old_p->dsdb_dn,
2099 invocation_id, seq_num, seq_num, now, 0, true);
2100 if (ret != LDB_SUCCESS) {
2101 talloc_free(tmp_ctx);
2106 /* for each new value, either update its meta-data, or add it
2109 for (i=0; i<el->num_values; i++) {
2110 struct parsed_dn *p = &dns[i], *old_p;
2113 (old_p = parsed_dn_find(old_dns,
2114 old_num_values, p->guid, NULL)) != NULL) {
2115 /* update in place */
2116 ret = replmd_update_la_val(old_el->values, old_p->v, p->dsdb_dn,
2117 old_p->dsdb_dn, invocation_id,
2118 seq_num, seq_num, now, 0, false);
2119 if (ret != LDB_SUCCESS) {
2120 talloc_free(tmp_ctx);
2125 new_values = talloc_realloc(tmp_ctx, new_values, struct ldb_val,
2127 if (new_values == NULL) {
2128 ldb_module_oom(module);
2129 talloc_free(tmp_ctx);
2130 return LDB_ERR_OPERATIONS_ERROR;
2132 ret = replmd_build_la_val(new_values, &new_values[num_new_values], dns[i].dsdb_dn,
2133 invocation_id, seq_num, seq_num, now, 0, false);
2134 if (ret != LDB_SUCCESS) {
2135 talloc_free(tmp_ctx);
2141 ret = replmd_add_backlink(module, schema, msg_guid, dns[i].guid, true, schema_attr, false);
2142 if (ret != LDB_SUCCESS) {
2143 talloc_free(tmp_ctx);
2148 /* add the new values to the end of old_el */
2149 if (num_new_values != 0) {
2150 el->values = talloc_realloc(msg->elements, old_el?old_el->values:NULL,
2151 struct ldb_val, old_num_values+num_new_values);
2152 if (el->values == NULL) {
2153 ldb_module_oom(module);
2154 return LDB_ERR_OPERATIONS_ERROR;
2156 memcpy(&el->values[old_num_values], &new_values[0],
2157 sizeof(struct ldb_val)*num_new_values);
2158 el->num_values = old_num_values + num_new_values;
2159 talloc_steal(msg->elements, new_values);
2161 el->values = old_el->values;
2162 el->num_values = old_el->num_values;
2163 talloc_steal(msg->elements, el->values);
2166 talloc_free(tmp_ctx);
2168 /* we now tell the backend to replace all existing values
2169 with the one we have constructed */
2170 el->flags = LDB_FLAG_MOD_REPLACE;
2177 handle linked attributes in modify requests
2179 static int replmd_modify_handle_linked_attribs(struct ldb_module *module,
2180 struct ldb_message *msg,
2181 uint64_t seq_num, time_t t,
2182 struct ldb_request *parent)
2184 struct ldb_result *res;
2187 struct ldb_context *ldb = ldb_module_get_ctx(module);
2188 struct ldb_message *old_msg;
2190 const struct dsdb_schema *schema;
2191 struct GUID old_guid;
2194 /* there the replmd_update_rpmd code has already
2195 * checked and saw that there are no linked
2200 if (dsdb_functional_level(ldb) == DS_DOMAIN_FUNCTION_2000) {
2201 /* don't do anything special for linked attributes */
2205 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, NULL,
2206 DSDB_FLAG_NEXT_MODULE |
2207 DSDB_SEARCH_SHOW_RECYCLED |
2208 DSDB_SEARCH_REVEAL_INTERNALS |
2209 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
2211 if (ret != LDB_SUCCESS) {
2214 schema = dsdb_get_schema(ldb, res);
2216 return LDB_ERR_OPERATIONS_ERROR;
2219 old_msg = res->msgs[0];
2221 old_guid = samdb_result_guid(old_msg, "objectGUID");
2223 for (i=0; i<msg->num_elements; i++) {
2224 struct ldb_message_element *el = &msg->elements[i];
2225 struct ldb_message_element *old_el, *new_el;
2226 const struct dsdb_attribute *schema_attr
2227 = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
2229 ldb_asprintf_errstring(ldb,
2230 "%s: attribute %s is not a valid attribute in schema",
2231 __FUNCTION__, el->name);
2232 return LDB_ERR_OBJECT_CLASS_VIOLATION;
2234 if (schema_attr->linkID == 0) {
2237 if ((schema_attr->linkID & 1) == 1) {
2238 if (parent && ldb_request_get_control(parent, DSDB_CONTROL_DBCHECK)) {
2241 /* Odd is for the target. Illegal to modify */
2242 ldb_asprintf_errstring(ldb,
2243 "attribute %s must not be modified directly, it is a linked attribute", el->name);
2244 return LDB_ERR_UNWILLING_TO_PERFORM;
2246 old_el = ldb_msg_find_element(old_msg, el->name);
2247 switch (el->flags & LDB_FLAG_MOD_MASK) {
2248 case LDB_FLAG_MOD_REPLACE:
2249 ret = replmd_modify_la_replace(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid, parent);
2251 case LDB_FLAG_MOD_DELETE:
2252 ret = replmd_modify_la_delete(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid, parent);
2254 case LDB_FLAG_MOD_ADD:
2255 ret = replmd_modify_la_add(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid, parent);
2258 ldb_asprintf_errstring(ldb,
2259 "invalid flags 0x%x for %s linked attribute",
2260 el->flags, el->name);
2261 return LDB_ERR_UNWILLING_TO_PERFORM;
2263 if (dsdb_check_single_valued_link(schema_attr, el) != LDB_SUCCESS) {
2264 ldb_asprintf_errstring(ldb,
2265 "Attribute %s is single valued but more than one value has been supplied",
2267 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
2269 el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
2274 if (ret != LDB_SUCCESS) {
2278 ldb_msg_remove_attr(old_msg, el->name);
2280 ldb_msg_add_empty(old_msg, el->name, 0, &new_el);
2281 new_el->num_values = el->num_values;
2282 new_el->values = talloc_steal(msg->elements, el->values);
2284 /* TODO: this relises a bit too heavily on the exact
2285 behaviour of ldb_msg_find_element and
2286 ldb_msg_remove_element */
2287 old_el = ldb_msg_find_element(msg, el->name);
2289 ldb_msg_remove_element(msg, old_el);
2300 static int replmd_modify(struct ldb_module *module, struct ldb_request *req)
2302 struct ldb_context *ldb;
2303 struct replmd_replicated_request *ac;
2304 struct ldb_request *down_req;
2305 struct ldb_message *msg;
2306 time_t t = time(NULL);
2308 bool is_urgent = false, rodc = false;
2309 unsigned int functional_level;
2310 const DATA_BLOB *guid_blob;
2312 /* do not manipulate our control entries */
2313 if (ldb_dn_is_special(req->op.mod.message->dn)) {
2314 return ldb_next_request(module, req);
2317 ldb = ldb_module_get_ctx(module);
2319 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_modify\n");
2321 guid_blob = ldb_msg_find_ldb_val(req->op.mod.message, "objectGUID");
2322 if ( guid_blob != NULL ) {
2323 ldb_set_errstring(ldb,
2324 "replmd_modify: it's not allowed to change the objectGUID!");
2325 return LDB_ERR_CONSTRAINT_VIOLATION;
2328 ac = replmd_ctx_init(module, req);
2330 return ldb_module_oom(module);
2333 functional_level = dsdb_functional_level(ldb);
2335 /* we have to copy the message as the caller might have it as a const */
2336 msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
2340 return LDB_ERR_OPERATIONS_ERROR;
2343 ldb_msg_remove_attr(msg, "whenChanged");
2344 ldb_msg_remove_attr(msg, "uSNChanged");
2346 ret = replmd_update_rpmd(module, ac->schema, req, NULL,
2347 msg, &ac->seq_num, t, &is_urgent, &rodc);
2348 if (rodc && (ret == LDB_ERR_REFERRAL)) {
2349 struct loadparm_context *lp_ctx;
2352 lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
2353 struct loadparm_context);
2355 referral = talloc_asprintf(req,
2357 lpcfg_dnsdomain(lp_ctx),
2358 ldb_dn_get_linearized(msg->dn));
2359 ret = ldb_module_send_referral(req, referral);
2364 if (ret != LDB_SUCCESS) {
2369 ret = replmd_modify_handle_linked_attribs(module, msg, ac->seq_num, t, req);
2370 if (ret != LDB_SUCCESS) {
2376 * - replace the old object with the newly constructed one
2379 ac->is_urgent = is_urgent;
2381 ret = ldb_build_mod_req(&down_req, ldb, ac,
2384 ac, replmd_op_callback,
2386 LDB_REQ_SET_LOCATION(down_req);
2387 if (ret != LDB_SUCCESS) {
2392 /* current partition control is needed by "replmd_op_callback" */
2393 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
2394 ret = ldb_request_add_control(down_req,
2395 DSDB_CONTROL_CURRENT_PARTITION_OID,
2397 if (ret != LDB_SUCCESS) {
2403 /* If we are in functional level 2000, then
2404 * replmd_modify_handle_linked_attribs will have done
2406 if (functional_level == DS_DOMAIN_FUNCTION_2000) {
2407 ret = ldb_request_add_control(down_req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
2408 if (ret != LDB_SUCCESS) {
2414 talloc_steal(down_req, msg);
2416 /* we only change whenChanged and uSNChanged if the seq_num
2418 if (ac->seq_num != 0) {
2419 ret = add_time_element(msg, "whenChanged", t);
2420 if (ret != LDB_SUCCESS) {
2426 ret = add_uint64_element(ldb, msg, "uSNChanged", ac->seq_num);
2427 if (ret != LDB_SUCCESS) {
2434 /* go on with the call chain */
2435 return ldb_next_request(module, down_req);
2438 static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares);
2441 handle a rename request
2443 On a rename we need to do an extra ldb_modify which sets the
2444 whenChanged and uSNChanged attributes. We do this in a callback after the success.
2446 static int replmd_rename(struct ldb_module *module, struct ldb_request *req)
2448 struct ldb_context *ldb;
2449 struct replmd_replicated_request *ac;
2451 struct ldb_request *down_req;
2453 /* do not manipulate our control entries */
2454 if (ldb_dn_is_special(req->op.mod.message->dn)) {
2455 return ldb_next_request(module, req);
2458 ldb = ldb_module_get_ctx(module);
2460 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_rename\n");
2462 ac = replmd_ctx_init(module, req);
2464 return ldb_module_oom(module);
2467 ret = ldb_build_rename_req(&down_req, ldb, ac,
2468 ac->req->op.rename.olddn,
2469 ac->req->op.rename.newdn,
2471 ac, replmd_rename_callback,
2473 LDB_REQ_SET_LOCATION(down_req);
2474 if (ret != LDB_SUCCESS) {
2479 /* go on with the call chain */
2480 return ldb_next_request(module, down_req);
2483 /* After the rename is compleated, update the whenchanged etc */
2484 static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares)
2486 struct ldb_context *ldb;
2487 struct replmd_replicated_request *ac;
2488 struct ldb_request *down_req;
2489 struct ldb_message *msg;
2490 const struct dsdb_attribute *rdn_attr;
2491 const char *rdn_name;
2492 const struct ldb_val *rdn_val;
2493 const char *attrs[5] = { NULL, };
2494 time_t t = time(NULL);
2496 bool is_urgent = false, rodc = false;
2498 ac = talloc_get_type(req->context, struct replmd_replicated_request);
2499 ldb = ldb_module_get_ctx(ac->module);
2501 if (ares->error != LDB_SUCCESS) {
2502 return ldb_module_done(ac->req, ares->controls,
2503 ares->response, ares->error);
2506 if (ares->type != LDB_REPLY_DONE) {
2507 ldb_set_errstring(ldb,
2508 "invalid ldb_reply_type in callback");
2510 return ldb_module_done(ac->req, NULL, NULL,
2511 LDB_ERR_OPERATIONS_ERROR);
2515 * - replace the old object with the newly constructed one
2518 msg = ldb_msg_new(ac);
2521 return LDB_ERR_OPERATIONS_ERROR;
2524 msg->dn = ac->req->op.rename.newdn;
2526 rdn_name = ldb_dn_get_rdn_name(msg->dn);
2527 if (rdn_name == NULL) {
2529 return ldb_module_done(ac->req, NULL, NULL,
2533 /* normalize the rdn attribute name */
2534 rdn_attr = dsdb_attribute_by_lDAPDisplayName(ac->schema, rdn_name);
2535 if (rdn_attr == NULL) {
2537 return ldb_module_done(ac->req, NULL, NULL,
2540 rdn_name = rdn_attr->lDAPDisplayName;
2542 rdn_val = ldb_dn_get_rdn_val(msg->dn);
2543 if (rdn_val == NULL) {
2545 return ldb_module_done(ac->req, NULL, NULL,
2549 if (ldb_msg_add_empty(msg, rdn_name, LDB_FLAG_MOD_REPLACE, NULL) != 0) {
2551 return ldb_module_done(ac->req, NULL, NULL,
2554 if (ldb_msg_add_value(msg, rdn_name, rdn_val, NULL) != 0) {
2556 return ldb_module_done(ac->req, NULL, NULL,
2559 if (ldb_msg_add_empty(msg, "name", LDB_FLAG_MOD_REPLACE, NULL) != 0) {
2561 return ldb_module_done(ac->req, NULL, NULL,
2564 if (ldb_msg_add_value(msg, "name", rdn_val, NULL) != 0) {
2566 return ldb_module_done(ac->req, NULL, NULL,
2571 * here we let replmd_update_rpmd() only search for
2572 * the existing "replPropertyMetaData" and rdn_name attributes.
2574 * We do not want the existing "name" attribute as
2575 * the "name" attribute needs to get the version
2576 * updated on rename even if the rdn value hasn't changed.
2578 * This is the diff of the meta data, for a moved user
2579 * on a w2k8r2 server:
2582 * -dn: CN=sdf df,CN=Users,DC=bla,DC=base
2583 * +dn: CN=sdf df,OU=TestOU,DC=bla,DC=base
2584 * replPropertyMetaData: NDR: struct replPropertyMetaDataBlob
2585 * version : 0x00000001 (1)
2586 * reserved : 0x00000000 (0)
2587 * @@ -66,11 +66,11 @@ replPropertyMetaData: NDR: struct re
2588 * local_usn : 0x00000000000037a5 (14245)
2589 * array: struct replPropertyMetaData1
2590 * attid : DRSUAPI_ATTID_name (0x90001)
2591 * - version : 0x00000001 (1)
2592 * - originating_change_time : Wed Feb 9 17:20:49 2011 CET
2593 * + version : 0x00000002 (2)
2594 * + originating_change_time : Wed Apr 6 15:21:01 2011 CEST
2595 * originating_invocation_id: 0d36ca05-5507-4e62-aca3-354bab0d39e1
2596 * - originating_usn : 0x00000000000037a5 (14245)
2597 * - local_usn : 0x00000000000037a5 (14245)
2598 * + originating_usn : 0x0000000000003834 (14388)
2599 * + local_usn : 0x0000000000003834 (14388)
2600 * array: struct replPropertyMetaData1
2601 * attid : DRSUAPI_ATTID_userAccountControl (0x90008)
2602 * version : 0x00000004 (4)
2604 attrs[0] = "replPropertyMetaData";
2605 attrs[1] = "objectClass";
2606 attrs[2] = "instanceType";
2607 attrs[3] = rdn_name;
2610 ret = replmd_update_rpmd(ac->module, ac->schema, req, attrs,
2611 msg, &ac->seq_num, t, &is_urgent, &rodc);
2612 if (rodc && (ret == LDB_ERR_REFERRAL)) {
2613 struct ldb_dn *olddn = ac->req->op.rename.olddn;
2614 struct loadparm_context *lp_ctx;
2617 lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
2618 struct loadparm_context);
2620 referral = talloc_asprintf(req,
2622 lpcfg_dnsdomain(lp_ctx),
2623 ldb_dn_get_linearized(olddn));
2624 ret = ldb_module_send_referral(req, referral);
2626 return ldb_module_done(req, NULL, NULL, ret);
2629 if (ret != LDB_SUCCESS) {
2631 return ldb_module_done(ac->req, NULL, NULL, ret);
2634 if (ac->seq_num == 0) {
2636 return ldb_module_done(ac->req, NULL, NULL,
2638 "internal error seq_num == 0"));
2640 ac->is_urgent = is_urgent;
2642 ret = ldb_build_mod_req(&down_req, ldb, ac,
2645 ac, replmd_op_callback,
2647 LDB_REQ_SET_LOCATION(down_req);
2648 if (ret != LDB_SUCCESS) {
2653 /* current partition control is needed by "replmd_op_callback" */
2654 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
2655 ret = ldb_request_add_control(down_req,
2656 DSDB_CONTROL_CURRENT_PARTITION_OID,
2658 if (ret != LDB_SUCCESS) {
2664 talloc_steal(down_req, msg);
2666 ret = add_time_element(msg, "whenChanged", t);
2667 if (ret != LDB_SUCCESS) {
2673 ret = add_uint64_element(ldb, msg, "uSNChanged", ac->seq_num);
2674 if (ret != LDB_SUCCESS) {
2680 /* go on with the call chain - do the modify after the rename */
2681 return ldb_next_request(ac->module, down_req);
2685 remove links from objects that point at this object when an object
2688 static int replmd_delete_remove_link(struct ldb_module *module,
2689 const struct dsdb_schema *schema,
2691 struct ldb_message_element *el,
2692 const struct dsdb_attribute *sa,
2693 struct ldb_request *parent)
2696 TALLOC_CTX *tmp_ctx = talloc_new(module);
2697 struct ldb_context *ldb = ldb_module_get_ctx(module);
2699 for (i=0; i<el->num_values; i++) {
2700 struct dsdb_dn *dsdb_dn;
2704 struct ldb_message *msg;
2705 const struct dsdb_attribute *target_attr;
2706 struct ldb_message_element *el2;
2707 struct ldb_val dn_val;
2709 if (dsdb_dn_is_deleted_val(&el->values[i])) {
2713 dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i], sa->syntax->ldap_oid);
2715 talloc_free(tmp_ctx);
2716 return LDB_ERR_OPERATIONS_ERROR;
2719 status = dsdb_get_extended_dn_guid(dsdb_dn->dn, &guid2, "GUID");
2720 if (!NT_STATUS_IS_OK(status)) {
2721 talloc_free(tmp_ctx);
2722 return LDB_ERR_OPERATIONS_ERROR;
2725 /* remove the link */
2726 msg = ldb_msg_new(tmp_ctx);
2728 ldb_module_oom(module);
2729 talloc_free(tmp_ctx);
2730 return LDB_ERR_OPERATIONS_ERROR;
2734 msg->dn = dsdb_dn->dn;
2736 target_attr = dsdb_attribute_by_linkID(schema, sa->linkID ^ 1);
2737 if (target_attr == NULL) {
2741 ret = ldb_msg_add_empty(msg, target_attr->lDAPDisplayName, LDB_FLAG_MOD_DELETE, &el2);
2742 if (ret != LDB_SUCCESS) {
2743 ldb_module_oom(module);
2744 talloc_free(tmp_ctx);
2745 return LDB_ERR_OPERATIONS_ERROR;
2747 dn_val = data_blob_string_const(ldb_dn_get_linearized(dn));
2748 el2->values = &dn_val;
2749 el2->num_values = 1;
2751 ret = dsdb_module_modify(module, msg, DSDB_FLAG_OWN_MODULE, parent);
2752 if (ret != LDB_SUCCESS) {
2753 talloc_free(tmp_ctx);
2757 talloc_free(tmp_ctx);
2763 handle update of replication meta data for deletion of objects
2765 This also handles the mapping of delete to a rename operation
2766 to allow deletes to be replicated.
2768 static int replmd_delete(struct ldb_module *module, struct ldb_request *req)
2770 int ret = LDB_ERR_OTHER;
2771 bool retb, disallow_move_on_delete;
2772 struct ldb_dn *old_dn, *new_dn;
2773 const char *rdn_name;
2774 const struct ldb_val *rdn_value, *new_rdn_value;
2776 struct ldb_context *ldb = ldb_module_get_ctx(module);
2777 const struct dsdb_schema *schema;
2778 struct ldb_message *msg, *old_msg;
2779 struct ldb_message_element *el;
2780 TALLOC_CTX *tmp_ctx;
2781 struct ldb_result *res, *parent_res;
2782 const char *preserved_attrs[] = {
2783 /* yes, this really is a hard coded list. See MS-ADTS
2784 section 3.1.1.5.5.1.1 */
2785 "nTSecurityDescriptor", "attributeID", "attributeSyntax", "dNReferenceUpdate", "dNSHostName",
2786 "flatName", "governsID", "groupType", "instanceType", "lDAPDisplayName", "legacyExchangeDN",
2787 "isDeleted", "isRecycled", "lastKnownParent", "msDS-LastKnownRDN", "mS-DS-CreatorSID",
2788 "mSMQOwnerID", "nCName", "objectClass", "distinguishedName", "objectGUID", "objectSid",
2789 "oMSyntax", "proxiedObjectName", "name", "replPropertyMetaData", "sAMAccountName",
2790 "securityIdentifier", "sIDHistory", "subClassOf", "systemFlags", "trustPartner", "trustDirection",
2791 "trustType", "trustAttributes", "userAccountControl", "uSNChanged", "uSNCreated", "whenCreated",
2792 "whenChanged", NULL};
2793 unsigned int i, el_count = 0;
2794 enum deletion_state { OBJECT_NOT_DELETED=1, OBJECT_DELETED=2, OBJECT_RECYCLED=3,
2795 OBJECT_TOMBSTONE=4, OBJECT_REMOVED=5 };
2796 enum deletion_state deletion_state, next_deletion_state;
2798 int functional_level;
2800 if (ldb_dn_is_special(req->op.del.dn)) {
2801 return ldb_next_request(module, req);
2804 tmp_ctx = talloc_new(ldb);
2807 return LDB_ERR_OPERATIONS_ERROR;
2810 schema = dsdb_get_schema(ldb, tmp_ctx);
2812 return LDB_ERR_OPERATIONS_ERROR;
2815 functional_level = dsdb_functional_level(ldb);
2817 old_dn = ldb_dn_copy(tmp_ctx, req->op.del.dn);
2819 /* we need the complete msg off disk, so we can work out which
2820 attributes need to be removed */
2821 ret = dsdb_module_search_dn(module, tmp_ctx, &res, old_dn, NULL,
2822 DSDB_FLAG_NEXT_MODULE |
2823 DSDB_SEARCH_SHOW_RECYCLED |
2824 DSDB_SEARCH_REVEAL_INTERNALS |
2825 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT, req);
2826 if (ret != LDB_SUCCESS) {
2827 talloc_free(tmp_ctx);
2830 old_msg = res->msgs[0];
2833 ret = dsdb_recyclebin_enabled(module, &enabled);
2834 if (ret != LDB_SUCCESS) {
2835 talloc_free(tmp_ctx);
2839 if (ldb_msg_check_string_attribute(old_msg, "isDeleted", "TRUE")) {
2841 deletion_state = OBJECT_TOMBSTONE;
2842 next_deletion_state = OBJECT_REMOVED;
2843 } else if (ldb_msg_check_string_attribute(old_msg, "isRecycled", "TRUE")) {
2844 deletion_state = OBJECT_RECYCLED;
2845 next_deletion_state = OBJECT_REMOVED;
2847 deletion_state = OBJECT_DELETED;
2848 next_deletion_state = OBJECT_RECYCLED;
2851 deletion_state = OBJECT_NOT_DELETED;
2853 next_deletion_state = OBJECT_DELETED;
2855 next_deletion_state = OBJECT_TOMBSTONE;
2859 if (next_deletion_state == OBJECT_REMOVED) {
2860 struct auth_session_info *session_info =
2861 (struct auth_session_info *)ldb_get_opaque(ldb, "sessionInfo");
2862 if (security_session_user_level(session_info, NULL) != SECURITY_SYSTEM) {
2863 ldb_asprintf_errstring(ldb, "Refusing to delete deleted object %s",
2864 ldb_dn_get_linearized(old_msg->dn));
2865 return LDB_ERR_UNWILLING_TO_PERFORM;
2868 /* it is already deleted - really remove it this time */
2869 talloc_free(tmp_ctx);
2870 return ldb_next_request(module, req);
2873 rdn_name = ldb_dn_get_rdn_name(old_dn);
2874 rdn_value = ldb_dn_get_rdn_val(old_dn);
2875 if ((rdn_name == NULL) || (rdn_value == NULL)) {
2876 talloc_free(tmp_ctx);
2877 return ldb_operr(ldb);
2880 msg = ldb_msg_new(tmp_ctx);
2882 ldb_module_oom(module);
2883 talloc_free(tmp_ctx);
2884 return LDB_ERR_OPERATIONS_ERROR;
2889 if (deletion_state == OBJECT_NOT_DELETED){
2890 /* consider the SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE flag */
2891 disallow_move_on_delete =
2892 (ldb_msg_find_attr_as_int(old_msg, "systemFlags", 0)
2893 & SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE);
2895 /* work out where we will be renaming this object to */
2896 if (!disallow_move_on_delete) {
2897 ret = dsdb_get_deleted_objects_dn(ldb, tmp_ctx, old_dn,
2899 if (ret != LDB_SUCCESS) {
2900 /* this is probably an attempted delete on a partition
2901 * that doesn't allow delete operations, such as the
2902 * schema partition */
2903 ldb_asprintf_errstring(ldb, "No Deleted Objects container for DN %s",
2904 ldb_dn_get_linearized(old_dn));
2905 talloc_free(tmp_ctx);
2906 return LDB_ERR_UNWILLING_TO_PERFORM;
2909 new_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
2910 if (new_dn == NULL) {
2911 ldb_module_oom(module);
2912 talloc_free(tmp_ctx);
2913 return LDB_ERR_OPERATIONS_ERROR;
2917 /* get the objects GUID from the search we just did */
2918 guid = samdb_result_guid(old_msg, "objectGUID");
2920 /* Add a formatted child */
2921 retb = ldb_dn_add_child_fmt(new_dn, "%s=%s\\0ADEL:%s",
2923 ldb_dn_escape_value(tmp_ctx, *rdn_value),
2924 GUID_string(tmp_ctx, &guid));
2926 DEBUG(0,(__location__ ": Unable to add a formatted child to dn: %s",
2927 ldb_dn_get_linearized(new_dn)));
2928 talloc_free(tmp_ctx);
2929 return LDB_ERR_OPERATIONS_ERROR;
2932 ret = ldb_msg_add_string(msg, "isDeleted", "TRUE");
2933 if (ret != LDB_SUCCESS) {
2934 DEBUG(0,(__location__ ": Failed to add isDeleted string to the msg\n"));
2935 ldb_module_oom(module);
2936 talloc_free(tmp_ctx);
2939 msg->elements[el_count++].flags = LDB_FLAG_MOD_REPLACE;
2943 now we need to modify the object in the following ways:
2945 - add isDeleted=TRUE
2946 - update rDN and name, with new rDN
2947 - remove linked attributes
2948 - remove objectCategory and sAMAccountType
2949 - remove attribs not on the preserved list
2950 - preserved if in above list, or is rDN
2951 - remove all linked attribs from this object
2952 - remove all links from other objects to this object
2953 - add lastKnownParent
2954 - update replPropertyMetaData?
2956 see MS-ADTS "Tombstone Requirements" section 3.1.1.5.5.1.1
2959 /* we need the storage form of the parent GUID */
2960 ret = dsdb_module_search_dn(module, tmp_ctx, &parent_res,
2961 ldb_dn_get_parent(tmp_ctx, old_dn), NULL,
2962 DSDB_FLAG_NEXT_MODULE |
2963 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
2964 DSDB_SEARCH_REVEAL_INTERNALS|
2965 DSDB_SEARCH_SHOW_RECYCLED, req);
2966 if (ret != LDB_SUCCESS) {
2967 talloc_free(tmp_ctx);
2971 if (deletion_state == OBJECT_NOT_DELETED){
2972 ret = ldb_msg_add_steal_string(msg, "lastKnownParent",
2973 ldb_dn_get_extended_linearized(tmp_ctx, parent_res->msgs[0]->dn, 1));
2974 if (ret != LDB_SUCCESS) {
2975 DEBUG(0,(__location__ ": Failed to add lastKnownParent string to the msg\n"));
2976 ldb_module_oom(module);
2977 talloc_free(tmp_ctx);
2980 msg->elements[el_count++].flags = LDB_FLAG_MOD_ADD;
2983 switch (next_deletion_state){
2985 case OBJECT_DELETED:
2987 ret = ldb_msg_add_value(msg, "msDS-LastKnownRDN", rdn_value, NULL);
2988 if (ret != LDB_SUCCESS) {
2989 DEBUG(0,(__location__ ": Failed to add msDS-LastKnownRDN string to the msg\n"));
2990 ldb_module_oom(module);
2991 talloc_free(tmp_ctx);
2994 msg->elements[el_count++].flags = LDB_FLAG_MOD_ADD;
2996 ret = ldb_msg_add_empty(msg, "objectCategory", LDB_FLAG_MOD_REPLACE, NULL);
2997 if (ret != LDB_SUCCESS) {
2998 talloc_free(tmp_ctx);
2999 ldb_module_oom(module);
3003 ret = ldb_msg_add_empty(msg, "sAMAccountType", LDB_FLAG_MOD_REPLACE, NULL);
3004 if (ret != LDB_SUCCESS) {
3005 talloc_free(tmp_ctx);
3006 ldb_module_oom(module);
3012 case OBJECT_RECYCLED:
3013 case OBJECT_TOMBSTONE:
3016 * we also mark it as recycled, meaning this object can't be
3017 * recovered (we are stripping its attributes).
3018 * This is done only if we have this schema object of course ...
3019 * This behavior is identical to the one of Windows 2008R2 which
3020 * always set the isRecycled attribute, even if the recycle-bin is
3021 * not activated and what ever the forest level is.
3023 if (dsdb_attribute_by_lDAPDisplayName(schema, "isRecycled") != NULL) {
3024 ret = ldb_msg_add_string(msg, "isRecycled", "TRUE");
3025 if (ret != LDB_SUCCESS) {
3026 DEBUG(0,(__location__ ": Failed to add isRecycled string to the msg\n"));
3027 ldb_module_oom(module);
3028 talloc_free(tmp_ctx);
3031 msg->elements[el_count++].flags = LDB_FLAG_MOD_ADD;
3034 /* work out which of the old attributes we will be removing */
3035 for (i=0; i<old_msg->num_elements; i++) {
3036 const struct dsdb_attribute *sa;
3037 el = &old_msg->elements[i];
3038 sa = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
3040 talloc_free(tmp_ctx);
3041 return LDB_ERR_OPERATIONS_ERROR;
3043 if (ldb_attr_cmp(el->name, rdn_name) == 0) {
3044 /* don't remove the rDN */
3047 if (sa->linkID && (sa->linkID & 1)) {
3049 we have a backlink in this object
3050 that needs to be removed. We're not
3051 allowed to remove it directly
3052 however, so we instead setup a
3053 modify to delete the corresponding
3056 ret = replmd_delete_remove_link(module, schema, old_dn, el, sa, req);
3057 if (ret != LDB_SUCCESS) {
3058 talloc_free(tmp_ctx);
3059 return LDB_ERR_OPERATIONS_ERROR;
3061 /* now we continue, which means we
3062 won't remove this backlink
3067 if (!sa->linkID && ldb_attr_in_list(preserved_attrs, el->name)) {
3070 ret = ldb_msg_add_empty(msg, el->name, LDB_FLAG_MOD_DELETE, &el);
3071 if (ret != LDB_SUCCESS) {
3072 talloc_free(tmp_ctx);
3073 ldb_module_oom(module);
3083 if (deletion_state == OBJECT_NOT_DELETED) {
3084 const struct dsdb_attribute *sa;
3086 /* work out what the new rdn value is, for updating the
3087 rDN and name fields */
3088 new_rdn_value = ldb_dn_get_rdn_val(new_dn);
3089 if (new_rdn_value == NULL) {
3090 talloc_free(tmp_ctx);
3091 return ldb_operr(ldb);
3094 sa = dsdb_attribute_by_lDAPDisplayName(schema, rdn_name);
3096 talloc_free(tmp_ctx);
3097 return LDB_ERR_OPERATIONS_ERROR;
3100 ret = ldb_msg_add_value(msg, sa->lDAPDisplayName, new_rdn_value,
3102 if (ret != LDB_SUCCESS) {
3103 talloc_free(tmp_ctx);
3106 el->flags = LDB_FLAG_MOD_REPLACE;
3108 el = ldb_msg_find_element(old_msg, "name");
3110 ret = ldb_msg_add_value(msg, "name", new_rdn_value, &el);
3111 if (ret != LDB_SUCCESS) {
3112 talloc_free(tmp_ctx);
3115 el->flags = LDB_FLAG_MOD_REPLACE;
3119 ret = dsdb_module_modify(module, msg, DSDB_FLAG_OWN_MODULE, req);
3120 if (ret != LDB_SUCCESS) {
3121 ldb_asprintf_errstring(ldb, "replmd_delete: Failed to modify object %s in delete - %s",
3122 ldb_dn_get_linearized(old_dn), ldb_errstring(ldb));
3123 talloc_free(tmp_ctx);
3127 if (deletion_state == OBJECT_NOT_DELETED) {
3128 /* now rename onto the new DN */
3129 ret = dsdb_module_rename(module, old_dn, new_dn, DSDB_FLAG_NEXT_MODULE, req);
3130 if (ret != LDB_SUCCESS){
3131 DEBUG(0,(__location__ ": Failed to rename object from '%s' to '%s' - %s\n",
3132 ldb_dn_get_linearized(old_dn),
3133 ldb_dn_get_linearized(new_dn),
3134 ldb_errstring(ldb)));
3135 talloc_free(tmp_ctx);
3140 talloc_free(tmp_ctx);
3142 return ldb_module_done(req, NULL, NULL, LDB_SUCCESS);
3147 static int replmd_replicated_request_error(struct replmd_replicated_request *ar, int ret)
3152 static int replmd_replicated_request_werror(struct replmd_replicated_request *ar, WERROR status)
3154 int ret = LDB_ERR_OTHER;
3155 /* TODO: do some error mapping */
3160 static struct replPropertyMetaData1 *
3161 replmd_replPropertyMetaData1_find_attid(struct replPropertyMetaDataBlob *md_blob,
3162 enum drsuapi_DsAttributeId attid)
3165 struct replPropertyMetaDataCtr1 *rpmd_ctr = &md_blob->ctr.ctr1;
3167 for (i = 0; i < rpmd_ctr->count; i++) {
3168 if (rpmd_ctr->array[i].attid == attid) {
3169 return &rpmd_ctr->array[i];
3177 return true if an update is newer than an existing entry
3178 see section 5.11 of MS-ADTS
3180 static bool replmd_update_is_newer(const struct GUID *current_invocation_id,
3181 const struct GUID *update_invocation_id,
3182 uint32_t current_version,
3183 uint32_t update_version,
3184 NTTIME current_change_time,
3185 NTTIME update_change_time)
3187 if (update_version != current_version) {
3188 return update_version > current_version;
3190 if (update_change_time != current_change_time) {
3191 return update_change_time > current_change_time;
3193 return GUID_compare(update_invocation_id, current_invocation_id) > 0;
3196 static bool replmd_replPropertyMetaData1_is_newer(struct replPropertyMetaData1 *cur_m,
3197 struct replPropertyMetaData1 *new_m)
3199 return replmd_update_is_newer(&cur_m->originating_invocation_id,
3200 &new_m->originating_invocation_id,
3203 cur_m->originating_change_time,
3204 new_m->originating_change_time);
3211 static struct ldb_dn *replmd_conflict_dn(TALLOC_CTX *mem_ctx, struct ldb_dn *dn, struct GUID *guid)
3213 const struct ldb_val *rdn_val;
3214 const char *rdn_name;
3215 struct ldb_dn *new_dn;
3217 rdn_val = ldb_dn_get_rdn_val(dn);
3218 rdn_name = ldb_dn_get_rdn_name(dn);
3219 if (!rdn_val || !rdn_name) {
3223 new_dn = ldb_dn_copy(mem_ctx, dn);
3228 if (!ldb_dn_remove_child_components(new_dn, 1)) {
3232 if (!ldb_dn_add_child_fmt(new_dn, "%s=%s\\0ACNF:%s",
3234 ldb_dn_escape_value(new_dn, *rdn_val),
3235 GUID_string(new_dn, guid))) {
3244 perform a modify operation which sets the rDN and name attributes to
3245 their current values. This has the effect of changing these
3246 attributes to have been last updated by the current DC. This is
3247 needed to ensure that renames performed as part of conflict
3248 resolution are propogated to other DCs
3250 static int replmd_name_modify(struct replmd_replicated_request *ar,
3251 struct ldb_request *req, struct ldb_dn *dn)
3253 struct ldb_message *msg;
3254 const char *rdn_name;
3255 const struct ldb_val *rdn_val;
3256 const struct dsdb_attribute *rdn_attr;
3259 msg = ldb_msg_new(req);
3265 rdn_name = ldb_dn_get_rdn_name(dn);
3266 if (rdn_name == NULL) {
3270 /* normalize the rdn attribute name */
3271 rdn_attr = dsdb_attribute_by_lDAPDisplayName(ar->schema, rdn_name);
3272 if (rdn_attr == NULL) {
3275 rdn_name = rdn_attr->lDAPDisplayName;
3277 rdn_val = ldb_dn_get_rdn_val(dn);
3278 if (rdn_val == NULL) {
3282 if (ldb_msg_add_empty(msg, rdn_name, LDB_FLAG_MOD_REPLACE, NULL) != 0) {
3285 if (ldb_msg_add_value(msg, rdn_name, rdn_val, NULL) != 0) {
3288 if (ldb_msg_add_empty(msg, "name", LDB_FLAG_MOD_REPLACE, NULL) != 0) {
3291 if (ldb_msg_add_value(msg, "name", rdn_val, NULL) != 0) {
3295 ret = dsdb_module_modify(ar->module, msg, DSDB_FLAG_OWN_MODULE, req);
3296 if (ret != LDB_SUCCESS) {
3297 DEBUG(0,(__location__ ": Failed to modify rDN/name of conflict DN '%s' - %s",
3298 ldb_dn_get_linearized(dn),
3299 ldb_errstring(ldb_module_get_ctx(ar->module))));
3309 DEBUG(0,(__location__ ": Failed to setup modify rDN/name of conflict DN '%s'",
3310 ldb_dn_get_linearized(dn)));
3311 return LDB_ERR_OPERATIONS_ERROR;
3316 callback for conflict DN handling where we have renamed the incoming
3317 record. After renaming it, we need to ensure the change of name and
3318 rDN for the incoming record is seen as an originating update by this DC.
3320 static int replmd_op_name_modify_callback(struct ldb_request *req, struct ldb_reply *ares)
3322 struct replmd_replicated_request *ar =
3323 talloc_get_type_abort(req->context, struct replmd_replicated_request);
3326 if (ares->error != LDB_SUCCESS) {
3327 /* call the normal callback for everything except success */
3328 return replmd_op_callback(req, ares);
3331 /* perform a modify of the rDN and name of the record */
3332 ret = replmd_name_modify(ar, req, req->op.add.message->dn);
3333 if (ret != LDB_SUCCESS) {
3335 return replmd_op_callback(req, ares);
3338 return replmd_op_callback(req, ares);
3342 callback for replmd_replicated_apply_add()
3343 This copes with the creation of conflict records in the case where
3344 the DN exists, but with a different objectGUID
3346 static int replmd_op_add_callback(struct ldb_request *req, struct ldb_reply *ares)
3348 struct ldb_dn *conflict_dn;
3349 struct replmd_replicated_request *ar =
3350 talloc_get_type_abort(req->context, struct replmd_replicated_request);
3351 struct ldb_result *res;
3352 const char *attrs[] = { "replPropertyMetaData", "objectGUID", NULL };
3354 const struct ldb_val *rmd_value, *omd_value;
3355 struct replPropertyMetaDataBlob omd, rmd;
3356 enum ndr_err_code ndr_err;
3357 bool rename_incoming_record, rodc;
3358 struct replPropertyMetaData1 *rmd_name, *omd_name;
3360 if (ares->error != LDB_ERR_ENTRY_ALREADY_EXISTS) {
3361 /* call the normal callback for everything except
3363 return replmd_op_callback(req, ares);
3366 ret = samdb_rodc(ldb_module_get_ctx(ar->module), &rodc);
3367 if (ret != LDB_SUCCESS) {
3371 * we have a conflict, and need to decide if we will keep the
3372 * new record or the old record
3374 conflict_dn = req->op.add.message->dn;
3378 * We are on an RODC, or were a GC for this
3379 * partition, so we have to fail this until
3380 * someone who owns the partition sorts it
3383 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
3384 "Conflict adding object '%s' from incoming replication as we are read only for the partition. \n"
3385 " - We must fail the operation until a master for this partition resolves the conflict",
3386 ldb_dn_get_linearized(conflict_dn));
3391 * we have a conflict, and need to decide if we will keep the
3392 * new record or the old record
3394 conflict_dn = req->op.add.message->dn;
3397 * first we need the replPropertyMetaData attribute from the
3400 ret = dsdb_module_search_dn(ar->module, req, &res, conflict_dn,
3402 DSDB_FLAG_NEXT_MODULE |
3403 DSDB_SEARCH_SHOW_DELETED |
3404 DSDB_SEARCH_SHOW_RECYCLED, req);
3405 if (ret != LDB_SUCCESS) {
3406 DEBUG(0,(__location__ ": Unable to find object for conflicting record '%s'\n",
3407 ldb_dn_get_linearized(conflict_dn)));
3411 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
3412 if (omd_value == NULL) {
3413 DEBUG(0,(__location__ ": Unable to find replPropertyMetaData for conflicting record '%s'\n",
3414 ldb_dn_get_linearized(conflict_dn)));
3418 ndr_err = ndr_pull_struct_blob(omd_value, res->msgs[0], &omd,
3419 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
3420 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
3421 DEBUG(0,(__location__ ": Failed to parse old replPropertyMetaData for %s\n",
3422 ldb_dn_get_linearized(conflict_dn)));
3427 * and the replPropertyMetaData attribute from the
3430 rmd_value = ldb_msg_find_ldb_val(req->op.add.message, "replPropertyMetaData");
3431 if (rmd_value == NULL) {
3432 DEBUG(0,(__location__ ": Unable to find replPropertyMetaData for new record '%s'\n",
3433 ldb_dn_get_linearized(conflict_dn)));
3437 ndr_err = ndr_pull_struct_blob(rmd_value, req, &rmd,
3438 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
3439 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
3440 DEBUG(0,(__location__ ": Failed to parse new replPropertyMetaData for %s\n",
3441 ldb_dn_get_linearized(conflict_dn)));
3445 /* we decide which is newer based on the RPMD on the name
3446 attribute. See [MS-DRSR] ResolveNameConflict */
3447 rmd_name = replmd_replPropertyMetaData1_find_attid(&rmd, DRSUAPI_ATTID_name);
3448 omd_name = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
3449 if (!rmd_name || !omd_name) {
3450 DEBUG(0,(__location__ ": Failed to find name attribute in replPropertyMetaData for %s\n",
3451 ldb_dn_get_linearized(conflict_dn)));
3455 rename_incoming_record = !replmd_replPropertyMetaData1_is_newer(omd_name, rmd_name);
3457 if (rename_incoming_record) {
3459 struct ldb_dn *new_dn;
3460 struct ldb_message *new_msg;
3462 guid = samdb_result_guid(req->op.add.message, "objectGUID");
3463 if (GUID_all_zero(&guid)) {
3464 DEBUG(0,(__location__ ": Failed to find objectGUID for conflicting incoming record %s\n",
3465 ldb_dn_get_linearized(conflict_dn)));
3468 new_dn = replmd_conflict_dn(req, conflict_dn, &guid);
3469 if (new_dn == NULL) {
3470 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
3471 ldb_dn_get_linearized(conflict_dn)));
3475 DEBUG(1,(__location__ ": Resolving conflict record via incoming rename '%s' -> '%s'\n",
3476 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
3478 /* re-submit the request, but with a different
3479 callback, so we don't loop forever. */
3480 new_msg = ldb_msg_copy_shallow(req, req->op.add.message);
3483 DEBUG(0,(__location__ ": Failed to copy conflict DN message for %s\n",
3484 ldb_dn_get_linearized(conflict_dn)));
3486 new_msg->dn = new_dn;
3487 req->op.add.message = new_msg;
3488 req->callback = replmd_op_name_modify_callback;
3490 return ldb_next_request(ar->module, req);
3492 /* we are renaming the existing record */
3494 struct ldb_dn *new_dn;
3496 guid = samdb_result_guid(res->msgs[0], "objectGUID");
3497 if (GUID_all_zero(&guid)) {
3498 DEBUG(0,(__location__ ": Failed to find objectGUID for existing conflict record %s\n",
3499 ldb_dn_get_linearized(conflict_dn)));
3503 new_dn = replmd_conflict_dn(req, conflict_dn, &guid);
3504 if (new_dn == NULL) {
3505 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
3506 ldb_dn_get_linearized(conflict_dn)));
3510 DEBUG(1,(__location__ ": Resolving conflict record via existing rename '%s' -> '%s'\n",
3511 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
3513 ret = dsdb_module_rename(ar->module, conflict_dn, new_dn,
3514 DSDB_FLAG_OWN_MODULE, req);
3515 if (ret != LDB_SUCCESS) {
3516 DEBUG(0,(__location__ ": Failed to rename conflict dn '%s' to '%s' - %s\n",
3517 ldb_dn_get_linearized(conflict_dn),
3518 ldb_dn_get_linearized(new_dn),
3519 ldb_errstring(ldb_module_get_ctx(ar->module))));
3524 * now we need to ensure that the rename is seen as an
3525 * originating update. We do that with a modify.
3527 ret = replmd_name_modify(ar, req, new_dn);
3528 if (ret != LDB_SUCCESS) {
3532 req->callback = replmd_op_callback;
3534 return ldb_next_request(ar->module, req);
3538 /* on failure do the original callback. This means replication
3539 * will stop with an error, but there is not much else we can
3542 return replmd_op_callback(req, ares);
3545 static int replmd_add_isrecycled(struct replmd_replicated_request *ar,
3546 struct ldb_context *ldb,
3547 struct ldb_message *msg,
3548 struct replPropertyMetaDataBlob *md) {
3549 time_t t = time(NULL);
3551 struct replPropertyMetaData1 *m;
3552 const struct GUID *our_invocation_id;
3553 const struct ldb_val* v;
3556 v = ldb_dn_get_rdn_val(msg->dn);
3557 if (!v || strcmp((char*)v->data, "Deleted Objects") == 0) {
3561 our_invocation_id = samdb_ntds_invocation_id(ldb);
3562 if (!our_invocation_id) {
3563 ldb_debug_set(ldb, LDB_DEBUG_ERROR,
3564 "replmd_add: unable to find invocationId\n");
3565 return replmd_replicated_request_error(ar, LDB_ERR_OPERATIONS_ERROR);
3568 unix_to_nt_time(&now, t);
3570 ret = ldb_msg_add_string(msg, "isRecycled", "TRUE");
3571 if (ret != LDB_SUCCESS) {
3572 return replmd_replicated_request_error(ar, ret);
3575 md->ctr.ctr1.count++;
3576 md->ctr.ctr1.array = talloc_realloc(ar, md->ctr.ctr1.array,
3577 struct replPropertyMetaData1,
3578 md->ctr.ctr1.count);
3579 if (md->ctr.ctr1.array == NULL) {
3580 return replmd_replicated_request_werror(ar, WERR_NOMEM);
3583 /* rdn is at the end so shift it to end first */
3584 m = &md->ctr.ctr1.array[md->ctr.ctr1.count - 2];
3585 md->ctr.ctr1.array[md->ctr.ctr1.count - 1] = *m;
3587 /* Allocate a new entry in the replPropertyMetadata */
3589 m->attid = ar->isRecycled->attributeID_id;
3591 m->originating_change_time = now;
3592 m->originating_invocation_id = *our_invocation_id;
3593 m->originating_usn = ar->seq_num;
3594 m->local_usn = ar->seq_num;
3599 this is called when a new object comes in over DRS
3601 static int replmd_replicated_apply_add(struct replmd_replicated_request *ar)
3603 struct ldb_context *ldb;
3604 struct ldb_request *change_req;
3605 enum ndr_err_code ndr_err;
3606 struct ldb_message *msg;
3607 struct replPropertyMetaDataBlob *md;
3608 struct ldb_val md_value;
3610 bool is_deleted = false;
3611 bool is_recycled = false;
3612 bool has_lastknownrdn = false;
3616 * TODO: check if the parent object exist
3620 * TODO: handle the conflict case where an object with the
3624 ldb = ldb_module_get_ctx(ar->module);
3625 msg = ar->objs->objects[ar->index_current].msg;
3626 md = ar->objs->objects[ar->index_current].meta_data;
3628 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
3629 if (ret != LDB_SUCCESS) {
3630 return replmd_replicated_request_error(ar, ret);
3633 ret = ldb_msg_add_value(msg, "objectGUID", &ar->objs->objects[ar->index_current].guid_value, NULL);
3634 if (ret != LDB_SUCCESS) {
3635 return replmd_replicated_request_error(ar, ret);
3638 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
3639 if (ret != LDB_SUCCESS) {
3640 return replmd_replicated_request_error(ar, ret);
3643 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", ar->seq_num);
3644 if (ret != LDB_SUCCESS) {
3645 return replmd_replicated_request_error(ar, ret);
3648 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
3649 if (ret != LDB_SUCCESS) {
3650 return replmd_replicated_request_error(ar, ret);
3653 if (!ar->is_recycled_tested) {
3654 ar->isRecycled = dsdb_attribute_by_lDAPDisplayName(ar->schema,
3656 ar->is_recycled_tested = true;
3659 /* remove any message elements that have zero values */
3660 for (i=0; i<msg->num_elements; i++) {
3661 struct ldb_message_element *el = &msg->elements[i];
3663 if (el->num_values == 0) {
3664 DEBUG(4,(__location__ ": Removing attribute %s with num_values==0\n",
3666 memmove(el, el+1, sizeof(*el)*(msg->num_elements - (i+1)));
3667 msg->num_elements--;
3672 if (ldb_attr_cmp(el->name, "isDeleted") == 0) {
3673 struct ldb_val *v = &el->values[0];
3675 if (strncmp((const char*)v->data, "TRUE", 4) == 0 ) {
3676 DEBUG(11, ("Found isDeleted on %s while doing replmd_replicated_apply_add\n",
3677 ldb_dn_get_linearized(msg->dn)));
3683 if (ldb_attr_cmp(el->name, "isRecycled") == 0) {
3684 struct ldb_val *v = &el->values[0];
3687 * Normaly we do not store boolean equals to false, but
3688 * nothing forbids to do so, especially if you undelete
3691 if (strncmp((const char*)v->data, "TRUE", 4) == 0 ) {
3697 if (ldb_attr_cmp(el->name, "msDS-LastKnownRDN") == 0) {
3698 has_lastknownrdn = true;
3703 if (is_deleted && ar->isRecycled && !is_recycled && !has_lastknownrdn) {
3705 * The object is deleted and we have the isRecycled attribute in
3706 * the schema, but it is missing on the object the recycle-bin
3707 * is not activated and it hasn't the msDS-LastKnownRDN so we
3708 * mark the object as deleted because it means that it comes
3709 * from a pre windows 2008R2 server or from a Samba DC before
3710 * changes related to isRecycled.
3712 //TODO does this have impact on whenChanged?
3713 ret = replmd_add_isrecycled(ar, ldb, msg, md);
3714 if (ret != LDB_SUCCESS) {
3718 ret = replmd_replPropertyMetaDataCtr1_sort(&md->ctr.ctr1,
3719 ar->schema, msg->dn);
3720 if (ret != LDB_SUCCESS) {
3726 * the meta data array is already sorted by the caller
3728 for (i=0; i < md->ctr.ctr1.count; i++) {
3729 md->ctr.ctr1.array[i].local_usn = ar->seq_num;
3731 ndr_err = ndr_push_struct_blob(&md_value, msg, md,
3732 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
3733 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
3734 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
3735 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
3737 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &md_value, NULL);
3738 if (ret != LDB_SUCCESS) {
3739 return replmd_replicated_request_error(ar, ret);
3742 replmd_ldb_message_sort(msg, ar->schema);
3745 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_ADD, msg);
3746 DEBUG(4, ("DRS replication add message:\n%s\n", s));
3750 ret = ldb_build_add_req(&change_req,
3756 replmd_op_add_callback,
3758 LDB_REQ_SET_LOCATION(change_req);
3759 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
3761 /* current partition control needed by "repmd_op_callback" */
3762 ret = ldb_request_add_control(change_req,
3763 DSDB_CONTROL_CURRENT_PARTITION_OID,
3765 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
3767 if (ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PARTIAL_REPLICA) {
3768 /* this tells the partition module to make it a
3769 partial replica if creating an NC */
3770 ret = ldb_request_add_control(change_req,
3771 DSDB_CONTROL_PARTIAL_REPLICA,
3773 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
3776 return ldb_next_request(ar->module, change_req);
3780 handle renames that come in over DRS replication
3782 static int replmd_replicated_handle_rename(struct replmd_replicated_request *ar,
3783 struct ldb_message *msg,
3784 struct replPropertyMetaDataBlob *rmd,
3785 struct replPropertyMetaDataBlob *omd,
3786 struct ldb_request *parent)
3788 struct replPropertyMetaData1 *md_remote;
3789 struct replPropertyMetaData1 *md_local;
3791 if (ldb_dn_compare(msg->dn, ar->search_msg->dn) == 0) {
3796 /* now we need to check for double renames. We could have a
3797 * local rename pending which our replication partner hasn't
3798 * received yet. We choose which one wins by looking at the
3799 * attribute stamps on the two objects, the newer one wins
3801 md_remote = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
3802 md_local = replmd_replPropertyMetaData1_find_attid(omd, DRSUAPI_ATTID_name);
3803 /* if there is no name attribute then we have to assume the
3804 object we've received is in fact newer */
3805 if (!md_remote || !md_local ||
3806 replmd_replPropertyMetaData1_is_newer(md_local, md_remote)) {
3807 DEBUG(4,("replmd_replicated_request rename %s => %s\n",
3808 ldb_dn_get_linearized(ar->search_msg->dn),
3809 ldb_dn_get_linearized(msg->dn)));
3810 /* pass rename to the next module
3811 * so it doesn't appear as an originating update */
3812 return dsdb_module_rename(ar->module,
3813 ar->search_msg->dn, msg->dn,
3814 DSDB_FLAG_NEXT_MODULE | DSDB_MODIFY_RELAX, parent);
3817 /* we're going to keep our old object */
3818 DEBUG(4,(__location__ ": Keeping object %s and rejecting older rename to %s\n",
3819 ldb_dn_get_linearized(ar->search_msg->dn),
3820 ldb_dn_get_linearized(msg->dn)));
3825 static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar)
3827 struct ldb_context *ldb;
3828 struct ldb_request *change_req;
3829 enum ndr_err_code ndr_err;
3830 struct ldb_message *msg;
3831 struct replPropertyMetaDataBlob *rmd;
3832 struct replPropertyMetaDataBlob omd;
3833 const struct ldb_val *omd_value;
3834 struct replPropertyMetaDataBlob nmd;
3835 struct ldb_val nmd_value;
3838 unsigned int removed_attrs = 0;
3840 bool found_old_is_deleted = false;
3841 bool found_old_is_recycled = false;
3842 bool found_old_has_lastknownrdn = false;
3843 bool old_is_deleted = false;
3844 bool old_is_recycled = false;
3845 bool old_has_lastknownrdn = false;
3846 bool found_new_is_deleted = false;
3847 bool found_new_is_recycled = false;
3848 bool found_new_has_lastknownrdn = false;
3849 bool new_is_deleted = false;
3850 bool new_is_recycled = false;
3851 bool new_has_lastknownrdn = false;
3853 ldb = ldb_module_get_ctx(ar->module);
3854 msg = ar->objs->objects[ar->index_current].msg;
3855 rmd = ar->objs->objects[ar->index_current].meta_data;
3859 if (!ar->is_recycled_tested) {
3860 ar->isDeleted = dsdb_attribute_by_lDAPDisplayName(ar->schema,
3862 ar->isRecycled = dsdb_attribute_by_lDAPDisplayName(ar->schema,
3864 ar->lastKnownRDN = dsdb_attribute_by_lDAPDisplayName(ar->schema,
3865 "msDS-lastKnownRDN");
3866 ar->is_recycled_tested = true;
3869 /* find existing meta data */
3870 omd_value = ldb_msg_find_ldb_val(ar->search_msg, "replPropertyMetaData");
3872 ndr_err = ndr_pull_struct_blob(omd_value, ar, &omd,
3873 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
3874 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
3875 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
3876 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
3879 if (omd.version != 1) {
3880 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
3884 /* handle renames that come in over DRS */
3885 ret = replmd_replicated_handle_rename(ar, msg, rmd, &omd, ar->req);
3886 if (ret != LDB_SUCCESS) {
3887 ldb_debug(ldb, LDB_DEBUG_FATAL,
3888 "replmd_replicated_request rename %s => %s failed - %s\n",
3889 ldb_dn_get_linearized(ar->search_msg->dn),
3890 ldb_dn_get_linearized(msg->dn),
3891 ldb_errstring(ldb));
3892 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
3895 for (i=0; i < ar->search_msg->num_elements; i++) {
3896 struct ldb_message_element *el = &ar->search_msg->elements[i];
3898 if (ar->isDeleted && strcmp(el->name, ar->isDeleted->lDAPDisplayName) == 0) {
3899 struct ldb_val *v = NULL;
3901 if (el->num_values == 0) {
3905 found_old_is_deleted = true;
3909 if (strncmp((const char*)v->data, "TRUE", 4) == 0) {
3910 old_is_deleted = true;
3914 if (ar->isRecycled && strcmp(el->name, ar->isRecycled->lDAPDisplayName) == 0) {
3915 struct ldb_val *v = NULL;
3917 if (el->num_values == 0) {
3921 found_old_is_recycled = true;
3925 if (strncmp((const char*)v->data, "TRUE", 4) == 0) {
3926 old_is_recycled = true;
3930 if (ar->lastKnownRDN && strcmp(el->name, ar->lastKnownRDN->lDAPDisplayName) == 0) {
3931 struct ldb_val *v = NULL;
3933 if (el->num_values == 0) {
3937 found_old_has_lastknownrdn = true;
3941 if (strncmp((const char*)v->data, "TRUE", 4) == 0) {
3942 old_has_lastknownrdn = true;
3949 nmd.ctr.ctr1.count = omd.ctr.ctr1.count + rmd->ctr.ctr1.count;
3950 nmd.ctr.ctr1.array = talloc_array(ar,
3951 struct replPropertyMetaData1,
3952 nmd.ctr.ctr1.count);
3953 if (!nmd.ctr.ctr1.array) return replmd_replicated_request_werror(ar, WERR_NOMEM);
3955 /* first copy the old meta data */
3956 for (i=0; i < omd.ctr.ctr1.count; i++) {
3957 nmd.ctr.ctr1.array[ni] = omd.ctr.ctr1.array[i];
3962 /* now merge in the new meta data */
3963 for (i=0; i < rmd->ctr.ctr1.count; i++) {
3966 for (j=0; j < ni; j++) {
3969 if (rmd->ctr.ctr1.array[i].attid != nmd.ctr.ctr1.array[j].attid) {
3973 if (ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING) {
3974 /* if we compare equal then do an
3975 update. This is used when a client
3976 asks for a FULL_SYNC, and can be
3977 used to recover a corrupt
3979 cmp = !replmd_replPropertyMetaData1_is_newer(&rmd->ctr.ctr1.array[i],
3980 &nmd.ctr.ctr1.array[j]);
3982 cmp = replmd_replPropertyMetaData1_is_newer(&nmd.ctr.ctr1.array[j],
3983 &rmd->ctr.ctr1.array[i]);
3986 /* replace the entry */
3987 nmd.ctr.ctr1.array[j] = rmd->ctr.ctr1.array[i];
3988 if (ar->seq_num == 0) {
3989 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
3990 if (ret != LDB_SUCCESS) {
3991 return replmd_replicated_request_error(ar, ret);
3994 nmd.ctr.ctr1.array[j].local_usn = ar->seq_num;
3999 if (rmd->ctr.ctr1.array[i].attid != DRSUAPI_ATTID_instanceType) {
4000 DEBUG(3,("Discarding older DRS attribute update to %s on %s from %s\n",
4001 msg->elements[i-removed_attrs].name,
4002 ldb_dn_get_linearized(msg->dn),
4003 GUID_string(ar, &rmd->ctr.ctr1.array[i].originating_invocation_id)));
4006 /* we don't want to apply this change so remove the attribute */
4007 ldb_msg_remove_element(msg, &msg->elements[i-removed_attrs]);
4014 if (found) continue;
4016 nmd.ctr.ctr1.array[ni] = rmd->ctr.ctr1.array[i];
4017 if (ar->seq_num == 0) {
4018 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
4019 if (ret != LDB_SUCCESS) {
4020 return replmd_replicated_request_error(ar, ret);
4023 nmd.ctr.ctr1.array[ni].local_usn = ar->seq_num;
4028 * finally correct the size of the meta_data array
4030 nmd.ctr.ctr1.count = ni;
4032 for (i=0; i < msg->num_elements; i++) {
4033 struct ldb_message_element *el = &msg->elements[i];
4035 if (ar->isDeleted && strcmp(el->name, ar->isDeleted->lDAPDisplayName) == 0) {
4036 struct ldb_val *v = NULL;
4038 if (el->num_values == 0) {
4042 found_new_is_deleted = true;
4046 if (strncmp((const char*)v->data, "TRUE", 4) == 0) {
4047 new_is_deleted = true;
4051 if (ar->isRecycled && strcmp(el->name, ar->isRecycled->lDAPDisplayName) == 0) {
4052 struct ldb_val *v = NULL;
4054 if (el->num_values == 0) {
4058 found_new_is_recycled = true;
4062 if (strncmp((const char*)v->data, "TRUE", 4) == 0) {
4063 new_is_recycled = true;
4067 if (ar->lastKnownRDN && strcmp(el->name, ar->lastKnownRDN->lDAPDisplayName) == 0) {
4068 struct ldb_val *v = NULL;
4070 if (el->num_values == 0) {
4074 found_new_has_lastknownrdn = true;
4078 if (strncmp((const char*)v->data, "TRUE", 4) == 0) {
4079 new_has_lastknownrdn = true;
4085 if (found_old_is_deleted) {
4086 is_deleted = old_is_deleted;
4088 if (found_new_is_deleted) {
4089 is_deleted = new_is_deleted;
4092 is_recycled = false;
4093 if (found_old_is_recycled) {
4094 is_recycled = old_is_recycled;
4096 if (found_new_is_recycled) {
4097 is_recycled = new_is_recycled;
4100 has_lastknownrdn = false;
4101 if (found_old_has_lastknownrdn) {
4102 has_lastknownrdn = old_has_lastknownrdn;
4104 if (found_new_has_lastknownrdn) {
4105 has_lastknownrdn = new_has_lastknownrdn;
4108 if (is_deleted && ar->isRecycled && !is_recycled && !has_lastknownrdn) {
4110 * The replicated attributes for the current object has the
4111 * isDeleted attribute but not the isRecycled and no the
4112 * lastKnownRDN. It means that we knew the object before and
4113 * now we are notified that is has been deleted but it's not a
4114 * recycled one. If we support the isRecycled attribute we had
4117 //TODO does this have impact on whenChanged?
4118 ret = replmd_add_isrecycled(ar, ldb, msg, &nmd);
4119 if (ret != LDB_SUCCESS) {
4125 * the rdn attribute (the alias for the name attribute),
4126 * 'cn' for most objects is the last entry in the meta data array
4129 * sort the new meta data array
4131 ret = replmd_replPropertyMetaDataCtr1_sort(&nmd.ctr.ctr1, ar->schema, msg->dn);
4132 if (ret != LDB_SUCCESS) {
4137 * check if some replicated attributes left, otherwise skip the ldb_modify() call
4139 if (msg->num_elements == 0) {
4140 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: skip replace\n",
4143 ar->index_current++;
4144 return replmd_replicated_apply_next(ar);
4147 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: replace %u attributes\n",
4148 ar->index_current, msg->num_elements);
4150 /* create the meta data value */
4151 ndr_err = ndr_push_struct_blob(&nmd_value, msg, &nmd,
4152 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
4153 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
4154 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
4155 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
4159 * when we know that we'll modify the record, add the whenChanged, uSNChanged
4160 * and replPopertyMetaData attributes
4162 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
4163 if (ret != LDB_SUCCESS) {
4164 return replmd_replicated_request_error(ar, ret);
4166 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
4167 if (ret != LDB_SUCCESS) {
4168 return replmd_replicated_request_error(ar, ret);
4170 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
4171 if (ret != LDB_SUCCESS) {
4172 return replmd_replicated_request_error(ar, ret);
4175 replmd_ldb_message_sort(msg, ar->schema);
4177 /* we want to replace the old values */
4178 for (i=0; i < msg->num_elements; i++) {
4179 msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
4183 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_MODIFY, msg);
4184 DEBUG(4, ("DRS replication modify message:\n%s\n", s));
4188 ret = ldb_build_mod_req(&change_req,
4196 LDB_REQ_SET_LOCATION(change_req);
4197 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
4199 /* current partition control needed by "repmd_op_callback" */
4200 ret = ldb_request_add_control(change_req,
4201 DSDB_CONTROL_CURRENT_PARTITION_OID,
4203 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
4205 return ldb_next_request(ar->module, change_req);
4208 static int replmd_replicated_apply_search_callback(struct ldb_request *req,
4209 struct ldb_reply *ares)
4211 struct replmd_replicated_request *ar = talloc_get_type(req->context,
4212 struct replmd_replicated_request);
4216 return ldb_module_done(ar->req, NULL, NULL,
4217 LDB_ERR_OPERATIONS_ERROR);
4219 if (ares->error != LDB_SUCCESS &&
4220 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
4221 return ldb_module_done(ar->req, ares->controls,
4222 ares->response, ares->error);
4225 switch (ares->type) {
4226 case LDB_REPLY_ENTRY:
4227 ar->search_msg = talloc_steal(ar, ares->message);
4230 case LDB_REPLY_REFERRAL:
4231 /* we ignore referrals */
4234 case LDB_REPLY_DONE:
4235 if (ar->search_msg != NULL) {
4236 ret = replmd_replicated_apply_merge(ar);
4238 ret = replmd_replicated_apply_add(ar);
4240 if (ret != LDB_SUCCESS) {
4241 return ldb_module_done(ar->req, NULL, NULL, ret);
4249 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar);
4251 static int replmd_replicated_apply_next(struct replmd_replicated_request *ar)
4253 struct ldb_context *ldb;
4257 struct ldb_request *search_req;
4258 struct ldb_search_options_control *options;
4260 if (ar->index_current >= ar->objs->num_objects) {
4261 /* done with it, go to next stage */
4262 return replmd_replicated_uptodate_vector(ar);
4265 ldb = ldb_module_get_ctx(ar->module);
4266 ar->search_msg = NULL;
4268 tmp_str = ldb_binary_encode(ar, ar->objs->objects[ar->index_current].guid_value);
4269 if (!tmp_str) return replmd_replicated_request_werror(ar, WERR_NOMEM);
4271 filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
4272 if (!filter) return replmd_replicated_request_werror(ar, WERR_NOMEM);
4273 talloc_free(tmp_str);
4275 ret = ldb_build_search_req(&search_req,
4284 replmd_replicated_apply_search_callback,
4286 LDB_REQ_SET_LOCATION(search_req);
4288 ret = ldb_request_add_control(search_req, LDB_CONTROL_SHOW_RECYCLED_OID,
4290 if (ret != LDB_SUCCESS) {
4294 /* we need to cope with cross-partition links, so search for
4295 the GUID over all partitions */
4296 options = talloc(search_req, struct ldb_search_options_control);
4297 if (options == NULL) {
4298 DEBUG(0, (__location__ ": out of memory\n"));
4299 return LDB_ERR_OPERATIONS_ERROR;
4301 options->search_options = LDB_SEARCH_OPTION_PHANTOM_ROOT;
4303 ret = ldb_request_add_control(search_req,
4304 LDB_CONTROL_SEARCH_OPTIONS_OID,
4306 if (ret != LDB_SUCCESS) {
4310 return ldb_next_request(ar->module, search_req);
4313 static int replmd_replicated_uptodate_modify_callback(struct ldb_request *req,
4314 struct ldb_reply *ares)
4316 struct ldb_context *ldb;
4317 struct replmd_replicated_request *ar = talloc_get_type(req->context,
4318 struct replmd_replicated_request);
4319 ldb = ldb_module_get_ctx(ar->module);
4322 return ldb_module_done(ar->req, NULL, NULL,
4323 LDB_ERR_OPERATIONS_ERROR);
4325 if (ares->error != LDB_SUCCESS) {
4326 return ldb_module_done(ar->req, ares->controls,
4327 ares->response, ares->error);
4330 if (ares->type != LDB_REPLY_DONE) {
4331 ldb_asprintf_errstring(ldb, "Invalid LDB reply type %d", ares->type);
4332 return ldb_module_done(ar->req, NULL, NULL,
4333 LDB_ERR_OPERATIONS_ERROR);
4338 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
4341 static int replmd_replicated_uptodate_modify(struct replmd_replicated_request *ar)
4343 struct ldb_context *ldb;
4344 struct ldb_request *change_req;
4345 enum ndr_err_code ndr_err;
4346 struct ldb_message *msg;
4347 struct replUpToDateVectorBlob ouv;
4348 const struct ldb_val *ouv_value;
4349 const struct drsuapi_DsReplicaCursor2CtrEx *ruv;
4350 struct replUpToDateVectorBlob nuv;
4351 struct ldb_val nuv_value;
4352 struct ldb_message_element *nuv_el = NULL;
4353 const struct GUID *our_invocation_id;
4354 struct ldb_message_element *orf_el = NULL;
4355 struct repsFromToBlob nrf;
4356 struct ldb_val *nrf_value = NULL;
4357 struct ldb_message_element *nrf_el = NULL;
4361 time_t t = time(NULL);
4364 uint32_t instanceType;
4366 ldb = ldb_module_get_ctx(ar->module);
4367 ruv = ar->objs->uptodateness_vector;
4373 unix_to_nt_time(&now, t);
4375 if (ar->search_msg == NULL) {
4376 /* this happens for a REPL_OBJ call where we are
4377 creating the target object by replicating it. The
4378 subdomain join code does this for the partition DN
4380 DEBUG(4,(__location__ ": Skipping UDV and repsFrom update as no target DN\n"));
4381 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
4384 instanceType = ldb_msg_find_attr_as_uint(ar->search_msg, "instanceType", 0);
4385 if (! (instanceType & INSTANCE_TYPE_IS_NC_HEAD)) {
4386 DEBUG(4,(__location__ ": Skipping UDV and repsFrom update as not NC root: %s\n",
4387 ldb_dn_get_linearized(ar->search_msg->dn)));
4388 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
4392 * first create the new replUpToDateVector
4394 ouv_value = ldb_msg_find_ldb_val(ar->search_msg, "replUpToDateVector");
4396 ndr_err = ndr_pull_struct_blob(ouv_value, ar, &ouv,
4397 (ndr_pull_flags_fn_t)ndr_pull_replUpToDateVectorBlob);
4398 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
4399 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
4400 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
4403 if (ouv.version != 2) {
4404 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
4409 * the new uptodateness vector will at least
4410 * contain 1 entry, one for the source_dsa
4412 * plus optional values from our old vector and the one from the source_dsa
4414 nuv.ctr.ctr2.count = 1 + ouv.ctr.ctr2.count;
4415 if (ruv) nuv.ctr.ctr2.count += ruv->count;
4416 nuv.ctr.ctr2.cursors = talloc_array(ar,
4417 struct drsuapi_DsReplicaCursor2,
4418 nuv.ctr.ctr2.count);
4419 if (!nuv.ctr.ctr2.cursors) return replmd_replicated_request_werror(ar, WERR_NOMEM);
4421 /* first copy the old vector */
4422 for (i=0; i < ouv.ctr.ctr2.count; i++) {
4423 nuv.ctr.ctr2.cursors[ni] = ouv.ctr.ctr2.cursors[i];
4427 /* get our invocation_id if we have one already attached to the ldb */
4428 our_invocation_id = samdb_ntds_invocation_id(ldb);
4430 /* merge in the source_dsa vector is available */
4431 for (i=0; (ruv && i < ruv->count); i++) {
4434 if (our_invocation_id &&
4435 GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
4436 our_invocation_id)) {
4440 for (j=0; j < ni; j++) {
4441 if (!GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
4442 &nuv.ctr.ctr2.cursors[j].source_dsa_invocation_id)) {
4449 * we update only the highest_usn and not the latest_sync_success time,
4450 * because the last success stands for direct replication
4452 if (ruv->cursors[i].highest_usn > nuv.ctr.ctr2.cursors[j].highest_usn) {
4453 nuv.ctr.ctr2.cursors[j].highest_usn = ruv->cursors[i].highest_usn;
4458 if (found) continue;
4460 /* if it's not there yet, add it */
4461 nuv.ctr.ctr2.cursors[ni] = ruv->cursors[i];
4466 * merge in the current highwatermark for the source_dsa
4469 for (j=0; j < ni; j++) {
4470 if (!GUID_equal(&ar->objs->source_dsa->source_dsa_invocation_id,
4471 &nuv.ctr.ctr2.cursors[j].source_dsa_invocation_id)) {
4478 * here we update the highest_usn and last_sync_success time
4479 * because we're directly replicating from the source_dsa
4481 * and use the tmp_highest_usn because this is what we have just applied
4484 nuv.ctr.ctr2.cursors[j].highest_usn = ar->objs->source_dsa->highwatermark.tmp_highest_usn;
4485 nuv.ctr.ctr2.cursors[j].last_sync_success = now;
4490 * here we update the highest_usn and last_sync_success time
4491 * because we're directly replicating from the source_dsa
4493 * and use the tmp_highest_usn because this is what we have just applied
4496 nuv.ctr.ctr2.cursors[ni].source_dsa_invocation_id= ar->objs->source_dsa->source_dsa_invocation_id;
4497 nuv.ctr.ctr2.cursors[ni].highest_usn = ar->objs->source_dsa->highwatermark.tmp_highest_usn;
4498 nuv.ctr.ctr2.cursors[ni].last_sync_success = now;
4503 * finally correct the size of the cursors array
4505 nuv.ctr.ctr2.count = ni;
4510 TYPESAFE_QSORT(nuv.ctr.ctr2.cursors, nuv.ctr.ctr2.count, drsuapi_DsReplicaCursor2_compare);
4513 * create the change ldb_message
4515 msg = ldb_msg_new(ar);
4516 if (!msg) return replmd_replicated_request_werror(ar, WERR_NOMEM);
4517 msg->dn = ar->search_msg->dn;
4519 ndr_err = ndr_push_struct_blob(&nuv_value, msg, &nuv,
4520 (ndr_push_flags_fn_t)ndr_push_replUpToDateVectorBlob);
4521 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
4522 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
4523 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
4525 ret = ldb_msg_add_value(msg, "replUpToDateVector", &nuv_value, &nuv_el);
4526 if (ret != LDB_SUCCESS) {
4527 return replmd_replicated_request_error(ar, ret);
4529 nuv_el->flags = LDB_FLAG_MOD_REPLACE;
4532 * now create the new repsFrom value from the given repsFromTo1 structure
4536 nrf.ctr.ctr1 = *ar->objs->source_dsa;
4537 nrf.ctr.ctr1.highwatermark.highest_usn = nrf.ctr.ctr1.highwatermark.tmp_highest_usn;
4540 * first see if we already have a repsFrom value for the current source dsa
4541 * if so we'll later replace this value
4543 orf_el = ldb_msg_find_element(ar->search_msg, "repsFrom");
4545 for (i=0; i < orf_el->num_values; i++) {
4546 struct repsFromToBlob *trf;
4548 trf = talloc(ar, struct repsFromToBlob);
4549 if (!trf) return replmd_replicated_request_werror(ar, WERR_NOMEM);
4551 ndr_err = ndr_pull_struct_blob(&orf_el->values[i], trf, trf,
4552 (ndr_pull_flags_fn_t)ndr_pull_repsFromToBlob);
4553 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
4554 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
4555 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
4558 if (trf->version != 1) {
4559 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
4563 * we compare the source dsa objectGUID not the invocation_id
4564 * because we want only one repsFrom value per source dsa
4565 * and when the invocation_id of the source dsa has changed we don't need
4566 * the old repsFrom with the old invocation_id
4568 if (!GUID_equal(&trf->ctr.ctr1.source_dsa_obj_guid,
4569 &ar->objs->source_dsa->source_dsa_obj_guid)) {
4575 nrf_value = &orf_el->values[i];
4580 * copy over all old values to the new ldb_message
4582 ret = ldb_msg_add_empty(msg, "repsFrom", 0, &nrf_el);
4583 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
4588 * if we haven't found an old repsFrom value for the current source dsa
4589 * we'll add a new value
4592 struct ldb_val zero_value;
4593 ZERO_STRUCT(zero_value);
4594 ret = ldb_msg_add_value(msg, "repsFrom", &zero_value, &nrf_el);
4595 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
4597 nrf_value = &nrf_el->values[nrf_el->num_values - 1];
4600 /* we now fill the value which is already attached to ldb_message */
4601 ndr_err = ndr_push_struct_blob(nrf_value, msg,
4603 (ndr_push_flags_fn_t)ndr_push_repsFromToBlob);
4604 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
4605 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
4606 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
4610 * the ldb_message_element for the attribute, has all the old values and the new one
4611 * so we'll replace the whole attribute with all values
4613 nrf_el->flags = LDB_FLAG_MOD_REPLACE;
4615 if (CHECK_DEBUGLVL(4)) {
4616 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_MODIFY, msg);
4617 DEBUG(4, ("DRS replication uptodate modify message:\n%s\n", s));
4621 /* prepare the ldb_modify() request */
4622 ret = ldb_build_mod_req(&change_req,
4628 replmd_replicated_uptodate_modify_callback,
4630 LDB_REQ_SET_LOCATION(change_req);
4631 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
4633 return ldb_next_request(ar->module, change_req);
4636 static int replmd_replicated_uptodate_search_callback(struct ldb_request *req,
4637 struct ldb_reply *ares)
4639 struct replmd_replicated_request *ar = talloc_get_type(req->context,
4640 struct replmd_replicated_request);
4644 return ldb_module_done(ar->req, NULL, NULL,
4645 LDB_ERR_OPERATIONS_ERROR);
4647 if (ares->error != LDB_SUCCESS &&
4648 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
4649 return ldb_module_done(ar->req, ares->controls,
4650 ares->response, ares->error);
4653 switch (ares->type) {
4654 case LDB_REPLY_ENTRY:
4655 ar->search_msg = talloc_steal(ar, ares->message);
4658 case LDB_REPLY_REFERRAL:
4659 /* we ignore referrals */
4662 case LDB_REPLY_DONE:
4663 ret = replmd_replicated_uptodate_modify(ar);
4664 if (ret != LDB_SUCCESS) {
4665 return ldb_module_done(ar->req, NULL, NULL, ret);
4674 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar)
4676 struct ldb_context *ldb;
4678 static const char *attrs[] = {
4679 "replUpToDateVector",
4684 struct ldb_request *search_req;
4686 ldb = ldb_module_get_ctx(ar->module);
4687 ar->search_msg = NULL;
4689 ret = ldb_build_search_req(&search_req,
4692 ar->objs->partition_dn,
4698 replmd_replicated_uptodate_search_callback,
4700 LDB_REQ_SET_LOCATION(search_req);
4701 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
4703 return ldb_next_request(ar->module, search_req);
4708 static int replmd_extended_replicated_objects(struct ldb_module *module, struct ldb_request *req)
4710 struct ldb_context *ldb;
4711 struct dsdb_extended_replicated_objects *objs;
4712 struct replmd_replicated_request *ar;
4713 struct ldb_control **ctrls;
4716 struct replmd_private *replmd_private =
4717 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
4718 struct dsdb_control_replicated_update *rep_update;
4720 ldb = ldb_module_get_ctx(module);
4722 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_extended_replicated_objects\n");
4724 objs = talloc_get_type(req->op.extended.data, struct dsdb_extended_replicated_objects);
4726 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: invalid extended data\n");
4727 return LDB_ERR_PROTOCOL_ERROR;
4730 if (objs->version != DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION) {
4731 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: extended data invalid version [%u != %u]\n",
4732 objs->version, DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION);
4733 return LDB_ERR_PROTOCOL_ERROR;
4736 ar = replmd_ctx_init(module, req);
4738 return LDB_ERR_OPERATIONS_ERROR;
4740 /* Set the flags to have the replmd_op_callback run over the full set of objects */
4741 ar->apply_mode = true;
4743 ar->schema = dsdb_get_schema(ldb, ar);
4745 ldb_debug_set(ldb, LDB_DEBUG_FATAL, "replmd_ctx_init: no loaded schema found\n");
4747 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
4748 return LDB_ERR_CONSTRAINT_VIOLATION;
4751 ctrls = req->controls;
4753 if (req->controls) {
4754 req->controls = talloc_memdup(ar, req->controls,
4755 talloc_get_size(req->controls));
4756 if (!req->controls) return replmd_replicated_request_werror(ar, WERR_NOMEM);
4759 /* This allows layers further down to know if a change came in
4760 over replication and what the replication flags were */
4761 rep_update = talloc_zero(ar, struct dsdb_control_replicated_update);
4762 if (rep_update == NULL) {
4763 return ldb_module_oom(module);
4765 rep_update->dsdb_repl_flags = objs->dsdb_repl_flags;
4767 ret = ldb_request_add_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID, false, rep_update);
4768 if (ret != LDB_SUCCESS) {
4772 /* If this change contained linked attributes in the body
4773 * (rather than in the links section) we need to update
4774 * backlinks in linked_attributes */
4775 ret = ldb_request_add_control(req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
4776 if (ret != LDB_SUCCESS) {
4780 ar->controls = req->controls;
4781 req->controls = ctrls;
4783 DEBUG(4,("linked_attributes_count=%u\n", objs->linked_attributes_count));
4785 /* save away the linked attributes for the end of the
4787 for (i=0; i<ar->objs->linked_attributes_count; i++) {
4788 struct la_entry *la_entry;
4790 if (replmd_private->la_ctx == NULL) {
4791 replmd_private->la_ctx = talloc_new(replmd_private);
4793 la_entry = talloc(replmd_private->la_ctx, struct la_entry);
4794 if (la_entry == NULL) {
4796 return LDB_ERR_OPERATIONS_ERROR;
4798 la_entry->la = talloc(la_entry, struct drsuapi_DsReplicaLinkedAttribute);
4799 if (la_entry->la == NULL) {
4800 talloc_free(la_entry);
4802 return LDB_ERR_OPERATIONS_ERROR;
4804 *la_entry->la = ar->objs->linked_attributes[i];
4806 /* we need to steal the non-scalars so they stay
4807 around until the end of the transaction */
4808 talloc_steal(la_entry->la, la_entry->la->identifier);
4809 talloc_steal(la_entry->la, la_entry->la->value.blob);
4811 DLIST_ADD(replmd_private->la_list, la_entry);
4814 return replmd_replicated_apply_next(ar);
4818 process one linked attribute structure
4820 static int replmd_process_linked_attribute(struct ldb_module *module,
4821 struct la_entry *la_entry,
4822 struct ldb_request *parent)
4824 struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
4825 struct ldb_context *ldb = ldb_module_get_ctx(module);
4826 struct ldb_message *msg;
4827 TALLOC_CTX *tmp_ctx = talloc_new(la_entry);
4828 const struct dsdb_schema *schema = dsdb_get_schema(ldb, tmp_ctx);
4830 const struct dsdb_attribute *attr;
4831 struct dsdb_dn *dsdb_dn;
4832 uint64_t seq_num = 0;
4833 struct ldb_message_element *old_el;
4835 time_t t = time(NULL);
4836 struct ldb_result *res;
4837 const char *attrs[2];
4838 struct parsed_dn *pdn_list, *pdn;
4839 struct GUID guid = GUID_zero();
4841 bool active = (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)?true:false;
4842 const struct GUID *our_invocation_id;
4845 linked_attributes[0]:
4846 &objs->linked_attributes[i]: struct drsuapi_DsReplicaLinkedAttribute
4848 identifier: struct drsuapi_DsReplicaObjectIdentifier
4849 __ndr_size : 0x0000003a (58)
4850 __ndr_size_sid : 0x00000000 (0)
4851 guid : 8e95b6a9-13dd-4158-89db-3220a5be5cc7
4853 __ndr_size_dn : 0x00000000 (0)
4855 attid : DRSUAPI_ATTID_member (0x1F)
4856 value: struct drsuapi_DsAttributeValue
4857 __ndr_size : 0x0000007e (126)
4859 blob : DATA_BLOB length=126
4860 flags : 0x00000001 (1)
4861 1: DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE
4862 originating_add_time : Wed Sep 2 22:20:01 2009 EST
4863 meta_data: struct drsuapi_DsReplicaMetaData
4864 version : 0x00000015 (21)
4865 originating_change_time : Wed Sep 2 23:39:07 2009 EST
4866 originating_invocation_id: 794640f3-18cf-40ee-a211-a93992b67a64
4867 originating_usn : 0x000000000001e19c (123292)
4869 (for cases where the link is to a normal DN)
4870 &target: struct drsuapi_DsReplicaObjectIdentifier3
4871 __ndr_size : 0x0000007e (126)
4872 __ndr_size_sid : 0x0000001c (28)
4873 guid : 7639e594-db75-4086-b0d4-67890ae46031
4874 sid : S-1-5-21-2848215498-2472035911-1947525656-19924
4875 __ndr_size_dn : 0x00000022 (34)
4876 dn : 'CN=UOne,OU=TestOU,DC=vsofs8,DC=com'
4879 /* find the attribute being modified */
4880 attr = dsdb_attribute_by_attributeID_id(schema, la->attid);
4882 DEBUG(0, (__location__ ": Unable to find attributeID 0x%x\n", la->attid));
4883 talloc_free(tmp_ctx);
4884 return LDB_ERR_OPERATIONS_ERROR;
4887 attrs[0] = attr->lDAPDisplayName;
4890 /* get the existing message from the db for the object with
4891 this GUID, returning attribute being modified. We will then
4892 use this msg as the basis for a modify call */
4893 ret = dsdb_module_search(module, tmp_ctx, &res, NULL, LDB_SCOPE_SUBTREE, attrs,
4894 DSDB_FLAG_NEXT_MODULE |
4895 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
4896 DSDB_SEARCH_SHOW_RECYCLED |
4897 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
4898 DSDB_SEARCH_REVEAL_INTERNALS,
4900 "objectGUID=%s", GUID_string(tmp_ctx, &la->identifier->guid));
4901 if (ret != LDB_SUCCESS) {
4902 talloc_free(tmp_ctx);
4905 if (res->count != 1) {
4906 ldb_asprintf_errstring(ldb, "DRS linked attribute for GUID %s - DN not found",
4907 GUID_string(tmp_ctx, &la->identifier->guid));
4908 talloc_free(tmp_ctx);
4909 return LDB_ERR_NO_SUCH_OBJECT;
4913 if (msg->num_elements == 0) {
4914 ret = ldb_msg_add_empty(msg, attr->lDAPDisplayName, LDB_FLAG_MOD_REPLACE, &old_el);
4915 if (ret != LDB_SUCCESS) {
4916 ldb_module_oom(module);
4917 talloc_free(tmp_ctx);
4918 return LDB_ERR_OPERATIONS_ERROR;
4921 old_el = &msg->elements[0];
4922 old_el->flags = LDB_FLAG_MOD_REPLACE;
4925 /* parse the existing links */
4926 ret = get_parsed_dns(module, tmp_ctx, old_el, &pdn_list, attr->syntax->ldap_oid, parent);
4927 if (ret != LDB_SUCCESS) {
4928 talloc_free(tmp_ctx);
4932 /* get our invocationId */
4933 our_invocation_id = samdb_ntds_invocation_id(ldb);
4934 if (!our_invocation_id) {
4935 ldb_debug_set(ldb, LDB_DEBUG_ERROR, __location__ ": unable to find invocationId\n");
4936 talloc_free(tmp_ctx);
4937 return LDB_ERR_OPERATIONS_ERROR;
4940 ret = replmd_check_upgrade_links(pdn_list, old_el->num_values, old_el, our_invocation_id);
4941 if (ret != LDB_SUCCESS) {
4942 talloc_free(tmp_ctx);
4946 status = dsdb_dn_la_from_blob(ldb, attr, schema, tmp_ctx, la->value.blob, &dsdb_dn);
4947 if (!W_ERROR_IS_OK(status)) {
4948 ldb_asprintf_errstring(ldb, "Failed to parsed linked attribute blob for %s on %s - %s\n",
4949 old_el->name, ldb_dn_get_linearized(msg->dn), win_errstr(status));
4950 return LDB_ERR_OPERATIONS_ERROR;
4953 ntstatus = dsdb_get_extended_dn_guid(dsdb_dn->dn, &guid, "GUID");
4954 if (!NT_STATUS_IS_OK(ntstatus) && active) {
4955 ldb_asprintf_errstring(ldb, "Failed to find GUID in linked attribute blob for %s on %s from %s",
4957 ldb_dn_get_linearized(dsdb_dn->dn),
4958 ldb_dn_get_linearized(msg->dn));
4959 return LDB_ERR_OPERATIONS_ERROR;
4962 /* re-resolve the DN by GUID, as the DRS server may give us an
4964 ret = dsdb_module_dn_by_guid(module, dsdb_dn, &guid, &dsdb_dn->dn, parent);
4965 if (ret != LDB_SUCCESS) {
4966 DEBUG(2,(__location__ ": WARNING: Failed to re-resolve GUID %s - using %s\n",
4967 GUID_string(tmp_ctx, &guid),
4968 ldb_dn_get_linearized(dsdb_dn->dn)));
4971 /* see if this link already exists */
4972 pdn = parsed_dn_find(pdn_list, old_el->num_values, &guid, dsdb_dn->dn);
4974 /* see if this update is newer than what we have already */
4975 struct GUID invocation_id = GUID_zero();
4976 uint32_t version = 0;
4977 uint32_t originating_usn = 0;
4978 NTTIME change_time = 0;
4979 uint32_t rmd_flags = dsdb_dn_rmd_flags(pdn->dsdb_dn->dn);
4981 dsdb_get_extended_dn_guid(pdn->dsdb_dn->dn, &invocation_id, "RMD_INVOCID");
4982 dsdb_get_extended_dn_uint32(pdn->dsdb_dn->dn, &version, "RMD_VERSION");
4983 dsdb_get_extended_dn_uint32(pdn->dsdb_dn->dn, &originating_usn, "RMD_ORIGINATING_USN");
4984 dsdb_get_extended_dn_nttime(pdn->dsdb_dn->dn, &change_time, "RMD_CHANGETIME");
4986 if (!replmd_update_is_newer(&invocation_id,
4987 &la->meta_data.originating_invocation_id,
4989 la->meta_data.version,
4991 la->meta_data.originating_change_time)) {
4992 DEBUG(3,("Discarding older DRS linked attribute update to %s on %s from %s\n",
4993 old_el->name, ldb_dn_get_linearized(msg->dn),
4994 GUID_string(tmp_ctx, &la->meta_data.originating_invocation_id)));
4995 talloc_free(tmp_ctx);
4999 /* get a seq_num for this change */
5000 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
5001 if (ret != LDB_SUCCESS) {
5002 talloc_free(tmp_ctx);
5006 if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
5007 /* remove the existing backlink */
5008 ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid, false, attr, false);
5009 if (ret != LDB_SUCCESS) {
5010 talloc_free(tmp_ctx);
5015 ret = replmd_update_la_val(tmp_ctx, pdn->v, dsdb_dn, pdn->dsdb_dn,
5016 &la->meta_data.originating_invocation_id,
5017 la->meta_data.originating_usn, seq_num,
5018 la->meta_data.originating_change_time,
5019 la->meta_data.version,
5021 if (ret != LDB_SUCCESS) {
5022 talloc_free(tmp_ctx);
5027 /* add the new backlink */
5028 ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid, true, attr, false);
5029 if (ret != LDB_SUCCESS) {
5030 talloc_free(tmp_ctx);
5035 /* get a seq_num for this change */
5036 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
5037 if (ret != LDB_SUCCESS) {
5038 talloc_free(tmp_ctx);
5042 old_el->values = talloc_realloc(msg->elements, old_el->values,
5043 struct ldb_val, old_el->num_values+1);
5044 if (!old_el->values) {
5045 ldb_module_oom(module);
5046 return LDB_ERR_OPERATIONS_ERROR;
5048 old_el->num_values++;
5050 ret = replmd_build_la_val(tmp_ctx, &old_el->values[old_el->num_values-1], dsdb_dn,
5051 &la->meta_data.originating_invocation_id,
5052 la->meta_data.originating_usn, seq_num,
5053 la->meta_data.originating_change_time,
5054 la->meta_data.version,
5055 (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)?false:true);
5056 if (ret != LDB_SUCCESS) {
5057 talloc_free(tmp_ctx);
5062 ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid,
5064 if (ret != LDB_SUCCESS) {
5065 talloc_free(tmp_ctx);
5071 /* we only change whenChanged and uSNChanged if the seq_num
5073 ret = add_time_element(msg, "whenChanged", t);
5074 if (ret != LDB_SUCCESS) {
5075 talloc_free(tmp_ctx);
5080 ret = add_uint64_element(ldb, msg, "uSNChanged", seq_num);
5081 if (ret != LDB_SUCCESS) {
5082 talloc_free(tmp_ctx);
5087 old_el = ldb_msg_find_element(msg, attr->lDAPDisplayName);
5088 if (old_el == NULL) {
5089 talloc_free(tmp_ctx);
5090 return ldb_operr(ldb);
5093 ret = dsdb_check_single_valued_link(attr, old_el);
5094 if (ret != LDB_SUCCESS) {
5095 talloc_free(tmp_ctx);
5099 old_el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
5101 ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE, parent);
5102 if (ret != LDB_SUCCESS) {
5103 ldb_debug(ldb, LDB_DEBUG_WARNING, "Failed to apply linked attribute change '%s'\n%s\n",
5105 ldb_ldif_message_string(ldb, tmp_ctx, LDB_CHANGETYPE_MODIFY, msg));
5106 talloc_free(tmp_ctx);
5110 talloc_free(tmp_ctx);
5115 static int replmd_extended(struct ldb_module *module, struct ldb_request *req)
5117 if (strcmp(req->op.extended.oid, DSDB_EXTENDED_REPLICATED_OBJECTS_OID) == 0) {
5118 return replmd_extended_replicated_objects(module, req);
5121 return ldb_next_request(module, req);
5126 we hook into the transaction operations to allow us to
5127 perform the linked attribute updates at the end of the whole
5128 transaction. This allows a forward linked attribute to be created
5129 before the object is created. During a vampire, w2k8 sends us linked
5130 attributes before the objects they are part of.
5132 static int replmd_start_transaction(struct ldb_module *module)
5134 /* create our private structure for this transaction */
5135 struct replmd_private *replmd_private = talloc_get_type(ldb_module_get_private(module),
5136 struct replmd_private);
5137 replmd_txn_cleanup(replmd_private);
5139 /* free any leftover mod_usn records from cancelled
5141 while (replmd_private->ncs) {
5142 struct nc_entry *e = replmd_private->ncs;
5143 DLIST_REMOVE(replmd_private->ncs, e);
5147 return ldb_next_start_trans(module);
5151 on prepare commit we loop over our queued la_context structures and
5154 static int replmd_prepare_commit(struct ldb_module *module)
5156 struct replmd_private *replmd_private =
5157 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
5158 struct la_entry *la, *prev;
5159 struct la_backlink *bl;
5162 /* walk the list backwards, to do the first entry first, as we
5163 * added the entries with DLIST_ADD() which puts them at the
5164 * start of the list */
5165 for (la = DLIST_TAIL(replmd_private->la_list); la; la=prev) {
5166 prev = DLIST_PREV(la);
5167 DLIST_REMOVE(replmd_private->la_list, la);
5168 ret = replmd_process_linked_attribute(module, la, NULL);
5169 if (ret != LDB_SUCCESS) {
5170 replmd_txn_cleanup(replmd_private);
5175 /* process our backlink list, creating and deleting backlinks
5177 for (bl=replmd_private->la_backlinks; bl; bl=bl->next) {
5178 ret = replmd_process_backlink(module, bl, NULL);
5179 if (ret != LDB_SUCCESS) {
5180 replmd_txn_cleanup(replmd_private);
5185 replmd_txn_cleanup(replmd_private);
5187 /* possibly change @REPLCHANGED */
5188 ret = replmd_notify_store(module, NULL);
5189 if (ret != LDB_SUCCESS) {
5193 return ldb_next_prepare_commit(module);
5196 static int replmd_del_transaction(struct ldb_module *module)
5198 struct replmd_private *replmd_private =
5199 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
5200 replmd_txn_cleanup(replmd_private);
5202 return ldb_next_del_trans(module);
5206 static const struct ldb_module_ops ldb_repl_meta_data_module_ops = {
5207 .name = "repl_meta_data",
5208 .init_context = replmd_init,
5210 .modify = replmd_modify,
5211 .rename = replmd_rename,
5212 .del = replmd_delete,
5213 .extended = replmd_extended,
5214 .start_transaction = replmd_start_transaction,
5215 .prepare_commit = replmd_prepare_commit,
5216 .del_transaction = replmd_del_transaction,
5219 int ldb_repl_meta_data_module_init(const char *version)
5221 LDB_MODULE_CHECK_VERSION(version);
5222 return ldb_register_module(&ldb_repl_meta_data_module_ops);