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/>.
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
35 * Author: Stefan Metzmacher
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 {
56 struct la_entry *la_list;
58 struct la_backlink *la_backlinks;
60 struct nc_entry *prev, *next;
63 uint64_t mod_usn_urgent;
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 */
83 uint32_t index_current;
84 struct dsdb_extended_replicated_objects *objs;
86 struct ldb_message *search_msg;
92 enum urgent_situation {
93 REPL_URGENT_ON_CREATE = 1,
94 REPL_URGENT_ON_UPDATE = 2,
95 REPL_URGENT_ON_DELETE = 4
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)},
112 /* Attributes looked for when updating or deleting, to check for a urgent replication needed */
113 static const char *urgent_attrs[] = {
116 "userAccountControl",
121 static bool replmd_check_urgent_objectclass(const struct ldb_message_element *objectclass_el,
122 enum urgent_situation situation)
125 for (i=0; urgent_objects[i].update_name; i++) {
127 if ((situation & urgent_objects[i].repl_situation) == 0) {
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) {
141 static bool replmd_check_urgent_attribute(const struct ldb_message_element *el)
143 if (ldb_attr_in_list(urgent_attrs, el->name)) {
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) {
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;
188 struct la_backlink *next, *prev;
189 const char *attr_name;
190 struct GUID forward_guid, target_guid;
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;
202 struct ldb_context *ldb = ldb_module_get_ctx(module);
203 struct ldb_message *msg;
204 TALLOC_CTX *tmp_ctx = talloc_new(bl);
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)));
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);
228 msg = ldb_msg_new(tmp_ctx);
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 */
237 dn_string = ldb_dn_get_extended_linearized(tmp_ctx, source_dn, 1);
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);
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)));
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),
273 talloc_free(tmp_ctx);
276 talloc_free(tmp_ctx);
281 add a backlink to the list of backlinks to add/delete in the prepare
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);
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
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)) {
315 /* we found an existing one */
316 if (bl->active == active) {
319 DLIST_REMOVE(replmd_private->la_backlinks, bl);
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;
333 bl = talloc(replmd_private->bl_ctx, struct la_backlink);
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)) {
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;
351 /* the caller may ask for this backlink to be processed
354 int ret = replmd_process_backlink(module, bl, NULL);
359 DLIST_ADD(replmd_private->la_backlinks, bl);
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)
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,
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) {
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;
446 modified_partition->mod_usn_urgent = ac->seq_num;
451 if (ac->apply_mode) {
455 ret = replmd_replicated_apply_next(ac);
456 if (ret != LDB_SUCCESS) {
457 return ldb_module_done(ac->req, NULL, NULL, ret);
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) {
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)));
492 DLIST_REMOVE(replmd_private->ncs, modified_partition);
493 talloc_free(modified_partition);
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);
520 ac->schema = dsdb_get_schema(ldb, ac);
522 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
523 "replmd_modify: no dsdb_schema loaded");
524 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
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;
540 if (ldb_msg_find_element(msg, attr) != NULL) {
544 s = ldb_timestring(msg, t);
546 return LDB_ERR_OPERATIONS_ERROR;
549 ret = ldb_msg_add_string(msg, attr, s);
550 if (ret != LDB_SUCCESS) {
554 el = ldb_msg_find_element(msg, attr);
555 /* always set as replace. This works because on add ops, the flag
557 el->flags = LDB_FLAG_MOD_REPLACE;
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;
571 if (ldb_msg_find_element(msg, attr) != NULL) {
575 ret = samdb_msg_add_uint64(ldb, msg, msg, attr, v);
576 if (ret != LDB_SUCCESS) {
580 el = ldb_msg_find_element(msg, attr);
581 /* always set as replace. This works because on add ops, the flag
583 el->flags = LDB_FLAG_MOD_REPLACE;
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) {
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) {
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) {
614 return m1->attid > m2->attid ? 1 : -1;
617 static int replmd_replPropertyMetaDataCtr1_sort(struct replPropertyMetaDataCtr1 *ctr1,
618 const struct dsdb_schema *schema,
621 const char *rdn_name;
622 const struct dsdb_attribute *rdn_sa;
624 rdn_name = ldb_dn_get_rdn_name(dn);
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);
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
664 return strcasecmp(e1->name, e2->name);
666 if (a1->attributeID_id == a2->attributeID_id) {
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)
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);
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;
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);
718 ret = dsdb_set_extended_dn_guid(dsdb_dn->dn, &target_guid, "GUID");
719 if (ret != LDB_SUCCESS) {
720 talloc_free(tmp_ctx);
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);
732 ret = replmd_add_backlink(module, schema, guid, &target_guid, true, sa, false);
733 if (ret != LDB_SUCCESS) {
734 talloc_free(tmp_ctx);
739 talloc_free(tmp_ctx);
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;
757 struct replPropertyMetaDataBlob nmd;
758 struct ldb_val nmd_value;
759 const struct GUID *our_invocation_id;
760 time_t t = time(NULL);
765 unsigned int functional_level;
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);
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;
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;
807 guid = GUID_random();
810 ac = replmd_ctx_init(module, req);
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) {
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");
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);
838 return LDB_ERR_OPERATIONS_ERROR;
841 /* generated times */
842 unix_to_nt_time(&now, t);
843 time_str = ldb_timestring(msg, t);
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) {
872 /* build the replication meta_data */
875 nmd.ctr.ctr1.count = msg->num_elements;
876 nmd.ctr.ctr1.array = talloc_array(msg,
877 struct replPropertyMetaData1,
879 if (!nmd.ctr.ctr1.array) {
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);
894 ldb_debug_set(ldb, LDB_DEBUG_ERROR,
895 "replmd_add: attribute '%s' not defined in schema\n",
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
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) {
914 /* linked attributes are not stored in
915 replPropertyMetaData in FL above w2k */
919 m->attid = sa->attributeID_id;
921 if (m->attid == 0x20030) {
922 const struct ldb_val *rdn_val = ldb_dn_get_rdn_val(msg->dn);
925 if (rdn_val == NULL) {
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;
942 m->originating_change_time = now;
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;
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) {
965 /* generated NDR encoded values */
966 ndr_err = ndr_push_struct_blob(&nmd_value, msg,
968 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
969 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
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) {
984 ret = ldb_msg_add_string(msg, "whenChanged", time_str);
985 if (ret != LDB_SUCCESS) {
990 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", ac->seq_num);
991 if (ret != LDB_SUCCESS) {
996 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ac->seq_num);
997 if (ret != LDB_SUCCESS) {
1002 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
1003 if (ret != LDB_SUCCESS) {
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,
1022 ac, replmd_op_callback,
1025 LDB_REQ_SET_LOCATION(down_req);
1026 if (ret != LDB_SUCCESS) {
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,
1036 if (ret != LDB_SUCCESS) {
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) {
1050 /* mark the control done */
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,
1070 const struct GUID *our_invocation_id,
1072 struct ldb_request *req)
1075 const struct dsdb_attribute *a;
1076 struct replPropertyMetaData1 *md1;
1078 a = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
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 */
1086 DEBUG(0,(__location__ ": Unable to find attribute %s in schema\n",
1088 return LDB_ERR_OPERATIONS_ERROR;
1091 if ((a->systemFlags & DS_FLAG_ATTR_NOT_REPLICATED) || (a->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED)) {
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
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
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;
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) {
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];
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);
1157 if (rdn_val == NULL) {
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;
1173 md1->originating_change_time = now;
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;
1185 static uint64_t find_max_local_usn(struct replPropertyMetaDataBlob omd)
1187 uint32_t count = omd.ctr.ctr1.count;
1190 for (i=0; i < count; i++) {
1191 struct replPropertyMetaData1 m = omd.ctr.ctr1.array[i];
1192 if (max < m.local_usn) {
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,
1210 bool *is_urgent, bool *rodc)
1212 const struct ldb_val *omd_value;
1213 enum ndr_err_code ndr_err;
1214 struct replPropertyMetaDataBlob omd;
1217 const struct GUID *our_invocation_id;
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;
1229 attrs = rename_attrs;
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"));
1244 unix_to_nt_time(&now, t);
1246 if (ldb_request_get_control(req, DSDB_CONTROL_CHANGEREPLMETADATA_OID)) {
1247 rmd_is_provided = true;
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;
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
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");
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) {
1306 objectclass_el = ldb_msg_find_element(res->msgs[0], "objectClass");
1307 if (is_urgent && replmd_check_urgent_objectclass(objectclass_el,
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;
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) {
1336 objectclass_el = ldb_msg_find_element(res->msgs[0], "objectClass");
1337 if (is_urgent && replmd_check_urgent_objectclass(objectclass_el,
1342 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
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) {
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
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"));
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) {
1408 return LDB_ERR_OPERATIONS_ERROR;
1411 ret = replmd_replPropertyMetaDataCtr1_sort(&omd.ctr.ctr1, schema, msg->dn);
1412 if (ret != LDB_SUCCESS) {
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)));
1432 el->values = md_value;
1439 struct dsdb_dn *dsdb_dn;
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,
1453 struct parsed_dn *ret;
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");
1466 BINARY_ARRAY_SEARCH(pdn, count, guid, guid, GUID_compare, 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)
1479 struct ldb_context *ldb = ldb_module_get_ctx(module);
1486 (*pdn) = talloc_array(mem_ctx, struct parsed_dn, el->num_values);
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];
1496 struct parsed_dn *p;
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;
1527 ret = dsdb_set_extended_dn_guid(dn, p->guid, "GUID");
1528 if (ret != LDB_SUCCESS) {
1531 } else if (!NT_STATUS_IS_OK(status)) {
1532 return LDB_ERR_OPERATIONS_ERROR;
1535 /* keep a pointer to the original ldb_val */
1539 TYPESAFE_QSORT(*pdn, el->num_values, parsed_dn_compare);
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;
1563 struct ldb_val usnv, local_usnv;
1564 struct ldb_val vers, flagsv;
1567 const char *dnstring;
1569 uint32_t rmd_flags = deleted?DSDB_RMD_FLAG_DELETED:0;
1571 tstring = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)nttime);
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);
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);
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);
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);
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)
1643 for (i=0; i<count; i++) {
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)) {
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,
1656 if (ret != 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;
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;
1683 const char *dnstring;
1685 uint32_t rmd_flags = deleted?DSDB_RMD_FLAG_DELETED:0;
1687 tstring = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)nttime);
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);
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);
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);
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,
1775 struct GUID *msg_guid,
1776 struct ldb_request *parent)
1779 struct parsed_dn *dns, *old_dns;
1780 TALLOC_CTX *tmp_ctx = talloc_new(msg);
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);
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);
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);
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);
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);
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);
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
1844 if (ldb_attr_cmp(el->name, "member") == 0) {
1845 return LDB_ERR_ENTRY_ALREADY_EXISTS;
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);
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);
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,
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;
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,
1901 struct GUID *msg_guid,
1902 struct ldb_request *parent)
1905 struct parsed_dn *dns, *old_dns;
1906 TALLOC_CTX *tmp_ctx = talloc_new(msg);
1908 const struct GUID *invocation_id;
1909 struct ldb_context *ldb = ldb_module_get_ctx(module);
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) {
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);
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);
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);
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;
1956 p2 = parsed_dn_find(old_dns, old_el->num_values, p->guid, NULL);
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;
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;
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];
1985 if (el->num_values && parsed_dn_find(dns, el->num_values, p->guid, NULL) == NULL) {
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);
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);
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;
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,
2029 struct GUID *msg_guid,
2030 struct ldb_request *parent)
2033 struct parsed_dn *dns, *old_dns;
2034 TALLOC_CTX *tmp_ctx = talloc_new(msg);
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;
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) {
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);
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);
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);
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);
2088 p = parsed_dn_find(dns, el->num_values, old_p->guid, NULL);
2090 /* we don't delete it if we are re-adding it */
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);
2102 /* for each new value, either update its meta-data, or add it
2105 for (i=0; i<el->num_values; i++) {
2106 struct parsed_dn *p = &dns[i], *old_p;
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);
2121 new_values = talloc_realloc(tmp_ctx, new_values, struct ldb_val,
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);
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);
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);
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;
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;
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;
2190 /* there the replmd_update_rpmd code has already
2191 * checked and saw that there are no linked
2196 if (dsdb_functional_level(ldb) == DS_DOMAIN_FUNCTION_2000) {
2197 /* don't do anything special for linked attributes */
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,
2207 if (ret != LDB_SUCCESS) {
2210 schema = dsdb_get_schema(ldb, res);
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);
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) {
2233 if ((schema_attr->linkID & 1) == 1) {
2234 if (parent && ldb_request_get_control(parent, DSDB_CONTROL_DBCHECK)) {
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);
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);
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);
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",
2263 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
2265 el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
2270 if (ret != LDB_SUCCESS) {
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);
2285 ldb_msg_remove_element(msg, old_el);
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);
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);
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);
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;
2348 lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
2349 struct loadparm_context);
2351 referral = talloc_asprintf(req,
2353 lpcfg_dnsdomain(lp_ctx),
2354 ldb_dn_get_linearized(msg->dn));
2355 ret = ldb_module_send_referral(req, referral);
2360 if (ret != LDB_SUCCESS) {
2365 ret = replmd_modify_handle_linked_attribs(module, msg, ac->seq_num, t, req);
2366 if (ret != LDB_SUCCESS) {
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,
2380 ac, replmd_op_callback,
2382 LDB_REQ_SET_LOCATION(down_req);
2383 if (ret != LDB_SUCCESS) {
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,
2393 if (ret != LDB_SUCCESS) {
2399 /* If we are in functional level 2000, then
2400 * replmd_modify_handle_linked_attribs will have done
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) {
2410 talloc_steal(down_req, msg);
2412 /* we only change whenChanged and uSNChanged if the seq_num
2414 if (ac->seq_num != 0) {
2415 ret = add_time_element(msg, "whenChanged", t);
2416 if (ret != LDB_SUCCESS) {
2422 ret = add_uint64_element(ldb, msg, "uSNChanged", ac->seq_num);
2423 if (ret != LDB_SUCCESS) {
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;
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);
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,
2467 ac, replmd_rename_callback,
2469 LDB_REQ_SET_LOCATION(down_req);
2470 if (ret != LDB_SUCCESS) {
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);
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");
2506 return ldb_module_done(ac->req, NULL, NULL,
2507 LDB_ERR_OPERATIONS_ERROR);
2511 * - replace the old object with the newly constructed one
2514 msg = ldb_msg_new(ac);
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) {
2525 return ldb_module_done(ac->req, NULL, NULL,
2529 /* normalize the rdn attribute name */
2530 rdn_attr = dsdb_attribute_by_lDAPDisplayName(ac->schema, rdn_name);
2531 if (rdn_attr == NULL) {
2533 return ldb_module_done(ac->req, NULL, NULL,
2536 rdn_name = rdn_attr->lDAPDisplayName;
2538 rdn_val = ldb_dn_get_rdn_val(msg->dn);
2539 if (rdn_val == NULL) {
2541 return ldb_module_done(ac->req, NULL, NULL,
2545 if (ldb_msg_add_empty(msg, rdn_name, LDB_FLAG_MOD_REPLACE, NULL) != 0) {
2547 return ldb_module_done(ac->req, NULL, NULL,
2550 if (ldb_msg_add_value(msg, rdn_name, rdn_val, NULL) != 0) {
2552 return ldb_module_done(ac->req, NULL, NULL,
2555 if (ldb_msg_add_empty(msg, "name", LDB_FLAG_MOD_REPLACE, NULL) != 0) {
2557 return ldb_module_done(ac->req, NULL, NULL,
2560 if (ldb_msg_add_value(msg, "name", rdn_val, NULL) != 0) {
2562 return ldb_module_done(ac->req, NULL, NULL,
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:
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;
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;
2613 lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
2614 struct loadparm_context);
2616 referral = talloc_asprintf(req,
2618 lpcfg_dnsdomain(lp_ctx),
2619 ldb_dn_get_linearized(olddn));
2620 ret = ldb_module_send_referral(req, referral);
2622 return ldb_module_done(req, NULL, NULL, ret);
2625 if (ret != LDB_SUCCESS) {
2627 return ldb_module_done(ac->req, NULL, NULL, ret);
2630 if (ac->seq_num == 0) {
2632 return ldb_module_done(ac->req, NULL, NULL,
2634 "internal error seq_num == 0"));
2636 ac->is_urgent = is_urgent;
2638 ret = ldb_build_mod_req(&down_req, ldb, ac,
2641 ac, replmd_op_callback,
2643 LDB_REQ_SET_LOCATION(down_req);
2644 if (ret != LDB_SUCCESS) {
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,
2654 if (ret != LDB_SUCCESS) {
2660 talloc_steal(down_req, msg);
2662 ret = add_time_element(msg, "whenChanged", t);
2663 if (ret != LDB_SUCCESS) {
2669 ret = add_uint64_element(ldb, msg, "uSNChanged", ac->seq_num);
2670 if (ret != LDB_SUCCESS) {
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
2684 static int replmd_delete_remove_link(struct ldb_module *module,
2685 const struct dsdb_schema *schema,
2687 struct ldb_message_element *el,
2688 const struct dsdb_attribute *sa,
2689 struct ldb_request *parent)
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;
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])) {
2709 dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i], sa->syntax->ldap_oid);
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);
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) {
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);
2753 talloc_free(tmp_ctx);
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;
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;
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);
2803 return LDB_ERR_OPERATIONS_ERROR;
2806 schema = dsdb_get_schema(ldb, tmp_ctx);
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);
2826 old_msg = res->msgs[0];
2829 ret = dsdb_recyclebin_enabled(module, &enabled);
2830 if (ret != LDB_SUCCESS) {
2831 talloc_free(tmp_ctx);
2835 if (ldb_msg_check_string_attribute(old_msg, "isDeleted", "TRUE")) {
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;
2843 deletion_state = OBJECT_DELETED;
2844 next_deletion_state = OBJECT_RECYCLED;
2847 deletion_state = OBJECT_NOT_DELETED;
2849 next_deletion_state = OBJECT_DELETED;
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);
2878 ldb_module_oom(module);
2879 talloc_free(tmp_ctx);
2880 return LDB_ERR_OPERATIONS_ERROR;
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,
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;
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",
2919 ldb_dn_escape_value(tmp_ctx, *rdn_value),
2920 GUID_string(tmp_ctx, &guid));
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);
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);
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);
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);
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);
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);
3008 case OBJECT_RECYCLED:
3009 case OBJECT_TOMBSTONE:
3012 * we also mark it as recycled, meaning this object can't be
3013 * recovered (we are stripping its attributes).
3014 * This is done only if we have this schema object of course ...
3015 * This behavior is identical to the one of Windows 2008R2 which
3016 * always set the isRecycled attribute, even if the recycle-bin is
3017 * not activated and what ever the forest level is.
3019 if (dsdb_attribute_by_lDAPDisplayName(schema, "isRecycled") != NULL) {
3020 ret = ldb_msg_add_string(msg, "isRecycled", "TRUE");
3021 if (ret != LDB_SUCCESS) {
3022 DEBUG(0,(__location__ ": Failed to add isRecycled string to the msg\n"));
3023 ldb_module_oom(module);
3024 talloc_free(tmp_ctx);
3027 msg->elements[el_count++].flags = LDB_FLAG_MOD_ADD;
3030 /* work out which of the old attributes we will be removing */
3031 for (i=0; i<old_msg->num_elements; i++) {
3032 const struct dsdb_attribute *sa;
3033 el = &old_msg->elements[i];
3034 sa = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
3036 talloc_free(tmp_ctx);
3037 return LDB_ERR_OPERATIONS_ERROR;
3039 if (ldb_attr_cmp(el->name, rdn_name) == 0) {
3040 /* don't remove the rDN */
3043 if (sa->linkID && (sa->linkID & 1)) {
3045 we have a backlink in this object
3046 that needs to be removed. We're not
3047 allowed to remove it directly
3048 however, so we instead setup a
3049 modify to delete the corresponding
3052 ret = replmd_delete_remove_link(module, schema, old_dn, el, sa, req);
3053 if (ret != LDB_SUCCESS) {
3054 talloc_free(tmp_ctx);
3055 return LDB_ERR_OPERATIONS_ERROR;
3057 /* now we continue, which means we
3058 won't remove this backlink
3063 if (!sa->linkID && ldb_attr_in_list(preserved_attrs, el->name)) {
3066 ret = ldb_msg_add_empty(msg, el->name, LDB_FLAG_MOD_DELETE, &el);
3067 if (ret != LDB_SUCCESS) {
3068 talloc_free(tmp_ctx);
3069 ldb_module_oom(module);
3079 if (deletion_state == OBJECT_NOT_DELETED) {
3080 const struct dsdb_attribute *sa;
3082 /* work out what the new rdn value is, for updating the
3083 rDN and name fields */
3084 new_rdn_value = ldb_dn_get_rdn_val(new_dn);
3085 if (new_rdn_value == NULL) {
3086 talloc_free(tmp_ctx);
3087 return ldb_operr(ldb);
3090 sa = dsdb_attribute_by_lDAPDisplayName(schema, rdn_name);
3092 talloc_free(tmp_ctx);
3093 return LDB_ERR_OPERATIONS_ERROR;
3096 ret = ldb_msg_add_value(msg, sa->lDAPDisplayName, new_rdn_value,
3098 if (ret != LDB_SUCCESS) {
3099 talloc_free(tmp_ctx);
3102 el->flags = LDB_FLAG_MOD_REPLACE;
3104 el = ldb_msg_find_element(old_msg, "name");
3106 ret = ldb_msg_add_value(msg, "name", new_rdn_value, &el);
3107 if (ret != LDB_SUCCESS) {
3108 talloc_free(tmp_ctx);
3111 el->flags = LDB_FLAG_MOD_REPLACE;
3115 ret = dsdb_module_modify(module, msg, DSDB_FLAG_OWN_MODULE, req);
3116 if (ret != LDB_SUCCESS) {
3117 ldb_asprintf_errstring(ldb, "replmd_delete: Failed to modify object %s in delete - %s",
3118 ldb_dn_get_linearized(old_dn), ldb_errstring(ldb));
3119 talloc_free(tmp_ctx);
3123 if (deletion_state == OBJECT_NOT_DELETED) {
3124 /* now rename onto the new DN */
3125 ret = dsdb_module_rename(module, old_dn, new_dn, DSDB_FLAG_NEXT_MODULE, req);
3126 if (ret != LDB_SUCCESS){
3127 DEBUG(0,(__location__ ": Failed to rename object from '%s' to '%s' - %s\n",
3128 ldb_dn_get_linearized(old_dn),
3129 ldb_dn_get_linearized(new_dn),
3130 ldb_errstring(ldb)));
3131 talloc_free(tmp_ctx);
3136 talloc_free(tmp_ctx);
3138 return ldb_module_done(req, NULL, NULL, LDB_SUCCESS);
3143 static int replmd_replicated_request_error(struct replmd_replicated_request *ar, int ret)
3148 static int replmd_replicated_request_werror(struct replmd_replicated_request *ar, WERROR status)
3150 int ret = LDB_ERR_OTHER;
3151 /* TODO: do some error mapping */
3156 static struct replPropertyMetaData1 *
3157 replmd_replPropertyMetaData1_find_attid(struct replPropertyMetaDataBlob *md_blob,
3158 enum drsuapi_DsAttributeId attid)
3161 struct replPropertyMetaDataCtr1 *rpmd_ctr = &md_blob->ctr.ctr1;
3163 for (i = 0; i < rpmd_ctr->count; i++) {
3164 if (rpmd_ctr->array[i].attid == attid) {
3165 return &rpmd_ctr->array[i];
3173 return true if an update is newer than an existing entry
3174 see section 5.11 of MS-ADTS
3176 static bool replmd_update_is_newer(const struct GUID *current_invocation_id,
3177 const struct GUID *update_invocation_id,
3178 uint32_t current_version,
3179 uint32_t update_version,
3180 NTTIME current_change_time,
3181 NTTIME update_change_time)
3183 if (update_version != current_version) {
3184 return update_version > current_version;
3186 if (update_change_time != current_change_time) {
3187 return update_change_time > current_change_time;
3189 return GUID_compare(update_invocation_id, current_invocation_id) > 0;
3192 static bool replmd_replPropertyMetaData1_is_newer(struct replPropertyMetaData1 *cur_m,
3193 struct replPropertyMetaData1 *new_m)
3195 return replmd_update_is_newer(&cur_m->originating_invocation_id,
3196 &new_m->originating_invocation_id,
3199 cur_m->originating_change_time,
3200 new_m->originating_change_time);
3207 static struct ldb_dn *replmd_conflict_dn(TALLOC_CTX *mem_ctx, struct ldb_dn *dn, struct GUID *guid)
3209 const struct ldb_val *rdn_val;
3210 const char *rdn_name;
3211 struct ldb_dn *new_dn;
3213 rdn_val = ldb_dn_get_rdn_val(dn);
3214 rdn_name = ldb_dn_get_rdn_name(dn);
3215 if (!rdn_val || !rdn_name) {
3219 new_dn = ldb_dn_copy(mem_ctx, dn);
3224 if (!ldb_dn_remove_child_components(new_dn, 1)) {
3228 if (!ldb_dn_add_child_fmt(new_dn, "%s=%s\\0ACNF:%s",
3230 ldb_dn_escape_value(new_dn, *rdn_val),
3231 GUID_string(new_dn, guid))) {
3240 perform a modify operation which sets the rDN and name attributes to
3241 their current values. This has the effect of changing these
3242 attributes to have been last updated by the current DC. This is
3243 needed to ensure that renames performed as part of conflict
3244 resolution are propogated to other DCs
3246 static int replmd_name_modify(struct replmd_replicated_request *ar,
3247 struct ldb_request *req, struct ldb_dn *dn)
3249 struct ldb_message *msg;
3250 const char *rdn_name;
3251 const struct ldb_val *rdn_val;
3252 const struct dsdb_attribute *rdn_attr;
3255 msg = ldb_msg_new(req);
3261 rdn_name = ldb_dn_get_rdn_name(dn);
3262 if (rdn_name == NULL) {
3266 /* normalize the rdn attribute name */
3267 rdn_attr = dsdb_attribute_by_lDAPDisplayName(ar->schema, rdn_name);
3268 if (rdn_attr == NULL) {
3271 rdn_name = rdn_attr->lDAPDisplayName;
3273 rdn_val = ldb_dn_get_rdn_val(dn);
3274 if (rdn_val == NULL) {
3278 if (ldb_msg_add_empty(msg, rdn_name, LDB_FLAG_MOD_REPLACE, NULL) != 0) {
3281 if (ldb_msg_add_value(msg, rdn_name, rdn_val, NULL) != 0) {
3284 if (ldb_msg_add_empty(msg, "name", LDB_FLAG_MOD_REPLACE, NULL) != 0) {
3287 if (ldb_msg_add_value(msg, "name", rdn_val, NULL) != 0) {
3291 ret = dsdb_module_modify(ar->module, msg, DSDB_FLAG_OWN_MODULE, req);
3292 if (ret != LDB_SUCCESS) {
3293 DEBUG(0,(__location__ ": Failed to modify rDN/name of conflict DN '%s' - %s",
3294 ldb_dn_get_linearized(dn),
3295 ldb_errstring(ldb_module_get_ctx(ar->module))));
3305 DEBUG(0,(__location__ ": Failed to setup modify rDN/name of conflict DN '%s'",
3306 ldb_dn_get_linearized(dn)));
3307 return LDB_ERR_OPERATIONS_ERROR;
3312 callback for conflict DN handling where we have renamed the incoming
3313 record. After renaming it, we need to ensure the change of name and
3314 rDN for the incoming record is seen as an originating update by this DC.
3316 static int replmd_op_name_modify_callback(struct ldb_request *req, struct ldb_reply *ares)
3318 struct replmd_replicated_request *ar =
3319 talloc_get_type_abort(req->context, struct replmd_replicated_request);
3322 if (ares->error != LDB_SUCCESS) {
3323 /* call the normal callback for everything except success */
3324 return replmd_op_callback(req, ares);
3327 /* perform a modify of the rDN and name of the record */
3328 ret = replmd_name_modify(ar, req, req->op.add.message->dn);
3329 if (ret != LDB_SUCCESS) {
3331 return replmd_op_callback(req, ares);
3334 return replmd_op_callback(req, ares);
3338 callback for replmd_replicated_apply_add()
3339 This copes with the creation of conflict records in the case where
3340 the DN exists, but with a different objectGUID
3342 static int replmd_op_add_callback(struct ldb_request *req, struct ldb_reply *ares)
3344 struct ldb_dn *conflict_dn;
3345 struct replmd_replicated_request *ar =
3346 talloc_get_type_abort(req->context, struct replmd_replicated_request);
3347 struct ldb_result *res;
3348 const char *attrs[] = { "replPropertyMetaData", "objectGUID", NULL };
3350 const struct ldb_val *rmd_value, *omd_value;
3351 struct replPropertyMetaDataBlob omd, rmd;
3352 enum ndr_err_code ndr_err;
3353 bool rename_incoming_record, rodc;
3354 struct replPropertyMetaData1 *rmd_name, *omd_name;
3356 if (ares->error != LDB_ERR_ENTRY_ALREADY_EXISTS) {
3357 /* call the normal callback for everything except
3359 return replmd_op_callback(req, ares);
3362 ret = samdb_rodc(ldb_module_get_ctx(ar->module), &rodc);
3363 if (ret != LDB_SUCCESS) {
3367 * we have a conflict, and need to decide if we will keep the
3368 * new record or the old record
3370 conflict_dn = req->op.add.message->dn;
3374 * We are on an RODC, or were a GC for this
3375 * partition, so we have to fail this until
3376 * someone who owns the partition sorts it
3379 ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
3380 "Conflict adding object '%s' from incoming replication as we are read only for the partition. \n"
3381 " - We must fail the operation until a master for this partition resolves the conflict",
3382 ldb_dn_get_linearized(conflict_dn));
3387 * we have a conflict, and need to decide if we will keep the
3388 * new record or the old record
3390 conflict_dn = req->op.add.message->dn;
3393 * first we need the replPropertyMetaData attribute from the
3396 ret = dsdb_module_search_dn(ar->module, req, &res, conflict_dn,
3398 DSDB_FLAG_NEXT_MODULE |
3399 DSDB_SEARCH_SHOW_DELETED |
3400 DSDB_SEARCH_SHOW_RECYCLED, req);
3401 if (ret != LDB_SUCCESS) {
3402 DEBUG(0,(__location__ ": Unable to find object for conflicting record '%s'\n",
3403 ldb_dn_get_linearized(conflict_dn)));
3407 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
3408 if (omd_value == NULL) {
3409 DEBUG(0,(__location__ ": Unable to find replPropertyMetaData for conflicting record '%s'\n",
3410 ldb_dn_get_linearized(conflict_dn)));
3414 ndr_err = ndr_pull_struct_blob(omd_value, res->msgs[0], &omd,
3415 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
3416 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
3417 DEBUG(0,(__location__ ": Failed to parse old replPropertyMetaData for %s\n",
3418 ldb_dn_get_linearized(conflict_dn)));
3423 * and the replPropertyMetaData attribute from the
3426 rmd_value = ldb_msg_find_ldb_val(req->op.add.message, "replPropertyMetaData");
3427 if (rmd_value == NULL) {
3428 DEBUG(0,(__location__ ": Unable to find replPropertyMetaData for new record '%s'\n",
3429 ldb_dn_get_linearized(conflict_dn)));
3433 ndr_err = ndr_pull_struct_blob(rmd_value, req, &rmd,
3434 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
3435 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
3436 DEBUG(0,(__location__ ": Failed to parse new replPropertyMetaData for %s\n",
3437 ldb_dn_get_linearized(conflict_dn)));
3441 /* we decide which is newer based on the RPMD on the name
3442 attribute. See [MS-DRSR] ResolveNameConflict */
3443 rmd_name = replmd_replPropertyMetaData1_find_attid(&rmd, DRSUAPI_ATTID_name);
3444 omd_name = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
3445 if (!rmd_name || !omd_name) {
3446 DEBUG(0,(__location__ ": Failed to find name attribute in replPropertyMetaData for %s\n",
3447 ldb_dn_get_linearized(conflict_dn)));
3451 rename_incoming_record = !replmd_replPropertyMetaData1_is_newer(omd_name, rmd_name);
3453 if (rename_incoming_record) {
3455 struct ldb_dn *new_dn;
3456 struct ldb_message *new_msg;
3458 guid = samdb_result_guid(req->op.add.message, "objectGUID");
3459 if (GUID_all_zero(&guid)) {
3460 DEBUG(0,(__location__ ": Failed to find objectGUID for conflicting incoming record %s\n",
3461 ldb_dn_get_linearized(conflict_dn)));
3464 new_dn = replmd_conflict_dn(req, conflict_dn, &guid);
3465 if (new_dn == NULL) {
3466 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
3467 ldb_dn_get_linearized(conflict_dn)));
3471 DEBUG(1,(__location__ ": Resolving conflict record via incoming rename '%s' -> '%s'\n",
3472 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
3474 /* re-submit the request, but with a different
3475 callback, so we don't loop forever. */
3476 new_msg = ldb_msg_copy_shallow(req, req->op.add.message);
3479 DEBUG(0,(__location__ ": Failed to copy conflict DN message for %s\n",
3480 ldb_dn_get_linearized(conflict_dn)));
3482 new_msg->dn = new_dn;
3483 req->op.add.message = new_msg;
3484 req->callback = replmd_op_name_modify_callback;
3486 return ldb_next_request(ar->module, req);
3488 /* we are renaming the existing record */
3490 struct ldb_dn *new_dn;
3492 guid = samdb_result_guid(res->msgs[0], "objectGUID");
3493 if (GUID_all_zero(&guid)) {
3494 DEBUG(0,(__location__ ": Failed to find objectGUID for existing conflict record %s\n",
3495 ldb_dn_get_linearized(conflict_dn)));
3499 new_dn = replmd_conflict_dn(req, conflict_dn, &guid);
3500 if (new_dn == NULL) {
3501 DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
3502 ldb_dn_get_linearized(conflict_dn)));
3506 DEBUG(1,(__location__ ": Resolving conflict record via existing rename '%s' -> '%s'\n",
3507 ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
3509 ret = dsdb_module_rename(ar->module, conflict_dn, new_dn,
3510 DSDB_FLAG_OWN_MODULE, req);
3511 if (ret != LDB_SUCCESS) {
3512 DEBUG(0,(__location__ ": Failed to rename conflict dn '%s' to '%s' - %s\n",
3513 ldb_dn_get_linearized(conflict_dn),
3514 ldb_dn_get_linearized(new_dn),
3515 ldb_errstring(ldb_module_get_ctx(ar->module))));
3520 * now we need to ensure that the rename is seen as an
3521 * originating update. We do that with a modify.
3523 ret = replmd_name_modify(ar, req, new_dn);
3524 if (ret != LDB_SUCCESS) {
3528 req->callback = replmd_op_callback;
3530 return ldb_next_request(ar->module, req);
3534 /* on failure do the original callback. This means replication
3535 * will stop with an error, but there is not much else we can
3538 return replmd_op_callback(req, ares);
3542 this is called when a new object comes in over DRS
3544 static int replmd_replicated_apply_add(struct replmd_replicated_request *ar)
3546 struct ldb_context *ldb;
3547 struct ldb_request *change_req;
3548 enum ndr_err_code ndr_err;
3549 struct ldb_message *msg;
3550 struct replPropertyMetaDataBlob *md;
3551 struct ldb_val md_value;
3556 * TODO: check if the parent object exist
3560 * TODO: handle the conflict case where an object with the
3564 ldb = ldb_module_get_ctx(ar->module);
3565 msg = ar->objs->objects[ar->index_current].msg;
3566 md = ar->objs->objects[ar->index_current].meta_data;
3568 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
3569 if (ret != LDB_SUCCESS) {
3570 return replmd_replicated_request_error(ar, ret);
3573 ret = ldb_msg_add_value(msg, "objectGUID", &ar->objs->objects[ar->index_current].guid_value, NULL);
3574 if (ret != LDB_SUCCESS) {
3575 return replmd_replicated_request_error(ar, ret);
3578 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
3579 if (ret != LDB_SUCCESS) {
3580 return replmd_replicated_request_error(ar, ret);
3583 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", ar->seq_num);
3584 if (ret != LDB_SUCCESS) {
3585 return replmd_replicated_request_error(ar, ret);
3588 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
3589 if (ret != LDB_SUCCESS) {
3590 return replmd_replicated_request_error(ar, ret);
3593 /* remove any message elements that have zero values */
3594 for (i=0; i<msg->num_elements; i++) {
3595 struct ldb_message_element *el = &msg->elements[i];
3597 if (el->num_values == 0) {
3598 DEBUG(4,(__location__ ": Removing attribute %s with num_values==0\n",
3600 memmove(el, el+1, sizeof(*el)*(msg->num_elements - (i+1)));
3601 msg->num_elements--;
3608 * the meta data array is already sorted by the caller
3610 for (i=0; i < md->ctr.ctr1.count; i++) {
3611 md->ctr.ctr1.array[i].local_usn = ar->seq_num;
3613 ndr_err = ndr_push_struct_blob(&md_value, msg, md,
3614 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
3615 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
3616 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
3617 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
3619 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &md_value, NULL);
3620 if (ret != LDB_SUCCESS) {
3621 return replmd_replicated_request_error(ar, ret);
3624 replmd_ldb_message_sort(msg, ar->schema);
3627 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_ADD, msg);
3628 DEBUG(4, ("DRS replication add message:\n%s\n", s));
3632 ret = ldb_build_add_req(&change_req,
3638 replmd_op_add_callback,
3640 LDB_REQ_SET_LOCATION(change_req);
3641 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
3643 /* current partition control needed by "repmd_op_callback" */
3644 ret = ldb_request_add_control(change_req,
3645 DSDB_CONTROL_CURRENT_PARTITION_OID,
3647 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
3649 if (ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PARTIAL_REPLICA) {
3650 /* this tells the partition module to make it a
3651 partial replica if creating an NC */
3652 ret = ldb_request_add_control(change_req,
3653 DSDB_CONTROL_PARTIAL_REPLICA,
3655 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
3658 return ldb_next_request(ar->module, change_req);
3662 handle renames that come in over DRS replication
3664 static int replmd_replicated_handle_rename(struct replmd_replicated_request *ar,
3665 struct ldb_message *msg,
3666 struct replPropertyMetaDataBlob *rmd,
3667 struct replPropertyMetaDataBlob *omd,
3668 struct ldb_request *parent)
3670 struct replPropertyMetaData1 *md_remote;
3671 struct replPropertyMetaData1 *md_local;
3673 if (ldb_dn_compare(msg->dn, ar->search_msg->dn) == 0) {
3678 /* now we need to check for double renames. We could have a
3679 * local rename pending which our replication partner hasn't
3680 * received yet. We choose which one wins by looking at the
3681 * attribute stamps on the two objects, the newer one wins
3683 md_remote = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
3684 md_local = replmd_replPropertyMetaData1_find_attid(omd, DRSUAPI_ATTID_name);
3685 /* if there is no name attribute then we have to assume the
3686 object we've received is in fact newer */
3687 if (!md_remote || !md_local ||
3688 replmd_replPropertyMetaData1_is_newer(md_local, md_remote)) {
3689 DEBUG(4,("replmd_replicated_request rename %s => %s\n",
3690 ldb_dn_get_linearized(ar->search_msg->dn),
3691 ldb_dn_get_linearized(msg->dn)));
3692 /* pass rename to the next module
3693 * so it doesn't appear as an originating update */
3694 return dsdb_module_rename(ar->module,
3695 ar->search_msg->dn, msg->dn,
3696 DSDB_FLAG_NEXT_MODULE | DSDB_MODIFY_RELAX, parent);
3699 /* we're going to keep our old object */
3700 DEBUG(4,(__location__ ": Keeping object %s and rejecting older rename to %s\n",
3701 ldb_dn_get_linearized(ar->search_msg->dn),
3702 ldb_dn_get_linearized(msg->dn)));
3707 static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar)
3709 struct ldb_context *ldb;
3710 struct ldb_request *change_req;
3711 enum ndr_err_code ndr_err;
3712 struct ldb_message *msg;
3713 struct replPropertyMetaDataBlob *rmd;
3714 struct replPropertyMetaDataBlob omd;
3715 const struct ldb_val *omd_value;
3716 struct replPropertyMetaDataBlob nmd;
3717 struct ldb_val nmd_value;
3720 unsigned int removed_attrs = 0;
3723 ldb = ldb_module_get_ctx(ar->module);
3724 msg = ar->objs->objects[ar->index_current].msg;
3725 rmd = ar->objs->objects[ar->index_current].meta_data;
3729 /* find existing meta data */
3730 omd_value = ldb_msg_find_ldb_val(ar->search_msg, "replPropertyMetaData");
3732 ndr_err = ndr_pull_struct_blob(omd_value, ar, &omd,
3733 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
3734 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
3735 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
3736 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
3739 if (omd.version != 1) {
3740 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
3744 /* handle renames that come in over DRS */
3745 ret = replmd_replicated_handle_rename(ar, msg, rmd, &omd, ar->req);
3746 if (ret != LDB_SUCCESS) {
3747 ldb_debug(ldb, LDB_DEBUG_FATAL,
3748 "replmd_replicated_request rename %s => %s failed - %s\n",
3749 ldb_dn_get_linearized(ar->search_msg->dn),
3750 ldb_dn_get_linearized(msg->dn),
3751 ldb_errstring(ldb));
3752 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
3757 nmd.ctr.ctr1.count = omd.ctr.ctr1.count + rmd->ctr.ctr1.count;
3758 nmd.ctr.ctr1.array = talloc_array(ar,
3759 struct replPropertyMetaData1,
3760 nmd.ctr.ctr1.count);
3761 if (!nmd.ctr.ctr1.array) return replmd_replicated_request_werror(ar, WERR_NOMEM);
3763 /* first copy the old meta data */
3764 for (i=0; i < omd.ctr.ctr1.count; i++) {
3765 nmd.ctr.ctr1.array[ni] = omd.ctr.ctr1.array[i];
3770 /* now merge in the new meta data */
3771 for (i=0; i < rmd->ctr.ctr1.count; i++) {
3774 for (j=0; j < ni; j++) {
3777 if (rmd->ctr.ctr1.array[i].attid != nmd.ctr.ctr1.array[j].attid) {
3781 if (ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING) {
3782 /* if we compare equal then do an
3783 update. This is used when a client
3784 asks for a FULL_SYNC, and can be
3785 used to recover a corrupt
3787 cmp = !replmd_replPropertyMetaData1_is_newer(&rmd->ctr.ctr1.array[i],
3788 &nmd.ctr.ctr1.array[j]);
3790 cmp = replmd_replPropertyMetaData1_is_newer(&nmd.ctr.ctr1.array[j],
3791 &rmd->ctr.ctr1.array[i]);
3794 /* replace the entry */
3795 nmd.ctr.ctr1.array[j] = rmd->ctr.ctr1.array[i];
3796 if (ar->seq_num == 0) {
3797 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
3798 if (ret != LDB_SUCCESS) {
3799 return replmd_replicated_request_error(ar, ret);
3802 nmd.ctr.ctr1.array[j].local_usn = ar->seq_num;
3807 if (rmd->ctr.ctr1.array[i].attid != DRSUAPI_ATTID_instanceType) {
3808 DEBUG(3,("Discarding older DRS attribute update to %s on %s from %s\n",
3809 msg->elements[i-removed_attrs].name,
3810 ldb_dn_get_linearized(msg->dn),
3811 GUID_string(ar, &rmd->ctr.ctr1.array[i].originating_invocation_id)));
3814 /* we don't want to apply this change so remove the attribute */
3815 ldb_msg_remove_element(msg, &msg->elements[i-removed_attrs]);
3822 if (found) continue;
3824 nmd.ctr.ctr1.array[ni] = rmd->ctr.ctr1.array[i];
3825 if (ar->seq_num == 0) {
3826 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
3827 if (ret != LDB_SUCCESS) {
3828 return replmd_replicated_request_error(ar, ret);
3831 nmd.ctr.ctr1.array[ni].local_usn = ar->seq_num;
3836 * finally correct the size of the meta_data array
3838 nmd.ctr.ctr1.count = ni;
3841 * the rdn attribute (the alias for the name attribute),
3842 * 'cn' for most objects is the last entry in the meta data array
3845 * sort the new meta data array
3847 ret = replmd_replPropertyMetaDataCtr1_sort(&nmd.ctr.ctr1, ar->schema, msg->dn);
3848 if (ret != LDB_SUCCESS) {
3853 * check if some replicated attributes left, otherwise skip the ldb_modify() call
3855 if (msg->num_elements == 0) {
3856 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: skip replace\n",
3859 ar->index_current++;
3860 return replmd_replicated_apply_next(ar);
3863 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: replace %u attributes\n",
3864 ar->index_current, msg->num_elements);
3866 /* create the meta data value */
3867 ndr_err = ndr_push_struct_blob(&nmd_value, msg, &nmd,
3868 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
3869 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
3870 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
3871 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
3875 * when we know that we'll modify the record, add the whenChanged, uSNChanged
3876 * and replPopertyMetaData attributes
3878 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
3879 if (ret != LDB_SUCCESS) {
3880 return replmd_replicated_request_error(ar, ret);
3882 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
3883 if (ret != LDB_SUCCESS) {
3884 return replmd_replicated_request_error(ar, ret);
3886 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
3887 if (ret != LDB_SUCCESS) {
3888 return replmd_replicated_request_error(ar, ret);
3891 replmd_ldb_message_sort(msg, ar->schema);
3893 /* we want to replace the old values */
3894 for (i=0; i < msg->num_elements; i++) {
3895 msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
3899 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_MODIFY, msg);
3900 DEBUG(4, ("DRS replication modify message:\n%s\n", s));
3904 ret = ldb_build_mod_req(&change_req,
3912 LDB_REQ_SET_LOCATION(change_req);
3913 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
3915 /* current partition control needed by "repmd_op_callback" */
3916 ret = ldb_request_add_control(change_req,
3917 DSDB_CONTROL_CURRENT_PARTITION_OID,
3919 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
3921 return ldb_next_request(ar->module, change_req);
3924 static int replmd_replicated_apply_search_callback(struct ldb_request *req,
3925 struct ldb_reply *ares)
3927 struct replmd_replicated_request *ar = talloc_get_type(req->context,
3928 struct replmd_replicated_request);
3932 return ldb_module_done(ar->req, NULL, NULL,
3933 LDB_ERR_OPERATIONS_ERROR);
3935 if (ares->error != LDB_SUCCESS &&
3936 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
3937 return ldb_module_done(ar->req, ares->controls,
3938 ares->response, ares->error);
3941 switch (ares->type) {
3942 case LDB_REPLY_ENTRY:
3943 ar->search_msg = talloc_steal(ar, ares->message);
3946 case LDB_REPLY_REFERRAL:
3947 /* we ignore referrals */
3950 case LDB_REPLY_DONE:
3951 if (ar->search_msg != NULL) {
3952 ret = replmd_replicated_apply_merge(ar);
3954 ret = replmd_replicated_apply_add(ar);
3956 if (ret != LDB_SUCCESS) {
3957 return ldb_module_done(ar->req, NULL, NULL, ret);
3965 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar);
3967 static int replmd_replicated_apply_next(struct replmd_replicated_request *ar)
3969 struct ldb_context *ldb;
3973 struct ldb_request *search_req;
3974 struct ldb_search_options_control *options;
3976 if (ar->index_current >= ar->objs->num_objects) {
3977 /* done with it, go to next stage */
3978 return replmd_replicated_uptodate_vector(ar);
3981 ldb = ldb_module_get_ctx(ar->module);
3982 ar->search_msg = NULL;
3984 tmp_str = ldb_binary_encode(ar, ar->objs->objects[ar->index_current].guid_value);
3985 if (!tmp_str) return replmd_replicated_request_werror(ar, WERR_NOMEM);
3987 filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
3988 if (!filter) return replmd_replicated_request_werror(ar, WERR_NOMEM);
3989 talloc_free(tmp_str);
3991 ret = ldb_build_search_req(&search_req,
4000 replmd_replicated_apply_search_callback,
4002 LDB_REQ_SET_LOCATION(search_req);
4004 ret = ldb_request_add_control(search_req, LDB_CONTROL_SHOW_RECYCLED_OID,
4006 if (ret != LDB_SUCCESS) {
4010 /* we need to cope with cross-partition links, so search for
4011 the GUID over all partitions */
4012 options = talloc(search_req, struct ldb_search_options_control);
4013 if (options == NULL) {
4014 DEBUG(0, (__location__ ": out of memory\n"));
4015 return LDB_ERR_OPERATIONS_ERROR;
4017 options->search_options = LDB_SEARCH_OPTION_PHANTOM_ROOT;
4019 ret = ldb_request_add_control(search_req,
4020 LDB_CONTROL_SEARCH_OPTIONS_OID,
4022 if (ret != LDB_SUCCESS) {
4026 return ldb_next_request(ar->module, search_req);
4029 static int replmd_replicated_uptodate_modify_callback(struct ldb_request *req,
4030 struct ldb_reply *ares)
4032 struct ldb_context *ldb;
4033 struct replmd_replicated_request *ar = talloc_get_type(req->context,
4034 struct replmd_replicated_request);
4035 ldb = ldb_module_get_ctx(ar->module);
4038 return ldb_module_done(ar->req, NULL, NULL,
4039 LDB_ERR_OPERATIONS_ERROR);
4041 if (ares->error != LDB_SUCCESS) {
4042 return ldb_module_done(ar->req, ares->controls,
4043 ares->response, ares->error);
4046 if (ares->type != LDB_REPLY_DONE) {
4047 ldb_asprintf_errstring(ldb, "Invalid LDB reply type %d", ares->type);
4048 return ldb_module_done(ar->req, NULL, NULL,
4049 LDB_ERR_OPERATIONS_ERROR);
4054 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
4057 static int replmd_replicated_uptodate_modify(struct replmd_replicated_request *ar)
4059 struct ldb_context *ldb;
4060 struct ldb_request *change_req;
4061 enum ndr_err_code ndr_err;
4062 struct ldb_message *msg;
4063 struct replUpToDateVectorBlob ouv;
4064 const struct ldb_val *ouv_value;
4065 const struct drsuapi_DsReplicaCursor2CtrEx *ruv;
4066 struct replUpToDateVectorBlob nuv;
4067 struct ldb_val nuv_value;
4068 struct ldb_message_element *nuv_el = NULL;
4069 const struct GUID *our_invocation_id;
4070 struct ldb_message_element *orf_el = NULL;
4071 struct repsFromToBlob nrf;
4072 struct ldb_val *nrf_value = NULL;
4073 struct ldb_message_element *nrf_el = NULL;
4077 time_t t = time(NULL);
4080 uint32_t instanceType;
4082 ldb = ldb_module_get_ctx(ar->module);
4083 ruv = ar->objs->uptodateness_vector;
4089 unix_to_nt_time(&now, t);
4091 if (ar->search_msg == NULL) {
4092 /* this happens for a REPL_OBJ call where we are
4093 creating the target object by replicating it. The
4094 subdomain join code does this for the partition DN
4096 DEBUG(4,(__location__ ": Skipping UDV and repsFrom update as no target DN\n"));
4097 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
4100 instanceType = ldb_msg_find_attr_as_uint(ar->search_msg, "instanceType", 0);
4101 if (! (instanceType & INSTANCE_TYPE_IS_NC_HEAD)) {
4102 DEBUG(4,(__location__ ": Skipping UDV and repsFrom update as not NC root: %s\n",
4103 ldb_dn_get_linearized(ar->search_msg->dn)));
4104 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
4108 * first create the new replUpToDateVector
4110 ouv_value = ldb_msg_find_ldb_val(ar->search_msg, "replUpToDateVector");
4112 ndr_err = ndr_pull_struct_blob(ouv_value, ar, &ouv,
4113 (ndr_pull_flags_fn_t)ndr_pull_replUpToDateVectorBlob);
4114 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
4115 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
4116 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
4119 if (ouv.version != 2) {
4120 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
4125 * the new uptodateness vector will at least
4126 * contain 1 entry, one for the source_dsa
4128 * plus optional values from our old vector and the one from the source_dsa
4130 nuv.ctr.ctr2.count = 1 + ouv.ctr.ctr2.count;
4131 if (ruv) nuv.ctr.ctr2.count += ruv->count;
4132 nuv.ctr.ctr2.cursors = talloc_array(ar,
4133 struct drsuapi_DsReplicaCursor2,
4134 nuv.ctr.ctr2.count);
4135 if (!nuv.ctr.ctr2.cursors) return replmd_replicated_request_werror(ar, WERR_NOMEM);
4137 /* first copy the old vector */
4138 for (i=0; i < ouv.ctr.ctr2.count; i++) {
4139 nuv.ctr.ctr2.cursors[ni] = ouv.ctr.ctr2.cursors[i];
4143 /* get our invocation_id if we have one already attached to the ldb */
4144 our_invocation_id = samdb_ntds_invocation_id(ldb);
4146 /* merge in the source_dsa vector is available */
4147 for (i=0; (ruv && i < ruv->count); i++) {
4150 if (our_invocation_id &&
4151 GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
4152 our_invocation_id)) {
4156 for (j=0; j < ni; j++) {
4157 if (!GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
4158 &nuv.ctr.ctr2.cursors[j].source_dsa_invocation_id)) {
4165 * we update only the highest_usn and not the latest_sync_success time,
4166 * because the last success stands for direct replication
4168 if (ruv->cursors[i].highest_usn > nuv.ctr.ctr2.cursors[j].highest_usn) {
4169 nuv.ctr.ctr2.cursors[j].highest_usn = ruv->cursors[i].highest_usn;
4174 if (found) continue;
4176 /* if it's not there yet, add it */
4177 nuv.ctr.ctr2.cursors[ni] = ruv->cursors[i];
4182 * merge in the current highwatermark for the source_dsa
4185 for (j=0; j < ni; j++) {
4186 if (!GUID_equal(&ar->objs->source_dsa->source_dsa_invocation_id,
4187 &nuv.ctr.ctr2.cursors[j].source_dsa_invocation_id)) {
4194 * here we update the highest_usn and last_sync_success time
4195 * because we're directly replicating from the source_dsa
4197 * and use the tmp_highest_usn because this is what we have just applied
4200 nuv.ctr.ctr2.cursors[j].highest_usn = ar->objs->source_dsa->highwatermark.tmp_highest_usn;
4201 nuv.ctr.ctr2.cursors[j].last_sync_success = now;
4206 * here we update the highest_usn and last_sync_success time
4207 * because we're directly replicating from the source_dsa
4209 * and use the tmp_highest_usn because this is what we have just applied
4212 nuv.ctr.ctr2.cursors[ni].source_dsa_invocation_id= ar->objs->source_dsa->source_dsa_invocation_id;
4213 nuv.ctr.ctr2.cursors[ni].highest_usn = ar->objs->source_dsa->highwatermark.tmp_highest_usn;
4214 nuv.ctr.ctr2.cursors[ni].last_sync_success = now;
4219 * finally correct the size of the cursors array
4221 nuv.ctr.ctr2.count = ni;
4226 TYPESAFE_QSORT(nuv.ctr.ctr2.cursors, nuv.ctr.ctr2.count, drsuapi_DsReplicaCursor2_compare);
4229 * create the change ldb_message
4231 msg = ldb_msg_new(ar);
4232 if (!msg) return replmd_replicated_request_werror(ar, WERR_NOMEM);
4233 msg->dn = ar->search_msg->dn;
4235 ndr_err = ndr_push_struct_blob(&nuv_value, msg, &nuv,
4236 (ndr_push_flags_fn_t)ndr_push_replUpToDateVectorBlob);
4237 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
4238 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
4239 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
4241 ret = ldb_msg_add_value(msg, "replUpToDateVector", &nuv_value, &nuv_el);
4242 if (ret != LDB_SUCCESS) {
4243 return replmd_replicated_request_error(ar, ret);
4245 nuv_el->flags = LDB_FLAG_MOD_REPLACE;
4248 * now create the new repsFrom value from the given repsFromTo1 structure
4252 nrf.ctr.ctr1 = *ar->objs->source_dsa;
4253 nrf.ctr.ctr1.highwatermark.highest_usn = nrf.ctr.ctr1.highwatermark.tmp_highest_usn;
4256 * first see if we already have a repsFrom value for the current source dsa
4257 * if so we'll later replace this value
4259 orf_el = ldb_msg_find_element(ar->search_msg, "repsFrom");
4261 for (i=0; i < orf_el->num_values; i++) {
4262 struct repsFromToBlob *trf;
4264 trf = talloc(ar, struct repsFromToBlob);
4265 if (!trf) return replmd_replicated_request_werror(ar, WERR_NOMEM);
4267 ndr_err = ndr_pull_struct_blob(&orf_el->values[i], trf, trf,
4268 (ndr_pull_flags_fn_t)ndr_pull_repsFromToBlob);
4269 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
4270 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
4271 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
4274 if (trf->version != 1) {
4275 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
4279 * we compare the source dsa objectGUID not the invocation_id
4280 * because we want only one repsFrom value per source dsa
4281 * and when the invocation_id of the source dsa has changed we don't need
4282 * the old repsFrom with the old invocation_id
4284 if (!GUID_equal(&trf->ctr.ctr1.source_dsa_obj_guid,
4285 &ar->objs->source_dsa->source_dsa_obj_guid)) {
4291 nrf_value = &orf_el->values[i];
4296 * copy over all old values to the new ldb_message
4298 ret = ldb_msg_add_empty(msg, "repsFrom", 0, &nrf_el);
4299 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
4304 * if we haven't found an old repsFrom value for the current source dsa
4305 * we'll add a new value
4308 struct ldb_val zero_value;
4309 ZERO_STRUCT(zero_value);
4310 ret = ldb_msg_add_value(msg, "repsFrom", &zero_value, &nrf_el);
4311 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
4313 nrf_value = &nrf_el->values[nrf_el->num_values - 1];
4316 /* we now fill the value which is already attached to ldb_message */
4317 ndr_err = ndr_push_struct_blob(nrf_value, msg,
4319 (ndr_push_flags_fn_t)ndr_push_repsFromToBlob);
4320 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
4321 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
4322 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
4326 * the ldb_message_element for the attribute, has all the old values and the new one
4327 * so we'll replace the whole attribute with all values
4329 nrf_el->flags = LDB_FLAG_MOD_REPLACE;
4331 if (CHECK_DEBUGLVL(4)) {
4332 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_MODIFY, msg);
4333 DEBUG(4, ("DRS replication uptodate modify message:\n%s\n", s));
4337 /* prepare the ldb_modify() request */
4338 ret = ldb_build_mod_req(&change_req,
4344 replmd_replicated_uptodate_modify_callback,
4346 LDB_REQ_SET_LOCATION(change_req);
4347 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
4349 return ldb_next_request(ar->module, change_req);
4352 static int replmd_replicated_uptodate_search_callback(struct ldb_request *req,
4353 struct ldb_reply *ares)
4355 struct replmd_replicated_request *ar = talloc_get_type(req->context,
4356 struct replmd_replicated_request);
4360 return ldb_module_done(ar->req, NULL, NULL,
4361 LDB_ERR_OPERATIONS_ERROR);
4363 if (ares->error != LDB_SUCCESS &&
4364 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
4365 return ldb_module_done(ar->req, ares->controls,
4366 ares->response, ares->error);
4369 switch (ares->type) {
4370 case LDB_REPLY_ENTRY:
4371 ar->search_msg = talloc_steal(ar, ares->message);
4374 case LDB_REPLY_REFERRAL:
4375 /* we ignore referrals */
4378 case LDB_REPLY_DONE:
4379 ret = replmd_replicated_uptodate_modify(ar);
4380 if (ret != LDB_SUCCESS) {
4381 return ldb_module_done(ar->req, NULL, NULL, ret);
4390 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar)
4392 struct ldb_context *ldb;
4394 static const char *attrs[] = {
4395 "replUpToDateVector",
4400 struct ldb_request *search_req;
4402 ldb = ldb_module_get_ctx(ar->module);
4403 ar->search_msg = NULL;
4405 ret = ldb_build_search_req(&search_req,
4408 ar->objs->partition_dn,
4414 replmd_replicated_uptodate_search_callback,
4416 LDB_REQ_SET_LOCATION(search_req);
4417 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
4419 return ldb_next_request(ar->module, search_req);
4424 static int replmd_extended_replicated_objects(struct ldb_module *module, struct ldb_request *req)
4426 struct ldb_context *ldb;
4427 struct dsdb_extended_replicated_objects *objs;
4428 struct replmd_replicated_request *ar;
4429 struct ldb_control **ctrls;
4432 struct replmd_private *replmd_private =
4433 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
4434 struct dsdb_control_replicated_update *rep_update;
4436 ldb = ldb_module_get_ctx(module);
4438 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_extended_replicated_objects\n");
4440 objs = talloc_get_type(req->op.extended.data, struct dsdb_extended_replicated_objects);
4442 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: invalid extended data\n");
4443 return LDB_ERR_PROTOCOL_ERROR;
4446 if (objs->version != DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION) {
4447 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: extended data invalid version [%u != %u]\n",
4448 objs->version, DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION);
4449 return LDB_ERR_PROTOCOL_ERROR;
4452 ar = replmd_ctx_init(module, req);
4454 return LDB_ERR_OPERATIONS_ERROR;
4456 /* Set the flags to have the replmd_op_callback run over the full set of objects */
4457 ar->apply_mode = true;
4459 ar->schema = dsdb_get_schema(ldb, ar);
4461 ldb_debug_set(ldb, LDB_DEBUG_FATAL, "replmd_ctx_init: no loaded schema found\n");
4463 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
4464 return LDB_ERR_CONSTRAINT_VIOLATION;
4467 ctrls = req->controls;
4469 if (req->controls) {
4470 req->controls = talloc_memdup(ar, req->controls,
4471 talloc_get_size(req->controls));
4472 if (!req->controls) return replmd_replicated_request_werror(ar, WERR_NOMEM);
4475 /* This allows layers further down to know if a change came in
4476 over replication and what the replication flags were */
4477 rep_update = talloc_zero(ar, struct dsdb_control_replicated_update);
4478 if (rep_update == NULL) {
4479 return ldb_module_oom(module);
4481 rep_update->dsdb_repl_flags = objs->dsdb_repl_flags;
4483 ret = ldb_request_add_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID, false, rep_update);
4484 if (ret != LDB_SUCCESS) {
4488 /* If this change contained linked attributes in the body
4489 * (rather than in the links section) we need to update
4490 * backlinks in linked_attributes */
4491 ret = ldb_request_add_control(req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
4492 if (ret != LDB_SUCCESS) {
4496 ar->controls = req->controls;
4497 req->controls = ctrls;
4499 DEBUG(4,("linked_attributes_count=%u\n", objs->linked_attributes_count));
4501 /* save away the linked attributes for the end of the
4503 for (i=0; i<ar->objs->linked_attributes_count; i++) {
4504 struct la_entry *la_entry;
4506 if (replmd_private->la_ctx == NULL) {
4507 replmd_private->la_ctx = talloc_new(replmd_private);
4509 la_entry = talloc(replmd_private->la_ctx, struct la_entry);
4510 if (la_entry == NULL) {
4512 return LDB_ERR_OPERATIONS_ERROR;
4514 la_entry->la = talloc(la_entry, struct drsuapi_DsReplicaLinkedAttribute);
4515 if (la_entry->la == NULL) {
4516 talloc_free(la_entry);
4518 return LDB_ERR_OPERATIONS_ERROR;
4520 *la_entry->la = ar->objs->linked_attributes[i];
4522 /* we need to steal the non-scalars so they stay
4523 around until the end of the transaction */
4524 talloc_steal(la_entry->la, la_entry->la->identifier);
4525 talloc_steal(la_entry->la, la_entry->la->value.blob);
4527 DLIST_ADD(replmd_private->la_list, la_entry);
4530 return replmd_replicated_apply_next(ar);
4534 process one linked attribute structure
4536 static int replmd_process_linked_attribute(struct ldb_module *module,
4537 struct la_entry *la_entry,
4538 struct ldb_request *parent)
4540 struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
4541 struct ldb_context *ldb = ldb_module_get_ctx(module);
4542 struct ldb_message *msg;
4543 TALLOC_CTX *tmp_ctx = talloc_new(la_entry);
4544 const struct dsdb_schema *schema = dsdb_get_schema(ldb, tmp_ctx);
4546 const struct dsdb_attribute *attr;
4547 struct dsdb_dn *dsdb_dn;
4548 uint64_t seq_num = 0;
4549 struct ldb_message_element *old_el;
4551 time_t t = time(NULL);
4552 struct ldb_result *res;
4553 const char *attrs[2];
4554 struct parsed_dn *pdn_list, *pdn;
4555 struct GUID guid = GUID_zero();
4557 bool active = (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)?true:false;
4558 const struct GUID *our_invocation_id;
4561 linked_attributes[0]:
4562 &objs->linked_attributes[i]: struct drsuapi_DsReplicaLinkedAttribute
4564 identifier: struct drsuapi_DsReplicaObjectIdentifier
4565 __ndr_size : 0x0000003a (58)
4566 __ndr_size_sid : 0x00000000 (0)
4567 guid : 8e95b6a9-13dd-4158-89db-3220a5be5cc7
4569 __ndr_size_dn : 0x00000000 (0)
4571 attid : DRSUAPI_ATTID_member (0x1F)
4572 value: struct drsuapi_DsAttributeValue
4573 __ndr_size : 0x0000007e (126)
4575 blob : DATA_BLOB length=126
4576 flags : 0x00000001 (1)
4577 1: DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE
4578 originating_add_time : Wed Sep 2 22:20:01 2009 EST
4579 meta_data: struct drsuapi_DsReplicaMetaData
4580 version : 0x00000015 (21)
4581 originating_change_time : Wed Sep 2 23:39:07 2009 EST
4582 originating_invocation_id: 794640f3-18cf-40ee-a211-a93992b67a64
4583 originating_usn : 0x000000000001e19c (123292)
4585 (for cases where the link is to a normal DN)
4586 &target: struct drsuapi_DsReplicaObjectIdentifier3
4587 __ndr_size : 0x0000007e (126)
4588 __ndr_size_sid : 0x0000001c (28)
4589 guid : 7639e594-db75-4086-b0d4-67890ae46031
4590 sid : S-1-5-21-2848215498-2472035911-1947525656-19924
4591 __ndr_size_dn : 0x00000022 (34)
4592 dn : 'CN=UOne,OU=TestOU,DC=vsofs8,DC=com'
4595 /* find the attribute being modified */
4596 attr = dsdb_attribute_by_attributeID_id(schema, la->attid);
4598 DEBUG(0, (__location__ ": Unable to find attributeID 0x%x\n", la->attid));
4599 talloc_free(tmp_ctx);
4600 return LDB_ERR_OPERATIONS_ERROR;
4603 attrs[0] = attr->lDAPDisplayName;
4606 /* get the existing message from the db for the object with
4607 this GUID, returning attribute being modified. We will then
4608 use this msg as the basis for a modify call */
4609 ret = dsdb_module_search(module, tmp_ctx, &res, NULL, LDB_SCOPE_SUBTREE, attrs,
4610 DSDB_FLAG_NEXT_MODULE |
4611 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
4612 DSDB_SEARCH_SHOW_RECYCLED |
4613 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
4614 DSDB_SEARCH_REVEAL_INTERNALS,
4616 "objectGUID=%s", GUID_string(tmp_ctx, &la->identifier->guid));
4617 if (ret != LDB_SUCCESS) {
4618 talloc_free(tmp_ctx);
4621 if (res->count != 1) {
4622 ldb_asprintf_errstring(ldb, "DRS linked attribute for GUID %s - DN not found",
4623 GUID_string(tmp_ctx, &la->identifier->guid));
4624 talloc_free(tmp_ctx);
4625 return LDB_ERR_NO_SUCH_OBJECT;
4629 if (msg->num_elements == 0) {
4630 ret = ldb_msg_add_empty(msg, attr->lDAPDisplayName, LDB_FLAG_MOD_REPLACE, &old_el);
4631 if (ret != LDB_SUCCESS) {
4632 ldb_module_oom(module);
4633 talloc_free(tmp_ctx);
4634 return LDB_ERR_OPERATIONS_ERROR;
4637 old_el = &msg->elements[0];
4638 old_el->flags = LDB_FLAG_MOD_REPLACE;
4641 /* parse the existing links */
4642 ret = get_parsed_dns(module, tmp_ctx, old_el, &pdn_list, attr->syntax->ldap_oid, parent);
4643 if (ret != LDB_SUCCESS) {
4644 talloc_free(tmp_ctx);
4648 /* get our invocationId */
4649 our_invocation_id = samdb_ntds_invocation_id(ldb);
4650 if (!our_invocation_id) {
4651 ldb_debug_set(ldb, LDB_DEBUG_ERROR, __location__ ": unable to find invocationId\n");
4652 talloc_free(tmp_ctx);
4653 return LDB_ERR_OPERATIONS_ERROR;
4656 ret = replmd_check_upgrade_links(pdn_list, old_el->num_values, old_el, our_invocation_id);
4657 if (ret != LDB_SUCCESS) {
4658 talloc_free(tmp_ctx);
4662 status = dsdb_dn_la_from_blob(ldb, attr, schema, tmp_ctx, la->value.blob, &dsdb_dn);
4663 if (!W_ERROR_IS_OK(status)) {
4664 ldb_asprintf_errstring(ldb, "Failed to parsed linked attribute blob for %s on %s - %s\n",
4665 old_el->name, ldb_dn_get_linearized(msg->dn), win_errstr(status));
4666 return LDB_ERR_OPERATIONS_ERROR;
4669 ntstatus = dsdb_get_extended_dn_guid(dsdb_dn->dn, &guid, "GUID");
4670 if (!NT_STATUS_IS_OK(ntstatus) && active) {
4671 ldb_asprintf_errstring(ldb, "Failed to find GUID in linked attribute blob for %s on %s from %s",
4673 ldb_dn_get_linearized(dsdb_dn->dn),
4674 ldb_dn_get_linearized(msg->dn));
4675 return LDB_ERR_OPERATIONS_ERROR;
4678 /* re-resolve the DN by GUID, as the DRS server may give us an
4680 ret = dsdb_module_dn_by_guid(module, dsdb_dn, &guid, &dsdb_dn->dn, parent);
4681 if (ret != LDB_SUCCESS) {
4682 DEBUG(2,(__location__ ": WARNING: Failed to re-resolve GUID %s - using %s\n",
4683 GUID_string(tmp_ctx, &guid),
4684 ldb_dn_get_linearized(dsdb_dn->dn)));
4687 /* see if this link already exists */
4688 pdn = parsed_dn_find(pdn_list, old_el->num_values, &guid, dsdb_dn->dn);
4690 /* see if this update is newer than what we have already */
4691 struct GUID invocation_id = GUID_zero();
4692 uint32_t version = 0;
4693 uint32_t originating_usn = 0;
4694 NTTIME change_time = 0;
4695 uint32_t rmd_flags = dsdb_dn_rmd_flags(pdn->dsdb_dn->dn);
4697 dsdb_get_extended_dn_guid(pdn->dsdb_dn->dn, &invocation_id, "RMD_INVOCID");
4698 dsdb_get_extended_dn_uint32(pdn->dsdb_dn->dn, &version, "RMD_VERSION");
4699 dsdb_get_extended_dn_uint32(pdn->dsdb_dn->dn, &originating_usn, "RMD_ORIGINATING_USN");
4700 dsdb_get_extended_dn_nttime(pdn->dsdb_dn->dn, &change_time, "RMD_CHANGETIME");
4702 if (!replmd_update_is_newer(&invocation_id,
4703 &la->meta_data.originating_invocation_id,
4705 la->meta_data.version,
4707 la->meta_data.originating_change_time)) {
4708 DEBUG(3,("Discarding older DRS linked attribute update to %s on %s from %s\n",
4709 old_el->name, ldb_dn_get_linearized(msg->dn),
4710 GUID_string(tmp_ctx, &la->meta_data.originating_invocation_id)));
4711 talloc_free(tmp_ctx);
4715 /* get a seq_num for this change */
4716 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
4717 if (ret != LDB_SUCCESS) {
4718 talloc_free(tmp_ctx);
4722 if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
4723 /* remove the existing backlink */
4724 ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid, false, attr, false);
4725 if (ret != LDB_SUCCESS) {
4726 talloc_free(tmp_ctx);
4731 ret = replmd_update_la_val(tmp_ctx, pdn->v, dsdb_dn, pdn->dsdb_dn,
4732 &la->meta_data.originating_invocation_id,
4733 la->meta_data.originating_usn, seq_num,
4734 la->meta_data.originating_change_time,
4735 la->meta_data.version,
4737 if (ret != LDB_SUCCESS) {
4738 talloc_free(tmp_ctx);
4743 /* add the new backlink */
4744 ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid, true, attr, false);
4745 if (ret != LDB_SUCCESS) {
4746 talloc_free(tmp_ctx);
4751 /* get a seq_num for this change */
4752 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
4753 if (ret != LDB_SUCCESS) {
4754 talloc_free(tmp_ctx);
4758 old_el->values = talloc_realloc(msg->elements, old_el->values,
4759 struct ldb_val, old_el->num_values+1);
4760 if (!old_el->values) {
4761 ldb_module_oom(module);
4762 return LDB_ERR_OPERATIONS_ERROR;
4764 old_el->num_values++;
4766 ret = replmd_build_la_val(tmp_ctx, &old_el->values[old_el->num_values-1], dsdb_dn,
4767 &la->meta_data.originating_invocation_id,
4768 la->meta_data.originating_usn, seq_num,
4769 la->meta_data.originating_change_time,
4770 la->meta_data.version,
4771 (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)?false:true);
4772 if (ret != LDB_SUCCESS) {
4773 talloc_free(tmp_ctx);
4778 ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid,
4780 if (ret != LDB_SUCCESS) {
4781 talloc_free(tmp_ctx);
4787 /* we only change whenChanged and uSNChanged if the seq_num
4789 ret = add_time_element(msg, "whenChanged", t);
4790 if (ret != LDB_SUCCESS) {
4791 talloc_free(tmp_ctx);
4796 ret = add_uint64_element(ldb, msg, "uSNChanged", seq_num);
4797 if (ret != LDB_SUCCESS) {
4798 talloc_free(tmp_ctx);
4803 old_el = ldb_msg_find_element(msg, attr->lDAPDisplayName);
4804 if (old_el == NULL) {
4805 talloc_free(tmp_ctx);
4806 return ldb_operr(ldb);
4809 ret = dsdb_check_single_valued_link(attr, old_el);
4810 if (ret != LDB_SUCCESS) {
4811 talloc_free(tmp_ctx);
4815 old_el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
4817 ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE, parent);
4818 if (ret != LDB_SUCCESS) {
4819 ldb_debug(ldb, LDB_DEBUG_WARNING, "Failed to apply linked attribute change '%s'\n%s\n",
4821 ldb_ldif_message_string(ldb, tmp_ctx, LDB_CHANGETYPE_MODIFY, msg));
4822 talloc_free(tmp_ctx);
4826 talloc_free(tmp_ctx);
4831 static int replmd_extended(struct ldb_module *module, struct ldb_request *req)
4833 if (strcmp(req->op.extended.oid, DSDB_EXTENDED_REPLICATED_OBJECTS_OID) == 0) {
4834 return replmd_extended_replicated_objects(module, req);
4837 return ldb_next_request(module, req);
4842 we hook into the transaction operations to allow us to
4843 perform the linked attribute updates at the end of the whole
4844 transaction. This allows a forward linked attribute to be created
4845 before the object is created. During a vampire, w2k8 sends us linked
4846 attributes before the objects they are part of.
4848 static int replmd_start_transaction(struct ldb_module *module)
4850 /* create our private structure for this transaction */
4851 struct replmd_private *replmd_private = talloc_get_type(ldb_module_get_private(module),
4852 struct replmd_private);
4853 replmd_txn_cleanup(replmd_private);
4855 /* free any leftover mod_usn records from cancelled
4857 while (replmd_private->ncs) {
4858 struct nc_entry *e = replmd_private->ncs;
4859 DLIST_REMOVE(replmd_private->ncs, e);
4863 return ldb_next_start_trans(module);
4867 on prepare commit we loop over our queued la_context structures and
4870 static int replmd_prepare_commit(struct ldb_module *module)
4872 struct replmd_private *replmd_private =
4873 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
4874 struct la_entry *la, *prev;
4875 struct la_backlink *bl;
4878 /* walk the list backwards, to do the first entry first, as we
4879 * added the entries with DLIST_ADD() which puts them at the
4880 * start of the list */
4881 for (la = DLIST_TAIL(replmd_private->la_list); la; la=prev) {
4882 prev = DLIST_PREV(la);
4883 DLIST_REMOVE(replmd_private->la_list, la);
4884 ret = replmd_process_linked_attribute(module, la, NULL);
4885 if (ret != LDB_SUCCESS) {
4886 replmd_txn_cleanup(replmd_private);
4891 /* process our backlink list, creating and deleting backlinks
4893 for (bl=replmd_private->la_backlinks; bl; bl=bl->next) {
4894 ret = replmd_process_backlink(module, bl, NULL);
4895 if (ret != LDB_SUCCESS) {
4896 replmd_txn_cleanup(replmd_private);
4901 replmd_txn_cleanup(replmd_private);
4903 /* possibly change @REPLCHANGED */
4904 ret = replmd_notify_store(module, NULL);
4905 if (ret != LDB_SUCCESS) {
4909 return ldb_next_prepare_commit(module);
4912 static int replmd_del_transaction(struct ldb_module *module)
4914 struct replmd_private *replmd_private =
4915 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
4916 replmd_txn_cleanup(replmd_private);
4918 return ldb_next_del_trans(module);
4922 static const struct ldb_module_ops ldb_repl_meta_data_module_ops = {
4923 .name = "repl_meta_data",
4924 .init_context = replmd_init,
4926 .modify = replmd_modify,
4927 .rename = replmd_rename,
4928 .del = replmd_delete,
4929 .extended = replmd_extended,
4930 .start_transaction = replmd_start_transaction,
4931 .prepare_commit = replmd_prepare_commit,
4932 .del_transaction = replmd_del_transaction,
4935 int ldb_repl_meta_data_module_init(const char *version)
4937 LDB_MODULE_CHECK_VERSION(version);
4938 return ldb_register_module(&ldb_repl_meta_data_module_ops);