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 struct replmd_private {
55 struct la_entry *la_list;
57 struct la_backlink *la_backlinks;
59 struct nc_entry *prev, *next;
62 uint64_t mod_usn_urgent;
67 struct la_entry *next, *prev;
68 struct drsuapi_DsReplicaLinkedAttribute *la;
71 struct replmd_replicated_request {
72 struct ldb_module *module;
73 struct ldb_request *req;
75 const struct dsdb_schema *schema;
77 /* the controls we pass down */
78 struct ldb_control **controls;
80 /* details for the mode where we apply a bunch of inbound replication meessages */
82 uint32_t index_current;
83 struct dsdb_extended_replicated_objects *objs;
85 struct ldb_message *search_msg;
91 enum urgent_situation {
92 REPL_URGENT_ON_CREATE = 1,
93 REPL_URGENT_ON_UPDATE = 2,
94 REPL_URGENT_ON_DELETE = 4
99 const char *update_name;
100 enum urgent_situation repl_situation;
101 } urgent_objects[] = {
102 {"nTDSDSA", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE)},
103 {"crossRef", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE)},
104 {"attributeSchema", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
105 {"classSchema", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
106 {"secret", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
107 {"rIDManager", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
111 /* Attributes looked for when updating or deleting, to check for a urgent replication needed */
112 static const char *urgent_attrs[] = {
115 "userAccountControl",
120 static bool replmd_check_urgent_objectclass(const struct ldb_message_element *objectclass_el,
121 enum urgent_situation situation)
124 for (i=0; urgent_objects[i].update_name; i++) {
126 if ((situation & urgent_objects[i].repl_situation) == 0) {
130 for (j=0; j<objectclass_el->num_values; j++) {
131 const struct ldb_val *v = &objectclass_el->values[j];
132 if (ldb_attr_cmp((const char *)v->data, urgent_objects[i].update_name) == 0) {
140 static bool replmd_check_urgent_attribute(const struct ldb_message_element *el)
142 if (ldb_attr_in_list(urgent_attrs, el->name)) {
149 static int replmd_replicated_apply_next(struct replmd_replicated_request *ar);
152 initialise the module
153 allocate the private structure and build the list
154 of partition DNs for use by replmd_notify()
156 static int replmd_init(struct ldb_module *module)
158 struct replmd_private *replmd_private;
159 struct ldb_context *ldb = ldb_module_get_ctx(module);
161 replmd_private = talloc_zero(module, struct replmd_private);
162 if (replmd_private == NULL) {
164 return LDB_ERR_OPERATIONS_ERROR;
166 ldb_module_set_private(module, replmd_private);
168 return ldb_next_init(module);
172 cleanup our per-transaction contexts
174 static void replmd_txn_cleanup(struct replmd_private *replmd_private)
176 talloc_free(replmd_private->la_ctx);
177 replmd_private->la_list = NULL;
178 replmd_private->la_ctx = NULL;
180 talloc_free(replmd_private->bl_ctx);
181 replmd_private->la_backlinks = NULL;
182 replmd_private->bl_ctx = NULL;
187 struct la_backlink *next, *prev;
188 const char *attr_name;
189 struct GUID forward_guid, target_guid;
194 process a backlinks we accumulated during a transaction, adding and
195 deleting the backlinks from the target objects
197 static int replmd_process_backlink(struct ldb_module *module, struct la_backlink *bl, struct ldb_request *parent)
199 struct ldb_dn *target_dn, *source_dn;
201 struct ldb_context *ldb = ldb_module_get_ctx(module);
202 struct ldb_message *msg;
203 TALLOC_CTX *tmp_ctx = talloc_new(bl);
209 - construct ldb_message
210 - either an add or a delete
212 ret = dsdb_module_dn_by_guid(module, tmp_ctx, &bl->target_guid, &target_dn, parent);
213 if (ret != LDB_SUCCESS) {
214 DEBUG(2,(__location__ ": WARNING: Failed to find target DN for linked attribute with GUID %s\n",
215 GUID_string(bl, &bl->target_guid)));
219 ret = dsdb_module_dn_by_guid(module, tmp_ctx, &bl->forward_guid, &source_dn, parent);
220 if (ret != LDB_SUCCESS) {
221 ldb_asprintf_errstring(ldb, "Failed to find source DN for linked attribute with GUID %s\n",
222 GUID_string(bl, &bl->forward_guid));
223 talloc_free(tmp_ctx);
227 msg = ldb_msg_new(tmp_ctx);
229 ldb_module_oom(module);
230 talloc_free(tmp_ctx);
231 return LDB_ERR_OPERATIONS_ERROR;
234 /* construct a ldb_message for adding/deleting the backlink */
236 dn_string = ldb_dn_get_extended_linearized(tmp_ctx, source_dn, 1);
238 ldb_module_oom(module);
239 talloc_free(tmp_ctx);
240 return LDB_ERR_OPERATIONS_ERROR;
242 ret = ldb_msg_add_steal_string(msg, bl->attr_name, dn_string);
243 if (ret != LDB_SUCCESS) {
244 talloc_free(tmp_ctx);
247 msg->elements[0].flags = bl->active?LDB_FLAG_MOD_ADD:LDB_FLAG_MOD_DELETE;
249 /* a backlink should never be single valued. Unfortunately the
250 exchange schema has a attribute
251 msExchBridgeheadedLocalConnectorsDNBL which is single
252 valued and a backlink. We need to cope with that by
253 ignoring the single value flag */
254 msg->elements[0].flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
256 ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE, parent);
257 if (ret != LDB_SUCCESS) {
258 ldb_asprintf_errstring(ldb, "Failed to %s backlink from %s to %s - %s",
259 bl->active?"add":"remove",
260 ldb_dn_get_linearized(source_dn),
261 ldb_dn_get_linearized(target_dn),
263 talloc_free(tmp_ctx);
266 talloc_free(tmp_ctx);
271 add a backlink to the list of backlinks to add/delete in the prepare
274 static int replmd_add_backlink(struct ldb_module *module, const struct dsdb_schema *schema,
275 struct GUID *forward_guid, struct GUID *target_guid,
276 bool active, const struct dsdb_attribute *schema_attr, bool immediate)
278 const struct dsdb_attribute *target_attr;
279 struct la_backlink *bl;
280 struct replmd_private *replmd_private =
281 talloc_get_type_abort(ldb_module_get_private(module), struct replmd_private);
283 target_attr = dsdb_attribute_by_linkID(schema, schema_attr->linkID ^ 1);
286 * windows 2003 has a broken schema where the
287 * definition of msDS-IsDomainFor is missing (which is
288 * supposed to be the backlink of the
289 * msDS-HasDomainNCs attribute
294 /* see if its already in the list */
295 for (bl=replmd_private->la_backlinks; bl; bl=bl->next) {
296 if (GUID_equal(forward_guid, &bl->forward_guid) &&
297 GUID_equal(target_guid, &bl->target_guid) &&
298 (target_attr->lDAPDisplayName == bl->attr_name ||
299 strcmp(target_attr->lDAPDisplayName, bl->attr_name) == 0)) {
305 /* we found an existing one */
306 if (bl->active == active) {
309 DLIST_REMOVE(replmd_private->la_backlinks, bl);
314 if (replmd_private->bl_ctx == NULL) {
315 replmd_private->bl_ctx = talloc_new(replmd_private);
316 if (replmd_private->bl_ctx == NULL) {
317 ldb_module_oom(module);
318 return LDB_ERR_OPERATIONS_ERROR;
323 bl = talloc(replmd_private->bl_ctx, struct la_backlink);
325 ldb_module_oom(module);
326 return LDB_ERR_OPERATIONS_ERROR;
329 /* Ensure the schema does not go away before the bl->attr_name is used */
330 if (!talloc_reference(bl, schema)) {
332 ldb_module_oom(module);
333 return LDB_ERR_OPERATIONS_ERROR;
336 bl->attr_name = target_attr->lDAPDisplayName;
337 bl->forward_guid = *forward_guid;
338 bl->target_guid = *target_guid;
341 /* the caller may ask for this backlink to be processed
344 int ret = replmd_process_backlink(module, bl, NULL);
349 DLIST_ADD(replmd_private->la_backlinks, bl);
356 * Callback for most write operations in this module:
358 * notify the repl task that a object has changed. The notifies are
359 * gathered up in the replmd_private structure then written to the
360 * @REPLCHANGED object in each partition during the prepare_commit
362 static int replmd_op_callback(struct ldb_request *req, struct ldb_reply *ares)
365 struct replmd_replicated_request *ac =
366 talloc_get_type_abort(req->context, struct replmd_replicated_request);
367 struct replmd_private *replmd_private =
368 talloc_get_type_abort(ldb_module_get_private(ac->module), struct replmd_private);
369 struct nc_entry *modified_partition;
370 struct ldb_control *partition_ctrl;
371 const struct dsdb_control_current_partition *partition;
373 struct ldb_control **controls;
375 partition_ctrl = ldb_reply_get_control(ares, DSDB_CONTROL_CURRENT_PARTITION_OID);
377 /* Remove the 'partition' control from what we pass up the chain */
378 controls = ldb_controls_except_specified(ares->controls, ares, partition_ctrl);
380 if (ares->error != LDB_SUCCESS) {
381 DEBUG(0,("%s failure. Error is: %s\n", __FUNCTION__, ldb_strerror(ares->error)));
382 return ldb_module_done(ac->req, controls,
383 ares->response, ares->error);
386 if (ares->type != LDB_REPLY_DONE) {
387 ldb_set_errstring(ldb_module_get_ctx(ac->module), "Invalid reply type for notify\n!");
388 return ldb_module_done(ac->req, NULL,
389 NULL, LDB_ERR_OPERATIONS_ERROR);
392 if (!partition_ctrl) {
393 ldb_set_errstring(ldb_module_get_ctx(ac->module),"No partition control on reply");
394 return ldb_module_done(ac->req, NULL,
395 NULL, LDB_ERR_OPERATIONS_ERROR);
398 partition = talloc_get_type_abort(partition_ctrl->data,
399 struct dsdb_control_current_partition);
401 if (ac->seq_num > 0) {
402 for (modified_partition = replmd_private->ncs; modified_partition;
403 modified_partition = modified_partition->next) {
404 if (ldb_dn_compare(modified_partition->dn, partition->dn) == 0) {
409 if (modified_partition == NULL) {
410 modified_partition = talloc_zero(replmd_private, struct nc_entry);
411 if (!modified_partition) {
412 ldb_oom(ldb_module_get_ctx(ac->module));
413 return ldb_module_done(ac->req, NULL,
414 NULL, LDB_ERR_OPERATIONS_ERROR);
416 modified_partition->dn = ldb_dn_copy(modified_partition, partition->dn);
417 if (!modified_partition->dn) {
418 ldb_oom(ldb_module_get_ctx(ac->module));
419 return ldb_module_done(ac->req, NULL,
420 NULL, LDB_ERR_OPERATIONS_ERROR);
422 DLIST_ADD(replmd_private->ncs, modified_partition);
425 if (ac->seq_num > modified_partition->mod_usn) {
426 modified_partition->mod_usn = ac->seq_num;
428 modified_partition->mod_usn_urgent = ac->seq_num;
433 if (ac->apply_mode) {
437 ret = replmd_replicated_apply_next(ac);
438 if (ret != LDB_SUCCESS) {
439 return ldb_module_done(ac->req, NULL, NULL, ret);
443 /* free the partition control container here, for the
444 * common path. Other cases will have it cleaned up
445 * eventually with the ares */
446 talloc_free(partition_ctrl);
447 return ldb_module_done(ac->req,
448 ldb_controls_except_specified(controls, ares, partition_ctrl),
449 ares->response, LDB_SUCCESS);
455 * update a @REPLCHANGED record in each partition if there have been
456 * any writes of replicated data in the partition
458 static int replmd_notify_store(struct ldb_module *module, struct ldb_request *parent)
460 struct replmd_private *replmd_private =
461 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
463 while (replmd_private->ncs) {
465 struct nc_entry *modified_partition = replmd_private->ncs;
467 ret = dsdb_module_save_partition_usn(module, modified_partition->dn,
468 modified_partition->mod_usn,
469 modified_partition->mod_usn_urgent, parent);
470 if (ret != LDB_SUCCESS) {
471 DEBUG(0,(__location__ ": Failed to save partition uSN for %s\n",
472 ldb_dn_get_linearized(modified_partition->dn)));
475 DLIST_REMOVE(replmd_private->ncs, modified_partition);
476 talloc_free(modified_partition);
484 created a replmd_replicated_request context
486 static struct replmd_replicated_request *replmd_ctx_init(struct ldb_module *module,
487 struct ldb_request *req)
489 struct ldb_context *ldb;
490 struct replmd_replicated_request *ac;
492 ldb = ldb_module_get_ctx(module);
494 ac = talloc_zero(req, struct replmd_replicated_request);
503 ac->schema = dsdb_get_schema(ldb, ac);
505 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
506 "replmd_modify: no dsdb_schema loaded");
507 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
515 add a time element to a record
517 static int add_time_element(struct ldb_message *msg, const char *attr, time_t t)
519 struct ldb_message_element *el;
523 if (ldb_msg_find_element(msg, attr) != NULL) {
527 s = ldb_timestring(msg, t);
529 return LDB_ERR_OPERATIONS_ERROR;
532 ret = ldb_msg_add_string(msg, attr, s);
533 if (ret != LDB_SUCCESS) {
537 el = ldb_msg_find_element(msg, attr);
538 /* always set as replace. This works because on add ops, the flag
540 el->flags = LDB_FLAG_MOD_REPLACE;
546 add a uint64_t element to a record
548 static int add_uint64_element(struct ldb_context *ldb, struct ldb_message *msg,
549 const char *attr, uint64_t v)
551 struct ldb_message_element *el;
554 if (ldb_msg_find_element(msg, attr) != NULL) {
558 ret = samdb_msg_add_uint64(ldb, msg, msg, attr, v);
559 if (ret != LDB_SUCCESS) {
563 el = ldb_msg_find_element(msg, attr);
564 /* always set as replace. This works because on add ops, the flag
566 el->flags = LDB_FLAG_MOD_REPLACE;
571 static int replmd_replPropertyMetaData1_attid_sort(const struct replPropertyMetaData1 *m1,
572 const struct replPropertyMetaData1 *m2,
573 const uint32_t *rdn_attid)
575 if (m1->attid == m2->attid) {
580 * the rdn attribute should be at the end!
581 * so we need to return a value greater than zero
582 * which means m1 is greater than m2
584 if (m1->attid == *rdn_attid) {
589 * the rdn attribute should be at the end!
590 * so we need to return a value less than zero
591 * which means m2 is greater than m1
593 if (m2->attid == *rdn_attid) {
597 return m1->attid > m2->attid ? 1 : -1;
600 static int replmd_replPropertyMetaDataCtr1_sort(struct replPropertyMetaDataCtr1 *ctr1,
601 const struct dsdb_schema *schema,
604 const char *rdn_name;
605 const struct dsdb_attribute *rdn_sa;
607 rdn_name = ldb_dn_get_rdn_name(dn);
609 DEBUG(0,(__location__ ": No rDN for %s?\n", ldb_dn_get_linearized(dn)));
610 return LDB_ERR_OPERATIONS_ERROR;
613 rdn_sa = dsdb_attribute_by_lDAPDisplayName(schema, rdn_name);
614 if (rdn_sa == NULL) {
615 DEBUG(0,(__location__ ": No sa found for rDN %s for %s\n", rdn_name, ldb_dn_get_linearized(dn)));
616 return LDB_ERR_OPERATIONS_ERROR;
619 DEBUG(6,("Sorting rpmd with attid exception %u rDN=%s DN=%s\n",
620 rdn_sa->attributeID_id, rdn_name, ldb_dn_get_linearized(dn)));
622 LDB_TYPESAFE_QSORT(ctr1->array, ctr1->count, &rdn_sa->attributeID_id, replmd_replPropertyMetaData1_attid_sort);
627 static int replmd_ldb_message_element_attid_sort(const struct ldb_message_element *e1,
628 const struct ldb_message_element *e2,
629 const struct dsdb_schema *schema)
631 const struct dsdb_attribute *a1;
632 const struct dsdb_attribute *a2;
635 * TODO: make this faster by caching the dsdb_attribute pointer
636 * on the ldb_messag_element
639 a1 = dsdb_attribute_by_lDAPDisplayName(schema, e1->name);
640 a2 = dsdb_attribute_by_lDAPDisplayName(schema, e2->name);
643 * TODO: remove this check, we should rely on e1 and e2 having valid attribute names
647 return strcasecmp(e1->name, e2->name);
649 if (a1->attributeID_id == a2->attributeID_id) {
652 return a1->attributeID_id > a2->attributeID_id ? 1 : -1;
655 static void replmd_ldb_message_sort(struct ldb_message *msg,
656 const struct dsdb_schema *schema)
658 LDB_TYPESAFE_QSORT(msg->elements, msg->num_elements, schema, replmd_ldb_message_element_attid_sort);
661 static int replmd_build_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
662 const struct GUID *invocation_id, uint64_t seq_num,
663 uint64_t local_usn, NTTIME nttime, uint32_t version, bool deleted);
667 fix up linked attributes in replmd_add.
668 This involves setting up the right meta-data in extended DN
669 components, and creating backlinks to the object
671 static int replmd_add_fix_la(struct ldb_module *module, struct ldb_message_element *el,
672 uint64_t seq_num, const struct GUID *invocationId, time_t t,
673 struct GUID *guid, const struct dsdb_attribute *sa, struct ldb_request *parent)
676 TALLOC_CTX *tmp_ctx = talloc_new(el->values);
677 struct ldb_context *ldb = ldb_module_get_ctx(module);
679 /* We will take a reference to the schema in replmd_add_backlink */
680 const struct dsdb_schema *schema = dsdb_get_schema(ldb, NULL);
683 unix_to_nt_time(&now, t);
685 for (i=0; i<el->num_values; i++) {
686 struct ldb_val *v = &el->values[i];
687 struct dsdb_dn *dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, v, sa->syntax->ldap_oid);
688 struct GUID target_guid;
692 /* note that the DN already has the extended
693 components from the extended_dn_store module */
694 status = dsdb_get_extended_dn_guid(dsdb_dn->dn, &target_guid, "GUID");
695 if (!NT_STATUS_IS_OK(status) || GUID_all_zero(&target_guid)) {
696 ret = dsdb_module_guid_by_dn(module, dsdb_dn->dn, &target_guid, parent);
697 if (ret != LDB_SUCCESS) {
698 talloc_free(tmp_ctx);
701 ret = dsdb_set_extended_dn_guid(dsdb_dn->dn, &target_guid, "GUID");
702 if (ret != LDB_SUCCESS) {
703 talloc_free(tmp_ctx);
708 ret = replmd_build_la_val(el->values, v, dsdb_dn, invocationId,
709 seq_num, seq_num, now, 0, false);
710 if (ret != LDB_SUCCESS) {
711 talloc_free(tmp_ctx);
715 ret = replmd_add_backlink(module, schema, guid, &target_guid, true, sa, false);
716 if (ret != LDB_SUCCESS) {
717 talloc_free(tmp_ctx);
722 talloc_free(tmp_ctx);
728 intercept add requests
730 static int replmd_add(struct ldb_module *module, struct ldb_request *req)
732 struct ldb_context *ldb;
733 struct ldb_control *control;
734 struct replmd_replicated_request *ac;
735 enum ndr_err_code ndr_err;
736 struct ldb_request *down_req;
737 struct ldb_message *msg;
738 const DATA_BLOB *guid_blob;
740 struct replPropertyMetaDataBlob nmd;
741 struct ldb_val nmd_value;
742 const struct GUID *our_invocation_id;
743 time_t t = time(NULL);
748 unsigned int functional_level;
750 bool allow_add_guid = false;
751 bool remove_current_guid = false;
752 bool is_urgent = false;
753 struct ldb_message_element *objectclass_el;
755 /* check if there's a show relax control (used by provision to say 'I know what I'm doing') */
756 control = ldb_request_get_control(req, LDB_CONTROL_RELAX_OID);
758 allow_add_guid = true;
761 /* do not manipulate our control entries */
762 if (ldb_dn_is_special(req->op.add.message->dn)) {
763 return ldb_next_request(module, req);
766 ldb = ldb_module_get_ctx(module);
768 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_add\n");
770 guid_blob = ldb_msg_find_ldb_val(req->op.add.message, "objectGUID");
771 if (guid_blob != NULL) {
772 if (!allow_add_guid) {
773 ldb_set_errstring(ldb,
774 "replmd_add: it's not allowed to add an object with objectGUID!");
775 return LDB_ERR_UNWILLING_TO_PERFORM;
777 NTSTATUS status = GUID_from_data_blob(guid_blob,&guid);
778 if (!NT_STATUS_IS_OK(status)) {
779 ldb_set_errstring(ldb,
780 "replmd_add: Unable to parse the 'objectGUID' as a GUID!");
781 return LDB_ERR_UNWILLING_TO_PERFORM;
783 /* we remove this attribute as it can be a string and
784 * will not be treated correctly and then we will re-add
785 * it later on in the good format */
786 remove_current_guid = true;
790 guid = GUID_random();
793 ac = replmd_ctx_init(module, req);
795 return ldb_module_oom(module);
798 functional_level = dsdb_functional_level(ldb);
800 /* Get a sequence number from the backend */
801 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ac->seq_num);
802 if (ret != LDB_SUCCESS) {
807 /* get our invocationId */
808 our_invocation_id = samdb_ntds_invocation_id(ldb);
809 if (!our_invocation_id) {
810 ldb_debug_set(ldb, LDB_DEBUG_ERROR,
811 "replmd_add: unable to find invocationId\n");
813 return LDB_ERR_OPERATIONS_ERROR;
816 /* we have to copy the message as the caller might have it as a const */
817 msg = ldb_msg_copy_shallow(ac, req->op.add.message);
821 return LDB_ERR_OPERATIONS_ERROR;
824 /* generated times */
825 unix_to_nt_time(&now, t);
826 time_str = ldb_timestring(msg, t);
830 return LDB_ERR_OPERATIONS_ERROR;
832 if (remove_current_guid) {
833 ldb_msg_remove_attr(msg,"objectGUID");
837 * remove autogenerated attributes
839 ldb_msg_remove_attr(msg, "whenCreated");
840 ldb_msg_remove_attr(msg, "whenChanged");
841 ldb_msg_remove_attr(msg, "uSNCreated");
842 ldb_msg_remove_attr(msg, "uSNChanged");
843 ldb_msg_remove_attr(msg, "replPropertyMetaData");
846 * readd replicated attributes
848 ret = ldb_msg_add_string(msg, "whenCreated", time_str);
849 if (ret != LDB_SUCCESS) {
855 /* build the replication meta_data */
858 nmd.ctr.ctr1.count = msg->num_elements;
859 nmd.ctr.ctr1.array = talloc_array(msg,
860 struct replPropertyMetaData1,
862 if (!nmd.ctr.ctr1.array) {
865 return LDB_ERR_OPERATIONS_ERROR;
868 for (i=0; i < msg->num_elements; i++) {
869 struct ldb_message_element *e = &msg->elements[i];
870 struct replPropertyMetaData1 *m = &nmd.ctr.ctr1.array[ni];
871 const struct dsdb_attribute *sa;
873 if (e->name[0] == '@') continue;
875 sa = dsdb_attribute_by_lDAPDisplayName(ac->schema, e->name);
877 ldb_debug_set(ldb, LDB_DEBUG_ERROR,
878 "replmd_add: attribute '%s' not defined in schema\n",
881 return LDB_ERR_NO_SUCH_ATTRIBUTE;
884 if ((sa->systemFlags & DS_FLAG_ATTR_NOT_REPLICATED) || (sa->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED)) {
885 /* if the attribute is not replicated (0x00000001)
886 * or constructed (0x00000004) it has no metadata
891 if (sa->linkID != 0 && functional_level > DS_DOMAIN_FUNCTION_2000) {
892 ret = replmd_add_fix_la(module, e, ac->seq_num, our_invocation_id, t, &guid, sa, req);
893 if (ret != LDB_SUCCESS) {
897 /* linked attributes are not stored in
898 replPropertyMetaData in FL above w2k */
902 m->attid = sa->attributeID_id;
904 m->originating_change_time = now;
905 m->originating_invocation_id = *our_invocation_id;
906 m->originating_usn = ac->seq_num;
907 m->local_usn = ac->seq_num;
911 /* fix meta data count */
912 nmd.ctr.ctr1.count = ni;
915 * sort meta data array, and move the rdn attribute entry to the end
917 ret = replmd_replPropertyMetaDataCtr1_sort(&nmd.ctr.ctr1, ac->schema, msg->dn);
918 if (ret != LDB_SUCCESS) {
923 /* generated NDR encoded values */
924 ndr_err = ndr_push_struct_blob(&nmd_value, msg,
926 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
927 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
930 return LDB_ERR_OPERATIONS_ERROR;
934 * add the autogenerated values
936 ret = dsdb_msg_add_guid(msg, &guid, "objectGUID");
937 if (ret != LDB_SUCCESS) {
942 ret = ldb_msg_add_string(msg, "whenChanged", time_str);
943 if (ret != LDB_SUCCESS) {
948 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", ac->seq_num);
949 if (ret != LDB_SUCCESS) {
954 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ac->seq_num);
955 if (ret != LDB_SUCCESS) {
960 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
961 if (ret != LDB_SUCCESS) {
968 * sort the attributes by attid before storing the object
970 replmd_ldb_message_sort(msg, ac->schema);
972 objectclass_el = ldb_msg_find_element(msg, "objectClass");
973 is_urgent = replmd_check_urgent_objectclass(objectclass_el,
974 REPL_URGENT_ON_CREATE);
976 ac->is_urgent = is_urgent;
977 ret = ldb_build_add_req(&down_req, ldb, ac,
980 ac, replmd_op_callback,
983 LDB_REQ_SET_LOCATION(down_req);
984 if (ret != LDB_SUCCESS) {
989 if (functional_level == DS_DOMAIN_FUNCTION_2000) {
990 ret = ldb_request_add_control(down_req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
991 if (ret != LDB_SUCCESS) {
997 /* mark the control done */
999 control->critical = 0;
1002 /* go on with the call chain */
1003 return ldb_next_request(module, down_req);
1008 * update the replPropertyMetaData for one element
1010 static int replmd_update_rpmd_element(struct ldb_context *ldb,
1011 struct ldb_message *msg,
1012 struct ldb_message_element *el,
1013 struct ldb_message_element *old_el,
1014 struct replPropertyMetaDataBlob *omd,
1015 const struct dsdb_schema *schema,
1017 const struct GUID *our_invocation_id,
1021 const struct dsdb_attribute *a;
1022 struct replPropertyMetaData1 *md1;
1024 a = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
1026 DEBUG(0,(__location__ ": Unable to find attribute %s in schema\n",
1028 return LDB_ERR_OPERATIONS_ERROR;
1031 if ((a->systemFlags & DS_FLAG_ATTR_NOT_REPLICATED) || (a->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED)) {
1035 /* if the attribute's value haven't changed then return LDB_SUCCESS */
1036 if (old_el != NULL && ldb_msg_element_compare(el, old_el) == 0) {
1040 for (i=0; i<omd->ctr.ctr1.count; i++) {
1041 if (a->attributeID_id == omd->ctr.ctr1.array[i].attid) break;
1044 if (a->linkID != 0 && dsdb_functional_level(ldb) > DS_DOMAIN_FUNCTION_2000) {
1045 /* linked attributes are not stored in
1046 replPropertyMetaData in FL above w2k, but we do
1047 raise the seqnum for the object */
1048 if (*seq_num == 0 &&
1049 ldb_sequence_number(ldb, LDB_SEQ_NEXT, seq_num) != LDB_SUCCESS) {
1050 return LDB_ERR_OPERATIONS_ERROR;
1055 if (i == omd->ctr.ctr1.count) {
1056 /* we need to add a new one */
1057 omd->ctr.ctr1.array = talloc_realloc(msg, omd->ctr.ctr1.array,
1058 struct replPropertyMetaData1, omd->ctr.ctr1.count+1);
1059 if (omd->ctr.ctr1.array == NULL) {
1061 return LDB_ERR_OPERATIONS_ERROR;
1063 omd->ctr.ctr1.count++;
1064 ZERO_STRUCT(omd->ctr.ctr1.array[i]);
1067 /* Get a new sequence number from the backend. We only do this
1068 * if we have a change that requires a new
1069 * replPropertyMetaData element
1071 if (*seq_num == 0) {
1072 int ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, seq_num);
1073 if (ret != LDB_SUCCESS) {
1074 return LDB_ERR_OPERATIONS_ERROR;
1078 md1 = &omd->ctr.ctr1.array[i];
1080 md1->attid = a->attributeID_id;
1081 md1->originating_change_time = now;
1082 md1->originating_invocation_id = *our_invocation_id;
1083 md1->originating_usn = *seq_num;
1084 md1->local_usn = *seq_num;
1089 static uint64_t find_max_local_usn(struct replPropertyMetaDataBlob omd)
1091 uint32_t count = omd.ctr.ctr1.count;
1094 for (i=0; i < count; i++) {
1095 struct replPropertyMetaData1 m = omd.ctr.ctr1.array[i];
1096 if (max < m.local_usn) {
1104 * update the replPropertyMetaData object each time we modify an
1105 * object. This is needed for DRS replication, as the merge on the
1106 * client is based on this object
1108 static int replmd_update_rpmd(struct ldb_module *module,
1109 const struct dsdb_schema *schema,
1110 struct ldb_request *req,
1111 struct ldb_message *msg, uint64_t *seq_num,
1115 const struct ldb_val *omd_value;
1116 enum ndr_err_code ndr_err;
1117 struct replPropertyMetaDataBlob omd;
1120 const struct GUID *our_invocation_id;
1122 const char *attrs[] = { "replPropertyMetaData", "*", NULL };
1123 const char *attrs2[] = { "uSNChanged", "objectClass", NULL };
1124 struct ldb_result *res;
1125 struct ldb_context *ldb;
1126 struct ldb_message_element *objectclass_el;
1127 enum urgent_situation situation;
1128 bool rodc, rmd_is_provided;
1130 ldb = ldb_module_get_ctx(module);
1132 our_invocation_id = samdb_ntds_invocation_id(ldb);
1133 if (!our_invocation_id) {
1134 /* this happens during an initial vampire while
1135 updating the schema */
1136 DEBUG(5,("No invocationID - skipping replPropertyMetaData update\n"));
1140 unix_to_nt_time(&now, t);
1142 if (ldb_request_get_control(req, DSDB_CONTROL_CHANGEREPLMETADATA_OID)) {
1143 rmd_is_provided = true;
1145 rmd_is_provided = false;
1148 /* if isDeleted is present and is TRUE, then we consider we are deleting,
1149 * otherwise we consider we are updating */
1150 if (ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")) {
1151 situation = REPL_URGENT_ON_DELETE;
1153 situation = REPL_URGENT_ON_UPDATE;
1156 if (rmd_is_provided) {
1157 /* In this case the change_replmetadata control was supplied */
1158 /* We check that it's the only attribute that is provided
1159 * (it's a rare case so it's better to keep the code simplier)
1160 * We also check that the highest local_usn is bigger than
1163 if( msg->num_elements != 1 ||
1164 strncmp(msg->elements[0].name,
1165 "replPropertyMetaData", 20) ) {
1166 DEBUG(0,(__location__ ": changereplmetada control called without "\
1167 "a specified replPropertyMetaData attribute or with others\n"));
1168 return LDB_ERR_OPERATIONS_ERROR;
1170 if (situation == REPL_URGENT_ON_DELETE) {
1171 DEBUG(0,(__location__ ": changereplmetada control can't be called when deleting an object\n"));
1172 return LDB_ERR_OPERATIONS_ERROR;
1174 omd_value = ldb_msg_find_ldb_val(msg, "replPropertyMetaData");
1176 DEBUG(0,(__location__ ": replPropertyMetaData was not specified for Object %s\n",
1177 ldb_dn_get_linearized(msg->dn)));
1178 return LDB_ERR_OPERATIONS_ERROR;
1180 ndr_err = ndr_pull_struct_blob(omd_value, msg, &omd,
1181 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
1182 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1183 DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s\n",
1184 ldb_dn_get_linearized(msg->dn)));
1185 return LDB_ERR_OPERATIONS_ERROR;
1187 *seq_num = find_max_local_usn(omd);
1189 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attrs2,
1190 DSDB_FLAG_NEXT_MODULE |
1191 DSDB_SEARCH_SHOW_RECYCLED |
1192 DSDB_SEARCH_SHOW_EXTENDED_DN |
1193 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
1194 DSDB_SEARCH_REVEAL_INTERNALS, req);
1196 if (ret != LDB_SUCCESS || res->count != 1) {
1197 DEBUG(0,(__location__ ": Object %s failed to find uSNChanged\n",
1198 ldb_dn_get_linearized(msg->dn)));
1199 return LDB_ERR_OPERATIONS_ERROR;
1202 objectclass_el = ldb_msg_find_element(res->msgs[0], "objectClass");
1203 if (is_urgent && replmd_check_urgent_objectclass(objectclass_el,
1208 db_seq = ldb_msg_find_attr_as_uint64(res->msgs[0], "uSNChanged", 0);
1209 if (*seq_num <= db_seq) {
1210 DEBUG(0,(__location__ ": changereplmetada control provided but max(local_usn)"\
1211 " is less or equal to uSNChanged (max = %lld uSNChanged = %lld)\n",
1212 (long long)*seq_num, (long long)db_seq));
1213 return LDB_ERR_OPERATIONS_ERROR;
1217 /* search for the existing replPropertyMetaDataBlob. We need
1218 * to use REVEAL and ask for DNs in storage format to support
1219 * the check for values being the same in
1220 * replmd_update_rpmd_element()
1222 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attrs,
1223 DSDB_FLAG_NEXT_MODULE |
1224 DSDB_SEARCH_SHOW_RECYCLED |
1225 DSDB_SEARCH_SHOW_EXTENDED_DN |
1226 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
1227 DSDB_SEARCH_REVEAL_INTERNALS, req);
1228 if (ret != LDB_SUCCESS || res->count != 1) {
1229 DEBUG(0,(__location__ ": Object %s failed to find replPropertyMetaData\n",
1230 ldb_dn_get_linearized(msg->dn)));
1231 return LDB_ERR_OPERATIONS_ERROR;
1234 objectclass_el = ldb_msg_find_element(res->msgs[0], "objectClass");
1235 if (is_urgent && replmd_check_urgent_objectclass(objectclass_el,
1240 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
1242 DEBUG(0,(__location__ ": Object %s does not have a replPropertyMetaData attribute\n",
1243 ldb_dn_get_linearized(msg->dn)));
1244 return LDB_ERR_OPERATIONS_ERROR;
1247 ndr_err = ndr_pull_struct_blob(omd_value, msg, &omd,
1248 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
1249 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1250 DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s\n",
1251 ldb_dn_get_linearized(msg->dn)));
1252 return LDB_ERR_OPERATIONS_ERROR;
1255 if (omd.version != 1) {
1256 DEBUG(0,(__location__ ": bad version %u in replPropertyMetaData for %s\n",
1257 omd.version, ldb_dn_get_linearized(msg->dn)));
1258 return LDB_ERR_OPERATIONS_ERROR;
1261 for (i=0; i<msg->num_elements; i++) {
1262 struct ldb_message_element *old_el;
1263 old_el = ldb_msg_find_element(res->msgs[0], msg->elements[i].name);
1264 ret = replmd_update_rpmd_element(ldb, msg, &msg->elements[i], old_el, &omd, schema, seq_num,
1265 our_invocation_id, now);
1266 if (ret != LDB_SUCCESS) {
1270 if (is_urgent && !*is_urgent && (situation == REPL_URGENT_ON_UPDATE)) {
1271 *is_urgent = replmd_check_urgent_attribute(&msg->elements[i]);
1277 * replmd_update_rpmd_element has done an update if the
1280 if (*seq_num != 0) {
1281 struct ldb_val *md_value;
1282 struct ldb_message_element *el;
1284 /*if we are RODC and this is a DRSR update then its ok*/
1285 if (!ldb_request_get_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID)) {
1286 ret = samdb_rodc(ldb, &rodc);
1287 if (ret != LDB_SUCCESS) {
1288 DEBUG(4, (__location__ ": unable to tell if we are an RODC\n"));
1290 ldb_asprintf_errstring(ldb, "RODC modify is forbidden\n");
1291 return LDB_ERR_REFERRAL;
1295 md_value = talloc(msg, struct ldb_val);
1296 if (md_value == NULL) {
1298 return LDB_ERR_OPERATIONS_ERROR;
1301 ret = replmd_replPropertyMetaDataCtr1_sort(&omd.ctr.ctr1, schema, msg->dn);
1302 if (ret != LDB_SUCCESS) {
1306 ndr_err = ndr_push_struct_blob(md_value, msg, &omd,
1307 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
1308 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1309 DEBUG(0,(__location__ ": Failed to marshall replPropertyMetaData for %s\n",
1310 ldb_dn_get_linearized(msg->dn)));
1311 return LDB_ERR_OPERATIONS_ERROR;
1314 ret = ldb_msg_add_empty(msg, "replPropertyMetaData", LDB_FLAG_MOD_REPLACE, &el);
1315 if (ret != LDB_SUCCESS) {
1316 DEBUG(0,(__location__ ": Failed to add updated replPropertyMetaData %s\n",
1317 ldb_dn_get_linearized(msg->dn)));
1322 el->values = md_value;
1329 struct dsdb_dn *dsdb_dn;
1334 static int parsed_dn_compare(struct parsed_dn *pdn1, struct parsed_dn *pdn2)
1336 return GUID_compare(pdn1->guid, pdn2->guid);
1339 static struct parsed_dn *parsed_dn_find(struct parsed_dn *pdn,
1340 unsigned int count, struct GUID *guid,
1343 struct parsed_dn *ret;
1345 if (dn && GUID_all_zero(guid)) {
1346 /* when updating a link using DRS, we sometimes get a
1347 NULL GUID. We then need to try and match by DN */
1348 for (i=0; i<count; i++) {
1349 if (ldb_dn_compare(pdn[i].dsdb_dn->dn, dn) == 0) {
1350 dsdb_get_extended_dn_guid(pdn[i].dsdb_dn->dn, guid, "GUID");
1356 BINARY_ARRAY_SEARCH(pdn, count, guid, guid, GUID_compare, ret);
1361 get a series of message element values as an array of DNs and GUIDs
1362 the result is sorted by GUID
1364 static int get_parsed_dns(struct ldb_module *module, TALLOC_CTX *mem_ctx,
1365 struct ldb_message_element *el, struct parsed_dn **pdn,
1366 const char *ldap_oid, struct ldb_request *parent)
1369 struct ldb_context *ldb = ldb_module_get_ctx(module);
1376 (*pdn) = talloc_array(mem_ctx, struct parsed_dn, el->num_values);
1378 ldb_module_oom(module);
1379 return LDB_ERR_OPERATIONS_ERROR;
1382 for (i=0; i<el->num_values; i++) {
1383 struct ldb_val *v = &el->values[i];
1386 struct parsed_dn *p;
1390 p->dsdb_dn = dsdb_dn_parse(*pdn, ldb, v, ldap_oid);
1391 if (p->dsdb_dn == NULL) {
1392 return LDB_ERR_INVALID_DN_SYNTAX;
1395 dn = p->dsdb_dn->dn;
1397 p->guid = talloc(*pdn, struct GUID);
1398 if (p->guid == NULL) {
1399 ldb_module_oom(module);
1400 return LDB_ERR_OPERATIONS_ERROR;
1403 status = dsdb_get_extended_dn_guid(dn, p->guid, "GUID");
1404 if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
1405 /* we got a DN without a GUID - go find the GUID */
1406 int ret = dsdb_module_guid_by_dn(module, dn, p->guid, parent);
1407 if (ret != LDB_SUCCESS) {
1408 ldb_asprintf_errstring(ldb, "Unable to find GUID for DN %s\n",
1409 ldb_dn_get_linearized(dn));
1412 ret = dsdb_set_extended_dn_guid(dn, p->guid, "GUID");
1413 if (ret != LDB_SUCCESS) {
1416 } else if (!NT_STATUS_IS_OK(status)) {
1417 return LDB_ERR_OPERATIONS_ERROR;
1420 /* keep a pointer to the original ldb_val */
1424 TYPESAFE_QSORT(*pdn, el->num_values, parsed_dn_compare);
1430 build a new extended DN, including all meta data fields
1432 RMD_FLAGS = DSDB_RMD_FLAG_* bits
1433 RMD_ADDTIME = originating_add_time
1434 RMD_INVOCID = originating_invocation_id
1435 RMD_CHANGETIME = originating_change_time
1436 RMD_ORIGINATING_USN = originating_usn
1437 RMD_LOCAL_USN = local_usn
1438 RMD_VERSION = version
1440 static int replmd_build_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
1441 const struct GUID *invocation_id, uint64_t seq_num,
1442 uint64_t local_usn, NTTIME nttime, uint32_t version, bool deleted)
1444 struct ldb_dn *dn = dsdb_dn->dn;
1445 const char *tstring, *usn_string, *flags_string;
1446 struct ldb_val tval;
1448 struct ldb_val usnv, local_usnv;
1449 struct ldb_val vers, flagsv;
1452 const char *dnstring;
1454 uint32_t rmd_flags = deleted?DSDB_RMD_FLAG_DELETED:0;
1456 tstring = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)nttime);
1458 return LDB_ERR_OPERATIONS_ERROR;
1460 tval = data_blob_string_const(tstring);
1462 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)seq_num);
1464 return LDB_ERR_OPERATIONS_ERROR;
1466 usnv = data_blob_string_const(usn_string);
1468 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)local_usn);
1470 return LDB_ERR_OPERATIONS_ERROR;
1472 local_usnv = data_blob_string_const(usn_string);
1474 vstring = talloc_asprintf(mem_ctx, "%lu", (unsigned long)version);
1476 return LDB_ERR_OPERATIONS_ERROR;
1478 vers = data_blob_string_const(vstring);
1480 status = GUID_to_ndr_blob(invocation_id, dn, &iid);
1481 if (!NT_STATUS_IS_OK(status)) {
1482 return LDB_ERR_OPERATIONS_ERROR;
1485 flags_string = talloc_asprintf(mem_ctx, "%u", rmd_flags);
1486 if (!flags_string) {
1487 return LDB_ERR_OPERATIONS_ERROR;
1489 flagsv = data_blob_string_const(flags_string);
1491 ret = ldb_dn_set_extended_component(dn, "RMD_FLAGS", &flagsv);
1492 if (ret != LDB_SUCCESS) return ret;
1493 ret = ldb_dn_set_extended_component(dn, "RMD_ADDTIME", &tval);
1494 if (ret != LDB_SUCCESS) return ret;
1495 ret = ldb_dn_set_extended_component(dn, "RMD_INVOCID", &iid);
1496 if (ret != LDB_SUCCESS) return ret;
1497 ret = ldb_dn_set_extended_component(dn, "RMD_CHANGETIME", &tval);
1498 if (ret != LDB_SUCCESS) return ret;
1499 ret = ldb_dn_set_extended_component(dn, "RMD_LOCAL_USN", &local_usnv);
1500 if (ret != LDB_SUCCESS) return ret;
1501 ret = ldb_dn_set_extended_component(dn, "RMD_ORIGINATING_USN", &usnv);
1502 if (ret != LDB_SUCCESS) return ret;
1503 ret = ldb_dn_set_extended_component(dn, "RMD_VERSION", &vers);
1504 if (ret != LDB_SUCCESS) return ret;
1506 dnstring = dsdb_dn_get_extended_linearized(mem_ctx, dsdb_dn, 1);
1507 if (dnstring == NULL) {
1508 return LDB_ERR_OPERATIONS_ERROR;
1510 *v = data_blob_string_const(dnstring);
1515 static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
1516 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
1517 uint64_t seq_num, uint64_t local_usn, NTTIME nttime,
1518 uint32_t version, bool deleted);
1521 check if any links need upgrading from w2k format
1523 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.
1525 static int replmd_check_upgrade_links(struct parsed_dn *dns, uint32_t count, struct ldb_message_element *parent_ctx, const struct GUID *invocation_id)
1528 for (i=0; i<count; i++) {
1533 status = dsdb_get_extended_dn_uint32(dns[i].dsdb_dn->dn, &version, "RMD_VERSION");
1534 if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
1538 /* it's an old one that needs upgrading */
1539 ret = replmd_update_la_val(parent_ctx->values, dns[i].v, dns[i].dsdb_dn, dns[i].dsdb_dn, invocation_id,
1541 if (ret != LDB_SUCCESS) {
1549 update an extended DN, including all meta data fields
1551 see replmd_build_la_val for value names
1553 static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
1554 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
1555 uint64_t seq_num, uint64_t local_usn, NTTIME nttime,
1556 uint32_t version, bool deleted)
1558 struct ldb_dn *dn = dsdb_dn->dn;
1559 const char *tstring, *usn_string, *flags_string;
1560 struct ldb_val tval;
1562 struct ldb_val usnv, local_usnv;
1563 struct ldb_val vers, flagsv;
1564 const struct ldb_val *old_addtime;
1565 uint32_t old_version;
1568 const char *dnstring;
1570 uint32_t rmd_flags = deleted?DSDB_RMD_FLAG_DELETED:0;
1572 tstring = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)nttime);
1574 return LDB_ERR_OPERATIONS_ERROR;
1576 tval = data_blob_string_const(tstring);
1578 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)seq_num);
1580 return LDB_ERR_OPERATIONS_ERROR;
1582 usnv = data_blob_string_const(usn_string);
1584 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)local_usn);
1586 return LDB_ERR_OPERATIONS_ERROR;
1588 local_usnv = data_blob_string_const(usn_string);
1590 status = GUID_to_ndr_blob(invocation_id, dn, &iid);
1591 if (!NT_STATUS_IS_OK(status)) {
1592 return LDB_ERR_OPERATIONS_ERROR;
1595 flags_string = talloc_asprintf(mem_ctx, "%u", rmd_flags);
1596 if (!flags_string) {
1597 return LDB_ERR_OPERATIONS_ERROR;
1599 flagsv = data_blob_string_const(flags_string);
1601 ret = ldb_dn_set_extended_component(dn, "RMD_FLAGS", &flagsv);
1602 if (ret != LDB_SUCCESS) return ret;
1604 /* get the ADDTIME from the original */
1605 old_addtime = ldb_dn_get_extended_component(old_dsdb_dn->dn, "RMD_ADDTIME");
1606 if (old_addtime == NULL) {
1607 old_addtime = &tval;
1609 if (dsdb_dn != old_dsdb_dn) {
1610 ret = ldb_dn_set_extended_component(dn, "RMD_ADDTIME", old_addtime);
1611 if (ret != LDB_SUCCESS) return ret;
1614 /* use our invocation id */
1615 ret = ldb_dn_set_extended_component(dn, "RMD_INVOCID", &iid);
1616 if (ret != LDB_SUCCESS) return ret;
1618 /* changetime is the current time */
1619 ret = ldb_dn_set_extended_component(dn, "RMD_CHANGETIME", &tval);
1620 if (ret != LDB_SUCCESS) return ret;
1622 /* update the USN */
1623 ret = ldb_dn_set_extended_component(dn, "RMD_ORIGINATING_USN", &usnv);
1624 if (ret != LDB_SUCCESS) return ret;
1626 ret = ldb_dn_set_extended_component(dn, "RMD_LOCAL_USN", &local_usnv);
1627 if (ret != LDB_SUCCESS) return ret;
1629 /* increase the version by 1 */
1630 status = dsdb_get_extended_dn_uint32(old_dsdb_dn->dn, &old_version, "RMD_VERSION");
1631 if (NT_STATUS_IS_OK(status) && old_version >= version) {
1632 version = old_version+1;
1634 vstring = talloc_asprintf(dn, "%lu", (unsigned long)version);
1635 vers = data_blob_string_const(vstring);
1636 ret = ldb_dn_set_extended_component(dn, "RMD_VERSION", &vers);
1637 if (ret != LDB_SUCCESS) return ret;
1639 dnstring = dsdb_dn_get_extended_linearized(mem_ctx, dsdb_dn, 1);
1640 if (dnstring == NULL) {
1641 return LDB_ERR_OPERATIONS_ERROR;
1643 *v = data_blob_string_const(dnstring);
1649 handle adding a linked attribute
1651 static int replmd_modify_la_add(struct ldb_module *module,
1652 const struct dsdb_schema *schema,
1653 struct ldb_message *msg,
1654 struct ldb_message_element *el,
1655 struct ldb_message_element *old_el,
1656 const struct dsdb_attribute *schema_attr,
1659 struct GUID *msg_guid,
1660 struct ldb_request *parent)
1663 struct parsed_dn *dns, *old_dns;
1664 TALLOC_CTX *tmp_ctx = talloc_new(msg);
1666 struct ldb_val *new_values = NULL;
1667 unsigned int num_new_values = 0;
1668 unsigned old_num_values = old_el?old_el->num_values:0;
1669 const struct GUID *invocation_id;
1670 struct ldb_context *ldb = ldb_module_get_ctx(module);
1673 unix_to_nt_time(&now, t);
1675 ret = get_parsed_dns(module, tmp_ctx, el, &dns, schema_attr->syntax->ldap_oid, parent);
1676 if (ret != LDB_SUCCESS) {
1677 talloc_free(tmp_ctx);
1681 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns, schema_attr->syntax->ldap_oid, parent);
1682 if (ret != LDB_SUCCESS) {
1683 talloc_free(tmp_ctx);
1687 invocation_id = samdb_ntds_invocation_id(ldb);
1688 if (!invocation_id) {
1689 talloc_free(tmp_ctx);
1690 return LDB_ERR_OPERATIONS_ERROR;
1693 ret = replmd_check_upgrade_links(old_dns, old_num_values, old_el, invocation_id);
1694 if (ret != LDB_SUCCESS) {
1695 talloc_free(tmp_ctx);
1699 /* for each new value, see if it exists already with the same GUID */
1700 for (i=0; i<el->num_values; i++) {
1701 struct parsed_dn *p = parsed_dn_find(old_dns, old_num_values, dns[i].guid, NULL);
1703 /* this is a new linked attribute value */
1704 new_values = talloc_realloc(tmp_ctx, new_values, struct ldb_val, num_new_values+1);
1705 if (new_values == NULL) {
1706 ldb_module_oom(module);
1707 talloc_free(tmp_ctx);
1708 return LDB_ERR_OPERATIONS_ERROR;
1710 ret = replmd_build_la_val(new_values, &new_values[num_new_values], dns[i].dsdb_dn,
1711 invocation_id, seq_num, seq_num, now, 0, false);
1712 if (ret != LDB_SUCCESS) {
1713 talloc_free(tmp_ctx);
1718 /* this is only allowed if the GUID was
1719 previously deleted. */
1720 uint32_t rmd_flags = dsdb_dn_rmd_flags(p->dsdb_dn->dn);
1722 if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
1723 ldb_asprintf_errstring(ldb, "Attribute %s already exists for target GUID %s",
1724 el->name, GUID_string(tmp_ctx, p->guid));
1725 talloc_free(tmp_ctx);
1726 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
1728 ret = replmd_update_la_val(old_el->values, p->v, dns[i].dsdb_dn, p->dsdb_dn,
1729 invocation_id, seq_num, seq_num, now, 0, false);
1730 if (ret != LDB_SUCCESS) {
1731 talloc_free(tmp_ctx);
1736 ret = replmd_add_backlink(module, schema, msg_guid, dns[i].guid, true, schema_attr, true);
1737 if (ret != LDB_SUCCESS) {
1738 talloc_free(tmp_ctx);
1743 /* add the new ones on to the end of the old values, constructing a new el->values */
1744 el->values = talloc_realloc(msg->elements, old_el?old_el->values:NULL,
1746 old_num_values+num_new_values);
1747 if (el->values == NULL) {
1748 ldb_module_oom(module);
1749 return LDB_ERR_OPERATIONS_ERROR;
1752 memcpy(&el->values[old_num_values], new_values, num_new_values*sizeof(struct ldb_val));
1753 el->num_values = old_num_values + num_new_values;
1755 talloc_steal(msg->elements, el->values);
1756 talloc_steal(el->values, new_values);
1758 talloc_free(tmp_ctx);
1760 /* we now tell the backend to replace all existing values
1761 with the one we have constructed */
1762 el->flags = LDB_FLAG_MOD_REPLACE;
1769 handle deleting all active linked attributes
1771 static int replmd_modify_la_delete(struct ldb_module *module,
1772 const struct dsdb_schema *schema,
1773 struct ldb_message *msg,
1774 struct ldb_message_element *el,
1775 struct ldb_message_element *old_el,
1776 const struct dsdb_attribute *schema_attr,
1779 struct GUID *msg_guid,
1780 struct ldb_request *parent)
1783 struct parsed_dn *dns, *old_dns;
1784 TALLOC_CTX *tmp_ctx = talloc_new(msg);
1786 const struct GUID *invocation_id;
1787 struct ldb_context *ldb = ldb_module_get_ctx(module);
1790 unix_to_nt_time(&now, t);
1792 /* check if there is nothing to delete */
1793 if ((!old_el || old_el->num_values == 0) &&
1794 el->num_values == 0) {
1798 if (!old_el || old_el->num_values == 0) {
1799 return LDB_ERR_NO_SUCH_ATTRIBUTE;
1802 ret = get_parsed_dns(module, tmp_ctx, el, &dns, schema_attr->syntax->ldap_oid, parent);
1803 if (ret != LDB_SUCCESS) {
1804 talloc_free(tmp_ctx);
1808 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns, schema_attr->syntax->ldap_oid, parent);
1809 if (ret != LDB_SUCCESS) {
1810 talloc_free(tmp_ctx);
1814 invocation_id = samdb_ntds_invocation_id(ldb);
1815 if (!invocation_id) {
1816 return LDB_ERR_OPERATIONS_ERROR;
1819 ret = replmd_check_upgrade_links(old_dns, old_el->num_values, old_el, invocation_id);
1820 if (ret != LDB_SUCCESS) {
1821 talloc_free(tmp_ctx);
1827 /* see if we are being asked to delete any links that
1828 don't exist or are already deleted */
1829 for (i=0; i<el->num_values; i++) {
1830 struct parsed_dn *p = &dns[i];
1831 struct parsed_dn *p2;
1834 p2 = parsed_dn_find(old_dns, old_el->num_values, p->guid, NULL);
1836 ldb_asprintf_errstring(ldb, "Attribute %s doesn't exist for target GUID %s",
1837 el->name, GUID_string(tmp_ctx, p->guid));
1838 return LDB_ERR_NO_SUCH_ATTRIBUTE;
1840 rmd_flags = dsdb_dn_rmd_flags(p2->dsdb_dn->dn);
1841 if (rmd_flags & DSDB_RMD_FLAG_DELETED) {
1842 ldb_asprintf_errstring(ldb, "Attribute %s already deleted for target GUID %s",
1843 el->name, GUID_string(tmp_ctx, p->guid));
1844 return LDB_ERR_NO_SUCH_ATTRIBUTE;
1848 /* for each new value, see if it exists already with the same GUID
1849 if it is not already deleted and matches the delete list then delete it
1851 for (i=0; i<old_el->num_values; i++) {
1852 struct parsed_dn *p = &old_dns[i];
1855 if (el->num_values && parsed_dn_find(dns, el->num_values, p->guid, NULL) == NULL) {
1859 rmd_flags = dsdb_dn_rmd_flags(p->dsdb_dn->dn);
1860 if (rmd_flags & DSDB_RMD_FLAG_DELETED) continue;
1862 ret = replmd_update_la_val(old_el->values, p->v, p->dsdb_dn, p->dsdb_dn,
1863 invocation_id, seq_num, seq_num, now, 0, true);
1864 if (ret != LDB_SUCCESS) {
1865 talloc_free(tmp_ctx);
1869 ret = replmd_add_backlink(module, schema, msg_guid, old_dns[i].guid, false, schema_attr, true);
1870 if (ret != LDB_SUCCESS) {
1871 talloc_free(tmp_ctx);
1876 el->values = talloc_steal(msg->elements, old_el->values);
1877 el->num_values = old_el->num_values;
1879 talloc_free(tmp_ctx);
1881 /* we now tell the backend to replace all existing values
1882 with the one we have constructed */
1883 el->flags = LDB_FLAG_MOD_REPLACE;
1889 handle replacing a linked attribute
1891 static int replmd_modify_la_replace(struct ldb_module *module,
1892 const struct dsdb_schema *schema,
1893 struct ldb_message *msg,
1894 struct ldb_message_element *el,
1895 struct ldb_message_element *old_el,
1896 const struct dsdb_attribute *schema_attr,
1899 struct GUID *msg_guid,
1900 struct ldb_request *parent)
1903 struct parsed_dn *dns, *old_dns;
1904 TALLOC_CTX *tmp_ctx = talloc_new(msg);
1906 const struct GUID *invocation_id;
1907 struct ldb_context *ldb = ldb_module_get_ctx(module);
1908 struct ldb_val *new_values = NULL;
1909 unsigned int num_new_values = 0;
1910 unsigned int old_num_values = old_el?old_el->num_values:0;
1913 unix_to_nt_time(&now, t);
1915 /* check if there is nothing to replace */
1916 if ((!old_el || old_el->num_values == 0) &&
1917 el->num_values == 0) {
1921 ret = get_parsed_dns(module, tmp_ctx, el, &dns, schema_attr->syntax->ldap_oid, parent);
1922 if (ret != LDB_SUCCESS) {
1923 talloc_free(tmp_ctx);
1927 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns, schema_attr->syntax->ldap_oid, parent);
1928 if (ret != LDB_SUCCESS) {
1929 talloc_free(tmp_ctx);
1933 invocation_id = samdb_ntds_invocation_id(ldb);
1934 if (!invocation_id) {
1935 return LDB_ERR_OPERATIONS_ERROR;
1938 ret = replmd_check_upgrade_links(old_dns, old_num_values, old_el, invocation_id);
1939 if (ret != LDB_SUCCESS) {
1940 talloc_free(tmp_ctx);
1944 /* mark all the old ones as deleted */
1945 for (i=0; i<old_num_values; i++) {
1946 struct parsed_dn *old_p = &old_dns[i];
1947 struct parsed_dn *p;
1948 uint32_t rmd_flags = dsdb_dn_rmd_flags(old_p->dsdb_dn->dn);
1950 if (rmd_flags & DSDB_RMD_FLAG_DELETED) continue;
1952 ret = replmd_add_backlink(module, schema, msg_guid, old_dns[i].guid, false, schema_attr, false);
1953 if (ret != LDB_SUCCESS) {
1954 talloc_free(tmp_ctx);
1958 p = parsed_dn_find(dns, el->num_values, old_p->guid, NULL);
1960 /* we don't delete it if we are re-adding it */
1964 ret = replmd_update_la_val(old_el->values, old_p->v, old_p->dsdb_dn, old_p->dsdb_dn,
1965 invocation_id, seq_num, seq_num, now, 0, true);
1966 if (ret != LDB_SUCCESS) {
1967 talloc_free(tmp_ctx);
1972 /* for each new value, either update its meta-data, or add it
1975 for (i=0; i<el->num_values; i++) {
1976 struct parsed_dn *p = &dns[i], *old_p;
1979 (old_p = parsed_dn_find(old_dns,
1980 old_num_values, p->guid, NULL)) != NULL) {
1981 /* update in place */
1982 ret = replmd_update_la_val(old_el->values, old_p->v, old_p->dsdb_dn,
1983 old_p->dsdb_dn, invocation_id,
1984 seq_num, seq_num, now, 0, false);
1985 if (ret != LDB_SUCCESS) {
1986 talloc_free(tmp_ctx);
1991 new_values = talloc_realloc(tmp_ctx, new_values, struct ldb_val,
1993 if (new_values == NULL) {
1994 ldb_module_oom(module);
1995 talloc_free(tmp_ctx);
1996 return LDB_ERR_OPERATIONS_ERROR;
1998 ret = replmd_build_la_val(new_values, &new_values[num_new_values], dns[i].dsdb_dn,
1999 invocation_id, seq_num, seq_num, now, 0, false);
2000 if (ret != LDB_SUCCESS) {
2001 talloc_free(tmp_ctx);
2007 ret = replmd_add_backlink(module, schema, msg_guid, dns[i].guid, true, schema_attr, false);
2008 if (ret != LDB_SUCCESS) {
2009 talloc_free(tmp_ctx);
2014 /* add the new values to the end of old_el */
2015 if (num_new_values != 0) {
2016 el->values = talloc_realloc(msg->elements, old_el?old_el->values:NULL,
2017 struct ldb_val, old_num_values+num_new_values);
2018 if (el->values == NULL) {
2019 ldb_module_oom(module);
2020 return LDB_ERR_OPERATIONS_ERROR;
2022 memcpy(&el->values[old_num_values], &new_values[0],
2023 sizeof(struct ldb_val)*num_new_values);
2024 el->num_values = old_num_values + num_new_values;
2025 talloc_steal(msg->elements, new_values);
2027 el->values = old_el->values;
2028 el->num_values = old_el->num_values;
2029 talloc_steal(msg->elements, el->values);
2032 talloc_free(tmp_ctx);
2034 /* we now tell the backend to replace all existing values
2035 with the one we have constructed */
2036 el->flags = LDB_FLAG_MOD_REPLACE;
2043 handle linked attributes in modify requests
2045 static int replmd_modify_handle_linked_attribs(struct ldb_module *module,
2046 struct ldb_message *msg,
2047 uint64_t seq_num, time_t t,
2048 struct ldb_request *parent)
2050 struct ldb_result *res;
2053 struct ldb_context *ldb = ldb_module_get_ctx(module);
2054 struct ldb_message *old_msg;
2056 const struct dsdb_schema *schema;
2057 struct GUID old_guid;
2060 /* there the replmd_update_rpmd code has already
2061 * checked and saw that there are no linked
2066 if (dsdb_functional_level(ldb) == DS_DOMAIN_FUNCTION_2000) {
2067 /* don't do anything special for linked attributes */
2071 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, NULL,
2072 DSDB_FLAG_NEXT_MODULE |
2073 DSDB_SEARCH_SHOW_RECYCLED |
2074 DSDB_SEARCH_REVEAL_INTERNALS |
2075 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
2077 if (ret != LDB_SUCCESS) {
2080 schema = dsdb_get_schema(ldb, res);
2082 return LDB_ERR_OPERATIONS_ERROR;
2085 old_msg = res->msgs[0];
2087 old_guid = samdb_result_guid(old_msg, "objectGUID");
2089 for (i=0; i<msg->num_elements; i++) {
2090 struct ldb_message_element *el = &msg->elements[i];
2091 struct ldb_message_element *old_el, *new_el;
2092 const struct dsdb_attribute *schema_attr
2093 = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
2095 ldb_asprintf_errstring(ldb,
2096 "%s: attribute %s is not a valid attribute in schema",
2097 __FUNCTION__, el->name);
2098 return LDB_ERR_OBJECT_CLASS_VIOLATION;
2100 if (schema_attr->linkID == 0) {
2103 if ((schema_attr->linkID & 1) == 1) {
2104 /* Odd is for the target. Illegal to modify */
2105 ldb_asprintf_errstring(ldb,
2106 "attribute %s must not be modified directly, it is a linked attribute", el->name);
2107 return LDB_ERR_UNWILLING_TO_PERFORM;
2109 old_el = ldb_msg_find_element(old_msg, el->name);
2110 switch (el->flags & LDB_FLAG_MOD_MASK) {
2111 case LDB_FLAG_MOD_REPLACE:
2112 ret = replmd_modify_la_replace(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid, parent);
2114 case LDB_FLAG_MOD_DELETE:
2115 ret = replmd_modify_la_delete(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid, parent);
2117 case LDB_FLAG_MOD_ADD:
2118 ret = replmd_modify_la_add(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid, parent);
2121 ldb_asprintf_errstring(ldb,
2122 "invalid flags 0x%x for %s linked attribute",
2123 el->flags, el->name);
2124 return LDB_ERR_UNWILLING_TO_PERFORM;
2126 if (ret != LDB_SUCCESS) {
2130 ldb_msg_remove_attr(old_msg, el->name);
2132 ldb_msg_add_empty(old_msg, el->name, 0, &new_el);
2133 new_el->num_values = el->num_values;
2134 new_el->values = talloc_steal(msg->elements, el->values);
2136 /* TODO: this relises a bit too heavily on the exact
2137 behaviour of ldb_msg_find_element and
2138 ldb_msg_remove_element */
2139 old_el = ldb_msg_find_element(msg, el->name);
2141 ldb_msg_remove_element(msg, old_el);
2152 static int replmd_modify(struct ldb_module *module, struct ldb_request *req)
2154 struct ldb_context *ldb;
2155 struct replmd_replicated_request *ac;
2156 struct ldb_request *down_req;
2157 struct ldb_message *msg;
2158 time_t t = time(NULL);
2160 bool is_urgent = false;
2161 struct loadparm_context *lp_ctx;
2163 unsigned int functional_level;
2164 const DATA_BLOB *guid_blob;
2166 /* do not manipulate our control entries */
2167 if (ldb_dn_is_special(req->op.mod.message->dn)) {
2168 return ldb_next_request(module, req);
2171 ldb = ldb_module_get_ctx(module);
2173 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_modify\n");
2175 guid_blob = ldb_msg_find_ldb_val(req->op.mod.message, "objectGUID");
2176 if ( guid_blob != NULL ) {
2177 ldb_set_errstring(ldb,
2178 "replmd_modify: it's not allowed to change the objectGUID!");
2179 return LDB_ERR_CONSTRAINT_VIOLATION;
2182 ac = replmd_ctx_init(module, req);
2184 return ldb_module_oom(module);
2187 functional_level = dsdb_functional_level(ldb);
2189 lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
2190 struct loadparm_context);
2192 /* we have to copy the message as the caller might have it as a const */
2193 msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
2197 return LDB_ERR_OPERATIONS_ERROR;
2200 ldb_msg_remove_attr(msg, "whenChanged");
2201 ldb_msg_remove_attr(msg, "uSNChanged");
2203 ret = replmd_update_rpmd(module, ac->schema, req, msg, &ac->seq_num, t, &is_urgent);
2204 if (ret == LDB_ERR_REFERRAL) {
2205 referral = talloc_asprintf(req,
2207 lpcfg_dnsdomain(lp_ctx),
2208 ldb_dn_get_linearized(msg->dn));
2209 ret = ldb_module_send_referral(req, referral);
2211 return ldb_module_done(req, NULL, NULL, ret);
2214 if (ret != LDB_SUCCESS) {
2219 ret = replmd_modify_handle_linked_attribs(module, msg, ac->seq_num, t, req);
2220 if (ret != LDB_SUCCESS) {
2226 * - replace the old object with the newly constructed one
2229 ac->is_urgent = is_urgent;
2231 ret = ldb_build_mod_req(&down_req, ldb, ac,
2234 ac, replmd_op_callback,
2236 LDB_REQ_SET_LOCATION(down_req);
2237 if (ret != LDB_SUCCESS) {
2242 /* If we are in functional level 2000, then
2243 * replmd_modify_handle_linked_attribs will have done
2245 if (functional_level == DS_DOMAIN_FUNCTION_2000) {
2246 ret = ldb_request_add_control(down_req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
2247 if (ret != LDB_SUCCESS) {
2253 talloc_steal(down_req, msg);
2255 /* we only change whenChanged and uSNChanged if the seq_num
2257 if (ac->seq_num != 0) {
2258 ret = add_time_element(msg, "whenChanged", t);
2259 if (ret != LDB_SUCCESS) {
2264 ret = add_uint64_element(ldb, msg, "uSNChanged", ac->seq_num);
2265 if (ret != LDB_SUCCESS) {
2271 /* go on with the call chain */
2272 return ldb_next_request(module, down_req);
2275 static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares);
2278 handle a rename request
2280 On a rename we need to do an extra ldb_modify which sets the
2281 whenChanged and uSNChanged attributes. We do this in a callback after the success.
2283 static int replmd_rename(struct ldb_module *module, struct ldb_request *req)
2285 struct ldb_context *ldb;
2286 struct replmd_replicated_request *ac;
2288 struct ldb_request *down_req;
2290 /* do not manipulate our control entries */
2291 if (ldb_dn_is_special(req->op.mod.message->dn)) {
2292 return ldb_next_request(module, req);
2295 ldb = ldb_module_get_ctx(module);
2297 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_rename\n");
2299 ac = replmd_ctx_init(module, req);
2301 return ldb_module_oom(module);
2304 ret = ldb_build_rename_req(&down_req, ldb, ac,
2305 ac->req->op.rename.olddn,
2306 ac->req->op.rename.newdn,
2308 ac, replmd_rename_callback,
2310 LDB_REQ_SET_LOCATION(down_req);
2311 if (ret != LDB_SUCCESS) {
2316 /* go on with the call chain */
2317 return ldb_next_request(module, down_req);
2320 /* After the rename is compleated, update the whenchanged etc */
2321 static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares)
2323 struct ldb_context *ldb;
2324 struct replmd_replicated_request *ac;
2325 struct ldb_request *down_req;
2326 struct ldb_message *msg;
2327 time_t t = time(NULL);
2330 ac = talloc_get_type(req->context, struct replmd_replicated_request);
2331 ldb = ldb_module_get_ctx(ac->module);
2333 if (ares->error != LDB_SUCCESS) {
2334 return ldb_module_done(ac->req, ares->controls,
2335 ares->response, ares->error);
2338 if (ares->type != LDB_REPLY_DONE) {
2339 ldb_set_errstring(ldb,
2340 "invalid ldb_reply_type in callback");
2342 return ldb_module_done(ac->req, NULL, NULL,
2343 LDB_ERR_OPERATIONS_ERROR);
2346 /* Get a sequence number from the backend */
2347 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ac->seq_num);
2348 if (ret != LDB_SUCCESS) {
2353 * - replace the old object with the newly constructed one
2356 msg = ldb_msg_new(ac);
2359 return LDB_ERR_OPERATIONS_ERROR;
2362 msg->dn = ac->req->op.rename.newdn;
2364 ret = ldb_build_mod_req(&down_req, ldb, ac,
2367 ac, replmd_op_callback,
2369 LDB_REQ_SET_LOCATION(down_req);
2370 if (ret != LDB_SUCCESS) {
2374 talloc_steal(down_req, msg);
2376 ret = add_time_element(msg, "whenChanged", t);
2377 if (ret != LDB_SUCCESS) {
2382 ret = add_uint64_element(ldb, msg, "uSNChanged", ac->seq_num);
2383 if (ret != LDB_SUCCESS) {
2388 /* go on with the call chain - do the modify after the rename */
2389 return ldb_next_request(ac->module, down_req);
2393 remove links from objects that point at this object when an object
2396 static int replmd_delete_remove_link(struct ldb_module *module,
2397 const struct dsdb_schema *schema,
2399 struct ldb_message_element *el,
2400 const struct dsdb_attribute *sa,
2401 struct ldb_request *parent)
2404 TALLOC_CTX *tmp_ctx = talloc_new(module);
2405 struct ldb_context *ldb = ldb_module_get_ctx(module);
2407 for (i=0; i<el->num_values; i++) {
2408 struct dsdb_dn *dsdb_dn;
2412 struct ldb_message *msg;
2413 const struct dsdb_attribute *target_attr;
2414 struct ldb_message_element *el2;
2415 struct ldb_val dn_val;
2417 if (dsdb_dn_is_deleted_val(&el->values[i])) {
2421 dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i], sa->syntax->ldap_oid);
2423 talloc_free(tmp_ctx);
2424 return LDB_ERR_OPERATIONS_ERROR;
2427 status = dsdb_get_extended_dn_guid(dsdb_dn->dn, &guid2, "GUID");
2428 if (!NT_STATUS_IS_OK(status)) {
2429 talloc_free(tmp_ctx);
2430 return LDB_ERR_OPERATIONS_ERROR;
2433 /* remove the link */
2434 msg = ldb_msg_new(tmp_ctx);
2436 ldb_module_oom(module);
2437 talloc_free(tmp_ctx);
2438 return LDB_ERR_OPERATIONS_ERROR;
2442 msg->dn = dsdb_dn->dn;
2444 target_attr = dsdb_attribute_by_linkID(schema, sa->linkID ^ 1);
2445 if (target_attr == NULL) {
2449 ret = ldb_msg_add_empty(msg, target_attr->lDAPDisplayName, LDB_FLAG_MOD_DELETE, &el2);
2450 if (ret != LDB_SUCCESS) {
2451 ldb_module_oom(module);
2452 talloc_free(tmp_ctx);
2453 return LDB_ERR_OPERATIONS_ERROR;
2455 dn_val = data_blob_string_const(ldb_dn_get_linearized(dn));
2456 el2->values = &dn_val;
2457 el2->num_values = 1;
2459 ret = dsdb_module_modify(module, msg, DSDB_FLAG_OWN_MODULE, parent);
2460 if (ret != LDB_SUCCESS) {
2461 talloc_free(tmp_ctx);
2465 talloc_free(tmp_ctx);
2471 handle update of replication meta data for deletion of objects
2473 This also handles the mapping of delete to a rename operation
2474 to allow deletes to be replicated.
2476 static int replmd_delete(struct ldb_module *module, struct ldb_request *req)
2478 int ret = LDB_ERR_OTHER;
2479 bool retb, disallow_move_on_delete;
2480 struct ldb_dn *old_dn, *new_dn;
2481 const char *rdn_name;
2482 const struct ldb_val *rdn_value, *new_rdn_value;
2484 struct ldb_context *ldb = ldb_module_get_ctx(module);
2485 const struct dsdb_schema *schema;
2486 struct ldb_message *msg, *old_msg;
2487 struct ldb_message_element *el;
2488 TALLOC_CTX *tmp_ctx;
2489 struct ldb_result *res, *parent_res;
2490 const char *preserved_attrs[] = {
2491 /* yes, this really is a hard coded list. See MS-ADTS
2492 section 3.1.1.5.5.1.1 */
2493 "nTSecurityDescriptor", "attributeID", "attributeSyntax", "dNReferenceUpdate", "dNSHostName",
2494 "flatName", "governsID", "groupType", "instanceType", "lDAPDisplayName", "legacyExchangeDN",
2495 "isDeleted", "isRecycled", "lastKnownParent", "msDS-LastKnownRDN", "mS-DS-CreatorSID",
2496 "mSMQOwnerID", "nCName", "objectClass", "distinguishedName", "objectGUID", "objectSid",
2497 "oMSyntax", "proxiedObjectName", "name", "replPropertyMetaData", "sAMAccountName",
2498 "securityIdentifier", "sIDHistory", "subClassOf", "systemFlags", "trustPartner", "trustDirection",
2499 "trustType", "trustAttributes", "userAccountControl", "uSNChanged", "uSNCreated", "whenCreated",
2500 "whenChanged", NULL};
2501 unsigned int i, el_count = 0;
2502 enum deletion_state { OBJECT_NOT_DELETED=1, OBJECT_DELETED=2, OBJECT_RECYCLED=3,
2503 OBJECT_TOMBSTONE=4, OBJECT_REMOVED=5 };
2504 enum deletion_state deletion_state, next_deletion_state;
2507 if (ldb_dn_is_special(req->op.del.dn)) {
2508 return ldb_next_request(module, req);
2511 tmp_ctx = talloc_new(ldb);
2514 return LDB_ERR_OPERATIONS_ERROR;
2517 schema = dsdb_get_schema(ldb, tmp_ctx);
2519 return LDB_ERR_OPERATIONS_ERROR;
2522 old_dn = ldb_dn_copy(tmp_ctx, req->op.del.dn);
2524 /* we need the complete msg off disk, so we can work out which
2525 attributes need to be removed */
2526 ret = dsdb_module_search_dn(module, tmp_ctx, &res, old_dn, NULL,
2527 DSDB_FLAG_NEXT_MODULE |
2528 DSDB_SEARCH_SHOW_RECYCLED |
2529 DSDB_SEARCH_REVEAL_INTERNALS |
2530 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT, req);
2531 if (ret != LDB_SUCCESS) {
2532 talloc_free(tmp_ctx);
2535 old_msg = res->msgs[0];
2538 ret = dsdb_recyclebin_enabled(module, &enabled);
2539 if (ret != LDB_SUCCESS) {
2540 talloc_free(tmp_ctx);
2544 if (ldb_msg_check_string_attribute(old_msg, "isDeleted", "TRUE")) {
2546 deletion_state = OBJECT_TOMBSTONE;
2547 next_deletion_state = OBJECT_REMOVED;
2548 } else if (ldb_msg_check_string_attribute(old_msg, "isRecycled", "TRUE")) {
2549 deletion_state = OBJECT_RECYCLED;
2550 next_deletion_state = OBJECT_REMOVED;
2552 deletion_state = OBJECT_DELETED;
2553 next_deletion_state = OBJECT_RECYCLED;
2556 deletion_state = OBJECT_NOT_DELETED;
2558 next_deletion_state = OBJECT_DELETED;
2560 next_deletion_state = OBJECT_TOMBSTONE;
2564 if (next_deletion_state == OBJECT_REMOVED) {
2565 struct auth_session_info *session_info =
2566 (struct auth_session_info *)ldb_get_opaque(ldb, "sessionInfo");
2567 if (security_session_user_level(session_info, NULL) != SECURITY_SYSTEM) {
2568 ldb_asprintf_errstring(ldb, "Refusing to delete deleted object %s",
2569 ldb_dn_get_linearized(old_msg->dn));
2570 return LDB_ERR_UNWILLING_TO_PERFORM;
2573 /* it is already deleted - really remove it this time */
2574 talloc_free(tmp_ctx);
2575 return ldb_next_request(module, req);
2578 rdn_name = ldb_dn_get_rdn_name(old_dn);
2579 rdn_value = ldb_dn_get_rdn_val(old_dn);
2580 if ((rdn_name == NULL) || (rdn_value == NULL)) {
2581 talloc_free(tmp_ctx);
2582 return ldb_operr(ldb);
2585 msg = ldb_msg_new(tmp_ctx);
2587 ldb_module_oom(module);
2588 talloc_free(tmp_ctx);
2589 return LDB_ERR_OPERATIONS_ERROR;
2594 if (deletion_state == OBJECT_NOT_DELETED){
2595 /* consider the SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE flag */
2596 disallow_move_on_delete =
2597 (ldb_msg_find_attr_as_int(old_msg, "systemFlags", 0)
2598 & SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE);
2600 /* work out where we will be renaming this object to */
2601 if (!disallow_move_on_delete) {
2602 ret = dsdb_get_deleted_objects_dn(ldb, tmp_ctx, old_dn,
2604 if (ret != LDB_SUCCESS) {
2605 /* this is probably an attempted delete on a partition
2606 * that doesn't allow delete operations, such as the
2607 * schema partition */
2608 ldb_asprintf_errstring(ldb, "No Deleted Objects container for DN %s",
2609 ldb_dn_get_linearized(old_dn));
2610 talloc_free(tmp_ctx);
2611 return LDB_ERR_UNWILLING_TO_PERFORM;
2614 new_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
2615 if (new_dn == NULL) {
2616 ldb_module_oom(module);
2617 talloc_free(tmp_ctx);
2618 return LDB_ERR_OPERATIONS_ERROR;
2622 /* get the objects GUID from the search we just did */
2623 guid = samdb_result_guid(old_msg, "objectGUID");
2625 /* Add a formatted child */
2626 retb = ldb_dn_add_child_fmt(new_dn, "%s=%s\\0ADEL:%s",
2628 ldb_dn_escape_value(tmp_ctx, *rdn_value),
2629 GUID_string(tmp_ctx, &guid));
2631 DEBUG(0,(__location__ ": Unable to add a formatted child to dn: %s",
2632 ldb_dn_get_linearized(new_dn)));
2633 talloc_free(tmp_ctx);
2634 return LDB_ERR_OPERATIONS_ERROR;
2637 ret = ldb_msg_add_string(msg, "isDeleted", "TRUE");
2638 if (ret != LDB_SUCCESS) {
2639 DEBUG(0,(__location__ ": Failed to add isDeleted string to the msg\n"));
2640 ldb_module_oom(module);
2641 talloc_free(tmp_ctx);
2644 msg->elements[el_count++].flags = LDB_FLAG_MOD_REPLACE;
2648 now we need to modify the object in the following ways:
2650 - add isDeleted=TRUE
2651 - update rDN and name, with new rDN
2652 - remove linked attributes
2653 - remove objectCategory and sAMAccountType
2654 - remove attribs not on the preserved list
2655 - preserved if in above list, or is rDN
2656 - remove all linked attribs from this object
2657 - remove all links from other objects to this object
2658 - add lastKnownParent
2659 - update replPropertyMetaData?
2661 see MS-ADTS "Tombstone Requirements" section 3.1.1.5.5.1.1
2664 /* we need the storage form of the parent GUID */
2665 ret = dsdb_module_search_dn(module, tmp_ctx, &parent_res,
2666 ldb_dn_get_parent(tmp_ctx, old_dn), NULL,
2667 DSDB_FLAG_NEXT_MODULE |
2668 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
2669 DSDB_SEARCH_REVEAL_INTERNALS|
2670 DSDB_SEARCH_SHOW_RECYCLED, req);
2671 if (ret != LDB_SUCCESS) {
2672 talloc_free(tmp_ctx);
2676 if (deletion_state == OBJECT_NOT_DELETED){
2677 ret = ldb_msg_add_steal_string(msg, "lastKnownParent",
2678 ldb_dn_get_extended_linearized(tmp_ctx, parent_res->msgs[0]->dn, 1));
2679 if (ret != LDB_SUCCESS) {
2680 DEBUG(0,(__location__ ": Failed to add lastKnownParent string to the msg\n"));
2681 ldb_module_oom(module);
2682 talloc_free(tmp_ctx);
2685 msg->elements[el_count++].flags = LDB_FLAG_MOD_ADD;
2688 switch (next_deletion_state){
2690 case OBJECT_DELETED:
2692 ret = ldb_msg_add_value(msg, "msDS-LastKnownRDN", rdn_value, NULL);
2693 if (ret != LDB_SUCCESS) {
2694 DEBUG(0,(__location__ ": Failed to add msDS-LastKnownRDN string to the msg\n"));
2695 ldb_module_oom(module);
2696 talloc_free(tmp_ctx);
2699 msg->elements[el_count++].flags = LDB_FLAG_MOD_ADD;
2701 ret = ldb_msg_add_empty(msg, "objectCategory", LDB_FLAG_MOD_DELETE, NULL);
2702 if (ret != LDB_SUCCESS) {
2703 talloc_free(tmp_ctx);
2704 ldb_module_oom(module);
2708 ret = ldb_msg_add_empty(msg, "sAMAccountType", LDB_FLAG_MOD_DELETE, NULL);
2709 if (ret != LDB_SUCCESS) {
2710 talloc_free(tmp_ctx);
2711 ldb_module_oom(module);
2717 case OBJECT_RECYCLED:
2718 case OBJECT_TOMBSTONE:
2720 /* we also mark it as recycled, meaning this object can't be
2721 recovered (we are stripping its attributes) */
2722 if (dsdb_functional_level(ldb) >= DS_DOMAIN_FUNCTION_2008_R2) {
2723 ret = ldb_msg_add_string(msg, "isRecycled", "TRUE");
2724 if (ret != LDB_SUCCESS) {
2725 DEBUG(0,(__location__ ": Failed to add isRecycled string to the msg\n"));
2726 ldb_module_oom(module);
2727 talloc_free(tmp_ctx);
2730 msg->elements[el_count++].flags = LDB_FLAG_MOD_ADD;
2733 /* work out which of the old attributes we will be removing */
2734 for (i=0; i<old_msg->num_elements; i++) {
2735 const struct dsdb_attribute *sa;
2736 el = &old_msg->elements[i];
2737 sa = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
2739 talloc_free(tmp_ctx);
2740 return LDB_ERR_OPERATIONS_ERROR;
2742 if (ldb_attr_cmp(el->name, rdn_name) == 0) {
2743 /* don't remove the rDN */
2746 if (sa->linkID && sa->linkID & 1) {
2747 ret = replmd_delete_remove_link(module, schema, old_dn, el, sa, req);
2748 if (ret != LDB_SUCCESS) {
2749 talloc_free(tmp_ctx);
2750 return LDB_ERR_OPERATIONS_ERROR;
2754 if (!sa->linkID && ldb_attr_in_list(preserved_attrs, el->name)) {
2757 ret = ldb_msg_add_empty(msg, el->name, LDB_FLAG_MOD_DELETE, &el);
2758 if (ret != LDB_SUCCESS) {
2759 talloc_free(tmp_ctx);
2760 ldb_module_oom(module);
2770 if (deletion_state == OBJECT_NOT_DELETED) {
2771 const struct dsdb_attribute *sa;
2773 /* work out what the new rdn value is, for updating the
2774 rDN and name fields */
2775 new_rdn_value = ldb_dn_get_rdn_val(new_dn);
2776 if (new_rdn_value == NULL) {
2777 talloc_free(tmp_ctx);
2778 return ldb_operr(ldb);
2781 sa = dsdb_attribute_by_lDAPDisplayName(schema, rdn_name);
2783 talloc_free(tmp_ctx);
2784 return LDB_ERR_OPERATIONS_ERROR;
2787 ret = ldb_msg_add_value(msg, sa->lDAPDisplayName, new_rdn_value,
2789 if (ret != LDB_SUCCESS) {
2790 talloc_free(tmp_ctx);
2793 el->flags = LDB_FLAG_MOD_REPLACE;
2795 el = ldb_msg_find_element(old_msg, "name");
2797 ret = ldb_msg_add_value(msg, "name", new_rdn_value, &el);
2798 if (ret != LDB_SUCCESS) {
2799 talloc_free(tmp_ctx);
2802 el->flags = LDB_FLAG_MOD_REPLACE;
2806 ret = dsdb_module_modify(module, msg, DSDB_FLAG_OWN_MODULE, req);
2807 if (ret != LDB_SUCCESS) {
2808 ldb_asprintf_errstring(ldb, "replmd_delete: Failed to modify object %s in delete - %s",
2809 ldb_dn_get_linearized(old_dn), ldb_errstring(ldb));
2810 talloc_free(tmp_ctx);
2814 if (deletion_state == OBJECT_NOT_DELETED) {
2815 /* now rename onto the new DN */
2816 ret = dsdb_module_rename(module, old_dn, new_dn, DSDB_FLAG_NEXT_MODULE, req);
2817 if (ret != LDB_SUCCESS){
2818 DEBUG(0,(__location__ ": Failed to rename object from '%s' to '%s' - %s\n",
2819 ldb_dn_get_linearized(old_dn),
2820 ldb_dn_get_linearized(new_dn),
2821 ldb_errstring(ldb)));
2822 talloc_free(tmp_ctx);
2827 talloc_free(tmp_ctx);
2829 return ldb_module_done(req, NULL, NULL, LDB_SUCCESS);
2834 static int replmd_replicated_request_error(struct replmd_replicated_request *ar, int ret)
2839 static int replmd_replicated_request_werror(struct replmd_replicated_request *ar, WERROR status)
2841 int ret = LDB_ERR_OTHER;
2842 /* TODO: do some error mapping */
2846 static int replmd_replicated_apply_add(struct replmd_replicated_request *ar)
2848 struct ldb_context *ldb;
2849 struct ldb_request *change_req;
2850 enum ndr_err_code ndr_err;
2851 struct ldb_message *msg;
2852 struct replPropertyMetaDataBlob *md;
2853 struct ldb_val md_value;
2858 * TODO: check if the parent object exist
2862 * TODO: handle the conflict case where an object with the
2866 ldb = ldb_module_get_ctx(ar->module);
2867 msg = ar->objs->objects[ar->index_current].msg;
2868 md = ar->objs->objects[ar->index_current].meta_data;
2870 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
2871 if (ret != LDB_SUCCESS) {
2872 return replmd_replicated_request_error(ar, ret);
2875 ret = ldb_msg_add_value(msg, "objectGUID", &ar->objs->objects[ar->index_current].guid_value, NULL);
2876 if (ret != LDB_SUCCESS) {
2877 return replmd_replicated_request_error(ar, ret);
2880 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
2881 if (ret != LDB_SUCCESS) {
2882 return replmd_replicated_request_error(ar, ret);
2885 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", ar->seq_num);
2886 if (ret != LDB_SUCCESS) {
2887 return replmd_replicated_request_error(ar, ret);
2890 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
2891 if (ret != LDB_SUCCESS) {
2892 return replmd_replicated_request_error(ar, ret);
2895 /* remove any message elements that have zero values */
2896 for (i=0; i<msg->num_elements; i++) {
2897 struct ldb_message_element *el = &msg->elements[i];
2899 if (el->num_values == 0) {
2900 DEBUG(4,(__location__ ": Removing attribute %s with num_values==0\n",
2902 memmove(el, el+1, sizeof(*el)*(msg->num_elements - (i+1)));
2903 msg->num_elements--;
2910 * the meta data array is already sorted by the caller
2912 for (i=0; i < md->ctr.ctr1.count; i++) {
2913 md->ctr.ctr1.array[i].local_usn = ar->seq_num;
2915 ndr_err = ndr_push_struct_blob(&md_value, msg, md,
2916 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
2917 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
2918 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
2919 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
2921 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &md_value, NULL);
2922 if (ret != LDB_SUCCESS) {
2923 return replmd_replicated_request_error(ar, ret);
2926 replmd_ldb_message_sort(msg, ar->schema);
2929 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_ADD, msg);
2930 DEBUG(4, ("DRS replication add message:\n%s\n", s));
2934 ret = ldb_build_add_req(&change_req,
2942 LDB_REQ_SET_LOCATION(change_req);
2943 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
2945 return ldb_next_request(ar->module, change_req);
2949 return true if an update is newer than an existing entry
2950 see section 5.11 of MS-ADTS
2952 static bool replmd_update_is_newer(const struct GUID *current_invocation_id,
2953 const struct GUID *update_invocation_id,
2954 uint32_t current_version,
2955 uint32_t update_version,
2956 NTTIME current_change_time,
2957 NTTIME update_change_time)
2959 if (update_version != current_version) {
2960 return update_version > current_version;
2962 if (update_change_time != current_change_time) {
2963 return update_change_time > current_change_time;
2965 return GUID_compare(update_invocation_id, current_invocation_id) > 0;
2968 static bool replmd_replPropertyMetaData1_is_newer(struct replPropertyMetaData1 *cur_m,
2969 struct replPropertyMetaData1 *new_m)
2971 return replmd_update_is_newer(&cur_m->originating_invocation_id,
2972 &new_m->originating_invocation_id,
2975 cur_m->originating_change_time,
2976 new_m->originating_change_time);
2979 static struct replPropertyMetaData1 *
2980 replmd_replPropertyMetaData1_find_attid(struct replPropertyMetaDataBlob *md_blob,
2981 enum drsuapi_DsAttributeId attid)
2984 struct replPropertyMetaDataCtr1 *rpmd_ctr = &md_blob->ctr.ctr1;
2986 for (i = 0; i < rpmd_ctr->count; i++) {
2987 if (rpmd_ctr->array[i].attid == attid) {
2988 return &rpmd_ctr->array[i];
2996 handle renames that come in over DRS replication
2998 static int replmd_replicated_handle_rename(struct replmd_replicated_request *ar,
2999 struct ldb_message *msg,
3000 struct replPropertyMetaDataBlob *rmd,
3001 struct replPropertyMetaDataBlob *omd,
3002 struct ldb_request *parent)
3004 struct replPropertyMetaData1 *md_remote;
3005 struct replPropertyMetaData1 *md_local;
3007 if (ldb_dn_compare(msg->dn, ar->search_msg->dn) == 0) {
3012 /* now we need to check for double renames. We could have a
3013 * local rename pending which our replication partner hasn't
3014 * received yet. We choose which one wins by looking at the
3015 * attribute stamps on the two objects, the newer one wins
3017 md_remote = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
3018 md_local = replmd_replPropertyMetaData1_find_attid(omd, DRSUAPI_ATTID_name);
3019 /* if there is no name attribute then we have to assume the
3020 object we've received is in fact newer */
3021 if (!md_remote || !md_local ||
3022 replmd_replPropertyMetaData1_is_newer(md_local, md_remote)) {
3023 DEBUG(4,("replmd_replicated_request rename %s => %s\n",
3024 ldb_dn_get_linearized(ar->search_msg->dn),
3025 ldb_dn_get_linearized(msg->dn)));
3026 /* pass rename to the next module
3027 * so it doesn't appear as an originating update */
3028 return dsdb_module_rename(ar->module,
3029 ar->search_msg->dn, msg->dn,
3030 DSDB_FLAG_NEXT_MODULE | DSDB_MODIFY_RELAX, parent);
3033 /* we're going to keep our old object */
3034 DEBUG(4,(__location__ ": Keeping object %s and rejecting older rename to %s\n",
3035 ldb_dn_get_linearized(ar->search_msg->dn),
3036 ldb_dn_get_linearized(msg->dn)));
3041 static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar)
3043 struct ldb_context *ldb;
3044 struct ldb_request *change_req;
3045 enum ndr_err_code ndr_err;
3046 struct ldb_message *msg;
3047 struct replPropertyMetaDataBlob *rmd;
3048 struct replPropertyMetaDataBlob omd;
3049 const struct ldb_val *omd_value;
3050 struct replPropertyMetaDataBlob nmd;
3051 struct ldb_val nmd_value;
3054 unsigned int removed_attrs = 0;
3057 ldb = ldb_module_get_ctx(ar->module);
3058 msg = ar->objs->objects[ar->index_current].msg;
3059 rmd = ar->objs->objects[ar->index_current].meta_data;
3063 /* find existing meta data */
3064 omd_value = ldb_msg_find_ldb_val(ar->search_msg, "replPropertyMetaData");
3066 ndr_err = ndr_pull_struct_blob(omd_value, ar, &omd,
3067 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
3068 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
3069 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
3070 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
3073 if (omd.version != 1) {
3074 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
3078 /* handle renames that come in over DRS */
3079 ret = replmd_replicated_handle_rename(ar, msg, rmd, &omd, ar->req);
3080 if (ret != LDB_SUCCESS) {
3081 ldb_debug(ldb, LDB_DEBUG_FATAL,
3082 "replmd_replicated_request rename %s => %s failed - %s\n",
3083 ldb_dn_get_linearized(ar->search_msg->dn),
3084 ldb_dn_get_linearized(msg->dn),
3085 ldb_errstring(ldb));
3086 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
3091 nmd.ctr.ctr1.count = omd.ctr.ctr1.count + rmd->ctr.ctr1.count;
3092 nmd.ctr.ctr1.array = talloc_array(ar,
3093 struct replPropertyMetaData1,
3094 nmd.ctr.ctr1.count);
3095 if (!nmd.ctr.ctr1.array) return replmd_replicated_request_werror(ar, WERR_NOMEM);
3097 /* first copy the old meta data */
3098 for (i=0; i < omd.ctr.ctr1.count; i++) {
3099 nmd.ctr.ctr1.array[ni] = omd.ctr.ctr1.array[i];
3103 /* now merge in the new meta data */
3104 for (i=0; i < rmd->ctr.ctr1.count; i++) {
3107 for (j=0; j < ni; j++) {
3110 if (rmd->ctr.ctr1.array[i].attid != nmd.ctr.ctr1.array[j].attid) {
3114 cmp = replmd_replPropertyMetaData1_is_newer(&nmd.ctr.ctr1.array[j],
3115 &rmd->ctr.ctr1.array[i]);
3117 /* replace the entry */
3118 nmd.ctr.ctr1.array[j] = rmd->ctr.ctr1.array[i];
3123 if (rmd->ctr.ctr1.array[i].attid != DRSUAPI_ATTID_instanceType) {
3124 DEBUG(3,("Discarding older DRS attribute update to %s on %s from %s\n",
3125 msg->elements[i-removed_attrs].name,
3126 ldb_dn_get_linearized(msg->dn),
3127 GUID_string(ar, &rmd->ctr.ctr1.array[i].originating_invocation_id)));
3130 /* we don't want to apply this change so remove the attribute */
3131 ldb_msg_remove_element(msg, &msg->elements[i-removed_attrs]);
3138 if (found) continue;
3140 nmd.ctr.ctr1.array[ni] = rmd->ctr.ctr1.array[i];
3145 * finally correct the size of the meta_data array
3147 nmd.ctr.ctr1.count = ni;
3150 * the rdn attribute (the alias for the name attribute),
3151 * 'cn' for most objects is the last entry in the meta data array
3154 * sort the new meta data array
3156 ret = replmd_replPropertyMetaDataCtr1_sort(&nmd.ctr.ctr1, ar->schema, msg->dn);
3157 if (ret != LDB_SUCCESS) {
3162 * check if some replicated attributes left, otherwise skip the ldb_modify() call
3164 if (msg->num_elements == 0) {
3165 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: skip replace\n",
3168 ar->index_current++;
3169 return replmd_replicated_apply_next(ar);
3172 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: replace %u attributes\n",
3173 ar->index_current, msg->num_elements);
3175 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
3176 if (ret != LDB_SUCCESS) {
3177 return replmd_replicated_request_error(ar, ret);
3180 for (i=0; i<ni; i++) {
3181 nmd.ctr.ctr1.array[i].local_usn = ar->seq_num;
3184 /* create the meta data value */
3185 ndr_err = ndr_push_struct_blob(&nmd_value, msg, &nmd,
3186 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
3187 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
3188 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
3189 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
3193 * when we know that we'll modify the record, add the whenChanged, uSNChanged
3194 * and replPopertyMetaData attributes
3196 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
3197 if (ret != LDB_SUCCESS) {
3198 return replmd_replicated_request_error(ar, ret);
3200 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
3201 if (ret != LDB_SUCCESS) {
3202 return replmd_replicated_request_error(ar, ret);
3204 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
3205 if (ret != LDB_SUCCESS) {
3206 return replmd_replicated_request_error(ar, ret);
3209 replmd_ldb_message_sort(msg, ar->schema);
3211 /* we want to replace the old values */
3212 for (i=0; i < msg->num_elements; i++) {
3213 msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
3217 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_MODIFY, msg);
3218 DEBUG(4, ("DRS replication modify message:\n%s\n", s));
3222 ret = ldb_build_mod_req(&change_req,
3230 LDB_REQ_SET_LOCATION(change_req);
3231 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
3233 return ldb_next_request(ar->module, change_req);
3236 static int replmd_replicated_apply_search_callback(struct ldb_request *req,
3237 struct ldb_reply *ares)
3239 struct replmd_replicated_request *ar = talloc_get_type(req->context,
3240 struct replmd_replicated_request);
3244 return ldb_module_done(ar->req, NULL, NULL,
3245 LDB_ERR_OPERATIONS_ERROR);
3247 if (ares->error != LDB_SUCCESS &&
3248 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
3249 return ldb_module_done(ar->req, ares->controls,
3250 ares->response, ares->error);
3253 switch (ares->type) {
3254 case LDB_REPLY_ENTRY:
3255 ar->search_msg = talloc_steal(ar, ares->message);
3258 case LDB_REPLY_REFERRAL:
3259 /* we ignore referrals */
3262 case LDB_REPLY_DONE:
3263 if (ar->search_msg != NULL) {
3264 ret = replmd_replicated_apply_merge(ar);
3266 ret = replmd_replicated_apply_add(ar);
3268 if (ret != LDB_SUCCESS) {
3269 return ldb_module_done(ar->req, NULL, NULL, ret);
3277 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar);
3279 static int replmd_replicated_apply_next(struct replmd_replicated_request *ar)
3281 struct ldb_context *ldb;
3285 struct ldb_request *search_req;
3286 struct ldb_search_options_control *options;
3288 if (ar->index_current >= ar->objs->num_objects) {
3289 /* done with it, go to next stage */
3290 return replmd_replicated_uptodate_vector(ar);
3293 ldb = ldb_module_get_ctx(ar->module);
3294 ar->search_msg = NULL;
3296 tmp_str = ldb_binary_encode(ar, ar->objs->objects[ar->index_current].guid_value);
3297 if (!tmp_str) return replmd_replicated_request_werror(ar, WERR_NOMEM);
3299 filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
3300 if (!filter) return replmd_replicated_request_werror(ar, WERR_NOMEM);
3301 talloc_free(tmp_str);
3303 ret = ldb_build_search_req(&search_req,
3312 replmd_replicated_apply_search_callback,
3314 LDB_REQ_SET_LOCATION(search_req);
3316 ret = ldb_request_add_control(search_req, LDB_CONTROL_SHOW_RECYCLED_OID,
3318 if (ret != LDB_SUCCESS) {
3322 /* we need to cope with cross-partition links, so search for
3323 the GUID over all partitions */
3324 options = talloc(search_req, struct ldb_search_options_control);
3325 if (options == NULL) {
3326 DEBUG(0, (__location__ ": out of memory\n"));
3327 return LDB_ERR_OPERATIONS_ERROR;
3329 options->search_options = LDB_SEARCH_OPTION_PHANTOM_ROOT;
3331 ret = ldb_request_add_control(search_req,
3332 LDB_CONTROL_SEARCH_OPTIONS_OID,
3334 if (ret != LDB_SUCCESS) {
3338 return ldb_next_request(ar->module, search_req);
3341 static int replmd_replicated_uptodate_modify_callback(struct ldb_request *req,
3342 struct ldb_reply *ares)
3344 struct ldb_context *ldb;
3345 struct replmd_replicated_request *ar = talloc_get_type(req->context,
3346 struct replmd_replicated_request);
3347 ldb = ldb_module_get_ctx(ar->module);
3350 return ldb_module_done(ar->req, NULL, NULL,
3351 LDB_ERR_OPERATIONS_ERROR);
3353 if (ares->error != LDB_SUCCESS) {
3354 return ldb_module_done(ar->req, ares->controls,
3355 ares->response, ares->error);
3358 if (ares->type != LDB_REPLY_DONE) {
3359 ldb_set_errstring(ldb, "Invalid reply type\n!");
3360 return ldb_module_done(ar->req, NULL, NULL,
3361 LDB_ERR_OPERATIONS_ERROR);
3366 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
3369 static int replmd_replicated_uptodate_modify(struct replmd_replicated_request *ar)
3371 struct ldb_context *ldb;
3372 struct ldb_request *change_req;
3373 enum ndr_err_code ndr_err;
3374 struct ldb_message *msg;
3375 struct replUpToDateVectorBlob ouv;
3376 const struct ldb_val *ouv_value;
3377 const struct drsuapi_DsReplicaCursor2CtrEx *ruv;
3378 struct replUpToDateVectorBlob nuv;
3379 struct ldb_val nuv_value;
3380 struct ldb_message_element *nuv_el = NULL;
3381 const struct GUID *our_invocation_id;
3382 struct ldb_message_element *orf_el = NULL;
3383 struct repsFromToBlob nrf;
3384 struct ldb_val *nrf_value = NULL;
3385 struct ldb_message_element *nrf_el = NULL;
3389 time_t t = time(NULL);
3392 uint32_t instanceType;
3394 ldb = ldb_module_get_ctx(ar->module);
3395 ruv = ar->objs->uptodateness_vector;
3401 unix_to_nt_time(&now, t);
3403 instanceType = ldb_msg_find_attr_as_uint(ar->search_msg, "instanceType", 0);
3404 if (! (instanceType & INSTANCE_TYPE_IS_NC_HEAD)) {
3405 DEBUG(4,(__location__ ": Skipping UDV and repsFrom update as not NC root: %s\n",
3406 ldb_dn_get_linearized(ar->search_msg->dn)));
3407 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
3411 * first create the new replUpToDateVector
3413 ouv_value = ldb_msg_find_ldb_val(ar->search_msg, "replUpToDateVector");
3415 ndr_err = ndr_pull_struct_blob(ouv_value, ar, &ouv,
3416 (ndr_pull_flags_fn_t)ndr_pull_replUpToDateVectorBlob);
3417 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
3418 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
3419 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
3422 if (ouv.version != 2) {
3423 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
3428 * the new uptodateness vector will at least
3429 * contain 1 entry, one for the source_dsa
3431 * plus optional values from our old vector and the one from the source_dsa
3433 nuv.ctr.ctr2.count = 1 + ouv.ctr.ctr2.count;
3434 if (ruv) nuv.ctr.ctr2.count += ruv->count;
3435 nuv.ctr.ctr2.cursors = talloc_array(ar,
3436 struct drsuapi_DsReplicaCursor2,
3437 nuv.ctr.ctr2.count);
3438 if (!nuv.ctr.ctr2.cursors) return replmd_replicated_request_werror(ar, WERR_NOMEM);
3440 /* first copy the old vector */
3441 for (i=0; i < ouv.ctr.ctr2.count; i++) {
3442 nuv.ctr.ctr2.cursors[ni] = ouv.ctr.ctr2.cursors[i];
3446 /* get our invocation_id if we have one already attached to the ldb */
3447 our_invocation_id = samdb_ntds_invocation_id(ldb);
3449 /* merge in the source_dsa vector is available */
3450 for (i=0; (ruv && i < ruv->count); i++) {
3453 if (our_invocation_id &&
3454 GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
3455 our_invocation_id)) {
3459 for (j=0; j < ni; j++) {
3460 if (!GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
3461 &nuv.ctr.ctr2.cursors[j].source_dsa_invocation_id)) {
3468 * we update only the highest_usn and not the latest_sync_success time,
3469 * because the last success stands for direct replication
3471 if (ruv->cursors[i].highest_usn > nuv.ctr.ctr2.cursors[j].highest_usn) {
3472 nuv.ctr.ctr2.cursors[j].highest_usn = ruv->cursors[i].highest_usn;
3477 if (found) continue;
3479 /* if it's not there yet, add it */
3480 nuv.ctr.ctr2.cursors[ni] = ruv->cursors[i];
3485 * merge in the current highwatermark for the source_dsa
3488 for (j=0; j < ni; j++) {
3489 if (!GUID_equal(&ar->objs->source_dsa->source_dsa_invocation_id,
3490 &nuv.ctr.ctr2.cursors[j].source_dsa_invocation_id)) {
3497 * here we update the highest_usn and last_sync_success time
3498 * because we're directly replicating from the source_dsa
3500 * and use the tmp_highest_usn because this is what we have just applied
3503 nuv.ctr.ctr2.cursors[j].highest_usn = ar->objs->source_dsa->highwatermark.tmp_highest_usn;
3504 nuv.ctr.ctr2.cursors[j].last_sync_success = now;
3509 * here we update the highest_usn and last_sync_success time
3510 * because we're directly replicating from the source_dsa
3512 * and use the tmp_highest_usn because this is what we have just applied
3515 nuv.ctr.ctr2.cursors[ni].source_dsa_invocation_id= ar->objs->source_dsa->source_dsa_invocation_id;
3516 nuv.ctr.ctr2.cursors[ni].highest_usn = ar->objs->source_dsa->highwatermark.tmp_highest_usn;
3517 nuv.ctr.ctr2.cursors[ni].last_sync_success = now;
3522 * finally correct the size of the cursors array
3524 nuv.ctr.ctr2.count = ni;
3529 TYPESAFE_QSORT(nuv.ctr.ctr2.cursors, nuv.ctr.ctr2.count, drsuapi_DsReplicaCursor2_compare);
3532 * create the change ldb_message
3534 msg = ldb_msg_new(ar);
3535 if (!msg) return replmd_replicated_request_werror(ar, WERR_NOMEM);
3536 msg->dn = ar->search_msg->dn;
3538 ndr_err = ndr_push_struct_blob(&nuv_value, msg, &nuv,
3539 (ndr_push_flags_fn_t)ndr_push_replUpToDateVectorBlob);
3540 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
3541 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
3542 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
3544 ret = ldb_msg_add_value(msg, "replUpToDateVector", &nuv_value, &nuv_el);
3545 if (ret != LDB_SUCCESS) {
3546 return replmd_replicated_request_error(ar, ret);
3548 nuv_el->flags = LDB_FLAG_MOD_REPLACE;
3551 * now create the new repsFrom value from the given repsFromTo1 structure
3555 nrf.ctr.ctr1 = *ar->objs->source_dsa;
3556 nrf.ctr.ctr1.highwatermark.highest_usn = nrf.ctr.ctr1.highwatermark.tmp_highest_usn;
3559 * first see if we already have a repsFrom value for the current source dsa
3560 * if so we'll later replace this value
3562 orf_el = ldb_msg_find_element(ar->search_msg, "repsFrom");
3564 for (i=0; i < orf_el->num_values; i++) {
3565 struct repsFromToBlob *trf;
3567 trf = talloc(ar, struct repsFromToBlob);
3568 if (!trf) return replmd_replicated_request_werror(ar, WERR_NOMEM);
3570 ndr_err = ndr_pull_struct_blob(&orf_el->values[i], trf, trf,
3571 (ndr_pull_flags_fn_t)ndr_pull_repsFromToBlob);
3572 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
3573 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
3574 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
3577 if (trf->version != 1) {
3578 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
3582 * we compare the source dsa objectGUID not the invocation_id
3583 * because we want only one repsFrom value per source dsa
3584 * and when the invocation_id of the source dsa has changed we don't need
3585 * the old repsFrom with the old invocation_id
3587 if (!GUID_equal(&trf->ctr.ctr1.source_dsa_obj_guid,
3588 &ar->objs->source_dsa->source_dsa_obj_guid)) {
3594 nrf_value = &orf_el->values[i];
3599 * copy over all old values to the new ldb_message
3601 ret = ldb_msg_add_empty(msg, "repsFrom", 0, &nrf_el);
3602 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
3607 * if we haven't found an old repsFrom value for the current source dsa
3608 * we'll add a new value
3611 struct ldb_val zero_value;
3612 ZERO_STRUCT(zero_value);
3613 ret = ldb_msg_add_value(msg, "repsFrom", &zero_value, &nrf_el);
3614 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
3616 nrf_value = &nrf_el->values[nrf_el->num_values - 1];
3619 /* we now fill the value which is already attached to ldb_message */
3620 ndr_err = ndr_push_struct_blob(nrf_value, msg,
3622 (ndr_push_flags_fn_t)ndr_push_repsFromToBlob);
3623 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
3624 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
3625 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
3629 * the ldb_message_element for the attribute, has all the old values and the new one
3630 * so we'll replace the whole attribute with all values
3632 nrf_el->flags = LDB_FLAG_MOD_REPLACE;
3635 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_MODIFY, msg);
3636 DEBUG(4, ("DRS replication uptodate modify message:\n%s\n", s));
3640 /* prepare the ldb_modify() request */
3641 ret = ldb_build_mod_req(&change_req,
3647 replmd_replicated_uptodate_modify_callback,
3649 LDB_REQ_SET_LOCATION(change_req);
3650 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
3652 return ldb_next_request(ar->module, change_req);
3655 static int replmd_replicated_uptodate_search_callback(struct ldb_request *req,
3656 struct ldb_reply *ares)
3658 struct replmd_replicated_request *ar = talloc_get_type(req->context,
3659 struct replmd_replicated_request);
3663 return ldb_module_done(ar->req, NULL, NULL,
3664 LDB_ERR_OPERATIONS_ERROR);
3666 if (ares->error != LDB_SUCCESS &&
3667 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
3668 return ldb_module_done(ar->req, ares->controls,
3669 ares->response, ares->error);
3672 switch (ares->type) {
3673 case LDB_REPLY_ENTRY:
3674 ar->search_msg = talloc_steal(ar, ares->message);
3677 case LDB_REPLY_REFERRAL:
3678 /* we ignore referrals */
3681 case LDB_REPLY_DONE:
3682 if (ar->search_msg == NULL) {
3683 ret = replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
3685 ret = replmd_replicated_uptodate_modify(ar);
3687 if (ret != LDB_SUCCESS) {
3688 return ldb_module_done(ar->req, NULL, NULL, ret);
3697 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar)
3699 struct ldb_context *ldb;
3701 static const char *attrs[] = {
3702 "replUpToDateVector",
3707 struct ldb_request *search_req;
3709 ldb = ldb_module_get_ctx(ar->module);
3710 ar->search_msg = NULL;
3712 ret = ldb_build_search_req(&search_req,
3715 ar->objs->partition_dn,
3721 replmd_replicated_uptodate_search_callback,
3723 LDB_REQ_SET_LOCATION(search_req);
3724 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
3726 return ldb_next_request(ar->module, search_req);
3731 static int replmd_extended_replicated_objects(struct ldb_module *module, struct ldb_request *req)
3733 struct ldb_context *ldb;
3734 struct dsdb_extended_replicated_objects *objs;
3735 struct replmd_replicated_request *ar;
3736 struct ldb_control **ctrls;
3739 struct replmd_private *replmd_private =
3740 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
3742 ldb = ldb_module_get_ctx(module);
3744 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_extended_replicated_objects\n");
3746 objs = talloc_get_type(req->op.extended.data, struct dsdb_extended_replicated_objects);
3748 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: invalid extended data\n");
3749 return LDB_ERR_PROTOCOL_ERROR;
3752 if (objs->version != DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION) {
3753 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: extended data invalid version [%u != %u]\n",
3754 objs->version, DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION);
3755 return LDB_ERR_PROTOCOL_ERROR;
3758 ar = replmd_ctx_init(module, req);
3760 return LDB_ERR_OPERATIONS_ERROR;
3762 /* Set the flags to have the replmd_op_callback run over the full set of objects */
3763 ar->apply_mode = true;
3765 ar->schema = dsdb_get_schema(ldb, ar);
3767 ldb_debug_set(ldb, LDB_DEBUG_FATAL, "replmd_ctx_init: no loaded schema found\n");
3769 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
3770 return LDB_ERR_CONSTRAINT_VIOLATION;
3773 ctrls = req->controls;
3775 if (req->controls) {
3776 req->controls = talloc_memdup(ar, req->controls,
3777 talloc_get_size(req->controls));
3778 if (!req->controls) return replmd_replicated_request_werror(ar, WERR_NOMEM);
3781 /* This allows layers further down to know if a change came in over replication */
3782 ret = ldb_request_add_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID, false, NULL);
3783 if (ret != LDB_SUCCESS) {
3787 /* If this change contained linked attributes in the body
3788 * (rather than in the links section) we need to update
3789 * backlinks in linked_attributes */
3790 ret = ldb_request_add_control(req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
3791 if (ret != LDB_SUCCESS) {
3795 ar->controls = req->controls;
3796 req->controls = ctrls;
3798 DEBUG(4,("linked_attributes_count=%u\n", objs->linked_attributes_count));
3800 /* save away the linked attributes for the end of the
3802 for (i=0; i<ar->objs->linked_attributes_count; i++) {
3803 struct la_entry *la_entry;
3805 if (replmd_private->la_ctx == NULL) {
3806 replmd_private->la_ctx = talloc_new(replmd_private);
3808 la_entry = talloc(replmd_private->la_ctx, struct la_entry);
3809 if (la_entry == NULL) {
3811 return LDB_ERR_OPERATIONS_ERROR;
3813 la_entry->la = talloc(la_entry, struct drsuapi_DsReplicaLinkedAttribute);
3814 if (la_entry->la == NULL) {
3815 talloc_free(la_entry);
3817 return LDB_ERR_OPERATIONS_ERROR;
3819 *la_entry->la = ar->objs->linked_attributes[i];
3821 /* we need to steal the non-scalars so they stay
3822 around until the end of the transaction */
3823 talloc_steal(la_entry->la, la_entry->la->identifier);
3824 talloc_steal(la_entry->la, la_entry->la->value.blob);
3826 DLIST_ADD(replmd_private->la_list, la_entry);
3829 return replmd_replicated_apply_next(ar);
3833 process one linked attribute structure
3835 static int replmd_process_linked_attribute(struct ldb_module *module,
3836 struct la_entry *la_entry,
3837 struct ldb_request *parent)
3839 struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
3840 struct ldb_context *ldb = ldb_module_get_ctx(module);
3841 struct ldb_message *msg;
3842 TALLOC_CTX *tmp_ctx = talloc_new(la_entry);
3843 const struct dsdb_schema *schema = dsdb_get_schema(ldb, tmp_ctx);
3845 const struct dsdb_attribute *attr;
3846 struct dsdb_dn *dsdb_dn;
3847 uint64_t seq_num = 0;
3848 struct ldb_message_element *old_el;
3850 time_t t = time(NULL);
3851 struct ldb_result *res;
3852 const char *attrs[2];
3853 struct parsed_dn *pdn_list, *pdn;
3854 struct GUID guid = GUID_zero();
3856 bool active = (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)?true:false;
3857 const struct GUID *our_invocation_id;
3860 linked_attributes[0]:
3861 &objs->linked_attributes[i]: struct drsuapi_DsReplicaLinkedAttribute
3863 identifier: struct drsuapi_DsReplicaObjectIdentifier
3864 __ndr_size : 0x0000003a (58)
3865 __ndr_size_sid : 0x00000000 (0)
3866 guid : 8e95b6a9-13dd-4158-89db-3220a5be5cc7
3868 __ndr_size_dn : 0x00000000 (0)
3870 attid : DRSUAPI_ATTID_member (0x1F)
3871 value: struct drsuapi_DsAttributeValue
3872 __ndr_size : 0x0000007e (126)
3874 blob : DATA_BLOB length=126
3875 flags : 0x00000001 (1)
3876 1: DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE
3877 originating_add_time : Wed Sep 2 22:20:01 2009 EST
3878 meta_data: struct drsuapi_DsReplicaMetaData
3879 version : 0x00000015 (21)
3880 originating_change_time : Wed Sep 2 23:39:07 2009 EST
3881 originating_invocation_id: 794640f3-18cf-40ee-a211-a93992b67a64
3882 originating_usn : 0x000000000001e19c (123292)
3884 (for cases where the link is to a normal DN)
3885 &target: struct drsuapi_DsReplicaObjectIdentifier3
3886 __ndr_size : 0x0000007e (126)
3887 __ndr_size_sid : 0x0000001c (28)
3888 guid : 7639e594-db75-4086-b0d4-67890ae46031
3889 sid : S-1-5-21-2848215498-2472035911-1947525656-19924
3890 __ndr_size_dn : 0x00000022 (34)
3891 dn : 'CN=UOne,OU=TestOU,DC=vsofs8,DC=com'
3894 /* find the attribute being modified */
3895 attr = dsdb_attribute_by_attributeID_id(schema, la->attid);
3897 DEBUG(0, (__location__ ": Unable to find attributeID 0x%x\n", la->attid));
3898 talloc_free(tmp_ctx);
3899 return LDB_ERR_OPERATIONS_ERROR;
3902 attrs[0] = attr->lDAPDisplayName;
3905 /* get the existing message from the db for the object with
3906 this GUID, returning attribute being modified. We will then
3907 use this msg as the basis for a modify call */
3908 ret = dsdb_module_search(module, tmp_ctx, &res, NULL, LDB_SCOPE_SUBTREE, attrs,
3909 DSDB_FLAG_NEXT_MODULE |
3910 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
3911 DSDB_SEARCH_SHOW_RECYCLED |
3912 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
3913 DSDB_SEARCH_REVEAL_INTERNALS,
3915 "objectGUID=%s", GUID_string(tmp_ctx, &la->identifier->guid));
3916 if (ret != LDB_SUCCESS) {
3917 talloc_free(tmp_ctx);
3920 if (res->count != 1) {
3921 ldb_asprintf_errstring(ldb, "DRS linked attribute for GUID %s - DN not found",
3922 GUID_string(tmp_ctx, &la->identifier->guid));
3923 talloc_free(tmp_ctx);
3924 return LDB_ERR_NO_SUCH_OBJECT;
3928 if (msg->num_elements == 0) {
3929 ret = ldb_msg_add_empty(msg, attr->lDAPDisplayName, LDB_FLAG_MOD_REPLACE, &old_el);
3930 if (ret != LDB_SUCCESS) {
3931 ldb_module_oom(module);
3932 talloc_free(tmp_ctx);
3933 return LDB_ERR_OPERATIONS_ERROR;
3936 old_el = &msg->elements[0];
3937 old_el->flags = LDB_FLAG_MOD_REPLACE;
3940 /* parse the existing links */
3941 ret = get_parsed_dns(module, tmp_ctx, old_el, &pdn_list, attr->syntax->ldap_oid, parent);
3942 if (ret != LDB_SUCCESS) {
3943 talloc_free(tmp_ctx);
3947 /* get our invocationId */
3948 our_invocation_id = samdb_ntds_invocation_id(ldb);
3949 if (!our_invocation_id) {
3950 ldb_debug_set(ldb, LDB_DEBUG_ERROR, __location__ ": unable to find invocationId\n");
3951 talloc_free(tmp_ctx);
3952 return LDB_ERR_OPERATIONS_ERROR;
3955 ret = replmd_check_upgrade_links(pdn_list, old_el->num_values, old_el, our_invocation_id);
3956 if (ret != LDB_SUCCESS) {
3957 talloc_free(tmp_ctx);
3961 status = dsdb_dn_la_from_blob(ldb, attr, schema, tmp_ctx, la->value.blob, &dsdb_dn);
3962 if (!W_ERROR_IS_OK(status)) {
3963 ldb_asprintf_errstring(ldb, "Failed to parsed linked attribute blob for %s on %s - %s\n",
3964 old_el->name, ldb_dn_get_linearized(msg->dn), win_errstr(status));
3965 return LDB_ERR_OPERATIONS_ERROR;
3968 ntstatus = dsdb_get_extended_dn_guid(dsdb_dn->dn, &guid, "GUID");
3969 if (!NT_STATUS_IS_OK(ntstatus) && active) {
3970 ldb_asprintf_errstring(ldb, "Failed to find GUID in linked attribute blob for %s on %s from %s",
3972 ldb_dn_get_linearized(dsdb_dn->dn),
3973 ldb_dn_get_linearized(msg->dn));
3974 return LDB_ERR_OPERATIONS_ERROR;
3977 /* re-resolve the DN by GUID, as the DRS server may give us an
3979 ret = dsdb_module_dn_by_guid(module, dsdb_dn, &guid, &dsdb_dn->dn, parent);
3980 if (ret != LDB_SUCCESS) {
3981 DEBUG(2,(__location__ ": WARNING: Failed to re-resolve GUID %s - using %s",
3982 GUID_string(tmp_ctx, &guid),
3983 ldb_dn_get_linearized(dsdb_dn->dn)));
3986 /* see if this link already exists */
3987 pdn = parsed_dn_find(pdn_list, old_el->num_values, &guid, dsdb_dn->dn);
3989 /* see if this update is newer than what we have already */
3990 struct GUID invocation_id = GUID_zero();
3991 uint32_t version = 0;
3992 uint32_t originating_usn = 0;
3993 NTTIME change_time = 0;
3994 uint32_t rmd_flags = dsdb_dn_rmd_flags(pdn->dsdb_dn->dn);
3996 dsdb_get_extended_dn_guid(pdn->dsdb_dn->dn, &invocation_id, "RMD_INVOCID");
3997 dsdb_get_extended_dn_uint32(pdn->dsdb_dn->dn, &version, "RMD_VERSION");
3998 dsdb_get_extended_dn_uint32(pdn->dsdb_dn->dn, &originating_usn, "RMD_ORIGINATING_USN");
3999 dsdb_get_extended_dn_nttime(pdn->dsdb_dn->dn, &change_time, "RMD_CHANGETIME");
4001 if (!replmd_update_is_newer(&invocation_id,
4002 &la->meta_data.originating_invocation_id,
4004 la->meta_data.version,
4006 la->meta_data.originating_change_time)) {
4007 DEBUG(3,("Discarding older DRS linked attribute update to %s on %s from %s\n",
4008 old_el->name, ldb_dn_get_linearized(msg->dn),
4009 GUID_string(tmp_ctx, &la->meta_data.originating_invocation_id)));
4010 talloc_free(tmp_ctx);
4014 /* get a seq_num for this change */
4015 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
4016 if (ret != LDB_SUCCESS) {
4017 talloc_free(tmp_ctx);
4021 if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
4022 /* remove the existing backlink */
4023 ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid, false, attr, false);
4024 if (ret != LDB_SUCCESS) {
4025 talloc_free(tmp_ctx);
4030 ret = replmd_update_la_val(tmp_ctx, pdn->v, dsdb_dn, pdn->dsdb_dn,
4031 &la->meta_data.originating_invocation_id,
4032 la->meta_data.originating_usn, seq_num,
4033 la->meta_data.originating_change_time,
4034 la->meta_data.version,
4036 if (ret != LDB_SUCCESS) {
4037 talloc_free(tmp_ctx);
4042 /* add the new backlink */
4043 ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid, true, attr, false);
4044 if (ret != LDB_SUCCESS) {
4045 talloc_free(tmp_ctx);
4050 /* get a seq_num for this change */
4051 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
4052 if (ret != LDB_SUCCESS) {
4053 talloc_free(tmp_ctx);
4057 old_el->values = talloc_realloc(msg->elements, old_el->values,
4058 struct ldb_val, old_el->num_values+1);
4059 if (!old_el->values) {
4060 ldb_module_oom(module);
4061 return LDB_ERR_OPERATIONS_ERROR;
4063 old_el->num_values++;
4065 ret = replmd_build_la_val(tmp_ctx, &old_el->values[old_el->num_values-1], dsdb_dn,
4066 &la->meta_data.originating_invocation_id,
4067 la->meta_data.originating_usn, seq_num,
4068 la->meta_data.originating_change_time,
4069 la->meta_data.version,
4070 (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)?false:true);
4071 if (ret != LDB_SUCCESS) {
4072 talloc_free(tmp_ctx);
4077 ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid,
4079 if (ret != LDB_SUCCESS) {
4080 talloc_free(tmp_ctx);
4086 /* we only change whenChanged and uSNChanged if the seq_num
4088 if (add_time_element(msg, "whenChanged", t) != LDB_SUCCESS) {
4089 talloc_free(tmp_ctx);
4090 return ldb_operr(ldb);
4093 if (add_uint64_element(ldb, msg, "uSNChanged",
4094 seq_num) != LDB_SUCCESS) {
4095 talloc_free(tmp_ctx);
4096 return ldb_operr(ldb);
4099 old_el = ldb_msg_find_element(msg, attr->lDAPDisplayName);
4100 if (old_el == NULL) {
4101 talloc_free(tmp_ctx);
4102 return ldb_operr(ldb);
4105 ret = dsdb_check_single_valued_link(attr, old_el);
4106 if (ret != LDB_SUCCESS) {
4107 talloc_free(tmp_ctx);
4111 old_el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
4113 ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE, parent);
4114 if (ret != LDB_SUCCESS) {
4115 ldb_debug(ldb, LDB_DEBUG_WARNING, "Failed to apply linked attribute change '%s'\n%s\n",
4117 ldb_ldif_message_string(ldb, tmp_ctx, LDB_CHANGETYPE_MODIFY, msg));
4118 talloc_free(tmp_ctx);
4122 talloc_free(tmp_ctx);
4127 static int replmd_extended(struct ldb_module *module, struct ldb_request *req)
4129 if (strcmp(req->op.extended.oid, DSDB_EXTENDED_REPLICATED_OBJECTS_OID) == 0) {
4130 return replmd_extended_replicated_objects(module, req);
4133 return ldb_next_request(module, req);
4138 we hook into the transaction operations to allow us to
4139 perform the linked attribute updates at the end of the whole
4140 transaction. This allows a forward linked attribute to be created
4141 before the object is created. During a vampire, w2k8 sends us linked
4142 attributes before the objects they are part of.
4144 static int replmd_start_transaction(struct ldb_module *module)
4146 /* create our private structure for this transaction */
4147 struct replmd_private *replmd_private = talloc_get_type(ldb_module_get_private(module),
4148 struct replmd_private);
4149 replmd_txn_cleanup(replmd_private);
4151 /* free any leftover mod_usn records from cancelled
4153 while (replmd_private->ncs) {
4154 struct nc_entry *e = replmd_private->ncs;
4155 DLIST_REMOVE(replmd_private->ncs, e);
4159 return ldb_next_start_trans(module);
4163 on prepare commit we loop over our queued la_context structures and
4166 static int replmd_prepare_commit(struct ldb_module *module)
4168 struct replmd_private *replmd_private =
4169 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
4170 struct la_entry *la, *prev;
4171 struct la_backlink *bl;
4174 /* walk the list backwards, to do the first entry first, as we
4175 * added the entries with DLIST_ADD() which puts them at the
4176 * start of the list */
4177 for (la = DLIST_TAIL(replmd_private->la_list); la; la=prev) {
4178 prev = DLIST_PREV(la);
4179 DLIST_REMOVE(replmd_private->la_list, la);
4180 ret = replmd_process_linked_attribute(module, la, NULL);
4181 if (ret != LDB_SUCCESS) {
4182 replmd_txn_cleanup(replmd_private);
4187 /* process our backlink list, creating and deleting backlinks
4189 for (bl=replmd_private->la_backlinks; bl; bl=bl->next) {
4190 ret = replmd_process_backlink(module, bl, NULL);
4191 if (ret != LDB_SUCCESS) {
4192 replmd_txn_cleanup(replmd_private);
4197 replmd_txn_cleanup(replmd_private);
4199 /* possibly change @REPLCHANGED */
4200 ret = replmd_notify_store(module, NULL);
4201 if (ret != LDB_SUCCESS) {
4205 return ldb_next_prepare_commit(module);
4208 static int replmd_del_transaction(struct ldb_module *module)
4210 struct replmd_private *replmd_private =
4211 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
4212 replmd_txn_cleanup(replmd_private);
4214 return ldb_next_del_trans(module);
4218 static const struct ldb_module_ops ldb_repl_meta_data_module_ops = {
4219 .name = "repl_meta_data",
4220 .init_context = replmd_init,
4222 .modify = replmd_modify,
4223 .rename = replmd_rename,
4224 .del = replmd_delete,
4225 .extended = replmd_extended,
4226 .start_transaction = replmd_start_transaction,
4227 .prepare_commit = replmd_prepare_commit,
4228 .del_transaction = replmd_del_transaction,
4231 int ldb_repl_meta_data_module_init(const char *version)
4233 LDB_MODULE_CHECK_VERSION(version);
4234 return ldb_register_module(&ldb_repl_meta_data_module_ops);