s4-drs: fixed sorting of replPropertyMetaData
[Samba.git] / source4 / dsdb / samdb / ldb_modules / repl_meta_data.c
blobf729d5c88fd0d335b6833e91394787b2bcebb9ce
1 /*
2 ldb database library
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/>.
24 * Name: ldb
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
33 * Author: Simo Sorce
34 * Author: Stefan Metzmacher
37 #include "includes.h"
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"
49 struct replmd_private {
50 TALLOC_CTX *la_ctx;
51 struct la_entry *la_list;
52 uint32_t num_ncs;
53 struct nc_entry {
54 struct ldb_dn *dn;
55 struct GUID guid;
56 uint64_t mod_usn;
57 } *ncs;
60 struct la_entry {
61 struct la_entry *next, *prev;
62 struct drsuapi_DsReplicaLinkedAttribute *la;
65 struct replmd_replicated_request {
66 struct ldb_module *module;
67 struct ldb_request *req;
69 const struct dsdb_schema *schema;
71 struct dsdb_extended_replicated_objects *objs;
73 /* the controls we pass down */
74 struct ldb_control **controls;
76 uint32_t index_current;
78 struct ldb_message *search_msg;
83 initialise the module
84 allocate the private structure and build the list
85 of partition DNs for use by replmd_notify()
87 static int replmd_init(struct ldb_module *module)
89 struct replmd_private *replmd_private;
90 struct ldb_context *ldb = ldb_module_get_ctx(module);
92 replmd_private = talloc_zero(module, struct replmd_private);
93 if (replmd_private == NULL) {
94 ldb_oom(ldb);
95 return LDB_ERR_OPERATIONS_ERROR;
97 ldb_module_set_private(module, replmd_private);
99 return ldb_next_init(module);
103 static int nc_compare(struct nc_entry *n1, struct nc_entry *n2)
105 return ldb_dn_compare(n1->dn, n2->dn);
109 build the list of partition DNs for use by replmd_notify()
111 static int replmd_load_NCs(struct ldb_module *module)
113 const char *attrs[] = { "namingContexts", NULL };
114 struct ldb_result *res = NULL;
115 int i, ret;
116 TALLOC_CTX *tmp_ctx;
117 struct ldb_context *ldb;
118 struct ldb_message_element *el;
119 struct replmd_private *replmd_private =
120 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
122 if (replmd_private->ncs != NULL) {
123 return LDB_SUCCESS;
126 ldb = ldb_module_get_ctx(module);
127 tmp_ctx = talloc_new(module);
129 /* load the list of naming contexts */
130 ret = ldb_search(ldb, tmp_ctx, &res, ldb_dn_new(tmp_ctx, ldb, ""),
131 LDB_SCOPE_BASE, attrs, NULL);
132 if (ret != LDB_SUCCESS ||
133 res->count != 1) {
134 DEBUG(0,(__location__ ": Failed to load rootDSE\n"));
135 return LDB_ERR_OPERATIONS_ERROR;
138 el = ldb_msg_find_element(res->msgs[0], "namingContexts");
139 if (el == NULL) {
140 DEBUG(0,(__location__ ": Failed to load namingContexts\n"));
141 return LDB_ERR_OPERATIONS_ERROR;
144 replmd_private->num_ncs = el->num_values;
145 replmd_private->ncs = talloc_array(replmd_private, struct nc_entry,
146 replmd_private->num_ncs);
147 if (replmd_private->ncs == NULL) {
148 ldb_oom(ldb);
149 return LDB_ERR_OPERATIONS_ERROR;
152 for (i=0; i<replmd_private->num_ncs; i++) {
153 replmd_private->ncs[i].dn =
154 ldb_dn_from_ldb_val(replmd_private->ncs,
155 ldb, &el->values[i]);
156 replmd_private->ncs[i].mod_usn = 0;
159 talloc_free(res);
161 /* now find the GUIDs of each of those DNs */
162 for (i=0; i<replmd_private->num_ncs; i++) {
163 const char *attrs2[] = { "objectGUID", NULL };
164 ret = ldb_search(ldb, tmp_ctx, &res, replmd_private->ncs[i].dn,
165 LDB_SCOPE_BASE, attrs2, NULL);
166 if (ret != LDB_SUCCESS ||
167 res->count != 1) {
168 /* this happens when the schema is first being
169 setup */
170 talloc_free(replmd_private->ncs);
171 replmd_private->ncs = NULL;
172 replmd_private->num_ncs = 0;
173 talloc_free(tmp_ctx);
174 return LDB_SUCCESS;
176 replmd_private->ncs[i].guid =
177 samdb_result_guid(res->msgs[0], "objectGUID");
178 talloc_free(res);
181 /* sort the NCs into order, most to least specific */
182 qsort(replmd_private->ncs, replmd_private->num_ncs,
183 sizeof(replmd_private->ncs[0]), QSORT_CAST nc_compare);
186 talloc_free(tmp_ctx);
188 return LDB_SUCCESS;
193 * notify the repl task that a object has changed. The notifies are
194 * gathered up in the replmd_private structure then written to the
195 * @REPLCHANGED object in each partition during the prepare_commit
197 static int replmd_notify(struct ldb_module *module, struct ldb_dn *dn, uint64_t uSN)
199 int ret, i;
200 struct replmd_private *replmd_private =
201 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
203 ret = replmd_load_NCs(module);
204 if (ret != LDB_SUCCESS) {
205 return ret;
207 if (replmd_private->num_ncs == 0) {
208 return LDB_SUCCESS;
211 for (i=0; i<replmd_private->num_ncs; i++) {
212 if (ldb_dn_compare_base(replmd_private->ncs[i].dn, dn) == 0) {
213 break;
216 if (i == replmd_private->num_ncs) {
217 DEBUG(0,(__location__ ": DN not within known NCs '%s'\n",
218 ldb_dn_get_linearized(dn)));
219 return LDB_ERR_OPERATIONS_ERROR;
222 if (uSN > replmd_private->ncs[i].mod_usn) {
223 replmd_private->ncs[i].mod_usn = uSN;
226 return LDB_SUCCESS;
231 * update a @REPLCHANGED record in each partition if there have been
232 * any writes of replicated data in the partition
234 static int replmd_notify_store(struct ldb_module *module)
236 int i;
237 struct replmd_private *replmd_private =
238 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
239 struct ldb_context *ldb = ldb_module_get_ctx(module);
241 for (i=0; i<replmd_private->num_ncs; i++) {
242 int ret;
244 if (replmd_private->ncs[i].mod_usn == 0) {
245 /* this partition has not changed in this
246 transaction */
247 continue;
250 ret = dsdb_save_partition_usn(ldb, replmd_private->ncs[i].dn,
251 replmd_private->ncs[i].mod_usn);
252 if (ret != LDB_SUCCESS) {
253 DEBUG(0,(__location__ ": Failed to save partition uSN for %s\n",
254 ldb_dn_get_linearized(replmd_private->ncs[i].dn)));
255 return ret;
259 return LDB_SUCCESS;
264 created a replmd_replicated_request context
266 static struct replmd_replicated_request *replmd_ctx_init(struct ldb_module *module,
267 struct ldb_request *req)
269 struct ldb_context *ldb;
270 struct replmd_replicated_request *ac;
272 ldb = ldb_module_get_ctx(module);
274 ac = talloc_zero(req, struct replmd_replicated_request);
275 if (ac == NULL) {
276 ldb_oom(ldb);
277 return NULL;
280 ac->module = module;
281 ac->req = req;
282 return ac;
286 add a time element to a record
288 static int add_time_element(struct ldb_message *msg, const char *attr, time_t t)
290 struct ldb_message_element *el;
291 char *s;
293 if (ldb_msg_find_element(msg, attr) != NULL) {
294 return LDB_SUCCESS;
297 s = ldb_timestring(msg, t);
298 if (s == NULL) {
299 return LDB_ERR_OPERATIONS_ERROR;
302 if (ldb_msg_add_string(msg, attr, s) != LDB_SUCCESS) {
303 return LDB_ERR_OPERATIONS_ERROR;
306 el = ldb_msg_find_element(msg, attr);
307 /* always set as replace. This works because on add ops, the flag
308 is ignored */
309 el->flags = LDB_FLAG_MOD_REPLACE;
311 return LDB_SUCCESS;
315 add a uint64_t element to a record
317 static int add_uint64_element(struct ldb_message *msg, const char *attr, uint64_t v)
319 struct ldb_message_element *el;
321 if (ldb_msg_find_element(msg, attr) != NULL) {
322 return LDB_SUCCESS;
325 if (ldb_msg_add_fmt(msg, attr, "%llu", (unsigned long long)v) != LDB_SUCCESS) {
326 return LDB_ERR_OPERATIONS_ERROR;
329 el = ldb_msg_find_element(msg, attr);
330 /* always set as replace. This works because on add ops, the flag
331 is ignored */
332 el->flags = LDB_FLAG_MOD_REPLACE;
334 return LDB_SUCCESS;
337 static int replmd_replPropertyMetaData1_attid_sort(const struct replPropertyMetaData1 *m1,
338 const struct replPropertyMetaData1 *m2,
339 const uint32_t *rdn_attid)
341 if (m1->attid == m2->attid) {
342 return 0;
346 * the rdn attribute should be at the end!
347 * so we need to return a value greater than zero
348 * which means m1 is greater than m2
350 if (m1->attid == *rdn_attid) {
351 return 1;
355 * the rdn attribute should be at the end!
356 * so we need to return a value less than zero
357 * which means m2 is greater than m1
359 if (m2->attid == *rdn_attid) {
360 return -1;
363 return m1->attid - m2->attid;
366 static int replmd_replPropertyMetaDataCtr1_sort(struct replPropertyMetaDataCtr1 *ctr1,
367 const struct dsdb_schema *schema,
368 struct ldb_dn *dn)
370 const char *rdn_name;
371 const struct dsdb_attribute *rdn_sa;
373 rdn_name = ldb_dn_get_rdn_name(dn);
374 if (!rdn_name) {
375 DEBUG(0,(__location__ ": No rDN for %s?\n", ldb_dn_get_linearized(dn)));
376 return LDB_ERR_OPERATIONS_ERROR;
379 rdn_sa = dsdb_attribute_by_lDAPDisplayName(schema, rdn_name);
380 if (rdn_sa == NULL) {
381 DEBUG(0,(__location__ ": No sa found for rDN %s for %s\n", rdn_name, ldb_dn_get_linearized(dn)));
382 return LDB_ERR_OPERATIONS_ERROR;
385 DEBUG(6,("Sorting rpmd with attid exception %u rDN=%s DN=%s\n",
386 rdn_sa->attributeID_id, rdn_name, ldb_dn_get_linearized(dn)));
388 ldb_qsort(ctr1->array, ctr1->count, sizeof(struct replPropertyMetaData1),
389 discard_const_p(void, &rdn_sa->attributeID_id),
390 (ldb_qsort_cmp_fn_t)replmd_replPropertyMetaData1_attid_sort);
392 return LDB_SUCCESS;
395 static int replmd_ldb_message_element_attid_sort(const struct ldb_message_element *e1,
396 const struct ldb_message_element *e2,
397 const struct dsdb_schema *schema)
399 const struct dsdb_attribute *a1;
400 const struct dsdb_attribute *a2;
403 * TODO: make this faster by caching the dsdb_attribute pointer
404 * on the ldb_messag_element
407 a1 = dsdb_attribute_by_lDAPDisplayName(schema, e1->name);
408 a2 = dsdb_attribute_by_lDAPDisplayName(schema, e2->name);
411 * TODO: remove this check, we should rely on e1 and e2 having valid attribute names
412 * in the schema
414 if (!a1 || !a2) {
415 return strcasecmp(e1->name, e2->name);
418 return a1->attributeID_id - a2->attributeID_id;
421 static void replmd_ldb_message_sort(struct ldb_message *msg,
422 const struct dsdb_schema *schema)
424 ldb_qsort(msg->elements, msg->num_elements, sizeof(struct ldb_message_element),
425 discard_const_p(void, schema), (ldb_qsort_cmp_fn_t)replmd_ldb_message_element_attid_sort);
428 static int replmd_op_callback(struct ldb_request *req, struct ldb_reply *ares)
430 struct ldb_context *ldb;
431 struct replmd_replicated_request *ac;
433 ac = talloc_get_type(req->context, struct replmd_replicated_request);
434 ldb = ldb_module_get_ctx(ac->module);
436 if (!ares) {
437 return ldb_module_done(ac->req, NULL, NULL,
438 LDB_ERR_OPERATIONS_ERROR);
440 if (ares->error != LDB_SUCCESS) {
441 return ldb_module_done(ac->req, ares->controls,
442 ares->response, ares->error);
445 if (ares->type != LDB_REPLY_DONE) {
446 ldb_set_errstring(ldb,
447 "invalid ldb_reply_type in callback");
448 talloc_free(ares);
449 return ldb_module_done(ac->req, NULL, NULL,
450 LDB_ERR_OPERATIONS_ERROR);
453 return ldb_module_done(ac->req, ares->controls,
454 ares->response, LDB_SUCCESS);
457 static int replmd_add(struct ldb_module *module, struct ldb_request *req)
459 struct ldb_context *ldb;
460 struct replmd_replicated_request *ac;
461 const struct dsdb_schema *schema;
462 enum ndr_err_code ndr_err;
463 struct ldb_request *down_req;
464 struct ldb_message *msg;
465 struct GUID guid;
466 struct ldb_val guid_value;
467 struct replPropertyMetaDataBlob nmd;
468 struct ldb_val nmd_value;
469 uint64_t seq_num;
470 const struct GUID *our_invocation_id;
471 time_t t = time(NULL);
472 NTTIME now;
473 char *time_str;
474 int ret;
475 uint32_t i, ni=0;
477 /* do not manipulate our control entries */
478 if (ldb_dn_is_special(req->op.add.message->dn)) {
479 return ldb_next_request(module, req);
482 ldb = ldb_module_get_ctx(module);
484 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_add\n");
486 schema = dsdb_get_schema(ldb);
487 if (!schema) {
488 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
489 "replmd_add: no dsdb_schema loaded");
490 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
491 return LDB_ERR_CONSTRAINT_VIOLATION;
494 ac = replmd_ctx_init(module, req);
495 if (!ac) {
496 return LDB_ERR_OPERATIONS_ERROR;
499 ac->schema = schema;
501 if (ldb_msg_find_element(req->op.add.message, "objectGUID") != NULL) {
502 ldb_debug_set(ldb, LDB_DEBUG_ERROR,
503 "replmd_add: it's not allowed to add an object with objectGUID\n");
504 return LDB_ERR_UNWILLING_TO_PERFORM;
507 /* Get a sequence number from the backend */
508 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
509 if (ret != LDB_SUCCESS) {
510 return ret;
513 /* a new GUID */
514 guid = GUID_random();
516 /* get our invocationId */
517 our_invocation_id = samdb_ntds_invocation_id(ldb);
518 if (!our_invocation_id) {
519 ldb_debug_set(ldb, LDB_DEBUG_ERROR,
520 "replmd_add: unable to find invocationId\n");
521 return LDB_ERR_OPERATIONS_ERROR;
524 /* we have to copy the message as the caller might have it as a const */
525 msg = ldb_msg_copy_shallow(ac, req->op.add.message);
526 if (msg == NULL) {
527 ldb_oom(ldb);
528 return LDB_ERR_OPERATIONS_ERROR;
531 /* generated times */
532 unix_to_nt_time(&now, t);
533 time_str = ldb_timestring(msg, t);
534 if (!time_str) {
535 return LDB_ERR_OPERATIONS_ERROR;
539 * remove autogenerated attributes
541 ldb_msg_remove_attr(msg, "whenCreated");
542 ldb_msg_remove_attr(msg, "whenChanged");
543 ldb_msg_remove_attr(msg, "uSNCreated");
544 ldb_msg_remove_attr(msg, "uSNChanged");
545 ldb_msg_remove_attr(msg, "replPropertyMetaData");
547 if (!ldb_msg_find_element(req->op.add.message, "instanceType")) {
548 ret = ldb_msg_add_fmt(msg, "instanceType", "%u", INSTANCE_TYPE_WRITE);
549 if (ret != LDB_SUCCESS) {
550 ldb_oom(ldb);
551 return LDB_ERR_OPERATIONS_ERROR;
556 * readd replicated attributes
558 ret = ldb_msg_add_string(msg, "whenCreated", time_str);
559 if (ret != LDB_SUCCESS) {
560 ldb_oom(ldb);
561 return LDB_ERR_OPERATIONS_ERROR;
564 /* build the replication meta_data */
565 ZERO_STRUCT(nmd);
566 nmd.version = 1;
567 nmd.ctr.ctr1.count = msg->num_elements;
568 nmd.ctr.ctr1.array = talloc_array(msg,
569 struct replPropertyMetaData1,
570 nmd.ctr.ctr1.count);
571 if (!nmd.ctr.ctr1.array) {
572 ldb_oom(ldb);
573 return LDB_ERR_OPERATIONS_ERROR;
576 for (i=0; i < msg->num_elements; i++) {
577 struct ldb_message_element *e = &msg->elements[i];
578 struct replPropertyMetaData1 *m = &nmd.ctr.ctr1.array[ni];
579 const struct dsdb_attribute *sa;
581 if (e->name[0] == '@') continue;
583 sa = dsdb_attribute_by_lDAPDisplayName(schema, e->name);
584 if (!sa) {
585 ldb_debug_set(ldb, LDB_DEBUG_ERROR,
586 "replmd_add: attribute '%s' not defined in schema\n",
587 e->name);
588 return LDB_ERR_NO_SUCH_ATTRIBUTE;
591 if ((sa->systemFlags & DS_FLAG_ATTR_NOT_REPLICATED) || (sa->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED)) {
592 /* if the attribute is not replicated (0x00000001)
593 * or constructed (0x00000004) it has no metadata
595 continue;
598 m->attid = sa->attributeID_id;
599 m->version = 1;
600 m->originating_change_time = now;
601 m->originating_invocation_id = *our_invocation_id;
602 m->originating_usn = seq_num;
603 m->local_usn = seq_num;
604 ni++;
607 /* fix meta data count */
608 nmd.ctr.ctr1.count = ni;
611 * sort meta data array, and move the rdn attribute entry to the end
613 ret = replmd_replPropertyMetaDataCtr1_sort(&nmd.ctr.ctr1, schema, msg->dn);
614 if (ret != LDB_SUCCESS) {
615 return ret;
618 /* generated NDR encoded values */
619 ndr_err = ndr_push_struct_blob(&guid_value, msg,
620 NULL,
621 &guid,
622 (ndr_push_flags_fn_t)ndr_push_GUID);
623 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
624 ldb_oom(ldb);
625 return LDB_ERR_OPERATIONS_ERROR;
627 ndr_err = ndr_push_struct_blob(&nmd_value, msg,
628 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
629 &nmd,
630 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
631 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
632 ldb_oom(ldb);
633 return LDB_ERR_OPERATIONS_ERROR;
637 * add the autogenerated values
639 ret = ldb_msg_add_value(msg, "objectGUID", &guid_value, NULL);
640 if (ret != LDB_SUCCESS) {
641 ldb_oom(ldb);
642 return LDB_ERR_OPERATIONS_ERROR;
644 ret = ldb_msg_add_string(msg, "whenChanged", time_str);
645 if (ret != LDB_SUCCESS) {
646 ldb_oom(ldb);
647 return LDB_ERR_OPERATIONS_ERROR;
649 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", seq_num);
650 if (ret != LDB_SUCCESS) {
651 ldb_oom(ldb);
652 return LDB_ERR_OPERATIONS_ERROR;
654 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", seq_num);
655 if (ret != LDB_SUCCESS) {
656 ldb_oom(ldb);
657 return LDB_ERR_OPERATIONS_ERROR;
659 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
660 if (ret != LDB_SUCCESS) {
661 ldb_oom(ldb);
662 return LDB_ERR_OPERATIONS_ERROR;
666 * sort the attributes by attid before storing the object
668 replmd_ldb_message_sort(msg, schema);
670 ret = ldb_build_add_req(&down_req, ldb, ac,
671 msg,
672 req->controls,
673 ac, replmd_op_callback,
674 req);
675 if (ret != LDB_SUCCESS) {
676 return ret;
679 ret = replmd_notify(module, msg->dn, seq_num);
680 if (ret != LDB_SUCCESS) {
681 return ret;
684 /* go on with the call chain */
685 return ldb_next_request(module, down_req);
690 * update the replPropertyMetaData for one element
692 static int replmd_update_rpmd_element(struct ldb_context *ldb,
693 struct ldb_message *msg,
694 struct ldb_message_element *el,
695 struct replPropertyMetaDataBlob *omd,
696 struct dsdb_schema *schema,
697 uint64_t *seq_num,
698 const struct GUID *our_invocation_id,
699 NTTIME now)
701 int i;
702 const struct dsdb_attribute *a;
703 struct replPropertyMetaData1 *md1;
705 a = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
706 if (a == NULL) {
707 DEBUG(0,(__location__ ": Unable to find attribute %s in schema\n",
708 el->name));
709 return LDB_ERR_OPERATIONS_ERROR;
712 if ((a->systemFlags & DS_FLAG_ATTR_NOT_REPLICATED) || (a->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED)) {
713 return LDB_SUCCESS;
716 for (i=0; i<omd->ctr.ctr1.count; i++) {
717 if (a->attributeID_id == omd->ctr.ctr1.array[i].attid) break;
719 if (i == omd->ctr.ctr1.count) {
720 /* we need to add a new one */
721 omd->ctr.ctr1.array = talloc_realloc(msg, omd->ctr.ctr1.array,
722 struct replPropertyMetaData1, omd->ctr.ctr1.count+1);
723 if (omd->ctr.ctr1.array == NULL) {
724 ldb_oom(ldb);
725 return LDB_ERR_OPERATIONS_ERROR;
727 omd->ctr.ctr1.count++;
728 ZERO_STRUCT(omd->ctr.ctr1.array[i]);
731 /* Get a new sequence number from the backend. We only do this
732 * if we have a change that requires a new
733 * replPropertyMetaData element
735 if (*seq_num == 0) {
736 int ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, seq_num);
737 if (ret != LDB_SUCCESS) {
738 return LDB_ERR_OPERATIONS_ERROR;
742 md1 = &omd->ctr.ctr1.array[i];
743 md1->version++;
744 md1->attid = a->attributeID_id;
745 md1->originating_change_time = now;
746 md1->originating_invocation_id = *our_invocation_id;
747 md1->originating_usn = *seq_num;
748 md1->local_usn = *seq_num;
750 return LDB_SUCCESS;
754 * update the replPropertyMetaData object each time we modify an
755 * object. This is needed for DRS replication, as the merge on the
756 * client is based on this object
758 static int replmd_update_rpmd(struct ldb_module *module,
759 struct ldb_message *msg, uint64_t *seq_num)
761 const struct ldb_val *omd_value;
762 enum ndr_err_code ndr_err;
763 struct replPropertyMetaDataBlob omd;
764 int i;
765 struct dsdb_schema *schema;
766 time_t t = time(NULL);
767 NTTIME now;
768 const struct GUID *our_invocation_id;
769 int ret;
770 const char *attrs[] = { "replPropertyMetaData" , NULL };
771 struct ldb_result *res;
772 struct ldb_context *ldb;
774 ldb = ldb_module_get_ctx(module);
776 our_invocation_id = samdb_ntds_invocation_id(ldb);
777 if (!our_invocation_id) {
778 /* this happens during an initial vampire while
779 updating the schema */
780 DEBUG(5,("No invocationID - skipping replPropertyMetaData update\n"));
781 return LDB_SUCCESS;
784 unix_to_nt_time(&now, t);
786 /* search for the existing replPropertyMetaDataBlob */
787 ret = dsdb_search_dn_with_deleted(ldb, msg, &res, msg->dn, attrs);
788 if (ret != LDB_SUCCESS || res->count < 1) {
789 DEBUG(0,(__location__ ": Object %s failed to find replPropertyMetaData\n",
790 ldb_dn_get_linearized(msg->dn)));
791 return LDB_ERR_OPERATIONS_ERROR;
795 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
796 if (!omd_value) {
797 DEBUG(0,(__location__ ": Object %s does not have a replPropertyMetaData attribute\n",
798 ldb_dn_get_linearized(msg->dn)));
799 return LDB_ERR_OPERATIONS_ERROR;
802 ndr_err = ndr_pull_struct_blob(omd_value, msg,
803 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")), &omd,
804 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
805 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
806 DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s\n",
807 ldb_dn_get_linearized(msg->dn)));
808 return LDB_ERR_OPERATIONS_ERROR;
811 if (omd.version != 1) {
812 DEBUG(0,(__location__ ": bad version %u in replPropertyMetaData for %s\n",
813 omd.version, ldb_dn_get_linearized(msg->dn)));
814 return LDB_ERR_OPERATIONS_ERROR;
817 schema = dsdb_get_schema(ldb);
819 for (i=0; i<msg->num_elements; i++) {
820 ret = replmd_update_rpmd_element(ldb, msg, &msg->elements[i], &omd, schema, seq_num,
821 our_invocation_id, now);
822 if (ret != LDB_SUCCESS) {
823 return ret;
828 * replmd_update_rpmd_element has done an update if the
829 * seq_num is set
831 if (*seq_num != 0) {
832 struct ldb_val *md_value;
833 struct ldb_message_element *el;
835 md_value = talloc(msg, struct ldb_val);
836 if (md_value == NULL) {
837 ldb_oom(ldb);
838 return LDB_ERR_OPERATIONS_ERROR;
841 ret = replmd_replPropertyMetaDataCtr1_sort(&omd.ctr.ctr1, schema, msg->dn);
842 if (ret != LDB_SUCCESS) {
843 return ret;
846 ndr_err = ndr_push_struct_blob(md_value, msg,
847 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
848 &omd,
849 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
850 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
851 DEBUG(0,(__location__ ": Failed to marshall replPropertyMetaData for %s\n",
852 ldb_dn_get_linearized(msg->dn)));
853 return LDB_ERR_OPERATIONS_ERROR;
856 ret = ldb_msg_add_empty(msg, "replPropertyMetaData", LDB_FLAG_MOD_REPLACE, &el);
857 if (ret != LDB_SUCCESS) {
858 DEBUG(0,(__location__ ": Failed to add updated replPropertyMetaData %s\n",
859 ldb_dn_get_linearized(msg->dn)));
860 return ret;
863 ret = replmd_notify(module, msg->dn, *seq_num);
864 if (ret != LDB_SUCCESS) {
865 return ret;
868 el->num_values = 1;
869 el->values = md_value;
872 return LDB_SUCCESS;
876 static int replmd_modify(struct ldb_module *module, struct ldb_request *req)
878 struct ldb_context *ldb;
879 struct replmd_replicated_request *ac;
880 const struct dsdb_schema *schema;
881 struct ldb_request *down_req;
882 struct ldb_message *msg;
883 int ret;
884 time_t t = time(NULL);
885 uint64_t seq_num = 0;
887 /* do not manipulate our control entries */
888 if (ldb_dn_is_special(req->op.mod.message->dn)) {
889 return ldb_next_request(module, req);
892 ldb = ldb_module_get_ctx(module);
894 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_modify\n");
896 schema = dsdb_get_schema(ldb);
897 if (!schema) {
898 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
899 "replmd_modify: no dsdb_schema loaded");
900 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
901 return LDB_ERR_CONSTRAINT_VIOLATION;
904 ac = replmd_ctx_init(module, req);
905 if (!ac) {
906 return LDB_ERR_OPERATIONS_ERROR;
909 ac->schema = schema;
911 /* we have to copy the message as the caller might have it as a const */
912 msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
913 if (msg == NULL) {
914 talloc_free(ac);
915 return LDB_ERR_OPERATIONS_ERROR;
918 /* TODO:
919 * - get the whole old object
920 * - if the old object doesn't exist report an error
921 * - give an error when a readonly attribute should
922 * be modified
923 * - merge the changed into the old object
924 * if the caller set values to the same value
925 * ignore the attribute, return success when no
926 * attribute was changed
929 ret = replmd_update_rpmd(module, msg, &seq_num);
930 if (ret != LDB_SUCCESS) {
931 return ret;
934 /* TODO:
935 * - replace the old object with the newly constructed one
938 ret = ldb_build_mod_req(&down_req, ldb, ac,
939 msg,
940 req->controls,
941 ac, replmd_op_callback,
942 req);
943 if (ret != LDB_SUCCESS) {
944 return ret;
946 talloc_steal(down_req, msg);
948 /* we only change whenChanged and uSNChanged if the seq_num
949 has changed */
950 if (seq_num != 0) {
951 if (add_time_element(msg, "whenChanged", t) != LDB_SUCCESS) {
952 talloc_free(ac);
953 return LDB_ERR_OPERATIONS_ERROR;
956 if (add_uint64_element(msg, "uSNChanged", seq_num) != LDB_SUCCESS) {
957 talloc_free(ac);
958 return LDB_ERR_OPERATIONS_ERROR;
962 /* go on with the call chain */
963 return ldb_next_request(module, down_req);
968 handle a rename request
970 On a rename we need to do an extra ldb_modify which sets the
971 whenChanged and uSNChanged attributes
973 static int replmd_rename(struct ldb_module *module, struct ldb_request *req)
975 struct ldb_context *ldb;
976 int ret, i;
977 time_t t = time(NULL);
978 uint64_t seq_num = 0;
979 struct ldb_message *msg;
980 struct replmd_private *replmd_private =
981 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
983 /* do not manipulate our control entries */
984 if (ldb_dn_is_special(req->op.mod.message->dn)) {
985 return ldb_next_request(module, req);
988 ldb = ldb_module_get_ctx(module);
990 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_rename\n");
992 /* Get a sequence number from the backend */
993 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
994 if (ret != LDB_SUCCESS) {
995 return ret;
998 msg = ldb_msg_new(req);
999 if (msg == NULL) {
1000 ldb_oom(ldb);
1001 return LDB_ERR_OPERATIONS_ERROR;
1004 msg->dn = req->op.rename.olddn;
1006 if (add_time_element(msg, "whenChanged", t) != LDB_SUCCESS) {
1007 talloc_free(msg);
1008 return LDB_ERR_OPERATIONS_ERROR;
1010 msg->elements[0].flags = LDB_FLAG_MOD_REPLACE;
1012 if (add_uint64_element(msg, "uSNChanged", seq_num) != LDB_SUCCESS) {
1013 talloc_free(msg);
1014 return LDB_ERR_OPERATIONS_ERROR;
1016 msg->elements[1].flags = LDB_FLAG_MOD_REPLACE;
1018 ret = ldb_modify(ldb, msg);
1019 talloc_free(msg);
1020 if (ret != LDB_SUCCESS) {
1021 return ret;
1024 ret = replmd_load_NCs(module);
1025 if (ret != 0) {
1026 return ret;
1029 /* now update the highest uSNs of the partitions that are
1030 affected. Note that two partitions could be changing */
1031 for (i=0; i<replmd_private->num_ncs; i++) {
1032 if (ldb_dn_compare_base(replmd_private->ncs[i].dn,
1033 req->op.rename.olddn) == 0) {
1034 break;
1037 if (i == replmd_private->num_ncs) {
1038 DEBUG(0,(__location__ ": rename olddn outside tree? %s\n",
1039 ldb_dn_get_linearized(req->op.rename.olddn)));
1040 return LDB_ERR_OPERATIONS_ERROR;
1042 replmd_private->ncs[i].mod_usn = seq_num;
1044 for (i=0; i<replmd_private->num_ncs; i++) {
1045 if (ldb_dn_compare_base(replmd_private->ncs[i].dn,
1046 req->op.rename.newdn) == 0) {
1047 break;
1050 if (i == replmd_private->num_ncs) {
1051 DEBUG(0,(__location__ ": rename newdn outside tree? %s\n",
1052 ldb_dn_get_linearized(req->op.rename.newdn)));
1053 return LDB_ERR_OPERATIONS_ERROR;
1055 replmd_private->ncs[i].mod_usn = seq_num;
1057 /* go on with the call chain */
1058 return ldb_next_request(module, req);
1062 static int replmd_replicated_request_error(struct replmd_replicated_request *ar, int ret)
1064 return ret;
1067 static int replmd_replicated_request_werror(struct replmd_replicated_request *ar, WERROR status)
1069 int ret = LDB_ERR_OTHER;
1070 /* TODO: do some error mapping */
1071 return ret;
1074 static int replmd_replicated_apply_next(struct replmd_replicated_request *ar);
1076 static int replmd_replicated_apply_add_callback(struct ldb_request *req,
1077 struct ldb_reply *ares)
1079 struct ldb_context *ldb;
1080 struct replmd_replicated_request *ar = talloc_get_type(req->context,
1081 struct replmd_replicated_request);
1082 int ret;
1084 ldb = ldb_module_get_ctx(ar->module);
1086 if (!ares) {
1087 return ldb_module_done(ar->req, NULL, NULL,
1088 LDB_ERR_OPERATIONS_ERROR);
1090 if (ares->error != LDB_SUCCESS) {
1091 return ldb_module_done(ar->req, ares->controls,
1092 ares->response, ares->error);
1095 if (ares->type != LDB_REPLY_DONE) {
1096 ldb_set_errstring(ldb, "Invalid reply type\n!");
1097 return ldb_module_done(ar->req, NULL, NULL,
1098 LDB_ERR_OPERATIONS_ERROR);
1101 talloc_free(ares);
1102 ar->index_current++;
1104 ret = replmd_replicated_apply_next(ar);
1105 if (ret != LDB_SUCCESS) {
1106 return ldb_module_done(ar->req, NULL, NULL, ret);
1109 return LDB_SUCCESS;
1112 static int replmd_replicated_apply_add(struct replmd_replicated_request *ar)
1114 struct ldb_context *ldb;
1115 struct ldb_request *change_req;
1116 enum ndr_err_code ndr_err;
1117 struct ldb_message *msg;
1118 struct replPropertyMetaDataBlob *md;
1119 struct ldb_val md_value;
1120 uint32_t i;
1121 uint64_t seq_num;
1122 int ret;
1125 * TODO: check if the parent object exist
1129 * TODO: handle the conflict case where an object with the
1130 * same name exist
1133 ldb = ldb_module_get_ctx(ar->module);
1134 msg = ar->objs->objects[ar->index_current].msg;
1135 md = ar->objs->objects[ar->index_current].meta_data;
1137 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
1138 if (ret != LDB_SUCCESS) {
1139 return replmd_replicated_request_error(ar, ret);
1142 ret = ldb_msg_add_value(msg, "objectGUID", &ar->objs->objects[ar->index_current].guid_value, NULL);
1143 if (ret != LDB_SUCCESS) {
1144 return replmd_replicated_request_error(ar, ret);
1147 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
1148 if (ret != LDB_SUCCESS) {
1149 return replmd_replicated_request_error(ar, ret);
1152 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", seq_num);
1153 if (ret != LDB_SUCCESS) {
1154 return replmd_replicated_request_error(ar, ret);
1157 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", seq_num);
1158 if (ret != LDB_SUCCESS) {
1159 return replmd_replicated_request_error(ar, ret);
1162 ret = replmd_notify(ar->module, msg->dn, seq_num);
1163 if (ret != LDB_SUCCESS) {
1164 return replmd_replicated_request_error(ar, ret);
1167 /* remove any message elements that have zero values */
1168 for (i=0; i<msg->num_elements; i++) {
1169 if (msg->elements[i].num_values == 0) {
1170 DEBUG(4,(__location__ ": Removing attribute %s with num_values==0\n",
1171 msg->elements[i].name));
1172 memmove(&msg->elements[i],
1173 &msg->elements[i+1],
1174 sizeof(msg->elements[i])*(msg->num_elements - (i+1)));
1175 msg->num_elements--;
1176 i--;
1181 * the meta data array is already sorted by the caller
1183 for (i=0; i < md->ctr.ctr1.count; i++) {
1184 md->ctr.ctr1.array[i].local_usn = seq_num;
1186 ndr_err = ndr_push_struct_blob(&md_value, msg,
1187 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
1189 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
1190 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1191 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
1192 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
1194 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &md_value, NULL);
1195 if (ret != LDB_SUCCESS) {
1196 return replmd_replicated_request_error(ar, ret);
1199 replmd_ldb_message_sort(msg, ar->schema);
1201 if (DEBUGLVL(4)) {
1202 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_ADD, msg);
1203 DEBUG(4, ("DRS replication add message:\n%s\n", s));
1204 talloc_free(s);
1207 ret = ldb_build_add_req(&change_req,
1208 ldb,
1210 msg,
1211 ar->controls,
1213 replmd_replicated_apply_add_callback,
1214 ar->req);
1215 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
1217 return ldb_next_request(ar->module, change_req);
1220 static int replmd_replPropertyMetaData1_conflict_compare(struct replPropertyMetaData1 *m1,
1221 struct replPropertyMetaData1 *m2)
1223 int ret;
1225 if (m1->version != m2->version) {
1226 return m1->version - m2->version;
1229 if (m1->originating_change_time != m2->originating_change_time) {
1230 return m1->originating_change_time - m2->originating_change_time;
1233 ret = GUID_compare(&m1->originating_invocation_id, &m2->originating_invocation_id);
1234 if (ret != 0) {
1235 return ret;
1238 return m1->originating_usn - m2->originating_usn;
1241 static int replmd_replicated_apply_merge_callback(struct ldb_request *req,
1242 struct ldb_reply *ares)
1244 struct ldb_context *ldb;
1245 struct replmd_replicated_request *ar = talloc_get_type(req->context,
1246 struct replmd_replicated_request);
1247 int ret;
1249 ldb = ldb_module_get_ctx(ar->module);
1251 if (!ares) {
1252 return ldb_module_done(ar->req, NULL, NULL,
1253 LDB_ERR_OPERATIONS_ERROR);
1255 if (ares->error != LDB_SUCCESS) {
1256 return ldb_module_done(ar->req, ares->controls,
1257 ares->response, ares->error);
1260 if (ares->type != LDB_REPLY_DONE) {
1261 ldb_set_errstring(ldb, "Invalid reply type\n!");
1262 return ldb_module_done(ar->req, NULL, NULL,
1263 LDB_ERR_OPERATIONS_ERROR);
1266 talloc_free(ares);
1267 ar->index_current++;
1269 ret = replmd_replicated_apply_next(ar);
1270 if (ret != LDB_SUCCESS) {
1271 return ldb_module_done(ar->req, NULL, NULL, ret);
1274 return LDB_SUCCESS;
1277 static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar)
1279 struct ldb_context *ldb;
1280 struct ldb_request *change_req;
1281 enum ndr_err_code ndr_err;
1282 struct ldb_message *msg;
1283 struct replPropertyMetaDataBlob *rmd;
1284 struct replPropertyMetaDataBlob omd;
1285 const struct ldb_val *omd_value;
1286 struct replPropertyMetaDataBlob nmd;
1287 struct ldb_val nmd_value;
1288 uint32_t i,j,ni=0;
1289 uint32_t removed_attrs = 0;
1290 uint64_t seq_num;
1291 int ret;
1293 ldb = ldb_module_get_ctx(ar->module);
1294 msg = ar->objs->objects[ar->index_current].msg;
1295 rmd = ar->objs->objects[ar->index_current].meta_data;
1296 ZERO_STRUCT(omd);
1297 omd.version = 1;
1300 * TODO: check repl data is correct after a rename
1302 if (ldb_dn_compare(msg->dn, ar->search_msg->dn) != 0) {
1303 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_request rename %s => %s\n",
1304 ldb_dn_get_linearized(ar->search_msg->dn),
1305 ldb_dn_get_linearized(msg->dn));
1306 if (ldb_rename(ldb, ar->search_msg->dn, msg->dn) != LDB_SUCCESS) {
1307 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_replicated_request rename %s => %s failed - %s\n",
1308 ldb_dn_get_linearized(ar->search_msg->dn),
1309 ldb_dn_get_linearized(msg->dn),
1310 ldb_errstring(ldb));
1311 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
1315 /* find existing meta data */
1316 omd_value = ldb_msg_find_ldb_val(ar->search_msg, "replPropertyMetaData");
1317 if (omd_value) {
1318 ndr_err = ndr_pull_struct_blob(omd_value, ar,
1319 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")), &omd,
1320 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
1321 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1322 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
1323 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
1326 if (omd.version != 1) {
1327 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
1331 ZERO_STRUCT(nmd);
1332 nmd.version = 1;
1333 nmd.ctr.ctr1.count = omd.ctr.ctr1.count + rmd->ctr.ctr1.count;
1334 nmd.ctr.ctr1.array = talloc_array(ar,
1335 struct replPropertyMetaData1,
1336 nmd.ctr.ctr1.count);
1337 if (!nmd.ctr.ctr1.array) return replmd_replicated_request_werror(ar, WERR_NOMEM);
1339 /* first copy the old meta data */
1340 for (i=0; i < omd.ctr.ctr1.count; i++) {
1341 nmd.ctr.ctr1.array[ni] = omd.ctr.ctr1.array[i];
1342 ni++;
1345 /* now merge in the new meta data */
1346 for (i=0; i < rmd->ctr.ctr1.count; i++) {
1347 bool found = false;
1349 for (j=0; j < ni; j++) {
1350 int cmp;
1352 if (rmd->ctr.ctr1.array[i].attid != nmd.ctr.ctr1.array[j].attid) {
1353 continue;
1356 cmp = replmd_replPropertyMetaData1_conflict_compare(&rmd->ctr.ctr1.array[i],
1357 &nmd.ctr.ctr1.array[j]);
1358 if (cmp > 0) {
1359 /* replace the entry */
1360 nmd.ctr.ctr1.array[j] = rmd->ctr.ctr1.array[i];
1361 found = true;
1362 break;
1365 /* we don't want to apply this change so remove the attribute */
1366 ldb_msg_remove_element(msg, &msg->elements[i-removed_attrs]);
1367 removed_attrs++;
1369 found = true;
1370 break;
1373 if (found) continue;
1375 nmd.ctr.ctr1.array[ni] = rmd->ctr.ctr1.array[i];
1376 ni++;
1380 * finally correct the size of the meta_data array
1382 nmd.ctr.ctr1.count = ni;
1385 * the rdn attribute (the alias for the name attribute),
1386 * 'cn' for most objects is the last entry in the meta data array
1387 * we have stored
1389 * sort the new meta data array
1391 ret = replmd_replPropertyMetaDataCtr1_sort(&nmd.ctr.ctr1, ar->schema, msg->dn);
1392 if (ret != LDB_SUCCESS) {
1393 return ret;
1397 * check if some replicated attributes left, otherwise skip the ldb_modify() call
1399 if (msg->num_elements == 0) {
1400 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: skip replace\n",
1401 ar->index_current);
1403 ar->index_current++;
1404 return replmd_replicated_apply_next(ar);
1407 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: replace %u attributes\n",
1408 ar->index_current, msg->num_elements);
1410 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
1411 if (ret != LDB_SUCCESS) {
1412 return replmd_replicated_request_error(ar, ret);
1415 for (i=0; i<ni; i++) {
1416 nmd.ctr.ctr1.array[i].local_usn = seq_num;
1419 /* create the meta data value */
1420 ndr_err = ndr_push_struct_blob(&nmd_value, msg,
1421 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
1422 &nmd,
1423 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
1424 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1425 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
1426 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
1430 * when we know that we'll modify the record, add the whenChanged, uSNChanged
1431 * and replPopertyMetaData attributes
1433 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
1434 if (ret != LDB_SUCCESS) {
1435 return replmd_replicated_request_error(ar, ret);
1437 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", seq_num);
1438 if (ret != LDB_SUCCESS) {
1439 return replmd_replicated_request_error(ar, ret);
1441 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
1442 if (ret != LDB_SUCCESS) {
1443 return replmd_replicated_request_error(ar, ret);
1446 replmd_ldb_message_sort(msg, ar->schema);
1448 /* we want to replace the old values */
1449 for (i=0; i < msg->num_elements; i++) {
1450 msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
1453 ret = replmd_notify(ar->module, msg->dn, seq_num);
1454 if (ret != LDB_SUCCESS) {
1455 return replmd_replicated_request_error(ar, ret);
1458 if (DEBUGLVL(4)) {
1459 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_MODIFY, msg);
1460 DEBUG(4, ("DRS replication modify message:\n%s\n", s));
1461 talloc_free(s);
1464 ret = ldb_build_mod_req(&change_req,
1465 ldb,
1467 msg,
1468 ar->controls,
1470 replmd_replicated_apply_merge_callback,
1471 ar->req);
1472 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
1474 return ldb_next_request(ar->module, change_req);
1477 static int replmd_replicated_apply_search_callback(struct ldb_request *req,
1478 struct ldb_reply *ares)
1480 struct replmd_replicated_request *ar = talloc_get_type(req->context,
1481 struct replmd_replicated_request);
1482 int ret;
1484 if (!ares) {
1485 return ldb_module_done(ar->req, NULL, NULL,
1486 LDB_ERR_OPERATIONS_ERROR);
1488 if (ares->error != LDB_SUCCESS &&
1489 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
1490 return ldb_module_done(ar->req, ares->controls,
1491 ares->response, ares->error);
1494 switch (ares->type) {
1495 case LDB_REPLY_ENTRY:
1496 ar->search_msg = talloc_steal(ar, ares->message);
1497 break;
1499 case LDB_REPLY_REFERRAL:
1500 /* we ignore referrals */
1501 break;
1503 case LDB_REPLY_DONE:
1504 if (ar->search_msg != NULL) {
1505 ret = replmd_replicated_apply_merge(ar);
1506 } else {
1507 ret = replmd_replicated_apply_add(ar);
1509 if (ret != LDB_SUCCESS) {
1510 return ldb_module_done(ar->req, NULL, NULL, ret);
1514 talloc_free(ares);
1515 return LDB_SUCCESS;
1518 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar);
1520 static int replmd_replicated_apply_next(struct replmd_replicated_request *ar)
1522 struct ldb_context *ldb;
1523 int ret;
1524 char *tmp_str;
1525 char *filter;
1526 struct ldb_request *search_req;
1528 if (ar->index_current >= ar->objs->num_objects) {
1529 /* done with it, go to next stage */
1530 return replmd_replicated_uptodate_vector(ar);
1533 ldb = ldb_module_get_ctx(ar->module);
1534 ar->search_msg = NULL;
1536 tmp_str = ldb_binary_encode(ar, ar->objs->objects[ar->index_current].guid_value);
1537 if (!tmp_str) return replmd_replicated_request_werror(ar, WERR_NOMEM);
1539 filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
1540 if (!filter) return replmd_replicated_request_werror(ar, WERR_NOMEM);
1541 talloc_free(tmp_str);
1543 ret = ldb_build_search_req(&search_req,
1544 ldb,
1546 ar->objs->partition_dn,
1547 LDB_SCOPE_SUBTREE,
1548 filter,
1549 NULL,
1550 NULL,
1552 replmd_replicated_apply_search_callback,
1553 ar->req);
1555 ret = ldb_request_add_control(search_req, LDB_CONTROL_SHOW_DELETED_OID, true, NULL);
1556 if (ret != LDB_SUCCESS) {
1557 return ret;
1561 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
1563 return ldb_next_request(ar->module, search_req);
1566 static int replmd_replicated_uptodate_modify_callback(struct ldb_request *req,
1567 struct ldb_reply *ares)
1569 struct ldb_context *ldb;
1570 struct replmd_replicated_request *ar = talloc_get_type(req->context,
1571 struct replmd_replicated_request);
1572 ldb = ldb_module_get_ctx(ar->module);
1574 if (!ares) {
1575 return ldb_module_done(ar->req, NULL, NULL,
1576 LDB_ERR_OPERATIONS_ERROR);
1578 if (ares->error != LDB_SUCCESS) {
1579 return ldb_module_done(ar->req, ares->controls,
1580 ares->response, ares->error);
1583 if (ares->type != LDB_REPLY_DONE) {
1584 ldb_set_errstring(ldb, "Invalid reply type\n!");
1585 return ldb_module_done(ar->req, NULL, NULL,
1586 LDB_ERR_OPERATIONS_ERROR);
1589 talloc_free(ares);
1591 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
1594 static int replmd_replicated_uptodate_modify(struct replmd_replicated_request *ar)
1596 struct ldb_context *ldb;
1597 struct ldb_request *change_req;
1598 enum ndr_err_code ndr_err;
1599 struct ldb_message *msg;
1600 struct replUpToDateVectorBlob ouv;
1601 const struct ldb_val *ouv_value;
1602 const struct drsuapi_DsReplicaCursor2CtrEx *ruv;
1603 struct replUpToDateVectorBlob nuv;
1604 struct ldb_val nuv_value;
1605 struct ldb_message_element *nuv_el = NULL;
1606 const struct GUID *our_invocation_id;
1607 struct ldb_message_element *orf_el = NULL;
1608 struct repsFromToBlob nrf;
1609 struct ldb_val *nrf_value = NULL;
1610 struct ldb_message_element *nrf_el = NULL;
1611 uint32_t i,j,ni=0;
1612 bool found = false;
1613 time_t t = time(NULL);
1614 NTTIME now;
1615 int ret;
1617 ldb = ldb_module_get_ctx(ar->module);
1618 ruv = ar->objs->uptodateness_vector;
1619 ZERO_STRUCT(ouv);
1620 ouv.version = 2;
1621 ZERO_STRUCT(nuv);
1622 nuv.version = 2;
1624 unix_to_nt_time(&now, t);
1627 * first create the new replUpToDateVector
1629 ouv_value = ldb_msg_find_ldb_val(ar->search_msg, "replUpToDateVector");
1630 if (ouv_value) {
1631 ndr_err = ndr_pull_struct_blob(ouv_value, ar,
1632 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")), &ouv,
1633 (ndr_pull_flags_fn_t)ndr_pull_replUpToDateVectorBlob);
1634 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1635 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
1636 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
1639 if (ouv.version != 2) {
1640 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
1645 * the new uptodateness vector will at least
1646 * contain 1 entry, one for the source_dsa
1648 * plus optional values from our old vector and the one from the source_dsa
1650 nuv.ctr.ctr2.count = 1 + ouv.ctr.ctr2.count;
1651 if (ruv) nuv.ctr.ctr2.count += ruv->count;
1652 nuv.ctr.ctr2.cursors = talloc_array(ar,
1653 struct drsuapi_DsReplicaCursor2,
1654 nuv.ctr.ctr2.count);
1655 if (!nuv.ctr.ctr2.cursors) return replmd_replicated_request_werror(ar, WERR_NOMEM);
1657 /* first copy the old vector */
1658 for (i=0; i < ouv.ctr.ctr2.count; i++) {
1659 nuv.ctr.ctr2.cursors[ni] = ouv.ctr.ctr2.cursors[i];
1660 ni++;
1663 /* get our invocation_id if we have one already attached to the ldb */
1664 our_invocation_id = samdb_ntds_invocation_id(ldb);
1666 /* merge in the source_dsa vector is available */
1667 for (i=0; (ruv && i < ruv->count); i++) {
1668 found = false;
1670 if (our_invocation_id &&
1671 GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
1672 our_invocation_id)) {
1673 continue;
1676 for (j=0; j < ni; j++) {
1677 if (!GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
1678 &nuv.ctr.ctr2.cursors[j].source_dsa_invocation_id)) {
1679 continue;
1682 found = true;
1685 * we update only the highest_usn and not the latest_sync_success time,
1686 * because the last success stands for direct replication
1688 if (ruv->cursors[i].highest_usn > nuv.ctr.ctr2.cursors[j].highest_usn) {
1689 nuv.ctr.ctr2.cursors[j].highest_usn = ruv->cursors[i].highest_usn;
1691 break;
1694 if (found) continue;
1696 /* if it's not there yet, add it */
1697 nuv.ctr.ctr2.cursors[ni] = ruv->cursors[i];
1698 ni++;
1702 * merge in the current highwatermark for the source_dsa
1704 found = false;
1705 for (j=0; j < ni; j++) {
1706 if (!GUID_equal(&ar->objs->source_dsa->source_dsa_invocation_id,
1707 &nuv.ctr.ctr2.cursors[j].source_dsa_invocation_id)) {
1708 continue;
1711 found = true;
1714 * here we update the highest_usn and last_sync_success time
1715 * because we're directly replicating from the source_dsa
1717 * and use the tmp_highest_usn because this is what we have just applied
1718 * to our ldb
1720 nuv.ctr.ctr2.cursors[j].highest_usn = ar->objs->source_dsa->highwatermark.tmp_highest_usn;
1721 nuv.ctr.ctr2.cursors[j].last_sync_success = now;
1722 break;
1724 if (!found) {
1726 * here we update the highest_usn and last_sync_success time
1727 * because we're directly replicating from the source_dsa
1729 * and use the tmp_highest_usn because this is what we have just applied
1730 * to our ldb
1732 nuv.ctr.ctr2.cursors[ni].source_dsa_invocation_id= ar->objs->source_dsa->source_dsa_invocation_id;
1733 nuv.ctr.ctr2.cursors[ni].highest_usn = ar->objs->source_dsa->highwatermark.tmp_highest_usn;
1734 nuv.ctr.ctr2.cursors[ni].last_sync_success = now;
1735 ni++;
1739 * finally correct the size of the cursors array
1741 nuv.ctr.ctr2.count = ni;
1744 * sort the cursors
1746 qsort(nuv.ctr.ctr2.cursors, nuv.ctr.ctr2.count,
1747 sizeof(struct drsuapi_DsReplicaCursor2),
1748 (comparison_fn_t)drsuapi_DsReplicaCursor2_compare);
1751 * create the change ldb_message
1753 msg = ldb_msg_new(ar);
1754 if (!msg) return replmd_replicated_request_werror(ar, WERR_NOMEM);
1755 msg->dn = ar->search_msg->dn;
1757 ndr_err = ndr_push_struct_blob(&nuv_value, msg,
1758 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
1759 &nuv,
1760 (ndr_push_flags_fn_t)ndr_push_replUpToDateVectorBlob);
1761 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1762 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
1763 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
1765 ret = ldb_msg_add_value(msg, "replUpToDateVector", &nuv_value, &nuv_el);
1766 if (ret != LDB_SUCCESS) {
1767 return replmd_replicated_request_error(ar, ret);
1769 nuv_el->flags = LDB_FLAG_MOD_REPLACE;
1772 * now create the new repsFrom value from the given repsFromTo1 structure
1774 ZERO_STRUCT(nrf);
1775 nrf.version = 1;
1776 nrf.ctr.ctr1 = *ar->objs->source_dsa;
1777 /* and fix some values... */
1778 nrf.ctr.ctr1.consecutive_sync_failures = 0;
1779 nrf.ctr.ctr1.last_success = now;
1780 nrf.ctr.ctr1.last_attempt = now;
1781 nrf.ctr.ctr1.result_last_attempt = WERR_OK;
1782 nrf.ctr.ctr1.highwatermark.highest_usn = nrf.ctr.ctr1.highwatermark.tmp_highest_usn;
1785 * first see if we already have a repsFrom value for the current source dsa
1786 * if so we'll later replace this value
1788 orf_el = ldb_msg_find_element(ar->search_msg, "repsFrom");
1789 if (orf_el) {
1790 for (i=0; i < orf_el->num_values; i++) {
1791 struct repsFromToBlob *trf;
1793 trf = talloc(ar, struct repsFromToBlob);
1794 if (!trf) return replmd_replicated_request_werror(ar, WERR_NOMEM);
1796 ndr_err = ndr_pull_struct_blob(&orf_el->values[i], trf, lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")), trf,
1797 (ndr_pull_flags_fn_t)ndr_pull_repsFromToBlob);
1798 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1799 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
1800 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
1803 if (trf->version != 1) {
1804 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
1808 * we compare the source dsa objectGUID not the invocation_id
1809 * because we want only one repsFrom value per source dsa
1810 * and when the invocation_id of the source dsa has changed we don't need
1811 * the old repsFrom with the old invocation_id
1813 if (!GUID_equal(&trf->ctr.ctr1.source_dsa_obj_guid,
1814 &ar->objs->source_dsa->source_dsa_obj_guid)) {
1815 talloc_free(trf);
1816 continue;
1819 talloc_free(trf);
1820 nrf_value = &orf_el->values[i];
1821 break;
1825 * copy over all old values to the new ldb_message
1827 ret = ldb_msg_add_empty(msg, "repsFrom", 0, &nrf_el);
1828 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
1829 *nrf_el = *orf_el;
1833 * if we haven't found an old repsFrom value for the current source dsa
1834 * we'll add a new value
1836 if (!nrf_value) {
1837 struct ldb_val zero_value;
1838 ZERO_STRUCT(zero_value);
1839 ret = ldb_msg_add_value(msg, "repsFrom", &zero_value, &nrf_el);
1840 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
1842 nrf_value = &nrf_el->values[nrf_el->num_values - 1];
1845 /* we now fill the value which is already attached to ldb_message */
1846 ndr_err = ndr_push_struct_blob(nrf_value, msg,
1847 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
1848 &nrf,
1849 (ndr_push_flags_fn_t)ndr_push_repsFromToBlob);
1850 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1851 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
1852 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
1856 * the ldb_message_element for the attribute, has all the old values and the new one
1857 * so we'll replace the whole attribute with all values
1859 nrf_el->flags = LDB_FLAG_MOD_REPLACE;
1861 if (DEBUGLVL(4)) {
1862 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_MODIFY, msg);
1863 DEBUG(4, ("DRS replication uptodate modify message:\n%s\n", s));
1864 talloc_free(s);
1867 /* prepare the ldb_modify() request */
1868 ret = ldb_build_mod_req(&change_req,
1869 ldb,
1871 msg,
1872 ar->controls,
1874 replmd_replicated_uptodate_modify_callback,
1875 ar->req);
1876 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
1878 return ldb_next_request(ar->module, change_req);
1881 static int replmd_replicated_uptodate_search_callback(struct ldb_request *req,
1882 struct ldb_reply *ares)
1884 struct replmd_replicated_request *ar = talloc_get_type(req->context,
1885 struct replmd_replicated_request);
1886 int ret;
1888 if (!ares) {
1889 return ldb_module_done(ar->req, NULL, NULL,
1890 LDB_ERR_OPERATIONS_ERROR);
1892 if (ares->error != LDB_SUCCESS &&
1893 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
1894 return ldb_module_done(ar->req, ares->controls,
1895 ares->response, ares->error);
1898 switch (ares->type) {
1899 case LDB_REPLY_ENTRY:
1900 ar->search_msg = talloc_steal(ar, ares->message);
1901 break;
1903 case LDB_REPLY_REFERRAL:
1904 /* we ignore referrals */
1905 break;
1907 case LDB_REPLY_DONE:
1908 if (ar->search_msg == NULL) {
1909 ret = replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
1910 } else {
1911 ret = replmd_replicated_uptodate_modify(ar);
1913 if (ret != LDB_SUCCESS) {
1914 return ldb_module_done(ar->req, NULL, NULL, ret);
1918 talloc_free(ares);
1919 return LDB_SUCCESS;
1923 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar)
1925 struct ldb_context *ldb;
1926 int ret;
1927 static const char *attrs[] = {
1928 "replUpToDateVector",
1929 "repsFrom",
1930 NULL
1932 struct ldb_request *search_req;
1934 ldb = ldb_module_get_ctx(ar->module);
1935 ar->search_msg = NULL;
1937 ret = ldb_build_search_req(&search_req,
1938 ldb,
1940 ar->objs->partition_dn,
1941 LDB_SCOPE_BASE,
1942 "(objectClass=*)",
1943 attrs,
1944 NULL,
1946 replmd_replicated_uptodate_search_callback,
1947 ar->req);
1948 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
1950 return ldb_next_request(ar->module, search_req);
1955 static int replmd_extended_replicated_objects(struct ldb_module *module, struct ldb_request *req)
1957 struct ldb_context *ldb;
1958 struct dsdb_extended_replicated_objects *objs;
1959 struct replmd_replicated_request *ar;
1960 struct ldb_control **ctrls;
1961 int ret, i;
1962 struct dsdb_control_current_partition *partition_ctrl;
1963 struct replmd_private *replmd_private =
1964 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
1966 ldb = ldb_module_get_ctx(module);
1968 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_extended_replicated_objects\n");
1970 objs = talloc_get_type(req->op.extended.data, struct dsdb_extended_replicated_objects);
1971 if (!objs) {
1972 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: invalid extended data\n");
1973 return LDB_ERR_PROTOCOL_ERROR;
1976 if (objs->version != DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION) {
1977 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: extended data invalid version [%u != %u]\n",
1978 objs->version, DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION);
1979 return LDB_ERR_PROTOCOL_ERROR;
1982 ar = replmd_ctx_init(module, req);
1983 if (!ar)
1984 return LDB_ERR_OPERATIONS_ERROR;
1986 ar->objs = objs;
1987 ar->schema = dsdb_get_schema(ldb);
1988 if (!ar->schema) {
1989 ldb_debug_set(ldb, LDB_DEBUG_FATAL, "replmd_ctx_init: no loaded schema found\n");
1990 talloc_free(ar);
1991 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
1992 return LDB_ERR_CONSTRAINT_VIOLATION;
1995 ctrls = req->controls;
1997 if (req->controls) {
1998 req->controls = talloc_memdup(ar, req->controls,
1999 talloc_get_size(req->controls));
2000 if (!req->controls) return replmd_replicated_request_werror(ar, WERR_NOMEM);
2003 ret = ldb_request_add_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID, false, NULL);
2004 if (ret != LDB_SUCCESS) {
2005 return ret;
2009 add the DSDB_CONTROL_CURRENT_PARTITION_OID control. This
2010 tells the partition module which partition this request is
2011 directed at. That is important as the partition roots appear
2012 twice in the directory, once as mount points in the top
2013 level store, and once as the roots of each partition. The
2014 replication code wants to operate on the root of the
2015 partitions, not the top level mount points
2017 partition_ctrl = talloc(req, struct dsdb_control_current_partition);
2018 if (partition_ctrl == NULL) {
2019 if (!partition_ctrl) return replmd_replicated_request_werror(ar, WERR_NOMEM);
2021 partition_ctrl->version = DSDB_CONTROL_CURRENT_PARTITION_VERSION;
2022 partition_ctrl->dn = objs->partition_dn;
2024 ret = ldb_request_add_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID, false, partition_ctrl);
2025 if (ret != LDB_SUCCESS) {
2026 return ret;
2029 ar->controls = req->controls;
2030 req->controls = ctrls;
2032 DEBUG(4,("linked_attributes_count=%u\n", objs->linked_attributes_count));
2034 /* save away the linked attributes for the end of the
2035 transaction */
2036 for (i=0; i<ar->objs->linked_attributes_count; i++) {
2037 struct la_entry *la_entry;
2039 if (replmd_private->la_ctx == NULL) {
2040 replmd_private->la_ctx = talloc_new(replmd_private);
2042 la_entry = talloc(replmd_private->la_ctx, struct la_entry);
2043 if (la_entry == NULL) {
2044 ldb_oom(ldb);
2045 return LDB_ERR_OPERATIONS_ERROR;
2047 la_entry->la = talloc(la_entry, struct drsuapi_DsReplicaLinkedAttribute);
2048 if (la_entry->la == NULL) {
2049 talloc_free(la_entry);
2050 ldb_oom(ldb);
2051 return LDB_ERR_OPERATIONS_ERROR;
2053 *la_entry->la = ar->objs->linked_attributes[i];
2055 /* we need to steal the non-scalars so they stay
2056 around until the end of the transaction */
2057 talloc_steal(la_entry->la, la_entry->la->identifier);
2058 talloc_steal(la_entry->la, la_entry->la->value.blob);
2060 DLIST_ADD(replmd_private->la_list, la_entry);
2063 return replmd_replicated_apply_next(ar);
2067 process one linked attribute structure
2069 static int replmd_process_linked_attribute(struct ldb_module *module,
2070 struct la_entry *la_entry)
2072 struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
2073 struct ldb_context *ldb = ldb_module_get_ctx(module);
2074 struct drsuapi_DsReplicaObjectIdentifier3 target;
2075 struct ldb_message *msg;
2076 struct ldb_message_element *ret_el;
2077 TALLOC_CTX *tmp_ctx = talloc_new(la_entry);
2078 enum ndr_err_code ndr_err;
2079 char *target_dn;
2080 struct ldb_request *mod_req;
2081 int ret;
2082 const struct dsdb_attribute *attr;
2085 linked_attributes[0]:
2086 &objs->linked_attributes[i]: struct drsuapi_DsReplicaLinkedAttribute
2087 identifier : *
2088 identifier: struct drsuapi_DsReplicaObjectIdentifier
2089 __ndr_size : 0x0000003a (58)
2090 __ndr_size_sid : 0x00000000 (0)
2091 guid : 8e95b6a9-13dd-4158-89db-3220a5be5cc7
2092 sid : S-0-0
2093 __ndr_size_dn : 0x00000000 (0)
2094 dn : ''
2095 attid : DRSUAPI_ATTRIBUTE_member (0x1F)
2096 value: struct drsuapi_DsAttributeValue
2097 __ndr_size : 0x0000007e (126)
2098 blob : *
2099 blob : DATA_BLOB length=126
2100 flags : 0x00000001 (1)
2101 1: DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE
2102 originating_add_time : Wed Sep 2 22:20:01 2009 EST
2103 meta_data: struct drsuapi_DsReplicaMetaData
2104 version : 0x00000015 (21)
2105 originating_change_time : Wed Sep 2 23:39:07 2009 EST
2106 originating_invocation_id: 794640f3-18cf-40ee-a211-a93992b67a64
2107 originating_usn : 0x000000000001e19c (123292)
2108 &target: struct drsuapi_DsReplicaObjectIdentifier3
2109 __ndr_size : 0x0000007e (126)
2110 __ndr_size_sid : 0x0000001c (28)
2111 guid : 7639e594-db75-4086-b0d4-67890ae46031
2112 sid : S-1-5-21-2848215498-2472035911-1947525656-19924
2113 __ndr_size_dn : 0x00000022 (34)
2114 dn : 'CN=UOne,OU=TestOU,DC=vsofs8,DC=com'
2116 if (DEBUGLVL(4)) {
2117 NDR_PRINT_DEBUG(drsuapi_DsReplicaLinkedAttribute, la);
2120 /* decode the target of the link */
2121 ndr_err = ndr_pull_struct_blob(la->value.blob,
2122 tmp_ctx, lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
2123 &target,
2124 (ndr_pull_flags_fn_t)ndr_pull_drsuapi_DsReplicaObjectIdentifier3);
2125 if (ndr_err != NDR_ERR_SUCCESS) {
2126 DEBUG(0,("Unable to decode linked_attribute target\n"));
2127 dump_data(4, la->value.blob->data, la->value.blob->length);
2128 talloc_free(tmp_ctx);
2129 return LDB_ERR_OPERATIONS_ERROR;
2131 if (DEBUGLVL(4)) {
2132 NDR_PRINT_DEBUG(drsuapi_DsReplicaObjectIdentifier3, &target);
2135 /* construct a modify request for this attribute change */
2136 msg = ldb_msg_new(tmp_ctx);
2137 if (!msg) {
2138 ldb_oom(ldb);
2139 talloc_free(tmp_ctx);
2140 return LDB_ERR_OPERATIONS_ERROR;
2143 ret = dsdb_find_dn_by_guid(ldb, tmp_ctx,
2144 GUID_string(tmp_ctx, &la->identifier->guid), &msg->dn);
2145 if (ret != LDB_SUCCESS) {
2146 talloc_free(tmp_ctx);
2147 return ret;
2150 /* find the attribute being modified */
2151 attr = dsdb_attribute_by_attributeID_id(dsdb_get_schema(ldb), la->attid);
2152 if (attr == NULL) {
2153 DEBUG(0, (__location__ ": Unable to find attributeID 0x%x\n", la->attid));
2154 talloc_free(tmp_ctx);
2155 return LDB_ERR_OPERATIONS_ERROR;
2158 if (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE) {
2159 ret = ldb_msg_add_empty(msg, attr->lDAPDisplayName,
2160 LDB_FLAG_MOD_ADD, &ret_el);
2161 } else {
2162 ret = ldb_msg_add_empty(msg, attr->lDAPDisplayName,
2163 LDB_FLAG_MOD_DELETE, &ret_el);
2165 if (ret != LDB_SUCCESS) {
2166 talloc_free(tmp_ctx);
2167 return ret;
2169 /* we allocate two entries here, in case we need a remove/add
2170 pair */
2171 ret_el->values = talloc_array(msg, struct ldb_val, 2);
2172 if (!ret_el->values) {
2173 ldb_oom(ldb);
2174 talloc_free(tmp_ctx);
2175 return LDB_ERR_OPERATIONS_ERROR;
2177 ret_el->num_values = 1;
2179 target_dn = talloc_asprintf(tmp_ctx, "<GUID=%s>;<SID=%s>;%s",
2180 GUID_string(tmp_ctx, &target.guid),
2181 dom_sid_string(tmp_ctx, &target.sid),
2182 target.dn);
2183 if (target_dn == NULL) {
2184 ldb_oom(ldb);
2185 talloc_free(tmp_ctx);
2186 return LDB_ERR_OPERATIONS_ERROR;
2188 ret_el->values[0] = data_blob_string_const(target_dn);
2190 ret = ldb_build_mod_req(&mod_req, ldb, tmp_ctx,
2191 msg,
2192 NULL,
2193 NULL,
2194 ldb_op_default_callback,
2195 NULL);
2196 if (ret != LDB_SUCCESS) {
2197 talloc_free(tmp_ctx);
2198 return ret;
2200 talloc_steal(mod_req, msg);
2202 if (DEBUGLVL(4)) {
2203 DEBUG(4,("Applying DRS linked attribute change:\n%s\n",
2204 ldb_ldif_message_string(ldb, tmp_ctx, LDB_CHANGETYPE_MODIFY, msg)));
2207 /* Run the new request */
2208 ret = ldb_next_request(module, mod_req);
2210 /* we need to wait for this to finish, as we are being called
2211 from the synchronous end_transaction hook of this module */
2212 if (ret == LDB_SUCCESS) {
2213 ret = ldb_wait(mod_req->handle, LDB_WAIT_ALL);
2216 if (ret == LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS) {
2217 /* the link destination exists, we need to update it
2218 * by deleting the old one for the same DN then adding
2219 * the new one */
2220 msg->elements = talloc_realloc(msg, msg->elements,
2221 struct ldb_message_element,
2222 msg->num_elements+1);
2223 if (msg->elements == NULL) {
2224 ldb_oom(ldb);
2225 talloc_free(tmp_ctx);
2226 return LDB_ERR_OPERATIONS_ERROR;
2228 /* this relies on the backend matching the old entry
2229 only by the DN portion of the extended DN */
2230 msg->elements[1] = msg->elements[0];
2231 msg->elements[0].flags = LDB_FLAG_MOD_DELETE;
2232 msg->num_elements++;
2234 ret = ldb_build_mod_req(&mod_req, ldb, tmp_ctx,
2235 msg,
2236 NULL,
2237 NULL,
2238 ldb_op_default_callback,
2239 NULL);
2240 if (ret != LDB_SUCCESS) {
2241 talloc_free(tmp_ctx);
2242 return ret;
2245 /* Run the new request */
2246 ret = ldb_next_request(module, mod_req);
2248 if (ret == LDB_SUCCESS) {
2249 ret = ldb_wait(mod_req->handle, LDB_WAIT_ALL);
2253 if (ret != LDB_SUCCESS) {
2254 ldb_debug(ldb, LDB_DEBUG_WARNING, "Failed to apply linked attribute change '%s' %s\n",
2255 ldb_errstring(ldb),
2256 ldb_ldif_message_string(ldb, tmp_ctx, LDB_CHANGETYPE_MODIFY, msg));
2257 ret = LDB_SUCCESS;
2260 talloc_free(tmp_ctx);
2262 return ret;
2265 static int replmd_extended(struct ldb_module *module, struct ldb_request *req)
2267 if (strcmp(req->op.extended.oid, DSDB_EXTENDED_REPLICATED_OBJECTS_OID) == 0) {
2268 return replmd_extended_replicated_objects(module, req);
2271 return ldb_next_request(module, req);
2276 we hook into the transaction operations to allow us to
2277 perform the linked attribute updates at the end of the whole
2278 transaction. This allows a forward linked attribute to be created
2279 before the object is created. During a vampire, w2k8 sends us linked
2280 attributes before the objects they are part of.
2282 static int replmd_start_transaction(struct ldb_module *module)
2284 /* create our private structure for this transaction */
2285 int i;
2286 struct replmd_private *replmd_private = talloc_get_type(ldb_module_get_private(module),
2287 struct replmd_private);
2288 talloc_free(replmd_private->la_ctx);
2289 replmd_private->la_list = NULL;
2290 replmd_private->la_ctx = NULL;
2292 for (i=0; i<replmd_private->num_ncs; i++) {
2293 replmd_private->ncs[i].mod_usn = 0;
2296 return ldb_next_start_trans(module);
2300 on prepare commit we loop over our queued la_context structures and
2301 apply each of them
2303 static int replmd_prepare_commit(struct ldb_module *module)
2305 struct replmd_private *replmd_private =
2306 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
2307 struct la_entry *la, *prev;
2308 int ret;
2310 /* walk the list backwards, to do the first entry first, as we
2311 * added the entries with DLIST_ADD() which puts them at the
2312 * start of the list */
2313 for (la = replmd_private->la_list; la && la->next; la=la->next) ;
2315 for (; la; la=prev) {
2316 prev = la->prev;
2317 DLIST_REMOVE(replmd_private->la_list, la);
2318 ret = replmd_process_linked_attribute(module, la);
2319 if (ret != LDB_SUCCESS) {
2320 return ret;
2324 talloc_free(replmd_private->la_ctx);
2325 replmd_private->la_list = NULL;
2326 replmd_private->la_ctx = NULL;
2328 /* possibly change @REPLCHANGED */
2329 ret = replmd_notify_store(module);
2330 if (ret != LDB_SUCCESS) {
2331 return ret;
2334 return ldb_next_prepare_commit(module);
2337 static int replmd_del_transaction(struct ldb_module *module)
2339 struct replmd_private *replmd_private =
2340 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
2341 talloc_free(replmd_private->la_ctx);
2342 replmd_private->la_list = NULL;
2343 replmd_private->la_ctx = NULL;
2344 return ldb_next_del_trans(module);
2348 _PUBLIC_ const struct ldb_module_ops ldb_repl_meta_data_module_ops = {
2349 .name = "repl_meta_data",
2350 .init_context = replmd_init,
2351 .add = replmd_add,
2352 .modify = replmd_modify,
2353 .rename = replmd_rename,
2354 .extended = replmd_extended,
2355 .start_transaction = replmd_start_transaction,
2356 .prepare_commit = replmd_prepare_commit,
2357 .del_transaction = replmd_del_transaction,