s4-dsdb: Modify the repl_meta_data behavior to allow Metadata change on attribute...
[Samba/gebeck_regimport.git] / source4 / dsdb / samdb / ldb_modules / repl_meta_data.c
blobeb5d036e62822aa9f79922321016bcdffbe3167a
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 static const uint64_t DELETED_OBJECT_CONTAINER_CHANGE_TIME = 253402127999L;
54 struct replmd_private {
55 TALLOC_CTX *la_ctx;
56 struct la_entry *la_list;
57 TALLOC_CTX *bl_ctx;
58 struct la_backlink *la_backlinks;
59 struct nc_entry {
60 struct nc_entry *prev, *next;
61 struct ldb_dn *dn;
62 uint64_t mod_usn;
63 uint64_t mod_usn_urgent;
64 } *ncs;
67 struct la_entry {
68 struct la_entry *next, *prev;
69 struct drsuapi_DsReplicaLinkedAttribute *la;
72 struct replmd_replicated_request {
73 struct ldb_module *module;
74 struct ldb_request *req;
76 const struct dsdb_schema *schema;
78 /* the controls we pass down */
79 struct ldb_control **controls;
81 /* details for the mode where we apply a bunch of inbound replication meessages */
82 bool apply_mode;
83 uint32_t index_current;
84 struct dsdb_extended_replicated_objects *objs;
86 struct ldb_message *search_msg;
88 uint64_t seq_num;
89 bool is_urgent;
92 enum urgent_situation {
93 REPL_URGENT_ON_CREATE = 1,
94 REPL_URGENT_ON_UPDATE = 2,
95 REPL_URGENT_ON_DELETE = 4
99 static const struct {
100 const char *update_name;
101 enum urgent_situation repl_situation;
102 } urgent_objects[] = {
103 {"nTDSDSA", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE)},
104 {"crossRef", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE)},
105 {"attributeSchema", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
106 {"classSchema", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
107 {"secret", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
108 {"rIDManager", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
109 {NULL, 0}
112 /* Attributes looked for when updating or deleting, to check for a urgent replication needed */
113 static const char *urgent_attrs[] = {
114 "lockoutTime",
115 "pwdLastSet",
116 "userAccountControl",
117 NULL
121 static bool replmd_check_urgent_objectclass(const struct ldb_message_element *objectclass_el,
122 enum urgent_situation situation)
124 unsigned int i, j;
125 for (i=0; urgent_objects[i].update_name; i++) {
127 if ((situation & urgent_objects[i].repl_situation) == 0) {
128 continue;
131 for (j=0; j<objectclass_el->num_values; j++) {
132 const struct ldb_val *v = &objectclass_el->values[j];
133 if (ldb_attr_cmp((const char *)v->data, urgent_objects[i].update_name) == 0) {
134 return true;
138 return false;
141 static bool replmd_check_urgent_attribute(const struct ldb_message_element *el)
143 if (ldb_attr_in_list(urgent_attrs, el->name)) {
144 return true;
146 return false;
150 static int replmd_replicated_apply_next(struct replmd_replicated_request *ar);
153 initialise the module
154 allocate the private structure and build the list
155 of partition DNs for use by replmd_notify()
157 static int replmd_init(struct ldb_module *module)
159 struct replmd_private *replmd_private;
160 struct ldb_context *ldb = ldb_module_get_ctx(module);
162 replmd_private = talloc_zero(module, struct replmd_private);
163 if (replmd_private == NULL) {
164 ldb_oom(ldb);
165 return LDB_ERR_OPERATIONS_ERROR;
167 ldb_module_set_private(module, replmd_private);
169 return ldb_next_init(module);
173 cleanup our per-transaction contexts
175 static void replmd_txn_cleanup(struct replmd_private *replmd_private)
177 talloc_free(replmd_private->la_ctx);
178 replmd_private->la_list = NULL;
179 replmd_private->la_ctx = NULL;
181 talloc_free(replmd_private->bl_ctx);
182 replmd_private->la_backlinks = NULL;
183 replmd_private->bl_ctx = NULL;
187 struct la_backlink {
188 struct la_backlink *next, *prev;
189 const char *attr_name;
190 struct GUID forward_guid, target_guid;
191 bool active;
195 process a backlinks we accumulated during a transaction, adding and
196 deleting the backlinks from the target objects
198 static int replmd_process_backlink(struct ldb_module *module, struct la_backlink *bl, struct ldb_request *parent)
200 struct ldb_dn *target_dn, *source_dn;
201 int ret;
202 struct ldb_context *ldb = ldb_module_get_ctx(module);
203 struct ldb_message *msg;
204 TALLOC_CTX *tmp_ctx = talloc_new(bl);
205 char *dn_string;
208 - find DN of target
209 - find DN of source
210 - construct ldb_message
211 - either an add or a delete
213 ret = dsdb_module_dn_by_guid(module, tmp_ctx, &bl->target_guid, &target_dn, parent);
214 if (ret != LDB_SUCCESS) {
215 DEBUG(2,(__location__ ": WARNING: Failed to find target DN for linked attribute with GUID %s\n",
216 GUID_string(bl, &bl->target_guid)));
217 return LDB_SUCCESS;
220 ret = dsdb_module_dn_by_guid(module, tmp_ctx, &bl->forward_guid, &source_dn, parent);
221 if (ret != LDB_SUCCESS) {
222 ldb_asprintf_errstring(ldb, "Failed to find source DN for linked attribute with GUID %s\n",
223 GUID_string(bl, &bl->forward_guid));
224 talloc_free(tmp_ctx);
225 return ret;
228 msg = ldb_msg_new(tmp_ctx);
229 if (msg == NULL) {
230 ldb_module_oom(module);
231 talloc_free(tmp_ctx);
232 return LDB_ERR_OPERATIONS_ERROR;
235 /* construct a ldb_message for adding/deleting the backlink */
236 msg->dn = target_dn;
237 dn_string = ldb_dn_get_extended_linearized(tmp_ctx, source_dn, 1);
238 if (!dn_string) {
239 ldb_module_oom(module);
240 talloc_free(tmp_ctx);
241 return LDB_ERR_OPERATIONS_ERROR;
243 ret = ldb_msg_add_steal_string(msg, bl->attr_name, dn_string);
244 if (ret != LDB_SUCCESS) {
245 talloc_free(tmp_ctx);
246 return ret;
248 msg->elements[0].flags = bl->active?LDB_FLAG_MOD_ADD:LDB_FLAG_MOD_DELETE;
250 /* a backlink should never be single valued. Unfortunately the
251 exchange schema has a attribute
252 msExchBridgeheadedLocalConnectorsDNBL which is single
253 valued and a backlink. We need to cope with that by
254 ignoring the single value flag */
255 msg->elements[0].flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
257 ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE, parent);
258 if (ret == LDB_ERR_NO_SUCH_ATTRIBUTE && !bl->active) {
259 /* we allow LDB_ERR_NO_SUCH_ATTRIBUTE as success to
260 cope with possible corruption where the backlink has
261 already been removed */
262 DEBUG(3,("WARNING: backlink from %s already removed from %s - %s\n",
263 ldb_dn_get_linearized(target_dn),
264 ldb_dn_get_linearized(source_dn),
265 ldb_errstring(ldb)));
266 ret = LDB_SUCCESS;
267 } else if (ret != LDB_SUCCESS) {
268 ldb_asprintf_errstring(ldb, "Failed to %s backlink from %s to %s - %s",
269 bl->active?"add":"remove",
270 ldb_dn_get_linearized(source_dn),
271 ldb_dn_get_linearized(target_dn),
272 ldb_errstring(ldb));
273 talloc_free(tmp_ctx);
274 return ret;
276 talloc_free(tmp_ctx);
277 return ret;
281 add a backlink to the list of backlinks to add/delete in the prepare
282 commit
284 static int replmd_add_backlink(struct ldb_module *module, const struct dsdb_schema *schema,
285 struct GUID *forward_guid, struct GUID *target_guid,
286 bool active, const struct dsdb_attribute *schema_attr, bool immediate)
288 const struct dsdb_attribute *target_attr;
289 struct la_backlink *bl;
290 struct replmd_private *replmd_private =
291 talloc_get_type_abort(ldb_module_get_private(module), struct replmd_private);
293 target_attr = dsdb_attribute_by_linkID(schema, schema_attr->linkID ^ 1);
294 if (!target_attr) {
296 * windows 2003 has a broken schema where the
297 * definition of msDS-IsDomainFor is missing (which is
298 * supposed to be the backlink of the
299 * msDS-HasDomainNCs attribute
301 return LDB_SUCCESS;
304 /* see if its already in the list */
305 for (bl=replmd_private->la_backlinks; bl; bl=bl->next) {
306 if (GUID_equal(forward_guid, &bl->forward_guid) &&
307 GUID_equal(target_guid, &bl->target_guid) &&
308 (target_attr->lDAPDisplayName == bl->attr_name ||
309 strcmp(target_attr->lDAPDisplayName, bl->attr_name) == 0)) {
310 break;
314 if (bl) {
315 /* we found an existing one */
316 if (bl->active == active) {
317 return LDB_SUCCESS;
319 DLIST_REMOVE(replmd_private->la_backlinks, bl);
320 talloc_free(bl);
321 return LDB_SUCCESS;
324 if (replmd_private->bl_ctx == NULL) {
325 replmd_private->bl_ctx = talloc_new(replmd_private);
326 if (replmd_private->bl_ctx == NULL) {
327 ldb_module_oom(module);
328 return LDB_ERR_OPERATIONS_ERROR;
332 /* its a new one */
333 bl = talloc(replmd_private->bl_ctx, struct la_backlink);
334 if (bl == NULL) {
335 ldb_module_oom(module);
336 return LDB_ERR_OPERATIONS_ERROR;
339 /* Ensure the schema does not go away before the bl->attr_name is used */
340 if (!talloc_reference(bl, schema)) {
341 talloc_free(bl);
342 ldb_module_oom(module);
343 return LDB_ERR_OPERATIONS_ERROR;
346 bl->attr_name = target_attr->lDAPDisplayName;
347 bl->forward_guid = *forward_guid;
348 bl->target_guid = *target_guid;
349 bl->active = active;
351 /* the caller may ask for this backlink to be processed
352 immediately */
353 if (immediate) {
354 int ret = replmd_process_backlink(module, bl, NULL);
355 talloc_free(bl);
356 return ret;
359 DLIST_ADD(replmd_private->la_backlinks, bl);
361 return LDB_SUCCESS;
366 * Callback for most write operations in this module:
368 * notify the repl task that a object has changed. The notifies are
369 * gathered up in the replmd_private structure then written to the
370 * @REPLCHANGED object in each partition during the prepare_commit
372 static int replmd_op_callback(struct ldb_request *req, struct ldb_reply *ares)
374 int ret;
375 struct replmd_replicated_request *ac =
376 talloc_get_type_abort(req->context, struct replmd_replicated_request);
377 struct replmd_private *replmd_private =
378 talloc_get_type_abort(ldb_module_get_private(ac->module), struct replmd_private);
379 struct nc_entry *modified_partition;
380 struct ldb_control *partition_ctrl;
381 const struct dsdb_control_current_partition *partition;
383 struct ldb_control **controls;
385 partition_ctrl = ldb_reply_get_control(ares, DSDB_CONTROL_CURRENT_PARTITION_OID);
387 controls = ares->controls;
388 if (ldb_request_get_control(ac->req,
389 DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
391 * Remove the current partition control from what we pass up
392 * the chain if it hasn't been requested manually.
394 controls = ldb_controls_except_specified(ares->controls, ares,
395 partition_ctrl);
398 if (ares->error != LDB_SUCCESS) {
399 DEBUG(5,("%s failure. Error is: %s\n", __FUNCTION__, ldb_strerror(ares->error)));
400 return ldb_module_done(ac->req, controls,
401 ares->response, ares->error);
404 if (ares->type != LDB_REPLY_DONE) {
405 ldb_set_errstring(ldb_module_get_ctx(ac->module), "Invalid reply type for notify\n!");
406 return ldb_module_done(ac->req, NULL,
407 NULL, LDB_ERR_OPERATIONS_ERROR);
410 if (!partition_ctrl) {
411 ldb_set_errstring(ldb_module_get_ctx(ac->module),"No partition control on reply");
412 return ldb_module_done(ac->req, NULL,
413 NULL, LDB_ERR_OPERATIONS_ERROR);
416 partition = talloc_get_type_abort(partition_ctrl->data,
417 struct dsdb_control_current_partition);
419 if (ac->seq_num > 0) {
420 for (modified_partition = replmd_private->ncs; modified_partition;
421 modified_partition = modified_partition->next) {
422 if (ldb_dn_compare(modified_partition->dn, partition->dn) == 0) {
423 break;
427 if (modified_partition == NULL) {
428 modified_partition = talloc_zero(replmd_private, struct nc_entry);
429 if (!modified_partition) {
430 ldb_oom(ldb_module_get_ctx(ac->module));
431 return ldb_module_done(ac->req, NULL,
432 NULL, LDB_ERR_OPERATIONS_ERROR);
434 modified_partition->dn = ldb_dn_copy(modified_partition, partition->dn);
435 if (!modified_partition->dn) {
436 ldb_oom(ldb_module_get_ctx(ac->module));
437 return ldb_module_done(ac->req, NULL,
438 NULL, LDB_ERR_OPERATIONS_ERROR);
440 DLIST_ADD(replmd_private->ncs, modified_partition);
443 if (ac->seq_num > modified_partition->mod_usn) {
444 modified_partition->mod_usn = ac->seq_num;
445 if (ac->is_urgent) {
446 modified_partition->mod_usn_urgent = ac->seq_num;
451 if (ac->apply_mode) {
452 talloc_free(ares);
453 ac->index_current++;
455 ret = replmd_replicated_apply_next(ac);
456 if (ret != LDB_SUCCESS) {
457 return ldb_module_done(ac->req, NULL, NULL, ret);
459 return ret;
460 } else {
461 /* free the partition control container here, for the
462 * common path. Other cases will have it cleaned up
463 * eventually with the ares */
464 talloc_free(partition_ctrl);
465 return ldb_module_done(ac->req, controls,
466 ares->response, LDB_SUCCESS);
472 * update a @REPLCHANGED record in each partition if there have been
473 * any writes of replicated data in the partition
475 static int replmd_notify_store(struct ldb_module *module, struct ldb_request *parent)
477 struct replmd_private *replmd_private =
478 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
480 while (replmd_private->ncs) {
481 int ret;
482 struct nc_entry *modified_partition = replmd_private->ncs;
484 ret = dsdb_module_save_partition_usn(module, modified_partition->dn,
485 modified_partition->mod_usn,
486 modified_partition->mod_usn_urgent, parent);
487 if (ret != LDB_SUCCESS) {
488 DEBUG(0,(__location__ ": Failed to save partition uSN for %s\n",
489 ldb_dn_get_linearized(modified_partition->dn)));
490 return ret;
492 DLIST_REMOVE(replmd_private->ncs, modified_partition);
493 talloc_free(modified_partition);
496 return LDB_SUCCESS;
501 created a replmd_replicated_request context
503 static struct replmd_replicated_request *replmd_ctx_init(struct ldb_module *module,
504 struct ldb_request *req)
506 struct ldb_context *ldb;
507 struct replmd_replicated_request *ac;
509 ldb = ldb_module_get_ctx(module);
511 ac = talloc_zero(req, struct replmd_replicated_request);
512 if (ac == NULL) {
513 ldb_oom(ldb);
514 return NULL;
517 ac->module = module;
518 ac->req = req;
520 ac->schema = dsdb_get_schema(ldb, ac);
521 if (!ac->schema) {
522 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
523 "replmd_modify: no dsdb_schema loaded");
524 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
525 return NULL;
528 return ac;
532 add a time element to a record
534 static int add_time_element(struct ldb_message *msg, const char *attr, time_t t)
536 struct ldb_message_element *el;
537 char *s;
538 int ret;
540 if (ldb_msg_find_element(msg, attr) != NULL) {
541 return LDB_SUCCESS;
544 s = ldb_timestring(msg, t);
545 if (s == NULL) {
546 return LDB_ERR_OPERATIONS_ERROR;
549 ret = ldb_msg_add_string(msg, attr, s);
550 if (ret != LDB_SUCCESS) {
551 return ret;
554 el = ldb_msg_find_element(msg, attr);
555 /* always set as replace. This works because on add ops, the flag
556 is ignored */
557 el->flags = LDB_FLAG_MOD_REPLACE;
559 return LDB_SUCCESS;
563 add a uint64_t element to a record
565 static int add_uint64_element(struct ldb_context *ldb, struct ldb_message *msg,
566 const char *attr, uint64_t v)
568 struct ldb_message_element *el;
569 int ret;
571 if (ldb_msg_find_element(msg, attr) != NULL) {
572 return LDB_SUCCESS;
575 ret = samdb_msg_add_uint64(ldb, msg, msg, attr, v);
576 if (ret != LDB_SUCCESS) {
577 return ret;
580 el = ldb_msg_find_element(msg, attr);
581 /* always set as replace. This works because on add ops, the flag
582 is ignored */
583 el->flags = LDB_FLAG_MOD_REPLACE;
585 return LDB_SUCCESS;
588 static int replmd_replPropertyMetaData1_attid_sort(const struct replPropertyMetaData1 *m1,
589 const struct replPropertyMetaData1 *m2,
590 const uint32_t *rdn_attid)
592 if (m1->attid == m2->attid) {
593 return 0;
597 * the rdn attribute should be at the end!
598 * so we need to return a value greater than zero
599 * which means m1 is greater than m2
601 if (m1->attid == *rdn_attid) {
602 return 1;
606 * the rdn attribute should be at the end!
607 * so we need to return a value less than zero
608 * which means m2 is greater than m1
610 if (m2->attid == *rdn_attid) {
611 return -1;
614 return m1->attid > m2->attid ? 1 : -1;
617 static int replmd_replPropertyMetaDataCtr1_sort(struct replPropertyMetaDataCtr1 *ctr1,
618 const struct dsdb_schema *schema,
619 struct ldb_dn *dn)
621 const char *rdn_name;
622 const struct dsdb_attribute *rdn_sa;
624 rdn_name = ldb_dn_get_rdn_name(dn);
625 if (!rdn_name) {
626 DEBUG(0,(__location__ ": No rDN for %s?\n", ldb_dn_get_linearized(dn)));
627 return LDB_ERR_OPERATIONS_ERROR;
630 rdn_sa = dsdb_attribute_by_lDAPDisplayName(schema, rdn_name);
631 if (rdn_sa == NULL) {
632 DEBUG(0,(__location__ ": No sa found for rDN %s for %s\n", rdn_name, ldb_dn_get_linearized(dn)));
633 return LDB_ERR_OPERATIONS_ERROR;
636 DEBUG(6,("Sorting rpmd with attid exception %u rDN=%s DN=%s\n",
637 rdn_sa->attributeID_id, rdn_name, ldb_dn_get_linearized(dn)));
639 LDB_TYPESAFE_QSORT(ctr1->array, ctr1->count, &rdn_sa->attributeID_id, replmd_replPropertyMetaData1_attid_sort);
641 return LDB_SUCCESS;
644 static int replmd_ldb_message_element_attid_sort(const struct ldb_message_element *e1,
645 const struct ldb_message_element *e2,
646 const struct dsdb_schema *schema)
648 const struct dsdb_attribute *a1;
649 const struct dsdb_attribute *a2;
652 * TODO: make this faster by caching the dsdb_attribute pointer
653 * on the ldb_messag_element
656 a1 = dsdb_attribute_by_lDAPDisplayName(schema, e1->name);
657 a2 = dsdb_attribute_by_lDAPDisplayName(schema, e2->name);
660 * TODO: remove this check, we should rely on e1 and e2 having valid attribute names
661 * in the schema
663 if (!a1 || !a2) {
664 return strcasecmp(e1->name, e2->name);
666 if (a1->attributeID_id == a2->attributeID_id) {
667 return 0;
669 return a1->attributeID_id > a2->attributeID_id ? 1 : -1;
672 static void replmd_ldb_message_sort(struct ldb_message *msg,
673 const struct dsdb_schema *schema)
675 LDB_TYPESAFE_QSORT(msg->elements, msg->num_elements, schema, replmd_ldb_message_element_attid_sort);
678 static int replmd_build_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
679 const struct GUID *invocation_id, uint64_t seq_num,
680 uint64_t local_usn, NTTIME nttime, uint32_t version, bool deleted);
684 fix up linked attributes in replmd_add.
685 This involves setting up the right meta-data in extended DN
686 components, and creating backlinks to the object
688 static int replmd_add_fix_la(struct ldb_module *module, struct ldb_message_element *el,
689 uint64_t seq_num, const struct GUID *invocationId, time_t t,
690 struct GUID *guid, const struct dsdb_attribute *sa, struct ldb_request *parent)
692 unsigned int i;
693 TALLOC_CTX *tmp_ctx = talloc_new(el->values);
694 struct ldb_context *ldb = ldb_module_get_ctx(module);
696 /* We will take a reference to the schema in replmd_add_backlink */
697 const struct dsdb_schema *schema = dsdb_get_schema(ldb, NULL);
698 NTTIME now;
700 unix_to_nt_time(&now, t);
702 for (i=0; i<el->num_values; i++) {
703 struct ldb_val *v = &el->values[i];
704 struct dsdb_dn *dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, v, sa->syntax->ldap_oid);
705 struct GUID target_guid;
706 NTSTATUS status;
707 int ret;
709 /* note that the DN already has the extended
710 components from the extended_dn_store module */
711 status = dsdb_get_extended_dn_guid(dsdb_dn->dn, &target_guid, "GUID");
712 if (!NT_STATUS_IS_OK(status) || GUID_all_zero(&target_guid)) {
713 ret = dsdb_module_guid_by_dn(module, dsdb_dn->dn, &target_guid, parent);
714 if (ret != LDB_SUCCESS) {
715 talloc_free(tmp_ctx);
716 return ret;
718 ret = dsdb_set_extended_dn_guid(dsdb_dn->dn, &target_guid, "GUID");
719 if (ret != LDB_SUCCESS) {
720 talloc_free(tmp_ctx);
721 return ret;
725 ret = replmd_build_la_val(el->values, v, dsdb_dn, invocationId,
726 seq_num, seq_num, now, 0, false);
727 if (ret != LDB_SUCCESS) {
728 talloc_free(tmp_ctx);
729 return ret;
732 ret = replmd_add_backlink(module, schema, guid, &target_guid, true, sa, false);
733 if (ret != LDB_SUCCESS) {
734 talloc_free(tmp_ctx);
735 return ret;
739 talloc_free(tmp_ctx);
740 return LDB_SUCCESS;
745 intercept add requests
747 static int replmd_add(struct ldb_module *module, struct ldb_request *req)
749 struct ldb_context *ldb;
750 struct ldb_control *control;
751 struct replmd_replicated_request *ac;
752 enum ndr_err_code ndr_err;
753 struct ldb_request *down_req;
754 struct ldb_message *msg;
755 const DATA_BLOB *guid_blob;
756 struct GUID guid;
757 struct replPropertyMetaDataBlob nmd;
758 struct ldb_val nmd_value;
759 const struct GUID *our_invocation_id;
760 time_t t = time(NULL);
761 NTTIME now;
762 char *time_str;
763 int ret;
764 unsigned int i;
765 unsigned int functional_level;
766 uint32_t ni=0;
767 bool allow_add_guid = false;
768 bool remove_current_guid = false;
769 bool is_urgent = false;
770 struct ldb_message_element *objectclass_el;
772 /* check if there's a show relax control (used by provision to say 'I know what I'm doing') */
773 control = ldb_request_get_control(req, LDB_CONTROL_RELAX_OID);
774 if (control) {
775 allow_add_guid = true;
778 /* do not manipulate our control entries */
779 if (ldb_dn_is_special(req->op.add.message->dn)) {
780 return ldb_next_request(module, req);
783 ldb = ldb_module_get_ctx(module);
785 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_add\n");
787 guid_blob = ldb_msg_find_ldb_val(req->op.add.message, "objectGUID");
788 if (guid_blob != NULL) {
789 if (!allow_add_guid) {
790 ldb_set_errstring(ldb,
791 "replmd_add: it's not allowed to add an object with objectGUID!");
792 return LDB_ERR_UNWILLING_TO_PERFORM;
793 } else {
794 NTSTATUS status = GUID_from_data_blob(guid_blob,&guid);
795 if (!NT_STATUS_IS_OK(status)) {
796 ldb_set_errstring(ldb,
797 "replmd_add: Unable to parse the 'objectGUID' as a GUID!");
798 return LDB_ERR_UNWILLING_TO_PERFORM;
800 /* we remove this attribute as it can be a string and
801 * will not be treated correctly and then we will re-add
802 * it later on in the good format */
803 remove_current_guid = true;
805 } else {
806 /* a new GUID */
807 guid = GUID_random();
810 ac = replmd_ctx_init(module, req);
811 if (ac == NULL) {
812 return ldb_module_oom(module);
815 functional_level = dsdb_functional_level(ldb);
817 /* Get a sequence number from the backend */
818 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ac->seq_num);
819 if (ret != LDB_SUCCESS) {
820 talloc_free(ac);
821 return ret;
824 /* get our invocationId */
825 our_invocation_id = samdb_ntds_invocation_id(ldb);
826 if (!our_invocation_id) {
827 ldb_debug_set(ldb, LDB_DEBUG_ERROR,
828 "replmd_add: unable to find invocationId\n");
829 talloc_free(ac);
830 return LDB_ERR_OPERATIONS_ERROR;
833 /* we have to copy the message as the caller might have it as a const */
834 msg = ldb_msg_copy_shallow(ac, req->op.add.message);
835 if (msg == NULL) {
836 ldb_oom(ldb);
837 talloc_free(ac);
838 return LDB_ERR_OPERATIONS_ERROR;
841 /* generated times */
842 unix_to_nt_time(&now, t);
843 time_str = ldb_timestring(msg, t);
844 if (!time_str) {
845 ldb_oom(ldb);
846 talloc_free(ac);
847 return LDB_ERR_OPERATIONS_ERROR;
849 if (remove_current_guid) {
850 ldb_msg_remove_attr(msg,"objectGUID");
854 * remove autogenerated attributes
856 ldb_msg_remove_attr(msg, "whenCreated");
857 ldb_msg_remove_attr(msg, "whenChanged");
858 ldb_msg_remove_attr(msg, "uSNCreated");
859 ldb_msg_remove_attr(msg, "uSNChanged");
860 ldb_msg_remove_attr(msg, "replPropertyMetaData");
863 * readd replicated attributes
865 ret = ldb_msg_add_string(msg, "whenCreated", time_str);
866 if (ret != LDB_SUCCESS) {
867 ldb_oom(ldb);
868 talloc_free(ac);
869 return ret;
872 /* build the replication meta_data */
873 ZERO_STRUCT(nmd);
874 nmd.version = 1;
875 nmd.ctr.ctr1.count = msg->num_elements;
876 nmd.ctr.ctr1.array = talloc_array(msg,
877 struct replPropertyMetaData1,
878 nmd.ctr.ctr1.count);
879 if (!nmd.ctr.ctr1.array) {
880 ldb_oom(ldb);
881 talloc_free(ac);
882 return LDB_ERR_OPERATIONS_ERROR;
885 for (i=0; i < msg->num_elements; i++) {
886 struct ldb_message_element *e = &msg->elements[i];
887 struct replPropertyMetaData1 *m = &nmd.ctr.ctr1.array[ni];
888 const struct dsdb_attribute *sa;
890 if (e->name[0] == '@') continue;
892 sa = dsdb_attribute_by_lDAPDisplayName(ac->schema, e->name);
893 if (!sa) {
894 ldb_debug_set(ldb, LDB_DEBUG_ERROR,
895 "replmd_add: attribute '%s' not defined in schema\n",
896 e->name);
897 talloc_free(ac);
898 return LDB_ERR_NO_SUCH_ATTRIBUTE;
901 if ((sa->systemFlags & DS_FLAG_ATTR_NOT_REPLICATED) || (sa->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED)) {
902 /* if the attribute is not replicated (0x00000001)
903 * or constructed (0x00000004) it has no metadata
905 continue;
908 if (sa->linkID != 0 && functional_level > DS_DOMAIN_FUNCTION_2000) {
909 ret = replmd_add_fix_la(module, e, ac->seq_num, our_invocation_id, t, &guid, sa, req);
910 if (ret != LDB_SUCCESS) {
911 talloc_free(ac);
912 return ret;
914 /* linked attributes are not stored in
915 replPropertyMetaData in FL above w2k */
916 continue;
919 m->attid = sa->attributeID_id;
920 m->version = 1;
921 if (m->attid == 0x20030) {
922 const struct ldb_val *rdn_val = ldb_dn_get_rdn_val(msg->dn);
923 const char* rdn;
925 if (rdn_val == NULL) {
926 ldb_oom(ldb);
927 talloc_free(ac);
928 return LDB_ERR_OPERATIONS_ERROR;
931 rdn = (const char*)rdn_val->data;
932 if (strcmp(rdn, "Deleted Objects") == 0) {
934 * Set the originating_change_time to 29/12/9999 at 23:59:59
935 * as specified in MS-ADTS 7.1.1.4.2 Deleted Objects Container
937 NTTIME deleted_obj_ts;
939 unix_to_nt_time(&deleted_obj_ts, DELETED_OBJECT_CONTAINER_CHANGE_TIME);
940 m->originating_change_time = deleted_obj_ts;
941 } else {
942 m->originating_change_time = now;
944 } else {
945 m->originating_change_time = now;
947 m->originating_invocation_id = *our_invocation_id;
948 m->originating_usn = ac->seq_num;
949 m->local_usn = ac->seq_num;
950 ni++;
953 /* fix meta data count */
954 nmd.ctr.ctr1.count = ni;
957 * sort meta data array, and move the rdn attribute entry to the end
959 ret = replmd_replPropertyMetaDataCtr1_sort(&nmd.ctr.ctr1, ac->schema, msg->dn);
960 if (ret != LDB_SUCCESS) {
961 talloc_free(ac);
962 return ret;
965 /* generated NDR encoded values */
966 ndr_err = ndr_push_struct_blob(&nmd_value, msg,
967 &nmd,
968 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
969 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
970 ldb_oom(ldb);
971 talloc_free(ac);
972 return LDB_ERR_OPERATIONS_ERROR;
976 * add the autogenerated values
978 ret = dsdb_msg_add_guid(msg, &guid, "objectGUID");
979 if (ret != LDB_SUCCESS) {
980 ldb_oom(ldb);
981 talloc_free(ac);
982 return ret;
984 ret = ldb_msg_add_string(msg, "whenChanged", time_str);
985 if (ret != LDB_SUCCESS) {
986 ldb_oom(ldb);
987 talloc_free(ac);
988 return ret;
990 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", ac->seq_num);
991 if (ret != LDB_SUCCESS) {
992 ldb_oom(ldb);
993 talloc_free(ac);
994 return ret;
996 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ac->seq_num);
997 if (ret != LDB_SUCCESS) {
998 ldb_oom(ldb);
999 talloc_free(ac);
1000 return ret;
1002 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
1003 if (ret != LDB_SUCCESS) {
1004 ldb_oom(ldb);
1005 talloc_free(ac);
1006 return ret;
1010 * sort the attributes by attid before storing the object
1012 replmd_ldb_message_sort(msg, ac->schema);
1014 objectclass_el = ldb_msg_find_element(msg, "objectClass");
1015 is_urgent = replmd_check_urgent_objectclass(objectclass_el,
1016 REPL_URGENT_ON_CREATE);
1018 ac->is_urgent = is_urgent;
1019 ret = ldb_build_add_req(&down_req, ldb, ac,
1020 msg,
1021 req->controls,
1022 ac, replmd_op_callback,
1023 req);
1025 LDB_REQ_SET_LOCATION(down_req);
1026 if (ret != LDB_SUCCESS) {
1027 talloc_free(ac);
1028 return ret;
1031 /* current partition control is needed by "replmd_op_callback" */
1032 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
1033 ret = ldb_request_add_control(down_req,
1034 DSDB_CONTROL_CURRENT_PARTITION_OID,
1035 false, NULL);
1036 if (ret != LDB_SUCCESS) {
1037 talloc_free(ac);
1038 return ret;
1042 if (functional_level == DS_DOMAIN_FUNCTION_2000) {
1043 ret = ldb_request_add_control(down_req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
1044 if (ret != LDB_SUCCESS) {
1045 talloc_free(ac);
1046 return ret;
1050 /* mark the control done */
1051 if (control) {
1052 control->critical = 0;
1055 /* go on with the call chain */
1056 return ldb_next_request(module, down_req);
1061 * update the replPropertyMetaData for one element
1063 static int replmd_update_rpmd_element(struct ldb_context *ldb,
1064 struct ldb_message *msg,
1065 struct ldb_message_element *el,
1066 struct ldb_message_element *old_el,
1067 struct replPropertyMetaDataBlob *omd,
1068 const struct dsdb_schema *schema,
1069 uint64_t *seq_num,
1070 const struct GUID *our_invocation_id,
1071 NTTIME now,
1072 struct ldb_request *req)
1074 uint32_t i;
1075 const struct dsdb_attribute *a;
1076 struct replPropertyMetaData1 *md1;
1078 a = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
1079 if (a == NULL) {
1080 if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)) {
1081 /* allow this to make it possible for dbcheck
1082 to remove bad attributes */
1083 return LDB_SUCCESS;
1086 DEBUG(0,(__location__ ": Unable to find attribute %s in schema\n",
1087 el->name));
1088 return LDB_ERR_OPERATIONS_ERROR;
1091 if ((a->systemFlags & DS_FLAG_ATTR_NOT_REPLICATED) || (a->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED)) {
1092 return LDB_SUCCESS;
1095 /* if the attribute's value haven't changed then return LDB_SUCCESS
1096 * Unless we have the provision control or if the attribute is
1097 * interSiteTopologyGenerator as this page explain: http://support.microsoft.com/kb/224815
1098 * this attribute is periodicaly written by the DC responsible for the intersite generation
1099 * in a given site
1101 if (old_el != NULL && ldb_msg_element_compare(el, old_el) == 0) {
1102 if (strcmp(el->name, "interSiteTopologyGenerator") != 0 &&
1103 !ldb_request_get_control(req, LDB_CONTROL_PROVISION_OID)) {
1105 * allow this to make it possible for dbcheck
1106 * to rebuild broken metadata
1108 return LDB_SUCCESS;
1112 for (i=0; i<omd->ctr.ctr1.count; i++) {
1113 if (a->attributeID_id == omd->ctr.ctr1.array[i].attid) break;
1116 if (a->linkID != 0 && dsdb_functional_level(ldb) > DS_DOMAIN_FUNCTION_2000) {
1117 /* linked attributes are not stored in
1118 replPropertyMetaData in FL above w2k, but we do
1119 raise the seqnum for the object */
1120 if (*seq_num == 0 &&
1121 ldb_sequence_number(ldb, LDB_SEQ_NEXT, seq_num) != LDB_SUCCESS) {
1122 return LDB_ERR_OPERATIONS_ERROR;
1124 return LDB_SUCCESS;
1127 if (i == omd->ctr.ctr1.count) {
1128 /* we need to add a new one */
1129 omd->ctr.ctr1.array = talloc_realloc(msg, omd->ctr.ctr1.array,
1130 struct replPropertyMetaData1, omd->ctr.ctr1.count+1);
1131 if (omd->ctr.ctr1.array == NULL) {
1132 ldb_oom(ldb);
1133 return LDB_ERR_OPERATIONS_ERROR;
1135 omd->ctr.ctr1.count++;
1136 ZERO_STRUCT(omd->ctr.ctr1.array[i]);
1139 /* Get a new sequence number from the backend. We only do this
1140 * if we have a change that requires a new
1141 * replPropertyMetaData element
1143 if (*seq_num == 0) {
1144 int ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, seq_num);
1145 if (ret != LDB_SUCCESS) {
1146 return LDB_ERR_OPERATIONS_ERROR;
1150 md1 = &omd->ctr.ctr1.array[i];
1151 md1->version++;
1152 md1->attid = a->attributeID_id;
1153 if (md1->attid == 0x20030) {
1154 const struct ldb_val *rdn_val = ldb_dn_get_rdn_val(msg->dn);
1155 const char* rdn;
1157 if (rdn_val == NULL) {
1158 ldb_oom(ldb);
1159 return LDB_ERR_OPERATIONS_ERROR;
1162 rdn = (const char*)rdn_val->data;
1163 if (strcmp(rdn, "Deleted Objects") == 0) {
1165 * Set the originating_change_time to 29/12/9999 at 23:59:59
1166 * as specified in MS-ADTS 7.1.1.4.2 Deleted Objects Container
1168 NTTIME deleted_obj_ts;
1170 unix_to_nt_time(&deleted_obj_ts, DELETED_OBJECT_CONTAINER_CHANGE_TIME);
1171 md1->originating_change_time = deleted_obj_ts;
1172 } else {
1173 md1->originating_change_time = now;
1175 } else {
1176 md1->originating_change_time = now;
1178 md1->originating_invocation_id = *our_invocation_id;
1179 md1->originating_usn = *seq_num;
1180 md1->local_usn = *seq_num;
1182 return LDB_SUCCESS;
1185 static uint64_t find_max_local_usn(struct replPropertyMetaDataBlob omd)
1187 uint32_t count = omd.ctr.ctr1.count;
1188 uint64_t max = 0;
1189 uint32_t i;
1190 for (i=0; i < count; i++) {
1191 struct replPropertyMetaData1 m = omd.ctr.ctr1.array[i];
1192 if (max < m.local_usn) {
1193 max = m.local_usn;
1196 return max;
1200 * update the replPropertyMetaData object each time we modify an
1201 * object. This is needed for DRS replication, as the merge on the
1202 * client is based on this object
1204 static int replmd_update_rpmd(struct ldb_module *module,
1205 const struct dsdb_schema *schema,
1206 struct ldb_request *req,
1207 const char * const *rename_attrs,
1208 struct ldb_message *msg, uint64_t *seq_num,
1209 time_t t,
1210 bool *is_urgent, bool *rodc)
1212 const struct ldb_val *omd_value;
1213 enum ndr_err_code ndr_err;
1214 struct replPropertyMetaDataBlob omd;
1215 unsigned int i;
1216 NTTIME now;
1217 const struct GUID *our_invocation_id;
1218 int ret;
1219 const char * const *attrs = NULL;
1220 const char * const attrs1[] = { "replPropertyMetaData", "*", NULL };
1221 const char * const attrs2[] = { "uSNChanged", "objectClass", "instanceType", NULL };
1222 struct ldb_result *res;
1223 struct ldb_context *ldb;
1224 struct ldb_message_element *objectclass_el;
1225 enum urgent_situation situation;
1226 bool rmd_is_provided;
1228 if (rename_attrs) {
1229 attrs = rename_attrs;
1230 } else {
1231 attrs = attrs1;
1234 ldb = ldb_module_get_ctx(module);
1236 our_invocation_id = samdb_ntds_invocation_id(ldb);
1237 if (!our_invocation_id) {
1238 /* this happens during an initial vampire while
1239 updating the schema */
1240 DEBUG(5,("No invocationID - skipping replPropertyMetaData update\n"));
1241 return LDB_SUCCESS;
1244 unix_to_nt_time(&now, t);
1246 if (ldb_request_get_control(req, DSDB_CONTROL_CHANGEREPLMETADATA_OID)) {
1247 rmd_is_provided = true;
1248 } else {
1249 rmd_is_provided = false;
1252 /* if isDeleted is present and is TRUE, then we consider we are deleting,
1253 * otherwise we consider we are updating */
1254 if (ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")) {
1255 situation = REPL_URGENT_ON_DELETE;
1256 } else if (rename_attrs) {
1257 situation = REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE;
1258 } else {
1259 situation = REPL_URGENT_ON_UPDATE;
1262 if (rmd_is_provided) {
1263 /* In this case the change_replmetadata control was supplied */
1264 /* We check that it's the only attribute that is provided
1265 * (it's a rare case so it's better to keep the code simplier)
1266 * We also check that the highest local_usn is bigger than
1267 * uSNChanged. */
1268 uint64_t db_seq;
1269 if( msg->num_elements != 1 ||
1270 strncmp(msg->elements[0].name,
1271 "replPropertyMetaData", 20) ) {
1272 DEBUG(0,(__location__ ": changereplmetada control called without "\
1273 "a specified replPropertyMetaData attribute or with others\n"));
1274 return LDB_ERR_OPERATIONS_ERROR;
1276 if (situation != REPL_URGENT_ON_UPDATE) {
1277 DEBUG(0,(__location__ ": changereplmetada control can't be called when deleting an object\n"));
1278 return LDB_ERR_OPERATIONS_ERROR;
1280 omd_value = ldb_msg_find_ldb_val(msg, "replPropertyMetaData");
1281 if (!omd_value) {
1282 DEBUG(0,(__location__ ": replPropertyMetaData was not specified for Object %s\n",
1283 ldb_dn_get_linearized(msg->dn)));
1284 return LDB_ERR_OPERATIONS_ERROR;
1286 ndr_err = ndr_pull_struct_blob(omd_value, msg, &omd,
1287 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
1288 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1289 DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s\n",
1290 ldb_dn_get_linearized(msg->dn)));
1291 return LDB_ERR_OPERATIONS_ERROR;
1293 *seq_num = find_max_local_usn(omd);
1295 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attrs2,
1296 DSDB_FLAG_NEXT_MODULE |
1297 DSDB_SEARCH_SHOW_RECYCLED |
1298 DSDB_SEARCH_SHOW_EXTENDED_DN |
1299 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
1300 DSDB_SEARCH_REVEAL_INTERNALS, req);
1302 if (ret != LDB_SUCCESS) {
1303 return ret;
1306 objectclass_el = ldb_msg_find_element(res->msgs[0], "objectClass");
1307 if (is_urgent && replmd_check_urgent_objectclass(objectclass_el,
1308 situation)) {
1309 *is_urgent = true;
1312 db_seq = ldb_msg_find_attr_as_uint64(res->msgs[0], "uSNChanged", 0);
1313 if (*seq_num <= db_seq) {
1314 DEBUG(0,(__location__ ": changereplmetada control provided but max(local_usn)"\
1315 " is less or equal to uSNChanged (max = %lld uSNChanged = %lld)\n",
1316 (long long)*seq_num, (long long)db_seq));
1317 return LDB_ERR_OPERATIONS_ERROR;
1320 } else {
1321 /* search for the existing replPropertyMetaDataBlob. We need
1322 * to use REVEAL and ask for DNs in storage format to support
1323 * the check for values being the same in
1324 * replmd_update_rpmd_element()
1326 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attrs,
1327 DSDB_FLAG_NEXT_MODULE |
1328 DSDB_SEARCH_SHOW_RECYCLED |
1329 DSDB_SEARCH_SHOW_EXTENDED_DN |
1330 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
1331 DSDB_SEARCH_REVEAL_INTERNALS, req);
1332 if (ret != LDB_SUCCESS) {
1333 return ret;
1336 objectclass_el = ldb_msg_find_element(res->msgs[0], "objectClass");
1337 if (is_urgent && replmd_check_urgent_objectclass(objectclass_el,
1338 situation)) {
1339 *is_urgent = true;
1342 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
1343 if (!omd_value) {
1344 DEBUG(0,(__location__ ": Object %s does not have a replPropertyMetaData attribute\n",
1345 ldb_dn_get_linearized(msg->dn)));
1346 return LDB_ERR_OPERATIONS_ERROR;
1349 ndr_err = ndr_pull_struct_blob(omd_value, msg, &omd,
1350 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
1351 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1352 DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s\n",
1353 ldb_dn_get_linearized(msg->dn)));
1354 return LDB_ERR_OPERATIONS_ERROR;
1357 if (omd.version != 1) {
1358 DEBUG(0,(__location__ ": bad version %u in replPropertyMetaData for %s\n",
1359 omd.version, ldb_dn_get_linearized(msg->dn)));
1360 return LDB_ERR_OPERATIONS_ERROR;
1363 for (i=0; i<msg->num_elements; i++) {
1364 struct ldb_message_element *old_el;
1365 old_el = ldb_msg_find_element(res->msgs[0], msg->elements[i].name);
1366 ret = replmd_update_rpmd_element(ldb, msg, &msg->elements[i], old_el, &omd, schema, seq_num,
1367 our_invocation_id, now, req);
1368 if (ret != LDB_SUCCESS) {
1369 return ret;
1372 if (is_urgent && !*is_urgent && (situation == REPL_URGENT_ON_UPDATE)) {
1373 *is_urgent = replmd_check_urgent_attribute(&msg->elements[i]);
1379 * replmd_update_rpmd_element has done an update if the
1380 * seq_num is set
1382 if (*seq_num != 0) {
1383 struct ldb_val *md_value;
1384 struct ldb_message_element *el;
1386 /*if we are RODC and this is a DRSR update then its ok*/
1387 if (!ldb_request_get_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID)) {
1388 unsigned instanceType;
1390 ret = samdb_rodc(ldb, rodc);
1391 if (ret != LDB_SUCCESS) {
1392 DEBUG(4, (__location__ ": unable to tell if we are an RODC\n"));
1393 } else if (*rodc) {
1394 ldb_set_errstring(ldb, "RODC modify is forbidden!");
1395 return LDB_ERR_REFERRAL;
1398 instanceType = ldb_msg_find_attr_as_uint(res->msgs[0], "instanceType", INSTANCE_TYPE_WRITE);
1399 if (!(instanceType & INSTANCE_TYPE_WRITE)) {
1400 return ldb_error(ldb, LDB_ERR_UNWILLING_TO_PERFORM,
1401 "cannot change replicated attribute on partial replica");
1405 md_value = talloc(msg, struct ldb_val);
1406 if (md_value == NULL) {
1407 ldb_oom(ldb);
1408 return LDB_ERR_OPERATIONS_ERROR;
1411 ret = replmd_replPropertyMetaDataCtr1_sort(&omd.ctr.ctr1, schema, msg->dn);
1412 if (ret != LDB_SUCCESS) {
1413 return ret;
1416 ndr_err = ndr_push_struct_blob(md_value, msg, &omd,
1417 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
1418 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1419 DEBUG(0,(__location__ ": Failed to marshall replPropertyMetaData for %s\n",
1420 ldb_dn_get_linearized(msg->dn)));
1421 return LDB_ERR_OPERATIONS_ERROR;
1424 ret = ldb_msg_add_empty(msg, "replPropertyMetaData", LDB_FLAG_MOD_REPLACE, &el);
1425 if (ret != LDB_SUCCESS) {
1426 DEBUG(0,(__location__ ": Failed to add updated replPropertyMetaData %s\n",
1427 ldb_dn_get_linearized(msg->dn)));
1428 return ret;
1431 el->num_values = 1;
1432 el->values = md_value;
1435 return LDB_SUCCESS;
1438 struct parsed_dn {
1439 struct dsdb_dn *dsdb_dn;
1440 struct GUID *guid;
1441 struct ldb_val *v;
1444 static int parsed_dn_compare(struct parsed_dn *pdn1, struct parsed_dn *pdn2)
1446 return GUID_compare(pdn1->guid, pdn2->guid);
1449 static struct parsed_dn *parsed_dn_find(struct parsed_dn *pdn,
1450 unsigned int count, struct GUID *guid,
1451 struct ldb_dn *dn)
1453 struct parsed_dn *ret;
1454 unsigned int i;
1455 if (dn && GUID_all_zero(guid)) {
1456 /* when updating a link using DRS, we sometimes get a
1457 NULL GUID. We then need to try and match by DN */
1458 for (i=0; i<count; i++) {
1459 if (ldb_dn_compare(pdn[i].dsdb_dn->dn, dn) == 0) {
1460 dsdb_get_extended_dn_guid(pdn[i].dsdb_dn->dn, guid, "GUID");
1461 return &pdn[i];
1464 return NULL;
1466 BINARY_ARRAY_SEARCH(pdn, count, guid, guid, GUID_compare, ret);
1467 return ret;
1471 get a series of message element values as an array of DNs and GUIDs
1472 the result is sorted by GUID
1474 static int get_parsed_dns(struct ldb_module *module, TALLOC_CTX *mem_ctx,
1475 struct ldb_message_element *el, struct parsed_dn **pdn,
1476 const char *ldap_oid, struct ldb_request *parent)
1478 unsigned int i;
1479 struct ldb_context *ldb = ldb_module_get_ctx(module);
1481 if (el == NULL) {
1482 *pdn = NULL;
1483 return LDB_SUCCESS;
1486 (*pdn) = talloc_array(mem_ctx, struct parsed_dn, el->num_values);
1487 if (!*pdn) {
1488 ldb_module_oom(module);
1489 return LDB_ERR_OPERATIONS_ERROR;
1492 for (i=0; i<el->num_values; i++) {
1493 struct ldb_val *v = &el->values[i];
1494 NTSTATUS status;
1495 struct ldb_dn *dn;
1496 struct parsed_dn *p;
1498 p = &(*pdn)[i];
1500 p->dsdb_dn = dsdb_dn_parse(*pdn, ldb, v, ldap_oid);
1501 if (p->dsdb_dn == NULL) {
1502 return LDB_ERR_INVALID_DN_SYNTAX;
1505 dn = p->dsdb_dn->dn;
1507 p->guid = talloc(*pdn, struct GUID);
1508 if (p->guid == NULL) {
1509 ldb_module_oom(module);
1510 return LDB_ERR_OPERATIONS_ERROR;
1513 status = dsdb_get_extended_dn_guid(dn, p->guid, "GUID");
1514 if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
1515 /* we got a DN without a GUID - go find the GUID */
1516 int ret = dsdb_module_guid_by_dn(module, dn, p->guid, parent);
1517 if (ret != LDB_SUCCESS) {
1518 ldb_asprintf_errstring(ldb, "Unable to find GUID for DN %s\n",
1519 ldb_dn_get_linearized(dn));
1520 if (ret == LDB_ERR_NO_SUCH_OBJECT &&
1521 LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_DELETE &&
1522 ldb_attr_cmp(el->name, "member") == 0) {
1523 return LDB_ERR_UNWILLING_TO_PERFORM;
1525 return ret;
1527 ret = dsdb_set_extended_dn_guid(dn, p->guid, "GUID");
1528 if (ret != LDB_SUCCESS) {
1529 return ret;
1531 } else if (!NT_STATUS_IS_OK(status)) {
1532 return LDB_ERR_OPERATIONS_ERROR;
1535 /* keep a pointer to the original ldb_val */
1536 p->v = v;
1539 TYPESAFE_QSORT(*pdn, el->num_values, parsed_dn_compare);
1541 return LDB_SUCCESS;
1545 build a new extended DN, including all meta data fields
1547 RMD_FLAGS = DSDB_RMD_FLAG_* bits
1548 RMD_ADDTIME = originating_add_time
1549 RMD_INVOCID = originating_invocation_id
1550 RMD_CHANGETIME = originating_change_time
1551 RMD_ORIGINATING_USN = originating_usn
1552 RMD_LOCAL_USN = local_usn
1553 RMD_VERSION = version
1555 static int replmd_build_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
1556 const struct GUID *invocation_id, uint64_t seq_num,
1557 uint64_t local_usn, NTTIME nttime, uint32_t version, bool deleted)
1559 struct ldb_dn *dn = dsdb_dn->dn;
1560 const char *tstring, *usn_string, *flags_string;
1561 struct ldb_val tval;
1562 struct ldb_val iid;
1563 struct ldb_val usnv, local_usnv;
1564 struct ldb_val vers, flagsv;
1565 NTSTATUS status;
1566 int ret;
1567 const char *dnstring;
1568 char *vstring;
1569 uint32_t rmd_flags = deleted?DSDB_RMD_FLAG_DELETED:0;
1571 tstring = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)nttime);
1572 if (!tstring) {
1573 return LDB_ERR_OPERATIONS_ERROR;
1575 tval = data_blob_string_const(tstring);
1577 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)seq_num);
1578 if (!usn_string) {
1579 return LDB_ERR_OPERATIONS_ERROR;
1581 usnv = data_blob_string_const(usn_string);
1583 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)local_usn);
1584 if (!usn_string) {
1585 return LDB_ERR_OPERATIONS_ERROR;
1587 local_usnv = data_blob_string_const(usn_string);
1589 vstring = talloc_asprintf(mem_ctx, "%lu", (unsigned long)version);
1590 if (!vstring) {
1591 return LDB_ERR_OPERATIONS_ERROR;
1593 vers = data_blob_string_const(vstring);
1595 status = GUID_to_ndr_blob(invocation_id, dn, &iid);
1596 if (!NT_STATUS_IS_OK(status)) {
1597 return LDB_ERR_OPERATIONS_ERROR;
1600 flags_string = talloc_asprintf(mem_ctx, "%u", rmd_flags);
1601 if (!flags_string) {
1602 return LDB_ERR_OPERATIONS_ERROR;
1604 flagsv = data_blob_string_const(flags_string);
1606 ret = ldb_dn_set_extended_component(dn, "RMD_FLAGS", &flagsv);
1607 if (ret != LDB_SUCCESS) return ret;
1608 ret = ldb_dn_set_extended_component(dn, "RMD_ADDTIME", &tval);
1609 if (ret != LDB_SUCCESS) return ret;
1610 ret = ldb_dn_set_extended_component(dn, "RMD_INVOCID", &iid);
1611 if (ret != LDB_SUCCESS) return ret;
1612 ret = ldb_dn_set_extended_component(dn, "RMD_CHANGETIME", &tval);
1613 if (ret != LDB_SUCCESS) return ret;
1614 ret = ldb_dn_set_extended_component(dn, "RMD_LOCAL_USN", &local_usnv);
1615 if (ret != LDB_SUCCESS) return ret;
1616 ret = ldb_dn_set_extended_component(dn, "RMD_ORIGINATING_USN", &usnv);
1617 if (ret != LDB_SUCCESS) return ret;
1618 ret = ldb_dn_set_extended_component(dn, "RMD_VERSION", &vers);
1619 if (ret != LDB_SUCCESS) return ret;
1621 dnstring = dsdb_dn_get_extended_linearized(mem_ctx, dsdb_dn, 1);
1622 if (dnstring == NULL) {
1623 return LDB_ERR_OPERATIONS_ERROR;
1625 *v = data_blob_string_const(dnstring);
1627 return LDB_SUCCESS;
1630 static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
1631 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
1632 uint64_t seq_num, uint64_t local_usn, NTTIME nttime,
1633 uint32_t version, bool deleted);
1636 check if any links need upgrading from w2k format
1638 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.
1640 static int replmd_check_upgrade_links(struct parsed_dn *dns, uint32_t count, struct ldb_message_element *parent_ctx, const struct GUID *invocation_id)
1642 uint32_t i;
1643 for (i=0; i<count; i++) {
1644 NTSTATUS status;
1645 uint32_t version;
1646 int ret;
1648 status = dsdb_get_extended_dn_uint32(dns[i].dsdb_dn->dn, &version, "RMD_VERSION");
1649 if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
1650 continue;
1653 /* it's an old one that needs upgrading */
1654 ret = replmd_update_la_val(parent_ctx->values, dns[i].v, dns[i].dsdb_dn, dns[i].dsdb_dn, invocation_id,
1655 1, 1, 0, 0, false);
1656 if (ret != LDB_SUCCESS) {
1657 return ret;
1660 return LDB_SUCCESS;
1664 update an extended DN, including all meta data fields
1666 see replmd_build_la_val for value names
1668 static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
1669 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
1670 uint64_t seq_num, uint64_t local_usn, NTTIME nttime,
1671 uint32_t version, bool deleted)
1673 struct ldb_dn *dn = dsdb_dn->dn;
1674 const char *tstring, *usn_string, *flags_string;
1675 struct ldb_val tval;
1676 struct ldb_val iid;
1677 struct ldb_val usnv, local_usnv;
1678 struct ldb_val vers, flagsv;
1679 const struct ldb_val *old_addtime;
1680 uint32_t old_version;
1681 NTSTATUS status;
1682 int ret;
1683 const char *dnstring;
1684 char *vstring;
1685 uint32_t rmd_flags = deleted?DSDB_RMD_FLAG_DELETED:0;
1687 tstring = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)nttime);
1688 if (!tstring) {
1689 return LDB_ERR_OPERATIONS_ERROR;
1691 tval = data_blob_string_const(tstring);
1693 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)seq_num);
1694 if (!usn_string) {
1695 return LDB_ERR_OPERATIONS_ERROR;
1697 usnv = data_blob_string_const(usn_string);
1699 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)local_usn);
1700 if (!usn_string) {
1701 return LDB_ERR_OPERATIONS_ERROR;
1703 local_usnv = data_blob_string_const(usn_string);
1705 status = GUID_to_ndr_blob(invocation_id, dn, &iid);
1706 if (!NT_STATUS_IS_OK(status)) {
1707 return LDB_ERR_OPERATIONS_ERROR;
1710 flags_string = talloc_asprintf(mem_ctx, "%u", rmd_flags);
1711 if (!flags_string) {
1712 return LDB_ERR_OPERATIONS_ERROR;
1714 flagsv = data_blob_string_const(flags_string);
1716 ret = ldb_dn_set_extended_component(dn, "RMD_FLAGS", &flagsv);
1717 if (ret != LDB_SUCCESS) return ret;
1719 /* get the ADDTIME from the original */
1720 old_addtime = ldb_dn_get_extended_component(old_dsdb_dn->dn, "RMD_ADDTIME");
1721 if (old_addtime == NULL) {
1722 old_addtime = &tval;
1724 if (dsdb_dn != old_dsdb_dn ||
1725 ldb_dn_get_extended_component(dn, "RMD_ADDTIME") == NULL) {
1726 ret = ldb_dn_set_extended_component(dn, "RMD_ADDTIME", old_addtime);
1727 if (ret != LDB_SUCCESS) return ret;
1730 /* use our invocation id */
1731 ret = ldb_dn_set_extended_component(dn, "RMD_INVOCID", &iid);
1732 if (ret != LDB_SUCCESS) return ret;
1734 /* changetime is the current time */
1735 ret = ldb_dn_set_extended_component(dn, "RMD_CHANGETIME", &tval);
1736 if (ret != LDB_SUCCESS) return ret;
1738 /* update the USN */
1739 ret = ldb_dn_set_extended_component(dn, "RMD_ORIGINATING_USN", &usnv);
1740 if (ret != LDB_SUCCESS) return ret;
1742 ret = ldb_dn_set_extended_component(dn, "RMD_LOCAL_USN", &local_usnv);
1743 if (ret != LDB_SUCCESS) return ret;
1745 /* increase the version by 1 */
1746 status = dsdb_get_extended_dn_uint32(old_dsdb_dn->dn, &old_version, "RMD_VERSION");
1747 if (NT_STATUS_IS_OK(status) && old_version >= version) {
1748 version = old_version+1;
1750 vstring = talloc_asprintf(dn, "%lu", (unsigned long)version);
1751 vers = data_blob_string_const(vstring);
1752 ret = ldb_dn_set_extended_component(dn, "RMD_VERSION", &vers);
1753 if (ret != LDB_SUCCESS) return ret;
1755 dnstring = dsdb_dn_get_extended_linearized(mem_ctx, dsdb_dn, 1);
1756 if (dnstring == NULL) {
1757 return LDB_ERR_OPERATIONS_ERROR;
1759 *v = data_blob_string_const(dnstring);
1761 return LDB_SUCCESS;
1765 handle adding a linked attribute
1767 static int replmd_modify_la_add(struct ldb_module *module,
1768 const struct dsdb_schema *schema,
1769 struct ldb_message *msg,
1770 struct ldb_message_element *el,
1771 struct ldb_message_element *old_el,
1772 const struct dsdb_attribute *schema_attr,
1773 uint64_t seq_num,
1774 time_t t,
1775 struct GUID *msg_guid,
1776 struct ldb_request *parent)
1778 unsigned int i;
1779 struct parsed_dn *dns, *old_dns;
1780 TALLOC_CTX *tmp_ctx = talloc_new(msg);
1781 int ret;
1782 struct ldb_val *new_values = NULL;
1783 unsigned int num_new_values = 0;
1784 unsigned old_num_values = old_el?old_el->num_values:0;
1785 const struct GUID *invocation_id;
1786 struct ldb_context *ldb = ldb_module_get_ctx(module);
1787 NTTIME now;
1789 unix_to_nt_time(&now, t);
1791 ret = get_parsed_dns(module, tmp_ctx, el, &dns, schema_attr->syntax->ldap_oid, parent);
1792 if (ret != LDB_SUCCESS) {
1793 talloc_free(tmp_ctx);
1794 return ret;
1797 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns, schema_attr->syntax->ldap_oid, parent);
1798 if (ret != LDB_SUCCESS) {
1799 talloc_free(tmp_ctx);
1800 return ret;
1803 invocation_id = samdb_ntds_invocation_id(ldb);
1804 if (!invocation_id) {
1805 talloc_free(tmp_ctx);
1806 return LDB_ERR_OPERATIONS_ERROR;
1809 ret = replmd_check_upgrade_links(old_dns, old_num_values, old_el, invocation_id);
1810 if (ret != LDB_SUCCESS) {
1811 talloc_free(tmp_ctx);
1812 return ret;
1815 /* for each new value, see if it exists already with the same GUID */
1816 for (i=0; i<el->num_values; i++) {
1817 struct parsed_dn *p = parsed_dn_find(old_dns, old_num_values, dns[i].guid, NULL);
1818 if (p == NULL) {
1819 /* this is a new linked attribute value */
1820 new_values = talloc_realloc(tmp_ctx, new_values, struct ldb_val, num_new_values+1);
1821 if (new_values == NULL) {
1822 ldb_module_oom(module);
1823 talloc_free(tmp_ctx);
1824 return LDB_ERR_OPERATIONS_ERROR;
1826 ret = replmd_build_la_val(new_values, &new_values[num_new_values], dns[i].dsdb_dn,
1827 invocation_id, seq_num, seq_num, now, 0, false);
1828 if (ret != LDB_SUCCESS) {
1829 talloc_free(tmp_ctx);
1830 return ret;
1832 num_new_values++;
1833 } else {
1834 /* this is only allowed if the GUID was
1835 previously deleted. */
1836 uint32_t rmd_flags = dsdb_dn_rmd_flags(p->dsdb_dn->dn);
1838 if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
1839 ldb_asprintf_errstring(ldb, "Attribute %s already exists for target GUID %s",
1840 el->name, GUID_string(tmp_ctx, p->guid));
1841 talloc_free(tmp_ctx);
1842 /* error codes for 'member' need to be
1843 special cased */
1844 if (ldb_attr_cmp(el->name, "member") == 0) {
1845 return LDB_ERR_ENTRY_ALREADY_EXISTS;
1846 } else {
1847 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
1850 ret = replmd_update_la_val(old_el->values, p->v, dns[i].dsdb_dn, p->dsdb_dn,
1851 invocation_id, seq_num, seq_num, now, 0, false);
1852 if (ret != LDB_SUCCESS) {
1853 talloc_free(tmp_ctx);
1854 return ret;
1858 ret = replmd_add_backlink(module, schema, msg_guid, dns[i].guid, true, schema_attr, true);
1859 if (ret != LDB_SUCCESS) {
1860 talloc_free(tmp_ctx);
1861 return ret;
1865 /* add the new ones on to the end of the old values, constructing a new el->values */
1866 el->values = talloc_realloc(msg->elements, old_el?old_el->values:NULL,
1867 struct ldb_val,
1868 old_num_values+num_new_values);
1869 if (el->values == NULL) {
1870 ldb_module_oom(module);
1871 return LDB_ERR_OPERATIONS_ERROR;
1874 memcpy(&el->values[old_num_values], new_values, num_new_values*sizeof(struct ldb_val));
1875 el->num_values = old_num_values + num_new_values;
1877 talloc_steal(msg->elements, el->values);
1878 talloc_steal(el->values, new_values);
1880 talloc_free(tmp_ctx);
1882 /* we now tell the backend to replace all existing values
1883 with the one we have constructed */
1884 el->flags = LDB_FLAG_MOD_REPLACE;
1886 return LDB_SUCCESS;
1891 handle deleting all active linked attributes
1893 static int replmd_modify_la_delete(struct ldb_module *module,
1894 const struct dsdb_schema *schema,
1895 struct ldb_message *msg,
1896 struct ldb_message_element *el,
1897 struct ldb_message_element *old_el,
1898 const struct dsdb_attribute *schema_attr,
1899 uint64_t seq_num,
1900 time_t t,
1901 struct GUID *msg_guid,
1902 struct ldb_request *parent)
1904 unsigned int i;
1905 struct parsed_dn *dns, *old_dns;
1906 TALLOC_CTX *tmp_ctx = talloc_new(msg);
1907 int ret;
1908 const struct GUID *invocation_id;
1909 struct ldb_context *ldb = ldb_module_get_ctx(module);
1910 NTTIME now;
1912 unix_to_nt_time(&now, t);
1914 /* check if there is nothing to delete */
1915 if ((!old_el || old_el->num_values == 0) &&
1916 el->num_values == 0) {
1917 return LDB_SUCCESS;
1920 if (!old_el || old_el->num_values == 0) {
1921 return LDB_ERR_NO_SUCH_ATTRIBUTE;
1924 ret = get_parsed_dns(module, tmp_ctx, el, &dns, schema_attr->syntax->ldap_oid, parent);
1925 if (ret != LDB_SUCCESS) {
1926 talloc_free(tmp_ctx);
1927 return ret;
1930 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns, schema_attr->syntax->ldap_oid, parent);
1931 if (ret != LDB_SUCCESS) {
1932 talloc_free(tmp_ctx);
1933 return ret;
1936 invocation_id = samdb_ntds_invocation_id(ldb);
1937 if (!invocation_id) {
1938 return LDB_ERR_OPERATIONS_ERROR;
1941 ret = replmd_check_upgrade_links(old_dns, old_el->num_values, old_el, invocation_id);
1942 if (ret != LDB_SUCCESS) {
1943 talloc_free(tmp_ctx);
1944 return ret;
1947 el->values = NULL;
1949 /* see if we are being asked to delete any links that
1950 don't exist or are already deleted */
1951 for (i=0; i<el->num_values; i++) {
1952 struct parsed_dn *p = &dns[i];
1953 struct parsed_dn *p2;
1954 uint32_t rmd_flags;
1956 p2 = parsed_dn_find(old_dns, old_el->num_values, p->guid, NULL);
1957 if (!p2) {
1958 ldb_asprintf_errstring(ldb, "Attribute %s doesn't exist for target GUID %s",
1959 el->name, GUID_string(tmp_ctx, p->guid));
1960 if (ldb_attr_cmp(el->name, "member") == 0) {
1961 return LDB_ERR_UNWILLING_TO_PERFORM;
1962 } else {
1963 return LDB_ERR_NO_SUCH_ATTRIBUTE;
1966 rmd_flags = dsdb_dn_rmd_flags(p2->dsdb_dn->dn);
1967 if (rmd_flags & DSDB_RMD_FLAG_DELETED) {
1968 ldb_asprintf_errstring(ldb, "Attribute %s already deleted for target GUID %s",
1969 el->name, GUID_string(tmp_ctx, p->guid));
1970 if (ldb_attr_cmp(el->name, "member") == 0) {
1971 return LDB_ERR_UNWILLING_TO_PERFORM;
1972 } else {
1973 return LDB_ERR_NO_SUCH_ATTRIBUTE;
1978 /* for each new value, see if it exists already with the same GUID
1979 if it is not already deleted and matches the delete list then delete it
1981 for (i=0; i<old_el->num_values; i++) {
1982 struct parsed_dn *p = &old_dns[i];
1983 uint32_t rmd_flags;
1985 if (el->num_values && parsed_dn_find(dns, el->num_values, p->guid, NULL) == NULL) {
1986 continue;
1989 rmd_flags = dsdb_dn_rmd_flags(p->dsdb_dn->dn);
1990 if (rmd_flags & DSDB_RMD_FLAG_DELETED) continue;
1992 ret = replmd_update_la_val(old_el->values, p->v, p->dsdb_dn, p->dsdb_dn,
1993 invocation_id, seq_num, seq_num, now, 0, true);
1994 if (ret != LDB_SUCCESS) {
1995 talloc_free(tmp_ctx);
1996 return ret;
1999 ret = replmd_add_backlink(module, schema, msg_guid, old_dns[i].guid, false, schema_attr, true);
2000 if (ret != LDB_SUCCESS) {
2001 talloc_free(tmp_ctx);
2002 return ret;
2006 el->values = talloc_steal(msg->elements, old_el->values);
2007 el->num_values = old_el->num_values;
2009 talloc_free(tmp_ctx);
2011 /* we now tell the backend to replace all existing values
2012 with the one we have constructed */
2013 el->flags = LDB_FLAG_MOD_REPLACE;
2015 return LDB_SUCCESS;
2019 handle replacing a linked attribute
2021 static int replmd_modify_la_replace(struct ldb_module *module,
2022 const struct dsdb_schema *schema,
2023 struct ldb_message *msg,
2024 struct ldb_message_element *el,
2025 struct ldb_message_element *old_el,
2026 const struct dsdb_attribute *schema_attr,
2027 uint64_t seq_num,
2028 time_t t,
2029 struct GUID *msg_guid,
2030 struct ldb_request *parent)
2032 unsigned int i;
2033 struct parsed_dn *dns, *old_dns;
2034 TALLOC_CTX *tmp_ctx = talloc_new(msg);
2035 int ret;
2036 const struct GUID *invocation_id;
2037 struct ldb_context *ldb = ldb_module_get_ctx(module);
2038 struct ldb_val *new_values = NULL;
2039 unsigned int num_new_values = 0;
2040 unsigned int old_num_values = old_el?old_el->num_values:0;
2041 NTTIME now;
2043 unix_to_nt_time(&now, t);
2045 /* check if there is nothing to replace */
2046 if ((!old_el || old_el->num_values == 0) &&
2047 el->num_values == 0) {
2048 return LDB_SUCCESS;
2051 ret = get_parsed_dns(module, tmp_ctx, el, &dns, schema_attr->syntax->ldap_oid, parent);
2052 if (ret != LDB_SUCCESS) {
2053 talloc_free(tmp_ctx);
2054 return ret;
2057 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns, schema_attr->syntax->ldap_oid, parent);
2058 if (ret != LDB_SUCCESS) {
2059 talloc_free(tmp_ctx);
2060 return ret;
2063 invocation_id = samdb_ntds_invocation_id(ldb);
2064 if (!invocation_id) {
2065 return LDB_ERR_OPERATIONS_ERROR;
2068 ret = replmd_check_upgrade_links(old_dns, old_num_values, old_el, invocation_id);
2069 if (ret != LDB_SUCCESS) {
2070 talloc_free(tmp_ctx);
2071 return ret;
2074 /* mark all the old ones as deleted */
2075 for (i=0; i<old_num_values; i++) {
2076 struct parsed_dn *old_p = &old_dns[i];
2077 struct parsed_dn *p;
2078 uint32_t rmd_flags = dsdb_dn_rmd_flags(old_p->dsdb_dn->dn);
2080 if (rmd_flags & DSDB_RMD_FLAG_DELETED) continue;
2082 ret = replmd_add_backlink(module, schema, msg_guid, old_dns[i].guid, false, schema_attr, false);
2083 if (ret != LDB_SUCCESS) {
2084 talloc_free(tmp_ctx);
2085 return ret;
2088 p = parsed_dn_find(dns, el->num_values, old_p->guid, NULL);
2089 if (p) {
2090 /* we don't delete it if we are re-adding it */
2091 continue;
2094 ret = replmd_update_la_val(old_el->values, old_p->v, old_p->dsdb_dn, old_p->dsdb_dn,
2095 invocation_id, seq_num, seq_num, now, 0, true);
2096 if (ret != LDB_SUCCESS) {
2097 talloc_free(tmp_ctx);
2098 return ret;
2102 /* for each new value, either update its meta-data, or add it
2103 * to old_el
2105 for (i=0; i<el->num_values; i++) {
2106 struct parsed_dn *p = &dns[i], *old_p;
2108 if (old_dns &&
2109 (old_p = parsed_dn_find(old_dns,
2110 old_num_values, p->guid, NULL)) != NULL) {
2111 /* update in place */
2112 ret = replmd_update_la_val(old_el->values, old_p->v, p->dsdb_dn,
2113 old_p->dsdb_dn, invocation_id,
2114 seq_num, seq_num, now, 0, false);
2115 if (ret != LDB_SUCCESS) {
2116 talloc_free(tmp_ctx);
2117 return ret;
2119 } else {
2120 /* add a new one */
2121 new_values = talloc_realloc(tmp_ctx, new_values, struct ldb_val,
2122 num_new_values+1);
2123 if (new_values == NULL) {
2124 ldb_module_oom(module);
2125 talloc_free(tmp_ctx);
2126 return LDB_ERR_OPERATIONS_ERROR;
2128 ret = replmd_build_la_val(new_values, &new_values[num_new_values], dns[i].dsdb_dn,
2129 invocation_id, seq_num, seq_num, now, 0, false);
2130 if (ret != LDB_SUCCESS) {
2131 talloc_free(tmp_ctx);
2132 return ret;
2134 num_new_values++;
2137 ret = replmd_add_backlink(module, schema, msg_guid, dns[i].guid, true, schema_attr, false);
2138 if (ret != LDB_SUCCESS) {
2139 talloc_free(tmp_ctx);
2140 return ret;
2144 /* add the new values to the end of old_el */
2145 if (num_new_values != 0) {
2146 el->values = talloc_realloc(msg->elements, old_el?old_el->values:NULL,
2147 struct ldb_val, old_num_values+num_new_values);
2148 if (el->values == NULL) {
2149 ldb_module_oom(module);
2150 return LDB_ERR_OPERATIONS_ERROR;
2152 memcpy(&el->values[old_num_values], &new_values[0],
2153 sizeof(struct ldb_val)*num_new_values);
2154 el->num_values = old_num_values + num_new_values;
2155 talloc_steal(msg->elements, new_values);
2156 } else {
2157 el->values = old_el->values;
2158 el->num_values = old_el->num_values;
2159 talloc_steal(msg->elements, el->values);
2162 talloc_free(tmp_ctx);
2164 /* we now tell the backend to replace all existing values
2165 with the one we have constructed */
2166 el->flags = LDB_FLAG_MOD_REPLACE;
2168 return LDB_SUCCESS;
2173 handle linked attributes in modify requests
2175 static int replmd_modify_handle_linked_attribs(struct ldb_module *module,
2176 struct ldb_message *msg,
2177 uint64_t seq_num, time_t t,
2178 struct ldb_request *parent)
2180 struct ldb_result *res;
2181 unsigned int i;
2182 int ret;
2183 struct ldb_context *ldb = ldb_module_get_ctx(module);
2184 struct ldb_message *old_msg;
2186 const struct dsdb_schema *schema;
2187 struct GUID old_guid;
2189 if (seq_num == 0) {
2190 /* there the replmd_update_rpmd code has already
2191 * checked and saw that there are no linked
2192 * attributes */
2193 return LDB_SUCCESS;
2196 if (dsdb_functional_level(ldb) == DS_DOMAIN_FUNCTION_2000) {
2197 /* don't do anything special for linked attributes */
2198 return LDB_SUCCESS;
2201 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, NULL,
2202 DSDB_FLAG_NEXT_MODULE |
2203 DSDB_SEARCH_SHOW_RECYCLED |
2204 DSDB_SEARCH_REVEAL_INTERNALS |
2205 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
2206 parent);
2207 if (ret != LDB_SUCCESS) {
2208 return ret;
2210 schema = dsdb_get_schema(ldb, res);
2211 if (!schema) {
2212 return LDB_ERR_OPERATIONS_ERROR;
2215 old_msg = res->msgs[0];
2217 old_guid = samdb_result_guid(old_msg, "objectGUID");
2219 for (i=0; i<msg->num_elements; i++) {
2220 struct ldb_message_element *el = &msg->elements[i];
2221 struct ldb_message_element *old_el, *new_el;
2222 const struct dsdb_attribute *schema_attr
2223 = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
2224 if (!schema_attr) {
2225 ldb_asprintf_errstring(ldb,
2226 "%s: attribute %s is not a valid attribute in schema",
2227 __FUNCTION__, el->name);
2228 return LDB_ERR_OBJECT_CLASS_VIOLATION;
2230 if (schema_attr->linkID == 0) {
2231 continue;
2233 if ((schema_attr->linkID & 1) == 1) {
2234 if (parent && ldb_request_get_control(parent, DSDB_CONTROL_DBCHECK)) {
2235 continue;
2237 /* Odd is for the target. Illegal to modify */
2238 ldb_asprintf_errstring(ldb,
2239 "attribute %s must not be modified directly, it is a linked attribute", el->name);
2240 return LDB_ERR_UNWILLING_TO_PERFORM;
2242 old_el = ldb_msg_find_element(old_msg, el->name);
2243 switch (el->flags & LDB_FLAG_MOD_MASK) {
2244 case LDB_FLAG_MOD_REPLACE:
2245 ret = replmd_modify_la_replace(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid, parent);
2246 break;
2247 case LDB_FLAG_MOD_DELETE:
2248 ret = replmd_modify_la_delete(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid, parent);
2249 break;
2250 case LDB_FLAG_MOD_ADD:
2251 ret = replmd_modify_la_add(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid, parent);
2252 break;
2253 default:
2254 ldb_asprintf_errstring(ldb,
2255 "invalid flags 0x%x for %s linked attribute",
2256 el->flags, el->name);
2257 return LDB_ERR_UNWILLING_TO_PERFORM;
2259 if (dsdb_check_single_valued_link(schema_attr, el) != LDB_SUCCESS) {
2260 ldb_asprintf_errstring(ldb,
2261 "Attribute %s is single valued but more than one value has been supplied",
2262 el->name);
2263 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
2264 } else {
2265 el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
2270 if (ret != LDB_SUCCESS) {
2271 return ret;
2273 if (old_el) {
2274 ldb_msg_remove_attr(old_msg, el->name);
2276 ldb_msg_add_empty(old_msg, el->name, 0, &new_el);
2277 new_el->num_values = el->num_values;
2278 new_el->values = talloc_steal(msg->elements, el->values);
2280 /* TODO: this relises a bit too heavily on the exact
2281 behaviour of ldb_msg_find_element and
2282 ldb_msg_remove_element */
2283 old_el = ldb_msg_find_element(msg, el->name);
2284 if (old_el != el) {
2285 ldb_msg_remove_element(msg, old_el);
2286 i--;
2290 talloc_free(res);
2291 return ret;
2296 static int replmd_modify(struct ldb_module *module, struct ldb_request *req)
2298 struct ldb_context *ldb;
2299 struct replmd_replicated_request *ac;
2300 struct ldb_request *down_req;
2301 struct ldb_message *msg;
2302 time_t t = time(NULL);
2303 int ret;
2304 bool is_urgent = false, rodc = false;
2305 unsigned int functional_level;
2306 const DATA_BLOB *guid_blob;
2308 /* do not manipulate our control entries */
2309 if (ldb_dn_is_special(req->op.mod.message->dn)) {
2310 return ldb_next_request(module, req);
2313 ldb = ldb_module_get_ctx(module);
2315 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_modify\n");
2317 guid_blob = ldb_msg_find_ldb_val(req->op.mod.message, "objectGUID");
2318 if ( guid_blob != NULL ) {
2319 ldb_set_errstring(ldb,
2320 "replmd_modify: it's not allowed to change the objectGUID!");
2321 return LDB_ERR_CONSTRAINT_VIOLATION;
2324 ac = replmd_ctx_init(module, req);
2325 if (ac == NULL) {
2326 return ldb_module_oom(module);
2329 functional_level = dsdb_functional_level(ldb);
2331 /* we have to copy the message as the caller might have it as a const */
2332 msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
2333 if (msg == NULL) {
2334 ldb_oom(ldb);
2335 talloc_free(ac);
2336 return LDB_ERR_OPERATIONS_ERROR;
2339 ldb_msg_remove_attr(msg, "whenChanged");
2340 ldb_msg_remove_attr(msg, "uSNChanged");
2342 ret = replmd_update_rpmd(module, ac->schema, req, NULL,
2343 msg, &ac->seq_num, t, &is_urgent, &rodc);
2344 if (rodc && (ret == LDB_ERR_REFERRAL)) {
2345 struct loadparm_context *lp_ctx;
2346 char *referral;
2348 lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
2349 struct loadparm_context);
2351 referral = talloc_asprintf(req,
2352 "ldap://%s/%s",
2353 lpcfg_dnsdomain(lp_ctx),
2354 ldb_dn_get_linearized(msg->dn));
2355 ret = ldb_module_send_referral(req, referral);
2356 talloc_free(ac);
2357 return ret;
2360 if (ret != LDB_SUCCESS) {
2361 talloc_free(ac);
2362 return ret;
2365 ret = replmd_modify_handle_linked_attribs(module, msg, ac->seq_num, t, req);
2366 if (ret != LDB_SUCCESS) {
2367 talloc_free(ac);
2368 return ret;
2371 /* TODO:
2372 * - replace the old object with the newly constructed one
2375 ac->is_urgent = is_urgent;
2377 ret = ldb_build_mod_req(&down_req, ldb, ac,
2378 msg,
2379 req->controls,
2380 ac, replmd_op_callback,
2381 req);
2382 LDB_REQ_SET_LOCATION(down_req);
2383 if (ret != LDB_SUCCESS) {
2384 talloc_free(ac);
2385 return ret;
2388 /* current partition control is needed by "replmd_op_callback" */
2389 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
2390 ret = ldb_request_add_control(down_req,
2391 DSDB_CONTROL_CURRENT_PARTITION_OID,
2392 false, NULL);
2393 if (ret != LDB_SUCCESS) {
2394 talloc_free(ac);
2395 return ret;
2399 /* If we are in functional level 2000, then
2400 * replmd_modify_handle_linked_attribs will have done
2401 * nothing */
2402 if (functional_level == DS_DOMAIN_FUNCTION_2000) {
2403 ret = ldb_request_add_control(down_req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
2404 if (ret != LDB_SUCCESS) {
2405 talloc_free(ac);
2406 return ret;
2410 talloc_steal(down_req, msg);
2412 /* we only change whenChanged and uSNChanged if the seq_num
2413 has changed */
2414 if (ac->seq_num != 0) {
2415 ret = add_time_element(msg, "whenChanged", t);
2416 if (ret != LDB_SUCCESS) {
2417 talloc_free(ac);
2418 ldb_operr(ldb);
2419 return ret;
2422 ret = add_uint64_element(ldb, msg, "uSNChanged", ac->seq_num);
2423 if (ret != LDB_SUCCESS) {
2424 talloc_free(ac);
2425 ldb_operr(ldb);
2426 return ret;
2430 /* go on with the call chain */
2431 return ldb_next_request(module, down_req);
2434 static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares);
2437 handle a rename request
2439 On a rename we need to do an extra ldb_modify which sets the
2440 whenChanged and uSNChanged attributes. We do this in a callback after the success.
2442 static int replmd_rename(struct ldb_module *module, struct ldb_request *req)
2444 struct ldb_context *ldb;
2445 struct replmd_replicated_request *ac;
2446 int ret;
2447 struct ldb_request *down_req;
2449 /* do not manipulate our control entries */
2450 if (ldb_dn_is_special(req->op.mod.message->dn)) {
2451 return ldb_next_request(module, req);
2454 ldb = ldb_module_get_ctx(module);
2456 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_rename\n");
2458 ac = replmd_ctx_init(module, req);
2459 if (ac == NULL) {
2460 return ldb_module_oom(module);
2463 ret = ldb_build_rename_req(&down_req, ldb, ac,
2464 ac->req->op.rename.olddn,
2465 ac->req->op.rename.newdn,
2466 ac->req->controls,
2467 ac, replmd_rename_callback,
2468 ac->req);
2469 LDB_REQ_SET_LOCATION(down_req);
2470 if (ret != LDB_SUCCESS) {
2471 talloc_free(ac);
2472 return ret;
2475 /* go on with the call chain */
2476 return ldb_next_request(module, down_req);
2479 /* After the rename is compleated, update the whenchanged etc */
2480 static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares)
2482 struct ldb_context *ldb;
2483 struct replmd_replicated_request *ac;
2484 struct ldb_request *down_req;
2485 struct ldb_message *msg;
2486 const struct dsdb_attribute *rdn_attr;
2487 const char *rdn_name;
2488 const struct ldb_val *rdn_val;
2489 const char *attrs[5] = { NULL, };
2490 time_t t = time(NULL);
2491 int ret;
2492 bool is_urgent = false, rodc = false;
2494 ac = talloc_get_type(req->context, struct replmd_replicated_request);
2495 ldb = ldb_module_get_ctx(ac->module);
2497 if (ares->error != LDB_SUCCESS) {
2498 return ldb_module_done(ac->req, ares->controls,
2499 ares->response, ares->error);
2502 if (ares->type != LDB_REPLY_DONE) {
2503 ldb_set_errstring(ldb,
2504 "invalid ldb_reply_type in callback");
2505 talloc_free(ares);
2506 return ldb_module_done(ac->req, NULL, NULL,
2507 LDB_ERR_OPERATIONS_ERROR);
2510 /* TODO:
2511 * - replace the old object with the newly constructed one
2514 msg = ldb_msg_new(ac);
2515 if (msg == NULL) {
2516 ldb_oom(ldb);
2517 return LDB_ERR_OPERATIONS_ERROR;
2520 msg->dn = ac->req->op.rename.newdn;
2522 rdn_name = ldb_dn_get_rdn_name(msg->dn);
2523 if (rdn_name == NULL) {
2524 talloc_free(ares);
2525 return ldb_module_done(ac->req, NULL, NULL,
2526 ldb_operr(ldb));
2529 /* normalize the rdn attribute name */
2530 rdn_attr = dsdb_attribute_by_lDAPDisplayName(ac->schema, rdn_name);
2531 if (rdn_attr == NULL) {
2532 talloc_free(ares);
2533 return ldb_module_done(ac->req, NULL, NULL,
2534 ldb_operr(ldb));
2536 rdn_name = rdn_attr->lDAPDisplayName;
2538 rdn_val = ldb_dn_get_rdn_val(msg->dn);
2539 if (rdn_val == NULL) {
2540 talloc_free(ares);
2541 return ldb_module_done(ac->req, NULL, NULL,
2542 ldb_operr(ldb));
2545 if (ldb_msg_add_empty(msg, rdn_name, LDB_FLAG_MOD_REPLACE, NULL) != 0) {
2546 talloc_free(ares);
2547 return ldb_module_done(ac->req, NULL, NULL,
2548 ldb_oom(ldb));
2550 if (ldb_msg_add_value(msg, rdn_name, rdn_val, NULL) != 0) {
2551 talloc_free(ares);
2552 return ldb_module_done(ac->req, NULL, NULL,
2553 ldb_oom(ldb));
2555 if (ldb_msg_add_empty(msg, "name", LDB_FLAG_MOD_REPLACE, NULL) != 0) {
2556 talloc_free(ares);
2557 return ldb_module_done(ac->req, NULL, NULL,
2558 ldb_oom(ldb));
2560 if (ldb_msg_add_value(msg, "name", rdn_val, NULL) != 0) {
2561 talloc_free(ares);
2562 return ldb_module_done(ac->req, NULL, NULL,
2563 ldb_oom(ldb));
2567 * here we let replmd_update_rpmd() only search for
2568 * the existing "replPropertyMetaData" and rdn_name attributes.
2570 * We do not want the existing "name" attribute as
2571 * the "name" attribute needs to get the version
2572 * updated on rename even if the rdn value hasn't changed.
2574 * This is the diff of the meta data, for a moved user
2575 * on a w2k8r2 server:
2577 * # record 1
2578 * -dn: CN=sdf df,CN=Users,DC=bla,DC=base
2579 * +dn: CN=sdf df,OU=TestOU,DC=bla,DC=base
2580 * replPropertyMetaData: NDR: struct replPropertyMetaDataBlob
2581 * version : 0x00000001 (1)
2582 * reserved : 0x00000000 (0)
2583 * @@ -66,11 +66,11 @@ replPropertyMetaData: NDR: struct re
2584 * local_usn : 0x00000000000037a5 (14245)
2585 * array: struct replPropertyMetaData1
2586 * attid : DRSUAPI_ATTID_name (0x90001)
2587 * - version : 0x00000001 (1)
2588 * - originating_change_time : Wed Feb 9 17:20:49 2011 CET
2589 * + version : 0x00000002 (2)
2590 * + originating_change_time : Wed Apr 6 15:21:01 2011 CEST
2591 * originating_invocation_id: 0d36ca05-5507-4e62-aca3-354bab0d39e1
2592 * - originating_usn : 0x00000000000037a5 (14245)
2593 * - local_usn : 0x00000000000037a5 (14245)
2594 * + originating_usn : 0x0000000000003834 (14388)
2595 * + local_usn : 0x0000000000003834 (14388)
2596 * array: struct replPropertyMetaData1
2597 * attid : DRSUAPI_ATTID_userAccountControl (0x90008)
2598 * version : 0x00000004 (4)
2600 attrs[0] = "replPropertyMetaData";
2601 attrs[1] = "objectClass";
2602 attrs[2] = "instanceType";
2603 attrs[3] = rdn_name;
2604 attrs[4] = NULL;
2606 ret = replmd_update_rpmd(ac->module, ac->schema, req, attrs,
2607 msg, &ac->seq_num, t, &is_urgent, &rodc);
2608 if (rodc && (ret == LDB_ERR_REFERRAL)) {
2609 struct ldb_dn *olddn = ac->req->op.rename.olddn;
2610 struct loadparm_context *lp_ctx;
2611 char *referral;
2613 lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
2614 struct loadparm_context);
2616 referral = talloc_asprintf(req,
2617 "ldap://%s/%s",
2618 lpcfg_dnsdomain(lp_ctx),
2619 ldb_dn_get_linearized(olddn));
2620 ret = ldb_module_send_referral(req, referral);
2621 talloc_free(ares);
2622 return ldb_module_done(req, NULL, NULL, ret);
2625 if (ret != LDB_SUCCESS) {
2626 talloc_free(ares);
2627 return ldb_module_done(ac->req, NULL, NULL, ret);
2630 if (ac->seq_num == 0) {
2631 talloc_free(ares);
2632 return ldb_module_done(ac->req, NULL, NULL,
2633 ldb_error(ldb, ret,
2634 "internal error seq_num == 0"));
2636 ac->is_urgent = is_urgent;
2638 ret = ldb_build_mod_req(&down_req, ldb, ac,
2639 msg,
2640 req->controls,
2641 ac, replmd_op_callback,
2642 req);
2643 LDB_REQ_SET_LOCATION(down_req);
2644 if (ret != LDB_SUCCESS) {
2645 talloc_free(ac);
2646 return ret;
2649 /* current partition control is needed by "replmd_op_callback" */
2650 if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
2651 ret = ldb_request_add_control(down_req,
2652 DSDB_CONTROL_CURRENT_PARTITION_OID,
2653 false, NULL);
2654 if (ret != LDB_SUCCESS) {
2655 talloc_free(ac);
2656 return ret;
2660 talloc_steal(down_req, msg);
2662 ret = add_time_element(msg, "whenChanged", t);
2663 if (ret != LDB_SUCCESS) {
2664 talloc_free(ac);
2665 ldb_operr(ldb);
2666 return ret;
2669 ret = add_uint64_element(ldb, msg, "uSNChanged", ac->seq_num);
2670 if (ret != LDB_SUCCESS) {
2671 talloc_free(ac);
2672 ldb_operr(ldb);
2673 return ret;
2676 /* go on with the call chain - do the modify after the rename */
2677 return ldb_next_request(ac->module, down_req);
2681 remove links from objects that point at this object when an object
2682 is deleted
2684 static int replmd_delete_remove_link(struct ldb_module *module,
2685 const struct dsdb_schema *schema,
2686 struct ldb_dn *dn,
2687 struct ldb_message_element *el,
2688 const struct dsdb_attribute *sa,
2689 struct ldb_request *parent)
2691 unsigned int i;
2692 TALLOC_CTX *tmp_ctx = talloc_new(module);
2693 struct ldb_context *ldb = ldb_module_get_ctx(module);
2695 for (i=0; i<el->num_values; i++) {
2696 struct dsdb_dn *dsdb_dn;
2697 NTSTATUS status;
2698 int ret;
2699 struct GUID guid2;
2700 struct ldb_message *msg;
2701 const struct dsdb_attribute *target_attr;
2702 struct ldb_message_element *el2;
2703 struct ldb_val dn_val;
2705 if (dsdb_dn_is_deleted_val(&el->values[i])) {
2706 continue;
2709 dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i], sa->syntax->ldap_oid);
2710 if (!dsdb_dn) {
2711 talloc_free(tmp_ctx);
2712 return LDB_ERR_OPERATIONS_ERROR;
2715 status = dsdb_get_extended_dn_guid(dsdb_dn->dn, &guid2, "GUID");
2716 if (!NT_STATUS_IS_OK(status)) {
2717 talloc_free(tmp_ctx);
2718 return LDB_ERR_OPERATIONS_ERROR;
2721 /* remove the link */
2722 msg = ldb_msg_new(tmp_ctx);
2723 if (!msg) {
2724 ldb_module_oom(module);
2725 talloc_free(tmp_ctx);
2726 return LDB_ERR_OPERATIONS_ERROR;
2730 msg->dn = dsdb_dn->dn;
2732 target_attr = dsdb_attribute_by_linkID(schema, sa->linkID ^ 1);
2733 if (target_attr == NULL) {
2734 continue;
2737 ret = ldb_msg_add_empty(msg, target_attr->lDAPDisplayName, LDB_FLAG_MOD_DELETE, &el2);
2738 if (ret != LDB_SUCCESS) {
2739 ldb_module_oom(module);
2740 talloc_free(tmp_ctx);
2741 return LDB_ERR_OPERATIONS_ERROR;
2743 dn_val = data_blob_string_const(ldb_dn_get_linearized(dn));
2744 el2->values = &dn_val;
2745 el2->num_values = 1;
2747 ret = dsdb_module_modify(module, msg, DSDB_FLAG_OWN_MODULE, parent);
2748 if (ret != LDB_SUCCESS) {
2749 talloc_free(tmp_ctx);
2750 return ret;
2753 talloc_free(tmp_ctx);
2754 return LDB_SUCCESS;
2759 handle update of replication meta data for deletion of objects
2761 This also handles the mapping of delete to a rename operation
2762 to allow deletes to be replicated.
2764 static int replmd_delete(struct ldb_module *module, struct ldb_request *req)
2766 int ret = LDB_ERR_OTHER;
2767 bool retb, disallow_move_on_delete;
2768 struct ldb_dn *old_dn, *new_dn;
2769 const char *rdn_name;
2770 const struct ldb_val *rdn_value, *new_rdn_value;
2771 struct GUID guid;
2772 struct ldb_context *ldb = ldb_module_get_ctx(module);
2773 const struct dsdb_schema *schema;
2774 struct ldb_message *msg, *old_msg;
2775 struct ldb_message_element *el;
2776 TALLOC_CTX *tmp_ctx;
2777 struct ldb_result *res, *parent_res;
2778 const char *preserved_attrs[] = {
2779 /* yes, this really is a hard coded list. See MS-ADTS
2780 section 3.1.1.5.5.1.1 */
2781 "nTSecurityDescriptor", "attributeID", "attributeSyntax", "dNReferenceUpdate", "dNSHostName",
2782 "flatName", "governsID", "groupType", "instanceType", "lDAPDisplayName", "legacyExchangeDN",
2783 "isDeleted", "isRecycled", "lastKnownParent", "msDS-LastKnownRDN", "mS-DS-CreatorSID",
2784 "mSMQOwnerID", "nCName", "objectClass", "distinguishedName", "objectGUID", "objectSid",
2785 "oMSyntax", "proxiedObjectName", "name", "replPropertyMetaData", "sAMAccountName",
2786 "securityIdentifier", "sIDHistory", "subClassOf", "systemFlags", "trustPartner", "trustDirection",
2787 "trustType", "trustAttributes", "userAccountControl", "uSNChanged", "uSNCreated", "whenCreated",
2788 "whenChanged", NULL};
2789 unsigned int i, el_count = 0;
2790 enum deletion_state { OBJECT_NOT_DELETED=1, OBJECT_DELETED=2, OBJECT_RECYCLED=3,
2791 OBJECT_TOMBSTONE=4, OBJECT_REMOVED=5 };
2792 enum deletion_state deletion_state, next_deletion_state;
2793 bool enabled;
2794 int functional_level;
2796 if (ldb_dn_is_special(req->op.del.dn)) {
2797 return ldb_next_request(module, req);
2800 tmp_ctx = talloc_new(ldb);
2801 if (!tmp_ctx) {
2802 ldb_oom(ldb);
2803 return LDB_ERR_OPERATIONS_ERROR;
2806 schema = dsdb_get_schema(ldb, tmp_ctx);
2807 if (!schema) {
2808 return LDB_ERR_OPERATIONS_ERROR;
2811 functional_level = dsdb_functional_level(ldb);
2813 old_dn = ldb_dn_copy(tmp_ctx, req->op.del.dn);
2815 /* we need the complete msg off disk, so we can work out which
2816 attributes need to be removed */
2817 ret = dsdb_module_search_dn(module, tmp_ctx, &res, old_dn, NULL,
2818 DSDB_FLAG_NEXT_MODULE |
2819 DSDB_SEARCH_SHOW_RECYCLED |
2820 DSDB_SEARCH_REVEAL_INTERNALS |
2821 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT, req);
2822 if (ret != LDB_SUCCESS) {
2823 talloc_free(tmp_ctx);
2824 return ret;
2826 old_msg = res->msgs[0];
2829 ret = dsdb_recyclebin_enabled(module, &enabled);
2830 if (ret != LDB_SUCCESS) {
2831 talloc_free(tmp_ctx);
2832 return ret;
2835 if (ldb_msg_check_string_attribute(old_msg, "isDeleted", "TRUE")) {
2836 if (!enabled) {
2837 deletion_state = OBJECT_TOMBSTONE;
2838 next_deletion_state = OBJECT_REMOVED;
2839 } else if (ldb_msg_check_string_attribute(old_msg, "isRecycled", "TRUE")) {
2840 deletion_state = OBJECT_RECYCLED;
2841 next_deletion_state = OBJECT_REMOVED;
2842 } else {
2843 deletion_state = OBJECT_DELETED;
2844 next_deletion_state = OBJECT_RECYCLED;
2846 } else {
2847 deletion_state = OBJECT_NOT_DELETED;
2848 if (enabled) {
2849 next_deletion_state = OBJECT_DELETED;
2850 } else {
2851 next_deletion_state = OBJECT_TOMBSTONE;
2855 if (next_deletion_state == OBJECT_REMOVED) {
2856 struct auth_session_info *session_info =
2857 (struct auth_session_info *)ldb_get_opaque(ldb, "sessionInfo");
2858 if (security_session_user_level(session_info, NULL) != SECURITY_SYSTEM) {
2859 ldb_asprintf_errstring(ldb, "Refusing to delete deleted object %s",
2860 ldb_dn_get_linearized(old_msg->dn));
2861 return LDB_ERR_UNWILLING_TO_PERFORM;
2864 /* it is already deleted - really remove it this time */
2865 talloc_free(tmp_ctx);
2866 return ldb_next_request(module, req);
2869 rdn_name = ldb_dn_get_rdn_name(old_dn);
2870 rdn_value = ldb_dn_get_rdn_val(old_dn);
2871 if ((rdn_name == NULL) || (rdn_value == NULL)) {
2872 talloc_free(tmp_ctx);
2873 return ldb_operr(ldb);
2876 msg = ldb_msg_new(tmp_ctx);
2877 if (msg == NULL) {
2878 ldb_module_oom(module);
2879 talloc_free(tmp_ctx);
2880 return LDB_ERR_OPERATIONS_ERROR;
2883 msg->dn = old_dn;
2885 if (deletion_state == OBJECT_NOT_DELETED){
2886 /* consider the SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE flag */
2887 disallow_move_on_delete =
2888 (ldb_msg_find_attr_as_int(old_msg, "systemFlags", 0)
2889 & SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE);
2891 /* work out where we will be renaming this object to */
2892 if (!disallow_move_on_delete) {
2893 ret = dsdb_get_deleted_objects_dn(ldb, tmp_ctx, old_dn,
2894 &new_dn);
2895 if (ret != LDB_SUCCESS) {
2896 /* this is probably an attempted delete on a partition
2897 * that doesn't allow delete operations, such as the
2898 * schema partition */
2899 ldb_asprintf_errstring(ldb, "No Deleted Objects container for DN %s",
2900 ldb_dn_get_linearized(old_dn));
2901 talloc_free(tmp_ctx);
2902 return LDB_ERR_UNWILLING_TO_PERFORM;
2904 } else {
2905 new_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
2906 if (new_dn == NULL) {
2907 ldb_module_oom(module);
2908 talloc_free(tmp_ctx);
2909 return LDB_ERR_OPERATIONS_ERROR;
2913 /* get the objects GUID from the search we just did */
2914 guid = samdb_result_guid(old_msg, "objectGUID");
2916 /* Add a formatted child */
2917 retb = ldb_dn_add_child_fmt(new_dn, "%s=%s\\0ADEL:%s",
2918 rdn_name,
2919 ldb_dn_escape_value(tmp_ctx, *rdn_value),
2920 GUID_string(tmp_ctx, &guid));
2921 if (!retb) {
2922 DEBUG(0,(__location__ ": Unable to add a formatted child to dn: %s",
2923 ldb_dn_get_linearized(new_dn)));
2924 talloc_free(tmp_ctx);
2925 return LDB_ERR_OPERATIONS_ERROR;
2928 ret = ldb_msg_add_string(msg, "isDeleted", "TRUE");
2929 if (ret != LDB_SUCCESS) {
2930 DEBUG(0,(__location__ ": Failed to add isDeleted string to the msg\n"));
2931 ldb_module_oom(module);
2932 talloc_free(tmp_ctx);
2933 return ret;
2935 msg->elements[el_count++].flags = LDB_FLAG_MOD_REPLACE;
2939 now we need to modify the object in the following ways:
2941 - add isDeleted=TRUE
2942 - update rDN and name, with new rDN
2943 - remove linked attributes
2944 - remove objectCategory and sAMAccountType
2945 - remove attribs not on the preserved list
2946 - preserved if in above list, or is rDN
2947 - remove all linked attribs from this object
2948 - remove all links from other objects to this object
2949 - add lastKnownParent
2950 - update replPropertyMetaData?
2952 see MS-ADTS "Tombstone Requirements" section 3.1.1.5.5.1.1
2955 /* we need the storage form of the parent GUID */
2956 ret = dsdb_module_search_dn(module, tmp_ctx, &parent_res,
2957 ldb_dn_get_parent(tmp_ctx, old_dn), NULL,
2958 DSDB_FLAG_NEXT_MODULE |
2959 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
2960 DSDB_SEARCH_REVEAL_INTERNALS|
2961 DSDB_SEARCH_SHOW_RECYCLED, req);
2962 if (ret != LDB_SUCCESS) {
2963 talloc_free(tmp_ctx);
2964 return ret;
2967 if (deletion_state == OBJECT_NOT_DELETED){
2968 ret = ldb_msg_add_steal_string(msg, "lastKnownParent",
2969 ldb_dn_get_extended_linearized(tmp_ctx, parent_res->msgs[0]->dn, 1));
2970 if (ret != LDB_SUCCESS) {
2971 DEBUG(0,(__location__ ": Failed to add lastKnownParent string to the msg\n"));
2972 ldb_module_oom(module);
2973 talloc_free(tmp_ctx);
2974 return ret;
2976 msg->elements[el_count++].flags = LDB_FLAG_MOD_ADD;
2979 switch (next_deletion_state){
2981 case OBJECT_DELETED:
2983 ret = ldb_msg_add_value(msg, "msDS-LastKnownRDN", rdn_value, NULL);
2984 if (ret != LDB_SUCCESS) {
2985 DEBUG(0,(__location__ ": Failed to add msDS-LastKnownRDN string to the msg\n"));
2986 ldb_module_oom(module);
2987 talloc_free(tmp_ctx);
2988 return ret;
2990 msg->elements[el_count++].flags = LDB_FLAG_MOD_ADD;
2992 ret = ldb_msg_add_empty(msg, "objectCategory", LDB_FLAG_MOD_REPLACE, NULL);
2993 if (ret != LDB_SUCCESS) {
2994 talloc_free(tmp_ctx);
2995 ldb_module_oom(module);
2996 return ret;
2999 ret = ldb_msg_add_empty(msg, "sAMAccountType", LDB_FLAG_MOD_REPLACE, NULL);
3000 if (ret != LDB_SUCCESS) {
3001 talloc_free(tmp_ctx);
3002 ldb_module_oom(module);
3003 return ret;
3006 break;
3008 case OBJECT_RECYCLED:
3009 case OBJECT_TOMBSTONE:
3011 /* we also mark it as recycled, meaning this object can't be
3012 recovered (we are stripping its attributes) */
3013 if (functional_level >= DS_DOMAIN_FUNCTION_2008_R2) {
3014 ret = ldb_msg_add_string(msg, "isRecycled", "TRUE");
3015 if (ret != LDB_SUCCESS) {
3016 DEBUG(0,(__location__ ": Failed to add isRecycled string to the msg\n"));
3017 ldb_module_oom(module);
3018 talloc_free(tmp_ctx);
3019 return ret;
3021 msg->elements[el_count++].flags = LDB_FLAG_MOD_ADD;
3024 /* work out which of the old attributes we will be removing */
3025 for (i=0; i<old_msg->num_elements; i++) {
3026 const struct dsdb_attribute *sa;
3027 el = &old_msg->elements[i];
3028 sa = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
3029 if (!sa) {
3030 talloc_free(tmp_ctx);
3031 return LDB_ERR_OPERATIONS_ERROR;
3033 if (ldb_attr_cmp(el->name, rdn_name) == 0) {
3034 /* don't remove the rDN */
3035 continue;
3037 if (sa->linkID && (sa->linkID & 1)) {
3039 we have a backlink in this object
3040 that needs to be removed. We're not
3041 allowed to remove it directly
3042 however, so we instead setup a
3043 modify to delete the corresponding
3044 forward link
3046 ret = replmd_delete_remove_link(module, schema, old_dn, el, sa, req);
3047 if (ret != LDB_SUCCESS) {
3048 talloc_free(tmp_ctx);
3049 return LDB_ERR_OPERATIONS_ERROR;
3051 /* now we continue, which means we
3052 won't remove this backlink
3053 directly
3055 continue;
3057 if (!sa->linkID && ldb_attr_in_list(preserved_attrs, el->name)) {
3058 continue;
3060 ret = ldb_msg_add_empty(msg, el->name, LDB_FLAG_MOD_DELETE, &el);
3061 if (ret != LDB_SUCCESS) {
3062 talloc_free(tmp_ctx);
3063 ldb_module_oom(module);
3064 return ret;
3067 break;
3069 default:
3070 break;
3073 if (deletion_state == OBJECT_NOT_DELETED) {
3074 const struct dsdb_attribute *sa;
3076 /* work out what the new rdn value is, for updating the
3077 rDN and name fields */
3078 new_rdn_value = ldb_dn_get_rdn_val(new_dn);
3079 if (new_rdn_value == NULL) {
3080 talloc_free(tmp_ctx);
3081 return ldb_operr(ldb);
3084 sa = dsdb_attribute_by_lDAPDisplayName(schema, rdn_name);
3085 if (!sa) {
3086 talloc_free(tmp_ctx);
3087 return LDB_ERR_OPERATIONS_ERROR;
3090 ret = ldb_msg_add_value(msg, sa->lDAPDisplayName, new_rdn_value,
3091 &el);
3092 if (ret != LDB_SUCCESS) {
3093 talloc_free(tmp_ctx);
3094 return ret;
3096 el->flags = LDB_FLAG_MOD_REPLACE;
3098 el = ldb_msg_find_element(old_msg, "name");
3099 if (el) {
3100 ret = ldb_msg_add_value(msg, "name", new_rdn_value, &el);
3101 if (ret != LDB_SUCCESS) {
3102 talloc_free(tmp_ctx);
3103 return ret;
3105 el->flags = LDB_FLAG_MOD_REPLACE;
3109 ret = dsdb_module_modify(module, msg, DSDB_FLAG_OWN_MODULE, req);
3110 if (ret != LDB_SUCCESS) {
3111 ldb_asprintf_errstring(ldb, "replmd_delete: Failed to modify object %s in delete - %s",
3112 ldb_dn_get_linearized(old_dn), ldb_errstring(ldb));
3113 talloc_free(tmp_ctx);
3114 return ret;
3117 if (deletion_state == OBJECT_NOT_DELETED) {
3118 /* now rename onto the new DN */
3119 ret = dsdb_module_rename(module, old_dn, new_dn, DSDB_FLAG_NEXT_MODULE, req);
3120 if (ret != LDB_SUCCESS){
3121 DEBUG(0,(__location__ ": Failed to rename object from '%s' to '%s' - %s\n",
3122 ldb_dn_get_linearized(old_dn),
3123 ldb_dn_get_linearized(new_dn),
3124 ldb_errstring(ldb)));
3125 talloc_free(tmp_ctx);
3126 return ret;
3130 talloc_free(tmp_ctx);
3132 return ldb_module_done(req, NULL, NULL, LDB_SUCCESS);
3137 static int replmd_replicated_request_error(struct replmd_replicated_request *ar, int ret)
3139 return ret;
3142 static int replmd_replicated_request_werror(struct replmd_replicated_request *ar, WERROR status)
3144 int ret = LDB_ERR_OTHER;
3145 /* TODO: do some error mapping */
3146 return ret;
3150 static struct replPropertyMetaData1 *
3151 replmd_replPropertyMetaData1_find_attid(struct replPropertyMetaDataBlob *md_blob,
3152 enum drsuapi_DsAttributeId attid)
3154 uint32_t i;
3155 struct replPropertyMetaDataCtr1 *rpmd_ctr = &md_blob->ctr.ctr1;
3157 for (i = 0; i < rpmd_ctr->count; i++) {
3158 if (rpmd_ctr->array[i].attid == attid) {
3159 return &rpmd_ctr->array[i];
3162 return NULL;
3167 return true if an update is newer than an existing entry
3168 see section 5.11 of MS-ADTS
3170 static bool replmd_update_is_newer(const struct GUID *current_invocation_id,
3171 const struct GUID *update_invocation_id,
3172 uint32_t current_version,
3173 uint32_t update_version,
3174 NTTIME current_change_time,
3175 NTTIME update_change_time)
3177 if (update_version != current_version) {
3178 return update_version > current_version;
3180 if (update_change_time != current_change_time) {
3181 return update_change_time > current_change_time;
3183 return GUID_compare(update_invocation_id, current_invocation_id) > 0;
3186 static bool replmd_replPropertyMetaData1_is_newer(struct replPropertyMetaData1 *cur_m,
3187 struct replPropertyMetaData1 *new_m)
3189 return replmd_update_is_newer(&cur_m->originating_invocation_id,
3190 &new_m->originating_invocation_id,
3191 cur_m->version,
3192 new_m->version,
3193 cur_m->originating_change_time,
3194 new_m->originating_change_time);
3199 form a conflict DN
3201 static struct ldb_dn *replmd_conflict_dn(TALLOC_CTX *mem_ctx, struct ldb_dn *dn, struct GUID *guid)
3203 const struct ldb_val *rdn_val;
3204 const char *rdn_name;
3205 struct ldb_dn *new_dn;
3207 rdn_val = ldb_dn_get_rdn_val(dn);
3208 rdn_name = ldb_dn_get_rdn_name(dn);
3209 if (!rdn_val || !rdn_name) {
3210 return NULL;
3213 new_dn = ldb_dn_copy(mem_ctx, dn);
3214 if (!new_dn) {
3215 return NULL;
3218 if (!ldb_dn_remove_child_components(new_dn, 1)) {
3219 return NULL;
3222 if (!ldb_dn_add_child_fmt(new_dn, "%s=%s\\0ACNF:%s",
3223 rdn_name,
3224 ldb_dn_escape_value(new_dn, *rdn_val),
3225 GUID_string(new_dn, guid))) {
3226 return NULL;
3229 return new_dn;
3234 perform a modify operation which sets the rDN and name attributes to
3235 their current values. This has the effect of changing these
3236 attributes to have been last updated by the current DC. This is
3237 needed to ensure that renames performed as part of conflict
3238 resolution are propogated to other DCs
3240 static int replmd_name_modify(struct replmd_replicated_request *ar,
3241 struct ldb_request *req, struct ldb_dn *dn)
3243 struct ldb_message *msg;
3244 const char *rdn_name;
3245 const struct ldb_val *rdn_val;
3246 const struct dsdb_attribute *rdn_attr;
3247 int ret;
3249 msg = ldb_msg_new(req);
3250 if (msg == NULL) {
3251 goto failed;
3253 msg->dn = dn;
3255 rdn_name = ldb_dn_get_rdn_name(dn);
3256 if (rdn_name == NULL) {
3257 goto failed;
3260 /* normalize the rdn attribute name */
3261 rdn_attr = dsdb_attribute_by_lDAPDisplayName(ar->schema, rdn_name);
3262 if (rdn_attr == NULL) {
3263 goto failed;
3265 rdn_name = rdn_attr->lDAPDisplayName;
3267 rdn_val = ldb_dn_get_rdn_val(dn);
3268 if (rdn_val == NULL) {
3269 goto failed;
3272 if (ldb_msg_add_empty(msg, rdn_name, LDB_FLAG_MOD_REPLACE, NULL) != 0) {
3273 goto failed;
3275 if (ldb_msg_add_value(msg, rdn_name, rdn_val, NULL) != 0) {
3276 goto failed;
3278 if (ldb_msg_add_empty(msg, "name", LDB_FLAG_MOD_REPLACE, NULL) != 0) {
3279 goto failed;
3281 if (ldb_msg_add_value(msg, "name", rdn_val, NULL) != 0) {
3282 goto failed;
3285 ret = dsdb_module_modify(ar->module, msg, DSDB_FLAG_OWN_MODULE, req);
3286 if (ret != LDB_SUCCESS) {
3287 DEBUG(0,(__location__ ": Failed to modify rDN/name of conflict DN '%s' - %s",
3288 ldb_dn_get_linearized(dn),
3289 ldb_errstring(ldb_module_get_ctx(ar->module))));
3290 return ret;
3293 talloc_free(msg);
3295 return LDB_SUCCESS;
3297 failed:
3298 talloc_free(msg);
3299 DEBUG(0,(__location__ ": Failed to setup modify rDN/name of conflict DN '%s'",
3300 ldb_dn_get_linearized(dn)));
3301 return LDB_ERR_OPERATIONS_ERROR;
3306 callback for conflict DN handling where we have renamed the incoming
3307 record. After renaming it, we need to ensure the change of name and
3308 rDN for the incoming record is seen as an originating update by this DC.
3310 static int replmd_op_name_modify_callback(struct ldb_request *req, struct ldb_reply *ares)
3312 struct replmd_replicated_request *ar =
3313 talloc_get_type_abort(req->context, struct replmd_replicated_request);
3314 int ret;
3316 if (ares->error != LDB_SUCCESS) {
3317 /* call the normal callback for everything except success */
3318 return replmd_op_callback(req, ares);
3321 /* perform a modify of the rDN and name of the record */
3322 ret = replmd_name_modify(ar, req, req->op.add.message->dn);
3323 if (ret != LDB_SUCCESS) {
3324 ares->error = ret;
3325 return replmd_op_callback(req, ares);
3328 return replmd_op_callback(req, ares);
3332 callback for replmd_replicated_apply_add()
3333 This copes with the creation of conflict records in the case where
3334 the DN exists, but with a different objectGUID
3336 static int replmd_op_add_callback(struct ldb_request *req, struct ldb_reply *ares)
3338 struct ldb_dn *conflict_dn;
3339 struct replmd_replicated_request *ar =
3340 talloc_get_type_abort(req->context, struct replmd_replicated_request);
3341 struct ldb_result *res;
3342 const char *attrs[] = { "replPropertyMetaData", "objectGUID", NULL };
3343 int ret;
3344 const struct ldb_val *rmd_value, *omd_value;
3345 struct replPropertyMetaDataBlob omd, rmd;
3346 enum ndr_err_code ndr_err;
3347 bool rename_incoming_record, rodc;
3348 struct replPropertyMetaData1 *rmd_name, *omd_name;
3350 if (ares->error != LDB_ERR_ENTRY_ALREADY_EXISTS) {
3351 /* call the normal callback for everything except
3352 conflicts */
3353 return replmd_op_callback(req, ares);
3356 ret = samdb_rodc(ldb_module_get_ctx(ar->module), &rodc);
3357 if (ret != LDB_SUCCESS) {
3358 return ret;
3361 * we have a conflict, and need to decide if we will keep the
3362 * new record or the old record
3364 conflict_dn = req->op.add.message->dn;
3366 if (rodc) {
3368 * We are on an RODC, or were a GC for this
3369 * partition, so we have to fail this until
3370 * someone who owns the partition sorts it
3371 * out
3373 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
3374 "Conflict adding object '%s' from incoming replication as we are read only for the partition. \n"
3375 " - We must fail the operation until a master for this partition resolves the conflict",
3376 ldb_dn_get_linearized(conflict_dn));
3377 goto failed;
3381 * we have a conflict, and need to decide if we will keep the
3382 * new record or the old record
3384 conflict_dn = req->op.add.message->dn;
3387 * first we need the replPropertyMetaData attribute from the
3388 * old record
3390 ret = dsdb_module_search_dn(ar->module, req, &res, conflict_dn,
3391 attrs,
3392 DSDB_FLAG_NEXT_MODULE |
3393 DSDB_SEARCH_SHOW_DELETED |
3394 DSDB_SEARCH_SHOW_RECYCLED, req);
3395 if (ret != LDB_SUCCESS) {
3396 DEBUG(0,(__location__ ": Unable to find object for conflicting record '%s'\n",
3397 ldb_dn_get_linearized(conflict_dn)));
3398 goto failed;
3401 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
3402 if (omd_value == NULL) {
3403 DEBUG(0,(__location__ ": Unable to find replPropertyMetaData for conflicting record '%s'\n",
3404 ldb_dn_get_linearized(conflict_dn)));
3405 goto failed;
3408 ndr_err = ndr_pull_struct_blob(omd_value, res->msgs[0], &omd,
3409 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
3410 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
3411 DEBUG(0,(__location__ ": Failed to parse old replPropertyMetaData for %s\n",
3412 ldb_dn_get_linearized(conflict_dn)));
3413 goto failed;
3417 * and the replPropertyMetaData attribute from the
3418 * new record
3420 rmd_value = ldb_msg_find_ldb_val(req->op.add.message, "replPropertyMetaData");
3421 if (rmd_value == NULL) {
3422 DEBUG(0,(__location__ ": Unable to find replPropertyMetaData for new record '%s'\n",
3423 ldb_dn_get_linearized(conflict_dn)));
3424 goto failed;
3427 ndr_err = ndr_pull_struct_blob(rmd_value, req, &rmd,
3428 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
3429 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
3430 DEBUG(0,(__location__ ": Failed to parse new replPropertyMetaData for %s\n",
3431 ldb_dn_get_linearized(conflict_dn)));
3432 goto failed;
3435 /* we decide which is newer based on the RPMD on the name
3436 attribute. See [MS-DRSR] ResolveNameConflict */
3437 rmd_name = replmd_replPropertyMetaData1_find_attid(&rmd, DRSUAPI_ATTID_name);
3438 omd_name = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
3439 if (!rmd_name || !omd_name) {
3440 DEBUG(0,(__location__ ": Failed to find name attribute in replPropertyMetaData for %s\n",
3441 ldb_dn_get_linearized(conflict_dn)));
3442 goto failed;
3445 rename_incoming_record = !replmd_replPropertyMetaData1_is_newer(omd_name, rmd_name);
3447 if (rename_incoming_record) {
3448 struct GUID guid;
3449 struct ldb_dn *new_dn;
3450 struct ldb_message *new_msg;
3452 guid = samdb_result_guid(req->op.add.message, "objectGUID");
3453 if (GUID_all_zero(&guid)) {
3454 DEBUG(0,(__location__ ": Failed to find objectGUID for conflicting incoming record %s\n",
3455 ldb_dn_get_linearized(conflict_dn)));
3456 goto failed;
3458 new_dn = replmd_conflict_dn(req, conflict_dn, &guid);
3459 if (new_dn == NULL) {
3460 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
3461 ldb_dn_get_linearized(conflict_dn)));
3462 goto failed;
3465 DEBUG(1,(__location__ ": Resolving conflict record via incoming rename '%s' -> '%s'\n",
3466 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
3468 /* re-submit the request, but with a different
3469 callback, so we don't loop forever. */
3470 new_msg = ldb_msg_copy_shallow(req, req->op.add.message);
3471 if (!new_msg) {
3472 goto failed;
3473 DEBUG(0,(__location__ ": Failed to copy conflict DN message for %s\n",
3474 ldb_dn_get_linearized(conflict_dn)));
3476 new_msg->dn = new_dn;
3477 req->op.add.message = new_msg;
3478 req->callback = replmd_op_name_modify_callback;
3480 return ldb_next_request(ar->module, req);
3481 } else {
3482 /* we are renaming the existing record */
3483 struct GUID guid;
3484 struct ldb_dn *new_dn;
3486 guid = samdb_result_guid(res->msgs[0], "objectGUID");
3487 if (GUID_all_zero(&guid)) {
3488 DEBUG(0,(__location__ ": Failed to find objectGUID for existing conflict record %s\n",
3489 ldb_dn_get_linearized(conflict_dn)));
3490 goto failed;
3493 new_dn = replmd_conflict_dn(req, conflict_dn, &guid);
3494 if (new_dn == NULL) {
3495 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
3496 ldb_dn_get_linearized(conflict_dn)));
3497 goto failed;
3500 DEBUG(1,(__location__ ": Resolving conflict record via existing rename '%s' -> '%s'\n",
3501 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
3503 ret = dsdb_module_rename(ar->module, conflict_dn, new_dn,
3504 DSDB_FLAG_OWN_MODULE, req);
3505 if (ret != LDB_SUCCESS) {
3506 DEBUG(0,(__location__ ": Failed to rename conflict dn '%s' to '%s' - %s\n",
3507 ldb_dn_get_linearized(conflict_dn),
3508 ldb_dn_get_linearized(new_dn),
3509 ldb_errstring(ldb_module_get_ctx(ar->module))));
3510 goto failed;
3514 * now we need to ensure that the rename is seen as an
3515 * originating update. We do that with a modify.
3517 ret = replmd_name_modify(ar, req, new_dn);
3518 if (ret != LDB_SUCCESS) {
3519 goto failed;
3522 req->callback = replmd_op_callback;
3524 return ldb_next_request(ar->module, req);
3527 failed:
3528 /* on failure do the original callback. This means replication
3529 * will stop with an error, but there is not much else we can
3530 * do
3532 return replmd_op_callback(req, ares);
3536 this is called when a new object comes in over DRS
3538 static int replmd_replicated_apply_add(struct replmd_replicated_request *ar)
3540 struct ldb_context *ldb;
3541 struct ldb_request *change_req;
3542 enum ndr_err_code ndr_err;
3543 struct ldb_message *msg;
3544 struct replPropertyMetaDataBlob *md;
3545 struct ldb_val md_value;
3546 unsigned int i;
3547 int ret;
3550 * TODO: check if the parent object exist
3554 * TODO: handle the conflict case where an object with the
3555 * same name exist
3558 ldb = ldb_module_get_ctx(ar->module);
3559 msg = ar->objs->objects[ar->index_current].msg;
3560 md = ar->objs->objects[ar->index_current].meta_data;
3562 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
3563 if (ret != LDB_SUCCESS) {
3564 return replmd_replicated_request_error(ar, ret);
3567 ret = ldb_msg_add_value(msg, "objectGUID", &ar->objs->objects[ar->index_current].guid_value, NULL);
3568 if (ret != LDB_SUCCESS) {
3569 return replmd_replicated_request_error(ar, ret);
3572 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
3573 if (ret != LDB_SUCCESS) {
3574 return replmd_replicated_request_error(ar, ret);
3577 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", ar->seq_num);
3578 if (ret != LDB_SUCCESS) {
3579 return replmd_replicated_request_error(ar, ret);
3582 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
3583 if (ret != LDB_SUCCESS) {
3584 return replmd_replicated_request_error(ar, ret);
3587 /* remove any message elements that have zero values */
3588 for (i=0; i<msg->num_elements; i++) {
3589 struct ldb_message_element *el = &msg->elements[i];
3591 if (el->num_values == 0) {
3592 DEBUG(4,(__location__ ": Removing attribute %s with num_values==0\n",
3593 el->name));
3594 memmove(el, el+1, sizeof(*el)*(msg->num_elements - (i+1)));
3595 msg->num_elements--;
3596 i--;
3597 continue;
3602 * the meta data array is already sorted by the caller
3604 for (i=0; i < md->ctr.ctr1.count; i++) {
3605 md->ctr.ctr1.array[i].local_usn = ar->seq_num;
3607 ndr_err = ndr_push_struct_blob(&md_value, msg, md,
3608 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
3609 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
3610 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
3611 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
3613 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &md_value, NULL);
3614 if (ret != LDB_SUCCESS) {
3615 return replmd_replicated_request_error(ar, ret);
3618 replmd_ldb_message_sort(msg, ar->schema);
3620 if (DEBUGLVL(4)) {
3621 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_ADD, msg);
3622 DEBUG(4, ("DRS replication add message:\n%s\n", s));
3623 talloc_free(s);
3626 ret = ldb_build_add_req(&change_req,
3627 ldb,
3629 msg,
3630 ar->controls,
3632 replmd_op_add_callback,
3633 ar->req);
3634 LDB_REQ_SET_LOCATION(change_req);
3635 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
3637 /* current partition control needed by "repmd_op_callback" */
3638 ret = ldb_request_add_control(change_req,
3639 DSDB_CONTROL_CURRENT_PARTITION_OID,
3640 false, NULL);
3641 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
3643 if (ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PARTIAL_REPLICA) {
3644 /* this tells the partition module to make it a
3645 partial replica if creating an NC */
3646 ret = ldb_request_add_control(change_req,
3647 DSDB_CONTROL_PARTIAL_REPLICA,
3648 false, NULL);
3649 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
3652 return ldb_next_request(ar->module, change_req);
3656 handle renames that come in over DRS replication
3658 static int replmd_replicated_handle_rename(struct replmd_replicated_request *ar,
3659 struct ldb_message *msg,
3660 struct replPropertyMetaDataBlob *rmd,
3661 struct replPropertyMetaDataBlob *omd,
3662 struct ldb_request *parent)
3664 struct replPropertyMetaData1 *md_remote;
3665 struct replPropertyMetaData1 *md_local;
3667 if (ldb_dn_compare(msg->dn, ar->search_msg->dn) == 0) {
3668 /* no rename */
3669 return LDB_SUCCESS;
3672 /* now we need to check for double renames. We could have a
3673 * local rename pending which our replication partner hasn't
3674 * received yet. We choose which one wins by looking at the
3675 * attribute stamps on the two objects, the newer one wins
3677 md_remote = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
3678 md_local = replmd_replPropertyMetaData1_find_attid(omd, DRSUAPI_ATTID_name);
3679 /* if there is no name attribute then we have to assume the
3680 object we've received is in fact newer */
3681 if (!md_remote || !md_local ||
3682 replmd_replPropertyMetaData1_is_newer(md_local, md_remote)) {
3683 DEBUG(4,("replmd_replicated_request rename %s => %s\n",
3684 ldb_dn_get_linearized(ar->search_msg->dn),
3685 ldb_dn_get_linearized(msg->dn)));
3686 /* pass rename to the next module
3687 * so it doesn't appear as an originating update */
3688 return dsdb_module_rename(ar->module,
3689 ar->search_msg->dn, msg->dn,
3690 DSDB_FLAG_NEXT_MODULE | DSDB_MODIFY_RELAX, parent);
3693 /* we're going to keep our old object */
3694 DEBUG(4,(__location__ ": Keeping object %s and rejecting older rename to %s\n",
3695 ldb_dn_get_linearized(ar->search_msg->dn),
3696 ldb_dn_get_linearized(msg->dn)));
3697 return LDB_SUCCESS;
3701 static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar)
3703 struct ldb_context *ldb;
3704 struct ldb_request *change_req;
3705 enum ndr_err_code ndr_err;
3706 struct ldb_message *msg;
3707 struct replPropertyMetaDataBlob *rmd;
3708 struct replPropertyMetaDataBlob omd;
3709 const struct ldb_val *omd_value;
3710 struct replPropertyMetaDataBlob nmd;
3711 struct ldb_val nmd_value;
3712 unsigned int i;
3713 uint32_t j,ni=0;
3714 unsigned int removed_attrs = 0;
3715 int ret;
3717 ldb = ldb_module_get_ctx(ar->module);
3718 msg = ar->objs->objects[ar->index_current].msg;
3719 rmd = ar->objs->objects[ar->index_current].meta_data;
3720 ZERO_STRUCT(omd);
3721 omd.version = 1;
3723 /* find existing meta data */
3724 omd_value = ldb_msg_find_ldb_val(ar->search_msg, "replPropertyMetaData");
3725 if (omd_value) {
3726 ndr_err = ndr_pull_struct_blob(omd_value, ar, &omd,
3727 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
3728 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
3729 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
3730 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
3733 if (omd.version != 1) {
3734 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
3738 /* handle renames that come in over DRS */
3739 ret = replmd_replicated_handle_rename(ar, msg, rmd, &omd, ar->req);
3740 if (ret != LDB_SUCCESS) {
3741 ldb_debug(ldb, LDB_DEBUG_FATAL,
3742 "replmd_replicated_request rename %s => %s failed - %s\n",
3743 ldb_dn_get_linearized(ar->search_msg->dn),
3744 ldb_dn_get_linearized(msg->dn),
3745 ldb_errstring(ldb));
3746 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
3749 ZERO_STRUCT(nmd);
3750 nmd.version = 1;
3751 nmd.ctr.ctr1.count = omd.ctr.ctr1.count + rmd->ctr.ctr1.count;
3752 nmd.ctr.ctr1.array = talloc_array(ar,
3753 struct replPropertyMetaData1,
3754 nmd.ctr.ctr1.count);
3755 if (!nmd.ctr.ctr1.array) return replmd_replicated_request_werror(ar, WERR_NOMEM);
3757 /* first copy the old meta data */
3758 for (i=0; i < omd.ctr.ctr1.count; i++) {
3759 nmd.ctr.ctr1.array[ni] = omd.ctr.ctr1.array[i];
3760 ni++;
3763 ar->seq_num = 0;
3764 /* now merge in the new meta data */
3765 for (i=0; i < rmd->ctr.ctr1.count; i++) {
3766 bool found = false;
3768 for (j=0; j < ni; j++) {
3769 bool cmp;
3771 if (rmd->ctr.ctr1.array[i].attid != nmd.ctr.ctr1.array[j].attid) {
3772 continue;
3775 if (ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING) {
3776 /* if we compare equal then do an
3777 update. This is used when a client
3778 asks for a FULL_SYNC, and can be
3779 used to recover a corrupt
3780 replica */
3781 cmp = !replmd_replPropertyMetaData1_is_newer(&rmd->ctr.ctr1.array[i],
3782 &nmd.ctr.ctr1.array[j]);
3783 } else {
3784 cmp = replmd_replPropertyMetaData1_is_newer(&nmd.ctr.ctr1.array[j],
3785 &rmd->ctr.ctr1.array[i]);
3787 if (cmp) {
3788 /* replace the entry */
3789 nmd.ctr.ctr1.array[j] = rmd->ctr.ctr1.array[i];
3790 if (ar->seq_num == 0) {
3791 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
3792 if (ret != LDB_SUCCESS) {
3793 return replmd_replicated_request_error(ar, ret);
3796 nmd.ctr.ctr1.array[j].local_usn = ar->seq_num;
3797 found = true;
3798 break;
3801 if (rmd->ctr.ctr1.array[i].attid != DRSUAPI_ATTID_instanceType) {
3802 DEBUG(3,("Discarding older DRS attribute update to %s on %s from %s\n",
3803 msg->elements[i-removed_attrs].name,
3804 ldb_dn_get_linearized(msg->dn),
3805 GUID_string(ar, &rmd->ctr.ctr1.array[i].originating_invocation_id)));
3808 /* we don't want to apply this change so remove the attribute */
3809 ldb_msg_remove_element(msg, &msg->elements[i-removed_attrs]);
3810 removed_attrs++;
3812 found = true;
3813 break;
3816 if (found) continue;
3818 nmd.ctr.ctr1.array[ni] = rmd->ctr.ctr1.array[i];
3819 if (ar->seq_num == 0) {
3820 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
3821 if (ret != LDB_SUCCESS) {
3822 return replmd_replicated_request_error(ar, ret);
3825 nmd.ctr.ctr1.array[ni].local_usn = ar->seq_num;
3826 ni++;
3830 * finally correct the size of the meta_data array
3832 nmd.ctr.ctr1.count = ni;
3835 * the rdn attribute (the alias for the name attribute),
3836 * 'cn' for most objects is the last entry in the meta data array
3837 * we have stored
3839 * sort the new meta data array
3841 ret = replmd_replPropertyMetaDataCtr1_sort(&nmd.ctr.ctr1, ar->schema, msg->dn);
3842 if (ret != LDB_SUCCESS) {
3843 return ret;
3847 * check if some replicated attributes left, otherwise skip the ldb_modify() call
3849 if (msg->num_elements == 0) {
3850 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: skip replace\n",
3851 ar->index_current);
3853 ar->index_current++;
3854 return replmd_replicated_apply_next(ar);
3857 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: replace %u attributes\n",
3858 ar->index_current, msg->num_elements);
3860 /* create the meta data value */
3861 ndr_err = ndr_push_struct_blob(&nmd_value, msg, &nmd,
3862 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
3863 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
3864 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
3865 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
3869 * when we know that we'll modify the record, add the whenChanged, uSNChanged
3870 * and replPopertyMetaData attributes
3872 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
3873 if (ret != LDB_SUCCESS) {
3874 return replmd_replicated_request_error(ar, ret);
3876 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
3877 if (ret != LDB_SUCCESS) {
3878 return replmd_replicated_request_error(ar, ret);
3880 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
3881 if (ret != LDB_SUCCESS) {
3882 return replmd_replicated_request_error(ar, ret);
3885 replmd_ldb_message_sort(msg, ar->schema);
3887 /* we want to replace the old values */
3888 for (i=0; i < msg->num_elements; i++) {
3889 msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
3892 if (DEBUGLVL(4)) {
3893 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_MODIFY, msg);
3894 DEBUG(4, ("DRS replication modify message:\n%s\n", s));
3895 talloc_free(s);
3898 ret = ldb_build_mod_req(&change_req,
3899 ldb,
3901 msg,
3902 ar->controls,
3904 replmd_op_callback,
3905 ar->req);
3906 LDB_REQ_SET_LOCATION(change_req);
3907 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
3909 /* current partition control needed by "repmd_op_callback" */
3910 ret = ldb_request_add_control(change_req,
3911 DSDB_CONTROL_CURRENT_PARTITION_OID,
3912 false, NULL);
3913 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
3915 return ldb_next_request(ar->module, change_req);
3918 static int replmd_replicated_apply_search_callback(struct ldb_request *req,
3919 struct ldb_reply *ares)
3921 struct replmd_replicated_request *ar = talloc_get_type(req->context,
3922 struct replmd_replicated_request);
3923 int ret;
3925 if (!ares) {
3926 return ldb_module_done(ar->req, NULL, NULL,
3927 LDB_ERR_OPERATIONS_ERROR);
3929 if (ares->error != LDB_SUCCESS &&
3930 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
3931 return ldb_module_done(ar->req, ares->controls,
3932 ares->response, ares->error);
3935 switch (ares->type) {
3936 case LDB_REPLY_ENTRY:
3937 ar->search_msg = talloc_steal(ar, ares->message);
3938 break;
3940 case LDB_REPLY_REFERRAL:
3941 /* we ignore referrals */
3942 break;
3944 case LDB_REPLY_DONE:
3945 if (ar->search_msg != NULL) {
3946 ret = replmd_replicated_apply_merge(ar);
3947 } else {
3948 ret = replmd_replicated_apply_add(ar);
3950 if (ret != LDB_SUCCESS) {
3951 return ldb_module_done(ar->req, NULL, NULL, ret);
3955 talloc_free(ares);
3956 return LDB_SUCCESS;
3959 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar);
3961 static int replmd_replicated_apply_next(struct replmd_replicated_request *ar)
3963 struct ldb_context *ldb;
3964 int ret;
3965 char *tmp_str;
3966 char *filter;
3967 struct ldb_request *search_req;
3968 struct ldb_search_options_control *options;
3970 if (ar->index_current >= ar->objs->num_objects) {
3971 /* done with it, go to next stage */
3972 return replmd_replicated_uptodate_vector(ar);
3975 ldb = ldb_module_get_ctx(ar->module);
3976 ar->search_msg = NULL;
3978 tmp_str = ldb_binary_encode(ar, ar->objs->objects[ar->index_current].guid_value);
3979 if (!tmp_str) return replmd_replicated_request_werror(ar, WERR_NOMEM);
3981 filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
3982 if (!filter) return replmd_replicated_request_werror(ar, WERR_NOMEM);
3983 talloc_free(tmp_str);
3985 ret = ldb_build_search_req(&search_req,
3986 ldb,
3988 NULL,
3989 LDB_SCOPE_SUBTREE,
3990 filter,
3991 NULL,
3992 NULL,
3994 replmd_replicated_apply_search_callback,
3995 ar->req);
3996 LDB_REQ_SET_LOCATION(search_req);
3998 ret = ldb_request_add_control(search_req, LDB_CONTROL_SHOW_RECYCLED_OID,
3999 true, NULL);
4000 if (ret != LDB_SUCCESS) {
4001 return ret;
4004 /* we need to cope with cross-partition links, so search for
4005 the GUID over all partitions */
4006 options = talloc(search_req, struct ldb_search_options_control);
4007 if (options == NULL) {
4008 DEBUG(0, (__location__ ": out of memory\n"));
4009 return LDB_ERR_OPERATIONS_ERROR;
4011 options->search_options = LDB_SEARCH_OPTION_PHANTOM_ROOT;
4013 ret = ldb_request_add_control(search_req,
4014 LDB_CONTROL_SEARCH_OPTIONS_OID,
4015 true, options);
4016 if (ret != LDB_SUCCESS) {
4017 return ret;
4020 return ldb_next_request(ar->module, search_req);
4023 static int replmd_replicated_uptodate_modify_callback(struct ldb_request *req,
4024 struct ldb_reply *ares)
4026 struct ldb_context *ldb;
4027 struct replmd_replicated_request *ar = talloc_get_type(req->context,
4028 struct replmd_replicated_request);
4029 ldb = ldb_module_get_ctx(ar->module);
4031 if (!ares) {
4032 return ldb_module_done(ar->req, NULL, NULL,
4033 LDB_ERR_OPERATIONS_ERROR);
4035 if (ares->error != LDB_SUCCESS) {
4036 return ldb_module_done(ar->req, ares->controls,
4037 ares->response, ares->error);
4040 if (ares->type != LDB_REPLY_DONE) {
4041 ldb_asprintf_errstring(ldb, "Invalid LDB reply type %d", ares->type);
4042 return ldb_module_done(ar->req, NULL, NULL,
4043 LDB_ERR_OPERATIONS_ERROR);
4046 talloc_free(ares);
4048 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
4051 static int replmd_replicated_uptodate_modify(struct replmd_replicated_request *ar)
4053 struct ldb_context *ldb;
4054 struct ldb_request *change_req;
4055 enum ndr_err_code ndr_err;
4056 struct ldb_message *msg;
4057 struct replUpToDateVectorBlob ouv;
4058 const struct ldb_val *ouv_value;
4059 const struct drsuapi_DsReplicaCursor2CtrEx *ruv;
4060 struct replUpToDateVectorBlob nuv;
4061 struct ldb_val nuv_value;
4062 struct ldb_message_element *nuv_el = NULL;
4063 const struct GUID *our_invocation_id;
4064 struct ldb_message_element *orf_el = NULL;
4065 struct repsFromToBlob nrf;
4066 struct ldb_val *nrf_value = NULL;
4067 struct ldb_message_element *nrf_el = NULL;
4068 unsigned int i;
4069 uint32_t j,ni=0;
4070 bool found = false;
4071 time_t t = time(NULL);
4072 NTTIME now;
4073 int ret;
4074 uint32_t instanceType;
4076 ldb = ldb_module_get_ctx(ar->module);
4077 ruv = ar->objs->uptodateness_vector;
4078 ZERO_STRUCT(ouv);
4079 ouv.version = 2;
4080 ZERO_STRUCT(nuv);
4081 nuv.version = 2;
4083 unix_to_nt_time(&now, t);
4085 if (ar->search_msg == NULL) {
4086 /* this happens for a REPL_OBJ call where we are
4087 creating the target object by replicating it. The
4088 subdomain join code does this for the partition DN
4090 DEBUG(4,(__location__ ": Skipping UDV and repsFrom update as no target DN\n"));
4091 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
4094 instanceType = ldb_msg_find_attr_as_uint(ar->search_msg, "instanceType", 0);
4095 if (! (instanceType & INSTANCE_TYPE_IS_NC_HEAD)) {
4096 DEBUG(4,(__location__ ": Skipping UDV and repsFrom update as not NC root: %s\n",
4097 ldb_dn_get_linearized(ar->search_msg->dn)));
4098 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
4102 * first create the new replUpToDateVector
4104 ouv_value = ldb_msg_find_ldb_val(ar->search_msg, "replUpToDateVector");
4105 if (ouv_value) {
4106 ndr_err = ndr_pull_struct_blob(ouv_value, ar, &ouv,
4107 (ndr_pull_flags_fn_t)ndr_pull_replUpToDateVectorBlob);
4108 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
4109 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
4110 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
4113 if (ouv.version != 2) {
4114 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
4119 * the new uptodateness vector will at least
4120 * contain 1 entry, one for the source_dsa
4122 * plus optional values from our old vector and the one from the source_dsa
4124 nuv.ctr.ctr2.count = 1 + ouv.ctr.ctr2.count;
4125 if (ruv) nuv.ctr.ctr2.count += ruv->count;
4126 nuv.ctr.ctr2.cursors = talloc_array(ar,
4127 struct drsuapi_DsReplicaCursor2,
4128 nuv.ctr.ctr2.count);
4129 if (!nuv.ctr.ctr2.cursors) return replmd_replicated_request_werror(ar, WERR_NOMEM);
4131 /* first copy the old vector */
4132 for (i=0; i < ouv.ctr.ctr2.count; i++) {
4133 nuv.ctr.ctr2.cursors[ni] = ouv.ctr.ctr2.cursors[i];
4134 ni++;
4137 /* get our invocation_id if we have one already attached to the ldb */
4138 our_invocation_id = samdb_ntds_invocation_id(ldb);
4140 /* merge in the source_dsa vector is available */
4141 for (i=0; (ruv && i < ruv->count); i++) {
4142 found = false;
4144 if (our_invocation_id &&
4145 GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
4146 our_invocation_id)) {
4147 continue;
4150 for (j=0; j < ni; j++) {
4151 if (!GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
4152 &nuv.ctr.ctr2.cursors[j].source_dsa_invocation_id)) {
4153 continue;
4156 found = true;
4159 * we update only the highest_usn and not the latest_sync_success time,
4160 * because the last success stands for direct replication
4162 if (ruv->cursors[i].highest_usn > nuv.ctr.ctr2.cursors[j].highest_usn) {
4163 nuv.ctr.ctr2.cursors[j].highest_usn = ruv->cursors[i].highest_usn;
4165 break;
4168 if (found) continue;
4170 /* if it's not there yet, add it */
4171 nuv.ctr.ctr2.cursors[ni] = ruv->cursors[i];
4172 ni++;
4176 * merge in the current highwatermark for the source_dsa
4178 found = false;
4179 for (j=0; j < ni; j++) {
4180 if (!GUID_equal(&ar->objs->source_dsa->source_dsa_invocation_id,
4181 &nuv.ctr.ctr2.cursors[j].source_dsa_invocation_id)) {
4182 continue;
4185 found = true;
4188 * here we update the highest_usn and last_sync_success time
4189 * because we're directly replicating from the source_dsa
4191 * and use the tmp_highest_usn because this is what we have just applied
4192 * to our ldb
4194 nuv.ctr.ctr2.cursors[j].highest_usn = ar->objs->source_dsa->highwatermark.tmp_highest_usn;
4195 nuv.ctr.ctr2.cursors[j].last_sync_success = now;
4196 break;
4198 if (!found) {
4200 * here we update the highest_usn and last_sync_success time
4201 * because we're directly replicating from the source_dsa
4203 * and use the tmp_highest_usn because this is what we have just applied
4204 * to our ldb
4206 nuv.ctr.ctr2.cursors[ni].source_dsa_invocation_id= ar->objs->source_dsa->source_dsa_invocation_id;
4207 nuv.ctr.ctr2.cursors[ni].highest_usn = ar->objs->source_dsa->highwatermark.tmp_highest_usn;
4208 nuv.ctr.ctr2.cursors[ni].last_sync_success = now;
4209 ni++;
4213 * finally correct the size of the cursors array
4215 nuv.ctr.ctr2.count = ni;
4218 * sort the cursors
4220 TYPESAFE_QSORT(nuv.ctr.ctr2.cursors, nuv.ctr.ctr2.count, drsuapi_DsReplicaCursor2_compare);
4223 * create the change ldb_message
4225 msg = ldb_msg_new(ar);
4226 if (!msg) return replmd_replicated_request_werror(ar, WERR_NOMEM);
4227 msg->dn = ar->search_msg->dn;
4229 ndr_err = ndr_push_struct_blob(&nuv_value, msg, &nuv,
4230 (ndr_push_flags_fn_t)ndr_push_replUpToDateVectorBlob);
4231 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
4232 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
4233 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
4235 ret = ldb_msg_add_value(msg, "replUpToDateVector", &nuv_value, &nuv_el);
4236 if (ret != LDB_SUCCESS) {
4237 return replmd_replicated_request_error(ar, ret);
4239 nuv_el->flags = LDB_FLAG_MOD_REPLACE;
4242 * now create the new repsFrom value from the given repsFromTo1 structure
4244 ZERO_STRUCT(nrf);
4245 nrf.version = 1;
4246 nrf.ctr.ctr1 = *ar->objs->source_dsa;
4247 nrf.ctr.ctr1.highwatermark.highest_usn = nrf.ctr.ctr1.highwatermark.tmp_highest_usn;
4250 * first see if we already have a repsFrom value for the current source dsa
4251 * if so we'll later replace this value
4253 orf_el = ldb_msg_find_element(ar->search_msg, "repsFrom");
4254 if (orf_el) {
4255 for (i=0; i < orf_el->num_values; i++) {
4256 struct repsFromToBlob *trf;
4258 trf = talloc(ar, struct repsFromToBlob);
4259 if (!trf) return replmd_replicated_request_werror(ar, WERR_NOMEM);
4261 ndr_err = ndr_pull_struct_blob(&orf_el->values[i], trf, trf,
4262 (ndr_pull_flags_fn_t)ndr_pull_repsFromToBlob);
4263 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
4264 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
4265 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
4268 if (trf->version != 1) {
4269 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
4273 * we compare the source dsa objectGUID not the invocation_id
4274 * because we want only one repsFrom value per source dsa
4275 * and when the invocation_id of the source dsa has changed we don't need
4276 * the old repsFrom with the old invocation_id
4278 if (!GUID_equal(&trf->ctr.ctr1.source_dsa_obj_guid,
4279 &ar->objs->source_dsa->source_dsa_obj_guid)) {
4280 talloc_free(trf);
4281 continue;
4284 talloc_free(trf);
4285 nrf_value = &orf_el->values[i];
4286 break;
4290 * copy over all old values to the new ldb_message
4292 ret = ldb_msg_add_empty(msg, "repsFrom", 0, &nrf_el);
4293 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
4294 *nrf_el = *orf_el;
4298 * if we haven't found an old repsFrom value for the current source dsa
4299 * we'll add a new value
4301 if (!nrf_value) {
4302 struct ldb_val zero_value;
4303 ZERO_STRUCT(zero_value);
4304 ret = ldb_msg_add_value(msg, "repsFrom", &zero_value, &nrf_el);
4305 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
4307 nrf_value = &nrf_el->values[nrf_el->num_values - 1];
4310 /* we now fill the value which is already attached to ldb_message */
4311 ndr_err = ndr_push_struct_blob(nrf_value, msg,
4312 &nrf,
4313 (ndr_push_flags_fn_t)ndr_push_repsFromToBlob);
4314 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
4315 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
4316 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
4320 * the ldb_message_element for the attribute, has all the old values and the new one
4321 * so we'll replace the whole attribute with all values
4323 nrf_el->flags = LDB_FLAG_MOD_REPLACE;
4325 if (CHECK_DEBUGLVL(4)) {
4326 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_MODIFY, msg);
4327 DEBUG(4, ("DRS replication uptodate modify message:\n%s\n", s));
4328 talloc_free(s);
4331 /* prepare the ldb_modify() request */
4332 ret = ldb_build_mod_req(&change_req,
4333 ldb,
4335 msg,
4336 ar->controls,
4338 replmd_replicated_uptodate_modify_callback,
4339 ar->req);
4340 LDB_REQ_SET_LOCATION(change_req);
4341 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
4343 return ldb_next_request(ar->module, change_req);
4346 static int replmd_replicated_uptodate_search_callback(struct ldb_request *req,
4347 struct ldb_reply *ares)
4349 struct replmd_replicated_request *ar = talloc_get_type(req->context,
4350 struct replmd_replicated_request);
4351 int ret;
4353 if (!ares) {
4354 return ldb_module_done(ar->req, NULL, NULL,
4355 LDB_ERR_OPERATIONS_ERROR);
4357 if (ares->error != LDB_SUCCESS &&
4358 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
4359 return ldb_module_done(ar->req, ares->controls,
4360 ares->response, ares->error);
4363 switch (ares->type) {
4364 case LDB_REPLY_ENTRY:
4365 ar->search_msg = talloc_steal(ar, ares->message);
4366 break;
4368 case LDB_REPLY_REFERRAL:
4369 /* we ignore referrals */
4370 break;
4372 case LDB_REPLY_DONE:
4373 ret = replmd_replicated_uptodate_modify(ar);
4374 if (ret != LDB_SUCCESS) {
4375 return ldb_module_done(ar->req, NULL, NULL, ret);
4379 talloc_free(ares);
4380 return LDB_SUCCESS;
4384 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar)
4386 struct ldb_context *ldb;
4387 int ret;
4388 static const char *attrs[] = {
4389 "replUpToDateVector",
4390 "repsFrom",
4391 "instanceType",
4392 NULL
4394 struct ldb_request *search_req;
4396 ldb = ldb_module_get_ctx(ar->module);
4397 ar->search_msg = NULL;
4399 ret = ldb_build_search_req(&search_req,
4400 ldb,
4402 ar->objs->partition_dn,
4403 LDB_SCOPE_BASE,
4404 "(objectClass=*)",
4405 attrs,
4406 NULL,
4408 replmd_replicated_uptodate_search_callback,
4409 ar->req);
4410 LDB_REQ_SET_LOCATION(search_req);
4411 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
4413 return ldb_next_request(ar->module, search_req);
4418 static int replmd_extended_replicated_objects(struct ldb_module *module, struct ldb_request *req)
4420 struct ldb_context *ldb;
4421 struct dsdb_extended_replicated_objects *objs;
4422 struct replmd_replicated_request *ar;
4423 struct ldb_control **ctrls;
4424 int ret;
4425 uint32_t i;
4426 struct replmd_private *replmd_private =
4427 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
4428 struct dsdb_control_replicated_update *rep_update;
4430 ldb = ldb_module_get_ctx(module);
4432 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_extended_replicated_objects\n");
4434 objs = talloc_get_type(req->op.extended.data, struct dsdb_extended_replicated_objects);
4435 if (!objs) {
4436 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: invalid extended data\n");
4437 return LDB_ERR_PROTOCOL_ERROR;
4440 if (objs->version != DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION) {
4441 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: extended data invalid version [%u != %u]\n",
4442 objs->version, DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION);
4443 return LDB_ERR_PROTOCOL_ERROR;
4446 ar = replmd_ctx_init(module, req);
4447 if (!ar)
4448 return LDB_ERR_OPERATIONS_ERROR;
4450 /* Set the flags to have the replmd_op_callback run over the full set of objects */
4451 ar->apply_mode = true;
4452 ar->objs = objs;
4453 ar->schema = dsdb_get_schema(ldb, ar);
4454 if (!ar->schema) {
4455 ldb_debug_set(ldb, LDB_DEBUG_FATAL, "replmd_ctx_init: no loaded schema found\n");
4456 talloc_free(ar);
4457 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
4458 return LDB_ERR_CONSTRAINT_VIOLATION;
4461 ctrls = req->controls;
4463 if (req->controls) {
4464 req->controls = talloc_memdup(ar, req->controls,
4465 talloc_get_size(req->controls));
4466 if (!req->controls) return replmd_replicated_request_werror(ar, WERR_NOMEM);
4469 /* This allows layers further down to know if a change came in
4470 over replication and what the replication flags were */
4471 rep_update = talloc_zero(ar, struct dsdb_control_replicated_update);
4472 if (rep_update == NULL) {
4473 return ldb_module_oom(module);
4475 rep_update->dsdb_repl_flags = objs->dsdb_repl_flags;
4477 ret = ldb_request_add_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID, false, rep_update);
4478 if (ret != LDB_SUCCESS) {
4479 return ret;
4482 /* If this change contained linked attributes in the body
4483 * (rather than in the links section) we need to update
4484 * backlinks in linked_attributes */
4485 ret = ldb_request_add_control(req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
4486 if (ret != LDB_SUCCESS) {
4487 return ret;
4490 ar->controls = req->controls;
4491 req->controls = ctrls;
4493 DEBUG(4,("linked_attributes_count=%u\n", objs->linked_attributes_count));
4495 /* save away the linked attributes for the end of the
4496 transaction */
4497 for (i=0; i<ar->objs->linked_attributes_count; i++) {
4498 struct la_entry *la_entry;
4500 if (replmd_private->la_ctx == NULL) {
4501 replmd_private->la_ctx = talloc_new(replmd_private);
4503 la_entry = talloc(replmd_private->la_ctx, struct la_entry);
4504 if (la_entry == NULL) {
4505 ldb_oom(ldb);
4506 return LDB_ERR_OPERATIONS_ERROR;
4508 la_entry->la = talloc(la_entry, struct drsuapi_DsReplicaLinkedAttribute);
4509 if (la_entry->la == NULL) {
4510 talloc_free(la_entry);
4511 ldb_oom(ldb);
4512 return LDB_ERR_OPERATIONS_ERROR;
4514 *la_entry->la = ar->objs->linked_attributes[i];
4516 /* we need to steal the non-scalars so they stay
4517 around until the end of the transaction */
4518 talloc_steal(la_entry->la, la_entry->la->identifier);
4519 talloc_steal(la_entry->la, la_entry->la->value.blob);
4521 DLIST_ADD(replmd_private->la_list, la_entry);
4524 return replmd_replicated_apply_next(ar);
4528 process one linked attribute structure
4530 static int replmd_process_linked_attribute(struct ldb_module *module,
4531 struct la_entry *la_entry,
4532 struct ldb_request *parent)
4534 struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
4535 struct ldb_context *ldb = ldb_module_get_ctx(module);
4536 struct ldb_message *msg;
4537 TALLOC_CTX *tmp_ctx = talloc_new(la_entry);
4538 const struct dsdb_schema *schema = dsdb_get_schema(ldb, tmp_ctx);
4539 int ret;
4540 const struct dsdb_attribute *attr;
4541 struct dsdb_dn *dsdb_dn;
4542 uint64_t seq_num = 0;
4543 struct ldb_message_element *old_el;
4544 WERROR status;
4545 time_t t = time(NULL);
4546 struct ldb_result *res;
4547 const char *attrs[2];
4548 struct parsed_dn *pdn_list, *pdn;
4549 struct GUID guid = GUID_zero();
4550 NTSTATUS ntstatus;
4551 bool active = (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)?true:false;
4552 const struct GUID *our_invocation_id;
4555 linked_attributes[0]:
4556 &objs->linked_attributes[i]: struct drsuapi_DsReplicaLinkedAttribute
4557 identifier : *
4558 identifier: struct drsuapi_DsReplicaObjectIdentifier
4559 __ndr_size : 0x0000003a (58)
4560 __ndr_size_sid : 0x00000000 (0)
4561 guid : 8e95b6a9-13dd-4158-89db-3220a5be5cc7
4562 sid : S-0-0
4563 __ndr_size_dn : 0x00000000 (0)
4564 dn : ''
4565 attid : DRSUAPI_ATTID_member (0x1F)
4566 value: struct drsuapi_DsAttributeValue
4567 __ndr_size : 0x0000007e (126)
4568 blob : *
4569 blob : DATA_BLOB length=126
4570 flags : 0x00000001 (1)
4571 1: DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE
4572 originating_add_time : Wed Sep 2 22:20:01 2009 EST
4573 meta_data: struct drsuapi_DsReplicaMetaData
4574 version : 0x00000015 (21)
4575 originating_change_time : Wed Sep 2 23:39:07 2009 EST
4576 originating_invocation_id: 794640f3-18cf-40ee-a211-a93992b67a64
4577 originating_usn : 0x000000000001e19c (123292)
4579 (for cases where the link is to a normal DN)
4580 &target: struct drsuapi_DsReplicaObjectIdentifier3
4581 __ndr_size : 0x0000007e (126)
4582 __ndr_size_sid : 0x0000001c (28)
4583 guid : 7639e594-db75-4086-b0d4-67890ae46031
4584 sid : S-1-5-21-2848215498-2472035911-1947525656-19924
4585 __ndr_size_dn : 0x00000022 (34)
4586 dn : 'CN=UOne,OU=TestOU,DC=vsofs8,DC=com'
4589 /* find the attribute being modified */
4590 attr = dsdb_attribute_by_attributeID_id(schema, la->attid);
4591 if (attr == NULL) {
4592 DEBUG(0, (__location__ ": Unable to find attributeID 0x%x\n", la->attid));
4593 talloc_free(tmp_ctx);
4594 return LDB_ERR_OPERATIONS_ERROR;
4597 attrs[0] = attr->lDAPDisplayName;
4598 attrs[1] = NULL;
4600 /* get the existing message from the db for the object with
4601 this GUID, returning attribute being modified. We will then
4602 use this msg as the basis for a modify call */
4603 ret = dsdb_module_search(module, tmp_ctx, &res, NULL, LDB_SCOPE_SUBTREE, attrs,
4604 DSDB_FLAG_NEXT_MODULE |
4605 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
4606 DSDB_SEARCH_SHOW_RECYCLED |
4607 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
4608 DSDB_SEARCH_REVEAL_INTERNALS,
4609 parent,
4610 "objectGUID=%s", GUID_string(tmp_ctx, &la->identifier->guid));
4611 if (ret != LDB_SUCCESS) {
4612 talloc_free(tmp_ctx);
4613 return ret;
4615 if (res->count != 1) {
4616 ldb_asprintf_errstring(ldb, "DRS linked attribute for GUID %s - DN not found",
4617 GUID_string(tmp_ctx, &la->identifier->guid));
4618 talloc_free(tmp_ctx);
4619 return LDB_ERR_NO_SUCH_OBJECT;
4621 msg = res->msgs[0];
4623 if (msg->num_elements == 0) {
4624 ret = ldb_msg_add_empty(msg, attr->lDAPDisplayName, LDB_FLAG_MOD_REPLACE, &old_el);
4625 if (ret != LDB_SUCCESS) {
4626 ldb_module_oom(module);
4627 talloc_free(tmp_ctx);
4628 return LDB_ERR_OPERATIONS_ERROR;
4630 } else {
4631 old_el = &msg->elements[0];
4632 old_el->flags = LDB_FLAG_MOD_REPLACE;
4635 /* parse the existing links */
4636 ret = get_parsed_dns(module, tmp_ctx, old_el, &pdn_list, attr->syntax->ldap_oid, parent);
4637 if (ret != LDB_SUCCESS) {
4638 talloc_free(tmp_ctx);
4639 return ret;
4642 /* get our invocationId */
4643 our_invocation_id = samdb_ntds_invocation_id(ldb);
4644 if (!our_invocation_id) {
4645 ldb_debug_set(ldb, LDB_DEBUG_ERROR, __location__ ": unable to find invocationId\n");
4646 talloc_free(tmp_ctx);
4647 return LDB_ERR_OPERATIONS_ERROR;
4650 ret = replmd_check_upgrade_links(pdn_list, old_el->num_values, old_el, our_invocation_id);
4651 if (ret != LDB_SUCCESS) {
4652 talloc_free(tmp_ctx);
4653 return ret;
4656 status = dsdb_dn_la_from_blob(ldb, attr, schema, tmp_ctx, la->value.blob, &dsdb_dn);
4657 if (!W_ERROR_IS_OK(status)) {
4658 ldb_asprintf_errstring(ldb, "Failed to parsed linked attribute blob for %s on %s - %s\n",
4659 old_el->name, ldb_dn_get_linearized(msg->dn), win_errstr(status));
4660 return LDB_ERR_OPERATIONS_ERROR;
4663 ntstatus = dsdb_get_extended_dn_guid(dsdb_dn->dn, &guid, "GUID");
4664 if (!NT_STATUS_IS_OK(ntstatus) && active) {
4665 ldb_asprintf_errstring(ldb, "Failed to find GUID in linked attribute blob for %s on %s from %s",
4666 old_el->name,
4667 ldb_dn_get_linearized(dsdb_dn->dn),
4668 ldb_dn_get_linearized(msg->dn));
4669 return LDB_ERR_OPERATIONS_ERROR;
4672 /* re-resolve the DN by GUID, as the DRS server may give us an
4673 old DN value */
4674 ret = dsdb_module_dn_by_guid(module, dsdb_dn, &guid, &dsdb_dn->dn, parent);
4675 if (ret != LDB_SUCCESS) {
4676 DEBUG(2,(__location__ ": WARNING: Failed to re-resolve GUID %s - using %s\n",
4677 GUID_string(tmp_ctx, &guid),
4678 ldb_dn_get_linearized(dsdb_dn->dn)));
4681 /* see if this link already exists */
4682 pdn = parsed_dn_find(pdn_list, old_el->num_values, &guid, dsdb_dn->dn);
4683 if (pdn != NULL) {
4684 /* see if this update is newer than what we have already */
4685 struct GUID invocation_id = GUID_zero();
4686 uint32_t version = 0;
4687 uint32_t originating_usn = 0;
4688 NTTIME change_time = 0;
4689 uint32_t rmd_flags = dsdb_dn_rmd_flags(pdn->dsdb_dn->dn);
4691 dsdb_get_extended_dn_guid(pdn->dsdb_dn->dn, &invocation_id, "RMD_INVOCID");
4692 dsdb_get_extended_dn_uint32(pdn->dsdb_dn->dn, &version, "RMD_VERSION");
4693 dsdb_get_extended_dn_uint32(pdn->dsdb_dn->dn, &originating_usn, "RMD_ORIGINATING_USN");
4694 dsdb_get_extended_dn_nttime(pdn->dsdb_dn->dn, &change_time, "RMD_CHANGETIME");
4696 if (!replmd_update_is_newer(&invocation_id,
4697 &la->meta_data.originating_invocation_id,
4698 version,
4699 la->meta_data.version,
4700 change_time,
4701 la->meta_data.originating_change_time)) {
4702 DEBUG(3,("Discarding older DRS linked attribute update to %s on %s from %s\n",
4703 old_el->name, ldb_dn_get_linearized(msg->dn),
4704 GUID_string(tmp_ctx, &la->meta_data.originating_invocation_id)));
4705 talloc_free(tmp_ctx);
4706 return LDB_SUCCESS;
4709 /* get a seq_num for this change */
4710 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
4711 if (ret != LDB_SUCCESS) {
4712 talloc_free(tmp_ctx);
4713 return ret;
4716 if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
4717 /* remove the existing backlink */
4718 ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid, false, attr, false);
4719 if (ret != LDB_SUCCESS) {
4720 talloc_free(tmp_ctx);
4721 return ret;
4725 ret = replmd_update_la_val(tmp_ctx, pdn->v, dsdb_dn, pdn->dsdb_dn,
4726 &la->meta_data.originating_invocation_id,
4727 la->meta_data.originating_usn, seq_num,
4728 la->meta_data.originating_change_time,
4729 la->meta_data.version,
4730 !active);
4731 if (ret != LDB_SUCCESS) {
4732 talloc_free(tmp_ctx);
4733 return ret;
4736 if (active) {
4737 /* add the new backlink */
4738 ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid, true, attr, false);
4739 if (ret != LDB_SUCCESS) {
4740 talloc_free(tmp_ctx);
4741 return ret;
4744 } else {
4745 /* get a seq_num for this change */
4746 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
4747 if (ret != LDB_SUCCESS) {
4748 talloc_free(tmp_ctx);
4749 return ret;
4752 old_el->values = talloc_realloc(msg->elements, old_el->values,
4753 struct ldb_val, old_el->num_values+1);
4754 if (!old_el->values) {
4755 ldb_module_oom(module);
4756 return LDB_ERR_OPERATIONS_ERROR;
4758 old_el->num_values++;
4760 ret = replmd_build_la_val(tmp_ctx, &old_el->values[old_el->num_values-1], dsdb_dn,
4761 &la->meta_data.originating_invocation_id,
4762 la->meta_data.originating_usn, seq_num,
4763 la->meta_data.originating_change_time,
4764 la->meta_data.version,
4765 (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)?false:true);
4766 if (ret != LDB_SUCCESS) {
4767 talloc_free(tmp_ctx);
4768 return ret;
4771 if (active) {
4772 ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid,
4773 true, attr, false);
4774 if (ret != LDB_SUCCESS) {
4775 talloc_free(tmp_ctx);
4776 return ret;
4781 /* we only change whenChanged and uSNChanged if the seq_num
4782 has changed */
4783 ret = add_time_element(msg, "whenChanged", t);
4784 if (ret != LDB_SUCCESS) {
4785 talloc_free(tmp_ctx);
4786 ldb_operr(ldb);
4787 return ret;
4790 ret = add_uint64_element(ldb, msg, "uSNChanged", seq_num);
4791 if (ret != LDB_SUCCESS) {
4792 talloc_free(tmp_ctx);
4793 ldb_operr(ldb);
4794 return ret;
4797 old_el = ldb_msg_find_element(msg, attr->lDAPDisplayName);
4798 if (old_el == NULL) {
4799 talloc_free(tmp_ctx);
4800 return ldb_operr(ldb);
4803 ret = dsdb_check_single_valued_link(attr, old_el);
4804 if (ret != LDB_SUCCESS) {
4805 talloc_free(tmp_ctx);
4806 return ret;
4809 old_el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
4811 ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE, parent);
4812 if (ret != LDB_SUCCESS) {
4813 ldb_debug(ldb, LDB_DEBUG_WARNING, "Failed to apply linked attribute change '%s'\n%s\n",
4814 ldb_errstring(ldb),
4815 ldb_ldif_message_string(ldb, tmp_ctx, LDB_CHANGETYPE_MODIFY, msg));
4816 talloc_free(tmp_ctx);
4817 return ret;
4820 talloc_free(tmp_ctx);
4822 return ret;
4825 static int replmd_extended(struct ldb_module *module, struct ldb_request *req)
4827 if (strcmp(req->op.extended.oid, DSDB_EXTENDED_REPLICATED_OBJECTS_OID) == 0) {
4828 return replmd_extended_replicated_objects(module, req);
4831 return ldb_next_request(module, req);
4836 we hook into the transaction operations to allow us to
4837 perform the linked attribute updates at the end of the whole
4838 transaction. This allows a forward linked attribute to be created
4839 before the object is created. During a vampire, w2k8 sends us linked
4840 attributes before the objects they are part of.
4842 static int replmd_start_transaction(struct ldb_module *module)
4844 /* create our private structure for this transaction */
4845 struct replmd_private *replmd_private = talloc_get_type(ldb_module_get_private(module),
4846 struct replmd_private);
4847 replmd_txn_cleanup(replmd_private);
4849 /* free any leftover mod_usn records from cancelled
4850 transactions */
4851 while (replmd_private->ncs) {
4852 struct nc_entry *e = replmd_private->ncs;
4853 DLIST_REMOVE(replmd_private->ncs, e);
4854 talloc_free(e);
4857 return ldb_next_start_trans(module);
4861 on prepare commit we loop over our queued la_context structures and
4862 apply each of them
4864 static int replmd_prepare_commit(struct ldb_module *module)
4866 struct replmd_private *replmd_private =
4867 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
4868 struct la_entry *la, *prev;
4869 struct la_backlink *bl;
4870 int ret;
4872 /* walk the list backwards, to do the first entry first, as we
4873 * added the entries with DLIST_ADD() which puts them at the
4874 * start of the list */
4875 for (la = DLIST_TAIL(replmd_private->la_list); la; la=prev) {
4876 prev = DLIST_PREV(la);
4877 DLIST_REMOVE(replmd_private->la_list, la);
4878 ret = replmd_process_linked_attribute(module, la, NULL);
4879 if (ret != LDB_SUCCESS) {
4880 replmd_txn_cleanup(replmd_private);
4881 return ret;
4885 /* process our backlink list, creating and deleting backlinks
4886 as necessary */
4887 for (bl=replmd_private->la_backlinks; bl; bl=bl->next) {
4888 ret = replmd_process_backlink(module, bl, NULL);
4889 if (ret != LDB_SUCCESS) {
4890 replmd_txn_cleanup(replmd_private);
4891 return ret;
4895 replmd_txn_cleanup(replmd_private);
4897 /* possibly change @REPLCHANGED */
4898 ret = replmd_notify_store(module, NULL);
4899 if (ret != LDB_SUCCESS) {
4900 return ret;
4903 return ldb_next_prepare_commit(module);
4906 static int replmd_del_transaction(struct ldb_module *module)
4908 struct replmd_private *replmd_private =
4909 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
4910 replmd_txn_cleanup(replmd_private);
4912 return ldb_next_del_trans(module);
4916 static const struct ldb_module_ops ldb_repl_meta_data_module_ops = {
4917 .name = "repl_meta_data",
4918 .init_context = replmd_init,
4919 .add = replmd_add,
4920 .modify = replmd_modify,
4921 .rename = replmd_rename,
4922 .del = replmd_delete,
4923 .extended = replmd_extended,
4924 .start_transaction = replmd_start_transaction,
4925 .prepare_commit = replmd_prepare_commit,
4926 .del_transaction = replmd_del_transaction,
4929 int ldb_repl_meta_data_module_init(const char *version)
4931 LDB_MODULE_CHECK_VERSION(version);
4932 return ldb_register_module(&ldb_repl_meta_data_module_ops);