s4-dsdb: if the provision control is specified, update replication metadata even...
[Samba/gebeck_regimport.git] / source4 / dsdb / samdb / ldb_modules / repl_meta_data.c
bloba76b88ecbc81814a18c1664246a43e599b571dea
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"
53 struct replmd_private {
54 TALLOC_CTX *la_ctx;
55 struct la_entry *la_list;
56 TALLOC_CTX *bl_ctx;
57 struct la_backlink *la_backlinks;
58 struct nc_entry {
59 struct nc_entry *prev, *next;
60 struct ldb_dn *dn;
61 uint64_t mod_usn;
62 uint64_t mod_usn_urgent;
63 } *ncs;
66 struct la_entry {
67 struct la_entry *next, *prev;
68 struct drsuapi_DsReplicaLinkedAttribute *la;
71 struct replmd_replicated_request {
72 struct ldb_module *module;
73 struct ldb_request *req;
75 const struct dsdb_schema *schema;
77 /* the controls we pass down */
78 struct ldb_control **controls;
80 /* details for the mode where we apply a bunch of inbound replication meessages */
81 bool apply_mode;
82 uint32_t index_current;
83 struct dsdb_extended_replicated_objects *objs;
85 struct ldb_message *search_msg;
87 uint64_t seq_num;
88 bool is_urgent;
91 enum urgent_situation {
92 REPL_URGENT_ON_CREATE = 1,
93 REPL_URGENT_ON_UPDATE = 2,
94 REPL_URGENT_ON_DELETE = 4
98 static const struct {
99 const char *update_name;
100 enum urgent_situation repl_situation;
101 } urgent_objects[] = {
102 {"nTDSDSA", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE)},
103 {"crossRef", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE)},
104 {"attributeSchema", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
105 {"classSchema", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
106 {"secret", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
107 {"rIDManager", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
108 {NULL, 0}
111 /* Attributes looked for when updating or deleting, to check for a urgent replication needed */
112 static const char *urgent_attrs[] = {
113 "lockoutTime",
114 "pwdLastSet",
115 "userAccountControl",
116 NULL
120 static bool replmd_check_urgent_objectclass(const struct ldb_message_element *objectclass_el,
121 enum urgent_situation situation)
123 unsigned int i, j;
124 for (i=0; urgent_objects[i].update_name; i++) {
126 if ((situation & urgent_objects[i].repl_situation) == 0) {
127 continue;
130 for (j=0; j<objectclass_el->num_values; j++) {
131 const struct ldb_val *v = &objectclass_el->values[j];
132 if (ldb_attr_cmp((const char *)v->data, urgent_objects[i].update_name) == 0) {
133 return true;
137 return false;
140 static bool replmd_check_urgent_attribute(const struct ldb_message_element *el)
142 if (ldb_attr_in_list(urgent_attrs, el->name)) {
143 return true;
145 return false;
149 static int replmd_replicated_apply_next(struct replmd_replicated_request *ar);
152 initialise the module
153 allocate the private structure and build the list
154 of partition DNs for use by replmd_notify()
156 static int replmd_init(struct ldb_module *module)
158 struct replmd_private *replmd_private;
159 struct ldb_context *ldb = ldb_module_get_ctx(module);
161 replmd_private = talloc_zero(module, struct replmd_private);
162 if (replmd_private == NULL) {
163 ldb_oom(ldb);
164 return LDB_ERR_OPERATIONS_ERROR;
166 ldb_module_set_private(module, replmd_private);
168 return ldb_next_init(module);
172 cleanup our per-transaction contexts
174 static void replmd_txn_cleanup(struct replmd_private *replmd_private)
176 talloc_free(replmd_private->la_ctx);
177 replmd_private->la_list = NULL;
178 replmd_private->la_ctx = NULL;
180 talloc_free(replmd_private->bl_ctx);
181 replmd_private->la_backlinks = NULL;
182 replmd_private->bl_ctx = NULL;
186 struct la_backlink {
187 struct la_backlink *next, *prev;
188 const char *attr_name;
189 struct GUID forward_guid, target_guid;
190 bool active;
194 process a backlinks we accumulated during a transaction, adding and
195 deleting the backlinks from the target objects
197 static int replmd_process_backlink(struct ldb_module *module, struct la_backlink *bl, struct ldb_request *parent)
199 struct ldb_dn *target_dn, *source_dn;
200 int ret;
201 struct ldb_context *ldb = ldb_module_get_ctx(module);
202 struct ldb_message *msg;
203 TALLOC_CTX *tmp_ctx = talloc_new(bl);
204 char *dn_string;
207 - find DN of target
208 - find DN of source
209 - construct ldb_message
210 - either an add or a delete
212 ret = dsdb_module_dn_by_guid(module, tmp_ctx, &bl->target_guid, &target_dn, parent);
213 if (ret != LDB_SUCCESS) {
214 DEBUG(2,(__location__ ": WARNING: Failed to find target DN for linked attribute with GUID %s\n",
215 GUID_string(bl, &bl->target_guid)));
216 return LDB_SUCCESS;
219 ret = dsdb_module_dn_by_guid(module, tmp_ctx, &bl->forward_guid, &source_dn, parent);
220 if (ret != LDB_SUCCESS) {
221 ldb_asprintf_errstring(ldb, "Failed to find source DN for linked attribute with GUID %s\n",
222 GUID_string(bl, &bl->forward_guid));
223 talloc_free(tmp_ctx);
224 return ret;
227 msg = ldb_msg_new(tmp_ctx);
228 if (msg == NULL) {
229 ldb_module_oom(module);
230 talloc_free(tmp_ctx);
231 return LDB_ERR_OPERATIONS_ERROR;
234 /* construct a ldb_message for adding/deleting the backlink */
235 msg->dn = target_dn;
236 dn_string = ldb_dn_get_extended_linearized(tmp_ctx, source_dn, 1);
237 if (!dn_string) {
238 ldb_module_oom(module);
239 talloc_free(tmp_ctx);
240 return LDB_ERR_OPERATIONS_ERROR;
242 ret = ldb_msg_add_steal_string(msg, bl->attr_name, dn_string);
243 if (ret != LDB_SUCCESS) {
244 talloc_free(tmp_ctx);
245 return ret;
247 msg->elements[0].flags = bl->active?LDB_FLAG_MOD_ADD:LDB_FLAG_MOD_DELETE;
249 /* a backlink should never be single valued. Unfortunately the
250 exchange schema has a attribute
251 msExchBridgeheadedLocalConnectorsDNBL which is single
252 valued and a backlink. We need to cope with that by
253 ignoring the single value flag */
254 msg->elements[0].flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
256 ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE, parent);
257 if (ret == LDB_ERR_NO_SUCH_ATTRIBUTE && !bl->active) {
258 /* we allow LDB_ERR_NO_SUCH_ATTRIBUTE as success to
259 cope with possible corruption where the backlink has
260 already been removed */
261 DEBUG(0,("WARNING: backlink from %s already removed from %s - %s\n",
262 ldb_dn_get_linearized(target_dn),
263 ldb_dn_get_linearized(source_dn),
264 ldb_errstring(ldb)));
265 ret = LDB_SUCCESS;
266 } else if (ret != LDB_SUCCESS) {
267 ldb_asprintf_errstring(ldb, "Failed to %s backlink from %s to %s - %s",
268 bl->active?"add":"remove",
269 ldb_dn_get_linearized(source_dn),
270 ldb_dn_get_linearized(target_dn),
271 ldb_errstring(ldb));
272 talloc_free(tmp_ctx);
273 return ret;
275 talloc_free(tmp_ctx);
276 return ret;
280 add a backlink to the list of backlinks to add/delete in the prepare
281 commit
283 static int replmd_add_backlink(struct ldb_module *module, const struct dsdb_schema *schema,
284 struct GUID *forward_guid, struct GUID *target_guid,
285 bool active, const struct dsdb_attribute *schema_attr, bool immediate)
287 const struct dsdb_attribute *target_attr;
288 struct la_backlink *bl;
289 struct replmd_private *replmd_private =
290 talloc_get_type_abort(ldb_module_get_private(module), struct replmd_private);
292 target_attr = dsdb_attribute_by_linkID(schema, schema_attr->linkID ^ 1);
293 if (!target_attr) {
295 * windows 2003 has a broken schema where the
296 * definition of msDS-IsDomainFor is missing (which is
297 * supposed to be the backlink of the
298 * msDS-HasDomainNCs attribute
300 return LDB_SUCCESS;
303 /* see if its already in the list */
304 for (bl=replmd_private->la_backlinks; bl; bl=bl->next) {
305 if (GUID_equal(forward_guid, &bl->forward_guid) &&
306 GUID_equal(target_guid, &bl->target_guid) &&
307 (target_attr->lDAPDisplayName == bl->attr_name ||
308 strcmp(target_attr->lDAPDisplayName, bl->attr_name) == 0)) {
309 break;
313 if (bl) {
314 /* we found an existing one */
315 if (bl->active == active) {
316 return LDB_SUCCESS;
318 DLIST_REMOVE(replmd_private->la_backlinks, bl);
319 talloc_free(bl);
320 return LDB_SUCCESS;
323 if (replmd_private->bl_ctx == NULL) {
324 replmd_private->bl_ctx = talloc_new(replmd_private);
325 if (replmd_private->bl_ctx == NULL) {
326 ldb_module_oom(module);
327 return LDB_ERR_OPERATIONS_ERROR;
331 /* its a new one */
332 bl = talloc(replmd_private->bl_ctx, struct la_backlink);
333 if (bl == NULL) {
334 ldb_module_oom(module);
335 return LDB_ERR_OPERATIONS_ERROR;
338 /* Ensure the schema does not go away before the bl->attr_name is used */
339 if (!talloc_reference(bl, schema)) {
340 talloc_free(bl);
341 ldb_module_oom(module);
342 return LDB_ERR_OPERATIONS_ERROR;
345 bl->attr_name = target_attr->lDAPDisplayName;
346 bl->forward_guid = *forward_guid;
347 bl->target_guid = *target_guid;
348 bl->active = active;
350 /* the caller may ask for this backlink to be processed
351 immediately */
352 if (immediate) {
353 int ret = replmd_process_backlink(module, bl, NULL);
354 talloc_free(bl);
355 return ret;
358 DLIST_ADD(replmd_private->la_backlinks, bl);
360 return LDB_SUCCESS;
365 * Callback for most write operations in this module:
367 * notify the repl task that a object has changed. The notifies are
368 * gathered up in the replmd_private structure then written to the
369 * @REPLCHANGED object in each partition during the prepare_commit
371 static int replmd_op_callback(struct ldb_request *req, struct ldb_reply *ares)
373 int ret;
374 struct replmd_replicated_request *ac =
375 talloc_get_type_abort(req->context, struct replmd_replicated_request);
376 struct replmd_private *replmd_private =
377 talloc_get_type_abort(ldb_module_get_private(ac->module), struct replmd_private);
378 struct nc_entry *modified_partition;
379 struct ldb_control *partition_ctrl;
380 const struct dsdb_control_current_partition *partition;
382 struct ldb_control **controls;
384 partition_ctrl = ldb_reply_get_control(ares, DSDB_CONTROL_CURRENT_PARTITION_OID);
386 controls = ares->controls;
387 if (ldb_request_get_control(ac->req,
388 DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
390 * Remove the current partition control from what we pass up
391 * the chain if it hasn't been requested manually.
393 controls = ldb_controls_except_specified(ares->controls, ares,
394 partition_ctrl);
397 if (ares->error != LDB_SUCCESS) {
398 DEBUG(0,("%s failure. Error is: %s\n", __FUNCTION__, ldb_strerror(ares->error)));
399 return ldb_module_done(ac->req, controls,
400 ares->response, ares->error);
403 if (ares->type != LDB_REPLY_DONE) {
404 ldb_set_errstring(ldb_module_get_ctx(ac->module), "Invalid reply type for notify\n!");
405 return ldb_module_done(ac->req, NULL,
406 NULL, LDB_ERR_OPERATIONS_ERROR);
409 if (!partition_ctrl) {
410 ldb_set_errstring(ldb_module_get_ctx(ac->module),"No partition control on reply");
411 return ldb_module_done(ac->req, NULL,
412 NULL, LDB_ERR_OPERATIONS_ERROR);
415 partition = talloc_get_type_abort(partition_ctrl->data,
416 struct dsdb_control_current_partition);
418 if (ac->seq_num > 0) {
419 for (modified_partition = replmd_private->ncs; modified_partition;
420 modified_partition = modified_partition->next) {
421 if (ldb_dn_compare(modified_partition->dn, partition->dn) == 0) {
422 break;
426 if (modified_partition == NULL) {
427 modified_partition = talloc_zero(replmd_private, struct nc_entry);
428 if (!modified_partition) {
429 ldb_oom(ldb_module_get_ctx(ac->module));
430 return ldb_module_done(ac->req, NULL,
431 NULL, LDB_ERR_OPERATIONS_ERROR);
433 modified_partition->dn = ldb_dn_copy(modified_partition, partition->dn);
434 if (!modified_partition->dn) {
435 ldb_oom(ldb_module_get_ctx(ac->module));
436 return ldb_module_done(ac->req, NULL,
437 NULL, LDB_ERR_OPERATIONS_ERROR);
439 DLIST_ADD(replmd_private->ncs, modified_partition);
442 if (ac->seq_num > modified_partition->mod_usn) {
443 modified_partition->mod_usn = ac->seq_num;
444 if (ac->is_urgent) {
445 modified_partition->mod_usn_urgent = ac->seq_num;
450 if (ac->apply_mode) {
451 talloc_free(ares);
452 ac->index_current++;
454 ret = replmd_replicated_apply_next(ac);
455 if (ret != LDB_SUCCESS) {
456 return ldb_module_done(ac->req, NULL, NULL, ret);
458 return ret;
459 } else {
460 /* free the partition control container here, for the
461 * common path. Other cases will have it cleaned up
462 * eventually with the ares */
463 talloc_free(partition_ctrl);
464 return ldb_module_done(ac->req, controls,
465 ares->response, LDB_SUCCESS);
471 * update a @REPLCHANGED record in each partition if there have been
472 * any writes of replicated data in the partition
474 static int replmd_notify_store(struct ldb_module *module, struct ldb_request *parent)
476 struct replmd_private *replmd_private =
477 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
479 while (replmd_private->ncs) {
480 int ret;
481 struct nc_entry *modified_partition = replmd_private->ncs;
483 ret = dsdb_module_save_partition_usn(module, modified_partition->dn,
484 modified_partition->mod_usn,
485 modified_partition->mod_usn_urgent, parent);
486 if (ret != LDB_SUCCESS) {
487 DEBUG(0,(__location__ ": Failed to save partition uSN for %s\n",
488 ldb_dn_get_linearized(modified_partition->dn)));
489 return ret;
491 DLIST_REMOVE(replmd_private->ncs, modified_partition);
492 talloc_free(modified_partition);
495 return LDB_SUCCESS;
500 created a replmd_replicated_request context
502 static struct replmd_replicated_request *replmd_ctx_init(struct ldb_module *module,
503 struct ldb_request *req)
505 struct ldb_context *ldb;
506 struct replmd_replicated_request *ac;
508 ldb = ldb_module_get_ctx(module);
510 ac = talloc_zero(req, struct replmd_replicated_request);
511 if (ac == NULL) {
512 ldb_oom(ldb);
513 return NULL;
516 ac->module = module;
517 ac->req = req;
519 ac->schema = dsdb_get_schema(ldb, ac);
520 if (!ac->schema) {
521 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
522 "replmd_modify: no dsdb_schema loaded");
523 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
524 return NULL;
527 return ac;
531 add a time element to a record
533 static int add_time_element(struct ldb_message *msg, const char *attr, time_t t)
535 struct ldb_message_element *el;
536 char *s;
537 int ret;
539 if (ldb_msg_find_element(msg, attr) != NULL) {
540 return LDB_SUCCESS;
543 s = ldb_timestring(msg, t);
544 if (s == NULL) {
545 return LDB_ERR_OPERATIONS_ERROR;
548 ret = ldb_msg_add_string(msg, attr, s);
549 if (ret != LDB_SUCCESS) {
550 return ret;
553 el = ldb_msg_find_element(msg, attr);
554 /* always set as replace. This works because on add ops, the flag
555 is ignored */
556 el->flags = LDB_FLAG_MOD_REPLACE;
558 return LDB_SUCCESS;
562 add a uint64_t element to a record
564 static int add_uint64_element(struct ldb_context *ldb, struct ldb_message *msg,
565 const char *attr, uint64_t v)
567 struct ldb_message_element *el;
568 int ret;
570 if (ldb_msg_find_element(msg, attr) != NULL) {
571 return LDB_SUCCESS;
574 ret = samdb_msg_add_uint64(ldb, msg, msg, attr, v);
575 if (ret != LDB_SUCCESS) {
576 return ret;
579 el = ldb_msg_find_element(msg, attr);
580 /* always set as replace. This works because on add ops, the flag
581 is ignored */
582 el->flags = LDB_FLAG_MOD_REPLACE;
584 return LDB_SUCCESS;
587 static int replmd_replPropertyMetaData1_attid_sort(const struct replPropertyMetaData1 *m1,
588 const struct replPropertyMetaData1 *m2,
589 const uint32_t *rdn_attid)
591 if (m1->attid == m2->attid) {
592 return 0;
596 * the rdn attribute should be at the end!
597 * so we need to return a value greater than zero
598 * which means m1 is greater than m2
600 if (m1->attid == *rdn_attid) {
601 return 1;
605 * the rdn attribute should be at the end!
606 * so we need to return a value less than zero
607 * which means m2 is greater than m1
609 if (m2->attid == *rdn_attid) {
610 return -1;
613 return m1->attid > m2->attid ? 1 : -1;
616 static int replmd_replPropertyMetaDataCtr1_sort(struct replPropertyMetaDataCtr1 *ctr1,
617 const struct dsdb_schema *schema,
618 struct ldb_dn *dn)
620 const char *rdn_name;
621 const struct dsdb_attribute *rdn_sa;
623 rdn_name = ldb_dn_get_rdn_name(dn);
624 if (!rdn_name) {
625 DEBUG(0,(__location__ ": No rDN for %s?\n", ldb_dn_get_linearized(dn)));
626 return LDB_ERR_OPERATIONS_ERROR;
629 rdn_sa = dsdb_attribute_by_lDAPDisplayName(schema, rdn_name);
630 if (rdn_sa == NULL) {
631 DEBUG(0,(__location__ ": No sa found for rDN %s for %s\n", rdn_name, ldb_dn_get_linearized(dn)));
632 return LDB_ERR_OPERATIONS_ERROR;
635 DEBUG(6,("Sorting rpmd with attid exception %u rDN=%s DN=%s\n",
636 rdn_sa->attributeID_id, rdn_name, ldb_dn_get_linearized(dn)));
638 LDB_TYPESAFE_QSORT(ctr1->array, ctr1->count, &rdn_sa->attributeID_id, replmd_replPropertyMetaData1_attid_sort);
640 return LDB_SUCCESS;
643 static int replmd_ldb_message_element_attid_sort(const struct ldb_message_element *e1,
644 const struct ldb_message_element *e2,
645 const struct dsdb_schema *schema)
647 const struct dsdb_attribute *a1;
648 const struct dsdb_attribute *a2;
651 * TODO: make this faster by caching the dsdb_attribute pointer
652 * on the ldb_messag_element
655 a1 = dsdb_attribute_by_lDAPDisplayName(schema, e1->name);
656 a2 = dsdb_attribute_by_lDAPDisplayName(schema, e2->name);
659 * TODO: remove this check, we should rely on e1 and e2 having valid attribute names
660 * in the schema
662 if (!a1 || !a2) {
663 return strcasecmp(e1->name, e2->name);
665 if (a1->attributeID_id == a2->attributeID_id) {
666 return 0;
668 return a1->attributeID_id > a2->attributeID_id ? 1 : -1;
671 static void replmd_ldb_message_sort(struct ldb_message *msg,
672 const struct dsdb_schema *schema)
674 LDB_TYPESAFE_QSORT(msg->elements, msg->num_elements, schema, replmd_ldb_message_element_attid_sort);
677 static int replmd_build_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
678 const struct GUID *invocation_id, uint64_t seq_num,
679 uint64_t local_usn, NTTIME nttime, uint32_t version, bool deleted);
683 fix up linked attributes in replmd_add.
684 This involves setting up the right meta-data in extended DN
685 components, and creating backlinks to the object
687 static int replmd_add_fix_la(struct ldb_module *module, struct ldb_message_element *el,
688 uint64_t seq_num, const struct GUID *invocationId, time_t t,
689 struct GUID *guid, const struct dsdb_attribute *sa, struct ldb_request *parent)
691 unsigned int i;
692 TALLOC_CTX *tmp_ctx = talloc_new(el->values);
693 struct ldb_context *ldb = ldb_module_get_ctx(module);
695 /* We will take a reference to the schema in replmd_add_backlink */
696 const struct dsdb_schema *schema = dsdb_get_schema(ldb, NULL);
697 NTTIME now;
699 unix_to_nt_time(&now, t);
701 for (i=0; i<el->num_values; i++) {
702 struct ldb_val *v = &el->values[i];
703 struct dsdb_dn *dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, v, sa->syntax->ldap_oid);
704 struct GUID target_guid;
705 NTSTATUS status;
706 int ret;
708 /* note that the DN already has the extended
709 components from the extended_dn_store module */
710 status = dsdb_get_extended_dn_guid(dsdb_dn->dn, &target_guid, "GUID");
711 if (!NT_STATUS_IS_OK(status) || GUID_all_zero(&target_guid)) {
712 ret = dsdb_module_guid_by_dn(module, dsdb_dn->dn, &target_guid, parent);
713 if (ret != LDB_SUCCESS) {
714 talloc_free(tmp_ctx);
715 return ret;
717 ret = dsdb_set_extended_dn_guid(dsdb_dn->dn, &target_guid, "GUID");
718 if (ret != LDB_SUCCESS) {
719 talloc_free(tmp_ctx);
720 return ret;
724 ret = replmd_build_la_val(el->values, v, dsdb_dn, invocationId,
725 seq_num, seq_num, now, 0, false);
726 if (ret != LDB_SUCCESS) {
727 talloc_free(tmp_ctx);
728 return ret;
731 ret = replmd_add_backlink(module, schema, guid, &target_guid, true, sa, false);
732 if (ret != LDB_SUCCESS) {
733 talloc_free(tmp_ctx);
734 return ret;
738 talloc_free(tmp_ctx);
739 return LDB_SUCCESS;
744 intercept add requests
746 static int replmd_add(struct ldb_module *module, struct ldb_request *req)
748 struct ldb_context *ldb;
749 struct ldb_control *control;
750 struct replmd_replicated_request *ac;
751 enum ndr_err_code ndr_err;
752 struct ldb_request *down_req;
753 struct ldb_message *msg;
754 const DATA_BLOB *guid_blob;
755 struct GUID guid;
756 struct replPropertyMetaDataBlob nmd;
757 struct ldb_val nmd_value;
758 const struct GUID *our_invocation_id;
759 time_t t = time(NULL);
760 NTTIME now;
761 char *time_str;
762 int ret;
763 unsigned int i;
764 unsigned int functional_level;
765 uint32_t ni=0;
766 bool allow_add_guid = false;
767 bool remove_current_guid = false;
768 bool is_urgent = false;
769 struct ldb_message_element *objectclass_el;
771 /* check if there's a show relax control (used by provision to say 'I know what I'm doing') */
772 control = ldb_request_get_control(req, LDB_CONTROL_RELAX_OID);
773 if (control) {
774 allow_add_guid = true;
777 /* do not manipulate our control entries */
778 if (ldb_dn_is_special(req->op.add.message->dn)) {
779 return ldb_next_request(module, req);
782 ldb = ldb_module_get_ctx(module);
784 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_add\n");
786 guid_blob = ldb_msg_find_ldb_val(req->op.add.message, "objectGUID");
787 if (guid_blob != NULL) {
788 if (!allow_add_guid) {
789 ldb_set_errstring(ldb,
790 "replmd_add: it's not allowed to add an object with objectGUID!");
791 return LDB_ERR_UNWILLING_TO_PERFORM;
792 } else {
793 NTSTATUS status = GUID_from_data_blob(guid_blob,&guid);
794 if (!NT_STATUS_IS_OK(status)) {
795 ldb_set_errstring(ldb,
796 "replmd_add: Unable to parse the 'objectGUID' as a GUID!");
797 return LDB_ERR_UNWILLING_TO_PERFORM;
799 /* we remove this attribute as it can be a string and
800 * will not be treated correctly and then we will re-add
801 * it later on in the good format */
802 remove_current_guid = true;
804 } else {
805 /* a new GUID */
806 guid = GUID_random();
809 ac = replmd_ctx_init(module, req);
810 if (ac == NULL) {
811 return ldb_module_oom(module);
814 functional_level = dsdb_functional_level(ldb);
816 /* Get a sequence number from the backend */
817 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ac->seq_num);
818 if (ret != LDB_SUCCESS) {
819 talloc_free(ac);
820 return ret;
823 /* get our invocationId */
824 our_invocation_id = samdb_ntds_invocation_id(ldb);
825 if (!our_invocation_id) {
826 ldb_debug_set(ldb, LDB_DEBUG_ERROR,
827 "replmd_add: unable to find invocationId\n");
828 talloc_free(ac);
829 return LDB_ERR_OPERATIONS_ERROR;
832 /* we have to copy the message as the caller might have it as a const */
833 msg = ldb_msg_copy_shallow(ac, req->op.add.message);
834 if (msg == NULL) {
835 ldb_oom(ldb);
836 talloc_free(ac);
837 return LDB_ERR_OPERATIONS_ERROR;
840 /* generated times */
841 unix_to_nt_time(&now, t);
842 time_str = ldb_timestring(msg, t);
843 if (!time_str) {
844 ldb_oom(ldb);
845 talloc_free(ac);
846 return LDB_ERR_OPERATIONS_ERROR;
848 if (remove_current_guid) {
849 ldb_msg_remove_attr(msg,"objectGUID");
853 * remove autogenerated attributes
855 ldb_msg_remove_attr(msg, "whenCreated");
856 ldb_msg_remove_attr(msg, "whenChanged");
857 ldb_msg_remove_attr(msg, "uSNCreated");
858 ldb_msg_remove_attr(msg, "uSNChanged");
859 ldb_msg_remove_attr(msg, "replPropertyMetaData");
862 * readd replicated attributes
864 ret = ldb_msg_add_string(msg, "whenCreated", time_str);
865 if (ret != LDB_SUCCESS) {
866 ldb_oom(ldb);
867 talloc_free(ac);
868 return ret;
871 /* build the replication meta_data */
872 ZERO_STRUCT(nmd);
873 nmd.version = 1;
874 nmd.ctr.ctr1.count = msg->num_elements;
875 nmd.ctr.ctr1.array = talloc_array(msg,
876 struct replPropertyMetaData1,
877 nmd.ctr.ctr1.count);
878 if (!nmd.ctr.ctr1.array) {
879 ldb_oom(ldb);
880 talloc_free(ac);
881 return LDB_ERR_OPERATIONS_ERROR;
884 for (i=0; i < msg->num_elements; i++) {
885 struct ldb_message_element *e = &msg->elements[i];
886 struct replPropertyMetaData1 *m = &nmd.ctr.ctr1.array[ni];
887 const struct dsdb_attribute *sa;
889 if (e->name[0] == '@') continue;
891 sa = dsdb_attribute_by_lDAPDisplayName(ac->schema, e->name);
892 if (!sa) {
893 ldb_debug_set(ldb, LDB_DEBUG_ERROR,
894 "replmd_add: attribute '%s' not defined in schema\n",
895 e->name);
896 talloc_free(ac);
897 return LDB_ERR_NO_SUCH_ATTRIBUTE;
900 if ((sa->systemFlags & DS_FLAG_ATTR_NOT_REPLICATED) || (sa->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED)) {
901 /* if the attribute is not replicated (0x00000001)
902 * or constructed (0x00000004) it has no metadata
904 continue;
907 if (sa->linkID != 0 && functional_level > DS_DOMAIN_FUNCTION_2000) {
908 ret = replmd_add_fix_la(module, e, ac->seq_num, our_invocation_id, t, &guid, sa, req);
909 if (ret != LDB_SUCCESS) {
910 talloc_free(ac);
911 return ret;
913 /* linked attributes are not stored in
914 replPropertyMetaData in FL above w2k */
915 continue;
918 m->attid = sa->attributeID_id;
919 m->version = 1;
920 m->originating_change_time = now;
921 m->originating_invocation_id = *our_invocation_id;
922 m->originating_usn = ac->seq_num;
923 m->local_usn = ac->seq_num;
924 ni++;
927 /* fix meta data count */
928 nmd.ctr.ctr1.count = ni;
931 * sort meta data array, and move the rdn attribute entry to the end
933 ret = replmd_replPropertyMetaDataCtr1_sort(&nmd.ctr.ctr1, ac->schema, msg->dn);
934 if (ret != LDB_SUCCESS) {
935 talloc_free(ac);
936 return ret;
939 /* generated NDR encoded values */
940 ndr_err = ndr_push_struct_blob(&nmd_value, msg,
941 &nmd,
942 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
943 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
944 ldb_oom(ldb);
945 talloc_free(ac);
946 return LDB_ERR_OPERATIONS_ERROR;
950 * add the autogenerated values
952 ret = dsdb_msg_add_guid(msg, &guid, "objectGUID");
953 if (ret != LDB_SUCCESS) {
954 ldb_oom(ldb);
955 talloc_free(ac);
956 return ret;
958 ret = ldb_msg_add_string(msg, "whenChanged", time_str);
959 if (ret != LDB_SUCCESS) {
960 ldb_oom(ldb);
961 talloc_free(ac);
962 return ret;
964 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", ac->seq_num);
965 if (ret != LDB_SUCCESS) {
966 ldb_oom(ldb);
967 talloc_free(ac);
968 return ret;
970 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ac->seq_num);
971 if (ret != LDB_SUCCESS) {
972 ldb_oom(ldb);
973 talloc_free(ac);
974 return ret;
976 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
977 if (ret != LDB_SUCCESS) {
978 ldb_oom(ldb);
979 talloc_free(ac);
980 return ret;
984 * sort the attributes by attid before storing the object
986 replmd_ldb_message_sort(msg, ac->schema);
988 objectclass_el = ldb_msg_find_element(msg, "objectClass");
989 is_urgent = replmd_check_urgent_objectclass(objectclass_el,
990 REPL_URGENT_ON_CREATE);
992 ac->is_urgent = is_urgent;
993 ret = ldb_build_add_req(&down_req, ldb, ac,
994 msg,
995 req->controls,
996 ac, replmd_op_callback,
997 req);
999 LDB_REQ_SET_LOCATION(down_req);
1000 if (ret != LDB_SUCCESS) {
1001 talloc_free(ac);
1002 return ret;
1005 /* current partition control is needed by "replmd_op_callback" */
1006 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
1007 ret = ldb_request_add_control(down_req,
1008 DSDB_CONTROL_CURRENT_PARTITION_OID,
1009 false, NULL);
1010 if (ret != LDB_SUCCESS) {
1011 talloc_free(ac);
1012 return ret;
1016 if (functional_level == DS_DOMAIN_FUNCTION_2000) {
1017 ret = ldb_request_add_control(down_req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
1018 if (ret != LDB_SUCCESS) {
1019 talloc_free(ac);
1020 return ret;
1024 /* mark the control done */
1025 if (control) {
1026 control->critical = 0;
1029 /* go on with the call chain */
1030 return ldb_next_request(module, down_req);
1035 * update the replPropertyMetaData for one element
1037 static int replmd_update_rpmd_element(struct ldb_context *ldb,
1038 struct ldb_message *msg,
1039 struct ldb_message_element *el,
1040 struct ldb_message_element *old_el,
1041 struct replPropertyMetaDataBlob *omd,
1042 const struct dsdb_schema *schema,
1043 uint64_t *seq_num,
1044 const struct GUID *our_invocation_id,
1045 NTTIME now,
1046 struct ldb_request *req)
1048 uint32_t i;
1049 const struct dsdb_attribute *a;
1050 struct replPropertyMetaData1 *md1;
1052 a = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
1053 if (a == NULL) {
1054 if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)) {
1055 /* allow this to make it possible for dbcheck
1056 to remove bad attributes */
1057 return LDB_SUCCESS;
1060 DEBUG(0,(__location__ ": Unable to find attribute %s in schema\n",
1061 el->name));
1062 return LDB_ERR_OPERATIONS_ERROR;
1065 if ((a->systemFlags & DS_FLAG_ATTR_NOT_REPLICATED) || (a->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED)) {
1066 return LDB_SUCCESS;
1069 /* if the attribute's value haven't changed then return LDB_SUCCESS */
1070 if (old_el != NULL && ldb_msg_element_compare(el, old_el) == 0) {
1071 if (!ldb_request_get_control(req, LDB_CONTROL_PROVISION_OID)) {
1073 * allow this to make it possible for dbcheck
1074 * to rebuild broken metadata
1076 return LDB_SUCCESS;
1080 for (i=0; i<omd->ctr.ctr1.count; i++) {
1081 if (a->attributeID_id == omd->ctr.ctr1.array[i].attid) break;
1084 if (a->linkID != 0 && dsdb_functional_level(ldb) > DS_DOMAIN_FUNCTION_2000) {
1085 /* linked attributes are not stored in
1086 replPropertyMetaData in FL above w2k, but we do
1087 raise the seqnum for the object */
1088 if (*seq_num == 0 &&
1089 ldb_sequence_number(ldb, LDB_SEQ_NEXT, seq_num) != LDB_SUCCESS) {
1090 return LDB_ERR_OPERATIONS_ERROR;
1092 return LDB_SUCCESS;
1095 if (i == omd->ctr.ctr1.count) {
1096 /* we need to add a new one */
1097 omd->ctr.ctr1.array = talloc_realloc(msg, omd->ctr.ctr1.array,
1098 struct replPropertyMetaData1, omd->ctr.ctr1.count+1);
1099 if (omd->ctr.ctr1.array == NULL) {
1100 ldb_oom(ldb);
1101 return LDB_ERR_OPERATIONS_ERROR;
1103 omd->ctr.ctr1.count++;
1104 ZERO_STRUCT(omd->ctr.ctr1.array[i]);
1107 /* Get a new sequence number from the backend. We only do this
1108 * if we have a change that requires a new
1109 * replPropertyMetaData element
1111 if (*seq_num == 0) {
1112 int ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, seq_num);
1113 if (ret != LDB_SUCCESS) {
1114 return LDB_ERR_OPERATIONS_ERROR;
1118 md1 = &omd->ctr.ctr1.array[i];
1119 md1->version++;
1120 md1->attid = a->attributeID_id;
1121 md1->originating_change_time = now;
1122 md1->originating_invocation_id = *our_invocation_id;
1123 md1->originating_usn = *seq_num;
1124 md1->local_usn = *seq_num;
1126 return LDB_SUCCESS;
1129 static uint64_t find_max_local_usn(struct replPropertyMetaDataBlob omd)
1131 uint32_t count = omd.ctr.ctr1.count;
1132 uint64_t max = 0;
1133 uint32_t i;
1134 for (i=0; i < count; i++) {
1135 struct replPropertyMetaData1 m = omd.ctr.ctr1.array[i];
1136 if (max < m.local_usn) {
1137 max = m.local_usn;
1140 return max;
1144 * update the replPropertyMetaData object each time we modify an
1145 * object. This is needed for DRS replication, as the merge on the
1146 * client is based on this object
1148 static int replmd_update_rpmd(struct ldb_module *module,
1149 const struct dsdb_schema *schema,
1150 struct ldb_request *req,
1151 const char * const *rename_attrs,
1152 struct ldb_message *msg, uint64_t *seq_num,
1153 time_t t,
1154 bool *is_urgent)
1156 const struct ldb_val *omd_value;
1157 enum ndr_err_code ndr_err;
1158 struct replPropertyMetaDataBlob omd;
1159 unsigned int i;
1160 NTTIME now;
1161 const struct GUID *our_invocation_id;
1162 int ret;
1163 const char * const *attrs = NULL;
1164 const char * const attrs1[] = { "replPropertyMetaData", "*", NULL };
1165 const char * const attrs2[] = { "uSNChanged", "objectClass", NULL };
1166 struct ldb_result *res;
1167 struct ldb_context *ldb;
1168 struct ldb_message_element *objectclass_el;
1169 enum urgent_situation situation;
1170 bool rodc, rmd_is_provided;
1172 if (rename_attrs) {
1173 attrs = rename_attrs;
1174 } else {
1175 attrs = attrs1;
1178 ldb = ldb_module_get_ctx(module);
1180 our_invocation_id = samdb_ntds_invocation_id(ldb);
1181 if (!our_invocation_id) {
1182 /* this happens during an initial vampire while
1183 updating the schema */
1184 DEBUG(5,("No invocationID - skipping replPropertyMetaData update\n"));
1185 return LDB_SUCCESS;
1188 unix_to_nt_time(&now, t);
1190 if (ldb_request_get_control(req, DSDB_CONTROL_CHANGEREPLMETADATA_OID)) {
1191 rmd_is_provided = true;
1192 } else {
1193 rmd_is_provided = false;
1196 /* if isDeleted is present and is TRUE, then we consider we are deleting,
1197 * otherwise we consider we are updating */
1198 if (ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")) {
1199 situation = REPL_URGENT_ON_DELETE;
1200 } else if (rename_attrs) {
1201 situation = REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE;
1202 } else {
1203 situation = REPL_URGENT_ON_UPDATE;
1206 if (rmd_is_provided) {
1207 /* In this case the change_replmetadata control was supplied */
1208 /* We check that it's the only attribute that is provided
1209 * (it's a rare case so it's better to keep the code simplier)
1210 * We also check that the highest local_usn is bigger than
1211 * uSNChanged. */
1212 uint64_t db_seq;
1213 if( msg->num_elements != 1 ||
1214 strncmp(msg->elements[0].name,
1215 "replPropertyMetaData", 20) ) {
1216 DEBUG(0,(__location__ ": changereplmetada control called without "\
1217 "a specified replPropertyMetaData attribute or with others\n"));
1218 return LDB_ERR_OPERATIONS_ERROR;
1220 if (situation != REPL_URGENT_ON_UPDATE) {
1221 DEBUG(0,(__location__ ": changereplmetada control can't be called when deleting an object\n"));
1222 return LDB_ERR_OPERATIONS_ERROR;
1224 omd_value = ldb_msg_find_ldb_val(msg, "replPropertyMetaData");
1225 if (!omd_value) {
1226 DEBUG(0,(__location__ ": replPropertyMetaData was not specified for Object %s\n",
1227 ldb_dn_get_linearized(msg->dn)));
1228 return LDB_ERR_OPERATIONS_ERROR;
1230 ndr_err = ndr_pull_struct_blob(omd_value, msg, &omd,
1231 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
1232 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1233 DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s\n",
1234 ldb_dn_get_linearized(msg->dn)));
1235 return LDB_ERR_OPERATIONS_ERROR;
1237 *seq_num = find_max_local_usn(omd);
1239 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attrs2,
1240 DSDB_FLAG_NEXT_MODULE |
1241 DSDB_SEARCH_SHOW_RECYCLED |
1242 DSDB_SEARCH_SHOW_EXTENDED_DN |
1243 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
1244 DSDB_SEARCH_REVEAL_INTERNALS, req);
1246 if (ret != LDB_SUCCESS || res->count != 1) {
1247 DEBUG(0,(__location__ ": Object %s failed to find uSNChanged\n",
1248 ldb_dn_get_linearized(msg->dn)));
1249 return LDB_ERR_OPERATIONS_ERROR;
1252 objectclass_el = ldb_msg_find_element(res->msgs[0], "objectClass");
1253 if (is_urgent && replmd_check_urgent_objectclass(objectclass_el,
1254 situation)) {
1255 *is_urgent = true;
1258 db_seq = ldb_msg_find_attr_as_uint64(res->msgs[0], "uSNChanged", 0);
1259 if (*seq_num <= db_seq) {
1260 DEBUG(0,(__location__ ": changereplmetada control provided but max(local_usn)"\
1261 " is less or equal to uSNChanged (max = %lld uSNChanged = %lld)\n",
1262 (long long)*seq_num, (long long)db_seq));
1263 return LDB_ERR_OPERATIONS_ERROR;
1266 } else {
1267 /* search for the existing replPropertyMetaDataBlob. We need
1268 * to use REVEAL and ask for DNs in storage format to support
1269 * the check for values being the same in
1270 * replmd_update_rpmd_element()
1272 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attrs,
1273 DSDB_FLAG_NEXT_MODULE |
1274 DSDB_SEARCH_SHOW_RECYCLED |
1275 DSDB_SEARCH_SHOW_EXTENDED_DN |
1276 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
1277 DSDB_SEARCH_REVEAL_INTERNALS, req);
1278 if (ret != LDB_SUCCESS || res->count != 1) {
1279 DEBUG(0,(__location__ ": Object %s failed to find replPropertyMetaData\n",
1280 ldb_dn_get_linearized(msg->dn)));
1281 return LDB_ERR_OPERATIONS_ERROR;
1284 objectclass_el = ldb_msg_find_element(res->msgs[0], "objectClass");
1285 if (is_urgent && replmd_check_urgent_objectclass(objectclass_el,
1286 situation)) {
1287 *is_urgent = true;
1290 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
1291 if (!omd_value) {
1292 DEBUG(0,(__location__ ": Object %s does not have a replPropertyMetaData attribute\n",
1293 ldb_dn_get_linearized(msg->dn)));
1294 return LDB_ERR_OPERATIONS_ERROR;
1297 ndr_err = ndr_pull_struct_blob(omd_value, msg, &omd,
1298 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
1299 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1300 DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s\n",
1301 ldb_dn_get_linearized(msg->dn)));
1302 return LDB_ERR_OPERATIONS_ERROR;
1305 if (omd.version != 1) {
1306 DEBUG(0,(__location__ ": bad version %u in replPropertyMetaData for %s\n",
1307 omd.version, ldb_dn_get_linearized(msg->dn)));
1308 return LDB_ERR_OPERATIONS_ERROR;
1311 for (i=0; i<msg->num_elements; i++) {
1312 struct ldb_message_element *old_el;
1313 old_el = ldb_msg_find_element(res->msgs[0], msg->elements[i].name);
1314 ret = replmd_update_rpmd_element(ldb, msg, &msg->elements[i], old_el, &omd, schema, seq_num,
1315 our_invocation_id, now, req);
1316 if (ret != LDB_SUCCESS) {
1317 return ret;
1320 if (is_urgent && !*is_urgent && (situation == REPL_URGENT_ON_UPDATE)) {
1321 *is_urgent = replmd_check_urgent_attribute(&msg->elements[i]);
1327 * replmd_update_rpmd_element has done an update if the
1328 * seq_num is set
1330 if (*seq_num != 0) {
1331 struct ldb_val *md_value;
1332 struct ldb_message_element *el;
1334 /*if we are RODC and this is a DRSR update then its ok*/
1335 if (!ldb_request_get_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID)) {
1336 ret = samdb_rodc(ldb, &rodc);
1337 if (ret != LDB_SUCCESS) {
1338 DEBUG(4, (__location__ ": unable to tell if we are an RODC\n"));
1339 } else if (rodc) {
1340 ldb_asprintf_errstring(ldb, "RODC modify is forbidden\n");
1341 return LDB_ERR_REFERRAL;
1345 md_value = talloc(msg, struct ldb_val);
1346 if (md_value == NULL) {
1347 ldb_oom(ldb);
1348 return LDB_ERR_OPERATIONS_ERROR;
1351 ret = replmd_replPropertyMetaDataCtr1_sort(&omd.ctr.ctr1, schema, msg->dn);
1352 if (ret != LDB_SUCCESS) {
1353 return ret;
1356 ndr_err = ndr_push_struct_blob(md_value, msg, &omd,
1357 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
1358 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1359 DEBUG(0,(__location__ ": Failed to marshall replPropertyMetaData for %s\n",
1360 ldb_dn_get_linearized(msg->dn)));
1361 return LDB_ERR_OPERATIONS_ERROR;
1364 ret = ldb_msg_add_empty(msg, "replPropertyMetaData", LDB_FLAG_MOD_REPLACE, &el);
1365 if (ret != LDB_SUCCESS) {
1366 DEBUG(0,(__location__ ": Failed to add updated replPropertyMetaData %s\n",
1367 ldb_dn_get_linearized(msg->dn)));
1368 return ret;
1371 el->num_values = 1;
1372 el->values = md_value;
1375 return LDB_SUCCESS;
1378 struct parsed_dn {
1379 struct dsdb_dn *dsdb_dn;
1380 struct GUID *guid;
1381 struct ldb_val *v;
1384 static int parsed_dn_compare(struct parsed_dn *pdn1, struct parsed_dn *pdn2)
1386 return GUID_compare(pdn1->guid, pdn2->guid);
1389 static struct parsed_dn *parsed_dn_find(struct parsed_dn *pdn,
1390 unsigned int count, struct GUID *guid,
1391 struct ldb_dn *dn)
1393 struct parsed_dn *ret;
1394 unsigned int i;
1395 if (dn && GUID_all_zero(guid)) {
1396 /* when updating a link using DRS, we sometimes get a
1397 NULL GUID. We then need to try and match by DN */
1398 for (i=0; i<count; i++) {
1399 if (ldb_dn_compare(pdn[i].dsdb_dn->dn, dn) == 0) {
1400 dsdb_get_extended_dn_guid(pdn[i].dsdb_dn->dn, guid, "GUID");
1401 return &pdn[i];
1404 return NULL;
1406 BINARY_ARRAY_SEARCH(pdn, count, guid, guid, GUID_compare, ret);
1407 return ret;
1411 get a series of message element values as an array of DNs and GUIDs
1412 the result is sorted by GUID
1414 static int get_parsed_dns(struct ldb_module *module, TALLOC_CTX *mem_ctx,
1415 struct ldb_message_element *el, struct parsed_dn **pdn,
1416 const char *ldap_oid, struct ldb_request *parent)
1418 unsigned int i;
1419 struct ldb_context *ldb = ldb_module_get_ctx(module);
1421 if (el == NULL) {
1422 *pdn = NULL;
1423 return LDB_SUCCESS;
1426 (*pdn) = talloc_array(mem_ctx, struct parsed_dn, el->num_values);
1427 if (!*pdn) {
1428 ldb_module_oom(module);
1429 return LDB_ERR_OPERATIONS_ERROR;
1432 for (i=0; i<el->num_values; i++) {
1433 struct ldb_val *v = &el->values[i];
1434 NTSTATUS status;
1435 struct ldb_dn *dn;
1436 struct parsed_dn *p;
1438 p = &(*pdn)[i];
1440 p->dsdb_dn = dsdb_dn_parse(*pdn, ldb, v, ldap_oid);
1441 if (p->dsdb_dn == NULL) {
1442 return LDB_ERR_INVALID_DN_SYNTAX;
1445 dn = p->dsdb_dn->dn;
1447 p->guid = talloc(*pdn, struct GUID);
1448 if (p->guid == NULL) {
1449 ldb_module_oom(module);
1450 return LDB_ERR_OPERATIONS_ERROR;
1453 status = dsdb_get_extended_dn_guid(dn, p->guid, "GUID");
1454 if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
1455 /* we got a DN without a GUID - go find the GUID */
1456 int ret = dsdb_module_guid_by_dn(module, dn, p->guid, parent);
1457 if (ret != LDB_SUCCESS) {
1458 ldb_asprintf_errstring(ldb, "Unable to find GUID for DN %s\n",
1459 ldb_dn_get_linearized(dn));
1460 return ret;
1462 ret = dsdb_set_extended_dn_guid(dn, p->guid, "GUID");
1463 if (ret != LDB_SUCCESS) {
1464 return ret;
1466 } else if (!NT_STATUS_IS_OK(status)) {
1467 return LDB_ERR_OPERATIONS_ERROR;
1470 /* keep a pointer to the original ldb_val */
1471 p->v = v;
1474 TYPESAFE_QSORT(*pdn, el->num_values, parsed_dn_compare);
1476 return LDB_SUCCESS;
1480 build a new extended DN, including all meta data fields
1482 RMD_FLAGS = DSDB_RMD_FLAG_* bits
1483 RMD_ADDTIME = originating_add_time
1484 RMD_INVOCID = originating_invocation_id
1485 RMD_CHANGETIME = originating_change_time
1486 RMD_ORIGINATING_USN = originating_usn
1487 RMD_LOCAL_USN = local_usn
1488 RMD_VERSION = version
1490 static int replmd_build_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
1491 const struct GUID *invocation_id, uint64_t seq_num,
1492 uint64_t local_usn, NTTIME nttime, uint32_t version, bool deleted)
1494 struct ldb_dn *dn = dsdb_dn->dn;
1495 const char *tstring, *usn_string, *flags_string;
1496 struct ldb_val tval;
1497 struct ldb_val iid;
1498 struct ldb_val usnv, local_usnv;
1499 struct ldb_val vers, flagsv;
1500 NTSTATUS status;
1501 int ret;
1502 const char *dnstring;
1503 char *vstring;
1504 uint32_t rmd_flags = deleted?DSDB_RMD_FLAG_DELETED:0;
1506 tstring = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)nttime);
1507 if (!tstring) {
1508 return LDB_ERR_OPERATIONS_ERROR;
1510 tval = data_blob_string_const(tstring);
1512 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)seq_num);
1513 if (!usn_string) {
1514 return LDB_ERR_OPERATIONS_ERROR;
1516 usnv = data_blob_string_const(usn_string);
1518 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)local_usn);
1519 if (!usn_string) {
1520 return LDB_ERR_OPERATIONS_ERROR;
1522 local_usnv = data_blob_string_const(usn_string);
1524 vstring = talloc_asprintf(mem_ctx, "%lu", (unsigned long)version);
1525 if (!vstring) {
1526 return LDB_ERR_OPERATIONS_ERROR;
1528 vers = data_blob_string_const(vstring);
1530 status = GUID_to_ndr_blob(invocation_id, dn, &iid);
1531 if (!NT_STATUS_IS_OK(status)) {
1532 return LDB_ERR_OPERATIONS_ERROR;
1535 flags_string = talloc_asprintf(mem_ctx, "%u", rmd_flags);
1536 if (!flags_string) {
1537 return LDB_ERR_OPERATIONS_ERROR;
1539 flagsv = data_blob_string_const(flags_string);
1541 ret = ldb_dn_set_extended_component(dn, "RMD_FLAGS", &flagsv);
1542 if (ret != LDB_SUCCESS) return ret;
1543 ret = ldb_dn_set_extended_component(dn, "RMD_ADDTIME", &tval);
1544 if (ret != LDB_SUCCESS) return ret;
1545 ret = ldb_dn_set_extended_component(dn, "RMD_INVOCID", &iid);
1546 if (ret != LDB_SUCCESS) return ret;
1547 ret = ldb_dn_set_extended_component(dn, "RMD_CHANGETIME", &tval);
1548 if (ret != LDB_SUCCESS) return ret;
1549 ret = ldb_dn_set_extended_component(dn, "RMD_LOCAL_USN", &local_usnv);
1550 if (ret != LDB_SUCCESS) return ret;
1551 ret = ldb_dn_set_extended_component(dn, "RMD_ORIGINATING_USN", &usnv);
1552 if (ret != LDB_SUCCESS) return ret;
1553 ret = ldb_dn_set_extended_component(dn, "RMD_VERSION", &vers);
1554 if (ret != LDB_SUCCESS) return ret;
1556 dnstring = dsdb_dn_get_extended_linearized(mem_ctx, dsdb_dn, 1);
1557 if (dnstring == NULL) {
1558 return LDB_ERR_OPERATIONS_ERROR;
1560 *v = data_blob_string_const(dnstring);
1562 return LDB_SUCCESS;
1565 static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
1566 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
1567 uint64_t seq_num, uint64_t local_usn, NTTIME nttime,
1568 uint32_t version, bool deleted);
1571 check if any links need upgrading from w2k format
1573 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.
1575 static int replmd_check_upgrade_links(struct parsed_dn *dns, uint32_t count, struct ldb_message_element *parent_ctx, const struct GUID *invocation_id)
1577 uint32_t i;
1578 for (i=0; i<count; i++) {
1579 NTSTATUS status;
1580 uint32_t version;
1581 int ret;
1583 status = dsdb_get_extended_dn_uint32(dns[i].dsdb_dn->dn, &version, "RMD_VERSION");
1584 if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
1585 continue;
1588 /* it's an old one that needs upgrading */
1589 ret = replmd_update_la_val(parent_ctx->values, dns[i].v, dns[i].dsdb_dn, dns[i].dsdb_dn, invocation_id,
1590 1, 1, 0, 0, false);
1591 if (ret != LDB_SUCCESS) {
1592 return ret;
1595 return LDB_SUCCESS;
1599 update an extended DN, including all meta data fields
1601 see replmd_build_la_val for value names
1603 static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
1604 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
1605 uint64_t seq_num, uint64_t local_usn, NTTIME nttime,
1606 uint32_t version, bool deleted)
1608 struct ldb_dn *dn = dsdb_dn->dn;
1609 const char *tstring, *usn_string, *flags_string;
1610 struct ldb_val tval;
1611 struct ldb_val iid;
1612 struct ldb_val usnv, local_usnv;
1613 struct ldb_val vers, flagsv;
1614 const struct ldb_val *old_addtime;
1615 uint32_t old_version;
1616 NTSTATUS status;
1617 int ret;
1618 const char *dnstring;
1619 char *vstring;
1620 uint32_t rmd_flags = deleted?DSDB_RMD_FLAG_DELETED:0;
1622 tstring = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)nttime);
1623 if (!tstring) {
1624 return LDB_ERR_OPERATIONS_ERROR;
1626 tval = data_blob_string_const(tstring);
1628 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)seq_num);
1629 if (!usn_string) {
1630 return LDB_ERR_OPERATIONS_ERROR;
1632 usnv = data_blob_string_const(usn_string);
1634 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)local_usn);
1635 if (!usn_string) {
1636 return LDB_ERR_OPERATIONS_ERROR;
1638 local_usnv = data_blob_string_const(usn_string);
1640 status = GUID_to_ndr_blob(invocation_id, dn, &iid);
1641 if (!NT_STATUS_IS_OK(status)) {
1642 return LDB_ERR_OPERATIONS_ERROR;
1645 flags_string = talloc_asprintf(mem_ctx, "%u", rmd_flags);
1646 if (!flags_string) {
1647 return LDB_ERR_OPERATIONS_ERROR;
1649 flagsv = data_blob_string_const(flags_string);
1651 ret = ldb_dn_set_extended_component(dn, "RMD_FLAGS", &flagsv);
1652 if (ret != LDB_SUCCESS) return ret;
1654 /* get the ADDTIME from the original */
1655 old_addtime = ldb_dn_get_extended_component(old_dsdb_dn->dn, "RMD_ADDTIME");
1656 if (old_addtime == NULL) {
1657 old_addtime = &tval;
1659 if (dsdb_dn != old_dsdb_dn ||
1660 ldb_dn_get_extended_component(dn, "RMD_ADDTIME") == NULL) {
1661 ret = ldb_dn_set_extended_component(dn, "RMD_ADDTIME", old_addtime);
1662 if (ret != LDB_SUCCESS) return ret;
1665 /* use our invocation id */
1666 ret = ldb_dn_set_extended_component(dn, "RMD_INVOCID", &iid);
1667 if (ret != LDB_SUCCESS) return ret;
1669 /* changetime is the current time */
1670 ret = ldb_dn_set_extended_component(dn, "RMD_CHANGETIME", &tval);
1671 if (ret != LDB_SUCCESS) return ret;
1673 /* update the USN */
1674 ret = ldb_dn_set_extended_component(dn, "RMD_ORIGINATING_USN", &usnv);
1675 if (ret != LDB_SUCCESS) return ret;
1677 ret = ldb_dn_set_extended_component(dn, "RMD_LOCAL_USN", &local_usnv);
1678 if (ret != LDB_SUCCESS) return ret;
1680 /* increase the version by 1 */
1681 status = dsdb_get_extended_dn_uint32(old_dsdb_dn->dn, &old_version, "RMD_VERSION");
1682 if (NT_STATUS_IS_OK(status) && old_version >= version) {
1683 version = old_version+1;
1685 vstring = talloc_asprintf(dn, "%lu", (unsigned long)version);
1686 vers = data_blob_string_const(vstring);
1687 ret = ldb_dn_set_extended_component(dn, "RMD_VERSION", &vers);
1688 if (ret != LDB_SUCCESS) return ret;
1690 dnstring = dsdb_dn_get_extended_linearized(mem_ctx, dsdb_dn, 1);
1691 if (dnstring == NULL) {
1692 return LDB_ERR_OPERATIONS_ERROR;
1694 *v = data_blob_string_const(dnstring);
1696 return LDB_SUCCESS;
1700 handle adding a linked attribute
1702 static int replmd_modify_la_add(struct ldb_module *module,
1703 const struct dsdb_schema *schema,
1704 struct ldb_message *msg,
1705 struct ldb_message_element *el,
1706 struct ldb_message_element *old_el,
1707 const struct dsdb_attribute *schema_attr,
1708 uint64_t seq_num,
1709 time_t t,
1710 struct GUID *msg_guid,
1711 struct ldb_request *parent)
1713 unsigned int i;
1714 struct parsed_dn *dns, *old_dns;
1715 TALLOC_CTX *tmp_ctx = talloc_new(msg);
1716 int ret;
1717 struct ldb_val *new_values = NULL;
1718 unsigned int num_new_values = 0;
1719 unsigned old_num_values = old_el?old_el->num_values:0;
1720 const struct GUID *invocation_id;
1721 struct ldb_context *ldb = ldb_module_get_ctx(module);
1722 NTTIME now;
1724 unix_to_nt_time(&now, t);
1726 ret = get_parsed_dns(module, tmp_ctx, el, &dns, schema_attr->syntax->ldap_oid, parent);
1727 if (ret != LDB_SUCCESS) {
1728 talloc_free(tmp_ctx);
1729 return ret;
1732 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns, schema_attr->syntax->ldap_oid, parent);
1733 if (ret != LDB_SUCCESS) {
1734 talloc_free(tmp_ctx);
1735 return ret;
1738 invocation_id = samdb_ntds_invocation_id(ldb);
1739 if (!invocation_id) {
1740 talloc_free(tmp_ctx);
1741 return LDB_ERR_OPERATIONS_ERROR;
1744 ret = replmd_check_upgrade_links(old_dns, old_num_values, old_el, invocation_id);
1745 if (ret != LDB_SUCCESS) {
1746 talloc_free(tmp_ctx);
1747 return ret;
1750 /* for each new value, see if it exists already with the same GUID */
1751 for (i=0; i<el->num_values; i++) {
1752 struct parsed_dn *p = parsed_dn_find(old_dns, old_num_values, dns[i].guid, NULL);
1753 if (p == NULL) {
1754 /* this is a new linked attribute value */
1755 new_values = talloc_realloc(tmp_ctx, new_values, struct ldb_val, num_new_values+1);
1756 if (new_values == NULL) {
1757 ldb_module_oom(module);
1758 talloc_free(tmp_ctx);
1759 return LDB_ERR_OPERATIONS_ERROR;
1761 ret = replmd_build_la_val(new_values, &new_values[num_new_values], dns[i].dsdb_dn,
1762 invocation_id, seq_num, seq_num, now, 0, false);
1763 if (ret != LDB_SUCCESS) {
1764 talloc_free(tmp_ctx);
1765 return ret;
1767 num_new_values++;
1768 } else {
1769 /* this is only allowed if the GUID was
1770 previously deleted. */
1771 uint32_t rmd_flags = dsdb_dn_rmd_flags(p->dsdb_dn->dn);
1773 if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
1774 ldb_asprintf_errstring(ldb, "Attribute %s already exists for target GUID %s",
1775 el->name, GUID_string(tmp_ctx, p->guid));
1776 talloc_free(tmp_ctx);
1777 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
1779 ret = replmd_update_la_val(old_el->values, p->v, dns[i].dsdb_dn, p->dsdb_dn,
1780 invocation_id, seq_num, seq_num, now, 0, false);
1781 if (ret != LDB_SUCCESS) {
1782 talloc_free(tmp_ctx);
1783 return ret;
1787 ret = replmd_add_backlink(module, schema, msg_guid, dns[i].guid, true, schema_attr, true);
1788 if (ret != LDB_SUCCESS) {
1789 talloc_free(tmp_ctx);
1790 return ret;
1794 /* add the new ones on to the end of the old values, constructing a new el->values */
1795 el->values = talloc_realloc(msg->elements, old_el?old_el->values:NULL,
1796 struct ldb_val,
1797 old_num_values+num_new_values);
1798 if (el->values == NULL) {
1799 ldb_module_oom(module);
1800 return LDB_ERR_OPERATIONS_ERROR;
1803 memcpy(&el->values[old_num_values], new_values, num_new_values*sizeof(struct ldb_val));
1804 el->num_values = old_num_values + num_new_values;
1806 talloc_steal(msg->elements, el->values);
1807 talloc_steal(el->values, new_values);
1809 talloc_free(tmp_ctx);
1811 /* we now tell the backend to replace all existing values
1812 with the one we have constructed */
1813 el->flags = LDB_FLAG_MOD_REPLACE;
1815 return LDB_SUCCESS;
1820 handle deleting all active linked attributes
1822 static int replmd_modify_la_delete(struct ldb_module *module,
1823 const struct dsdb_schema *schema,
1824 struct ldb_message *msg,
1825 struct ldb_message_element *el,
1826 struct ldb_message_element *old_el,
1827 const struct dsdb_attribute *schema_attr,
1828 uint64_t seq_num,
1829 time_t t,
1830 struct GUID *msg_guid,
1831 struct ldb_request *parent)
1833 unsigned int i;
1834 struct parsed_dn *dns, *old_dns;
1835 TALLOC_CTX *tmp_ctx = talloc_new(msg);
1836 int ret;
1837 const struct GUID *invocation_id;
1838 struct ldb_context *ldb = ldb_module_get_ctx(module);
1839 NTTIME now;
1841 unix_to_nt_time(&now, t);
1843 /* check if there is nothing to delete */
1844 if ((!old_el || old_el->num_values == 0) &&
1845 el->num_values == 0) {
1846 return LDB_SUCCESS;
1849 if (!old_el || old_el->num_values == 0) {
1850 return LDB_ERR_NO_SUCH_ATTRIBUTE;
1853 ret = get_parsed_dns(module, tmp_ctx, el, &dns, schema_attr->syntax->ldap_oid, parent);
1854 if (ret != LDB_SUCCESS) {
1855 talloc_free(tmp_ctx);
1856 return ret;
1859 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns, schema_attr->syntax->ldap_oid, parent);
1860 if (ret != LDB_SUCCESS) {
1861 talloc_free(tmp_ctx);
1862 return ret;
1865 invocation_id = samdb_ntds_invocation_id(ldb);
1866 if (!invocation_id) {
1867 return LDB_ERR_OPERATIONS_ERROR;
1870 ret = replmd_check_upgrade_links(old_dns, old_el->num_values, old_el, invocation_id);
1871 if (ret != LDB_SUCCESS) {
1872 talloc_free(tmp_ctx);
1873 return ret;
1876 el->values = NULL;
1878 /* see if we are being asked to delete any links that
1879 don't exist or are already deleted */
1880 for (i=0; i<el->num_values; i++) {
1881 struct parsed_dn *p = &dns[i];
1882 struct parsed_dn *p2;
1883 uint32_t rmd_flags;
1885 p2 = parsed_dn_find(old_dns, old_el->num_values, p->guid, NULL);
1886 if (!p2) {
1887 ldb_asprintf_errstring(ldb, "Attribute %s doesn't exist for target GUID %s",
1888 el->name, GUID_string(tmp_ctx, p->guid));
1889 return LDB_ERR_NO_SUCH_ATTRIBUTE;
1891 rmd_flags = dsdb_dn_rmd_flags(p2->dsdb_dn->dn);
1892 if (rmd_flags & DSDB_RMD_FLAG_DELETED) {
1893 ldb_asprintf_errstring(ldb, "Attribute %s already deleted for target GUID %s",
1894 el->name, GUID_string(tmp_ctx, p->guid));
1895 return LDB_ERR_NO_SUCH_ATTRIBUTE;
1899 /* for each new value, see if it exists already with the same GUID
1900 if it is not already deleted and matches the delete list then delete it
1902 for (i=0; i<old_el->num_values; i++) {
1903 struct parsed_dn *p = &old_dns[i];
1904 uint32_t rmd_flags;
1906 if (el->num_values && parsed_dn_find(dns, el->num_values, p->guid, NULL) == NULL) {
1907 continue;
1910 rmd_flags = dsdb_dn_rmd_flags(p->dsdb_dn->dn);
1911 if (rmd_flags & DSDB_RMD_FLAG_DELETED) continue;
1913 ret = replmd_update_la_val(old_el->values, p->v, p->dsdb_dn, p->dsdb_dn,
1914 invocation_id, seq_num, seq_num, now, 0, true);
1915 if (ret != LDB_SUCCESS) {
1916 talloc_free(tmp_ctx);
1917 return ret;
1920 ret = replmd_add_backlink(module, schema, msg_guid, old_dns[i].guid, false, schema_attr, true);
1921 if (ret != LDB_SUCCESS) {
1922 talloc_free(tmp_ctx);
1923 return ret;
1927 el->values = talloc_steal(msg->elements, old_el->values);
1928 el->num_values = old_el->num_values;
1930 talloc_free(tmp_ctx);
1932 /* we now tell the backend to replace all existing values
1933 with the one we have constructed */
1934 el->flags = LDB_FLAG_MOD_REPLACE;
1936 return LDB_SUCCESS;
1940 handle replacing a linked attribute
1942 static int replmd_modify_la_replace(struct ldb_module *module,
1943 const struct dsdb_schema *schema,
1944 struct ldb_message *msg,
1945 struct ldb_message_element *el,
1946 struct ldb_message_element *old_el,
1947 const struct dsdb_attribute *schema_attr,
1948 uint64_t seq_num,
1949 time_t t,
1950 struct GUID *msg_guid,
1951 struct ldb_request *parent)
1953 unsigned int i;
1954 struct parsed_dn *dns, *old_dns;
1955 TALLOC_CTX *tmp_ctx = talloc_new(msg);
1956 int ret;
1957 const struct GUID *invocation_id;
1958 struct ldb_context *ldb = ldb_module_get_ctx(module);
1959 struct ldb_val *new_values = NULL;
1960 unsigned int num_new_values = 0;
1961 unsigned int old_num_values = old_el?old_el->num_values:0;
1962 NTTIME now;
1964 unix_to_nt_time(&now, t);
1966 /* check if there is nothing to replace */
1967 if ((!old_el || old_el->num_values == 0) &&
1968 el->num_values == 0) {
1969 return LDB_SUCCESS;
1972 ret = get_parsed_dns(module, tmp_ctx, el, &dns, schema_attr->syntax->ldap_oid, parent);
1973 if (ret != LDB_SUCCESS) {
1974 talloc_free(tmp_ctx);
1975 return ret;
1978 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns, schema_attr->syntax->ldap_oid, parent);
1979 if (ret != LDB_SUCCESS) {
1980 talloc_free(tmp_ctx);
1981 return ret;
1984 invocation_id = samdb_ntds_invocation_id(ldb);
1985 if (!invocation_id) {
1986 return LDB_ERR_OPERATIONS_ERROR;
1989 ret = replmd_check_upgrade_links(old_dns, old_num_values, old_el, invocation_id);
1990 if (ret != LDB_SUCCESS) {
1991 talloc_free(tmp_ctx);
1992 return ret;
1995 /* mark all the old ones as deleted */
1996 for (i=0; i<old_num_values; i++) {
1997 struct parsed_dn *old_p = &old_dns[i];
1998 struct parsed_dn *p;
1999 uint32_t rmd_flags = dsdb_dn_rmd_flags(old_p->dsdb_dn->dn);
2001 if (rmd_flags & DSDB_RMD_FLAG_DELETED) continue;
2003 ret = replmd_add_backlink(module, schema, msg_guid, old_dns[i].guid, false, schema_attr, false);
2004 if (ret != LDB_SUCCESS) {
2005 talloc_free(tmp_ctx);
2006 return ret;
2009 p = parsed_dn_find(dns, el->num_values, old_p->guid, NULL);
2010 if (p) {
2011 /* we don't delete it if we are re-adding it */
2012 continue;
2015 ret = replmd_update_la_val(old_el->values, old_p->v, old_p->dsdb_dn, old_p->dsdb_dn,
2016 invocation_id, seq_num, seq_num, now, 0, true);
2017 if (ret != LDB_SUCCESS) {
2018 talloc_free(tmp_ctx);
2019 return ret;
2023 /* for each new value, either update its meta-data, or add it
2024 * to old_el
2026 for (i=0; i<el->num_values; i++) {
2027 struct parsed_dn *p = &dns[i], *old_p;
2029 if (old_dns &&
2030 (old_p = parsed_dn_find(old_dns,
2031 old_num_values, p->guid, NULL)) != NULL) {
2032 /* update in place */
2033 ret = replmd_update_la_val(old_el->values, old_p->v, p->dsdb_dn,
2034 old_p->dsdb_dn, invocation_id,
2035 seq_num, seq_num, now, 0, false);
2036 if (ret != LDB_SUCCESS) {
2037 talloc_free(tmp_ctx);
2038 return ret;
2040 } else {
2041 /* add a new one */
2042 new_values = talloc_realloc(tmp_ctx, new_values, struct ldb_val,
2043 num_new_values+1);
2044 if (new_values == NULL) {
2045 ldb_module_oom(module);
2046 talloc_free(tmp_ctx);
2047 return LDB_ERR_OPERATIONS_ERROR;
2049 ret = replmd_build_la_val(new_values, &new_values[num_new_values], dns[i].dsdb_dn,
2050 invocation_id, seq_num, seq_num, now, 0, false);
2051 if (ret != LDB_SUCCESS) {
2052 talloc_free(tmp_ctx);
2053 return ret;
2055 num_new_values++;
2058 ret = replmd_add_backlink(module, schema, msg_guid, dns[i].guid, true, schema_attr, false);
2059 if (ret != LDB_SUCCESS) {
2060 talloc_free(tmp_ctx);
2061 return ret;
2065 /* add the new values to the end of old_el */
2066 if (num_new_values != 0) {
2067 el->values = talloc_realloc(msg->elements, old_el?old_el->values:NULL,
2068 struct ldb_val, old_num_values+num_new_values);
2069 if (el->values == NULL) {
2070 ldb_module_oom(module);
2071 return LDB_ERR_OPERATIONS_ERROR;
2073 memcpy(&el->values[old_num_values], &new_values[0],
2074 sizeof(struct ldb_val)*num_new_values);
2075 el->num_values = old_num_values + num_new_values;
2076 talloc_steal(msg->elements, new_values);
2077 } else {
2078 el->values = old_el->values;
2079 el->num_values = old_el->num_values;
2080 talloc_steal(msg->elements, el->values);
2083 talloc_free(tmp_ctx);
2085 /* we now tell the backend to replace all existing values
2086 with the one we have constructed */
2087 el->flags = LDB_FLAG_MOD_REPLACE;
2089 return LDB_SUCCESS;
2094 handle linked attributes in modify requests
2096 static int replmd_modify_handle_linked_attribs(struct ldb_module *module,
2097 struct ldb_message *msg,
2098 uint64_t seq_num, time_t t,
2099 struct ldb_request *parent)
2101 struct ldb_result *res;
2102 unsigned int i;
2103 int ret;
2104 struct ldb_context *ldb = ldb_module_get_ctx(module);
2105 struct ldb_message *old_msg;
2107 const struct dsdb_schema *schema;
2108 struct GUID old_guid;
2110 if (seq_num == 0) {
2111 /* there the replmd_update_rpmd code has already
2112 * checked and saw that there are no linked
2113 * attributes */
2114 return LDB_SUCCESS;
2117 if (dsdb_functional_level(ldb) == DS_DOMAIN_FUNCTION_2000) {
2118 /* don't do anything special for linked attributes */
2119 return LDB_SUCCESS;
2122 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, NULL,
2123 DSDB_FLAG_NEXT_MODULE |
2124 DSDB_SEARCH_SHOW_RECYCLED |
2125 DSDB_SEARCH_REVEAL_INTERNALS |
2126 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
2127 parent);
2128 if (ret != LDB_SUCCESS) {
2129 return ret;
2131 schema = dsdb_get_schema(ldb, res);
2132 if (!schema) {
2133 return LDB_ERR_OPERATIONS_ERROR;
2136 old_msg = res->msgs[0];
2138 old_guid = samdb_result_guid(old_msg, "objectGUID");
2140 for (i=0; i<msg->num_elements; i++) {
2141 struct ldb_message_element *el = &msg->elements[i];
2142 struct ldb_message_element *old_el, *new_el;
2143 const struct dsdb_attribute *schema_attr
2144 = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
2145 if (!schema_attr) {
2146 ldb_asprintf_errstring(ldb,
2147 "%s: attribute %s is not a valid attribute in schema",
2148 __FUNCTION__, el->name);
2149 return LDB_ERR_OBJECT_CLASS_VIOLATION;
2151 if (schema_attr->linkID == 0) {
2152 continue;
2154 if ((schema_attr->linkID & 1) == 1) {
2155 /* Odd is for the target. Illegal to modify */
2156 ldb_asprintf_errstring(ldb,
2157 "attribute %s must not be modified directly, it is a linked attribute", el->name);
2158 return LDB_ERR_UNWILLING_TO_PERFORM;
2160 old_el = ldb_msg_find_element(old_msg, el->name);
2161 switch (el->flags & LDB_FLAG_MOD_MASK) {
2162 case LDB_FLAG_MOD_REPLACE:
2163 ret = replmd_modify_la_replace(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid, parent);
2164 break;
2165 case LDB_FLAG_MOD_DELETE:
2166 ret = replmd_modify_la_delete(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid, parent);
2167 break;
2168 case LDB_FLAG_MOD_ADD:
2169 ret = replmd_modify_la_add(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid, parent);
2170 break;
2171 default:
2172 ldb_asprintf_errstring(ldb,
2173 "invalid flags 0x%x for %s linked attribute",
2174 el->flags, el->name);
2175 return LDB_ERR_UNWILLING_TO_PERFORM;
2177 if (dsdb_check_single_valued_link(schema_attr, el) != LDB_SUCCESS) {
2178 ldb_asprintf_errstring(ldb,
2179 "Attribute %s is single valued but more than one value has been supplied",
2180 el->name);
2181 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
2182 } else {
2183 el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
2188 if (ret != LDB_SUCCESS) {
2189 return ret;
2191 if (old_el) {
2192 ldb_msg_remove_attr(old_msg, el->name);
2194 ldb_msg_add_empty(old_msg, el->name, 0, &new_el);
2195 new_el->num_values = el->num_values;
2196 new_el->values = talloc_steal(msg->elements, el->values);
2198 /* TODO: this relises a bit too heavily on the exact
2199 behaviour of ldb_msg_find_element and
2200 ldb_msg_remove_element */
2201 old_el = ldb_msg_find_element(msg, el->name);
2202 if (old_el != el) {
2203 ldb_msg_remove_element(msg, old_el);
2204 i--;
2208 talloc_free(res);
2209 return ret;
2214 static int replmd_modify(struct ldb_module *module, struct ldb_request *req)
2216 struct ldb_context *ldb;
2217 struct replmd_replicated_request *ac;
2218 struct ldb_request *down_req;
2219 struct ldb_message *msg;
2220 time_t t = time(NULL);
2221 int ret;
2222 bool is_urgent = false;
2223 struct loadparm_context *lp_ctx;
2224 char *referral;
2225 unsigned int functional_level;
2226 const DATA_BLOB *guid_blob;
2228 /* do not manipulate our control entries */
2229 if (ldb_dn_is_special(req->op.mod.message->dn)) {
2230 return ldb_next_request(module, req);
2233 ldb = ldb_module_get_ctx(module);
2235 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_modify\n");
2237 guid_blob = ldb_msg_find_ldb_val(req->op.mod.message, "objectGUID");
2238 if ( guid_blob != NULL ) {
2239 ldb_set_errstring(ldb,
2240 "replmd_modify: it's not allowed to change the objectGUID!");
2241 return LDB_ERR_CONSTRAINT_VIOLATION;
2244 ac = replmd_ctx_init(module, req);
2245 if (ac == NULL) {
2246 return ldb_module_oom(module);
2249 functional_level = dsdb_functional_level(ldb);
2251 lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
2252 struct loadparm_context);
2254 /* we have to copy the message as the caller might have it as a const */
2255 msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
2256 if (msg == NULL) {
2257 ldb_oom(ldb);
2258 talloc_free(ac);
2259 return LDB_ERR_OPERATIONS_ERROR;
2262 ldb_msg_remove_attr(msg, "whenChanged");
2263 ldb_msg_remove_attr(msg, "uSNChanged");
2265 ret = replmd_update_rpmd(module, ac->schema, req, NULL,
2266 msg, &ac->seq_num, t, &is_urgent);
2267 if (ret == LDB_ERR_REFERRAL) {
2268 referral = talloc_asprintf(req,
2269 "ldap://%s/%s",
2270 lpcfg_dnsdomain(lp_ctx),
2271 ldb_dn_get_linearized(msg->dn));
2272 ret = ldb_module_send_referral(req, referral);
2273 talloc_free(ac);
2274 return ldb_module_done(req, NULL, NULL, ret);
2277 if (ret != LDB_SUCCESS) {
2278 talloc_free(ac);
2279 return ret;
2282 ret = replmd_modify_handle_linked_attribs(module, msg, ac->seq_num, t, req);
2283 if (ret != LDB_SUCCESS) {
2284 talloc_free(ac);
2285 return ret;
2288 /* TODO:
2289 * - replace the old object with the newly constructed one
2292 ac->is_urgent = is_urgent;
2294 ret = ldb_build_mod_req(&down_req, ldb, ac,
2295 msg,
2296 req->controls,
2297 ac, replmd_op_callback,
2298 req);
2299 LDB_REQ_SET_LOCATION(down_req);
2300 if (ret != LDB_SUCCESS) {
2301 talloc_free(ac);
2302 return ret;
2305 /* current partition control is needed by "replmd_op_callback" */
2306 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
2307 ret = ldb_request_add_control(down_req,
2308 DSDB_CONTROL_CURRENT_PARTITION_OID,
2309 false, NULL);
2310 if (ret != LDB_SUCCESS) {
2311 talloc_free(ac);
2312 return ret;
2316 /* If we are in functional level 2000, then
2317 * replmd_modify_handle_linked_attribs will have done
2318 * nothing */
2319 if (functional_level == DS_DOMAIN_FUNCTION_2000) {
2320 ret = ldb_request_add_control(down_req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
2321 if (ret != LDB_SUCCESS) {
2322 talloc_free(ac);
2323 return ret;
2327 talloc_steal(down_req, msg);
2329 /* we only change whenChanged and uSNChanged if the seq_num
2330 has changed */
2331 if (ac->seq_num != 0) {
2332 ret = add_time_element(msg, "whenChanged", t);
2333 if (ret != LDB_SUCCESS) {
2334 talloc_free(ac);
2335 return ret;
2338 ret = add_uint64_element(ldb, msg, "uSNChanged", ac->seq_num);
2339 if (ret != LDB_SUCCESS) {
2340 talloc_free(ac);
2341 return ret;
2345 /* go on with the call chain */
2346 return ldb_next_request(module, down_req);
2349 static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares);
2352 handle a rename request
2354 On a rename we need to do an extra ldb_modify which sets the
2355 whenChanged and uSNChanged attributes. We do this in a callback after the success.
2357 static int replmd_rename(struct ldb_module *module, struct ldb_request *req)
2359 struct ldb_context *ldb;
2360 struct replmd_replicated_request *ac;
2361 int ret;
2362 struct ldb_request *down_req;
2364 /* do not manipulate our control entries */
2365 if (ldb_dn_is_special(req->op.mod.message->dn)) {
2366 return ldb_next_request(module, req);
2369 ldb = ldb_module_get_ctx(module);
2371 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_rename\n");
2373 ac = replmd_ctx_init(module, req);
2374 if (ac == NULL) {
2375 return ldb_module_oom(module);
2378 ret = ldb_build_rename_req(&down_req, ldb, ac,
2379 ac->req->op.rename.olddn,
2380 ac->req->op.rename.newdn,
2381 ac->req->controls,
2382 ac, replmd_rename_callback,
2383 ac->req);
2384 LDB_REQ_SET_LOCATION(down_req);
2385 if (ret != LDB_SUCCESS) {
2386 talloc_free(ac);
2387 return ret;
2390 /* go on with the call chain */
2391 return ldb_next_request(module, down_req);
2394 /* After the rename is compleated, update the whenchanged etc */
2395 static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares)
2397 struct ldb_context *ldb;
2398 struct replmd_replicated_request *ac;
2399 struct ldb_request *down_req;
2400 struct ldb_message *msg;
2401 const struct dsdb_attribute *rdn_attr;
2402 const char *rdn_name;
2403 const struct ldb_val *rdn_val;
2404 const char *attrs[4] = { NULL, };
2405 time_t t = time(NULL);
2406 int ret;
2407 bool is_urgent = false;
2409 ac = talloc_get_type(req->context, struct replmd_replicated_request);
2410 ldb = ldb_module_get_ctx(ac->module);
2412 if (ares->error != LDB_SUCCESS) {
2413 return ldb_module_done(ac->req, ares->controls,
2414 ares->response, ares->error);
2417 if (ares->type != LDB_REPLY_DONE) {
2418 ldb_set_errstring(ldb,
2419 "invalid ldb_reply_type in callback");
2420 talloc_free(ares);
2421 return ldb_module_done(ac->req, NULL, NULL,
2422 LDB_ERR_OPERATIONS_ERROR);
2425 /* TODO:
2426 * - replace the old object with the newly constructed one
2429 msg = ldb_msg_new(ac);
2430 if (msg == NULL) {
2431 ldb_oom(ldb);
2432 return LDB_ERR_OPERATIONS_ERROR;
2435 msg->dn = ac->req->op.rename.newdn;
2437 rdn_name = ldb_dn_get_rdn_name(msg->dn);
2438 if (rdn_name == NULL) {
2439 talloc_free(ares);
2440 return ldb_module_done(ac->req, NULL, NULL,
2441 ldb_operr(ldb));
2444 /* normalize the rdn attribute name */
2445 rdn_attr = dsdb_attribute_by_lDAPDisplayName(ac->schema, rdn_name);
2446 if (rdn_attr == NULL) {
2447 talloc_free(ares);
2448 return ldb_module_done(ac->req, NULL, NULL,
2449 ldb_operr(ldb));
2451 rdn_name = rdn_attr->lDAPDisplayName;
2453 rdn_val = ldb_dn_get_rdn_val(msg->dn);
2454 if (rdn_val == NULL) {
2455 talloc_free(ares);
2456 return ldb_module_done(ac->req, NULL, NULL,
2457 ldb_operr(ldb));
2460 if (ldb_msg_add_empty(msg, rdn_name, LDB_FLAG_MOD_REPLACE, NULL) != 0) {
2461 talloc_free(ares);
2462 return ldb_module_done(ac->req, NULL, NULL,
2463 ldb_oom(ldb));
2465 if (ldb_msg_add_value(msg, rdn_name, rdn_val, NULL) != 0) {
2466 talloc_free(ares);
2467 return ldb_module_done(ac->req, NULL, NULL,
2468 ldb_oom(ldb));
2470 if (ldb_msg_add_empty(msg, "name", LDB_FLAG_MOD_REPLACE, NULL) != 0) {
2471 talloc_free(ares);
2472 return ldb_module_done(ac->req, NULL, NULL,
2473 ldb_oom(ldb));
2475 if (ldb_msg_add_value(msg, "name", rdn_val, NULL) != 0) {
2476 talloc_free(ares);
2477 return ldb_module_done(ac->req, NULL, NULL,
2478 ldb_oom(ldb));
2482 * here we let replmd_update_rpmd() only search for
2483 * the existing "replPropertyMetaData" and rdn_name attributes.
2485 * We do not want the existing "name" attribute as
2486 * the "name" attribute needs to get the version
2487 * updated on rename even if the rdn value hasn't changed.
2489 * This is the diff of the meta data, for a moved user
2490 * on a w2k8r2 server:
2492 * # record 1
2493 * -dn: CN=sdf df,CN=Users,DC=bla,DC=base
2494 * +dn: CN=sdf df,OU=TestOU,DC=bla,DC=base
2495 * replPropertyMetaData: NDR: struct replPropertyMetaDataBlob
2496 * version : 0x00000001 (1)
2497 * reserved : 0x00000000 (0)
2498 * @@ -66,11 +66,11 @@ replPropertyMetaData: NDR: struct re
2499 * local_usn : 0x00000000000037a5 (14245)
2500 * array: struct replPropertyMetaData1
2501 * attid : DRSUAPI_ATTID_name (0x90001)
2502 * - version : 0x00000001 (1)
2503 * - originating_change_time : Wed Feb 9 17:20:49 2011 CET
2504 * + version : 0x00000002 (2)
2505 * + originating_change_time : Wed Apr 6 15:21:01 2011 CEST
2506 * originating_invocation_id: 0d36ca05-5507-4e62-aca3-354bab0d39e1
2507 * - originating_usn : 0x00000000000037a5 (14245)
2508 * - local_usn : 0x00000000000037a5 (14245)
2509 * + originating_usn : 0x0000000000003834 (14388)
2510 * + local_usn : 0x0000000000003834 (14388)
2511 * array: struct replPropertyMetaData1
2512 * attid : DRSUAPI_ATTID_userAccountControl (0x90008)
2513 * version : 0x00000004 (4)
2515 attrs[0] = "replPropertyMetaData";
2516 attrs[1] = "objectClass";
2517 attrs[2] = rdn_name;
2518 attrs[3] = NULL;
2520 ret = replmd_update_rpmd(ac->module, ac->schema, req, attrs,
2521 msg, &ac->seq_num, t, &is_urgent);
2522 if (ret == LDB_ERR_REFERRAL) {
2523 struct ldb_dn *olddn = ac->req->op.rename.olddn;
2524 struct loadparm_context *lp_ctx;
2525 char *referral;
2527 lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
2528 struct loadparm_context);
2530 referral = talloc_asprintf(req,
2531 "ldap://%s/%s",
2532 lpcfg_dnsdomain(lp_ctx),
2533 ldb_dn_get_linearized(olddn));
2534 ret = ldb_module_send_referral(req, referral);
2535 talloc_free(ac);
2536 return ldb_module_done(req, NULL, NULL, ret);
2539 if (ret != LDB_SUCCESS) {
2540 talloc_free(ares);
2541 return ldb_module_done(ac->req, NULL, NULL,
2542 ldb_error(ldb, ret,
2543 "failed to call replmd_update_rpmd()"));
2546 if (ac->seq_num == 0) {
2547 talloc_free(ares);
2548 return ldb_module_done(ac->req, NULL, NULL,
2549 ldb_error(ldb, ret,
2550 "internal error seq_num == 0"));
2552 ac->is_urgent = is_urgent;
2554 ret = ldb_build_mod_req(&down_req, ldb, ac,
2555 msg,
2556 req->controls,
2557 ac, replmd_op_callback,
2558 req);
2559 LDB_REQ_SET_LOCATION(down_req);
2560 if (ret != LDB_SUCCESS) {
2561 talloc_free(ac);
2562 return ret;
2565 /* current partition control is needed by "replmd_op_callback" */
2566 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
2567 ret = ldb_request_add_control(down_req,
2568 DSDB_CONTROL_CURRENT_PARTITION_OID,
2569 false, NULL);
2570 if (ret != LDB_SUCCESS) {
2571 talloc_free(ac);
2572 return ret;
2576 talloc_steal(down_req, msg);
2578 ret = add_time_element(msg, "whenChanged", t);
2579 if (ret != LDB_SUCCESS) {
2580 talloc_free(ac);
2581 return ret;
2584 ret = add_uint64_element(ldb, msg, "uSNChanged", ac->seq_num);
2585 if (ret != LDB_SUCCESS) {
2586 talloc_free(ac);
2587 return ret;
2590 /* go on with the call chain - do the modify after the rename */
2591 return ldb_next_request(ac->module, down_req);
2595 remove links from objects that point at this object when an object
2596 is deleted
2598 static int replmd_delete_remove_link(struct ldb_module *module,
2599 const struct dsdb_schema *schema,
2600 struct ldb_dn *dn,
2601 struct ldb_message_element *el,
2602 const struct dsdb_attribute *sa,
2603 struct ldb_request *parent)
2605 unsigned int i;
2606 TALLOC_CTX *tmp_ctx = talloc_new(module);
2607 struct ldb_context *ldb = ldb_module_get_ctx(module);
2609 for (i=0; i<el->num_values; i++) {
2610 struct dsdb_dn *dsdb_dn;
2611 NTSTATUS status;
2612 int ret;
2613 struct GUID guid2;
2614 struct ldb_message *msg;
2615 const struct dsdb_attribute *target_attr;
2616 struct ldb_message_element *el2;
2617 struct ldb_val dn_val;
2619 if (dsdb_dn_is_deleted_val(&el->values[i])) {
2620 continue;
2623 dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i], sa->syntax->ldap_oid);
2624 if (!dsdb_dn) {
2625 talloc_free(tmp_ctx);
2626 return LDB_ERR_OPERATIONS_ERROR;
2629 status = dsdb_get_extended_dn_guid(dsdb_dn->dn, &guid2, "GUID");
2630 if (!NT_STATUS_IS_OK(status)) {
2631 talloc_free(tmp_ctx);
2632 return LDB_ERR_OPERATIONS_ERROR;
2635 /* remove the link */
2636 msg = ldb_msg_new(tmp_ctx);
2637 if (!msg) {
2638 ldb_module_oom(module);
2639 talloc_free(tmp_ctx);
2640 return LDB_ERR_OPERATIONS_ERROR;
2644 msg->dn = dsdb_dn->dn;
2646 target_attr = dsdb_attribute_by_linkID(schema, sa->linkID ^ 1);
2647 if (target_attr == NULL) {
2648 continue;
2651 ret = ldb_msg_add_empty(msg, target_attr->lDAPDisplayName, LDB_FLAG_MOD_DELETE, &el2);
2652 if (ret != LDB_SUCCESS) {
2653 ldb_module_oom(module);
2654 talloc_free(tmp_ctx);
2655 return LDB_ERR_OPERATIONS_ERROR;
2657 dn_val = data_blob_string_const(ldb_dn_get_linearized(dn));
2658 el2->values = &dn_val;
2659 el2->num_values = 1;
2661 ret = dsdb_module_modify(module, msg, DSDB_FLAG_OWN_MODULE, parent);
2662 if (ret != LDB_SUCCESS) {
2663 talloc_free(tmp_ctx);
2664 return ret;
2667 talloc_free(tmp_ctx);
2668 return LDB_SUCCESS;
2673 handle update of replication meta data for deletion of objects
2675 This also handles the mapping of delete to a rename operation
2676 to allow deletes to be replicated.
2678 static int replmd_delete(struct ldb_module *module, struct ldb_request *req)
2680 int ret = LDB_ERR_OTHER;
2681 bool retb, disallow_move_on_delete;
2682 struct ldb_dn *old_dn, *new_dn;
2683 const char *rdn_name;
2684 const struct ldb_val *rdn_value, *new_rdn_value;
2685 struct GUID guid;
2686 struct ldb_context *ldb = ldb_module_get_ctx(module);
2687 const struct dsdb_schema *schema;
2688 struct ldb_message *msg, *old_msg;
2689 struct ldb_message_element *el;
2690 TALLOC_CTX *tmp_ctx;
2691 struct ldb_result *res, *parent_res;
2692 const char *preserved_attrs[] = {
2693 /* yes, this really is a hard coded list. See MS-ADTS
2694 section 3.1.1.5.5.1.1 */
2695 "nTSecurityDescriptor", "attributeID", "attributeSyntax", "dNReferenceUpdate", "dNSHostName",
2696 "flatName", "governsID", "groupType", "instanceType", "lDAPDisplayName", "legacyExchangeDN",
2697 "isDeleted", "isRecycled", "lastKnownParent", "msDS-LastKnownRDN", "mS-DS-CreatorSID",
2698 "mSMQOwnerID", "nCName", "objectClass", "distinguishedName", "objectGUID", "objectSid",
2699 "oMSyntax", "proxiedObjectName", "name", "replPropertyMetaData", "sAMAccountName",
2700 "securityIdentifier", "sIDHistory", "subClassOf", "systemFlags", "trustPartner", "trustDirection",
2701 "trustType", "trustAttributes", "userAccountControl", "uSNChanged", "uSNCreated", "whenCreated",
2702 "whenChanged", NULL};
2703 unsigned int i, el_count = 0;
2704 enum deletion_state { OBJECT_NOT_DELETED=1, OBJECT_DELETED=2, OBJECT_RECYCLED=3,
2705 OBJECT_TOMBSTONE=4, OBJECT_REMOVED=5 };
2706 enum deletion_state deletion_state, next_deletion_state;
2707 bool enabled;
2709 if (ldb_dn_is_special(req->op.del.dn)) {
2710 return ldb_next_request(module, req);
2713 tmp_ctx = talloc_new(ldb);
2714 if (!tmp_ctx) {
2715 ldb_oom(ldb);
2716 return LDB_ERR_OPERATIONS_ERROR;
2719 schema = dsdb_get_schema(ldb, tmp_ctx);
2720 if (!schema) {
2721 return LDB_ERR_OPERATIONS_ERROR;
2724 old_dn = ldb_dn_copy(tmp_ctx, req->op.del.dn);
2726 /* we need the complete msg off disk, so we can work out which
2727 attributes need to be removed */
2728 ret = dsdb_module_search_dn(module, tmp_ctx, &res, old_dn, NULL,
2729 DSDB_FLAG_NEXT_MODULE |
2730 DSDB_SEARCH_SHOW_RECYCLED |
2731 DSDB_SEARCH_REVEAL_INTERNALS |
2732 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT, req);
2733 if (ret != LDB_SUCCESS) {
2734 talloc_free(tmp_ctx);
2735 return ret;
2737 old_msg = res->msgs[0];
2740 ret = dsdb_recyclebin_enabled(module, &enabled);
2741 if (ret != LDB_SUCCESS) {
2742 talloc_free(tmp_ctx);
2743 return ret;
2746 if (ldb_msg_check_string_attribute(old_msg, "isDeleted", "TRUE")) {
2747 if (!enabled) {
2748 deletion_state = OBJECT_TOMBSTONE;
2749 next_deletion_state = OBJECT_REMOVED;
2750 } else if (ldb_msg_check_string_attribute(old_msg, "isRecycled", "TRUE")) {
2751 deletion_state = OBJECT_RECYCLED;
2752 next_deletion_state = OBJECT_REMOVED;
2753 } else {
2754 deletion_state = OBJECT_DELETED;
2755 next_deletion_state = OBJECT_RECYCLED;
2757 } else {
2758 deletion_state = OBJECT_NOT_DELETED;
2759 if (enabled) {
2760 next_deletion_state = OBJECT_DELETED;
2761 } else {
2762 next_deletion_state = OBJECT_TOMBSTONE;
2766 if (next_deletion_state == OBJECT_REMOVED) {
2767 struct auth_session_info *session_info =
2768 (struct auth_session_info *)ldb_get_opaque(ldb, "sessionInfo");
2769 if (security_session_user_level(session_info, NULL) != SECURITY_SYSTEM) {
2770 ldb_asprintf_errstring(ldb, "Refusing to delete deleted object %s",
2771 ldb_dn_get_linearized(old_msg->dn));
2772 return LDB_ERR_UNWILLING_TO_PERFORM;
2775 /* it is already deleted - really remove it this time */
2776 talloc_free(tmp_ctx);
2777 return ldb_next_request(module, req);
2780 rdn_name = ldb_dn_get_rdn_name(old_dn);
2781 rdn_value = ldb_dn_get_rdn_val(old_dn);
2782 if ((rdn_name == NULL) || (rdn_value == NULL)) {
2783 talloc_free(tmp_ctx);
2784 return ldb_operr(ldb);
2787 msg = ldb_msg_new(tmp_ctx);
2788 if (msg == NULL) {
2789 ldb_module_oom(module);
2790 talloc_free(tmp_ctx);
2791 return LDB_ERR_OPERATIONS_ERROR;
2794 msg->dn = old_dn;
2796 if (deletion_state == OBJECT_NOT_DELETED){
2797 /* consider the SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE flag */
2798 disallow_move_on_delete =
2799 (ldb_msg_find_attr_as_int(old_msg, "systemFlags", 0)
2800 & SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE);
2802 /* work out where we will be renaming this object to */
2803 if (!disallow_move_on_delete) {
2804 ret = dsdb_get_deleted_objects_dn(ldb, tmp_ctx, old_dn,
2805 &new_dn);
2806 if (ret != LDB_SUCCESS) {
2807 /* this is probably an attempted delete on a partition
2808 * that doesn't allow delete operations, such as the
2809 * schema partition */
2810 ldb_asprintf_errstring(ldb, "No Deleted Objects container for DN %s",
2811 ldb_dn_get_linearized(old_dn));
2812 talloc_free(tmp_ctx);
2813 return LDB_ERR_UNWILLING_TO_PERFORM;
2815 } else {
2816 new_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
2817 if (new_dn == NULL) {
2818 ldb_module_oom(module);
2819 talloc_free(tmp_ctx);
2820 return LDB_ERR_OPERATIONS_ERROR;
2824 /* get the objects GUID from the search we just did */
2825 guid = samdb_result_guid(old_msg, "objectGUID");
2827 /* Add a formatted child */
2828 retb = ldb_dn_add_child_fmt(new_dn, "%s=%s\\0ADEL:%s",
2829 rdn_name,
2830 ldb_dn_escape_value(tmp_ctx, *rdn_value),
2831 GUID_string(tmp_ctx, &guid));
2832 if (!retb) {
2833 DEBUG(0,(__location__ ": Unable to add a formatted child to dn: %s",
2834 ldb_dn_get_linearized(new_dn)));
2835 talloc_free(tmp_ctx);
2836 return LDB_ERR_OPERATIONS_ERROR;
2839 ret = ldb_msg_add_string(msg, "isDeleted", "TRUE");
2840 if (ret != LDB_SUCCESS) {
2841 DEBUG(0,(__location__ ": Failed to add isDeleted string to the msg\n"));
2842 ldb_module_oom(module);
2843 talloc_free(tmp_ctx);
2844 return ret;
2846 msg->elements[el_count++].flags = LDB_FLAG_MOD_REPLACE;
2850 now we need to modify the object in the following ways:
2852 - add isDeleted=TRUE
2853 - update rDN and name, with new rDN
2854 - remove linked attributes
2855 - remove objectCategory and sAMAccountType
2856 - remove attribs not on the preserved list
2857 - preserved if in above list, or is rDN
2858 - remove all linked attribs from this object
2859 - remove all links from other objects to this object
2860 - add lastKnownParent
2861 - update replPropertyMetaData?
2863 see MS-ADTS "Tombstone Requirements" section 3.1.1.5.5.1.1
2866 /* we need the storage form of the parent GUID */
2867 ret = dsdb_module_search_dn(module, tmp_ctx, &parent_res,
2868 ldb_dn_get_parent(tmp_ctx, old_dn), NULL,
2869 DSDB_FLAG_NEXT_MODULE |
2870 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
2871 DSDB_SEARCH_REVEAL_INTERNALS|
2872 DSDB_SEARCH_SHOW_RECYCLED, req);
2873 if (ret != LDB_SUCCESS) {
2874 talloc_free(tmp_ctx);
2875 return ret;
2878 if (deletion_state == OBJECT_NOT_DELETED){
2879 ret = ldb_msg_add_steal_string(msg, "lastKnownParent",
2880 ldb_dn_get_extended_linearized(tmp_ctx, parent_res->msgs[0]->dn, 1));
2881 if (ret != LDB_SUCCESS) {
2882 DEBUG(0,(__location__ ": Failed to add lastKnownParent string to the msg\n"));
2883 ldb_module_oom(module);
2884 talloc_free(tmp_ctx);
2885 return ret;
2887 msg->elements[el_count++].flags = LDB_FLAG_MOD_ADD;
2890 switch (next_deletion_state){
2892 case OBJECT_DELETED:
2894 ret = ldb_msg_add_value(msg, "msDS-LastKnownRDN", rdn_value, NULL);
2895 if (ret != LDB_SUCCESS) {
2896 DEBUG(0,(__location__ ": Failed to add msDS-LastKnownRDN string to the msg\n"));
2897 ldb_module_oom(module);
2898 talloc_free(tmp_ctx);
2899 return ret;
2901 msg->elements[el_count++].flags = LDB_FLAG_MOD_ADD;
2903 ret = ldb_msg_add_empty(msg, "objectCategory", LDB_FLAG_MOD_DELETE, NULL);
2904 if (ret != LDB_SUCCESS) {
2905 talloc_free(tmp_ctx);
2906 ldb_module_oom(module);
2907 return ret;
2910 ret = ldb_msg_add_empty(msg, "sAMAccountType", LDB_FLAG_MOD_DELETE, NULL);
2911 if (ret != LDB_SUCCESS) {
2912 talloc_free(tmp_ctx);
2913 ldb_module_oom(module);
2914 return ret;
2917 break;
2919 case OBJECT_RECYCLED:
2920 case OBJECT_TOMBSTONE:
2922 /* we also mark it as recycled, meaning this object can't be
2923 recovered (we are stripping its attributes) */
2924 if (dsdb_functional_level(ldb) >= DS_DOMAIN_FUNCTION_2008_R2) {
2925 ret = ldb_msg_add_string(msg, "isRecycled", "TRUE");
2926 if (ret != LDB_SUCCESS) {
2927 DEBUG(0,(__location__ ": Failed to add isRecycled string to the msg\n"));
2928 ldb_module_oom(module);
2929 talloc_free(tmp_ctx);
2930 return ret;
2932 msg->elements[el_count++].flags = LDB_FLAG_MOD_ADD;
2935 /* work out which of the old attributes we will be removing */
2936 for (i=0; i<old_msg->num_elements; i++) {
2937 const struct dsdb_attribute *sa;
2938 el = &old_msg->elements[i];
2939 sa = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
2940 if (!sa) {
2941 talloc_free(tmp_ctx);
2942 return LDB_ERR_OPERATIONS_ERROR;
2944 if (ldb_attr_cmp(el->name, rdn_name) == 0) {
2945 /* don't remove the rDN */
2946 continue;
2948 if (sa->linkID && sa->linkID & 1) {
2949 ret = replmd_delete_remove_link(module, schema, old_dn, el, sa, req);
2950 if (ret != LDB_SUCCESS) {
2951 talloc_free(tmp_ctx);
2952 return LDB_ERR_OPERATIONS_ERROR;
2954 continue;
2956 if (!sa->linkID && ldb_attr_in_list(preserved_attrs, el->name)) {
2957 continue;
2959 ret = ldb_msg_add_empty(msg, el->name, LDB_FLAG_MOD_DELETE, &el);
2960 if (ret != LDB_SUCCESS) {
2961 talloc_free(tmp_ctx);
2962 ldb_module_oom(module);
2963 return ret;
2966 break;
2968 default:
2969 break;
2972 if (deletion_state == OBJECT_NOT_DELETED) {
2973 const struct dsdb_attribute *sa;
2975 /* work out what the new rdn value is, for updating the
2976 rDN and name fields */
2977 new_rdn_value = ldb_dn_get_rdn_val(new_dn);
2978 if (new_rdn_value == NULL) {
2979 talloc_free(tmp_ctx);
2980 return ldb_operr(ldb);
2983 sa = dsdb_attribute_by_lDAPDisplayName(schema, rdn_name);
2984 if (!sa) {
2985 talloc_free(tmp_ctx);
2986 return LDB_ERR_OPERATIONS_ERROR;
2989 ret = ldb_msg_add_value(msg, sa->lDAPDisplayName, new_rdn_value,
2990 &el);
2991 if (ret != LDB_SUCCESS) {
2992 talloc_free(tmp_ctx);
2993 return ret;
2995 el->flags = LDB_FLAG_MOD_REPLACE;
2997 el = ldb_msg_find_element(old_msg, "name");
2998 if (el) {
2999 ret = ldb_msg_add_value(msg, "name", new_rdn_value, &el);
3000 if (ret != LDB_SUCCESS) {
3001 talloc_free(tmp_ctx);
3002 return ret;
3004 el->flags = LDB_FLAG_MOD_REPLACE;
3008 ret = dsdb_module_modify(module, msg, DSDB_FLAG_OWN_MODULE, req);
3009 if (ret != LDB_SUCCESS) {
3010 ldb_asprintf_errstring(ldb, "replmd_delete: Failed to modify object %s in delete - %s",
3011 ldb_dn_get_linearized(old_dn), ldb_errstring(ldb));
3012 talloc_free(tmp_ctx);
3013 return ret;
3016 if (deletion_state == OBJECT_NOT_DELETED) {
3017 /* now rename onto the new DN */
3018 ret = dsdb_module_rename(module, old_dn, new_dn, DSDB_FLAG_NEXT_MODULE, req);
3019 if (ret != LDB_SUCCESS){
3020 DEBUG(0,(__location__ ": Failed to rename object from '%s' to '%s' - %s\n",
3021 ldb_dn_get_linearized(old_dn),
3022 ldb_dn_get_linearized(new_dn),
3023 ldb_errstring(ldb)));
3024 talloc_free(tmp_ctx);
3025 return ret;
3029 talloc_free(tmp_ctx);
3031 return ldb_module_done(req, NULL, NULL, LDB_SUCCESS);
3036 static int replmd_replicated_request_error(struct replmd_replicated_request *ar, int ret)
3038 return ret;
3041 static int replmd_replicated_request_werror(struct replmd_replicated_request *ar, WERROR status)
3043 int ret = LDB_ERR_OTHER;
3044 /* TODO: do some error mapping */
3045 return ret;
3049 static struct replPropertyMetaData1 *
3050 replmd_replPropertyMetaData1_find_attid(struct replPropertyMetaDataBlob *md_blob,
3051 enum drsuapi_DsAttributeId attid)
3053 uint32_t i;
3054 struct replPropertyMetaDataCtr1 *rpmd_ctr = &md_blob->ctr.ctr1;
3056 for (i = 0; i < rpmd_ctr->count; i++) {
3057 if (rpmd_ctr->array[i].attid == attid) {
3058 return &rpmd_ctr->array[i];
3061 return NULL;
3066 return true if an update is newer than an existing entry
3067 see section 5.11 of MS-ADTS
3069 static bool replmd_update_is_newer(const struct GUID *current_invocation_id,
3070 const struct GUID *update_invocation_id,
3071 uint32_t current_version,
3072 uint32_t update_version,
3073 NTTIME current_change_time,
3074 NTTIME update_change_time)
3076 if (update_version != current_version) {
3077 return update_version > current_version;
3079 if (update_change_time != current_change_time) {
3080 return update_change_time > current_change_time;
3082 return GUID_compare(update_invocation_id, current_invocation_id) > 0;
3085 static bool replmd_replPropertyMetaData1_is_newer(struct replPropertyMetaData1 *cur_m,
3086 struct replPropertyMetaData1 *new_m)
3088 return replmd_update_is_newer(&cur_m->originating_invocation_id,
3089 &new_m->originating_invocation_id,
3090 cur_m->version,
3091 new_m->version,
3092 cur_m->originating_change_time,
3093 new_m->originating_change_time);
3098 form a conflict DN
3100 static struct ldb_dn *replmd_conflict_dn(TALLOC_CTX *mem_ctx, struct ldb_dn *dn, struct GUID *guid)
3102 const struct ldb_val *rdn_val;
3103 const char *rdn_name;
3104 struct ldb_dn *new_dn;
3106 rdn_val = ldb_dn_get_rdn_val(dn);
3107 rdn_name = ldb_dn_get_rdn_name(dn);
3108 if (!rdn_val || !rdn_name) {
3109 return NULL;
3112 new_dn = ldb_dn_copy(mem_ctx, dn);
3113 if (!new_dn) {
3114 return NULL;
3117 if (!ldb_dn_remove_child_components(new_dn, 1)) {
3118 return NULL;
3121 if (!ldb_dn_add_child_fmt(new_dn, "%s=%s\\0ACNF:%s",
3122 rdn_name,
3123 ldb_dn_escape_value(new_dn, *rdn_val),
3124 GUID_string(new_dn, guid))) {
3125 return NULL;
3128 return new_dn;
3133 perform a modify operation which sets the rDN and name attributes to
3134 their current values. This has the effect of changing these
3135 attributes to have been last updated by the current DC. This is
3136 needed to ensure that renames performed as part of conflict
3137 resolution are propogated to other DCs
3139 static int replmd_name_modify(struct replmd_replicated_request *ar,
3140 struct ldb_request *req, struct ldb_dn *dn)
3142 struct ldb_message *msg;
3143 const char *rdn_name;
3144 const struct ldb_val *rdn_val;
3145 const struct dsdb_attribute *rdn_attr;
3146 int ret;
3148 msg = ldb_msg_new(req);
3149 if (msg == NULL) {
3150 goto failed;
3152 msg->dn = dn;
3154 rdn_name = ldb_dn_get_rdn_name(dn);
3155 if (rdn_name == NULL) {
3156 goto failed;
3159 /* normalize the rdn attribute name */
3160 rdn_attr = dsdb_attribute_by_lDAPDisplayName(ar->schema, rdn_name);
3161 if (rdn_attr == NULL) {
3162 goto failed;
3164 rdn_name = rdn_attr->lDAPDisplayName;
3166 rdn_val = ldb_dn_get_rdn_val(dn);
3167 if (rdn_val == NULL) {
3168 goto failed;
3171 if (ldb_msg_add_empty(msg, rdn_name, LDB_FLAG_MOD_REPLACE, NULL) != 0) {
3172 goto failed;
3174 if (ldb_msg_add_value(msg, rdn_name, rdn_val, NULL) != 0) {
3175 goto failed;
3177 if (ldb_msg_add_empty(msg, "name", LDB_FLAG_MOD_REPLACE, NULL) != 0) {
3178 goto failed;
3180 if (ldb_msg_add_value(msg, "name", rdn_val, NULL) != 0) {
3181 goto failed;
3184 ret = dsdb_module_modify(ar->module, msg, DSDB_FLAG_OWN_MODULE, req);
3185 if (ret != LDB_SUCCESS) {
3186 DEBUG(0,(__location__ ": Failed to modify rDN/name of conflict DN '%s' - %s",
3187 ldb_dn_get_linearized(dn),
3188 ldb_errstring(ldb_module_get_ctx(ar->module))));
3189 return ret;
3192 talloc_free(msg);
3194 return LDB_SUCCESS;
3196 failed:
3197 talloc_free(msg);
3198 DEBUG(0,(__location__ ": Failed to setup modify rDN/name of conflict DN '%s'",
3199 ldb_dn_get_linearized(dn)));
3200 return LDB_ERR_OPERATIONS_ERROR;
3205 callback for conflict DN handling where we have renamed the incoming
3206 record. After renaming it, we need to ensure the change of name and
3207 rDN for the incoming record is seen as an originating update by this DC.
3209 static int replmd_op_name_modify_callback(struct ldb_request *req, struct ldb_reply *ares)
3211 struct replmd_replicated_request *ar =
3212 talloc_get_type_abort(req->context, struct replmd_replicated_request);
3213 int ret;
3215 if (ares->error != LDB_SUCCESS) {
3216 /* call the normal callback for everything except success */
3217 return replmd_op_callback(req, ares);
3220 /* perform a modify of the rDN and name of the record */
3221 ret = replmd_name_modify(ar, req, req->op.add.message->dn);
3222 if (ret != LDB_SUCCESS) {
3223 ares->error = ret;
3224 return replmd_op_callback(req, ares);
3227 return replmd_op_callback(req, ares);
3231 callback for replmd_replicated_apply_add()
3232 This copes with the creation of conflict records in the case where
3233 the DN exists, but with a different objectGUID
3235 static int replmd_op_add_callback(struct ldb_request *req, struct ldb_reply *ares)
3237 struct ldb_dn *conflict_dn;
3238 struct replmd_replicated_request *ar =
3239 talloc_get_type_abort(req->context, struct replmd_replicated_request);
3240 struct ldb_result *res;
3241 const char *attrs[] = { "replPropertyMetaData", "objectGUID", NULL };
3242 int ret;
3243 const struct ldb_val *rmd_value, *omd_value;
3244 struct replPropertyMetaDataBlob omd, rmd;
3245 enum ndr_err_code ndr_err;
3246 bool rename_incoming_record;
3247 struct replPropertyMetaData1 *rmd_name, *omd_name;
3249 if (ares->error != LDB_ERR_ENTRY_ALREADY_EXISTS) {
3250 /* call the normal callback for everything except
3251 conflicts */
3252 return replmd_op_callback(req, ares);
3256 * we have a conflict, and need to decide if we will keep the
3257 * new record or the old record
3259 conflict_dn = req->op.add.message->dn;
3262 * first we need the replPropertyMetaData attribute from the
3263 * old record
3265 ret = dsdb_module_search_dn(ar->module, req, &res, conflict_dn,
3266 attrs,
3267 DSDB_FLAG_NEXT_MODULE |
3268 DSDB_SEARCH_SHOW_DELETED |
3269 DSDB_SEARCH_SHOW_RECYCLED, req);
3270 if (ret != LDB_SUCCESS) {
3271 DEBUG(0,(__location__ ": Unable to find object for conflicting record '%s'\n",
3272 ldb_dn_get_linearized(conflict_dn)));
3273 goto failed;
3276 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
3277 if (omd_value == NULL) {
3278 DEBUG(0,(__location__ ": Unable to find replPropertyMetaData for conflicting record '%s'\n",
3279 ldb_dn_get_linearized(conflict_dn)));
3280 goto failed;
3283 ndr_err = ndr_pull_struct_blob(omd_value, res->msgs[0], &omd,
3284 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
3285 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
3286 DEBUG(0,(__location__ ": Failed to parse old replPropertyMetaData for %s\n",
3287 ldb_dn_get_linearized(conflict_dn)));
3288 goto failed;
3292 * and the replPropertyMetaData attribute from the
3293 * new record
3295 rmd_value = ldb_msg_find_ldb_val(req->op.add.message, "replPropertyMetaData");
3296 if (rmd_value == NULL) {
3297 DEBUG(0,(__location__ ": Unable to find replPropertyMetaData for new record '%s'\n",
3298 ldb_dn_get_linearized(conflict_dn)));
3299 goto failed;
3302 ndr_err = ndr_pull_struct_blob(rmd_value, req, &rmd,
3303 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
3304 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
3305 DEBUG(0,(__location__ ": Failed to parse new replPropertyMetaData for %s\n",
3306 ldb_dn_get_linearized(conflict_dn)));
3307 goto failed;
3310 /* we decide which is newer based on the RPMD on the name
3311 attribute. See [MS-DRSR] ResolveNameConflict */
3312 rmd_name = replmd_replPropertyMetaData1_find_attid(&rmd, DRSUAPI_ATTID_name);
3313 omd_name = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
3314 if (!rmd_name || !omd_name) {
3315 DEBUG(0,(__location__ ": Failed to find name attribute in replPropertyMetaData for %s\n",
3316 ldb_dn_get_linearized(conflict_dn)));
3317 goto failed;
3320 rename_incoming_record = !replmd_replPropertyMetaData1_is_newer(omd_name, rmd_name);
3322 if (rename_incoming_record) {
3323 struct GUID guid;
3324 struct ldb_dn *new_dn;
3325 struct ldb_message *new_msg;
3327 guid = samdb_result_guid(req->op.add.message, "objectGUID");
3328 if (GUID_all_zero(&guid)) {
3329 DEBUG(0,(__location__ ": Failed to find objectGUID for conflicting incoming record %s\n",
3330 ldb_dn_get_linearized(conflict_dn)));
3331 goto failed;
3333 new_dn = replmd_conflict_dn(req, conflict_dn, &guid);
3334 if (new_dn == NULL) {
3335 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
3336 ldb_dn_get_linearized(conflict_dn)));
3337 goto failed;
3340 DEBUG(1,(__location__ ": Resolving conflict record via incoming rename '%s' -> '%s'\n",
3341 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
3343 /* re-submit the request, but with a different
3344 callback, so we don't loop forever. */
3345 new_msg = ldb_msg_copy_shallow(req, req->op.add.message);
3346 if (!new_msg) {
3347 goto failed;
3348 DEBUG(0,(__location__ ": Failed to copy conflict DN message for %s\n",
3349 ldb_dn_get_linearized(conflict_dn)));
3351 new_msg->dn = new_dn;
3352 req->op.add.message = new_msg;
3353 req->callback = replmd_op_name_modify_callback;
3355 return ldb_next_request(ar->module, req);
3356 } else {
3357 /* we are renaming the existing record */
3358 struct GUID guid;
3359 struct ldb_dn *new_dn;
3361 guid = samdb_result_guid(res->msgs[0], "objectGUID");
3362 if (GUID_all_zero(&guid)) {
3363 DEBUG(0,(__location__ ": Failed to find objectGUID for existing conflict record %s\n",
3364 ldb_dn_get_linearized(conflict_dn)));
3365 goto failed;
3368 new_dn = replmd_conflict_dn(req, conflict_dn, &guid);
3369 if (new_dn == NULL) {
3370 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
3371 ldb_dn_get_linearized(conflict_dn)));
3372 goto failed;
3375 DEBUG(1,(__location__ ": Resolving conflict record via existing rename '%s' -> '%s'\n",
3376 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
3378 ret = dsdb_module_rename(ar->module, conflict_dn, new_dn,
3379 DSDB_FLAG_OWN_MODULE, req);
3380 if (ret != LDB_SUCCESS) {
3381 DEBUG(0,(__location__ ": Failed to rename conflict dn '%s' to '%s' - %s\n",
3382 ldb_dn_get_linearized(conflict_dn),
3383 ldb_dn_get_linearized(new_dn),
3384 ldb_errstring(ldb_module_get_ctx(ar->module))));
3385 goto failed;
3389 * now we need to ensure that the rename is seen as an
3390 * originating update. We do that with a modify.
3392 ret = replmd_name_modify(ar, req, new_dn);
3393 if (ret != LDB_SUCCESS) {
3394 goto failed;
3397 req->callback = replmd_op_callback;
3399 return ldb_next_request(ar->module, req);
3402 failed:
3403 /* on failure do the original callback. This means replication
3404 * will stop with an error, but there is not much else we can
3405 * do
3407 return replmd_op_callback(req, ares);
3411 this is called when a new object comes in over DRS
3413 static int replmd_replicated_apply_add(struct replmd_replicated_request *ar)
3415 struct ldb_context *ldb;
3416 struct ldb_request *change_req;
3417 enum ndr_err_code ndr_err;
3418 struct ldb_message *msg;
3419 struct replPropertyMetaDataBlob *md;
3420 struct ldb_val md_value;
3421 unsigned int i;
3422 int ret;
3425 * TODO: check if the parent object exist
3429 * TODO: handle the conflict case where an object with the
3430 * same name exist
3433 ldb = ldb_module_get_ctx(ar->module);
3434 msg = ar->objs->objects[ar->index_current].msg;
3435 md = ar->objs->objects[ar->index_current].meta_data;
3437 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
3438 if (ret != LDB_SUCCESS) {
3439 return replmd_replicated_request_error(ar, ret);
3442 ret = ldb_msg_add_value(msg, "objectGUID", &ar->objs->objects[ar->index_current].guid_value, NULL);
3443 if (ret != LDB_SUCCESS) {
3444 return replmd_replicated_request_error(ar, ret);
3447 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
3448 if (ret != LDB_SUCCESS) {
3449 return replmd_replicated_request_error(ar, ret);
3452 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", ar->seq_num);
3453 if (ret != LDB_SUCCESS) {
3454 return replmd_replicated_request_error(ar, ret);
3457 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
3458 if (ret != LDB_SUCCESS) {
3459 return replmd_replicated_request_error(ar, ret);
3462 /* remove any message elements that have zero values */
3463 for (i=0; i<msg->num_elements; i++) {
3464 struct ldb_message_element *el = &msg->elements[i];
3466 if (el->num_values == 0) {
3467 DEBUG(4,(__location__ ": Removing attribute %s with num_values==0\n",
3468 el->name));
3469 memmove(el, el+1, sizeof(*el)*(msg->num_elements - (i+1)));
3470 msg->num_elements--;
3471 i--;
3472 continue;
3477 * the meta data array is already sorted by the caller
3479 for (i=0; i < md->ctr.ctr1.count; i++) {
3480 md->ctr.ctr1.array[i].local_usn = ar->seq_num;
3482 ndr_err = ndr_push_struct_blob(&md_value, msg, md,
3483 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
3484 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
3485 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
3486 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
3488 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &md_value, NULL);
3489 if (ret != LDB_SUCCESS) {
3490 return replmd_replicated_request_error(ar, ret);
3493 replmd_ldb_message_sort(msg, ar->schema);
3495 if (DEBUGLVL(4)) {
3496 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_ADD, msg);
3497 DEBUG(4, ("DRS replication add message:\n%s\n", s));
3498 talloc_free(s);
3501 ret = ldb_build_add_req(&change_req,
3502 ldb,
3504 msg,
3505 ar->controls,
3507 replmd_op_add_callback,
3508 ar->req);
3509 LDB_REQ_SET_LOCATION(change_req);
3510 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
3512 /* current partition control needed by "repmd_op_callback" */
3513 ret = ldb_request_add_control(change_req,
3514 DSDB_CONTROL_CURRENT_PARTITION_OID,
3515 false, NULL);
3516 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
3518 return ldb_next_request(ar->module, change_req);
3522 handle renames that come in over DRS replication
3524 static int replmd_replicated_handle_rename(struct replmd_replicated_request *ar,
3525 struct ldb_message *msg,
3526 struct replPropertyMetaDataBlob *rmd,
3527 struct replPropertyMetaDataBlob *omd,
3528 struct ldb_request *parent)
3530 struct replPropertyMetaData1 *md_remote;
3531 struct replPropertyMetaData1 *md_local;
3533 if (ldb_dn_compare(msg->dn, ar->search_msg->dn) == 0) {
3534 /* no rename */
3535 return LDB_SUCCESS;
3538 /* now we need to check for double renames. We could have a
3539 * local rename pending which our replication partner hasn't
3540 * received yet. We choose which one wins by looking at the
3541 * attribute stamps on the two objects, the newer one wins
3543 md_remote = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
3544 md_local = replmd_replPropertyMetaData1_find_attid(omd, DRSUAPI_ATTID_name);
3545 /* if there is no name attribute then we have to assume the
3546 object we've received is in fact newer */
3547 if (!md_remote || !md_local ||
3548 replmd_replPropertyMetaData1_is_newer(md_local, md_remote)) {
3549 DEBUG(4,("replmd_replicated_request rename %s => %s\n",
3550 ldb_dn_get_linearized(ar->search_msg->dn),
3551 ldb_dn_get_linearized(msg->dn)));
3552 /* pass rename to the next module
3553 * so it doesn't appear as an originating update */
3554 return dsdb_module_rename(ar->module,
3555 ar->search_msg->dn, msg->dn,
3556 DSDB_FLAG_NEXT_MODULE | DSDB_MODIFY_RELAX, parent);
3559 /* we're going to keep our old object */
3560 DEBUG(4,(__location__ ": Keeping object %s and rejecting older rename to %s\n",
3561 ldb_dn_get_linearized(ar->search_msg->dn),
3562 ldb_dn_get_linearized(msg->dn)));
3563 return LDB_SUCCESS;
3567 static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar)
3569 struct ldb_context *ldb;
3570 struct ldb_request *change_req;
3571 enum ndr_err_code ndr_err;
3572 struct ldb_message *msg;
3573 struct replPropertyMetaDataBlob *rmd;
3574 struct replPropertyMetaDataBlob omd;
3575 const struct ldb_val *omd_value;
3576 struct replPropertyMetaDataBlob nmd;
3577 struct ldb_val nmd_value;
3578 unsigned int i;
3579 uint32_t j,ni=0;
3580 unsigned int removed_attrs = 0;
3581 int ret;
3583 ldb = ldb_module_get_ctx(ar->module);
3584 msg = ar->objs->objects[ar->index_current].msg;
3585 rmd = ar->objs->objects[ar->index_current].meta_data;
3586 ZERO_STRUCT(omd);
3587 omd.version = 1;
3589 /* find existing meta data */
3590 omd_value = ldb_msg_find_ldb_val(ar->search_msg, "replPropertyMetaData");
3591 if (omd_value) {
3592 ndr_err = ndr_pull_struct_blob(omd_value, ar, &omd,
3593 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
3594 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
3595 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
3596 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
3599 if (omd.version != 1) {
3600 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
3604 /* handle renames that come in over DRS */
3605 ret = replmd_replicated_handle_rename(ar, msg, rmd, &omd, ar->req);
3606 if (ret != LDB_SUCCESS) {
3607 ldb_debug(ldb, LDB_DEBUG_FATAL,
3608 "replmd_replicated_request rename %s => %s failed - %s\n",
3609 ldb_dn_get_linearized(ar->search_msg->dn),
3610 ldb_dn_get_linearized(msg->dn),
3611 ldb_errstring(ldb));
3612 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
3615 ZERO_STRUCT(nmd);
3616 nmd.version = 1;
3617 nmd.ctr.ctr1.count = omd.ctr.ctr1.count + rmd->ctr.ctr1.count;
3618 nmd.ctr.ctr1.array = talloc_array(ar,
3619 struct replPropertyMetaData1,
3620 nmd.ctr.ctr1.count);
3621 if (!nmd.ctr.ctr1.array) return replmd_replicated_request_werror(ar, WERR_NOMEM);
3623 /* first copy the old meta data */
3624 for (i=0; i < omd.ctr.ctr1.count; i++) {
3625 nmd.ctr.ctr1.array[ni] = omd.ctr.ctr1.array[i];
3626 ni++;
3629 ar->seq_num = 0;
3630 /* now merge in the new meta data */
3631 for (i=0; i < rmd->ctr.ctr1.count; i++) {
3632 bool found = false;
3634 for (j=0; j < ni; j++) {
3635 bool cmp;
3637 if (rmd->ctr.ctr1.array[i].attid != nmd.ctr.ctr1.array[j].attid) {
3638 continue;
3641 cmp = replmd_replPropertyMetaData1_is_newer(&nmd.ctr.ctr1.array[j],
3642 &rmd->ctr.ctr1.array[i]);
3643 if (cmp) {
3644 /* replace the entry */
3645 nmd.ctr.ctr1.array[j] = rmd->ctr.ctr1.array[i];
3646 if (ar->seq_num == 0) {
3647 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
3648 if (ret != LDB_SUCCESS) {
3649 return replmd_replicated_request_error(ar, ret);
3652 nmd.ctr.ctr1.array[j].local_usn = ar->seq_num;
3653 found = true;
3654 break;
3657 if (rmd->ctr.ctr1.array[i].attid != DRSUAPI_ATTID_instanceType) {
3658 DEBUG(3,("Discarding older DRS attribute update to %s on %s from %s\n",
3659 msg->elements[i-removed_attrs].name,
3660 ldb_dn_get_linearized(msg->dn),
3661 GUID_string(ar, &rmd->ctr.ctr1.array[i].originating_invocation_id)));
3664 /* we don't want to apply this change so remove the attribute */
3665 ldb_msg_remove_element(msg, &msg->elements[i-removed_attrs]);
3666 removed_attrs++;
3668 found = true;
3669 break;
3672 if (found) continue;
3674 nmd.ctr.ctr1.array[ni] = rmd->ctr.ctr1.array[i];
3675 if (ar->seq_num == 0) {
3676 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
3677 if (ret != LDB_SUCCESS) {
3678 return replmd_replicated_request_error(ar, ret);
3681 nmd.ctr.ctr1.array[ni].local_usn = ar->seq_num;
3682 ni++;
3686 * finally correct the size of the meta_data array
3688 nmd.ctr.ctr1.count = ni;
3691 * the rdn attribute (the alias for the name attribute),
3692 * 'cn' for most objects is the last entry in the meta data array
3693 * we have stored
3695 * sort the new meta data array
3697 ret = replmd_replPropertyMetaDataCtr1_sort(&nmd.ctr.ctr1, ar->schema, msg->dn);
3698 if (ret != LDB_SUCCESS) {
3699 return ret;
3703 * check if some replicated attributes left, otherwise skip the ldb_modify() call
3705 if (msg->num_elements == 0) {
3706 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: skip replace\n",
3707 ar->index_current);
3709 ar->index_current++;
3710 return replmd_replicated_apply_next(ar);
3713 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: replace %u attributes\n",
3714 ar->index_current, msg->num_elements);
3716 /* create the meta data value */
3717 ndr_err = ndr_push_struct_blob(&nmd_value, msg, &nmd,
3718 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
3719 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
3720 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
3721 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
3725 * when we know that we'll modify the record, add the whenChanged, uSNChanged
3726 * and replPopertyMetaData attributes
3728 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
3729 if (ret != LDB_SUCCESS) {
3730 return replmd_replicated_request_error(ar, ret);
3732 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
3733 if (ret != LDB_SUCCESS) {
3734 return replmd_replicated_request_error(ar, ret);
3736 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
3737 if (ret != LDB_SUCCESS) {
3738 return replmd_replicated_request_error(ar, ret);
3741 replmd_ldb_message_sort(msg, ar->schema);
3743 /* we want to replace the old values */
3744 for (i=0; i < msg->num_elements; i++) {
3745 msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
3748 if (DEBUGLVL(4)) {
3749 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_MODIFY, msg);
3750 DEBUG(4, ("DRS replication modify message:\n%s\n", s));
3751 talloc_free(s);
3754 ret = ldb_build_mod_req(&change_req,
3755 ldb,
3757 msg,
3758 ar->controls,
3760 replmd_op_callback,
3761 ar->req);
3762 LDB_REQ_SET_LOCATION(change_req);
3763 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
3765 /* current partition control needed by "repmd_op_callback" */
3766 ret = ldb_request_add_control(change_req,
3767 DSDB_CONTROL_CURRENT_PARTITION_OID,
3768 false, NULL);
3769 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
3771 return ldb_next_request(ar->module, change_req);
3774 static int replmd_replicated_apply_search_callback(struct ldb_request *req,
3775 struct ldb_reply *ares)
3777 struct replmd_replicated_request *ar = talloc_get_type(req->context,
3778 struct replmd_replicated_request);
3779 int ret;
3781 if (!ares) {
3782 return ldb_module_done(ar->req, NULL, NULL,
3783 LDB_ERR_OPERATIONS_ERROR);
3785 if (ares->error != LDB_SUCCESS &&
3786 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
3787 return ldb_module_done(ar->req, ares->controls,
3788 ares->response, ares->error);
3791 switch (ares->type) {
3792 case LDB_REPLY_ENTRY:
3793 ar->search_msg = talloc_steal(ar, ares->message);
3794 break;
3796 case LDB_REPLY_REFERRAL:
3797 /* we ignore referrals */
3798 break;
3800 case LDB_REPLY_DONE:
3801 if (ar->search_msg != NULL) {
3802 ret = replmd_replicated_apply_merge(ar);
3803 } else {
3804 ret = replmd_replicated_apply_add(ar);
3806 if (ret != LDB_SUCCESS) {
3807 return ldb_module_done(ar->req, NULL, NULL, ret);
3811 talloc_free(ares);
3812 return LDB_SUCCESS;
3815 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar);
3817 static int replmd_replicated_apply_next(struct replmd_replicated_request *ar)
3819 struct ldb_context *ldb;
3820 int ret;
3821 char *tmp_str;
3822 char *filter;
3823 struct ldb_request *search_req;
3824 struct ldb_search_options_control *options;
3826 if (ar->index_current >= ar->objs->num_objects) {
3827 /* done with it, go to next stage */
3828 return replmd_replicated_uptodate_vector(ar);
3831 ldb = ldb_module_get_ctx(ar->module);
3832 ar->search_msg = NULL;
3834 tmp_str = ldb_binary_encode(ar, ar->objs->objects[ar->index_current].guid_value);
3835 if (!tmp_str) return replmd_replicated_request_werror(ar, WERR_NOMEM);
3837 filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
3838 if (!filter) return replmd_replicated_request_werror(ar, WERR_NOMEM);
3839 talloc_free(tmp_str);
3841 ret = ldb_build_search_req(&search_req,
3842 ldb,
3844 NULL,
3845 LDB_SCOPE_SUBTREE,
3846 filter,
3847 NULL,
3848 NULL,
3850 replmd_replicated_apply_search_callback,
3851 ar->req);
3852 LDB_REQ_SET_LOCATION(search_req);
3854 ret = ldb_request_add_control(search_req, LDB_CONTROL_SHOW_RECYCLED_OID,
3855 true, NULL);
3856 if (ret != LDB_SUCCESS) {
3857 return ret;
3860 /* we need to cope with cross-partition links, so search for
3861 the GUID over all partitions */
3862 options = talloc(search_req, struct ldb_search_options_control);
3863 if (options == NULL) {
3864 DEBUG(0, (__location__ ": out of memory\n"));
3865 return LDB_ERR_OPERATIONS_ERROR;
3867 options->search_options = LDB_SEARCH_OPTION_PHANTOM_ROOT;
3869 ret = ldb_request_add_control(search_req,
3870 LDB_CONTROL_SEARCH_OPTIONS_OID,
3871 true, options);
3872 if (ret != LDB_SUCCESS) {
3873 return ret;
3876 return ldb_next_request(ar->module, search_req);
3879 static int replmd_replicated_uptodate_modify_callback(struct ldb_request *req,
3880 struct ldb_reply *ares)
3882 struct ldb_context *ldb;
3883 struct replmd_replicated_request *ar = talloc_get_type(req->context,
3884 struct replmd_replicated_request);
3885 ldb = ldb_module_get_ctx(ar->module);
3887 if (!ares) {
3888 return ldb_module_done(ar->req, NULL, NULL,
3889 LDB_ERR_OPERATIONS_ERROR);
3891 if (ares->error != LDB_SUCCESS) {
3892 return ldb_module_done(ar->req, ares->controls,
3893 ares->response, ares->error);
3896 if (ares->type != LDB_REPLY_DONE) {
3897 ldb_set_errstring(ldb, "Invalid reply type\n!");
3898 return ldb_module_done(ar->req, NULL, NULL,
3899 LDB_ERR_OPERATIONS_ERROR);
3902 talloc_free(ares);
3904 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
3907 static int replmd_replicated_uptodate_modify(struct replmd_replicated_request *ar)
3909 struct ldb_context *ldb;
3910 struct ldb_request *change_req;
3911 enum ndr_err_code ndr_err;
3912 struct ldb_message *msg;
3913 struct replUpToDateVectorBlob ouv;
3914 const struct ldb_val *ouv_value;
3915 const struct drsuapi_DsReplicaCursor2CtrEx *ruv;
3916 struct replUpToDateVectorBlob nuv;
3917 struct ldb_val nuv_value;
3918 struct ldb_message_element *nuv_el = NULL;
3919 const struct GUID *our_invocation_id;
3920 struct ldb_message_element *orf_el = NULL;
3921 struct repsFromToBlob nrf;
3922 struct ldb_val *nrf_value = NULL;
3923 struct ldb_message_element *nrf_el = NULL;
3924 unsigned int i;
3925 uint32_t j,ni=0;
3926 bool found = false;
3927 time_t t = time(NULL);
3928 NTTIME now;
3929 int ret;
3930 uint32_t instanceType;
3932 ldb = ldb_module_get_ctx(ar->module);
3933 ruv = ar->objs->uptodateness_vector;
3934 ZERO_STRUCT(ouv);
3935 ouv.version = 2;
3936 ZERO_STRUCT(nuv);
3937 nuv.version = 2;
3939 unix_to_nt_time(&now, t);
3941 instanceType = ldb_msg_find_attr_as_uint(ar->search_msg, "instanceType", 0);
3942 if (! (instanceType & INSTANCE_TYPE_IS_NC_HEAD)) {
3943 DEBUG(4,(__location__ ": Skipping UDV and repsFrom update as not NC root: %s\n",
3944 ldb_dn_get_linearized(ar->search_msg->dn)));
3945 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
3949 * first create the new replUpToDateVector
3951 ouv_value = ldb_msg_find_ldb_val(ar->search_msg, "replUpToDateVector");
3952 if (ouv_value) {
3953 ndr_err = ndr_pull_struct_blob(ouv_value, ar, &ouv,
3954 (ndr_pull_flags_fn_t)ndr_pull_replUpToDateVectorBlob);
3955 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
3956 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
3957 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
3960 if (ouv.version != 2) {
3961 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
3966 * the new uptodateness vector will at least
3967 * contain 1 entry, one for the source_dsa
3969 * plus optional values from our old vector and the one from the source_dsa
3971 nuv.ctr.ctr2.count = 1 + ouv.ctr.ctr2.count;
3972 if (ruv) nuv.ctr.ctr2.count += ruv->count;
3973 nuv.ctr.ctr2.cursors = talloc_array(ar,
3974 struct drsuapi_DsReplicaCursor2,
3975 nuv.ctr.ctr2.count);
3976 if (!nuv.ctr.ctr2.cursors) return replmd_replicated_request_werror(ar, WERR_NOMEM);
3978 /* first copy the old vector */
3979 for (i=0; i < ouv.ctr.ctr2.count; i++) {
3980 nuv.ctr.ctr2.cursors[ni] = ouv.ctr.ctr2.cursors[i];
3981 ni++;
3984 /* get our invocation_id if we have one already attached to the ldb */
3985 our_invocation_id = samdb_ntds_invocation_id(ldb);
3987 /* merge in the source_dsa vector is available */
3988 for (i=0; (ruv && i < ruv->count); i++) {
3989 found = false;
3991 if (our_invocation_id &&
3992 GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
3993 our_invocation_id)) {
3994 continue;
3997 for (j=0; j < ni; j++) {
3998 if (!GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
3999 &nuv.ctr.ctr2.cursors[j].source_dsa_invocation_id)) {
4000 continue;
4003 found = true;
4006 * we update only the highest_usn and not the latest_sync_success time,
4007 * because the last success stands for direct replication
4009 if (ruv->cursors[i].highest_usn > nuv.ctr.ctr2.cursors[j].highest_usn) {
4010 nuv.ctr.ctr2.cursors[j].highest_usn = ruv->cursors[i].highest_usn;
4012 break;
4015 if (found) continue;
4017 /* if it's not there yet, add it */
4018 nuv.ctr.ctr2.cursors[ni] = ruv->cursors[i];
4019 ni++;
4023 * merge in the current highwatermark for the source_dsa
4025 found = false;
4026 for (j=0; j < ni; j++) {
4027 if (!GUID_equal(&ar->objs->source_dsa->source_dsa_invocation_id,
4028 &nuv.ctr.ctr2.cursors[j].source_dsa_invocation_id)) {
4029 continue;
4032 found = true;
4035 * here we update the highest_usn and last_sync_success time
4036 * because we're directly replicating from the source_dsa
4038 * and use the tmp_highest_usn because this is what we have just applied
4039 * to our ldb
4041 nuv.ctr.ctr2.cursors[j].highest_usn = ar->objs->source_dsa->highwatermark.tmp_highest_usn;
4042 nuv.ctr.ctr2.cursors[j].last_sync_success = now;
4043 break;
4045 if (!found) {
4047 * here we update the highest_usn and last_sync_success time
4048 * because we're directly replicating from the source_dsa
4050 * and use the tmp_highest_usn because this is what we have just applied
4051 * to our ldb
4053 nuv.ctr.ctr2.cursors[ni].source_dsa_invocation_id= ar->objs->source_dsa->source_dsa_invocation_id;
4054 nuv.ctr.ctr2.cursors[ni].highest_usn = ar->objs->source_dsa->highwatermark.tmp_highest_usn;
4055 nuv.ctr.ctr2.cursors[ni].last_sync_success = now;
4056 ni++;
4060 * finally correct the size of the cursors array
4062 nuv.ctr.ctr2.count = ni;
4065 * sort the cursors
4067 TYPESAFE_QSORT(nuv.ctr.ctr2.cursors, nuv.ctr.ctr2.count, drsuapi_DsReplicaCursor2_compare);
4070 * create the change ldb_message
4072 msg = ldb_msg_new(ar);
4073 if (!msg) return replmd_replicated_request_werror(ar, WERR_NOMEM);
4074 msg->dn = ar->search_msg->dn;
4076 ndr_err = ndr_push_struct_blob(&nuv_value, msg, &nuv,
4077 (ndr_push_flags_fn_t)ndr_push_replUpToDateVectorBlob);
4078 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
4079 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
4080 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
4082 ret = ldb_msg_add_value(msg, "replUpToDateVector", &nuv_value, &nuv_el);
4083 if (ret != LDB_SUCCESS) {
4084 return replmd_replicated_request_error(ar, ret);
4086 nuv_el->flags = LDB_FLAG_MOD_REPLACE;
4089 * now create the new repsFrom value from the given repsFromTo1 structure
4091 ZERO_STRUCT(nrf);
4092 nrf.version = 1;
4093 nrf.ctr.ctr1 = *ar->objs->source_dsa;
4094 nrf.ctr.ctr1.highwatermark.highest_usn = nrf.ctr.ctr1.highwatermark.tmp_highest_usn;
4097 * first see if we already have a repsFrom value for the current source dsa
4098 * if so we'll later replace this value
4100 orf_el = ldb_msg_find_element(ar->search_msg, "repsFrom");
4101 if (orf_el) {
4102 for (i=0; i < orf_el->num_values; i++) {
4103 struct repsFromToBlob *trf;
4105 trf = talloc(ar, struct repsFromToBlob);
4106 if (!trf) return replmd_replicated_request_werror(ar, WERR_NOMEM);
4108 ndr_err = ndr_pull_struct_blob(&orf_el->values[i], trf, trf,
4109 (ndr_pull_flags_fn_t)ndr_pull_repsFromToBlob);
4110 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
4111 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
4112 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
4115 if (trf->version != 1) {
4116 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
4120 * we compare the source dsa objectGUID not the invocation_id
4121 * because we want only one repsFrom value per source dsa
4122 * and when the invocation_id of the source dsa has changed we don't need
4123 * the old repsFrom with the old invocation_id
4125 if (!GUID_equal(&trf->ctr.ctr1.source_dsa_obj_guid,
4126 &ar->objs->source_dsa->source_dsa_obj_guid)) {
4127 talloc_free(trf);
4128 continue;
4131 talloc_free(trf);
4132 nrf_value = &orf_el->values[i];
4133 break;
4137 * copy over all old values to the new ldb_message
4139 ret = ldb_msg_add_empty(msg, "repsFrom", 0, &nrf_el);
4140 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
4141 *nrf_el = *orf_el;
4145 * if we haven't found an old repsFrom value for the current source dsa
4146 * we'll add a new value
4148 if (!nrf_value) {
4149 struct ldb_val zero_value;
4150 ZERO_STRUCT(zero_value);
4151 ret = ldb_msg_add_value(msg, "repsFrom", &zero_value, &nrf_el);
4152 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
4154 nrf_value = &nrf_el->values[nrf_el->num_values - 1];
4157 /* we now fill the value which is already attached to ldb_message */
4158 ndr_err = ndr_push_struct_blob(nrf_value, msg,
4159 &nrf,
4160 (ndr_push_flags_fn_t)ndr_push_repsFromToBlob);
4161 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
4162 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
4163 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
4167 * the ldb_message_element for the attribute, has all the old values and the new one
4168 * so we'll replace the whole attribute with all values
4170 nrf_el->flags = LDB_FLAG_MOD_REPLACE;
4172 if (DEBUGLVL(4)) {
4173 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_MODIFY, msg);
4174 DEBUG(4, ("DRS replication uptodate modify message:\n%s\n", s));
4175 talloc_free(s);
4178 /* prepare the ldb_modify() request */
4179 ret = ldb_build_mod_req(&change_req,
4180 ldb,
4182 msg,
4183 ar->controls,
4185 replmd_replicated_uptodate_modify_callback,
4186 ar->req);
4187 LDB_REQ_SET_LOCATION(change_req);
4188 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
4190 return ldb_next_request(ar->module, change_req);
4193 static int replmd_replicated_uptodate_search_callback(struct ldb_request *req,
4194 struct ldb_reply *ares)
4196 struct replmd_replicated_request *ar = talloc_get_type(req->context,
4197 struct replmd_replicated_request);
4198 int ret;
4200 if (!ares) {
4201 return ldb_module_done(ar->req, NULL, NULL,
4202 LDB_ERR_OPERATIONS_ERROR);
4204 if (ares->error != LDB_SUCCESS &&
4205 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
4206 return ldb_module_done(ar->req, ares->controls,
4207 ares->response, ares->error);
4210 switch (ares->type) {
4211 case LDB_REPLY_ENTRY:
4212 ar->search_msg = talloc_steal(ar, ares->message);
4213 break;
4215 case LDB_REPLY_REFERRAL:
4216 /* we ignore referrals */
4217 break;
4219 case LDB_REPLY_DONE:
4220 if (ar->search_msg == NULL) {
4221 ret = replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
4222 } else {
4223 ret = replmd_replicated_uptodate_modify(ar);
4225 if (ret != LDB_SUCCESS) {
4226 return ldb_module_done(ar->req, NULL, NULL, ret);
4230 talloc_free(ares);
4231 return LDB_SUCCESS;
4235 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar)
4237 struct ldb_context *ldb;
4238 int ret;
4239 static const char *attrs[] = {
4240 "replUpToDateVector",
4241 "repsFrom",
4242 "instanceType",
4243 NULL
4245 struct ldb_request *search_req;
4247 ldb = ldb_module_get_ctx(ar->module);
4248 ar->search_msg = NULL;
4250 ret = ldb_build_search_req(&search_req,
4251 ldb,
4253 ar->objs->partition_dn,
4254 LDB_SCOPE_BASE,
4255 "(objectClass=*)",
4256 attrs,
4257 NULL,
4259 replmd_replicated_uptodate_search_callback,
4260 ar->req);
4261 LDB_REQ_SET_LOCATION(search_req);
4262 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
4264 return ldb_next_request(ar->module, search_req);
4269 static int replmd_extended_replicated_objects(struct ldb_module *module, struct ldb_request *req)
4271 struct ldb_context *ldb;
4272 struct dsdb_extended_replicated_objects *objs;
4273 struct replmd_replicated_request *ar;
4274 struct ldb_control **ctrls;
4275 int ret;
4276 uint32_t i;
4277 struct replmd_private *replmd_private =
4278 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
4280 ldb = ldb_module_get_ctx(module);
4282 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_extended_replicated_objects\n");
4284 objs = talloc_get_type(req->op.extended.data, struct dsdb_extended_replicated_objects);
4285 if (!objs) {
4286 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: invalid extended data\n");
4287 return LDB_ERR_PROTOCOL_ERROR;
4290 if (objs->version != DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION) {
4291 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: extended data invalid version [%u != %u]\n",
4292 objs->version, DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION);
4293 return LDB_ERR_PROTOCOL_ERROR;
4296 ar = replmd_ctx_init(module, req);
4297 if (!ar)
4298 return LDB_ERR_OPERATIONS_ERROR;
4300 /* Set the flags to have the replmd_op_callback run over the full set of objects */
4301 ar->apply_mode = true;
4302 ar->objs = objs;
4303 ar->schema = dsdb_get_schema(ldb, ar);
4304 if (!ar->schema) {
4305 ldb_debug_set(ldb, LDB_DEBUG_FATAL, "replmd_ctx_init: no loaded schema found\n");
4306 talloc_free(ar);
4307 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
4308 return LDB_ERR_CONSTRAINT_VIOLATION;
4311 ctrls = req->controls;
4313 if (req->controls) {
4314 req->controls = talloc_memdup(ar, req->controls,
4315 talloc_get_size(req->controls));
4316 if (!req->controls) return replmd_replicated_request_werror(ar, WERR_NOMEM);
4319 /* This allows layers further down to know if a change came in over replication */
4320 ret = ldb_request_add_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID, false, NULL);
4321 if (ret != LDB_SUCCESS) {
4322 return ret;
4325 /* If this change contained linked attributes in the body
4326 * (rather than in the links section) we need to update
4327 * backlinks in linked_attributes */
4328 ret = ldb_request_add_control(req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
4329 if (ret != LDB_SUCCESS) {
4330 return ret;
4333 ar->controls = req->controls;
4334 req->controls = ctrls;
4336 DEBUG(4,("linked_attributes_count=%u\n", objs->linked_attributes_count));
4338 /* save away the linked attributes for the end of the
4339 transaction */
4340 for (i=0; i<ar->objs->linked_attributes_count; i++) {
4341 struct la_entry *la_entry;
4343 if (replmd_private->la_ctx == NULL) {
4344 replmd_private->la_ctx = talloc_new(replmd_private);
4346 la_entry = talloc(replmd_private->la_ctx, struct la_entry);
4347 if (la_entry == NULL) {
4348 ldb_oom(ldb);
4349 return LDB_ERR_OPERATIONS_ERROR;
4351 la_entry->la = talloc(la_entry, struct drsuapi_DsReplicaLinkedAttribute);
4352 if (la_entry->la == NULL) {
4353 talloc_free(la_entry);
4354 ldb_oom(ldb);
4355 return LDB_ERR_OPERATIONS_ERROR;
4357 *la_entry->la = ar->objs->linked_attributes[i];
4359 /* we need to steal the non-scalars so they stay
4360 around until the end of the transaction */
4361 talloc_steal(la_entry->la, la_entry->la->identifier);
4362 talloc_steal(la_entry->la, la_entry->la->value.blob);
4364 DLIST_ADD(replmd_private->la_list, la_entry);
4367 return replmd_replicated_apply_next(ar);
4371 process one linked attribute structure
4373 static int replmd_process_linked_attribute(struct ldb_module *module,
4374 struct la_entry *la_entry,
4375 struct ldb_request *parent)
4377 struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
4378 struct ldb_context *ldb = ldb_module_get_ctx(module);
4379 struct ldb_message *msg;
4380 TALLOC_CTX *tmp_ctx = talloc_new(la_entry);
4381 const struct dsdb_schema *schema = dsdb_get_schema(ldb, tmp_ctx);
4382 int ret;
4383 const struct dsdb_attribute *attr;
4384 struct dsdb_dn *dsdb_dn;
4385 uint64_t seq_num = 0;
4386 struct ldb_message_element *old_el;
4387 WERROR status;
4388 time_t t = time(NULL);
4389 struct ldb_result *res;
4390 const char *attrs[2];
4391 struct parsed_dn *pdn_list, *pdn;
4392 struct GUID guid = GUID_zero();
4393 NTSTATUS ntstatus;
4394 bool active = (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)?true:false;
4395 const struct GUID *our_invocation_id;
4398 linked_attributes[0]:
4399 &objs->linked_attributes[i]: struct drsuapi_DsReplicaLinkedAttribute
4400 identifier : *
4401 identifier: struct drsuapi_DsReplicaObjectIdentifier
4402 __ndr_size : 0x0000003a (58)
4403 __ndr_size_sid : 0x00000000 (0)
4404 guid : 8e95b6a9-13dd-4158-89db-3220a5be5cc7
4405 sid : S-0-0
4406 __ndr_size_dn : 0x00000000 (0)
4407 dn : ''
4408 attid : DRSUAPI_ATTID_member (0x1F)
4409 value: struct drsuapi_DsAttributeValue
4410 __ndr_size : 0x0000007e (126)
4411 blob : *
4412 blob : DATA_BLOB length=126
4413 flags : 0x00000001 (1)
4414 1: DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE
4415 originating_add_time : Wed Sep 2 22:20:01 2009 EST
4416 meta_data: struct drsuapi_DsReplicaMetaData
4417 version : 0x00000015 (21)
4418 originating_change_time : Wed Sep 2 23:39:07 2009 EST
4419 originating_invocation_id: 794640f3-18cf-40ee-a211-a93992b67a64
4420 originating_usn : 0x000000000001e19c (123292)
4422 (for cases where the link is to a normal DN)
4423 &target: struct drsuapi_DsReplicaObjectIdentifier3
4424 __ndr_size : 0x0000007e (126)
4425 __ndr_size_sid : 0x0000001c (28)
4426 guid : 7639e594-db75-4086-b0d4-67890ae46031
4427 sid : S-1-5-21-2848215498-2472035911-1947525656-19924
4428 __ndr_size_dn : 0x00000022 (34)
4429 dn : 'CN=UOne,OU=TestOU,DC=vsofs8,DC=com'
4432 /* find the attribute being modified */
4433 attr = dsdb_attribute_by_attributeID_id(schema, la->attid);
4434 if (attr == NULL) {
4435 DEBUG(0, (__location__ ": Unable to find attributeID 0x%x\n", la->attid));
4436 talloc_free(tmp_ctx);
4437 return LDB_ERR_OPERATIONS_ERROR;
4440 attrs[0] = attr->lDAPDisplayName;
4441 attrs[1] = NULL;
4443 /* get the existing message from the db for the object with
4444 this GUID, returning attribute being modified. We will then
4445 use this msg as the basis for a modify call */
4446 ret = dsdb_module_search(module, tmp_ctx, &res, NULL, LDB_SCOPE_SUBTREE, attrs,
4447 DSDB_FLAG_NEXT_MODULE |
4448 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
4449 DSDB_SEARCH_SHOW_RECYCLED |
4450 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
4451 DSDB_SEARCH_REVEAL_INTERNALS,
4452 parent,
4453 "objectGUID=%s", GUID_string(tmp_ctx, &la->identifier->guid));
4454 if (ret != LDB_SUCCESS) {
4455 talloc_free(tmp_ctx);
4456 return ret;
4458 if (res->count != 1) {
4459 ldb_asprintf_errstring(ldb, "DRS linked attribute for GUID %s - DN not found",
4460 GUID_string(tmp_ctx, &la->identifier->guid));
4461 talloc_free(tmp_ctx);
4462 return LDB_ERR_NO_SUCH_OBJECT;
4464 msg = res->msgs[0];
4466 if (msg->num_elements == 0) {
4467 ret = ldb_msg_add_empty(msg, attr->lDAPDisplayName, LDB_FLAG_MOD_REPLACE, &old_el);
4468 if (ret != LDB_SUCCESS) {
4469 ldb_module_oom(module);
4470 talloc_free(tmp_ctx);
4471 return LDB_ERR_OPERATIONS_ERROR;
4473 } else {
4474 old_el = &msg->elements[0];
4475 old_el->flags = LDB_FLAG_MOD_REPLACE;
4478 /* parse the existing links */
4479 ret = get_parsed_dns(module, tmp_ctx, old_el, &pdn_list, attr->syntax->ldap_oid, parent);
4480 if (ret != LDB_SUCCESS) {
4481 talloc_free(tmp_ctx);
4482 return ret;
4485 /* get our invocationId */
4486 our_invocation_id = samdb_ntds_invocation_id(ldb);
4487 if (!our_invocation_id) {
4488 ldb_debug_set(ldb, LDB_DEBUG_ERROR, __location__ ": unable to find invocationId\n");
4489 talloc_free(tmp_ctx);
4490 return LDB_ERR_OPERATIONS_ERROR;
4493 ret = replmd_check_upgrade_links(pdn_list, old_el->num_values, old_el, our_invocation_id);
4494 if (ret != LDB_SUCCESS) {
4495 talloc_free(tmp_ctx);
4496 return ret;
4499 status = dsdb_dn_la_from_blob(ldb, attr, schema, tmp_ctx, la->value.blob, &dsdb_dn);
4500 if (!W_ERROR_IS_OK(status)) {
4501 ldb_asprintf_errstring(ldb, "Failed to parsed linked attribute blob for %s on %s - %s\n",
4502 old_el->name, ldb_dn_get_linearized(msg->dn), win_errstr(status));
4503 return LDB_ERR_OPERATIONS_ERROR;
4506 ntstatus = dsdb_get_extended_dn_guid(dsdb_dn->dn, &guid, "GUID");
4507 if (!NT_STATUS_IS_OK(ntstatus) && active) {
4508 ldb_asprintf_errstring(ldb, "Failed to find GUID in linked attribute blob for %s on %s from %s",
4509 old_el->name,
4510 ldb_dn_get_linearized(dsdb_dn->dn),
4511 ldb_dn_get_linearized(msg->dn));
4512 return LDB_ERR_OPERATIONS_ERROR;
4515 /* re-resolve the DN by GUID, as the DRS server may give us an
4516 old DN value */
4517 ret = dsdb_module_dn_by_guid(module, dsdb_dn, &guid, &dsdb_dn->dn, parent);
4518 if (ret != LDB_SUCCESS) {
4519 DEBUG(2,(__location__ ": WARNING: Failed to re-resolve GUID %s - using %s",
4520 GUID_string(tmp_ctx, &guid),
4521 ldb_dn_get_linearized(dsdb_dn->dn)));
4524 /* see if this link already exists */
4525 pdn = parsed_dn_find(pdn_list, old_el->num_values, &guid, dsdb_dn->dn);
4526 if (pdn != NULL) {
4527 /* see if this update is newer than what we have already */
4528 struct GUID invocation_id = GUID_zero();
4529 uint32_t version = 0;
4530 uint32_t originating_usn = 0;
4531 NTTIME change_time = 0;
4532 uint32_t rmd_flags = dsdb_dn_rmd_flags(pdn->dsdb_dn->dn);
4534 dsdb_get_extended_dn_guid(pdn->dsdb_dn->dn, &invocation_id, "RMD_INVOCID");
4535 dsdb_get_extended_dn_uint32(pdn->dsdb_dn->dn, &version, "RMD_VERSION");
4536 dsdb_get_extended_dn_uint32(pdn->dsdb_dn->dn, &originating_usn, "RMD_ORIGINATING_USN");
4537 dsdb_get_extended_dn_nttime(pdn->dsdb_dn->dn, &change_time, "RMD_CHANGETIME");
4539 if (!replmd_update_is_newer(&invocation_id,
4540 &la->meta_data.originating_invocation_id,
4541 version,
4542 la->meta_data.version,
4543 change_time,
4544 la->meta_data.originating_change_time)) {
4545 DEBUG(3,("Discarding older DRS linked attribute update to %s on %s from %s\n",
4546 old_el->name, ldb_dn_get_linearized(msg->dn),
4547 GUID_string(tmp_ctx, &la->meta_data.originating_invocation_id)));
4548 talloc_free(tmp_ctx);
4549 return LDB_SUCCESS;
4552 /* get a seq_num for this change */
4553 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
4554 if (ret != LDB_SUCCESS) {
4555 talloc_free(tmp_ctx);
4556 return ret;
4559 if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
4560 /* remove the existing backlink */
4561 ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid, false, attr, false);
4562 if (ret != LDB_SUCCESS) {
4563 talloc_free(tmp_ctx);
4564 return ret;
4568 ret = replmd_update_la_val(tmp_ctx, pdn->v, dsdb_dn, pdn->dsdb_dn,
4569 &la->meta_data.originating_invocation_id,
4570 la->meta_data.originating_usn, seq_num,
4571 la->meta_data.originating_change_time,
4572 la->meta_data.version,
4573 !active);
4574 if (ret != LDB_SUCCESS) {
4575 talloc_free(tmp_ctx);
4576 return ret;
4579 if (active) {
4580 /* add the new backlink */
4581 ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid, true, attr, false);
4582 if (ret != LDB_SUCCESS) {
4583 talloc_free(tmp_ctx);
4584 return ret;
4587 } else {
4588 /* get a seq_num for this change */
4589 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
4590 if (ret != LDB_SUCCESS) {
4591 talloc_free(tmp_ctx);
4592 return ret;
4595 old_el->values = talloc_realloc(msg->elements, old_el->values,
4596 struct ldb_val, old_el->num_values+1);
4597 if (!old_el->values) {
4598 ldb_module_oom(module);
4599 return LDB_ERR_OPERATIONS_ERROR;
4601 old_el->num_values++;
4603 ret = replmd_build_la_val(tmp_ctx, &old_el->values[old_el->num_values-1], dsdb_dn,
4604 &la->meta_data.originating_invocation_id,
4605 la->meta_data.originating_usn, seq_num,
4606 la->meta_data.originating_change_time,
4607 la->meta_data.version,
4608 (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)?false:true);
4609 if (ret != LDB_SUCCESS) {
4610 talloc_free(tmp_ctx);
4611 return ret;
4614 if (active) {
4615 ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid,
4616 true, attr, false);
4617 if (ret != LDB_SUCCESS) {
4618 talloc_free(tmp_ctx);
4619 return ret;
4624 /* we only change whenChanged and uSNChanged if the seq_num
4625 has changed */
4626 if (add_time_element(msg, "whenChanged", t) != LDB_SUCCESS) {
4627 talloc_free(tmp_ctx);
4628 return ldb_operr(ldb);
4631 if (add_uint64_element(ldb, msg, "uSNChanged",
4632 seq_num) != LDB_SUCCESS) {
4633 talloc_free(tmp_ctx);
4634 return ldb_operr(ldb);
4637 old_el = ldb_msg_find_element(msg, attr->lDAPDisplayName);
4638 if (old_el == NULL) {
4639 talloc_free(tmp_ctx);
4640 return ldb_operr(ldb);
4643 ret = dsdb_check_single_valued_link(attr, old_el);
4644 if (ret != LDB_SUCCESS) {
4645 talloc_free(tmp_ctx);
4646 return ret;
4649 old_el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
4651 ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE, parent);
4652 if (ret != LDB_SUCCESS) {
4653 ldb_debug(ldb, LDB_DEBUG_WARNING, "Failed to apply linked attribute change '%s'\n%s\n",
4654 ldb_errstring(ldb),
4655 ldb_ldif_message_string(ldb, tmp_ctx, LDB_CHANGETYPE_MODIFY, msg));
4656 talloc_free(tmp_ctx);
4657 return ret;
4660 talloc_free(tmp_ctx);
4662 return ret;
4665 static int replmd_extended(struct ldb_module *module, struct ldb_request *req)
4667 if (strcmp(req->op.extended.oid, DSDB_EXTENDED_REPLICATED_OBJECTS_OID) == 0) {
4668 return replmd_extended_replicated_objects(module, req);
4671 return ldb_next_request(module, req);
4676 we hook into the transaction operations to allow us to
4677 perform the linked attribute updates at the end of the whole
4678 transaction. This allows a forward linked attribute to be created
4679 before the object is created. During a vampire, w2k8 sends us linked
4680 attributes before the objects they are part of.
4682 static int replmd_start_transaction(struct ldb_module *module)
4684 /* create our private structure for this transaction */
4685 struct replmd_private *replmd_private = talloc_get_type(ldb_module_get_private(module),
4686 struct replmd_private);
4687 replmd_txn_cleanup(replmd_private);
4689 /* free any leftover mod_usn records from cancelled
4690 transactions */
4691 while (replmd_private->ncs) {
4692 struct nc_entry *e = replmd_private->ncs;
4693 DLIST_REMOVE(replmd_private->ncs, e);
4694 talloc_free(e);
4697 return ldb_next_start_trans(module);
4701 on prepare commit we loop over our queued la_context structures and
4702 apply each of them
4704 static int replmd_prepare_commit(struct ldb_module *module)
4706 struct replmd_private *replmd_private =
4707 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
4708 struct la_entry *la, *prev;
4709 struct la_backlink *bl;
4710 int ret;
4712 /* walk the list backwards, to do the first entry first, as we
4713 * added the entries with DLIST_ADD() which puts them at the
4714 * start of the list */
4715 for (la = DLIST_TAIL(replmd_private->la_list); la; la=prev) {
4716 prev = DLIST_PREV(la);
4717 DLIST_REMOVE(replmd_private->la_list, la);
4718 ret = replmd_process_linked_attribute(module, la, NULL);
4719 if (ret != LDB_SUCCESS) {
4720 replmd_txn_cleanup(replmd_private);
4721 return ret;
4725 /* process our backlink list, creating and deleting backlinks
4726 as necessary */
4727 for (bl=replmd_private->la_backlinks; bl; bl=bl->next) {
4728 ret = replmd_process_backlink(module, bl, NULL);
4729 if (ret != LDB_SUCCESS) {
4730 replmd_txn_cleanup(replmd_private);
4731 return ret;
4735 replmd_txn_cleanup(replmd_private);
4737 /* possibly change @REPLCHANGED */
4738 ret = replmd_notify_store(module, NULL);
4739 if (ret != LDB_SUCCESS) {
4740 return ret;
4743 return ldb_next_prepare_commit(module);
4746 static int replmd_del_transaction(struct ldb_module *module)
4748 struct replmd_private *replmd_private =
4749 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
4750 replmd_txn_cleanup(replmd_private);
4752 return ldb_next_del_trans(module);
4756 static const struct ldb_module_ops ldb_repl_meta_data_module_ops = {
4757 .name = "repl_meta_data",
4758 .init_context = replmd_init,
4759 .add = replmd_add,
4760 .modify = replmd_modify,
4761 .rename = replmd_rename,
4762 .del = replmd_delete,
4763 .extended = replmd_extended,
4764 .start_transaction = replmd_start_transaction,
4765 .prepare_commit = replmd_prepare_commit,
4766 .del_transaction = replmd_del_transaction,
4769 int ldb_repl_meta_data_module_init(const char *version)
4771 LDB_MODULE_CHECK_VERSION(version);
4772 return ldb_register_module(&ldb_repl_meta_data_module_ops);