s4-dsdb: Use parent_object_guid to find the correct parent for new objects
[Samba.git] / source4 / dsdb / samdb / ldb_modules / repl_meta_data.c
blob1dc7ea057c618fe5918b1c6d40e643fd183bda40
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 enum urgent_situation {
98 REPL_URGENT_ON_CREATE = 1,
99 REPL_URGENT_ON_UPDATE = 2,
100 REPL_URGENT_ON_DELETE = 4
104 static const struct {
105 const char *update_name;
106 enum urgent_situation repl_situation;
107 } urgent_objects[] = {
108 {"nTDSDSA", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE)},
109 {"crossRef", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE)},
110 {"attributeSchema", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
111 {"classSchema", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
112 {"secret", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
113 {"rIDManager", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
114 {NULL, 0}
117 /* Attributes looked for when updating or deleting, to check for a urgent replication needed */
118 static const char *urgent_attrs[] = {
119 "lockoutTime",
120 "pwdLastSet",
121 "userAccountControl",
122 NULL
126 static bool replmd_check_urgent_objectclass(const struct ldb_message_element *objectclass_el,
127 enum urgent_situation situation)
129 unsigned int i, j;
130 for (i=0; urgent_objects[i].update_name; i++) {
132 if ((situation & urgent_objects[i].repl_situation) == 0) {
133 continue;
136 for (j=0; j<objectclass_el->num_values; j++) {
137 const struct ldb_val *v = &objectclass_el->values[j];
138 if (ldb_attr_cmp((const char *)v->data, urgent_objects[i].update_name) == 0) {
139 return true;
143 return false;
146 static bool replmd_check_urgent_attribute(const struct ldb_message_element *el)
148 if (ldb_attr_in_list(urgent_attrs, el->name)) {
149 return true;
151 return false;
155 static int replmd_replicated_apply_next(struct replmd_replicated_request *ar);
158 initialise the module
159 allocate the private structure and build the list
160 of partition DNs for use by replmd_notify()
162 static int replmd_init(struct ldb_module *module)
164 struct replmd_private *replmd_private;
165 struct ldb_context *ldb = ldb_module_get_ctx(module);
167 replmd_private = talloc_zero(module, struct replmd_private);
168 if (replmd_private == NULL) {
169 ldb_oom(ldb);
170 return LDB_ERR_OPERATIONS_ERROR;
172 ldb_module_set_private(module, replmd_private);
174 return ldb_next_init(module);
178 cleanup our per-transaction contexts
180 static void replmd_txn_cleanup(struct replmd_private *replmd_private)
182 talloc_free(replmd_private->la_ctx);
183 replmd_private->la_list = NULL;
184 replmd_private->la_ctx = NULL;
186 talloc_free(replmd_private->bl_ctx);
187 replmd_private->la_backlinks = NULL;
188 replmd_private->bl_ctx = NULL;
192 struct la_backlink {
193 struct la_backlink *next, *prev;
194 const char *attr_name;
195 struct GUID forward_guid, target_guid;
196 bool active;
200 process a backlinks we accumulated during a transaction, adding and
201 deleting the backlinks from the target objects
203 static int replmd_process_backlink(struct ldb_module *module, struct la_backlink *bl, struct ldb_request *parent)
205 struct ldb_dn *target_dn, *source_dn;
206 int ret;
207 struct ldb_context *ldb = ldb_module_get_ctx(module);
208 struct ldb_message *msg;
209 TALLOC_CTX *tmp_ctx = talloc_new(bl);
210 char *dn_string;
213 - find DN of target
214 - find DN of source
215 - construct ldb_message
216 - either an add or a delete
218 ret = dsdb_module_dn_by_guid(module, tmp_ctx, &bl->target_guid, &target_dn, parent);
219 if (ret != LDB_SUCCESS) {
220 DEBUG(2,(__location__ ": WARNING: Failed to find target DN for linked attribute with GUID %s\n",
221 GUID_string(bl, &bl->target_guid)));
222 return LDB_SUCCESS;
225 ret = dsdb_module_dn_by_guid(module, tmp_ctx, &bl->forward_guid, &source_dn, parent);
226 if (ret != LDB_SUCCESS) {
227 ldb_asprintf_errstring(ldb, "Failed to find source DN for linked attribute with GUID %s\n",
228 GUID_string(bl, &bl->forward_guid));
229 talloc_free(tmp_ctx);
230 return ret;
233 msg = ldb_msg_new(tmp_ctx);
234 if (msg == NULL) {
235 ldb_module_oom(module);
236 talloc_free(tmp_ctx);
237 return LDB_ERR_OPERATIONS_ERROR;
240 /* construct a ldb_message for adding/deleting the backlink */
241 msg->dn = target_dn;
242 dn_string = ldb_dn_get_extended_linearized(tmp_ctx, source_dn, 1);
243 if (!dn_string) {
244 ldb_module_oom(module);
245 talloc_free(tmp_ctx);
246 return LDB_ERR_OPERATIONS_ERROR;
248 ret = ldb_msg_add_steal_string(msg, bl->attr_name, dn_string);
249 if (ret != LDB_SUCCESS) {
250 talloc_free(tmp_ctx);
251 return ret;
253 msg->elements[0].flags = bl->active?LDB_FLAG_MOD_ADD:LDB_FLAG_MOD_DELETE;
255 /* a backlink should never be single valued. Unfortunately the
256 exchange schema has a attribute
257 msExchBridgeheadedLocalConnectorsDNBL which is single
258 valued and a backlink. We need to cope with that by
259 ignoring the single value flag */
260 msg->elements[0].flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
262 ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE, parent);
263 if (ret == LDB_ERR_NO_SUCH_ATTRIBUTE && !bl->active) {
264 /* we allow LDB_ERR_NO_SUCH_ATTRIBUTE as success to
265 cope with possible corruption where the backlink has
266 already been removed */
267 DEBUG(3,("WARNING: backlink from %s already removed from %s - %s\n",
268 ldb_dn_get_linearized(target_dn),
269 ldb_dn_get_linearized(source_dn),
270 ldb_errstring(ldb)));
271 ret = LDB_SUCCESS;
272 } else if (ret != LDB_SUCCESS) {
273 ldb_asprintf_errstring(ldb, "Failed to %s backlink from %s to %s - %s",
274 bl->active?"add":"remove",
275 ldb_dn_get_linearized(source_dn),
276 ldb_dn_get_linearized(target_dn),
277 ldb_errstring(ldb));
278 talloc_free(tmp_ctx);
279 return ret;
281 talloc_free(tmp_ctx);
282 return ret;
286 add a backlink to the list of backlinks to add/delete in the prepare
287 commit
289 static int replmd_add_backlink(struct ldb_module *module, const struct dsdb_schema *schema,
290 struct GUID *forward_guid, struct GUID *target_guid,
291 bool active, const struct dsdb_attribute *schema_attr, bool immediate)
293 const struct dsdb_attribute *target_attr;
294 struct la_backlink *bl;
295 struct replmd_private *replmd_private =
296 talloc_get_type_abort(ldb_module_get_private(module), struct replmd_private);
298 target_attr = dsdb_attribute_by_linkID(schema, schema_attr->linkID ^ 1);
299 if (!target_attr) {
301 * windows 2003 has a broken schema where the
302 * definition of msDS-IsDomainFor is missing (which is
303 * supposed to be the backlink of the
304 * msDS-HasDomainNCs attribute
306 return LDB_SUCCESS;
309 /* see if its already in the list */
310 for (bl=replmd_private->la_backlinks; bl; bl=bl->next) {
311 if (GUID_equal(forward_guid, &bl->forward_guid) &&
312 GUID_equal(target_guid, &bl->target_guid) &&
313 (target_attr->lDAPDisplayName == bl->attr_name ||
314 strcmp(target_attr->lDAPDisplayName, bl->attr_name) == 0)) {
315 break;
319 if (bl) {
320 /* we found an existing one */
321 if (bl->active == active) {
322 return LDB_SUCCESS;
324 DLIST_REMOVE(replmd_private->la_backlinks, bl);
325 talloc_free(bl);
326 return LDB_SUCCESS;
329 if (replmd_private->bl_ctx == NULL) {
330 replmd_private->bl_ctx = talloc_new(replmd_private);
331 if (replmd_private->bl_ctx == NULL) {
332 ldb_module_oom(module);
333 return LDB_ERR_OPERATIONS_ERROR;
337 /* its a new one */
338 bl = talloc(replmd_private->bl_ctx, struct la_backlink);
339 if (bl == NULL) {
340 ldb_module_oom(module);
341 return LDB_ERR_OPERATIONS_ERROR;
344 /* Ensure the schema does not go away before the bl->attr_name is used */
345 if (!talloc_reference(bl, schema)) {
346 talloc_free(bl);
347 ldb_module_oom(module);
348 return LDB_ERR_OPERATIONS_ERROR;
351 bl->attr_name = target_attr->lDAPDisplayName;
352 bl->forward_guid = *forward_guid;
353 bl->target_guid = *target_guid;
354 bl->active = active;
356 /* the caller may ask for this backlink to be processed
357 immediately */
358 if (immediate) {
359 int ret = replmd_process_backlink(module, bl, NULL);
360 talloc_free(bl);
361 return ret;
364 DLIST_ADD(replmd_private->la_backlinks, bl);
366 return LDB_SUCCESS;
371 * Callback for most write operations in this module:
373 * notify the repl task that a object has changed. The notifies are
374 * gathered up in the replmd_private structure then written to the
375 * @REPLCHANGED object in each partition during the prepare_commit
377 static int replmd_op_callback(struct ldb_request *req, struct ldb_reply *ares)
379 int ret;
380 struct replmd_replicated_request *ac =
381 talloc_get_type_abort(req->context, struct replmd_replicated_request);
382 struct replmd_private *replmd_private =
383 talloc_get_type_abort(ldb_module_get_private(ac->module), struct replmd_private);
384 struct nc_entry *modified_partition;
385 struct ldb_control *partition_ctrl;
386 const struct dsdb_control_current_partition *partition;
388 struct ldb_control **controls;
390 partition_ctrl = ldb_reply_get_control(ares, DSDB_CONTROL_CURRENT_PARTITION_OID);
392 controls = ares->controls;
393 if (ldb_request_get_control(ac->req,
394 DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
396 * Remove the current partition control from what we pass up
397 * the chain if it hasn't been requested manually.
399 controls = ldb_controls_except_specified(ares->controls, ares,
400 partition_ctrl);
403 if (ares->error != LDB_SUCCESS) {
404 DEBUG(5,("%s failure. Error is: %s\n", __FUNCTION__, ldb_strerror(ares->error)));
405 return ldb_module_done(ac->req, controls,
406 ares->response, ares->error);
409 if (ares->type != LDB_REPLY_DONE) {
410 ldb_set_errstring(ldb_module_get_ctx(ac->module), "Invalid reply type for notify\n!");
411 return ldb_module_done(ac->req, NULL,
412 NULL, LDB_ERR_OPERATIONS_ERROR);
415 if (!partition_ctrl) {
416 ldb_set_errstring(ldb_module_get_ctx(ac->module),"No partition control on reply");
417 return ldb_module_done(ac->req, NULL,
418 NULL, LDB_ERR_OPERATIONS_ERROR);
421 partition = talloc_get_type_abort(partition_ctrl->data,
422 struct dsdb_control_current_partition);
424 if (ac->seq_num > 0) {
425 for (modified_partition = replmd_private->ncs; modified_partition;
426 modified_partition = modified_partition->next) {
427 if (ldb_dn_compare(modified_partition->dn, partition->dn) == 0) {
428 break;
432 if (modified_partition == NULL) {
433 modified_partition = talloc_zero(replmd_private, struct nc_entry);
434 if (!modified_partition) {
435 ldb_oom(ldb_module_get_ctx(ac->module));
436 return ldb_module_done(ac->req, NULL,
437 NULL, LDB_ERR_OPERATIONS_ERROR);
439 modified_partition->dn = ldb_dn_copy(modified_partition, partition->dn);
440 if (!modified_partition->dn) {
441 ldb_oom(ldb_module_get_ctx(ac->module));
442 return ldb_module_done(ac->req, NULL,
443 NULL, LDB_ERR_OPERATIONS_ERROR);
445 DLIST_ADD(replmd_private->ncs, modified_partition);
448 if (ac->seq_num > modified_partition->mod_usn) {
449 modified_partition->mod_usn = ac->seq_num;
450 if (ac->is_urgent) {
451 modified_partition->mod_usn_urgent = ac->seq_num;
456 if (ac->apply_mode) {
457 talloc_free(ares);
458 ac->index_current++;
460 ret = replmd_replicated_apply_next(ac);
461 if (ret != LDB_SUCCESS) {
462 return ldb_module_done(ac->req, NULL, NULL, ret);
464 return ret;
465 } else {
466 /* free the partition control container here, for the
467 * common path. Other cases will have it cleaned up
468 * eventually with the ares */
469 talloc_free(partition_ctrl);
470 return ldb_module_done(ac->req, controls,
471 ares->response, LDB_SUCCESS);
477 * update a @REPLCHANGED record in each partition if there have been
478 * any writes of replicated data in the partition
480 static int replmd_notify_store(struct ldb_module *module, struct ldb_request *parent)
482 struct replmd_private *replmd_private =
483 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
485 while (replmd_private->ncs) {
486 int ret;
487 struct nc_entry *modified_partition = replmd_private->ncs;
489 ret = dsdb_module_save_partition_usn(module, modified_partition->dn,
490 modified_partition->mod_usn,
491 modified_partition->mod_usn_urgent, parent);
492 if (ret != LDB_SUCCESS) {
493 DEBUG(0,(__location__ ": Failed to save partition uSN for %s\n",
494 ldb_dn_get_linearized(modified_partition->dn)));
495 return ret;
497 DLIST_REMOVE(replmd_private->ncs, modified_partition);
498 talloc_free(modified_partition);
501 return LDB_SUCCESS;
506 created a replmd_replicated_request context
508 static struct replmd_replicated_request *replmd_ctx_init(struct ldb_module *module,
509 struct ldb_request *req)
511 struct ldb_context *ldb;
512 struct replmd_replicated_request *ac;
514 ldb = ldb_module_get_ctx(module);
516 ac = talloc_zero(req, struct replmd_replicated_request);
517 if (ac == NULL) {
518 ldb_oom(ldb);
519 return NULL;
522 ac->module = module;
523 ac->req = req;
525 ac->schema = dsdb_get_schema(ldb, ac);
526 if (!ac->schema) {
527 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
528 "replmd_modify: no dsdb_schema loaded");
529 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
530 return NULL;
533 return ac;
537 add a time element to a record
539 static int add_time_element(struct ldb_message *msg, const char *attr, time_t t)
541 struct ldb_message_element *el;
542 char *s;
543 int ret;
545 if (ldb_msg_find_element(msg, attr) != NULL) {
546 return LDB_SUCCESS;
549 s = ldb_timestring(msg, t);
550 if (s == NULL) {
551 return LDB_ERR_OPERATIONS_ERROR;
554 ret = ldb_msg_add_string(msg, attr, s);
555 if (ret != LDB_SUCCESS) {
556 return ret;
559 el = ldb_msg_find_element(msg, attr);
560 /* always set as replace. This works because on add ops, the flag
561 is ignored */
562 el->flags = LDB_FLAG_MOD_REPLACE;
564 return LDB_SUCCESS;
568 add a uint64_t element to a record
570 static int add_uint64_element(struct ldb_context *ldb, struct ldb_message *msg,
571 const char *attr, uint64_t v)
573 struct ldb_message_element *el;
574 int ret;
576 if (ldb_msg_find_element(msg, attr) != NULL) {
577 return LDB_SUCCESS;
580 ret = samdb_msg_add_uint64(ldb, msg, msg, attr, v);
581 if (ret != LDB_SUCCESS) {
582 return ret;
585 el = ldb_msg_find_element(msg, attr);
586 /* always set as replace. This works because on add ops, the flag
587 is ignored */
588 el->flags = LDB_FLAG_MOD_REPLACE;
590 return LDB_SUCCESS;
593 static int replmd_replPropertyMetaData1_attid_sort(const struct replPropertyMetaData1 *m1,
594 const struct replPropertyMetaData1 *m2,
595 const uint32_t *rdn_attid)
597 if (m1->attid == m2->attid) {
598 return 0;
602 * the rdn attribute should be at the end!
603 * so we need to return a value greater than zero
604 * which means m1 is greater than m2
606 if (m1->attid == *rdn_attid) {
607 return 1;
611 * the rdn attribute should be at the end!
612 * so we need to return a value less than zero
613 * which means m2 is greater than m1
615 if (m2->attid == *rdn_attid) {
616 return -1;
619 return m1->attid > m2->attid ? 1 : -1;
622 static int replmd_replPropertyMetaDataCtr1_sort(struct replPropertyMetaDataCtr1 *ctr1,
623 const struct dsdb_schema *schema,
624 struct ldb_dn *dn)
626 const char *rdn_name;
627 const struct dsdb_attribute *rdn_sa;
629 rdn_name = ldb_dn_get_rdn_name(dn);
630 if (!rdn_name) {
631 DEBUG(0,(__location__ ": No rDN for %s?\n", ldb_dn_get_linearized(dn)));
632 return LDB_ERR_OPERATIONS_ERROR;
635 rdn_sa = dsdb_attribute_by_lDAPDisplayName(schema, rdn_name);
636 if (rdn_sa == NULL) {
637 DEBUG(0,(__location__ ": No sa found for rDN %s for %s\n", rdn_name, ldb_dn_get_linearized(dn)));
638 return LDB_ERR_OPERATIONS_ERROR;
641 DEBUG(6,("Sorting rpmd with attid exception %u rDN=%s DN=%s\n",
642 rdn_sa->attributeID_id, rdn_name, ldb_dn_get_linearized(dn)));
644 LDB_TYPESAFE_QSORT(ctr1->array, ctr1->count, &rdn_sa->attributeID_id, replmd_replPropertyMetaData1_attid_sort);
646 return LDB_SUCCESS;
649 static int replmd_ldb_message_element_attid_sort(const struct ldb_message_element *e1,
650 const struct ldb_message_element *e2,
651 const struct dsdb_schema *schema)
653 const struct dsdb_attribute *a1;
654 const struct dsdb_attribute *a2;
657 * TODO: make this faster by caching the dsdb_attribute pointer
658 * on the ldb_messag_element
661 a1 = dsdb_attribute_by_lDAPDisplayName(schema, e1->name);
662 a2 = dsdb_attribute_by_lDAPDisplayName(schema, e2->name);
665 * TODO: remove this check, we should rely on e1 and e2 having valid attribute names
666 * in the schema
668 if (!a1 || !a2) {
669 return strcasecmp(e1->name, e2->name);
671 if (a1->attributeID_id == a2->attributeID_id) {
672 return 0;
674 return a1->attributeID_id > a2->attributeID_id ? 1 : -1;
677 static void replmd_ldb_message_sort(struct ldb_message *msg,
678 const struct dsdb_schema *schema)
680 LDB_TYPESAFE_QSORT(msg->elements, msg->num_elements, schema, replmd_ldb_message_element_attid_sort);
683 static int replmd_build_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
684 const struct GUID *invocation_id, uint64_t seq_num,
685 uint64_t local_usn, NTTIME nttime, uint32_t version, bool deleted);
689 fix up linked attributes in replmd_add.
690 This involves setting up the right meta-data in extended DN
691 components, and creating backlinks to the object
693 static int replmd_add_fix_la(struct ldb_module *module, struct ldb_message_element *el,
694 uint64_t seq_num, const struct GUID *invocationId, time_t t,
695 struct GUID *guid, const struct dsdb_attribute *sa, struct ldb_request *parent)
697 unsigned int i;
698 TALLOC_CTX *tmp_ctx = talloc_new(el->values);
699 struct ldb_context *ldb = ldb_module_get_ctx(module);
701 /* We will take a reference to the schema in replmd_add_backlink */
702 const struct dsdb_schema *schema = dsdb_get_schema(ldb, NULL);
703 NTTIME now;
705 unix_to_nt_time(&now, t);
707 for (i=0; i<el->num_values; i++) {
708 struct ldb_val *v = &el->values[i];
709 struct dsdb_dn *dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, v, sa->syntax->ldap_oid);
710 struct GUID target_guid;
711 NTSTATUS status;
712 int ret;
714 /* note that the DN already has the extended
715 components from the extended_dn_store module */
716 status = dsdb_get_extended_dn_guid(dsdb_dn->dn, &target_guid, "GUID");
717 if (!NT_STATUS_IS_OK(status) || GUID_all_zero(&target_guid)) {
718 ret = dsdb_module_guid_by_dn(module, dsdb_dn->dn, &target_guid, parent);
719 if (ret != LDB_SUCCESS) {
720 talloc_free(tmp_ctx);
721 return ret;
723 ret = dsdb_set_extended_dn_guid(dsdb_dn->dn, &target_guid, "GUID");
724 if (ret != LDB_SUCCESS) {
725 talloc_free(tmp_ctx);
726 return ret;
730 ret = replmd_build_la_val(el->values, v, dsdb_dn, invocationId,
731 seq_num, seq_num, now, 0, false);
732 if (ret != LDB_SUCCESS) {
733 talloc_free(tmp_ctx);
734 return ret;
737 ret = replmd_add_backlink(module, schema, guid, &target_guid, true, sa, false);
738 if (ret != LDB_SUCCESS) {
739 talloc_free(tmp_ctx);
740 return ret;
744 talloc_free(tmp_ctx);
745 return LDB_SUCCESS;
750 intercept add requests
752 static int replmd_add(struct ldb_module *module, struct ldb_request *req)
754 struct samldb_msds_intid_persistant *msds_intid_struct;
755 struct ldb_context *ldb;
756 struct ldb_control *control;
757 struct replmd_replicated_request *ac;
758 enum ndr_err_code ndr_err;
759 struct ldb_request *down_req;
760 struct ldb_message *msg;
761 const DATA_BLOB *guid_blob;
762 struct GUID guid;
763 struct replPropertyMetaDataBlob nmd;
764 struct ldb_val nmd_value;
765 const struct GUID *our_invocation_id;
766 time_t t = time(NULL);
767 NTTIME now;
768 char *time_str;
769 int ret;
770 unsigned int i;
771 unsigned int functional_level;
772 uint32_t ni=0;
773 bool allow_add_guid = false;
774 bool remove_current_guid = false;
775 bool is_urgent = false;
776 struct ldb_message_element *objectclass_el;
778 /* check if there's a show relax control (used by provision to say 'I know what I'm doing') */
779 control = ldb_request_get_control(req, LDB_CONTROL_RELAX_OID);
780 if (control) {
781 allow_add_guid = true;
784 /* do not manipulate our control entries */
785 if (ldb_dn_is_special(req->op.add.message->dn)) {
786 return ldb_next_request(module, req);
789 ldb = ldb_module_get_ctx(module);
791 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_add\n");
793 guid_blob = ldb_msg_find_ldb_val(req->op.add.message, "objectGUID");
794 if (guid_blob != NULL) {
795 if (!allow_add_guid) {
796 ldb_set_errstring(ldb,
797 "replmd_add: it's not allowed to add an object with objectGUID!");
798 return LDB_ERR_UNWILLING_TO_PERFORM;
799 } else {
800 NTSTATUS status = GUID_from_data_blob(guid_blob,&guid);
801 if (!NT_STATUS_IS_OK(status)) {
802 ldb_set_errstring(ldb,
803 "replmd_add: Unable to parse the 'objectGUID' as a GUID!");
804 return LDB_ERR_UNWILLING_TO_PERFORM;
806 /* we remove this attribute as it can be a string and
807 * will not be treated correctly and then we will re-add
808 * it later on in the good format */
809 remove_current_guid = true;
811 } else {
812 /* a new GUID */
813 guid = GUID_random();
816 ac = replmd_ctx_init(module, req);
817 if (ac == NULL) {
818 return ldb_module_oom(module);
821 functional_level = dsdb_functional_level(ldb);
823 /* Get a sequence number from the backend */
824 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ac->seq_num);
825 if (ret != LDB_SUCCESS) {
826 talloc_free(ac);
827 return ret;
830 /* get our invocationId */
831 our_invocation_id = samdb_ntds_invocation_id(ldb);
832 if (!our_invocation_id) {
833 ldb_debug_set(ldb, LDB_DEBUG_ERROR,
834 "replmd_add: unable to find invocationId\n");
835 talloc_free(ac);
836 return LDB_ERR_OPERATIONS_ERROR;
839 /* we have to copy the message as the caller might have it as a const */
840 msg = ldb_msg_copy_shallow(ac, req->op.add.message);
841 if (msg == NULL) {
842 ldb_oom(ldb);
843 talloc_free(ac);
844 return LDB_ERR_OPERATIONS_ERROR;
847 /* generated times */
848 unix_to_nt_time(&now, t);
849 time_str = ldb_timestring(msg, t);
850 if (!time_str) {
851 ldb_oom(ldb);
852 talloc_free(ac);
853 return LDB_ERR_OPERATIONS_ERROR;
855 if (remove_current_guid) {
856 ldb_msg_remove_attr(msg,"objectGUID");
860 * remove autogenerated attributes
862 ldb_msg_remove_attr(msg, "whenCreated");
863 ldb_msg_remove_attr(msg, "whenChanged");
864 ldb_msg_remove_attr(msg, "uSNCreated");
865 ldb_msg_remove_attr(msg, "uSNChanged");
866 ldb_msg_remove_attr(msg, "replPropertyMetaData");
869 * readd replicated attributes
871 ret = ldb_msg_add_string(msg, "whenCreated", time_str);
872 if (ret != LDB_SUCCESS) {
873 ldb_oom(ldb);
874 talloc_free(ac);
875 return ret;
878 /* build the replication meta_data */
879 ZERO_STRUCT(nmd);
880 nmd.version = 1;
881 nmd.ctr.ctr1.count = msg->num_elements;
882 nmd.ctr.ctr1.array = talloc_array(msg,
883 struct replPropertyMetaData1,
884 nmd.ctr.ctr1.count);
885 if (!nmd.ctr.ctr1.array) {
886 ldb_oom(ldb);
887 talloc_free(ac);
888 return LDB_ERR_OPERATIONS_ERROR;
891 for (i=0; i < msg->num_elements; i++) {
892 struct ldb_message_element *e = &msg->elements[i];
893 struct replPropertyMetaData1 *m = &nmd.ctr.ctr1.array[ni];
894 const struct dsdb_attribute *sa;
896 if (e->name[0] == '@') continue;
898 sa = dsdb_attribute_by_lDAPDisplayName(ac->schema, e->name);
899 if (!sa) {
900 ldb_debug_set(ldb, LDB_DEBUG_ERROR,
901 "replmd_add: attribute '%s' not defined in schema\n",
902 e->name);
903 talloc_free(ac);
904 return LDB_ERR_NO_SUCH_ATTRIBUTE;
907 if ((sa->systemFlags & DS_FLAG_ATTR_NOT_REPLICATED) || (sa->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED)) {
908 /* if the attribute is not replicated (0x00000001)
909 * or constructed (0x00000004) it has no metadata
911 continue;
914 if (sa->linkID != 0 && functional_level > DS_DOMAIN_FUNCTION_2000) {
915 ret = replmd_add_fix_la(module, e, ac->seq_num, our_invocation_id, t, &guid, sa, req);
916 if (ret != LDB_SUCCESS) {
917 talloc_free(ac);
918 return ret;
920 /* linked attributes are not stored in
921 replPropertyMetaData in FL above w2k */
922 continue;
925 m->attid = sa->attributeID_id;
926 m->version = 1;
927 if (m->attid == 0x20030) {
928 const struct ldb_val *rdn_val = ldb_dn_get_rdn_val(msg->dn);
929 const char* rdn;
931 if (rdn_val == NULL) {
932 ldb_oom(ldb);
933 talloc_free(ac);
934 return LDB_ERR_OPERATIONS_ERROR;
937 rdn = (const char*)rdn_val->data;
938 if (strcmp(rdn, "Deleted Objects") == 0) {
940 * Set the originating_change_time to 29/12/9999 at 23:59:59
941 * as specified in MS-ADTS 7.1.1.4.2 Deleted Objects Container
943 m->originating_change_time = DELETED_OBJECT_CONTAINER_CHANGE_TIME;
944 } else {
945 m->originating_change_time = now;
947 } else {
948 m->originating_change_time = now;
950 m->originating_invocation_id = *our_invocation_id;
951 m->originating_usn = ac->seq_num;
952 m->local_usn = ac->seq_num;
953 ni++;
956 /* fix meta data count */
957 nmd.ctr.ctr1.count = ni;
960 * sort meta data array, and move the rdn attribute entry to the end
962 ret = replmd_replPropertyMetaDataCtr1_sort(&nmd.ctr.ctr1, ac->schema, msg->dn);
963 if (ret != LDB_SUCCESS) {
964 talloc_free(ac);
965 return ret;
968 /* generated NDR encoded values */
969 ndr_err = ndr_push_struct_blob(&nmd_value, msg,
970 &nmd,
971 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
972 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
973 ldb_oom(ldb);
974 talloc_free(ac);
975 return LDB_ERR_OPERATIONS_ERROR;
979 * add the autogenerated values
981 ret = dsdb_msg_add_guid(msg, &guid, "objectGUID");
982 if (ret != LDB_SUCCESS) {
983 ldb_oom(ldb);
984 talloc_free(ac);
985 return ret;
987 ret = ldb_msg_add_string(msg, "whenChanged", time_str);
988 if (ret != LDB_SUCCESS) {
989 ldb_oom(ldb);
990 talloc_free(ac);
991 return ret;
993 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", ac->seq_num);
994 if (ret != LDB_SUCCESS) {
995 ldb_oom(ldb);
996 talloc_free(ac);
997 return ret;
999 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ac->seq_num);
1000 if (ret != LDB_SUCCESS) {
1001 ldb_oom(ldb);
1002 talloc_free(ac);
1003 return ret;
1005 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
1006 if (ret != LDB_SUCCESS) {
1007 ldb_oom(ldb);
1008 talloc_free(ac);
1009 return ret;
1013 * sort the attributes by attid before storing the object
1015 replmd_ldb_message_sort(msg, ac->schema);
1017 objectclass_el = ldb_msg_find_element(msg, "objectClass");
1018 is_urgent = replmd_check_urgent_objectclass(objectclass_el,
1019 REPL_URGENT_ON_CREATE);
1021 ac->is_urgent = is_urgent;
1022 ret = ldb_build_add_req(&down_req, ldb, ac,
1023 msg,
1024 req->controls,
1025 ac, replmd_op_callback,
1026 req);
1028 LDB_REQ_SET_LOCATION(down_req);
1029 if (ret != LDB_SUCCESS) {
1030 talloc_free(ac);
1031 return ret;
1034 /* current partition control is needed by "replmd_op_callback" */
1035 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
1036 ret = ldb_request_add_control(down_req,
1037 DSDB_CONTROL_CURRENT_PARTITION_OID,
1038 false, NULL);
1039 if (ret != LDB_SUCCESS) {
1040 talloc_free(ac);
1041 return ret;
1045 if (functional_level == DS_DOMAIN_FUNCTION_2000) {
1046 ret = ldb_request_add_control(down_req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
1047 if (ret != LDB_SUCCESS) {
1048 talloc_free(ac);
1049 return ret;
1053 /* mark the control done */
1054 if (control) {
1055 control->critical = 0;
1057 if (ldb_dn_compare_base(ac->schema->base_dn, req->op.add.message->dn) != 0) {
1059 /* Update the usn in the SAMLDB_MSDS_INTID_OPAQUE opaque */
1060 msds_intid_struct = (struct samldb_msds_intid_persistant *) ldb_get_opaque(ldb, SAMLDB_MSDS_INTID_OPAQUE);
1061 if (msds_intid_struct) {
1062 msds_intid_struct->usn = ac->seq_num;
1065 /* go on with the call chain */
1066 return ldb_next_request(module, down_req);
1071 * update the replPropertyMetaData for one element
1073 static int replmd_update_rpmd_element(struct ldb_context *ldb,
1074 struct ldb_message *msg,
1075 struct ldb_message_element *el,
1076 struct ldb_message_element *old_el,
1077 struct replPropertyMetaDataBlob *omd,
1078 const struct dsdb_schema *schema,
1079 uint64_t *seq_num,
1080 const struct GUID *our_invocation_id,
1081 NTTIME now,
1082 struct ldb_request *req)
1084 uint32_t i;
1085 const struct dsdb_attribute *a;
1086 struct replPropertyMetaData1 *md1;
1088 a = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
1089 if (a == NULL) {
1090 if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)) {
1091 /* allow this to make it possible for dbcheck
1092 to remove bad attributes */
1093 return LDB_SUCCESS;
1096 DEBUG(0,(__location__ ": Unable to find attribute %s in schema\n",
1097 el->name));
1098 return LDB_ERR_OPERATIONS_ERROR;
1101 if ((a->systemFlags & DS_FLAG_ATTR_NOT_REPLICATED) || (a->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED)) {
1102 return LDB_SUCCESS;
1105 /* if the attribute's value haven't changed then return LDB_SUCCESS
1106 * Unless we have the provision control or if the attribute is
1107 * interSiteTopologyGenerator as this page explain: http://support.microsoft.com/kb/224815
1108 * this attribute is periodicaly written by the DC responsible for the intersite generation
1109 * in a given site
1111 if (old_el != NULL && ldb_msg_element_compare(el, old_el) == 0) {
1112 if (strcmp(el->name, "interSiteTopologyGenerator") != 0 &&
1113 !ldb_request_get_control(req, LDB_CONTROL_PROVISION_OID)) {
1115 * allow this to make it possible for dbcheck
1116 * to rebuild broken metadata
1118 return LDB_SUCCESS;
1122 for (i=0; i<omd->ctr.ctr1.count; i++) {
1123 if (a->attributeID_id == omd->ctr.ctr1.array[i].attid) break;
1126 if (a->linkID != 0 && dsdb_functional_level(ldb) > DS_DOMAIN_FUNCTION_2000) {
1127 /* linked attributes are not stored in
1128 replPropertyMetaData in FL above w2k, but we do
1129 raise the seqnum for the object */
1130 if (*seq_num == 0 &&
1131 ldb_sequence_number(ldb, LDB_SEQ_NEXT, seq_num) != LDB_SUCCESS) {
1132 return LDB_ERR_OPERATIONS_ERROR;
1134 return LDB_SUCCESS;
1137 if (i == omd->ctr.ctr1.count) {
1138 /* we need to add a new one */
1139 omd->ctr.ctr1.array = talloc_realloc(msg, omd->ctr.ctr1.array,
1140 struct replPropertyMetaData1, omd->ctr.ctr1.count+1);
1141 if (omd->ctr.ctr1.array == NULL) {
1142 ldb_oom(ldb);
1143 return LDB_ERR_OPERATIONS_ERROR;
1145 omd->ctr.ctr1.count++;
1146 ZERO_STRUCT(omd->ctr.ctr1.array[i]);
1149 /* Get a new sequence number from the backend. We only do this
1150 * if we have a change that requires a new
1151 * replPropertyMetaData element
1153 if (*seq_num == 0) {
1154 int ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, seq_num);
1155 if (ret != LDB_SUCCESS) {
1156 return LDB_ERR_OPERATIONS_ERROR;
1160 md1 = &omd->ctr.ctr1.array[i];
1161 md1->version++;
1162 md1->attid = a->attributeID_id;
1163 if (md1->attid == 0x20030) {
1164 const struct ldb_val *rdn_val = ldb_dn_get_rdn_val(msg->dn);
1165 const char* rdn;
1167 if (rdn_val == NULL) {
1168 ldb_oom(ldb);
1169 return LDB_ERR_OPERATIONS_ERROR;
1172 rdn = (const char*)rdn_val->data;
1173 if (strcmp(rdn, "Deleted Objects") == 0) {
1175 * Set the originating_change_time to 29/12/9999 at 23:59:59
1176 * as specified in MS-ADTS 7.1.1.4.2 Deleted Objects Container
1178 md1->originating_change_time = DELETED_OBJECT_CONTAINER_CHANGE_TIME;
1179 } else {
1180 md1->originating_change_time = now;
1182 } else {
1183 md1->originating_change_time = now;
1185 md1->originating_invocation_id = *our_invocation_id;
1186 md1->originating_usn = *seq_num;
1187 md1->local_usn = *seq_num;
1189 return LDB_SUCCESS;
1192 static uint64_t find_max_local_usn(struct replPropertyMetaDataBlob omd)
1194 uint32_t count = omd.ctr.ctr1.count;
1195 uint64_t max = 0;
1196 uint32_t i;
1197 for (i=0; i < count; i++) {
1198 struct replPropertyMetaData1 m = omd.ctr.ctr1.array[i];
1199 if (max < m.local_usn) {
1200 max = m.local_usn;
1203 return max;
1207 * update the replPropertyMetaData object each time we modify an
1208 * object. This is needed for DRS replication, as the merge on the
1209 * client is based on this object
1211 static int replmd_update_rpmd(struct ldb_module *module,
1212 const struct dsdb_schema *schema,
1213 struct ldb_request *req,
1214 const char * const *rename_attrs,
1215 struct ldb_message *msg, uint64_t *seq_num,
1216 time_t t,
1217 bool *is_urgent, bool *rodc)
1219 const struct ldb_val *omd_value;
1220 enum ndr_err_code ndr_err;
1221 struct replPropertyMetaDataBlob omd;
1222 unsigned int i;
1223 NTTIME now;
1224 const struct GUID *our_invocation_id;
1225 int ret;
1226 const char * const *attrs = NULL;
1227 const char * const attrs1[] = { "replPropertyMetaData", "*", NULL };
1228 const char * const attrs2[] = { "uSNChanged", "objectClass", "instanceType", NULL };
1229 struct ldb_result *res;
1230 struct ldb_context *ldb;
1231 struct ldb_message_element *objectclass_el;
1232 enum urgent_situation situation;
1233 bool rmd_is_provided;
1235 if (rename_attrs) {
1236 attrs = rename_attrs;
1237 } else {
1238 attrs = attrs1;
1241 ldb = ldb_module_get_ctx(module);
1243 our_invocation_id = samdb_ntds_invocation_id(ldb);
1244 if (!our_invocation_id) {
1245 /* this happens during an initial vampire while
1246 updating the schema */
1247 DEBUG(5,("No invocationID - skipping replPropertyMetaData update\n"));
1248 return LDB_SUCCESS;
1251 unix_to_nt_time(&now, t);
1253 if (ldb_request_get_control(req, DSDB_CONTROL_CHANGEREPLMETADATA_OID)) {
1254 rmd_is_provided = true;
1255 } else {
1256 rmd_is_provided = false;
1259 /* if isDeleted is present and is TRUE, then we consider we are deleting,
1260 * otherwise we consider we are updating */
1261 if (ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")) {
1262 situation = REPL_URGENT_ON_DELETE;
1263 } else if (rename_attrs) {
1264 situation = REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE;
1265 } else {
1266 situation = REPL_URGENT_ON_UPDATE;
1269 if (rmd_is_provided) {
1270 /* In this case the change_replmetadata control was supplied */
1271 /* We check that it's the only attribute that is provided
1272 * (it's a rare case so it's better to keep the code simplier)
1273 * We also check that the highest local_usn is bigger than
1274 * uSNChanged. */
1275 uint64_t db_seq;
1276 if( msg->num_elements != 1 ||
1277 strncmp(msg->elements[0].name,
1278 "replPropertyMetaData", 20) ) {
1279 DEBUG(0,(__location__ ": changereplmetada control called without "\
1280 "a specified replPropertyMetaData attribute or with others\n"));
1281 return LDB_ERR_OPERATIONS_ERROR;
1283 if (situation != REPL_URGENT_ON_UPDATE) {
1284 DEBUG(0,(__location__ ": changereplmetada control can't be called when deleting an object\n"));
1285 return LDB_ERR_OPERATIONS_ERROR;
1287 omd_value = ldb_msg_find_ldb_val(msg, "replPropertyMetaData");
1288 if (!omd_value) {
1289 DEBUG(0,(__location__ ": replPropertyMetaData was not specified for Object %s\n",
1290 ldb_dn_get_linearized(msg->dn)));
1291 return LDB_ERR_OPERATIONS_ERROR;
1293 ndr_err = ndr_pull_struct_blob(omd_value, msg, &omd,
1294 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
1295 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1296 DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s\n",
1297 ldb_dn_get_linearized(msg->dn)));
1298 return LDB_ERR_OPERATIONS_ERROR;
1300 *seq_num = find_max_local_usn(omd);
1302 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attrs2,
1303 DSDB_FLAG_NEXT_MODULE |
1304 DSDB_SEARCH_SHOW_RECYCLED |
1305 DSDB_SEARCH_SHOW_EXTENDED_DN |
1306 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
1307 DSDB_SEARCH_REVEAL_INTERNALS, req);
1309 if (ret != LDB_SUCCESS) {
1310 return ret;
1313 objectclass_el = ldb_msg_find_element(res->msgs[0], "objectClass");
1314 if (is_urgent && replmd_check_urgent_objectclass(objectclass_el,
1315 situation)) {
1316 *is_urgent = true;
1319 db_seq = ldb_msg_find_attr_as_uint64(res->msgs[0], "uSNChanged", 0);
1320 if (*seq_num <= db_seq) {
1321 DEBUG(0,(__location__ ": changereplmetada control provided but max(local_usn)"\
1322 " is less or equal to uSNChanged (max = %lld uSNChanged = %lld)\n",
1323 (long long)*seq_num, (long long)db_seq));
1324 return LDB_ERR_OPERATIONS_ERROR;
1327 } else {
1328 /* search for the existing replPropertyMetaDataBlob. We need
1329 * to use REVEAL and ask for DNs in storage format to support
1330 * the check for values being the same in
1331 * replmd_update_rpmd_element()
1333 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attrs,
1334 DSDB_FLAG_NEXT_MODULE |
1335 DSDB_SEARCH_SHOW_RECYCLED |
1336 DSDB_SEARCH_SHOW_EXTENDED_DN |
1337 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
1338 DSDB_SEARCH_REVEAL_INTERNALS, req);
1339 if (ret != LDB_SUCCESS) {
1340 return ret;
1343 objectclass_el = ldb_msg_find_element(res->msgs[0], "objectClass");
1344 if (is_urgent && replmd_check_urgent_objectclass(objectclass_el,
1345 situation)) {
1346 *is_urgent = true;
1349 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
1350 if (!omd_value) {
1351 DEBUG(0,(__location__ ": Object %s does not have a replPropertyMetaData attribute\n",
1352 ldb_dn_get_linearized(msg->dn)));
1353 return LDB_ERR_OPERATIONS_ERROR;
1356 ndr_err = ndr_pull_struct_blob(omd_value, msg, &omd,
1357 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
1358 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1359 DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s\n",
1360 ldb_dn_get_linearized(msg->dn)));
1361 return LDB_ERR_OPERATIONS_ERROR;
1364 if (omd.version != 1) {
1365 DEBUG(0,(__location__ ": bad version %u in replPropertyMetaData for %s\n",
1366 omd.version, ldb_dn_get_linearized(msg->dn)));
1367 return LDB_ERR_OPERATIONS_ERROR;
1370 for (i=0; i<msg->num_elements; i++) {
1371 struct ldb_message_element *old_el;
1372 old_el = ldb_msg_find_element(res->msgs[0], msg->elements[i].name);
1373 ret = replmd_update_rpmd_element(ldb, msg, &msg->elements[i], old_el, &omd, schema, seq_num,
1374 our_invocation_id, now, req);
1375 if (ret != LDB_SUCCESS) {
1376 return ret;
1379 if (is_urgent && !*is_urgent && (situation == REPL_URGENT_ON_UPDATE)) {
1380 *is_urgent = replmd_check_urgent_attribute(&msg->elements[i]);
1386 * replmd_update_rpmd_element has done an update if the
1387 * seq_num is set
1389 if (*seq_num != 0) {
1390 struct ldb_val *md_value;
1391 struct ldb_message_element *el;
1393 /*if we are RODC and this is a DRSR update then its ok*/
1394 if (!ldb_request_get_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID)) {
1395 unsigned instanceType;
1397 ret = samdb_rodc(ldb, rodc);
1398 if (ret != LDB_SUCCESS) {
1399 DEBUG(4, (__location__ ": unable to tell if we are an RODC\n"));
1400 } else if (*rodc) {
1401 ldb_set_errstring(ldb, "RODC modify is forbidden!");
1402 return LDB_ERR_REFERRAL;
1405 instanceType = ldb_msg_find_attr_as_uint(res->msgs[0], "instanceType", INSTANCE_TYPE_WRITE);
1406 if (!(instanceType & INSTANCE_TYPE_WRITE)) {
1407 return ldb_error(ldb, LDB_ERR_UNWILLING_TO_PERFORM,
1408 "cannot change replicated attribute on partial replica");
1412 md_value = talloc(msg, struct ldb_val);
1413 if (md_value == NULL) {
1414 ldb_oom(ldb);
1415 return LDB_ERR_OPERATIONS_ERROR;
1418 ret = replmd_replPropertyMetaDataCtr1_sort(&omd.ctr.ctr1, schema, msg->dn);
1419 if (ret != LDB_SUCCESS) {
1420 return ret;
1423 ndr_err = ndr_push_struct_blob(md_value, msg, &omd,
1424 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
1425 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1426 DEBUG(0,(__location__ ": Failed to marshall replPropertyMetaData for %s\n",
1427 ldb_dn_get_linearized(msg->dn)));
1428 return LDB_ERR_OPERATIONS_ERROR;
1431 ret = ldb_msg_add_empty(msg, "replPropertyMetaData", LDB_FLAG_MOD_REPLACE, &el);
1432 if (ret != LDB_SUCCESS) {
1433 DEBUG(0,(__location__ ": Failed to add updated replPropertyMetaData %s\n",
1434 ldb_dn_get_linearized(msg->dn)));
1435 return ret;
1438 el->num_values = 1;
1439 el->values = md_value;
1442 return LDB_SUCCESS;
1445 struct parsed_dn {
1446 struct dsdb_dn *dsdb_dn;
1447 struct GUID *guid;
1448 struct ldb_val *v;
1451 static int parsed_dn_compare(struct parsed_dn *pdn1, struct parsed_dn *pdn2)
1453 return GUID_compare(pdn1->guid, pdn2->guid);
1456 static struct parsed_dn *parsed_dn_find(struct parsed_dn *pdn,
1457 unsigned int count, struct GUID *guid,
1458 struct ldb_dn *dn)
1460 struct parsed_dn *ret;
1461 unsigned int i;
1462 if (dn && GUID_all_zero(guid)) {
1463 /* when updating a link using DRS, we sometimes get a
1464 NULL GUID. We then need to try and match by DN */
1465 for (i=0; i<count; i++) {
1466 if (ldb_dn_compare(pdn[i].dsdb_dn->dn, dn) == 0) {
1467 dsdb_get_extended_dn_guid(pdn[i].dsdb_dn->dn, guid, "GUID");
1468 return &pdn[i];
1471 return NULL;
1473 BINARY_ARRAY_SEARCH(pdn, count, guid, guid, GUID_compare, ret);
1474 return ret;
1478 get a series of message element values as an array of DNs and GUIDs
1479 the result is sorted by GUID
1481 static int get_parsed_dns(struct ldb_module *module, TALLOC_CTX *mem_ctx,
1482 struct ldb_message_element *el, struct parsed_dn **pdn,
1483 const char *ldap_oid, struct ldb_request *parent)
1485 unsigned int i;
1486 struct ldb_context *ldb = ldb_module_get_ctx(module);
1488 if (el == NULL) {
1489 *pdn = NULL;
1490 return LDB_SUCCESS;
1493 (*pdn) = talloc_array(mem_ctx, struct parsed_dn, el->num_values);
1494 if (!*pdn) {
1495 ldb_module_oom(module);
1496 return LDB_ERR_OPERATIONS_ERROR;
1499 for (i=0; i<el->num_values; i++) {
1500 struct ldb_val *v = &el->values[i];
1501 NTSTATUS status;
1502 struct ldb_dn *dn;
1503 struct parsed_dn *p;
1505 p = &(*pdn)[i];
1507 p->dsdb_dn = dsdb_dn_parse(*pdn, ldb, v, ldap_oid);
1508 if (p->dsdb_dn == NULL) {
1509 return LDB_ERR_INVALID_DN_SYNTAX;
1512 dn = p->dsdb_dn->dn;
1514 p->guid = talloc(*pdn, struct GUID);
1515 if (p->guid == NULL) {
1516 ldb_module_oom(module);
1517 return LDB_ERR_OPERATIONS_ERROR;
1520 status = dsdb_get_extended_dn_guid(dn, p->guid, "GUID");
1521 if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
1522 /* we got a DN without a GUID - go find the GUID */
1523 int ret = dsdb_module_guid_by_dn(module, dn, p->guid, parent);
1524 if (ret != LDB_SUCCESS) {
1525 ldb_asprintf_errstring(ldb, "Unable to find GUID for DN %s\n",
1526 ldb_dn_get_linearized(dn));
1527 if (ret == LDB_ERR_NO_SUCH_OBJECT &&
1528 LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_DELETE &&
1529 ldb_attr_cmp(el->name, "member") == 0) {
1530 return LDB_ERR_UNWILLING_TO_PERFORM;
1532 return ret;
1534 ret = dsdb_set_extended_dn_guid(dn, p->guid, "GUID");
1535 if (ret != LDB_SUCCESS) {
1536 return ret;
1538 } else if (!NT_STATUS_IS_OK(status)) {
1539 return LDB_ERR_OPERATIONS_ERROR;
1542 /* keep a pointer to the original ldb_val */
1543 p->v = v;
1546 TYPESAFE_QSORT(*pdn, el->num_values, parsed_dn_compare);
1548 return LDB_SUCCESS;
1552 build a new extended DN, including all meta data fields
1554 RMD_FLAGS = DSDB_RMD_FLAG_* bits
1555 RMD_ADDTIME = originating_add_time
1556 RMD_INVOCID = originating_invocation_id
1557 RMD_CHANGETIME = originating_change_time
1558 RMD_ORIGINATING_USN = originating_usn
1559 RMD_LOCAL_USN = local_usn
1560 RMD_VERSION = version
1562 static int replmd_build_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
1563 const struct GUID *invocation_id, uint64_t seq_num,
1564 uint64_t local_usn, NTTIME nttime, uint32_t version, bool deleted)
1566 struct ldb_dn *dn = dsdb_dn->dn;
1567 const char *tstring, *usn_string, *flags_string;
1568 struct ldb_val tval;
1569 struct ldb_val iid;
1570 struct ldb_val usnv, local_usnv;
1571 struct ldb_val vers, flagsv;
1572 NTSTATUS status;
1573 int ret;
1574 const char *dnstring;
1575 char *vstring;
1576 uint32_t rmd_flags = deleted?DSDB_RMD_FLAG_DELETED:0;
1578 tstring = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)nttime);
1579 if (!tstring) {
1580 return LDB_ERR_OPERATIONS_ERROR;
1582 tval = data_blob_string_const(tstring);
1584 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)seq_num);
1585 if (!usn_string) {
1586 return LDB_ERR_OPERATIONS_ERROR;
1588 usnv = data_blob_string_const(usn_string);
1590 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)local_usn);
1591 if (!usn_string) {
1592 return LDB_ERR_OPERATIONS_ERROR;
1594 local_usnv = data_blob_string_const(usn_string);
1596 vstring = talloc_asprintf(mem_ctx, "%lu", (unsigned long)version);
1597 if (!vstring) {
1598 return LDB_ERR_OPERATIONS_ERROR;
1600 vers = data_blob_string_const(vstring);
1602 status = GUID_to_ndr_blob(invocation_id, dn, &iid);
1603 if (!NT_STATUS_IS_OK(status)) {
1604 return LDB_ERR_OPERATIONS_ERROR;
1607 flags_string = talloc_asprintf(mem_ctx, "%u", rmd_flags);
1608 if (!flags_string) {
1609 return LDB_ERR_OPERATIONS_ERROR;
1611 flagsv = data_blob_string_const(flags_string);
1613 ret = ldb_dn_set_extended_component(dn, "RMD_FLAGS", &flagsv);
1614 if (ret != LDB_SUCCESS) return ret;
1615 ret = ldb_dn_set_extended_component(dn, "RMD_ADDTIME", &tval);
1616 if (ret != LDB_SUCCESS) return ret;
1617 ret = ldb_dn_set_extended_component(dn, "RMD_INVOCID", &iid);
1618 if (ret != LDB_SUCCESS) return ret;
1619 ret = ldb_dn_set_extended_component(dn, "RMD_CHANGETIME", &tval);
1620 if (ret != LDB_SUCCESS) return ret;
1621 ret = ldb_dn_set_extended_component(dn, "RMD_LOCAL_USN", &local_usnv);
1622 if (ret != LDB_SUCCESS) return ret;
1623 ret = ldb_dn_set_extended_component(dn, "RMD_ORIGINATING_USN", &usnv);
1624 if (ret != LDB_SUCCESS) return ret;
1625 ret = ldb_dn_set_extended_component(dn, "RMD_VERSION", &vers);
1626 if (ret != LDB_SUCCESS) return ret;
1628 dnstring = dsdb_dn_get_extended_linearized(mem_ctx, dsdb_dn, 1);
1629 if (dnstring == NULL) {
1630 return LDB_ERR_OPERATIONS_ERROR;
1632 *v = data_blob_string_const(dnstring);
1634 return LDB_SUCCESS;
1637 static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
1638 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
1639 uint64_t seq_num, uint64_t local_usn, NTTIME nttime,
1640 uint32_t version, bool deleted);
1643 check if any links need upgrading from w2k format
1645 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.
1647 static int replmd_check_upgrade_links(struct parsed_dn *dns, uint32_t count, struct ldb_message_element *parent_ctx, const struct GUID *invocation_id)
1649 uint32_t i;
1650 for (i=0; i<count; i++) {
1651 NTSTATUS status;
1652 uint32_t version;
1653 int ret;
1655 status = dsdb_get_extended_dn_uint32(dns[i].dsdb_dn->dn, &version, "RMD_VERSION");
1656 if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
1657 continue;
1660 /* it's an old one that needs upgrading */
1661 ret = replmd_update_la_val(parent_ctx->values, dns[i].v, dns[i].dsdb_dn, dns[i].dsdb_dn, invocation_id,
1662 1, 1, 0, 0, false);
1663 if (ret != LDB_SUCCESS) {
1664 return ret;
1667 return LDB_SUCCESS;
1671 update an extended DN, including all meta data fields
1673 see replmd_build_la_val for value names
1675 static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
1676 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
1677 uint64_t seq_num, uint64_t local_usn, NTTIME nttime,
1678 uint32_t version, bool deleted)
1680 struct ldb_dn *dn = dsdb_dn->dn;
1681 const char *tstring, *usn_string, *flags_string;
1682 struct ldb_val tval;
1683 struct ldb_val iid;
1684 struct ldb_val usnv, local_usnv;
1685 struct ldb_val vers, flagsv;
1686 const struct ldb_val *old_addtime;
1687 uint32_t old_version;
1688 NTSTATUS status;
1689 int ret;
1690 const char *dnstring;
1691 char *vstring;
1692 uint32_t rmd_flags = deleted?DSDB_RMD_FLAG_DELETED:0;
1694 tstring = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)nttime);
1695 if (!tstring) {
1696 return LDB_ERR_OPERATIONS_ERROR;
1698 tval = data_blob_string_const(tstring);
1700 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)seq_num);
1701 if (!usn_string) {
1702 return LDB_ERR_OPERATIONS_ERROR;
1704 usnv = data_blob_string_const(usn_string);
1706 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)local_usn);
1707 if (!usn_string) {
1708 return LDB_ERR_OPERATIONS_ERROR;
1710 local_usnv = data_blob_string_const(usn_string);
1712 status = GUID_to_ndr_blob(invocation_id, dn, &iid);
1713 if (!NT_STATUS_IS_OK(status)) {
1714 return LDB_ERR_OPERATIONS_ERROR;
1717 flags_string = talloc_asprintf(mem_ctx, "%u", rmd_flags);
1718 if (!flags_string) {
1719 return LDB_ERR_OPERATIONS_ERROR;
1721 flagsv = data_blob_string_const(flags_string);
1723 ret = ldb_dn_set_extended_component(dn, "RMD_FLAGS", &flagsv);
1724 if (ret != LDB_SUCCESS) return ret;
1726 /* get the ADDTIME from the original */
1727 old_addtime = ldb_dn_get_extended_component(old_dsdb_dn->dn, "RMD_ADDTIME");
1728 if (old_addtime == NULL) {
1729 old_addtime = &tval;
1731 if (dsdb_dn != old_dsdb_dn ||
1732 ldb_dn_get_extended_component(dn, "RMD_ADDTIME") == NULL) {
1733 ret = ldb_dn_set_extended_component(dn, "RMD_ADDTIME", old_addtime);
1734 if (ret != LDB_SUCCESS) return ret;
1737 /* use our invocation id */
1738 ret = ldb_dn_set_extended_component(dn, "RMD_INVOCID", &iid);
1739 if (ret != LDB_SUCCESS) return ret;
1741 /* changetime is the current time */
1742 ret = ldb_dn_set_extended_component(dn, "RMD_CHANGETIME", &tval);
1743 if (ret != LDB_SUCCESS) return ret;
1745 /* update the USN */
1746 ret = ldb_dn_set_extended_component(dn, "RMD_ORIGINATING_USN", &usnv);
1747 if (ret != LDB_SUCCESS) return ret;
1749 ret = ldb_dn_set_extended_component(dn, "RMD_LOCAL_USN", &local_usnv);
1750 if (ret != LDB_SUCCESS) return ret;
1752 /* increase the version by 1 */
1753 status = dsdb_get_extended_dn_uint32(old_dsdb_dn->dn, &old_version, "RMD_VERSION");
1754 if (NT_STATUS_IS_OK(status) && old_version >= version) {
1755 version = old_version+1;
1757 vstring = talloc_asprintf(dn, "%lu", (unsigned long)version);
1758 vers = data_blob_string_const(vstring);
1759 ret = ldb_dn_set_extended_component(dn, "RMD_VERSION", &vers);
1760 if (ret != LDB_SUCCESS) return ret;
1762 dnstring = dsdb_dn_get_extended_linearized(mem_ctx, dsdb_dn, 1);
1763 if (dnstring == NULL) {
1764 return LDB_ERR_OPERATIONS_ERROR;
1766 *v = data_blob_string_const(dnstring);
1768 return LDB_SUCCESS;
1772 handle adding a linked attribute
1774 static int replmd_modify_la_add(struct ldb_module *module,
1775 const struct dsdb_schema *schema,
1776 struct ldb_message *msg,
1777 struct ldb_message_element *el,
1778 struct ldb_message_element *old_el,
1779 const struct dsdb_attribute *schema_attr,
1780 uint64_t seq_num,
1781 time_t t,
1782 struct GUID *msg_guid,
1783 struct ldb_request *parent)
1785 unsigned int i;
1786 struct parsed_dn *dns, *old_dns;
1787 TALLOC_CTX *tmp_ctx = talloc_new(msg);
1788 int ret;
1789 struct ldb_val *new_values = NULL;
1790 unsigned int num_new_values = 0;
1791 unsigned old_num_values = old_el?old_el->num_values:0;
1792 const struct GUID *invocation_id;
1793 struct ldb_context *ldb = ldb_module_get_ctx(module);
1794 NTTIME now;
1796 unix_to_nt_time(&now, t);
1798 ret = get_parsed_dns(module, tmp_ctx, el, &dns, schema_attr->syntax->ldap_oid, parent);
1799 if (ret != LDB_SUCCESS) {
1800 talloc_free(tmp_ctx);
1801 return ret;
1804 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns, schema_attr->syntax->ldap_oid, parent);
1805 if (ret != LDB_SUCCESS) {
1806 talloc_free(tmp_ctx);
1807 return ret;
1810 invocation_id = samdb_ntds_invocation_id(ldb);
1811 if (!invocation_id) {
1812 talloc_free(tmp_ctx);
1813 return LDB_ERR_OPERATIONS_ERROR;
1816 ret = replmd_check_upgrade_links(old_dns, old_num_values, old_el, invocation_id);
1817 if (ret != LDB_SUCCESS) {
1818 talloc_free(tmp_ctx);
1819 return ret;
1822 /* for each new value, see if it exists already with the same GUID */
1823 for (i=0; i<el->num_values; i++) {
1824 struct parsed_dn *p = parsed_dn_find(old_dns, old_num_values, dns[i].guid, NULL);
1825 if (p == NULL) {
1826 /* this is a new linked attribute value */
1827 new_values = talloc_realloc(tmp_ctx, new_values, struct ldb_val, num_new_values+1);
1828 if (new_values == NULL) {
1829 ldb_module_oom(module);
1830 talloc_free(tmp_ctx);
1831 return LDB_ERR_OPERATIONS_ERROR;
1833 ret = replmd_build_la_val(new_values, &new_values[num_new_values], dns[i].dsdb_dn,
1834 invocation_id, seq_num, seq_num, now, 0, false);
1835 if (ret != LDB_SUCCESS) {
1836 talloc_free(tmp_ctx);
1837 return ret;
1839 num_new_values++;
1840 } else {
1841 /* this is only allowed if the GUID was
1842 previously deleted. */
1843 uint32_t rmd_flags = dsdb_dn_rmd_flags(p->dsdb_dn->dn);
1845 if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
1846 ldb_asprintf_errstring(ldb, "Attribute %s already exists for target GUID %s",
1847 el->name, GUID_string(tmp_ctx, p->guid));
1848 talloc_free(tmp_ctx);
1849 /* error codes for 'member' need to be
1850 special cased */
1851 if (ldb_attr_cmp(el->name, "member") == 0) {
1852 return LDB_ERR_ENTRY_ALREADY_EXISTS;
1853 } else {
1854 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
1857 ret = replmd_update_la_val(old_el->values, p->v, dns[i].dsdb_dn, p->dsdb_dn,
1858 invocation_id, seq_num, seq_num, now, 0, false);
1859 if (ret != LDB_SUCCESS) {
1860 talloc_free(tmp_ctx);
1861 return ret;
1865 ret = replmd_add_backlink(module, schema, msg_guid, dns[i].guid, true, schema_attr, true);
1866 if (ret != LDB_SUCCESS) {
1867 talloc_free(tmp_ctx);
1868 return ret;
1872 /* add the new ones on to the end of the old values, constructing a new el->values */
1873 el->values = talloc_realloc(msg->elements, old_el?old_el->values:NULL,
1874 struct ldb_val,
1875 old_num_values+num_new_values);
1876 if (el->values == NULL) {
1877 ldb_module_oom(module);
1878 return LDB_ERR_OPERATIONS_ERROR;
1881 memcpy(&el->values[old_num_values], new_values, num_new_values*sizeof(struct ldb_val));
1882 el->num_values = old_num_values + num_new_values;
1884 talloc_steal(msg->elements, el->values);
1885 talloc_steal(el->values, new_values);
1887 talloc_free(tmp_ctx);
1889 /* we now tell the backend to replace all existing values
1890 with the one we have constructed */
1891 el->flags = LDB_FLAG_MOD_REPLACE;
1893 return LDB_SUCCESS;
1898 handle deleting all active linked attributes
1900 static int replmd_modify_la_delete(struct ldb_module *module,
1901 const struct dsdb_schema *schema,
1902 struct ldb_message *msg,
1903 struct ldb_message_element *el,
1904 struct ldb_message_element *old_el,
1905 const struct dsdb_attribute *schema_attr,
1906 uint64_t seq_num,
1907 time_t t,
1908 struct GUID *msg_guid,
1909 struct ldb_request *parent)
1911 unsigned int i;
1912 struct parsed_dn *dns, *old_dns;
1913 TALLOC_CTX *tmp_ctx = talloc_new(msg);
1914 int ret;
1915 const struct GUID *invocation_id;
1916 struct ldb_context *ldb = ldb_module_get_ctx(module);
1917 NTTIME now;
1919 unix_to_nt_time(&now, t);
1921 /* check if there is nothing to delete */
1922 if ((!old_el || old_el->num_values == 0) &&
1923 el->num_values == 0) {
1924 return LDB_SUCCESS;
1927 if (!old_el || old_el->num_values == 0) {
1928 return LDB_ERR_NO_SUCH_ATTRIBUTE;
1931 ret = get_parsed_dns(module, tmp_ctx, el, &dns, schema_attr->syntax->ldap_oid, parent);
1932 if (ret != LDB_SUCCESS) {
1933 talloc_free(tmp_ctx);
1934 return ret;
1937 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns, schema_attr->syntax->ldap_oid, parent);
1938 if (ret != LDB_SUCCESS) {
1939 talloc_free(tmp_ctx);
1940 return ret;
1943 invocation_id = samdb_ntds_invocation_id(ldb);
1944 if (!invocation_id) {
1945 return LDB_ERR_OPERATIONS_ERROR;
1948 ret = replmd_check_upgrade_links(old_dns, old_el->num_values, old_el, invocation_id);
1949 if (ret != LDB_SUCCESS) {
1950 talloc_free(tmp_ctx);
1951 return ret;
1954 el->values = NULL;
1956 /* see if we are being asked to delete any links that
1957 don't exist or are already deleted */
1958 for (i=0; i<el->num_values; i++) {
1959 struct parsed_dn *p = &dns[i];
1960 struct parsed_dn *p2;
1961 uint32_t rmd_flags;
1963 p2 = parsed_dn_find(old_dns, old_el->num_values, p->guid, NULL);
1964 if (!p2) {
1965 ldb_asprintf_errstring(ldb, "Attribute %s doesn't exist for target GUID %s",
1966 el->name, GUID_string(tmp_ctx, p->guid));
1967 if (ldb_attr_cmp(el->name, "member") == 0) {
1968 return LDB_ERR_UNWILLING_TO_PERFORM;
1969 } else {
1970 return LDB_ERR_NO_SUCH_ATTRIBUTE;
1973 rmd_flags = dsdb_dn_rmd_flags(p2->dsdb_dn->dn);
1974 if (rmd_flags & DSDB_RMD_FLAG_DELETED) {
1975 ldb_asprintf_errstring(ldb, "Attribute %s already deleted for target GUID %s",
1976 el->name, GUID_string(tmp_ctx, p->guid));
1977 if (ldb_attr_cmp(el->name, "member") == 0) {
1978 return LDB_ERR_UNWILLING_TO_PERFORM;
1979 } else {
1980 return LDB_ERR_NO_SUCH_ATTRIBUTE;
1985 /* for each new value, see if it exists already with the same GUID
1986 if it is not already deleted and matches the delete list then delete it
1988 for (i=0; i<old_el->num_values; i++) {
1989 struct parsed_dn *p = &old_dns[i];
1990 uint32_t rmd_flags;
1992 if (el->num_values && parsed_dn_find(dns, el->num_values, p->guid, NULL) == NULL) {
1993 continue;
1996 rmd_flags = dsdb_dn_rmd_flags(p->dsdb_dn->dn);
1997 if (rmd_flags & DSDB_RMD_FLAG_DELETED) continue;
1999 ret = replmd_update_la_val(old_el->values, p->v, p->dsdb_dn, p->dsdb_dn,
2000 invocation_id, seq_num, seq_num, now, 0, true);
2001 if (ret != LDB_SUCCESS) {
2002 talloc_free(tmp_ctx);
2003 return ret;
2006 ret = replmd_add_backlink(module, schema, msg_guid, old_dns[i].guid, false, schema_attr, true);
2007 if (ret != LDB_SUCCESS) {
2008 talloc_free(tmp_ctx);
2009 return ret;
2013 el->values = talloc_steal(msg->elements, old_el->values);
2014 el->num_values = old_el->num_values;
2016 talloc_free(tmp_ctx);
2018 /* we now tell the backend to replace all existing values
2019 with the one we have constructed */
2020 el->flags = LDB_FLAG_MOD_REPLACE;
2022 return LDB_SUCCESS;
2026 handle replacing a linked attribute
2028 static int replmd_modify_la_replace(struct ldb_module *module,
2029 const struct dsdb_schema *schema,
2030 struct ldb_message *msg,
2031 struct ldb_message_element *el,
2032 struct ldb_message_element *old_el,
2033 const struct dsdb_attribute *schema_attr,
2034 uint64_t seq_num,
2035 time_t t,
2036 struct GUID *msg_guid,
2037 struct ldb_request *parent)
2039 unsigned int i;
2040 struct parsed_dn *dns, *old_dns;
2041 TALLOC_CTX *tmp_ctx = talloc_new(msg);
2042 int ret;
2043 const struct GUID *invocation_id;
2044 struct ldb_context *ldb = ldb_module_get_ctx(module);
2045 struct ldb_val *new_values = NULL;
2046 unsigned int num_new_values = 0;
2047 unsigned int old_num_values = old_el?old_el->num_values:0;
2048 NTTIME now;
2050 unix_to_nt_time(&now, t);
2052 /* check if there is nothing to replace */
2053 if ((!old_el || old_el->num_values == 0) &&
2054 el->num_values == 0) {
2055 return LDB_SUCCESS;
2058 ret = get_parsed_dns(module, tmp_ctx, el, &dns, schema_attr->syntax->ldap_oid, parent);
2059 if (ret != LDB_SUCCESS) {
2060 talloc_free(tmp_ctx);
2061 return ret;
2064 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns, schema_attr->syntax->ldap_oid, parent);
2065 if (ret != LDB_SUCCESS) {
2066 talloc_free(tmp_ctx);
2067 return ret;
2070 invocation_id = samdb_ntds_invocation_id(ldb);
2071 if (!invocation_id) {
2072 return LDB_ERR_OPERATIONS_ERROR;
2075 ret = replmd_check_upgrade_links(old_dns, old_num_values, old_el, invocation_id);
2076 if (ret != LDB_SUCCESS) {
2077 talloc_free(tmp_ctx);
2078 return ret;
2081 /* mark all the old ones as deleted */
2082 for (i=0; i<old_num_values; i++) {
2083 struct parsed_dn *old_p = &old_dns[i];
2084 struct parsed_dn *p;
2085 uint32_t rmd_flags = dsdb_dn_rmd_flags(old_p->dsdb_dn->dn);
2087 if (rmd_flags & DSDB_RMD_FLAG_DELETED) continue;
2089 ret = replmd_add_backlink(module, schema, msg_guid, old_dns[i].guid, false, schema_attr, false);
2090 if (ret != LDB_SUCCESS) {
2091 talloc_free(tmp_ctx);
2092 return ret;
2095 p = parsed_dn_find(dns, el->num_values, old_p->guid, NULL);
2096 if (p) {
2097 /* we don't delete it if we are re-adding it */
2098 continue;
2101 ret = replmd_update_la_val(old_el->values, old_p->v, old_p->dsdb_dn, old_p->dsdb_dn,
2102 invocation_id, seq_num, seq_num, now, 0, true);
2103 if (ret != LDB_SUCCESS) {
2104 talloc_free(tmp_ctx);
2105 return ret;
2109 /* for each new value, either update its meta-data, or add it
2110 * to old_el
2112 for (i=0; i<el->num_values; i++) {
2113 struct parsed_dn *p = &dns[i], *old_p;
2115 if (old_dns &&
2116 (old_p = parsed_dn_find(old_dns,
2117 old_num_values, p->guid, NULL)) != NULL) {
2118 /* update in place */
2119 ret = replmd_update_la_val(old_el->values, old_p->v, p->dsdb_dn,
2120 old_p->dsdb_dn, invocation_id,
2121 seq_num, seq_num, now, 0, false);
2122 if (ret != LDB_SUCCESS) {
2123 talloc_free(tmp_ctx);
2124 return ret;
2126 } else {
2127 /* add a new one */
2128 new_values = talloc_realloc(tmp_ctx, new_values, struct ldb_val,
2129 num_new_values+1);
2130 if (new_values == NULL) {
2131 ldb_module_oom(module);
2132 talloc_free(tmp_ctx);
2133 return LDB_ERR_OPERATIONS_ERROR;
2135 ret = replmd_build_la_val(new_values, &new_values[num_new_values], dns[i].dsdb_dn,
2136 invocation_id, seq_num, seq_num, now, 0, false);
2137 if (ret != LDB_SUCCESS) {
2138 talloc_free(tmp_ctx);
2139 return ret;
2141 num_new_values++;
2144 ret = replmd_add_backlink(module, schema, msg_guid, dns[i].guid, true, schema_attr, false);
2145 if (ret != LDB_SUCCESS) {
2146 talloc_free(tmp_ctx);
2147 return ret;
2151 /* add the new values to the end of old_el */
2152 if (num_new_values != 0) {
2153 el->values = talloc_realloc(msg->elements, old_el?old_el->values:NULL,
2154 struct ldb_val, old_num_values+num_new_values);
2155 if (el->values == NULL) {
2156 ldb_module_oom(module);
2157 return LDB_ERR_OPERATIONS_ERROR;
2159 memcpy(&el->values[old_num_values], &new_values[0],
2160 sizeof(struct ldb_val)*num_new_values);
2161 el->num_values = old_num_values + num_new_values;
2162 talloc_steal(msg->elements, new_values);
2163 } else {
2164 el->values = old_el->values;
2165 el->num_values = old_el->num_values;
2166 talloc_steal(msg->elements, el->values);
2169 talloc_free(tmp_ctx);
2171 /* we now tell the backend to replace all existing values
2172 with the one we have constructed */
2173 el->flags = LDB_FLAG_MOD_REPLACE;
2175 return LDB_SUCCESS;
2180 handle linked attributes in modify requests
2182 static int replmd_modify_handle_linked_attribs(struct ldb_module *module,
2183 struct ldb_message *msg,
2184 uint64_t seq_num, time_t t,
2185 struct ldb_request *parent)
2187 struct ldb_result *res;
2188 unsigned int i;
2189 int ret;
2190 struct ldb_context *ldb = ldb_module_get_ctx(module);
2191 struct ldb_message *old_msg;
2193 const struct dsdb_schema *schema;
2194 struct GUID old_guid;
2196 if (seq_num == 0) {
2197 /* there the replmd_update_rpmd code has already
2198 * checked and saw that there are no linked
2199 * attributes */
2200 return LDB_SUCCESS;
2203 if (dsdb_functional_level(ldb) == DS_DOMAIN_FUNCTION_2000) {
2204 /* don't do anything special for linked attributes */
2205 return LDB_SUCCESS;
2208 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, NULL,
2209 DSDB_FLAG_NEXT_MODULE |
2210 DSDB_SEARCH_SHOW_RECYCLED |
2211 DSDB_SEARCH_REVEAL_INTERNALS |
2212 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
2213 parent);
2214 if (ret != LDB_SUCCESS) {
2215 return ret;
2217 schema = dsdb_get_schema(ldb, res);
2218 if (!schema) {
2219 return LDB_ERR_OPERATIONS_ERROR;
2222 old_msg = res->msgs[0];
2224 old_guid = samdb_result_guid(old_msg, "objectGUID");
2226 for (i=0; i<msg->num_elements; i++) {
2227 struct ldb_message_element *el = &msg->elements[i];
2228 struct ldb_message_element *old_el, *new_el;
2229 const struct dsdb_attribute *schema_attr
2230 = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
2231 if (!schema_attr) {
2232 ldb_asprintf_errstring(ldb,
2233 "%s: attribute %s is not a valid attribute in schema",
2234 __FUNCTION__, el->name);
2235 return LDB_ERR_OBJECT_CLASS_VIOLATION;
2237 if (schema_attr->linkID == 0) {
2238 continue;
2240 if ((schema_attr->linkID & 1) == 1) {
2241 if (parent && ldb_request_get_control(parent, DSDB_CONTROL_DBCHECK)) {
2242 continue;
2244 /* Odd is for the target. Illegal to modify */
2245 ldb_asprintf_errstring(ldb,
2246 "attribute %s must not be modified directly, it is a linked attribute", el->name);
2247 return LDB_ERR_UNWILLING_TO_PERFORM;
2249 old_el = ldb_msg_find_element(old_msg, el->name);
2250 switch (el->flags & LDB_FLAG_MOD_MASK) {
2251 case LDB_FLAG_MOD_REPLACE:
2252 ret = replmd_modify_la_replace(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid, parent);
2253 break;
2254 case LDB_FLAG_MOD_DELETE:
2255 ret = replmd_modify_la_delete(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid, parent);
2256 break;
2257 case LDB_FLAG_MOD_ADD:
2258 ret = replmd_modify_la_add(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid, parent);
2259 break;
2260 default:
2261 ldb_asprintf_errstring(ldb,
2262 "invalid flags 0x%x for %s linked attribute",
2263 el->flags, el->name);
2264 return LDB_ERR_UNWILLING_TO_PERFORM;
2266 if (dsdb_check_single_valued_link(schema_attr, el) != LDB_SUCCESS) {
2267 ldb_asprintf_errstring(ldb,
2268 "Attribute %s is single valued but more than one value has been supplied",
2269 el->name);
2270 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
2271 } else {
2272 el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
2277 if (ret != LDB_SUCCESS) {
2278 return ret;
2280 if (old_el) {
2281 ldb_msg_remove_attr(old_msg, el->name);
2283 ldb_msg_add_empty(old_msg, el->name, 0, &new_el);
2284 new_el->num_values = el->num_values;
2285 new_el->values = talloc_steal(msg->elements, el->values);
2287 /* TODO: this relises a bit too heavily on the exact
2288 behaviour of ldb_msg_find_element and
2289 ldb_msg_remove_element */
2290 old_el = ldb_msg_find_element(msg, el->name);
2291 if (old_el != el) {
2292 ldb_msg_remove_element(msg, old_el);
2293 i--;
2297 talloc_free(res);
2298 return ret;
2303 static int replmd_modify(struct ldb_module *module, struct ldb_request *req)
2305 struct samldb_msds_intid_persistant *msds_intid_struct;
2306 struct ldb_context *ldb;
2307 struct replmd_replicated_request *ac;
2308 struct ldb_request *down_req;
2309 struct ldb_message *msg;
2310 time_t t = time(NULL);
2311 int ret;
2312 bool is_urgent = false, rodc = false;
2313 unsigned int functional_level;
2314 const DATA_BLOB *guid_blob;
2316 /* do not manipulate our control entries */
2317 if (ldb_dn_is_special(req->op.mod.message->dn)) {
2318 return ldb_next_request(module, req);
2321 ldb = ldb_module_get_ctx(module);
2323 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_modify\n");
2325 guid_blob = ldb_msg_find_ldb_val(req->op.mod.message, "objectGUID");
2326 if ( guid_blob != NULL ) {
2327 ldb_set_errstring(ldb,
2328 "replmd_modify: it's not allowed to change the objectGUID!");
2329 return LDB_ERR_CONSTRAINT_VIOLATION;
2332 ac = replmd_ctx_init(module, req);
2333 if (ac == NULL) {
2334 return ldb_module_oom(module);
2337 functional_level = dsdb_functional_level(ldb);
2339 /* we have to copy the message as the caller might have it as a const */
2340 msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
2341 if (msg == NULL) {
2342 ldb_oom(ldb);
2343 talloc_free(ac);
2344 return LDB_ERR_OPERATIONS_ERROR;
2347 ldb_msg_remove_attr(msg, "whenChanged");
2348 ldb_msg_remove_attr(msg, "uSNChanged");
2350 ret = replmd_update_rpmd(module, ac->schema, req, NULL,
2351 msg, &ac->seq_num, t, &is_urgent, &rodc);
2352 if (rodc && (ret == LDB_ERR_REFERRAL)) {
2353 struct loadparm_context *lp_ctx;
2354 char *referral;
2356 lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
2357 struct loadparm_context);
2359 referral = talloc_asprintf(req,
2360 "ldap://%s/%s",
2361 lpcfg_dnsdomain(lp_ctx),
2362 ldb_dn_get_linearized(msg->dn));
2363 ret = ldb_module_send_referral(req, referral);
2364 talloc_free(ac);
2365 return ret;
2368 if (ret != LDB_SUCCESS) {
2369 talloc_free(ac);
2370 return ret;
2373 ret = replmd_modify_handle_linked_attribs(module, msg, ac->seq_num, t, req);
2374 if (ret != LDB_SUCCESS) {
2375 talloc_free(ac);
2376 return ret;
2379 /* TODO:
2380 * - replace the old object with the newly constructed one
2383 ac->is_urgent = is_urgent;
2385 ret = ldb_build_mod_req(&down_req, ldb, ac,
2386 msg,
2387 req->controls,
2388 ac, replmd_op_callback,
2389 req);
2390 LDB_REQ_SET_LOCATION(down_req);
2391 if (ret != LDB_SUCCESS) {
2392 talloc_free(ac);
2393 return ret;
2396 /* current partition control is needed by "replmd_op_callback" */
2397 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
2398 ret = ldb_request_add_control(down_req,
2399 DSDB_CONTROL_CURRENT_PARTITION_OID,
2400 false, NULL);
2401 if (ret != LDB_SUCCESS) {
2402 talloc_free(ac);
2403 return ret;
2407 /* If we are in functional level 2000, then
2408 * replmd_modify_handle_linked_attribs will have done
2409 * nothing */
2410 if (functional_level == DS_DOMAIN_FUNCTION_2000) {
2411 ret = ldb_request_add_control(down_req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
2412 if (ret != LDB_SUCCESS) {
2413 talloc_free(ac);
2414 return ret;
2418 talloc_steal(down_req, msg);
2420 /* we only change whenChanged and uSNChanged if the seq_num
2421 has changed */
2422 if (ac->seq_num != 0) {
2423 ret = add_time_element(msg, "whenChanged", t);
2424 if (ret != LDB_SUCCESS) {
2425 talloc_free(ac);
2426 ldb_operr(ldb);
2427 return ret;
2430 ret = add_uint64_element(ldb, msg, "uSNChanged", ac->seq_num);
2431 if (ret != LDB_SUCCESS) {
2432 talloc_free(ac);
2433 ldb_operr(ldb);
2434 return ret;
2438 if (!ldb_dn_compare_base(ac->schema->base_dn, msg->dn)) {
2439 /* Update the usn in the SAMLDB_MSDS_INTID_OPAQUE opaque */
2440 msds_intid_struct = (struct samldb_msds_intid_persistant *) ldb_get_opaque(ldb, SAMLDB_MSDS_INTID_OPAQUE);
2441 if (msds_intid_struct) {
2442 msds_intid_struct->usn = ac->seq_num;
2446 /* go on with the call chain */
2447 return ldb_next_request(module, down_req);
2450 static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares);
2453 handle a rename request
2455 On a rename we need to do an extra ldb_modify which sets the
2456 whenChanged and uSNChanged attributes. We do this in a callback after the success.
2458 static int replmd_rename(struct ldb_module *module, struct ldb_request *req)
2460 struct ldb_context *ldb;
2461 struct replmd_replicated_request *ac;
2462 int ret;
2463 struct ldb_request *down_req;
2465 /* do not manipulate our control entries */
2466 if (ldb_dn_is_special(req->op.mod.message->dn)) {
2467 return ldb_next_request(module, req);
2470 ldb = ldb_module_get_ctx(module);
2472 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_rename\n");
2474 ac = replmd_ctx_init(module, req);
2475 if (ac == NULL) {
2476 return ldb_module_oom(module);
2479 ret = ldb_build_rename_req(&down_req, ldb, ac,
2480 ac->req->op.rename.olddn,
2481 ac->req->op.rename.newdn,
2482 ac->req->controls,
2483 ac, replmd_rename_callback,
2484 ac->req);
2485 LDB_REQ_SET_LOCATION(down_req);
2486 if (ret != LDB_SUCCESS) {
2487 talloc_free(ac);
2488 return ret;
2491 /* go on with the call chain */
2492 return ldb_next_request(module, down_req);
2495 /* After the rename is compleated, update the whenchanged etc */
2496 static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares)
2498 struct ldb_context *ldb;
2499 struct replmd_replicated_request *ac;
2500 struct ldb_request *down_req;
2501 struct ldb_message *msg;
2502 const struct dsdb_attribute *rdn_attr;
2503 const char *rdn_name;
2504 const struct ldb_val *rdn_val;
2505 const char *attrs[5] = { NULL, };
2506 time_t t = time(NULL);
2507 int ret;
2508 bool is_urgent = false, rodc = false;
2510 ac = talloc_get_type(req->context, struct replmd_replicated_request);
2511 ldb = ldb_module_get_ctx(ac->module);
2513 if (ares->error != LDB_SUCCESS) {
2514 return ldb_module_done(ac->req, ares->controls,
2515 ares->response, ares->error);
2518 if (ares->type != LDB_REPLY_DONE) {
2519 ldb_set_errstring(ldb,
2520 "invalid ldb_reply_type in callback");
2521 talloc_free(ares);
2522 return ldb_module_done(ac->req, NULL, NULL,
2523 LDB_ERR_OPERATIONS_ERROR);
2526 /* TODO:
2527 * - replace the old object with the newly constructed one
2530 msg = ldb_msg_new(ac);
2531 if (msg == NULL) {
2532 ldb_oom(ldb);
2533 return LDB_ERR_OPERATIONS_ERROR;
2536 msg->dn = ac->req->op.rename.newdn;
2538 rdn_name = ldb_dn_get_rdn_name(msg->dn);
2539 if (rdn_name == NULL) {
2540 talloc_free(ares);
2541 return ldb_module_done(ac->req, NULL, NULL,
2542 ldb_operr(ldb));
2545 /* normalize the rdn attribute name */
2546 rdn_attr = dsdb_attribute_by_lDAPDisplayName(ac->schema, rdn_name);
2547 if (rdn_attr == NULL) {
2548 talloc_free(ares);
2549 return ldb_module_done(ac->req, NULL, NULL,
2550 ldb_operr(ldb));
2552 rdn_name = rdn_attr->lDAPDisplayName;
2554 rdn_val = ldb_dn_get_rdn_val(msg->dn);
2555 if (rdn_val == NULL) {
2556 talloc_free(ares);
2557 return ldb_module_done(ac->req, NULL, NULL,
2558 ldb_operr(ldb));
2561 if (ldb_msg_add_empty(msg, rdn_name, LDB_FLAG_MOD_REPLACE, NULL) != 0) {
2562 talloc_free(ares);
2563 return ldb_module_done(ac->req, NULL, NULL,
2564 ldb_oom(ldb));
2566 if (ldb_msg_add_value(msg, rdn_name, rdn_val, NULL) != 0) {
2567 talloc_free(ares);
2568 return ldb_module_done(ac->req, NULL, NULL,
2569 ldb_oom(ldb));
2571 if (ldb_msg_add_empty(msg, "name", LDB_FLAG_MOD_REPLACE, NULL) != 0) {
2572 talloc_free(ares);
2573 return ldb_module_done(ac->req, NULL, NULL,
2574 ldb_oom(ldb));
2576 if (ldb_msg_add_value(msg, "name", rdn_val, NULL) != 0) {
2577 talloc_free(ares);
2578 return ldb_module_done(ac->req, NULL, NULL,
2579 ldb_oom(ldb));
2583 * here we let replmd_update_rpmd() only search for
2584 * the existing "replPropertyMetaData" and rdn_name attributes.
2586 * We do not want the existing "name" attribute as
2587 * the "name" attribute needs to get the version
2588 * updated on rename even if the rdn value hasn't changed.
2590 * This is the diff of the meta data, for a moved user
2591 * on a w2k8r2 server:
2593 * # record 1
2594 * -dn: CN=sdf df,CN=Users,DC=bla,DC=base
2595 * +dn: CN=sdf df,OU=TestOU,DC=bla,DC=base
2596 * replPropertyMetaData: NDR: struct replPropertyMetaDataBlob
2597 * version : 0x00000001 (1)
2598 * reserved : 0x00000000 (0)
2599 * @@ -66,11 +66,11 @@ replPropertyMetaData: NDR: struct re
2600 * local_usn : 0x00000000000037a5 (14245)
2601 * array: struct replPropertyMetaData1
2602 * attid : DRSUAPI_ATTID_name (0x90001)
2603 * - version : 0x00000001 (1)
2604 * - originating_change_time : Wed Feb 9 17:20:49 2011 CET
2605 * + version : 0x00000002 (2)
2606 * + originating_change_time : Wed Apr 6 15:21:01 2011 CEST
2607 * originating_invocation_id: 0d36ca05-5507-4e62-aca3-354bab0d39e1
2608 * - originating_usn : 0x00000000000037a5 (14245)
2609 * - local_usn : 0x00000000000037a5 (14245)
2610 * + originating_usn : 0x0000000000003834 (14388)
2611 * + local_usn : 0x0000000000003834 (14388)
2612 * array: struct replPropertyMetaData1
2613 * attid : DRSUAPI_ATTID_userAccountControl (0x90008)
2614 * version : 0x00000004 (4)
2616 attrs[0] = "replPropertyMetaData";
2617 attrs[1] = "objectClass";
2618 attrs[2] = "instanceType";
2619 attrs[3] = rdn_name;
2620 attrs[4] = NULL;
2622 ret = replmd_update_rpmd(ac->module, ac->schema, req, attrs,
2623 msg, &ac->seq_num, t, &is_urgent, &rodc);
2624 if (rodc && (ret == LDB_ERR_REFERRAL)) {
2625 struct ldb_dn *olddn = ac->req->op.rename.olddn;
2626 struct loadparm_context *lp_ctx;
2627 char *referral;
2629 lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
2630 struct loadparm_context);
2632 referral = talloc_asprintf(req,
2633 "ldap://%s/%s",
2634 lpcfg_dnsdomain(lp_ctx),
2635 ldb_dn_get_linearized(olddn));
2636 ret = ldb_module_send_referral(req, referral);
2637 talloc_free(ares);
2638 return ldb_module_done(req, NULL, NULL, ret);
2641 if (ret != LDB_SUCCESS) {
2642 talloc_free(ares);
2643 return ldb_module_done(ac->req, NULL, NULL, ret);
2646 if (ac->seq_num == 0) {
2647 talloc_free(ares);
2648 return ldb_module_done(ac->req, NULL, NULL,
2649 ldb_error(ldb, ret,
2650 "internal error seq_num == 0"));
2652 ac->is_urgent = is_urgent;
2654 ret = ldb_build_mod_req(&down_req, ldb, ac,
2655 msg,
2656 req->controls,
2657 ac, replmd_op_callback,
2658 req);
2659 LDB_REQ_SET_LOCATION(down_req);
2660 if (ret != LDB_SUCCESS) {
2661 talloc_free(ac);
2662 return ret;
2665 /* current partition control is needed by "replmd_op_callback" */
2666 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
2667 ret = ldb_request_add_control(down_req,
2668 DSDB_CONTROL_CURRENT_PARTITION_OID,
2669 false, NULL);
2670 if (ret != LDB_SUCCESS) {
2671 talloc_free(ac);
2672 return ret;
2676 talloc_steal(down_req, msg);
2678 ret = add_time_element(msg, "whenChanged", t);
2679 if (ret != LDB_SUCCESS) {
2680 talloc_free(ac);
2681 ldb_operr(ldb);
2682 return ret;
2685 ret = add_uint64_element(ldb, msg, "uSNChanged", ac->seq_num);
2686 if (ret != LDB_SUCCESS) {
2687 talloc_free(ac);
2688 ldb_operr(ldb);
2689 return ret;
2692 /* go on with the call chain - do the modify after the rename */
2693 return ldb_next_request(ac->module, down_req);
2697 remove links from objects that point at this object when an object
2698 is deleted
2700 static int replmd_delete_remove_link(struct ldb_module *module,
2701 const struct dsdb_schema *schema,
2702 struct ldb_dn *dn,
2703 struct ldb_message_element *el,
2704 const struct dsdb_attribute *sa,
2705 struct ldb_request *parent)
2707 unsigned int i;
2708 TALLOC_CTX *tmp_ctx = talloc_new(module);
2709 struct ldb_context *ldb = ldb_module_get_ctx(module);
2711 for (i=0; i<el->num_values; i++) {
2712 struct dsdb_dn *dsdb_dn;
2713 NTSTATUS status;
2714 int ret;
2715 struct GUID guid2;
2716 struct ldb_message *msg;
2717 const struct dsdb_attribute *target_attr;
2718 struct ldb_message_element *el2;
2719 struct ldb_val dn_val;
2721 if (dsdb_dn_is_deleted_val(&el->values[i])) {
2722 continue;
2725 dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i], sa->syntax->ldap_oid);
2726 if (!dsdb_dn) {
2727 talloc_free(tmp_ctx);
2728 return LDB_ERR_OPERATIONS_ERROR;
2731 status = dsdb_get_extended_dn_guid(dsdb_dn->dn, &guid2, "GUID");
2732 if (!NT_STATUS_IS_OK(status)) {
2733 talloc_free(tmp_ctx);
2734 return LDB_ERR_OPERATIONS_ERROR;
2737 /* remove the link */
2738 msg = ldb_msg_new(tmp_ctx);
2739 if (!msg) {
2740 ldb_module_oom(module);
2741 talloc_free(tmp_ctx);
2742 return LDB_ERR_OPERATIONS_ERROR;
2746 msg->dn = dsdb_dn->dn;
2748 target_attr = dsdb_attribute_by_linkID(schema, sa->linkID ^ 1);
2749 if (target_attr == NULL) {
2750 continue;
2753 ret = ldb_msg_add_empty(msg, target_attr->lDAPDisplayName, LDB_FLAG_MOD_DELETE, &el2);
2754 if (ret != LDB_SUCCESS) {
2755 ldb_module_oom(module);
2756 talloc_free(tmp_ctx);
2757 return LDB_ERR_OPERATIONS_ERROR;
2759 dn_val = data_blob_string_const(ldb_dn_get_linearized(dn));
2760 el2->values = &dn_val;
2761 el2->num_values = 1;
2763 ret = dsdb_module_modify(module, msg, DSDB_FLAG_OWN_MODULE, parent);
2764 if (ret != LDB_SUCCESS) {
2765 talloc_free(tmp_ctx);
2766 return ret;
2769 talloc_free(tmp_ctx);
2770 return LDB_SUCCESS;
2775 handle update of replication meta data for deletion of objects
2777 This also handles the mapping of delete to a rename operation
2778 to allow deletes to be replicated.
2780 static int replmd_delete(struct ldb_module *module, struct ldb_request *req)
2782 int ret = LDB_ERR_OTHER;
2783 bool retb, disallow_move_on_delete;
2784 struct ldb_dn *old_dn, *new_dn;
2785 const char *rdn_name;
2786 const struct ldb_val *rdn_value, *new_rdn_value;
2787 struct GUID guid;
2788 struct ldb_context *ldb = ldb_module_get_ctx(module);
2789 const struct dsdb_schema *schema;
2790 struct ldb_message *msg, *old_msg;
2791 struct ldb_message_element *el;
2792 TALLOC_CTX *tmp_ctx;
2793 struct ldb_result *res, *parent_res;
2794 const char *preserved_attrs[] = {
2795 /* yes, this really is a hard coded list. See MS-ADTS
2796 section 3.1.1.5.5.1.1 */
2797 "nTSecurityDescriptor", "attributeID", "attributeSyntax", "dNReferenceUpdate", "dNSHostName",
2798 "flatName", "governsID", "groupType", "instanceType", "lDAPDisplayName", "legacyExchangeDN",
2799 "isDeleted", "isRecycled", "lastKnownParent", "msDS-LastKnownRDN", "mS-DS-CreatorSID",
2800 "mSMQOwnerID", "nCName", "objectClass", "distinguishedName", "objectGUID", "objectSid",
2801 "oMSyntax", "proxiedObjectName", "name", "replPropertyMetaData", "sAMAccountName",
2802 "securityIdentifier", "sIDHistory", "subClassOf", "systemFlags", "trustPartner", "trustDirection",
2803 "trustType", "trustAttributes", "userAccountControl", "uSNChanged", "uSNCreated", "whenCreated",
2804 "whenChanged", NULL};
2805 unsigned int i, el_count = 0;
2806 enum deletion_state { OBJECT_NOT_DELETED=1, OBJECT_DELETED=2, OBJECT_RECYCLED=3,
2807 OBJECT_TOMBSTONE=4, OBJECT_REMOVED=5 };
2808 enum deletion_state deletion_state, next_deletion_state;
2809 bool enabled;
2811 if (ldb_dn_is_special(req->op.del.dn)) {
2812 return ldb_next_request(module, req);
2815 tmp_ctx = talloc_new(ldb);
2816 if (!tmp_ctx) {
2817 ldb_oom(ldb);
2818 return LDB_ERR_OPERATIONS_ERROR;
2821 schema = dsdb_get_schema(ldb, tmp_ctx);
2822 if (!schema) {
2823 return LDB_ERR_OPERATIONS_ERROR;
2826 old_dn = ldb_dn_copy(tmp_ctx, req->op.del.dn);
2828 /* we need the complete msg off disk, so we can work out which
2829 attributes need to be removed */
2830 ret = dsdb_module_search_dn(module, tmp_ctx, &res, old_dn, NULL,
2831 DSDB_FLAG_NEXT_MODULE |
2832 DSDB_SEARCH_SHOW_RECYCLED |
2833 DSDB_SEARCH_REVEAL_INTERNALS |
2834 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT, req);
2835 if (ret != LDB_SUCCESS) {
2836 talloc_free(tmp_ctx);
2837 return ret;
2839 old_msg = res->msgs[0];
2842 ret = dsdb_recyclebin_enabled(module, &enabled);
2843 if (ret != LDB_SUCCESS) {
2844 talloc_free(tmp_ctx);
2845 return ret;
2848 if (ldb_msg_check_string_attribute(old_msg, "isDeleted", "TRUE")) {
2849 if (!enabled) {
2850 deletion_state = OBJECT_TOMBSTONE;
2851 next_deletion_state = OBJECT_REMOVED;
2852 } else if (ldb_msg_check_string_attribute(old_msg, "isRecycled", "TRUE")) {
2853 deletion_state = OBJECT_RECYCLED;
2854 next_deletion_state = OBJECT_REMOVED;
2855 } else {
2856 deletion_state = OBJECT_DELETED;
2857 next_deletion_state = OBJECT_RECYCLED;
2859 } else {
2860 deletion_state = OBJECT_NOT_DELETED;
2861 if (enabled) {
2862 next_deletion_state = OBJECT_DELETED;
2863 } else {
2864 next_deletion_state = OBJECT_TOMBSTONE;
2868 if (next_deletion_state == OBJECT_REMOVED) {
2869 struct auth_session_info *session_info =
2870 (struct auth_session_info *)ldb_get_opaque(ldb, "sessionInfo");
2871 if (security_session_user_level(session_info, NULL) != SECURITY_SYSTEM) {
2872 ldb_asprintf_errstring(ldb, "Refusing to delete deleted object %s",
2873 ldb_dn_get_linearized(old_msg->dn));
2874 return LDB_ERR_UNWILLING_TO_PERFORM;
2877 /* it is already deleted - really remove it this time */
2878 talloc_free(tmp_ctx);
2879 return ldb_next_request(module, req);
2882 rdn_name = ldb_dn_get_rdn_name(old_dn);
2883 rdn_value = ldb_dn_get_rdn_val(old_dn);
2884 if ((rdn_name == NULL) || (rdn_value == NULL)) {
2885 talloc_free(tmp_ctx);
2886 return ldb_operr(ldb);
2889 msg = ldb_msg_new(tmp_ctx);
2890 if (msg == NULL) {
2891 ldb_module_oom(module);
2892 talloc_free(tmp_ctx);
2893 return LDB_ERR_OPERATIONS_ERROR;
2896 msg->dn = old_dn;
2898 if (deletion_state == OBJECT_NOT_DELETED){
2899 /* consider the SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE flag */
2900 disallow_move_on_delete =
2901 (ldb_msg_find_attr_as_int(old_msg, "systemFlags", 0)
2902 & SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE);
2904 /* work out where we will be renaming this object to */
2905 if (!disallow_move_on_delete) {
2906 ret = dsdb_get_deleted_objects_dn(ldb, tmp_ctx, old_dn,
2907 &new_dn);
2908 if (ret != LDB_SUCCESS) {
2909 /* this is probably an attempted delete on a partition
2910 * that doesn't allow delete operations, such as the
2911 * schema partition */
2912 ldb_asprintf_errstring(ldb, "No Deleted Objects container for DN %s",
2913 ldb_dn_get_linearized(old_dn));
2914 talloc_free(tmp_ctx);
2915 return LDB_ERR_UNWILLING_TO_PERFORM;
2917 } else {
2918 new_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
2919 if (new_dn == NULL) {
2920 ldb_module_oom(module);
2921 talloc_free(tmp_ctx);
2922 return LDB_ERR_OPERATIONS_ERROR;
2926 /* get the objects GUID from the search we just did */
2927 guid = samdb_result_guid(old_msg, "objectGUID");
2929 /* Add a formatted child */
2930 retb = ldb_dn_add_child_fmt(new_dn, "%s=%s\\0ADEL:%s",
2931 rdn_name,
2932 ldb_dn_escape_value(tmp_ctx, *rdn_value),
2933 GUID_string(tmp_ctx, &guid));
2934 if (!retb) {
2935 DEBUG(0,(__location__ ": Unable to add a formatted child to dn: %s",
2936 ldb_dn_get_linearized(new_dn)));
2937 talloc_free(tmp_ctx);
2938 return LDB_ERR_OPERATIONS_ERROR;
2941 ret = ldb_msg_add_string(msg, "isDeleted", "TRUE");
2942 if (ret != LDB_SUCCESS) {
2943 DEBUG(0,(__location__ ": Failed to add isDeleted string to the msg\n"));
2944 ldb_module_oom(module);
2945 talloc_free(tmp_ctx);
2946 return ret;
2948 msg->elements[el_count++].flags = LDB_FLAG_MOD_REPLACE;
2952 now we need to modify the object in the following ways:
2954 - add isDeleted=TRUE
2955 - update rDN and name, with new rDN
2956 - remove linked attributes
2957 - remove objectCategory and sAMAccountType
2958 - remove attribs not on the preserved list
2959 - preserved if in above list, or is rDN
2960 - remove all linked attribs from this object
2961 - remove all links from other objects to this object
2962 - add lastKnownParent
2963 - update replPropertyMetaData?
2965 see MS-ADTS "Tombstone Requirements" section 3.1.1.5.5.1.1
2968 /* we need the storage form of the parent GUID */
2969 ret = dsdb_module_search_dn(module, tmp_ctx, &parent_res,
2970 ldb_dn_get_parent(tmp_ctx, old_dn), NULL,
2971 DSDB_FLAG_NEXT_MODULE |
2972 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
2973 DSDB_SEARCH_REVEAL_INTERNALS|
2974 DSDB_SEARCH_SHOW_RECYCLED, req);
2975 if (ret != LDB_SUCCESS) {
2976 talloc_free(tmp_ctx);
2977 return ret;
2980 if (deletion_state == OBJECT_NOT_DELETED){
2981 ret = ldb_msg_add_steal_string(msg, "lastKnownParent",
2982 ldb_dn_get_extended_linearized(tmp_ctx, parent_res->msgs[0]->dn, 1));
2983 if (ret != LDB_SUCCESS) {
2984 DEBUG(0,(__location__ ": Failed to add lastKnownParent string to the msg\n"));
2985 ldb_module_oom(module);
2986 talloc_free(tmp_ctx);
2987 return ret;
2989 msg->elements[el_count++].flags = LDB_FLAG_MOD_ADD;
2992 switch (next_deletion_state){
2994 case OBJECT_DELETED:
2996 ret = ldb_msg_add_value(msg, "msDS-LastKnownRDN", rdn_value, NULL);
2997 if (ret != LDB_SUCCESS) {
2998 DEBUG(0,(__location__ ": Failed to add msDS-LastKnownRDN string to the msg\n"));
2999 ldb_module_oom(module);
3000 talloc_free(tmp_ctx);
3001 return ret;
3003 msg->elements[el_count++].flags = LDB_FLAG_MOD_ADD;
3005 ret = ldb_msg_add_empty(msg, "objectCategory", LDB_FLAG_MOD_REPLACE, NULL);
3006 if (ret != LDB_SUCCESS) {
3007 talloc_free(tmp_ctx);
3008 ldb_module_oom(module);
3009 return ret;
3012 ret = ldb_msg_add_empty(msg, "sAMAccountType", LDB_FLAG_MOD_REPLACE, NULL);
3013 if (ret != LDB_SUCCESS) {
3014 talloc_free(tmp_ctx);
3015 ldb_module_oom(module);
3016 return ret;
3019 break;
3021 case OBJECT_RECYCLED:
3022 case OBJECT_TOMBSTONE:
3025 * we also mark it as recycled, meaning this object can't be
3026 * recovered (we are stripping its attributes).
3027 * This is done only if we have this schema object of course ...
3028 * This behavior is identical to the one of Windows 2008R2 which
3029 * always set the isRecycled attribute, even if the recycle-bin is
3030 * not activated and what ever the forest level is.
3032 if (dsdb_attribute_by_lDAPDisplayName(schema, "isRecycled") != NULL) {
3033 ret = ldb_msg_add_string(msg, "isRecycled", "TRUE");
3034 if (ret != LDB_SUCCESS) {
3035 DEBUG(0,(__location__ ": Failed to add isRecycled string to the msg\n"));
3036 ldb_module_oom(module);
3037 talloc_free(tmp_ctx);
3038 return ret;
3040 msg->elements[el_count++].flags = LDB_FLAG_MOD_ADD;
3043 /* work out which of the old attributes we will be removing */
3044 for (i=0; i<old_msg->num_elements; i++) {
3045 const struct dsdb_attribute *sa;
3046 el = &old_msg->elements[i];
3047 sa = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
3048 if (!sa) {
3049 talloc_free(tmp_ctx);
3050 return LDB_ERR_OPERATIONS_ERROR;
3052 if (ldb_attr_cmp(el->name, rdn_name) == 0) {
3053 /* don't remove the rDN */
3054 continue;
3056 if (sa->linkID && (sa->linkID & 1)) {
3058 we have a backlink in this object
3059 that needs to be removed. We're not
3060 allowed to remove it directly
3061 however, so we instead setup a
3062 modify to delete the corresponding
3063 forward link
3065 ret = replmd_delete_remove_link(module, schema, old_dn, el, sa, req);
3066 if (ret != LDB_SUCCESS) {
3067 talloc_free(tmp_ctx);
3068 return LDB_ERR_OPERATIONS_ERROR;
3070 /* now we continue, which means we
3071 won't remove this backlink
3072 directly
3074 continue;
3076 if (!sa->linkID && ldb_attr_in_list(preserved_attrs, el->name)) {
3077 continue;
3079 ret = ldb_msg_add_empty(msg, el->name, LDB_FLAG_MOD_DELETE, &el);
3080 if (ret != LDB_SUCCESS) {
3081 talloc_free(tmp_ctx);
3082 ldb_module_oom(module);
3083 return ret;
3086 break;
3088 default:
3089 break;
3092 if (deletion_state == OBJECT_NOT_DELETED) {
3093 const struct dsdb_attribute *sa;
3095 /* work out what the new rdn value is, for updating the
3096 rDN and name fields */
3097 new_rdn_value = ldb_dn_get_rdn_val(new_dn);
3098 if (new_rdn_value == NULL) {
3099 talloc_free(tmp_ctx);
3100 return ldb_operr(ldb);
3103 sa = dsdb_attribute_by_lDAPDisplayName(schema, rdn_name);
3104 if (!sa) {
3105 talloc_free(tmp_ctx);
3106 return LDB_ERR_OPERATIONS_ERROR;
3109 ret = ldb_msg_add_value(msg, sa->lDAPDisplayName, new_rdn_value,
3110 &el);
3111 if (ret != LDB_SUCCESS) {
3112 talloc_free(tmp_ctx);
3113 return ret;
3115 el->flags = LDB_FLAG_MOD_REPLACE;
3117 el = ldb_msg_find_element(old_msg, "name");
3118 if (el) {
3119 ret = ldb_msg_add_value(msg, "name", new_rdn_value, &el);
3120 if (ret != LDB_SUCCESS) {
3121 talloc_free(tmp_ctx);
3122 return ret;
3124 el->flags = LDB_FLAG_MOD_REPLACE;
3128 ret = dsdb_module_modify(module, msg, DSDB_FLAG_OWN_MODULE, req);
3129 if (ret != LDB_SUCCESS) {
3130 ldb_asprintf_errstring(ldb, "replmd_delete: Failed to modify object %s in delete - %s",
3131 ldb_dn_get_linearized(old_dn), ldb_errstring(ldb));
3132 talloc_free(tmp_ctx);
3133 return ret;
3136 if (deletion_state == OBJECT_NOT_DELETED) {
3137 /* now rename onto the new DN */
3138 ret = dsdb_module_rename(module, old_dn, new_dn, DSDB_FLAG_NEXT_MODULE, req);
3139 if (ret != LDB_SUCCESS){
3140 DEBUG(0,(__location__ ": Failed to rename object from '%s' to '%s' - %s\n",
3141 ldb_dn_get_linearized(old_dn),
3142 ldb_dn_get_linearized(new_dn),
3143 ldb_errstring(ldb)));
3144 talloc_free(tmp_ctx);
3145 return ret;
3149 talloc_free(tmp_ctx);
3151 return ldb_module_done(req, NULL, NULL, LDB_SUCCESS);
3156 static int replmd_replicated_request_error(struct replmd_replicated_request *ar, int ret)
3158 return ret;
3161 static int replmd_replicated_request_werror(struct replmd_replicated_request *ar, WERROR status)
3163 int ret = LDB_ERR_OTHER;
3164 /* TODO: do some error mapping */
3165 return ret;
3169 static struct replPropertyMetaData1 *
3170 replmd_replPropertyMetaData1_find_attid(struct replPropertyMetaDataBlob *md_blob,
3171 enum drsuapi_DsAttributeId attid)
3173 uint32_t i;
3174 struct replPropertyMetaDataCtr1 *rpmd_ctr = &md_blob->ctr.ctr1;
3176 for (i = 0; i < rpmd_ctr->count; i++) {
3177 if (rpmd_ctr->array[i].attid == attid) {
3178 return &rpmd_ctr->array[i];
3181 return NULL;
3186 return true if an update is newer than an existing entry
3187 see section 5.11 of MS-ADTS
3189 static bool replmd_update_is_newer(const struct GUID *current_invocation_id,
3190 const struct GUID *update_invocation_id,
3191 uint32_t current_version,
3192 uint32_t update_version,
3193 NTTIME current_change_time,
3194 NTTIME update_change_time)
3196 if (update_version != current_version) {
3197 return update_version > current_version;
3199 if (update_change_time != current_change_time) {
3200 return update_change_time > current_change_time;
3202 return GUID_compare(update_invocation_id, current_invocation_id) > 0;
3205 static bool replmd_replPropertyMetaData1_is_newer(struct replPropertyMetaData1 *cur_m,
3206 struct replPropertyMetaData1 *new_m)
3208 return replmd_update_is_newer(&cur_m->originating_invocation_id,
3209 &new_m->originating_invocation_id,
3210 cur_m->version,
3211 new_m->version,
3212 cur_m->originating_change_time,
3213 new_m->originating_change_time);
3218 form a conflict DN
3220 static struct ldb_dn *replmd_conflict_dn(TALLOC_CTX *mem_ctx, struct ldb_dn *dn, struct GUID *guid)
3222 const struct ldb_val *rdn_val;
3223 const char *rdn_name;
3224 struct ldb_dn *new_dn;
3226 rdn_val = ldb_dn_get_rdn_val(dn);
3227 rdn_name = ldb_dn_get_rdn_name(dn);
3228 if (!rdn_val || !rdn_name) {
3229 return NULL;
3232 new_dn = ldb_dn_copy(mem_ctx, dn);
3233 if (!new_dn) {
3234 return NULL;
3237 if (!ldb_dn_remove_child_components(new_dn, 1)) {
3238 return NULL;
3241 if (!ldb_dn_add_child_fmt(new_dn, "%s=%s\\0ACNF:%s",
3242 rdn_name,
3243 ldb_dn_escape_value(new_dn, *rdn_val),
3244 GUID_string(new_dn, guid))) {
3245 return NULL;
3248 return new_dn;
3253 perform a modify operation which sets the rDN and name attributes to
3254 their current values. This has the effect of changing these
3255 attributes to have been last updated by the current DC. This is
3256 needed to ensure that renames performed as part of conflict
3257 resolution are propogated to other DCs
3259 static int replmd_name_modify(struct replmd_replicated_request *ar,
3260 struct ldb_request *req, struct ldb_dn *dn)
3262 struct ldb_message *msg;
3263 const char *rdn_name;
3264 const struct ldb_val *rdn_val;
3265 const struct dsdb_attribute *rdn_attr;
3266 int ret;
3268 msg = ldb_msg_new(req);
3269 if (msg == NULL) {
3270 goto failed;
3272 msg->dn = dn;
3274 rdn_name = ldb_dn_get_rdn_name(dn);
3275 if (rdn_name == NULL) {
3276 goto failed;
3279 /* normalize the rdn attribute name */
3280 rdn_attr = dsdb_attribute_by_lDAPDisplayName(ar->schema, rdn_name);
3281 if (rdn_attr == NULL) {
3282 goto failed;
3284 rdn_name = rdn_attr->lDAPDisplayName;
3286 rdn_val = ldb_dn_get_rdn_val(dn);
3287 if (rdn_val == NULL) {
3288 goto failed;
3291 if (ldb_msg_add_empty(msg, rdn_name, LDB_FLAG_MOD_REPLACE, NULL) != 0) {
3292 goto failed;
3294 if (ldb_msg_add_value(msg, rdn_name, rdn_val, NULL) != 0) {
3295 goto failed;
3297 if (ldb_msg_add_empty(msg, "name", LDB_FLAG_MOD_REPLACE, NULL) != 0) {
3298 goto failed;
3300 if (ldb_msg_add_value(msg, "name", rdn_val, NULL) != 0) {
3301 goto failed;
3304 ret = dsdb_module_modify(ar->module, msg, DSDB_FLAG_OWN_MODULE, req);
3305 if (ret != LDB_SUCCESS) {
3306 DEBUG(0,(__location__ ": Failed to modify rDN/name of conflict DN '%s' - %s",
3307 ldb_dn_get_linearized(dn),
3308 ldb_errstring(ldb_module_get_ctx(ar->module))));
3309 return ret;
3312 talloc_free(msg);
3314 return LDB_SUCCESS;
3316 failed:
3317 talloc_free(msg);
3318 DEBUG(0,(__location__ ": Failed to setup modify rDN/name of conflict DN '%s'",
3319 ldb_dn_get_linearized(dn)));
3320 return LDB_ERR_OPERATIONS_ERROR;
3325 callback for conflict DN handling where we have renamed the incoming
3326 record. After renaming it, we need to ensure the change of name and
3327 rDN for the incoming record is seen as an originating update by this DC.
3329 static int replmd_op_name_modify_callback(struct ldb_request *req, struct ldb_reply *ares)
3331 struct replmd_replicated_request *ar =
3332 talloc_get_type_abort(req->context, struct replmd_replicated_request);
3333 int ret;
3335 if (ares->error != LDB_SUCCESS) {
3336 /* call the normal callback for everything except success */
3337 return replmd_op_callback(req, ares);
3340 /* perform a modify of the rDN and name of the record */
3341 ret = replmd_name_modify(ar, req, req->op.add.message->dn);
3342 if (ret != LDB_SUCCESS) {
3343 ares->error = ret;
3344 return replmd_op_callback(req, ares);
3347 return replmd_op_callback(req, ares);
3351 callback for replmd_replicated_apply_add()
3352 This copes with the creation of conflict records in the case where
3353 the DN exists, but with a different objectGUID
3355 static int replmd_op_add_callback(struct ldb_request *req, struct ldb_reply *ares)
3357 struct ldb_dn *conflict_dn;
3358 struct replmd_replicated_request *ar =
3359 talloc_get_type_abort(req->context, struct replmd_replicated_request);
3360 struct ldb_result *res;
3361 const char *attrs[] = { "replPropertyMetaData", "objectGUID", NULL };
3362 int ret;
3363 const struct ldb_val *rmd_value, *omd_value;
3364 struct replPropertyMetaDataBlob omd, rmd;
3365 enum ndr_err_code ndr_err;
3366 bool rename_incoming_record, rodc;
3367 struct replPropertyMetaData1 *rmd_name, *omd_name;
3369 if (ares->error != LDB_ERR_ENTRY_ALREADY_EXISTS) {
3370 /* call the normal callback for everything except
3371 conflicts */
3372 return replmd_op_callback(req, ares);
3375 ret = samdb_rodc(ldb_module_get_ctx(ar->module), &rodc);
3376 if (ret != LDB_SUCCESS) {
3377 return ret;
3380 * we have a conflict, and need to decide if we will keep the
3381 * new record or the old record
3383 conflict_dn = req->op.add.message->dn;
3385 if (rodc) {
3387 * We are on an RODC, or were a GC for this
3388 * partition, so we have to fail this until
3389 * someone who owns the partition sorts it
3390 * out
3392 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
3393 "Conflict adding object '%s' from incoming replication as we are read only for the partition. \n"
3394 " - We must fail the operation until a master for this partition resolves the conflict",
3395 ldb_dn_get_linearized(conflict_dn));
3396 goto failed;
3400 * we have a conflict, and need to decide if we will keep the
3401 * new record or the old record
3403 conflict_dn = req->op.add.message->dn;
3406 * first we need the replPropertyMetaData attribute from the
3407 * old record
3409 ret = dsdb_module_search_dn(ar->module, req, &res, conflict_dn,
3410 attrs,
3411 DSDB_FLAG_NEXT_MODULE |
3412 DSDB_SEARCH_SHOW_DELETED |
3413 DSDB_SEARCH_SHOW_RECYCLED, req);
3414 if (ret != LDB_SUCCESS) {
3415 DEBUG(0,(__location__ ": Unable to find object for conflicting record '%s'\n",
3416 ldb_dn_get_linearized(conflict_dn)));
3417 goto failed;
3420 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
3421 if (omd_value == NULL) {
3422 DEBUG(0,(__location__ ": Unable to find replPropertyMetaData for conflicting record '%s'\n",
3423 ldb_dn_get_linearized(conflict_dn)));
3424 goto failed;
3427 ndr_err = ndr_pull_struct_blob(omd_value, res->msgs[0], &omd,
3428 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
3429 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
3430 DEBUG(0,(__location__ ": Failed to parse old replPropertyMetaData for %s\n",
3431 ldb_dn_get_linearized(conflict_dn)));
3432 goto failed;
3436 * and the replPropertyMetaData attribute from the
3437 * new record
3439 rmd_value = ldb_msg_find_ldb_val(req->op.add.message, "replPropertyMetaData");
3440 if (rmd_value == NULL) {
3441 DEBUG(0,(__location__ ": Unable to find replPropertyMetaData for new record '%s'\n",
3442 ldb_dn_get_linearized(conflict_dn)));
3443 goto failed;
3446 ndr_err = ndr_pull_struct_blob(rmd_value, req, &rmd,
3447 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
3448 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
3449 DEBUG(0,(__location__ ": Failed to parse new replPropertyMetaData for %s\n",
3450 ldb_dn_get_linearized(conflict_dn)));
3451 goto failed;
3454 /* we decide which is newer based on the RPMD on the name
3455 attribute. See [MS-DRSR] ResolveNameConflict */
3456 rmd_name = replmd_replPropertyMetaData1_find_attid(&rmd, DRSUAPI_ATTID_name);
3457 omd_name = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
3458 if (!rmd_name || !omd_name) {
3459 DEBUG(0,(__location__ ": Failed to find name attribute in replPropertyMetaData for %s\n",
3460 ldb_dn_get_linearized(conflict_dn)));
3461 goto failed;
3464 rename_incoming_record = !replmd_replPropertyMetaData1_is_newer(omd_name, rmd_name);
3466 if (rename_incoming_record) {
3467 struct GUID guid;
3468 struct ldb_dn *new_dn;
3469 struct ldb_message *new_msg;
3471 guid = samdb_result_guid(req->op.add.message, "objectGUID");
3472 if (GUID_all_zero(&guid)) {
3473 DEBUG(0,(__location__ ": Failed to find objectGUID for conflicting incoming record %s\n",
3474 ldb_dn_get_linearized(conflict_dn)));
3475 goto failed;
3477 new_dn = replmd_conflict_dn(req, conflict_dn, &guid);
3478 if (new_dn == NULL) {
3479 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
3480 ldb_dn_get_linearized(conflict_dn)));
3481 goto failed;
3484 DEBUG(1,(__location__ ": Resolving conflict record via incoming rename '%s' -> '%s'\n",
3485 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
3487 /* re-submit the request, but with a different
3488 callback, so we don't loop forever. */
3489 new_msg = ldb_msg_copy_shallow(req, req->op.add.message);
3490 if (!new_msg) {
3491 goto failed;
3492 DEBUG(0,(__location__ ": Failed to copy conflict DN message for %s\n",
3493 ldb_dn_get_linearized(conflict_dn)));
3495 new_msg->dn = new_dn;
3496 req->op.add.message = new_msg;
3497 req->callback = replmd_op_name_modify_callback;
3499 return ldb_next_request(ar->module, req);
3500 } else {
3501 /* we are renaming the existing record */
3502 struct GUID guid;
3503 struct ldb_dn *new_dn;
3505 guid = samdb_result_guid(res->msgs[0], "objectGUID");
3506 if (GUID_all_zero(&guid)) {
3507 DEBUG(0,(__location__ ": Failed to find objectGUID for existing conflict record %s\n",
3508 ldb_dn_get_linearized(conflict_dn)));
3509 goto failed;
3512 new_dn = replmd_conflict_dn(req, conflict_dn, &guid);
3513 if (new_dn == NULL) {
3514 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
3515 ldb_dn_get_linearized(conflict_dn)));
3516 goto failed;
3519 DEBUG(1,(__location__ ": Resolving conflict record via existing rename '%s' -> '%s'\n",
3520 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
3522 ret = dsdb_module_rename(ar->module, conflict_dn, new_dn,
3523 DSDB_FLAG_OWN_MODULE, req);
3524 if (ret != LDB_SUCCESS) {
3525 DEBUG(0,(__location__ ": Failed to rename conflict dn '%s' to '%s' - %s\n",
3526 ldb_dn_get_linearized(conflict_dn),
3527 ldb_dn_get_linearized(new_dn),
3528 ldb_errstring(ldb_module_get_ctx(ar->module))));
3529 goto failed;
3533 * now we need to ensure that the rename is seen as an
3534 * originating update. We do that with a modify.
3536 ret = replmd_name_modify(ar, req, new_dn);
3537 if (ret != LDB_SUCCESS) {
3538 goto failed;
3541 req->callback = replmd_op_callback;
3543 return ldb_next_request(ar->module, req);
3546 failed:
3547 /* on failure do the original callback. This means replication
3548 * will stop with an error, but there is not much else we can
3549 * do
3551 return replmd_op_callback(req, ares);
3555 this is called when a new object comes in over DRS
3557 static int replmd_replicated_apply_add(struct replmd_replicated_request *ar)
3559 struct ldb_context *ldb;
3560 struct ldb_request *change_req;
3561 enum ndr_err_code ndr_err;
3562 struct ldb_message *msg;
3563 struct replPropertyMetaDataBlob *md;
3564 struct ldb_val md_value;
3565 unsigned int i;
3566 int ret;
3569 * TODO: check if the parent object exist
3573 * TODO: handle the conflict case where an object with the
3574 * same name exist
3577 ldb = ldb_module_get_ctx(ar->module);
3578 msg = ar->objs->objects[ar->index_current].msg;
3579 md = ar->objs->objects[ar->index_current].meta_data;
3581 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
3582 if (ret != LDB_SUCCESS) {
3583 return replmd_replicated_request_error(ar, ret);
3586 ret = ldb_msg_add_value(msg, "objectGUID", &ar->objs->objects[ar->index_current].guid_value, NULL);
3587 if (ret != LDB_SUCCESS) {
3588 return replmd_replicated_request_error(ar, ret);
3591 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
3592 if (ret != LDB_SUCCESS) {
3593 return replmd_replicated_request_error(ar, ret);
3596 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", ar->seq_num);
3597 if (ret != LDB_SUCCESS) {
3598 return replmd_replicated_request_error(ar, ret);
3601 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
3602 if (ret != LDB_SUCCESS) {
3603 return replmd_replicated_request_error(ar, ret);
3606 /* remove any message elements that have zero values */
3607 for (i=0; i<msg->num_elements; i++) {
3608 struct ldb_message_element *el = &msg->elements[i];
3610 if (el->num_values == 0) {
3611 DEBUG(4,(__location__ ": Removing attribute %s with num_values==0\n",
3612 el->name));
3613 memmove(el, el+1, sizeof(*el)*(msg->num_elements - (i+1)));
3614 msg->num_elements--;
3615 i--;
3616 continue;
3621 * the meta data array is already sorted by the caller
3623 for (i=0; i < md->ctr.ctr1.count; i++) {
3624 md->ctr.ctr1.array[i].local_usn = ar->seq_num;
3626 ndr_err = ndr_push_struct_blob(&md_value, msg, md,
3627 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
3628 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
3629 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
3630 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
3632 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &md_value, NULL);
3633 if (ret != LDB_SUCCESS) {
3634 return replmd_replicated_request_error(ar, ret);
3637 replmd_ldb_message_sort(msg, ar->schema);
3639 if (DEBUGLVL(4)) {
3640 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_ADD, msg);
3641 DEBUG(4, ("DRS replication add message:\n%s\n", s));
3642 talloc_free(s);
3645 ret = ldb_build_add_req(&change_req,
3646 ldb,
3648 msg,
3649 ar->controls,
3651 replmd_op_add_callback,
3652 ar->req);
3653 LDB_REQ_SET_LOCATION(change_req);
3654 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
3656 /* current partition control needed by "repmd_op_callback" */
3657 ret = ldb_request_add_control(change_req,
3658 DSDB_CONTROL_CURRENT_PARTITION_OID,
3659 false, NULL);
3660 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
3662 if (ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PARTIAL_REPLICA) {
3663 /* this tells the partition module to make it a
3664 partial replica if creating an NC */
3665 ret = ldb_request_add_control(change_req,
3666 DSDB_CONTROL_PARTIAL_REPLICA,
3667 false, NULL);
3668 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
3671 return ldb_next_request(ar->module, change_req);
3674 static int replmd_replicated_apply_search_for_parent_callback(struct ldb_request *req,
3675 struct ldb_reply *ares)
3677 struct replmd_replicated_request *ar = talloc_get_type(req->context,
3678 struct replmd_replicated_request);
3679 int ret;
3681 if (!ares) {
3682 return ldb_module_done(ar->req, NULL, NULL,
3683 LDB_ERR_OPERATIONS_ERROR);
3685 if (ares->error != LDB_SUCCESS &&
3686 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
3687 return ldb_module_done(ar->req, ares->controls,
3688 ares->response, ares->error);
3691 switch (ares->type) {
3692 case LDB_REPLY_ENTRY:
3694 struct ldb_message *parent_msg = ares->message;
3695 struct ldb_message *msg = ar->objs->objects[ar->index_current].msg;
3696 struct ldb_dn *parent_dn;
3697 int comp_num = ldb_dn_get_comp_num(msg->dn);
3698 if (comp_num > 1) {
3699 if (!ldb_dn_remove_base_components(msg->dn, comp_num - 1)) {
3700 talloc_free(ares);
3701 return ldb_module_done(ar->req, NULL, NULL, ldb_module_operr(ar->module));
3704 if (!ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")
3705 && ldb_msg_check_string_attribute(parent_msg, "isDeleted", "TRUE")) {
3706 /* Per MS-DRSR 4.1.10.6.10
3707 * FindBestParentObject we need to move this
3708 * new object under a deleted object to
3709 * lost-and-found */
3711 ret = dsdb_wellknown_dn(ldb_module_get_ctx(ar->module), msg,
3712 ldb_get_default_basedn(ldb_module_get_ctx(ar->module)),
3713 DS_GUID_LOSTANDFOUND_CONTAINER,
3714 &parent_dn);
3715 if (ret != LDB_SUCCESS) {
3716 return ldb_module_done(ar->req, NULL, NULL, ldb_module_operr(ar->module));
3718 } else {
3719 parent_dn = parent_msg->dn;
3721 if (!ldb_dn_add_base(msg->dn, parent_dn)) {
3722 talloc_free(ares);
3723 return ldb_module_done(ar->req, NULL, NULL, ldb_module_operr(ar->module));
3725 break;
3727 case LDB_REPLY_REFERRAL:
3728 /* we ignore referrals */
3729 break;
3731 case LDB_REPLY_DONE:
3732 ret = replmd_replicated_apply_add(ar);
3733 if (ret != LDB_SUCCESS) {
3734 return ldb_module_done(ar->req, NULL, NULL, ret);
3738 talloc_free(ares);
3739 return LDB_SUCCESS;
3743 * Look for the parent object, so we put the new object in the right place
3746 static int replmd_replicated_apply_search_for_parent(struct replmd_replicated_request *ar)
3748 struct ldb_context *ldb;
3749 int ret;
3750 char *tmp_str;
3751 char *filter;
3752 struct ldb_request *search_req;
3753 static const char *attrs[] = {"isDeleted", NULL};
3755 ldb = ldb_module_get_ctx(ar->module);
3757 if (!ar->objs->objects[ar->index_current].parent_guid_value.data) {
3758 return replmd_replicated_apply_add(ar);
3761 tmp_str = ldb_binary_encode(ar, ar->objs->objects[ar->index_current].parent_guid_value);
3762 if (!tmp_str) return replmd_replicated_request_werror(ar, WERR_NOMEM);
3764 filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
3765 if (!filter) return replmd_replicated_request_werror(ar, WERR_NOMEM);
3766 talloc_free(tmp_str);
3768 ret = ldb_build_search_req(&search_req,
3769 ldb,
3771 ar->objs->partition_dn,
3772 LDB_SCOPE_SUBTREE,
3773 filter,
3774 attrs,
3775 NULL,
3777 replmd_replicated_apply_search_for_parent_callback,
3778 ar->req);
3779 LDB_REQ_SET_LOCATION(search_req);
3781 ret = ldb_request_add_control(search_req, LDB_CONTROL_SHOW_RECYCLED_OID,
3782 true, NULL);
3783 if (ret != LDB_SUCCESS) {
3784 return ret;
3787 return ldb_next_request(ar->module, search_req);
3791 handle renames that come in over DRS replication
3793 static int replmd_replicated_handle_rename(struct replmd_replicated_request *ar,
3794 struct ldb_message *msg,
3795 struct replPropertyMetaDataBlob *rmd,
3796 struct replPropertyMetaDataBlob *omd,
3797 struct ldb_request *parent)
3799 struct replPropertyMetaData1 *md_remote;
3800 struct replPropertyMetaData1 *md_local;
3802 if (ldb_dn_compare(msg->dn, ar->search_msg->dn) == 0) {
3803 /* no rename */
3804 return LDB_SUCCESS;
3807 /* now we need to check for double renames. We could have a
3808 * local rename pending which our replication partner hasn't
3809 * received yet. We choose which one wins by looking at the
3810 * attribute stamps on the two objects, the newer one wins
3812 md_remote = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
3813 md_local = replmd_replPropertyMetaData1_find_attid(omd, DRSUAPI_ATTID_name);
3814 /* if there is no name attribute then we have to assume the
3815 object we've received is in fact newer */
3816 if (!md_remote || !md_local ||
3817 replmd_replPropertyMetaData1_is_newer(md_local, md_remote)) {
3818 DEBUG(4,("replmd_replicated_request rename %s => %s\n",
3819 ldb_dn_get_linearized(ar->search_msg->dn),
3820 ldb_dn_get_linearized(msg->dn)));
3821 /* pass rename to the next module
3822 * so it doesn't appear as an originating update */
3823 return dsdb_module_rename(ar->module,
3824 ar->search_msg->dn, msg->dn,
3825 DSDB_FLAG_NEXT_MODULE | DSDB_MODIFY_RELAX, parent);
3828 /* we're going to keep our old object */
3829 DEBUG(4,(__location__ ": Keeping object %s and rejecting older rename to %s\n",
3830 ldb_dn_get_linearized(ar->search_msg->dn),
3831 ldb_dn_get_linearized(msg->dn)));
3832 return LDB_SUCCESS;
3836 static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar)
3838 struct ldb_context *ldb;
3839 struct ldb_request *change_req;
3840 enum ndr_err_code ndr_err;
3841 struct ldb_message *msg;
3842 struct replPropertyMetaDataBlob *rmd;
3843 struct replPropertyMetaDataBlob omd;
3844 const struct ldb_val *omd_value;
3845 struct replPropertyMetaDataBlob nmd;
3846 struct ldb_val nmd_value;
3847 unsigned int i;
3848 uint32_t j,ni=0;
3849 unsigned int removed_attrs = 0;
3850 int ret;
3852 ldb = ldb_module_get_ctx(ar->module);
3853 msg = ar->objs->objects[ar->index_current].msg;
3854 rmd = ar->objs->objects[ar->index_current].meta_data;
3855 ZERO_STRUCT(omd);
3856 omd.version = 1;
3858 /* find existing meta data */
3859 omd_value = ldb_msg_find_ldb_val(ar->search_msg, "replPropertyMetaData");
3860 if (omd_value) {
3861 ndr_err = ndr_pull_struct_blob(omd_value, ar, &omd,
3862 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
3863 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
3864 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
3865 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
3868 if (omd.version != 1) {
3869 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
3873 /* handle renames that come in over DRS */
3874 ret = replmd_replicated_handle_rename(ar, msg, rmd, &omd, ar->req);
3875 if (ret != LDB_SUCCESS) {
3876 ldb_debug(ldb, LDB_DEBUG_FATAL,
3877 "replmd_replicated_request rename %s => %s failed - %s\n",
3878 ldb_dn_get_linearized(ar->search_msg->dn),
3879 ldb_dn_get_linearized(msg->dn),
3880 ldb_errstring(ldb));
3881 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
3884 ZERO_STRUCT(nmd);
3885 nmd.version = 1;
3886 nmd.ctr.ctr1.count = omd.ctr.ctr1.count + rmd->ctr.ctr1.count;
3887 nmd.ctr.ctr1.array = talloc_array(ar,
3888 struct replPropertyMetaData1,
3889 nmd.ctr.ctr1.count);
3890 if (!nmd.ctr.ctr1.array) return replmd_replicated_request_werror(ar, WERR_NOMEM);
3892 /* first copy the old meta data */
3893 for (i=0; i < omd.ctr.ctr1.count; i++) {
3894 nmd.ctr.ctr1.array[ni] = omd.ctr.ctr1.array[i];
3895 ni++;
3898 ar->seq_num = 0;
3899 /* now merge in the new meta data */
3900 for (i=0; i < rmd->ctr.ctr1.count; i++) {
3901 bool found = false;
3903 for (j=0; j < ni; j++) {
3904 bool cmp;
3906 if (rmd->ctr.ctr1.array[i].attid != nmd.ctr.ctr1.array[j].attid) {
3907 continue;
3910 if (ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING) {
3911 /* if we compare equal then do an
3912 update. This is used when a client
3913 asks for a FULL_SYNC, and can be
3914 used to recover a corrupt
3915 replica */
3916 cmp = !replmd_replPropertyMetaData1_is_newer(&rmd->ctr.ctr1.array[i],
3917 &nmd.ctr.ctr1.array[j]);
3918 } else {
3919 cmp = replmd_replPropertyMetaData1_is_newer(&nmd.ctr.ctr1.array[j],
3920 &rmd->ctr.ctr1.array[i]);
3922 if (cmp) {
3923 /* replace the entry */
3924 nmd.ctr.ctr1.array[j] = rmd->ctr.ctr1.array[i];
3925 if (ar->seq_num == 0) {
3926 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
3927 if (ret != LDB_SUCCESS) {
3928 return replmd_replicated_request_error(ar, ret);
3931 nmd.ctr.ctr1.array[j].local_usn = ar->seq_num;
3932 found = true;
3933 break;
3936 if (rmd->ctr.ctr1.array[i].attid != DRSUAPI_ATTID_instanceType) {
3937 DEBUG(3,("Discarding older DRS attribute update to %s on %s from %s\n",
3938 msg->elements[i-removed_attrs].name,
3939 ldb_dn_get_linearized(msg->dn),
3940 GUID_string(ar, &rmd->ctr.ctr1.array[i].originating_invocation_id)));
3943 /* we don't want to apply this change so remove the attribute */
3944 ldb_msg_remove_element(msg, &msg->elements[i-removed_attrs]);
3945 removed_attrs++;
3947 found = true;
3948 break;
3951 if (found) continue;
3953 nmd.ctr.ctr1.array[ni] = rmd->ctr.ctr1.array[i];
3954 if (ar->seq_num == 0) {
3955 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
3956 if (ret != LDB_SUCCESS) {
3957 return replmd_replicated_request_error(ar, ret);
3960 nmd.ctr.ctr1.array[ni].local_usn = ar->seq_num;
3961 ni++;
3965 * finally correct the size of the meta_data array
3967 nmd.ctr.ctr1.count = ni;
3970 * the rdn attribute (the alias for the name attribute),
3971 * 'cn' for most objects is the last entry in the meta data array
3972 * we have stored
3974 * sort the new meta data array
3976 ret = replmd_replPropertyMetaDataCtr1_sort(&nmd.ctr.ctr1, ar->schema, msg->dn);
3977 if (ret != LDB_SUCCESS) {
3978 return ret;
3982 * check if some replicated attributes left, otherwise skip the ldb_modify() call
3984 if (msg->num_elements == 0) {
3985 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: skip replace\n",
3986 ar->index_current);
3988 ar->index_current++;
3989 return replmd_replicated_apply_next(ar);
3992 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: replace %u attributes\n",
3993 ar->index_current, msg->num_elements);
3995 /* create the meta data value */
3996 ndr_err = ndr_push_struct_blob(&nmd_value, msg, &nmd,
3997 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
3998 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
3999 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
4000 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
4004 * when we know that we'll modify the record, add the whenChanged, uSNChanged
4005 * and replPopertyMetaData attributes
4007 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
4008 if (ret != LDB_SUCCESS) {
4009 return replmd_replicated_request_error(ar, ret);
4011 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
4012 if (ret != LDB_SUCCESS) {
4013 return replmd_replicated_request_error(ar, ret);
4015 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
4016 if (ret != LDB_SUCCESS) {
4017 return replmd_replicated_request_error(ar, ret);
4020 replmd_ldb_message_sort(msg, ar->schema);
4022 /* we want to replace the old values */
4023 for (i=0; i < msg->num_elements; i++) {
4024 msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
4027 if (DEBUGLVL(4)) {
4028 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_MODIFY, msg);
4029 DEBUG(4, ("DRS replication modify message:\n%s\n", s));
4030 talloc_free(s);
4033 ret = ldb_build_mod_req(&change_req,
4034 ldb,
4036 msg,
4037 ar->controls,
4039 replmd_op_callback,
4040 ar->req);
4041 LDB_REQ_SET_LOCATION(change_req);
4042 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
4044 /* current partition control needed by "repmd_op_callback" */
4045 ret = ldb_request_add_control(change_req,
4046 DSDB_CONTROL_CURRENT_PARTITION_OID,
4047 false, NULL);
4048 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
4050 return ldb_next_request(ar->module, change_req);
4053 static int replmd_replicated_apply_search_callback(struct ldb_request *req,
4054 struct ldb_reply *ares)
4056 struct replmd_replicated_request *ar = talloc_get_type(req->context,
4057 struct replmd_replicated_request);
4058 int ret;
4060 if (!ares) {
4061 return ldb_module_done(ar->req, NULL, NULL,
4062 LDB_ERR_OPERATIONS_ERROR);
4064 if (ares->error != LDB_SUCCESS &&
4065 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
4066 return ldb_module_done(ar->req, ares->controls,
4067 ares->response, ares->error);
4070 switch (ares->type) {
4071 case LDB_REPLY_ENTRY:
4072 ar->search_msg = talloc_steal(ar, ares->message);
4073 break;
4075 case LDB_REPLY_REFERRAL:
4076 /* we ignore referrals */
4077 break;
4079 case LDB_REPLY_DONE:
4080 if (ar->search_msg != NULL) {
4081 ret = replmd_replicated_apply_merge(ar);
4082 } else {
4083 ret = replmd_replicated_apply_search_for_parent(ar);
4085 if (ret != LDB_SUCCESS) {
4086 return ldb_module_done(ar->req, NULL, NULL, ret);
4090 talloc_free(ares);
4091 return LDB_SUCCESS;
4094 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar);
4096 static int replmd_replicated_apply_next(struct replmd_replicated_request *ar)
4098 struct ldb_context *ldb;
4099 int ret;
4100 char *tmp_str;
4101 char *filter;
4102 struct ldb_request *search_req;
4103 struct ldb_search_options_control *options;
4105 if (ar->index_current >= ar->objs->num_objects) {
4106 /* done with it, go to next stage */
4107 return replmd_replicated_uptodate_vector(ar);
4110 ldb = ldb_module_get_ctx(ar->module);
4111 ar->search_msg = NULL;
4113 tmp_str = ldb_binary_encode(ar, ar->objs->objects[ar->index_current].guid_value);
4114 if (!tmp_str) return replmd_replicated_request_werror(ar, WERR_NOMEM);
4116 filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
4117 if (!filter) return replmd_replicated_request_werror(ar, WERR_NOMEM);
4118 talloc_free(tmp_str);
4120 ret = ldb_build_search_req(&search_req,
4121 ldb,
4123 NULL,
4124 LDB_SCOPE_SUBTREE,
4125 filter,
4126 NULL,
4127 NULL,
4129 replmd_replicated_apply_search_callback,
4130 ar->req);
4131 LDB_REQ_SET_LOCATION(search_req);
4133 ret = ldb_request_add_control(search_req, LDB_CONTROL_SHOW_RECYCLED_OID,
4134 true, NULL);
4135 if (ret != LDB_SUCCESS) {
4136 return ret;
4139 /* we need to cope with cross-partition links, so search for
4140 the GUID over all partitions */
4141 options = talloc(search_req, struct ldb_search_options_control);
4142 if (options == NULL) {
4143 DEBUG(0, (__location__ ": out of memory\n"));
4144 return LDB_ERR_OPERATIONS_ERROR;
4146 options->search_options = LDB_SEARCH_OPTION_PHANTOM_ROOT;
4148 ret = ldb_request_add_control(search_req,
4149 LDB_CONTROL_SEARCH_OPTIONS_OID,
4150 true, options);
4151 if (ret != LDB_SUCCESS) {
4152 return ret;
4155 return ldb_next_request(ar->module, search_req);
4158 static int replmd_replicated_uptodate_modify_callback(struct ldb_request *req,
4159 struct ldb_reply *ares)
4161 struct ldb_context *ldb;
4162 struct replmd_replicated_request *ar = talloc_get_type(req->context,
4163 struct replmd_replicated_request);
4164 ldb = ldb_module_get_ctx(ar->module);
4166 if (!ares) {
4167 return ldb_module_done(ar->req, NULL, NULL,
4168 LDB_ERR_OPERATIONS_ERROR);
4170 if (ares->error != LDB_SUCCESS) {
4171 return ldb_module_done(ar->req, ares->controls,
4172 ares->response, ares->error);
4175 if (ares->type != LDB_REPLY_DONE) {
4176 ldb_asprintf_errstring(ldb, "Invalid LDB reply type %d", ares->type);
4177 return ldb_module_done(ar->req, NULL, NULL,
4178 LDB_ERR_OPERATIONS_ERROR);
4181 talloc_free(ares);
4183 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
4186 static int replmd_replicated_uptodate_modify(struct replmd_replicated_request *ar)
4188 struct ldb_context *ldb;
4189 struct ldb_request *change_req;
4190 enum ndr_err_code ndr_err;
4191 struct ldb_message *msg;
4192 struct replUpToDateVectorBlob ouv;
4193 const struct ldb_val *ouv_value;
4194 const struct drsuapi_DsReplicaCursor2CtrEx *ruv;
4195 struct replUpToDateVectorBlob nuv;
4196 struct ldb_val nuv_value;
4197 struct ldb_message_element *nuv_el = NULL;
4198 const struct GUID *our_invocation_id;
4199 struct ldb_message_element *orf_el = NULL;
4200 struct repsFromToBlob nrf;
4201 struct ldb_val *nrf_value = NULL;
4202 struct ldb_message_element *nrf_el = NULL;
4203 unsigned int i;
4204 uint32_t j,ni=0;
4205 bool found = false;
4206 time_t t = time(NULL);
4207 NTTIME now;
4208 int ret;
4209 uint32_t instanceType;
4211 ldb = ldb_module_get_ctx(ar->module);
4212 ruv = ar->objs->uptodateness_vector;
4213 ZERO_STRUCT(ouv);
4214 ouv.version = 2;
4215 ZERO_STRUCT(nuv);
4216 nuv.version = 2;
4218 unix_to_nt_time(&now, t);
4220 if (ar->search_msg == NULL) {
4221 /* this happens for a REPL_OBJ call where we are
4222 creating the target object by replicating it. The
4223 subdomain join code does this for the partition DN
4225 DEBUG(4,(__location__ ": Skipping UDV and repsFrom update as no target DN\n"));
4226 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
4229 instanceType = ldb_msg_find_attr_as_uint(ar->search_msg, "instanceType", 0);
4230 if (! (instanceType & INSTANCE_TYPE_IS_NC_HEAD)) {
4231 DEBUG(4,(__location__ ": Skipping UDV and repsFrom update as not NC root: %s\n",
4232 ldb_dn_get_linearized(ar->search_msg->dn)));
4233 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
4237 * first create the new replUpToDateVector
4239 ouv_value = ldb_msg_find_ldb_val(ar->search_msg, "replUpToDateVector");
4240 if (ouv_value) {
4241 ndr_err = ndr_pull_struct_blob(ouv_value, ar, &ouv,
4242 (ndr_pull_flags_fn_t)ndr_pull_replUpToDateVectorBlob);
4243 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
4244 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
4245 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
4248 if (ouv.version != 2) {
4249 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
4254 * the new uptodateness vector will at least
4255 * contain 1 entry, one for the source_dsa
4257 * plus optional values from our old vector and the one from the source_dsa
4259 nuv.ctr.ctr2.count = 1 + ouv.ctr.ctr2.count;
4260 if (ruv) nuv.ctr.ctr2.count += ruv->count;
4261 nuv.ctr.ctr2.cursors = talloc_array(ar,
4262 struct drsuapi_DsReplicaCursor2,
4263 nuv.ctr.ctr2.count);
4264 if (!nuv.ctr.ctr2.cursors) return replmd_replicated_request_werror(ar, WERR_NOMEM);
4266 /* first copy the old vector */
4267 for (i=0; i < ouv.ctr.ctr2.count; i++) {
4268 nuv.ctr.ctr2.cursors[ni] = ouv.ctr.ctr2.cursors[i];
4269 ni++;
4272 /* get our invocation_id if we have one already attached to the ldb */
4273 our_invocation_id = samdb_ntds_invocation_id(ldb);
4275 /* merge in the source_dsa vector is available */
4276 for (i=0; (ruv && i < ruv->count); i++) {
4277 found = false;
4279 if (our_invocation_id &&
4280 GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
4281 our_invocation_id)) {
4282 continue;
4285 for (j=0; j < ni; j++) {
4286 if (!GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
4287 &nuv.ctr.ctr2.cursors[j].source_dsa_invocation_id)) {
4288 continue;
4291 found = true;
4294 * we update only the highest_usn and not the latest_sync_success time,
4295 * because the last success stands for direct replication
4297 if (ruv->cursors[i].highest_usn > nuv.ctr.ctr2.cursors[j].highest_usn) {
4298 nuv.ctr.ctr2.cursors[j].highest_usn = ruv->cursors[i].highest_usn;
4300 break;
4303 if (found) continue;
4305 /* if it's not there yet, add it */
4306 nuv.ctr.ctr2.cursors[ni] = ruv->cursors[i];
4307 ni++;
4311 * merge in the current highwatermark for the source_dsa
4313 found = false;
4314 for (j=0; j < ni; j++) {
4315 if (!GUID_equal(&ar->objs->source_dsa->source_dsa_invocation_id,
4316 &nuv.ctr.ctr2.cursors[j].source_dsa_invocation_id)) {
4317 continue;
4320 found = true;
4323 * here we update the highest_usn and last_sync_success time
4324 * because we're directly replicating from the source_dsa
4326 * and use the tmp_highest_usn because this is what we have just applied
4327 * to our ldb
4329 nuv.ctr.ctr2.cursors[j].highest_usn = ar->objs->source_dsa->highwatermark.tmp_highest_usn;
4330 nuv.ctr.ctr2.cursors[j].last_sync_success = now;
4331 break;
4333 if (!found) {
4335 * here we update the highest_usn and last_sync_success time
4336 * because we're directly replicating from the source_dsa
4338 * and use the tmp_highest_usn because this is what we have just applied
4339 * to our ldb
4341 nuv.ctr.ctr2.cursors[ni].source_dsa_invocation_id= ar->objs->source_dsa->source_dsa_invocation_id;
4342 nuv.ctr.ctr2.cursors[ni].highest_usn = ar->objs->source_dsa->highwatermark.tmp_highest_usn;
4343 nuv.ctr.ctr2.cursors[ni].last_sync_success = now;
4344 ni++;
4348 * finally correct the size of the cursors array
4350 nuv.ctr.ctr2.count = ni;
4353 * sort the cursors
4355 TYPESAFE_QSORT(nuv.ctr.ctr2.cursors, nuv.ctr.ctr2.count, drsuapi_DsReplicaCursor2_compare);
4358 * create the change ldb_message
4360 msg = ldb_msg_new(ar);
4361 if (!msg) return replmd_replicated_request_werror(ar, WERR_NOMEM);
4362 msg->dn = ar->search_msg->dn;
4364 ndr_err = ndr_push_struct_blob(&nuv_value, msg, &nuv,
4365 (ndr_push_flags_fn_t)ndr_push_replUpToDateVectorBlob);
4366 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
4367 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
4368 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
4370 ret = ldb_msg_add_value(msg, "replUpToDateVector", &nuv_value, &nuv_el);
4371 if (ret != LDB_SUCCESS) {
4372 return replmd_replicated_request_error(ar, ret);
4374 nuv_el->flags = LDB_FLAG_MOD_REPLACE;
4377 * now create the new repsFrom value from the given repsFromTo1 structure
4379 ZERO_STRUCT(nrf);
4380 nrf.version = 1;
4381 nrf.ctr.ctr1 = *ar->objs->source_dsa;
4382 nrf.ctr.ctr1.highwatermark.highest_usn = nrf.ctr.ctr1.highwatermark.tmp_highest_usn;
4385 * first see if we already have a repsFrom value for the current source dsa
4386 * if so we'll later replace this value
4388 orf_el = ldb_msg_find_element(ar->search_msg, "repsFrom");
4389 if (orf_el) {
4390 for (i=0; i < orf_el->num_values; i++) {
4391 struct repsFromToBlob *trf;
4393 trf = talloc(ar, struct repsFromToBlob);
4394 if (!trf) return replmd_replicated_request_werror(ar, WERR_NOMEM);
4396 ndr_err = ndr_pull_struct_blob(&orf_el->values[i], trf, trf,
4397 (ndr_pull_flags_fn_t)ndr_pull_repsFromToBlob);
4398 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
4399 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
4400 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
4403 if (trf->version != 1) {
4404 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
4408 * we compare the source dsa objectGUID not the invocation_id
4409 * because we want only one repsFrom value per source dsa
4410 * and when the invocation_id of the source dsa has changed we don't need
4411 * the old repsFrom with the old invocation_id
4413 if (!GUID_equal(&trf->ctr.ctr1.source_dsa_obj_guid,
4414 &ar->objs->source_dsa->source_dsa_obj_guid)) {
4415 talloc_free(trf);
4416 continue;
4419 talloc_free(trf);
4420 nrf_value = &orf_el->values[i];
4421 break;
4425 * copy over all old values to the new ldb_message
4427 ret = ldb_msg_add_empty(msg, "repsFrom", 0, &nrf_el);
4428 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
4429 *nrf_el = *orf_el;
4433 * if we haven't found an old repsFrom value for the current source dsa
4434 * we'll add a new value
4436 if (!nrf_value) {
4437 struct ldb_val zero_value;
4438 ZERO_STRUCT(zero_value);
4439 ret = ldb_msg_add_value(msg, "repsFrom", &zero_value, &nrf_el);
4440 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
4442 nrf_value = &nrf_el->values[nrf_el->num_values - 1];
4445 /* we now fill the value which is already attached to ldb_message */
4446 ndr_err = ndr_push_struct_blob(nrf_value, msg,
4447 &nrf,
4448 (ndr_push_flags_fn_t)ndr_push_repsFromToBlob);
4449 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
4450 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
4451 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
4455 * the ldb_message_element for the attribute, has all the old values and the new one
4456 * so we'll replace the whole attribute with all values
4458 nrf_el->flags = LDB_FLAG_MOD_REPLACE;
4460 if (CHECK_DEBUGLVL(4)) {
4461 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_MODIFY, msg);
4462 DEBUG(4, ("DRS replication uptodate modify message:\n%s\n", s));
4463 talloc_free(s);
4466 /* prepare the ldb_modify() request */
4467 ret = ldb_build_mod_req(&change_req,
4468 ldb,
4470 msg,
4471 ar->controls,
4473 replmd_replicated_uptodate_modify_callback,
4474 ar->req);
4475 LDB_REQ_SET_LOCATION(change_req);
4476 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
4478 return ldb_next_request(ar->module, change_req);
4481 static int replmd_replicated_uptodate_search_callback(struct ldb_request *req,
4482 struct ldb_reply *ares)
4484 struct replmd_replicated_request *ar = talloc_get_type(req->context,
4485 struct replmd_replicated_request);
4486 int ret;
4488 if (!ares) {
4489 return ldb_module_done(ar->req, NULL, NULL,
4490 LDB_ERR_OPERATIONS_ERROR);
4492 if (ares->error != LDB_SUCCESS &&
4493 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
4494 return ldb_module_done(ar->req, ares->controls,
4495 ares->response, ares->error);
4498 switch (ares->type) {
4499 case LDB_REPLY_ENTRY:
4500 ar->search_msg = talloc_steal(ar, ares->message);
4501 break;
4503 case LDB_REPLY_REFERRAL:
4504 /* we ignore referrals */
4505 break;
4507 case LDB_REPLY_DONE:
4508 ret = replmd_replicated_uptodate_modify(ar);
4509 if (ret != LDB_SUCCESS) {
4510 return ldb_module_done(ar->req, NULL, NULL, ret);
4514 talloc_free(ares);
4515 return LDB_SUCCESS;
4519 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar)
4521 struct ldb_context *ldb;
4522 int ret;
4523 static const char *attrs[] = {
4524 "replUpToDateVector",
4525 "repsFrom",
4526 "instanceType",
4527 NULL
4529 struct ldb_request *search_req;
4531 ldb = ldb_module_get_ctx(ar->module);
4532 ar->search_msg = NULL;
4534 ret = ldb_build_search_req(&search_req,
4535 ldb,
4537 ar->objs->partition_dn,
4538 LDB_SCOPE_BASE,
4539 "(objectClass=*)",
4540 attrs,
4541 NULL,
4543 replmd_replicated_uptodate_search_callback,
4544 ar->req);
4545 LDB_REQ_SET_LOCATION(search_req);
4546 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
4548 return ldb_next_request(ar->module, search_req);
4553 static int replmd_extended_replicated_objects(struct ldb_module *module, struct ldb_request *req)
4555 struct ldb_context *ldb;
4556 struct dsdb_extended_replicated_objects *objs;
4557 struct replmd_replicated_request *ar;
4558 struct ldb_control **ctrls;
4559 int ret;
4560 uint32_t i;
4561 struct replmd_private *replmd_private =
4562 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
4563 struct dsdb_control_replicated_update *rep_update;
4565 ldb = ldb_module_get_ctx(module);
4567 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_extended_replicated_objects\n");
4569 objs = talloc_get_type(req->op.extended.data, struct dsdb_extended_replicated_objects);
4570 if (!objs) {
4571 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: invalid extended data\n");
4572 return LDB_ERR_PROTOCOL_ERROR;
4575 if (objs->version != DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION) {
4576 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: extended data invalid version [%u != %u]\n",
4577 objs->version, DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION);
4578 return LDB_ERR_PROTOCOL_ERROR;
4581 ar = replmd_ctx_init(module, req);
4582 if (!ar)
4583 return LDB_ERR_OPERATIONS_ERROR;
4585 /* Set the flags to have the replmd_op_callback run over the full set of objects */
4586 ar->apply_mode = true;
4587 ar->objs = objs;
4588 ar->schema = dsdb_get_schema(ldb, ar);
4589 if (!ar->schema) {
4590 ldb_debug_set(ldb, LDB_DEBUG_FATAL, "replmd_ctx_init: no loaded schema found\n");
4591 talloc_free(ar);
4592 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
4593 return LDB_ERR_CONSTRAINT_VIOLATION;
4596 ctrls = req->controls;
4598 if (req->controls) {
4599 req->controls = talloc_memdup(ar, req->controls,
4600 talloc_get_size(req->controls));
4601 if (!req->controls) return replmd_replicated_request_werror(ar, WERR_NOMEM);
4604 /* This allows layers further down to know if a change came in
4605 over replication and what the replication flags were */
4606 rep_update = talloc_zero(ar, struct dsdb_control_replicated_update);
4607 if (rep_update == NULL) {
4608 return ldb_module_oom(module);
4610 rep_update->dsdb_repl_flags = objs->dsdb_repl_flags;
4612 ret = ldb_request_add_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID, false, rep_update);
4613 if (ret != LDB_SUCCESS) {
4614 return ret;
4617 /* If this change contained linked attributes in the body
4618 * (rather than in the links section) we need to update
4619 * backlinks in linked_attributes */
4620 ret = ldb_request_add_control(req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
4621 if (ret != LDB_SUCCESS) {
4622 return ret;
4625 ar->controls = req->controls;
4626 req->controls = ctrls;
4628 DEBUG(4,("linked_attributes_count=%u\n", objs->linked_attributes_count));
4630 /* save away the linked attributes for the end of the
4631 transaction */
4632 for (i=0; i<ar->objs->linked_attributes_count; i++) {
4633 struct la_entry *la_entry;
4635 if (replmd_private->la_ctx == NULL) {
4636 replmd_private->la_ctx = talloc_new(replmd_private);
4638 la_entry = talloc(replmd_private->la_ctx, struct la_entry);
4639 if (la_entry == NULL) {
4640 ldb_oom(ldb);
4641 return LDB_ERR_OPERATIONS_ERROR;
4643 la_entry->la = talloc(la_entry, struct drsuapi_DsReplicaLinkedAttribute);
4644 if (la_entry->la == NULL) {
4645 talloc_free(la_entry);
4646 ldb_oom(ldb);
4647 return LDB_ERR_OPERATIONS_ERROR;
4649 *la_entry->la = ar->objs->linked_attributes[i];
4651 /* we need to steal the non-scalars so they stay
4652 around until the end of the transaction */
4653 talloc_steal(la_entry->la, la_entry->la->identifier);
4654 talloc_steal(la_entry->la, la_entry->la->value.blob);
4656 DLIST_ADD(replmd_private->la_list, la_entry);
4659 return replmd_replicated_apply_next(ar);
4663 process one linked attribute structure
4665 static int replmd_process_linked_attribute(struct ldb_module *module,
4666 struct la_entry *la_entry,
4667 struct ldb_request *parent)
4669 struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
4670 struct ldb_context *ldb = ldb_module_get_ctx(module);
4671 struct ldb_message *msg;
4672 TALLOC_CTX *tmp_ctx = talloc_new(la_entry);
4673 const struct dsdb_schema *schema = dsdb_get_schema(ldb, tmp_ctx);
4674 int ret;
4675 const struct dsdb_attribute *attr;
4676 struct dsdb_dn *dsdb_dn;
4677 uint64_t seq_num = 0;
4678 struct ldb_message_element *old_el;
4679 WERROR status;
4680 time_t t = time(NULL);
4681 struct ldb_result *res;
4682 const char *attrs[2];
4683 struct parsed_dn *pdn_list, *pdn;
4684 struct GUID guid = GUID_zero();
4685 NTSTATUS ntstatus;
4686 bool active = (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)?true:false;
4687 const struct GUID *our_invocation_id;
4690 linked_attributes[0]:
4691 &objs->linked_attributes[i]: struct drsuapi_DsReplicaLinkedAttribute
4692 identifier : *
4693 identifier: struct drsuapi_DsReplicaObjectIdentifier
4694 __ndr_size : 0x0000003a (58)
4695 __ndr_size_sid : 0x00000000 (0)
4696 guid : 8e95b6a9-13dd-4158-89db-3220a5be5cc7
4697 sid : S-0-0
4698 __ndr_size_dn : 0x00000000 (0)
4699 dn : ''
4700 attid : DRSUAPI_ATTID_member (0x1F)
4701 value: struct drsuapi_DsAttributeValue
4702 __ndr_size : 0x0000007e (126)
4703 blob : *
4704 blob : DATA_BLOB length=126
4705 flags : 0x00000001 (1)
4706 1: DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE
4707 originating_add_time : Wed Sep 2 22:20:01 2009 EST
4708 meta_data: struct drsuapi_DsReplicaMetaData
4709 version : 0x00000015 (21)
4710 originating_change_time : Wed Sep 2 23:39:07 2009 EST
4711 originating_invocation_id: 794640f3-18cf-40ee-a211-a93992b67a64
4712 originating_usn : 0x000000000001e19c (123292)
4714 (for cases where the link is to a normal DN)
4715 &target: struct drsuapi_DsReplicaObjectIdentifier3
4716 __ndr_size : 0x0000007e (126)
4717 __ndr_size_sid : 0x0000001c (28)
4718 guid : 7639e594-db75-4086-b0d4-67890ae46031
4719 sid : S-1-5-21-2848215498-2472035911-1947525656-19924
4720 __ndr_size_dn : 0x00000022 (34)
4721 dn : 'CN=UOne,OU=TestOU,DC=vsofs8,DC=com'
4724 /* find the attribute being modified */
4725 attr = dsdb_attribute_by_attributeID_id(schema, la->attid);
4726 if (attr == NULL) {
4727 DEBUG(0, (__location__ ": Unable to find attributeID 0x%x\n", la->attid));
4728 talloc_free(tmp_ctx);
4729 return LDB_ERR_OPERATIONS_ERROR;
4732 attrs[0] = attr->lDAPDisplayName;
4733 attrs[1] = NULL;
4735 /* get the existing message from the db for the object with
4736 this GUID, returning attribute being modified. We will then
4737 use this msg as the basis for a modify call */
4738 ret = dsdb_module_search(module, tmp_ctx, &res, NULL, LDB_SCOPE_SUBTREE, attrs,
4739 DSDB_FLAG_NEXT_MODULE |
4740 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
4741 DSDB_SEARCH_SHOW_RECYCLED |
4742 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
4743 DSDB_SEARCH_REVEAL_INTERNALS,
4744 parent,
4745 "objectGUID=%s", GUID_string(tmp_ctx, &la->identifier->guid));
4746 if (ret != LDB_SUCCESS) {
4747 talloc_free(tmp_ctx);
4748 return ret;
4750 if (res->count != 1) {
4751 ldb_asprintf_errstring(ldb, "DRS linked attribute for GUID %s - DN not found",
4752 GUID_string(tmp_ctx, &la->identifier->guid));
4753 talloc_free(tmp_ctx);
4754 return LDB_ERR_NO_SUCH_OBJECT;
4756 msg = res->msgs[0];
4758 if (msg->num_elements == 0) {
4759 ret = ldb_msg_add_empty(msg, attr->lDAPDisplayName, LDB_FLAG_MOD_REPLACE, &old_el);
4760 if (ret != LDB_SUCCESS) {
4761 ldb_module_oom(module);
4762 talloc_free(tmp_ctx);
4763 return LDB_ERR_OPERATIONS_ERROR;
4765 } else {
4766 old_el = &msg->elements[0];
4767 old_el->flags = LDB_FLAG_MOD_REPLACE;
4770 /* parse the existing links */
4771 ret = get_parsed_dns(module, tmp_ctx, old_el, &pdn_list, attr->syntax->ldap_oid, parent);
4772 if (ret != LDB_SUCCESS) {
4773 talloc_free(tmp_ctx);
4774 return ret;
4777 /* get our invocationId */
4778 our_invocation_id = samdb_ntds_invocation_id(ldb);
4779 if (!our_invocation_id) {
4780 ldb_debug_set(ldb, LDB_DEBUG_ERROR, __location__ ": unable to find invocationId\n");
4781 talloc_free(tmp_ctx);
4782 return LDB_ERR_OPERATIONS_ERROR;
4785 ret = replmd_check_upgrade_links(pdn_list, old_el->num_values, old_el, our_invocation_id);
4786 if (ret != LDB_SUCCESS) {
4787 talloc_free(tmp_ctx);
4788 return ret;
4791 status = dsdb_dn_la_from_blob(ldb, attr, schema, tmp_ctx, la->value.blob, &dsdb_dn);
4792 if (!W_ERROR_IS_OK(status)) {
4793 ldb_asprintf_errstring(ldb, "Failed to parsed linked attribute blob for %s on %s - %s\n",
4794 old_el->name, ldb_dn_get_linearized(msg->dn), win_errstr(status));
4795 return LDB_ERR_OPERATIONS_ERROR;
4798 ntstatus = dsdb_get_extended_dn_guid(dsdb_dn->dn, &guid, "GUID");
4799 if (!NT_STATUS_IS_OK(ntstatus) && active) {
4800 ldb_asprintf_errstring(ldb, "Failed to find GUID in linked attribute blob for %s on %s from %s",
4801 old_el->name,
4802 ldb_dn_get_linearized(dsdb_dn->dn),
4803 ldb_dn_get_linearized(msg->dn));
4804 return LDB_ERR_OPERATIONS_ERROR;
4807 /* re-resolve the DN by GUID, as the DRS server may give us an
4808 old DN value */
4809 ret = dsdb_module_dn_by_guid(module, dsdb_dn, &guid, &dsdb_dn->dn, parent);
4810 if (ret != LDB_SUCCESS) {
4811 DEBUG(2,(__location__ ": WARNING: Failed to re-resolve GUID %s - using %s\n",
4812 GUID_string(tmp_ctx, &guid),
4813 ldb_dn_get_linearized(dsdb_dn->dn)));
4816 /* see if this link already exists */
4817 pdn = parsed_dn_find(pdn_list, old_el->num_values, &guid, dsdb_dn->dn);
4818 if (pdn != NULL) {
4819 /* see if this update is newer than what we have already */
4820 struct GUID invocation_id = GUID_zero();
4821 uint32_t version = 0;
4822 uint32_t originating_usn = 0;
4823 NTTIME change_time = 0;
4824 uint32_t rmd_flags = dsdb_dn_rmd_flags(pdn->dsdb_dn->dn);
4826 dsdb_get_extended_dn_guid(pdn->dsdb_dn->dn, &invocation_id, "RMD_INVOCID");
4827 dsdb_get_extended_dn_uint32(pdn->dsdb_dn->dn, &version, "RMD_VERSION");
4828 dsdb_get_extended_dn_uint32(pdn->dsdb_dn->dn, &originating_usn, "RMD_ORIGINATING_USN");
4829 dsdb_get_extended_dn_nttime(pdn->dsdb_dn->dn, &change_time, "RMD_CHANGETIME");
4831 if (!replmd_update_is_newer(&invocation_id,
4832 &la->meta_data.originating_invocation_id,
4833 version,
4834 la->meta_data.version,
4835 change_time,
4836 la->meta_data.originating_change_time)) {
4837 DEBUG(3,("Discarding older DRS linked attribute update to %s on %s from %s\n",
4838 old_el->name, ldb_dn_get_linearized(msg->dn),
4839 GUID_string(tmp_ctx, &la->meta_data.originating_invocation_id)));
4840 talloc_free(tmp_ctx);
4841 return LDB_SUCCESS;
4844 /* get a seq_num for this change */
4845 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
4846 if (ret != LDB_SUCCESS) {
4847 talloc_free(tmp_ctx);
4848 return ret;
4851 if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
4852 /* remove the existing backlink */
4853 ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid, false, attr, false);
4854 if (ret != LDB_SUCCESS) {
4855 talloc_free(tmp_ctx);
4856 return ret;
4860 ret = replmd_update_la_val(tmp_ctx, pdn->v, dsdb_dn, pdn->dsdb_dn,
4861 &la->meta_data.originating_invocation_id,
4862 la->meta_data.originating_usn, seq_num,
4863 la->meta_data.originating_change_time,
4864 la->meta_data.version,
4865 !active);
4866 if (ret != LDB_SUCCESS) {
4867 talloc_free(tmp_ctx);
4868 return ret;
4871 if (active) {
4872 /* add the new backlink */
4873 ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid, true, attr, false);
4874 if (ret != LDB_SUCCESS) {
4875 talloc_free(tmp_ctx);
4876 return ret;
4879 } else {
4880 /* get a seq_num for this change */
4881 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
4882 if (ret != LDB_SUCCESS) {
4883 talloc_free(tmp_ctx);
4884 return ret;
4887 old_el->values = talloc_realloc(msg->elements, old_el->values,
4888 struct ldb_val, old_el->num_values+1);
4889 if (!old_el->values) {
4890 ldb_module_oom(module);
4891 return LDB_ERR_OPERATIONS_ERROR;
4893 old_el->num_values++;
4895 ret = replmd_build_la_val(tmp_ctx, &old_el->values[old_el->num_values-1], dsdb_dn,
4896 &la->meta_data.originating_invocation_id,
4897 la->meta_data.originating_usn, seq_num,
4898 la->meta_data.originating_change_time,
4899 la->meta_data.version,
4900 (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)?false:true);
4901 if (ret != LDB_SUCCESS) {
4902 talloc_free(tmp_ctx);
4903 return ret;
4906 if (active) {
4907 ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid,
4908 true, attr, false);
4909 if (ret != LDB_SUCCESS) {
4910 talloc_free(tmp_ctx);
4911 return ret;
4916 /* we only change whenChanged and uSNChanged if the seq_num
4917 has changed */
4918 ret = add_time_element(msg, "whenChanged", t);
4919 if (ret != LDB_SUCCESS) {
4920 talloc_free(tmp_ctx);
4921 ldb_operr(ldb);
4922 return ret;
4925 ret = add_uint64_element(ldb, msg, "uSNChanged", seq_num);
4926 if (ret != LDB_SUCCESS) {
4927 talloc_free(tmp_ctx);
4928 ldb_operr(ldb);
4929 return ret;
4932 old_el = ldb_msg_find_element(msg, attr->lDAPDisplayName);
4933 if (old_el == NULL) {
4934 talloc_free(tmp_ctx);
4935 return ldb_operr(ldb);
4938 ret = dsdb_check_single_valued_link(attr, old_el);
4939 if (ret != LDB_SUCCESS) {
4940 talloc_free(tmp_ctx);
4941 return ret;
4944 old_el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
4946 ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE, parent);
4947 if (ret != LDB_SUCCESS) {
4948 ldb_debug(ldb, LDB_DEBUG_WARNING, "Failed to apply linked attribute change '%s'\n%s\n",
4949 ldb_errstring(ldb),
4950 ldb_ldif_message_string(ldb, tmp_ctx, LDB_CHANGETYPE_MODIFY, msg));
4951 talloc_free(tmp_ctx);
4952 return ret;
4955 talloc_free(tmp_ctx);
4957 return ret;
4960 static int replmd_extended(struct ldb_module *module, struct ldb_request *req)
4962 if (strcmp(req->op.extended.oid, DSDB_EXTENDED_REPLICATED_OBJECTS_OID) == 0) {
4963 return replmd_extended_replicated_objects(module, req);
4966 return ldb_next_request(module, req);
4971 we hook into the transaction operations to allow us to
4972 perform the linked attribute updates at the end of the whole
4973 transaction. This allows a forward linked attribute to be created
4974 before the object is created. During a vampire, w2k8 sends us linked
4975 attributes before the objects they are part of.
4977 static int replmd_start_transaction(struct ldb_module *module)
4979 /* create our private structure for this transaction */
4980 struct replmd_private *replmd_private = talloc_get_type(ldb_module_get_private(module),
4981 struct replmd_private);
4982 replmd_txn_cleanup(replmd_private);
4984 /* free any leftover mod_usn records from cancelled
4985 transactions */
4986 while (replmd_private->ncs) {
4987 struct nc_entry *e = replmd_private->ncs;
4988 DLIST_REMOVE(replmd_private->ncs, e);
4989 talloc_free(e);
4992 return ldb_next_start_trans(module);
4996 on prepare commit we loop over our queued la_context structures and
4997 apply each of them
4999 static int replmd_prepare_commit(struct ldb_module *module)
5001 struct replmd_private *replmd_private =
5002 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
5003 struct la_entry *la, *prev;
5004 struct la_backlink *bl;
5005 int ret;
5007 /* walk the list backwards, to do the first entry first, as we
5008 * added the entries with DLIST_ADD() which puts them at the
5009 * start of the list */
5010 for (la = DLIST_TAIL(replmd_private->la_list); la; la=prev) {
5011 prev = DLIST_PREV(la);
5012 DLIST_REMOVE(replmd_private->la_list, la);
5013 ret = replmd_process_linked_attribute(module, la, NULL);
5014 if (ret != LDB_SUCCESS) {
5015 replmd_txn_cleanup(replmd_private);
5016 return ret;
5020 /* process our backlink list, creating and deleting backlinks
5021 as necessary */
5022 for (bl=replmd_private->la_backlinks; bl; bl=bl->next) {
5023 ret = replmd_process_backlink(module, bl, NULL);
5024 if (ret != LDB_SUCCESS) {
5025 replmd_txn_cleanup(replmd_private);
5026 return ret;
5030 replmd_txn_cleanup(replmd_private);
5032 /* possibly change @REPLCHANGED */
5033 ret = replmd_notify_store(module, NULL);
5034 if (ret != LDB_SUCCESS) {
5035 return ret;
5038 return ldb_next_prepare_commit(module);
5041 static int replmd_del_transaction(struct ldb_module *module)
5043 struct replmd_private *replmd_private =
5044 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
5045 replmd_txn_cleanup(replmd_private);
5047 return ldb_next_del_trans(module);
5051 static const struct ldb_module_ops ldb_repl_meta_data_module_ops = {
5052 .name = "repl_meta_data",
5053 .init_context = replmd_init,
5054 .add = replmd_add,
5055 .modify = replmd_modify,
5056 .rename = replmd_rename,
5057 .del = replmd_delete,
5058 .extended = replmd_extended,
5059 .start_transaction = replmd_start_transaction,
5060 .prepare_commit = replmd_prepare_commit,
5061 .del_transaction = replmd_del_transaction,
5064 int ldb_repl_meta_data_module_init(const char *version)
5066 LDB_MODULE_CHECK_VERSION(version);
5067 return ldb_register_module(&ldb_repl_meta_data_module_ops);