s4-drs: make links to foreign partitions non-fatal
[Samba/gebeck_regimport.git] / source4 / dsdb / samdb / ldb_modules / repl_meta_data.c
blob9dcf24a6cba49c16e8f2557ef1bb2440bac11bd1
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_message *msg, uint64_t *seq_num,
1080 time_t t,
1081 bool *is_urgent)
1083 const struct ldb_val *omd_value;
1084 enum ndr_err_code ndr_err;
1085 struct replPropertyMetaDataBlob omd;
1086 unsigned int i;
1087 NTTIME now;
1088 const struct GUID *our_invocation_id;
1089 int ret;
1090 const char *attrs[] = { "replPropertyMetaData", "*", NULL };
1091 struct ldb_result *res;
1092 struct ldb_context *ldb;
1093 struct ldb_message_element *objectclass_el;
1094 enum urgent_situation situation;
1096 ldb = ldb_module_get_ctx(module);
1098 our_invocation_id = samdb_ntds_invocation_id(ldb);
1099 if (!our_invocation_id) {
1100 /* this happens during an initial vampire while
1101 updating the schema */
1102 DEBUG(5,("No invocationID - skipping replPropertyMetaData update\n"));
1103 return LDB_SUCCESS;
1106 unix_to_nt_time(&now, t);
1108 /* search for the existing replPropertyMetaDataBlob. We need
1109 * to use REVEAL and ask for DNs in storage format to support
1110 * the check for values being the same in
1111 * replmd_update_rpmd_element()
1113 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attrs,
1114 DSDB_SEARCH_SHOW_DELETED |
1115 DSDB_SEARCH_SHOW_EXTENDED_DN |
1116 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
1117 DSDB_SEARCH_REVEAL_INTERNALS);
1118 if (ret != LDB_SUCCESS || res->count != 1) {
1119 DEBUG(0,(__location__ ": Object %s failed to find replPropertyMetaData\n",
1120 ldb_dn_get_linearized(msg->dn)));
1121 return LDB_ERR_OPERATIONS_ERROR;
1124 /* if isDeleted is present and is TRUE, then we consider we are deleting,
1125 * otherwise we consider we are updating */
1126 if (ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")) {
1127 situation = REPL_URGENT_ON_DELETE;
1128 } else {
1129 situation = REPL_URGENT_ON_UPDATE;
1132 objectclass_el = ldb_msg_find_element(res->msgs[0], "objectClass");
1133 if (is_urgent && replmd_check_urgent_objectclass(objectclass_el,
1134 situation)) {
1135 *is_urgent = true;
1138 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
1139 if (!omd_value) {
1140 DEBUG(0,(__location__ ": Object %s does not have a replPropertyMetaData attribute\n",
1141 ldb_dn_get_linearized(msg->dn)));
1142 return LDB_ERR_OPERATIONS_ERROR;
1145 ndr_err = ndr_pull_struct_blob(omd_value, msg,
1146 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")), &omd,
1147 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
1148 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1149 DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s\n",
1150 ldb_dn_get_linearized(msg->dn)));
1151 return LDB_ERR_OPERATIONS_ERROR;
1154 if (omd.version != 1) {
1155 DEBUG(0,(__location__ ": bad version %u in replPropertyMetaData for %s\n",
1156 omd.version, ldb_dn_get_linearized(msg->dn)));
1157 return LDB_ERR_OPERATIONS_ERROR;
1160 for (i=0; i<msg->num_elements; i++) {
1161 struct ldb_message_element *old_el;
1162 old_el = ldb_msg_find_element(res->msgs[0], msg->elements[i].name);
1163 ret = replmd_update_rpmd_element(ldb, msg, &msg->elements[i], old_el, &omd, schema, seq_num,
1164 our_invocation_id, now);
1165 if (ret != LDB_SUCCESS) {
1166 return ret;
1169 if (is_urgent && !*is_urgent && (situation == REPL_URGENT_ON_UPDATE)) {
1170 *is_urgent = replmd_check_urgent_attribute(&msg->elements[i]);
1176 * replmd_update_rpmd_element has done an update if the
1177 * seq_num is set
1179 if (*seq_num != 0) {
1180 struct ldb_val *md_value;
1181 struct ldb_message_element *el;
1183 md_value = talloc(msg, struct ldb_val);
1184 if (md_value == NULL) {
1185 ldb_oom(ldb);
1186 return LDB_ERR_OPERATIONS_ERROR;
1189 ret = replmd_replPropertyMetaDataCtr1_sort(&omd.ctr.ctr1, schema, msg->dn);
1190 if (ret != LDB_SUCCESS) {
1191 return ret;
1194 ndr_err = ndr_push_struct_blob(md_value, msg,
1195 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
1196 &omd,
1197 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
1198 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1199 DEBUG(0,(__location__ ": Failed to marshall replPropertyMetaData for %s\n",
1200 ldb_dn_get_linearized(msg->dn)));
1201 return LDB_ERR_OPERATIONS_ERROR;
1204 ret = ldb_msg_add_empty(msg, "replPropertyMetaData", LDB_FLAG_MOD_REPLACE, &el);
1205 if (ret != LDB_SUCCESS) {
1206 DEBUG(0,(__location__ ": Failed to add updated replPropertyMetaData %s\n",
1207 ldb_dn_get_linearized(msg->dn)));
1208 return ret;
1211 el->num_values = 1;
1212 el->values = md_value;
1215 return LDB_SUCCESS;
1218 struct parsed_dn {
1219 struct dsdb_dn *dsdb_dn;
1220 struct GUID *guid;
1221 struct ldb_val *v;
1224 static int parsed_dn_compare(struct parsed_dn *pdn1, struct parsed_dn *pdn2)
1226 return GUID_compare(pdn1->guid, pdn2->guid);
1229 static struct parsed_dn *parsed_dn_find(struct parsed_dn *pdn, int count, struct GUID *guid, struct ldb_dn *dn)
1231 struct parsed_dn *ret;
1232 if (dn && GUID_all_zero(guid)) {
1233 /* when updating a link using DRS, we sometimes get a
1234 NULL GUID. We then need to try and match by DN */
1235 int i;
1236 for (i=0; i<count; i++) {
1237 if (ldb_dn_compare(pdn[i].dsdb_dn->dn, dn) == 0) {
1238 dsdb_get_extended_dn_guid(pdn[i].dsdb_dn->dn, guid, "GUID");
1239 return &pdn[i];
1242 return NULL;
1244 BINARY_ARRAY_SEARCH(pdn, count, guid, guid, GUID_compare, ret);
1245 return ret;
1249 get a series of message element values as an array of DNs and GUIDs
1250 the result is sorted by GUID
1252 static int get_parsed_dns(struct ldb_module *module, TALLOC_CTX *mem_ctx,
1253 struct ldb_message_element *el, struct parsed_dn **pdn,
1254 const char *ldap_oid)
1256 unsigned int i;
1257 struct ldb_context *ldb = ldb_module_get_ctx(module);
1259 if (el == NULL) {
1260 *pdn = NULL;
1261 return LDB_SUCCESS;
1264 (*pdn) = talloc_array(mem_ctx, struct parsed_dn, el->num_values);
1265 if (!*pdn) {
1266 ldb_module_oom(module);
1267 return LDB_ERR_OPERATIONS_ERROR;
1270 for (i=0; i<el->num_values; i++) {
1271 struct ldb_val *v = &el->values[i];
1272 NTSTATUS status;
1273 struct ldb_dn *dn;
1274 struct parsed_dn *p;
1276 p = &(*pdn)[i];
1278 p->dsdb_dn = dsdb_dn_parse(*pdn, ldb, v, ldap_oid);
1279 if (p->dsdb_dn == NULL) {
1280 return LDB_ERR_INVALID_DN_SYNTAX;
1283 dn = p->dsdb_dn->dn;
1285 p->guid = talloc(*pdn, struct GUID);
1286 if (p->guid == NULL) {
1287 ldb_module_oom(module);
1288 return LDB_ERR_OPERATIONS_ERROR;
1291 status = dsdb_get_extended_dn_guid(dn, p->guid, "GUID");
1292 if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
1293 /* we got a DN without a GUID - go find the GUID */
1294 int ret = dsdb_module_guid_by_dn(module, dn, p->guid);
1295 if (ret != LDB_SUCCESS) {
1296 ldb_asprintf_errstring(ldb, "Unable to find GUID for DN %s\n",
1297 ldb_dn_get_linearized(dn));
1298 return ret;
1300 ret = dsdb_set_extended_dn_guid(dn, p->guid, "GUID");
1301 if (ret != LDB_SUCCESS) {
1302 return ret;
1304 } else if (!NT_STATUS_IS_OK(status)) {
1305 return LDB_ERR_OPERATIONS_ERROR;
1308 /* keep a pointer to the original ldb_val */
1309 p->v = v;
1312 TYPESAFE_QSORT(*pdn, el->num_values, parsed_dn_compare);
1314 return LDB_SUCCESS;
1318 build a new extended DN, including all meta data fields
1320 RMD_FLAGS = DSDB_RMD_FLAG_* bits
1321 RMD_ADDTIME = originating_add_time
1322 RMD_INVOCID = originating_invocation_id
1323 RMD_CHANGETIME = originating_change_time
1324 RMD_ORIGINATING_USN = originating_usn
1325 RMD_LOCAL_USN = local_usn
1326 RMD_VERSION = version
1328 static int replmd_build_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
1329 const struct GUID *invocation_id, uint64_t seq_num,
1330 uint64_t local_usn, NTTIME nttime, uint32_t version, bool deleted)
1332 struct ldb_dn *dn = dsdb_dn->dn;
1333 const char *tstring, *usn_string, *flags_string;
1334 struct ldb_val tval;
1335 struct ldb_val iid;
1336 struct ldb_val usnv, local_usnv;
1337 struct ldb_val vers, flagsv;
1338 NTSTATUS status;
1339 int ret;
1340 const char *dnstring;
1341 char *vstring;
1342 uint32_t rmd_flags = deleted?DSDB_RMD_FLAG_DELETED:0;
1344 tstring = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)nttime);
1345 if (!tstring) {
1346 return LDB_ERR_OPERATIONS_ERROR;
1348 tval = data_blob_string_const(tstring);
1350 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)seq_num);
1351 if (!usn_string) {
1352 return LDB_ERR_OPERATIONS_ERROR;
1354 usnv = data_blob_string_const(usn_string);
1356 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)local_usn);
1357 if (!usn_string) {
1358 return LDB_ERR_OPERATIONS_ERROR;
1360 local_usnv = data_blob_string_const(usn_string);
1362 vstring = talloc_asprintf(mem_ctx, "%lu", (unsigned long)version);
1363 if (!vstring) {
1364 return LDB_ERR_OPERATIONS_ERROR;
1366 vers = data_blob_string_const(vstring);
1368 status = GUID_to_ndr_blob(invocation_id, dn, &iid);
1369 if (!NT_STATUS_IS_OK(status)) {
1370 return LDB_ERR_OPERATIONS_ERROR;
1373 flags_string = talloc_asprintf(mem_ctx, "%u", rmd_flags);
1374 if (!flags_string) {
1375 return LDB_ERR_OPERATIONS_ERROR;
1377 flagsv = data_blob_string_const(flags_string);
1379 ret = ldb_dn_set_extended_component(dn, "RMD_FLAGS", &flagsv);
1380 if (ret != LDB_SUCCESS) return ret;
1381 ret = ldb_dn_set_extended_component(dn, "RMD_ADDTIME", &tval);
1382 if (ret != LDB_SUCCESS) return ret;
1383 ret = ldb_dn_set_extended_component(dn, "RMD_INVOCID", &iid);
1384 if (ret != LDB_SUCCESS) return ret;
1385 ret = ldb_dn_set_extended_component(dn, "RMD_CHANGETIME", &tval);
1386 if (ret != LDB_SUCCESS) return ret;
1387 ret = ldb_dn_set_extended_component(dn, "RMD_LOCAL_USN", &local_usnv);
1388 if (ret != LDB_SUCCESS) return ret;
1389 ret = ldb_dn_set_extended_component(dn, "RMD_ORIGINATING_USN", &usnv);
1390 if (ret != LDB_SUCCESS) return ret;
1391 ret = ldb_dn_set_extended_component(dn, "RMD_VERSION", &vers);
1392 if (ret != LDB_SUCCESS) return ret;
1394 dnstring = dsdb_dn_get_extended_linearized(mem_ctx, dsdb_dn, 1);
1395 if (dnstring == NULL) {
1396 return LDB_ERR_OPERATIONS_ERROR;
1398 *v = data_blob_string_const(dnstring);
1400 return LDB_SUCCESS;
1403 static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
1404 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
1405 uint64_t seq_num, uint64_t local_usn, NTTIME nttime,
1406 uint32_t version, bool deleted);
1409 check if any links need upgrading from w2k format
1411 static int replmd_check_upgrade_links(struct parsed_dn *dns, uint32_t count, const struct GUID *invocation_id)
1413 uint32_t i;
1414 for (i=0; i<count; i++) {
1415 NTSTATUS status;
1416 uint32_t version;
1417 int ret;
1419 status = dsdb_get_extended_dn_uint32(dns[i].dsdb_dn->dn, &version, "RMD_VERSION");
1420 if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
1421 continue;
1424 /* it's an old one that needs upgrading */
1425 ret = replmd_update_la_val(dns, dns[i].v, dns[i].dsdb_dn, dns[i].dsdb_dn, invocation_id,
1426 1, 1, 0, 0, false);
1427 if (ret != LDB_SUCCESS) {
1428 return ret;
1431 return LDB_SUCCESS;
1435 update an extended DN, including all meta data fields
1437 see replmd_build_la_val for value names
1439 static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
1440 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
1441 uint64_t seq_num, uint64_t local_usn, NTTIME nttime,
1442 uint32_t version, bool deleted)
1444 struct ldb_dn *dn = dsdb_dn->dn;
1445 const char *tstring, *usn_string, *flags_string;
1446 struct ldb_val tval;
1447 struct ldb_val iid;
1448 struct ldb_val usnv, local_usnv;
1449 struct ldb_val vers, flagsv;
1450 const struct ldb_val *old_addtime;
1451 uint32_t old_version;
1452 NTSTATUS status;
1453 int ret;
1454 const char *dnstring;
1455 char *vstring;
1456 uint32_t rmd_flags = deleted?DSDB_RMD_FLAG_DELETED:0;
1458 tstring = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)nttime);
1459 if (!tstring) {
1460 return LDB_ERR_OPERATIONS_ERROR;
1462 tval = data_blob_string_const(tstring);
1464 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)seq_num);
1465 if (!usn_string) {
1466 return LDB_ERR_OPERATIONS_ERROR;
1468 usnv = data_blob_string_const(usn_string);
1470 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)local_usn);
1471 if (!usn_string) {
1472 return LDB_ERR_OPERATIONS_ERROR;
1474 local_usnv = data_blob_string_const(usn_string);
1476 status = GUID_to_ndr_blob(invocation_id, dn, &iid);
1477 if (!NT_STATUS_IS_OK(status)) {
1478 return LDB_ERR_OPERATIONS_ERROR;
1481 flags_string = talloc_asprintf(mem_ctx, "%u", rmd_flags);
1482 if (!flags_string) {
1483 return LDB_ERR_OPERATIONS_ERROR;
1485 flagsv = data_blob_string_const(flags_string);
1487 ret = ldb_dn_set_extended_component(dn, "RMD_FLAGS", &flagsv);
1488 if (ret != LDB_SUCCESS) return ret;
1490 /* get the ADDTIME from the original */
1491 old_addtime = ldb_dn_get_extended_component(old_dsdb_dn->dn, "RMD_ADDTIME");
1492 if (old_addtime == NULL) {
1493 old_addtime = &tval;
1495 if (dsdb_dn != old_dsdb_dn) {
1496 ret = ldb_dn_set_extended_component(dn, "RMD_ADDTIME", old_addtime);
1497 if (ret != LDB_SUCCESS) return ret;
1500 /* use our invocation id */
1501 ret = ldb_dn_set_extended_component(dn, "RMD_INVOCID", &iid);
1502 if (ret != LDB_SUCCESS) return ret;
1504 /* changetime is the current time */
1505 ret = ldb_dn_set_extended_component(dn, "RMD_CHANGETIME", &tval);
1506 if (ret != LDB_SUCCESS) return ret;
1508 /* update the USN */
1509 ret = ldb_dn_set_extended_component(dn, "RMD_ORIGINATING_USN", &usnv);
1510 if (ret != LDB_SUCCESS) return ret;
1512 ret = ldb_dn_set_extended_component(dn, "RMD_LOCAL_USN", &local_usnv);
1513 if (ret != LDB_SUCCESS) return ret;
1515 /* increase the version by 1 */
1516 status = dsdb_get_extended_dn_uint32(old_dsdb_dn->dn, &old_version, "RMD_VERSION");
1517 if (NT_STATUS_IS_OK(status) && old_version >= version) {
1518 version = old_version+1;
1520 vstring = talloc_asprintf(dn, "%lu", (unsigned long)version);
1521 vers = data_blob_string_const(vstring);
1522 ret = ldb_dn_set_extended_component(dn, "RMD_VERSION", &vers);
1523 if (ret != LDB_SUCCESS) return ret;
1525 dnstring = dsdb_dn_get_extended_linearized(mem_ctx, dsdb_dn, 1);
1526 if (dnstring == NULL) {
1527 return LDB_ERR_OPERATIONS_ERROR;
1529 *v = data_blob_string_const(dnstring);
1531 return LDB_SUCCESS;
1535 handle adding a linked attribute
1537 static int replmd_modify_la_add(struct ldb_module *module,
1538 const struct dsdb_schema *schema,
1539 struct ldb_message *msg,
1540 struct ldb_message_element *el,
1541 struct ldb_message_element *old_el,
1542 const struct dsdb_attribute *schema_attr,
1543 uint64_t seq_num,
1544 time_t t,
1545 struct GUID *msg_guid)
1547 unsigned int i;
1548 struct parsed_dn *dns, *old_dns;
1549 TALLOC_CTX *tmp_ctx = talloc_new(msg);
1550 int ret;
1551 struct ldb_val *new_values = NULL;
1552 unsigned int num_new_values = 0;
1553 unsigned old_num_values = old_el?old_el->num_values:0;
1554 const struct GUID *invocation_id;
1555 struct ldb_context *ldb = ldb_module_get_ctx(module);
1556 NTTIME now;
1558 unix_to_nt_time(&now, t);
1560 ret = get_parsed_dns(module, tmp_ctx, el, &dns, schema_attr->syntax->ldap_oid);
1561 if (ret != LDB_SUCCESS) {
1562 talloc_free(tmp_ctx);
1563 return ret;
1566 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns, schema_attr->syntax->ldap_oid);
1567 if (ret != LDB_SUCCESS) {
1568 talloc_free(tmp_ctx);
1569 return ret;
1572 invocation_id = samdb_ntds_invocation_id(ldb);
1573 if (!invocation_id) {
1574 talloc_free(tmp_ctx);
1575 return LDB_ERR_OPERATIONS_ERROR;
1578 ret = replmd_check_upgrade_links(old_dns, old_num_values, invocation_id);
1579 if (ret != LDB_SUCCESS) {
1580 talloc_free(tmp_ctx);
1581 return ret;
1584 /* for each new value, see if it exists already with the same GUID */
1585 for (i=0; i<el->num_values; i++) {
1586 struct parsed_dn *p = parsed_dn_find(old_dns, old_num_values, dns[i].guid, NULL);
1587 if (p == NULL) {
1588 /* this is a new linked attribute value */
1589 new_values = talloc_realloc(tmp_ctx, new_values, struct ldb_val, num_new_values+1);
1590 if (new_values == NULL) {
1591 ldb_module_oom(module);
1592 talloc_free(tmp_ctx);
1593 return LDB_ERR_OPERATIONS_ERROR;
1595 ret = replmd_build_la_val(new_values, &new_values[num_new_values], dns[i].dsdb_dn,
1596 invocation_id, seq_num, seq_num, now, 0, false);
1597 if (ret != LDB_SUCCESS) {
1598 talloc_free(tmp_ctx);
1599 return ret;
1601 num_new_values++;
1602 } else {
1603 /* this is only allowed if the GUID was
1604 previously deleted. */
1605 uint32_t rmd_flags = dsdb_dn_rmd_flags(p->dsdb_dn->dn);
1607 if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
1608 ldb_asprintf_errstring(ldb, "Attribute %s already exists for target GUID %s",
1609 el->name, GUID_string(tmp_ctx, p->guid));
1610 talloc_free(tmp_ctx);
1611 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
1613 ret = replmd_update_la_val(old_el->values, p->v, dns[i].dsdb_dn, p->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;
1621 ret = replmd_add_backlink(module, schema, msg_guid, dns[i].guid, true, schema_attr, true);
1622 if (ret != LDB_SUCCESS) {
1623 talloc_free(tmp_ctx);
1624 return ret;
1628 /* add the new ones on to the end of the old values, constructing a new el->values */
1629 el->values = talloc_realloc(msg->elements, old_el?old_el->values:NULL,
1630 struct ldb_val,
1631 old_num_values+num_new_values);
1632 if (el->values == NULL) {
1633 ldb_module_oom(module);
1634 return LDB_ERR_OPERATIONS_ERROR;
1637 memcpy(&el->values[old_num_values], new_values, num_new_values*sizeof(struct ldb_val));
1638 el->num_values = old_num_values + num_new_values;
1640 talloc_steal(msg->elements, el->values);
1641 talloc_steal(el->values, new_values);
1643 talloc_free(tmp_ctx);
1645 /* we now tell the backend to replace all existing values
1646 with the one we have constructed */
1647 el->flags = LDB_FLAG_MOD_REPLACE;
1649 return LDB_SUCCESS;
1654 handle deleting all active linked attributes
1656 static int replmd_modify_la_delete(struct ldb_module *module,
1657 const struct dsdb_schema *schema,
1658 struct ldb_message *msg,
1659 struct ldb_message_element *el,
1660 struct ldb_message_element *old_el,
1661 const struct dsdb_attribute *schema_attr,
1662 uint64_t seq_num,
1663 time_t t,
1664 struct GUID *msg_guid)
1666 unsigned int i;
1667 struct parsed_dn *dns, *old_dns;
1668 TALLOC_CTX *tmp_ctx = talloc_new(msg);
1669 int ret;
1670 const struct GUID *invocation_id;
1671 struct ldb_context *ldb = ldb_module_get_ctx(module);
1672 NTTIME now;
1674 unix_to_nt_time(&now, t);
1676 /* check if there is nothing to delete */
1677 if ((!old_el || old_el->num_values == 0) &&
1678 el->num_values == 0) {
1679 return LDB_SUCCESS;
1682 if (!old_el || old_el->num_values == 0) {
1683 return LDB_ERR_NO_SUCH_ATTRIBUTE;
1686 ret = get_parsed_dns(module, tmp_ctx, el, &dns, schema_attr->syntax->ldap_oid);
1687 if (ret != LDB_SUCCESS) {
1688 talloc_free(tmp_ctx);
1689 return ret;
1692 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns, schema_attr->syntax->ldap_oid);
1693 if (ret != LDB_SUCCESS) {
1694 talloc_free(tmp_ctx);
1695 return ret;
1698 invocation_id = samdb_ntds_invocation_id(ldb);
1699 if (!invocation_id) {
1700 return LDB_ERR_OPERATIONS_ERROR;
1703 ret = replmd_check_upgrade_links(old_dns, old_el->num_values, invocation_id);
1704 if (ret != LDB_SUCCESS) {
1705 talloc_free(tmp_ctx);
1706 return ret;
1709 el->values = NULL;
1711 /* see if we are being asked to delete any links that
1712 don't exist or are already deleted */
1713 for (i=0; i<el->num_values; i++) {
1714 struct parsed_dn *p = &dns[i];
1715 struct parsed_dn *p2;
1716 uint32_t rmd_flags;
1718 p2 = parsed_dn_find(old_dns, old_el->num_values, p->guid, NULL);
1719 if (!p2) {
1720 ldb_asprintf_errstring(ldb, "Attribute %s doesn't exist for target GUID %s",
1721 el->name, GUID_string(tmp_ctx, p->guid));
1722 return LDB_ERR_NO_SUCH_ATTRIBUTE;
1724 rmd_flags = dsdb_dn_rmd_flags(p2->dsdb_dn->dn);
1725 if (rmd_flags & DSDB_RMD_FLAG_DELETED) {
1726 ldb_asprintf_errstring(ldb, "Attribute %s already deleted for target GUID %s",
1727 el->name, GUID_string(tmp_ctx, p->guid));
1728 return LDB_ERR_NO_SUCH_ATTRIBUTE;
1732 /* for each new value, see if it exists already with the same GUID
1733 if it is not already deleted and matches the delete list then delete it
1735 for (i=0; i<old_el->num_values; i++) {
1736 struct parsed_dn *p = &old_dns[i];
1737 uint32_t rmd_flags;
1739 if (el->num_values && parsed_dn_find(dns, el->num_values, p->guid, NULL) == NULL) {
1740 continue;
1743 rmd_flags = dsdb_dn_rmd_flags(p->dsdb_dn->dn);
1744 if (rmd_flags & DSDB_RMD_FLAG_DELETED) continue;
1746 ret = replmd_update_la_val(old_el->values, p->v, p->dsdb_dn, p->dsdb_dn,
1747 invocation_id, seq_num, seq_num, now, 0, true);
1748 if (ret != LDB_SUCCESS) {
1749 talloc_free(tmp_ctx);
1750 return ret;
1753 ret = replmd_add_backlink(module, schema, msg_guid, old_dns[i].guid, false, schema_attr, true);
1754 if (ret != LDB_SUCCESS) {
1755 talloc_free(tmp_ctx);
1756 return ret;
1760 el->values = talloc_steal(msg->elements, old_el->values);
1761 el->num_values = old_el->num_values;
1763 talloc_free(tmp_ctx);
1765 /* we now tell the backend to replace all existing values
1766 with the one we have constructed */
1767 el->flags = LDB_FLAG_MOD_REPLACE;
1769 return LDB_SUCCESS;
1773 handle replacing a linked attribute
1775 static int replmd_modify_la_replace(struct ldb_module *module,
1776 const struct dsdb_schema *schema,
1777 struct ldb_message *msg,
1778 struct ldb_message_element *el,
1779 struct ldb_message_element *old_el,
1780 const struct dsdb_attribute *schema_attr,
1781 uint64_t seq_num,
1782 time_t t,
1783 struct GUID *msg_guid)
1785 unsigned int i;
1786 struct parsed_dn *dns, *old_dns;
1787 TALLOC_CTX *tmp_ctx = talloc_new(msg);
1788 int ret;
1789 const struct GUID *invocation_id;
1790 struct ldb_context *ldb = ldb_module_get_ctx(module);
1791 struct ldb_val *new_values = NULL;
1792 unsigned int num_new_values = 0;
1793 unsigned int old_num_values = old_el?old_el->num_values:0;
1794 NTTIME now;
1796 unix_to_nt_time(&now, t);
1798 /* check if there is nothing to replace */
1799 if ((!old_el || old_el->num_values == 0) &&
1800 el->num_values == 0) {
1801 return LDB_SUCCESS;
1804 ret = get_parsed_dns(module, tmp_ctx, el, &dns, schema_attr->syntax->ldap_oid);
1805 if (ret != LDB_SUCCESS) {
1806 talloc_free(tmp_ctx);
1807 return ret;
1810 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns, schema_attr->syntax->ldap_oid);
1811 if (ret != LDB_SUCCESS) {
1812 talloc_free(tmp_ctx);
1813 return ret;
1816 invocation_id = samdb_ntds_invocation_id(ldb);
1817 if (!invocation_id) {
1818 return LDB_ERR_OPERATIONS_ERROR;
1821 ret = replmd_check_upgrade_links(old_dns, old_num_values, invocation_id);
1822 if (ret != LDB_SUCCESS) {
1823 talloc_free(tmp_ctx);
1824 return ret;
1827 /* mark all the old ones as deleted */
1828 for (i=0; i<old_num_values; i++) {
1829 struct parsed_dn *old_p = &old_dns[i];
1830 struct parsed_dn *p;
1831 uint32_t rmd_flags = dsdb_dn_rmd_flags(old_p->dsdb_dn->dn);
1833 if (rmd_flags & DSDB_RMD_FLAG_DELETED) continue;
1835 ret = replmd_add_backlink(module, schema, msg_guid, old_dns[i].guid, false, schema_attr, false);
1836 if (ret != LDB_SUCCESS) {
1837 talloc_free(tmp_ctx);
1838 return ret;
1841 p = parsed_dn_find(dns, el->num_values, old_p->guid, NULL);
1842 if (p) {
1843 /* we don't delete it if we are re-adding it */
1844 continue;
1847 ret = replmd_update_la_val(old_el->values, old_p->v, old_p->dsdb_dn, old_p->dsdb_dn,
1848 invocation_id, seq_num, seq_num, now, 0, true);
1849 if (ret != LDB_SUCCESS) {
1850 talloc_free(tmp_ctx);
1851 return ret;
1855 /* for each new value, either update its meta-data, or add it
1856 * to old_el
1858 for (i=0; i<el->num_values; i++) {
1859 struct parsed_dn *p = &dns[i], *old_p;
1861 if (old_dns &&
1862 (old_p = parsed_dn_find(old_dns,
1863 old_num_values, p->guid, NULL)) != NULL) {
1864 /* update in place */
1865 ret = replmd_update_la_val(old_el->values, old_p->v, old_p->dsdb_dn,
1866 old_p->dsdb_dn, invocation_id,
1867 seq_num, seq_num, now, 0, false);
1868 if (ret != LDB_SUCCESS) {
1869 talloc_free(tmp_ctx);
1870 return ret;
1872 } else {
1873 /* add a new one */
1874 new_values = talloc_realloc(tmp_ctx, new_values, struct ldb_val,
1875 num_new_values+1);
1876 if (new_values == NULL) {
1877 ldb_module_oom(module);
1878 talloc_free(tmp_ctx);
1879 return LDB_ERR_OPERATIONS_ERROR;
1881 ret = replmd_build_la_val(new_values, &new_values[num_new_values], dns[i].dsdb_dn,
1882 invocation_id, seq_num, seq_num, now, 0, false);
1883 if (ret != LDB_SUCCESS) {
1884 talloc_free(tmp_ctx);
1885 return ret;
1887 num_new_values++;
1890 ret = replmd_add_backlink(module, schema, msg_guid, dns[i].guid, true, schema_attr, false);
1891 if (ret != LDB_SUCCESS) {
1892 talloc_free(tmp_ctx);
1893 return ret;
1897 /* add the new values to the end of old_el */
1898 if (num_new_values != 0) {
1899 el->values = talloc_realloc(msg->elements, old_el?old_el->values:NULL,
1900 struct ldb_val, old_num_values+num_new_values);
1901 if (el->values == NULL) {
1902 ldb_module_oom(module);
1903 return LDB_ERR_OPERATIONS_ERROR;
1905 memcpy(&el->values[old_num_values], &new_values[0],
1906 sizeof(struct ldb_val)*num_new_values);
1907 el->num_values = old_num_values + num_new_values;
1908 talloc_steal(msg->elements, new_values);
1909 } else {
1910 el->values = old_el->values;
1911 el->num_values = old_el->num_values;
1912 talloc_steal(msg->elements, el->values);
1915 talloc_free(tmp_ctx);
1917 /* we now tell the backend to replace all existing values
1918 with the one we have constructed */
1919 el->flags = LDB_FLAG_MOD_REPLACE;
1921 return LDB_SUCCESS;
1926 handle linked attributes in modify requests
1928 static int replmd_modify_handle_linked_attribs(struct ldb_module *module,
1929 struct ldb_message *msg,
1930 uint64_t seq_num, time_t t)
1932 struct ldb_result *res;
1933 unsigned int i;
1934 int ret;
1935 struct ldb_context *ldb = ldb_module_get_ctx(module);
1936 struct ldb_message *old_msg;
1938 const struct dsdb_schema *schema;
1939 struct GUID old_guid;
1941 if (seq_num == 0) {
1942 /* there the replmd_update_rpmd code has already
1943 * checked and saw that there are no linked
1944 * attributes */
1945 return LDB_SUCCESS;
1948 #if !W2K3_LINKED_ATTRIBUTES
1949 return LDB_SUCCESS;
1950 #endif
1952 if (dsdb_functional_level(ldb) == DS_DOMAIN_FUNCTION_2000) {
1953 /* don't do anything special for linked attributes */
1954 return LDB_SUCCESS;
1957 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, NULL,
1958 DSDB_SEARCH_SHOW_DELETED |
1959 DSDB_SEARCH_REVEAL_INTERNALS |
1960 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT);
1961 if (ret != LDB_SUCCESS) {
1962 return ret;
1964 schema = dsdb_get_schema(ldb, res);
1965 if (!schema) {
1966 return LDB_ERR_OPERATIONS_ERROR;
1969 old_msg = res->msgs[0];
1971 old_guid = samdb_result_guid(old_msg, "objectGUID");
1973 for (i=0; i<msg->num_elements; i++) {
1974 struct ldb_message_element *el = &msg->elements[i];
1975 struct ldb_message_element *old_el, *new_el;
1976 const struct dsdb_attribute *schema_attr
1977 = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
1978 if (!schema_attr) {
1979 ldb_asprintf_errstring(ldb,
1980 "attribute %s is not a valid attribute in schema", el->name);
1981 return LDB_ERR_OBJECT_CLASS_VIOLATION;
1983 if (schema_attr->linkID == 0) {
1984 continue;
1986 if ((schema_attr->linkID & 1) == 1) {
1987 /* Odd is for the target. Illegal to modify */
1988 ldb_asprintf_errstring(ldb,
1989 "attribute %s must not be modified directly, it is a linked attribute", el->name);
1990 return LDB_ERR_UNWILLING_TO_PERFORM;
1992 old_el = ldb_msg_find_element(old_msg, el->name);
1993 switch (el->flags & LDB_FLAG_MOD_MASK) {
1994 case LDB_FLAG_MOD_REPLACE:
1995 ret = replmd_modify_la_replace(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid);
1996 break;
1997 case LDB_FLAG_MOD_DELETE:
1998 ret = replmd_modify_la_delete(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid);
1999 break;
2000 case LDB_FLAG_MOD_ADD:
2001 ret = replmd_modify_la_add(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid);
2002 break;
2003 default:
2004 ldb_asprintf_errstring(ldb,
2005 "invalid flags 0x%x for %s linked attribute",
2006 el->flags, el->name);
2007 return LDB_ERR_UNWILLING_TO_PERFORM;
2009 if (ret != LDB_SUCCESS) {
2010 return ret;
2012 if (old_el) {
2013 ldb_msg_remove_attr(old_msg, el->name);
2015 ldb_msg_add_empty(old_msg, el->name, 0, &new_el);
2016 new_el->num_values = el->num_values;
2017 new_el->values = talloc_steal(msg->elements, el->values);
2019 /* TODO: this relises a bit too heavily on the exact
2020 behaviour of ldb_msg_find_element and
2021 ldb_msg_remove_element */
2022 old_el = ldb_msg_find_element(msg, el->name);
2023 if (old_el != el) {
2024 ldb_msg_remove_element(msg, old_el);
2025 i--;
2029 talloc_free(res);
2030 return ret;
2035 static int replmd_modify(struct ldb_module *module, struct ldb_request *req)
2037 struct ldb_context *ldb;
2038 struct replmd_replicated_request *ac;
2039 struct ldb_request *down_req;
2040 struct ldb_message *msg;
2041 time_t t = time(NULL);
2042 int ret;
2043 bool is_urgent = false;
2045 /* do not manipulate our control entries */
2046 if (ldb_dn_is_special(req->op.mod.message->dn)) {
2047 return ldb_next_request(module, req);
2050 ldb = ldb_module_get_ctx(module);
2052 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_modify\n");
2054 ac = replmd_ctx_init(module, req);
2055 if (!ac) {
2056 return LDB_ERR_OPERATIONS_ERROR;
2059 /* we have to copy the message as the caller might have it as a const */
2060 msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
2061 if (msg == NULL) {
2062 ldb_oom(ldb);
2063 talloc_free(ac);
2064 return LDB_ERR_OPERATIONS_ERROR;
2067 ldb_msg_remove_attr(msg, "whenChanged");
2068 ldb_msg_remove_attr(msg, "uSNChanged");
2070 ret = replmd_update_rpmd(module, ac->schema, msg, &ac->seq_num, t, &is_urgent);
2071 if (ret != LDB_SUCCESS) {
2072 talloc_free(ac);
2073 return ret;
2076 ret = replmd_modify_handle_linked_attribs(module, msg, ac->seq_num, t);
2077 if (ret != LDB_SUCCESS) {
2078 talloc_free(ac);
2079 return ret;
2082 /* TODO:
2083 * - replace the old object with the newly constructed one
2086 ac->is_urgent = is_urgent;
2088 ret = ldb_build_mod_req(&down_req, ldb, ac,
2089 msg,
2090 req->controls,
2091 ac, replmd_op_callback,
2092 req);
2093 if (ret != LDB_SUCCESS) {
2094 talloc_free(ac);
2095 return ret;
2097 talloc_steal(down_req, msg);
2099 /* we only change whenChanged and uSNChanged if the seq_num
2100 has changed */
2101 if (ac->seq_num != 0) {
2102 if (add_time_element(msg, "whenChanged", t) != LDB_SUCCESS) {
2103 talloc_free(ac);
2104 return ret;
2107 if (add_uint64_element(msg, "uSNChanged", ac->seq_num) != LDB_SUCCESS) {
2108 talloc_free(ac);
2109 return ret;
2113 /* go on with the call chain */
2114 return ldb_next_request(module, down_req);
2117 static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares);
2120 handle a rename request
2122 On a rename we need to do an extra ldb_modify which sets the
2123 whenChanged and uSNChanged attributes. We do this in a callback after the success.
2125 static int replmd_rename(struct ldb_module *module, struct ldb_request *req)
2127 struct ldb_context *ldb;
2128 struct replmd_replicated_request *ac;
2129 int ret;
2130 struct ldb_request *down_req;
2132 /* do not manipulate our control entries */
2133 if (ldb_dn_is_special(req->op.mod.message->dn)) {
2134 return ldb_next_request(module, req);
2137 ldb = ldb_module_get_ctx(module);
2139 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_rename\n");
2141 ac = replmd_ctx_init(module, req);
2142 if (!ac) {
2143 return LDB_ERR_OPERATIONS_ERROR;
2145 ret = ldb_build_rename_req(&down_req, ldb, ac,
2146 ac->req->op.rename.olddn,
2147 ac->req->op.rename.newdn,
2148 ac->req->controls,
2149 ac, replmd_rename_callback,
2150 ac->req);
2152 if (ret != LDB_SUCCESS) {
2153 talloc_free(ac);
2154 return ret;
2157 /* go on with the call chain */
2158 return ldb_next_request(module, down_req);
2161 /* After the rename is compleated, update the whenchanged etc */
2162 static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares)
2164 struct ldb_context *ldb;
2165 struct replmd_replicated_request *ac;
2166 struct ldb_request *down_req;
2167 struct ldb_message *msg;
2168 time_t t = time(NULL);
2169 int ret;
2171 ac = talloc_get_type(req->context, struct replmd_replicated_request);
2172 ldb = ldb_module_get_ctx(ac->module);
2174 if (ares->error != LDB_SUCCESS) {
2175 return ldb_module_done(ac->req, ares->controls,
2176 ares->response, ares->error);
2179 if (ares->type != LDB_REPLY_DONE) {
2180 ldb_set_errstring(ldb,
2181 "invalid ldb_reply_type in callback");
2182 talloc_free(ares);
2183 return ldb_module_done(ac->req, NULL, NULL,
2184 LDB_ERR_OPERATIONS_ERROR);
2187 /* Get a sequence number from the backend */
2188 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ac->seq_num);
2189 if (ret != LDB_SUCCESS) {
2190 return ret;
2193 /* TODO:
2194 * - replace the old object with the newly constructed one
2197 msg = ldb_msg_new(ac);
2198 if (msg == NULL) {
2199 ldb_oom(ldb);
2200 return LDB_ERR_OPERATIONS_ERROR;
2203 msg->dn = ac->req->op.rename.newdn;
2205 ret = ldb_build_mod_req(&down_req, ldb, ac,
2206 msg,
2207 req->controls,
2208 ac, replmd_op_callback,
2209 req);
2211 if (ret != LDB_SUCCESS) {
2212 talloc_free(ac);
2213 return ret;
2215 talloc_steal(down_req, msg);
2217 if (add_time_element(msg, "whenChanged", t) != LDB_SUCCESS) {
2218 talloc_free(ac);
2219 return ret;
2222 if (add_uint64_element(msg, "uSNChanged", ac->seq_num) != LDB_SUCCESS) {
2223 talloc_free(ac);
2224 return ret;
2227 /* go on with the call chain - do the modify after the rename */
2228 return ldb_next_request(ac->module, down_req);
2232 remove links from objects that point at this object when an object
2233 is deleted
2235 static int replmd_delete_remove_link(struct ldb_module *module,
2236 const struct dsdb_schema *schema,
2237 struct ldb_dn *dn,
2238 struct ldb_message_element *el,
2239 const struct dsdb_attribute *sa)
2241 unsigned int i;
2242 TALLOC_CTX *tmp_ctx = talloc_new(module);
2243 struct ldb_context *ldb = ldb_module_get_ctx(module);
2245 for (i=0; i<el->num_values; i++) {
2246 struct dsdb_dn *dsdb_dn;
2247 NTSTATUS status;
2248 int ret;
2249 struct GUID guid2;
2250 struct ldb_message *msg;
2251 const struct dsdb_attribute *target_attr;
2252 struct ldb_message_element *el2;
2253 struct ldb_val dn_val;
2255 if (dsdb_dn_is_deleted_val(&el->values[i])) {
2256 continue;
2259 dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i], sa->syntax->ldap_oid);
2260 if (!dsdb_dn) {
2261 talloc_free(tmp_ctx);
2262 return LDB_ERR_OPERATIONS_ERROR;
2265 status = dsdb_get_extended_dn_guid(dsdb_dn->dn, &guid2, "GUID");
2266 if (!NT_STATUS_IS_OK(status)) {
2267 talloc_free(tmp_ctx);
2268 return LDB_ERR_OPERATIONS_ERROR;
2271 /* remove the link */
2272 msg = ldb_msg_new(tmp_ctx);
2273 if (!msg) {
2274 ldb_module_oom(module);
2275 talloc_free(tmp_ctx);
2276 return LDB_ERR_OPERATIONS_ERROR;
2280 msg->dn = dsdb_dn->dn;
2282 target_attr = dsdb_attribute_by_linkID(schema, sa->linkID ^ 1);
2283 if (target_attr == NULL) {
2284 continue;
2287 ret = ldb_msg_add_empty(msg, target_attr->lDAPDisplayName, LDB_FLAG_MOD_DELETE, &el2);
2288 if (ret != LDB_SUCCESS) {
2289 ldb_module_oom(module);
2290 talloc_free(tmp_ctx);
2291 return LDB_ERR_OPERATIONS_ERROR;
2293 dn_val = data_blob_string_const(ldb_dn_get_linearized(dn));
2294 el2->values = &dn_val;
2295 el2->num_values = 1;
2297 ret = dsdb_module_modify(module, msg, DSDB_FLAG_OWN_MODULE);
2298 if (ret != LDB_SUCCESS) {
2299 talloc_free(tmp_ctx);
2300 return ret;
2303 talloc_free(tmp_ctx);
2304 return LDB_SUCCESS;
2309 handle update of replication meta data for deletion of objects
2311 This also handles the mapping of delete to a rename operation
2312 to allow deletes to be replicated.
2314 static int replmd_delete(struct ldb_module *module, struct ldb_request *req)
2316 int ret = LDB_ERR_OTHER;
2317 bool retb;
2318 struct ldb_dn *old_dn, *new_dn;
2319 const char *rdn_name;
2320 const struct ldb_val *rdn_value, *new_rdn_value;
2321 struct GUID guid;
2322 struct ldb_context *ldb = ldb_module_get_ctx(module);
2323 const struct dsdb_schema *schema;
2324 struct ldb_message *msg, *old_msg;
2325 struct ldb_message_element *el;
2326 TALLOC_CTX *tmp_ctx;
2327 struct ldb_result *res, *parent_res;
2328 const char *preserved_attrs[] = {
2329 /* yes, this really is a hard coded list. See MS-ADTS
2330 section 3.1.1.5.5.1.1 */
2331 "nTSecurityDescriptor", "attributeID", "attributeSyntax", "dNReferenceUpdate", "dNSHostName",
2332 "flatName", "governsID", "groupType", "instanceType", "lDAPDisplayName", "legacyExchangeDN",
2333 "isDeleted", "isRecycled", "lastKnownParent", "msDS-LastKnownRDN", "mS-DS-CreatorSID",
2334 "mSMQOwnerID", "nCName", "objectClass", "distinguishedName", "objectGUID", "objectSid",
2335 "oMSyntax", "proxiedObjectName", "name", "replPropertyMetaData", "sAMAccountName",
2336 "securityIdentifier", "sIDHistory", "subClassOf", "systemFlags", "trustPartner", "trustDirection",
2337 "trustType", "trustAttributes", "userAccountControl", "uSNChanged", "uSNCreated", "whenCreated",
2338 "whenChanged", NULL};
2339 unsigned int i, el_count = 0;
2340 enum deletion_state { OBJECT_NOT_DELETED=1, OBJECT_DELETED=2, OBJECT_RECYCLED=3,
2341 OBJECT_TOMBSTONE=4, OBJECT_REMOVED=5 };
2342 enum deletion_state deletion_state, next_deletion_state;
2343 bool enabled;
2345 if (ldb_dn_is_special(req->op.del.dn)) {
2346 return ldb_next_request(module, req);
2349 tmp_ctx = talloc_new(ldb);
2350 if (!tmp_ctx) {
2351 ldb_oom(ldb);
2352 return LDB_ERR_OPERATIONS_ERROR;
2355 schema = dsdb_get_schema(ldb, tmp_ctx);
2356 if (!schema) {
2357 return LDB_ERR_OPERATIONS_ERROR;
2360 old_dn = ldb_dn_copy(tmp_ctx, req->op.del.dn);
2362 /* we need the complete msg off disk, so we can work out which
2363 attributes need to be removed */
2364 ret = dsdb_module_search_dn(module, tmp_ctx, &res, old_dn, NULL,
2365 DSDB_SEARCH_SHOW_DELETED |
2366 DSDB_SEARCH_REVEAL_INTERNALS |
2367 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT);
2368 if (ret != LDB_SUCCESS) {
2369 talloc_free(tmp_ctx);
2370 return ret;
2372 old_msg = res->msgs[0];
2375 ret = dsdb_recyclebin_enabled(module, &enabled);
2376 if (ret != LDB_SUCCESS) {
2377 talloc_free(tmp_ctx);
2378 return ret;
2381 if (ldb_msg_check_string_attribute(old_msg, "isDeleted", "TRUE")) {
2382 if (!enabled) {
2383 deletion_state = OBJECT_TOMBSTONE;
2384 next_deletion_state = OBJECT_REMOVED;
2385 } else if (ldb_msg_check_string_attribute(old_msg, "isRecycled", "TRUE")) {
2386 deletion_state = OBJECT_RECYCLED;
2387 next_deletion_state = OBJECT_REMOVED;
2388 } else {
2389 deletion_state = OBJECT_DELETED;
2390 next_deletion_state = OBJECT_RECYCLED;
2392 } else {
2393 deletion_state = OBJECT_NOT_DELETED;
2394 if (enabled) {
2395 next_deletion_state = OBJECT_DELETED;
2396 } else {
2397 next_deletion_state = OBJECT_TOMBSTONE;
2401 if (next_deletion_state == OBJECT_REMOVED) {
2402 struct auth_session_info *session_info =
2403 (struct auth_session_info *)ldb_get_opaque(ldb, "sessionInfo");
2404 if (security_session_user_level(session_info, NULL) != SECURITY_SYSTEM) {
2405 ldb_asprintf_errstring(ldb, "Refusing to delete deleted object %s",
2406 ldb_dn_get_linearized(old_msg->dn));
2407 return LDB_ERR_UNWILLING_TO_PERFORM;
2410 /* it is already deleted - really remove it this time */
2411 talloc_free(tmp_ctx);
2412 return ldb_next_request(module, req);
2415 rdn_name = ldb_dn_get_rdn_name(old_dn);
2416 rdn_value = ldb_dn_get_rdn_val(old_dn);
2418 msg = ldb_msg_new(tmp_ctx);
2419 if (msg == NULL) {
2420 ldb_module_oom(module);
2421 talloc_free(tmp_ctx);
2422 return LDB_ERR_OPERATIONS_ERROR;
2425 msg->dn = old_dn;
2427 if (deletion_state == OBJECT_NOT_DELETED){
2428 /* work out where we will be renaming this object to */
2429 ret = dsdb_get_deleted_objects_dn(ldb, tmp_ctx, old_dn, &new_dn);
2430 if (ret != LDB_SUCCESS) {
2431 /* this is probably an attempted delete on a partition
2432 * that doesn't allow delete operations, such as the
2433 * schema partition */
2434 ldb_asprintf_errstring(ldb, "No Deleted Objects container for DN %s",
2435 ldb_dn_get_linearized(old_dn));
2436 talloc_free(tmp_ctx);
2437 return LDB_ERR_UNWILLING_TO_PERFORM;
2440 /* get the objects GUID from the search we just did */
2441 guid = samdb_result_guid(old_msg, "objectGUID");
2443 /* Add a formatted child */
2444 retb = ldb_dn_add_child_fmt(new_dn, "%s=%s\\0ADEL:%s",
2445 rdn_name,
2446 rdn_value->data,
2447 GUID_string(tmp_ctx, &guid));
2448 if (!retb) {
2449 DEBUG(0,(__location__ ": Unable to add a formatted child to dn: %s",
2450 ldb_dn_get_linearized(new_dn)));
2451 talloc_free(tmp_ctx);
2452 return LDB_ERR_OPERATIONS_ERROR;
2455 ret = ldb_msg_add_string(msg, "isDeleted", "TRUE");
2456 if (ret != LDB_SUCCESS) {
2457 DEBUG(0,(__location__ ": Failed to add isDeleted string to the msg\n"));
2458 ldb_module_oom(module);
2459 talloc_free(tmp_ctx);
2460 return ret;
2462 msg->elements[el_count++].flags = LDB_FLAG_MOD_ADD;
2466 now we need to modify the object in the following ways:
2468 - add isDeleted=TRUE
2469 - update rDN and name, with new rDN
2470 - remove linked attributes
2471 - remove objectCategory and sAMAccountType
2472 - remove attribs not on the preserved list
2473 - preserved if in above list, or is rDN
2474 - remove all linked attribs from this object
2475 - remove all links from other objects to this object
2476 - add lastKnownParent
2477 - update replPropertyMetaData?
2479 see MS-ADTS "Tombstone Requirements" section 3.1.1.5.5.1.1
2482 /* we need the storage form of the parent GUID */
2483 ret = dsdb_module_search_dn(module, tmp_ctx, &parent_res,
2484 ldb_dn_get_parent(tmp_ctx, old_dn), NULL,
2485 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
2486 DSDB_SEARCH_REVEAL_INTERNALS|
2487 DSDB_SEARCH_SHOW_DELETED);
2488 if (ret != LDB_SUCCESS) {
2489 talloc_free(tmp_ctx);
2490 return ret;
2493 if (deletion_state == OBJECT_NOT_DELETED){
2494 ret = ldb_msg_add_steal_string(msg, "lastKnownParent",
2495 ldb_dn_get_extended_linearized(tmp_ctx, parent_res->msgs[0]->dn, 1));
2496 if (ret != LDB_SUCCESS) {
2497 DEBUG(0,(__location__ ": Failed to add lastKnownParent string to the msg\n"));
2498 ldb_module_oom(module);
2499 talloc_free(tmp_ctx);
2500 return ret;
2502 msg->elements[el_count++].flags = LDB_FLAG_MOD_ADD;
2505 switch (next_deletion_state){
2507 case OBJECT_DELETED:
2509 ret = ldb_msg_add_value(msg, "msDS-LastKnownRDN", rdn_value, NULL);
2510 if (ret != LDB_SUCCESS) {
2511 DEBUG(0,(__location__ ": Failed to add msDS-LastKnownRDN string to the msg\n"));
2512 ldb_module_oom(module);
2513 talloc_free(tmp_ctx);
2514 return ret;
2516 msg->elements[el_count++].flags = LDB_FLAG_MOD_ADD;
2518 ret = ldb_msg_add_empty(msg, "objectCategory", LDB_FLAG_MOD_DELETE, NULL);
2519 if (ret != LDB_SUCCESS) {
2520 talloc_free(tmp_ctx);
2521 ldb_module_oom(module);
2522 return ret;
2525 ret = ldb_msg_add_empty(msg, "sAMAccountType", LDB_FLAG_MOD_DELETE, NULL);
2526 if (ret != LDB_SUCCESS) {
2527 talloc_free(tmp_ctx);
2528 ldb_module_oom(module);
2529 return ret;
2532 break;
2534 case OBJECT_RECYCLED:
2535 case OBJECT_TOMBSTONE:
2537 /* we also mark it as recycled, meaning this object can't be
2538 recovered (we are stripping its attributes) */
2539 if (dsdb_functional_level(ldb) >= DS_DOMAIN_FUNCTION_2008_R2) {
2540 ret = ldb_msg_add_string(msg, "isRecycled", "TRUE");
2541 if (ret != LDB_SUCCESS) {
2542 DEBUG(0,(__location__ ": Failed to add isRecycled string to the msg\n"));
2543 ldb_module_oom(module);
2544 talloc_free(tmp_ctx);
2545 return ret;
2547 msg->elements[el_count++].flags = LDB_FLAG_MOD_ADD;
2550 /* work out which of the old attributes we will be removing */
2551 for (i=0; i<old_msg->num_elements; i++) {
2552 const struct dsdb_attribute *sa;
2553 el = &old_msg->elements[i];
2554 sa = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
2555 if (!sa) {
2556 talloc_free(tmp_ctx);
2557 return LDB_ERR_OPERATIONS_ERROR;
2559 if (ldb_attr_cmp(el->name, rdn_name) == 0) {
2560 /* don't remove the rDN */
2561 continue;
2563 if (sa->linkID && sa->linkID & 1) {
2564 ret = replmd_delete_remove_link(module, schema, old_dn, el, sa);
2565 if (ret != LDB_SUCCESS) {
2566 talloc_free(tmp_ctx);
2567 return LDB_ERR_OPERATIONS_ERROR;
2569 continue;
2571 if (!sa->linkID && ldb_attr_in_list(preserved_attrs, el->name)) {
2572 continue;
2574 ret = ldb_msg_add_empty(msg, el->name, LDB_FLAG_MOD_DELETE, &el);
2575 if (ret != LDB_SUCCESS) {
2576 talloc_free(tmp_ctx);
2577 ldb_module_oom(module);
2578 return ret;
2581 break;
2583 default:
2584 break;
2587 if (deletion_state == OBJECT_NOT_DELETED) {
2588 /* work out what the new rdn value is, for updating the
2589 rDN and name fields */
2590 new_rdn_value = ldb_dn_get_rdn_val(new_dn);
2592 ret = ldb_msg_add_value(msg, strlower_talloc(tmp_ctx, rdn_name), new_rdn_value, &el);
2593 if (ret != LDB_SUCCESS) {
2594 talloc_free(tmp_ctx);
2595 return ret;
2597 el->flags = LDB_FLAG_MOD_REPLACE;
2599 el = ldb_msg_find_element(old_msg, "name");
2600 if (el) {
2601 ret = ldb_msg_add_value(msg, "name", new_rdn_value, &el);
2602 if (ret != LDB_SUCCESS) {
2603 talloc_free(tmp_ctx);
2604 return ret;
2606 el->flags = LDB_FLAG_MOD_REPLACE;
2610 ret = dsdb_module_modify(module, msg, DSDB_FLAG_OWN_MODULE);
2611 if (ret != LDB_SUCCESS) {
2612 ldb_asprintf_errstring(ldb, "replmd_delete: Failed to modify object %s in delete - %s",
2613 ldb_dn_get_linearized(old_dn), ldb_errstring(ldb));
2614 talloc_free(tmp_ctx);
2615 return ret;
2618 if (deletion_state == OBJECT_NOT_DELETED) {
2619 /* now rename onto the new DN */
2620 ret = dsdb_module_rename(module, old_dn, new_dn, 0);
2621 if (ret != LDB_SUCCESS){
2622 DEBUG(0,(__location__ ": Failed to rename object from '%s' to '%s' - %s\n",
2623 ldb_dn_get_linearized(old_dn),
2624 ldb_dn_get_linearized(new_dn),
2625 ldb_errstring(ldb)));
2626 talloc_free(tmp_ctx);
2627 return ret;
2631 talloc_free(tmp_ctx);
2633 return ldb_module_done(req, NULL, NULL, LDB_SUCCESS);
2638 static int replmd_replicated_request_error(struct replmd_replicated_request *ar, int ret)
2640 return ret;
2643 static int replmd_replicated_request_werror(struct replmd_replicated_request *ar, WERROR status)
2645 int ret = LDB_ERR_OTHER;
2646 /* TODO: do some error mapping */
2647 return ret;
2650 static int replmd_replicated_apply_add(struct replmd_replicated_request *ar)
2652 struct ldb_context *ldb;
2653 struct ldb_request *change_req;
2654 enum ndr_err_code ndr_err;
2655 struct ldb_message *msg;
2656 struct replPropertyMetaDataBlob *md;
2657 struct ldb_val md_value;
2658 unsigned int i;
2659 int ret;
2662 * TODO: check if the parent object exist
2666 * TODO: handle the conflict case where an object with the
2667 * same name exist
2670 ldb = ldb_module_get_ctx(ar->module);
2671 msg = ar->objs->objects[ar->index_current].msg;
2672 md = ar->objs->objects[ar->index_current].meta_data;
2674 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
2675 if (ret != LDB_SUCCESS) {
2676 return replmd_replicated_request_error(ar, ret);
2679 ret = ldb_msg_add_value(msg, "objectGUID", &ar->objs->objects[ar->index_current].guid_value, NULL);
2680 if (ret != LDB_SUCCESS) {
2681 return replmd_replicated_request_error(ar, ret);
2684 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
2685 if (ret != LDB_SUCCESS) {
2686 return replmd_replicated_request_error(ar, ret);
2689 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", ar->seq_num);
2690 if (ret != LDB_SUCCESS) {
2691 return replmd_replicated_request_error(ar, ret);
2694 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
2695 if (ret != LDB_SUCCESS) {
2696 return replmd_replicated_request_error(ar, ret);
2699 /* remove any message elements that have zero values */
2700 for (i=0; i<msg->num_elements; i++) {
2701 struct ldb_message_element *el = &msg->elements[i];
2703 if (el->num_values == 0) {
2704 DEBUG(4,(__location__ ": Removing attribute %s with num_values==0\n",
2705 el->name));
2706 memmove(el, el+1, sizeof(*el)*(msg->num_elements - (i+1)));
2707 msg->num_elements--;
2708 i--;
2709 continue;
2714 * the meta data array is already sorted by the caller
2716 for (i=0; i < md->ctr.ctr1.count; i++) {
2717 md->ctr.ctr1.array[i].local_usn = ar->seq_num;
2719 ndr_err = ndr_push_struct_blob(&md_value, msg,
2720 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
2722 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
2723 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
2724 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
2725 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
2727 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &md_value, NULL);
2728 if (ret != LDB_SUCCESS) {
2729 return replmd_replicated_request_error(ar, ret);
2732 replmd_ldb_message_sort(msg, ar->schema);
2734 if (DEBUGLVL(4)) {
2735 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_ADD, msg);
2736 DEBUG(4, ("DRS replication add message:\n%s\n", s));
2737 talloc_free(s);
2740 ret = ldb_build_add_req(&change_req,
2741 ldb,
2743 msg,
2744 ar->controls,
2746 replmd_op_callback,
2747 ar->req);
2748 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
2750 return ldb_next_request(ar->module, change_req);
2754 return true if an update is newer than an existing entry
2755 see section 5.11 of MS-ADTS
2757 static bool replmd_update_is_newer(const struct GUID *current_invocation_id,
2758 const struct GUID *update_invocation_id,
2759 uint32_t current_version,
2760 uint32_t update_version,
2761 NTTIME current_change_time,
2762 NTTIME update_change_time)
2764 if (update_version != current_version) {
2765 return update_version > current_version;
2767 if (update_change_time > current_change_time) {
2768 return true;
2770 if (update_change_time == current_change_time) {
2771 return GUID_compare(update_invocation_id, current_invocation_id) > 0;
2773 return false;
2776 static bool replmd_replPropertyMetaData1_is_newer(struct replPropertyMetaData1 *cur_m,
2777 struct replPropertyMetaData1 *new_m)
2779 return replmd_update_is_newer(&cur_m->originating_invocation_id,
2780 &new_m->originating_invocation_id,
2781 cur_m->version,
2782 new_m->version,
2783 cur_m->originating_change_time,
2784 new_m->originating_change_time);
2787 static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar)
2789 struct ldb_context *ldb;
2790 struct ldb_request *change_req;
2791 enum ndr_err_code ndr_err;
2792 struct ldb_message *msg;
2793 struct replPropertyMetaDataBlob *rmd;
2794 struct replPropertyMetaDataBlob omd;
2795 const struct ldb_val *omd_value;
2796 struct replPropertyMetaDataBlob nmd;
2797 struct ldb_val nmd_value;
2798 unsigned int i;
2799 uint32_t j,ni=0;
2800 unsigned int removed_attrs = 0;
2801 int ret;
2803 ldb = ldb_module_get_ctx(ar->module);
2804 msg = ar->objs->objects[ar->index_current].msg;
2805 rmd = ar->objs->objects[ar->index_current].meta_data;
2806 ZERO_STRUCT(omd);
2807 omd.version = 1;
2810 * TODO: check repl data is correct after a rename
2812 if (ldb_dn_compare(msg->dn, ar->search_msg->dn) != 0) {
2813 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_request rename %s => %s\n",
2814 ldb_dn_get_linearized(ar->search_msg->dn),
2815 ldb_dn_get_linearized(msg->dn));
2816 if (dsdb_module_rename(ar->module,
2817 ar->search_msg->dn, msg->dn,
2818 DSDB_FLAG_OWN_MODULE) != LDB_SUCCESS) {
2819 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_replicated_request rename %s => %s failed - %s\n",
2820 ldb_dn_get_linearized(ar->search_msg->dn),
2821 ldb_dn_get_linearized(msg->dn),
2822 ldb_errstring(ldb));
2823 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
2827 /* find existing meta data */
2828 omd_value = ldb_msg_find_ldb_val(ar->search_msg, "replPropertyMetaData");
2829 if (omd_value) {
2830 ndr_err = ndr_pull_struct_blob(omd_value, ar,
2831 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")), &omd,
2832 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
2833 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
2834 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
2835 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
2838 if (omd.version != 1) {
2839 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
2843 ZERO_STRUCT(nmd);
2844 nmd.version = 1;
2845 nmd.ctr.ctr1.count = omd.ctr.ctr1.count + rmd->ctr.ctr1.count;
2846 nmd.ctr.ctr1.array = talloc_array(ar,
2847 struct replPropertyMetaData1,
2848 nmd.ctr.ctr1.count);
2849 if (!nmd.ctr.ctr1.array) return replmd_replicated_request_werror(ar, WERR_NOMEM);
2851 /* first copy the old meta data */
2852 for (i=0; i < omd.ctr.ctr1.count; i++) {
2853 nmd.ctr.ctr1.array[ni] = omd.ctr.ctr1.array[i];
2854 ni++;
2857 /* now merge in the new meta data */
2858 for (i=0; i < rmd->ctr.ctr1.count; i++) {
2859 bool found = false;
2861 for (j=0; j < ni; j++) {
2862 bool cmp;
2864 if (rmd->ctr.ctr1.array[i].attid != nmd.ctr.ctr1.array[j].attid) {
2865 continue;
2868 cmp = replmd_replPropertyMetaData1_is_newer(&nmd.ctr.ctr1.array[j],
2869 &rmd->ctr.ctr1.array[i]);
2870 if (cmp) {
2871 /* replace the entry */
2872 nmd.ctr.ctr1.array[j] = rmd->ctr.ctr1.array[i];
2873 found = true;
2874 break;
2877 if (rmd->ctr.ctr1.array[i].attid != DRSUAPI_ATTRIBUTE_instanceType) {
2878 DEBUG(1,("Discarding older DRS attribute update to %s on %s from %s\n",
2879 msg->elements[i-removed_attrs].name,
2880 ldb_dn_get_linearized(msg->dn),
2881 GUID_string(ar, &rmd->ctr.ctr1.array[i].originating_invocation_id)));
2884 /* we don't want to apply this change so remove the attribute */
2885 ldb_msg_remove_element(msg, &msg->elements[i-removed_attrs]);
2886 removed_attrs++;
2888 found = true;
2889 break;
2892 if (found) continue;
2894 nmd.ctr.ctr1.array[ni] = rmd->ctr.ctr1.array[i];
2895 ni++;
2899 * finally correct the size of the meta_data array
2901 nmd.ctr.ctr1.count = ni;
2904 * the rdn attribute (the alias for the name attribute),
2905 * 'cn' for most objects is the last entry in the meta data array
2906 * we have stored
2908 * sort the new meta data array
2910 ret = replmd_replPropertyMetaDataCtr1_sort(&nmd.ctr.ctr1, ar->schema, msg->dn);
2911 if (ret != LDB_SUCCESS) {
2912 return ret;
2916 * check if some replicated attributes left, otherwise skip the ldb_modify() call
2918 if (msg->num_elements == 0) {
2919 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: skip replace\n",
2920 ar->index_current);
2922 ar->index_current++;
2923 return replmd_replicated_apply_next(ar);
2926 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: replace %u attributes\n",
2927 ar->index_current, msg->num_elements);
2929 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
2930 if (ret != LDB_SUCCESS) {
2931 return replmd_replicated_request_error(ar, ret);
2934 for (i=0; i<ni; i++) {
2935 nmd.ctr.ctr1.array[i].local_usn = ar->seq_num;
2938 /* create the meta data value */
2939 ndr_err = ndr_push_struct_blob(&nmd_value, msg,
2940 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
2941 &nmd,
2942 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
2943 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
2944 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
2945 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
2949 * when we know that we'll modify the record, add the whenChanged, uSNChanged
2950 * and replPopertyMetaData attributes
2952 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
2953 if (ret != LDB_SUCCESS) {
2954 return replmd_replicated_request_error(ar, ret);
2956 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
2957 if (ret != LDB_SUCCESS) {
2958 return replmd_replicated_request_error(ar, ret);
2960 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
2961 if (ret != LDB_SUCCESS) {
2962 return replmd_replicated_request_error(ar, ret);
2965 replmd_ldb_message_sort(msg, ar->schema);
2967 /* we want to replace the old values */
2968 for (i=0; i < msg->num_elements; i++) {
2969 msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
2972 if (DEBUGLVL(4)) {
2973 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_MODIFY, msg);
2974 DEBUG(4, ("DRS replication modify message:\n%s\n", s));
2975 talloc_free(s);
2978 ret = ldb_build_mod_req(&change_req,
2979 ldb,
2981 msg,
2982 ar->controls,
2984 replmd_op_callback,
2985 ar->req);
2986 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
2988 return ldb_next_request(ar->module, change_req);
2991 static int replmd_replicated_apply_search_callback(struct ldb_request *req,
2992 struct ldb_reply *ares)
2994 struct replmd_replicated_request *ar = talloc_get_type(req->context,
2995 struct replmd_replicated_request);
2996 int ret;
2998 if (!ares) {
2999 return ldb_module_done(ar->req, NULL, NULL,
3000 LDB_ERR_OPERATIONS_ERROR);
3002 if (ares->error != LDB_SUCCESS &&
3003 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
3004 return ldb_module_done(ar->req, ares->controls,
3005 ares->response, ares->error);
3008 switch (ares->type) {
3009 case LDB_REPLY_ENTRY:
3010 ar->search_msg = talloc_steal(ar, ares->message);
3011 break;
3013 case LDB_REPLY_REFERRAL:
3014 /* we ignore referrals */
3015 break;
3017 case LDB_REPLY_DONE:
3018 if (ar->search_msg != NULL) {
3019 ret = replmd_replicated_apply_merge(ar);
3020 } else {
3021 ret = replmd_replicated_apply_add(ar);
3023 if (ret != LDB_SUCCESS) {
3024 return ldb_module_done(ar->req, NULL, NULL, ret);
3028 talloc_free(ares);
3029 return LDB_SUCCESS;
3032 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar);
3034 static int replmd_replicated_apply_next(struct replmd_replicated_request *ar)
3036 struct ldb_context *ldb;
3037 int ret;
3038 char *tmp_str;
3039 char *filter;
3040 struct ldb_request *search_req;
3041 struct ldb_search_options_control *options;
3043 if (ar->index_current >= ar->objs->num_objects) {
3044 /* done with it, go to next stage */
3045 return replmd_replicated_uptodate_vector(ar);
3048 ldb = ldb_module_get_ctx(ar->module);
3049 ar->search_msg = NULL;
3051 tmp_str = ldb_binary_encode(ar, ar->objs->objects[ar->index_current].guid_value);
3052 if (!tmp_str) return replmd_replicated_request_werror(ar, WERR_NOMEM);
3054 filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
3055 if (!filter) return replmd_replicated_request_werror(ar, WERR_NOMEM);
3056 talloc_free(tmp_str);
3058 ret = ldb_build_search_req(&search_req,
3059 ldb,
3061 NULL,
3062 LDB_SCOPE_SUBTREE,
3063 filter,
3064 NULL,
3065 NULL,
3067 replmd_replicated_apply_search_callback,
3068 ar->req);
3070 ret = ldb_request_add_control(search_req, LDB_CONTROL_SHOW_DELETED_OID, true, NULL);
3071 if (ret != LDB_SUCCESS) {
3072 return ret;
3075 /* we need to cope with cross-partition links, so search for
3076 the GUID over all partitions */
3077 options = talloc(search_req, struct ldb_search_options_control);
3078 if (options == NULL) {
3079 DEBUG(0, (__location__ ": out of memory\n"));
3080 return LDB_ERR_OPERATIONS_ERROR;
3082 options->search_options = LDB_SEARCH_OPTION_PHANTOM_ROOT;
3084 ret = ldb_request_add_control(search_req,
3085 LDB_CONTROL_SEARCH_OPTIONS_OID,
3086 true, options);
3087 if (ret != LDB_SUCCESS) {
3088 return ret;
3091 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
3093 return ldb_next_request(ar->module, search_req);
3096 static int replmd_replicated_uptodate_modify_callback(struct ldb_request *req,
3097 struct ldb_reply *ares)
3099 struct ldb_context *ldb;
3100 struct replmd_replicated_request *ar = talloc_get_type(req->context,
3101 struct replmd_replicated_request);
3102 ldb = ldb_module_get_ctx(ar->module);
3104 if (!ares) {
3105 return ldb_module_done(ar->req, NULL, NULL,
3106 LDB_ERR_OPERATIONS_ERROR);
3108 if (ares->error != LDB_SUCCESS) {
3109 return ldb_module_done(ar->req, ares->controls,
3110 ares->response, ares->error);
3113 if (ares->type != LDB_REPLY_DONE) {
3114 ldb_set_errstring(ldb, "Invalid reply type\n!");
3115 return ldb_module_done(ar->req, NULL, NULL,
3116 LDB_ERR_OPERATIONS_ERROR);
3119 talloc_free(ares);
3121 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
3124 static int replmd_replicated_uptodate_modify(struct replmd_replicated_request *ar)
3126 struct ldb_context *ldb;
3127 struct ldb_request *change_req;
3128 enum ndr_err_code ndr_err;
3129 struct ldb_message *msg;
3130 struct replUpToDateVectorBlob ouv;
3131 const struct ldb_val *ouv_value;
3132 const struct drsuapi_DsReplicaCursor2CtrEx *ruv;
3133 struct replUpToDateVectorBlob nuv;
3134 struct ldb_val nuv_value;
3135 struct ldb_message_element *nuv_el = NULL;
3136 const struct GUID *our_invocation_id;
3137 struct ldb_message_element *orf_el = NULL;
3138 struct repsFromToBlob nrf;
3139 struct ldb_val *nrf_value = NULL;
3140 struct ldb_message_element *nrf_el = NULL;
3141 unsigned int i;
3142 uint32_t j,ni=0;
3143 bool found = false;
3144 time_t t = time(NULL);
3145 NTTIME now;
3146 int ret;
3148 ldb = ldb_module_get_ctx(ar->module);
3149 ruv = ar->objs->uptodateness_vector;
3150 ZERO_STRUCT(ouv);
3151 ouv.version = 2;
3152 ZERO_STRUCT(nuv);
3153 nuv.version = 2;
3155 unix_to_nt_time(&now, t);
3158 * first create the new replUpToDateVector
3160 ouv_value = ldb_msg_find_ldb_val(ar->search_msg, "replUpToDateVector");
3161 if (ouv_value) {
3162 ndr_err = ndr_pull_struct_blob(ouv_value, ar,
3163 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")), &ouv,
3164 (ndr_pull_flags_fn_t)ndr_pull_replUpToDateVectorBlob);
3165 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
3166 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
3167 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
3170 if (ouv.version != 2) {
3171 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
3176 * the new uptodateness vector will at least
3177 * contain 1 entry, one for the source_dsa
3179 * plus optional values from our old vector and the one from the source_dsa
3181 nuv.ctr.ctr2.count = 1 + ouv.ctr.ctr2.count;
3182 if (ruv) nuv.ctr.ctr2.count += ruv->count;
3183 nuv.ctr.ctr2.cursors = talloc_array(ar,
3184 struct drsuapi_DsReplicaCursor2,
3185 nuv.ctr.ctr2.count);
3186 if (!nuv.ctr.ctr2.cursors) return replmd_replicated_request_werror(ar, WERR_NOMEM);
3188 /* first copy the old vector */
3189 for (i=0; i < ouv.ctr.ctr2.count; i++) {
3190 nuv.ctr.ctr2.cursors[ni] = ouv.ctr.ctr2.cursors[i];
3191 ni++;
3194 /* get our invocation_id if we have one already attached to the ldb */
3195 our_invocation_id = samdb_ntds_invocation_id(ldb);
3197 /* merge in the source_dsa vector is available */
3198 for (i=0; (ruv && i < ruv->count); i++) {
3199 found = false;
3201 if (our_invocation_id &&
3202 GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
3203 our_invocation_id)) {
3204 continue;
3207 for (j=0; j < ni; j++) {
3208 if (!GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
3209 &nuv.ctr.ctr2.cursors[j].source_dsa_invocation_id)) {
3210 continue;
3213 found = true;
3216 * we update only the highest_usn and not the latest_sync_success time,
3217 * because the last success stands for direct replication
3219 if (ruv->cursors[i].highest_usn > nuv.ctr.ctr2.cursors[j].highest_usn) {
3220 nuv.ctr.ctr2.cursors[j].highest_usn = ruv->cursors[i].highest_usn;
3222 break;
3225 if (found) continue;
3227 /* if it's not there yet, add it */
3228 nuv.ctr.ctr2.cursors[ni] = ruv->cursors[i];
3229 ni++;
3233 * merge in the current highwatermark for the source_dsa
3235 found = false;
3236 for (j=0; j < ni; j++) {
3237 if (!GUID_equal(&ar->objs->source_dsa->source_dsa_invocation_id,
3238 &nuv.ctr.ctr2.cursors[j].source_dsa_invocation_id)) {
3239 continue;
3242 found = true;
3245 * here we update the highest_usn and last_sync_success time
3246 * because we're directly replicating from the source_dsa
3248 * and use the tmp_highest_usn because this is what we have just applied
3249 * to our ldb
3251 nuv.ctr.ctr2.cursors[j].highest_usn = ar->objs->source_dsa->highwatermark.tmp_highest_usn;
3252 nuv.ctr.ctr2.cursors[j].last_sync_success = now;
3253 break;
3255 if (!found) {
3257 * here we update the highest_usn and last_sync_success time
3258 * because we're directly replicating from the source_dsa
3260 * and use the tmp_highest_usn because this is what we have just applied
3261 * to our ldb
3263 nuv.ctr.ctr2.cursors[ni].source_dsa_invocation_id= ar->objs->source_dsa->source_dsa_invocation_id;
3264 nuv.ctr.ctr2.cursors[ni].highest_usn = ar->objs->source_dsa->highwatermark.tmp_highest_usn;
3265 nuv.ctr.ctr2.cursors[ni].last_sync_success = now;
3266 ni++;
3270 * finally correct the size of the cursors array
3272 nuv.ctr.ctr2.count = ni;
3275 * sort the cursors
3277 TYPESAFE_QSORT(nuv.ctr.ctr2.cursors, nuv.ctr.ctr2.count, drsuapi_DsReplicaCursor2_compare);
3280 * create the change ldb_message
3282 msg = ldb_msg_new(ar);
3283 if (!msg) return replmd_replicated_request_werror(ar, WERR_NOMEM);
3284 msg->dn = ar->search_msg->dn;
3286 ndr_err = ndr_push_struct_blob(&nuv_value, msg,
3287 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
3288 &nuv,
3289 (ndr_push_flags_fn_t)ndr_push_replUpToDateVectorBlob);
3290 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
3291 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
3292 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
3294 ret = ldb_msg_add_value(msg, "replUpToDateVector", &nuv_value, &nuv_el);
3295 if (ret != LDB_SUCCESS) {
3296 return replmd_replicated_request_error(ar, ret);
3298 nuv_el->flags = LDB_FLAG_MOD_REPLACE;
3301 * now create the new repsFrom value from the given repsFromTo1 structure
3303 ZERO_STRUCT(nrf);
3304 nrf.version = 1;
3305 nrf.ctr.ctr1 = *ar->objs->source_dsa;
3306 /* and fix some values... */
3307 nrf.ctr.ctr1.consecutive_sync_failures = 0;
3308 nrf.ctr.ctr1.last_success = now;
3309 nrf.ctr.ctr1.last_attempt = now;
3310 nrf.ctr.ctr1.result_last_attempt = WERR_OK;
3311 nrf.ctr.ctr1.highwatermark.highest_usn = nrf.ctr.ctr1.highwatermark.tmp_highest_usn;
3314 * first see if we already have a repsFrom value for the current source dsa
3315 * if so we'll later replace this value
3317 orf_el = ldb_msg_find_element(ar->search_msg, "repsFrom");
3318 if (orf_el) {
3319 for (i=0; i < orf_el->num_values; i++) {
3320 struct repsFromToBlob *trf;
3322 trf = talloc(ar, struct repsFromToBlob);
3323 if (!trf) return replmd_replicated_request_werror(ar, WERR_NOMEM);
3325 ndr_err = ndr_pull_struct_blob(&orf_el->values[i], trf, lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")), trf,
3326 (ndr_pull_flags_fn_t)ndr_pull_repsFromToBlob);
3327 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
3328 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
3329 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
3332 if (trf->version != 1) {
3333 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
3337 * we compare the source dsa objectGUID not the invocation_id
3338 * because we want only one repsFrom value per source dsa
3339 * and when the invocation_id of the source dsa has changed we don't need
3340 * the old repsFrom with the old invocation_id
3342 if (!GUID_equal(&trf->ctr.ctr1.source_dsa_obj_guid,
3343 &ar->objs->source_dsa->source_dsa_obj_guid)) {
3344 talloc_free(trf);
3345 continue;
3348 talloc_free(trf);
3349 nrf_value = &orf_el->values[i];
3350 break;
3354 * copy over all old values to the new ldb_message
3356 ret = ldb_msg_add_empty(msg, "repsFrom", 0, &nrf_el);
3357 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
3358 *nrf_el = *orf_el;
3362 * if we haven't found an old repsFrom value for the current source dsa
3363 * we'll add a new value
3365 if (!nrf_value) {
3366 struct ldb_val zero_value;
3367 ZERO_STRUCT(zero_value);
3368 ret = ldb_msg_add_value(msg, "repsFrom", &zero_value, &nrf_el);
3369 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
3371 nrf_value = &nrf_el->values[nrf_el->num_values - 1];
3374 /* we now fill the value which is already attached to ldb_message */
3375 ndr_err = ndr_push_struct_blob(nrf_value, msg,
3376 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
3377 &nrf,
3378 (ndr_push_flags_fn_t)ndr_push_repsFromToBlob);
3379 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
3380 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
3381 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
3385 * the ldb_message_element for the attribute, has all the old values and the new one
3386 * so we'll replace the whole attribute with all values
3388 nrf_el->flags = LDB_FLAG_MOD_REPLACE;
3390 if (DEBUGLVL(4)) {
3391 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_MODIFY, msg);
3392 DEBUG(4, ("DRS replication uptodate modify message:\n%s\n", s));
3393 talloc_free(s);
3396 /* prepare the ldb_modify() request */
3397 ret = ldb_build_mod_req(&change_req,
3398 ldb,
3400 msg,
3401 ar->controls,
3403 replmd_replicated_uptodate_modify_callback,
3404 ar->req);
3405 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
3407 return ldb_next_request(ar->module, change_req);
3410 static int replmd_replicated_uptodate_search_callback(struct ldb_request *req,
3411 struct ldb_reply *ares)
3413 struct replmd_replicated_request *ar = talloc_get_type(req->context,
3414 struct replmd_replicated_request);
3415 int ret;
3417 if (!ares) {
3418 return ldb_module_done(ar->req, NULL, NULL,
3419 LDB_ERR_OPERATIONS_ERROR);
3421 if (ares->error != LDB_SUCCESS &&
3422 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
3423 return ldb_module_done(ar->req, ares->controls,
3424 ares->response, ares->error);
3427 switch (ares->type) {
3428 case LDB_REPLY_ENTRY:
3429 ar->search_msg = talloc_steal(ar, ares->message);
3430 break;
3432 case LDB_REPLY_REFERRAL:
3433 /* we ignore referrals */
3434 break;
3436 case LDB_REPLY_DONE:
3437 if (ar->search_msg == NULL) {
3438 ret = replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
3439 } else {
3440 ret = replmd_replicated_uptodate_modify(ar);
3442 if (ret != LDB_SUCCESS) {
3443 return ldb_module_done(ar->req, NULL, NULL, ret);
3447 talloc_free(ares);
3448 return LDB_SUCCESS;
3452 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar)
3454 struct ldb_context *ldb;
3455 int ret;
3456 static const char *attrs[] = {
3457 "replUpToDateVector",
3458 "repsFrom",
3459 NULL
3461 struct ldb_request *search_req;
3463 ldb = ldb_module_get_ctx(ar->module);
3464 ar->search_msg = NULL;
3466 ret = ldb_build_search_req(&search_req,
3467 ldb,
3469 ar->objs->partition_dn,
3470 LDB_SCOPE_BASE,
3471 "(objectClass=*)",
3472 attrs,
3473 NULL,
3475 replmd_replicated_uptodate_search_callback,
3476 ar->req);
3477 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
3479 return ldb_next_request(ar->module, search_req);
3484 static int replmd_extended_replicated_objects(struct ldb_module *module, struct ldb_request *req)
3486 struct ldb_context *ldb;
3487 struct dsdb_extended_replicated_objects *objs;
3488 struct replmd_replicated_request *ar;
3489 struct ldb_control **ctrls;
3490 int ret;
3491 uint32_t i;
3492 struct replmd_private *replmd_private =
3493 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
3495 ldb = ldb_module_get_ctx(module);
3497 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_extended_replicated_objects\n");
3499 objs = talloc_get_type(req->op.extended.data, struct dsdb_extended_replicated_objects);
3500 if (!objs) {
3501 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: invalid extended data\n");
3502 return LDB_ERR_PROTOCOL_ERROR;
3505 if (objs->version != DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION) {
3506 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: extended data invalid version [%u != %u]\n",
3507 objs->version, DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION);
3508 return LDB_ERR_PROTOCOL_ERROR;
3511 ar = replmd_ctx_init(module, req);
3512 if (!ar)
3513 return LDB_ERR_OPERATIONS_ERROR;
3515 /* Set the flags to have the replmd_op_callback run over the full set of objects */
3516 ar->apply_mode = true;
3517 ar->objs = objs;
3518 ar->schema = dsdb_get_schema(ldb, ar);
3519 if (!ar->schema) {
3520 ldb_debug_set(ldb, LDB_DEBUG_FATAL, "replmd_ctx_init: no loaded schema found\n");
3521 talloc_free(ar);
3522 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
3523 return LDB_ERR_CONSTRAINT_VIOLATION;
3526 ctrls = req->controls;
3528 if (req->controls) {
3529 req->controls = talloc_memdup(ar, req->controls,
3530 talloc_get_size(req->controls));
3531 if (!req->controls) return replmd_replicated_request_werror(ar, WERR_NOMEM);
3534 ret = ldb_request_add_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID, false, NULL);
3535 if (ret != LDB_SUCCESS) {
3536 return ret;
3539 ar->controls = req->controls;
3540 req->controls = ctrls;
3542 DEBUG(4,("linked_attributes_count=%u\n", objs->linked_attributes_count));
3544 /* save away the linked attributes for the end of the
3545 transaction */
3546 for (i=0; i<ar->objs->linked_attributes_count; i++) {
3547 struct la_entry *la_entry;
3549 if (replmd_private->la_ctx == NULL) {
3550 replmd_private->la_ctx = talloc_new(replmd_private);
3552 la_entry = talloc(replmd_private->la_ctx, struct la_entry);
3553 if (la_entry == NULL) {
3554 ldb_oom(ldb);
3555 return LDB_ERR_OPERATIONS_ERROR;
3557 la_entry->la = talloc(la_entry, struct drsuapi_DsReplicaLinkedAttribute);
3558 if (la_entry->la == NULL) {
3559 talloc_free(la_entry);
3560 ldb_oom(ldb);
3561 return LDB_ERR_OPERATIONS_ERROR;
3563 *la_entry->la = ar->objs->linked_attributes[i];
3565 /* we need to steal the non-scalars so they stay
3566 around until the end of the transaction */
3567 talloc_steal(la_entry->la, la_entry->la->identifier);
3568 talloc_steal(la_entry->la, la_entry->la->value.blob);
3570 DLIST_ADD(replmd_private->la_list, la_entry);
3573 return replmd_replicated_apply_next(ar);
3577 process one linked attribute structure
3579 static int replmd_process_linked_attribute(struct ldb_module *module,
3580 struct la_entry *la_entry)
3582 struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
3583 struct ldb_context *ldb = ldb_module_get_ctx(module);
3584 struct ldb_message *msg;
3585 TALLOC_CTX *tmp_ctx = talloc_new(la_entry);
3586 const struct dsdb_schema *schema = dsdb_get_schema(ldb, tmp_ctx);
3587 int ret;
3588 const struct dsdb_attribute *attr;
3589 struct dsdb_dn *dsdb_dn;
3590 uint64_t seq_num = 0;
3591 struct ldb_message_element *old_el;
3592 WERROR status;
3593 time_t t = time(NULL);
3594 struct ldb_result *res;
3595 const char *attrs[2];
3596 struct parsed_dn *pdn_list, *pdn;
3597 struct GUID guid = GUID_zero();
3598 NTSTATUS ntstatus;
3599 bool active = (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)?true:false;
3600 const struct GUID *our_invocation_id;
3603 linked_attributes[0]:
3604 &objs->linked_attributes[i]: struct drsuapi_DsReplicaLinkedAttribute
3605 identifier : *
3606 identifier: struct drsuapi_DsReplicaObjectIdentifier
3607 __ndr_size : 0x0000003a (58)
3608 __ndr_size_sid : 0x00000000 (0)
3609 guid : 8e95b6a9-13dd-4158-89db-3220a5be5cc7
3610 sid : S-0-0
3611 __ndr_size_dn : 0x00000000 (0)
3612 dn : ''
3613 attid : DRSUAPI_ATTRIBUTE_member (0x1F)
3614 value: struct drsuapi_DsAttributeValue
3615 __ndr_size : 0x0000007e (126)
3616 blob : *
3617 blob : DATA_BLOB length=126
3618 flags : 0x00000001 (1)
3619 1: DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE
3620 originating_add_time : Wed Sep 2 22:20:01 2009 EST
3621 meta_data: struct drsuapi_DsReplicaMetaData
3622 version : 0x00000015 (21)
3623 originating_change_time : Wed Sep 2 23:39:07 2009 EST
3624 originating_invocation_id: 794640f3-18cf-40ee-a211-a93992b67a64
3625 originating_usn : 0x000000000001e19c (123292)
3627 (for cases where the link is to a normal DN)
3628 &target: struct drsuapi_DsReplicaObjectIdentifier3
3629 __ndr_size : 0x0000007e (126)
3630 __ndr_size_sid : 0x0000001c (28)
3631 guid : 7639e594-db75-4086-b0d4-67890ae46031
3632 sid : S-1-5-21-2848215498-2472035911-1947525656-19924
3633 __ndr_size_dn : 0x00000022 (34)
3634 dn : 'CN=UOne,OU=TestOU,DC=vsofs8,DC=com'
3637 /* find the attribute being modified */
3638 attr = dsdb_attribute_by_attributeID_id(schema, la->attid);
3639 if (attr == NULL) {
3640 DEBUG(0, (__location__ ": Unable to find attributeID 0x%x\n", la->attid));
3641 talloc_free(tmp_ctx);
3642 return LDB_ERR_OPERATIONS_ERROR;
3645 attrs[0] = attr->lDAPDisplayName;
3646 attrs[1] = NULL;
3648 /* get the existing message from the db for the object with
3649 this GUID, returning attribute being modified. We will then
3650 use this msg as the basis for a modify call */
3651 ret = dsdb_module_search(module, tmp_ctx, &res, NULL, LDB_SCOPE_SUBTREE, attrs,
3652 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
3653 DSDB_SEARCH_SHOW_DELETED |
3654 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
3655 DSDB_SEARCH_REVEAL_INTERNALS,
3656 "objectGUID=%s", GUID_string(tmp_ctx, &la->identifier->guid));
3657 if (ret != LDB_SUCCESS) {
3658 talloc_free(tmp_ctx);
3659 return ret;
3661 if (res->count != 1) {
3662 ldb_asprintf_errstring(ldb, "DRS linked attribute for GUID %s - DN not found",
3663 GUID_string(tmp_ctx, &la->identifier->guid));
3664 talloc_free(tmp_ctx);
3665 return LDB_ERR_NO_SUCH_OBJECT;
3667 msg = res->msgs[0];
3669 if (msg->num_elements == 0) {
3670 ret = ldb_msg_add_empty(msg, attr->lDAPDisplayName, LDB_FLAG_MOD_REPLACE, &old_el);
3671 if (ret != LDB_SUCCESS) {
3672 ldb_module_oom(module);
3673 talloc_free(tmp_ctx);
3674 return LDB_ERR_OPERATIONS_ERROR;
3676 } else {
3677 old_el = &msg->elements[0];
3678 old_el->flags = LDB_FLAG_MOD_REPLACE;
3681 /* parse the existing links */
3682 ret = get_parsed_dns(module, tmp_ctx, old_el, &pdn_list, attr->syntax->ldap_oid);
3683 if (ret != LDB_SUCCESS) {
3684 talloc_free(tmp_ctx);
3685 return ret;
3688 /* get our invocationId */
3689 our_invocation_id = samdb_ntds_invocation_id(ldb);
3690 if (!our_invocation_id) {
3691 ldb_debug_set(ldb, LDB_DEBUG_ERROR, __location__ ": unable to find invocationId\n");
3692 talloc_free(tmp_ctx);
3693 return LDB_ERR_OPERATIONS_ERROR;
3696 ret = replmd_check_upgrade_links(pdn_list, old_el->num_values, our_invocation_id);
3697 if (ret != LDB_SUCCESS) {
3698 talloc_free(tmp_ctx);
3699 return ret;
3702 status = dsdb_dn_la_from_blob(ldb, attr, schema, tmp_ctx, la->value.blob, &dsdb_dn);
3703 if (!W_ERROR_IS_OK(status)) {
3704 ldb_asprintf_errstring(ldb, "Failed to parsed linked attribute blob for %s on %s - %s\n",
3705 old_el->name, ldb_dn_get_linearized(msg->dn), win_errstr(status));
3706 return LDB_ERR_OPERATIONS_ERROR;
3709 ntstatus = dsdb_get_extended_dn_guid(dsdb_dn->dn, &guid, "GUID");
3710 if (!NT_STATUS_IS_OK(ntstatus) && active) {
3711 ldb_asprintf_errstring(ldb, "Failed to find GUID in linked attribute blob for %s on %s from %s",
3712 old_el->name,
3713 ldb_dn_get_linearized(dsdb_dn->dn),
3714 ldb_dn_get_linearized(msg->dn));
3715 return LDB_ERR_OPERATIONS_ERROR;
3718 /* re-resolve the DN by GUID, as the DRS server may give us an
3719 old DN value */
3720 ret = dsdb_module_dn_by_guid(module, dsdb_dn, &guid, &dsdb_dn->dn);
3721 if (ret != LDB_SUCCESS) {
3722 DEBUG(2,(__location__ ": WARNING: Failed to re-resolve GUID %s - using %s",
3723 GUID_string(tmp_ctx, &guid),
3724 ldb_dn_get_linearized(dsdb_dn->dn)));
3727 /* see if this link already exists */
3728 pdn = parsed_dn_find(pdn_list, old_el->num_values, &guid, dsdb_dn->dn);
3729 if (pdn != NULL) {
3730 /* see if this update is newer than what we have already */
3731 struct GUID invocation_id = GUID_zero();
3732 uint32_t version = 0;
3733 NTTIME change_time = 0;
3734 uint32_t rmd_flags = dsdb_dn_rmd_flags(pdn->dsdb_dn->dn);
3736 dsdb_get_extended_dn_guid(pdn->dsdb_dn->dn, &invocation_id, "RMD_INVOCID");
3737 dsdb_get_extended_dn_uint32(pdn->dsdb_dn->dn, &version, "RMD_VERSION");
3738 dsdb_get_extended_dn_nttime(pdn->dsdb_dn->dn, &change_time, "RMD_CHANGETIME");
3740 if (!replmd_update_is_newer(&invocation_id,
3741 &la->meta_data.originating_invocation_id,
3742 version,
3743 la->meta_data.version,
3744 change_time,
3745 la->meta_data.originating_change_time)) {
3746 DEBUG(1,("Discarding older DRS linked attribute update to %s on %s from %s\n",
3747 old_el->name, ldb_dn_get_linearized(msg->dn),
3748 GUID_string(tmp_ctx, &la->meta_data.originating_invocation_id)));
3749 talloc_free(tmp_ctx);
3750 return LDB_SUCCESS;
3753 /* get a seq_num for this change */
3754 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
3755 if (ret != LDB_SUCCESS) {
3756 talloc_free(tmp_ctx);
3757 return ret;
3760 if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
3761 /* remove the existing backlink */
3762 ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid, false, attr, false);
3763 if (ret != LDB_SUCCESS) {
3764 talloc_free(tmp_ctx);
3765 return ret;
3769 ret = replmd_update_la_val(tmp_ctx, pdn->v, dsdb_dn, pdn->dsdb_dn,
3770 &la->meta_data.originating_invocation_id,
3771 la->meta_data.originating_usn, seq_num,
3772 la->meta_data.originating_change_time,
3773 la->meta_data.version,
3774 !active);
3775 if (ret != LDB_SUCCESS) {
3776 talloc_free(tmp_ctx);
3777 return ret;
3780 if (active) {
3781 /* add the new backlink */
3782 ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid, true, attr, false);
3783 if (ret != LDB_SUCCESS) {
3784 talloc_free(tmp_ctx);
3785 return ret;
3788 } else {
3789 /* get a seq_num for this change */
3790 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
3791 if (ret != LDB_SUCCESS) {
3792 talloc_free(tmp_ctx);
3793 return ret;
3796 old_el->values = talloc_realloc(msg->elements, old_el->values,
3797 struct ldb_val, old_el->num_values+1);
3798 if (!old_el->values) {
3799 ldb_module_oom(module);
3800 return LDB_ERR_OPERATIONS_ERROR;
3802 old_el->num_values++;
3804 ret = replmd_build_la_val(tmp_ctx, &old_el->values[old_el->num_values-1], dsdb_dn,
3805 &la->meta_data.originating_invocation_id,
3806 la->meta_data.originating_usn, seq_num,
3807 la->meta_data.originating_change_time,
3808 la->meta_data.version,
3809 (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)?false:true);
3810 if (ret != LDB_SUCCESS) {
3811 talloc_free(tmp_ctx);
3812 return ret;
3815 if (active) {
3816 ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid,
3817 true, attr, false);
3818 if (ret != LDB_SUCCESS) {
3819 talloc_free(tmp_ctx);
3820 return ret;
3825 /* we only change whenChanged and uSNChanged if the seq_num
3826 has changed */
3827 if (add_time_element(msg, "whenChanged", t) != LDB_SUCCESS) {
3828 talloc_free(tmp_ctx);
3829 return LDB_ERR_OPERATIONS_ERROR;
3832 if (add_uint64_element(msg, "uSNChanged", seq_num) != LDB_SUCCESS) {
3833 talloc_free(tmp_ctx);
3834 return LDB_ERR_OPERATIONS_ERROR;
3837 ret = dsdb_check_single_valued_link(attr, old_el);
3838 if (ret != LDB_SUCCESS) {
3839 talloc_free(tmp_ctx);
3840 return ret;
3843 ret = dsdb_module_modify(module, msg, DSDB_MODIFY_RELAX);
3844 if (ret != LDB_SUCCESS) {
3845 ldb_debug(ldb, LDB_DEBUG_WARNING, "Failed to apply linked attribute change '%s'\n%s\n",
3846 ldb_errstring(ldb),
3847 ldb_ldif_message_string(ldb, tmp_ctx, LDB_CHANGETYPE_MODIFY, msg));
3848 talloc_free(tmp_ctx);
3849 return ret;
3852 talloc_free(tmp_ctx);
3854 return ret;
3857 static int replmd_extended(struct ldb_module *module, struct ldb_request *req)
3859 if (strcmp(req->op.extended.oid, DSDB_EXTENDED_REPLICATED_OBJECTS_OID) == 0) {
3860 return replmd_extended_replicated_objects(module, req);
3863 return ldb_next_request(module, req);
3868 we hook into the transaction operations to allow us to
3869 perform the linked attribute updates at the end of the whole
3870 transaction. This allows a forward linked attribute to be created
3871 before the object is created. During a vampire, w2k8 sends us linked
3872 attributes before the objects they are part of.
3874 static int replmd_start_transaction(struct ldb_module *module)
3876 /* create our private structure for this transaction */
3877 struct replmd_private *replmd_private = talloc_get_type(ldb_module_get_private(module),
3878 struct replmd_private);
3879 replmd_txn_cleanup(replmd_private);
3881 /* free any leftover mod_usn records from cancelled
3882 transactions */
3883 while (replmd_private->ncs) {
3884 struct nc_entry *e = replmd_private->ncs;
3885 DLIST_REMOVE(replmd_private->ncs, e);
3886 talloc_free(e);
3889 return ldb_next_start_trans(module);
3893 on prepare commit we loop over our queued la_context structures and
3894 apply each of them
3896 static int replmd_prepare_commit(struct ldb_module *module)
3898 struct replmd_private *replmd_private =
3899 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
3900 struct la_entry *la, *prev;
3901 struct la_backlink *bl;
3902 int ret;
3904 /* walk the list backwards, to do the first entry first, as we
3905 * added the entries with DLIST_ADD() which puts them at the
3906 * start of the list */
3907 for (la = DLIST_TAIL(replmd_private->la_list); la; la=prev) {
3908 prev = DLIST_PREV(la);
3909 DLIST_REMOVE(replmd_private->la_list, la);
3910 ret = replmd_process_linked_attribute(module, la);
3911 if (ret != LDB_SUCCESS) {
3912 replmd_txn_cleanup(replmd_private);
3913 return ret;
3917 /* process our backlink list, creating and deleting backlinks
3918 as necessary */
3919 for (bl=replmd_private->la_backlinks; bl; bl=bl->next) {
3920 ret = replmd_process_backlink(module, bl);
3921 if (ret != LDB_SUCCESS) {
3922 replmd_txn_cleanup(replmd_private);
3923 return ret;
3927 replmd_txn_cleanup(replmd_private);
3929 /* possibly change @REPLCHANGED */
3930 ret = replmd_notify_store(module);
3931 if (ret != LDB_SUCCESS) {
3932 return ret;
3935 return ldb_next_prepare_commit(module);
3938 static int replmd_del_transaction(struct ldb_module *module)
3940 struct replmd_private *replmd_private =
3941 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
3942 replmd_txn_cleanup(replmd_private);
3944 return ldb_next_del_trans(module);
3948 _PUBLIC_ const struct ldb_module_ops ldb_repl_meta_data_module_ops = {
3949 .name = "repl_meta_data",
3950 .init_context = replmd_init,
3951 .add = replmd_add,
3952 .modify = replmd_modify,
3953 .rename = replmd_rename,
3954 .del = replmd_delete,
3955 .extended = replmd_extended,
3956 .start_transaction = replmd_start_transaction,
3957 .prepare_commit = replmd_prepare_commit,
3958 .del_transaction = replmd_del_transaction,