s4/rodc: Support read-only database
[Samba/gebeck_regimport.git] / source4 / dsdb / samdb / ldb_modules / repl_meta_data.c
blob374467cb94dc2c7b155b711c3f582e3ebd5b09db
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"
48 #include "dsdb/samdb/ldb_modules/util.h"
49 #include "lib/util/binsearch.h"
50 #include "libcli/security/security.h"
51 #include "lib/util/tsort.h"
53 #define W2K3_LINKED_ATTRIBUTES 1
55 struct replmd_private {
56 TALLOC_CTX *la_ctx;
57 struct la_entry *la_list;
58 TALLOC_CTX *bl_ctx;
59 struct la_backlink *la_backlinks;
60 struct nc_entry {
61 struct nc_entry *prev, *next;
62 struct ldb_dn *dn;
63 uint64_t mod_usn;
64 uint64_t mod_usn_urgent;
65 } *ncs;
68 struct la_entry {
69 struct la_entry *next, *prev;
70 struct drsuapi_DsReplicaLinkedAttribute *la;
73 struct replmd_replicated_request {
74 struct ldb_module *module;
75 struct ldb_request *req;
77 const struct dsdb_schema *schema;
79 /* the controls we pass down */
80 struct ldb_control **controls;
82 /* details for the mode where we apply a bunch of inbound replication meessages */
83 bool apply_mode;
84 uint32_t index_current;
85 struct dsdb_extended_replicated_objects *objs;
87 struct ldb_message *search_msg;
89 uint64_t seq_num;
90 bool is_urgent;
93 enum urgent_situation {
94 REPL_URGENT_ON_CREATE = 1,
95 REPL_URGENT_ON_UPDATE = 2,
96 REPL_URGENT_ON_DELETE = 4
100 static const struct {
101 const char *update_name;
102 enum urgent_situation repl_situation;
103 } urgent_objects[] = {
104 {"nTDSDSA", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE)},
105 {"crossRef", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE)},
106 {"attributeSchema", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
107 {"classSchema", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
108 {"secret", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
109 {"rIDManager", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
110 {NULL, 0}
113 /* Attributes looked for when updating or deleting, to check for a urgent replication needed */
114 static const char *urgent_attrs[] = {
115 "lockoutTime",
116 "pwdLastSet",
117 "userAccountControl",
118 NULL
122 static bool replmd_check_urgent_objectclass(const struct ldb_message_element *objectclass_el,
123 enum urgent_situation situation)
125 int i, j;
126 for (i=0; urgent_objects[i].update_name; i++) {
128 if ((situation & urgent_objects[i].repl_situation) == 0) {
129 continue;
132 for (j=0; j<objectclass_el->num_values; j++) {
133 const struct ldb_val *v = &objectclass_el->values[j];
134 if (ldb_attr_cmp((const char *)v->data, urgent_objects[i].update_name) == 0) {
135 return true;
139 return false;
142 static bool replmd_check_urgent_attribute(const struct ldb_message_element *el)
144 if (ldb_attr_in_list(urgent_attrs, el->name)) {
145 return true;
147 return false;
151 static int replmd_replicated_apply_next(struct replmd_replicated_request *ar);
154 initialise the module
155 allocate the private structure and build the list
156 of partition DNs for use by replmd_notify()
158 static int replmd_init(struct ldb_module *module)
160 struct replmd_private *replmd_private;
161 struct ldb_context *ldb = ldb_module_get_ctx(module);
163 replmd_private = talloc_zero(module, struct replmd_private);
164 if (replmd_private == NULL) {
165 ldb_oom(ldb);
166 return LDB_ERR_OPERATIONS_ERROR;
168 ldb_module_set_private(module, replmd_private);
170 return ldb_next_init(module);
174 cleanup our per-transaction contexts
176 static void replmd_txn_cleanup(struct replmd_private *replmd_private)
178 talloc_free(replmd_private->la_ctx);
179 replmd_private->la_list = NULL;
180 replmd_private->la_ctx = NULL;
182 talloc_free(replmd_private->bl_ctx);
183 replmd_private->la_backlinks = NULL;
184 replmd_private->bl_ctx = NULL;
188 struct la_backlink {
189 struct la_backlink *next, *prev;
190 const char *attr_name;
191 struct GUID forward_guid, target_guid;
192 bool active;
196 process a backlinks we accumulated during a transaction, adding and
197 deleting the backlinks from the target objects
199 static int replmd_process_backlink(struct ldb_module *module, struct la_backlink *bl)
201 struct ldb_dn *target_dn, *source_dn;
202 int ret;
203 struct ldb_context *ldb = ldb_module_get_ctx(module);
204 struct ldb_message *msg;
205 TALLOC_CTX *tmp_ctx = talloc_new(bl);
206 char *dn_string;
209 - find DN of target
210 - find DN of source
211 - construct ldb_message
212 - either an add or a delete
214 ret = dsdb_module_dn_by_guid(module, tmp_ctx, &bl->target_guid, &target_dn);
215 if (ret != LDB_SUCCESS) {
216 DEBUG(2,(__location__ ": WARNING: Failed to find target DN for linked attribute with GUID %s\n",
217 GUID_string(bl, &bl->target_guid)));
218 return LDB_SUCCESS;
221 ret = dsdb_module_dn_by_guid(module, tmp_ctx, &bl->forward_guid, &source_dn);
222 if (ret != LDB_SUCCESS) {
223 ldb_asprintf_errstring(ldb, "Failed to find source DN for linked attribute with GUID %s\n",
224 GUID_string(bl, &bl->forward_guid));
225 talloc_free(tmp_ctx);
226 return ret;
229 msg = ldb_msg_new(tmp_ctx);
230 if (msg == NULL) {
231 ldb_module_oom(module);
232 talloc_free(tmp_ctx);
233 return LDB_ERR_OPERATIONS_ERROR;
236 /* construct a ldb_message for adding/deleting the backlink */
237 msg->dn = target_dn;
238 dn_string = ldb_dn_get_extended_linearized(tmp_ctx, source_dn, 1);
239 if (!dn_string) {
240 ldb_module_oom(module);
241 talloc_free(tmp_ctx);
242 return LDB_ERR_OPERATIONS_ERROR;
244 ret = ldb_msg_add_steal_string(msg, bl->attr_name, dn_string);
245 if (ret != LDB_SUCCESS) {
246 talloc_free(tmp_ctx);
247 return ret;
249 msg->elements[0].flags = bl->active?LDB_FLAG_MOD_ADD:LDB_FLAG_MOD_DELETE;
251 ret = dsdb_module_modify(module, msg, 0);
252 if (ret != LDB_SUCCESS) {
253 ldb_asprintf_errstring(ldb, "Failed to %s backlink from %s to %s - %s",
254 bl->active?"add":"remove",
255 ldb_dn_get_linearized(source_dn),
256 ldb_dn_get_linearized(target_dn),
257 ldb_errstring(ldb));
258 talloc_free(tmp_ctx);
259 return ret;
261 talloc_free(tmp_ctx);
262 return ret;
266 add a backlink to the list of backlinks to add/delete in the prepare
267 commit
269 static int replmd_add_backlink(struct ldb_module *module, const struct dsdb_schema *schema,
270 struct GUID *forward_guid, struct GUID *target_guid,
271 bool active, const struct dsdb_attribute *schema_attr, bool immediate)
273 const struct dsdb_attribute *target_attr;
274 struct la_backlink *bl;
275 struct replmd_private *replmd_private =
276 talloc_get_type_abort(ldb_module_get_private(module), struct replmd_private);
278 target_attr = dsdb_attribute_by_linkID(schema, schema_attr->linkID ^ 1);
279 if (!target_attr) {
281 * windows 2003 has a broken schema where the
282 * definition of msDS-IsDomainFor is missing (which is
283 * supposed to be the backlink of the
284 * msDS-HasDomainNCs attribute
286 return LDB_SUCCESS;
289 /* see if its already in the list */
290 for (bl=replmd_private->la_backlinks; bl; bl=bl->next) {
291 if (GUID_equal(forward_guid, &bl->forward_guid) &&
292 GUID_equal(target_guid, &bl->target_guid) &&
293 (target_attr->lDAPDisplayName == bl->attr_name ||
294 strcmp(target_attr->lDAPDisplayName, bl->attr_name) == 0)) {
295 break;
299 if (bl) {
300 /* we found an existing one */
301 if (bl->active == active) {
302 return LDB_SUCCESS;
304 DLIST_REMOVE(replmd_private->la_backlinks, bl);
305 talloc_free(bl);
306 return LDB_SUCCESS;
309 if (replmd_private->bl_ctx == NULL) {
310 replmd_private->bl_ctx = talloc_new(replmd_private);
311 if (replmd_private->bl_ctx == NULL) {
312 ldb_module_oom(module);
313 return LDB_ERR_OPERATIONS_ERROR;
317 /* its a new one */
318 bl = talloc(replmd_private->bl_ctx, struct la_backlink);
319 if (bl == NULL) {
320 ldb_module_oom(module);
321 return LDB_ERR_OPERATIONS_ERROR;
324 /* Ensure the schema does not go away before the bl->attr_name is used */
325 if (!talloc_reference(bl, schema)) {
326 talloc_free(bl);
327 ldb_module_oom(module);
328 return LDB_ERR_OPERATIONS_ERROR;
331 bl->attr_name = target_attr->lDAPDisplayName;
332 bl->forward_guid = *forward_guid;
333 bl->target_guid = *target_guid;
334 bl->active = active;
336 /* the caller may ask for this backlink to be processed
337 immediately */
338 if (immediate) {
339 int ret = replmd_process_backlink(module, bl);
340 talloc_free(bl);
341 return ret;
344 DLIST_ADD(replmd_private->la_backlinks, bl);
346 return LDB_SUCCESS;
351 * Callback for most write operations in this module:
353 * notify the repl task that a object has changed. The notifies are
354 * gathered up in the replmd_private structure then written to the
355 * @REPLCHANGED object in each partition during the prepare_commit
357 static int replmd_op_callback(struct ldb_request *req, struct ldb_reply *ares)
359 int ret;
360 struct replmd_replicated_request *ac =
361 talloc_get_type_abort(req->context, struct replmd_replicated_request);
362 struct replmd_private *replmd_private =
363 talloc_get_type_abort(ldb_module_get_private(ac->module), struct replmd_private);
364 struct nc_entry *modified_partition;
365 struct ldb_control *partition_ctrl;
366 const struct dsdb_control_current_partition *partition;
368 struct ldb_control **controls;
370 partition_ctrl = ldb_reply_get_control(ares, DSDB_CONTROL_CURRENT_PARTITION_OID);
372 /* Remove the 'partition' control from what we pass up the chain */
373 controls = controls_except_specified(ares->controls, ares, partition_ctrl);
375 if (ares->error != LDB_SUCCESS) {
376 return ldb_module_done(ac->req, controls,
377 ares->response, ares->error);
380 if (ares->type != LDB_REPLY_DONE) {
381 ldb_set_errstring(ldb_module_get_ctx(ac->module), "Invalid reply type for notify\n!");
382 return ldb_module_done(ac->req, NULL,
383 NULL, LDB_ERR_OPERATIONS_ERROR);
386 if (!partition_ctrl) {
387 ldb_set_errstring(ldb_module_get_ctx(ac->module),"No partition control on reply");
388 return ldb_module_done(ac->req, NULL,
389 NULL, LDB_ERR_OPERATIONS_ERROR);
392 partition = talloc_get_type_abort(partition_ctrl->data,
393 struct dsdb_control_current_partition);
395 if (ac->seq_num > 0) {
396 for (modified_partition = replmd_private->ncs; modified_partition;
397 modified_partition = modified_partition->next) {
398 if (ldb_dn_compare(modified_partition->dn, partition->dn) == 0) {
399 break;
403 if (modified_partition == NULL) {
404 modified_partition = talloc_zero(replmd_private, struct nc_entry);
405 if (!modified_partition) {
406 ldb_oom(ldb_module_get_ctx(ac->module));
407 return ldb_module_done(ac->req, NULL,
408 NULL, LDB_ERR_OPERATIONS_ERROR);
410 modified_partition->dn = ldb_dn_copy(modified_partition, partition->dn);
411 if (!modified_partition->dn) {
412 ldb_oom(ldb_module_get_ctx(ac->module));
413 return ldb_module_done(ac->req, NULL,
414 NULL, LDB_ERR_OPERATIONS_ERROR);
416 DLIST_ADD(replmd_private->ncs, modified_partition);
419 if (ac->seq_num > modified_partition->mod_usn) {
420 modified_partition->mod_usn = ac->seq_num;
421 if (ac->is_urgent) {
422 modified_partition->mod_usn_urgent = ac->seq_num;
427 if (ac->apply_mode) {
428 talloc_free(ares);
429 ac->index_current++;
431 ret = replmd_replicated_apply_next(ac);
432 if (ret != LDB_SUCCESS) {
433 return ldb_module_done(ac->req, NULL, NULL, ret);
435 return ret;
436 } else {
437 /* free the partition control container here, for the
438 * common path. Other cases will have it cleaned up
439 * eventually with the ares */
440 talloc_free(partition_ctrl);
441 return ldb_module_done(ac->req,
442 controls_except_specified(controls, ares, partition_ctrl),
443 ares->response, LDB_SUCCESS);
449 * update a @REPLCHANGED record in each partition if there have been
450 * any writes of replicated data in the partition
452 static int replmd_notify_store(struct ldb_module *module)
454 struct replmd_private *replmd_private =
455 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
457 while (replmd_private->ncs) {
458 int ret;
459 struct nc_entry *modified_partition = replmd_private->ncs;
461 ret = dsdb_module_save_partition_usn(module, modified_partition->dn,
462 modified_partition->mod_usn,
463 modified_partition->mod_usn_urgent);
464 if (ret != LDB_SUCCESS) {
465 DEBUG(0,(__location__ ": Failed to save partition uSN for %s\n",
466 ldb_dn_get_linearized(modified_partition->dn)));
467 return ret;
469 DLIST_REMOVE(replmd_private->ncs, modified_partition);
470 talloc_free(modified_partition);
473 return LDB_SUCCESS;
478 created a replmd_replicated_request context
480 static struct replmd_replicated_request *replmd_ctx_init(struct ldb_module *module,
481 struct ldb_request *req)
483 struct ldb_context *ldb;
484 struct replmd_replicated_request *ac;
486 ldb = ldb_module_get_ctx(module);
488 ac = talloc_zero(req, struct replmd_replicated_request);
489 if (ac == NULL) {
490 ldb_oom(ldb);
491 return NULL;
494 ac->module = module;
495 ac->req = req;
497 ac->schema = dsdb_get_schema(ldb, ac);
498 if (!ac->schema) {
499 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
500 "replmd_modify: no dsdb_schema loaded");
501 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
502 return NULL;
505 return ac;
509 add a time element to a record
511 static int add_time_element(struct ldb_message *msg, const char *attr, time_t t)
513 struct ldb_message_element *el;
514 char *s;
516 if (ldb_msg_find_element(msg, attr) != NULL) {
517 return LDB_SUCCESS;
520 s = ldb_timestring(msg, t);
521 if (s == NULL) {
522 return LDB_ERR_OPERATIONS_ERROR;
525 if (ldb_msg_add_string(msg, attr, s) != LDB_SUCCESS) {
526 return LDB_ERR_OPERATIONS_ERROR;
529 el = ldb_msg_find_element(msg, attr);
530 /* always set as replace. This works because on add ops, the flag
531 is ignored */
532 el->flags = LDB_FLAG_MOD_REPLACE;
534 return LDB_SUCCESS;
538 add a uint64_t element to a record
540 static int add_uint64_element(struct ldb_message *msg, const char *attr, uint64_t v)
542 struct ldb_message_element *el;
544 if (ldb_msg_find_element(msg, attr) != NULL) {
545 return LDB_SUCCESS;
548 if (ldb_msg_add_fmt(msg, attr, "%llu", (unsigned long long)v) != LDB_SUCCESS) {
549 return LDB_ERR_OPERATIONS_ERROR;
552 el = ldb_msg_find_element(msg, attr);
553 /* always set as replace. This works because on add ops, the flag
554 is ignored */
555 el->flags = LDB_FLAG_MOD_REPLACE;
557 return LDB_SUCCESS;
560 static int replmd_replPropertyMetaData1_attid_sort(const struct replPropertyMetaData1 *m1,
561 const struct replPropertyMetaData1 *m2,
562 const uint32_t *rdn_attid)
564 if (m1->attid == m2->attid) {
565 return 0;
569 * the rdn attribute should be at the end!
570 * so we need to return a value greater than zero
571 * which means m1 is greater than m2
573 if (m1->attid == *rdn_attid) {
574 return 1;
578 * the rdn attribute should be at the end!
579 * so we need to return a value less than zero
580 * which means m2 is greater than m1
582 if (m2->attid == *rdn_attid) {
583 return -1;
586 return m1->attid > m2->attid ? 1 : -1;
589 static int replmd_replPropertyMetaDataCtr1_sort(struct replPropertyMetaDataCtr1 *ctr1,
590 const struct dsdb_schema *schema,
591 struct ldb_dn *dn)
593 const char *rdn_name;
594 const struct dsdb_attribute *rdn_sa;
596 rdn_name = ldb_dn_get_rdn_name(dn);
597 if (!rdn_name) {
598 DEBUG(0,(__location__ ": No rDN for %s?\n", ldb_dn_get_linearized(dn)));
599 return LDB_ERR_OPERATIONS_ERROR;
602 rdn_sa = dsdb_attribute_by_lDAPDisplayName(schema, rdn_name);
603 if (rdn_sa == NULL) {
604 DEBUG(0,(__location__ ": No sa found for rDN %s for %s\n", rdn_name, ldb_dn_get_linearized(dn)));
605 return LDB_ERR_OPERATIONS_ERROR;
608 DEBUG(6,("Sorting rpmd with attid exception %u rDN=%s DN=%s\n",
609 rdn_sa->attributeID_id, rdn_name, ldb_dn_get_linearized(dn)));
611 LDB_TYPESAFE_QSORT(ctr1->array, ctr1->count, &rdn_sa->attributeID_id, replmd_replPropertyMetaData1_attid_sort);
613 return LDB_SUCCESS;
616 static int replmd_ldb_message_element_attid_sort(const struct ldb_message_element *e1,
617 const struct ldb_message_element *e2,
618 const struct dsdb_schema *schema)
620 const struct dsdb_attribute *a1;
621 const struct dsdb_attribute *a2;
624 * TODO: make this faster by caching the dsdb_attribute pointer
625 * on the ldb_messag_element
628 a1 = dsdb_attribute_by_lDAPDisplayName(schema, e1->name);
629 a2 = dsdb_attribute_by_lDAPDisplayName(schema, e2->name);
632 * TODO: remove this check, we should rely on e1 and e2 having valid attribute names
633 * in the schema
635 if (!a1 || !a2) {
636 return strcasecmp(e1->name, e2->name);
638 if (a1->attributeID_id == a2->attributeID_id) {
639 return 0;
641 return a1->attributeID_id > a2->attributeID_id ? 1 : -1;
644 static void replmd_ldb_message_sort(struct ldb_message *msg,
645 const struct dsdb_schema *schema)
647 LDB_TYPESAFE_QSORT(msg->elements, msg->num_elements, schema, replmd_ldb_message_element_attid_sort);
650 static int replmd_build_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
651 const struct GUID *invocation_id, uint64_t seq_num,
652 uint64_t local_usn, NTTIME nttime, uint32_t version, bool deleted);
656 fix up linked attributes in replmd_add.
657 This involves setting up the right meta-data in extended DN
658 components, and creating backlinks to the object
660 static int replmd_add_fix_la(struct ldb_module *module, struct ldb_message_element *el,
661 uint64_t seq_num, const struct GUID *invocationId, time_t t,
662 struct GUID *guid, const struct dsdb_attribute *sa)
664 unsigned int i;
665 TALLOC_CTX *tmp_ctx = talloc_new(el->values);
666 struct ldb_context *ldb = ldb_module_get_ctx(module);
668 /* We will take a reference to the schema in replmd_add_backlink */
669 const struct dsdb_schema *schema = dsdb_get_schema(ldb, NULL);
670 NTTIME now;
672 unix_to_nt_time(&now, t);
674 for (i=0; i<el->num_values; i++) {
675 struct ldb_val *v = &el->values[i];
676 struct dsdb_dn *dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, v, sa->syntax->ldap_oid);
677 struct GUID target_guid;
678 NTSTATUS status;
679 int ret;
681 /* note that the DN already has the extended
682 components from the extended_dn_store module */
683 status = dsdb_get_extended_dn_guid(dsdb_dn->dn, &target_guid, "GUID");
684 if (!NT_STATUS_IS_OK(status) || GUID_all_zero(&target_guid)) {
685 ret = dsdb_module_guid_by_dn(module, dsdb_dn->dn, &target_guid);
686 if (ret != LDB_SUCCESS) {
687 talloc_free(tmp_ctx);
688 return ret;
690 ret = dsdb_set_extended_dn_guid(dsdb_dn->dn, &target_guid, "GUID");
691 if (ret != LDB_SUCCESS) {
692 talloc_free(tmp_ctx);
693 return ret;
697 ret = replmd_build_la_val(el->values, v, dsdb_dn, invocationId,
698 seq_num, seq_num, now, 0, false);
699 if (ret != LDB_SUCCESS) {
700 talloc_free(tmp_ctx);
701 return ret;
704 ret = replmd_add_backlink(module, schema, guid, &target_guid, true, sa, false);
705 if (ret != LDB_SUCCESS) {
706 talloc_free(tmp_ctx);
707 return ret;
711 talloc_free(tmp_ctx);
712 return LDB_SUCCESS;
717 intercept add requests
719 static int replmd_add(struct ldb_module *module, struct ldb_request *req)
721 struct ldb_context *ldb;
722 struct ldb_control *control;
723 struct replmd_replicated_request *ac;
724 enum ndr_err_code ndr_err;
725 struct ldb_request *down_req;
726 struct ldb_message *msg;
727 const DATA_BLOB *guid_blob;
728 struct GUID guid;
729 struct replPropertyMetaDataBlob nmd;
730 struct ldb_val nmd_value;
731 const struct GUID *our_invocation_id;
732 time_t t = time(NULL);
733 NTTIME now;
734 char *time_str;
735 int ret;
736 unsigned int i;
737 uint32_t ni=0;
738 bool allow_add_guid = false;
739 bool remove_current_guid = false;
740 bool is_urgent = false;
741 struct ldb_message_element *objectclass_el;
743 /* check if there's a show relax control (used by provision to say 'I know what I'm doing') */
744 control = ldb_request_get_control(req, LDB_CONTROL_RELAX_OID);
745 if (control) {
746 allow_add_guid = true;
749 /* do not manipulate our control entries */
750 if (ldb_dn_is_special(req->op.add.message->dn)) {
751 return ldb_next_request(module, req);
754 ldb = ldb_module_get_ctx(module);
756 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_add\n");
758 ac = replmd_ctx_init(module, req);
759 if (!ac) {
760 return LDB_ERR_OPERATIONS_ERROR;
763 guid_blob = ldb_msg_find_ldb_val(req->op.add.message, "objectGUID");
764 if ( guid_blob != NULL ) {
765 if( !allow_add_guid ) {
766 ldb_debug_set(ldb, LDB_DEBUG_ERROR,
767 "replmd_add: it's not allowed to add an object with objectGUID\n");
768 talloc_free(ac);
769 return LDB_ERR_UNWILLING_TO_PERFORM;
770 } else {
771 NTSTATUS status = GUID_from_data_blob(guid_blob,&guid);
772 if ( !NT_STATUS_IS_OK(status)) {
773 ldb_debug_set(ldb, LDB_DEBUG_ERROR,
774 "replmd_add: Unable to parse as a GUID the attribute objectGUID\n");
775 talloc_free(ac);
776 return LDB_ERR_UNWILLING_TO_PERFORM;
778 /* we remove this attribute as it can be a string and will not be treated
779 correctly and then we will readd it latter on in the good format*/
780 remove_current_guid = true;
782 } else {
783 /* a new GUID */
784 guid = GUID_random();
787 /* Get a sequence number from the backend */
788 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ac->seq_num);
789 if (ret != LDB_SUCCESS) {
790 talloc_free(ac);
791 return ret;
794 /* get our invocationId */
795 our_invocation_id = samdb_ntds_invocation_id(ldb);
796 if (!our_invocation_id) {
797 ldb_debug_set(ldb, LDB_DEBUG_ERROR,
798 "replmd_add: unable to find invocationId\n");
799 talloc_free(ac);
800 return LDB_ERR_OPERATIONS_ERROR;
803 /* we have to copy the message as the caller might have it as a const */
804 msg = ldb_msg_copy_shallow(ac, req->op.add.message);
805 if (msg == NULL) {
806 ldb_oom(ldb);
807 talloc_free(ac);
808 return LDB_ERR_OPERATIONS_ERROR;
811 /* generated times */
812 unix_to_nt_time(&now, t);
813 time_str = ldb_timestring(msg, t);
814 if (!time_str) {
815 ldb_oom(ldb);
816 talloc_free(ac);
817 return LDB_ERR_OPERATIONS_ERROR;
819 if (remove_current_guid) {
820 ldb_msg_remove_attr(msg,"objectGUID");
824 * remove autogenerated attributes
826 ldb_msg_remove_attr(msg, "whenCreated");
827 ldb_msg_remove_attr(msg, "whenChanged");
828 ldb_msg_remove_attr(msg, "uSNCreated");
829 ldb_msg_remove_attr(msg, "uSNChanged");
830 ldb_msg_remove_attr(msg, "replPropertyMetaData");
833 * readd replicated attributes
835 ret = ldb_msg_add_string(msg, "whenCreated", time_str);
836 if (ret != LDB_SUCCESS) {
837 ldb_oom(ldb);
838 talloc_free(ac);
839 return ret;
842 /* build the replication meta_data */
843 ZERO_STRUCT(nmd);
844 nmd.version = 1;
845 nmd.ctr.ctr1.count = msg->num_elements;
846 nmd.ctr.ctr1.array = talloc_array(msg,
847 struct replPropertyMetaData1,
848 nmd.ctr.ctr1.count);
849 if (!nmd.ctr.ctr1.array) {
850 ldb_oom(ldb);
851 talloc_free(ac);
852 return LDB_ERR_OPERATIONS_ERROR;
855 for (i=0; i < msg->num_elements; i++) {
856 struct ldb_message_element *e = &msg->elements[i];
857 struct replPropertyMetaData1 *m = &nmd.ctr.ctr1.array[ni];
858 const struct dsdb_attribute *sa;
860 if (e->name[0] == '@') continue;
862 sa = dsdb_attribute_by_lDAPDisplayName(ac->schema, e->name);
863 if (!sa) {
864 ldb_debug_set(ldb, LDB_DEBUG_ERROR,
865 "replmd_add: attribute '%s' not defined in schema\n",
866 e->name);
867 talloc_free(ac);
868 return LDB_ERR_NO_SUCH_ATTRIBUTE;
871 if ((sa->systemFlags & DS_FLAG_ATTR_NOT_REPLICATED) || (sa->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED)) {
872 /* if the attribute is not replicated (0x00000001)
873 * or constructed (0x00000004) it has no metadata
875 continue;
878 #if W2K3_LINKED_ATTRIBUTES
879 if (sa->linkID != 0 && dsdb_functional_level(ldb) > DS_DOMAIN_FUNCTION_2000) {
880 ret = replmd_add_fix_la(module, e, ac->seq_num, our_invocation_id, t, &guid, sa);
881 if (ret != LDB_SUCCESS) {
882 talloc_free(ac);
883 return ret;
885 /* linked attributes are not stored in
886 replPropertyMetaData in FL above w2k */
887 continue;
889 #endif
891 m->attid = sa->attributeID_id;
892 m->version = 1;
893 m->originating_change_time = now;
894 m->originating_invocation_id = *our_invocation_id;
895 m->originating_usn = ac->seq_num;
896 m->local_usn = ac->seq_num;
897 ni++;
900 /* fix meta data count */
901 nmd.ctr.ctr1.count = ni;
904 * sort meta data array, and move the rdn attribute entry to the end
906 ret = replmd_replPropertyMetaDataCtr1_sort(&nmd.ctr.ctr1, ac->schema, msg->dn);
907 if (ret != LDB_SUCCESS) {
908 talloc_free(ac);
909 return ret;
912 /* generated NDR encoded values */
913 ndr_err = ndr_push_struct_blob(&nmd_value, msg,
914 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
915 &nmd,
916 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
917 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
918 ldb_oom(ldb);
919 talloc_free(ac);
920 return LDB_ERR_OPERATIONS_ERROR;
924 * add the autogenerated values
926 ret = dsdb_msg_add_guid(msg, &guid, "objectGUID");
927 if (ret != LDB_SUCCESS) {
928 ldb_oom(ldb);
929 talloc_free(ac);
930 return ret;
932 ret = ldb_msg_add_string(msg, "whenChanged", time_str);
933 if (ret != LDB_SUCCESS) {
934 ldb_oom(ldb);
935 talloc_free(ac);
936 return ret;
938 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", ac->seq_num);
939 if (ret != LDB_SUCCESS) {
940 ldb_oom(ldb);
941 talloc_free(ac);
942 return ret;
944 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ac->seq_num);
945 if (ret != LDB_SUCCESS) {
946 ldb_oom(ldb);
947 talloc_free(ac);
948 return ret;
950 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
951 if (ret != LDB_SUCCESS) {
952 ldb_oom(ldb);
953 talloc_free(ac);
954 return ret;
958 * sort the attributes by attid before storing the object
960 replmd_ldb_message_sort(msg, ac->schema);
962 objectclass_el = ldb_msg_find_element(msg, "objectClass");
963 is_urgent = replmd_check_urgent_objectclass(objectclass_el,
964 REPL_URGENT_ON_CREATE);
966 ac->is_urgent = is_urgent;
967 ret = ldb_build_add_req(&down_req, ldb, ac,
968 msg,
969 req->controls,
970 ac, replmd_op_callback,
971 req);
973 if (ret != LDB_SUCCESS) {
974 talloc_free(ac);
975 return ret;
978 /* mark the control done */
979 if (control) {
980 control->critical = 0;
983 /* go on with the call chain */
984 return ldb_next_request(module, down_req);
989 * update the replPropertyMetaData for one element
991 static int replmd_update_rpmd_element(struct ldb_context *ldb,
992 struct ldb_message *msg,
993 struct ldb_message_element *el,
994 struct ldb_message_element *old_el,
995 struct replPropertyMetaDataBlob *omd,
996 const struct dsdb_schema *schema,
997 uint64_t *seq_num,
998 const struct GUID *our_invocation_id,
999 NTTIME now)
1001 uint32_t i;
1002 const struct dsdb_attribute *a;
1003 struct replPropertyMetaData1 *md1;
1005 a = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
1006 if (a == NULL) {
1007 DEBUG(0,(__location__ ": Unable to find attribute %s in schema\n",
1008 el->name));
1009 return LDB_ERR_OPERATIONS_ERROR;
1012 if ((a->systemFlags & DS_FLAG_ATTR_NOT_REPLICATED) || (a->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED)) {
1013 return LDB_SUCCESS;
1016 /* if the attribute's value haven't changed then return LDB_SUCCESS */
1017 if (old_el != NULL && ldb_msg_element_compare(el, old_el) == 0) {
1018 return LDB_SUCCESS;
1021 for (i=0; i<omd->ctr.ctr1.count; i++) {
1022 if (a->attributeID_id == omd->ctr.ctr1.array[i].attid) break;
1025 #if W2K3_LINKED_ATTRIBUTES
1026 if (a->linkID != 0 && dsdb_functional_level(ldb) > DS_DOMAIN_FUNCTION_2000) {
1027 /* linked attributes are not stored in
1028 replPropertyMetaData in FL above w2k, but we do
1029 raise the seqnum for the object */
1030 if (*seq_num == 0 &&
1031 ldb_sequence_number(ldb, LDB_SEQ_NEXT, seq_num) != LDB_SUCCESS) {
1032 return LDB_ERR_OPERATIONS_ERROR;
1034 return LDB_SUCCESS;
1036 #endif
1038 if (i == omd->ctr.ctr1.count) {
1039 /* we need to add a new one */
1040 omd->ctr.ctr1.array = talloc_realloc(msg, omd->ctr.ctr1.array,
1041 struct replPropertyMetaData1, omd->ctr.ctr1.count+1);
1042 if (omd->ctr.ctr1.array == NULL) {
1043 ldb_oom(ldb);
1044 return LDB_ERR_OPERATIONS_ERROR;
1046 omd->ctr.ctr1.count++;
1047 ZERO_STRUCT(omd->ctr.ctr1.array[i]);
1050 /* Get a new sequence number from the backend. We only do this
1051 * if we have a change that requires a new
1052 * replPropertyMetaData element
1054 if (*seq_num == 0) {
1055 int ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, seq_num);
1056 if (ret != LDB_SUCCESS) {
1057 return LDB_ERR_OPERATIONS_ERROR;
1061 md1 = &omd->ctr.ctr1.array[i];
1062 md1->version++;
1063 md1->attid = a->attributeID_id;
1064 md1->originating_change_time = now;
1065 md1->originating_invocation_id = *our_invocation_id;
1066 md1->originating_usn = *seq_num;
1067 md1->local_usn = *seq_num;
1069 return LDB_SUCCESS;
1073 * update the replPropertyMetaData object each time we modify an
1074 * object. This is needed for DRS replication, as the merge on the
1075 * client is based on this object
1077 static int replmd_update_rpmd(struct ldb_module *module,
1078 const struct dsdb_schema *schema,
1079 struct ldb_request *req,
1080 struct ldb_message *msg, uint64_t *seq_num,
1081 time_t t,
1082 bool *is_urgent)
1084 const struct ldb_val *omd_value;
1085 enum ndr_err_code ndr_err;
1086 struct replPropertyMetaDataBlob omd;
1087 unsigned int i;
1088 NTTIME now;
1089 const struct GUID *our_invocation_id;
1090 int ret;
1091 const char *attrs[] = { "replPropertyMetaData", "*", NULL };
1092 struct ldb_result *res;
1093 struct ldb_context *ldb;
1094 struct ldb_message_element *objectclass_el;
1095 enum urgent_situation situation;
1096 bool rodc;
1098 ldb = ldb_module_get_ctx(module);
1100 our_invocation_id = samdb_ntds_invocation_id(ldb);
1101 if (!our_invocation_id) {
1102 /* this happens during an initial vampire while
1103 updating the schema */
1104 DEBUG(5,("No invocationID - skipping replPropertyMetaData update\n"));
1105 return LDB_SUCCESS;
1108 unix_to_nt_time(&now, t);
1110 /* search for the existing replPropertyMetaDataBlob. We need
1111 * to use REVEAL and ask for DNs in storage format to support
1112 * the check for values being the same in
1113 * replmd_update_rpmd_element()
1115 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attrs,
1116 DSDB_SEARCH_SHOW_DELETED |
1117 DSDB_SEARCH_SHOW_EXTENDED_DN |
1118 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
1119 DSDB_SEARCH_REVEAL_INTERNALS);
1120 if (ret != LDB_SUCCESS || res->count != 1) {
1121 DEBUG(0,(__location__ ": Object %s failed to find replPropertyMetaData\n",
1122 ldb_dn_get_linearized(msg->dn)));
1123 return LDB_ERR_OPERATIONS_ERROR;
1126 /* if isDeleted is present and is TRUE, then we consider we are deleting,
1127 * otherwise we consider we are updating */
1128 if (ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")) {
1129 situation = REPL_URGENT_ON_DELETE;
1130 } else {
1131 situation = REPL_URGENT_ON_UPDATE;
1134 objectclass_el = ldb_msg_find_element(res->msgs[0], "objectClass");
1135 if (is_urgent && replmd_check_urgent_objectclass(objectclass_el,
1136 situation)) {
1137 *is_urgent = true;
1140 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
1141 if (!omd_value) {
1142 DEBUG(0,(__location__ ": Object %s does not have a replPropertyMetaData attribute\n",
1143 ldb_dn_get_linearized(msg->dn)));
1144 return LDB_ERR_OPERATIONS_ERROR;
1147 ndr_err = ndr_pull_struct_blob(omd_value, msg,
1148 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")), &omd,
1149 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
1150 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1151 DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s\n",
1152 ldb_dn_get_linearized(msg->dn)));
1153 return LDB_ERR_OPERATIONS_ERROR;
1156 if (omd.version != 1) {
1157 DEBUG(0,(__location__ ": bad version %u in replPropertyMetaData for %s\n",
1158 omd.version, ldb_dn_get_linearized(msg->dn)));
1159 return LDB_ERR_OPERATIONS_ERROR;
1162 /*we have elements that will be modified*/
1163 if (msg->num_elements > 0) {
1164 /*if we are RODC and this is a DRSR update then its ok*/
1165 if (!ldb_request_get_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID)) {
1166 ret = samdb_rodc(ldb, &rodc);
1167 if (ret != LDB_SUCCESS) {
1168 DEBUG(4, (__location__ ": unable to tell if we are an RODC\n"));
1169 } else if (rodc) {
1170 ldb_asprintf_errstring(ldb, "RODC modify is forbidden\n");
1171 return LDB_ERR_REFERRAL;
1176 for (i=0; i<msg->num_elements; i++) {
1177 struct ldb_message_element *old_el;
1178 old_el = ldb_msg_find_element(res->msgs[0], msg->elements[i].name);
1179 ret = replmd_update_rpmd_element(ldb, msg, &msg->elements[i], old_el, &omd, schema, seq_num,
1180 our_invocation_id, now);
1181 if (ret != LDB_SUCCESS) {
1182 return ret;
1185 if (is_urgent && !*is_urgent && (situation == REPL_URGENT_ON_UPDATE)) {
1186 *is_urgent = replmd_check_urgent_attribute(&msg->elements[i]);
1192 * replmd_update_rpmd_element has done an update if the
1193 * seq_num is set
1195 if (*seq_num != 0) {
1196 struct ldb_val *md_value;
1197 struct ldb_message_element *el;
1199 md_value = talloc(msg, struct ldb_val);
1200 if (md_value == NULL) {
1201 ldb_oom(ldb);
1202 return LDB_ERR_OPERATIONS_ERROR;
1205 ret = replmd_replPropertyMetaDataCtr1_sort(&omd.ctr.ctr1, schema, msg->dn);
1206 if (ret != LDB_SUCCESS) {
1207 return ret;
1210 ndr_err = ndr_push_struct_blob(md_value, msg,
1211 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
1212 &omd,
1213 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
1214 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1215 DEBUG(0,(__location__ ": Failed to marshall replPropertyMetaData for %s\n",
1216 ldb_dn_get_linearized(msg->dn)));
1217 return LDB_ERR_OPERATIONS_ERROR;
1220 ret = ldb_msg_add_empty(msg, "replPropertyMetaData", LDB_FLAG_MOD_REPLACE, &el);
1221 if (ret != LDB_SUCCESS) {
1222 DEBUG(0,(__location__ ": Failed to add updated replPropertyMetaData %s\n",
1223 ldb_dn_get_linearized(msg->dn)));
1224 return ret;
1227 el->num_values = 1;
1228 el->values = md_value;
1231 return LDB_SUCCESS;
1234 struct parsed_dn {
1235 struct dsdb_dn *dsdb_dn;
1236 struct GUID *guid;
1237 struct ldb_val *v;
1240 static int parsed_dn_compare(struct parsed_dn *pdn1, struct parsed_dn *pdn2)
1242 return GUID_compare(pdn1->guid, pdn2->guid);
1245 static struct parsed_dn *parsed_dn_find(struct parsed_dn *pdn, int count, struct GUID *guid, struct ldb_dn *dn)
1247 struct parsed_dn *ret;
1248 if (dn && GUID_all_zero(guid)) {
1249 /* when updating a link using DRS, we sometimes get a
1250 NULL GUID. We then need to try and match by DN */
1251 int i;
1252 for (i=0; i<count; i++) {
1253 if (ldb_dn_compare(pdn[i].dsdb_dn->dn, dn) == 0) {
1254 dsdb_get_extended_dn_guid(pdn[i].dsdb_dn->dn, guid, "GUID");
1255 return &pdn[i];
1258 return NULL;
1260 BINARY_ARRAY_SEARCH(pdn, count, guid, guid, GUID_compare, ret);
1261 return ret;
1265 get a series of message element values as an array of DNs and GUIDs
1266 the result is sorted by GUID
1268 static int get_parsed_dns(struct ldb_module *module, TALLOC_CTX *mem_ctx,
1269 struct ldb_message_element *el, struct parsed_dn **pdn,
1270 const char *ldap_oid)
1272 unsigned int i;
1273 struct ldb_context *ldb = ldb_module_get_ctx(module);
1275 if (el == NULL) {
1276 *pdn = NULL;
1277 return LDB_SUCCESS;
1280 (*pdn) = talloc_array(mem_ctx, struct parsed_dn, el->num_values);
1281 if (!*pdn) {
1282 ldb_module_oom(module);
1283 return LDB_ERR_OPERATIONS_ERROR;
1286 for (i=0; i<el->num_values; i++) {
1287 struct ldb_val *v = &el->values[i];
1288 NTSTATUS status;
1289 struct ldb_dn *dn;
1290 struct parsed_dn *p;
1292 p = &(*pdn)[i];
1294 p->dsdb_dn = dsdb_dn_parse(*pdn, ldb, v, ldap_oid);
1295 if (p->dsdb_dn == NULL) {
1296 return LDB_ERR_INVALID_DN_SYNTAX;
1299 dn = p->dsdb_dn->dn;
1301 p->guid = talloc(*pdn, struct GUID);
1302 if (p->guid == NULL) {
1303 ldb_module_oom(module);
1304 return LDB_ERR_OPERATIONS_ERROR;
1307 status = dsdb_get_extended_dn_guid(dn, p->guid, "GUID");
1308 if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
1309 /* we got a DN without a GUID - go find the GUID */
1310 int ret = dsdb_module_guid_by_dn(module, dn, p->guid);
1311 if (ret != LDB_SUCCESS) {
1312 ldb_asprintf_errstring(ldb, "Unable to find GUID for DN %s\n",
1313 ldb_dn_get_linearized(dn));
1314 return ret;
1316 ret = dsdb_set_extended_dn_guid(dn, p->guid, "GUID");
1317 if (ret != LDB_SUCCESS) {
1318 return ret;
1320 } else if (!NT_STATUS_IS_OK(status)) {
1321 return LDB_ERR_OPERATIONS_ERROR;
1324 /* keep a pointer to the original ldb_val */
1325 p->v = v;
1328 TYPESAFE_QSORT(*pdn, el->num_values, parsed_dn_compare);
1330 return LDB_SUCCESS;
1334 build a new extended DN, including all meta data fields
1336 RMD_FLAGS = DSDB_RMD_FLAG_* bits
1337 RMD_ADDTIME = originating_add_time
1338 RMD_INVOCID = originating_invocation_id
1339 RMD_CHANGETIME = originating_change_time
1340 RMD_ORIGINATING_USN = originating_usn
1341 RMD_LOCAL_USN = local_usn
1342 RMD_VERSION = version
1344 static int replmd_build_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
1345 const struct GUID *invocation_id, uint64_t seq_num,
1346 uint64_t local_usn, NTTIME nttime, uint32_t version, bool deleted)
1348 struct ldb_dn *dn = dsdb_dn->dn;
1349 const char *tstring, *usn_string, *flags_string;
1350 struct ldb_val tval;
1351 struct ldb_val iid;
1352 struct ldb_val usnv, local_usnv;
1353 struct ldb_val vers, flagsv;
1354 NTSTATUS status;
1355 int ret;
1356 const char *dnstring;
1357 char *vstring;
1358 uint32_t rmd_flags = deleted?DSDB_RMD_FLAG_DELETED:0;
1360 tstring = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)nttime);
1361 if (!tstring) {
1362 return LDB_ERR_OPERATIONS_ERROR;
1364 tval = data_blob_string_const(tstring);
1366 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)seq_num);
1367 if (!usn_string) {
1368 return LDB_ERR_OPERATIONS_ERROR;
1370 usnv = data_blob_string_const(usn_string);
1372 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)local_usn);
1373 if (!usn_string) {
1374 return LDB_ERR_OPERATIONS_ERROR;
1376 local_usnv = data_blob_string_const(usn_string);
1378 vstring = talloc_asprintf(mem_ctx, "%lu", (unsigned long)version);
1379 if (!vstring) {
1380 return LDB_ERR_OPERATIONS_ERROR;
1382 vers = data_blob_string_const(vstring);
1384 status = GUID_to_ndr_blob(invocation_id, dn, &iid);
1385 if (!NT_STATUS_IS_OK(status)) {
1386 return LDB_ERR_OPERATIONS_ERROR;
1389 flags_string = talloc_asprintf(mem_ctx, "%u", rmd_flags);
1390 if (!flags_string) {
1391 return LDB_ERR_OPERATIONS_ERROR;
1393 flagsv = data_blob_string_const(flags_string);
1395 ret = ldb_dn_set_extended_component(dn, "RMD_FLAGS", &flagsv);
1396 if (ret != LDB_SUCCESS) return ret;
1397 ret = ldb_dn_set_extended_component(dn, "RMD_ADDTIME", &tval);
1398 if (ret != LDB_SUCCESS) return ret;
1399 ret = ldb_dn_set_extended_component(dn, "RMD_INVOCID", &iid);
1400 if (ret != LDB_SUCCESS) return ret;
1401 ret = ldb_dn_set_extended_component(dn, "RMD_CHANGETIME", &tval);
1402 if (ret != LDB_SUCCESS) return ret;
1403 ret = ldb_dn_set_extended_component(dn, "RMD_LOCAL_USN", &local_usnv);
1404 if (ret != LDB_SUCCESS) return ret;
1405 ret = ldb_dn_set_extended_component(dn, "RMD_ORIGINATING_USN", &usnv);
1406 if (ret != LDB_SUCCESS) return ret;
1407 ret = ldb_dn_set_extended_component(dn, "RMD_VERSION", &vers);
1408 if (ret != LDB_SUCCESS) return ret;
1410 dnstring = dsdb_dn_get_extended_linearized(mem_ctx, dsdb_dn, 1);
1411 if (dnstring == NULL) {
1412 return LDB_ERR_OPERATIONS_ERROR;
1414 *v = data_blob_string_const(dnstring);
1416 return LDB_SUCCESS;
1419 static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
1420 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
1421 uint64_t seq_num, uint64_t local_usn, NTTIME nttime,
1422 uint32_t version, bool deleted);
1425 check if any links need upgrading from w2k format
1427 The parent_ctx is the ldb_message_element which contains the values array that dns[i].v points at, and which should be used for allocating any new value.
1429 static int replmd_check_upgrade_links(struct parsed_dn *dns, uint32_t count, struct ldb_message_element *parent_ctx, const struct GUID *invocation_id)
1431 uint32_t i;
1432 for (i=0; i<count; i++) {
1433 NTSTATUS status;
1434 uint32_t version;
1435 int ret;
1437 status = dsdb_get_extended_dn_uint32(dns[i].dsdb_dn->dn, &version, "RMD_VERSION");
1438 if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
1439 continue;
1442 /* it's an old one that needs upgrading */
1443 ret = replmd_update_la_val(parent_ctx->values, dns[i].v, dns[i].dsdb_dn, dns[i].dsdb_dn, invocation_id,
1444 1, 1, 0, 0, false);
1445 if (ret != LDB_SUCCESS) {
1446 return ret;
1449 return LDB_SUCCESS;
1453 update an extended DN, including all meta data fields
1455 see replmd_build_la_val for value names
1457 static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
1458 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
1459 uint64_t seq_num, uint64_t local_usn, NTTIME nttime,
1460 uint32_t version, bool deleted)
1462 struct ldb_dn *dn = dsdb_dn->dn;
1463 const char *tstring, *usn_string, *flags_string;
1464 struct ldb_val tval;
1465 struct ldb_val iid;
1466 struct ldb_val usnv, local_usnv;
1467 struct ldb_val vers, flagsv;
1468 const struct ldb_val *old_addtime;
1469 uint32_t old_version;
1470 NTSTATUS status;
1471 int ret;
1472 const char *dnstring;
1473 char *vstring;
1474 uint32_t rmd_flags = deleted?DSDB_RMD_FLAG_DELETED:0;
1476 tstring = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)nttime);
1477 if (!tstring) {
1478 return LDB_ERR_OPERATIONS_ERROR;
1480 tval = data_blob_string_const(tstring);
1482 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)seq_num);
1483 if (!usn_string) {
1484 return LDB_ERR_OPERATIONS_ERROR;
1486 usnv = data_blob_string_const(usn_string);
1488 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)local_usn);
1489 if (!usn_string) {
1490 return LDB_ERR_OPERATIONS_ERROR;
1492 local_usnv = data_blob_string_const(usn_string);
1494 status = GUID_to_ndr_blob(invocation_id, dn, &iid);
1495 if (!NT_STATUS_IS_OK(status)) {
1496 return LDB_ERR_OPERATIONS_ERROR;
1499 flags_string = talloc_asprintf(mem_ctx, "%u", rmd_flags);
1500 if (!flags_string) {
1501 return LDB_ERR_OPERATIONS_ERROR;
1503 flagsv = data_blob_string_const(flags_string);
1505 ret = ldb_dn_set_extended_component(dn, "RMD_FLAGS", &flagsv);
1506 if (ret != LDB_SUCCESS) return ret;
1508 /* get the ADDTIME from the original */
1509 old_addtime = ldb_dn_get_extended_component(old_dsdb_dn->dn, "RMD_ADDTIME");
1510 if (old_addtime == NULL) {
1511 old_addtime = &tval;
1513 if (dsdb_dn != old_dsdb_dn) {
1514 ret = ldb_dn_set_extended_component(dn, "RMD_ADDTIME", old_addtime);
1515 if (ret != LDB_SUCCESS) return ret;
1518 /* use our invocation id */
1519 ret = ldb_dn_set_extended_component(dn, "RMD_INVOCID", &iid);
1520 if (ret != LDB_SUCCESS) return ret;
1522 /* changetime is the current time */
1523 ret = ldb_dn_set_extended_component(dn, "RMD_CHANGETIME", &tval);
1524 if (ret != LDB_SUCCESS) return ret;
1526 /* update the USN */
1527 ret = ldb_dn_set_extended_component(dn, "RMD_ORIGINATING_USN", &usnv);
1528 if (ret != LDB_SUCCESS) return ret;
1530 ret = ldb_dn_set_extended_component(dn, "RMD_LOCAL_USN", &local_usnv);
1531 if (ret != LDB_SUCCESS) return ret;
1533 /* increase the version by 1 */
1534 status = dsdb_get_extended_dn_uint32(old_dsdb_dn->dn, &old_version, "RMD_VERSION");
1535 if (NT_STATUS_IS_OK(status) && old_version >= version) {
1536 version = old_version+1;
1538 vstring = talloc_asprintf(dn, "%lu", (unsigned long)version);
1539 vers = data_blob_string_const(vstring);
1540 ret = ldb_dn_set_extended_component(dn, "RMD_VERSION", &vers);
1541 if (ret != LDB_SUCCESS) return ret;
1543 dnstring = dsdb_dn_get_extended_linearized(mem_ctx, dsdb_dn, 1);
1544 if (dnstring == NULL) {
1545 return LDB_ERR_OPERATIONS_ERROR;
1547 *v = data_blob_string_const(dnstring);
1549 return LDB_SUCCESS;
1553 handle adding a linked attribute
1555 static int replmd_modify_la_add(struct ldb_module *module,
1556 const struct dsdb_schema *schema,
1557 struct ldb_message *msg,
1558 struct ldb_message_element *el,
1559 struct ldb_message_element *old_el,
1560 const struct dsdb_attribute *schema_attr,
1561 uint64_t seq_num,
1562 time_t t,
1563 struct GUID *msg_guid)
1565 unsigned int i;
1566 struct parsed_dn *dns, *old_dns;
1567 TALLOC_CTX *tmp_ctx = talloc_new(msg);
1568 int ret;
1569 struct ldb_val *new_values = NULL;
1570 unsigned int num_new_values = 0;
1571 unsigned old_num_values = old_el?old_el->num_values:0;
1572 const struct GUID *invocation_id;
1573 struct ldb_context *ldb = ldb_module_get_ctx(module);
1574 NTTIME now;
1576 unix_to_nt_time(&now, t);
1578 ret = get_parsed_dns(module, tmp_ctx, el, &dns, schema_attr->syntax->ldap_oid);
1579 if (ret != LDB_SUCCESS) {
1580 talloc_free(tmp_ctx);
1581 return ret;
1584 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns, schema_attr->syntax->ldap_oid);
1585 if (ret != LDB_SUCCESS) {
1586 talloc_free(tmp_ctx);
1587 return ret;
1590 invocation_id = samdb_ntds_invocation_id(ldb);
1591 if (!invocation_id) {
1592 talloc_free(tmp_ctx);
1593 return LDB_ERR_OPERATIONS_ERROR;
1596 ret = replmd_check_upgrade_links(old_dns, old_num_values, old_el, invocation_id);
1597 if (ret != LDB_SUCCESS) {
1598 talloc_free(tmp_ctx);
1599 return ret;
1602 /* for each new value, see if it exists already with the same GUID */
1603 for (i=0; i<el->num_values; i++) {
1604 struct parsed_dn *p = parsed_dn_find(old_dns, old_num_values, dns[i].guid, NULL);
1605 if (p == NULL) {
1606 /* this is a new linked attribute value */
1607 new_values = talloc_realloc(tmp_ctx, new_values, struct ldb_val, num_new_values+1);
1608 if (new_values == NULL) {
1609 ldb_module_oom(module);
1610 talloc_free(tmp_ctx);
1611 return LDB_ERR_OPERATIONS_ERROR;
1613 ret = replmd_build_la_val(new_values, &new_values[num_new_values], dns[i].dsdb_dn,
1614 invocation_id, seq_num, seq_num, now, 0, false);
1615 if (ret != LDB_SUCCESS) {
1616 talloc_free(tmp_ctx);
1617 return ret;
1619 num_new_values++;
1620 } else {
1621 /* this is only allowed if the GUID was
1622 previously deleted. */
1623 uint32_t rmd_flags = dsdb_dn_rmd_flags(p->dsdb_dn->dn);
1625 if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
1626 ldb_asprintf_errstring(ldb, "Attribute %s already exists for target GUID %s",
1627 el->name, GUID_string(tmp_ctx, p->guid));
1628 talloc_free(tmp_ctx);
1629 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
1631 ret = replmd_update_la_val(old_el->values, p->v, dns[i].dsdb_dn, p->dsdb_dn,
1632 invocation_id, seq_num, seq_num, now, 0, false);
1633 if (ret != LDB_SUCCESS) {
1634 talloc_free(tmp_ctx);
1635 return ret;
1639 ret = replmd_add_backlink(module, schema, msg_guid, dns[i].guid, true, schema_attr, true);
1640 if (ret != LDB_SUCCESS) {
1641 talloc_free(tmp_ctx);
1642 return ret;
1646 /* add the new ones on to the end of the old values, constructing a new el->values */
1647 el->values = talloc_realloc(msg->elements, old_el?old_el->values:NULL,
1648 struct ldb_val,
1649 old_num_values+num_new_values);
1650 if (el->values == NULL) {
1651 ldb_module_oom(module);
1652 return LDB_ERR_OPERATIONS_ERROR;
1655 memcpy(&el->values[old_num_values], new_values, num_new_values*sizeof(struct ldb_val));
1656 el->num_values = old_num_values + num_new_values;
1658 talloc_steal(msg->elements, el->values);
1659 talloc_steal(el->values, new_values);
1661 talloc_free(tmp_ctx);
1663 /* we now tell the backend to replace all existing values
1664 with the one we have constructed */
1665 el->flags = LDB_FLAG_MOD_REPLACE;
1667 return LDB_SUCCESS;
1672 handle deleting all active linked attributes
1674 static int replmd_modify_la_delete(struct ldb_module *module,
1675 const struct dsdb_schema *schema,
1676 struct ldb_message *msg,
1677 struct ldb_message_element *el,
1678 struct ldb_message_element *old_el,
1679 const struct dsdb_attribute *schema_attr,
1680 uint64_t seq_num,
1681 time_t t,
1682 struct GUID *msg_guid)
1684 unsigned int i;
1685 struct parsed_dn *dns, *old_dns;
1686 TALLOC_CTX *tmp_ctx = talloc_new(msg);
1687 int ret;
1688 const struct GUID *invocation_id;
1689 struct ldb_context *ldb = ldb_module_get_ctx(module);
1690 NTTIME now;
1692 unix_to_nt_time(&now, t);
1694 /* check if there is nothing to delete */
1695 if ((!old_el || old_el->num_values == 0) &&
1696 el->num_values == 0) {
1697 return LDB_SUCCESS;
1700 if (!old_el || old_el->num_values == 0) {
1701 return LDB_ERR_NO_SUCH_ATTRIBUTE;
1704 ret = get_parsed_dns(module, tmp_ctx, el, &dns, schema_attr->syntax->ldap_oid);
1705 if (ret != LDB_SUCCESS) {
1706 talloc_free(tmp_ctx);
1707 return ret;
1710 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns, schema_attr->syntax->ldap_oid);
1711 if (ret != LDB_SUCCESS) {
1712 talloc_free(tmp_ctx);
1713 return ret;
1716 invocation_id = samdb_ntds_invocation_id(ldb);
1717 if (!invocation_id) {
1718 return LDB_ERR_OPERATIONS_ERROR;
1721 ret = replmd_check_upgrade_links(old_dns, old_el->num_values, old_el, invocation_id);
1722 if (ret != LDB_SUCCESS) {
1723 talloc_free(tmp_ctx);
1724 return ret;
1727 el->values = NULL;
1729 /* see if we are being asked to delete any links that
1730 don't exist or are already deleted */
1731 for (i=0; i<el->num_values; i++) {
1732 struct parsed_dn *p = &dns[i];
1733 struct parsed_dn *p2;
1734 uint32_t rmd_flags;
1736 p2 = parsed_dn_find(old_dns, old_el->num_values, p->guid, NULL);
1737 if (!p2) {
1738 ldb_asprintf_errstring(ldb, "Attribute %s doesn't exist for target GUID %s",
1739 el->name, GUID_string(tmp_ctx, p->guid));
1740 return LDB_ERR_NO_SUCH_ATTRIBUTE;
1742 rmd_flags = dsdb_dn_rmd_flags(p2->dsdb_dn->dn);
1743 if (rmd_flags & DSDB_RMD_FLAG_DELETED) {
1744 ldb_asprintf_errstring(ldb, "Attribute %s already deleted for target GUID %s",
1745 el->name, GUID_string(tmp_ctx, p->guid));
1746 return LDB_ERR_NO_SUCH_ATTRIBUTE;
1750 /* for each new value, see if it exists already with the same GUID
1751 if it is not already deleted and matches the delete list then delete it
1753 for (i=0; i<old_el->num_values; i++) {
1754 struct parsed_dn *p = &old_dns[i];
1755 uint32_t rmd_flags;
1757 if (el->num_values && parsed_dn_find(dns, el->num_values, p->guid, NULL) == NULL) {
1758 continue;
1761 rmd_flags = dsdb_dn_rmd_flags(p->dsdb_dn->dn);
1762 if (rmd_flags & DSDB_RMD_FLAG_DELETED) continue;
1764 ret = replmd_update_la_val(old_el->values, p->v, p->dsdb_dn, p->dsdb_dn,
1765 invocation_id, seq_num, seq_num, now, 0, true);
1766 if (ret != LDB_SUCCESS) {
1767 talloc_free(tmp_ctx);
1768 return ret;
1771 ret = replmd_add_backlink(module, schema, msg_guid, old_dns[i].guid, false, schema_attr, true);
1772 if (ret != LDB_SUCCESS) {
1773 talloc_free(tmp_ctx);
1774 return ret;
1778 el->values = talloc_steal(msg->elements, old_el->values);
1779 el->num_values = old_el->num_values;
1781 talloc_free(tmp_ctx);
1783 /* we now tell the backend to replace all existing values
1784 with the one we have constructed */
1785 el->flags = LDB_FLAG_MOD_REPLACE;
1787 return LDB_SUCCESS;
1791 handle replacing a linked attribute
1793 static int replmd_modify_la_replace(struct ldb_module *module,
1794 const struct dsdb_schema *schema,
1795 struct ldb_message *msg,
1796 struct ldb_message_element *el,
1797 struct ldb_message_element *old_el,
1798 const struct dsdb_attribute *schema_attr,
1799 uint64_t seq_num,
1800 time_t t,
1801 struct GUID *msg_guid)
1803 unsigned int i;
1804 struct parsed_dn *dns, *old_dns;
1805 TALLOC_CTX *tmp_ctx = talloc_new(msg);
1806 int ret;
1807 const struct GUID *invocation_id;
1808 struct ldb_context *ldb = ldb_module_get_ctx(module);
1809 struct ldb_val *new_values = NULL;
1810 unsigned int num_new_values = 0;
1811 unsigned int old_num_values = old_el?old_el->num_values:0;
1812 NTTIME now;
1814 unix_to_nt_time(&now, t);
1816 /* check if there is nothing to replace */
1817 if ((!old_el || old_el->num_values == 0) &&
1818 el->num_values == 0) {
1819 return LDB_SUCCESS;
1822 ret = get_parsed_dns(module, tmp_ctx, el, &dns, schema_attr->syntax->ldap_oid);
1823 if (ret != LDB_SUCCESS) {
1824 talloc_free(tmp_ctx);
1825 return ret;
1828 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns, schema_attr->syntax->ldap_oid);
1829 if (ret != LDB_SUCCESS) {
1830 talloc_free(tmp_ctx);
1831 return ret;
1834 invocation_id = samdb_ntds_invocation_id(ldb);
1835 if (!invocation_id) {
1836 return LDB_ERR_OPERATIONS_ERROR;
1839 ret = replmd_check_upgrade_links(old_dns, old_num_values, old_el, invocation_id);
1840 if (ret != LDB_SUCCESS) {
1841 talloc_free(tmp_ctx);
1842 return ret;
1845 /* mark all the old ones as deleted */
1846 for (i=0; i<old_num_values; i++) {
1847 struct parsed_dn *old_p = &old_dns[i];
1848 struct parsed_dn *p;
1849 uint32_t rmd_flags = dsdb_dn_rmd_flags(old_p->dsdb_dn->dn);
1851 if (rmd_flags & DSDB_RMD_FLAG_DELETED) continue;
1853 ret = replmd_add_backlink(module, schema, msg_guid, old_dns[i].guid, false, schema_attr, false);
1854 if (ret != LDB_SUCCESS) {
1855 talloc_free(tmp_ctx);
1856 return ret;
1859 p = parsed_dn_find(dns, el->num_values, old_p->guid, NULL);
1860 if (p) {
1861 /* we don't delete it if we are re-adding it */
1862 continue;
1865 ret = replmd_update_la_val(old_el->values, old_p->v, old_p->dsdb_dn, old_p->dsdb_dn,
1866 invocation_id, seq_num, seq_num, now, 0, true);
1867 if (ret != LDB_SUCCESS) {
1868 talloc_free(tmp_ctx);
1869 return ret;
1873 /* for each new value, either update its meta-data, or add it
1874 * to old_el
1876 for (i=0; i<el->num_values; i++) {
1877 struct parsed_dn *p = &dns[i], *old_p;
1879 if (old_dns &&
1880 (old_p = parsed_dn_find(old_dns,
1881 old_num_values, p->guid, NULL)) != NULL) {
1882 /* update in place */
1883 ret = replmd_update_la_val(old_el->values, old_p->v, old_p->dsdb_dn,
1884 old_p->dsdb_dn, invocation_id,
1885 seq_num, seq_num, now, 0, false);
1886 if (ret != LDB_SUCCESS) {
1887 talloc_free(tmp_ctx);
1888 return ret;
1890 } else {
1891 /* add a new one */
1892 new_values = talloc_realloc(tmp_ctx, new_values, struct ldb_val,
1893 num_new_values+1);
1894 if (new_values == NULL) {
1895 ldb_module_oom(module);
1896 talloc_free(tmp_ctx);
1897 return LDB_ERR_OPERATIONS_ERROR;
1899 ret = replmd_build_la_val(new_values, &new_values[num_new_values], dns[i].dsdb_dn,
1900 invocation_id, seq_num, seq_num, now, 0, false);
1901 if (ret != LDB_SUCCESS) {
1902 talloc_free(tmp_ctx);
1903 return ret;
1905 num_new_values++;
1908 ret = replmd_add_backlink(module, schema, msg_guid, dns[i].guid, true, schema_attr, false);
1909 if (ret != LDB_SUCCESS) {
1910 talloc_free(tmp_ctx);
1911 return ret;
1915 /* add the new values to the end of old_el */
1916 if (num_new_values != 0) {
1917 el->values = talloc_realloc(msg->elements, old_el?old_el->values:NULL,
1918 struct ldb_val, old_num_values+num_new_values);
1919 if (el->values == NULL) {
1920 ldb_module_oom(module);
1921 return LDB_ERR_OPERATIONS_ERROR;
1923 memcpy(&el->values[old_num_values], &new_values[0],
1924 sizeof(struct ldb_val)*num_new_values);
1925 el->num_values = old_num_values + num_new_values;
1926 talloc_steal(msg->elements, new_values);
1927 } else {
1928 el->values = old_el->values;
1929 el->num_values = old_el->num_values;
1930 talloc_steal(msg->elements, el->values);
1933 talloc_free(tmp_ctx);
1935 /* we now tell the backend to replace all existing values
1936 with the one we have constructed */
1937 el->flags = LDB_FLAG_MOD_REPLACE;
1939 return LDB_SUCCESS;
1944 handle linked attributes in modify requests
1946 static int replmd_modify_handle_linked_attribs(struct ldb_module *module,
1947 struct ldb_message *msg,
1948 uint64_t seq_num, time_t t)
1950 struct ldb_result *res;
1951 unsigned int i;
1952 int ret;
1953 struct ldb_context *ldb = ldb_module_get_ctx(module);
1954 struct ldb_message *old_msg;
1956 const struct dsdb_schema *schema;
1957 struct GUID old_guid;
1959 if (seq_num == 0) {
1960 /* there the replmd_update_rpmd code has already
1961 * checked and saw that there are no linked
1962 * attributes */
1963 return LDB_SUCCESS;
1966 #if !W2K3_LINKED_ATTRIBUTES
1967 return LDB_SUCCESS;
1968 #endif
1970 if (dsdb_functional_level(ldb) == DS_DOMAIN_FUNCTION_2000) {
1971 /* don't do anything special for linked attributes */
1972 return LDB_SUCCESS;
1975 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, NULL,
1976 DSDB_SEARCH_SHOW_DELETED |
1977 DSDB_SEARCH_REVEAL_INTERNALS |
1978 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT);
1979 if (ret != LDB_SUCCESS) {
1980 return ret;
1982 schema = dsdb_get_schema(ldb, res);
1983 if (!schema) {
1984 return LDB_ERR_OPERATIONS_ERROR;
1987 old_msg = res->msgs[0];
1989 old_guid = samdb_result_guid(old_msg, "objectGUID");
1991 for (i=0; i<msg->num_elements; i++) {
1992 struct ldb_message_element *el = &msg->elements[i];
1993 struct ldb_message_element *old_el, *new_el;
1994 const struct dsdb_attribute *schema_attr
1995 = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
1996 if (!schema_attr) {
1997 ldb_asprintf_errstring(ldb,
1998 "attribute %s is not a valid attribute in schema", el->name);
1999 return LDB_ERR_OBJECT_CLASS_VIOLATION;
2001 if (schema_attr->linkID == 0) {
2002 continue;
2004 if ((schema_attr->linkID & 1) == 1) {
2005 /* Odd is for the target. Illegal to modify */
2006 ldb_asprintf_errstring(ldb,
2007 "attribute %s must not be modified directly, it is a linked attribute", el->name);
2008 return LDB_ERR_UNWILLING_TO_PERFORM;
2010 old_el = ldb_msg_find_element(old_msg, el->name);
2011 switch (el->flags & LDB_FLAG_MOD_MASK) {
2012 case LDB_FLAG_MOD_REPLACE:
2013 ret = replmd_modify_la_replace(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid);
2014 break;
2015 case LDB_FLAG_MOD_DELETE:
2016 ret = replmd_modify_la_delete(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid);
2017 break;
2018 case LDB_FLAG_MOD_ADD:
2019 ret = replmd_modify_la_add(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid);
2020 break;
2021 default:
2022 ldb_asprintf_errstring(ldb,
2023 "invalid flags 0x%x for %s linked attribute",
2024 el->flags, el->name);
2025 return LDB_ERR_UNWILLING_TO_PERFORM;
2027 if (ret != LDB_SUCCESS) {
2028 return ret;
2030 if (old_el) {
2031 ldb_msg_remove_attr(old_msg, el->name);
2033 ldb_msg_add_empty(old_msg, el->name, 0, &new_el);
2034 new_el->num_values = el->num_values;
2035 new_el->values = talloc_steal(msg->elements, el->values);
2037 /* TODO: this relises a bit too heavily on the exact
2038 behaviour of ldb_msg_find_element and
2039 ldb_msg_remove_element */
2040 old_el = ldb_msg_find_element(msg, el->name);
2041 if (old_el != el) {
2042 ldb_msg_remove_element(msg, old_el);
2043 i--;
2047 talloc_free(res);
2048 return ret;
2053 static int replmd_modify(struct ldb_module *module, struct ldb_request *req)
2055 struct ldb_context *ldb;
2056 struct replmd_replicated_request *ac;
2057 struct ldb_request *down_req;
2058 struct ldb_message *msg;
2059 time_t t = time(NULL);
2060 int ret;
2061 bool is_urgent = false;
2062 struct loadparm_context *lp_ctx;
2063 char *referral;
2065 /* do not manipulate our control entries */
2066 if (ldb_dn_is_special(req->op.mod.message->dn)) {
2067 return ldb_next_request(module, req);
2070 ldb = ldb_module_get_ctx(module);
2071 lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
2072 struct loadparm_context);
2074 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_modify\n");
2076 ac = replmd_ctx_init(module, req);
2077 if (!ac) {
2078 return LDB_ERR_OPERATIONS_ERROR;
2081 /* we have to copy the message as the caller might have it as a const */
2082 msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
2083 if (msg == NULL) {
2084 ldb_oom(ldb);
2085 talloc_free(ac);
2086 return LDB_ERR_OPERATIONS_ERROR;
2089 ldb_msg_remove_attr(msg, "whenChanged");
2090 ldb_msg_remove_attr(msg, "uSNChanged");
2092 ret = replmd_update_rpmd(module, ac->schema, req, msg, &ac->seq_num, t, &is_urgent);
2093 if (ret == LDB_ERR_REFERRAL) {
2094 talloc_free(ac);
2096 referral = talloc_asprintf(req,
2097 "ldap://%s/%s",
2098 lp_dnsdomain(lp_ctx),
2099 ldb_dn_get_linearized(msg->dn));
2100 ret = ldb_module_send_referral(req, referral);
2101 return ldb_module_done(req, NULL, NULL, ret);
2104 if (ret != LDB_SUCCESS) {
2105 talloc_free(ac);
2106 return ret;
2109 ret = replmd_modify_handle_linked_attribs(module, msg, ac->seq_num, t);
2110 if (ret != LDB_SUCCESS) {
2111 talloc_free(ac);
2112 return ret;
2115 /* TODO:
2116 * - replace the old object with the newly constructed one
2119 ac->is_urgent = is_urgent;
2121 ret = ldb_build_mod_req(&down_req, ldb, ac,
2122 msg,
2123 req->controls,
2124 ac, replmd_op_callback,
2125 req);
2126 if (ret != LDB_SUCCESS) {
2127 talloc_free(ac);
2128 return ret;
2130 talloc_steal(down_req, msg);
2132 /* we only change whenChanged and uSNChanged if the seq_num
2133 has changed */
2134 if (ac->seq_num != 0) {
2135 if (add_time_element(msg, "whenChanged", t) != LDB_SUCCESS) {
2136 talloc_free(ac);
2137 return ret;
2140 if (add_uint64_element(msg, "uSNChanged", ac->seq_num) != LDB_SUCCESS) {
2141 talloc_free(ac);
2142 return ret;
2146 /* go on with the call chain */
2147 return ldb_next_request(module, down_req);
2150 static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares);
2153 handle a rename request
2155 On a rename we need to do an extra ldb_modify which sets the
2156 whenChanged and uSNChanged attributes. We do this in a callback after the success.
2158 static int replmd_rename(struct ldb_module *module, struct ldb_request *req)
2160 struct ldb_context *ldb;
2161 struct replmd_replicated_request *ac;
2162 int ret;
2163 struct ldb_request *down_req;
2165 /* do not manipulate our control entries */
2166 if (ldb_dn_is_special(req->op.mod.message->dn)) {
2167 return ldb_next_request(module, req);
2170 ldb = ldb_module_get_ctx(module);
2172 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_rename\n");
2174 ac = replmd_ctx_init(module, req);
2175 if (!ac) {
2176 return LDB_ERR_OPERATIONS_ERROR;
2178 ret = ldb_build_rename_req(&down_req, ldb, ac,
2179 ac->req->op.rename.olddn,
2180 ac->req->op.rename.newdn,
2181 ac->req->controls,
2182 ac, replmd_rename_callback,
2183 ac->req);
2185 if (ret != LDB_SUCCESS) {
2186 talloc_free(ac);
2187 return ret;
2190 /* go on with the call chain */
2191 return ldb_next_request(module, down_req);
2194 /* After the rename is compleated, update the whenchanged etc */
2195 static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares)
2197 struct ldb_context *ldb;
2198 struct replmd_replicated_request *ac;
2199 struct ldb_request *down_req;
2200 struct ldb_message *msg;
2201 time_t t = time(NULL);
2202 int ret;
2204 ac = talloc_get_type(req->context, struct replmd_replicated_request);
2205 ldb = ldb_module_get_ctx(ac->module);
2207 if (ares->error != LDB_SUCCESS) {
2208 return ldb_module_done(ac->req, ares->controls,
2209 ares->response, ares->error);
2212 if (ares->type != LDB_REPLY_DONE) {
2213 ldb_set_errstring(ldb,
2214 "invalid ldb_reply_type in callback");
2215 talloc_free(ares);
2216 return ldb_module_done(ac->req, NULL, NULL,
2217 LDB_ERR_OPERATIONS_ERROR);
2220 /* Get a sequence number from the backend */
2221 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ac->seq_num);
2222 if (ret != LDB_SUCCESS) {
2223 return ret;
2226 /* TODO:
2227 * - replace the old object with the newly constructed one
2230 msg = ldb_msg_new(ac);
2231 if (msg == NULL) {
2232 ldb_oom(ldb);
2233 return LDB_ERR_OPERATIONS_ERROR;
2236 msg->dn = ac->req->op.rename.newdn;
2238 ret = ldb_build_mod_req(&down_req, ldb, ac,
2239 msg,
2240 req->controls,
2241 ac, replmd_op_callback,
2242 req);
2244 if (ret != LDB_SUCCESS) {
2245 talloc_free(ac);
2246 return ret;
2248 talloc_steal(down_req, msg);
2250 if (add_time_element(msg, "whenChanged", t) != LDB_SUCCESS) {
2251 talloc_free(ac);
2252 return ret;
2255 if (add_uint64_element(msg, "uSNChanged", ac->seq_num) != LDB_SUCCESS) {
2256 talloc_free(ac);
2257 return ret;
2260 /* go on with the call chain - do the modify after the rename */
2261 return ldb_next_request(ac->module, down_req);
2265 remove links from objects that point at this object when an object
2266 is deleted
2268 static int replmd_delete_remove_link(struct ldb_module *module,
2269 const struct dsdb_schema *schema,
2270 struct ldb_dn *dn,
2271 struct ldb_message_element *el,
2272 const struct dsdb_attribute *sa)
2274 unsigned int i;
2275 TALLOC_CTX *tmp_ctx = talloc_new(module);
2276 struct ldb_context *ldb = ldb_module_get_ctx(module);
2278 for (i=0; i<el->num_values; i++) {
2279 struct dsdb_dn *dsdb_dn;
2280 NTSTATUS status;
2281 int ret;
2282 struct GUID guid2;
2283 struct ldb_message *msg;
2284 const struct dsdb_attribute *target_attr;
2285 struct ldb_message_element *el2;
2286 struct ldb_val dn_val;
2288 if (dsdb_dn_is_deleted_val(&el->values[i])) {
2289 continue;
2292 dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i], sa->syntax->ldap_oid);
2293 if (!dsdb_dn) {
2294 talloc_free(tmp_ctx);
2295 return LDB_ERR_OPERATIONS_ERROR;
2298 status = dsdb_get_extended_dn_guid(dsdb_dn->dn, &guid2, "GUID");
2299 if (!NT_STATUS_IS_OK(status)) {
2300 talloc_free(tmp_ctx);
2301 return LDB_ERR_OPERATIONS_ERROR;
2304 /* remove the link */
2305 msg = ldb_msg_new(tmp_ctx);
2306 if (!msg) {
2307 ldb_module_oom(module);
2308 talloc_free(tmp_ctx);
2309 return LDB_ERR_OPERATIONS_ERROR;
2313 msg->dn = dsdb_dn->dn;
2315 target_attr = dsdb_attribute_by_linkID(schema, sa->linkID ^ 1);
2316 if (target_attr == NULL) {
2317 continue;
2320 ret = ldb_msg_add_empty(msg, target_attr->lDAPDisplayName, LDB_FLAG_MOD_DELETE, &el2);
2321 if (ret != LDB_SUCCESS) {
2322 ldb_module_oom(module);
2323 talloc_free(tmp_ctx);
2324 return LDB_ERR_OPERATIONS_ERROR;
2326 dn_val = data_blob_string_const(ldb_dn_get_linearized(dn));
2327 el2->values = &dn_val;
2328 el2->num_values = 1;
2330 ret = dsdb_module_modify(module, msg, DSDB_FLAG_OWN_MODULE);
2331 if (ret != LDB_SUCCESS) {
2332 talloc_free(tmp_ctx);
2333 return ret;
2336 talloc_free(tmp_ctx);
2337 return LDB_SUCCESS;
2342 handle update of replication meta data for deletion of objects
2344 This also handles the mapping of delete to a rename operation
2345 to allow deletes to be replicated.
2347 static int replmd_delete(struct ldb_module *module, struct ldb_request *req)
2349 int ret = LDB_ERR_OTHER;
2350 bool retb;
2351 struct ldb_dn *old_dn, *new_dn;
2352 const char *rdn_name;
2353 const struct ldb_val *rdn_value, *new_rdn_value;
2354 struct GUID guid;
2355 struct ldb_context *ldb = ldb_module_get_ctx(module);
2356 const struct dsdb_schema *schema;
2357 struct ldb_message *msg, *old_msg;
2358 struct ldb_message_element *el;
2359 TALLOC_CTX *tmp_ctx;
2360 struct ldb_result *res, *parent_res;
2361 const char *preserved_attrs[] = {
2362 /* yes, this really is a hard coded list. See MS-ADTS
2363 section 3.1.1.5.5.1.1 */
2364 "nTSecurityDescriptor", "attributeID", "attributeSyntax", "dNReferenceUpdate", "dNSHostName",
2365 "flatName", "governsID", "groupType", "instanceType", "lDAPDisplayName", "legacyExchangeDN",
2366 "isDeleted", "isRecycled", "lastKnownParent", "msDS-LastKnownRDN", "mS-DS-CreatorSID",
2367 "mSMQOwnerID", "nCName", "objectClass", "distinguishedName", "objectGUID", "objectSid",
2368 "oMSyntax", "proxiedObjectName", "name", "replPropertyMetaData", "sAMAccountName",
2369 "securityIdentifier", "sIDHistory", "subClassOf", "systemFlags", "trustPartner", "trustDirection",
2370 "trustType", "trustAttributes", "userAccountControl", "uSNChanged", "uSNCreated", "whenCreated",
2371 "whenChanged", NULL};
2372 unsigned int i, el_count = 0;
2373 enum deletion_state { OBJECT_NOT_DELETED=1, OBJECT_DELETED=2, OBJECT_RECYCLED=3,
2374 OBJECT_TOMBSTONE=4, OBJECT_REMOVED=5 };
2375 enum deletion_state deletion_state, next_deletion_state;
2376 bool enabled;
2378 if (ldb_dn_is_special(req->op.del.dn)) {
2379 return ldb_next_request(module, req);
2382 tmp_ctx = talloc_new(ldb);
2383 if (!tmp_ctx) {
2384 ldb_oom(ldb);
2385 return LDB_ERR_OPERATIONS_ERROR;
2388 schema = dsdb_get_schema(ldb, tmp_ctx);
2389 if (!schema) {
2390 return LDB_ERR_OPERATIONS_ERROR;
2393 old_dn = ldb_dn_copy(tmp_ctx, req->op.del.dn);
2395 /* we need the complete msg off disk, so we can work out which
2396 attributes need to be removed */
2397 ret = dsdb_module_search_dn(module, tmp_ctx, &res, old_dn, NULL,
2398 DSDB_SEARCH_SHOW_DELETED |
2399 DSDB_SEARCH_REVEAL_INTERNALS |
2400 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT);
2401 if (ret != LDB_SUCCESS) {
2402 talloc_free(tmp_ctx);
2403 return ret;
2405 old_msg = res->msgs[0];
2408 ret = dsdb_recyclebin_enabled(module, &enabled);
2409 if (ret != LDB_SUCCESS) {
2410 talloc_free(tmp_ctx);
2411 return ret;
2414 if (ldb_msg_check_string_attribute(old_msg, "isDeleted", "TRUE")) {
2415 if (!enabled) {
2416 deletion_state = OBJECT_TOMBSTONE;
2417 next_deletion_state = OBJECT_REMOVED;
2418 } else if (ldb_msg_check_string_attribute(old_msg, "isRecycled", "TRUE")) {
2419 deletion_state = OBJECT_RECYCLED;
2420 next_deletion_state = OBJECT_REMOVED;
2421 } else {
2422 deletion_state = OBJECT_DELETED;
2423 next_deletion_state = OBJECT_RECYCLED;
2425 } else {
2426 deletion_state = OBJECT_NOT_DELETED;
2427 if (enabled) {
2428 next_deletion_state = OBJECT_DELETED;
2429 } else {
2430 next_deletion_state = OBJECT_TOMBSTONE;
2434 if (next_deletion_state == OBJECT_REMOVED) {
2435 struct auth_session_info *session_info =
2436 (struct auth_session_info *)ldb_get_opaque(ldb, "sessionInfo");
2437 if (security_session_user_level(session_info, NULL) != SECURITY_SYSTEM) {
2438 ldb_asprintf_errstring(ldb, "Refusing to delete deleted object %s",
2439 ldb_dn_get_linearized(old_msg->dn));
2440 return LDB_ERR_UNWILLING_TO_PERFORM;
2443 /* it is already deleted - really remove it this time */
2444 talloc_free(tmp_ctx);
2445 return ldb_next_request(module, req);
2448 rdn_name = ldb_dn_get_rdn_name(old_dn);
2449 rdn_value = ldb_dn_get_rdn_val(old_dn);
2451 msg = ldb_msg_new(tmp_ctx);
2452 if (msg == NULL) {
2453 ldb_module_oom(module);
2454 talloc_free(tmp_ctx);
2455 return LDB_ERR_OPERATIONS_ERROR;
2458 msg->dn = old_dn;
2460 if (deletion_state == OBJECT_NOT_DELETED){
2461 /* work out where we will be renaming this object to */
2462 ret = dsdb_get_deleted_objects_dn(ldb, tmp_ctx, old_dn, &new_dn);
2463 if (ret != LDB_SUCCESS) {
2464 /* this is probably an attempted delete on a partition
2465 * that doesn't allow delete operations, such as the
2466 * schema partition */
2467 ldb_asprintf_errstring(ldb, "No Deleted Objects container for DN %s",
2468 ldb_dn_get_linearized(old_dn));
2469 talloc_free(tmp_ctx);
2470 return LDB_ERR_UNWILLING_TO_PERFORM;
2473 /* get the objects GUID from the search we just did */
2474 guid = samdb_result_guid(old_msg, "objectGUID");
2476 /* Add a formatted child */
2477 retb = ldb_dn_add_child_fmt(new_dn, "%s=%s\\0ADEL:%s",
2478 rdn_name,
2479 rdn_value->data,
2480 GUID_string(tmp_ctx, &guid));
2481 if (!retb) {
2482 DEBUG(0,(__location__ ": Unable to add a formatted child to dn: %s",
2483 ldb_dn_get_linearized(new_dn)));
2484 talloc_free(tmp_ctx);
2485 return LDB_ERR_OPERATIONS_ERROR;
2488 ret = ldb_msg_add_string(msg, "isDeleted", "TRUE");
2489 if (ret != LDB_SUCCESS) {
2490 DEBUG(0,(__location__ ": Failed to add isDeleted string to the msg\n"));
2491 ldb_module_oom(module);
2492 talloc_free(tmp_ctx);
2493 return ret;
2495 msg->elements[el_count++].flags = LDB_FLAG_MOD_ADD;
2499 now we need to modify the object in the following ways:
2501 - add isDeleted=TRUE
2502 - update rDN and name, with new rDN
2503 - remove linked attributes
2504 - remove objectCategory and sAMAccountType
2505 - remove attribs not on the preserved list
2506 - preserved if in above list, or is rDN
2507 - remove all linked attribs from this object
2508 - remove all links from other objects to this object
2509 - add lastKnownParent
2510 - update replPropertyMetaData?
2512 see MS-ADTS "Tombstone Requirements" section 3.1.1.5.5.1.1
2515 /* we need the storage form of the parent GUID */
2516 ret = dsdb_module_search_dn(module, tmp_ctx, &parent_res,
2517 ldb_dn_get_parent(tmp_ctx, old_dn), NULL,
2518 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
2519 DSDB_SEARCH_REVEAL_INTERNALS|
2520 DSDB_SEARCH_SHOW_DELETED);
2521 if (ret != LDB_SUCCESS) {
2522 talloc_free(tmp_ctx);
2523 return ret;
2526 if (deletion_state == OBJECT_NOT_DELETED){
2527 ret = ldb_msg_add_steal_string(msg, "lastKnownParent",
2528 ldb_dn_get_extended_linearized(tmp_ctx, parent_res->msgs[0]->dn, 1));
2529 if (ret != LDB_SUCCESS) {
2530 DEBUG(0,(__location__ ": Failed to add lastKnownParent string to the msg\n"));
2531 ldb_module_oom(module);
2532 talloc_free(tmp_ctx);
2533 return ret;
2535 msg->elements[el_count++].flags = LDB_FLAG_MOD_ADD;
2538 switch (next_deletion_state){
2540 case OBJECT_DELETED:
2542 ret = ldb_msg_add_value(msg, "msDS-LastKnownRDN", rdn_value, NULL);
2543 if (ret != LDB_SUCCESS) {
2544 DEBUG(0,(__location__ ": Failed to add msDS-LastKnownRDN string to the msg\n"));
2545 ldb_module_oom(module);
2546 talloc_free(tmp_ctx);
2547 return ret;
2549 msg->elements[el_count++].flags = LDB_FLAG_MOD_ADD;
2551 ret = ldb_msg_add_empty(msg, "objectCategory", LDB_FLAG_MOD_DELETE, NULL);
2552 if (ret != LDB_SUCCESS) {
2553 talloc_free(tmp_ctx);
2554 ldb_module_oom(module);
2555 return ret;
2558 ret = ldb_msg_add_empty(msg, "sAMAccountType", LDB_FLAG_MOD_DELETE, NULL);
2559 if (ret != LDB_SUCCESS) {
2560 talloc_free(tmp_ctx);
2561 ldb_module_oom(module);
2562 return ret;
2565 break;
2567 case OBJECT_RECYCLED:
2568 case OBJECT_TOMBSTONE:
2570 /* we also mark it as recycled, meaning this object can't be
2571 recovered (we are stripping its attributes) */
2572 if (dsdb_functional_level(ldb) >= DS_DOMAIN_FUNCTION_2008_R2) {
2573 ret = ldb_msg_add_string(msg, "isRecycled", "TRUE");
2574 if (ret != LDB_SUCCESS) {
2575 DEBUG(0,(__location__ ": Failed to add isRecycled string to the msg\n"));
2576 ldb_module_oom(module);
2577 talloc_free(tmp_ctx);
2578 return ret;
2580 msg->elements[el_count++].flags = LDB_FLAG_MOD_ADD;
2583 /* work out which of the old attributes we will be removing */
2584 for (i=0; i<old_msg->num_elements; i++) {
2585 const struct dsdb_attribute *sa;
2586 el = &old_msg->elements[i];
2587 sa = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
2588 if (!sa) {
2589 talloc_free(tmp_ctx);
2590 return LDB_ERR_OPERATIONS_ERROR;
2592 if (ldb_attr_cmp(el->name, rdn_name) == 0) {
2593 /* don't remove the rDN */
2594 continue;
2596 if (sa->linkID && sa->linkID & 1) {
2597 ret = replmd_delete_remove_link(module, schema, old_dn, el, sa);
2598 if (ret != LDB_SUCCESS) {
2599 talloc_free(tmp_ctx);
2600 return LDB_ERR_OPERATIONS_ERROR;
2602 continue;
2604 if (!sa->linkID && ldb_attr_in_list(preserved_attrs, el->name)) {
2605 continue;
2607 ret = ldb_msg_add_empty(msg, el->name, LDB_FLAG_MOD_DELETE, &el);
2608 if (ret != LDB_SUCCESS) {
2609 talloc_free(tmp_ctx);
2610 ldb_module_oom(module);
2611 return ret;
2614 break;
2616 default:
2617 break;
2620 if (deletion_state == OBJECT_NOT_DELETED) {
2621 /* work out what the new rdn value is, for updating the
2622 rDN and name fields */
2623 new_rdn_value = ldb_dn_get_rdn_val(new_dn);
2625 ret = ldb_msg_add_value(msg, strlower_talloc(tmp_ctx, rdn_name), new_rdn_value, &el);
2626 if (ret != LDB_SUCCESS) {
2627 talloc_free(tmp_ctx);
2628 return ret;
2630 el->flags = LDB_FLAG_MOD_REPLACE;
2632 el = ldb_msg_find_element(old_msg, "name");
2633 if (el) {
2634 ret = ldb_msg_add_value(msg, "name", new_rdn_value, &el);
2635 if (ret != LDB_SUCCESS) {
2636 talloc_free(tmp_ctx);
2637 return ret;
2639 el->flags = LDB_FLAG_MOD_REPLACE;
2643 ret = dsdb_module_modify(module, msg, DSDB_FLAG_OWN_MODULE);
2644 if (ret != LDB_SUCCESS) {
2645 ldb_asprintf_errstring(ldb, "replmd_delete: Failed to modify object %s in delete - %s",
2646 ldb_dn_get_linearized(old_dn), ldb_errstring(ldb));
2647 talloc_free(tmp_ctx);
2648 return ret;
2651 if (deletion_state == OBJECT_NOT_DELETED) {
2652 /* now rename onto the new DN */
2653 ret = dsdb_module_rename(module, old_dn, new_dn, 0);
2654 if (ret != LDB_SUCCESS){
2655 DEBUG(0,(__location__ ": Failed to rename object from '%s' to '%s' - %s\n",
2656 ldb_dn_get_linearized(old_dn),
2657 ldb_dn_get_linearized(new_dn),
2658 ldb_errstring(ldb)));
2659 talloc_free(tmp_ctx);
2660 return ret;
2664 talloc_free(tmp_ctx);
2666 return ldb_module_done(req, NULL, NULL, LDB_SUCCESS);
2671 static int replmd_replicated_request_error(struct replmd_replicated_request *ar, int ret)
2673 return ret;
2676 static int replmd_replicated_request_werror(struct replmd_replicated_request *ar, WERROR status)
2678 int ret = LDB_ERR_OTHER;
2679 /* TODO: do some error mapping */
2680 return ret;
2683 static int replmd_replicated_apply_add(struct replmd_replicated_request *ar)
2685 struct ldb_context *ldb;
2686 struct ldb_request *change_req;
2687 enum ndr_err_code ndr_err;
2688 struct ldb_message *msg;
2689 struct replPropertyMetaDataBlob *md;
2690 struct ldb_val md_value;
2691 unsigned int i;
2692 int ret;
2695 * TODO: check if the parent object exist
2699 * TODO: handle the conflict case where an object with the
2700 * same name exist
2703 ldb = ldb_module_get_ctx(ar->module);
2704 msg = ar->objs->objects[ar->index_current].msg;
2705 md = ar->objs->objects[ar->index_current].meta_data;
2707 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
2708 if (ret != LDB_SUCCESS) {
2709 return replmd_replicated_request_error(ar, ret);
2712 ret = ldb_msg_add_value(msg, "objectGUID", &ar->objs->objects[ar->index_current].guid_value, NULL);
2713 if (ret != LDB_SUCCESS) {
2714 return replmd_replicated_request_error(ar, ret);
2717 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
2718 if (ret != LDB_SUCCESS) {
2719 return replmd_replicated_request_error(ar, ret);
2722 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", ar->seq_num);
2723 if (ret != LDB_SUCCESS) {
2724 return replmd_replicated_request_error(ar, ret);
2727 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
2728 if (ret != LDB_SUCCESS) {
2729 return replmd_replicated_request_error(ar, ret);
2732 /* remove any message elements that have zero values */
2733 for (i=0; i<msg->num_elements; i++) {
2734 struct ldb_message_element *el = &msg->elements[i];
2736 if (el->num_values == 0) {
2737 DEBUG(4,(__location__ ": Removing attribute %s with num_values==0\n",
2738 el->name));
2739 memmove(el, el+1, sizeof(*el)*(msg->num_elements - (i+1)));
2740 msg->num_elements--;
2741 i--;
2742 continue;
2747 * the meta data array is already sorted by the caller
2749 for (i=0; i < md->ctr.ctr1.count; i++) {
2750 md->ctr.ctr1.array[i].local_usn = ar->seq_num;
2752 ndr_err = ndr_push_struct_blob(&md_value, msg,
2753 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
2755 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
2756 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
2757 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
2758 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
2760 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &md_value, NULL);
2761 if (ret != LDB_SUCCESS) {
2762 return replmd_replicated_request_error(ar, ret);
2765 replmd_ldb_message_sort(msg, ar->schema);
2767 if (DEBUGLVL(4)) {
2768 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_ADD, msg);
2769 DEBUG(4, ("DRS replication add message:\n%s\n", s));
2770 talloc_free(s);
2773 ret = ldb_build_add_req(&change_req,
2774 ldb,
2776 msg,
2777 ar->controls,
2779 replmd_op_callback,
2780 ar->req);
2781 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
2783 return ldb_next_request(ar->module, change_req);
2787 return true if an update is newer than an existing entry
2788 see section 5.11 of MS-ADTS
2790 static bool replmd_update_is_newer(const struct GUID *current_invocation_id,
2791 const struct GUID *update_invocation_id,
2792 uint32_t current_version,
2793 uint32_t update_version,
2794 NTTIME current_change_time,
2795 NTTIME update_change_time)
2797 if (update_version != current_version) {
2798 return update_version > current_version;
2800 if (update_change_time > current_change_time) {
2801 return true;
2803 if (update_change_time == current_change_time) {
2804 return GUID_compare(update_invocation_id, current_invocation_id) > 0;
2806 return false;
2809 static bool replmd_replPropertyMetaData1_is_newer(struct replPropertyMetaData1 *cur_m,
2810 struct replPropertyMetaData1 *new_m)
2812 return replmd_update_is_newer(&cur_m->originating_invocation_id,
2813 &new_m->originating_invocation_id,
2814 cur_m->version,
2815 new_m->version,
2816 cur_m->originating_change_time,
2817 new_m->originating_change_time);
2820 static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar)
2822 struct ldb_context *ldb;
2823 struct ldb_request *change_req;
2824 enum ndr_err_code ndr_err;
2825 struct ldb_message *msg;
2826 struct replPropertyMetaDataBlob *rmd;
2827 struct replPropertyMetaDataBlob omd;
2828 const struct ldb_val *omd_value;
2829 struct replPropertyMetaDataBlob nmd;
2830 struct ldb_val nmd_value;
2831 unsigned int i;
2832 uint32_t j,ni=0;
2833 unsigned int removed_attrs = 0;
2834 int ret;
2836 ldb = ldb_module_get_ctx(ar->module);
2837 msg = ar->objs->objects[ar->index_current].msg;
2838 rmd = ar->objs->objects[ar->index_current].meta_data;
2839 ZERO_STRUCT(omd);
2840 omd.version = 1;
2843 * TODO: check repl data is correct after a rename
2845 if (ldb_dn_compare(msg->dn, ar->search_msg->dn) != 0) {
2846 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_request rename %s => %s\n",
2847 ldb_dn_get_linearized(ar->search_msg->dn),
2848 ldb_dn_get_linearized(msg->dn));
2849 if (dsdb_module_rename(ar->module,
2850 ar->search_msg->dn, msg->dn,
2851 DSDB_FLAG_OWN_MODULE) != LDB_SUCCESS) {
2852 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_replicated_request rename %s => %s failed - %s\n",
2853 ldb_dn_get_linearized(ar->search_msg->dn),
2854 ldb_dn_get_linearized(msg->dn),
2855 ldb_errstring(ldb));
2856 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
2860 /* find existing meta data */
2861 omd_value = ldb_msg_find_ldb_val(ar->search_msg, "replPropertyMetaData");
2862 if (omd_value) {
2863 ndr_err = ndr_pull_struct_blob(omd_value, ar,
2864 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")), &omd,
2865 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
2866 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
2867 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
2868 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
2871 if (omd.version != 1) {
2872 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
2876 ZERO_STRUCT(nmd);
2877 nmd.version = 1;
2878 nmd.ctr.ctr1.count = omd.ctr.ctr1.count + rmd->ctr.ctr1.count;
2879 nmd.ctr.ctr1.array = talloc_array(ar,
2880 struct replPropertyMetaData1,
2881 nmd.ctr.ctr1.count);
2882 if (!nmd.ctr.ctr1.array) return replmd_replicated_request_werror(ar, WERR_NOMEM);
2884 /* first copy the old meta data */
2885 for (i=0; i < omd.ctr.ctr1.count; i++) {
2886 nmd.ctr.ctr1.array[ni] = omd.ctr.ctr1.array[i];
2887 ni++;
2890 /* now merge in the new meta data */
2891 for (i=0; i < rmd->ctr.ctr1.count; i++) {
2892 bool found = false;
2894 for (j=0; j < ni; j++) {
2895 bool cmp;
2897 if (rmd->ctr.ctr1.array[i].attid != nmd.ctr.ctr1.array[j].attid) {
2898 continue;
2901 cmp = replmd_replPropertyMetaData1_is_newer(&nmd.ctr.ctr1.array[j],
2902 &rmd->ctr.ctr1.array[i]);
2903 if (cmp) {
2904 /* replace the entry */
2905 nmd.ctr.ctr1.array[j] = rmd->ctr.ctr1.array[i];
2906 found = true;
2907 break;
2910 if (rmd->ctr.ctr1.array[i].attid != DRSUAPI_ATTRIBUTE_instanceType) {
2911 DEBUG(3,("Discarding older DRS attribute update to %s on %s from %s\n",
2912 msg->elements[i-removed_attrs].name,
2913 ldb_dn_get_linearized(msg->dn),
2914 GUID_string(ar, &rmd->ctr.ctr1.array[i].originating_invocation_id)));
2917 /* we don't want to apply this change so remove the attribute */
2918 ldb_msg_remove_element(msg, &msg->elements[i-removed_attrs]);
2919 removed_attrs++;
2921 found = true;
2922 break;
2925 if (found) continue;
2927 nmd.ctr.ctr1.array[ni] = rmd->ctr.ctr1.array[i];
2928 ni++;
2932 * finally correct the size of the meta_data array
2934 nmd.ctr.ctr1.count = ni;
2937 * the rdn attribute (the alias for the name attribute),
2938 * 'cn' for most objects is the last entry in the meta data array
2939 * we have stored
2941 * sort the new meta data array
2943 ret = replmd_replPropertyMetaDataCtr1_sort(&nmd.ctr.ctr1, ar->schema, msg->dn);
2944 if (ret != LDB_SUCCESS) {
2945 return ret;
2949 * check if some replicated attributes left, otherwise skip the ldb_modify() call
2951 if (msg->num_elements == 0) {
2952 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: skip replace\n",
2953 ar->index_current);
2955 ar->index_current++;
2956 return replmd_replicated_apply_next(ar);
2959 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: replace %u attributes\n",
2960 ar->index_current, msg->num_elements);
2962 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
2963 if (ret != LDB_SUCCESS) {
2964 return replmd_replicated_request_error(ar, ret);
2967 for (i=0; i<ni; i++) {
2968 nmd.ctr.ctr1.array[i].local_usn = ar->seq_num;
2971 /* create the meta data value */
2972 ndr_err = ndr_push_struct_blob(&nmd_value, msg,
2973 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
2974 &nmd,
2975 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
2976 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
2977 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
2978 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
2982 * when we know that we'll modify the record, add the whenChanged, uSNChanged
2983 * and replPopertyMetaData attributes
2985 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
2986 if (ret != LDB_SUCCESS) {
2987 return replmd_replicated_request_error(ar, ret);
2989 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
2990 if (ret != LDB_SUCCESS) {
2991 return replmd_replicated_request_error(ar, ret);
2993 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
2994 if (ret != LDB_SUCCESS) {
2995 return replmd_replicated_request_error(ar, ret);
2998 replmd_ldb_message_sort(msg, ar->schema);
3000 /* we want to replace the old values */
3001 for (i=0; i < msg->num_elements; i++) {
3002 msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
3005 if (DEBUGLVL(4)) {
3006 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_MODIFY, msg);
3007 DEBUG(4, ("DRS replication modify message:\n%s\n", s));
3008 talloc_free(s);
3011 ret = ldb_build_mod_req(&change_req,
3012 ldb,
3014 msg,
3015 ar->controls,
3017 replmd_op_callback,
3018 ar->req);
3019 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
3021 return ldb_next_request(ar->module, change_req);
3024 static int replmd_replicated_apply_search_callback(struct ldb_request *req,
3025 struct ldb_reply *ares)
3027 struct replmd_replicated_request *ar = talloc_get_type(req->context,
3028 struct replmd_replicated_request);
3029 int ret;
3031 if (!ares) {
3032 return ldb_module_done(ar->req, NULL, NULL,
3033 LDB_ERR_OPERATIONS_ERROR);
3035 if (ares->error != LDB_SUCCESS &&
3036 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
3037 return ldb_module_done(ar->req, ares->controls,
3038 ares->response, ares->error);
3041 switch (ares->type) {
3042 case LDB_REPLY_ENTRY:
3043 ar->search_msg = talloc_steal(ar, ares->message);
3044 break;
3046 case LDB_REPLY_REFERRAL:
3047 /* we ignore referrals */
3048 break;
3050 case LDB_REPLY_DONE:
3051 if (ar->search_msg != NULL) {
3052 ret = replmd_replicated_apply_merge(ar);
3053 } else {
3054 ret = replmd_replicated_apply_add(ar);
3056 if (ret != LDB_SUCCESS) {
3057 return ldb_module_done(ar->req, NULL, NULL, ret);
3061 talloc_free(ares);
3062 return LDB_SUCCESS;
3065 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar);
3067 static int replmd_replicated_apply_next(struct replmd_replicated_request *ar)
3069 struct ldb_context *ldb;
3070 int ret;
3071 char *tmp_str;
3072 char *filter;
3073 struct ldb_request *search_req;
3074 struct ldb_search_options_control *options;
3076 if (ar->index_current >= ar->objs->num_objects) {
3077 /* done with it, go to next stage */
3078 return replmd_replicated_uptodate_vector(ar);
3081 ldb = ldb_module_get_ctx(ar->module);
3082 ar->search_msg = NULL;
3084 tmp_str = ldb_binary_encode(ar, ar->objs->objects[ar->index_current].guid_value);
3085 if (!tmp_str) return replmd_replicated_request_werror(ar, WERR_NOMEM);
3087 filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
3088 if (!filter) return replmd_replicated_request_werror(ar, WERR_NOMEM);
3089 talloc_free(tmp_str);
3091 ret = ldb_build_search_req(&search_req,
3092 ldb,
3094 NULL,
3095 LDB_SCOPE_SUBTREE,
3096 filter,
3097 NULL,
3098 NULL,
3100 replmd_replicated_apply_search_callback,
3101 ar->req);
3103 ret = ldb_request_add_control(search_req, LDB_CONTROL_SHOW_DELETED_OID, true, NULL);
3104 if (ret != LDB_SUCCESS) {
3105 return ret;
3108 /* we need to cope with cross-partition links, so search for
3109 the GUID over all partitions */
3110 options = talloc(search_req, struct ldb_search_options_control);
3111 if (options == NULL) {
3112 DEBUG(0, (__location__ ": out of memory\n"));
3113 return LDB_ERR_OPERATIONS_ERROR;
3115 options->search_options = LDB_SEARCH_OPTION_PHANTOM_ROOT;
3117 ret = ldb_request_add_control(search_req,
3118 LDB_CONTROL_SEARCH_OPTIONS_OID,
3119 true, options);
3120 if (ret != LDB_SUCCESS) {
3121 return ret;
3124 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
3126 return ldb_next_request(ar->module, search_req);
3129 static int replmd_replicated_uptodate_modify_callback(struct ldb_request *req,
3130 struct ldb_reply *ares)
3132 struct ldb_context *ldb;
3133 struct replmd_replicated_request *ar = talloc_get_type(req->context,
3134 struct replmd_replicated_request);
3135 ldb = ldb_module_get_ctx(ar->module);
3137 if (!ares) {
3138 return ldb_module_done(ar->req, NULL, NULL,
3139 LDB_ERR_OPERATIONS_ERROR);
3141 if (ares->error != LDB_SUCCESS) {
3142 return ldb_module_done(ar->req, ares->controls,
3143 ares->response, ares->error);
3146 if (ares->type != LDB_REPLY_DONE) {
3147 ldb_set_errstring(ldb, "Invalid reply type\n!");
3148 return ldb_module_done(ar->req, NULL, NULL,
3149 LDB_ERR_OPERATIONS_ERROR);
3152 talloc_free(ares);
3154 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
3157 static int replmd_replicated_uptodate_modify(struct replmd_replicated_request *ar)
3159 struct ldb_context *ldb;
3160 struct ldb_request *change_req;
3161 enum ndr_err_code ndr_err;
3162 struct ldb_message *msg;
3163 struct replUpToDateVectorBlob ouv;
3164 const struct ldb_val *ouv_value;
3165 const struct drsuapi_DsReplicaCursor2CtrEx *ruv;
3166 struct replUpToDateVectorBlob nuv;
3167 struct ldb_val nuv_value;
3168 struct ldb_message_element *nuv_el = NULL;
3169 const struct GUID *our_invocation_id;
3170 struct ldb_message_element *orf_el = NULL;
3171 struct repsFromToBlob nrf;
3172 struct ldb_val *nrf_value = NULL;
3173 struct ldb_message_element *nrf_el = NULL;
3174 unsigned int i;
3175 uint32_t j,ni=0;
3176 bool found = false;
3177 time_t t = time(NULL);
3178 NTTIME now;
3179 int ret;
3181 ldb = ldb_module_get_ctx(ar->module);
3182 ruv = ar->objs->uptodateness_vector;
3183 ZERO_STRUCT(ouv);
3184 ouv.version = 2;
3185 ZERO_STRUCT(nuv);
3186 nuv.version = 2;
3188 unix_to_nt_time(&now, t);
3191 * first create the new replUpToDateVector
3193 ouv_value = ldb_msg_find_ldb_val(ar->search_msg, "replUpToDateVector");
3194 if (ouv_value) {
3195 ndr_err = ndr_pull_struct_blob(ouv_value, ar,
3196 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")), &ouv,
3197 (ndr_pull_flags_fn_t)ndr_pull_replUpToDateVectorBlob);
3198 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
3199 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
3200 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
3203 if (ouv.version != 2) {
3204 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
3209 * the new uptodateness vector will at least
3210 * contain 1 entry, one for the source_dsa
3212 * plus optional values from our old vector and the one from the source_dsa
3214 nuv.ctr.ctr2.count = 1 + ouv.ctr.ctr2.count;
3215 if (ruv) nuv.ctr.ctr2.count += ruv->count;
3216 nuv.ctr.ctr2.cursors = talloc_array(ar,
3217 struct drsuapi_DsReplicaCursor2,
3218 nuv.ctr.ctr2.count);
3219 if (!nuv.ctr.ctr2.cursors) return replmd_replicated_request_werror(ar, WERR_NOMEM);
3221 /* first copy the old vector */
3222 for (i=0; i < ouv.ctr.ctr2.count; i++) {
3223 nuv.ctr.ctr2.cursors[ni] = ouv.ctr.ctr2.cursors[i];
3224 ni++;
3227 /* get our invocation_id if we have one already attached to the ldb */
3228 our_invocation_id = samdb_ntds_invocation_id(ldb);
3230 /* merge in the source_dsa vector is available */
3231 for (i=0; (ruv && i < ruv->count); i++) {
3232 found = false;
3234 if (our_invocation_id &&
3235 GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
3236 our_invocation_id)) {
3237 continue;
3240 for (j=0; j < ni; j++) {
3241 if (!GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
3242 &nuv.ctr.ctr2.cursors[j].source_dsa_invocation_id)) {
3243 continue;
3246 found = true;
3249 * we update only the highest_usn and not the latest_sync_success time,
3250 * because the last success stands for direct replication
3252 if (ruv->cursors[i].highest_usn > nuv.ctr.ctr2.cursors[j].highest_usn) {
3253 nuv.ctr.ctr2.cursors[j].highest_usn = ruv->cursors[i].highest_usn;
3255 break;
3258 if (found) continue;
3260 /* if it's not there yet, add it */
3261 nuv.ctr.ctr2.cursors[ni] = ruv->cursors[i];
3262 ni++;
3266 * merge in the current highwatermark for the source_dsa
3268 found = false;
3269 for (j=0; j < ni; j++) {
3270 if (!GUID_equal(&ar->objs->source_dsa->source_dsa_invocation_id,
3271 &nuv.ctr.ctr2.cursors[j].source_dsa_invocation_id)) {
3272 continue;
3275 found = true;
3278 * here we update the highest_usn and last_sync_success time
3279 * because we're directly replicating from the source_dsa
3281 * and use the tmp_highest_usn because this is what we have just applied
3282 * to our ldb
3284 nuv.ctr.ctr2.cursors[j].highest_usn = ar->objs->source_dsa->highwatermark.tmp_highest_usn;
3285 nuv.ctr.ctr2.cursors[j].last_sync_success = now;
3286 break;
3288 if (!found) {
3290 * here we update the highest_usn and last_sync_success time
3291 * because we're directly replicating from the source_dsa
3293 * and use the tmp_highest_usn because this is what we have just applied
3294 * to our ldb
3296 nuv.ctr.ctr2.cursors[ni].source_dsa_invocation_id= ar->objs->source_dsa->source_dsa_invocation_id;
3297 nuv.ctr.ctr2.cursors[ni].highest_usn = ar->objs->source_dsa->highwatermark.tmp_highest_usn;
3298 nuv.ctr.ctr2.cursors[ni].last_sync_success = now;
3299 ni++;
3303 * finally correct the size of the cursors array
3305 nuv.ctr.ctr2.count = ni;
3308 * sort the cursors
3310 TYPESAFE_QSORT(nuv.ctr.ctr2.cursors, nuv.ctr.ctr2.count, drsuapi_DsReplicaCursor2_compare);
3313 * create the change ldb_message
3315 msg = ldb_msg_new(ar);
3316 if (!msg) return replmd_replicated_request_werror(ar, WERR_NOMEM);
3317 msg->dn = ar->search_msg->dn;
3319 ndr_err = ndr_push_struct_blob(&nuv_value, msg,
3320 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
3321 &nuv,
3322 (ndr_push_flags_fn_t)ndr_push_replUpToDateVectorBlob);
3323 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
3324 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
3325 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
3327 ret = ldb_msg_add_value(msg, "replUpToDateVector", &nuv_value, &nuv_el);
3328 if (ret != LDB_SUCCESS) {
3329 return replmd_replicated_request_error(ar, ret);
3331 nuv_el->flags = LDB_FLAG_MOD_REPLACE;
3334 * now create the new repsFrom value from the given repsFromTo1 structure
3336 ZERO_STRUCT(nrf);
3337 nrf.version = 1;
3338 nrf.ctr.ctr1 = *ar->objs->source_dsa;
3339 /* and fix some values... */
3340 nrf.ctr.ctr1.consecutive_sync_failures = 0;
3341 nrf.ctr.ctr1.last_success = now;
3342 nrf.ctr.ctr1.last_attempt = now;
3343 nrf.ctr.ctr1.result_last_attempt = WERR_OK;
3344 nrf.ctr.ctr1.highwatermark.highest_usn = nrf.ctr.ctr1.highwatermark.tmp_highest_usn;
3347 * first see if we already have a repsFrom value for the current source dsa
3348 * if so we'll later replace this value
3350 orf_el = ldb_msg_find_element(ar->search_msg, "repsFrom");
3351 if (orf_el) {
3352 for (i=0; i < orf_el->num_values; i++) {
3353 struct repsFromToBlob *trf;
3355 trf = talloc(ar, struct repsFromToBlob);
3356 if (!trf) return replmd_replicated_request_werror(ar, WERR_NOMEM);
3358 ndr_err = ndr_pull_struct_blob(&orf_el->values[i], trf, lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")), trf,
3359 (ndr_pull_flags_fn_t)ndr_pull_repsFromToBlob);
3360 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
3361 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
3362 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
3365 if (trf->version != 1) {
3366 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
3370 * we compare the source dsa objectGUID not the invocation_id
3371 * because we want only one repsFrom value per source dsa
3372 * and when the invocation_id of the source dsa has changed we don't need
3373 * the old repsFrom with the old invocation_id
3375 if (!GUID_equal(&trf->ctr.ctr1.source_dsa_obj_guid,
3376 &ar->objs->source_dsa->source_dsa_obj_guid)) {
3377 talloc_free(trf);
3378 continue;
3381 talloc_free(trf);
3382 nrf_value = &orf_el->values[i];
3383 break;
3387 * copy over all old values to the new ldb_message
3389 ret = ldb_msg_add_empty(msg, "repsFrom", 0, &nrf_el);
3390 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
3391 *nrf_el = *orf_el;
3395 * if we haven't found an old repsFrom value for the current source dsa
3396 * we'll add a new value
3398 if (!nrf_value) {
3399 struct ldb_val zero_value;
3400 ZERO_STRUCT(zero_value);
3401 ret = ldb_msg_add_value(msg, "repsFrom", &zero_value, &nrf_el);
3402 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
3404 nrf_value = &nrf_el->values[nrf_el->num_values - 1];
3407 /* we now fill the value which is already attached to ldb_message */
3408 ndr_err = ndr_push_struct_blob(nrf_value, msg,
3409 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
3410 &nrf,
3411 (ndr_push_flags_fn_t)ndr_push_repsFromToBlob);
3412 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
3413 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
3414 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
3418 * the ldb_message_element for the attribute, has all the old values and the new one
3419 * so we'll replace the whole attribute with all values
3421 nrf_el->flags = LDB_FLAG_MOD_REPLACE;
3423 if (DEBUGLVL(4)) {
3424 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_MODIFY, msg);
3425 DEBUG(4, ("DRS replication uptodate modify message:\n%s\n", s));
3426 talloc_free(s);
3429 /* prepare the ldb_modify() request */
3430 ret = ldb_build_mod_req(&change_req,
3431 ldb,
3433 msg,
3434 ar->controls,
3436 replmd_replicated_uptodate_modify_callback,
3437 ar->req);
3438 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
3440 return ldb_next_request(ar->module, change_req);
3443 static int replmd_replicated_uptodate_search_callback(struct ldb_request *req,
3444 struct ldb_reply *ares)
3446 struct replmd_replicated_request *ar = talloc_get_type(req->context,
3447 struct replmd_replicated_request);
3448 int ret;
3450 if (!ares) {
3451 return ldb_module_done(ar->req, NULL, NULL,
3452 LDB_ERR_OPERATIONS_ERROR);
3454 if (ares->error != LDB_SUCCESS &&
3455 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
3456 return ldb_module_done(ar->req, ares->controls,
3457 ares->response, ares->error);
3460 switch (ares->type) {
3461 case LDB_REPLY_ENTRY:
3462 ar->search_msg = talloc_steal(ar, ares->message);
3463 break;
3465 case LDB_REPLY_REFERRAL:
3466 /* we ignore referrals */
3467 break;
3469 case LDB_REPLY_DONE:
3470 if (ar->search_msg == NULL) {
3471 ret = replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
3472 } else {
3473 ret = replmd_replicated_uptodate_modify(ar);
3475 if (ret != LDB_SUCCESS) {
3476 return ldb_module_done(ar->req, NULL, NULL, ret);
3480 talloc_free(ares);
3481 return LDB_SUCCESS;
3485 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar)
3487 struct ldb_context *ldb;
3488 int ret;
3489 static const char *attrs[] = {
3490 "replUpToDateVector",
3491 "repsFrom",
3492 NULL
3494 struct ldb_request *search_req;
3496 ldb = ldb_module_get_ctx(ar->module);
3497 ar->search_msg = NULL;
3499 ret = ldb_build_search_req(&search_req,
3500 ldb,
3502 ar->objs->partition_dn,
3503 LDB_SCOPE_BASE,
3504 "(objectClass=*)",
3505 attrs,
3506 NULL,
3508 replmd_replicated_uptodate_search_callback,
3509 ar->req);
3510 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
3512 return ldb_next_request(ar->module, search_req);
3517 static int replmd_extended_replicated_objects(struct ldb_module *module, struct ldb_request *req)
3519 struct ldb_context *ldb;
3520 struct dsdb_extended_replicated_objects *objs;
3521 struct replmd_replicated_request *ar;
3522 struct ldb_control **ctrls;
3523 int ret;
3524 uint32_t i;
3525 struct replmd_private *replmd_private =
3526 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
3528 ldb = ldb_module_get_ctx(module);
3530 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_extended_replicated_objects\n");
3532 objs = talloc_get_type(req->op.extended.data, struct dsdb_extended_replicated_objects);
3533 if (!objs) {
3534 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: invalid extended data\n");
3535 return LDB_ERR_PROTOCOL_ERROR;
3538 if (objs->version != DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION) {
3539 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: extended data invalid version [%u != %u]\n",
3540 objs->version, DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION);
3541 return LDB_ERR_PROTOCOL_ERROR;
3544 ar = replmd_ctx_init(module, req);
3545 if (!ar)
3546 return LDB_ERR_OPERATIONS_ERROR;
3548 /* Set the flags to have the replmd_op_callback run over the full set of objects */
3549 ar->apply_mode = true;
3550 ar->objs = objs;
3551 ar->schema = dsdb_get_schema(ldb, ar);
3552 if (!ar->schema) {
3553 ldb_debug_set(ldb, LDB_DEBUG_FATAL, "replmd_ctx_init: no loaded schema found\n");
3554 talloc_free(ar);
3555 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
3556 return LDB_ERR_CONSTRAINT_VIOLATION;
3559 ctrls = req->controls;
3561 if (req->controls) {
3562 req->controls = talloc_memdup(ar, req->controls,
3563 talloc_get_size(req->controls));
3564 if (!req->controls) return replmd_replicated_request_werror(ar, WERR_NOMEM);
3567 ret = ldb_request_add_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID, false, NULL);
3568 if (ret != LDB_SUCCESS) {
3569 return ret;
3572 ar->controls = req->controls;
3573 req->controls = ctrls;
3575 DEBUG(4,("linked_attributes_count=%u\n", objs->linked_attributes_count));
3577 /* save away the linked attributes for the end of the
3578 transaction */
3579 for (i=0; i<ar->objs->linked_attributes_count; i++) {
3580 struct la_entry *la_entry;
3582 if (replmd_private->la_ctx == NULL) {
3583 replmd_private->la_ctx = talloc_new(replmd_private);
3585 la_entry = talloc(replmd_private->la_ctx, struct la_entry);
3586 if (la_entry == NULL) {
3587 ldb_oom(ldb);
3588 return LDB_ERR_OPERATIONS_ERROR;
3590 la_entry->la = talloc(la_entry, struct drsuapi_DsReplicaLinkedAttribute);
3591 if (la_entry->la == NULL) {
3592 talloc_free(la_entry);
3593 ldb_oom(ldb);
3594 return LDB_ERR_OPERATIONS_ERROR;
3596 *la_entry->la = ar->objs->linked_attributes[i];
3598 /* we need to steal the non-scalars so they stay
3599 around until the end of the transaction */
3600 talloc_steal(la_entry->la, la_entry->la->identifier);
3601 talloc_steal(la_entry->la, la_entry->la->value.blob);
3603 DLIST_ADD(replmd_private->la_list, la_entry);
3606 return replmd_replicated_apply_next(ar);
3610 process one linked attribute structure
3612 static int replmd_process_linked_attribute(struct ldb_module *module,
3613 struct la_entry *la_entry)
3615 struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
3616 struct ldb_context *ldb = ldb_module_get_ctx(module);
3617 struct ldb_message *msg;
3618 TALLOC_CTX *tmp_ctx = talloc_new(la_entry);
3619 const struct dsdb_schema *schema = dsdb_get_schema(ldb, tmp_ctx);
3620 int ret;
3621 const struct dsdb_attribute *attr;
3622 struct dsdb_dn *dsdb_dn;
3623 uint64_t seq_num = 0;
3624 struct ldb_message_element *old_el;
3625 WERROR status;
3626 time_t t = time(NULL);
3627 struct ldb_result *res;
3628 const char *attrs[2];
3629 struct parsed_dn *pdn_list, *pdn;
3630 struct GUID guid = GUID_zero();
3631 NTSTATUS ntstatus;
3632 bool active = (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)?true:false;
3633 const struct GUID *our_invocation_id;
3636 linked_attributes[0]:
3637 &objs->linked_attributes[i]: struct drsuapi_DsReplicaLinkedAttribute
3638 identifier : *
3639 identifier: struct drsuapi_DsReplicaObjectIdentifier
3640 __ndr_size : 0x0000003a (58)
3641 __ndr_size_sid : 0x00000000 (0)
3642 guid : 8e95b6a9-13dd-4158-89db-3220a5be5cc7
3643 sid : S-0-0
3644 __ndr_size_dn : 0x00000000 (0)
3645 dn : ''
3646 attid : DRSUAPI_ATTRIBUTE_member (0x1F)
3647 value: struct drsuapi_DsAttributeValue
3648 __ndr_size : 0x0000007e (126)
3649 blob : *
3650 blob : DATA_BLOB length=126
3651 flags : 0x00000001 (1)
3652 1: DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE
3653 originating_add_time : Wed Sep 2 22:20:01 2009 EST
3654 meta_data: struct drsuapi_DsReplicaMetaData
3655 version : 0x00000015 (21)
3656 originating_change_time : Wed Sep 2 23:39:07 2009 EST
3657 originating_invocation_id: 794640f3-18cf-40ee-a211-a93992b67a64
3658 originating_usn : 0x000000000001e19c (123292)
3660 (for cases where the link is to a normal DN)
3661 &target: struct drsuapi_DsReplicaObjectIdentifier3
3662 __ndr_size : 0x0000007e (126)
3663 __ndr_size_sid : 0x0000001c (28)
3664 guid : 7639e594-db75-4086-b0d4-67890ae46031
3665 sid : S-1-5-21-2848215498-2472035911-1947525656-19924
3666 __ndr_size_dn : 0x00000022 (34)
3667 dn : 'CN=UOne,OU=TestOU,DC=vsofs8,DC=com'
3670 /* find the attribute being modified */
3671 attr = dsdb_attribute_by_attributeID_id(schema, la->attid);
3672 if (attr == NULL) {
3673 DEBUG(0, (__location__ ": Unable to find attributeID 0x%x\n", la->attid));
3674 talloc_free(tmp_ctx);
3675 return LDB_ERR_OPERATIONS_ERROR;
3678 attrs[0] = attr->lDAPDisplayName;
3679 attrs[1] = NULL;
3681 /* get the existing message from the db for the object with
3682 this GUID, returning attribute being modified. We will then
3683 use this msg as the basis for a modify call */
3684 ret = dsdb_module_search(module, tmp_ctx, &res, NULL, LDB_SCOPE_SUBTREE, attrs,
3685 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
3686 DSDB_SEARCH_SHOW_DELETED |
3687 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
3688 DSDB_SEARCH_REVEAL_INTERNALS,
3689 "objectGUID=%s", GUID_string(tmp_ctx, &la->identifier->guid));
3690 if (ret != LDB_SUCCESS) {
3691 talloc_free(tmp_ctx);
3692 return ret;
3694 if (res->count != 1) {
3695 ldb_asprintf_errstring(ldb, "DRS linked attribute for GUID %s - DN not found",
3696 GUID_string(tmp_ctx, &la->identifier->guid));
3697 talloc_free(tmp_ctx);
3698 return LDB_ERR_NO_SUCH_OBJECT;
3700 msg = res->msgs[0];
3702 if (msg->num_elements == 0) {
3703 ret = ldb_msg_add_empty(msg, attr->lDAPDisplayName, LDB_FLAG_MOD_REPLACE, &old_el);
3704 if (ret != LDB_SUCCESS) {
3705 ldb_module_oom(module);
3706 talloc_free(tmp_ctx);
3707 return LDB_ERR_OPERATIONS_ERROR;
3709 } else {
3710 old_el = &msg->elements[0];
3711 old_el->flags = LDB_FLAG_MOD_REPLACE;
3714 /* parse the existing links */
3715 ret = get_parsed_dns(module, tmp_ctx, old_el, &pdn_list, attr->syntax->ldap_oid);
3716 if (ret != LDB_SUCCESS) {
3717 talloc_free(tmp_ctx);
3718 return ret;
3721 /* get our invocationId */
3722 our_invocation_id = samdb_ntds_invocation_id(ldb);
3723 if (!our_invocation_id) {
3724 ldb_debug_set(ldb, LDB_DEBUG_ERROR, __location__ ": unable to find invocationId\n");
3725 talloc_free(tmp_ctx);
3726 return LDB_ERR_OPERATIONS_ERROR;
3729 ret = replmd_check_upgrade_links(pdn_list, old_el->num_values, old_el, our_invocation_id);
3730 if (ret != LDB_SUCCESS) {
3731 talloc_free(tmp_ctx);
3732 return ret;
3735 status = dsdb_dn_la_from_blob(ldb, attr, schema, tmp_ctx, la->value.blob, &dsdb_dn);
3736 if (!W_ERROR_IS_OK(status)) {
3737 ldb_asprintf_errstring(ldb, "Failed to parsed linked attribute blob for %s on %s - %s\n",
3738 old_el->name, ldb_dn_get_linearized(msg->dn), win_errstr(status));
3739 return LDB_ERR_OPERATIONS_ERROR;
3742 ntstatus = dsdb_get_extended_dn_guid(dsdb_dn->dn, &guid, "GUID");
3743 if (!NT_STATUS_IS_OK(ntstatus) && active) {
3744 ldb_asprintf_errstring(ldb, "Failed to find GUID in linked attribute blob for %s on %s from %s",
3745 old_el->name,
3746 ldb_dn_get_linearized(dsdb_dn->dn),
3747 ldb_dn_get_linearized(msg->dn));
3748 return LDB_ERR_OPERATIONS_ERROR;
3751 /* re-resolve the DN by GUID, as the DRS server may give us an
3752 old DN value */
3753 ret = dsdb_module_dn_by_guid(module, dsdb_dn, &guid, &dsdb_dn->dn);
3754 if (ret != LDB_SUCCESS) {
3755 DEBUG(2,(__location__ ": WARNING: Failed to re-resolve GUID %s - using %s",
3756 GUID_string(tmp_ctx, &guid),
3757 ldb_dn_get_linearized(dsdb_dn->dn)));
3760 /* see if this link already exists */
3761 pdn = parsed_dn_find(pdn_list, old_el->num_values, &guid, dsdb_dn->dn);
3762 if (pdn != NULL) {
3763 /* see if this update is newer than what we have already */
3764 struct GUID invocation_id = GUID_zero();
3765 uint32_t version = 0;
3766 NTTIME change_time = 0;
3767 uint32_t rmd_flags = dsdb_dn_rmd_flags(pdn->dsdb_dn->dn);
3769 dsdb_get_extended_dn_guid(pdn->dsdb_dn->dn, &invocation_id, "RMD_INVOCID");
3770 dsdb_get_extended_dn_uint32(pdn->dsdb_dn->dn, &version, "RMD_VERSION");
3771 dsdb_get_extended_dn_nttime(pdn->dsdb_dn->dn, &change_time, "RMD_CHANGETIME");
3773 if (!replmd_update_is_newer(&invocation_id,
3774 &la->meta_data.originating_invocation_id,
3775 version,
3776 la->meta_data.version,
3777 change_time,
3778 la->meta_data.originating_change_time)) {
3779 DEBUG(3,("Discarding older DRS linked attribute update to %s on %s from %s\n",
3780 old_el->name, ldb_dn_get_linearized(msg->dn),
3781 GUID_string(tmp_ctx, &la->meta_data.originating_invocation_id)));
3782 talloc_free(tmp_ctx);
3783 return LDB_SUCCESS;
3786 /* get a seq_num for this change */
3787 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
3788 if (ret != LDB_SUCCESS) {
3789 talloc_free(tmp_ctx);
3790 return ret;
3793 if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
3794 /* remove the existing backlink */
3795 ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid, false, attr, false);
3796 if (ret != LDB_SUCCESS) {
3797 talloc_free(tmp_ctx);
3798 return ret;
3802 ret = replmd_update_la_val(tmp_ctx, pdn->v, dsdb_dn, pdn->dsdb_dn,
3803 &la->meta_data.originating_invocation_id,
3804 la->meta_data.originating_usn, seq_num,
3805 la->meta_data.originating_change_time,
3806 la->meta_data.version,
3807 !active);
3808 if (ret != LDB_SUCCESS) {
3809 talloc_free(tmp_ctx);
3810 return ret;
3813 if (active) {
3814 /* add the new backlink */
3815 ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid, true, attr, false);
3816 if (ret != LDB_SUCCESS) {
3817 talloc_free(tmp_ctx);
3818 return ret;
3821 } else {
3822 /* get a seq_num for this change */
3823 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
3824 if (ret != LDB_SUCCESS) {
3825 talloc_free(tmp_ctx);
3826 return ret;
3829 old_el->values = talloc_realloc(msg->elements, old_el->values,
3830 struct ldb_val, old_el->num_values+1);
3831 if (!old_el->values) {
3832 ldb_module_oom(module);
3833 return LDB_ERR_OPERATIONS_ERROR;
3835 old_el->num_values++;
3837 ret = replmd_build_la_val(tmp_ctx, &old_el->values[old_el->num_values-1], dsdb_dn,
3838 &la->meta_data.originating_invocation_id,
3839 la->meta_data.originating_usn, seq_num,
3840 la->meta_data.originating_change_time,
3841 la->meta_data.version,
3842 (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)?false:true);
3843 if (ret != LDB_SUCCESS) {
3844 talloc_free(tmp_ctx);
3845 return ret;
3848 if (active) {
3849 ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid,
3850 true, attr, false);
3851 if (ret != LDB_SUCCESS) {
3852 talloc_free(tmp_ctx);
3853 return ret;
3858 /* we only change whenChanged and uSNChanged if the seq_num
3859 has changed */
3860 if (add_time_element(msg, "whenChanged", t) != LDB_SUCCESS) {
3861 talloc_free(tmp_ctx);
3862 return LDB_ERR_OPERATIONS_ERROR;
3865 if (add_uint64_element(msg, "uSNChanged", seq_num) != LDB_SUCCESS) {
3866 talloc_free(tmp_ctx);
3867 return LDB_ERR_OPERATIONS_ERROR;
3870 ret = dsdb_check_single_valued_link(attr, old_el);
3871 if (ret != LDB_SUCCESS) {
3872 talloc_free(tmp_ctx);
3873 return ret;
3876 ret = dsdb_module_modify(module, msg, DSDB_MODIFY_RELAX);
3877 if (ret != LDB_SUCCESS) {
3878 ldb_debug(ldb, LDB_DEBUG_WARNING, "Failed to apply linked attribute change '%s'\n%s\n",
3879 ldb_errstring(ldb),
3880 ldb_ldif_message_string(ldb, tmp_ctx, LDB_CHANGETYPE_MODIFY, msg));
3881 talloc_free(tmp_ctx);
3882 return ret;
3885 talloc_free(tmp_ctx);
3887 return ret;
3890 static int replmd_extended(struct ldb_module *module, struct ldb_request *req)
3892 if (strcmp(req->op.extended.oid, DSDB_EXTENDED_REPLICATED_OBJECTS_OID) == 0) {
3893 return replmd_extended_replicated_objects(module, req);
3896 return ldb_next_request(module, req);
3901 we hook into the transaction operations to allow us to
3902 perform the linked attribute updates at the end of the whole
3903 transaction. This allows a forward linked attribute to be created
3904 before the object is created. During a vampire, w2k8 sends us linked
3905 attributes before the objects they are part of.
3907 static int replmd_start_transaction(struct ldb_module *module)
3909 /* create our private structure for this transaction */
3910 struct replmd_private *replmd_private = talloc_get_type(ldb_module_get_private(module),
3911 struct replmd_private);
3912 replmd_txn_cleanup(replmd_private);
3914 /* free any leftover mod_usn records from cancelled
3915 transactions */
3916 while (replmd_private->ncs) {
3917 struct nc_entry *e = replmd_private->ncs;
3918 DLIST_REMOVE(replmd_private->ncs, e);
3919 talloc_free(e);
3922 return ldb_next_start_trans(module);
3926 on prepare commit we loop over our queued la_context structures and
3927 apply each of them
3929 static int replmd_prepare_commit(struct ldb_module *module)
3931 struct replmd_private *replmd_private =
3932 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
3933 struct la_entry *la, *prev;
3934 struct la_backlink *bl;
3935 int ret;
3937 /* walk the list backwards, to do the first entry first, as we
3938 * added the entries with DLIST_ADD() which puts them at the
3939 * start of the list */
3940 for (la = DLIST_TAIL(replmd_private->la_list); la; la=prev) {
3941 prev = DLIST_PREV(la);
3942 DLIST_REMOVE(replmd_private->la_list, la);
3943 ret = replmd_process_linked_attribute(module, la);
3944 if (ret != LDB_SUCCESS) {
3945 replmd_txn_cleanup(replmd_private);
3946 return ret;
3950 /* process our backlink list, creating and deleting backlinks
3951 as necessary */
3952 for (bl=replmd_private->la_backlinks; bl; bl=bl->next) {
3953 ret = replmd_process_backlink(module, bl);
3954 if (ret != LDB_SUCCESS) {
3955 replmd_txn_cleanup(replmd_private);
3956 return ret;
3960 replmd_txn_cleanup(replmd_private);
3962 /* possibly change @REPLCHANGED */
3963 ret = replmd_notify_store(module);
3964 if (ret != LDB_SUCCESS) {
3965 return ret;
3968 return ldb_next_prepare_commit(module);
3971 static int replmd_del_transaction(struct ldb_module *module)
3973 struct replmd_private *replmd_private =
3974 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
3975 replmd_txn_cleanup(replmd_private);
3977 return ldb_next_del_trans(module);
3981 _PUBLIC_ const struct ldb_module_ops ldb_repl_meta_data_module_ops = {
3982 .name = "repl_meta_data",
3983 .init_context = replmd_init,
3984 .add = replmd_add,
3985 .modify = replmd_modify,
3986 .rename = replmd_rename,
3987 .del = replmd_delete,
3988 .extended = replmd_extended,
3989 .start_transaction = replmd_start_transaction,
3990 .prepare_commit = replmd_prepare_commit,
3991 .del_transaction = replmd_del_transaction,