dsdb-repl_meta_data: Handle renames better, considering only the RDN as given, and...
[Samba/gebeck_regimport.git] / source4 / dsdb / samdb / ldb_modules / repl_meta_data.c
blob5fc71ab1d2bced362dd86ca3e10f46a304f7d973
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
8 Copyright (C) Matthieu Patou <mat@samba.org> 2010
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 3 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>.
25 * Name: ldb
27 * Component: ldb repl_meta_data module
29 * Description: - add a unique objectGUID onto every new record,
30 * - handle whenCreated, whenChanged timestamps
31 * - handle uSNCreated, uSNChanged numbers
32 * - handle replPropertyMetaData attribute
34 * Author: Simo Sorce
35 * Author: Stefan Metzmacher
38 #include "includes.h"
39 #include "ldb_module.h"
40 #include "dsdb/samdb/samdb.h"
41 #include "dsdb/common/proto.h"
42 #include "../libds/common/flags.h"
43 #include "librpc/gen_ndr/ndr_misc.h"
44 #include "librpc/gen_ndr/ndr_drsuapi.h"
45 #include "librpc/gen_ndr/ndr_drsblobs.h"
46 #include "param/param.h"
47 #include "libcli/security/security.h"
48 #include "lib/util/dlinklist.h"
49 #include "dsdb/samdb/ldb_modules/util.h"
50 #include "lib/util/binsearch.h"
51 #include "lib/util/tsort.h"
54 * It's 29/12/9999 at 23:59:59 UTC as specified in MS-ADTS 7.1.1.4.2
55 * Deleted Objects Container
57 static const NTTIME DELETED_OBJECT_CONTAINER_CHANGE_TIME = 2650466015990000000ULL;
59 struct replmd_private {
60 TALLOC_CTX *la_ctx;
61 struct la_entry *la_list;
62 TALLOC_CTX *bl_ctx;
63 struct la_backlink *la_backlinks;
64 struct nc_entry {
65 struct nc_entry *prev, *next;
66 struct ldb_dn *dn;
67 uint64_t mod_usn;
68 uint64_t mod_usn_urgent;
69 } *ncs;
72 struct la_entry {
73 struct la_entry *next, *prev;
74 struct drsuapi_DsReplicaLinkedAttribute *la;
77 struct replmd_replicated_request {
78 struct ldb_module *module;
79 struct ldb_request *req;
81 const struct dsdb_schema *schema;
83 /* the controls we pass down */
84 struct ldb_control **controls;
86 /* details for the mode where we apply a bunch of inbound replication meessages */
87 bool apply_mode;
88 uint32_t index_current;
89 struct dsdb_extended_replicated_objects *objs;
91 struct ldb_message *search_msg;
93 uint64_t seq_num;
94 bool is_urgent;
97 static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar);
99 enum urgent_situation {
100 REPL_URGENT_ON_CREATE = 1,
101 REPL_URGENT_ON_UPDATE = 2,
102 REPL_URGENT_ON_DELETE = 4
106 static const struct {
107 const char *update_name;
108 enum urgent_situation repl_situation;
109 } urgent_objects[] = {
110 {"nTDSDSA", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE)},
111 {"crossRef", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE)},
112 {"attributeSchema", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
113 {"classSchema", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
114 {"secret", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
115 {"rIDManager", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
116 {NULL, 0}
119 /* Attributes looked for when updating or deleting, to check for a urgent replication needed */
120 static const char *urgent_attrs[] = {
121 "lockoutTime",
122 "pwdLastSet",
123 "userAccountControl",
124 NULL
128 static bool replmd_check_urgent_objectclass(const struct ldb_message_element *objectclass_el,
129 enum urgent_situation situation)
131 unsigned int i, j;
132 for (i=0; urgent_objects[i].update_name; i++) {
134 if ((situation & urgent_objects[i].repl_situation) == 0) {
135 continue;
138 for (j=0; j<objectclass_el->num_values; j++) {
139 const struct ldb_val *v = &objectclass_el->values[j];
140 if (ldb_attr_cmp((const char *)v->data, urgent_objects[i].update_name) == 0) {
141 return true;
145 return false;
148 static bool replmd_check_urgent_attribute(const struct ldb_message_element *el)
150 if (ldb_attr_in_list(urgent_attrs, el->name)) {
151 return true;
153 return false;
157 static int replmd_replicated_apply_next(struct replmd_replicated_request *ar);
160 initialise the module
161 allocate the private structure and build the list
162 of partition DNs for use by replmd_notify()
164 static int replmd_init(struct ldb_module *module)
166 struct replmd_private *replmd_private;
167 struct ldb_context *ldb = ldb_module_get_ctx(module);
169 replmd_private = talloc_zero(module, struct replmd_private);
170 if (replmd_private == NULL) {
171 ldb_oom(ldb);
172 return LDB_ERR_OPERATIONS_ERROR;
174 ldb_module_set_private(module, replmd_private);
176 return ldb_next_init(module);
180 cleanup our per-transaction contexts
182 static void replmd_txn_cleanup(struct replmd_private *replmd_private)
184 talloc_free(replmd_private->la_ctx);
185 replmd_private->la_list = NULL;
186 replmd_private->la_ctx = NULL;
188 talloc_free(replmd_private->bl_ctx);
189 replmd_private->la_backlinks = NULL;
190 replmd_private->bl_ctx = NULL;
194 struct la_backlink {
195 struct la_backlink *next, *prev;
196 const char *attr_name;
197 struct GUID forward_guid, target_guid;
198 bool active;
202 process a backlinks we accumulated during a transaction, adding and
203 deleting the backlinks from the target objects
205 static int replmd_process_backlink(struct ldb_module *module, struct la_backlink *bl, struct ldb_request *parent)
207 struct ldb_dn *target_dn, *source_dn;
208 int ret;
209 struct ldb_context *ldb = ldb_module_get_ctx(module);
210 struct ldb_message *msg;
211 TALLOC_CTX *tmp_ctx = talloc_new(bl);
212 char *dn_string;
215 - find DN of target
216 - find DN of source
217 - construct ldb_message
218 - either an add or a delete
220 ret = dsdb_module_dn_by_guid(module, tmp_ctx, &bl->target_guid, &target_dn, parent);
221 if (ret != LDB_SUCCESS) {
222 DEBUG(2,(__location__ ": WARNING: Failed to find target DN for linked attribute with GUID %s\n",
223 GUID_string(bl, &bl->target_guid)));
224 return LDB_SUCCESS;
227 ret = dsdb_module_dn_by_guid(module, tmp_ctx, &bl->forward_guid, &source_dn, parent);
228 if (ret != LDB_SUCCESS) {
229 ldb_asprintf_errstring(ldb, "Failed to find source DN for linked attribute with GUID %s\n",
230 GUID_string(bl, &bl->forward_guid));
231 talloc_free(tmp_ctx);
232 return ret;
235 msg = ldb_msg_new(tmp_ctx);
236 if (msg == NULL) {
237 ldb_module_oom(module);
238 talloc_free(tmp_ctx);
239 return LDB_ERR_OPERATIONS_ERROR;
242 /* construct a ldb_message for adding/deleting the backlink */
243 msg->dn = target_dn;
244 dn_string = ldb_dn_get_extended_linearized(tmp_ctx, source_dn, 1);
245 if (!dn_string) {
246 ldb_module_oom(module);
247 talloc_free(tmp_ctx);
248 return LDB_ERR_OPERATIONS_ERROR;
250 ret = ldb_msg_add_steal_string(msg, bl->attr_name, dn_string);
251 if (ret != LDB_SUCCESS) {
252 talloc_free(tmp_ctx);
253 return ret;
255 msg->elements[0].flags = bl->active?LDB_FLAG_MOD_ADD:LDB_FLAG_MOD_DELETE;
257 /* a backlink should never be single valued. Unfortunately the
258 exchange schema has a attribute
259 msExchBridgeheadedLocalConnectorsDNBL which is single
260 valued and a backlink. We need to cope with that by
261 ignoring the single value flag */
262 msg->elements[0].flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
264 ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE, parent);
265 if (ret == LDB_ERR_NO_SUCH_ATTRIBUTE && !bl->active) {
266 /* we allow LDB_ERR_NO_SUCH_ATTRIBUTE as success to
267 cope with possible corruption where the backlink has
268 already been removed */
269 DEBUG(3,("WARNING: backlink from %s already removed from %s - %s\n",
270 ldb_dn_get_linearized(target_dn),
271 ldb_dn_get_linearized(source_dn),
272 ldb_errstring(ldb)));
273 ret = LDB_SUCCESS;
274 } else if (ret != LDB_SUCCESS) {
275 ldb_asprintf_errstring(ldb, "Failed to %s backlink from %s to %s - %s",
276 bl->active?"add":"remove",
277 ldb_dn_get_linearized(source_dn),
278 ldb_dn_get_linearized(target_dn),
279 ldb_errstring(ldb));
280 talloc_free(tmp_ctx);
281 return ret;
283 talloc_free(tmp_ctx);
284 return ret;
288 add a backlink to the list of backlinks to add/delete in the prepare
289 commit
291 static int replmd_add_backlink(struct ldb_module *module, const struct dsdb_schema *schema,
292 struct GUID *forward_guid, struct GUID *target_guid,
293 bool active, const struct dsdb_attribute *schema_attr, bool immediate)
295 const struct dsdb_attribute *target_attr;
296 struct la_backlink *bl;
297 struct replmd_private *replmd_private =
298 talloc_get_type_abort(ldb_module_get_private(module), struct replmd_private);
300 target_attr = dsdb_attribute_by_linkID(schema, schema_attr->linkID ^ 1);
301 if (!target_attr) {
303 * windows 2003 has a broken schema where the
304 * definition of msDS-IsDomainFor is missing (which is
305 * supposed to be the backlink of the
306 * msDS-HasDomainNCs attribute
308 return LDB_SUCCESS;
311 /* see if its already in the list */
312 for (bl=replmd_private->la_backlinks; bl; bl=bl->next) {
313 if (GUID_equal(forward_guid, &bl->forward_guid) &&
314 GUID_equal(target_guid, &bl->target_guid) &&
315 (target_attr->lDAPDisplayName == bl->attr_name ||
316 strcmp(target_attr->lDAPDisplayName, bl->attr_name) == 0)) {
317 break;
321 if (bl) {
322 /* we found an existing one */
323 if (bl->active == active) {
324 return LDB_SUCCESS;
326 DLIST_REMOVE(replmd_private->la_backlinks, bl);
327 talloc_free(bl);
328 return LDB_SUCCESS;
331 if (replmd_private->bl_ctx == NULL) {
332 replmd_private->bl_ctx = talloc_new(replmd_private);
333 if (replmd_private->bl_ctx == NULL) {
334 ldb_module_oom(module);
335 return LDB_ERR_OPERATIONS_ERROR;
339 /* its a new one */
340 bl = talloc(replmd_private->bl_ctx, struct la_backlink);
341 if (bl == NULL) {
342 ldb_module_oom(module);
343 return LDB_ERR_OPERATIONS_ERROR;
346 /* Ensure the schema does not go away before the bl->attr_name is used */
347 if (!talloc_reference(bl, schema)) {
348 talloc_free(bl);
349 ldb_module_oom(module);
350 return LDB_ERR_OPERATIONS_ERROR;
353 bl->attr_name = target_attr->lDAPDisplayName;
354 bl->forward_guid = *forward_guid;
355 bl->target_guid = *target_guid;
356 bl->active = active;
358 /* the caller may ask for this backlink to be processed
359 immediately */
360 if (immediate) {
361 int ret = replmd_process_backlink(module, bl, NULL);
362 talloc_free(bl);
363 return ret;
366 DLIST_ADD(replmd_private->la_backlinks, bl);
368 return LDB_SUCCESS;
373 * Callback for most write operations in this module:
375 * notify the repl task that a object has changed. The notifies are
376 * gathered up in the replmd_private structure then written to the
377 * @REPLCHANGED object in each partition during the prepare_commit
379 static int replmd_op_callback(struct ldb_request *req, struct ldb_reply *ares)
381 int ret;
382 struct replmd_replicated_request *ac =
383 talloc_get_type_abort(req->context, struct replmd_replicated_request);
384 struct replmd_private *replmd_private =
385 talloc_get_type_abort(ldb_module_get_private(ac->module), struct replmd_private);
386 struct nc_entry *modified_partition;
387 struct ldb_control *partition_ctrl;
388 const struct dsdb_control_current_partition *partition;
390 struct ldb_control **controls;
392 partition_ctrl = ldb_reply_get_control(ares, DSDB_CONTROL_CURRENT_PARTITION_OID);
394 controls = ares->controls;
395 if (ldb_request_get_control(ac->req,
396 DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
398 * Remove the current partition control from what we pass up
399 * the chain if it hasn't been requested manually.
401 controls = ldb_controls_except_specified(ares->controls, ares,
402 partition_ctrl);
405 if (ares->error != LDB_SUCCESS) {
406 DEBUG(5,("%s failure. Error is: %s\n", __FUNCTION__, ldb_strerror(ares->error)));
407 return ldb_module_done(ac->req, controls,
408 ares->response, ares->error);
411 if (ares->type != LDB_REPLY_DONE) {
412 ldb_set_errstring(ldb_module_get_ctx(ac->module), "Invalid reply type for notify\n!");
413 return ldb_module_done(ac->req, NULL,
414 NULL, LDB_ERR_OPERATIONS_ERROR);
417 if (!partition_ctrl) {
418 ldb_set_errstring(ldb_module_get_ctx(ac->module),"No partition control on reply");
419 return ldb_module_done(ac->req, NULL,
420 NULL, LDB_ERR_OPERATIONS_ERROR);
423 partition = talloc_get_type_abort(partition_ctrl->data,
424 struct dsdb_control_current_partition);
426 if (ac->seq_num > 0) {
427 for (modified_partition = replmd_private->ncs; modified_partition;
428 modified_partition = modified_partition->next) {
429 if (ldb_dn_compare(modified_partition->dn, partition->dn) == 0) {
430 break;
434 if (modified_partition == NULL) {
435 modified_partition = talloc_zero(replmd_private, struct nc_entry);
436 if (!modified_partition) {
437 ldb_oom(ldb_module_get_ctx(ac->module));
438 return ldb_module_done(ac->req, NULL,
439 NULL, LDB_ERR_OPERATIONS_ERROR);
441 modified_partition->dn = ldb_dn_copy(modified_partition, partition->dn);
442 if (!modified_partition->dn) {
443 ldb_oom(ldb_module_get_ctx(ac->module));
444 return ldb_module_done(ac->req, NULL,
445 NULL, LDB_ERR_OPERATIONS_ERROR);
447 DLIST_ADD(replmd_private->ncs, modified_partition);
450 if (ac->seq_num > modified_partition->mod_usn) {
451 modified_partition->mod_usn = ac->seq_num;
452 if (ac->is_urgent) {
453 modified_partition->mod_usn_urgent = ac->seq_num;
458 if (ac->apply_mode) {
459 talloc_free(ares);
460 ac->index_current++;
462 ret = replmd_replicated_apply_next(ac);
463 if (ret != LDB_SUCCESS) {
464 return ldb_module_done(ac->req, NULL, NULL, ret);
466 return ret;
467 } else {
468 /* free the partition control container here, for the
469 * common path. Other cases will have it cleaned up
470 * eventually with the ares */
471 talloc_free(partition_ctrl);
472 return ldb_module_done(ac->req, controls,
473 ares->response, LDB_SUCCESS);
479 * update a @REPLCHANGED record in each partition if there have been
480 * any writes of replicated data in the partition
482 static int replmd_notify_store(struct ldb_module *module, struct ldb_request *parent)
484 struct replmd_private *replmd_private =
485 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
487 while (replmd_private->ncs) {
488 int ret;
489 struct nc_entry *modified_partition = replmd_private->ncs;
491 ret = dsdb_module_save_partition_usn(module, modified_partition->dn,
492 modified_partition->mod_usn,
493 modified_partition->mod_usn_urgent, parent);
494 if (ret != LDB_SUCCESS) {
495 DEBUG(0,(__location__ ": Failed to save partition uSN for %s\n",
496 ldb_dn_get_linearized(modified_partition->dn)));
497 return ret;
499 DLIST_REMOVE(replmd_private->ncs, modified_partition);
500 talloc_free(modified_partition);
503 return LDB_SUCCESS;
508 created a replmd_replicated_request context
510 static struct replmd_replicated_request *replmd_ctx_init(struct ldb_module *module,
511 struct ldb_request *req)
513 struct ldb_context *ldb;
514 struct replmd_replicated_request *ac;
516 ldb = ldb_module_get_ctx(module);
518 ac = talloc_zero(req, struct replmd_replicated_request);
519 if (ac == NULL) {
520 ldb_oom(ldb);
521 return NULL;
524 ac->module = module;
525 ac->req = req;
527 ac->schema = dsdb_get_schema(ldb, ac);
528 if (!ac->schema) {
529 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
530 "replmd_modify: no dsdb_schema loaded");
531 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
532 return NULL;
535 return ac;
539 add a time element to a record
541 static int add_time_element(struct ldb_message *msg, const char *attr, time_t t)
543 struct ldb_message_element *el;
544 char *s;
545 int ret;
547 if (ldb_msg_find_element(msg, attr) != NULL) {
548 return LDB_SUCCESS;
551 s = ldb_timestring(msg, t);
552 if (s == NULL) {
553 return LDB_ERR_OPERATIONS_ERROR;
556 ret = ldb_msg_add_string(msg, attr, s);
557 if (ret != LDB_SUCCESS) {
558 return ret;
561 el = ldb_msg_find_element(msg, attr);
562 /* always set as replace. This works because on add ops, the flag
563 is ignored */
564 el->flags = LDB_FLAG_MOD_REPLACE;
566 return LDB_SUCCESS;
570 add a uint64_t element to a record
572 static int add_uint64_element(struct ldb_context *ldb, struct ldb_message *msg,
573 const char *attr, uint64_t v)
575 struct ldb_message_element *el;
576 int ret;
578 if (ldb_msg_find_element(msg, attr) != NULL) {
579 return LDB_SUCCESS;
582 ret = samdb_msg_add_uint64(ldb, msg, msg, attr, v);
583 if (ret != LDB_SUCCESS) {
584 return ret;
587 el = ldb_msg_find_element(msg, attr);
588 /* always set as replace. This works because on add ops, the flag
589 is ignored */
590 el->flags = LDB_FLAG_MOD_REPLACE;
592 return LDB_SUCCESS;
595 static int replmd_replPropertyMetaData1_attid_sort(const struct replPropertyMetaData1 *m1,
596 const struct replPropertyMetaData1 *m2,
597 const uint32_t *rdn_attid)
599 if (m1->attid == m2->attid) {
600 return 0;
604 * the rdn attribute should be at the end!
605 * so we need to return a value greater than zero
606 * which means m1 is greater than m2
608 if (m1->attid == *rdn_attid) {
609 return 1;
613 * the rdn attribute should be at the end!
614 * so we need to return a value less than zero
615 * which means m2 is greater than m1
617 if (m2->attid == *rdn_attid) {
618 return -1;
621 return m1->attid > m2->attid ? 1 : -1;
624 static int replmd_replPropertyMetaDataCtr1_sort(struct replPropertyMetaDataCtr1 *ctr1,
625 const struct dsdb_schema *schema,
626 struct ldb_dn *dn)
628 const char *rdn_name;
629 const struct dsdb_attribute *rdn_sa;
631 rdn_name = ldb_dn_get_rdn_name(dn);
632 if (!rdn_name) {
633 DEBUG(0,(__location__ ": No rDN for %s?\n", ldb_dn_get_linearized(dn)));
634 return LDB_ERR_OPERATIONS_ERROR;
637 rdn_sa = dsdb_attribute_by_lDAPDisplayName(schema, rdn_name);
638 if (rdn_sa == NULL) {
639 DEBUG(0,(__location__ ": No sa found for rDN %s for %s\n", rdn_name, ldb_dn_get_linearized(dn)));
640 return LDB_ERR_OPERATIONS_ERROR;
643 DEBUG(6,("Sorting rpmd with attid exception %u rDN=%s DN=%s\n",
644 rdn_sa->attributeID_id, rdn_name, ldb_dn_get_linearized(dn)));
646 LDB_TYPESAFE_QSORT(ctr1->array, ctr1->count, &rdn_sa->attributeID_id, replmd_replPropertyMetaData1_attid_sort);
648 return LDB_SUCCESS;
651 static int replmd_ldb_message_element_attid_sort(const struct ldb_message_element *e1,
652 const struct ldb_message_element *e2,
653 const struct dsdb_schema *schema)
655 const struct dsdb_attribute *a1;
656 const struct dsdb_attribute *a2;
659 * TODO: make this faster by caching the dsdb_attribute pointer
660 * on the ldb_messag_element
663 a1 = dsdb_attribute_by_lDAPDisplayName(schema, e1->name);
664 a2 = dsdb_attribute_by_lDAPDisplayName(schema, e2->name);
666 if (a1->attributeID_id == a2->attributeID_id) {
667 return 0;
669 return a1->attributeID_id > a2->attributeID_id ? 1 : -1;
672 static void replmd_ldb_message_sort(struct ldb_message *msg,
673 const struct dsdb_schema *schema)
675 LDB_TYPESAFE_QSORT(msg->elements, msg->num_elements, schema, replmd_ldb_message_element_attid_sort);
678 static int replmd_build_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
679 const struct GUID *invocation_id, uint64_t seq_num,
680 uint64_t local_usn, NTTIME nttime, uint32_t version, bool deleted);
684 fix up linked attributes in replmd_add.
685 This involves setting up the right meta-data in extended DN
686 components, and creating backlinks to the object
688 static int replmd_add_fix_la(struct ldb_module *module, struct ldb_message_element *el,
689 uint64_t seq_num, const struct GUID *invocationId, time_t t,
690 struct GUID *guid, const struct dsdb_attribute *sa, struct ldb_request *parent)
692 unsigned int i;
693 TALLOC_CTX *tmp_ctx = talloc_new(el->values);
694 struct ldb_context *ldb = ldb_module_get_ctx(module);
696 /* We will take a reference to the schema in replmd_add_backlink */
697 const struct dsdb_schema *schema = dsdb_get_schema(ldb, NULL);
698 NTTIME now;
700 unix_to_nt_time(&now, t);
702 for (i=0; i<el->num_values; i++) {
703 struct ldb_val *v = &el->values[i];
704 struct dsdb_dn *dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, v, sa->syntax->ldap_oid);
705 struct GUID target_guid;
706 NTSTATUS status;
707 int ret;
709 /* note that the DN already has the extended
710 components from the extended_dn_store module */
711 status = dsdb_get_extended_dn_guid(dsdb_dn->dn, &target_guid, "GUID");
712 if (!NT_STATUS_IS_OK(status) || GUID_all_zero(&target_guid)) {
713 ret = dsdb_module_guid_by_dn(module, dsdb_dn->dn, &target_guid, parent);
714 if (ret != LDB_SUCCESS) {
715 talloc_free(tmp_ctx);
716 return ret;
718 ret = dsdb_set_extended_dn_guid(dsdb_dn->dn, &target_guid, "GUID");
719 if (ret != LDB_SUCCESS) {
720 talloc_free(tmp_ctx);
721 return ret;
725 ret = replmd_build_la_val(el->values, v, dsdb_dn, invocationId,
726 seq_num, seq_num, now, 0, false);
727 if (ret != LDB_SUCCESS) {
728 talloc_free(tmp_ctx);
729 return ret;
732 ret = replmd_add_backlink(module, schema, guid, &target_guid, true, sa, false);
733 if (ret != LDB_SUCCESS) {
734 talloc_free(tmp_ctx);
735 return ret;
739 talloc_free(tmp_ctx);
740 return LDB_SUCCESS;
745 intercept add requests
747 static int replmd_add(struct ldb_module *module, struct ldb_request *req)
749 struct samldb_msds_intid_persistant *msds_intid_struct;
750 struct ldb_context *ldb;
751 struct ldb_control *control;
752 struct replmd_replicated_request *ac;
753 enum ndr_err_code ndr_err;
754 struct ldb_request *down_req;
755 struct ldb_message *msg;
756 const DATA_BLOB *guid_blob;
757 struct GUID guid;
758 struct replPropertyMetaDataBlob nmd;
759 struct ldb_val nmd_value;
760 const struct GUID *our_invocation_id;
761 time_t t = time(NULL);
762 NTTIME now;
763 char *time_str;
764 int ret;
765 unsigned int i;
766 unsigned int functional_level;
767 uint32_t ni=0;
768 bool allow_add_guid = false;
769 bool remove_current_guid = false;
770 bool is_urgent = false;
771 struct ldb_message_element *objectclass_el;
773 /* check if there's a show relax control (used by provision to say 'I know what I'm doing') */
774 control = ldb_request_get_control(req, LDB_CONTROL_RELAX_OID);
775 if (control) {
776 allow_add_guid = true;
779 /* do not manipulate our control entries */
780 if (ldb_dn_is_special(req->op.add.message->dn)) {
781 return ldb_next_request(module, req);
784 ldb = ldb_module_get_ctx(module);
786 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_add\n");
788 guid_blob = ldb_msg_find_ldb_val(req->op.add.message, "objectGUID");
789 if (guid_blob != NULL) {
790 if (!allow_add_guid) {
791 ldb_set_errstring(ldb,
792 "replmd_add: it's not allowed to add an object with objectGUID!");
793 return LDB_ERR_UNWILLING_TO_PERFORM;
794 } else {
795 NTSTATUS status = GUID_from_data_blob(guid_blob,&guid);
796 if (!NT_STATUS_IS_OK(status)) {
797 ldb_set_errstring(ldb,
798 "replmd_add: Unable to parse the 'objectGUID' as a GUID!");
799 return LDB_ERR_UNWILLING_TO_PERFORM;
801 /* we remove this attribute as it can be a string and
802 * will not be treated correctly and then we will re-add
803 * it later on in the good format */
804 remove_current_guid = true;
806 } else {
807 /* a new GUID */
808 guid = GUID_random();
811 ac = replmd_ctx_init(module, req);
812 if (ac == NULL) {
813 return ldb_module_oom(module);
816 functional_level = dsdb_functional_level(ldb);
818 /* Get a sequence number from the backend */
819 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ac->seq_num);
820 if (ret != LDB_SUCCESS) {
821 talloc_free(ac);
822 return ret;
825 /* get our invocationId */
826 our_invocation_id = samdb_ntds_invocation_id(ldb);
827 if (!our_invocation_id) {
828 ldb_debug_set(ldb, LDB_DEBUG_ERROR,
829 "replmd_add: unable to find invocationId\n");
830 talloc_free(ac);
831 return LDB_ERR_OPERATIONS_ERROR;
834 /* we have to copy the message as the caller might have it as a const */
835 msg = ldb_msg_copy_shallow(ac, req->op.add.message);
836 if (msg == NULL) {
837 ldb_oom(ldb);
838 talloc_free(ac);
839 return LDB_ERR_OPERATIONS_ERROR;
842 /* generated times */
843 unix_to_nt_time(&now, t);
844 time_str = ldb_timestring(msg, t);
845 if (!time_str) {
846 ldb_oom(ldb);
847 talloc_free(ac);
848 return LDB_ERR_OPERATIONS_ERROR;
850 if (remove_current_guid) {
851 ldb_msg_remove_attr(msg,"objectGUID");
855 * remove autogenerated attributes
857 ldb_msg_remove_attr(msg, "whenCreated");
858 ldb_msg_remove_attr(msg, "whenChanged");
859 ldb_msg_remove_attr(msg, "uSNCreated");
860 ldb_msg_remove_attr(msg, "uSNChanged");
861 ldb_msg_remove_attr(msg, "replPropertyMetaData");
864 * readd replicated attributes
866 ret = ldb_msg_add_string(msg, "whenCreated", time_str);
867 if (ret != LDB_SUCCESS) {
868 ldb_oom(ldb);
869 talloc_free(ac);
870 return ret;
873 /* build the replication meta_data */
874 ZERO_STRUCT(nmd);
875 nmd.version = 1;
876 nmd.ctr.ctr1.count = msg->num_elements;
877 nmd.ctr.ctr1.array = talloc_array(msg,
878 struct replPropertyMetaData1,
879 nmd.ctr.ctr1.count);
880 if (!nmd.ctr.ctr1.array) {
881 ldb_oom(ldb);
882 talloc_free(ac);
883 return LDB_ERR_OPERATIONS_ERROR;
886 for (i=0; i < msg->num_elements; i++) {
887 struct ldb_message_element *e = &msg->elements[i];
888 struct replPropertyMetaData1 *m = &nmd.ctr.ctr1.array[ni];
889 const struct dsdb_attribute *sa;
891 if (e->name[0] == '@') continue;
893 sa = dsdb_attribute_by_lDAPDisplayName(ac->schema, e->name);
894 if (!sa) {
895 ldb_debug_set(ldb, LDB_DEBUG_ERROR,
896 "replmd_add: attribute '%s' not defined in schema\n",
897 e->name);
898 talloc_free(ac);
899 return LDB_ERR_NO_SUCH_ATTRIBUTE;
902 if ((sa->systemFlags & DS_FLAG_ATTR_NOT_REPLICATED) || (sa->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED)) {
903 /* if the attribute is not replicated (0x00000001)
904 * or constructed (0x00000004) it has no metadata
906 continue;
909 if (sa->linkID != 0 && functional_level > DS_DOMAIN_FUNCTION_2000) {
910 ret = replmd_add_fix_la(module, e, ac->seq_num, our_invocation_id, t, &guid, sa, req);
911 if (ret != LDB_SUCCESS) {
912 talloc_free(ac);
913 return ret;
915 /* linked attributes are not stored in
916 replPropertyMetaData in FL above w2k */
917 continue;
920 m->attid = sa->attributeID_id;
921 m->version = 1;
922 if (m->attid == 0x20030) {
923 const struct ldb_val *rdn_val = ldb_dn_get_rdn_val(msg->dn);
924 const char* rdn;
926 if (rdn_val == NULL) {
927 ldb_oom(ldb);
928 talloc_free(ac);
929 return LDB_ERR_OPERATIONS_ERROR;
932 rdn = (const char*)rdn_val->data;
933 if (strcmp(rdn, "Deleted Objects") == 0) {
935 * Set the originating_change_time to 29/12/9999 at 23:59:59
936 * as specified in MS-ADTS 7.1.1.4.2 Deleted Objects Container
938 m->originating_change_time = DELETED_OBJECT_CONTAINER_CHANGE_TIME;
939 } else {
940 m->originating_change_time = now;
942 } else {
943 m->originating_change_time = now;
945 m->originating_invocation_id = *our_invocation_id;
946 m->originating_usn = ac->seq_num;
947 m->local_usn = ac->seq_num;
948 ni++;
951 /* fix meta data count */
952 nmd.ctr.ctr1.count = ni;
955 * sort meta data array, and move the rdn attribute entry to the end
957 ret = replmd_replPropertyMetaDataCtr1_sort(&nmd.ctr.ctr1, ac->schema, msg->dn);
958 if (ret != LDB_SUCCESS) {
959 talloc_free(ac);
960 return ret;
963 /* generated NDR encoded values */
964 ndr_err = ndr_push_struct_blob(&nmd_value, msg,
965 &nmd,
966 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
967 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
968 ldb_oom(ldb);
969 talloc_free(ac);
970 return LDB_ERR_OPERATIONS_ERROR;
974 * add the autogenerated values
976 ret = dsdb_msg_add_guid(msg, &guid, "objectGUID");
977 if (ret != LDB_SUCCESS) {
978 ldb_oom(ldb);
979 talloc_free(ac);
980 return ret;
982 ret = ldb_msg_add_string(msg, "whenChanged", time_str);
983 if (ret != LDB_SUCCESS) {
984 ldb_oom(ldb);
985 talloc_free(ac);
986 return ret;
988 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", ac->seq_num);
989 if (ret != LDB_SUCCESS) {
990 ldb_oom(ldb);
991 talloc_free(ac);
992 return ret;
994 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ac->seq_num);
995 if (ret != LDB_SUCCESS) {
996 ldb_oom(ldb);
997 talloc_free(ac);
998 return ret;
1000 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
1001 if (ret != LDB_SUCCESS) {
1002 ldb_oom(ldb);
1003 talloc_free(ac);
1004 return ret;
1008 * sort the attributes by attid before storing the object
1010 replmd_ldb_message_sort(msg, ac->schema);
1012 objectclass_el = ldb_msg_find_element(msg, "objectClass");
1013 is_urgent = replmd_check_urgent_objectclass(objectclass_el,
1014 REPL_URGENT_ON_CREATE);
1016 ac->is_urgent = is_urgent;
1017 ret = ldb_build_add_req(&down_req, ldb, ac,
1018 msg,
1019 req->controls,
1020 ac, replmd_op_callback,
1021 req);
1023 LDB_REQ_SET_LOCATION(down_req);
1024 if (ret != LDB_SUCCESS) {
1025 talloc_free(ac);
1026 return ret;
1029 /* current partition control is needed by "replmd_op_callback" */
1030 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
1031 ret = ldb_request_add_control(down_req,
1032 DSDB_CONTROL_CURRENT_PARTITION_OID,
1033 false, NULL);
1034 if (ret != LDB_SUCCESS) {
1035 talloc_free(ac);
1036 return ret;
1040 if (functional_level == DS_DOMAIN_FUNCTION_2000) {
1041 ret = ldb_request_add_control(down_req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
1042 if (ret != LDB_SUCCESS) {
1043 talloc_free(ac);
1044 return ret;
1048 /* mark the control done */
1049 if (control) {
1050 control->critical = 0;
1052 if (ldb_dn_compare_base(ac->schema->base_dn, req->op.add.message->dn) != 0) {
1054 /* Update the usn in the SAMLDB_MSDS_INTID_OPAQUE opaque */
1055 msds_intid_struct = (struct samldb_msds_intid_persistant *) ldb_get_opaque(ldb, SAMLDB_MSDS_INTID_OPAQUE);
1056 if (msds_intid_struct) {
1057 msds_intid_struct->usn = ac->seq_num;
1060 /* go on with the call chain */
1061 return ldb_next_request(module, down_req);
1066 * update the replPropertyMetaData for one element
1068 static int replmd_update_rpmd_element(struct ldb_context *ldb,
1069 struct ldb_message *msg,
1070 struct ldb_message_element *el,
1071 struct ldb_message_element *old_el,
1072 struct replPropertyMetaDataBlob *omd,
1073 const struct dsdb_schema *schema,
1074 uint64_t *seq_num,
1075 const struct GUID *our_invocation_id,
1076 NTTIME now,
1077 struct ldb_request *req)
1079 uint32_t i;
1080 const struct dsdb_attribute *a;
1081 struct replPropertyMetaData1 *md1;
1083 a = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
1084 if (a == NULL) {
1085 if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)) {
1086 /* allow this to make it possible for dbcheck
1087 to remove bad attributes */
1088 return LDB_SUCCESS;
1091 DEBUG(0,(__location__ ": Unable to find attribute %s in schema\n",
1092 el->name));
1093 return LDB_ERR_OPERATIONS_ERROR;
1096 if ((a->systemFlags & DS_FLAG_ATTR_NOT_REPLICATED) || (a->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED)) {
1097 return LDB_SUCCESS;
1100 /* if the attribute's value haven't changed then return LDB_SUCCESS
1101 * Unless we have the provision control or if the attribute is
1102 * interSiteTopologyGenerator as this page explain: http://support.microsoft.com/kb/224815
1103 * this attribute is periodicaly written by the DC responsible for the intersite generation
1104 * in a given site
1106 if (old_el != NULL && ldb_msg_element_compare(el, old_el) == 0) {
1107 if (strcmp(el->name, "interSiteTopologyGenerator") != 0 &&
1108 !ldb_request_get_control(req, LDB_CONTROL_PROVISION_OID)) {
1110 * allow this to make it possible for dbcheck
1111 * to rebuild broken metadata
1113 return LDB_SUCCESS;
1117 for (i=0; i<omd->ctr.ctr1.count; i++) {
1118 if (a->attributeID_id == omd->ctr.ctr1.array[i].attid) break;
1121 if (a->linkID != 0 && dsdb_functional_level(ldb) > DS_DOMAIN_FUNCTION_2000) {
1122 /* linked attributes are not stored in
1123 replPropertyMetaData in FL above w2k, but we do
1124 raise the seqnum for the object */
1125 if (*seq_num == 0 &&
1126 ldb_sequence_number(ldb, LDB_SEQ_NEXT, seq_num) != LDB_SUCCESS) {
1127 return LDB_ERR_OPERATIONS_ERROR;
1129 return LDB_SUCCESS;
1132 if (i == omd->ctr.ctr1.count) {
1133 /* we need to add a new one */
1134 omd->ctr.ctr1.array = talloc_realloc(msg, omd->ctr.ctr1.array,
1135 struct replPropertyMetaData1, omd->ctr.ctr1.count+1);
1136 if (omd->ctr.ctr1.array == NULL) {
1137 ldb_oom(ldb);
1138 return LDB_ERR_OPERATIONS_ERROR;
1140 omd->ctr.ctr1.count++;
1141 ZERO_STRUCT(omd->ctr.ctr1.array[i]);
1144 /* Get a new sequence number from the backend. We only do this
1145 * if we have a change that requires a new
1146 * replPropertyMetaData element
1148 if (*seq_num == 0) {
1149 int ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, seq_num);
1150 if (ret != LDB_SUCCESS) {
1151 return LDB_ERR_OPERATIONS_ERROR;
1155 md1 = &omd->ctr.ctr1.array[i];
1156 md1->version++;
1157 md1->attid = a->attributeID_id;
1158 if (md1->attid == 0x20030) {
1159 const struct ldb_val *rdn_val = ldb_dn_get_rdn_val(msg->dn);
1160 const char* rdn;
1162 if (rdn_val == NULL) {
1163 ldb_oom(ldb);
1164 return LDB_ERR_OPERATIONS_ERROR;
1167 rdn = (const char*)rdn_val->data;
1168 if (strcmp(rdn, "Deleted Objects") == 0) {
1170 * Set the originating_change_time to 29/12/9999 at 23:59:59
1171 * as specified in MS-ADTS 7.1.1.4.2 Deleted Objects Container
1173 md1->originating_change_time = DELETED_OBJECT_CONTAINER_CHANGE_TIME;
1174 } else {
1175 md1->originating_change_time = now;
1177 } else {
1178 md1->originating_change_time = now;
1180 md1->originating_invocation_id = *our_invocation_id;
1181 md1->originating_usn = *seq_num;
1182 md1->local_usn = *seq_num;
1184 return LDB_SUCCESS;
1187 static uint64_t find_max_local_usn(struct replPropertyMetaDataBlob omd)
1189 uint32_t count = omd.ctr.ctr1.count;
1190 uint64_t max = 0;
1191 uint32_t i;
1192 for (i=0; i < count; i++) {
1193 struct replPropertyMetaData1 m = omd.ctr.ctr1.array[i];
1194 if (max < m.local_usn) {
1195 max = m.local_usn;
1198 return max;
1202 * update the replPropertyMetaData object each time we modify an
1203 * object. This is needed for DRS replication, as the merge on the
1204 * client is based on this object
1206 static int replmd_update_rpmd(struct ldb_module *module,
1207 const struct dsdb_schema *schema,
1208 struct ldb_request *req,
1209 const char * const *rename_attrs,
1210 struct ldb_message *msg, uint64_t *seq_num,
1211 time_t t,
1212 bool *is_urgent, bool *rodc)
1214 const struct ldb_val *omd_value;
1215 enum ndr_err_code ndr_err;
1216 struct replPropertyMetaDataBlob omd;
1217 unsigned int i;
1218 NTTIME now;
1219 const struct GUID *our_invocation_id;
1220 int ret;
1221 const char * const *attrs = NULL;
1222 const char * const attrs1[] = { "replPropertyMetaData", "*", NULL };
1223 const char * const attrs2[] = { "uSNChanged", "objectClass", "instanceType", NULL };
1224 struct ldb_result *res;
1225 struct ldb_context *ldb;
1226 struct ldb_message_element *objectclass_el;
1227 enum urgent_situation situation;
1228 bool rmd_is_provided;
1230 if (rename_attrs) {
1231 attrs = rename_attrs;
1232 } else {
1233 attrs = attrs1;
1236 ldb = ldb_module_get_ctx(module);
1238 our_invocation_id = samdb_ntds_invocation_id(ldb);
1239 if (!our_invocation_id) {
1240 /* this happens during an initial vampire while
1241 updating the schema */
1242 DEBUG(5,("No invocationID - skipping replPropertyMetaData update\n"));
1243 return LDB_SUCCESS;
1246 unix_to_nt_time(&now, t);
1248 if (ldb_request_get_control(req, DSDB_CONTROL_CHANGEREPLMETADATA_OID)) {
1249 rmd_is_provided = true;
1250 } else {
1251 rmd_is_provided = false;
1254 /* if isDeleted is present and is TRUE, then we consider we are deleting,
1255 * otherwise we consider we are updating */
1256 if (ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")) {
1257 situation = REPL_URGENT_ON_DELETE;
1258 } else if (rename_attrs) {
1259 situation = REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE;
1260 } else {
1261 situation = REPL_URGENT_ON_UPDATE;
1264 if (rmd_is_provided) {
1265 /* In this case the change_replmetadata control was supplied */
1266 /* We check that it's the only attribute that is provided
1267 * (it's a rare case so it's better to keep the code simplier)
1268 * We also check that the highest local_usn is bigger than
1269 * uSNChanged. */
1270 uint64_t db_seq;
1271 if( msg->num_elements != 1 ||
1272 strncmp(msg->elements[0].name,
1273 "replPropertyMetaData", 20) ) {
1274 DEBUG(0,(__location__ ": changereplmetada control called without "\
1275 "a specified replPropertyMetaData attribute or with others\n"));
1276 return LDB_ERR_OPERATIONS_ERROR;
1278 if (situation != REPL_URGENT_ON_UPDATE) {
1279 DEBUG(0,(__location__ ": changereplmetada control can't be called when deleting an object\n"));
1280 return LDB_ERR_OPERATIONS_ERROR;
1282 omd_value = ldb_msg_find_ldb_val(msg, "replPropertyMetaData");
1283 if (!omd_value) {
1284 DEBUG(0,(__location__ ": replPropertyMetaData was not specified for Object %s\n",
1285 ldb_dn_get_linearized(msg->dn)));
1286 return LDB_ERR_OPERATIONS_ERROR;
1288 ndr_err = ndr_pull_struct_blob(omd_value, msg, &omd,
1289 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
1290 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1291 DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s\n",
1292 ldb_dn_get_linearized(msg->dn)));
1293 return LDB_ERR_OPERATIONS_ERROR;
1295 *seq_num = find_max_local_usn(omd);
1297 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attrs2,
1298 DSDB_FLAG_NEXT_MODULE |
1299 DSDB_SEARCH_SHOW_RECYCLED |
1300 DSDB_SEARCH_SHOW_EXTENDED_DN |
1301 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
1302 DSDB_SEARCH_REVEAL_INTERNALS, req);
1304 if (ret != LDB_SUCCESS) {
1305 return ret;
1308 objectclass_el = ldb_msg_find_element(res->msgs[0], "objectClass");
1309 if (is_urgent && replmd_check_urgent_objectclass(objectclass_el,
1310 situation)) {
1311 *is_urgent = true;
1314 db_seq = ldb_msg_find_attr_as_uint64(res->msgs[0], "uSNChanged", 0);
1315 if (*seq_num <= db_seq) {
1316 DEBUG(0,(__location__ ": changereplmetada control provided but max(local_usn)"\
1317 " is less or equal to uSNChanged (max = %lld uSNChanged = %lld)\n",
1318 (long long)*seq_num, (long long)db_seq));
1319 return LDB_ERR_OPERATIONS_ERROR;
1322 } else {
1323 /* search for the existing replPropertyMetaDataBlob. We need
1324 * to use REVEAL and ask for DNs in storage format to support
1325 * the check for values being the same in
1326 * replmd_update_rpmd_element()
1328 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attrs,
1329 DSDB_FLAG_NEXT_MODULE |
1330 DSDB_SEARCH_SHOW_RECYCLED |
1331 DSDB_SEARCH_SHOW_EXTENDED_DN |
1332 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
1333 DSDB_SEARCH_REVEAL_INTERNALS, req);
1334 if (ret != LDB_SUCCESS) {
1335 return ret;
1338 objectclass_el = ldb_msg_find_element(res->msgs[0], "objectClass");
1339 if (is_urgent && replmd_check_urgent_objectclass(objectclass_el,
1340 situation)) {
1341 *is_urgent = true;
1344 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
1345 if (!omd_value) {
1346 DEBUG(0,(__location__ ": Object %s does not have a replPropertyMetaData attribute\n",
1347 ldb_dn_get_linearized(msg->dn)));
1348 return LDB_ERR_OPERATIONS_ERROR;
1351 ndr_err = ndr_pull_struct_blob(omd_value, msg, &omd,
1352 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
1353 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1354 DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s\n",
1355 ldb_dn_get_linearized(msg->dn)));
1356 return LDB_ERR_OPERATIONS_ERROR;
1359 if (omd.version != 1) {
1360 DEBUG(0,(__location__ ": bad version %u in replPropertyMetaData for %s\n",
1361 omd.version, ldb_dn_get_linearized(msg->dn)));
1362 return LDB_ERR_OPERATIONS_ERROR;
1365 for (i=0; i<msg->num_elements; i++) {
1366 struct ldb_message_element *old_el;
1367 old_el = ldb_msg_find_element(res->msgs[0], msg->elements[i].name);
1368 ret = replmd_update_rpmd_element(ldb, msg, &msg->elements[i], old_el, &omd, schema, seq_num,
1369 our_invocation_id, now, req);
1370 if (ret != LDB_SUCCESS) {
1371 return ret;
1374 if (is_urgent && !*is_urgent && (situation == REPL_URGENT_ON_UPDATE)) {
1375 *is_urgent = replmd_check_urgent_attribute(&msg->elements[i]);
1381 * replmd_update_rpmd_element has done an update if the
1382 * seq_num is set
1384 if (*seq_num != 0) {
1385 struct ldb_val *md_value;
1386 struct ldb_message_element *el;
1388 /*if we are RODC and this is a DRSR update then its ok*/
1389 if (!ldb_request_get_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID)
1390 && !ldb_request_get_control(req, DSDB_CONTROL_DBCHECK_MODIFY_RO_REPLICA)) {
1391 unsigned instanceType;
1393 ret = samdb_rodc(ldb, rodc);
1394 if (ret != LDB_SUCCESS) {
1395 DEBUG(4, (__location__ ": unable to tell if we are an RODC\n"));
1396 } else if (*rodc) {
1397 ldb_set_errstring(ldb, "RODC modify is forbidden!");
1398 return LDB_ERR_REFERRAL;
1401 instanceType = ldb_msg_find_attr_as_uint(res->msgs[0], "instanceType", INSTANCE_TYPE_WRITE);
1402 if (!(instanceType & INSTANCE_TYPE_WRITE)) {
1403 return ldb_error(ldb, LDB_ERR_UNWILLING_TO_PERFORM,
1404 "cannot change replicated attribute on partial replica");
1408 md_value = talloc(msg, struct ldb_val);
1409 if (md_value == NULL) {
1410 ldb_oom(ldb);
1411 return LDB_ERR_OPERATIONS_ERROR;
1414 ret = replmd_replPropertyMetaDataCtr1_sort(&omd.ctr.ctr1, schema, msg->dn);
1415 if (ret != LDB_SUCCESS) {
1416 return ret;
1419 ndr_err = ndr_push_struct_blob(md_value, msg, &omd,
1420 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
1421 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1422 DEBUG(0,(__location__ ": Failed to marshall replPropertyMetaData for %s\n",
1423 ldb_dn_get_linearized(msg->dn)));
1424 return LDB_ERR_OPERATIONS_ERROR;
1427 ret = ldb_msg_add_empty(msg, "replPropertyMetaData", LDB_FLAG_MOD_REPLACE, &el);
1428 if (ret != LDB_SUCCESS) {
1429 DEBUG(0,(__location__ ": Failed to add updated replPropertyMetaData %s\n",
1430 ldb_dn_get_linearized(msg->dn)));
1431 return ret;
1434 el->num_values = 1;
1435 el->values = md_value;
1438 return LDB_SUCCESS;
1441 struct parsed_dn {
1442 struct dsdb_dn *dsdb_dn;
1443 struct GUID *guid;
1444 struct ldb_val *v;
1447 static int parsed_dn_compare(struct parsed_dn *pdn1, struct parsed_dn *pdn2)
1449 return GUID_compare(pdn1->guid, pdn2->guid);
1452 static struct parsed_dn *parsed_dn_find(struct parsed_dn *pdn,
1453 unsigned int count, struct GUID *guid,
1454 struct ldb_dn *dn)
1456 struct parsed_dn *ret;
1457 unsigned int i;
1458 if (dn && GUID_all_zero(guid)) {
1459 /* when updating a link using DRS, we sometimes get a
1460 NULL GUID. We then need to try and match by DN */
1461 for (i=0; i<count; i++) {
1462 if (ldb_dn_compare(pdn[i].dsdb_dn->dn, dn) == 0) {
1463 dsdb_get_extended_dn_guid(pdn[i].dsdb_dn->dn, guid, "GUID");
1464 return &pdn[i];
1467 return NULL;
1469 BINARY_ARRAY_SEARCH(pdn, count, guid, guid, GUID_compare, ret);
1470 return ret;
1474 get a series of message element values as an array of DNs and GUIDs
1475 the result is sorted by GUID
1477 static int get_parsed_dns(struct ldb_module *module, TALLOC_CTX *mem_ctx,
1478 struct ldb_message_element *el, struct parsed_dn **pdn,
1479 const char *ldap_oid, struct ldb_request *parent)
1481 unsigned int i;
1482 struct ldb_context *ldb = ldb_module_get_ctx(module);
1484 if (el == NULL) {
1485 *pdn = NULL;
1486 return LDB_SUCCESS;
1489 (*pdn) = talloc_array(mem_ctx, struct parsed_dn, el->num_values);
1490 if (!*pdn) {
1491 ldb_module_oom(module);
1492 return LDB_ERR_OPERATIONS_ERROR;
1495 for (i=0; i<el->num_values; i++) {
1496 struct ldb_val *v = &el->values[i];
1497 NTSTATUS status;
1498 struct ldb_dn *dn;
1499 struct parsed_dn *p;
1501 p = &(*pdn)[i];
1503 p->dsdb_dn = dsdb_dn_parse(*pdn, ldb, v, ldap_oid);
1504 if (p->dsdb_dn == NULL) {
1505 return LDB_ERR_INVALID_DN_SYNTAX;
1508 dn = p->dsdb_dn->dn;
1510 p->guid = talloc(*pdn, struct GUID);
1511 if (p->guid == NULL) {
1512 ldb_module_oom(module);
1513 return LDB_ERR_OPERATIONS_ERROR;
1516 status = dsdb_get_extended_dn_guid(dn, p->guid, "GUID");
1517 if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
1518 /* we got a DN without a GUID - go find the GUID */
1519 int ret = dsdb_module_guid_by_dn(module, dn, p->guid, parent);
1520 if (ret != LDB_SUCCESS) {
1521 ldb_asprintf_errstring(ldb, "Unable to find GUID for DN %s\n",
1522 ldb_dn_get_linearized(dn));
1523 if (ret == LDB_ERR_NO_SUCH_OBJECT &&
1524 LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_DELETE &&
1525 ldb_attr_cmp(el->name, "member") == 0) {
1526 return LDB_ERR_UNWILLING_TO_PERFORM;
1528 return ret;
1530 ret = dsdb_set_extended_dn_guid(dn, p->guid, "GUID");
1531 if (ret != LDB_SUCCESS) {
1532 return ret;
1534 } else if (!NT_STATUS_IS_OK(status)) {
1535 return LDB_ERR_OPERATIONS_ERROR;
1538 /* keep a pointer to the original ldb_val */
1539 p->v = v;
1542 TYPESAFE_QSORT(*pdn, el->num_values, parsed_dn_compare);
1544 return LDB_SUCCESS;
1548 build a new extended DN, including all meta data fields
1550 RMD_FLAGS = DSDB_RMD_FLAG_* bits
1551 RMD_ADDTIME = originating_add_time
1552 RMD_INVOCID = originating_invocation_id
1553 RMD_CHANGETIME = originating_change_time
1554 RMD_ORIGINATING_USN = originating_usn
1555 RMD_LOCAL_USN = local_usn
1556 RMD_VERSION = version
1558 static int replmd_build_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
1559 const struct GUID *invocation_id, uint64_t seq_num,
1560 uint64_t local_usn, NTTIME nttime, uint32_t version, bool deleted)
1562 struct ldb_dn *dn = dsdb_dn->dn;
1563 const char *tstring, *usn_string, *flags_string;
1564 struct ldb_val tval;
1565 struct ldb_val iid;
1566 struct ldb_val usnv, local_usnv;
1567 struct ldb_val vers, flagsv;
1568 NTSTATUS status;
1569 int ret;
1570 const char *dnstring;
1571 char *vstring;
1572 uint32_t rmd_flags = deleted?DSDB_RMD_FLAG_DELETED:0;
1574 tstring = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)nttime);
1575 if (!tstring) {
1576 return LDB_ERR_OPERATIONS_ERROR;
1578 tval = data_blob_string_const(tstring);
1580 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)seq_num);
1581 if (!usn_string) {
1582 return LDB_ERR_OPERATIONS_ERROR;
1584 usnv = data_blob_string_const(usn_string);
1586 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)local_usn);
1587 if (!usn_string) {
1588 return LDB_ERR_OPERATIONS_ERROR;
1590 local_usnv = data_blob_string_const(usn_string);
1592 vstring = talloc_asprintf(mem_ctx, "%lu", (unsigned long)version);
1593 if (!vstring) {
1594 return LDB_ERR_OPERATIONS_ERROR;
1596 vers = data_blob_string_const(vstring);
1598 status = GUID_to_ndr_blob(invocation_id, dn, &iid);
1599 if (!NT_STATUS_IS_OK(status)) {
1600 return LDB_ERR_OPERATIONS_ERROR;
1603 flags_string = talloc_asprintf(mem_ctx, "%u", rmd_flags);
1604 if (!flags_string) {
1605 return LDB_ERR_OPERATIONS_ERROR;
1607 flagsv = data_blob_string_const(flags_string);
1609 ret = ldb_dn_set_extended_component(dn, "RMD_FLAGS", &flagsv);
1610 if (ret != LDB_SUCCESS) return ret;
1611 ret = ldb_dn_set_extended_component(dn, "RMD_ADDTIME", &tval);
1612 if (ret != LDB_SUCCESS) return ret;
1613 ret = ldb_dn_set_extended_component(dn, "RMD_INVOCID", &iid);
1614 if (ret != LDB_SUCCESS) return ret;
1615 ret = ldb_dn_set_extended_component(dn, "RMD_CHANGETIME", &tval);
1616 if (ret != LDB_SUCCESS) return ret;
1617 ret = ldb_dn_set_extended_component(dn, "RMD_LOCAL_USN", &local_usnv);
1618 if (ret != LDB_SUCCESS) return ret;
1619 ret = ldb_dn_set_extended_component(dn, "RMD_ORIGINATING_USN", &usnv);
1620 if (ret != LDB_SUCCESS) return ret;
1621 ret = ldb_dn_set_extended_component(dn, "RMD_VERSION", &vers);
1622 if (ret != LDB_SUCCESS) return ret;
1624 dnstring = dsdb_dn_get_extended_linearized(mem_ctx, dsdb_dn, 1);
1625 if (dnstring == NULL) {
1626 return LDB_ERR_OPERATIONS_ERROR;
1628 *v = data_blob_string_const(dnstring);
1630 return LDB_SUCCESS;
1633 static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
1634 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
1635 uint64_t seq_num, uint64_t local_usn, NTTIME nttime,
1636 uint32_t version, bool deleted);
1639 check if any links need upgrading from w2k format
1641 The parent_ctx is the ldb_message_element which contains the values array that dns[i].v points at, and which should be used for allocating any new value.
1643 static int replmd_check_upgrade_links(struct parsed_dn *dns, uint32_t count, struct ldb_message_element *parent_ctx, const struct GUID *invocation_id)
1645 uint32_t i;
1646 for (i=0; i<count; i++) {
1647 NTSTATUS status;
1648 uint32_t version;
1649 int ret;
1651 status = dsdb_get_extended_dn_uint32(dns[i].dsdb_dn->dn, &version, "RMD_VERSION");
1652 if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
1653 continue;
1656 /* it's an old one that needs upgrading */
1657 ret = replmd_update_la_val(parent_ctx->values, dns[i].v, dns[i].dsdb_dn, dns[i].dsdb_dn, invocation_id,
1658 1, 1, 0, 0, false);
1659 if (ret != LDB_SUCCESS) {
1660 return ret;
1663 return LDB_SUCCESS;
1667 update an extended DN, including all meta data fields
1669 see replmd_build_la_val for value names
1671 static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
1672 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
1673 uint64_t seq_num, uint64_t local_usn, NTTIME nttime,
1674 uint32_t version, bool deleted)
1676 struct ldb_dn *dn = dsdb_dn->dn;
1677 const char *tstring, *usn_string, *flags_string;
1678 struct ldb_val tval;
1679 struct ldb_val iid;
1680 struct ldb_val usnv, local_usnv;
1681 struct ldb_val vers, flagsv;
1682 const struct ldb_val *old_addtime;
1683 uint32_t old_version;
1684 NTSTATUS status;
1685 int ret;
1686 const char *dnstring;
1687 char *vstring;
1688 uint32_t rmd_flags = deleted?DSDB_RMD_FLAG_DELETED:0;
1690 tstring = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)nttime);
1691 if (!tstring) {
1692 return LDB_ERR_OPERATIONS_ERROR;
1694 tval = data_blob_string_const(tstring);
1696 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)seq_num);
1697 if (!usn_string) {
1698 return LDB_ERR_OPERATIONS_ERROR;
1700 usnv = data_blob_string_const(usn_string);
1702 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)local_usn);
1703 if (!usn_string) {
1704 return LDB_ERR_OPERATIONS_ERROR;
1706 local_usnv = data_blob_string_const(usn_string);
1708 status = GUID_to_ndr_blob(invocation_id, dn, &iid);
1709 if (!NT_STATUS_IS_OK(status)) {
1710 return LDB_ERR_OPERATIONS_ERROR;
1713 flags_string = talloc_asprintf(mem_ctx, "%u", rmd_flags);
1714 if (!flags_string) {
1715 return LDB_ERR_OPERATIONS_ERROR;
1717 flagsv = data_blob_string_const(flags_string);
1719 ret = ldb_dn_set_extended_component(dn, "RMD_FLAGS", &flagsv);
1720 if (ret != LDB_SUCCESS) return ret;
1722 /* get the ADDTIME from the original */
1723 old_addtime = ldb_dn_get_extended_component(old_dsdb_dn->dn, "RMD_ADDTIME");
1724 if (old_addtime == NULL) {
1725 old_addtime = &tval;
1727 if (dsdb_dn != old_dsdb_dn ||
1728 ldb_dn_get_extended_component(dn, "RMD_ADDTIME") == NULL) {
1729 ret = ldb_dn_set_extended_component(dn, "RMD_ADDTIME", old_addtime);
1730 if (ret != LDB_SUCCESS) return ret;
1733 /* use our invocation id */
1734 ret = ldb_dn_set_extended_component(dn, "RMD_INVOCID", &iid);
1735 if (ret != LDB_SUCCESS) return ret;
1737 /* changetime is the current time */
1738 ret = ldb_dn_set_extended_component(dn, "RMD_CHANGETIME", &tval);
1739 if (ret != LDB_SUCCESS) return ret;
1741 /* update the USN */
1742 ret = ldb_dn_set_extended_component(dn, "RMD_ORIGINATING_USN", &usnv);
1743 if (ret != LDB_SUCCESS) return ret;
1745 ret = ldb_dn_set_extended_component(dn, "RMD_LOCAL_USN", &local_usnv);
1746 if (ret != LDB_SUCCESS) return ret;
1748 /* increase the version by 1 */
1749 status = dsdb_get_extended_dn_uint32(old_dsdb_dn->dn, &old_version, "RMD_VERSION");
1750 if (NT_STATUS_IS_OK(status) && old_version >= version) {
1751 version = old_version+1;
1753 vstring = talloc_asprintf(dn, "%lu", (unsigned long)version);
1754 vers = data_blob_string_const(vstring);
1755 ret = ldb_dn_set_extended_component(dn, "RMD_VERSION", &vers);
1756 if (ret != LDB_SUCCESS) return ret;
1758 dnstring = dsdb_dn_get_extended_linearized(mem_ctx, dsdb_dn, 1);
1759 if (dnstring == NULL) {
1760 return LDB_ERR_OPERATIONS_ERROR;
1762 *v = data_blob_string_const(dnstring);
1764 return LDB_SUCCESS;
1768 handle adding a linked attribute
1770 static int replmd_modify_la_add(struct ldb_module *module,
1771 const struct dsdb_schema *schema,
1772 struct ldb_message *msg,
1773 struct ldb_message_element *el,
1774 struct ldb_message_element *old_el,
1775 const struct dsdb_attribute *schema_attr,
1776 uint64_t seq_num,
1777 time_t t,
1778 struct GUID *msg_guid,
1779 struct ldb_request *parent)
1781 unsigned int i;
1782 struct parsed_dn *dns, *old_dns;
1783 TALLOC_CTX *tmp_ctx = talloc_new(msg);
1784 int ret;
1785 struct ldb_val *new_values = NULL;
1786 unsigned int num_new_values = 0;
1787 unsigned old_num_values = old_el?old_el->num_values:0;
1788 const struct GUID *invocation_id;
1789 struct ldb_context *ldb = ldb_module_get_ctx(module);
1790 NTTIME now;
1792 unix_to_nt_time(&now, t);
1794 ret = get_parsed_dns(module, tmp_ctx, el, &dns, schema_attr->syntax->ldap_oid, parent);
1795 if (ret != LDB_SUCCESS) {
1796 talloc_free(tmp_ctx);
1797 return ret;
1800 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns, schema_attr->syntax->ldap_oid, parent);
1801 if (ret != LDB_SUCCESS) {
1802 talloc_free(tmp_ctx);
1803 return ret;
1806 invocation_id = samdb_ntds_invocation_id(ldb);
1807 if (!invocation_id) {
1808 talloc_free(tmp_ctx);
1809 return LDB_ERR_OPERATIONS_ERROR;
1812 ret = replmd_check_upgrade_links(old_dns, old_num_values, old_el, invocation_id);
1813 if (ret != LDB_SUCCESS) {
1814 talloc_free(tmp_ctx);
1815 return ret;
1818 /* for each new value, see if it exists already with the same GUID */
1819 for (i=0; i<el->num_values; i++) {
1820 struct parsed_dn *p = parsed_dn_find(old_dns, old_num_values, dns[i].guid, NULL);
1821 if (p == NULL) {
1822 /* this is a new linked attribute value */
1823 new_values = talloc_realloc(tmp_ctx, new_values, struct ldb_val, num_new_values+1);
1824 if (new_values == NULL) {
1825 ldb_module_oom(module);
1826 talloc_free(tmp_ctx);
1827 return LDB_ERR_OPERATIONS_ERROR;
1829 ret = replmd_build_la_val(new_values, &new_values[num_new_values], dns[i].dsdb_dn,
1830 invocation_id, seq_num, seq_num, now, 0, false);
1831 if (ret != LDB_SUCCESS) {
1832 talloc_free(tmp_ctx);
1833 return ret;
1835 num_new_values++;
1836 } else {
1837 /* this is only allowed if the GUID was
1838 previously deleted. */
1839 uint32_t rmd_flags = dsdb_dn_rmd_flags(p->dsdb_dn->dn);
1841 if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
1842 ldb_asprintf_errstring(ldb, "Attribute %s already exists for target GUID %s",
1843 el->name, GUID_string(tmp_ctx, p->guid));
1844 talloc_free(tmp_ctx);
1845 /* error codes for 'member' need to be
1846 special cased */
1847 if (ldb_attr_cmp(el->name, "member") == 0) {
1848 return LDB_ERR_ENTRY_ALREADY_EXISTS;
1849 } else {
1850 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
1853 ret = replmd_update_la_val(old_el->values, p->v, dns[i].dsdb_dn, p->dsdb_dn,
1854 invocation_id, seq_num, seq_num, now, 0, false);
1855 if (ret != LDB_SUCCESS) {
1856 talloc_free(tmp_ctx);
1857 return ret;
1861 ret = replmd_add_backlink(module, schema, msg_guid, dns[i].guid, true, schema_attr, true);
1862 if (ret != LDB_SUCCESS) {
1863 talloc_free(tmp_ctx);
1864 return ret;
1868 /* add the new ones on to the end of the old values, constructing a new el->values */
1869 el->values = talloc_realloc(msg->elements, old_el?old_el->values:NULL,
1870 struct ldb_val,
1871 old_num_values+num_new_values);
1872 if (el->values == NULL) {
1873 ldb_module_oom(module);
1874 return LDB_ERR_OPERATIONS_ERROR;
1877 memcpy(&el->values[old_num_values], new_values, num_new_values*sizeof(struct ldb_val));
1878 el->num_values = old_num_values + num_new_values;
1880 talloc_steal(msg->elements, el->values);
1881 talloc_steal(el->values, new_values);
1883 talloc_free(tmp_ctx);
1885 /* we now tell the backend to replace all existing values
1886 with the one we have constructed */
1887 el->flags = LDB_FLAG_MOD_REPLACE;
1889 return LDB_SUCCESS;
1894 handle deleting all active linked attributes
1896 static int replmd_modify_la_delete(struct ldb_module *module,
1897 const struct dsdb_schema *schema,
1898 struct ldb_message *msg,
1899 struct ldb_message_element *el,
1900 struct ldb_message_element *old_el,
1901 const struct dsdb_attribute *schema_attr,
1902 uint64_t seq_num,
1903 time_t t,
1904 struct GUID *msg_guid,
1905 struct ldb_request *parent)
1907 unsigned int i;
1908 struct parsed_dn *dns, *old_dns;
1909 TALLOC_CTX *tmp_ctx = talloc_new(msg);
1910 int ret;
1911 const struct GUID *invocation_id;
1912 struct ldb_context *ldb = ldb_module_get_ctx(module);
1913 NTTIME now;
1915 unix_to_nt_time(&now, t);
1917 /* check if there is nothing to delete */
1918 if ((!old_el || old_el->num_values == 0) &&
1919 el->num_values == 0) {
1920 return LDB_SUCCESS;
1923 if (!old_el || old_el->num_values == 0) {
1924 return LDB_ERR_NO_SUCH_ATTRIBUTE;
1927 ret = get_parsed_dns(module, tmp_ctx, el, &dns, schema_attr->syntax->ldap_oid, parent);
1928 if (ret != LDB_SUCCESS) {
1929 talloc_free(tmp_ctx);
1930 return ret;
1933 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns, schema_attr->syntax->ldap_oid, parent);
1934 if (ret != LDB_SUCCESS) {
1935 talloc_free(tmp_ctx);
1936 return ret;
1939 invocation_id = samdb_ntds_invocation_id(ldb);
1940 if (!invocation_id) {
1941 return LDB_ERR_OPERATIONS_ERROR;
1944 ret = replmd_check_upgrade_links(old_dns, old_el->num_values, old_el, invocation_id);
1945 if (ret != LDB_SUCCESS) {
1946 talloc_free(tmp_ctx);
1947 return ret;
1950 el->values = NULL;
1952 /* see if we are being asked to delete any links that
1953 don't exist or are already deleted */
1954 for (i=0; i<el->num_values; i++) {
1955 struct parsed_dn *p = &dns[i];
1956 struct parsed_dn *p2;
1957 uint32_t rmd_flags;
1959 p2 = parsed_dn_find(old_dns, old_el->num_values, p->guid, NULL);
1960 if (!p2) {
1961 ldb_asprintf_errstring(ldb, "Attribute %s doesn't exist for target GUID %s",
1962 el->name, GUID_string(tmp_ctx, p->guid));
1963 if (ldb_attr_cmp(el->name, "member") == 0) {
1964 return LDB_ERR_UNWILLING_TO_PERFORM;
1965 } else {
1966 return LDB_ERR_NO_SUCH_ATTRIBUTE;
1969 rmd_flags = dsdb_dn_rmd_flags(p2->dsdb_dn->dn);
1970 if (rmd_flags & DSDB_RMD_FLAG_DELETED) {
1971 ldb_asprintf_errstring(ldb, "Attribute %s already deleted for target GUID %s",
1972 el->name, GUID_string(tmp_ctx, p->guid));
1973 if (ldb_attr_cmp(el->name, "member") == 0) {
1974 return LDB_ERR_UNWILLING_TO_PERFORM;
1975 } else {
1976 return LDB_ERR_NO_SUCH_ATTRIBUTE;
1981 /* for each new value, see if it exists already with the same GUID
1982 if it is not already deleted and matches the delete list then delete it
1984 for (i=0; i<old_el->num_values; i++) {
1985 struct parsed_dn *p = &old_dns[i];
1986 uint32_t rmd_flags;
1988 if (el->num_values && parsed_dn_find(dns, el->num_values, p->guid, NULL) == NULL) {
1989 continue;
1992 rmd_flags = dsdb_dn_rmd_flags(p->dsdb_dn->dn);
1993 if (rmd_flags & DSDB_RMD_FLAG_DELETED) continue;
1995 ret = replmd_update_la_val(old_el->values, p->v, p->dsdb_dn, p->dsdb_dn,
1996 invocation_id, seq_num, seq_num, now, 0, true);
1997 if (ret != LDB_SUCCESS) {
1998 talloc_free(tmp_ctx);
1999 return ret;
2002 ret = replmd_add_backlink(module, schema, msg_guid, old_dns[i].guid, false, schema_attr, true);
2003 if (ret != LDB_SUCCESS) {
2004 talloc_free(tmp_ctx);
2005 return ret;
2009 el->values = talloc_steal(msg->elements, old_el->values);
2010 el->num_values = old_el->num_values;
2012 talloc_free(tmp_ctx);
2014 /* we now tell the backend to replace all existing values
2015 with the one we have constructed */
2016 el->flags = LDB_FLAG_MOD_REPLACE;
2018 return LDB_SUCCESS;
2022 handle replacing a linked attribute
2024 static int replmd_modify_la_replace(struct ldb_module *module,
2025 const struct dsdb_schema *schema,
2026 struct ldb_message *msg,
2027 struct ldb_message_element *el,
2028 struct ldb_message_element *old_el,
2029 const struct dsdb_attribute *schema_attr,
2030 uint64_t seq_num,
2031 time_t t,
2032 struct GUID *msg_guid,
2033 struct ldb_request *parent)
2035 unsigned int i;
2036 struct parsed_dn *dns, *old_dns;
2037 TALLOC_CTX *tmp_ctx = talloc_new(msg);
2038 int ret;
2039 const struct GUID *invocation_id;
2040 struct ldb_context *ldb = ldb_module_get_ctx(module);
2041 struct ldb_val *new_values = NULL;
2042 unsigned int num_new_values = 0;
2043 unsigned int old_num_values = old_el?old_el->num_values:0;
2044 NTTIME now;
2046 unix_to_nt_time(&now, t);
2048 /* check if there is nothing to replace */
2049 if ((!old_el || old_el->num_values == 0) &&
2050 el->num_values == 0) {
2051 return LDB_SUCCESS;
2054 ret = get_parsed_dns(module, tmp_ctx, el, &dns, schema_attr->syntax->ldap_oid, parent);
2055 if (ret != LDB_SUCCESS) {
2056 talloc_free(tmp_ctx);
2057 return ret;
2060 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns, schema_attr->syntax->ldap_oid, parent);
2061 if (ret != LDB_SUCCESS) {
2062 talloc_free(tmp_ctx);
2063 return ret;
2066 invocation_id = samdb_ntds_invocation_id(ldb);
2067 if (!invocation_id) {
2068 return LDB_ERR_OPERATIONS_ERROR;
2071 ret = replmd_check_upgrade_links(old_dns, old_num_values, old_el, invocation_id);
2072 if (ret != LDB_SUCCESS) {
2073 talloc_free(tmp_ctx);
2074 return ret;
2077 /* mark all the old ones as deleted */
2078 for (i=0; i<old_num_values; i++) {
2079 struct parsed_dn *old_p = &old_dns[i];
2080 struct parsed_dn *p;
2081 uint32_t rmd_flags = dsdb_dn_rmd_flags(old_p->dsdb_dn->dn);
2083 if (rmd_flags & DSDB_RMD_FLAG_DELETED) continue;
2085 ret = replmd_add_backlink(module, schema, msg_guid, old_dns[i].guid, false, schema_attr, false);
2086 if (ret != LDB_SUCCESS) {
2087 talloc_free(tmp_ctx);
2088 return ret;
2091 p = parsed_dn_find(dns, el->num_values, old_p->guid, NULL);
2092 if (p) {
2093 /* we don't delete it if we are re-adding it */
2094 continue;
2097 ret = replmd_update_la_val(old_el->values, old_p->v, old_p->dsdb_dn, old_p->dsdb_dn,
2098 invocation_id, seq_num, seq_num, now, 0, true);
2099 if (ret != LDB_SUCCESS) {
2100 talloc_free(tmp_ctx);
2101 return ret;
2105 /* for each new value, either update its meta-data, or add it
2106 * to old_el
2108 for (i=0; i<el->num_values; i++) {
2109 struct parsed_dn *p = &dns[i], *old_p;
2111 if (old_dns &&
2112 (old_p = parsed_dn_find(old_dns,
2113 old_num_values, p->guid, NULL)) != NULL) {
2114 /* update in place */
2115 ret = replmd_update_la_val(old_el->values, old_p->v, p->dsdb_dn,
2116 old_p->dsdb_dn, invocation_id,
2117 seq_num, seq_num, now, 0, false);
2118 if (ret != LDB_SUCCESS) {
2119 talloc_free(tmp_ctx);
2120 return ret;
2122 } else {
2123 /* add a new one */
2124 new_values = talloc_realloc(tmp_ctx, new_values, struct ldb_val,
2125 num_new_values+1);
2126 if (new_values == NULL) {
2127 ldb_module_oom(module);
2128 talloc_free(tmp_ctx);
2129 return LDB_ERR_OPERATIONS_ERROR;
2131 ret = replmd_build_la_val(new_values, &new_values[num_new_values], dns[i].dsdb_dn,
2132 invocation_id, seq_num, seq_num, now, 0, false);
2133 if (ret != LDB_SUCCESS) {
2134 talloc_free(tmp_ctx);
2135 return ret;
2137 num_new_values++;
2140 ret = replmd_add_backlink(module, schema, msg_guid, dns[i].guid, true, schema_attr, false);
2141 if (ret != LDB_SUCCESS) {
2142 talloc_free(tmp_ctx);
2143 return ret;
2147 /* add the new values to the end of old_el */
2148 if (num_new_values != 0) {
2149 el->values = talloc_realloc(msg->elements, old_el?old_el->values:NULL,
2150 struct ldb_val, old_num_values+num_new_values);
2151 if (el->values == NULL) {
2152 ldb_module_oom(module);
2153 return LDB_ERR_OPERATIONS_ERROR;
2155 memcpy(&el->values[old_num_values], &new_values[0],
2156 sizeof(struct ldb_val)*num_new_values);
2157 el->num_values = old_num_values + num_new_values;
2158 talloc_steal(msg->elements, new_values);
2159 } else {
2160 el->values = old_el->values;
2161 el->num_values = old_el->num_values;
2162 talloc_steal(msg->elements, el->values);
2165 talloc_free(tmp_ctx);
2167 /* we now tell the backend to replace all existing values
2168 with the one we have constructed */
2169 el->flags = LDB_FLAG_MOD_REPLACE;
2171 return LDB_SUCCESS;
2176 handle linked attributes in modify requests
2178 static int replmd_modify_handle_linked_attribs(struct ldb_module *module,
2179 struct ldb_message *msg,
2180 uint64_t seq_num, time_t t,
2181 struct ldb_request *parent)
2183 struct ldb_result *res;
2184 unsigned int i;
2185 int ret;
2186 struct ldb_context *ldb = ldb_module_get_ctx(module);
2187 struct ldb_message *old_msg;
2189 const struct dsdb_schema *schema;
2190 struct GUID old_guid;
2192 if (seq_num == 0) {
2193 /* there the replmd_update_rpmd code has already
2194 * checked and saw that there are no linked
2195 * attributes */
2196 return LDB_SUCCESS;
2199 if (dsdb_functional_level(ldb) == DS_DOMAIN_FUNCTION_2000) {
2200 /* don't do anything special for linked attributes */
2201 return LDB_SUCCESS;
2204 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, NULL,
2205 DSDB_FLAG_NEXT_MODULE |
2206 DSDB_SEARCH_SHOW_RECYCLED |
2207 DSDB_SEARCH_REVEAL_INTERNALS |
2208 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
2209 parent);
2210 if (ret != LDB_SUCCESS) {
2211 return ret;
2213 schema = dsdb_get_schema(ldb, res);
2214 if (!schema) {
2215 return LDB_ERR_OPERATIONS_ERROR;
2218 old_msg = res->msgs[0];
2220 old_guid = samdb_result_guid(old_msg, "objectGUID");
2222 for (i=0; i<msg->num_elements; i++) {
2223 struct ldb_message_element *el = &msg->elements[i];
2224 struct ldb_message_element *old_el, *new_el;
2225 const struct dsdb_attribute *schema_attr
2226 = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
2227 if (!schema_attr) {
2228 ldb_asprintf_errstring(ldb,
2229 "%s: attribute %s is not a valid attribute in schema",
2230 __FUNCTION__, el->name);
2231 return LDB_ERR_OBJECT_CLASS_VIOLATION;
2233 if (schema_attr->linkID == 0) {
2234 continue;
2236 if ((schema_attr->linkID & 1) == 1) {
2237 if (parent && ldb_request_get_control(parent, DSDB_CONTROL_DBCHECK)) {
2238 continue;
2240 /* Odd is for the target. Illegal to modify */
2241 ldb_asprintf_errstring(ldb,
2242 "attribute %s must not be modified directly, it is a linked attribute", el->name);
2243 return LDB_ERR_UNWILLING_TO_PERFORM;
2245 old_el = ldb_msg_find_element(old_msg, el->name);
2246 switch (el->flags & LDB_FLAG_MOD_MASK) {
2247 case LDB_FLAG_MOD_REPLACE:
2248 ret = replmd_modify_la_replace(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid, parent);
2249 break;
2250 case LDB_FLAG_MOD_DELETE:
2251 ret = replmd_modify_la_delete(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid, parent);
2252 break;
2253 case LDB_FLAG_MOD_ADD:
2254 ret = replmd_modify_la_add(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid, parent);
2255 break;
2256 default:
2257 ldb_asprintf_errstring(ldb,
2258 "invalid flags 0x%x for %s linked attribute",
2259 el->flags, el->name);
2260 return LDB_ERR_UNWILLING_TO_PERFORM;
2262 if (dsdb_check_single_valued_link(schema_attr, el) != LDB_SUCCESS) {
2263 ldb_asprintf_errstring(ldb,
2264 "Attribute %s is single valued but more than one value has been supplied",
2265 el->name);
2266 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
2267 } else {
2268 el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
2273 if (ret != LDB_SUCCESS) {
2274 return ret;
2276 if (old_el) {
2277 ldb_msg_remove_attr(old_msg, el->name);
2279 ldb_msg_add_empty(old_msg, el->name, 0, &new_el);
2280 new_el->num_values = el->num_values;
2281 new_el->values = talloc_steal(msg->elements, el->values);
2283 /* TODO: this relises a bit too heavily on the exact
2284 behaviour of ldb_msg_find_element and
2285 ldb_msg_remove_element */
2286 old_el = ldb_msg_find_element(msg, el->name);
2287 if (old_el != el) {
2288 ldb_msg_remove_element(msg, old_el);
2289 i--;
2293 talloc_free(res);
2294 return ret;
2299 static int replmd_modify(struct ldb_module *module, struct ldb_request *req)
2301 struct samldb_msds_intid_persistant *msds_intid_struct;
2302 struct ldb_context *ldb;
2303 struct replmd_replicated_request *ac;
2304 struct ldb_request *down_req;
2305 struct ldb_message *msg;
2306 time_t t = time(NULL);
2307 int ret;
2308 bool is_urgent = false, rodc = false;
2309 unsigned int functional_level;
2310 const DATA_BLOB *guid_blob;
2311 struct ldb_control *sd_propagation_control;
2313 /* do not manipulate our control entries */
2314 if (ldb_dn_is_special(req->op.mod.message->dn)) {
2315 return ldb_next_request(module, req);
2318 sd_propagation_control = ldb_request_get_control(req,
2319 DSDB_CONTROL_SEC_DESC_PROPAGATION_OID);
2320 if (sd_propagation_control != NULL) {
2321 if (req->op.mod.message->num_elements != 1) {
2322 return ldb_module_operr(module);
2324 ret = strcmp(req->op.mod.message->elements[0].name,
2325 "nTSecurityDescriptor");
2326 if (ret != 0) {
2327 return ldb_module_operr(module);
2330 return ldb_next_request(module, req);
2333 ldb = ldb_module_get_ctx(module);
2335 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_modify\n");
2337 guid_blob = ldb_msg_find_ldb_val(req->op.mod.message, "objectGUID");
2338 if ( guid_blob != NULL ) {
2339 ldb_set_errstring(ldb,
2340 "replmd_modify: it's not allowed to change the objectGUID!");
2341 return LDB_ERR_CONSTRAINT_VIOLATION;
2344 ac = replmd_ctx_init(module, req);
2345 if (ac == NULL) {
2346 return ldb_module_oom(module);
2349 functional_level = dsdb_functional_level(ldb);
2351 /* we have to copy the message as the caller might have it as a const */
2352 msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
2353 if (msg == NULL) {
2354 ldb_oom(ldb);
2355 talloc_free(ac);
2356 return LDB_ERR_OPERATIONS_ERROR;
2359 ldb_msg_remove_attr(msg, "whenChanged");
2360 ldb_msg_remove_attr(msg, "uSNChanged");
2362 ret = replmd_update_rpmd(module, ac->schema, req, NULL,
2363 msg, &ac->seq_num, t, &is_urgent, &rodc);
2364 if (rodc && (ret == LDB_ERR_REFERRAL)) {
2365 struct loadparm_context *lp_ctx;
2366 char *referral;
2368 lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
2369 struct loadparm_context);
2371 referral = talloc_asprintf(req,
2372 "ldap://%s/%s",
2373 lpcfg_dnsdomain(lp_ctx),
2374 ldb_dn_get_linearized(msg->dn));
2375 ret = ldb_module_send_referral(req, referral);
2376 talloc_free(ac);
2377 return ret;
2380 if (ret != LDB_SUCCESS) {
2381 talloc_free(ac);
2382 return ret;
2385 ret = replmd_modify_handle_linked_attribs(module, msg, ac->seq_num, t, req);
2386 if (ret != LDB_SUCCESS) {
2387 talloc_free(ac);
2388 return ret;
2391 /* TODO:
2392 * - replace the old object with the newly constructed one
2395 ac->is_urgent = is_urgent;
2397 ret = ldb_build_mod_req(&down_req, ldb, ac,
2398 msg,
2399 req->controls,
2400 ac, replmd_op_callback,
2401 req);
2402 LDB_REQ_SET_LOCATION(down_req);
2403 if (ret != LDB_SUCCESS) {
2404 talloc_free(ac);
2405 return ret;
2408 /* current partition control is needed by "replmd_op_callback" */
2409 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
2410 ret = ldb_request_add_control(down_req,
2411 DSDB_CONTROL_CURRENT_PARTITION_OID,
2412 false, NULL);
2413 if (ret != LDB_SUCCESS) {
2414 talloc_free(ac);
2415 return ret;
2419 /* If we are in functional level 2000, then
2420 * replmd_modify_handle_linked_attribs will have done
2421 * nothing */
2422 if (functional_level == DS_DOMAIN_FUNCTION_2000) {
2423 ret = ldb_request_add_control(down_req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
2424 if (ret != LDB_SUCCESS) {
2425 talloc_free(ac);
2426 return ret;
2430 talloc_steal(down_req, msg);
2432 /* we only change whenChanged and uSNChanged if the seq_num
2433 has changed */
2434 if (ac->seq_num != 0) {
2435 ret = add_time_element(msg, "whenChanged", t);
2436 if (ret != LDB_SUCCESS) {
2437 talloc_free(ac);
2438 ldb_operr(ldb);
2439 return ret;
2442 ret = add_uint64_element(ldb, msg, "uSNChanged", ac->seq_num);
2443 if (ret != LDB_SUCCESS) {
2444 talloc_free(ac);
2445 ldb_operr(ldb);
2446 return ret;
2450 if (!ldb_dn_compare_base(ac->schema->base_dn, msg->dn)) {
2451 /* Update the usn in the SAMLDB_MSDS_INTID_OPAQUE opaque */
2452 msds_intid_struct = (struct samldb_msds_intid_persistant *) ldb_get_opaque(ldb, SAMLDB_MSDS_INTID_OPAQUE);
2453 if (msds_intid_struct) {
2454 msds_intid_struct->usn = ac->seq_num;
2458 /* go on with the call chain */
2459 return ldb_next_request(module, down_req);
2462 static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares);
2465 handle a rename request
2467 On a rename we need to do an extra ldb_modify which sets the
2468 whenChanged and uSNChanged attributes. We do this in a callback after the success.
2470 static int replmd_rename(struct ldb_module *module, struct ldb_request *req)
2472 struct ldb_context *ldb;
2473 struct replmd_replicated_request *ac;
2474 int ret;
2475 struct ldb_request *down_req;
2477 /* do not manipulate our control entries */
2478 if (ldb_dn_is_special(req->op.mod.message->dn)) {
2479 return ldb_next_request(module, req);
2482 ldb = ldb_module_get_ctx(module);
2484 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_rename\n");
2486 ac = replmd_ctx_init(module, req);
2487 if (ac == NULL) {
2488 return ldb_module_oom(module);
2491 ret = ldb_build_rename_req(&down_req, ldb, ac,
2492 ac->req->op.rename.olddn,
2493 ac->req->op.rename.newdn,
2494 ac->req->controls,
2495 ac, replmd_rename_callback,
2496 ac->req);
2497 LDB_REQ_SET_LOCATION(down_req);
2498 if (ret != LDB_SUCCESS) {
2499 talloc_free(ac);
2500 return ret;
2503 /* go on with the call chain */
2504 return ldb_next_request(module, down_req);
2507 /* After the rename is compleated, update the whenchanged etc */
2508 static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares)
2510 struct ldb_context *ldb;
2511 struct replmd_replicated_request *ac;
2512 struct ldb_request *down_req;
2513 struct ldb_message *msg;
2514 const struct dsdb_attribute *rdn_attr;
2515 const char *rdn_name;
2516 const struct ldb_val *rdn_val;
2517 const char *attrs[5] = { NULL, };
2518 time_t t = time(NULL);
2519 int ret;
2520 bool is_urgent = false, rodc = false;
2522 ac = talloc_get_type(req->context, struct replmd_replicated_request);
2523 ldb = ldb_module_get_ctx(ac->module);
2525 if (ares->error != LDB_SUCCESS) {
2526 return ldb_module_done(ac->req, ares->controls,
2527 ares->response, ares->error);
2530 if (ares->type != LDB_REPLY_DONE) {
2531 ldb_set_errstring(ldb,
2532 "invalid ldb_reply_type in callback");
2533 talloc_free(ares);
2534 return ldb_module_done(ac->req, NULL, NULL,
2535 LDB_ERR_OPERATIONS_ERROR);
2538 /* TODO:
2539 * - replace the old object with the newly constructed one
2542 msg = ldb_msg_new(ac);
2543 if (msg == NULL) {
2544 ldb_oom(ldb);
2545 return LDB_ERR_OPERATIONS_ERROR;
2548 msg->dn = ac->req->op.rename.newdn;
2550 rdn_name = ldb_dn_get_rdn_name(msg->dn);
2551 if (rdn_name == NULL) {
2552 talloc_free(ares);
2553 return ldb_module_done(ac->req, NULL, NULL,
2554 ldb_operr(ldb));
2557 /* normalize the rdn attribute name */
2558 rdn_attr = dsdb_attribute_by_lDAPDisplayName(ac->schema, rdn_name);
2559 if (rdn_attr == NULL) {
2560 talloc_free(ares);
2561 return ldb_module_done(ac->req, NULL, NULL,
2562 ldb_operr(ldb));
2564 rdn_name = rdn_attr->lDAPDisplayName;
2566 rdn_val = ldb_dn_get_rdn_val(msg->dn);
2567 if (rdn_val == NULL) {
2568 talloc_free(ares);
2569 return ldb_module_done(ac->req, NULL, NULL,
2570 ldb_operr(ldb));
2573 if (ldb_msg_add_empty(msg, rdn_name, LDB_FLAG_MOD_REPLACE, NULL) != 0) {
2574 talloc_free(ares);
2575 return ldb_module_done(ac->req, NULL, NULL,
2576 ldb_oom(ldb));
2578 if (ldb_msg_add_value(msg, rdn_name, rdn_val, NULL) != 0) {
2579 talloc_free(ares);
2580 return ldb_module_done(ac->req, NULL, NULL,
2581 ldb_oom(ldb));
2583 if (ldb_msg_add_empty(msg, "name", LDB_FLAG_MOD_REPLACE, NULL) != 0) {
2584 talloc_free(ares);
2585 return ldb_module_done(ac->req, NULL, NULL,
2586 ldb_oom(ldb));
2588 if (ldb_msg_add_value(msg, "name", rdn_val, NULL) != 0) {
2589 talloc_free(ares);
2590 return ldb_module_done(ac->req, NULL, NULL,
2591 ldb_oom(ldb));
2595 * here we let replmd_update_rpmd() only search for
2596 * the existing "replPropertyMetaData" and rdn_name attributes.
2598 * We do not want the existing "name" attribute as
2599 * the "name" attribute needs to get the version
2600 * updated on rename even if the rdn value hasn't changed.
2602 * This is the diff of the meta data, for a moved user
2603 * on a w2k8r2 server:
2605 * # record 1
2606 * -dn: CN=sdf df,CN=Users,DC=bla,DC=base
2607 * +dn: CN=sdf df,OU=TestOU,DC=bla,DC=base
2608 * replPropertyMetaData: NDR: struct replPropertyMetaDataBlob
2609 * version : 0x00000001 (1)
2610 * reserved : 0x00000000 (0)
2611 * @@ -66,11 +66,11 @@ replPropertyMetaData: NDR: struct re
2612 * local_usn : 0x00000000000037a5 (14245)
2613 * array: struct replPropertyMetaData1
2614 * attid : DRSUAPI_ATTID_name (0x90001)
2615 * - version : 0x00000001 (1)
2616 * - originating_change_time : Wed Feb 9 17:20:49 2011 CET
2617 * + version : 0x00000002 (2)
2618 * + originating_change_time : Wed Apr 6 15:21:01 2011 CEST
2619 * originating_invocation_id: 0d36ca05-5507-4e62-aca3-354bab0d39e1
2620 * - originating_usn : 0x00000000000037a5 (14245)
2621 * - local_usn : 0x00000000000037a5 (14245)
2622 * + originating_usn : 0x0000000000003834 (14388)
2623 * + local_usn : 0x0000000000003834 (14388)
2624 * array: struct replPropertyMetaData1
2625 * attid : DRSUAPI_ATTID_userAccountControl (0x90008)
2626 * version : 0x00000004 (4)
2628 attrs[0] = "replPropertyMetaData";
2629 attrs[1] = "objectClass";
2630 attrs[2] = "instanceType";
2631 attrs[3] = rdn_name;
2632 attrs[4] = NULL;
2634 ret = replmd_update_rpmd(ac->module, ac->schema, req, attrs,
2635 msg, &ac->seq_num, t, &is_urgent, &rodc);
2636 if (rodc && (ret == LDB_ERR_REFERRAL)) {
2637 struct ldb_dn *olddn = ac->req->op.rename.olddn;
2638 struct loadparm_context *lp_ctx;
2639 char *referral;
2641 lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
2642 struct loadparm_context);
2644 referral = talloc_asprintf(req,
2645 "ldap://%s/%s",
2646 lpcfg_dnsdomain(lp_ctx),
2647 ldb_dn_get_linearized(olddn));
2648 ret = ldb_module_send_referral(req, referral);
2649 talloc_free(ares);
2650 return ldb_module_done(req, NULL, NULL, ret);
2653 if (ret != LDB_SUCCESS) {
2654 talloc_free(ares);
2655 return ldb_module_done(ac->req, NULL, NULL, ret);
2658 if (ac->seq_num == 0) {
2659 talloc_free(ares);
2660 return ldb_module_done(ac->req, NULL, NULL,
2661 ldb_error(ldb, ret,
2662 "internal error seq_num == 0"));
2664 ac->is_urgent = is_urgent;
2666 ret = ldb_build_mod_req(&down_req, ldb, ac,
2667 msg,
2668 req->controls,
2669 ac, replmd_op_callback,
2670 req);
2671 LDB_REQ_SET_LOCATION(down_req);
2672 if (ret != LDB_SUCCESS) {
2673 talloc_free(ac);
2674 return ret;
2677 /* current partition control is needed by "replmd_op_callback" */
2678 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
2679 ret = ldb_request_add_control(down_req,
2680 DSDB_CONTROL_CURRENT_PARTITION_OID,
2681 false, NULL);
2682 if (ret != LDB_SUCCESS) {
2683 talloc_free(ac);
2684 return ret;
2688 talloc_steal(down_req, msg);
2690 ret = add_time_element(msg, "whenChanged", t);
2691 if (ret != LDB_SUCCESS) {
2692 talloc_free(ac);
2693 ldb_operr(ldb);
2694 return ret;
2697 ret = add_uint64_element(ldb, msg, "uSNChanged", ac->seq_num);
2698 if (ret != LDB_SUCCESS) {
2699 talloc_free(ac);
2700 ldb_operr(ldb);
2701 return ret;
2704 /* go on with the call chain - do the modify after the rename */
2705 return ldb_next_request(ac->module, down_req);
2709 remove links from objects that point at this object when an object
2710 is deleted
2712 static int replmd_delete_remove_link(struct ldb_module *module,
2713 const struct dsdb_schema *schema,
2714 struct ldb_dn *dn,
2715 struct ldb_message_element *el,
2716 const struct dsdb_attribute *sa,
2717 struct ldb_request *parent)
2719 unsigned int i;
2720 TALLOC_CTX *tmp_ctx = talloc_new(module);
2721 struct ldb_context *ldb = ldb_module_get_ctx(module);
2723 for (i=0; i<el->num_values; i++) {
2724 struct dsdb_dn *dsdb_dn;
2725 NTSTATUS status;
2726 int ret;
2727 struct GUID guid2;
2728 struct ldb_message *msg;
2729 const struct dsdb_attribute *target_attr;
2730 struct ldb_message_element *el2;
2731 struct ldb_val dn_val;
2733 if (dsdb_dn_is_deleted_val(&el->values[i])) {
2734 continue;
2737 dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i], sa->syntax->ldap_oid);
2738 if (!dsdb_dn) {
2739 talloc_free(tmp_ctx);
2740 return LDB_ERR_OPERATIONS_ERROR;
2743 status = dsdb_get_extended_dn_guid(dsdb_dn->dn, &guid2, "GUID");
2744 if (!NT_STATUS_IS_OK(status)) {
2745 talloc_free(tmp_ctx);
2746 return LDB_ERR_OPERATIONS_ERROR;
2749 /* remove the link */
2750 msg = ldb_msg_new(tmp_ctx);
2751 if (!msg) {
2752 ldb_module_oom(module);
2753 talloc_free(tmp_ctx);
2754 return LDB_ERR_OPERATIONS_ERROR;
2758 msg->dn = dsdb_dn->dn;
2760 target_attr = dsdb_attribute_by_linkID(schema, sa->linkID ^ 1);
2761 if (target_attr == NULL) {
2762 continue;
2765 ret = ldb_msg_add_empty(msg, target_attr->lDAPDisplayName, LDB_FLAG_MOD_DELETE, &el2);
2766 if (ret != LDB_SUCCESS) {
2767 ldb_module_oom(module);
2768 talloc_free(tmp_ctx);
2769 return LDB_ERR_OPERATIONS_ERROR;
2771 dn_val = data_blob_string_const(ldb_dn_get_linearized(dn));
2772 el2->values = &dn_val;
2773 el2->num_values = 1;
2775 ret = dsdb_module_modify(module, msg, DSDB_FLAG_OWN_MODULE, parent);
2776 if (ret != LDB_SUCCESS) {
2777 talloc_free(tmp_ctx);
2778 return ret;
2781 talloc_free(tmp_ctx);
2782 return LDB_SUCCESS;
2787 handle update of replication meta data for deletion of objects
2789 This also handles the mapping of delete to a rename operation
2790 to allow deletes to be replicated.
2792 static int replmd_delete(struct ldb_module *module, struct ldb_request *req)
2794 int ret = LDB_ERR_OTHER;
2795 bool retb, disallow_move_on_delete;
2796 struct ldb_dn *old_dn, *new_dn;
2797 const char *rdn_name;
2798 const struct ldb_val *rdn_value, *new_rdn_value;
2799 struct GUID guid;
2800 struct ldb_context *ldb = ldb_module_get_ctx(module);
2801 const struct dsdb_schema *schema;
2802 struct ldb_message *msg, *old_msg;
2803 struct ldb_message_element *el;
2804 TALLOC_CTX *tmp_ctx;
2805 struct ldb_result *res, *parent_res;
2806 const char *preserved_attrs[] = {
2807 /* yes, this really is a hard coded list. See MS-ADTS
2808 section 3.1.1.5.5.1.1 */
2809 "nTSecurityDescriptor", "attributeID", "attributeSyntax", "dNReferenceUpdate", "dNSHostName",
2810 "flatName", "governsID", "groupType", "instanceType", "lDAPDisplayName", "legacyExchangeDN",
2811 "isDeleted", "isRecycled", "lastKnownParent", "msDS-LastKnownRDN", "mS-DS-CreatorSID",
2812 "mSMQOwnerID", "nCName", "objectClass", "distinguishedName", "objectGUID", "objectSid",
2813 "oMSyntax", "proxiedObjectName", "name", "replPropertyMetaData", "sAMAccountName",
2814 "securityIdentifier", "sIDHistory", "subClassOf", "systemFlags", "trustPartner", "trustDirection",
2815 "trustType", "trustAttributes", "userAccountControl", "uSNChanged", "uSNCreated", "whenCreated",
2816 "whenChanged", NULL};
2817 unsigned int i, el_count = 0;
2818 enum deletion_state { OBJECT_NOT_DELETED=1, OBJECT_DELETED=2, OBJECT_RECYCLED=3,
2819 OBJECT_TOMBSTONE=4, OBJECT_REMOVED=5 };
2820 enum deletion_state deletion_state, next_deletion_state;
2821 bool enabled;
2823 if (ldb_dn_is_special(req->op.del.dn)) {
2824 return ldb_next_request(module, req);
2827 tmp_ctx = talloc_new(ldb);
2828 if (!tmp_ctx) {
2829 ldb_oom(ldb);
2830 return LDB_ERR_OPERATIONS_ERROR;
2833 schema = dsdb_get_schema(ldb, tmp_ctx);
2834 if (!schema) {
2835 return LDB_ERR_OPERATIONS_ERROR;
2838 old_dn = ldb_dn_copy(tmp_ctx, req->op.del.dn);
2840 /* we need the complete msg off disk, so we can work out which
2841 attributes need to be removed */
2842 ret = dsdb_module_search_dn(module, tmp_ctx, &res, old_dn, NULL,
2843 DSDB_FLAG_NEXT_MODULE |
2844 DSDB_SEARCH_SHOW_RECYCLED |
2845 DSDB_SEARCH_REVEAL_INTERNALS |
2846 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT, req);
2847 if (ret != LDB_SUCCESS) {
2848 talloc_free(tmp_ctx);
2849 return ret;
2851 old_msg = res->msgs[0];
2854 ret = dsdb_recyclebin_enabled(module, &enabled);
2855 if (ret != LDB_SUCCESS) {
2856 talloc_free(tmp_ctx);
2857 return ret;
2860 if (ldb_msg_check_string_attribute(old_msg, "isDeleted", "TRUE")) {
2861 if (!enabled) {
2862 deletion_state = OBJECT_TOMBSTONE;
2863 next_deletion_state = OBJECT_REMOVED;
2864 } else if (ldb_msg_check_string_attribute(old_msg, "isRecycled", "TRUE")) {
2865 deletion_state = OBJECT_RECYCLED;
2866 next_deletion_state = OBJECT_REMOVED;
2867 } else {
2868 deletion_state = OBJECT_DELETED;
2869 next_deletion_state = OBJECT_RECYCLED;
2871 } else {
2872 deletion_state = OBJECT_NOT_DELETED;
2873 if (enabled) {
2874 next_deletion_state = OBJECT_DELETED;
2875 } else {
2876 next_deletion_state = OBJECT_TOMBSTONE;
2880 if (next_deletion_state == OBJECT_REMOVED) {
2881 struct auth_session_info *session_info =
2882 (struct auth_session_info *)ldb_get_opaque(ldb, "sessionInfo");
2883 if (security_session_user_level(session_info, NULL) != SECURITY_SYSTEM) {
2884 ldb_asprintf_errstring(ldb, "Refusing to delete deleted object %s",
2885 ldb_dn_get_linearized(old_msg->dn));
2886 return LDB_ERR_UNWILLING_TO_PERFORM;
2889 /* it is already deleted - really remove it this time */
2890 talloc_free(tmp_ctx);
2891 return ldb_next_request(module, req);
2894 rdn_name = ldb_dn_get_rdn_name(old_dn);
2895 rdn_value = ldb_dn_get_rdn_val(old_dn);
2896 if ((rdn_name == NULL) || (rdn_value == NULL)) {
2897 talloc_free(tmp_ctx);
2898 return ldb_operr(ldb);
2901 msg = ldb_msg_new(tmp_ctx);
2902 if (msg == NULL) {
2903 ldb_module_oom(module);
2904 talloc_free(tmp_ctx);
2905 return LDB_ERR_OPERATIONS_ERROR;
2908 msg->dn = old_dn;
2910 if (deletion_state == OBJECT_NOT_DELETED){
2911 /* consider the SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE flag */
2912 disallow_move_on_delete =
2913 (ldb_msg_find_attr_as_int(old_msg, "systemFlags", 0)
2914 & SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE);
2916 /* work out where we will be renaming this object to */
2917 if (!disallow_move_on_delete) {
2918 ret = dsdb_get_deleted_objects_dn(ldb, tmp_ctx, old_dn,
2919 &new_dn);
2920 if (ret != LDB_SUCCESS) {
2921 /* this is probably an attempted delete on a partition
2922 * that doesn't allow delete operations, such as the
2923 * schema partition */
2924 ldb_asprintf_errstring(ldb, "No Deleted Objects container for DN %s",
2925 ldb_dn_get_linearized(old_dn));
2926 talloc_free(tmp_ctx);
2927 return LDB_ERR_UNWILLING_TO_PERFORM;
2929 } else {
2930 new_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
2931 if (new_dn == NULL) {
2932 ldb_module_oom(module);
2933 talloc_free(tmp_ctx);
2934 return LDB_ERR_OPERATIONS_ERROR;
2938 /* get the objects GUID from the search we just did */
2939 guid = samdb_result_guid(old_msg, "objectGUID");
2941 /* Add a formatted child */
2942 retb = ldb_dn_add_child_fmt(new_dn, "%s=%s\\0ADEL:%s",
2943 rdn_name,
2944 ldb_dn_escape_value(tmp_ctx, *rdn_value),
2945 GUID_string(tmp_ctx, &guid));
2946 if (!retb) {
2947 DEBUG(0,(__location__ ": Unable to add a formatted child to dn: %s",
2948 ldb_dn_get_linearized(new_dn)));
2949 talloc_free(tmp_ctx);
2950 return LDB_ERR_OPERATIONS_ERROR;
2953 ret = ldb_msg_add_string(msg, "isDeleted", "TRUE");
2954 if (ret != LDB_SUCCESS) {
2955 DEBUG(0,(__location__ ": Failed to add isDeleted string to the msg\n"));
2956 ldb_module_oom(module);
2957 talloc_free(tmp_ctx);
2958 return ret;
2960 msg->elements[el_count++].flags = LDB_FLAG_MOD_REPLACE;
2964 now we need to modify the object in the following ways:
2966 - add isDeleted=TRUE
2967 - update rDN and name, with new rDN
2968 - remove linked attributes
2969 - remove objectCategory and sAMAccountType
2970 - remove attribs not on the preserved list
2971 - preserved if in above list, or is rDN
2972 - remove all linked attribs from this object
2973 - remove all links from other objects to this object
2974 - add lastKnownParent
2975 - update replPropertyMetaData?
2977 see MS-ADTS "Tombstone Requirements" section 3.1.1.5.5.1.1
2980 /* we need the storage form of the parent GUID */
2981 ret = dsdb_module_search_dn(module, tmp_ctx, &parent_res,
2982 ldb_dn_get_parent(tmp_ctx, old_dn), NULL,
2983 DSDB_FLAG_NEXT_MODULE |
2984 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
2985 DSDB_SEARCH_REVEAL_INTERNALS|
2986 DSDB_SEARCH_SHOW_RECYCLED, req);
2987 if (ret != LDB_SUCCESS) {
2988 talloc_free(tmp_ctx);
2989 return ret;
2992 if (deletion_state == OBJECT_NOT_DELETED){
2993 ret = ldb_msg_add_steal_string(msg, "lastKnownParent",
2994 ldb_dn_get_extended_linearized(tmp_ctx, parent_res->msgs[0]->dn, 1));
2995 if (ret != LDB_SUCCESS) {
2996 DEBUG(0,(__location__ ": Failed to add lastKnownParent string to the msg\n"));
2997 ldb_module_oom(module);
2998 talloc_free(tmp_ctx);
2999 return ret;
3001 msg->elements[el_count++].flags = LDB_FLAG_MOD_REPLACE;
3004 switch (next_deletion_state){
3006 case OBJECT_DELETED:
3008 ret = ldb_msg_add_value(msg, "msDS-LastKnownRDN", rdn_value, NULL);
3009 if (ret != LDB_SUCCESS) {
3010 DEBUG(0,(__location__ ": Failed to add msDS-LastKnownRDN string to the msg\n"));
3011 ldb_module_oom(module);
3012 talloc_free(tmp_ctx);
3013 return ret;
3015 msg->elements[el_count++].flags = LDB_FLAG_MOD_ADD;
3017 ret = ldb_msg_add_empty(msg, "objectCategory", LDB_FLAG_MOD_REPLACE, NULL);
3018 if (ret != LDB_SUCCESS) {
3019 talloc_free(tmp_ctx);
3020 ldb_module_oom(module);
3021 return ret;
3024 ret = ldb_msg_add_empty(msg, "sAMAccountType", LDB_FLAG_MOD_REPLACE, NULL);
3025 if (ret != LDB_SUCCESS) {
3026 talloc_free(tmp_ctx);
3027 ldb_module_oom(module);
3028 return ret;
3031 break;
3033 case OBJECT_RECYCLED:
3034 case OBJECT_TOMBSTONE:
3037 * we also mark it as recycled, meaning this object can't be
3038 * recovered (we are stripping its attributes).
3039 * This is done only if we have this schema object of course ...
3040 * This behavior is identical to the one of Windows 2008R2 which
3041 * always set the isRecycled attribute, even if the recycle-bin is
3042 * not activated and what ever the forest level is.
3044 if (dsdb_attribute_by_lDAPDisplayName(schema, "isRecycled") != NULL) {
3045 ret = ldb_msg_add_string(msg, "isRecycled", "TRUE");
3046 if (ret != LDB_SUCCESS) {
3047 DEBUG(0,(__location__ ": Failed to add isRecycled string to the msg\n"));
3048 ldb_module_oom(module);
3049 talloc_free(tmp_ctx);
3050 return ret;
3052 msg->elements[el_count++].flags = LDB_FLAG_MOD_ADD;
3055 /* work out which of the old attributes we will be removing */
3056 for (i=0; i<old_msg->num_elements; i++) {
3057 const struct dsdb_attribute *sa;
3058 el = &old_msg->elements[i];
3059 sa = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
3060 if (!sa) {
3061 talloc_free(tmp_ctx);
3062 return LDB_ERR_OPERATIONS_ERROR;
3064 if (ldb_attr_cmp(el->name, rdn_name) == 0) {
3065 /* don't remove the rDN */
3066 continue;
3068 if (sa->linkID && (sa->linkID & 1)) {
3070 we have a backlink in this object
3071 that needs to be removed. We're not
3072 allowed to remove it directly
3073 however, so we instead setup a
3074 modify to delete the corresponding
3075 forward link
3077 ret = replmd_delete_remove_link(module, schema, old_dn, el, sa, req);
3078 if (ret != LDB_SUCCESS) {
3079 talloc_free(tmp_ctx);
3080 return LDB_ERR_OPERATIONS_ERROR;
3082 /* now we continue, which means we
3083 won't remove this backlink
3084 directly
3086 continue;
3088 if (!sa->linkID && ldb_attr_in_list(preserved_attrs, el->name)) {
3089 continue;
3091 ret = ldb_msg_add_empty(msg, el->name, LDB_FLAG_MOD_DELETE, &el);
3092 if (ret != LDB_SUCCESS) {
3093 talloc_free(tmp_ctx);
3094 ldb_module_oom(module);
3095 return ret;
3098 break;
3100 default:
3101 break;
3104 if (deletion_state == OBJECT_NOT_DELETED) {
3105 const struct dsdb_attribute *sa;
3107 /* work out what the new rdn value is, for updating the
3108 rDN and name fields */
3109 new_rdn_value = ldb_dn_get_rdn_val(new_dn);
3110 if (new_rdn_value == NULL) {
3111 talloc_free(tmp_ctx);
3112 return ldb_operr(ldb);
3115 sa = dsdb_attribute_by_lDAPDisplayName(schema, rdn_name);
3116 if (!sa) {
3117 talloc_free(tmp_ctx);
3118 return LDB_ERR_OPERATIONS_ERROR;
3121 ret = ldb_msg_add_value(msg, sa->lDAPDisplayName, new_rdn_value,
3122 &el);
3123 if (ret != LDB_SUCCESS) {
3124 talloc_free(tmp_ctx);
3125 return ret;
3127 el->flags = LDB_FLAG_MOD_REPLACE;
3129 el = ldb_msg_find_element(old_msg, "name");
3130 if (el) {
3131 ret = ldb_msg_add_value(msg, "name", new_rdn_value, &el);
3132 if (ret != LDB_SUCCESS) {
3133 talloc_free(tmp_ctx);
3134 return ret;
3136 el->flags = LDB_FLAG_MOD_REPLACE;
3140 ret = dsdb_module_modify(module, msg, DSDB_FLAG_OWN_MODULE, req);
3141 if (ret != LDB_SUCCESS) {
3142 ldb_asprintf_errstring(ldb, "replmd_delete: Failed to modify object %s in delete - %s",
3143 ldb_dn_get_linearized(old_dn), ldb_errstring(ldb));
3144 talloc_free(tmp_ctx);
3145 return ret;
3148 if (deletion_state == OBJECT_NOT_DELETED) {
3149 /* now rename onto the new DN */
3150 ret = dsdb_module_rename(module, old_dn, new_dn, DSDB_FLAG_NEXT_MODULE, req);
3151 if (ret != LDB_SUCCESS){
3152 DEBUG(0,(__location__ ": Failed to rename object from '%s' to '%s' - %s\n",
3153 ldb_dn_get_linearized(old_dn),
3154 ldb_dn_get_linearized(new_dn),
3155 ldb_errstring(ldb)));
3156 talloc_free(tmp_ctx);
3157 return ret;
3161 talloc_free(tmp_ctx);
3163 return ldb_module_done(req, NULL, NULL, LDB_SUCCESS);
3168 static int replmd_replicated_request_error(struct replmd_replicated_request *ar, int ret)
3170 return ret;
3173 static int replmd_replicated_request_werror(struct replmd_replicated_request *ar, WERROR status)
3175 int ret = LDB_ERR_OTHER;
3176 /* TODO: do some error mapping */
3177 return ret;
3181 static struct replPropertyMetaData1 *
3182 replmd_replPropertyMetaData1_find_attid(struct replPropertyMetaDataBlob *md_blob,
3183 enum drsuapi_DsAttributeId attid)
3185 uint32_t i;
3186 struct replPropertyMetaDataCtr1 *rpmd_ctr = &md_blob->ctr.ctr1;
3188 for (i = 0; i < rpmd_ctr->count; i++) {
3189 if (rpmd_ctr->array[i].attid == attid) {
3190 return &rpmd_ctr->array[i];
3193 return NULL;
3198 return true if an update is newer than an existing entry
3199 see section 5.11 of MS-ADTS
3201 static bool replmd_update_is_newer(const struct GUID *current_invocation_id,
3202 const struct GUID *update_invocation_id,
3203 uint32_t current_version,
3204 uint32_t update_version,
3205 NTTIME current_change_time,
3206 NTTIME update_change_time)
3208 if (update_version != current_version) {
3209 return update_version > current_version;
3211 if (update_change_time != current_change_time) {
3212 return update_change_time > current_change_time;
3214 return GUID_compare(update_invocation_id, current_invocation_id) > 0;
3217 static bool replmd_replPropertyMetaData1_is_newer(struct replPropertyMetaData1 *cur_m,
3218 struct replPropertyMetaData1 *new_m)
3220 return replmd_update_is_newer(&cur_m->originating_invocation_id,
3221 &new_m->originating_invocation_id,
3222 cur_m->version,
3223 new_m->version,
3224 cur_m->originating_change_time,
3225 new_m->originating_change_time);
3230 form a conflict DN
3232 static struct ldb_dn *replmd_conflict_dn(TALLOC_CTX *mem_ctx, struct ldb_dn *dn, struct GUID *guid)
3234 const struct ldb_val *rdn_val;
3235 const char *rdn_name;
3236 struct ldb_dn *new_dn;
3238 rdn_val = ldb_dn_get_rdn_val(dn);
3239 rdn_name = ldb_dn_get_rdn_name(dn);
3240 if (!rdn_val || !rdn_name) {
3241 return NULL;
3244 new_dn = ldb_dn_copy(mem_ctx, dn);
3245 if (!new_dn) {
3246 return NULL;
3249 if (!ldb_dn_remove_child_components(new_dn, 1)) {
3250 return NULL;
3253 if (!ldb_dn_add_child_fmt(new_dn, "%s=%s\\0ACNF:%s",
3254 rdn_name,
3255 ldb_dn_escape_value(new_dn, *rdn_val),
3256 GUID_string(new_dn, guid))) {
3257 return NULL;
3260 return new_dn;
3265 perform a modify operation which sets the rDN and name attributes to
3266 their current values. This has the effect of changing these
3267 attributes to have been last updated by the current DC. This is
3268 needed to ensure that renames performed as part of conflict
3269 resolution are propogated to other DCs
3271 static int replmd_name_modify(struct replmd_replicated_request *ar,
3272 struct ldb_request *req, struct ldb_dn *dn)
3274 struct ldb_message *msg;
3275 const char *rdn_name;
3276 const struct ldb_val *rdn_val;
3277 const struct dsdb_attribute *rdn_attr;
3278 int ret;
3280 msg = ldb_msg_new(req);
3281 if (msg == NULL) {
3282 goto failed;
3284 msg->dn = dn;
3286 rdn_name = ldb_dn_get_rdn_name(dn);
3287 if (rdn_name == NULL) {
3288 goto failed;
3291 /* normalize the rdn attribute name */
3292 rdn_attr = dsdb_attribute_by_lDAPDisplayName(ar->schema, rdn_name);
3293 if (rdn_attr == NULL) {
3294 goto failed;
3296 rdn_name = rdn_attr->lDAPDisplayName;
3298 rdn_val = ldb_dn_get_rdn_val(dn);
3299 if (rdn_val == NULL) {
3300 goto failed;
3303 if (ldb_msg_add_empty(msg, rdn_name, LDB_FLAG_MOD_REPLACE, NULL) != 0) {
3304 goto failed;
3306 if (ldb_msg_add_value(msg, rdn_name, rdn_val, NULL) != 0) {
3307 goto failed;
3309 if (ldb_msg_add_empty(msg, "name", LDB_FLAG_MOD_REPLACE, NULL) != 0) {
3310 goto failed;
3312 if (ldb_msg_add_value(msg, "name", rdn_val, NULL) != 0) {
3313 goto failed;
3316 ret = dsdb_module_modify(ar->module, msg, DSDB_FLAG_OWN_MODULE, req);
3317 if (ret != LDB_SUCCESS) {
3318 DEBUG(0,(__location__ ": Failed to modify rDN/name of conflict DN '%s' - %s",
3319 ldb_dn_get_linearized(dn),
3320 ldb_errstring(ldb_module_get_ctx(ar->module))));
3321 return ret;
3324 talloc_free(msg);
3326 return LDB_SUCCESS;
3328 failed:
3329 talloc_free(msg);
3330 DEBUG(0,(__location__ ": Failed to setup modify rDN/name of conflict DN '%s'",
3331 ldb_dn_get_linearized(dn)));
3332 return LDB_ERR_OPERATIONS_ERROR;
3337 callback for conflict DN handling where we have renamed the incoming
3338 record. After renaming it, we need to ensure the change of name and
3339 rDN for the incoming record is seen as an originating update by this DC.
3341 This also handles updating lastKnownParent for entries sent to lostAndFound
3343 static int replmd_op_name_modify_callback(struct ldb_request *req, struct ldb_reply *ares)
3345 struct replmd_replicated_request *ar =
3346 talloc_get_type_abort(req->context, struct replmd_replicated_request);
3347 struct ldb_dn *conflict_dn;
3348 int ret;
3350 if (ares->error != LDB_SUCCESS) {
3351 /* call the normal callback for everything except success */
3352 return replmd_op_callback(req, ares);
3355 switch (req->operation) {
3356 case LDB_ADD:
3357 conflict_dn = req->op.add.message->dn;
3358 break;
3359 case LDB_MODIFY:
3360 conflict_dn = req->op.mod.message->dn;
3361 break;
3362 default:
3363 smb_panic("replmd_op_name_modify_callback called in unknown circumstances");
3366 /* perform a modify of the rDN and name of the record */
3367 ret = replmd_name_modify(ar, req, conflict_dn);
3368 if (ret != LDB_SUCCESS) {
3369 ares->error = ret;
3370 return replmd_op_callback(req, ares);
3373 if (ar->objs->objects[ar->index_current].last_known_parent) {
3374 struct ldb_message *msg = ldb_msg_new(req);
3375 if (msg == NULL) {
3376 ldb_module_oom(ar->module);
3377 return LDB_ERR_OPERATIONS_ERROR;
3380 msg->dn = req->op.add.message->dn;
3382 ret = ldb_msg_add_steal_string(msg, "lastKnownParent",
3383 ldb_dn_get_extended_linearized(msg, ar->objs->objects[ar->index_current].last_known_parent, 1));
3384 if (ret != LDB_SUCCESS) {
3385 DEBUG(0,(__location__ ": Failed to add lastKnownParent string to the msg\n"));
3386 ldb_module_oom(ar->module);
3387 return ret;
3389 msg->elements[0].flags = LDB_FLAG_MOD_REPLACE;
3391 ret = dsdb_module_modify(ar->module, msg, DSDB_FLAG_OWN_MODULE, req);
3392 if (ret != LDB_SUCCESS) {
3393 DEBUG(0,(__location__ ": Failed to modify lastKnownParent of lostAndFound DN '%s' - %s",
3394 ldb_dn_get_linearized(msg->dn),
3395 ldb_errstring(ldb_module_get_ctx(ar->module))));
3396 return ret;
3398 TALLOC_FREE(msg);
3401 return replmd_op_callback(req, ares);
3405 callback for replmd_replicated_apply_add() and replmd_replicated_handle_rename()
3406 This copes with the creation of conflict records in the case where
3407 the DN exists, but with a different objectGUID
3409 static int replmd_op_possible_conflict_callback(struct ldb_request *req, struct ldb_reply *ares, int (*callback)(struct ldb_request *req, struct ldb_reply *ares))
3411 struct ldb_dn *conflict_dn;
3412 struct replmd_replicated_request *ar =
3413 talloc_get_type_abort(req->context, struct replmd_replicated_request);
3414 struct ldb_result *res;
3415 const char *attrs[] = { "replPropertyMetaData", "objectGUID", NULL };
3416 int ret;
3417 const struct ldb_val *omd_value;
3418 struct replPropertyMetaDataBlob omd, *rmd;
3419 enum ndr_err_code ndr_err;
3420 bool rename_incoming_record, rodc;
3421 struct replPropertyMetaData1 *rmd_name, *omd_name;
3422 struct ldb_message *msg;
3424 req->callback = callback;
3426 if (ares->error != LDB_ERR_ENTRY_ALREADY_EXISTS) {
3427 /* call the normal callback for everything except
3428 conflicts */
3429 return ldb_module_done(req, ares->controls, ares->response, ares->error);
3432 ret = samdb_rodc(ldb_module_get_ctx(ar->module), &rodc);
3433 if (ret != LDB_SUCCESS) {
3434 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module), "Failed to determine if we are an RODC when attempting to form conflict DN: %s", ldb_errstring(ldb_module_get_ctx(ar->module)));
3435 return ldb_module_done(req, ares->controls, ares->response, LDB_ERR_OPERATIONS_ERROR);
3438 * we have a conflict, and need to decide if we will keep the
3439 * new record or the old record
3442 msg = ar->objs->objects[ar->index_current].msg;
3444 switch (req->operation) {
3445 case LDB_ADD:
3446 conflict_dn = msg->dn;
3447 break;
3448 case LDB_RENAME:
3449 conflict_dn = req->op.rename.newdn;
3450 break;
3451 default:
3452 return ldb_module_done(req, ares->controls, ares->response, ldb_module_operr(ar->module));
3455 if (rodc) {
3457 * We are on an RODC, or were a GC for this
3458 * partition, so we have to fail this until
3459 * someone who owns the partition sorts it
3460 * out
3462 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
3463 "Conflict adding object '%s' from incoming replication as we are read only for the partition. \n"
3464 " - We must fail the operation until a master for this partition resolves the conflict",
3465 ldb_dn_get_linearized(conflict_dn));
3466 goto failed;
3470 * first we need the replPropertyMetaData attribute from the
3471 * old record
3473 ret = dsdb_module_search_dn(ar->module, req, &res, conflict_dn,
3474 attrs,
3475 DSDB_FLAG_NEXT_MODULE |
3476 DSDB_SEARCH_SHOW_DELETED |
3477 DSDB_SEARCH_SHOW_RECYCLED, req);
3478 if (ret != LDB_SUCCESS) {
3479 DEBUG(0,(__location__ ": Unable to find object for conflicting record '%s'\n",
3480 ldb_dn_get_linearized(conflict_dn)));
3481 goto failed;
3484 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
3485 if (omd_value == NULL) {
3486 DEBUG(0,(__location__ ": Unable to find replPropertyMetaData for conflicting record '%s'\n",
3487 ldb_dn_get_linearized(conflict_dn)));
3488 goto failed;
3491 ndr_err = ndr_pull_struct_blob(omd_value, res->msgs[0], &omd,
3492 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
3493 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
3494 DEBUG(0,(__location__ ": Failed to parse old replPropertyMetaData for %s\n",
3495 ldb_dn_get_linearized(conflict_dn)));
3496 goto failed;
3499 rmd = ar->objs->objects[ar->index_current].meta_data;
3501 /* we decide which is newer based on the RPMD on the name
3502 attribute. See [MS-DRSR] ResolveNameConflict */
3503 rmd_name = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
3504 omd_name = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
3505 if (!rmd_name || !omd_name) {
3506 DEBUG(0,(__location__ ": Failed to find name attribute in replPropertyMetaData for %s\n",
3507 ldb_dn_get_linearized(conflict_dn)));
3508 goto failed;
3511 rename_incoming_record = !(ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING) &&
3512 !replmd_replPropertyMetaData1_is_newer(omd_name, rmd_name);
3514 if (rename_incoming_record) {
3515 struct GUID guid;
3516 struct ldb_dn *new_dn;
3517 struct ldb_message *new_msg;
3520 * We want to run the original callback here, which
3521 * will return LDB_ERR_ENTRY_ALREADY_EXISTS to the
3522 * caller, which will in turn know to rename the
3523 * incoming record. The error string is set in case
3524 * this isn't handled properly at some point in the
3525 * future.
3527 if (req->operation == LDB_RENAME) {
3528 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
3529 "Unable to handle incoming renames where this would "
3530 "create a conflict. Incoming record is %s (caller to handle)\n",
3531 ldb_dn_get_extended_linearized(req, conflict_dn, 1));
3533 goto failed;
3536 guid = samdb_result_guid(msg, "objectGUID");
3537 if (GUID_all_zero(&guid)) {
3538 DEBUG(0,(__location__ ": Failed to find objectGUID for conflicting incoming record %s\n",
3539 ldb_dn_get_linearized(conflict_dn)));
3540 goto failed;
3542 new_dn = replmd_conflict_dn(req, conflict_dn, &guid);
3543 if (new_dn == NULL) {
3544 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
3545 ldb_dn_get_linearized(conflict_dn)));
3546 goto failed;
3549 DEBUG(2,(__location__ ": Resolving conflict record via incoming rename '%s' -> '%s'\n",
3550 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
3552 /* re-submit the request, but with a different
3553 callback, so we don't loop forever. */
3554 new_msg = ldb_msg_copy_shallow(req, msg);
3555 if (!new_msg) {
3556 goto failed;
3557 DEBUG(0,(__location__ ": Failed to copy conflict DN message for %s\n",
3558 ldb_dn_get_linearized(conflict_dn)));
3560 new_msg->dn = new_dn;
3561 req->op.add.message = new_msg;
3562 req->callback = replmd_op_name_modify_callback;
3564 return ldb_next_request(ar->module, req);
3565 } else {
3566 /* we are renaming the existing record */
3567 struct GUID guid;
3568 struct ldb_dn *new_dn;
3570 guid = samdb_result_guid(res->msgs[0], "objectGUID");
3571 if (GUID_all_zero(&guid)) {
3572 DEBUG(0,(__location__ ": Failed to find objectGUID for existing conflict record %s\n",
3573 ldb_dn_get_linearized(conflict_dn)));
3574 goto failed;
3577 new_dn = replmd_conflict_dn(req, conflict_dn, &guid);
3578 if (new_dn == NULL) {
3579 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
3580 ldb_dn_get_linearized(conflict_dn)));
3581 goto failed;
3584 DEBUG(2,(__location__ ": Resolving conflict record via existing rename '%s' -> '%s'\n",
3585 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
3587 ret = dsdb_module_rename(ar->module, conflict_dn, new_dn,
3588 DSDB_FLAG_OWN_MODULE, req);
3589 if (ret != LDB_SUCCESS) {
3590 DEBUG(0,(__location__ ": Failed to rename conflict dn '%s' to '%s' - %s\n",
3591 ldb_dn_get_linearized(conflict_dn),
3592 ldb_dn_get_linearized(new_dn),
3593 ldb_errstring(ldb_module_get_ctx(ar->module))));
3594 goto failed;
3598 * now we need to ensure that the rename is seen as an
3599 * originating update. We do that with a modify.
3601 ret = replmd_name_modify(ar, req, new_dn);
3602 if (ret != LDB_SUCCESS) {
3603 goto failed;
3606 return ldb_next_request(ar->module, req);
3609 failed:
3610 /* on failure do the original callback. This means replication
3611 * will stop with an error, but there is not much else we can
3612 * do
3614 return ldb_module_done(req, ares->controls, ares->response, ares->error);
3618 callback for replmd_replicated_apply_add()
3619 This copes with the creation of conflict records in the case where
3620 the DN exists, but with a different objectGUID
3622 static int replmd_op_add_callback(struct ldb_request *req, struct ldb_reply *ares)
3624 struct replmd_replicated_request *ar =
3625 talloc_get_type_abort(req->context, struct replmd_replicated_request);
3627 if (ar->objs->objects[ar->index_current].last_known_parent) {
3628 /* This is like a conflict DN, where we put the object in LostAndFound
3629 see MS-DRSR 4.1.10.6.10 FindBestParentObject */
3630 return replmd_op_possible_conflict_callback(req, ares, replmd_op_name_modify_callback);
3633 return replmd_op_possible_conflict_callback(req, ares, replmd_op_callback);
3637 callback for replmd_replicated_handle_rename()
3638 This copes with the creation of conflict records in the case where
3639 the DN exists, but with a different objectGUID
3641 static int replmd_op_rename_callback(struct ldb_request *req, struct ldb_reply *ares)
3643 return replmd_op_possible_conflict_callback(req, ares, ldb_modify_default_callback);
3647 this is called when a new object comes in over DRS
3649 static int replmd_replicated_apply_add(struct replmd_replicated_request *ar)
3651 struct ldb_context *ldb;
3652 struct ldb_request *change_req;
3653 enum ndr_err_code ndr_err;
3654 struct ldb_message *msg;
3655 struct replPropertyMetaDataBlob *md;
3656 struct ldb_val md_value;
3657 unsigned int i;
3658 int ret;
3659 bool remote_isDeleted = false;
3662 * TODO: check if the parent object exist
3666 * TODO: handle the conflict case where an object with the
3667 * same name exist
3670 ldb = ldb_module_get_ctx(ar->module);
3671 msg = ar->objs->objects[ar->index_current].msg;
3672 md = ar->objs->objects[ar->index_current].meta_data;
3674 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
3675 if (ret != LDB_SUCCESS) {
3676 return replmd_replicated_request_error(ar, ret);
3679 ret = ldb_msg_add_value(msg, "objectGUID", &ar->objs->objects[ar->index_current].guid_value, NULL);
3680 if (ret != LDB_SUCCESS) {
3681 return replmd_replicated_request_error(ar, ret);
3684 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
3685 if (ret != LDB_SUCCESS) {
3686 return replmd_replicated_request_error(ar, ret);
3689 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", ar->seq_num);
3690 if (ret != LDB_SUCCESS) {
3691 return replmd_replicated_request_error(ar, ret);
3694 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
3695 if (ret != LDB_SUCCESS) {
3696 return replmd_replicated_request_error(ar, ret);
3699 /* remove any message elements that have zero values */
3700 for (i=0; i<msg->num_elements; i++) {
3701 struct ldb_message_element *el = &msg->elements[i];
3703 if (el->num_values == 0) {
3704 DEBUG(4,(__location__ ": Removing attribute %s with num_values==0\n",
3705 el->name));
3706 memmove(el, el+1, sizeof(*el)*(msg->num_elements - (i+1)));
3707 msg->num_elements--;
3708 i--;
3709 continue;
3713 remote_isDeleted = ldb_msg_find_attr_as_bool(msg,
3714 "isDeleted", false);
3717 * the meta data array is already sorted by the caller
3719 for (i=0; i < md->ctr.ctr1.count; i++) {
3720 md->ctr.ctr1.array[i].local_usn = ar->seq_num;
3722 ndr_err = ndr_push_struct_blob(&md_value, msg, md,
3723 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
3724 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
3725 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
3726 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
3728 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &md_value, NULL);
3729 if (ret != LDB_SUCCESS) {
3730 return replmd_replicated_request_error(ar, ret);
3733 replmd_ldb_message_sort(msg, ar->schema);
3735 if (!remote_isDeleted) {
3736 ret = dsdb_module_schedule_sd_propagation(ar->module,
3737 ar->objs->partition_dn,
3738 msg->dn, true);
3739 if (ret != LDB_SUCCESS) {
3740 return replmd_replicated_request_error(ar, ret);
3744 if (DEBUGLVL(4)) {
3745 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_ADD, msg);
3746 DEBUG(4, ("DRS replication add message:\n%s\n", s));
3747 talloc_free(s);
3750 ret = ldb_build_add_req(&change_req,
3751 ldb,
3753 msg,
3754 ar->controls,
3756 replmd_op_add_callback,
3757 ar->req);
3758 LDB_REQ_SET_LOCATION(change_req);
3759 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
3761 /* current partition control needed by "repmd_op_callback" */
3762 ret = ldb_request_add_control(change_req,
3763 DSDB_CONTROL_CURRENT_PARTITION_OID,
3764 false, NULL);
3765 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
3767 if (ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PARTIAL_REPLICA) {
3768 /* this tells the partition module to make it a
3769 partial replica if creating an NC */
3770 ret = ldb_request_add_control(change_req,
3771 DSDB_CONTROL_PARTIAL_REPLICA,
3772 false, NULL);
3773 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
3776 return ldb_next_request(ar->module, change_req);
3779 static int replmd_replicated_apply_search_for_parent_callback(struct ldb_request *req,
3780 struct ldb_reply *ares)
3782 struct replmd_replicated_request *ar = talloc_get_type(req->context,
3783 struct replmd_replicated_request);
3784 int ret;
3786 if (!ares) {
3787 return ldb_module_done(ar->req, NULL, NULL,
3788 LDB_ERR_OPERATIONS_ERROR);
3790 if (ares->error != LDB_SUCCESS &&
3791 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
3792 return ldb_module_done(ar->req, ares->controls,
3793 ares->response, ares->error);
3796 switch (ares->type) {
3797 case LDB_REPLY_ENTRY:
3799 struct ldb_message *parent_msg = ares->message;
3800 struct ldb_message *msg = ar->objs->objects[ar->index_current].msg;
3801 struct ldb_dn *parent_dn;
3802 int comp_num;
3804 if (!ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")
3805 && ldb_msg_check_string_attribute(parent_msg, "isDeleted", "TRUE")) {
3806 /* Per MS-DRSR 4.1.10.6.10
3807 * FindBestParentObject we need to move this
3808 * new object under a deleted object to
3809 * lost-and-found */
3810 struct ldb_dn *nc_root;
3812 ret = dsdb_find_nc_root(ldb_module_get_ctx(ar->module), msg, msg->dn, &nc_root);
3813 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
3814 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
3815 "No suitable NC root found for %s. "
3816 "We need to move this object because parent object %s "
3817 "is deleted, but this object is not.",
3818 ldb_dn_get_linearized(msg->dn),
3819 ldb_dn_get_linearized(parent_msg->dn));
3820 return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
3821 } else if (ret != LDB_SUCCESS) {
3822 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
3823 "Unable to find NC root for %s: %s. "
3824 "We need to move this object because parent object %s "
3825 "is deleted, but this object is not.",
3826 ldb_dn_get_linearized(msg->dn),
3827 ldb_errstring(ldb_module_get_ctx(ar->module)),
3828 ldb_dn_get_linearized(parent_msg->dn));
3829 return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
3832 ret = dsdb_wellknown_dn(ldb_module_get_ctx(ar->module), msg,
3833 nc_root,
3834 DS_GUID_LOSTANDFOUND_CONTAINER,
3835 &parent_dn);
3836 if (ret != LDB_SUCCESS) {
3837 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
3838 "Unable to find LostAndFound Container for %s "
3839 "in partition %s: %s. "
3840 "We need to move this object because parent object %s "
3841 "is deleted, but this object is not.",
3842 ldb_dn_get_linearized(msg->dn), ldb_dn_get_linearized(nc_root),
3843 ldb_errstring(ldb_module_get_ctx(ar->module)),
3844 ldb_dn_get_linearized(parent_msg->dn));
3845 return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
3847 ar->objs->objects[ar->index_current].last_known_parent
3848 = talloc_steal(ar->objs->objects[ar->index_current].msg, parent_msg->dn);
3849 } else {
3850 parent_dn = parent_msg->dn;
3853 comp_num = ldb_dn_get_comp_num(msg->dn);
3854 if (comp_num > 1) {
3855 if (!ldb_dn_remove_base_components(msg->dn, comp_num - 1)) {
3856 talloc_free(ares);
3857 return ldb_module_done(ar->req, NULL, NULL, ldb_module_operr(ar->module));
3860 if (!ldb_dn_add_base(msg->dn, parent_dn)) {
3861 talloc_free(ares);
3862 return ldb_module_done(ar->req, NULL, NULL, ldb_module_operr(ar->module));
3864 break;
3866 case LDB_REPLY_REFERRAL:
3867 /* we ignore referrals */
3868 break;
3870 case LDB_REPLY_DONE:
3871 if (ar->search_msg != NULL) {
3872 ret = replmd_replicated_apply_merge(ar);
3873 } else {
3874 ret = replmd_replicated_apply_add(ar);
3876 if (ret != LDB_SUCCESS) {
3877 return ldb_module_done(ar->req, NULL, NULL, ret);
3881 talloc_free(ares);
3882 return LDB_SUCCESS;
3886 * Look for the parent object, so we put the new object in the right place
3889 static int replmd_replicated_apply_search_for_parent(struct replmd_replicated_request *ar)
3891 struct ldb_context *ldb;
3892 int ret;
3893 char *tmp_str;
3894 char *filter;
3895 struct ldb_request *search_req;
3896 static const char *attrs[] = {"isDeleted", NULL};
3898 ldb = ldb_module_get_ctx(ar->module);
3900 if (!ar->objs->objects[ar->index_current].parent_guid_value.data) {
3901 if (ar->search_msg != NULL) {
3902 return replmd_replicated_apply_merge(ar);
3903 } else {
3904 return replmd_replicated_apply_add(ar);
3908 tmp_str = ldb_binary_encode(ar, ar->objs->objects[ar->index_current].parent_guid_value);
3909 if (!tmp_str) return replmd_replicated_request_werror(ar, WERR_NOMEM);
3911 filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
3912 if (!filter) return replmd_replicated_request_werror(ar, WERR_NOMEM);
3913 talloc_free(tmp_str);
3915 ret = ldb_build_search_req(&search_req,
3916 ldb,
3918 ar->objs->partition_dn,
3919 LDB_SCOPE_SUBTREE,
3920 filter,
3921 attrs,
3922 NULL,
3924 replmd_replicated_apply_search_for_parent_callback,
3925 ar->req);
3926 LDB_REQ_SET_LOCATION(search_req);
3928 ret = dsdb_request_add_controls(search_req,
3929 DSDB_SEARCH_SHOW_RECYCLED|
3930 DSDB_SEARCH_SHOW_DELETED|
3931 DSDB_SEARCH_SHOW_EXTENDED_DN);
3932 if (ret != LDB_SUCCESS) {
3933 return ret;
3936 return ldb_next_request(ar->module, search_req);
3940 handle renames that come in over DRS replication
3942 static int replmd_replicated_handle_rename(struct replmd_replicated_request *ar,
3943 struct ldb_message *msg,
3944 struct ldb_request *parent)
3946 struct ldb_request *req;
3947 int ret;
3948 TALLOC_CTX *tmp_ctx = talloc_new(msg);
3949 struct ldb_result *res;
3951 DEBUG(4,("replmd_replicated_request rename %s => %s\n",
3952 ldb_dn_get_linearized(ar->search_msg->dn),
3953 ldb_dn_get_linearized(msg->dn)));
3956 res = talloc_zero(tmp_ctx, struct ldb_result);
3957 if (!res) {
3958 talloc_free(tmp_ctx);
3959 return ldb_oom(ldb_module_get_ctx(ar->module));
3962 /* pass rename to the next module
3963 * so it doesn't appear as an originating update */
3964 ret = ldb_build_rename_req(&req, ldb_module_get_ctx(ar->module), tmp_ctx,
3965 ar->search_msg->dn, msg->dn,
3966 NULL,
3968 replmd_op_rename_callback,
3969 parent);
3970 LDB_REQ_SET_LOCATION(req);
3971 if (ret != LDB_SUCCESS) {
3972 talloc_free(tmp_ctx);
3973 return ret;
3976 ret = dsdb_request_add_controls(req, DSDB_MODIFY_RELAX);
3977 if (ret != LDB_SUCCESS) {
3978 talloc_free(tmp_ctx);
3979 return ret;
3982 ret = ldb_next_request(ar->module, req);
3984 if (ret == LDB_SUCCESS) {
3985 ret = ldb_wait(req->handle, LDB_WAIT_ALL);
3988 talloc_free(tmp_ctx);
3989 return ret;
3993 static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar)
3995 struct ldb_context *ldb;
3996 struct ldb_request *change_req;
3997 enum ndr_err_code ndr_err;
3998 struct ldb_message *msg;
3999 struct replPropertyMetaDataBlob *rmd;
4000 struct replPropertyMetaDataBlob omd;
4001 const struct ldb_val *omd_value;
4002 struct replPropertyMetaDataBlob nmd;
4003 struct ldb_val nmd_value;
4004 unsigned int i;
4005 uint32_t j,ni=0;
4006 unsigned int removed_attrs = 0;
4007 int ret;
4008 int (*callback)(struct ldb_request *req, struct ldb_reply *ares) = replmd_op_callback;
4009 bool isDeleted = false;
4010 bool local_isDeleted = false;
4011 bool remote_isDeleted = false;
4012 bool take_remote_isDeleted = false;
4013 bool sd_updated = false;
4014 bool renamed = false;
4016 ldb = ldb_module_get_ctx(ar->module);
4017 msg = ar->objs->objects[ar->index_current].msg;
4019 rmd = ar->objs->objects[ar->index_current].meta_data;
4020 ZERO_STRUCT(omd);
4021 omd.version = 1;
4023 /* find existing meta data */
4024 omd_value = ldb_msg_find_ldb_val(ar->search_msg, "replPropertyMetaData");
4025 if (omd_value) {
4026 ndr_err = ndr_pull_struct_blob(omd_value, ar, &omd,
4027 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
4028 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
4029 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
4030 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
4033 if (omd.version != 1) {
4034 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
4038 local_isDeleted = ldb_msg_find_attr_as_bool(ar->search_msg,
4039 "isDeleted", false);
4040 remote_isDeleted = ldb_msg_find_attr_as_bool(msg,
4041 "isDeleted", false);
4043 if (strcmp(ldb_dn_get_linearized(msg->dn), ldb_dn_get_linearized(ar->search_msg->dn)) == 0) {
4044 ret = LDB_SUCCESS;
4045 } else {
4047 * handle renames, even just by case that come in over
4048 * DRS. Changes in the parent DN don't hit us here,
4049 * because the search for a parent will clean up those
4050 * components.
4052 * We also have already filtered out the case where
4053 * the peer has an older name to what we have (see
4054 * replmd_replicated_apply_search_callback())
4056 renamed = true;
4057 ret = replmd_replicated_handle_rename(ar, msg, ar->req);
4061 * This particular error code means that we already tried the
4062 * conflict algrorithm, and the existing record name was newer, so we
4063 * need to rename the incoming record
4065 if (ret == LDB_ERR_ENTRY_ALREADY_EXISTS) {
4066 struct GUID guid;
4067 NTSTATUS status;
4068 struct ldb_dn *new_dn;
4069 status = GUID_from_ndr_blob(&ar->objs->objects[ar->index_current].guid_value, &guid);
4070 /* This really, really can't fail */
4071 SMB_ASSERT(NT_STATUS_IS_OK(status));
4073 new_dn = replmd_conflict_dn(msg, msg->dn, &guid);
4074 if (new_dn == NULL) {
4075 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4076 "Failed to form conflict DN for %s\n",
4077 ldb_dn_get_linearized(msg->dn));
4079 return replmd_replicated_request_werror(ar, WERR_NOMEM);
4082 ret = dsdb_module_rename(ar->module, ar->search_msg->dn, new_dn,
4083 DSDB_FLAG_NEXT_MODULE, ar->req);
4084 if (ret != LDB_SUCCESS) {
4085 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
4086 "Failed to rename incoming conflicting dn '%s' (was '%s') to '%s' - %s\n",
4087 ldb_dn_get_linearized(msg->dn),
4088 ldb_dn_get_linearized(ar->search_msg->dn),
4089 ldb_dn_get_linearized(new_dn),
4090 ldb_errstring(ldb_module_get_ctx(ar->module)));
4091 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
4094 /* Set the callback to one that will fix up the name to be a conflict DN */
4095 callback = replmd_op_name_modify_callback;
4096 msg->dn = new_dn;
4097 renamed = true;
4098 } else if (ret != LDB_SUCCESS) {
4099 ldb_debug(ldb, LDB_DEBUG_FATAL,
4100 "replmd_replicated_request rename %s => %s failed - %s\n",
4101 ldb_dn_get_linearized(ar->search_msg->dn),
4102 ldb_dn_get_linearized(msg->dn),
4103 ldb_errstring(ldb));
4104 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
4107 ZERO_STRUCT(nmd);
4108 nmd.version = 1;
4109 nmd.ctr.ctr1.count = omd.ctr.ctr1.count + rmd->ctr.ctr1.count;
4110 nmd.ctr.ctr1.array = talloc_array(ar,
4111 struct replPropertyMetaData1,
4112 nmd.ctr.ctr1.count);
4113 if (!nmd.ctr.ctr1.array) return replmd_replicated_request_werror(ar, WERR_NOMEM);
4115 /* first copy the old meta data */
4116 for (i=0; i < omd.ctr.ctr1.count; i++) {
4117 nmd.ctr.ctr1.array[ni] = omd.ctr.ctr1.array[i];
4118 ni++;
4121 ar->seq_num = 0;
4122 /* now merge in the new meta data */
4123 for (i=0; i < rmd->ctr.ctr1.count; i++) {
4124 bool found = false;
4126 for (j=0; j < ni; j++) {
4127 bool cmp;
4129 if (rmd->ctr.ctr1.array[i].attid != nmd.ctr.ctr1.array[j].attid) {
4130 continue;
4133 if (ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING) {
4134 /* if we compare equal then do an
4135 update. This is used when a client
4136 asks for a FULL_SYNC, and can be
4137 used to recover a corrupt
4138 replica */
4139 cmp = !replmd_replPropertyMetaData1_is_newer(&rmd->ctr.ctr1.array[i],
4140 &nmd.ctr.ctr1.array[j]);
4141 } else {
4142 cmp = replmd_replPropertyMetaData1_is_newer(&nmd.ctr.ctr1.array[j],
4143 &rmd->ctr.ctr1.array[i]);
4145 if (cmp) {
4146 /* replace the entry */
4147 nmd.ctr.ctr1.array[j] = rmd->ctr.ctr1.array[i];
4148 if (ar->seq_num == 0) {
4149 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
4150 if (ret != LDB_SUCCESS) {
4151 return replmd_replicated_request_error(ar, ret);
4154 nmd.ctr.ctr1.array[j].local_usn = ar->seq_num;
4155 switch (nmd.ctr.ctr1.array[j].attid) {
4156 case DRSUAPI_ATTID_ntSecurityDescriptor:
4157 sd_updated = true;
4158 break;
4159 case DRSUAPI_ATTID_isDeleted:
4160 take_remote_isDeleted = true;
4161 break;
4162 default:
4163 break;
4165 found = true;
4166 break;
4169 if (rmd->ctr.ctr1.array[i].attid != DRSUAPI_ATTID_instanceType) {
4170 DEBUG(3,("Discarding older DRS attribute update to %s on %s from %s\n",
4171 msg->elements[i-removed_attrs].name,
4172 ldb_dn_get_linearized(msg->dn),
4173 GUID_string(ar, &rmd->ctr.ctr1.array[i].originating_invocation_id)));
4176 /* we don't want to apply this change so remove the attribute */
4177 ldb_msg_remove_element(msg, &msg->elements[i-removed_attrs]);
4178 removed_attrs++;
4180 found = true;
4181 break;
4184 if (found) continue;
4186 nmd.ctr.ctr1.array[ni] = rmd->ctr.ctr1.array[i];
4187 if (ar->seq_num == 0) {
4188 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
4189 if (ret != LDB_SUCCESS) {
4190 return replmd_replicated_request_error(ar, ret);
4193 nmd.ctr.ctr1.array[ni].local_usn = ar->seq_num;
4194 switch (nmd.ctr.ctr1.array[ni].attid) {
4195 case DRSUAPI_ATTID_ntSecurityDescriptor:
4196 sd_updated = true;
4197 break;
4198 case DRSUAPI_ATTID_isDeleted:
4199 take_remote_isDeleted = true;
4200 break;
4201 default:
4202 break;
4204 ni++;
4208 * finally correct the size of the meta_data array
4210 nmd.ctr.ctr1.count = ni;
4213 * the rdn attribute (the alias for the name attribute),
4214 * 'cn' for most objects is the last entry in the meta data array
4215 * we have stored
4217 * sort the new meta data array
4219 ret = replmd_replPropertyMetaDataCtr1_sort(&nmd.ctr.ctr1, ar->schema, msg->dn);
4220 if (ret != LDB_SUCCESS) {
4221 return ret;
4225 * check if some replicated attributes left, otherwise skip the ldb_modify() call
4227 if (msg->num_elements == 0) {
4228 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: skip replace\n",
4229 ar->index_current);
4231 ar->index_current++;
4232 return replmd_replicated_apply_next(ar);
4235 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: replace %u attributes\n",
4236 ar->index_current, msg->num_elements);
4238 if (take_remote_isDeleted) {
4239 isDeleted = remote_isDeleted;
4240 } else {
4241 isDeleted = local_isDeleted;
4244 if (renamed) {
4245 sd_updated = true;
4248 if (sd_updated && !isDeleted) {
4249 ret = dsdb_module_schedule_sd_propagation(ar->module,
4250 ar->objs->partition_dn,
4251 msg->dn, true);
4252 if (ret != LDB_SUCCESS) {
4253 return ldb_operr(ldb);
4257 /* create the meta data value */
4258 ndr_err = ndr_push_struct_blob(&nmd_value, msg, &nmd,
4259 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
4260 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
4261 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
4262 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
4266 * when we know that we'll modify the record, add the whenChanged, uSNChanged
4267 * and replPopertyMetaData attributes
4269 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
4270 if (ret != LDB_SUCCESS) {
4271 return replmd_replicated_request_error(ar, ret);
4273 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
4274 if (ret != LDB_SUCCESS) {
4275 return replmd_replicated_request_error(ar, ret);
4277 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
4278 if (ret != LDB_SUCCESS) {
4279 return replmd_replicated_request_error(ar, ret);
4282 replmd_ldb_message_sort(msg, ar->schema);
4284 /* we want to replace the old values */
4285 for (i=0; i < msg->num_elements; i++) {
4286 msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
4289 if (DEBUGLVL(4)) {
4290 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_MODIFY, msg);
4291 DEBUG(4, ("DRS replication modify message:\n%s\n", s));
4292 talloc_free(s);
4295 ret = ldb_build_mod_req(&change_req,
4296 ldb,
4298 msg,
4299 ar->controls,
4301 callback,
4302 ar->req);
4303 LDB_REQ_SET_LOCATION(change_req);
4304 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
4306 /* current partition control needed by "repmd_op_callback" */
4307 ret = ldb_request_add_control(change_req,
4308 DSDB_CONTROL_CURRENT_PARTITION_OID,
4309 false, NULL);
4310 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
4312 return ldb_next_request(ar->module, change_req);
4315 static int replmd_replicated_apply_search_callback(struct ldb_request *req,
4316 struct ldb_reply *ares)
4318 struct replmd_replicated_request *ar = talloc_get_type(req->context,
4319 struct replmd_replicated_request);
4320 int ret;
4322 if (!ares) {
4323 return ldb_module_done(ar->req, NULL, NULL,
4324 LDB_ERR_OPERATIONS_ERROR);
4326 if (ares->error != LDB_SUCCESS &&
4327 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
4328 return ldb_module_done(ar->req, ares->controls,
4329 ares->response, ares->error);
4332 switch (ares->type) {
4333 case LDB_REPLY_ENTRY:
4334 ar->search_msg = talloc_steal(ar, ares->message);
4335 break;
4337 case LDB_REPLY_REFERRAL:
4338 /* we ignore referrals */
4339 break;
4341 case LDB_REPLY_DONE:
4343 struct replPropertyMetaData1 *md_remote;
4344 struct replPropertyMetaData1 *md_local;
4346 struct replPropertyMetaDataBlob omd;
4347 const struct ldb_val *omd_value;
4348 struct replPropertyMetaDataBlob *rmd;
4349 struct ldb_message *msg;
4351 ar->objs->objects[ar->index_current].last_known_parent = NULL;
4354 * This is the ADD case, find the appropriate parent,
4355 * as this object doesn't exist locally:
4357 if (ar->search_msg == NULL) {
4358 ret = replmd_replicated_apply_search_for_parent(ar);
4359 if (ret != LDB_SUCCESS) {
4360 return ldb_module_done(ar->req, NULL, NULL, ret);
4362 talloc_free(ares);
4363 return LDB_SUCCESS;
4367 * Otherwise, in the MERGE case, work out if we are
4368 * attempting a rename, and if so find the parent the
4369 * newly renamed object wants to belong under (which
4370 * may not be the parent in it's attached string DN
4372 rmd = ar->objs->objects[ar->index_current].meta_data;
4373 ZERO_STRUCT(omd);
4374 omd.version = 1;
4376 /* find existing meta data */
4377 omd_value = ldb_msg_find_ldb_val(ar->search_msg, "replPropertyMetaData");
4378 if (omd_value) {
4379 enum ndr_err_code ndr_err;
4380 ndr_err = ndr_pull_struct_blob(omd_value, ar, &omd,
4381 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
4382 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
4383 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
4384 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
4387 if (omd.version != 1) {
4388 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
4393 * now we need to check for double renames. We could have a
4394 * local rename pending which our replication partner hasn't
4395 * received yet. We choose which one wins by looking at the
4396 * attribute stamps on the two objects, the newer one wins
4398 md_remote = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
4399 md_local = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
4400 /* if there is no name attribute then we have to assume the
4401 object we've received is in fact newer */
4402 if (ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING ||
4403 !md_remote || !md_local ||
4404 replmd_replPropertyMetaData1_is_newer(md_local, md_remote)) {
4405 ret = replmd_replicated_apply_search_for_parent(ar);
4406 } else {
4407 msg = ar->objs->objects[ar->index_current].msg;
4409 /* Otherwise, just merge on the existing object, force no rename */
4410 DEBUG(4,(__location__ ": Keeping object %s and rejecting older rename to %s\n",
4411 ldb_dn_get_linearized(ar->search_msg->dn),
4412 ldb_dn_get_linearized(msg->dn)));
4415 * This assignment ensures that the strcmp()
4416 * in replmd_replicated_apply_merge() avoids
4417 * the rename call
4419 msg->dn = ar->search_msg->dn;
4420 ret = replmd_replicated_apply_merge(ar);
4422 if (ret != LDB_SUCCESS) {
4423 return ldb_module_done(ar->req, NULL, NULL, ret);
4428 talloc_free(ares);
4429 return LDB_SUCCESS;
4432 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar);
4434 static int replmd_replicated_apply_next(struct replmd_replicated_request *ar)
4436 struct ldb_context *ldb;
4437 int ret;
4438 char *tmp_str;
4439 char *filter;
4440 struct ldb_request *search_req;
4441 struct ldb_search_options_control *options;
4443 if (ar->index_current >= ar->objs->num_objects) {
4444 /* done with it, go to next stage */
4445 return replmd_replicated_uptodate_vector(ar);
4448 ldb = ldb_module_get_ctx(ar->module);
4449 ar->search_msg = NULL;
4451 tmp_str = ldb_binary_encode(ar, ar->objs->objects[ar->index_current].guid_value);
4452 if (!tmp_str) return replmd_replicated_request_werror(ar, WERR_NOMEM);
4454 filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
4455 if (!filter) return replmd_replicated_request_werror(ar, WERR_NOMEM);
4456 talloc_free(tmp_str);
4458 ret = ldb_build_search_req(&search_req,
4459 ldb,
4461 NULL,
4462 LDB_SCOPE_SUBTREE,
4463 filter,
4464 NULL,
4465 NULL,
4467 replmd_replicated_apply_search_callback,
4468 ar->req);
4469 LDB_REQ_SET_LOCATION(search_req);
4471 ret = ldb_request_add_control(search_req, LDB_CONTROL_SHOW_RECYCLED_OID,
4472 true, NULL);
4473 if (ret != LDB_SUCCESS) {
4474 return ret;
4477 /* we need to cope with cross-partition links, so search for
4478 the GUID over all partitions */
4479 options = talloc(search_req, struct ldb_search_options_control);
4480 if (options == NULL) {
4481 DEBUG(0, (__location__ ": out of memory\n"));
4482 return LDB_ERR_OPERATIONS_ERROR;
4484 options->search_options = LDB_SEARCH_OPTION_PHANTOM_ROOT;
4486 ret = ldb_request_add_control(search_req,
4487 LDB_CONTROL_SEARCH_OPTIONS_OID,
4488 true, options);
4489 if (ret != LDB_SUCCESS) {
4490 return ret;
4493 return ldb_next_request(ar->module, search_req);
4496 static int replmd_replicated_uptodate_modify_callback(struct ldb_request *req,
4497 struct ldb_reply *ares)
4499 struct ldb_context *ldb;
4500 struct replmd_replicated_request *ar = talloc_get_type(req->context,
4501 struct replmd_replicated_request);
4502 ldb = ldb_module_get_ctx(ar->module);
4504 if (!ares) {
4505 return ldb_module_done(ar->req, NULL, NULL,
4506 LDB_ERR_OPERATIONS_ERROR);
4508 if (ares->error != LDB_SUCCESS) {
4509 return ldb_module_done(ar->req, ares->controls,
4510 ares->response, ares->error);
4513 if (ares->type != LDB_REPLY_DONE) {
4514 ldb_asprintf_errstring(ldb, "Invalid LDB reply type %d", ares->type);
4515 return ldb_module_done(ar->req, NULL, NULL,
4516 LDB_ERR_OPERATIONS_ERROR);
4519 talloc_free(ares);
4521 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
4524 static int replmd_replicated_uptodate_modify(struct replmd_replicated_request *ar)
4526 struct ldb_context *ldb;
4527 struct ldb_request *change_req;
4528 enum ndr_err_code ndr_err;
4529 struct ldb_message *msg;
4530 struct replUpToDateVectorBlob ouv;
4531 const struct ldb_val *ouv_value;
4532 const struct drsuapi_DsReplicaCursor2CtrEx *ruv;
4533 struct replUpToDateVectorBlob nuv;
4534 struct ldb_val nuv_value;
4535 struct ldb_message_element *nuv_el = NULL;
4536 const struct GUID *our_invocation_id;
4537 struct ldb_message_element *orf_el = NULL;
4538 struct repsFromToBlob nrf;
4539 struct ldb_val *nrf_value = NULL;
4540 struct ldb_message_element *nrf_el = NULL;
4541 unsigned int i;
4542 uint32_t j,ni=0;
4543 bool found = false;
4544 time_t t = time(NULL);
4545 NTTIME now;
4546 int ret;
4547 uint32_t instanceType;
4549 ldb = ldb_module_get_ctx(ar->module);
4550 ruv = ar->objs->uptodateness_vector;
4551 ZERO_STRUCT(ouv);
4552 ouv.version = 2;
4553 ZERO_STRUCT(nuv);
4554 nuv.version = 2;
4556 unix_to_nt_time(&now, t);
4558 if (ar->search_msg == NULL) {
4559 /* this happens for a REPL_OBJ call where we are
4560 creating the target object by replicating it. The
4561 subdomain join code does this for the partition DN
4563 DEBUG(4,(__location__ ": Skipping UDV and repsFrom update as no target DN\n"));
4564 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
4567 instanceType = ldb_msg_find_attr_as_uint(ar->search_msg, "instanceType", 0);
4568 if (! (instanceType & INSTANCE_TYPE_IS_NC_HEAD)) {
4569 DEBUG(4,(__location__ ": Skipping UDV and repsFrom update as not NC root: %s\n",
4570 ldb_dn_get_linearized(ar->search_msg->dn)));
4571 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
4575 * first create the new replUpToDateVector
4577 ouv_value = ldb_msg_find_ldb_val(ar->search_msg, "replUpToDateVector");
4578 if (ouv_value) {
4579 ndr_err = ndr_pull_struct_blob(ouv_value, ar, &ouv,
4580 (ndr_pull_flags_fn_t)ndr_pull_replUpToDateVectorBlob);
4581 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
4582 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
4583 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
4586 if (ouv.version != 2) {
4587 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
4592 * the new uptodateness vector will at least
4593 * contain 1 entry, one for the source_dsa
4595 * plus optional values from our old vector and the one from the source_dsa
4597 nuv.ctr.ctr2.count = ouv.ctr.ctr2.count;
4598 if (ruv) nuv.ctr.ctr2.count += ruv->count;
4599 nuv.ctr.ctr2.cursors = talloc_array(ar,
4600 struct drsuapi_DsReplicaCursor2,
4601 nuv.ctr.ctr2.count);
4602 if (!nuv.ctr.ctr2.cursors) return replmd_replicated_request_werror(ar, WERR_NOMEM);
4604 /* first copy the old vector */
4605 for (i=0; i < ouv.ctr.ctr2.count; i++) {
4606 nuv.ctr.ctr2.cursors[ni] = ouv.ctr.ctr2.cursors[i];
4607 ni++;
4610 /* get our invocation_id if we have one already attached to the ldb */
4611 our_invocation_id = samdb_ntds_invocation_id(ldb);
4613 /* merge in the source_dsa vector is available */
4614 for (i=0; (ruv && i < ruv->count); i++) {
4615 found = false;
4617 if (our_invocation_id &&
4618 GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
4619 our_invocation_id)) {
4620 continue;
4623 for (j=0; j < ni; j++) {
4624 if (!GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
4625 &nuv.ctr.ctr2.cursors[j].source_dsa_invocation_id)) {
4626 continue;
4629 found = true;
4631 if (ruv->cursors[i].highest_usn > nuv.ctr.ctr2.cursors[j].highest_usn) {
4632 nuv.ctr.ctr2.cursors[j] = ruv->cursors[i];
4634 break;
4637 if (found) continue;
4639 /* if it's not there yet, add it */
4640 nuv.ctr.ctr2.cursors[ni] = ruv->cursors[i];
4641 ni++;
4645 * finally correct the size of the cursors array
4647 nuv.ctr.ctr2.count = ni;
4650 * sort the cursors
4652 TYPESAFE_QSORT(nuv.ctr.ctr2.cursors, nuv.ctr.ctr2.count, drsuapi_DsReplicaCursor2_compare);
4655 * create the change ldb_message
4657 msg = ldb_msg_new(ar);
4658 if (!msg) return replmd_replicated_request_werror(ar, WERR_NOMEM);
4659 msg->dn = ar->search_msg->dn;
4661 ndr_err = ndr_push_struct_blob(&nuv_value, msg, &nuv,
4662 (ndr_push_flags_fn_t)ndr_push_replUpToDateVectorBlob);
4663 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
4664 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
4665 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
4667 ret = ldb_msg_add_value(msg, "replUpToDateVector", &nuv_value, &nuv_el);
4668 if (ret != LDB_SUCCESS) {
4669 return replmd_replicated_request_error(ar, ret);
4671 nuv_el->flags = LDB_FLAG_MOD_REPLACE;
4674 * now create the new repsFrom value from the given repsFromTo1 structure
4676 ZERO_STRUCT(nrf);
4677 nrf.version = 1;
4678 nrf.ctr.ctr1 = *ar->objs->source_dsa;
4679 nrf.ctr.ctr1.last_attempt = now;
4680 nrf.ctr.ctr1.last_success = now;
4681 nrf.ctr.ctr1.result_last_attempt = WERR_OK;
4684 * first see if we already have a repsFrom value for the current source dsa
4685 * if so we'll later replace this value
4687 orf_el = ldb_msg_find_element(ar->search_msg, "repsFrom");
4688 if (orf_el) {
4689 for (i=0; i < orf_el->num_values; i++) {
4690 struct repsFromToBlob *trf;
4692 trf = talloc(ar, struct repsFromToBlob);
4693 if (!trf) return replmd_replicated_request_werror(ar, WERR_NOMEM);
4695 ndr_err = ndr_pull_struct_blob(&orf_el->values[i], trf, trf,
4696 (ndr_pull_flags_fn_t)ndr_pull_repsFromToBlob);
4697 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
4698 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
4699 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
4702 if (trf->version != 1) {
4703 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
4707 * we compare the source dsa objectGUID not the invocation_id
4708 * because we want only one repsFrom value per source dsa
4709 * and when the invocation_id of the source dsa has changed we don't need
4710 * the old repsFrom with the old invocation_id
4712 if (!GUID_equal(&trf->ctr.ctr1.source_dsa_obj_guid,
4713 &ar->objs->source_dsa->source_dsa_obj_guid)) {
4714 talloc_free(trf);
4715 continue;
4718 talloc_free(trf);
4719 nrf_value = &orf_el->values[i];
4720 break;
4724 * copy over all old values to the new ldb_message
4726 ret = ldb_msg_add_empty(msg, "repsFrom", 0, &nrf_el);
4727 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
4728 *nrf_el = *orf_el;
4732 * if we haven't found an old repsFrom value for the current source dsa
4733 * we'll add a new value
4735 if (!nrf_value) {
4736 struct ldb_val zero_value;
4737 ZERO_STRUCT(zero_value);
4738 ret = ldb_msg_add_value(msg, "repsFrom", &zero_value, &nrf_el);
4739 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
4741 nrf_value = &nrf_el->values[nrf_el->num_values - 1];
4744 /* we now fill the value which is already attached to ldb_message */
4745 ndr_err = ndr_push_struct_blob(nrf_value, msg,
4746 &nrf,
4747 (ndr_push_flags_fn_t)ndr_push_repsFromToBlob);
4748 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
4749 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
4750 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
4754 * the ldb_message_element for the attribute, has all the old values and the new one
4755 * so we'll replace the whole attribute with all values
4757 nrf_el->flags = LDB_FLAG_MOD_REPLACE;
4759 if (CHECK_DEBUGLVL(4)) {
4760 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_MODIFY, msg);
4761 DEBUG(4, ("DRS replication uptodate modify message:\n%s\n", s));
4762 talloc_free(s);
4765 /* prepare the ldb_modify() request */
4766 ret = ldb_build_mod_req(&change_req,
4767 ldb,
4769 msg,
4770 ar->controls,
4772 replmd_replicated_uptodate_modify_callback,
4773 ar->req);
4774 LDB_REQ_SET_LOCATION(change_req);
4775 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
4777 return ldb_next_request(ar->module, change_req);
4780 static int replmd_replicated_uptodate_search_callback(struct ldb_request *req,
4781 struct ldb_reply *ares)
4783 struct replmd_replicated_request *ar = talloc_get_type(req->context,
4784 struct replmd_replicated_request);
4785 int ret;
4787 if (!ares) {
4788 return ldb_module_done(ar->req, NULL, NULL,
4789 LDB_ERR_OPERATIONS_ERROR);
4791 if (ares->error != LDB_SUCCESS &&
4792 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
4793 return ldb_module_done(ar->req, ares->controls,
4794 ares->response, ares->error);
4797 switch (ares->type) {
4798 case LDB_REPLY_ENTRY:
4799 ar->search_msg = talloc_steal(ar, ares->message);
4800 break;
4802 case LDB_REPLY_REFERRAL:
4803 /* we ignore referrals */
4804 break;
4806 case LDB_REPLY_DONE:
4807 ret = replmd_replicated_uptodate_modify(ar);
4808 if (ret != LDB_SUCCESS) {
4809 return ldb_module_done(ar->req, NULL, NULL, ret);
4813 talloc_free(ares);
4814 return LDB_SUCCESS;
4818 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar)
4820 struct ldb_context *ldb;
4821 int ret;
4822 static const char *attrs[] = {
4823 "replUpToDateVector",
4824 "repsFrom",
4825 "instanceType",
4826 NULL
4828 struct ldb_request *search_req;
4830 ldb = ldb_module_get_ctx(ar->module);
4831 ar->search_msg = NULL;
4833 ret = ldb_build_search_req(&search_req,
4834 ldb,
4836 ar->objs->partition_dn,
4837 LDB_SCOPE_BASE,
4838 "(objectClass=*)",
4839 attrs,
4840 NULL,
4842 replmd_replicated_uptodate_search_callback,
4843 ar->req);
4844 LDB_REQ_SET_LOCATION(search_req);
4845 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
4847 return ldb_next_request(ar->module, search_req);
4852 static int replmd_extended_replicated_objects(struct ldb_module *module, struct ldb_request *req)
4854 struct ldb_context *ldb;
4855 struct dsdb_extended_replicated_objects *objs;
4856 struct replmd_replicated_request *ar;
4857 struct ldb_control **ctrls;
4858 int ret;
4859 uint32_t i;
4860 struct replmd_private *replmd_private =
4861 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
4862 struct dsdb_control_replicated_update *rep_update;
4864 ldb = ldb_module_get_ctx(module);
4866 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_extended_replicated_objects\n");
4868 objs = talloc_get_type(req->op.extended.data, struct dsdb_extended_replicated_objects);
4869 if (!objs) {
4870 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: invalid extended data\n");
4871 return LDB_ERR_PROTOCOL_ERROR;
4874 if (objs->version != DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION) {
4875 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: extended data invalid version [%u != %u]\n",
4876 objs->version, DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION);
4877 return LDB_ERR_PROTOCOL_ERROR;
4880 ar = replmd_ctx_init(module, req);
4881 if (!ar)
4882 return LDB_ERR_OPERATIONS_ERROR;
4884 /* Set the flags to have the replmd_op_callback run over the full set of objects */
4885 ar->apply_mode = true;
4886 ar->objs = objs;
4887 ar->schema = dsdb_get_schema(ldb, ar);
4888 if (!ar->schema) {
4889 ldb_debug_set(ldb, LDB_DEBUG_FATAL, "replmd_ctx_init: no loaded schema found\n");
4890 talloc_free(ar);
4891 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
4892 return LDB_ERR_CONSTRAINT_VIOLATION;
4895 ctrls = req->controls;
4897 if (req->controls) {
4898 req->controls = talloc_memdup(ar, req->controls,
4899 talloc_get_size(req->controls));
4900 if (!req->controls) return replmd_replicated_request_werror(ar, WERR_NOMEM);
4903 /* This allows layers further down to know if a change came in
4904 over replication and what the replication flags were */
4905 rep_update = talloc_zero(ar, struct dsdb_control_replicated_update);
4906 if (rep_update == NULL) {
4907 return ldb_module_oom(module);
4909 rep_update->dsdb_repl_flags = objs->dsdb_repl_flags;
4911 ret = ldb_request_add_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID, false, rep_update);
4912 if (ret != LDB_SUCCESS) {
4913 return ret;
4916 /* If this change contained linked attributes in the body
4917 * (rather than in the links section) we need to update
4918 * backlinks in linked_attributes */
4919 ret = ldb_request_add_control(req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
4920 if (ret != LDB_SUCCESS) {
4921 return ret;
4924 ar->controls = req->controls;
4925 req->controls = ctrls;
4927 DEBUG(4,("linked_attributes_count=%u\n", objs->linked_attributes_count));
4929 /* save away the linked attributes for the end of the
4930 transaction */
4931 for (i=0; i<ar->objs->linked_attributes_count; i++) {
4932 struct la_entry *la_entry;
4934 if (replmd_private->la_ctx == NULL) {
4935 replmd_private->la_ctx = talloc_new(replmd_private);
4937 la_entry = talloc(replmd_private->la_ctx, struct la_entry);
4938 if (la_entry == NULL) {
4939 ldb_oom(ldb);
4940 return LDB_ERR_OPERATIONS_ERROR;
4942 la_entry->la = talloc(la_entry, struct drsuapi_DsReplicaLinkedAttribute);
4943 if (la_entry->la == NULL) {
4944 talloc_free(la_entry);
4945 ldb_oom(ldb);
4946 return LDB_ERR_OPERATIONS_ERROR;
4948 *la_entry->la = ar->objs->linked_attributes[i];
4950 /* we need to steal the non-scalars so they stay
4951 around until the end of the transaction */
4952 talloc_steal(la_entry->la, la_entry->la->identifier);
4953 talloc_steal(la_entry->la, la_entry->la->value.blob);
4955 DLIST_ADD(replmd_private->la_list, la_entry);
4958 return replmd_replicated_apply_next(ar);
4962 process one linked attribute structure
4964 static int replmd_process_linked_attribute(struct ldb_module *module,
4965 struct la_entry *la_entry,
4966 struct ldb_request *parent)
4968 struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
4969 struct ldb_context *ldb = ldb_module_get_ctx(module);
4970 struct ldb_message *msg;
4971 TALLOC_CTX *tmp_ctx = talloc_new(la_entry);
4972 const struct dsdb_schema *schema = dsdb_get_schema(ldb, tmp_ctx);
4973 int ret;
4974 const struct dsdb_attribute *attr;
4975 struct dsdb_dn *dsdb_dn;
4976 uint64_t seq_num = 0;
4977 struct ldb_message_element *old_el;
4978 WERROR status;
4979 time_t t = time(NULL);
4980 struct ldb_result *res;
4981 const char *attrs[2];
4982 struct parsed_dn *pdn_list, *pdn;
4983 struct GUID guid = GUID_zero();
4984 NTSTATUS ntstatus;
4985 bool active = (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)?true:false;
4986 const struct GUID *our_invocation_id;
4989 linked_attributes[0]:
4990 &objs->linked_attributes[i]: struct drsuapi_DsReplicaLinkedAttribute
4991 identifier : *
4992 identifier: struct drsuapi_DsReplicaObjectIdentifier
4993 __ndr_size : 0x0000003a (58)
4994 __ndr_size_sid : 0x00000000 (0)
4995 guid : 8e95b6a9-13dd-4158-89db-3220a5be5cc7
4996 sid : S-0-0
4997 __ndr_size_dn : 0x00000000 (0)
4998 dn : ''
4999 attid : DRSUAPI_ATTID_member (0x1F)
5000 value: struct drsuapi_DsAttributeValue
5001 __ndr_size : 0x0000007e (126)
5002 blob : *
5003 blob : DATA_BLOB length=126
5004 flags : 0x00000001 (1)
5005 1: DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE
5006 originating_add_time : Wed Sep 2 22:20:01 2009 EST
5007 meta_data: struct drsuapi_DsReplicaMetaData
5008 version : 0x00000015 (21)
5009 originating_change_time : Wed Sep 2 23:39:07 2009 EST
5010 originating_invocation_id: 794640f3-18cf-40ee-a211-a93992b67a64
5011 originating_usn : 0x000000000001e19c (123292)
5013 (for cases where the link is to a normal DN)
5014 &target: struct drsuapi_DsReplicaObjectIdentifier3
5015 __ndr_size : 0x0000007e (126)
5016 __ndr_size_sid : 0x0000001c (28)
5017 guid : 7639e594-db75-4086-b0d4-67890ae46031
5018 sid : S-1-5-21-2848215498-2472035911-1947525656-19924
5019 __ndr_size_dn : 0x00000022 (34)
5020 dn : 'CN=UOne,OU=TestOU,DC=vsofs8,DC=com'
5023 /* find the attribute being modified */
5024 attr = dsdb_attribute_by_attributeID_id(schema, la->attid);
5025 if (attr == NULL) {
5026 DEBUG(0, (__location__ ": Unable to find attributeID 0x%x\n", la->attid));
5027 talloc_free(tmp_ctx);
5028 return LDB_ERR_OPERATIONS_ERROR;
5031 attrs[0] = attr->lDAPDisplayName;
5032 attrs[1] = NULL;
5034 /* get the existing message from the db for the object with
5035 this GUID, returning attribute being modified. We will then
5036 use this msg as the basis for a modify call */
5037 ret = dsdb_module_search(module, tmp_ctx, &res, NULL, LDB_SCOPE_SUBTREE, attrs,
5038 DSDB_FLAG_NEXT_MODULE |
5039 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
5040 DSDB_SEARCH_SHOW_RECYCLED |
5041 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
5042 DSDB_SEARCH_REVEAL_INTERNALS,
5043 parent,
5044 "objectGUID=%s", GUID_string(tmp_ctx, &la->identifier->guid));
5045 if (ret != LDB_SUCCESS) {
5046 talloc_free(tmp_ctx);
5047 return ret;
5049 if (res->count != 1) {
5050 ldb_asprintf_errstring(ldb, "DRS linked attribute for GUID %s - DN not found",
5051 GUID_string(tmp_ctx, &la->identifier->guid));
5052 talloc_free(tmp_ctx);
5053 return LDB_ERR_NO_SUCH_OBJECT;
5055 msg = res->msgs[0];
5057 if (msg->num_elements == 0) {
5058 ret = ldb_msg_add_empty(msg, attr->lDAPDisplayName, LDB_FLAG_MOD_REPLACE, &old_el);
5059 if (ret != LDB_SUCCESS) {
5060 ldb_module_oom(module);
5061 talloc_free(tmp_ctx);
5062 return LDB_ERR_OPERATIONS_ERROR;
5064 } else {
5065 old_el = &msg->elements[0];
5066 old_el->flags = LDB_FLAG_MOD_REPLACE;
5069 /* parse the existing links */
5070 ret = get_parsed_dns(module, tmp_ctx, old_el, &pdn_list, attr->syntax->ldap_oid, parent);
5071 if (ret != LDB_SUCCESS) {
5072 talloc_free(tmp_ctx);
5073 return ret;
5076 /* get our invocationId */
5077 our_invocation_id = samdb_ntds_invocation_id(ldb);
5078 if (!our_invocation_id) {
5079 ldb_debug_set(ldb, LDB_DEBUG_ERROR, __location__ ": unable to find invocationId\n");
5080 talloc_free(tmp_ctx);
5081 return LDB_ERR_OPERATIONS_ERROR;
5084 ret = replmd_check_upgrade_links(pdn_list, old_el->num_values, old_el, our_invocation_id);
5085 if (ret != LDB_SUCCESS) {
5086 talloc_free(tmp_ctx);
5087 return ret;
5090 status = dsdb_dn_la_from_blob(ldb, attr, schema, tmp_ctx, la->value.blob, &dsdb_dn);
5091 if (!W_ERROR_IS_OK(status)) {
5092 ldb_asprintf_errstring(ldb, "Failed to parsed linked attribute blob for %s on %s - %s\n",
5093 old_el->name, ldb_dn_get_linearized(msg->dn), win_errstr(status));
5094 return LDB_ERR_OPERATIONS_ERROR;
5097 ntstatus = dsdb_get_extended_dn_guid(dsdb_dn->dn, &guid, "GUID");
5098 if (!NT_STATUS_IS_OK(ntstatus) && active) {
5099 ldb_asprintf_errstring(ldb, "Failed to find GUID in linked attribute blob for %s on %s from %s",
5100 old_el->name,
5101 ldb_dn_get_linearized(dsdb_dn->dn),
5102 ldb_dn_get_linearized(msg->dn));
5103 return LDB_ERR_OPERATIONS_ERROR;
5106 /* re-resolve the DN by GUID, as the DRS server may give us an
5107 old DN value */
5108 ret = dsdb_module_dn_by_guid(module, dsdb_dn, &guid, &dsdb_dn->dn, parent);
5109 if (ret != LDB_SUCCESS) {
5110 DEBUG(2,(__location__ ": WARNING: Failed to re-resolve GUID %s - using %s\n",
5111 GUID_string(tmp_ctx, &guid),
5112 ldb_dn_get_linearized(dsdb_dn->dn)));
5115 /* see if this link already exists */
5116 pdn = parsed_dn_find(pdn_list, old_el->num_values, &guid, dsdb_dn->dn);
5117 if (pdn != NULL) {
5118 /* see if this update is newer than what we have already */
5119 struct GUID invocation_id = GUID_zero();
5120 uint32_t version = 0;
5121 uint32_t originating_usn = 0;
5122 NTTIME change_time = 0;
5123 uint32_t rmd_flags = dsdb_dn_rmd_flags(pdn->dsdb_dn->dn);
5125 dsdb_get_extended_dn_guid(pdn->dsdb_dn->dn, &invocation_id, "RMD_INVOCID");
5126 dsdb_get_extended_dn_uint32(pdn->dsdb_dn->dn, &version, "RMD_VERSION");
5127 dsdb_get_extended_dn_uint32(pdn->dsdb_dn->dn, &originating_usn, "RMD_ORIGINATING_USN");
5128 dsdb_get_extended_dn_nttime(pdn->dsdb_dn->dn, &change_time, "RMD_CHANGETIME");
5130 if (!replmd_update_is_newer(&invocation_id,
5131 &la->meta_data.originating_invocation_id,
5132 version,
5133 la->meta_data.version,
5134 change_time,
5135 la->meta_data.originating_change_time)) {
5136 DEBUG(3,("Discarding older DRS linked attribute update to %s on %s from %s\n",
5137 old_el->name, ldb_dn_get_linearized(msg->dn),
5138 GUID_string(tmp_ctx, &la->meta_data.originating_invocation_id)));
5139 talloc_free(tmp_ctx);
5140 return LDB_SUCCESS;
5143 /* get a seq_num for this change */
5144 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
5145 if (ret != LDB_SUCCESS) {
5146 talloc_free(tmp_ctx);
5147 return ret;
5150 if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
5151 /* remove the existing backlink */
5152 ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid, false, attr, false);
5153 if (ret != LDB_SUCCESS) {
5154 talloc_free(tmp_ctx);
5155 return ret;
5159 ret = replmd_update_la_val(tmp_ctx, pdn->v, dsdb_dn, pdn->dsdb_dn,
5160 &la->meta_data.originating_invocation_id,
5161 la->meta_data.originating_usn, seq_num,
5162 la->meta_data.originating_change_time,
5163 la->meta_data.version,
5164 !active);
5165 if (ret != LDB_SUCCESS) {
5166 talloc_free(tmp_ctx);
5167 return ret;
5170 if (active) {
5171 /* add the new backlink */
5172 ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid, true, attr, false);
5173 if (ret != LDB_SUCCESS) {
5174 talloc_free(tmp_ctx);
5175 return ret;
5178 } else {
5179 /* get a seq_num for this change */
5180 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
5181 if (ret != LDB_SUCCESS) {
5182 talloc_free(tmp_ctx);
5183 return ret;
5186 old_el->values = talloc_realloc(msg->elements, old_el->values,
5187 struct ldb_val, old_el->num_values+1);
5188 if (!old_el->values) {
5189 ldb_module_oom(module);
5190 return LDB_ERR_OPERATIONS_ERROR;
5192 old_el->num_values++;
5194 ret = replmd_build_la_val(tmp_ctx, &old_el->values[old_el->num_values-1], dsdb_dn,
5195 &la->meta_data.originating_invocation_id,
5196 la->meta_data.originating_usn, seq_num,
5197 la->meta_data.originating_change_time,
5198 la->meta_data.version,
5199 (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)?false:true);
5200 if (ret != LDB_SUCCESS) {
5201 talloc_free(tmp_ctx);
5202 return ret;
5205 if (active) {
5206 ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid,
5207 true, attr, false);
5208 if (ret != LDB_SUCCESS) {
5209 talloc_free(tmp_ctx);
5210 return ret;
5215 /* we only change whenChanged and uSNChanged if the seq_num
5216 has changed */
5217 ret = add_time_element(msg, "whenChanged", t);
5218 if (ret != LDB_SUCCESS) {
5219 talloc_free(tmp_ctx);
5220 ldb_operr(ldb);
5221 return ret;
5224 ret = add_uint64_element(ldb, msg, "uSNChanged", seq_num);
5225 if (ret != LDB_SUCCESS) {
5226 talloc_free(tmp_ctx);
5227 ldb_operr(ldb);
5228 return ret;
5231 old_el = ldb_msg_find_element(msg, attr->lDAPDisplayName);
5232 if (old_el == NULL) {
5233 talloc_free(tmp_ctx);
5234 return ldb_operr(ldb);
5237 ret = dsdb_check_single_valued_link(attr, old_el);
5238 if (ret != LDB_SUCCESS) {
5239 talloc_free(tmp_ctx);
5240 return ret;
5243 old_el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
5245 ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE, parent);
5246 if (ret != LDB_SUCCESS) {
5247 ldb_debug(ldb, LDB_DEBUG_WARNING, "Failed to apply linked attribute change '%s'\n%s\n",
5248 ldb_errstring(ldb),
5249 ldb_ldif_message_string(ldb, tmp_ctx, LDB_CHANGETYPE_MODIFY, msg));
5250 talloc_free(tmp_ctx);
5251 return ret;
5254 talloc_free(tmp_ctx);
5256 return ret;
5259 static int replmd_extended(struct ldb_module *module, struct ldb_request *req)
5261 if (strcmp(req->op.extended.oid, DSDB_EXTENDED_REPLICATED_OBJECTS_OID) == 0) {
5262 return replmd_extended_replicated_objects(module, req);
5265 return ldb_next_request(module, req);
5270 we hook into the transaction operations to allow us to
5271 perform the linked attribute updates at the end of the whole
5272 transaction. This allows a forward linked attribute to be created
5273 before the object is created. During a vampire, w2k8 sends us linked
5274 attributes before the objects they are part of.
5276 static int replmd_start_transaction(struct ldb_module *module)
5278 /* create our private structure for this transaction */
5279 struct replmd_private *replmd_private = talloc_get_type(ldb_module_get_private(module),
5280 struct replmd_private);
5281 replmd_txn_cleanup(replmd_private);
5283 /* free any leftover mod_usn records from cancelled
5284 transactions */
5285 while (replmd_private->ncs) {
5286 struct nc_entry *e = replmd_private->ncs;
5287 DLIST_REMOVE(replmd_private->ncs, e);
5288 talloc_free(e);
5291 return ldb_next_start_trans(module);
5295 on prepare commit we loop over our queued la_context structures and
5296 apply each of them
5298 static int replmd_prepare_commit(struct ldb_module *module)
5300 struct replmd_private *replmd_private =
5301 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
5302 struct la_entry *la, *prev;
5303 struct la_backlink *bl;
5304 int ret;
5306 /* walk the list backwards, to do the first entry first, as we
5307 * added the entries with DLIST_ADD() which puts them at the
5308 * start of the list */
5309 for (la = DLIST_TAIL(replmd_private->la_list); la; la=prev) {
5310 prev = DLIST_PREV(la);
5311 DLIST_REMOVE(replmd_private->la_list, la);
5312 ret = replmd_process_linked_attribute(module, la, NULL);
5313 if (ret != LDB_SUCCESS) {
5314 replmd_txn_cleanup(replmd_private);
5315 return ret;
5319 /* process our backlink list, creating and deleting backlinks
5320 as necessary */
5321 for (bl=replmd_private->la_backlinks; bl; bl=bl->next) {
5322 ret = replmd_process_backlink(module, bl, NULL);
5323 if (ret != LDB_SUCCESS) {
5324 replmd_txn_cleanup(replmd_private);
5325 return ret;
5329 replmd_txn_cleanup(replmd_private);
5331 /* possibly change @REPLCHANGED */
5332 ret = replmd_notify_store(module, NULL);
5333 if (ret != LDB_SUCCESS) {
5334 return ret;
5337 return ldb_next_prepare_commit(module);
5340 static int replmd_del_transaction(struct ldb_module *module)
5342 struct replmd_private *replmd_private =
5343 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
5344 replmd_txn_cleanup(replmd_private);
5346 return ldb_next_del_trans(module);
5350 static const struct ldb_module_ops ldb_repl_meta_data_module_ops = {
5351 .name = "repl_meta_data",
5352 .init_context = replmd_init,
5353 .add = replmd_add,
5354 .modify = replmd_modify,
5355 .rename = replmd_rename,
5356 .del = replmd_delete,
5357 .extended = replmd_extended,
5358 .start_transaction = replmd_start_transaction,
5359 .prepare_commit = replmd_prepare_commit,
5360 .del_transaction = replmd_del_transaction,
5363 int ldb_repl_meta_data_module_init(const char *version)
5365 LDB_MODULE_CHECK_VERSION(version);
5366 return ldb_register_module(&ldb_repl_meta_data_module_ops);