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
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 3 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>.
26 * Component: ldb repl_meta_data module
28 * Description: - add a unique objectGUID onto every new record,
29 * - handle whenCreated, whenChanged timestamps
30 * - handle uSNCreated, uSNChanged numbers
31 * - handle replPropertyMetaData attribute
34 * Author: Stefan Metzmacher
38 #include "ldb_module.h"
39 #include "dsdb/samdb/samdb.h"
40 #include "dsdb/common/proto.h"
41 #include "../libds/common/flags.h"
42 #include "librpc/gen_ndr/ndr_misc.h"
43 #include "librpc/gen_ndr/ndr_drsuapi.h"
44 #include "librpc/gen_ndr/ndr_drsblobs.h"
45 #include "param/param.h"
46 #include "libcli/security/dom_sid.h"
47 #include "lib/util/dlinklist.h"
48 #include "dsdb/samdb/ldb_modules/util.h"
49 #include "lib/util/binsearch.h"
51 #define W2K3_LINKED_ATTRIBUTES 1
53 struct replmd_private {
55 struct la_entry *la_list;
57 struct la_backlink *la_backlinks;
59 struct nc_entry *prev, *next;
66 struct la_entry *next, *prev;
67 struct drsuapi_DsReplicaLinkedAttribute *la;
70 struct replmd_replicated_request {
71 struct ldb_module *module;
72 struct ldb_request *req;
74 const struct dsdb_schema *schema;
76 /* the controls we pass down */
77 struct ldb_control **controls;
79 /* details for the mode where we apply a bunch of inbound replication meessages */
81 uint32_t index_current;
82 struct dsdb_extended_replicated_objects *objs;
84 struct ldb_message *search_msg;
90 static int replmd_replicated_apply_next(struct replmd_replicated_request *ar);
95 allocate the private structure and build the list
96 of partition DNs for use by replmd_notify()
98 static int replmd_init(struct ldb_module *module)
100 struct replmd_private *replmd_private;
101 struct ldb_context *ldb = ldb_module_get_ctx(module);
103 replmd_private = talloc_zero(module, struct replmd_private);
104 if (replmd_private == NULL) {
106 return LDB_ERR_OPERATIONS_ERROR;
108 ldb_module_set_private(module, replmd_private);
110 return ldb_next_init(module);
114 cleanup our per-transaction contexts
116 static void replmd_txn_cleanup(struct replmd_private *replmd_private)
118 talloc_free(replmd_private->la_ctx);
119 replmd_private->la_list = NULL;
120 replmd_private->la_ctx = NULL;
122 talloc_free(replmd_private->bl_ctx);
123 replmd_private->la_backlinks = NULL;
124 replmd_private->bl_ctx = NULL;
129 struct la_backlink *next, *prev;
130 const char *attr_name;
131 struct GUID forward_guid, target_guid;
136 add a backlink to the list of backlinks to add/delete in the prepare
139 static int replmd_add_backlink(struct ldb_module *module, const struct dsdb_schema *schema,
140 struct GUID *forward_guid, struct GUID *target_guid,
141 bool active, const struct dsdb_attribute *schema_attr)
143 const struct dsdb_attribute *target_attr;
144 struct la_backlink *bl;
145 struct replmd_private *replmd_private =
146 talloc_get_type_abort(ldb_module_get_private(module), struct replmd_private);
148 target_attr = dsdb_attribute_by_linkID(schema, schema_attr->linkID + 1);
151 * windows 2003 has a broken schema where the
152 * definition of msDS-IsDomainFor is missing (which is
153 * supposed to be the backlink of the
154 * msDS-HasDomainNCs attribute
159 /* see if its already in the list */
160 for (bl=replmd_private->la_backlinks; bl; bl=bl->next) {
161 if (GUID_equal(forward_guid, &bl->forward_guid) &&
162 GUID_equal(target_guid, &bl->target_guid) &&
163 (target_attr->lDAPDisplayName == bl->attr_name ||
164 strcmp(target_attr->lDAPDisplayName, bl->attr_name) == 0)) {
170 /* we found an existing one */
171 if (bl->active == active) {
174 DLIST_REMOVE(replmd_private->la_backlinks, bl);
179 if (replmd_private->bl_ctx == NULL) {
180 replmd_private->bl_ctx = talloc_new(replmd_private);
181 if (replmd_private->bl_ctx == NULL) {
182 ldb_module_oom(module);
183 return LDB_ERR_OPERATIONS_ERROR;
188 bl = talloc(replmd_private->bl_ctx, struct la_backlink);
190 ldb_module_oom(module);
191 return LDB_ERR_OPERATIONS_ERROR;
194 bl->attr_name = target_attr->lDAPDisplayName;
195 bl->forward_guid = *forward_guid;
196 bl->target_guid = *target_guid;
199 DLIST_ADD(replmd_private->la_backlinks, bl);
205 process the list of backlinks we accumulated during
206 a transaction, adding and deleting the backlinks
207 from the target objects
209 static int replmd_process_backlink(struct ldb_module *module, struct la_backlink *bl)
211 struct ldb_dn *target_dn, *source_dn;
213 struct ldb_context *ldb = ldb_module_get_ctx(module);
214 struct ldb_message *msg;
215 TALLOC_CTX *tmp_ctx = talloc_new(bl);
221 - construct ldb_message
222 - either an add or a delete
224 ret = dsdb_module_dn_by_guid(module, tmp_ctx, &bl->target_guid, &target_dn);
225 if (ret != LDB_SUCCESS) {
226 ldb_asprintf_errstring(ldb, "Failed to find target DN for linked attribute with GUID %s\n",
227 GUID_string(bl, &bl->target_guid));
228 talloc_free(tmp_ctx);
232 ret = dsdb_module_dn_by_guid(module, tmp_ctx, &bl->forward_guid, &source_dn);
233 if (ret != LDB_SUCCESS) {
234 ldb_asprintf_errstring(ldb, "Failed to find source DN for linked attribute with GUID %s\n",
235 GUID_string(bl, &bl->forward_guid));
236 talloc_free(tmp_ctx);
240 msg = ldb_msg_new(tmp_ctx);
242 ldb_module_oom(module);
243 talloc_free(tmp_ctx);
244 return LDB_ERR_OPERATIONS_ERROR;
247 /* construct a ldb_message for adding/deleting the backlink */
249 dn_string = ldb_dn_get_extended_linearized(tmp_ctx, source_dn, 1);
251 ldb_module_oom(module);
252 talloc_free(tmp_ctx);
253 return LDB_ERR_OPERATIONS_ERROR;
255 ret = ldb_msg_add_steal_string(msg, bl->attr_name, dn_string);
256 if (ret != LDB_SUCCESS) {
257 talloc_free(tmp_ctx);
260 msg->elements[0].flags = bl->active?LDB_FLAG_MOD_ADD:LDB_FLAG_MOD_DELETE;
262 ret = dsdb_module_modify(module, msg, 0);
263 if (ret != LDB_SUCCESS) {
264 ldb_asprintf_errstring(ldb, "Failed to %s backlink from %s to %s - %s",
265 bl->active?"add":"remove",
266 ldb_dn_get_linearized(source_dn),
267 ldb_dn_get_linearized(target_dn),
269 talloc_free(tmp_ctx);
272 talloc_free(tmp_ctx);
278 * Callback for most write operations in this module:
280 * notify the repl task that a object has changed. The notifies are
281 * gathered up in the replmd_private structure then written to the
282 * @REPLCHANGED object in each partition during the prepare_commit
284 static int replmd_op_callback(struct ldb_request *req, struct ldb_reply *ares)
287 struct replmd_replicated_request *ac =
288 talloc_get_type_abort(req->context, struct replmd_replicated_request);
289 struct replmd_private *replmd_private =
290 talloc_get_type_abort(ldb_module_get_private(ac->module), struct replmd_private);
291 struct nc_entry *modified_partition;
292 struct ldb_control *partition_ctrl;
293 const struct dsdb_control_current_partition *partition;
295 struct ldb_control **controls;
297 partition_ctrl = ldb_reply_get_control(ares, DSDB_CONTROL_CURRENT_PARTITION_OID);
299 /* Remove the 'partition' control from what we pass up the chain */
300 controls = controls_except_specified(ares->controls, ares, partition_ctrl);
302 if (ares->error != LDB_SUCCESS) {
303 return ldb_module_done(ac->req, controls,
304 ares->response, ares->error);
307 if (ares->type != LDB_REPLY_DONE) {
308 ldb_set_errstring(ldb_module_get_ctx(ac->module), "Invalid reply type for notify\n!");
309 return ldb_module_done(ac->req, NULL,
310 NULL, LDB_ERR_OPERATIONS_ERROR);
313 if (!partition_ctrl) {
314 return ldb_module_done(ac->req, NULL,
315 NULL, LDB_ERR_OPERATIONS_ERROR);
318 partition = talloc_get_type_abort(partition_ctrl->data,
319 struct dsdb_control_current_partition);
321 if (ac->seq_num > 0) {
322 for (modified_partition = replmd_private->ncs; modified_partition;
323 modified_partition = modified_partition->next) {
324 if (ldb_dn_compare(modified_partition->dn, partition->dn) == 0) {
329 if (modified_partition == NULL) {
330 modified_partition = talloc_zero(replmd_private, struct nc_entry);
331 if (!modified_partition) {
332 ldb_oom(ldb_module_get_ctx(ac->module));
333 return ldb_module_done(ac->req, NULL,
334 NULL, LDB_ERR_OPERATIONS_ERROR);
336 modified_partition->dn = ldb_dn_copy(modified_partition, partition->dn);
337 if (!modified_partition->dn) {
338 ldb_oom(ldb_module_get_ctx(ac->module));
339 return ldb_module_done(ac->req, NULL,
340 NULL, LDB_ERR_OPERATIONS_ERROR);
342 DLIST_ADD(replmd_private->ncs, modified_partition);
345 if (ac->seq_num > modified_partition->mod_usn) {
346 modified_partition->mod_usn = ac->seq_num;
350 if (ac->apply_mode) {
354 ret = replmd_replicated_apply_next(ac);
355 if (ret != LDB_SUCCESS) {
356 return ldb_module_done(ac->req, NULL, NULL, ret);
360 /* free the partition control container here, for the
361 * common path. Other cases will have it cleaned up
362 * eventually with the ares */
363 talloc_free(partition_ctrl);
364 return ldb_module_done(ac->req,
365 controls_except_specified(controls, ares, partition_ctrl),
366 ares->response, LDB_SUCCESS);
372 * update a @REPLCHANGED record in each partition if there have been
373 * any writes of replicated data in the partition
375 static int replmd_notify_store(struct ldb_module *module)
377 struct replmd_private *replmd_private =
378 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
379 struct ldb_context *ldb = ldb_module_get_ctx(module);
381 while (replmd_private->ncs) {
383 struct nc_entry *modified_partition = replmd_private->ncs;
385 ret = dsdb_save_partition_usn(ldb, modified_partition->dn, modified_partition->mod_usn);
386 if (ret != LDB_SUCCESS) {
387 DEBUG(0,(__location__ ": Failed to save partition uSN for %s\n",
388 ldb_dn_get_linearized(modified_partition->dn)));
391 DLIST_REMOVE(replmd_private->ncs, modified_partition);
392 talloc_free(modified_partition);
400 created a replmd_replicated_request context
402 static struct replmd_replicated_request *replmd_ctx_init(struct ldb_module *module,
403 struct ldb_request *req)
405 struct ldb_context *ldb;
406 struct replmd_replicated_request *ac;
408 ldb = ldb_module_get_ctx(module);
410 ac = talloc_zero(req, struct replmd_replicated_request);
419 ac->schema = dsdb_get_schema(ldb);
421 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
422 "replmd_modify: no dsdb_schema loaded");
423 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
431 add a time element to a record
433 static int add_time_element(struct ldb_message *msg, const char *attr, time_t t)
435 struct ldb_message_element *el;
438 if (ldb_msg_find_element(msg, attr) != NULL) {
442 s = ldb_timestring(msg, t);
444 return LDB_ERR_OPERATIONS_ERROR;
447 if (ldb_msg_add_string(msg, attr, s) != LDB_SUCCESS) {
448 return LDB_ERR_OPERATIONS_ERROR;
451 el = ldb_msg_find_element(msg, attr);
452 /* always set as replace. This works because on add ops, the flag
454 el->flags = LDB_FLAG_MOD_REPLACE;
460 add a uint64_t element to a record
462 static int add_uint64_element(struct ldb_message *msg, const char *attr, uint64_t v)
464 struct ldb_message_element *el;
466 if (ldb_msg_find_element(msg, attr) != NULL) {
470 if (ldb_msg_add_fmt(msg, attr, "%llu", (unsigned long long)v) != LDB_SUCCESS) {
471 return LDB_ERR_OPERATIONS_ERROR;
474 el = ldb_msg_find_element(msg, attr);
475 /* always set as replace. This works because on add ops, the flag
477 el->flags = LDB_FLAG_MOD_REPLACE;
482 static int replmd_replPropertyMetaData1_attid_sort(const struct replPropertyMetaData1 *m1,
483 const struct replPropertyMetaData1 *m2,
484 const uint32_t *rdn_attid)
486 if (m1->attid == m2->attid) {
491 * the rdn attribute should be at the end!
492 * so we need to return a value greater than zero
493 * which means m1 is greater than m2
495 if (m1->attid == *rdn_attid) {
500 * the rdn attribute should be at the end!
501 * so we need to return a value less than zero
502 * which means m2 is greater than m1
504 if (m2->attid == *rdn_attid) {
508 return m1->attid > m2->attid ? 1 : -1;
511 static int replmd_replPropertyMetaDataCtr1_sort(struct replPropertyMetaDataCtr1 *ctr1,
512 const struct dsdb_schema *schema,
515 const char *rdn_name;
516 const struct dsdb_attribute *rdn_sa;
518 rdn_name = ldb_dn_get_rdn_name(dn);
520 DEBUG(0,(__location__ ": No rDN for %s?\n", ldb_dn_get_linearized(dn)));
521 return LDB_ERR_OPERATIONS_ERROR;
524 rdn_sa = dsdb_attribute_by_lDAPDisplayName(schema, rdn_name);
525 if (rdn_sa == NULL) {
526 DEBUG(0,(__location__ ": No sa found for rDN %s for %s\n", rdn_name, ldb_dn_get_linearized(dn)));
527 return LDB_ERR_OPERATIONS_ERROR;
530 DEBUG(6,("Sorting rpmd with attid exception %u rDN=%s DN=%s\n",
531 rdn_sa->attributeID_id, rdn_name, ldb_dn_get_linearized(dn)));
533 ldb_qsort(ctr1->array, ctr1->count, sizeof(struct replPropertyMetaData1),
534 discard_const_p(void, &rdn_sa->attributeID_id),
535 (ldb_qsort_cmp_fn_t)replmd_replPropertyMetaData1_attid_sort);
540 static int replmd_ldb_message_element_attid_sort(const struct ldb_message_element *e1,
541 const struct ldb_message_element *e2,
542 const struct dsdb_schema *schema)
544 const struct dsdb_attribute *a1;
545 const struct dsdb_attribute *a2;
548 * TODO: make this faster by caching the dsdb_attribute pointer
549 * on the ldb_messag_element
552 a1 = dsdb_attribute_by_lDAPDisplayName(schema, e1->name);
553 a2 = dsdb_attribute_by_lDAPDisplayName(schema, e2->name);
556 * TODO: remove this check, we should rely on e1 and e2 having valid attribute names
560 return strcasecmp(e1->name, e2->name);
562 if (a1->attributeID_id == a2->attributeID_id) {
565 return a1->attributeID_id > a2->attributeID_id ? 1 : -1;
568 static void replmd_ldb_message_sort(struct ldb_message *msg,
569 const struct dsdb_schema *schema)
571 ldb_qsort(msg->elements, msg->num_elements, sizeof(struct ldb_message_element),
572 discard_const_p(void, schema), (ldb_qsort_cmp_fn_t)replmd_ldb_message_element_attid_sort);
575 static int replmd_add(struct ldb_module *module, struct ldb_request *req)
577 struct ldb_context *ldb;
578 struct ldb_control *control;
579 struct replmd_replicated_request *ac;
580 enum ndr_err_code ndr_err;
581 struct ldb_request *down_req;
582 struct ldb_message *msg;
583 const DATA_BLOB *guid_blob;
585 struct replPropertyMetaDataBlob nmd;
586 struct ldb_val nmd_value;
587 const struct GUID *our_invocation_id;
588 time_t t = time(NULL);
593 bool allow_add_guid = false;
594 bool remove_current_guid = false;
596 /* check if there's a show relax control (used by provision to say 'I know what I'm doing') */
597 control = ldb_request_get_control(req, LDB_CONTROL_RELAX_OID);
602 /* do not manipulate our control entries */
603 if (ldb_dn_is_special(req->op.add.message->dn)) {
604 return ldb_next_request(module, req);
607 ldb = ldb_module_get_ctx(module);
609 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_add\n");
611 ac = replmd_ctx_init(module, req);
613 return LDB_ERR_OPERATIONS_ERROR;
616 guid_blob = ldb_msg_find_ldb_val(req->op.add.message, "objectGUID");
617 if ( guid_blob != NULL ) {
618 if( !allow_add_guid ) {
619 ldb_debug_set(ldb, LDB_DEBUG_ERROR,
620 "replmd_add: it's not allowed to add an object with objectGUID\n");
622 return LDB_ERR_UNWILLING_TO_PERFORM;
624 NTSTATUS status = GUID_from_data_blob(guid_blob,&guid);
625 if ( !NT_STATUS_IS_OK(status)) {
626 ldb_debug_set(ldb, LDB_DEBUG_ERROR,
627 "replmd_add: Unable to parse as a GUID the attribute objectGUID\n");
629 return LDB_ERR_UNWILLING_TO_PERFORM;
631 /* we remove this attribute as it can be a string and will not be treated
632 correctly and then we will readd it latter on in the good format*/
633 remove_current_guid = true;
637 guid = GUID_random();
640 /* Get a sequence number from the backend */
641 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ac->seq_num);
642 if (ret != LDB_SUCCESS) {
647 /* get our invocationId */
648 our_invocation_id = samdb_ntds_invocation_id(ldb);
649 if (!our_invocation_id) {
650 ldb_debug_set(ldb, LDB_DEBUG_ERROR,
651 "replmd_add: unable to find invocationId\n");
653 return LDB_ERR_OPERATIONS_ERROR;
656 /* we have to copy the message as the caller might have it as a const */
657 msg = ldb_msg_copy_shallow(ac, req->op.add.message);
661 return LDB_ERR_OPERATIONS_ERROR;
664 /* generated times */
665 unix_to_nt_time(&now, t);
666 time_str = ldb_timestring(msg, t);
670 return LDB_ERR_OPERATIONS_ERROR;
672 if (remove_current_guid) {
673 ldb_msg_remove_attr(msg,"objectGUID");
677 * remove autogenerated attributes
679 ldb_msg_remove_attr(msg, "whenCreated");
680 ldb_msg_remove_attr(msg, "whenChanged");
681 ldb_msg_remove_attr(msg, "uSNCreated");
682 ldb_msg_remove_attr(msg, "uSNChanged");
683 ldb_msg_remove_attr(msg, "replPropertyMetaData");
686 * readd replicated attributes
688 ret = ldb_msg_add_string(msg, "whenCreated", time_str);
689 if (ret != LDB_SUCCESS) {
695 /* build the replication meta_data */
698 nmd.ctr.ctr1.count = msg->num_elements;
699 nmd.ctr.ctr1.array = talloc_array(msg,
700 struct replPropertyMetaData1,
702 if (!nmd.ctr.ctr1.array) {
705 return LDB_ERR_OPERATIONS_ERROR;
708 for (i=0; i < msg->num_elements; i++) {
709 struct ldb_message_element *e = &msg->elements[i];
710 struct replPropertyMetaData1 *m = &nmd.ctr.ctr1.array[ni];
711 const struct dsdb_attribute *sa;
713 if (e->name[0] == '@') continue;
715 sa = dsdb_attribute_by_lDAPDisplayName(ac->schema, e->name);
717 ldb_debug_set(ldb, LDB_DEBUG_ERROR,
718 "replmd_add: attribute '%s' not defined in schema\n",
721 return LDB_ERR_NO_SUCH_ATTRIBUTE;
724 if ((sa->systemFlags & DS_FLAG_ATTR_NOT_REPLICATED) || (sa->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED)) {
725 /* if the attribute is not replicated (0x00000001)
726 * or constructed (0x00000004) it has no metadata
731 m->attid = sa->attributeID_id;
733 m->originating_change_time = now;
734 m->originating_invocation_id = *our_invocation_id;
735 m->originating_usn = ac->seq_num;
736 m->local_usn = ac->seq_num;
740 /* fix meta data count */
741 nmd.ctr.ctr1.count = ni;
744 * sort meta data array, and move the rdn attribute entry to the end
746 ret = replmd_replPropertyMetaDataCtr1_sort(&nmd.ctr.ctr1, ac->schema, msg->dn);
747 if (ret != LDB_SUCCESS) {
752 /* generated NDR encoded values */
753 ndr_err = ndr_push_struct_blob(&nmd_value, msg,
754 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
756 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
757 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
760 return LDB_ERR_OPERATIONS_ERROR;
764 * add the autogenerated values
766 ret = dsdb_msg_add_guid(msg, &guid, "objectGUID");
767 if (ret != LDB_SUCCESS) {
772 ret = ldb_msg_add_string(msg, "whenChanged", time_str);
773 if (ret != LDB_SUCCESS) {
778 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", ac->seq_num);
779 if (ret != LDB_SUCCESS) {
784 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ac->seq_num);
785 if (ret != LDB_SUCCESS) {
790 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
791 if (ret != LDB_SUCCESS) {
798 * sort the attributes by attid before storing the object
800 replmd_ldb_message_sort(msg, ac->schema);
802 ret = ldb_build_add_req(&down_req, ldb, ac,
805 ac, replmd_op_callback,
807 if (ret != LDB_SUCCESS) {
812 /* mark the control done */
814 control->critical = 0;
817 /* go on with the call chain */
818 return ldb_next_request(module, down_req);
823 * update the replPropertyMetaData for one element
825 static int replmd_update_rpmd_element(struct ldb_context *ldb,
826 struct ldb_message *msg,
827 struct ldb_message_element *el,
828 struct replPropertyMetaDataBlob *omd,
829 const struct dsdb_schema *schema,
831 const struct GUID *our_invocation_id,
835 const struct dsdb_attribute *a;
836 struct replPropertyMetaData1 *md1;
838 a = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
840 DEBUG(0,(__location__ ": Unable to find attribute %s in schema\n",
842 return LDB_ERR_OPERATIONS_ERROR;
845 if ((a->systemFlags & DS_FLAG_ATTR_NOT_REPLICATED) || (a->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED)) {
849 for (i=0; i<omd->ctr.ctr1.count; i++) {
850 if (a->attributeID_id == omd->ctr.ctr1.array[i].attid) break;
853 #if W2K3_LINKED_ATTRIBUTES
854 if (a->linkID != 0 && dsdb_functional_level(ldb) > DS_DOMAIN_FUNCTION_2000) {
855 /* linked attributes are not stored in
856 replPropertyMetaData in FL above w2k, but we do
857 raise the seqnum for the object */
859 ldb_sequence_number(ldb, LDB_SEQ_NEXT, seq_num) != LDB_SUCCESS) {
860 return LDB_ERR_OPERATIONS_ERROR;
866 if (i == omd->ctr.ctr1.count) {
867 /* we need to add a new one */
868 omd->ctr.ctr1.array = talloc_realloc(msg, omd->ctr.ctr1.array,
869 struct replPropertyMetaData1, omd->ctr.ctr1.count+1);
870 if (omd->ctr.ctr1.array == NULL) {
872 return LDB_ERR_OPERATIONS_ERROR;
874 omd->ctr.ctr1.count++;
875 ZERO_STRUCT(omd->ctr.ctr1.array[i]);
878 /* Get a new sequence number from the backend. We only do this
879 * if we have a change that requires a new
880 * replPropertyMetaData element
883 int ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, seq_num);
884 if (ret != LDB_SUCCESS) {
885 return LDB_ERR_OPERATIONS_ERROR;
889 md1 = &omd->ctr.ctr1.array[i];
891 md1->attid = a->attributeID_id;
892 md1->originating_change_time = now;
893 md1->originating_invocation_id = *our_invocation_id;
894 md1->originating_usn = *seq_num;
895 md1->local_usn = *seq_num;
901 * update the replPropertyMetaData object each time we modify an
902 * object. This is needed for DRS replication, as the merge on the
903 * client is based on this object
905 static int replmd_update_rpmd(struct ldb_module *module,
906 const struct dsdb_schema *schema,
907 struct ldb_message *msg, uint64_t *seq_num,
910 const struct ldb_val *omd_value;
911 enum ndr_err_code ndr_err;
912 struct replPropertyMetaDataBlob omd;
915 const struct GUID *our_invocation_id;
917 const char *attrs[] = { "replPropertyMetaData" , NULL };
918 struct ldb_result *res;
919 struct ldb_context *ldb;
921 ldb = ldb_module_get_ctx(module);
923 our_invocation_id = samdb_ntds_invocation_id(ldb);
924 if (!our_invocation_id) {
925 /* this happens during an initial vampire while
926 updating the schema */
927 DEBUG(5,("No invocationID - skipping replPropertyMetaData update\n"));
931 unix_to_nt_time(&now, t);
933 /* search for the existing replPropertyMetaDataBlob */
934 ret = dsdb_search_dn_with_deleted(ldb, msg, &res, msg->dn, attrs);
935 if (ret != LDB_SUCCESS || res->count != 1) {
936 DEBUG(0,(__location__ ": Object %s failed to find replPropertyMetaData\n",
937 ldb_dn_get_linearized(msg->dn)));
938 return LDB_ERR_OPERATIONS_ERROR;
942 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
944 DEBUG(0,(__location__ ": Object %s does not have a replPropertyMetaData attribute\n",
945 ldb_dn_get_linearized(msg->dn)));
946 return LDB_ERR_OPERATIONS_ERROR;
949 ndr_err = ndr_pull_struct_blob(omd_value, msg,
950 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")), &omd,
951 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
952 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
953 DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s\n",
954 ldb_dn_get_linearized(msg->dn)));
955 return LDB_ERR_OPERATIONS_ERROR;
958 if (omd.version != 1) {
959 DEBUG(0,(__location__ ": bad version %u in replPropertyMetaData for %s\n",
960 omd.version, ldb_dn_get_linearized(msg->dn)));
961 return LDB_ERR_OPERATIONS_ERROR;
964 for (i=0; i<msg->num_elements; i++) {
965 ret = replmd_update_rpmd_element(ldb, msg, &msg->elements[i], &omd, schema, seq_num,
966 our_invocation_id, now);
967 if (ret != LDB_SUCCESS) {
973 * replmd_update_rpmd_element has done an update if the
977 struct ldb_val *md_value;
978 struct ldb_message_element *el;
980 md_value = talloc(msg, struct ldb_val);
981 if (md_value == NULL) {
983 return LDB_ERR_OPERATIONS_ERROR;
986 ret = replmd_replPropertyMetaDataCtr1_sort(&omd.ctr.ctr1, schema, msg->dn);
987 if (ret != LDB_SUCCESS) {
991 ndr_err = ndr_push_struct_blob(md_value, msg,
992 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
994 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
995 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
996 DEBUG(0,(__location__ ": Failed to marshall replPropertyMetaData for %s\n",
997 ldb_dn_get_linearized(msg->dn)));
998 return LDB_ERR_OPERATIONS_ERROR;
1001 ret = ldb_msg_add_empty(msg, "replPropertyMetaData", LDB_FLAG_MOD_REPLACE, &el);
1002 if (ret != LDB_SUCCESS) {
1003 DEBUG(0,(__location__ ": Failed to add updated replPropertyMetaData %s\n",
1004 ldb_dn_get_linearized(msg->dn)));
1009 el->values = md_value;
1017 struct dsdb_dn *dsdb_dn;
1022 static int parsed_dn_compare(struct parsed_dn *pdn1, struct parsed_dn *pdn2)
1024 return GUID_compare(pdn1->guid, pdn2->guid);
1027 static struct parsed_dn *parsed_dn_find(struct parsed_dn *pdn, int count, struct GUID *guid)
1029 struct parsed_dn *ret;
1030 BINARY_ARRAY_SEARCH(pdn, count, guid, guid, GUID_compare, ret);
1035 get a series of message element values as an array of DNs and GUIDs
1036 the result is sorted by GUID
1038 static int get_parsed_dns(struct ldb_module *module, TALLOC_CTX *mem_ctx,
1039 struct ldb_message_element *el, struct parsed_dn **pdn,
1040 const char *ldap_oid)
1043 struct ldb_context *ldb = ldb_module_get_ctx(module);
1050 (*pdn) = talloc_array(mem_ctx, struct parsed_dn, el->num_values);
1052 ldb_module_oom(module);
1053 return LDB_ERR_OPERATIONS_ERROR;
1056 for (i=0; i<el->num_values; i++) {
1057 struct ldb_val *v = &el->values[i];
1060 struct parsed_dn *p;
1064 p->dsdb_dn = dsdb_dn_parse(*pdn, ldb, v, ldap_oid);
1065 if (p->dsdb_dn == NULL) {
1066 return LDB_ERR_INVALID_DN_SYNTAX;
1069 dn = p->dsdb_dn->dn;
1071 p->guid = talloc(*pdn, struct GUID);
1072 if (p->guid == NULL) {
1073 ldb_module_oom(module);
1074 return LDB_ERR_OPERATIONS_ERROR;
1077 status = dsdb_get_extended_dn_guid(dn, p->guid);
1078 if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
1079 /* we got a DN without a GUID - go find the GUID */
1080 int ret = dsdb_find_guid_by_dn(ldb, dn, p->guid);
1081 if (ret != LDB_SUCCESS) {
1082 ldb_asprintf_errstring(ldb, "Unable to find GUID for DN %s\n",
1083 ldb_dn_get_linearized(dn));
1086 } else if (!NT_STATUS_IS_OK(status)) {
1087 return LDB_ERR_OPERATIONS_ERROR;
1090 /* keep a pointer to the original ldb_val */
1094 qsort(*pdn, el->num_values, sizeof((*pdn)[0]), (comparison_fn_t)parsed_dn_compare);
1100 build a new extended DN, including all meta data fields
1102 DELETED = 1 or missing
1103 RMD_ADDTIME = originating_add_time
1104 RMD_INVOCID = originating_invocation_id
1105 RMD_CHANGETIME = originating_change_time
1106 RMD_USN = originating_usn
1107 RMD_VERSION = version
1109 static int replmd_build_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct parsed_dn *p,
1110 const struct GUID *invocation_id, uint64_t seq_num, time_t t)
1112 struct ldb_dn *dn = p->dsdb_dn->dn;
1114 const char *tstring, *usn_string;
1115 struct ldb_val tval;
1117 struct ldb_val usnv;
1118 struct ldb_val vers;
1121 const char *dnstring;
1123 unix_to_nt_time(&now, t);
1124 tstring = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)now);
1126 return LDB_ERR_OPERATIONS_ERROR;
1128 tval = data_blob_string_const(tstring);
1130 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)seq_num);
1132 return LDB_ERR_OPERATIONS_ERROR;
1134 usnv = data_blob_string_const(usn_string);
1136 vers = data_blob_string_const("0");
1138 status = GUID_to_ndr_blob(invocation_id, dn, &iid);
1139 if (!NT_STATUS_IS_OK(status)) {
1140 return LDB_ERR_OPERATIONS_ERROR;
1143 ret = ldb_dn_set_extended_component(dn, "DELETED", NULL);
1144 if (ret != LDB_SUCCESS) return ret;
1145 ret = ldb_dn_set_extended_component(dn, "RMD_ADDTIME", &tval);
1146 if (ret != LDB_SUCCESS) return ret;
1147 ret = ldb_dn_set_extended_component(dn, "RMD_INVOCID", &iid);
1148 if (ret != LDB_SUCCESS) return ret;
1149 ret = ldb_dn_set_extended_component(dn, "RMD_CHANGETIME", &tval);
1150 if (ret != LDB_SUCCESS) return ret;
1151 ret = ldb_dn_set_extended_component(dn, "RMD_USN", &usnv);
1152 if (ret != LDB_SUCCESS) return ret;
1153 ret = ldb_dn_set_extended_component(dn, "RMD_VERSION", &vers);
1154 if (ret != LDB_SUCCESS) return ret;
1156 dnstring = dsdb_dn_get_extended_linearized(mem_ctx, p->dsdb_dn, 1);
1157 if (dnstring == NULL) {
1158 return LDB_ERR_OPERATIONS_ERROR;
1160 *v = data_blob_string_const(dnstring);
1167 update an extended DN, including all meta data fields
1169 see replmd_build_la_val for value names
1171 static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct parsed_dn *p,
1172 struct parsed_dn *old_p, const struct GUID *invocation_id,
1173 uint64_t seq_num, time_t t, bool deleted)
1175 struct ldb_dn *dn = p->dsdb_dn->dn;
1177 const char *tstring, *usn_string;
1178 struct ldb_val tval;
1180 struct ldb_val usnv;
1181 struct ldb_val vers;
1182 const struct ldb_val *old_addtime, *old_version;
1185 const char *dnstring;
1187 unix_to_nt_time(&now, t);
1188 tstring = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)now);
1190 return LDB_ERR_OPERATIONS_ERROR;
1192 tval = data_blob_string_const(tstring);
1194 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)seq_num);
1196 return LDB_ERR_OPERATIONS_ERROR;
1198 usnv = data_blob_string_const(usn_string);
1200 status = GUID_to_ndr_blob(invocation_id, dn, &iid);
1201 if (!NT_STATUS_IS_OK(status)) {
1202 return LDB_ERR_OPERATIONS_ERROR;
1207 dv = data_blob_string_const("1");
1208 ret = ldb_dn_set_extended_component(dn, "DELETED", &dv);
1210 ret = ldb_dn_set_extended_component(dn, "DELETED", NULL);
1212 if (ret != LDB_SUCCESS) return ret;
1214 /* get the ADDTIME from the original */
1215 old_addtime = ldb_dn_get_extended_component(old_p->dsdb_dn->dn, "RMD_ADDTIME");
1216 if (old_addtime == NULL) {
1217 old_addtime = &tval;
1220 ret = ldb_dn_set_extended_component(dn, "RMD_ADDTIME", old_addtime);
1221 if (ret != LDB_SUCCESS) return ret;
1224 /* use our invocation id */
1225 ret = ldb_dn_set_extended_component(dn, "RMD_INVOCID", &iid);
1226 if (ret != LDB_SUCCESS) return ret;
1228 /* changetime is the current time */
1229 ret = ldb_dn_set_extended_component(dn, "RMD_CHANGETIME", &tval);
1230 if (ret != LDB_SUCCESS) return ret;
1232 /* update the USN */
1233 ret = ldb_dn_set_extended_component(dn, "RMD_USN", &usnv);
1234 if (ret != LDB_SUCCESS) return ret;
1236 /* increase the version by 1 */
1237 old_version = ldb_dn_get_extended_component(old_p->dsdb_dn->dn, "RMD_VERSION");
1238 if (old_version == NULL) {
1239 vers = data_blob_string_const("0");
1242 vstring = talloc_strndup(dn, (const char *)old_version->data, old_version->length);
1244 return LDB_ERR_OPERATIONS_ERROR;
1246 vstring = talloc_asprintf(dn, "%lu",
1247 (unsigned long)strtoul(vstring, NULL, 0)+1);
1248 vers = data_blob_string_const(vstring);
1250 ret = ldb_dn_set_extended_component(dn, "RMD_VERSION", &vers);
1251 if (ret != LDB_SUCCESS) return ret;
1253 dnstring = dsdb_dn_get_extended_linearized(mem_ctx, p->dsdb_dn, 1);
1254 if (dnstring == NULL) {
1255 return LDB_ERR_OPERATIONS_ERROR;
1257 *v = data_blob_string_const(dnstring);
1263 handle adding a linked attribute
1265 static int replmd_modify_la_add(struct ldb_module *module,
1266 struct dsdb_schema *schema,
1267 struct ldb_message *msg,
1268 struct ldb_message_element *el,
1269 struct ldb_message_element *old_el,
1270 const struct dsdb_attribute *schema_attr,
1273 struct GUID *msg_guid)
1276 struct parsed_dn *dns, *old_dns;
1277 TALLOC_CTX *tmp_ctx = talloc_new(msg);
1279 struct ldb_val *new_values = NULL;
1280 unsigned int num_new_values = 0;
1281 unsigned old_num_values = old_el?old_el->num_values:0;
1282 const struct GUID *invocation_id;
1283 struct ldb_context *ldb = ldb_module_get_ctx(module);
1285 ret = get_parsed_dns(module, tmp_ctx, el, &dns, schema_attr->syntax->ldap_oid);
1286 if (ret != LDB_SUCCESS) {
1287 talloc_free(tmp_ctx);
1291 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns, schema_attr->syntax->ldap_oid);
1292 if (ret != LDB_SUCCESS) {
1293 talloc_free(tmp_ctx);
1297 invocation_id = samdb_ntds_invocation_id(ldb);
1298 if (!invocation_id) {
1299 return LDB_ERR_OPERATIONS_ERROR;
1302 /* for each new value, see if it exists already with the same GUID */
1303 for (i=0; i<el->num_values; i++) {
1304 struct parsed_dn *p = parsed_dn_find(old_dns, old_num_values, dns[i].guid);
1306 /* this is a new linked attribute value */
1307 new_values = talloc_realloc(tmp_ctx, new_values, struct ldb_val, num_new_values+1);
1308 if (new_values == NULL) {
1309 ldb_module_oom(module);
1310 talloc_free(tmp_ctx);
1311 return LDB_ERR_OPERATIONS_ERROR;
1313 ret = replmd_build_la_val(new_values, &new_values[num_new_values], &dns[i],
1314 invocation_id, seq_num, t);
1315 if (ret != LDB_SUCCESS) {
1316 talloc_free(tmp_ctx);
1321 /* this is only allowed if the GUID was
1322 previously deleted. */
1323 const struct ldb_val *v;
1324 v = ldb_dn_get_extended_component(p->dsdb_dn->dn, "DELETED");
1326 ldb_asprintf_errstring(ldb, "Attribute %s already exists for target GUID %s",
1327 el->name, GUID_string(tmp_ctx, p->guid));
1328 talloc_free(tmp_ctx);
1329 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
1331 ret = replmd_update_la_val(old_el->values, p->v, &dns[i], p, invocation_id, seq_num, t, false);
1332 if (ret != LDB_SUCCESS) {
1333 talloc_free(tmp_ctx);
1338 ret = replmd_add_backlink(module, schema, msg_guid, dns[i].guid, true, schema_attr);
1339 if (ret != LDB_SUCCESS) {
1340 talloc_free(tmp_ctx);
1345 /* add the new ones on to the end of the old values, constructing a new el->values */
1346 el->values = talloc_realloc(msg->elements, old_el?old_el->values:NULL,
1348 old_num_values+num_new_values);
1349 if (el->values == NULL) {
1350 ldb_module_oom(module);
1351 return LDB_ERR_OPERATIONS_ERROR;
1354 memcpy(&el->values[old_num_values], new_values, num_new_values*sizeof(struct ldb_val));
1355 el->num_values = old_num_values + num_new_values;
1357 talloc_steal(msg->elements, el->values);
1358 talloc_steal(el->values, new_values);
1360 talloc_free(tmp_ctx);
1362 /* we now tell the backend to replace all existing values
1363 with the one we have constructed */
1364 el->flags = LDB_FLAG_MOD_REPLACE;
1371 handle deleting all active linked attributes
1373 static int replmd_modify_la_delete(struct ldb_module *module,
1374 struct dsdb_schema *schema,
1375 struct ldb_message *msg,
1376 struct ldb_message_element *el,
1377 struct ldb_message_element *old_el,
1378 const struct dsdb_attribute *schema_attr,
1381 struct GUID *msg_guid)
1384 struct parsed_dn *dns, *old_dns;
1385 TALLOC_CTX *tmp_ctx = talloc_new(msg);
1387 const struct GUID *invocation_id;
1388 struct ldb_context *ldb = ldb_module_get_ctx(module);
1390 /* check if there is nothing to delete */
1391 if ((!old_el || old_el->num_values == 0) &&
1392 el->num_values == 0) {
1396 if (!old_el || old_el->num_values == 0) {
1397 return LDB_ERR_NO_SUCH_ATTRIBUTE;
1400 ret = get_parsed_dns(module, tmp_ctx, el, &dns, schema_attr->syntax->ldap_oid);
1401 if (ret != LDB_SUCCESS) {
1402 talloc_free(tmp_ctx);
1406 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns, schema_attr->syntax->ldap_oid);
1407 if (ret != LDB_SUCCESS) {
1408 talloc_free(tmp_ctx);
1412 invocation_id = samdb_ntds_invocation_id(ldb);
1413 if (!invocation_id) {
1414 return LDB_ERR_OPERATIONS_ERROR;
1419 /* see if we are being asked to delete any links that
1420 don't exist or are already deleted */
1421 for (i=0; i<el->num_values; i++) {
1422 struct parsed_dn *p = &dns[i];
1423 struct parsed_dn *p2;
1424 const struct ldb_val *v;
1426 p2 = parsed_dn_find(old_dns, old_el->num_values, p->guid);
1428 ldb_asprintf_errstring(ldb, "Attribute %s doesn't exist for target GUID %s",
1429 el->name, GUID_string(tmp_ctx, p->guid));
1430 return LDB_ERR_NO_SUCH_ATTRIBUTE;
1432 v = ldb_dn_get_extended_component(p2->dsdb_dn->dn, "DELETED");
1434 ldb_asprintf_errstring(ldb, "Attribute %s already deleted for target GUID %s",
1435 el->name, GUID_string(tmp_ctx, p->guid));
1436 return LDB_ERR_NO_SUCH_ATTRIBUTE;
1440 /* for each new value, see if it exists already with the same GUID
1441 if it is not already deleted and matches the delete list then delete it
1443 for (i=0; i<old_el->num_values; i++) {
1444 struct parsed_dn *p = &old_dns[i];
1445 const struct ldb_val *v;
1447 if (dns && parsed_dn_find(dns, el->num_values, p->guid) == NULL) {
1451 v = ldb_dn_get_extended_component(p->dsdb_dn->dn, "DELETED");
1452 if (v != NULL) continue;
1454 ret = replmd_update_la_val(old_el->values, p->v, p, p, invocation_id, seq_num, t, true);
1455 if (ret != LDB_SUCCESS) {
1456 talloc_free(tmp_ctx);
1460 ret = replmd_add_backlink(module, schema, msg_guid, old_dns[i].guid, false, schema_attr);
1461 if (ret != LDB_SUCCESS) {
1462 talloc_free(tmp_ctx);
1467 el->values = talloc_steal(msg->elements, old_el->values);
1468 el->num_values = old_el->num_values;
1470 talloc_free(tmp_ctx);
1472 /* we now tell the backend to replace all existing values
1473 with the one we have constructed */
1474 el->flags = LDB_FLAG_MOD_REPLACE;
1480 handle replacing a linked attribute
1482 static int replmd_modify_la_replace(struct ldb_module *module,
1483 struct dsdb_schema *schema,
1484 struct ldb_message *msg,
1485 struct ldb_message_element *el,
1486 struct ldb_message_element *old_el,
1487 const struct dsdb_attribute *schema_attr,
1490 struct GUID *msg_guid)
1493 struct parsed_dn *dns, *old_dns;
1494 TALLOC_CTX *tmp_ctx = talloc_new(msg);
1496 const struct GUID *invocation_id;
1497 struct ldb_context *ldb = ldb_module_get_ctx(module);
1498 struct ldb_val *new_values = NULL;
1499 uint32_t num_new_values = 0;
1500 unsigned old_num_values = old_el?old_el->num_values:0;
1502 /* check if there is nothing to replace */
1503 if ((!old_el || old_el->num_values == 0) &&
1504 el->num_values == 0) {
1508 ret = get_parsed_dns(module, tmp_ctx, el, &dns, schema_attr->syntax->ldap_oid);
1509 if (ret != LDB_SUCCESS) {
1510 talloc_free(tmp_ctx);
1514 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns, schema_attr->syntax->ldap_oid);
1515 if (ret != LDB_SUCCESS) {
1516 talloc_free(tmp_ctx);
1520 invocation_id = samdb_ntds_invocation_id(ldb);
1521 if (!invocation_id) {
1522 return LDB_ERR_OPERATIONS_ERROR;
1525 /* mark all the old ones as deleted */
1526 for (i=0; i<old_num_values; i++) {
1527 struct parsed_dn *old_p = &old_dns[i];
1528 struct parsed_dn *p;
1529 const struct ldb_val *v;
1531 v = ldb_dn_get_extended_component(old_p->dsdb_dn->dn, "DELETED");
1534 ret = replmd_add_backlink(module, schema, msg_guid, old_dns[i].guid, false, schema_attr);
1535 if (ret != LDB_SUCCESS) {
1536 talloc_free(tmp_ctx);
1540 p = parsed_dn_find(dns, el->num_values, old_p->guid);
1542 /* we don't delete it if we are re-adding it */
1546 ret = replmd_update_la_val(old_el->values, old_p->v, old_p, old_p,
1547 invocation_id, seq_num, t, true);
1548 if (ret != LDB_SUCCESS) {
1549 talloc_free(tmp_ctx);
1554 /* for each new value, either update its meta-data, or add it
1557 for (i=0; i<el->num_values; i++) {
1558 struct parsed_dn *p = &dns[i], *old_p;
1561 (old_p = parsed_dn_find(old_dns,
1562 old_num_values, p->guid)) != NULL) {
1563 /* update in place */
1564 ret = replmd_update_la_val(old_el->values, old_p->v, old_p,
1565 old_p, invocation_id,
1567 if (ret != LDB_SUCCESS) {
1568 talloc_free(tmp_ctx);
1573 new_values = talloc_realloc(tmp_ctx, new_values, struct ldb_val,
1575 if (new_values == NULL) {
1576 ldb_module_oom(module);
1577 talloc_free(tmp_ctx);
1578 return LDB_ERR_OPERATIONS_ERROR;
1580 ret = replmd_build_la_val(new_values, &new_values[num_new_values], &dns[i],
1581 invocation_id, seq_num, t);
1582 if (ret != LDB_SUCCESS) {
1583 talloc_free(tmp_ctx);
1589 ret = replmd_add_backlink(module, schema, msg_guid, dns[i].guid, true, schema_attr);
1590 if (ret != LDB_SUCCESS) {
1591 talloc_free(tmp_ctx);
1596 /* add the new values to the end of old_el */
1597 if (num_new_values != 0) {
1598 el->values = talloc_realloc(msg->elements, old_el?old_el->values:NULL,
1599 struct ldb_val, old_num_values+num_new_values);
1600 if (el->values == NULL) {
1601 ldb_module_oom(module);
1602 return LDB_ERR_OPERATIONS_ERROR;
1604 memcpy(&el->values[old_num_values], &new_values[0],
1605 sizeof(struct ldb_val)*num_new_values);
1606 el->num_values = old_num_values + num_new_values;
1607 talloc_steal(msg->elements, new_values);
1609 el->values = old_el->values;
1610 el->num_values = old_el->num_values;
1611 talloc_steal(msg->elements, el->values);
1614 talloc_free(tmp_ctx);
1616 /* we now tell the backend to replace all existing values
1617 with the one we have constructed */
1618 el->flags = LDB_FLAG_MOD_REPLACE;
1625 handle linked attributes in modify requests
1627 static int replmd_modify_handle_linked_attribs(struct ldb_module *module,
1628 struct ldb_message *msg,
1629 uint64_t seq_num, time_t t)
1631 struct ldb_result *res;
1633 struct ldb_context *ldb = ldb_module_get_ctx(module);
1634 struct ldb_message *old_msg;
1635 struct dsdb_schema *schema = dsdb_get_schema(ldb);
1636 struct GUID old_guid;
1639 /* there the replmd_update_rpmd code has already
1640 * checked and saw that there are no linked
1645 #if !W2K3_LINKED_ATTRIBUTES
1649 if (dsdb_functional_level(ldb) == DS_DOMAIN_FUNCTION_2000) {
1650 /* don't do anything special for linked attributes */
1654 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, NULL,
1655 DSDB_SEARCH_SHOW_DELETED |
1656 DSDB_SEARCH_REVEAL_INTERNALS |
1657 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT);
1658 if (ret != LDB_SUCCESS) {
1661 old_msg = res->msgs[0];
1663 old_guid = samdb_result_guid(old_msg, "objectGUID");
1665 for (i=0; i<msg->num_elements; i++) {
1666 struct ldb_message_element *el = &msg->elements[i];
1667 struct ldb_message_element *old_el, *new_el;
1668 const struct dsdb_attribute *schema_attr
1669 = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
1671 ldb_asprintf_errstring(ldb,
1672 "attribute %s is not a valid attribute in schema", el->name);
1673 return LDB_ERR_OBJECT_CLASS_VIOLATION;
1675 if (schema_attr->linkID == 0) {
1678 if ((schema_attr->linkID & 1) == 1) {
1679 /* Odd is for the target. Illegal to modify */
1680 ldb_asprintf_errstring(ldb,
1681 "attribute %s must not be modified directly, it is a linked attribute", el->name);
1682 return LDB_ERR_UNWILLING_TO_PERFORM;
1684 old_el = ldb_msg_find_element(old_msg, el->name);
1685 switch (el->flags & LDB_FLAG_MOD_MASK) {
1686 case LDB_FLAG_MOD_REPLACE:
1687 ret = replmd_modify_la_replace(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid);
1689 case LDB_FLAG_MOD_DELETE:
1690 ret = replmd_modify_la_delete(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid);
1692 case LDB_FLAG_MOD_ADD:
1693 ret = replmd_modify_la_add(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid);
1696 ldb_asprintf_errstring(ldb,
1697 "invalid flags 0x%x for %s linked attribute",
1698 el->flags, el->name);
1699 return LDB_ERR_UNWILLING_TO_PERFORM;
1701 if (ret != LDB_SUCCESS) {
1705 ldb_msg_remove_attr(old_msg, el->name);
1707 ldb_msg_add_empty(old_msg, el->name, 0, &new_el);
1708 new_el->num_values = el->num_values;
1709 new_el->values = el->values;
1711 /* TODO: this relises a bit too heavily on the exact
1712 behaviour of ldb_msg_find_element and
1713 ldb_msg_remove_element */
1714 old_el = ldb_msg_find_element(msg, el->name);
1716 ldb_msg_remove_element(msg, old_el);
1727 static int replmd_modify(struct ldb_module *module, struct ldb_request *req)
1729 struct ldb_context *ldb;
1730 struct replmd_replicated_request *ac;
1731 struct ldb_request *down_req;
1732 struct ldb_message *msg;
1733 time_t t = time(NULL);
1736 /* do not manipulate our control entries */
1737 if (ldb_dn_is_special(req->op.mod.message->dn)) {
1738 return ldb_next_request(module, req);
1741 ldb = ldb_module_get_ctx(module);
1743 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_modify\n");
1745 ac = replmd_ctx_init(module, req);
1747 return LDB_ERR_OPERATIONS_ERROR;
1750 /* we have to copy the message as the caller might have it as a const */
1751 msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
1755 return LDB_ERR_OPERATIONS_ERROR;
1758 ret = replmd_update_rpmd(module, ac->schema, msg, &ac->seq_num, t);
1759 if (ret != LDB_SUCCESS) {
1764 ret = replmd_modify_handle_linked_attribs(module, msg, ac->seq_num, t);
1765 if (ret != LDB_SUCCESS) {
1771 * - replace the old object with the newly constructed one
1774 ret = ldb_build_mod_req(&down_req, ldb, ac,
1777 ac, replmd_op_callback,
1779 if (ret != LDB_SUCCESS) {
1783 talloc_steal(down_req, msg);
1785 /* we only change whenChanged and uSNChanged if the seq_num
1787 if (ac->seq_num != 0) {
1788 if (add_time_element(msg, "whenChanged", t) != LDB_SUCCESS) {
1793 if (add_uint64_element(msg, "uSNChanged", ac->seq_num) != LDB_SUCCESS) {
1799 /* go on with the call chain */
1800 return ldb_next_request(module, down_req);
1803 static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares);
1806 handle a rename request
1808 On a rename we need to do an extra ldb_modify which sets the
1809 whenChanged and uSNChanged attributes. We do this in a callback after the success.
1811 static int replmd_rename(struct ldb_module *module, struct ldb_request *req)
1813 struct ldb_context *ldb;
1814 struct replmd_replicated_request *ac;
1816 struct ldb_request *down_req;
1818 /* do not manipulate our control entries */
1819 if (ldb_dn_is_special(req->op.mod.message->dn)) {
1820 return ldb_next_request(module, req);
1823 ldb = ldb_module_get_ctx(module);
1825 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_rename\n");
1827 ac = replmd_ctx_init(module, req);
1829 return LDB_ERR_OPERATIONS_ERROR;
1831 ret = ldb_build_rename_req(&down_req, ldb, ac,
1832 ac->req->op.rename.olddn,
1833 ac->req->op.rename.newdn,
1835 ac, replmd_rename_callback,
1838 if (ret != LDB_SUCCESS) {
1843 /* go on with the call chain */
1844 return ldb_next_request(module, down_req);
1847 /* After the rename is compleated, update the whenchanged etc */
1848 static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares)
1850 struct ldb_context *ldb;
1851 struct replmd_replicated_request *ac;
1852 struct ldb_request *down_req;
1853 struct ldb_message *msg;
1854 time_t t = time(NULL);
1857 ac = talloc_get_type(req->context, struct replmd_replicated_request);
1858 ldb = ldb_module_get_ctx(ac->module);
1860 if (ares->error != LDB_SUCCESS) {
1861 return ldb_module_done(ac->req, ares->controls,
1862 ares->response, ares->error);
1865 if (ares->type != LDB_REPLY_DONE) {
1866 ldb_set_errstring(ldb,
1867 "invalid ldb_reply_type in callback");
1869 return ldb_module_done(ac->req, NULL, NULL,
1870 LDB_ERR_OPERATIONS_ERROR);
1873 /* Get a sequence number from the backend */
1874 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ac->seq_num);
1875 if (ret != LDB_SUCCESS) {
1880 * - replace the old object with the newly constructed one
1883 msg = ldb_msg_new(ac);
1886 return LDB_ERR_OPERATIONS_ERROR;
1889 msg->dn = ac->req->op.rename.newdn;
1891 ret = ldb_build_mod_req(&down_req, ldb, ac,
1894 ac, replmd_op_callback,
1897 if (ret != LDB_SUCCESS) {
1901 talloc_steal(down_req, msg);
1903 if (add_time_element(msg, "whenChanged", t) != LDB_SUCCESS) {
1908 if (add_uint64_element(msg, "uSNChanged", ac->seq_num) != LDB_SUCCESS) {
1913 /* go on with the call chain - do the modify after the rename */
1914 return ldb_next_request(ac->module, down_req);
1917 /* remove forwards and backlinks as needed when an object
1919 static int replmd_delete_remove_link(struct ldb_module *module,
1920 struct dsdb_schema *schema,
1922 struct ldb_message_element *el,
1923 const struct dsdb_attribute *sa)
1926 TALLOC_CTX *tmp_ctx = talloc_new(module);
1927 struct ldb_context *ldb = ldb_module_get_ctx(module);
1929 for (i=0; i<el->num_values; i++) {
1930 struct dsdb_dn *dsdb_dn;
1931 const struct ldb_val *v;
1935 struct ldb_message *msg;
1936 const struct dsdb_attribute *target_attr;
1937 struct ldb_message_element *el2;
1938 struct ldb_val dn_val;
1940 if (dsdb_dn_is_deleted_val(&el->values[i])) {
1944 dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i], sa->syntax->ldap_oid);
1946 talloc_free(tmp_ctx);
1947 return LDB_ERR_OPERATIONS_ERROR;
1950 v = ldb_dn_get_extended_component(dsdb_dn->dn, "GUID");
1952 talloc_free(tmp_ctx);
1953 return LDB_ERR_OPERATIONS_ERROR;
1955 status = GUID_from_ndr_blob(v, &guid2);
1956 if (!NT_STATUS_IS_OK(status)) {
1957 talloc_free(tmp_ctx);
1958 return LDB_ERR_OPERATIONS_ERROR;
1961 /* remove the link */
1962 msg = ldb_msg_new(tmp_ctx);
1964 ldb_module_oom(module);
1965 talloc_free(tmp_ctx);
1966 return LDB_ERR_OPERATIONS_ERROR;
1970 msg->dn = dsdb_dn->dn;
1972 if (sa->linkID & 1) {
1973 target_attr = dsdb_attribute_by_linkID(schema, sa->linkID - 1);
1975 target_attr = dsdb_attribute_by_linkID(schema, sa->linkID + 1);
1978 ret = ldb_msg_add_empty(msg, target_attr->lDAPDisplayName, LDB_FLAG_MOD_DELETE, &el2);
1979 if (ret != LDB_SUCCESS) {
1980 ldb_module_oom(module);
1981 talloc_free(tmp_ctx);
1982 return LDB_ERR_OPERATIONS_ERROR;
1984 dn_val = data_blob_string_const(ldb_dn_get_linearized(dn));
1985 el2->values = &dn_val;
1986 el2->num_values = 1;
1988 ret = dsdb_module_modify(module, msg, 0);
1989 if (ret != LDB_SUCCESS) {
1990 talloc_free(tmp_ctx);
1994 talloc_free(tmp_ctx);
2000 handle update of replication meta data for deletion of objects
2002 This also handles the mapping of delete to a rename operation
2003 to allow deletes to be replicated.
2005 static int replmd_delete(struct ldb_module *module, struct ldb_request *req)
2007 int ret = LDB_ERR_OTHER;
2009 struct ldb_dn *old_dn, *new_dn;
2010 const char *rdn_name;
2011 const struct ldb_val *rdn_value, *new_rdn_value;
2013 struct ldb_context *ldb = ldb_module_get_ctx(module);
2014 struct dsdb_schema *schema = dsdb_get_schema(ldb);
2015 struct ldb_message *msg, *old_msg;
2016 struct ldb_message_element *el;
2017 TALLOC_CTX *tmp_ctx;
2018 struct ldb_result *res, *parent_res;
2019 const char *preserved_attrs[] = {
2020 /* yes, this really is a hard coded list. See MS-ADTS
2021 section 3.1.1.5.5.1.1 */
2022 "nTSecurityDescriptor", "attributeID", "attributeSyntax", "dNReferenceUpdate", "dNSHostName",
2023 "flatName", "governsID", "groupType", "instanceType", "lDAPDisplayName", "legacyExchangeDN",
2024 "isDeleted", "isRecycled", "lastKnownParent", "msDS-LastKnownRDN", "mS-DS-CreatorSID",
2025 "mSMQOwnerID", "nCName", "objectClass", "distinguishedName", "objectGUID", "objectSid",
2026 "oMSyntax", "proxiedObjectName", "name", "replPropertyMetaData", "sAMAccountName",
2027 "securityIdentifier", "sIDHistory", "subClassOf", "systemFlags", "trustPartner", "trustDirection",
2028 "trustType", "trustAttributes", "userAccountControl", "uSNChanged", "uSNCreated", "whenCreate",
2030 uint32_t el_count = 0;
2033 tmp_ctx = talloc_new(ldb);
2035 old_dn = ldb_dn_copy(tmp_ctx, req->op.del.dn);
2037 /* we need the complete msg off disk, so we can work out which
2038 attributes need to be removed */
2039 ret = dsdb_module_search_dn(module, tmp_ctx, &res, old_dn, NULL,
2040 DSDB_SEARCH_SHOW_DELETED |
2041 DSDB_SEARCH_REVEAL_INTERNALS |
2042 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT);
2043 if (ret != LDB_SUCCESS) {
2044 talloc_free(tmp_ctx);
2047 old_msg = res->msgs[0];
2049 /* work out where we will be renaming this object to */
2050 ret = dsdb_get_deleted_objects_dn(ldb, tmp_ctx, old_dn, &new_dn);
2051 if (ret != LDB_SUCCESS) {
2052 /* this is probably an attempted delete on a partition
2053 * that doesn't allow delete operations, such as the
2054 * schema partition */
2055 ldb_asprintf_errstring(ldb, "No Deleted Objects container for DN %s",
2056 ldb_dn_get_linearized(old_dn));
2057 talloc_free(tmp_ctx);
2058 return LDB_ERR_UNWILLING_TO_PERFORM;
2061 rdn_name = ldb_dn_get_rdn_name(old_dn);
2062 rdn_value = ldb_dn_get_rdn_val(old_dn);
2064 /* get the objects GUID from the search we just did */
2065 guid = samdb_result_guid(old_msg, "objectGUID");
2067 /* Add a formatted child */
2068 retb = ldb_dn_add_child_fmt(new_dn, "%s=%s\\0ADEL:%s",
2071 GUID_string(tmp_ctx, &guid));
2073 DEBUG(0,(__location__ ": Unable to add a formatted child to dn: %s",
2074 ldb_dn_get_linearized(new_dn)));
2075 talloc_free(tmp_ctx);
2076 return LDB_ERR_OPERATIONS_ERROR;
2079 /* New DN name generated, renaming the original DN */
2080 ret = dsdb_module_rename(module, old_dn, new_dn, 0);
2081 if (ret != LDB_SUCCESS){
2082 DEBUG(0,(__location__ ": Failed to rename object from '%s' to '%s'\n",
2083 ldb_dn_get_linearized(old_dn),
2084 ldb_dn_get_linearized(new_dn)));
2085 talloc_free(tmp_ctx);
2090 now we need to modify the object in the following ways:
2092 - add isDeleted=TRUE
2093 - update rDN and name, with new rDN
2094 - remove linked attributes
2095 - remove objectCategory and sAMAccountType
2096 - remove attribs not on the preserved list
2097 - preserved if in above list, or is rDN
2098 - remove all linked attribs from this object
2099 - remove all links from other objects to this object
2100 - add lastKnownParent
2101 - update replPropertyMetaData?
2103 see MS-ADTS "Tombstone Requirements" section 3.1.1.5.5.1.1
2106 msg = ldb_msg_new(tmp_ctx);
2108 ldb_module_oom(module);
2109 talloc_free(tmp_ctx);
2110 return LDB_ERR_OPERATIONS_ERROR;
2115 ret = ldb_msg_add_string(msg, "isDeleted", "TRUE");
2116 if (ret != LDB_SUCCESS) {
2117 DEBUG(0,(__location__ ": Failed to add isDeleted string to the msg\n"));
2118 ldb_module_oom(module);
2119 talloc_free(tmp_ctx);
2122 msg->elements[el_count++].flags = LDB_FLAG_MOD_ADD;
2124 /* we need the storage form of the parent GUID */
2125 ret = dsdb_module_search_dn(module, tmp_ctx, &parent_res,
2126 ldb_dn_get_parent(tmp_ctx, old_dn), NULL,
2127 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT);
2128 if (ret != LDB_SUCCESS) {
2129 talloc_free(tmp_ctx);
2133 ret = ldb_msg_add_steal_string(msg, "lastKnownParent",
2134 ldb_dn_get_extended_linearized(tmp_ctx, parent_res->msgs[0]->dn, 1));
2135 if (ret != LDB_SUCCESS) {
2136 DEBUG(0,(__location__ ": Failed to add lastKnownParent string to the msg\n"));
2137 ldb_module_oom(module);
2138 talloc_free(tmp_ctx);
2141 msg->elements[el_count++].flags = LDB_FLAG_MOD_ADD;
2143 /* work out which of the old attributes we will be removing */
2144 for (i=0; i<old_msg->num_elements; i++) {
2145 const struct dsdb_attribute *sa;
2146 el = &old_msg->elements[i];
2147 sa = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
2149 talloc_free(tmp_ctx);
2150 return LDB_ERR_OPERATIONS_ERROR;
2152 if (ldb_attr_cmp(el->name, rdn_name) == 0) {
2153 /* don't remove the rDN */
2158 ret = replmd_delete_remove_link(module, schema, old_dn, el, sa);
2159 if (ret != LDB_SUCCESS) {
2160 talloc_free(tmp_ctx);
2161 return LDB_ERR_OPERATIONS_ERROR;
2165 if (!sa->linkID && ldb_attr_in_list(preserved_attrs, el->name)) {
2169 ret = ldb_msg_add_empty(msg, el->name, LDB_FLAG_MOD_DELETE, &el);
2170 if (ret != LDB_SUCCESS) {
2171 talloc_free(tmp_ctx);
2172 ldb_module_oom(module);
2177 /* work out what the new rdn value is, for updating the
2178 rDN and name fields */
2179 new_rdn_value = ldb_dn_get_rdn_val(new_dn);
2180 ret = ldb_msg_add_value(msg, rdn_name, new_rdn_value, &el);
2181 if (ret != LDB_SUCCESS) {
2182 talloc_free(tmp_ctx);
2185 el->flags = LDB_FLAG_MOD_REPLACE;
2187 el = ldb_msg_find_element(old_msg, "name");
2189 ret = ldb_msg_add_value(msg, "name", new_rdn_value, &el);
2190 if (ret != LDB_SUCCESS) {
2191 talloc_free(tmp_ctx);
2194 el->flags = LDB_FLAG_MOD_REPLACE;
2197 ret = dsdb_module_modify(module, msg, 0);
2198 if (ret != LDB_SUCCESS){
2199 ldb_asprintf_errstring(ldb, "replmd_delete: Failed to modify object %s in delete - %s",
2200 ldb_dn_get_linearized(old_dn), ldb_errstring(ldb));
2201 talloc_free(tmp_ctx);
2205 talloc_free(tmp_ctx);
2207 return ldb_module_done(req, NULL, NULL, LDB_SUCCESS);
2212 static int replmd_replicated_request_error(struct replmd_replicated_request *ar, int ret)
2217 static int replmd_replicated_request_werror(struct replmd_replicated_request *ar, WERROR status)
2219 int ret = LDB_ERR_OTHER;
2220 /* TODO: do some error mapping */
2224 static int replmd_replicated_apply_add(struct replmd_replicated_request *ar)
2226 struct ldb_context *ldb;
2227 struct ldb_request *change_req;
2228 enum ndr_err_code ndr_err;
2229 struct ldb_message *msg;
2230 struct replPropertyMetaDataBlob *md;
2231 struct ldb_val md_value;
2236 * TODO: check if the parent object exist
2240 * TODO: handle the conflict case where an object with the
2244 ldb = ldb_module_get_ctx(ar->module);
2245 msg = ar->objs->objects[ar->index_current].msg;
2246 md = ar->objs->objects[ar->index_current].meta_data;
2248 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
2249 if (ret != LDB_SUCCESS) {
2250 return replmd_replicated_request_error(ar, ret);
2253 ret = ldb_msg_add_value(msg, "objectGUID", &ar->objs->objects[ar->index_current].guid_value, NULL);
2254 if (ret != LDB_SUCCESS) {
2255 return replmd_replicated_request_error(ar, ret);
2258 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
2259 if (ret != LDB_SUCCESS) {
2260 return replmd_replicated_request_error(ar, ret);
2263 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", ar->seq_num);
2264 if (ret != LDB_SUCCESS) {
2265 return replmd_replicated_request_error(ar, ret);
2268 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
2269 if (ret != LDB_SUCCESS) {
2270 return replmd_replicated_request_error(ar, ret);
2273 /* remove any message elements that have zero values */
2274 for (i=0; i<msg->num_elements; i++) {
2275 if (msg->elements[i].num_values == 0) {
2276 DEBUG(4,(__location__ ": Removing attribute %s with num_values==0\n",
2277 msg->elements[i].name));
2278 memmove(&msg->elements[i],
2279 &msg->elements[i+1],
2280 sizeof(msg->elements[i])*(msg->num_elements - (i+1)));
2281 msg->num_elements--;
2287 * the meta data array is already sorted by the caller
2289 for (i=0; i < md->ctr.ctr1.count; i++) {
2290 md->ctr.ctr1.array[i].local_usn = ar->seq_num;
2292 ndr_err = ndr_push_struct_blob(&md_value, msg,
2293 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
2295 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
2296 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
2297 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
2298 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
2300 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &md_value, NULL);
2301 if (ret != LDB_SUCCESS) {
2302 return replmd_replicated_request_error(ar, ret);
2305 replmd_ldb_message_sort(msg, ar->schema);
2308 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_ADD, msg);
2309 DEBUG(4, ("DRS replication add message:\n%s\n", s));
2313 ret = ldb_build_add_req(&change_req,
2321 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
2323 return ldb_next_request(ar->module, change_req);
2326 static int replmd_replPropertyMetaData1_conflict_compare(struct replPropertyMetaData1 *m1,
2327 struct replPropertyMetaData1 *m2)
2331 if (m1->version != m2->version) {
2332 return m1->version - m2->version;
2335 if (m1->originating_change_time != m2->originating_change_time) {
2336 return m1->originating_change_time - m2->originating_change_time;
2339 ret = GUID_compare(&m1->originating_invocation_id, &m2->originating_invocation_id);
2344 return m1->originating_usn - m2->originating_usn;
2347 static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar)
2349 struct ldb_context *ldb;
2350 struct ldb_request *change_req;
2351 enum ndr_err_code ndr_err;
2352 struct ldb_message *msg;
2353 struct replPropertyMetaDataBlob *rmd;
2354 struct replPropertyMetaDataBlob omd;
2355 const struct ldb_val *omd_value;
2356 struct replPropertyMetaDataBlob nmd;
2357 struct ldb_val nmd_value;
2359 uint32_t removed_attrs = 0;
2362 ldb = ldb_module_get_ctx(ar->module);
2363 msg = ar->objs->objects[ar->index_current].msg;
2364 rmd = ar->objs->objects[ar->index_current].meta_data;
2369 * TODO: check repl data is correct after a rename
2371 if (ldb_dn_compare(msg->dn, ar->search_msg->dn) != 0) {
2372 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_request rename %s => %s\n",
2373 ldb_dn_get_linearized(ar->search_msg->dn),
2374 ldb_dn_get_linearized(msg->dn));
2375 /* we can't use dsdb_module_rename() here as we need
2376 the rename call to be intercepted by this module, to
2377 allow it to process linked attribute changes */
2378 if (ldb_rename(ldb, ar->search_msg->dn, msg->dn) != LDB_SUCCESS) {
2379 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_replicated_request rename %s => %s failed - %s\n",
2380 ldb_dn_get_linearized(ar->search_msg->dn),
2381 ldb_dn_get_linearized(msg->dn),
2382 ldb_errstring(ldb));
2383 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
2387 /* find existing meta data */
2388 omd_value = ldb_msg_find_ldb_val(ar->search_msg, "replPropertyMetaData");
2390 ndr_err = ndr_pull_struct_blob(omd_value, ar,
2391 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")), &omd,
2392 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
2393 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
2394 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
2395 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
2398 if (omd.version != 1) {
2399 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
2405 nmd.ctr.ctr1.count = omd.ctr.ctr1.count + rmd->ctr.ctr1.count;
2406 nmd.ctr.ctr1.array = talloc_array(ar,
2407 struct replPropertyMetaData1,
2408 nmd.ctr.ctr1.count);
2409 if (!nmd.ctr.ctr1.array) return replmd_replicated_request_werror(ar, WERR_NOMEM);
2411 /* first copy the old meta data */
2412 for (i=0; i < omd.ctr.ctr1.count; i++) {
2413 nmd.ctr.ctr1.array[ni] = omd.ctr.ctr1.array[i];
2417 /* now merge in the new meta data */
2418 for (i=0; i < rmd->ctr.ctr1.count; i++) {
2421 for (j=0; j < ni; j++) {
2424 if (rmd->ctr.ctr1.array[i].attid != nmd.ctr.ctr1.array[j].attid) {
2428 cmp = replmd_replPropertyMetaData1_conflict_compare(&rmd->ctr.ctr1.array[i],
2429 &nmd.ctr.ctr1.array[j]);
2431 /* replace the entry */
2432 nmd.ctr.ctr1.array[j] = rmd->ctr.ctr1.array[i];
2437 /* we don't want to apply this change so remove the attribute */
2438 ldb_msg_remove_element(msg, &msg->elements[i-removed_attrs]);
2445 if (found) continue;
2447 nmd.ctr.ctr1.array[ni] = rmd->ctr.ctr1.array[i];
2452 * finally correct the size of the meta_data array
2454 nmd.ctr.ctr1.count = ni;
2457 * the rdn attribute (the alias for the name attribute),
2458 * 'cn' for most objects is the last entry in the meta data array
2461 * sort the new meta data array
2463 ret = replmd_replPropertyMetaDataCtr1_sort(&nmd.ctr.ctr1, ar->schema, msg->dn);
2464 if (ret != LDB_SUCCESS) {
2469 * check if some replicated attributes left, otherwise skip the ldb_modify() call
2471 if (msg->num_elements == 0) {
2472 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: skip replace\n",
2475 ar->index_current++;
2476 return replmd_replicated_apply_next(ar);
2479 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: replace %u attributes\n",
2480 ar->index_current, msg->num_elements);
2482 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
2483 if (ret != LDB_SUCCESS) {
2484 return replmd_replicated_request_error(ar, ret);
2487 for (i=0; i<ni; i++) {
2488 nmd.ctr.ctr1.array[i].local_usn = ar->seq_num;
2491 /* create the meta data value */
2492 ndr_err = ndr_push_struct_blob(&nmd_value, msg,
2493 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
2495 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
2496 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
2497 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
2498 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
2502 * when we know that we'll modify the record, add the whenChanged, uSNChanged
2503 * and replPopertyMetaData attributes
2505 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
2506 if (ret != LDB_SUCCESS) {
2507 return replmd_replicated_request_error(ar, ret);
2509 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
2510 if (ret != LDB_SUCCESS) {
2511 return replmd_replicated_request_error(ar, ret);
2513 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
2514 if (ret != LDB_SUCCESS) {
2515 return replmd_replicated_request_error(ar, ret);
2518 replmd_ldb_message_sort(msg, ar->schema);
2520 /* we want to replace the old values */
2521 for (i=0; i < msg->num_elements; i++) {
2522 msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
2526 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_MODIFY, msg);
2527 DEBUG(4, ("DRS replication modify message:\n%s\n", s));
2531 ret = ldb_build_mod_req(&change_req,
2539 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
2541 return ldb_next_request(ar->module, change_req);
2544 static int replmd_replicated_apply_search_callback(struct ldb_request *req,
2545 struct ldb_reply *ares)
2547 struct replmd_replicated_request *ar = talloc_get_type(req->context,
2548 struct replmd_replicated_request);
2552 return ldb_module_done(ar->req, NULL, NULL,
2553 LDB_ERR_OPERATIONS_ERROR);
2555 if (ares->error != LDB_SUCCESS &&
2556 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
2557 return ldb_module_done(ar->req, ares->controls,
2558 ares->response, ares->error);
2561 switch (ares->type) {
2562 case LDB_REPLY_ENTRY:
2563 ar->search_msg = talloc_steal(ar, ares->message);
2566 case LDB_REPLY_REFERRAL:
2567 /* we ignore referrals */
2570 case LDB_REPLY_DONE:
2571 if (ar->search_msg != NULL) {
2572 ret = replmd_replicated_apply_merge(ar);
2574 ret = replmd_replicated_apply_add(ar);
2576 if (ret != LDB_SUCCESS) {
2577 return ldb_module_done(ar->req, NULL, NULL, ret);
2585 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar);
2587 static int replmd_replicated_apply_next(struct replmd_replicated_request *ar)
2589 struct ldb_context *ldb;
2593 struct ldb_request *search_req;
2594 struct ldb_search_options_control *options;
2596 if (ar->index_current >= ar->objs->num_objects) {
2597 /* done with it, go to next stage */
2598 return replmd_replicated_uptodate_vector(ar);
2601 ldb = ldb_module_get_ctx(ar->module);
2602 ar->search_msg = NULL;
2604 tmp_str = ldb_binary_encode(ar, ar->objs->objects[ar->index_current].guid_value);
2605 if (!tmp_str) return replmd_replicated_request_werror(ar, WERR_NOMEM);
2607 filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
2608 if (!filter) return replmd_replicated_request_werror(ar, WERR_NOMEM);
2609 talloc_free(tmp_str);
2611 ret = ldb_build_search_req(&search_req,
2620 replmd_replicated_apply_search_callback,
2623 ret = ldb_request_add_control(search_req, LDB_CONTROL_SHOW_DELETED_OID, true, NULL);
2624 if (ret != LDB_SUCCESS) {
2628 /* we need to cope with cross-partition links, so search for
2629 the GUID over all partitions */
2630 options = talloc(search_req, struct ldb_search_options_control);
2631 if (options == NULL) {
2632 DEBUG(0, (__location__ ": out of memory\n"));
2633 return LDB_ERR_OPERATIONS_ERROR;
2635 options->search_options = LDB_SEARCH_OPTION_PHANTOM_ROOT;
2637 ret = ldb_request_add_control(search_req,
2638 LDB_CONTROL_SEARCH_OPTIONS_OID,
2640 if (ret != LDB_SUCCESS) {
2644 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
2646 return ldb_next_request(ar->module, search_req);
2649 static int replmd_replicated_uptodate_modify_callback(struct ldb_request *req,
2650 struct ldb_reply *ares)
2652 struct ldb_context *ldb;
2653 struct replmd_replicated_request *ar = talloc_get_type(req->context,
2654 struct replmd_replicated_request);
2655 ldb = ldb_module_get_ctx(ar->module);
2658 return ldb_module_done(ar->req, NULL, NULL,
2659 LDB_ERR_OPERATIONS_ERROR);
2661 if (ares->error != LDB_SUCCESS) {
2662 return ldb_module_done(ar->req, ares->controls,
2663 ares->response, ares->error);
2666 if (ares->type != LDB_REPLY_DONE) {
2667 ldb_set_errstring(ldb, "Invalid reply type\n!");
2668 return ldb_module_done(ar->req, NULL, NULL,
2669 LDB_ERR_OPERATIONS_ERROR);
2674 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
2677 static int replmd_replicated_uptodate_modify(struct replmd_replicated_request *ar)
2679 struct ldb_context *ldb;
2680 struct ldb_request *change_req;
2681 enum ndr_err_code ndr_err;
2682 struct ldb_message *msg;
2683 struct replUpToDateVectorBlob ouv;
2684 const struct ldb_val *ouv_value;
2685 const struct drsuapi_DsReplicaCursor2CtrEx *ruv;
2686 struct replUpToDateVectorBlob nuv;
2687 struct ldb_val nuv_value;
2688 struct ldb_message_element *nuv_el = NULL;
2689 const struct GUID *our_invocation_id;
2690 struct ldb_message_element *orf_el = NULL;
2691 struct repsFromToBlob nrf;
2692 struct ldb_val *nrf_value = NULL;
2693 struct ldb_message_element *nrf_el = NULL;
2696 time_t t = time(NULL);
2700 ldb = ldb_module_get_ctx(ar->module);
2701 ruv = ar->objs->uptodateness_vector;
2707 unix_to_nt_time(&now, t);
2710 * first create the new replUpToDateVector
2712 ouv_value = ldb_msg_find_ldb_val(ar->search_msg, "replUpToDateVector");
2714 ndr_err = ndr_pull_struct_blob(ouv_value, ar,
2715 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")), &ouv,
2716 (ndr_pull_flags_fn_t)ndr_pull_replUpToDateVectorBlob);
2717 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
2718 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
2719 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
2722 if (ouv.version != 2) {
2723 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
2728 * the new uptodateness vector will at least
2729 * contain 1 entry, one for the source_dsa
2731 * plus optional values from our old vector and the one from the source_dsa
2733 nuv.ctr.ctr2.count = 1 + ouv.ctr.ctr2.count;
2734 if (ruv) nuv.ctr.ctr2.count += ruv->count;
2735 nuv.ctr.ctr2.cursors = talloc_array(ar,
2736 struct drsuapi_DsReplicaCursor2,
2737 nuv.ctr.ctr2.count);
2738 if (!nuv.ctr.ctr2.cursors) return replmd_replicated_request_werror(ar, WERR_NOMEM);
2740 /* first copy the old vector */
2741 for (i=0; i < ouv.ctr.ctr2.count; i++) {
2742 nuv.ctr.ctr2.cursors[ni] = ouv.ctr.ctr2.cursors[i];
2746 /* get our invocation_id if we have one already attached to the ldb */
2747 our_invocation_id = samdb_ntds_invocation_id(ldb);
2749 /* merge in the source_dsa vector is available */
2750 for (i=0; (ruv && i < ruv->count); i++) {
2753 if (our_invocation_id &&
2754 GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
2755 our_invocation_id)) {
2759 for (j=0; j < ni; j++) {
2760 if (!GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
2761 &nuv.ctr.ctr2.cursors[j].source_dsa_invocation_id)) {
2768 * we update only the highest_usn and not the latest_sync_success time,
2769 * because the last success stands for direct replication
2771 if (ruv->cursors[i].highest_usn > nuv.ctr.ctr2.cursors[j].highest_usn) {
2772 nuv.ctr.ctr2.cursors[j].highest_usn = ruv->cursors[i].highest_usn;
2777 if (found) continue;
2779 /* if it's not there yet, add it */
2780 nuv.ctr.ctr2.cursors[ni] = ruv->cursors[i];
2785 * merge in the current highwatermark for the source_dsa
2788 for (j=0; j < ni; j++) {
2789 if (!GUID_equal(&ar->objs->source_dsa->source_dsa_invocation_id,
2790 &nuv.ctr.ctr2.cursors[j].source_dsa_invocation_id)) {
2797 * here we update the highest_usn and last_sync_success time
2798 * because we're directly replicating from the source_dsa
2800 * and use the tmp_highest_usn because this is what we have just applied
2803 nuv.ctr.ctr2.cursors[j].highest_usn = ar->objs->source_dsa->highwatermark.tmp_highest_usn;
2804 nuv.ctr.ctr2.cursors[j].last_sync_success = now;
2809 * here we update the highest_usn and last_sync_success time
2810 * because we're directly replicating from the source_dsa
2812 * and use the tmp_highest_usn because this is what we have just applied
2815 nuv.ctr.ctr2.cursors[ni].source_dsa_invocation_id= ar->objs->source_dsa->source_dsa_invocation_id;
2816 nuv.ctr.ctr2.cursors[ni].highest_usn = ar->objs->source_dsa->highwatermark.tmp_highest_usn;
2817 nuv.ctr.ctr2.cursors[ni].last_sync_success = now;
2822 * finally correct the size of the cursors array
2824 nuv.ctr.ctr2.count = ni;
2829 qsort(nuv.ctr.ctr2.cursors, nuv.ctr.ctr2.count,
2830 sizeof(struct drsuapi_DsReplicaCursor2),
2831 (comparison_fn_t)drsuapi_DsReplicaCursor2_compare);
2834 * create the change ldb_message
2836 msg = ldb_msg_new(ar);
2837 if (!msg) return replmd_replicated_request_werror(ar, WERR_NOMEM);
2838 msg->dn = ar->search_msg->dn;
2840 ndr_err = ndr_push_struct_blob(&nuv_value, msg,
2841 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
2843 (ndr_push_flags_fn_t)ndr_push_replUpToDateVectorBlob);
2844 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
2845 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
2846 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
2848 ret = ldb_msg_add_value(msg, "replUpToDateVector", &nuv_value, &nuv_el);
2849 if (ret != LDB_SUCCESS) {
2850 return replmd_replicated_request_error(ar, ret);
2852 nuv_el->flags = LDB_FLAG_MOD_REPLACE;
2855 * now create the new repsFrom value from the given repsFromTo1 structure
2859 nrf.ctr.ctr1 = *ar->objs->source_dsa;
2860 /* and fix some values... */
2861 nrf.ctr.ctr1.consecutive_sync_failures = 0;
2862 nrf.ctr.ctr1.last_success = now;
2863 nrf.ctr.ctr1.last_attempt = now;
2864 nrf.ctr.ctr1.result_last_attempt = WERR_OK;
2865 nrf.ctr.ctr1.highwatermark.highest_usn = nrf.ctr.ctr1.highwatermark.tmp_highest_usn;
2868 * first see if we already have a repsFrom value for the current source dsa
2869 * if so we'll later replace this value
2871 orf_el = ldb_msg_find_element(ar->search_msg, "repsFrom");
2873 for (i=0; i < orf_el->num_values; i++) {
2874 struct repsFromToBlob *trf;
2876 trf = talloc(ar, struct repsFromToBlob);
2877 if (!trf) return replmd_replicated_request_werror(ar, WERR_NOMEM);
2879 ndr_err = ndr_pull_struct_blob(&orf_el->values[i], trf, lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")), trf,
2880 (ndr_pull_flags_fn_t)ndr_pull_repsFromToBlob);
2881 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
2882 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
2883 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
2886 if (trf->version != 1) {
2887 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
2891 * we compare the source dsa objectGUID not the invocation_id
2892 * because we want only one repsFrom value per source dsa
2893 * and when the invocation_id of the source dsa has changed we don't need
2894 * the old repsFrom with the old invocation_id
2896 if (!GUID_equal(&trf->ctr.ctr1.source_dsa_obj_guid,
2897 &ar->objs->source_dsa->source_dsa_obj_guid)) {
2903 nrf_value = &orf_el->values[i];
2908 * copy over all old values to the new ldb_message
2910 ret = ldb_msg_add_empty(msg, "repsFrom", 0, &nrf_el);
2911 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
2916 * if we haven't found an old repsFrom value for the current source dsa
2917 * we'll add a new value
2920 struct ldb_val zero_value;
2921 ZERO_STRUCT(zero_value);
2922 ret = ldb_msg_add_value(msg, "repsFrom", &zero_value, &nrf_el);
2923 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
2925 nrf_value = &nrf_el->values[nrf_el->num_values - 1];
2928 /* we now fill the value which is already attached to ldb_message */
2929 ndr_err = ndr_push_struct_blob(nrf_value, msg,
2930 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
2932 (ndr_push_flags_fn_t)ndr_push_repsFromToBlob);
2933 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
2934 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
2935 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
2939 * the ldb_message_element for the attribute, has all the old values and the new one
2940 * so we'll replace the whole attribute with all values
2942 nrf_el->flags = LDB_FLAG_MOD_REPLACE;
2945 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_MODIFY, msg);
2946 DEBUG(4, ("DRS replication uptodate modify message:\n%s\n", s));
2950 /* prepare the ldb_modify() request */
2951 ret = ldb_build_mod_req(&change_req,
2957 replmd_replicated_uptodate_modify_callback,
2959 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
2961 return ldb_next_request(ar->module, change_req);
2964 static int replmd_replicated_uptodate_search_callback(struct ldb_request *req,
2965 struct ldb_reply *ares)
2967 struct replmd_replicated_request *ar = talloc_get_type(req->context,
2968 struct replmd_replicated_request);
2972 return ldb_module_done(ar->req, NULL, NULL,
2973 LDB_ERR_OPERATIONS_ERROR);
2975 if (ares->error != LDB_SUCCESS &&
2976 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
2977 return ldb_module_done(ar->req, ares->controls,
2978 ares->response, ares->error);
2981 switch (ares->type) {
2982 case LDB_REPLY_ENTRY:
2983 ar->search_msg = talloc_steal(ar, ares->message);
2986 case LDB_REPLY_REFERRAL:
2987 /* we ignore referrals */
2990 case LDB_REPLY_DONE:
2991 if (ar->search_msg == NULL) {
2992 ret = replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
2994 ret = replmd_replicated_uptodate_modify(ar);
2996 if (ret != LDB_SUCCESS) {
2997 return ldb_module_done(ar->req, NULL, NULL, ret);
3006 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar)
3008 struct ldb_context *ldb;
3010 static const char *attrs[] = {
3011 "replUpToDateVector",
3015 struct ldb_request *search_req;
3017 ldb = ldb_module_get_ctx(ar->module);
3018 ar->search_msg = NULL;
3020 ret = ldb_build_search_req(&search_req,
3023 ar->objs->partition_dn,
3029 replmd_replicated_uptodate_search_callback,
3031 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
3033 return ldb_next_request(ar->module, search_req);
3038 static int replmd_extended_replicated_objects(struct ldb_module *module, struct ldb_request *req)
3040 struct ldb_context *ldb;
3041 struct dsdb_extended_replicated_objects *objs;
3042 struct replmd_replicated_request *ar;
3043 struct ldb_control **ctrls;
3045 struct replmd_private *replmd_private =
3046 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
3048 ldb = ldb_module_get_ctx(module);
3050 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_extended_replicated_objects\n");
3052 objs = talloc_get_type(req->op.extended.data, struct dsdb_extended_replicated_objects);
3054 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: invalid extended data\n");
3055 return LDB_ERR_PROTOCOL_ERROR;
3058 if (objs->version != DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION) {
3059 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: extended data invalid version [%u != %u]\n",
3060 objs->version, DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION);
3061 return LDB_ERR_PROTOCOL_ERROR;
3064 ar = replmd_ctx_init(module, req);
3066 return LDB_ERR_OPERATIONS_ERROR;
3068 /* Set the flags to have the replmd_op_callback run over the full set of objects */
3069 ar->apply_mode = true;
3071 ar->schema = dsdb_get_schema(ldb);
3073 ldb_debug_set(ldb, LDB_DEBUG_FATAL, "replmd_ctx_init: no loaded schema found\n");
3075 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
3076 return LDB_ERR_CONSTRAINT_VIOLATION;
3079 ctrls = req->controls;
3081 if (req->controls) {
3082 req->controls = talloc_memdup(ar, req->controls,
3083 talloc_get_size(req->controls));
3084 if (!req->controls) return replmd_replicated_request_werror(ar, WERR_NOMEM);
3087 ret = ldb_request_add_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID, false, NULL);
3088 if (ret != LDB_SUCCESS) {
3092 ar->controls = req->controls;
3093 req->controls = ctrls;
3095 DEBUG(4,("linked_attributes_count=%u\n", objs->linked_attributes_count));
3097 /* save away the linked attributes for the end of the
3099 for (i=0; i<ar->objs->linked_attributes_count; i++) {
3100 struct la_entry *la_entry;
3102 if (replmd_private->la_ctx == NULL) {
3103 replmd_private->la_ctx = talloc_new(replmd_private);
3105 la_entry = talloc(replmd_private->la_ctx, struct la_entry);
3106 if (la_entry == NULL) {
3108 return LDB_ERR_OPERATIONS_ERROR;
3110 la_entry->la = talloc(la_entry, struct drsuapi_DsReplicaLinkedAttribute);
3111 if (la_entry->la == NULL) {
3112 talloc_free(la_entry);
3114 return LDB_ERR_OPERATIONS_ERROR;
3116 *la_entry->la = ar->objs->linked_attributes[i];
3118 /* we need to steal the non-scalars so they stay
3119 around until the end of the transaction */
3120 talloc_steal(la_entry->la, la_entry->la->identifier);
3121 talloc_steal(la_entry->la, la_entry->la->value.blob);
3123 DLIST_ADD(replmd_private->la_list, la_entry);
3126 return replmd_replicated_apply_next(ar);
3130 process one linked attribute structure
3132 static int replmd_process_linked_attribute(struct ldb_module *module,
3133 struct la_entry *la_entry)
3135 struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
3136 struct ldb_context *ldb = ldb_module_get_ctx(module);
3137 struct dsdb_schema *schema = dsdb_get_schema(ldb);
3138 struct drsuapi_DsReplicaObjectIdentifier3 target;
3139 struct ldb_message *msg;
3140 TALLOC_CTX *tmp_ctx = talloc_new(la_entry);
3141 struct ldb_request *mod_req;
3143 const struct dsdb_attribute *attr;
3144 struct ldb_dn *target_dn;
3145 struct dsdb_dn *dsdb_dn;
3146 uint64_t seq_num = 0;
3147 struct drsuapi_DsReplicaAttribute drs;
3148 struct drsuapi_DsAttributeValue val;
3149 struct ldb_message_element el;
3150 const struct ldb_val *guid;
3152 time_t t = time(NULL);
3154 drs.value_ctr.num_values = 1;
3155 drs.value_ctr.values = &val;
3156 val.blob = la->value.blob;
3159 linked_attributes[0]:
3160 &objs->linked_attributes[i]: struct drsuapi_DsReplicaLinkedAttribute
3162 identifier: struct drsuapi_DsReplicaObjectIdentifier
3163 __ndr_size : 0x0000003a (58)
3164 __ndr_size_sid : 0x00000000 (0)
3165 guid : 8e95b6a9-13dd-4158-89db-3220a5be5cc7
3167 __ndr_size_dn : 0x00000000 (0)
3169 attid : DRSUAPI_ATTRIBUTE_member (0x1F)
3170 value: struct drsuapi_DsAttributeValue
3171 __ndr_size : 0x0000007e (126)
3173 blob : DATA_BLOB length=126
3174 flags : 0x00000001 (1)
3175 1: DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE
3176 originating_add_time : Wed Sep 2 22:20:01 2009 EST
3177 meta_data: struct drsuapi_DsReplicaMetaData
3178 version : 0x00000015 (21)
3179 originating_change_time : Wed Sep 2 23:39:07 2009 EST
3180 originating_invocation_id: 794640f3-18cf-40ee-a211-a93992b67a64
3181 originating_usn : 0x000000000001e19c (123292)
3183 (for cases where the link is to a normal DN)
3184 &target: struct drsuapi_DsReplicaObjectIdentifier3
3185 __ndr_size : 0x0000007e (126)
3186 __ndr_size_sid : 0x0000001c (28)
3187 guid : 7639e594-db75-4086-b0d4-67890ae46031
3188 sid : S-1-5-21-2848215498-2472035911-1947525656-19924
3189 __ndr_size_dn : 0x00000022 (34)
3190 dn : 'CN=UOne,OU=TestOU,DC=vsofs8,DC=com'
3193 /* find the attribute being modified */
3194 attr = dsdb_attribute_by_attributeID_id(schema, la->attid);
3196 DEBUG(0, (__location__ ": Unable to find attributeID 0x%x\n", la->attid));
3197 talloc_free(tmp_ctx);
3198 return LDB_ERR_OPERATIONS_ERROR;
3201 status = attr->syntax->drsuapi_to_ldb(ldb, schema, attr, &drs, tmp_ctx, &el);
3203 /* construct a modify request for this attribute change */
3204 msg = ldb_msg_new(tmp_ctx);
3207 talloc_free(tmp_ctx);
3208 return LDB_ERR_OPERATIONS_ERROR;
3211 ret = dsdb_find_dn_by_guid(ldb, tmp_ctx,
3212 GUID_string(tmp_ctx, &la->identifier->guid), &msg->dn);
3213 if (ret != LDB_SUCCESS) {
3214 talloc_free(tmp_ctx);
3218 el.name = attr->lDAPDisplayName;
3219 if (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE) {
3220 ret = ldb_msg_add(msg, &el, LDB_FLAG_MOD_ADD);
3222 ret = ldb_msg_add(msg, &el, LDB_FLAG_MOD_DELETE);
3224 if (ret != LDB_SUCCESS) {
3225 talloc_free(tmp_ctx);
3229 dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, &el.values[0], attr->syntax->ldap_oid);
3231 DEBUG(0,(__location__ ": Failed to parse just-generated DN\n"));
3232 talloc_free(tmp_ctx);
3233 return LDB_ERR_INVALID_DN_SYNTAX;
3236 guid = ldb_dn_get_extended_component(dsdb_dn->dn, "GUID");
3238 DEBUG(0,(__location__ ": Failed to parse GUID from just-generated DN\n"));
3239 talloc_free(tmp_ctx);
3240 return LDB_ERR_INVALID_DN_SYNTAX;
3243 ret = dsdb_find_dn_by_guid(ldb, tmp_ctx, ldb_binary_encode(tmp_ctx, *guid), &target_dn);
3244 if (ret != LDB_SUCCESS) {
3245 /* If this proves to be a problem in the future, then
3246 * just remove the return - perhaps we can just use
3247 * the details the replication peer supplied */
3249 DEBUG(0,(__location__ ": Failed to map GUID %s to DN\n", GUID_string(tmp_ctx, &target.guid)));
3250 talloc_free(tmp_ctx);
3251 return LDB_ERR_OPERATIONS_ERROR;
3254 /* Now update with full DN we just found in the DB (including extended components) */
3255 dsdb_dn->dn = target_dn;
3256 /* Now make a linearized version, using the original binary components (if any) */
3257 el.values[0] = data_blob_string_const(dsdb_dn_get_extended_linearized(tmp_ctx, dsdb_dn, 1));
3260 ret = replmd_update_rpmd(module, schema, msg, &seq_num, t);
3261 if (ret != LDB_SUCCESS) {
3262 talloc_free(tmp_ctx);
3266 /* we only change whenChanged and uSNChanged if the seq_num
3269 if (add_time_element(msg, "whenChanged", t) != LDB_SUCCESS) {
3270 talloc_free(tmp_ctx);
3271 return LDB_ERR_OPERATIONS_ERROR;
3274 if (add_uint64_element(msg, "uSNChanged", seq_num) != LDB_SUCCESS) {
3275 talloc_free(tmp_ctx);
3276 return LDB_ERR_OPERATIONS_ERROR;
3280 ret = ldb_build_mod_req(&mod_req, ldb, tmp_ctx,
3284 ldb_op_default_callback,
3286 if (ret != LDB_SUCCESS) {
3287 talloc_free(tmp_ctx);
3290 talloc_steal(mod_req, msg);
3293 DEBUG(4,("Applying DRS linked attribute change:\n%s\n",
3294 ldb_ldif_message_string(ldb, tmp_ctx, LDB_CHANGETYPE_MODIFY, msg)));
3297 /* Run the new request */
3298 ret = ldb_next_request(module, mod_req);
3300 /* we need to wait for this to finish, as we are being called
3301 from the synchronous end_transaction hook of this module */
3302 if (ret == LDB_SUCCESS) {
3303 ret = ldb_wait(mod_req->handle, LDB_WAIT_ALL);
3306 if (ret == LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS) {
3307 /* the link destination exists, we need to update it
3308 * by deleting the old one for the same DN then adding
3310 msg->elements = talloc_realloc(msg, msg->elements,
3311 struct ldb_message_element,
3312 msg->num_elements+1);
3313 if (msg->elements == NULL) {
3315 talloc_free(tmp_ctx);
3316 return LDB_ERR_OPERATIONS_ERROR;
3318 /* this relies on the backend matching the old entry
3319 only by the DN portion of the extended DN */
3320 msg->elements[1] = msg->elements[0];
3321 msg->elements[0].flags = LDB_FLAG_MOD_DELETE;
3322 msg->num_elements++;
3324 ret = ldb_build_mod_req(&mod_req, ldb, tmp_ctx,
3328 ldb_op_default_callback,
3330 if (ret != LDB_SUCCESS) {
3331 talloc_free(tmp_ctx);
3335 /* Run the new request */
3336 ret = ldb_next_request(module, mod_req);
3338 if (ret == LDB_SUCCESS) {
3339 ret = ldb_wait(mod_req->handle, LDB_WAIT_ALL);
3343 if (ret != LDB_SUCCESS) {
3344 ldb_debug(ldb, LDB_DEBUG_WARNING, "Failed to apply linked attribute change '%s' %s\n",
3346 ldb_ldif_message_string(ldb, tmp_ctx, LDB_CHANGETYPE_MODIFY, msg));
3350 talloc_free(tmp_ctx);
3355 static int replmd_extended(struct ldb_module *module, struct ldb_request *req)
3357 if (strcmp(req->op.extended.oid, DSDB_EXTENDED_REPLICATED_OBJECTS_OID) == 0) {
3358 return replmd_extended_replicated_objects(module, req);
3361 return ldb_next_request(module, req);
3366 we hook into the transaction operations to allow us to
3367 perform the linked attribute updates at the end of the whole
3368 transaction. This allows a forward linked attribute to be created
3369 before the object is created. During a vampire, w2k8 sends us linked
3370 attributes before the objects they are part of.
3372 static int replmd_start_transaction(struct ldb_module *module)
3374 /* create our private structure for this transaction */
3375 struct replmd_private *replmd_private = talloc_get_type(ldb_module_get_private(module),
3376 struct replmd_private);
3377 replmd_txn_cleanup(replmd_private);
3379 /* free any leftover mod_usn records from cancelled
3381 while (replmd_private->ncs) {
3382 struct nc_entry *e = replmd_private->ncs;
3383 DLIST_REMOVE(replmd_private->ncs, e);
3387 return ldb_next_start_trans(module);
3391 on prepare commit we loop over our queued la_context structures and
3394 static int replmd_prepare_commit(struct ldb_module *module)
3396 struct replmd_private *replmd_private =
3397 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
3398 struct la_entry *la, *prev;
3399 struct la_backlink *bl;
3402 /* walk the list backwards, to do the first entry first, as we
3403 * added the entries with DLIST_ADD() which puts them at the
3404 * start of the list */
3405 for (la = replmd_private->la_list; la && la->next; la=la->next) ;
3407 for (; la; la=prev) {
3409 DLIST_REMOVE(replmd_private->la_list, la);
3410 ret = replmd_process_linked_attribute(module, la);
3411 if (ret != LDB_SUCCESS) {
3412 replmd_txn_cleanup(replmd_private);
3417 /* process our backlink list, creating and deleting backlinks
3419 for (bl=replmd_private->la_backlinks; bl; bl=bl->next) {
3420 ret = replmd_process_backlink(module, bl);
3421 if (ret != LDB_SUCCESS) {
3422 replmd_txn_cleanup(replmd_private);
3427 replmd_txn_cleanup(replmd_private);
3429 /* possibly change @REPLCHANGED */
3430 ret = replmd_notify_store(module);
3431 if (ret != LDB_SUCCESS) {
3435 return ldb_next_prepare_commit(module);
3438 static int replmd_del_transaction(struct ldb_module *module)
3440 struct replmd_private *replmd_private =
3441 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
3442 replmd_txn_cleanup(replmd_private);
3444 return ldb_next_del_trans(module);
3448 _PUBLIC_ const struct ldb_module_ops ldb_repl_meta_data_module_ops = {
3449 .name = "repl_meta_data",
3450 .init_context = replmd_init,
3452 .modify = replmd_modify,
3453 .rename = replmd_rename,
3454 .del = replmd_delete,
3455 .extended = replmd_extended,
3456 .start_transaction = replmd_start_transaction,
3457 .prepare_commit = replmd_prepare_commit,
3458 .del_transaction = replmd_del_transaction,