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;
92 enum urgent_situation {
93 REPL_URGENT_ON_CREATE = 1,
94 REPL_URGENT_ON_UPDATE = 2,
95 REPL_URGENT_ON_DELETE = 4
100 const char *update_name;
101 enum urgent_situation repl_situation;
102 } urgent_objects[] = {
103 {"nTDSDSA", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE)},
104 {"crossRef", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE)},
105 {"attributeSchema", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
106 {"classSchema", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
107 {"secret", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
108 {"rIDManager", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
112 /* Attributes looked for when updating or deleting, to check for a urgent replication needed */
113 static const char *urgent_attrs[] = {
116 "userAccountControl",
121 static bool replmd_check_urgent_objectclass(const struct ldb_message_element *objectclass_el,
122 enum urgent_situation situation)
125 for (i=0; urgent_objects[i].update_name; i++) {
127 if ((situation & urgent_objects[i].repl_situation) == 0) {
131 for (j=0; j<objectclass_el->num_values; j++) {
132 const struct ldb_val *v = &objectclass_el->values[j];
133 if (ldb_attr_cmp((const char *)v->data, urgent_objects[i].update_name) == 0) {
141 static bool replmd_check_urgent_attribute(const struct ldb_message_element *el)
143 if (ldb_attr_in_list(urgent_attrs, el->name)) {
150 static int replmd_replicated_apply_next(struct replmd_replicated_request *ar);
153 initialise the module
154 allocate the private structure and build the list
155 of partition DNs for use by replmd_notify()
157 static int replmd_init(struct ldb_module *module)
159 struct replmd_private *replmd_private;
160 struct ldb_context *ldb = ldb_module_get_ctx(module);
162 replmd_private = talloc_zero(module, struct replmd_private);
163 if (replmd_private == NULL) {
165 return LDB_ERR_OPERATIONS_ERROR;
167 ldb_module_set_private(module, replmd_private);
169 return ldb_next_init(module);
173 cleanup our per-transaction contexts
175 static void replmd_txn_cleanup(struct replmd_private *replmd_private)
177 talloc_free(replmd_private->la_ctx);
178 replmd_private->la_list = NULL;
179 replmd_private->la_ctx = NULL;
181 talloc_free(replmd_private->bl_ctx);
182 replmd_private->la_backlinks = NULL;
183 replmd_private->bl_ctx = NULL;
188 struct la_backlink *next, *prev;
189 const char *attr_name;
190 struct GUID forward_guid, target_guid;
195 process a backlinks we accumulated during a transaction, adding and
196 deleting the backlinks from the target objects
198 static int replmd_process_backlink(struct ldb_module *module, struct la_backlink *bl, struct ldb_request *parent)
200 struct ldb_dn *target_dn, *source_dn;
202 struct ldb_context *ldb = ldb_module_get_ctx(module);
203 struct ldb_message *msg;
204 TALLOC_CTX *tmp_ctx = talloc_new(bl);
210 - construct ldb_message
211 - either an add or a delete
213 ret = dsdb_module_dn_by_guid(module, tmp_ctx, &bl->target_guid, &target_dn, parent);
214 if (ret != LDB_SUCCESS) {
215 DEBUG(2,(__location__ ": WARNING: Failed to find target DN for linked attribute with GUID %s\n",
216 GUID_string(bl, &bl->target_guid)));
220 ret = dsdb_module_dn_by_guid(module, tmp_ctx, &bl->forward_guid, &source_dn, parent);
221 if (ret != LDB_SUCCESS) {
222 ldb_asprintf_errstring(ldb, "Failed to find source DN for linked attribute with GUID %s\n",
223 GUID_string(bl, &bl->forward_guid));
224 talloc_free(tmp_ctx);
228 msg = ldb_msg_new(tmp_ctx);
230 ldb_module_oom(module);
231 talloc_free(tmp_ctx);
232 return LDB_ERR_OPERATIONS_ERROR;
235 /* construct a ldb_message for adding/deleting the backlink */
237 dn_string = ldb_dn_get_extended_linearized(tmp_ctx, source_dn, 1);
239 ldb_module_oom(module);
240 talloc_free(tmp_ctx);
241 return LDB_ERR_OPERATIONS_ERROR;
243 ret = ldb_msg_add_steal_string(msg, bl->attr_name, dn_string);
244 if (ret != LDB_SUCCESS) {
245 talloc_free(tmp_ctx);
248 msg->elements[0].flags = bl->active?LDB_FLAG_MOD_ADD:LDB_FLAG_MOD_DELETE;
250 /* a backlink should never be single valued. Unfortunately the
251 exchange schema has a attribute
252 msExchBridgeheadedLocalConnectorsDNBL which is single
253 valued and a backlink. We need to cope with that by
254 ignoring the single value flag */
255 msg->elements[0].flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
257 ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE, parent);
258 if (ret == LDB_ERR_NO_SUCH_ATTRIBUTE && !bl->active) {
259 /* we allow LDB_ERR_NO_SUCH_ATTRIBUTE as success to
260 cope with possible corruption where the backlink has
261 already been removed */
262 DEBUG(3,("WARNING: backlink from %s already removed from %s - %s\n",
263 ldb_dn_get_linearized(target_dn),
264 ldb_dn_get_linearized(source_dn),
265 ldb_errstring(ldb)));
267 } else if (ret != LDB_SUCCESS) {
268 ldb_asprintf_errstring(ldb, "Failed to %s backlink from %s to %s - %s",
269 bl->active?"add":"remove",
270 ldb_dn_get_linearized(source_dn),
271 ldb_dn_get_linearized(target_dn),
273 talloc_free(tmp_ctx);
276 talloc_free(tmp_ctx);
281 add a backlink to the list of backlinks to add/delete in the prepare
284 static int replmd_add_backlink(struct ldb_module *module, const struct dsdb_schema *schema,
285 struct GUID *forward_guid, struct GUID *target_guid,
286 bool active, const struct dsdb_attribute *schema_attr, bool immediate)
288 const struct dsdb_attribute *target_attr;
289 struct la_backlink *bl;
290 struct replmd_private *replmd_private =
291 talloc_get_type_abort(ldb_module_get_private(module), struct replmd_private);
293 target_attr = dsdb_attribute_by_linkID(schema, schema_attr->linkID ^ 1);
296 * windows 2003 has a broken schema where the
297 * definition of msDS-IsDomainFor is missing (which is
298 * supposed to be the backlink of the
299 * msDS-HasDomainNCs attribute
304 /* see if its already in the list */
305 for (bl=replmd_private->la_backlinks; bl; bl=bl->next) {
306 if (GUID_equal(forward_guid, &bl->forward_guid) &&
307 GUID_equal(target_guid, &bl->target_guid) &&
308 (target_attr->lDAPDisplayName == bl->attr_name ||
309 strcmp(target_attr->lDAPDisplayName, bl->attr_name) == 0)) {
315 /* we found an existing one */
316 if (bl->active == active) {
319 DLIST_REMOVE(replmd_private->la_backlinks, bl);
324 if (replmd_private->bl_ctx == NULL) {
325 replmd_private->bl_ctx = talloc_new(replmd_private);
326 if (replmd_private->bl_ctx == NULL) {
327 ldb_module_oom(module);
328 return LDB_ERR_OPERATIONS_ERROR;
333 bl = talloc(replmd_private->bl_ctx, struct la_backlink);
335 ldb_module_oom(module);
336 return LDB_ERR_OPERATIONS_ERROR;
339 /* Ensure the schema does not go away before the bl->attr_name is used */
340 if (!talloc_reference(bl, schema)) {
342 ldb_module_oom(module);
343 return LDB_ERR_OPERATIONS_ERROR;
346 bl->attr_name = target_attr->lDAPDisplayName;
347 bl->forward_guid = *forward_guid;
348 bl->target_guid = *target_guid;
351 /* the caller may ask for this backlink to be processed
354 int ret = replmd_process_backlink(module, bl, NULL);
359 DLIST_ADD(replmd_private->la_backlinks, bl);
366 * Callback for most write operations in this module:
368 * notify the repl task that a object has changed. The notifies are
369 * gathered up in the replmd_private structure then written to the
370 * @REPLCHANGED object in each partition during the prepare_commit
372 static int replmd_op_callback(struct ldb_request *req, struct ldb_reply *ares)
375 struct replmd_replicated_request *ac =
376 talloc_get_type_abort(req->context, struct replmd_replicated_request);
377 struct replmd_private *replmd_private =
378 talloc_get_type_abort(ldb_module_get_private(ac->module), struct replmd_private);
379 struct nc_entry *modified_partition;
380 struct ldb_control *partition_ctrl;
381 const struct dsdb_control_current_partition *partition;
383 struct ldb_control **controls;
385 partition_ctrl = ldb_reply_get_control(ares, DSDB_CONTROL_CURRENT_PARTITION_OID);
387 controls = ares->controls;
388 if (ldb_request_get_control(ac->req,
389 DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
391 * Remove the current partition control from what we pass up
392 * the chain if it hasn't been requested manually.
394 controls = ldb_controls_except_specified(ares->controls, ares,
398 if (ares->error != LDB_SUCCESS) {
399 DEBUG(5,("%s failure. Error is: %s\n", __FUNCTION__, ldb_strerror(ares->error)));
400 return ldb_module_done(ac->req, controls,
401 ares->response, ares->error);
404 if (ares->type != LDB_REPLY_DONE) {
405 ldb_set_errstring(ldb_module_get_ctx(ac->module), "Invalid reply type for notify\n!");
406 return ldb_module_done(ac->req, NULL,
407 NULL, LDB_ERR_OPERATIONS_ERROR);
410 if (!partition_ctrl) {
411 ldb_set_errstring(ldb_module_get_ctx(ac->module),"No partition control on reply");
412 return ldb_module_done(ac->req, NULL,
413 NULL, LDB_ERR_OPERATIONS_ERROR);
416 partition = talloc_get_type_abort(partition_ctrl->data,
417 struct dsdb_control_current_partition);
419 if (ac->seq_num > 0) {
420 for (modified_partition = replmd_private->ncs; modified_partition;
421 modified_partition = modified_partition->next) {
422 if (ldb_dn_compare(modified_partition->dn, partition->dn) == 0) {
427 if (modified_partition == NULL) {
428 modified_partition = talloc_zero(replmd_private, struct nc_entry);
429 if (!modified_partition) {
430 ldb_oom(ldb_module_get_ctx(ac->module));
431 return ldb_module_done(ac->req, NULL,
432 NULL, LDB_ERR_OPERATIONS_ERROR);
434 modified_partition->dn = ldb_dn_copy(modified_partition, partition->dn);
435 if (!modified_partition->dn) {
436 ldb_oom(ldb_module_get_ctx(ac->module));
437 return ldb_module_done(ac->req, NULL,
438 NULL, LDB_ERR_OPERATIONS_ERROR);
440 DLIST_ADD(replmd_private->ncs, modified_partition);
443 if (ac->seq_num > modified_partition->mod_usn) {
444 modified_partition->mod_usn = ac->seq_num;
446 modified_partition->mod_usn_urgent = ac->seq_num;
451 if (ac->apply_mode) {
455 ret = replmd_replicated_apply_next(ac);
456 if (ret != LDB_SUCCESS) {
457 return ldb_module_done(ac->req, NULL, NULL, ret);
461 /* free the partition control container here, for the
462 * common path. Other cases will have it cleaned up
463 * eventually with the ares */
464 talloc_free(partition_ctrl);
465 return ldb_module_done(ac->req, controls,
466 ares->response, LDB_SUCCESS);
472 * update a @REPLCHANGED record in each partition if there have been
473 * any writes of replicated data in the partition
475 static int replmd_notify_store(struct ldb_module *module, struct ldb_request *parent)
477 struct replmd_private *replmd_private =
478 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
480 while (replmd_private->ncs) {
482 struct nc_entry *modified_partition = replmd_private->ncs;
484 ret = dsdb_module_save_partition_usn(module, modified_partition->dn,
485 modified_partition->mod_usn,
486 modified_partition->mod_usn_urgent, parent);
487 if (ret != LDB_SUCCESS) {
488 DEBUG(0,(__location__ ": Failed to save partition uSN for %s\n",
489 ldb_dn_get_linearized(modified_partition->dn)));
492 DLIST_REMOVE(replmd_private->ncs, modified_partition);
493 talloc_free(modified_partition);
501 created a replmd_replicated_request context
503 static struct replmd_replicated_request *replmd_ctx_init(struct ldb_module *module,
504 struct ldb_request *req)
506 struct ldb_context *ldb;
507 struct replmd_replicated_request *ac;
509 ldb = ldb_module_get_ctx(module);
511 ac = talloc_zero(req, struct replmd_replicated_request);
520 ac->schema = dsdb_get_schema(ldb, ac);
522 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
523 "replmd_modify: no dsdb_schema loaded");
524 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
532 add a time element to a record
534 static int add_time_element(struct ldb_message *msg, const char *attr, time_t t)
536 struct ldb_message_element *el;
540 if (ldb_msg_find_element(msg, attr) != NULL) {
544 s = ldb_timestring(msg, t);
546 return LDB_ERR_OPERATIONS_ERROR;
549 ret = ldb_msg_add_string(msg, attr, s);
550 if (ret != LDB_SUCCESS) {
554 el = ldb_msg_find_element(msg, attr);
555 /* always set as replace. This works because on add ops, the flag
557 el->flags = LDB_FLAG_MOD_REPLACE;
563 add a uint64_t element to a record
565 static int add_uint64_element(struct ldb_context *ldb, struct ldb_message *msg,
566 const char *attr, uint64_t v)
568 struct ldb_message_element *el;
571 if (ldb_msg_find_element(msg, attr) != NULL) {
575 ret = samdb_msg_add_uint64(ldb, msg, msg, attr, v);
576 if (ret != LDB_SUCCESS) {
580 el = ldb_msg_find_element(msg, attr);
581 /* always set as replace. This works because on add ops, the flag
583 el->flags = LDB_FLAG_MOD_REPLACE;
588 static int replmd_replPropertyMetaData1_attid_sort(const struct replPropertyMetaData1 *m1,
589 const struct replPropertyMetaData1 *m2,
590 const uint32_t *rdn_attid)
592 if (m1->attid == m2->attid) {
597 * the rdn attribute should be at the end!
598 * so we need to return a value greater than zero
599 * which means m1 is greater than m2
601 if (m1->attid == *rdn_attid) {
606 * the rdn attribute should be at the end!
607 * so we need to return a value less than zero
608 * which means m2 is greater than m1
610 if (m2->attid == *rdn_attid) {
614 return m1->attid > m2->attid ? 1 : -1;
617 static int replmd_replPropertyMetaDataCtr1_sort(struct replPropertyMetaDataCtr1 *ctr1,
618 const struct dsdb_schema *schema,
621 const char *rdn_name;
622 const struct dsdb_attribute *rdn_sa;
624 rdn_name = ldb_dn_get_rdn_name(dn);
626 DEBUG(0,(__location__ ": No rDN for %s?\n", ldb_dn_get_linearized(dn)));
627 return LDB_ERR_OPERATIONS_ERROR;
630 rdn_sa = dsdb_attribute_by_lDAPDisplayName(schema, rdn_name);
631 if (rdn_sa == NULL) {
632 DEBUG(0,(__location__ ": No sa found for rDN %s for %s\n", rdn_name, ldb_dn_get_linearized(dn)));
633 return LDB_ERR_OPERATIONS_ERROR;
636 DEBUG(6,("Sorting rpmd with attid exception %u rDN=%s DN=%s\n",
637 rdn_sa->attributeID_id, rdn_name, ldb_dn_get_linearized(dn)));
639 LDB_TYPESAFE_QSORT(ctr1->array, ctr1->count, &rdn_sa->attributeID_id, replmd_replPropertyMetaData1_attid_sort);
644 static int replmd_ldb_message_element_attid_sort(const struct ldb_message_element *e1,
645 const struct ldb_message_element *e2,
646 const struct dsdb_schema *schema)
648 const struct dsdb_attribute *a1;
649 const struct dsdb_attribute *a2;
652 * TODO: make this faster by caching the dsdb_attribute pointer
653 * on the ldb_messag_element
656 a1 = dsdb_attribute_by_lDAPDisplayName(schema, e1->name);
657 a2 = dsdb_attribute_by_lDAPDisplayName(schema, e2->name);
660 * TODO: remove this check, we should rely on e1 and e2 having valid attribute names
664 return strcasecmp(e1->name, e2->name);
666 if (a1->attributeID_id == a2->attributeID_id) {
669 return a1->attributeID_id > a2->attributeID_id ? 1 : -1;
672 static void replmd_ldb_message_sort(struct ldb_message *msg,
673 const struct dsdb_schema *schema)
675 LDB_TYPESAFE_QSORT(msg->elements, msg->num_elements, schema, replmd_ldb_message_element_attid_sort);
678 static int replmd_build_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
679 const struct GUID *invocation_id, uint64_t seq_num,
680 uint64_t local_usn, NTTIME nttime, uint32_t version, bool deleted);
684 fix up linked attributes in replmd_add.
685 This involves setting up the right meta-data in extended DN
686 components, and creating backlinks to the object
688 static int replmd_add_fix_la(struct ldb_module *module, struct ldb_message_element *el,
689 uint64_t seq_num, const struct GUID *invocationId, time_t t,
690 struct GUID *guid, const struct dsdb_attribute *sa, struct ldb_request *parent)
693 TALLOC_CTX *tmp_ctx = talloc_new(el->values);
694 struct ldb_context *ldb = ldb_module_get_ctx(module);
696 /* We will take a reference to the schema in replmd_add_backlink */
697 const struct dsdb_schema *schema = dsdb_get_schema(ldb, NULL);
700 unix_to_nt_time(&now, t);
702 for (i=0; i<el->num_values; i++) {
703 struct ldb_val *v = &el->values[i];
704 struct dsdb_dn *dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, v, sa->syntax->ldap_oid);
705 struct GUID target_guid;
709 /* note that the DN already has the extended
710 components from the extended_dn_store module */
711 status = dsdb_get_extended_dn_guid(dsdb_dn->dn, &target_guid, "GUID");
712 if (!NT_STATUS_IS_OK(status) || GUID_all_zero(&target_guid)) {
713 ret = dsdb_module_guid_by_dn(module, dsdb_dn->dn, &target_guid, parent);
714 if (ret != LDB_SUCCESS) {
715 talloc_free(tmp_ctx);
718 ret = dsdb_set_extended_dn_guid(dsdb_dn->dn, &target_guid, "GUID");
719 if (ret != LDB_SUCCESS) {
720 talloc_free(tmp_ctx);
725 ret = replmd_build_la_val(el->values, v, dsdb_dn, invocationId,
726 seq_num, seq_num, now, 0, false);
727 if (ret != LDB_SUCCESS) {
728 talloc_free(tmp_ctx);
732 ret = replmd_add_backlink(module, schema, guid, &target_guid, true, sa, false);
733 if (ret != LDB_SUCCESS) {
734 talloc_free(tmp_ctx);
739 talloc_free(tmp_ctx);
745 intercept add requests
747 static int replmd_add(struct ldb_module *module, struct ldb_request *req)
749 struct ldb_context *ldb;
750 struct ldb_control *control;
751 struct replmd_replicated_request *ac;
752 enum ndr_err_code ndr_err;
753 struct ldb_request *down_req;
754 struct ldb_message *msg;
755 const DATA_BLOB *guid_blob;
757 struct replPropertyMetaDataBlob nmd;
758 struct ldb_val nmd_value;
759 const struct GUID *our_invocation_id;
760 time_t t = time(NULL);
765 unsigned int functional_level;
767 bool allow_add_guid = false;
768 bool remove_current_guid = false;
769 bool is_urgent = false;
770 struct ldb_message_element *objectclass_el;
772 /* check if there's a show relax control (used by provision to say 'I know what I'm doing') */
773 control = ldb_request_get_control(req, LDB_CONTROL_RELAX_OID);
775 allow_add_guid = true;
778 /* do not manipulate our control entries */
779 if (ldb_dn_is_special(req->op.add.message->dn)) {
780 return ldb_next_request(module, req);
783 ldb = ldb_module_get_ctx(module);
785 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_add\n");
787 guid_blob = ldb_msg_find_ldb_val(req->op.add.message, "objectGUID");
788 if (guid_blob != NULL) {
789 if (!allow_add_guid) {
790 ldb_set_errstring(ldb,
791 "replmd_add: it's not allowed to add an object with objectGUID!");
792 return LDB_ERR_UNWILLING_TO_PERFORM;
794 NTSTATUS status = GUID_from_data_blob(guid_blob,&guid);
795 if (!NT_STATUS_IS_OK(status)) {
796 ldb_set_errstring(ldb,
797 "replmd_add: Unable to parse the 'objectGUID' as a GUID!");
798 return LDB_ERR_UNWILLING_TO_PERFORM;
800 /* we remove this attribute as it can be a string and
801 * will not be treated correctly and then we will re-add
802 * it later on in the good format */
803 remove_current_guid = true;
807 guid = GUID_random();
810 ac = replmd_ctx_init(module, req);
812 return ldb_module_oom(module);
815 functional_level = dsdb_functional_level(ldb);
817 /* Get a sequence number from the backend */
818 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ac->seq_num);
819 if (ret != LDB_SUCCESS) {
824 /* get our invocationId */
825 our_invocation_id = samdb_ntds_invocation_id(ldb);
826 if (!our_invocation_id) {
827 ldb_debug_set(ldb, LDB_DEBUG_ERROR,
828 "replmd_add: unable to find invocationId\n");
830 return LDB_ERR_OPERATIONS_ERROR;
833 /* we have to copy the message as the caller might have it as a const */
834 msg = ldb_msg_copy_shallow(ac, req->op.add.message);
838 return LDB_ERR_OPERATIONS_ERROR;
841 /* generated times */
842 unix_to_nt_time(&now, t);
843 time_str = ldb_timestring(msg, t);
847 return LDB_ERR_OPERATIONS_ERROR;
849 if (remove_current_guid) {
850 ldb_msg_remove_attr(msg,"objectGUID");
854 * remove autogenerated attributes
856 ldb_msg_remove_attr(msg, "whenCreated");
857 ldb_msg_remove_attr(msg, "whenChanged");
858 ldb_msg_remove_attr(msg, "uSNCreated");
859 ldb_msg_remove_attr(msg, "uSNChanged");
860 ldb_msg_remove_attr(msg, "replPropertyMetaData");
863 * readd replicated attributes
865 ret = ldb_msg_add_string(msg, "whenCreated", time_str);
866 if (ret != LDB_SUCCESS) {
872 /* build the replication meta_data */
875 nmd.ctr.ctr1.count = msg->num_elements;
876 nmd.ctr.ctr1.array = talloc_array(msg,
877 struct replPropertyMetaData1,
879 if (!nmd.ctr.ctr1.array) {
882 return LDB_ERR_OPERATIONS_ERROR;
885 for (i=0; i < msg->num_elements; i++) {
886 struct ldb_message_element *e = &msg->elements[i];
887 struct replPropertyMetaData1 *m = &nmd.ctr.ctr1.array[ni];
888 const struct dsdb_attribute *sa;
890 if (e->name[0] == '@') continue;
892 sa = dsdb_attribute_by_lDAPDisplayName(ac->schema, e->name);
894 ldb_debug_set(ldb, LDB_DEBUG_ERROR,
895 "replmd_add: attribute '%s' not defined in schema\n",
898 return LDB_ERR_NO_SUCH_ATTRIBUTE;
901 if ((sa->systemFlags & DS_FLAG_ATTR_NOT_REPLICATED) || (sa->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED)) {
902 /* if the attribute is not replicated (0x00000001)
903 * or constructed (0x00000004) it has no metadata
908 if (sa->linkID != 0 && functional_level > DS_DOMAIN_FUNCTION_2000) {
909 ret = replmd_add_fix_la(module, e, ac->seq_num, our_invocation_id, t, &guid, sa, req);
910 if (ret != LDB_SUCCESS) {
914 /* linked attributes are not stored in
915 replPropertyMetaData in FL above w2k */
919 m->attid = sa->attributeID_id;
921 if (m->attid == 0x20030) {
922 const struct ldb_val *rdn_val = ldb_dn_get_rdn_val(msg->dn);
925 if (rdn_val == NULL) {
928 return LDB_ERR_OPERATIONS_ERROR;
931 rdn = (const char*)rdn_val->data;
932 if (strcmp(rdn, "Deleted Objects") == 0) {
934 * Set the originating_change_time to 29/12/9999 at 23:59:59
935 * as specified in MS-ADTS 7.1.1.4.2 Deleted Objects Container
937 NTTIME deleted_obj_ts;
939 unix_to_nt_time(&deleted_obj_ts, DELETED_OBJECT_CONTAINER_CHANGE_TIME);
940 m->originating_change_time = deleted_obj_ts;
942 m->originating_change_time = now;
945 m->originating_change_time = now;
947 m->originating_invocation_id = *our_invocation_id;
948 m->originating_usn = ac->seq_num;
949 m->local_usn = ac->seq_num;
953 /* fix meta data count */
954 nmd.ctr.ctr1.count = ni;
957 * sort meta data array, and move the rdn attribute entry to the end
959 ret = replmd_replPropertyMetaDataCtr1_sort(&nmd.ctr.ctr1, ac->schema, msg->dn);
960 if (ret != LDB_SUCCESS) {
965 /* generated NDR encoded values */
966 ndr_err = ndr_push_struct_blob(&nmd_value, msg,
968 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
969 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
972 return LDB_ERR_OPERATIONS_ERROR;
976 * add the autogenerated values
978 ret = dsdb_msg_add_guid(msg, &guid, "objectGUID");
979 if (ret != LDB_SUCCESS) {
984 ret = ldb_msg_add_string(msg, "whenChanged", time_str);
985 if (ret != LDB_SUCCESS) {
990 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", ac->seq_num);
991 if (ret != LDB_SUCCESS) {
996 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ac->seq_num);
997 if (ret != LDB_SUCCESS) {
1002 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
1003 if (ret != LDB_SUCCESS) {
1010 * sort the attributes by attid before storing the object
1012 replmd_ldb_message_sort(msg, ac->schema);
1014 objectclass_el = ldb_msg_find_element(msg, "objectClass");
1015 is_urgent = replmd_check_urgent_objectclass(objectclass_el,
1016 REPL_URGENT_ON_CREATE);
1018 ac->is_urgent = is_urgent;
1019 ret = ldb_build_add_req(&down_req, ldb, ac,
1022 ac, replmd_op_callback,
1025 LDB_REQ_SET_LOCATION(down_req);
1026 if (ret != LDB_SUCCESS) {
1031 /* current partition control is needed by "replmd_op_callback" */
1032 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
1033 ret = ldb_request_add_control(down_req,
1034 DSDB_CONTROL_CURRENT_PARTITION_OID,
1036 if (ret != LDB_SUCCESS) {
1042 if (functional_level == DS_DOMAIN_FUNCTION_2000) {
1043 ret = ldb_request_add_control(down_req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
1044 if (ret != LDB_SUCCESS) {
1050 /* mark the control done */
1052 control->critical = 0;
1055 /* go on with the call chain */
1056 return ldb_next_request(module, down_req);
1061 * update the replPropertyMetaData for one element
1063 static int replmd_update_rpmd_element(struct ldb_context *ldb,
1064 struct ldb_message *msg,
1065 struct ldb_message_element *el,
1066 struct ldb_message_element *old_el,
1067 struct replPropertyMetaDataBlob *omd,
1068 const struct dsdb_schema *schema,
1070 const struct GUID *our_invocation_id,
1072 struct ldb_request *req)
1075 const struct dsdb_attribute *a;
1076 struct replPropertyMetaData1 *md1;
1078 a = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
1080 if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)) {
1081 /* allow this to make it possible for dbcheck
1082 to remove bad attributes */
1086 DEBUG(0,(__location__ ": Unable to find attribute %s in schema\n",
1088 return LDB_ERR_OPERATIONS_ERROR;
1091 if ((a->systemFlags & DS_FLAG_ATTR_NOT_REPLICATED) || (a->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED)) {
1095 /* if the attribute's value haven't changed then return LDB_SUCCESS */
1096 if (old_el != NULL && ldb_msg_element_compare(el, old_el) == 0) {
1097 if (!ldb_request_get_control(req, LDB_CONTROL_PROVISION_OID)) {
1099 * allow this to make it possible for dbcheck
1100 * to rebuild broken metadata
1106 for (i=0; i<omd->ctr.ctr1.count; i++) {
1107 if (a->attributeID_id == omd->ctr.ctr1.array[i].attid) break;
1110 if (a->linkID != 0 && dsdb_functional_level(ldb) > DS_DOMAIN_FUNCTION_2000) {
1111 /* linked attributes are not stored in
1112 replPropertyMetaData in FL above w2k, but we do
1113 raise the seqnum for the object */
1114 if (*seq_num == 0 &&
1115 ldb_sequence_number(ldb, LDB_SEQ_NEXT, seq_num) != LDB_SUCCESS) {
1116 return LDB_ERR_OPERATIONS_ERROR;
1121 if (i == omd->ctr.ctr1.count) {
1122 /* we need to add a new one */
1123 omd->ctr.ctr1.array = talloc_realloc(msg, omd->ctr.ctr1.array,
1124 struct replPropertyMetaData1, omd->ctr.ctr1.count+1);
1125 if (omd->ctr.ctr1.array == NULL) {
1127 return LDB_ERR_OPERATIONS_ERROR;
1129 omd->ctr.ctr1.count++;
1130 ZERO_STRUCT(omd->ctr.ctr1.array[i]);
1133 /* Get a new sequence number from the backend. We only do this
1134 * if we have a change that requires a new
1135 * replPropertyMetaData element
1137 if (*seq_num == 0) {
1138 int ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, seq_num);
1139 if (ret != LDB_SUCCESS) {
1140 return LDB_ERR_OPERATIONS_ERROR;
1144 md1 = &omd->ctr.ctr1.array[i];
1146 md1->attid = a->attributeID_id;
1147 if (md1->attid == 0x20030) {
1148 const struct ldb_val *rdn_val = ldb_dn_get_rdn_val(msg->dn);
1151 if (rdn_val == NULL) {
1153 return LDB_ERR_OPERATIONS_ERROR;
1156 rdn = (const char*)rdn_val->data;
1157 if (strcmp(rdn, "Deleted Objects") == 0) {
1159 * Set the originating_change_time to 29/12/9999 at 23:59:59
1160 * as specified in MS-ADTS 7.1.1.4.2 Deleted Objects Container
1162 NTTIME deleted_obj_ts;
1164 unix_to_nt_time(&deleted_obj_ts, DELETED_OBJECT_CONTAINER_CHANGE_TIME);
1165 md1->originating_change_time = deleted_obj_ts;
1167 md1->originating_change_time = now;
1170 md1->originating_change_time = now;
1172 md1->originating_invocation_id = *our_invocation_id;
1173 md1->originating_usn = *seq_num;
1174 md1->local_usn = *seq_num;
1179 static uint64_t find_max_local_usn(struct replPropertyMetaDataBlob omd)
1181 uint32_t count = omd.ctr.ctr1.count;
1184 for (i=0; i < count; i++) {
1185 struct replPropertyMetaData1 m = omd.ctr.ctr1.array[i];
1186 if (max < m.local_usn) {
1194 * update the replPropertyMetaData object each time we modify an
1195 * object. This is needed for DRS replication, as the merge on the
1196 * client is based on this object
1198 static int replmd_update_rpmd(struct ldb_module *module,
1199 const struct dsdb_schema *schema,
1200 struct ldb_request *req,
1201 const char * const *rename_attrs,
1202 struct ldb_message *msg, uint64_t *seq_num,
1204 bool *is_urgent, bool *rodc)
1206 const struct ldb_val *omd_value;
1207 enum ndr_err_code ndr_err;
1208 struct replPropertyMetaDataBlob omd;
1211 const struct GUID *our_invocation_id;
1213 const char * const *attrs = NULL;
1214 const char * const attrs1[] = { "replPropertyMetaData", "*", NULL };
1215 const char * const attrs2[] = { "uSNChanged", "objectClass", "instanceType", NULL };
1216 struct ldb_result *res;
1217 struct ldb_context *ldb;
1218 struct ldb_message_element *objectclass_el;
1219 enum urgent_situation situation;
1220 bool rmd_is_provided;
1223 attrs = rename_attrs;
1228 ldb = ldb_module_get_ctx(module);
1230 our_invocation_id = samdb_ntds_invocation_id(ldb);
1231 if (!our_invocation_id) {
1232 /* this happens during an initial vampire while
1233 updating the schema */
1234 DEBUG(5,("No invocationID - skipping replPropertyMetaData update\n"));
1238 unix_to_nt_time(&now, t);
1240 if (ldb_request_get_control(req, DSDB_CONTROL_CHANGEREPLMETADATA_OID)) {
1241 rmd_is_provided = true;
1243 rmd_is_provided = false;
1246 /* if isDeleted is present and is TRUE, then we consider we are deleting,
1247 * otherwise we consider we are updating */
1248 if (ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")) {
1249 situation = REPL_URGENT_ON_DELETE;
1250 } else if (rename_attrs) {
1251 situation = REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE;
1253 situation = REPL_URGENT_ON_UPDATE;
1256 if (rmd_is_provided) {
1257 /* In this case the change_replmetadata control was supplied */
1258 /* We check that it's the only attribute that is provided
1259 * (it's a rare case so it's better to keep the code simplier)
1260 * We also check that the highest local_usn is bigger than
1263 if( msg->num_elements != 1 ||
1264 strncmp(msg->elements[0].name,
1265 "replPropertyMetaData", 20) ) {
1266 DEBUG(0,(__location__ ": changereplmetada control called without "\
1267 "a specified replPropertyMetaData attribute or with others\n"));
1268 return LDB_ERR_OPERATIONS_ERROR;
1270 if (situation != REPL_URGENT_ON_UPDATE) {
1271 DEBUG(0,(__location__ ": changereplmetada control can't be called when deleting an object\n"));
1272 return LDB_ERR_OPERATIONS_ERROR;
1274 omd_value = ldb_msg_find_ldb_val(msg, "replPropertyMetaData");
1276 DEBUG(0,(__location__ ": replPropertyMetaData was not specified for Object %s\n",
1277 ldb_dn_get_linearized(msg->dn)));
1278 return LDB_ERR_OPERATIONS_ERROR;
1280 ndr_err = ndr_pull_struct_blob(omd_value, msg, &omd,
1281 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
1282 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1283 DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s\n",
1284 ldb_dn_get_linearized(msg->dn)));
1285 return LDB_ERR_OPERATIONS_ERROR;
1287 *seq_num = find_max_local_usn(omd);
1289 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attrs2,
1290 DSDB_FLAG_NEXT_MODULE |
1291 DSDB_SEARCH_SHOW_RECYCLED |
1292 DSDB_SEARCH_SHOW_EXTENDED_DN |
1293 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
1294 DSDB_SEARCH_REVEAL_INTERNALS, req);
1296 if (ret != LDB_SUCCESS) {
1300 objectclass_el = ldb_msg_find_element(res->msgs[0], "objectClass");
1301 if (is_urgent && replmd_check_urgent_objectclass(objectclass_el,
1306 db_seq = ldb_msg_find_attr_as_uint64(res->msgs[0], "uSNChanged", 0);
1307 if (*seq_num <= db_seq) {
1308 DEBUG(0,(__location__ ": changereplmetada control provided but max(local_usn)"\
1309 " is less or equal to uSNChanged (max = %lld uSNChanged = %lld)\n",
1310 (long long)*seq_num, (long long)db_seq));
1311 return LDB_ERR_OPERATIONS_ERROR;
1315 /* search for the existing replPropertyMetaDataBlob. We need
1316 * to use REVEAL and ask for DNs in storage format to support
1317 * the check for values being the same in
1318 * replmd_update_rpmd_element()
1320 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attrs,
1321 DSDB_FLAG_NEXT_MODULE |
1322 DSDB_SEARCH_SHOW_RECYCLED |
1323 DSDB_SEARCH_SHOW_EXTENDED_DN |
1324 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
1325 DSDB_SEARCH_REVEAL_INTERNALS, req);
1326 if (ret != LDB_SUCCESS) {
1330 objectclass_el = ldb_msg_find_element(res->msgs[0], "objectClass");
1331 if (is_urgent && replmd_check_urgent_objectclass(objectclass_el,
1336 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
1338 DEBUG(0,(__location__ ": Object %s does not have a replPropertyMetaData attribute\n",
1339 ldb_dn_get_linearized(msg->dn)));
1340 return LDB_ERR_OPERATIONS_ERROR;
1343 ndr_err = ndr_pull_struct_blob(omd_value, msg, &omd,
1344 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
1345 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1346 DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s\n",
1347 ldb_dn_get_linearized(msg->dn)));
1348 return LDB_ERR_OPERATIONS_ERROR;
1351 if (omd.version != 1) {
1352 DEBUG(0,(__location__ ": bad version %u in replPropertyMetaData for %s\n",
1353 omd.version, ldb_dn_get_linearized(msg->dn)));
1354 return LDB_ERR_OPERATIONS_ERROR;
1357 for (i=0; i<msg->num_elements; i++) {
1358 struct ldb_message_element *old_el;
1359 old_el = ldb_msg_find_element(res->msgs[0], msg->elements[i].name);
1360 ret = replmd_update_rpmd_element(ldb, msg, &msg->elements[i], old_el, &omd, schema, seq_num,
1361 our_invocation_id, now, req);
1362 if (ret != LDB_SUCCESS) {
1366 if (is_urgent && !*is_urgent && (situation == REPL_URGENT_ON_UPDATE)) {
1367 *is_urgent = replmd_check_urgent_attribute(&msg->elements[i]);
1373 * replmd_update_rpmd_element has done an update if the
1376 if (*seq_num != 0) {
1377 struct ldb_val *md_value;
1378 struct ldb_message_element *el;
1380 /*if we are RODC and this is a DRSR update then its ok*/
1381 if (!ldb_request_get_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID)) {
1382 unsigned instanceType;
1384 ret = samdb_rodc(ldb, rodc);
1385 if (ret != LDB_SUCCESS) {
1386 DEBUG(4, (__location__ ": unable to tell if we are an RODC\n"));
1388 ldb_set_errstring(ldb, "RODC modify is forbidden!");
1389 return LDB_ERR_REFERRAL;
1392 instanceType = ldb_msg_find_attr_as_uint(res->msgs[0], "instanceType", INSTANCE_TYPE_WRITE);
1393 if (!(instanceType & INSTANCE_TYPE_WRITE)) {
1394 return ldb_error(ldb, LDB_ERR_UNWILLING_TO_PERFORM,
1395 "cannot change replicated attribute on partial replica");
1399 md_value = talloc(msg, struct ldb_val);
1400 if (md_value == NULL) {
1402 return LDB_ERR_OPERATIONS_ERROR;
1405 ret = replmd_replPropertyMetaDataCtr1_sort(&omd.ctr.ctr1, schema, msg->dn);
1406 if (ret != LDB_SUCCESS) {
1410 ndr_err = ndr_push_struct_blob(md_value, msg, &omd,
1411 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
1412 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1413 DEBUG(0,(__location__ ": Failed to marshall replPropertyMetaData for %s\n",
1414 ldb_dn_get_linearized(msg->dn)));
1415 return LDB_ERR_OPERATIONS_ERROR;
1418 ret = ldb_msg_add_empty(msg, "replPropertyMetaData", LDB_FLAG_MOD_REPLACE, &el);
1419 if (ret != LDB_SUCCESS) {
1420 DEBUG(0,(__location__ ": Failed to add updated replPropertyMetaData %s\n",
1421 ldb_dn_get_linearized(msg->dn)));
1426 el->values = md_value;
1433 struct dsdb_dn *dsdb_dn;
1438 static int parsed_dn_compare(struct parsed_dn *pdn1, struct parsed_dn *pdn2)
1440 return GUID_compare(pdn1->guid, pdn2->guid);
1443 static struct parsed_dn *parsed_dn_find(struct parsed_dn *pdn,
1444 unsigned int count, struct GUID *guid,
1447 struct parsed_dn *ret;
1449 if (dn && GUID_all_zero(guid)) {
1450 /* when updating a link using DRS, we sometimes get a
1451 NULL GUID. We then need to try and match by DN */
1452 for (i=0; i<count; i++) {
1453 if (ldb_dn_compare(pdn[i].dsdb_dn->dn, dn) == 0) {
1454 dsdb_get_extended_dn_guid(pdn[i].dsdb_dn->dn, guid, "GUID");
1460 BINARY_ARRAY_SEARCH(pdn, count, guid, guid, GUID_compare, ret);
1465 get a series of message element values as an array of DNs and GUIDs
1466 the result is sorted by GUID
1468 static int get_parsed_dns(struct ldb_module *module, TALLOC_CTX *mem_ctx,
1469 struct ldb_message_element *el, struct parsed_dn **pdn,
1470 const char *ldap_oid, struct ldb_request *parent)
1473 struct ldb_context *ldb = ldb_module_get_ctx(module);
1480 (*pdn) = talloc_array(mem_ctx, struct parsed_dn, el->num_values);
1482 ldb_module_oom(module);
1483 return LDB_ERR_OPERATIONS_ERROR;
1486 for (i=0; i<el->num_values; i++) {
1487 struct ldb_val *v = &el->values[i];
1490 struct parsed_dn *p;
1494 p->dsdb_dn = dsdb_dn_parse(*pdn, ldb, v, ldap_oid);
1495 if (p->dsdb_dn == NULL) {
1496 return LDB_ERR_INVALID_DN_SYNTAX;
1499 dn = p->dsdb_dn->dn;
1501 p->guid = talloc(*pdn, struct GUID);
1502 if (p->guid == NULL) {
1503 ldb_module_oom(module);
1504 return LDB_ERR_OPERATIONS_ERROR;
1507 status = dsdb_get_extended_dn_guid(dn, p->guid, "GUID");
1508 if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
1509 /* we got a DN without a GUID - go find the GUID */
1510 int ret = dsdb_module_guid_by_dn(module, dn, p->guid, parent);
1511 if (ret != LDB_SUCCESS) {
1512 ldb_asprintf_errstring(ldb, "Unable to find GUID for DN %s\n",
1513 ldb_dn_get_linearized(dn));
1514 if (ret == LDB_ERR_NO_SUCH_OBJECT &&
1515 LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_DELETE &&
1516 ldb_attr_cmp(el->name, "member") == 0) {
1517 return LDB_ERR_UNWILLING_TO_PERFORM;
1521 ret = dsdb_set_extended_dn_guid(dn, p->guid, "GUID");
1522 if (ret != LDB_SUCCESS) {
1525 } else if (!NT_STATUS_IS_OK(status)) {
1526 return LDB_ERR_OPERATIONS_ERROR;
1529 /* keep a pointer to the original ldb_val */
1533 TYPESAFE_QSORT(*pdn, el->num_values, parsed_dn_compare);
1539 build a new extended DN, including all meta data fields
1541 RMD_FLAGS = DSDB_RMD_FLAG_* bits
1542 RMD_ADDTIME = originating_add_time
1543 RMD_INVOCID = originating_invocation_id
1544 RMD_CHANGETIME = originating_change_time
1545 RMD_ORIGINATING_USN = originating_usn
1546 RMD_LOCAL_USN = local_usn
1547 RMD_VERSION = version
1549 static int replmd_build_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
1550 const struct GUID *invocation_id, uint64_t seq_num,
1551 uint64_t local_usn, NTTIME nttime, uint32_t version, bool deleted)
1553 struct ldb_dn *dn = dsdb_dn->dn;
1554 const char *tstring, *usn_string, *flags_string;
1555 struct ldb_val tval;
1557 struct ldb_val usnv, local_usnv;
1558 struct ldb_val vers, flagsv;
1561 const char *dnstring;
1563 uint32_t rmd_flags = deleted?DSDB_RMD_FLAG_DELETED:0;
1565 tstring = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)nttime);
1567 return LDB_ERR_OPERATIONS_ERROR;
1569 tval = data_blob_string_const(tstring);
1571 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)seq_num);
1573 return LDB_ERR_OPERATIONS_ERROR;
1575 usnv = data_blob_string_const(usn_string);
1577 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)local_usn);
1579 return LDB_ERR_OPERATIONS_ERROR;
1581 local_usnv = data_blob_string_const(usn_string);
1583 vstring = talloc_asprintf(mem_ctx, "%lu", (unsigned long)version);
1585 return LDB_ERR_OPERATIONS_ERROR;
1587 vers = data_blob_string_const(vstring);
1589 status = GUID_to_ndr_blob(invocation_id, dn, &iid);
1590 if (!NT_STATUS_IS_OK(status)) {
1591 return LDB_ERR_OPERATIONS_ERROR;
1594 flags_string = talloc_asprintf(mem_ctx, "%u", rmd_flags);
1595 if (!flags_string) {
1596 return LDB_ERR_OPERATIONS_ERROR;
1598 flagsv = data_blob_string_const(flags_string);
1600 ret = ldb_dn_set_extended_component(dn, "RMD_FLAGS", &flagsv);
1601 if (ret != LDB_SUCCESS) return ret;
1602 ret = ldb_dn_set_extended_component(dn, "RMD_ADDTIME", &tval);
1603 if (ret != LDB_SUCCESS) return ret;
1604 ret = ldb_dn_set_extended_component(dn, "RMD_INVOCID", &iid);
1605 if (ret != LDB_SUCCESS) return ret;
1606 ret = ldb_dn_set_extended_component(dn, "RMD_CHANGETIME", &tval);
1607 if (ret != LDB_SUCCESS) return ret;
1608 ret = ldb_dn_set_extended_component(dn, "RMD_LOCAL_USN", &local_usnv);
1609 if (ret != LDB_SUCCESS) return ret;
1610 ret = ldb_dn_set_extended_component(dn, "RMD_ORIGINATING_USN", &usnv);
1611 if (ret != LDB_SUCCESS) return ret;
1612 ret = ldb_dn_set_extended_component(dn, "RMD_VERSION", &vers);
1613 if (ret != LDB_SUCCESS) return ret;
1615 dnstring = dsdb_dn_get_extended_linearized(mem_ctx, dsdb_dn, 1);
1616 if (dnstring == NULL) {
1617 return LDB_ERR_OPERATIONS_ERROR;
1619 *v = data_blob_string_const(dnstring);
1624 static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
1625 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
1626 uint64_t seq_num, uint64_t local_usn, NTTIME nttime,
1627 uint32_t version, bool deleted);
1630 check if any links need upgrading from w2k format
1632 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.
1634 static int replmd_check_upgrade_links(struct parsed_dn *dns, uint32_t count, struct ldb_message_element *parent_ctx, const struct GUID *invocation_id)
1637 for (i=0; i<count; i++) {
1642 status = dsdb_get_extended_dn_uint32(dns[i].dsdb_dn->dn, &version, "RMD_VERSION");
1643 if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
1647 /* it's an old one that needs upgrading */
1648 ret = replmd_update_la_val(parent_ctx->values, dns[i].v, dns[i].dsdb_dn, dns[i].dsdb_dn, invocation_id,
1650 if (ret != LDB_SUCCESS) {
1658 update an extended DN, including all meta data fields
1660 see replmd_build_la_val for value names
1662 static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
1663 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
1664 uint64_t seq_num, uint64_t local_usn, NTTIME nttime,
1665 uint32_t version, bool deleted)
1667 struct ldb_dn *dn = dsdb_dn->dn;
1668 const char *tstring, *usn_string, *flags_string;
1669 struct ldb_val tval;
1671 struct ldb_val usnv, local_usnv;
1672 struct ldb_val vers, flagsv;
1673 const struct ldb_val *old_addtime;
1674 uint32_t old_version;
1677 const char *dnstring;
1679 uint32_t rmd_flags = deleted?DSDB_RMD_FLAG_DELETED:0;
1681 tstring = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)nttime);
1683 return LDB_ERR_OPERATIONS_ERROR;
1685 tval = data_blob_string_const(tstring);
1687 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)seq_num);
1689 return LDB_ERR_OPERATIONS_ERROR;
1691 usnv = data_blob_string_const(usn_string);
1693 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)local_usn);
1695 return LDB_ERR_OPERATIONS_ERROR;
1697 local_usnv = data_blob_string_const(usn_string);
1699 status = GUID_to_ndr_blob(invocation_id, dn, &iid);
1700 if (!NT_STATUS_IS_OK(status)) {
1701 return LDB_ERR_OPERATIONS_ERROR;
1704 flags_string = talloc_asprintf(mem_ctx, "%u", rmd_flags);
1705 if (!flags_string) {
1706 return LDB_ERR_OPERATIONS_ERROR;
1708 flagsv = data_blob_string_const(flags_string);
1710 ret = ldb_dn_set_extended_component(dn, "RMD_FLAGS", &flagsv);
1711 if (ret != LDB_SUCCESS) return ret;
1713 /* get the ADDTIME from the original */
1714 old_addtime = ldb_dn_get_extended_component(old_dsdb_dn->dn, "RMD_ADDTIME");
1715 if (old_addtime == NULL) {
1716 old_addtime = &tval;
1718 if (dsdb_dn != old_dsdb_dn ||
1719 ldb_dn_get_extended_component(dn, "RMD_ADDTIME") == NULL) {
1720 ret = ldb_dn_set_extended_component(dn, "RMD_ADDTIME", old_addtime);
1721 if (ret != LDB_SUCCESS) return ret;
1724 /* use our invocation id */
1725 ret = ldb_dn_set_extended_component(dn, "RMD_INVOCID", &iid);
1726 if (ret != LDB_SUCCESS) return ret;
1728 /* changetime is the current time */
1729 ret = ldb_dn_set_extended_component(dn, "RMD_CHANGETIME", &tval);
1730 if (ret != LDB_SUCCESS) return ret;
1732 /* update the USN */
1733 ret = ldb_dn_set_extended_component(dn, "RMD_ORIGINATING_USN", &usnv);
1734 if (ret != LDB_SUCCESS) return ret;
1736 ret = ldb_dn_set_extended_component(dn, "RMD_LOCAL_USN", &local_usnv);
1737 if (ret != LDB_SUCCESS) return ret;
1739 /* increase the version by 1 */
1740 status = dsdb_get_extended_dn_uint32(old_dsdb_dn->dn, &old_version, "RMD_VERSION");
1741 if (NT_STATUS_IS_OK(status) && old_version >= version) {
1742 version = old_version+1;
1744 vstring = talloc_asprintf(dn, "%lu", (unsigned long)version);
1745 vers = data_blob_string_const(vstring);
1746 ret = ldb_dn_set_extended_component(dn, "RMD_VERSION", &vers);
1747 if (ret != LDB_SUCCESS) return ret;
1749 dnstring = dsdb_dn_get_extended_linearized(mem_ctx, dsdb_dn, 1);
1750 if (dnstring == NULL) {
1751 return LDB_ERR_OPERATIONS_ERROR;
1753 *v = data_blob_string_const(dnstring);
1759 handle adding a linked attribute
1761 static int replmd_modify_la_add(struct ldb_module *module,
1762 const struct dsdb_schema *schema,
1763 struct ldb_message *msg,
1764 struct ldb_message_element *el,
1765 struct ldb_message_element *old_el,
1766 const struct dsdb_attribute *schema_attr,
1769 struct GUID *msg_guid,
1770 struct ldb_request *parent)
1773 struct parsed_dn *dns, *old_dns;
1774 TALLOC_CTX *tmp_ctx = talloc_new(msg);
1776 struct ldb_val *new_values = NULL;
1777 unsigned int num_new_values = 0;
1778 unsigned old_num_values = old_el?old_el->num_values:0;
1779 const struct GUID *invocation_id;
1780 struct ldb_context *ldb = ldb_module_get_ctx(module);
1783 unix_to_nt_time(&now, t);
1785 ret = get_parsed_dns(module, tmp_ctx, el, &dns, schema_attr->syntax->ldap_oid, parent);
1786 if (ret != LDB_SUCCESS) {
1787 talloc_free(tmp_ctx);
1791 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns, schema_attr->syntax->ldap_oid, parent);
1792 if (ret != LDB_SUCCESS) {
1793 talloc_free(tmp_ctx);
1797 invocation_id = samdb_ntds_invocation_id(ldb);
1798 if (!invocation_id) {
1799 talloc_free(tmp_ctx);
1800 return LDB_ERR_OPERATIONS_ERROR;
1803 ret = replmd_check_upgrade_links(old_dns, old_num_values, old_el, invocation_id);
1804 if (ret != LDB_SUCCESS) {
1805 talloc_free(tmp_ctx);
1809 /* for each new value, see if it exists already with the same GUID */
1810 for (i=0; i<el->num_values; i++) {
1811 struct parsed_dn *p = parsed_dn_find(old_dns, old_num_values, dns[i].guid, NULL);
1813 /* this is a new linked attribute value */
1814 new_values = talloc_realloc(tmp_ctx, new_values, struct ldb_val, num_new_values+1);
1815 if (new_values == NULL) {
1816 ldb_module_oom(module);
1817 talloc_free(tmp_ctx);
1818 return LDB_ERR_OPERATIONS_ERROR;
1820 ret = replmd_build_la_val(new_values, &new_values[num_new_values], dns[i].dsdb_dn,
1821 invocation_id, seq_num, seq_num, now, 0, false);
1822 if (ret != LDB_SUCCESS) {
1823 talloc_free(tmp_ctx);
1828 /* this is only allowed if the GUID was
1829 previously deleted. */
1830 uint32_t rmd_flags = dsdb_dn_rmd_flags(p->dsdb_dn->dn);
1832 if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
1833 ldb_asprintf_errstring(ldb, "Attribute %s already exists for target GUID %s",
1834 el->name, GUID_string(tmp_ctx, p->guid));
1835 talloc_free(tmp_ctx);
1836 /* error codes for 'member' need to be
1838 if (ldb_attr_cmp(el->name, "member") == 0) {
1839 return LDB_ERR_ENTRY_ALREADY_EXISTS;
1841 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
1844 ret = replmd_update_la_val(old_el->values, p->v, dns[i].dsdb_dn, p->dsdb_dn,
1845 invocation_id, seq_num, seq_num, now, 0, false);
1846 if (ret != LDB_SUCCESS) {
1847 talloc_free(tmp_ctx);
1852 ret = replmd_add_backlink(module, schema, msg_guid, dns[i].guid, true, schema_attr, true);
1853 if (ret != LDB_SUCCESS) {
1854 talloc_free(tmp_ctx);
1859 /* add the new ones on to the end of the old values, constructing a new el->values */
1860 el->values = talloc_realloc(msg->elements, old_el?old_el->values:NULL,
1862 old_num_values+num_new_values);
1863 if (el->values == NULL) {
1864 ldb_module_oom(module);
1865 return LDB_ERR_OPERATIONS_ERROR;
1868 memcpy(&el->values[old_num_values], new_values, num_new_values*sizeof(struct ldb_val));
1869 el->num_values = old_num_values + num_new_values;
1871 talloc_steal(msg->elements, el->values);
1872 talloc_steal(el->values, new_values);
1874 talloc_free(tmp_ctx);
1876 /* we now tell the backend to replace all existing values
1877 with the one we have constructed */
1878 el->flags = LDB_FLAG_MOD_REPLACE;
1885 handle deleting all active linked attributes
1887 static int replmd_modify_la_delete(struct ldb_module *module,
1888 const struct dsdb_schema *schema,
1889 struct ldb_message *msg,
1890 struct ldb_message_element *el,
1891 struct ldb_message_element *old_el,
1892 const struct dsdb_attribute *schema_attr,
1895 struct GUID *msg_guid,
1896 struct ldb_request *parent)
1899 struct parsed_dn *dns, *old_dns;
1900 TALLOC_CTX *tmp_ctx = talloc_new(msg);
1902 const struct GUID *invocation_id;
1903 struct ldb_context *ldb = ldb_module_get_ctx(module);
1906 unix_to_nt_time(&now, t);
1908 /* check if there is nothing to delete */
1909 if ((!old_el || old_el->num_values == 0) &&
1910 el->num_values == 0) {
1914 if (!old_el || old_el->num_values == 0) {
1915 return LDB_ERR_NO_SUCH_ATTRIBUTE;
1918 ret = get_parsed_dns(module, tmp_ctx, el, &dns, schema_attr->syntax->ldap_oid, parent);
1919 if (ret != LDB_SUCCESS) {
1920 talloc_free(tmp_ctx);
1924 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns, schema_attr->syntax->ldap_oid, parent);
1925 if (ret != LDB_SUCCESS) {
1926 talloc_free(tmp_ctx);
1930 invocation_id = samdb_ntds_invocation_id(ldb);
1931 if (!invocation_id) {
1932 return LDB_ERR_OPERATIONS_ERROR;
1935 ret = replmd_check_upgrade_links(old_dns, old_el->num_values, old_el, invocation_id);
1936 if (ret != LDB_SUCCESS) {
1937 talloc_free(tmp_ctx);
1943 /* see if we are being asked to delete any links that
1944 don't exist or are already deleted */
1945 for (i=0; i<el->num_values; i++) {
1946 struct parsed_dn *p = &dns[i];
1947 struct parsed_dn *p2;
1950 p2 = parsed_dn_find(old_dns, old_el->num_values, p->guid, NULL);
1952 ldb_asprintf_errstring(ldb, "Attribute %s doesn't exist for target GUID %s",
1953 el->name, GUID_string(tmp_ctx, p->guid));
1954 if (ldb_attr_cmp(el->name, "member") == 0) {
1955 return LDB_ERR_UNWILLING_TO_PERFORM;
1957 return LDB_ERR_NO_SUCH_ATTRIBUTE;
1960 rmd_flags = dsdb_dn_rmd_flags(p2->dsdb_dn->dn);
1961 if (rmd_flags & DSDB_RMD_FLAG_DELETED) {
1962 ldb_asprintf_errstring(ldb, "Attribute %s already deleted 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;
1972 /* for each new value, see if it exists already with the same GUID
1973 if it is not already deleted and matches the delete list then delete it
1975 for (i=0; i<old_el->num_values; i++) {
1976 struct parsed_dn *p = &old_dns[i];
1979 if (el->num_values && parsed_dn_find(dns, el->num_values, p->guid, NULL) == NULL) {
1983 rmd_flags = dsdb_dn_rmd_flags(p->dsdb_dn->dn);
1984 if (rmd_flags & DSDB_RMD_FLAG_DELETED) continue;
1986 ret = replmd_update_la_val(old_el->values, p->v, p->dsdb_dn, p->dsdb_dn,
1987 invocation_id, seq_num, seq_num, now, 0, true);
1988 if (ret != LDB_SUCCESS) {
1989 talloc_free(tmp_ctx);
1993 ret = replmd_add_backlink(module, schema, msg_guid, old_dns[i].guid, false, schema_attr, true);
1994 if (ret != LDB_SUCCESS) {
1995 talloc_free(tmp_ctx);
2000 el->values = talloc_steal(msg->elements, old_el->values);
2001 el->num_values = old_el->num_values;
2003 talloc_free(tmp_ctx);
2005 /* we now tell the backend to replace all existing values
2006 with the one we have constructed */
2007 el->flags = LDB_FLAG_MOD_REPLACE;
2013 handle replacing a linked attribute
2015 static int replmd_modify_la_replace(struct ldb_module *module,
2016 const struct dsdb_schema *schema,
2017 struct ldb_message *msg,
2018 struct ldb_message_element *el,
2019 struct ldb_message_element *old_el,
2020 const struct dsdb_attribute *schema_attr,
2023 struct GUID *msg_guid,
2024 struct ldb_request *parent)
2027 struct parsed_dn *dns, *old_dns;
2028 TALLOC_CTX *tmp_ctx = talloc_new(msg);
2030 const struct GUID *invocation_id;
2031 struct ldb_context *ldb = ldb_module_get_ctx(module);
2032 struct ldb_val *new_values = NULL;
2033 unsigned int num_new_values = 0;
2034 unsigned int old_num_values = old_el?old_el->num_values:0;
2037 unix_to_nt_time(&now, t);
2039 /* check if there is nothing to replace */
2040 if ((!old_el || old_el->num_values == 0) &&
2041 el->num_values == 0) {
2045 ret = get_parsed_dns(module, tmp_ctx, el, &dns, schema_attr->syntax->ldap_oid, parent);
2046 if (ret != LDB_SUCCESS) {
2047 talloc_free(tmp_ctx);
2051 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns, schema_attr->syntax->ldap_oid, parent);
2052 if (ret != LDB_SUCCESS) {
2053 talloc_free(tmp_ctx);
2057 invocation_id = samdb_ntds_invocation_id(ldb);
2058 if (!invocation_id) {
2059 return LDB_ERR_OPERATIONS_ERROR;
2062 ret = replmd_check_upgrade_links(old_dns, old_num_values, old_el, invocation_id);
2063 if (ret != LDB_SUCCESS) {
2064 talloc_free(tmp_ctx);
2068 /* mark all the old ones as deleted */
2069 for (i=0; i<old_num_values; i++) {
2070 struct parsed_dn *old_p = &old_dns[i];
2071 struct parsed_dn *p;
2072 uint32_t rmd_flags = dsdb_dn_rmd_flags(old_p->dsdb_dn->dn);
2074 if (rmd_flags & DSDB_RMD_FLAG_DELETED) continue;
2076 ret = replmd_add_backlink(module, schema, msg_guid, old_dns[i].guid, false, schema_attr, false);
2077 if (ret != LDB_SUCCESS) {
2078 talloc_free(tmp_ctx);
2082 p = parsed_dn_find(dns, el->num_values, old_p->guid, NULL);
2084 /* we don't delete it if we are re-adding it */
2088 ret = replmd_update_la_val(old_el->values, old_p->v, old_p->dsdb_dn, old_p->dsdb_dn,
2089 invocation_id, seq_num, seq_num, now, 0, true);
2090 if (ret != LDB_SUCCESS) {
2091 talloc_free(tmp_ctx);
2096 /* for each new value, either update its meta-data, or add it
2099 for (i=0; i<el->num_values; i++) {
2100 struct parsed_dn *p = &dns[i], *old_p;
2103 (old_p = parsed_dn_find(old_dns,
2104 old_num_values, p->guid, NULL)) != NULL) {
2105 /* update in place */
2106 ret = replmd_update_la_val(old_el->values, old_p->v, p->dsdb_dn,
2107 old_p->dsdb_dn, invocation_id,
2108 seq_num, seq_num, now, 0, false);
2109 if (ret != LDB_SUCCESS) {
2110 talloc_free(tmp_ctx);
2115 new_values = talloc_realloc(tmp_ctx, new_values, struct ldb_val,
2117 if (new_values == NULL) {
2118 ldb_module_oom(module);
2119 talloc_free(tmp_ctx);
2120 return LDB_ERR_OPERATIONS_ERROR;
2122 ret = replmd_build_la_val(new_values, &new_values[num_new_values], dns[i].dsdb_dn,
2123 invocation_id, seq_num, seq_num, now, 0, false);
2124 if (ret != LDB_SUCCESS) {
2125 talloc_free(tmp_ctx);
2131 ret = replmd_add_backlink(module, schema, msg_guid, dns[i].guid, true, schema_attr, false);
2132 if (ret != LDB_SUCCESS) {
2133 talloc_free(tmp_ctx);
2138 /* add the new values to the end of old_el */
2139 if (num_new_values != 0) {
2140 el->values = talloc_realloc(msg->elements, old_el?old_el->values:NULL,
2141 struct ldb_val, old_num_values+num_new_values);
2142 if (el->values == NULL) {
2143 ldb_module_oom(module);
2144 return LDB_ERR_OPERATIONS_ERROR;
2146 memcpy(&el->values[old_num_values], &new_values[0],
2147 sizeof(struct ldb_val)*num_new_values);
2148 el->num_values = old_num_values + num_new_values;
2149 talloc_steal(msg->elements, new_values);
2151 el->values = old_el->values;
2152 el->num_values = old_el->num_values;
2153 talloc_steal(msg->elements, el->values);
2156 talloc_free(tmp_ctx);
2158 /* we now tell the backend to replace all existing values
2159 with the one we have constructed */
2160 el->flags = LDB_FLAG_MOD_REPLACE;
2167 handle linked attributes in modify requests
2169 static int replmd_modify_handle_linked_attribs(struct ldb_module *module,
2170 struct ldb_message *msg,
2171 uint64_t seq_num, time_t t,
2172 struct ldb_request *parent)
2174 struct ldb_result *res;
2177 struct ldb_context *ldb = ldb_module_get_ctx(module);
2178 struct ldb_message *old_msg;
2180 const struct dsdb_schema *schema;
2181 struct GUID old_guid;
2184 /* there the replmd_update_rpmd code has already
2185 * checked and saw that there are no linked
2190 if (dsdb_functional_level(ldb) == DS_DOMAIN_FUNCTION_2000) {
2191 /* don't do anything special for linked attributes */
2195 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, NULL,
2196 DSDB_FLAG_NEXT_MODULE |
2197 DSDB_SEARCH_SHOW_RECYCLED |
2198 DSDB_SEARCH_REVEAL_INTERNALS |
2199 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
2201 if (ret != LDB_SUCCESS) {
2204 schema = dsdb_get_schema(ldb, res);
2206 return LDB_ERR_OPERATIONS_ERROR;
2209 old_msg = res->msgs[0];
2211 old_guid = samdb_result_guid(old_msg, "objectGUID");
2213 for (i=0; i<msg->num_elements; i++) {
2214 struct ldb_message_element *el = &msg->elements[i];
2215 struct ldb_message_element *old_el, *new_el;
2216 const struct dsdb_attribute *schema_attr
2217 = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
2219 ldb_asprintf_errstring(ldb,
2220 "%s: attribute %s is not a valid attribute in schema",
2221 __FUNCTION__, el->name);
2222 return LDB_ERR_OBJECT_CLASS_VIOLATION;
2224 if (schema_attr->linkID == 0) {
2227 if ((schema_attr->linkID & 1) == 1) {
2228 if (parent && ldb_request_get_control(parent, DSDB_CONTROL_DBCHECK)) {
2231 /* Odd is for the target. Illegal to modify */
2232 ldb_asprintf_errstring(ldb,
2233 "attribute %s must not be modified directly, it is a linked attribute", el->name);
2234 return LDB_ERR_UNWILLING_TO_PERFORM;
2236 old_el = ldb_msg_find_element(old_msg, el->name);
2237 switch (el->flags & LDB_FLAG_MOD_MASK) {
2238 case LDB_FLAG_MOD_REPLACE:
2239 ret = replmd_modify_la_replace(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid, parent);
2241 case LDB_FLAG_MOD_DELETE:
2242 ret = replmd_modify_la_delete(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid, parent);
2244 case LDB_FLAG_MOD_ADD:
2245 ret = replmd_modify_la_add(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid, parent);
2248 ldb_asprintf_errstring(ldb,
2249 "invalid flags 0x%x for %s linked attribute",
2250 el->flags, el->name);
2251 return LDB_ERR_UNWILLING_TO_PERFORM;
2253 if (dsdb_check_single_valued_link(schema_attr, el) != LDB_SUCCESS) {
2254 ldb_asprintf_errstring(ldb,
2255 "Attribute %s is single valued but more than one value has been supplied",
2257 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
2259 el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
2264 if (ret != LDB_SUCCESS) {
2268 ldb_msg_remove_attr(old_msg, el->name);
2270 ldb_msg_add_empty(old_msg, el->name, 0, &new_el);
2271 new_el->num_values = el->num_values;
2272 new_el->values = talloc_steal(msg->elements, el->values);
2274 /* TODO: this relises a bit too heavily on the exact
2275 behaviour of ldb_msg_find_element and
2276 ldb_msg_remove_element */
2277 old_el = ldb_msg_find_element(msg, el->name);
2279 ldb_msg_remove_element(msg, old_el);
2290 static int replmd_modify(struct ldb_module *module, struct ldb_request *req)
2292 struct ldb_context *ldb;
2293 struct replmd_replicated_request *ac;
2294 struct ldb_request *down_req;
2295 struct ldb_message *msg;
2296 time_t t = time(NULL);
2298 bool is_urgent = false, rodc = false;
2299 unsigned int functional_level;
2300 const DATA_BLOB *guid_blob;
2302 /* do not manipulate our control entries */
2303 if (ldb_dn_is_special(req->op.mod.message->dn)) {
2304 return ldb_next_request(module, req);
2307 ldb = ldb_module_get_ctx(module);
2309 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_modify\n");
2311 guid_blob = ldb_msg_find_ldb_val(req->op.mod.message, "objectGUID");
2312 if ( guid_blob != NULL ) {
2313 ldb_set_errstring(ldb,
2314 "replmd_modify: it's not allowed to change the objectGUID!");
2315 return LDB_ERR_CONSTRAINT_VIOLATION;
2318 ac = replmd_ctx_init(module, req);
2320 return ldb_module_oom(module);
2323 functional_level = dsdb_functional_level(ldb);
2325 /* we have to copy the message as the caller might have it as a const */
2326 msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
2330 return LDB_ERR_OPERATIONS_ERROR;
2333 ldb_msg_remove_attr(msg, "whenChanged");
2334 ldb_msg_remove_attr(msg, "uSNChanged");
2336 ret = replmd_update_rpmd(module, ac->schema, req, NULL,
2337 msg, &ac->seq_num, t, &is_urgent, &rodc);
2338 if (rodc && (ret == LDB_ERR_REFERRAL)) {
2339 struct loadparm_context *lp_ctx;
2342 lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
2343 struct loadparm_context);
2345 referral = talloc_asprintf(req,
2347 lpcfg_dnsdomain(lp_ctx),
2348 ldb_dn_get_linearized(msg->dn));
2349 ret = ldb_module_send_referral(req, referral);
2354 if (ret != LDB_SUCCESS) {
2359 ret = replmd_modify_handle_linked_attribs(module, msg, ac->seq_num, t, req);
2360 if (ret != LDB_SUCCESS) {
2366 * - replace the old object with the newly constructed one
2369 ac->is_urgent = is_urgent;
2371 ret = ldb_build_mod_req(&down_req, ldb, ac,
2374 ac, replmd_op_callback,
2376 LDB_REQ_SET_LOCATION(down_req);
2377 if (ret != LDB_SUCCESS) {
2382 /* current partition control is needed by "replmd_op_callback" */
2383 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
2384 ret = ldb_request_add_control(down_req,
2385 DSDB_CONTROL_CURRENT_PARTITION_OID,
2387 if (ret != LDB_SUCCESS) {
2393 /* If we are in functional level 2000, then
2394 * replmd_modify_handle_linked_attribs will have done
2396 if (functional_level == DS_DOMAIN_FUNCTION_2000) {
2397 ret = ldb_request_add_control(down_req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
2398 if (ret != LDB_SUCCESS) {
2404 talloc_steal(down_req, msg);
2406 /* we only change whenChanged and uSNChanged if the seq_num
2408 if (ac->seq_num != 0) {
2409 ret = add_time_element(msg, "whenChanged", t);
2410 if (ret != LDB_SUCCESS) {
2416 ret = add_uint64_element(ldb, msg, "uSNChanged", ac->seq_num);
2417 if (ret != LDB_SUCCESS) {
2424 /* go on with the call chain */
2425 return ldb_next_request(module, down_req);
2428 static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares);
2431 handle a rename request
2433 On a rename we need to do an extra ldb_modify which sets the
2434 whenChanged and uSNChanged attributes. We do this in a callback after the success.
2436 static int replmd_rename(struct ldb_module *module, struct ldb_request *req)
2438 struct ldb_context *ldb;
2439 struct replmd_replicated_request *ac;
2441 struct ldb_request *down_req;
2443 /* do not manipulate our control entries */
2444 if (ldb_dn_is_special(req->op.mod.message->dn)) {
2445 return ldb_next_request(module, req);
2448 ldb = ldb_module_get_ctx(module);
2450 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_rename\n");
2452 ac = replmd_ctx_init(module, req);
2454 return ldb_module_oom(module);
2457 ret = ldb_build_rename_req(&down_req, ldb, ac,
2458 ac->req->op.rename.olddn,
2459 ac->req->op.rename.newdn,
2461 ac, replmd_rename_callback,
2463 LDB_REQ_SET_LOCATION(down_req);
2464 if (ret != LDB_SUCCESS) {
2469 /* go on with the call chain */
2470 return ldb_next_request(module, down_req);
2473 /* After the rename is compleated, update the whenchanged etc */
2474 static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares)
2476 struct ldb_context *ldb;
2477 struct replmd_replicated_request *ac;
2478 struct ldb_request *down_req;
2479 struct ldb_message *msg;
2480 const struct dsdb_attribute *rdn_attr;
2481 const char *rdn_name;
2482 const struct ldb_val *rdn_val;
2483 const char *attrs[5] = { NULL, };
2484 time_t t = time(NULL);
2486 bool is_urgent = false, rodc = false;
2488 ac = talloc_get_type(req->context, struct replmd_replicated_request);
2489 ldb = ldb_module_get_ctx(ac->module);
2491 if (ares->error != LDB_SUCCESS) {
2492 return ldb_module_done(ac->req, ares->controls,
2493 ares->response, ares->error);
2496 if (ares->type != LDB_REPLY_DONE) {
2497 ldb_set_errstring(ldb,
2498 "invalid ldb_reply_type in callback");
2500 return ldb_module_done(ac->req, NULL, NULL,
2501 LDB_ERR_OPERATIONS_ERROR);
2505 * - replace the old object with the newly constructed one
2508 msg = ldb_msg_new(ac);
2511 return LDB_ERR_OPERATIONS_ERROR;
2514 msg->dn = ac->req->op.rename.newdn;
2516 rdn_name = ldb_dn_get_rdn_name(msg->dn);
2517 if (rdn_name == NULL) {
2519 return ldb_module_done(ac->req, NULL, NULL,
2523 /* normalize the rdn attribute name */
2524 rdn_attr = dsdb_attribute_by_lDAPDisplayName(ac->schema, rdn_name);
2525 if (rdn_attr == NULL) {
2527 return ldb_module_done(ac->req, NULL, NULL,
2530 rdn_name = rdn_attr->lDAPDisplayName;
2532 rdn_val = ldb_dn_get_rdn_val(msg->dn);
2533 if (rdn_val == NULL) {
2535 return ldb_module_done(ac->req, NULL, NULL,
2539 if (ldb_msg_add_empty(msg, rdn_name, LDB_FLAG_MOD_REPLACE, NULL) != 0) {
2541 return ldb_module_done(ac->req, NULL, NULL,
2544 if (ldb_msg_add_value(msg, rdn_name, rdn_val, NULL) != 0) {
2546 return ldb_module_done(ac->req, NULL, NULL,
2549 if (ldb_msg_add_empty(msg, "name", LDB_FLAG_MOD_REPLACE, NULL) != 0) {
2551 return ldb_module_done(ac->req, NULL, NULL,
2554 if (ldb_msg_add_value(msg, "name", rdn_val, NULL) != 0) {
2556 return ldb_module_done(ac->req, NULL, NULL,
2561 * here we let replmd_update_rpmd() only search for
2562 * the existing "replPropertyMetaData" and rdn_name attributes.
2564 * We do not want the existing "name" attribute as
2565 * the "name" attribute needs to get the version
2566 * updated on rename even if the rdn value hasn't changed.
2568 * This is the diff of the meta data, for a moved user
2569 * on a w2k8r2 server:
2572 * -dn: CN=sdf df,CN=Users,DC=bla,DC=base
2573 * +dn: CN=sdf df,OU=TestOU,DC=bla,DC=base
2574 * replPropertyMetaData: NDR: struct replPropertyMetaDataBlob
2575 * version : 0x00000001 (1)
2576 * reserved : 0x00000000 (0)
2577 * @@ -66,11 +66,11 @@ replPropertyMetaData: NDR: struct re
2578 * local_usn : 0x00000000000037a5 (14245)
2579 * array: struct replPropertyMetaData1
2580 * attid : DRSUAPI_ATTID_name (0x90001)
2581 * - version : 0x00000001 (1)
2582 * - originating_change_time : Wed Feb 9 17:20:49 2011 CET
2583 * + version : 0x00000002 (2)
2584 * + originating_change_time : Wed Apr 6 15:21:01 2011 CEST
2585 * originating_invocation_id: 0d36ca05-5507-4e62-aca3-354bab0d39e1
2586 * - originating_usn : 0x00000000000037a5 (14245)
2587 * - local_usn : 0x00000000000037a5 (14245)
2588 * + originating_usn : 0x0000000000003834 (14388)
2589 * + local_usn : 0x0000000000003834 (14388)
2590 * array: struct replPropertyMetaData1
2591 * attid : DRSUAPI_ATTID_userAccountControl (0x90008)
2592 * version : 0x00000004 (4)
2594 attrs[0] = "replPropertyMetaData";
2595 attrs[1] = "objectClass";
2596 attrs[2] = "instanceType";
2597 attrs[3] = rdn_name;
2600 ret = replmd_update_rpmd(ac->module, ac->schema, req, attrs,
2601 msg, &ac->seq_num, t, &is_urgent, &rodc);
2602 if (rodc && (ret == LDB_ERR_REFERRAL)) {
2603 struct ldb_dn *olddn = ac->req->op.rename.olddn;
2604 struct loadparm_context *lp_ctx;
2607 lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
2608 struct loadparm_context);
2610 referral = talloc_asprintf(req,
2612 lpcfg_dnsdomain(lp_ctx),
2613 ldb_dn_get_linearized(olddn));
2614 ret = ldb_module_send_referral(req, referral);
2616 return ldb_module_done(req, NULL, NULL, ret);
2619 if (ret != LDB_SUCCESS) {
2621 return ldb_module_done(ac->req, NULL, NULL, ret);
2624 if (ac->seq_num == 0) {
2626 return ldb_module_done(ac->req, NULL, NULL,
2628 "internal error seq_num == 0"));
2630 ac->is_urgent = is_urgent;
2632 ret = ldb_build_mod_req(&down_req, ldb, ac,
2635 ac, replmd_op_callback,
2637 LDB_REQ_SET_LOCATION(down_req);
2638 if (ret != LDB_SUCCESS) {
2643 /* current partition control is needed by "replmd_op_callback" */
2644 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
2645 ret = ldb_request_add_control(down_req,
2646 DSDB_CONTROL_CURRENT_PARTITION_OID,
2648 if (ret != LDB_SUCCESS) {
2654 talloc_steal(down_req, msg);
2656 ret = add_time_element(msg, "whenChanged", t);
2657 if (ret != LDB_SUCCESS) {
2663 ret = add_uint64_element(ldb, msg, "uSNChanged", ac->seq_num);
2664 if (ret != LDB_SUCCESS) {
2670 /* go on with the call chain - do the modify after the rename */
2671 return ldb_next_request(ac->module, down_req);
2675 remove links from objects that point at this object when an object
2678 static int replmd_delete_remove_link(struct ldb_module *module,
2679 const struct dsdb_schema *schema,
2681 struct ldb_message_element *el,
2682 const struct dsdb_attribute *sa,
2683 struct ldb_request *parent)
2686 TALLOC_CTX *tmp_ctx = talloc_new(module);
2687 struct ldb_context *ldb = ldb_module_get_ctx(module);
2689 for (i=0; i<el->num_values; i++) {
2690 struct dsdb_dn *dsdb_dn;
2694 struct ldb_message *msg;
2695 const struct dsdb_attribute *target_attr;
2696 struct ldb_message_element *el2;
2697 struct ldb_val dn_val;
2699 if (dsdb_dn_is_deleted_val(&el->values[i])) {
2703 dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i], sa->syntax->ldap_oid);
2705 talloc_free(tmp_ctx);
2706 return LDB_ERR_OPERATIONS_ERROR;
2709 status = dsdb_get_extended_dn_guid(dsdb_dn->dn, &guid2, "GUID");
2710 if (!NT_STATUS_IS_OK(status)) {
2711 talloc_free(tmp_ctx);
2712 return LDB_ERR_OPERATIONS_ERROR;
2715 /* remove the link */
2716 msg = ldb_msg_new(tmp_ctx);
2718 ldb_module_oom(module);
2719 talloc_free(tmp_ctx);
2720 return LDB_ERR_OPERATIONS_ERROR;
2724 msg->dn = dsdb_dn->dn;
2726 target_attr = dsdb_attribute_by_linkID(schema, sa->linkID ^ 1);
2727 if (target_attr == NULL) {
2731 ret = ldb_msg_add_empty(msg, target_attr->lDAPDisplayName, LDB_FLAG_MOD_DELETE, &el2);
2732 if (ret != LDB_SUCCESS) {
2733 ldb_module_oom(module);
2734 talloc_free(tmp_ctx);
2735 return LDB_ERR_OPERATIONS_ERROR;
2737 dn_val = data_blob_string_const(ldb_dn_get_linearized(dn));
2738 el2->values = &dn_val;
2739 el2->num_values = 1;
2741 ret = dsdb_module_modify(module, msg, DSDB_FLAG_OWN_MODULE, parent);
2742 if (ret != LDB_SUCCESS) {
2743 talloc_free(tmp_ctx);
2747 talloc_free(tmp_ctx);
2753 handle update of replication meta data for deletion of objects
2755 This also handles the mapping of delete to a rename operation
2756 to allow deletes to be replicated.
2758 static int replmd_delete(struct ldb_module *module, struct ldb_request *req)
2760 int ret = LDB_ERR_OTHER;
2761 bool retb, disallow_move_on_delete;
2762 struct ldb_dn *old_dn, *new_dn;
2763 const char *rdn_name;
2764 const struct ldb_val *rdn_value, *new_rdn_value;
2766 struct ldb_context *ldb = ldb_module_get_ctx(module);
2767 const struct dsdb_schema *schema;
2768 struct ldb_message *msg, *old_msg;
2769 struct ldb_message_element *el;
2770 TALLOC_CTX *tmp_ctx;
2771 struct ldb_result *res, *parent_res;
2772 const char *preserved_attrs[] = {
2773 /* yes, this really is a hard coded list. See MS-ADTS
2774 section 3.1.1.5.5.1.1 */
2775 "nTSecurityDescriptor", "attributeID", "attributeSyntax", "dNReferenceUpdate", "dNSHostName",
2776 "flatName", "governsID", "groupType", "instanceType", "lDAPDisplayName", "legacyExchangeDN",
2777 "isDeleted", "isRecycled", "lastKnownParent", "msDS-LastKnownRDN", "mS-DS-CreatorSID",
2778 "mSMQOwnerID", "nCName", "objectClass", "distinguishedName", "objectGUID", "objectSid",
2779 "oMSyntax", "proxiedObjectName", "name", "replPropertyMetaData", "sAMAccountName",
2780 "securityIdentifier", "sIDHistory", "subClassOf", "systemFlags", "trustPartner", "trustDirection",
2781 "trustType", "trustAttributes", "userAccountControl", "uSNChanged", "uSNCreated", "whenCreated",
2782 "whenChanged", NULL};
2783 unsigned int i, el_count = 0;
2784 enum deletion_state { OBJECT_NOT_DELETED=1, OBJECT_DELETED=2, OBJECT_RECYCLED=3,
2785 OBJECT_TOMBSTONE=4, OBJECT_REMOVED=5 };
2786 enum deletion_state deletion_state, next_deletion_state;
2788 int functional_level;
2790 if (ldb_dn_is_special(req->op.del.dn)) {
2791 return ldb_next_request(module, req);
2794 tmp_ctx = talloc_new(ldb);
2797 return LDB_ERR_OPERATIONS_ERROR;
2800 schema = dsdb_get_schema(ldb, tmp_ctx);
2802 return LDB_ERR_OPERATIONS_ERROR;
2805 functional_level = dsdb_functional_level(ldb);
2807 old_dn = ldb_dn_copy(tmp_ctx, req->op.del.dn);
2809 /* we need the complete msg off disk, so we can work out which
2810 attributes need to be removed */
2811 ret = dsdb_module_search_dn(module, tmp_ctx, &res, old_dn, NULL,
2812 DSDB_FLAG_NEXT_MODULE |
2813 DSDB_SEARCH_SHOW_RECYCLED |
2814 DSDB_SEARCH_REVEAL_INTERNALS |
2815 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT, req);
2816 if (ret != LDB_SUCCESS) {
2817 talloc_free(tmp_ctx);
2820 old_msg = res->msgs[0];
2823 ret = dsdb_recyclebin_enabled(module, &enabled);
2824 if (ret != LDB_SUCCESS) {
2825 talloc_free(tmp_ctx);
2829 if (ldb_msg_check_string_attribute(old_msg, "isDeleted", "TRUE")) {
2831 deletion_state = OBJECT_TOMBSTONE;
2832 next_deletion_state = OBJECT_REMOVED;
2833 } else if (ldb_msg_check_string_attribute(old_msg, "isRecycled", "TRUE")) {
2834 deletion_state = OBJECT_RECYCLED;
2835 next_deletion_state = OBJECT_REMOVED;
2837 deletion_state = OBJECT_DELETED;
2838 next_deletion_state = OBJECT_RECYCLED;
2841 deletion_state = OBJECT_NOT_DELETED;
2843 next_deletion_state = OBJECT_DELETED;
2845 next_deletion_state = OBJECT_TOMBSTONE;
2849 if (next_deletion_state == OBJECT_REMOVED) {
2850 struct auth_session_info *session_info =
2851 (struct auth_session_info *)ldb_get_opaque(ldb, "sessionInfo");
2852 if (security_session_user_level(session_info, NULL) != SECURITY_SYSTEM) {
2853 ldb_asprintf_errstring(ldb, "Refusing to delete deleted object %s",
2854 ldb_dn_get_linearized(old_msg->dn));
2855 return LDB_ERR_UNWILLING_TO_PERFORM;
2858 /* it is already deleted - really remove it this time */
2859 talloc_free(tmp_ctx);
2860 return ldb_next_request(module, req);
2863 rdn_name = ldb_dn_get_rdn_name(old_dn);
2864 rdn_value = ldb_dn_get_rdn_val(old_dn);
2865 if ((rdn_name == NULL) || (rdn_value == NULL)) {
2866 talloc_free(tmp_ctx);
2867 return ldb_operr(ldb);
2870 msg = ldb_msg_new(tmp_ctx);
2872 ldb_module_oom(module);
2873 talloc_free(tmp_ctx);
2874 return LDB_ERR_OPERATIONS_ERROR;
2879 if (deletion_state == OBJECT_NOT_DELETED){
2880 /* consider the SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE flag */
2881 disallow_move_on_delete =
2882 (ldb_msg_find_attr_as_int(old_msg, "systemFlags", 0)
2883 & SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE);
2885 /* work out where we will be renaming this object to */
2886 if (!disallow_move_on_delete) {
2887 ret = dsdb_get_deleted_objects_dn(ldb, tmp_ctx, old_dn,
2889 if (ret != LDB_SUCCESS) {
2890 /* this is probably an attempted delete on a partition
2891 * that doesn't allow delete operations, such as the
2892 * schema partition */
2893 ldb_asprintf_errstring(ldb, "No Deleted Objects container for DN %s",
2894 ldb_dn_get_linearized(old_dn));
2895 talloc_free(tmp_ctx);
2896 return LDB_ERR_UNWILLING_TO_PERFORM;
2899 new_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
2900 if (new_dn == NULL) {
2901 ldb_module_oom(module);
2902 talloc_free(tmp_ctx);
2903 return LDB_ERR_OPERATIONS_ERROR;
2907 /* get the objects GUID from the search we just did */
2908 guid = samdb_result_guid(old_msg, "objectGUID");
2910 /* Add a formatted child */
2911 retb = ldb_dn_add_child_fmt(new_dn, "%s=%s\\0ADEL:%s",
2913 ldb_dn_escape_value(tmp_ctx, *rdn_value),
2914 GUID_string(tmp_ctx, &guid));
2916 DEBUG(0,(__location__ ": Unable to add a formatted child to dn: %s",
2917 ldb_dn_get_linearized(new_dn)));
2918 talloc_free(tmp_ctx);
2919 return LDB_ERR_OPERATIONS_ERROR;
2922 ret = ldb_msg_add_string(msg, "isDeleted", "TRUE");
2923 if (ret != LDB_SUCCESS) {
2924 DEBUG(0,(__location__ ": Failed to add isDeleted string to the msg\n"));
2925 ldb_module_oom(module);
2926 talloc_free(tmp_ctx);
2929 msg->elements[el_count++].flags = LDB_FLAG_MOD_REPLACE;
2933 now we need to modify the object in the following ways:
2935 - add isDeleted=TRUE
2936 - update rDN and name, with new rDN
2937 - remove linked attributes
2938 - remove objectCategory and sAMAccountType
2939 - remove attribs not on the preserved list
2940 - preserved if in above list, or is rDN
2941 - remove all linked attribs from this object
2942 - remove all links from other objects to this object
2943 - add lastKnownParent
2944 - update replPropertyMetaData?
2946 see MS-ADTS "Tombstone Requirements" section 3.1.1.5.5.1.1
2949 /* we need the storage form of the parent GUID */
2950 ret = dsdb_module_search_dn(module, tmp_ctx, &parent_res,
2951 ldb_dn_get_parent(tmp_ctx, old_dn), NULL,
2952 DSDB_FLAG_NEXT_MODULE |
2953 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
2954 DSDB_SEARCH_REVEAL_INTERNALS|
2955 DSDB_SEARCH_SHOW_RECYCLED, req);
2956 if (ret != LDB_SUCCESS) {
2957 talloc_free(tmp_ctx);
2961 if (deletion_state == OBJECT_NOT_DELETED){
2962 ret = ldb_msg_add_steal_string(msg, "lastKnownParent",
2963 ldb_dn_get_extended_linearized(tmp_ctx, parent_res->msgs[0]->dn, 1));
2964 if (ret != LDB_SUCCESS) {
2965 DEBUG(0,(__location__ ": Failed to add lastKnownParent string to the msg\n"));
2966 ldb_module_oom(module);
2967 talloc_free(tmp_ctx);
2970 msg->elements[el_count++].flags = LDB_FLAG_MOD_ADD;
2973 switch (next_deletion_state){
2975 case OBJECT_DELETED:
2977 ret = ldb_msg_add_value(msg, "msDS-LastKnownRDN", rdn_value, NULL);
2978 if (ret != LDB_SUCCESS) {
2979 DEBUG(0,(__location__ ": Failed to add msDS-LastKnownRDN string to the msg\n"));
2980 ldb_module_oom(module);
2981 talloc_free(tmp_ctx);
2984 msg->elements[el_count++].flags = LDB_FLAG_MOD_ADD;
2986 ret = ldb_msg_add_empty(msg, "objectCategory", LDB_FLAG_MOD_REPLACE, NULL);
2987 if (ret != LDB_SUCCESS) {
2988 talloc_free(tmp_ctx);
2989 ldb_module_oom(module);
2993 ret = ldb_msg_add_empty(msg, "sAMAccountType", LDB_FLAG_MOD_REPLACE, NULL);
2994 if (ret != LDB_SUCCESS) {
2995 talloc_free(tmp_ctx);
2996 ldb_module_oom(module);
3002 case OBJECT_RECYCLED:
3003 case OBJECT_TOMBSTONE:
3005 /* we also mark it as recycled, meaning this object can't be
3006 recovered (we are stripping its attributes) */
3007 if (functional_level >= DS_DOMAIN_FUNCTION_2008_R2) {
3008 ret = ldb_msg_add_string(msg, "isRecycled", "TRUE");
3009 if (ret != LDB_SUCCESS) {
3010 DEBUG(0,(__location__ ": Failed to add isRecycled string to the msg\n"));
3011 ldb_module_oom(module);
3012 talloc_free(tmp_ctx);
3015 msg->elements[el_count++].flags = LDB_FLAG_MOD_ADD;
3018 /* work out which of the old attributes we will be removing */
3019 for (i=0; i<old_msg->num_elements; i++) {
3020 const struct dsdb_attribute *sa;
3021 el = &old_msg->elements[i];
3022 sa = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
3024 talloc_free(tmp_ctx);
3025 return LDB_ERR_OPERATIONS_ERROR;
3027 if (ldb_attr_cmp(el->name, rdn_name) == 0) {
3028 /* don't remove the rDN */
3031 if (sa->linkID && (sa->linkID & 1)) {
3033 we have a backlink in this object
3034 that needs to be removed. We're not
3035 allowed to remove it directly
3036 however, so we instead setup a
3037 modify to delete the corresponding
3040 ret = replmd_delete_remove_link(module, schema, old_dn, el, sa, req);
3041 if (ret != LDB_SUCCESS) {
3042 talloc_free(tmp_ctx);
3043 return LDB_ERR_OPERATIONS_ERROR;
3045 /* now we continue, which means we
3046 won't remove this backlink
3051 if (!sa->linkID && ldb_attr_in_list(preserved_attrs, el->name)) {
3054 ret = ldb_msg_add_empty(msg, el->name, LDB_FLAG_MOD_DELETE, &el);
3055 if (ret != LDB_SUCCESS) {
3056 talloc_free(tmp_ctx);
3057 ldb_module_oom(module);
3067 if (deletion_state == OBJECT_NOT_DELETED) {
3068 const struct dsdb_attribute *sa;
3070 /* work out what the new rdn value is, for updating the
3071 rDN and name fields */
3072 new_rdn_value = ldb_dn_get_rdn_val(new_dn);
3073 if (new_rdn_value == NULL) {
3074 talloc_free(tmp_ctx);
3075 return ldb_operr(ldb);
3078 sa = dsdb_attribute_by_lDAPDisplayName(schema, rdn_name);
3080 talloc_free(tmp_ctx);
3081 return LDB_ERR_OPERATIONS_ERROR;
3084 ret = ldb_msg_add_value(msg, sa->lDAPDisplayName, new_rdn_value,
3086 if (ret != LDB_SUCCESS) {
3087 talloc_free(tmp_ctx);
3090 el->flags = LDB_FLAG_MOD_REPLACE;
3092 el = ldb_msg_find_element(old_msg, "name");
3094 ret = ldb_msg_add_value(msg, "name", new_rdn_value, &el);
3095 if (ret != LDB_SUCCESS) {
3096 talloc_free(tmp_ctx);
3099 el->flags = LDB_FLAG_MOD_REPLACE;
3103 ret = dsdb_module_modify(module, msg, DSDB_FLAG_OWN_MODULE, req);
3104 if (ret != LDB_SUCCESS) {
3105 ldb_asprintf_errstring(ldb, "replmd_delete: Failed to modify object %s in delete - %s",
3106 ldb_dn_get_linearized(old_dn), ldb_errstring(ldb));
3107 talloc_free(tmp_ctx);
3111 if (deletion_state == OBJECT_NOT_DELETED) {
3112 /* now rename onto the new DN */
3113 ret = dsdb_module_rename(module, old_dn, new_dn, DSDB_FLAG_NEXT_MODULE, req);
3114 if (ret != LDB_SUCCESS){
3115 DEBUG(0,(__location__ ": Failed to rename object from '%s' to '%s' - %s\n",
3116 ldb_dn_get_linearized(old_dn),
3117 ldb_dn_get_linearized(new_dn),
3118 ldb_errstring(ldb)));
3119 talloc_free(tmp_ctx);
3124 talloc_free(tmp_ctx);
3126 return ldb_module_done(req, NULL, NULL, LDB_SUCCESS);
3131 static int replmd_replicated_request_error(struct replmd_replicated_request *ar, int ret)
3136 static int replmd_replicated_request_werror(struct replmd_replicated_request *ar, WERROR status)
3138 int ret = LDB_ERR_OTHER;
3139 /* TODO: do some error mapping */
3144 static struct replPropertyMetaData1 *
3145 replmd_replPropertyMetaData1_find_attid(struct replPropertyMetaDataBlob *md_blob,
3146 enum drsuapi_DsAttributeId attid)
3149 struct replPropertyMetaDataCtr1 *rpmd_ctr = &md_blob->ctr.ctr1;
3151 for (i = 0; i < rpmd_ctr->count; i++) {
3152 if (rpmd_ctr->array[i].attid == attid) {
3153 return &rpmd_ctr->array[i];
3161 return true if an update is newer than an existing entry
3162 see section 5.11 of MS-ADTS
3164 static bool replmd_update_is_newer(const struct GUID *current_invocation_id,
3165 const struct GUID *update_invocation_id,
3166 uint32_t current_version,
3167 uint32_t update_version,
3168 NTTIME current_change_time,
3169 NTTIME update_change_time)
3171 if (update_version != current_version) {
3172 return update_version > current_version;
3174 if (update_change_time != current_change_time) {
3175 return update_change_time > current_change_time;
3177 return GUID_compare(update_invocation_id, current_invocation_id) > 0;
3180 static bool replmd_replPropertyMetaData1_is_newer(struct replPropertyMetaData1 *cur_m,
3181 struct replPropertyMetaData1 *new_m)
3183 return replmd_update_is_newer(&cur_m->originating_invocation_id,
3184 &new_m->originating_invocation_id,
3187 cur_m->originating_change_time,
3188 new_m->originating_change_time);
3195 static struct ldb_dn *replmd_conflict_dn(TALLOC_CTX *mem_ctx, struct ldb_dn *dn, struct GUID *guid)
3197 const struct ldb_val *rdn_val;
3198 const char *rdn_name;
3199 struct ldb_dn *new_dn;
3201 rdn_val = ldb_dn_get_rdn_val(dn);
3202 rdn_name = ldb_dn_get_rdn_name(dn);
3203 if (!rdn_val || !rdn_name) {
3207 new_dn = ldb_dn_copy(mem_ctx, dn);
3212 if (!ldb_dn_remove_child_components(new_dn, 1)) {
3216 if (!ldb_dn_add_child_fmt(new_dn, "%s=%s\\0ACNF:%s",
3218 ldb_dn_escape_value(new_dn, *rdn_val),
3219 GUID_string(new_dn, guid))) {
3228 perform a modify operation which sets the rDN and name attributes to
3229 their current values. This has the effect of changing these
3230 attributes to have been last updated by the current DC. This is
3231 needed to ensure that renames performed as part of conflict
3232 resolution are propogated to other DCs
3234 static int replmd_name_modify(struct replmd_replicated_request *ar,
3235 struct ldb_request *req, struct ldb_dn *dn)
3237 struct ldb_message *msg;
3238 const char *rdn_name;
3239 const struct ldb_val *rdn_val;
3240 const struct dsdb_attribute *rdn_attr;
3243 msg = ldb_msg_new(req);
3249 rdn_name = ldb_dn_get_rdn_name(dn);
3250 if (rdn_name == NULL) {
3254 /* normalize the rdn attribute name */
3255 rdn_attr = dsdb_attribute_by_lDAPDisplayName(ar->schema, rdn_name);
3256 if (rdn_attr == NULL) {
3259 rdn_name = rdn_attr->lDAPDisplayName;
3261 rdn_val = ldb_dn_get_rdn_val(dn);
3262 if (rdn_val == NULL) {
3266 if (ldb_msg_add_empty(msg, rdn_name, LDB_FLAG_MOD_REPLACE, NULL) != 0) {
3269 if (ldb_msg_add_value(msg, rdn_name, rdn_val, NULL) != 0) {
3272 if (ldb_msg_add_empty(msg, "name", LDB_FLAG_MOD_REPLACE, NULL) != 0) {
3275 if (ldb_msg_add_value(msg, "name", rdn_val, NULL) != 0) {
3279 ret = dsdb_module_modify(ar->module, msg, DSDB_FLAG_OWN_MODULE, req);
3280 if (ret != LDB_SUCCESS) {
3281 DEBUG(0,(__location__ ": Failed to modify rDN/name of conflict DN '%s' - %s",
3282 ldb_dn_get_linearized(dn),
3283 ldb_errstring(ldb_module_get_ctx(ar->module))));
3293 DEBUG(0,(__location__ ": Failed to setup modify rDN/name of conflict DN '%s'",
3294 ldb_dn_get_linearized(dn)));
3295 return LDB_ERR_OPERATIONS_ERROR;
3300 callback for conflict DN handling where we have renamed the incoming
3301 record. After renaming it, we need to ensure the change of name and
3302 rDN for the incoming record is seen as an originating update by this DC.
3304 static int replmd_op_name_modify_callback(struct ldb_request *req, struct ldb_reply *ares)
3306 struct replmd_replicated_request *ar =
3307 talloc_get_type_abort(req->context, struct replmd_replicated_request);
3310 if (ares->error != LDB_SUCCESS) {
3311 /* call the normal callback for everything except success */
3312 return replmd_op_callback(req, ares);
3315 /* perform a modify of the rDN and name of the record */
3316 ret = replmd_name_modify(ar, req, req->op.add.message->dn);
3317 if (ret != LDB_SUCCESS) {
3319 return replmd_op_callback(req, ares);
3322 return replmd_op_callback(req, ares);
3326 callback for replmd_replicated_apply_add()
3327 This copes with the creation of conflict records in the case where
3328 the DN exists, but with a different objectGUID
3330 static int replmd_op_add_callback(struct ldb_request *req, struct ldb_reply *ares)
3332 struct ldb_dn *conflict_dn;
3333 struct replmd_replicated_request *ar =
3334 talloc_get_type_abort(req->context, struct replmd_replicated_request);
3335 struct ldb_result *res;
3336 const char *attrs[] = { "replPropertyMetaData", "objectGUID", NULL };
3338 const struct ldb_val *rmd_value, *omd_value;
3339 struct replPropertyMetaDataBlob omd, rmd;
3340 enum ndr_err_code ndr_err;
3341 bool rename_incoming_record, rodc;
3342 struct replPropertyMetaData1 *rmd_name, *omd_name;
3344 if (ares->error != LDB_ERR_ENTRY_ALREADY_EXISTS) {
3345 /* call the normal callback for everything except
3347 return replmd_op_callback(req, ares);
3350 ret = samdb_rodc(ldb_module_get_ctx(ar->module), &rodc);
3351 if (ret != LDB_SUCCESS) {
3355 * we have a conflict, and need to decide if we will keep the
3356 * new record or the old record
3358 conflict_dn = req->op.add.message->dn;
3362 * We are on an RODC, or were a GC for this
3363 * partition, so we have to fail this until
3364 * someone who owns the partition sorts it
3367 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
3368 "Conflict adding object '%s' from incoming replication as we are read only for the partition. \n"
3369 " - We must fail the operation until a master for this partition resolves the conflict",
3370 ldb_dn_get_linearized(conflict_dn));
3375 * we have a conflict, and need to decide if we will keep the
3376 * new record or the old record
3378 conflict_dn = req->op.add.message->dn;
3381 * first we need the replPropertyMetaData attribute from the
3384 ret = dsdb_module_search_dn(ar->module, req, &res, conflict_dn,
3386 DSDB_FLAG_NEXT_MODULE |
3387 DSDB_SEARCH_SHOW_DELETED |
3388 DSDB_SEARCH_SHOW_RECYCLED, req);
3389 if (ret != LDB_SUCCESS) {
3390 DEBUG(0,(__location__ ": Unable to find object for conflicting record '%s'\n",
3391 ldb_dn_get_linearized(conflict_dn)));
3395 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
3396 if (omd_value == NULL) {
3397 DEBUG(0,(__location__ ": Unable to find replPropertyMetaData for conflicting record '%s'\n",
3398 ldb_dn_get_linearized(conflict_dn)));
3402 ndr_err = ndr_pull_struct_blob(omd_value, res->msgs[0], &omd,
3403 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
3404 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
3405 DEBUG(0,(__location__ ": Failed to parse old replPropertyMetaData for %s\n",
3406 ldb_dn_get_linearized(conflict_dn)));
3411 * and the replPropertyMetaData attribute from the
3414 rmd_value = ldb_msg_find_ldb_val(req->op.add.message, "replPropertyMetaData");
3415 if (rmd_value == NULL) {
3416 DEBUG(0,(__location__ ": Unable to find replPropertyMetaData for new record '%s'\n",
3417 ldb_dn_get_linearized(conflict_dn)));
3421 ndr_err = ndr_pull_struct_blob(rmd_value, req, &rmd,
3422 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
3423 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
3424 DEBUG(0,(__location__ ": Failed to parse new replPropertyMetaData for %s\n",
3425 ldb_dn_get_linearized(conflict_dn)));
3429 /* we decide which is newer based on the RPMD on the name
3430 attribute. See [MS-DRSR] ResolveNameConflict */
3431 rmd_name = replmd_replPropertyMetaData1_find_attid(&rmd, DRSUAPI_ATTID_name);
3432 omd_name = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
3433 if (!rmd_name || !omd_name) {
3434 DEBUG(0,(__location__ ": Failed to find name attribute in replPropertyMetaData for %s\n",
3435 ldb_dn_get_linearized(conflict_dn)));
3439 rename_incoming_record = !replmd_replPropertyMetaData1_is_newer(omd_name, rmd_name);
3441 if (rename_incoming_record) {
3443 struct ldb_dn *new_dn;
3444 struct ldb_message *new_msg;
3446 guid = samdb_result_guid(req->op.add.message, "objectGUID");
3447 if (GUID_all_zero(&guid)) {
3448 DEBUG(0,(__location__ ": Failed to find objectGUID for conflicting incoming record %s\n",
3449 ldb_dn_get_linearized(conflict_dn)));
3452 new_dn = replmd_conflict_dn(req, conflict_dn, &guid);
3453 if (new_dn == NULL) {
3454 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
3455 ldb_dn_get_linearized(conflict_dn)));
3459 DEBUG(1,(__location__ ": Resolving conflict record via incoming rename '%s' -> '%s'\n",
3460 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
3462 /* re-submit the request, but with a different
3463 callback, so we don't loop forever. */
3464 new_msg = ldb_msg_copy_shallow(req, req->op.add.message);
3467 DEBUG(0,(__location__ ": Failed to copy conflict DN message for %s\n",
3468 ldb_dn_get_linearized(conflict_dn)));
3470 new_msg->dn = new_dn;
3471 req->op.add.message = new_msg;
3472 req->callback = replmd_op_name_modify_callback;
3474 return ldb_next_request(ar->module, req);
3476 /* we are renaming the existing record */
3478 struct ldb_dn *new_dn;
3480 guid = samdb_result_guid(res->msgs[0], "objectGUID");
3481 if (GUID_all_zero(&guid)) {
3482 DEBUG(0,(__location__ ": Failed to find objectGUID for existing conflict record %s\n",
3483 ldb_dn_get_linearized(conflict_dn)));
3487 new_dn = replmd_conflict_dn(req, conflict_dn, &guid);
3488 if (new_dn == NULL) {
3489 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
3490 ldb_dn_get_linearized(conflict_dn)));
3494 DEBUG(1,(__location__ ": Resolving conflict record via existing rename '%s' -> '%s'\n",
3495 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
3497 ret = dsdb_module_rename(ar->module, conflict_dn, new_dn,
3498 DSDB_FLAG_OWN_MODULE, req);
3499 if (ret != LDB_SUCCESS) {
3500 DEBUG(0,(__location__ ": Failed to rename conflict dn '%s' to '%s' - %s\n",
3501 ldb_dn_get_linearized(conflict_dn),
3502 ldb_dn_get_linearized(new_dn),
3503 ldb_errstring(ldb_module_get_ctx(ar->module))));
3508 * now we need to ensure that the rename is seen as an
3509 * originating update. We do that with a modify.
3511 ret = replmd_name_modify(ar, req, new_dn);
3512 if (ret != LDB_SUCCESS) {
3516 req->callback = replmd_op_callback;
3518 return ldb_next_request(ar->module, req);
3522 /* on failure do the original callback. This means replication
3523 * will stop with an error, but there is not much else we can
3526 return replmd_op_callback(req, ares);
3530 this is called when a new object comes in over DRS
3532 static int replmd_replicated_apply_add(struct replmd_replicated_request *ar)
3534 struct ldb_context *ldb;
3535 struct ldb_request *change_req;
3536 enum ndr_err_code ndr_err;
3537 struct ldb_message *msg;
3538 struct replPropertyMetaDataBlob *md;
3539 struct ldb_val md_value;
3544 * TODO: check if the parent object exist
3548 * TODO: handle the conflict case where an object with the
3552 ldb = ldb_module_get_ctx(ar->module);
3553 msg = ar->objs->objects[ar->index_current].msg;
3554 md = ar->objs->objects[ar->index_current].meta_data;
3556 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
3557 if (ret != LDB_SUCCESS) {
3558 return replmd_replicated_request_error(ar, ret);
3561 ret = ldb_msg_add_value(msg, "objectGUID", &ar->objs->objects[ar->index_current].guid_value, NULL);
3562 if (ret != LDB_SUCCESS) {
3563 return replmd_replicated_request_error(ar, ret);
3566 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
3567 if (ret != LDB_SUCCESS) {
3568 return replmd_replicated_request_error(ar, ret);
3571 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", ar->seq_num);
3572 if (ret != LDB_SUCCESS) {
3573 return replmd_replicated_request_error(ar, ret);
3576 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
3577 if (ret != LDB_SUCCESS) {
3578 return replmd_replicated_request_error(ar, ret);
3581 /* remove any message elements that have zero values */
3582 for (i=0; i<msg->num_elements; i++) {
3583 struct ldb_message_element *el = &msg->elements[i];
3585 if (el->num_values == 0) {
3586 DEBUG(4,(__location__ ": Removing attribute %s with num_values==0\n",
3588 memmove(el, el+1, sizeof(*el)*(msg->num_elements - (i+1)));
3589 msg->num_elements--;
3596 * the meta data array is already sorted by the caller
3598 for (i=0; i < md->ctr.ctr1.count; i++) {
3599 md->ctr.ctr1.array[i].local_usn = ar->seq_num;
3601 ndr_err = ndr_push_struct_blob(&md_value, msg, md,
3602 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
3603 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
3604 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
3605 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
3607 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &md_value, NULL);
3608 if (ret != LDB_SUCCESS) {
3609 return replmd_replicated_request_error(ar, ret);
3612 replmd_ldb_message_sort(msg, ar->schema);
3615 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_ADD, msg);
3616 DEBUG(4, ("DRS replication add message:\n%s\n", s));
3620 ret = ldb_build_add_req(&change_req,
3626 replmd_op_add_callback,
3628 LDB_REQ_SET_LOCATION(change_req);
3629 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
3631 /* current partition control needed by "repmd_op_callback" */
3632 ret = ldb_request_add_control(change_req,
3633 DSDB_CONTROL_CURRENT_PARTITION_OID,
3635 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
3637 if (ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PARTIAL_REPLICA) {
3638 /* this tells the partition module to make it a
3639 partial replica if creating an NC */
3640 ret = ldb_request_add_control(change_req,
3641 DSDB_CONTROL_PARTIAL_REPLICA,
3643 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
3646 return ldb_next_request(ar->module, change_req);
3650 handle renames that come in over DRS replication
3652 static int replmd_replicated_handle_rename(struct replmd_replicated_request *ar,
3653 struct ldb_message *msg,
3654 struct replPropertyMetaDataBlob *rmd,
3655 struct replPropertyMetaDataBlob *omd,
3656 struct ldb_request *parent)
3658 struct replPropertyMetaData1 *md_remote;
3659 struct replPropertyMetaData1 *md_local;
3661 if (ldb_dn_compare(msg->dn, ar->search_msg->dn) == 0) {
3666 /* now we need to check for double renames. We could have a
3667 * local rename pending which our replication partner hasn't
3668 * received yet. We choose which one wins by looking at the
3669 * attribute stamps on the two objects, the newer one wins
3671 md_remote = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
3672 md_local = replmd_replPropertyMetaData1_find_attid(omd, DRSUAPI_ATTID_name);
3673 /* if there is no name attribute then we have to assume the
3674 object we've received is in fact newer */
3675 if (!md_remote || !md_local ||
3676 replmd_replPropertyMetaData1_is_newer(md_local, md_remote)) {
3677 DEBUG(4,("replmd_replicated_request rename %s => %s\n",
3678 ldb_dn_get_linearized(ar->search_msg->dn),
3679 ldb_dn_get_linearized(msg->dn)));
3680 /* pass rename to the next module
3681 * so it doesn't appear as an originating update */
3682 return dsdb_module_rename(ar->module,
3683 ar->search_msg->dn, msg->dn,
3684 DSDB_FLAG_NEXT_MODULE | DSDB_MODIFY_RELAX, parent);
3687 /* we're going to keep our old object */
3688 DEBUG(4,(__location__ ": Keeping object %s and rejecting older rename to %s\n",
3689 ldb_dn_get_linearized(ar->search_msg->dn),
3690 ldb_dn_get_linearized(msg->dn)));
3695 static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar)
3697 struct ldb_context *ldb;
3698 struct ldb_request *change_req;
3699 enum ndr_err_code ndr_err;
3700 struct ldb_message *msg;
3701 struct replPropertyMetaDataBlob *rmd;
3702 struct replPropertyMetaDataBlob omd;
3703 const struct ldb_val *omd_value;
3704 struct replPropertyMetaDataBlob nmd;
3705 struct ldb_val nmd_value;
3708 unsigned int removed_attrs = 0;
3711 ldb = ldb_module_get_ctx(ar->module);
3712 msg = ar->objs->objects[ar->index_current].msg;
3713 rmd = ar->objs->objects[ar->index_current].meta_data;
3717 /* find existing meta data */
3718 omd_value = ldb_msg_find_ldb_val(ar->search_msg, "replPropertyMetaData");
3720 ndr_err = ndr_pull_struct_blob(omd_value, ar, &omd,
3721 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
3722 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
3723 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
3724 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
3727 if (omd.version != 1) {
3728 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
3732 /* handle renames that come in over DRS */
3733 ret = replmd_replicated_handle_rename(ar, msg, rmd, &omd, ar->req);
3734 if (ret != LDB_SUCCESS) {
3735 ldb_debug(ldb, LDB_DEBUG_FATAL,
3736 "replmd_replicated_request rename %s => %s failed - %s\n",
3737 ldb_dn_get_linearized(ar->search_msg->dn),
3738 ldb_dn_get_linearized(msg->dn),
3739 ldb_errstring(ldb));
3740 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
3745 nmd.ctr.ctr1.count = omd.ctr.ctr1.count + rmd->ctr.ctr1.count;
3746 nmd.ctr.ctr1.array = talloc_array(ar,
3747 struct replPropertyMetaData1,
3748 nmd.ctr.ctr1.count);
3749 if (!nmd.ctr.ctr1.array) return replmd_replicated_request_werror(ar, WERR_NOMEM);
3751 /* first copy the old meta data */
3752 for (i=0; i < omd.ctr.ctr1.count; i++) {
3753 nmd.ctr.ctr1.array[ni] = omd.ctr.ctr1.array[i];
3758 /* now merge in the new meta data */
3759 for (i=0; i < rmd->ctr.ctr1.count; i++) {
3762 for (j=0; j < ni; j++) {
3765 if (rmd->ctr.ctr1.array[i].attid != nmd.ctr.ctr1.array[j].attid) {
3769 if (ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING) {
3770 /* if we compare equal then do an
3771 update. This is used when a client
3772 asks for a FULL_SYNC, and can be
3773 used to recover a corrupt
3775 cmp = !replmd_replPropertyMetaData1_is_newer(&rmd->ctr.ctr1.array[i],
3776 &nmd.ctr.ctr1.array[j]);
3778 cmp = replmd_replPropertyMetaData1_is_newer(&nmd.ctr.ctr1.array[j],
3779 &rmd->ctr.ctr1.array[i]);
3782 /* replace the entry */
3783 nmd.ctr.ctr1.array[j] = rmd->ctr.ctr1.array[i];
3784 if (ar->seq_num == 0) {
3785 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
3786 if (ret != LDB_SUCCESS) {
3787 return replmd_replicated_request_error(ar, ret);
3790 nmd.ctr.ctr1.array[j].local_usn = ar->seq_num;
3795 if (rmd->ctr.ctr1.array[i].attid != DRSUAPI_ATTID_instanceType) {
3796 DEBUG(3,("Discarding older DRS attribute update to %s on %s from %s\n",
3797 msg->elements[i-removed_attrs].name,
3798 ldb_dn_get_linearized(msg->dn),
3799 GUID_string(ar, &rmd->ctr.ctr1.array[i].originating_invocation_id)));
3802 /* we don't want to apply this change so remove the attribute */
3803 ldb_msg_remove_element(msg, &msg->elements[i-removed_attrs]);
3810 if (found) continue;
3812 nmd.ctr.ctr1.array[ni] = rmd->ctr.ctr1.array[i];
3813 if (ar->seq_num == 0) {
3814 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
3815 if (ret != LDB_SUCCESS) {
3816 return replmd_replicated_request_error(ar, ret);
3819 nmd.ctr.ctr1.array[ni].local_usn = ar->seq_num;
3824 * finally correct the size of the meta_data array
3826 nmd.ctr.ctr1.count = ni;
3829 * the rdn attribute (the alias for the name attribute),
3830 * 'cn' for most objects is the last entry in the meta data array
3833 * sort the new meta data array
3835 ret = replmd_replPropertyMetaDataCtr1_sort(&nmd.ctr.ctr1, ar->schema, msg->dn);
3836 if (ret != LDB_SUCCESS) {
3841 * check if some replicated attributes left, otherwise skip the ldb_modify() call
3843 if (msg->num_elements == 0) {
3844 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: skip replace\n",
3847 ar->index_current++;
3848 return replmd_replicated_apply_next(ar);
3851 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: replace %u attributes\n",
3852 ar->index_current, msg->num_elements);
3854 /* create the meta data value */
3855 ndr_err = ndr_push_struct_blob(&nmd_value, msg, &nmd,
3856 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
3857 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
3858 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
3859 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
3863 * when we know that we'll modify the record, add the whenChanged, uSNChanged
3864 * and replPopertyMetaData attributes
3866 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
3867 if (ret != LDB_SUCCESS) {
3868 return replmd_replicated_request_error(ar, ret);
3870 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
3871 if (ret != LDB_SUCCESS) {
3872 return replmd_replicated_request_error(ar, ret);
3874 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
3875 if (ret != LDB_SUCCESS) {
3876 return replmd_replicated_request_error(ar, ret);
3879 replmd_ldb_message_sort(msg, ar->schema);
3881 /* we want to replace the old values */
3882 for (i=0; i < msg->num_elements; i++) {
3883 msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
3887 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_MODIFY, msg);
3888 DEBUG(4, ("DRS replication modify message:\n%s\n", s));
3892 ret = ldb_build_mod_req(&change_req,
3900 LDB_REQ_SET_LOCATION(change_req);
3901 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
3903 /* current partition control needed by "repmd_op_callback" */
3904 ret = ldb_request_add_control(change_req,
3905 DSDB_CONTROL_CURRENT_PARTITION_OID,
3907 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
3909 return ldb_next_request(ar->module, change_req);
3912 static int replmd_replicated_apply_search_callback(struct ldb_request *req,
3913 struct ldb_reply *ares)
3915 struct replmd_replicated_request *ar = talloc_get_type(req->context,
3916 struct replmd_replicated_request);
3920 return ldb_module_done(ar->req, NULL, NULL,
3921 LDB_ERR_OPERATIONS_ERROR);
3923 if (ares->error != LDB_SUCCESS &&
3924 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
3925 return ldb_module_done(ar->req, ares->controls,
3926 ares->response, ares->error);
3929 switch (ares->type) {
3930 case LDB_REPLY_ENTRY:
3931 ar->search_msg = talloc_steal(ar, ares->message);
3934 case LDB_REPLY_REFERRAL:
3935 /* we ignore referrals */
3938 case LDB_REPLY_DONE:
3939 if (ar->search_msg != NULL) {
3940 ret = replmd_replicated_apply_merge(ar);
3942 ret = replmd_replicated_apply_add(ar);
3944 if (ret != LDB_SUCCESS) {
3945 return ldb_module_done(ar->req, NULL, NULL, ret);
3953 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar);
3955 static int replmd_replicated_apply_next(struct replmd_replicated_request *ar)
3957 struct ldb_context *ldb;
3961 struct ldb_request *search_req;
3962 struct ldb_search_options_control *options;
3964 if (ar->index_current >= ar->objs->num_objects) {
3965 /* done with it, go to next stage */
3966 return replmd_replicated_uptodate_vector(ar);
3969 ldb = ldb_module_get_ctx(ar->module);
3970 ar->search_msg = NULL;
3972 tmp_str = ldb_binary_encode(ar, ar->objs->objects[ar->index_current].guid_value);
3973 if (!tmp_str) return replmd_replicated_request_werror(ar, WERR_NOMEM);
3975 filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
3976 if (!filter) return replmd_replicated_request_werror(ar, WERR_NOMEM);
3977 talloc_free(tmp_str);
3979 ret = ldb_build_search_req(&search_req,
3988 replmd_replicated_apply_search_callback,
3990 LDB_REQ_SET_LOCATION(search_req);
3992 ret = ldb_request_add_control(search_req, LDB_CONTROL_SHOW_RECYCLED_OID,
3994 if (ret != LDB_SUCCESS) {
3998 /* we need to cope with cross-partition links, so search for
3999 the GUID over all partitions */
4000 options = talloc(search_req, struct ldb_search_options_control);
4001 if (options == NULL) {
4002 DEBUG(0, (__location__ ": out of memory\n"));
4003 return LDB_ERR_OPERATIONS_ERROR;
4005 options->search_options = LDB_SEARCH_OPTION_PHANTOM_ROOT;
4007 ret = ldb_request_add_control(search_req,
4008 LDB_CONTROL_SEARCH_OPTIONS_OID,
4010 if (ret != LDB_SUCCESS) {
4014 return ldb_next_request(ar->module, search_req);
4017 static int replmd_replicated_uptodate_modify_callback(struct ldb_request *req,
4018 struct ldb_reply *ares)
4020 struct ldb_context *ldb;
4021 struct replmd_replicated_request *ar = talloc_get_type(req->context,
4022 struct replmd_replicated_request);
4023 ldb = ldb_module_get_ctx(ar->module);
4026 return ldb_module_done(ar->req, NULL, NULL,
4027 LDB_ERR_OPERATIONS_ERROR);
4029 if (ares->error != LDB_SUCCESS) {
4030 return ldb_module_done(ar->req, ares->controls,
4031 ares->response, ares->error);
4034 if (ares->type != LDB_REPLY_DONE) {
4035 ldb_asprintf_errstring(ldb, "Invalid LDB reply type %d", ares->type);
4036 return ldb_module_done(ar->req, NULL, NULL,
4037 LDB_ERR_OPERATIONS_ERROR);
4042 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
4045 static int replmd_replicated_uptodate_modify(struct replmd_replicated_request *ar)
4047 struct ldb_context *ldb;
4048 struct ldb_request *change_req;
4049 enum ndr_err_code ndr_err;
4050 struct ldb_message *msg;
4051 struct replUpToDateVectorBlob ouv;
4052 const struct ldb_val *ouv_value;
4053 const struct drsuapi_DsReplicaCursor2CtrEx *ruv;
4054 struct replUpToDateVectorBlob nuv;
4055 struct ldb_val nuv_value;
4056 struct ldb_message_element *nuv_el = NULL;
4057 const struct GUID *our_invocation_id;
4058 struct ldb_message_element *orf_el = NULL;
4059 struct repsFromToBlob nrf;
4060 struct ldb_val *nrf_value = NULL;
4061 struct ldb_message_element *nrf_el = NULL;
4065 time_t t = time(NULL);
4068 uint32_t instanceType;
4070 ldb = ldb_module_get_ctx(ar->module);
4071 ruv = ar->objs->uptodateness_vector;
4077 unix_to_nt_time(&now, t);
4079 if (ar->search_msg == NULL) {
4080 /* this happens for a REPL_OBJ call where we are
4081 creating the target object by replicating it. The
4082 subdomain join code does this for the partition DN
4084 DEBUG(4,(__location__ ": Skipping UDV and repsFrom update as no target DN\n"));
4085 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
4088 instanceType = ldb_msg_find_attr_as_uint(ar->search_msg, "instanceType", 0);
4089 if (! (instanceType & INSTANCE_TYPE_IS_NC_HEAD)) {
4090 DEBUG(4,(__location__ ": Skipping UDV and repsFrom update as not NC root: %s\n",
4091 ldb_dn_get_linearized(ar->search_msg->dn)));
4092 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
4096 * first create the new replUpToDateVector
4098 ouv_value = ldb_msg_find_ldb_val(ar->search_msg, "replUpToDateVector");
4100 ndr_err = ndr_pull_struct_blob(ouv_value, ar, &ouv,
4101 (ndr_pull_flags_fn_t)ndr_pull_replUpToDateVectorBlob);
4102 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
4103 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
4104 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
4107 if (ouv.version != 2) {
4108 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
4113 * the new uptodateness vector will at least
4114 * contain 1 entry, one for the source_dsa
4116 * plus optional values from our old vector and the one from the source_dsa
4118 nuv.ctr.ctr2.count = 1 + ouv.ctr.ctr2.count;
4119 if (ruv) nuv.ctr.ctr2.count += ruv->count;
4120 nuv.ctr.ctr2.cursors = talloc_array(ar,
4121 struct drsuapi_DsReplicaCursor2,
4122 nuv.ctr.ctr2.count);
4123 if (!nuv.ctr.ctr2.cursors) return replmd_replicated_request_werror(ar, WERR_NOMEM);
4125 /* first copy the old vector */
4126 for (i=0; i < ouv.ctr.ctr2.count; i++) {
4127 nuv.ctr.ctr2.cursors[ni] = ouv.ctr.ctr2.cursors[i];
4131 /* get our invocation_id if we have one already attached to the ldb */
4132 our_invocation_id = samdb_ntds_invocation_id(ldb);
4134 /* merge in the source_dsa vector is available */
4135 for (i=0; (ruv && i < ruv->count); i++) {
4138 if (our_invocation_id &&
4139 GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
4140 our_invocation_id)) {
4144 for (j=0; j < ni; j++) {
4145 if (!GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
4146 &nuv.ctr.ctr2.cursors[j].source_dsa_invocation_id)) {
4153 * we update only the highest_usn and not the latest_sync_success time,
4154 * because the last success stands for direct replication
4156 if (ruv->cursors[i].highest_usn > nuv.ctr.ctr2.cursors[j].highest_usn) {
4157 nuv.ctr.ctr2.cursors[j].highest_usn = ruv->cursors[i].highest_usn;
4162 if (found) continue;
4164 /* if it's not there yet, add it */
4165 nuv.ctr.ctr2.cursors[ni] = ruv->cursors[i];
4170 * merge in the current highwatermark for the source_dsa
4173 for (j=0; j < ni; j++) {
4174 if (!GUID_equal(&ar->objs->source_dsa->source_dsa_invocation_id,
4175 &nuv.ctr.ctr2.cursors[j].source_dsa_invocation_id)) {
4182 * here we update the highest_usn and last_sync_success time
4183 * because we're directly replicating from the source_dsa
4185 * and use the tmp_highest_usn because this is what we have just applied
4188 nuv.ctr.ctr2.cursors[j].highest_usn = ar->objs->source_dsa->highwatermark.tmp_highest_usn;
4189 nuv.ctr.ctr2.cursors[j].last_sync_success = now;
4194 * here we update the highest_usn and last_sync_success time
4195 * because we're directly replicating from the source_dsa
4197 * and use the tmp_highest_usn because this is what we have just applied
4200 nuv.ctr.ctr2.cursors[ni].source_dsa_invocation_id= ar->objs->source_dsa->source_dsa_invocation_id;
4201 nuv.ctr.ctr2.cursors[ni].highest_usn = ar->objs->source_dsa->highwatermark.tmp_highest_usn;
4202 nuv.ctr.ctr2.cursors[ni].last_sync_success = now;
4207 * finally correct the size of the cursors array
4209 nuv.ctr.ctr2.count = ni;
4214 TYPESAFE_QSORT(nuv.ctr.ctr2.cursors, nuv.ctr.ctr2.count, drsuapi_DsReplicaCursor2_compare);
4217 * create the change ldb_message
4219 msg = ldb_msg_new(ar);
4220 if (!msg) return replmd_replicated_request_werror(ar, WERR_NOMEM);
4221 msg->dn = ar->search_msg->dn;
4223 ndr_err = ndr_push_struct_blob(&nuv_value, msg, &nuv,
4224 (ndr_push_flags_fn_t)ndr_push_replUpToDateVectorBlob);
4225 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
4226 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
4227 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
4229 ret = ldb_msg_add_value(msg, "replUpToDateVector", &nuv_value, &nuv_el);
4230 if (ret != LDB_SUCCESS) {
4231 return replmd_replicated_request_error(ar, ret);
4233 nuv_el->flags = LDB_FLAG_MOD_REPLACE;
4236 * now create the new repsFrom value from the given repsFromTo1 structure
4240 nrf.ctr.ctr1 = *ar->objs->source_dsa;
4241 nrf.ctr.ctr1.highwatermark.highest_usn = nrf.ctr.ctr1.highwatermark.tmp_highest_usn;
4244 * first see if we already have a repsFrom value for the current source dsa
4245 * if so we'll later replace this value
4247 orf_el = ldb_msg_find_element(ar->search_msg, "repsFrom");
4249 for (i=0; i < orf_el->num_values; i++) {
4250 struct repsFromToBlob *trf;
4252 trf = talloc(ar, struct repsFromToBlob);
4253 if (!trf) return replmd_replicated_request_werror(ar, WERR_NOMEM);
4255 ndr_err = ndr_pull_struct_blob(&orf_el->values[i], trf, trf,
4256 (ndr_pull_flags_fn_t)ndr_pull_repsFromToBlob);
4257 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
4258 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
4259 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
4262 if (trf->version != 1) {
4263 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
4267 * we compare the source dsa objectGUID not the invocation_id
4268 * because we want only one repsFrom value per source dsa
4269 * and when the invocation_id of the source dsa has changed we don't need
4270 * the old repsFrom with the old invocation_id
4272 if (!GUID_equal(&trf->ctr.ctr1.source_dsa_obj_guid,
4273 &ar->objs->source_dsa->source_dsa_obj_guid)) {
4279 nrf_value = &orf_el->values[i];
4284 * copy over all old values to the new ldb_message
4286 ret = ldb_msg_add_empty(msg, "repsFrom", 0, &nrf_el);
4287 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
4292 * if we haven't found an old repsFrom value for the current source dsa
4293 * we'll add a new value
4296 struct ldb_val zero_value;
4297 ZERO_STRUCT(zero_value);
4298 ret = ldb_msg_add_value(msg, "repsFrom", &zero_value, &nrf_el);
4299 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
4301 nrf_value = &nrf_el->values[nrf_el->num_values - 1];
4304 /* we now fill the value which is already attached to ldb_message */
4305 ndr_err = ndr_push_struct_blob(nrf_value, msg,
4307 (ndr_push_flags_fn_t)ndr_push_repsFromToBlob);
4308 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
4309 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
4310 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
4314 * the ldb_message_element for the attribute, has all the old values and the new one
4315 * so we'll replace the whole attribute with all values
4317 nrf_el->flags = LDB_FLAG_MOD_REPLACE;
4319 if (CHECK_DEBUGLVL(4)) {
4320 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_MODIFY, msg);
4321 DEBUG(4, ("DRS replication uptodate modify message:\n%s\n", s));
4325 /* prepare the ldb_modify() request */
4326 ret = ldb_build_mod_req(&change_req,
4332 replmd_replicated_uptodate_modify_callback,
4334 LDB_REQ_SET_LOCATION(change_req);
4335 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
4337 return ldb_next_request(ar->module, change_req);
4340 static int replmd_replicated_uptodate_search_callback(struct ldb_request *req,
4341 struct ldb_reply *ares)
4343 struct replmd_replicated_request *ar = talloc_get_type(req->context,
4344 struct replmd_replicated_request);
4348 return ldb_module_done(ar->req, NULL, NULL,
4349 LDB_ERR_OPERATIONS_ERROR);
4351 if (ares->error != LDB_SUCCESS &&
4352 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
4353 return ldb_module_done(ar->req, ares->controls,
4354 ares->response, ares->error);
4357 switch (ares->type) {
4358 case LDB_REPLY_ENTRY:
4359 ar->search_msg = talloc_steal(ar, ares->message);
4362 case LDB_REPLY_REFERRAL:
4363 /* we ignore referrals */
4366 case LDB_REPLY_DONE:
4367 ret = replmd_replicated_uptodate_modify(ar);
4368 if (ret != LDB_SUCCESS) {
4369 return ldb_module_done(ar->req, NULL, NULL, ret);
4378 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar)
4380 struct ldb_context *ldb;
4382 static const char *attrs[] = {
4383 "replUpToDateVector",
4388 struct ldb_request *search_req;
4390 ldb = ldb_module_get_ctx(ar->module);
4391 ar->search_msg = NULL;
4393 ret = ldb_build_search_req(&search_req,
4396 ar->objs->partition_dn,
4402 replmd_replicated_uptodate_search_callback,
4404 LDB_REQ_SET_LOCATION(search_req);
4405 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
4407 return ldb_next_request(ar->module, search_req);
4412 static int replmd_extended_replicated_objects(struct ldb_module *module, struct ldb_request *req)
4414 struct ldb_context *ldb;
4415 struct dsdb_extended_replicated_objects *objs;
4416 struct replmd_replicated_request *ar;
4417 struct ldb_control **ctrls;
4420 struct replmd_private *replmd_private =
4421 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
4422 struct dsdb_control_replicated_update *rep_update;
4424 ldb = ldb_module_get_ctx(module);
4426 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_extended_replicated_objects\n");
4428 objs = talloc_get_type(req->op.extended.data, struct dsdb_extended_replicated_objects);
4430 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: invalid extended data\n");
4431 return LDB_ERR_PROTOCOL_ERROR;
4434 if (objs->version != DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION) {
4435 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: extended data invalid version [%u != %u]\n",
4436 objs->version, DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION);
4437 return LDB_ERR_PROTOCOL_ERROR;
4440 ar = replmd_ctx_init(module, req);
4442 return LDB_ERR_OPERATIONS_ERROR;
4444 /* Set the flags to have the replmd_op_callback run over the full set of objects */
4445 ar->apply_mode = true;
4447 ar->schema = dsdb_get_schema(ldb, ar);
4449 ldb_debug_set(ldb, LDB_DEBUG_FATAL, "replmd_ctx_init: no loaded schema found\n");
4451 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
4452 return LDB_ERR_CONSTRAINT_VIOLATION;
4455 ctrls = req->controls;
4457 if (req->controls) {
4458 req->controls = talloc_memdup(ar, req->controls,
4459 talloc_get_size(req->controls));
4460 if (!req->controls) return replmd_replicated_request_werror(ar, WERR_NOMEM);
4463 /* This allows layers further down to know if a change came in
4464 over replication and what the replication flags were */
4465 rep_update = talloc_zero(ar, struct dsdb_control_replicated_update);
4466 if (rep_update == NULL) {
4467 return ldb_module_oom(module);
4469 rep_update->dsdb_repl_flags = objs->dsdb_repl_flags;
4471 ret = ldb_request_add_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID, false, rep_update);
4472 if (ret != LDB_SUCCESS) {
4476 /* If this change contained linked attributes in the body
4477 * (rather than in the links section) we need to update
4478 * backlinks in linked_attributes */
4479 ret = ldb_request_add_control(req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
4480 if (ret != LDB_SUCCESS) {
4484 ar->controls = req->controls;
4485 req->controls = ctrls;
4487 DEBUG(4,("linked_attributes_count=%u\n", objs->linked_attributes_count));
4489 /* save away the linked attributes for the end of the
4491 for (i=0; i<ar->objs->linked_attributes_count; i++) {
4492 struct la_entry *la_entry;
4494 if (replmd_private->la_ctx == NULL) {
4495 replmd_private->la_ctx = talloc_new(replmd_private);
4497 la_entry = talloc(replmd_private->la_ctx, struct la_entry);
4498 if (la_entry == NULL) {
4500 return LDB_ERR_OPERATIONS_ERROR;
4502 la_entry->la = talloc(la_entry, struct drsuapi_DsReplicaLinkedAttribute);
4503 if (la_entry->la == NULL) {
4504 talloc_free(la_entry);
4506 return LDB_ERR_OPERATIONS_ERROR;
4508 *la_entry->la = ar->objs->linked_attributes[i];
4510 /* we need to steal the non-scalars so they stay
4511 around until the end of the transaction */
4512 talloc_steal(la_entry->la, la_entry->la->identifier);
4513 talloc_steal(la_entry->la, la_entry->la->value.blob);
4515 DLIST_ADD(replmd_private->la_list, la_entry);
4518 return replmd_replicated_apply_next(ar);
4522 process one linked attribute structure
4524 static int replmd_process_linked_attribute(struct ldb_module *module,
4525 struct la_entry *la_entry,
4526 struct ldb_request *parent)
4528 struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
4529 struct ldb_context *ldb = ldb_module_get_ctx(module);
4530 struct ldb_message *msg;
4531 TALLOC_CTX *tmp_ctx = talloc_new(la_entry);
4532 const struct dsdb_schema *schema = dsdb_get_schema(ldb, tmp_ctx);
4534 const struct dsdb_attribute *attr;
4535 struct dsdb_dn *dsdb_dn;
4536 uint64_t seq_num = 0;
4537 struct ldb_message_element *old_el;
4539 time_t t = time(NULL);
4540 struct ldb_result *res;
4541 const char *attrs[2];
4542 struct parsed_dn *pdn_list, *pdn;
4543 struct GUID guid = GUID_zero();
4545 bool active = (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)?true:false;
4546 const struct GUID *our_invocation_id;
4549 linked_attributes[0]:
4550 &objs->linked_attributes[i]: struct drsuapi_DsReplicaLinkedAttribute
4552 identifier: struct drsuapi_DsReplicaObjectIdentifier
4553 __ndr_size : 0x0000003a (58)
4554 __ndr_size_sid : 0x00000000 (0)
4555 guid : 8e95b6a9-13dd-4158-89db-3220a5be5cc7
4557 __ndr_size_dn : 0x00000000 (0)
4559 attid : DRSUAPI_ATTID_member (0x1F)
4560 value: struct drsuapi_DsAttributeValue
4561 __ndr_size : 0x0000007e (126)
4563 blob : DATA_BLOB length=126
4564 flags : 0x00000001 (1)
4565 1: DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE
4566 originating_add_time : Wed Sep 2 22:20:01 2009 EST
4567 meta_data: struct drsuapi_DsReplicaMetaData
4568 version : 0x00000015 (21)
4569 originating_change_time : Wed Sep 2 23:39:07 2009 EST
4570 originating_invocation_id: 794640f3-18cf-40ee-a211-a93992b67a64
4571 originating_usn : 0x000000000001e19c (123292)
4573 (for cases where the link is to a normal DN)
4574 &target: struct drsuapi_DsReplicaObjectIdentifier3
4575 __ndr_size : 0x0000007e (126)
4576 __ndr_size_sid : 0x0000001c (28)
4577 guid : 7639e594-db75-4086-b0d4-67890ae46031
4578 sid : S-1-5-21-2848215498-2472035911-1947525656-19924
4579 __ndr_size_dn : 0x00000022 (34)
4580 dn : 'CN=UOne,OU=TestOU,DC=vsofs8,DC=com'
4583 /* find the attribute being modified */
4584 attr = dsdb_attribute_by_attributeID_id(schema, la->attid);
4586 DEBUG(0, (__location__ ": Unable to find attributeID 0x%x\n", la->attid));
4587 talloc_free(tmp_ctx);
4588 return LDB_ERR_OPERATIONS_ERROR;
4591 attrs[0] = attr->lDAPDisplayName;
4594 /* get the existing message from the db for the object with
4595 this GUID, returning attribute being modified. We will then
4596 use this msg as the basis for a modify call */
4597 ret = dsdb_module_search(module, tmp_ctx, &res, NULL, LDB_SCOPE_SUBTREE, attrs,
4598 DSDB_FLAG_NEXT_MODULE |
4599 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
4600 DSDB_SEARCH_SHOW_RECYCLED |
4601 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
4602 DSDB_SEARCH_REVEAL_INTERNALS,
4604 "objectGUID=%s", GUID_string(tmp_ctx, &la->identifier->guid));
4605 if (ret != LDB_SUCCESS) {
4606 talloc_free(tmp_ctx);
4609 if (res->count != 1) {
4610 ldb_asprintf_errstring(ldb, "DRS linked attribute for GUID %s - DN not found",
4611 GUID_string(tmp_ctx, &la->identifier->guid));
4612 talloc_free(tmp_ctx);
4613 return LDB_ERR_NO_SUCH_OBJECT;
4617 if (msg->num_elements == 0) {
4618 ret = ldb_msg_add_empty(msg, attr->lDAPDisplayName, LDB_FLAG_MOD_REPLACE, &old_el);
4619 if (ret != LDB_SUCCESS) {
4620 ldb_module_oom(module);
4621 talloc_free(tmp_ctx);
4622 return LDB_ERR_OPERATIONS_ERROR;
4625 old_el = &msg->elements[0];
4626 old_el->flags = LDB_FLAG_MOD_REPLACE;
4629 /* parse the existing links */
4630 ret = get_parsed_dns(module, tmp_ctx, old_el, &pdn_list, attr->syntax->ldap_oid, parent);
4631 if (ret != LDB_SUCCESS) {
4632 talloc_free(tmp_ctx);
4636 /* get our invocationId */
4637 our_invocation_id = samdb_ntds_invocation_id(ldb);
4638 if (!our_invocation_id) {
4639 ldb_debug_set(ldb, LDB_DEBUG_ERROR, __location__ ": unable to find invocationId\n");
4640 talloc_free(tmp_ctx);
4641 return LDB_ERR_OPERATIONS_ERROR;
4644 ret = replmd_check_upgrade_links(pdn_list, old_el->num_values, old_el, our_invocation_id);
4645 if (ret != LDB_SUCCESS) {
4646 talloc_free(tmp_ctx);
4650 status = dsdb_dn_la_from_blob(ldb, attr, schema, tmp_ctx, la->value.blob, &dsdb_dn);
4651 if (!W_ERROR_IS_OK(status)) {
4652 ldb_asprintf_errstring(ldb, "Failed to parsed linked attribute blob for %s on %s - %s\n",
4653 old_el->name, ldb_dn_get_linearized(msg->dn), win_errstr(status));
4654 return LDB_ERR_OPERATIONS_ERROR;
4657 ntstatus = dsdb_get_extended_dn_guid(dsdb_dn->dn, &guid, "GUID");
4658 if (!NT_STATUS_IS_OK(ntstatus) && active) {
4659 ldb_asprintf_errstring(ldb, "Failed to find GUID in linked attribute blob for %s on %s from %s",
4661 ldb_dn_get_linearized(dsdb_dn->dn),
4662 ldb_dn_get_linearized(msg->dn));
4663 return LDB_ERR_OPERATIONS_ERROR;
4666 /* re-resolve the DN by GUID, as the DRS server may give us an
4668 ret = dsdb_module_dn_by_guid(module, dsdb_dn, &guid, &dsdb_dn->dn, parent);
4669 if (ret != LDB_SUCCESS) {
4670 DEBUG(2,(__location__ ": WARNING: Failed to re-resolve GUID %s - using %s\n",
4671 GUID_string(tmp_ctx, &guid),
4672 ldb_dn_get_linearized(dsdb_dn->dn)));
4675 /* see if this link already exists */
4676 pdn = parsed_dn_find(pdn_list, old_el->num_values, &guid, dsdb_dn->dn);
4678 /* see if this update is newer than what we have already */
4679 struct GUID invocation_id = GUID_zero();
4680 uint32_t version = 0;
4681 uint32_t originating_usn = 0;
4682 NTTIME change_time = 0;
4683 uint32_t rmd_flags = dsdb_dn_rmd_flags(pdn->dsdb_dn->dn);
4685 dsdb_get_extended_dn_guid(pdn->dsdb_dn->dn, &invocation_id, "RMD_INVOCID");
4686 dsdb_get_extended_dn_uint32(pdn->dsdb_dn->dn, &version, "RMD_VERSION");
4687 dsdb_get_extended_dn_uint32(pdn->dsdb_dn->dn, &originating_usn, "RMD_ORIGINATING_USN");
4688 dsdb_get_extended_dn_nttime(pdn->dsdb_dn->dn, &change_time, "RMD_CHANGETIME");
4690 if (!replmd_update_is_newer(&invocation_id,
4691 &la->meta_data.originating_invocation_id,
4693 la->meta_data.version,
4695 la->meta_data.originating_change_time)) {
4696 DEBUG(3,("Discarding older DRS linked attribute update to %s on %s from %s\n",
4697 old_el->name, ldb_dn_get_linearized(msg->dn),
4698 GUID_string(tmp_ctx, &la->meta_data.originating_invocation_id)));
4699 talloc_free(tmp_ctx);
4703 /* get a seq_num for this change */
4704 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
4705 if (ret != LDB_SUCCESS) {
4706 talloc_free(tmp_ctx);
4710 if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
4711 /* remove the existing backlink */
4712 ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid, false, attr, false);
4713 if (ret != LDB_SUCCESS) {
4714 talloc_free(tmp_ctx);
4719 ret = replmd_update_la_val(tmp_ctx, pdn->v, dsdb_dn, pdn->dsdb_dn,
4720 &la->meta_data.originating_invocation_id,
4721 la->meta_data.originating_usn, seq_num,
4722 la->meta_data.originating_change_time,
4723 la->meta_data.version,
4725 if (ret != LDB_SUCCESS) {
4726 talloc_free(tmp_ctx);
4731 /* add the new backlink */
4732 ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid, true, attr, false);
4733 if (ret != LDB_SUCCESS) {
4734 talloc_free(tmp_ctx);
4739 /* get a seq_num for this change */
4740 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
4741 if (ret != LDB_SUCCESS) {
4742 talloc_free(tmp_ctx);
4746 old_el->values = talloc_realloc(msg->elements, old_el->values,
4747 struct ldb_val, old_el->num_values+1);
4748 if (!old_el->values) {
4749 ldb_module_oom(module);
4750 return LDB_ERR_OPERATIONS_ERROR;
4752 old_el->num_values++;
4754 ret = replmd_build_la_val(tmp_ctx, &old_el->values[old_el->num_values-1], dsdb_dn,
4755 &la->meta_data.originating_invocation_id,
4756 la->meta_data.originating_usn, seq_num,
4757 la->meta_data.originating_change_time,
4758 la->meta_data.version,
4759 (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)?false:true);
4760 if (ret != LDB_SUCCESS) {
4761 talloc_free(tmp_ctx);
4766 ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid,
4768 if (ret != LDB_SUCCESS) {
4769 talloc_free(tmp_ctx);
4775 /* we only change whenChanged and uSNChanged if the seq_num
4777 ret = add_time_element(msg, "whenChanged", t);
4778 if (ret != LDB_SUCCESS) {
4779 talloc_free(tmp_ctx);
4784 ret = add_uint64_element(ldb, msg, "uSNChanged", seq_num);
4785 if (ret != LDB_SUCCESS) {
4786 talloc_free(tmp_ctx);
4791 old_el = ldb_msg_find_element(msg, attr->lDAPDisplayName);
4792 if (old_el == NULL) {
4793 talloc_free(tmp_ctx);
4794 return ldb_operr(ldb);
4797 ret = dsdb_check_single_valued_link(attr, old_el);
4798 if (ret != LDB_SUCCESS) {
4799 talloc_free(tmp_ctx);
4803 old_el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
4805 ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE, parent);
4806 if (ret != LDB_SUCCESS) {
4807 ldb_debug(ldb, LDB_DEBUG_WARNING, "Failed to apply linked attribute change '%s'\n%s\n",
4809 ldb_ldif_message_string(ldb, tmp_ctx, LDB_CHANGETYPE_MODIFY, msg));
4810 talloc_free(tmp_ctx);
4814 talloc_free(tmp_ctx);
4819 static int replmd_extended(struct ldb_module *module, struct ldb_request *req)
4821 if (strcmp(req->op.extended.oid, DSDB_EXTENDED_REPLICATED_OBJECTS_OID) == 0) {
4822 return replmd_extended_replicated_objects(module, req);
4825 return ldb_next_request(module, req);
4830 we hook into the transaction operations to allow us to
4831 perform the linked attribute updates at the end of the whole
4832 transaction. This allows a forward linked attribute to be created
4833 before the object is created. During a vampire, w2k8 sends us linked
4834 attributes before the objects they are part of.
4836 static int replmd_start_transaction(struct ldb_module *module)
4838 /* create our private structure for this transaction */
4839 struct replmd_private *replmd_private = talloc_get_type(ldb_module_get_private(module),
4840 struct replmd_private);
4841 replmd_txn_cleanup(replmd_private);
4843 /* free any leftover mod_usn records from cancelled
4845 while (replmd_private->ncs) {
4846 struct nc_entry *e = replmd_private->ncs;
4847 DLIST_REMOVE(replmd_private->ncs, e);
4851 return ldb_next_start_trans(module);
4855 on prepare commit we loop over our queued la_context structures and
4858 static int replmd_prepare_commit(struct ldb_module *module)
4860 struct replmd_private *replmd_private =
4861 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
4862 struct la_entry *la, *prev;
4863 struct la_backlink *bl;
4866 /* walk the list backwards, to do the first entry first, as we
4867 * added the entries with DLIST_ADD() which puts them at the
4868 * start of the list */
4869 for (la = DLIST_TAIL(replmd_private->la_list); la; la=prev) {
4870 prev = DLIST_PREV(la);
4871 DLIST_REMOVE(replmd_private->la_list, la);
4872 ret = replmd_process_linked_attribute(module, la, NULL);
4873 if (ret != LDB_SUCCESS) {
4874 replmd_txn_cleanup(replmd_private);
4879 /* process our backlink list, creating and deleting backlinks
4881 for (bl=replmd_private->la_backlinks; bl; bl=bl->next) {
4882 ret = replmd_process_backlink(module, bl, NULL);
4883 if (ret != LDB_SUCCESS) {
4884 replmd_txn_cleanup(replmd_private);
4889 replmd_txn_cleanup(replmd_private);
4891 /* possibly change @REPLCHANGED */
4892 ret = replmd_notify_store(module, NULL);
4893 if (ret != LDB_SUCCESS) {
4897 return ldb_next_prepare_commit(module);
4900 static int replmd_del_transaction(struct ldb_module *module)
4902 struct replmd_private *replmd_private =
4903 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
4904 replmd_txn_cleanup(replmd_private);
4906 return ldb_next_del_trans(module);
4910 static const struct ldb_module_ops ldb_repl_meta_data_module_ops = {
4911 .name = "repl_meta_data",
4912 .init_context = replmd_init,
4914 .modify = replmd_modify,
4915 .rename = replmd_rename,
4916 .del = replmd_delete,
4917 .extended = replmd_extended,
4918 .start_transaction = replmd_start_transaction,
4919 .prepare_commit = replmd_prepare_commit,
4920 .del_transaction = replmd_del_transaction,
4923 int ldb_repl_meta_data_module_init(const char *version)
4925 LDB_MODULE_CHECK_VERSION(version);
4926 return ldb_register_module(&ldb_repl_meta_data_module_ops);