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 struct replmd_private {
55 struct la_entry *la_list;
57 struct la_backlink *la_backlinks;
59 struct nc_entry *prev, *next;
62 uint64_t mod_usn_urgent;
67 struct la_entry *next, *prev;
68 struct drsuapi_DsReplicaLinkedAttribute *la;
71 struct replmd_replicated_request {
72 struct ldb_module *module;
73 struct ldb_request *req;
75 const struct dsdb_schema *schema;
77 /* the controls we pass down */
78 struct ldb_control **controls;
80 /* details for the mode where we apply a bunch of inbound replication meessages */
82 uint32_t index_current;
83 struct dsdb_extended_replicated_objects *objs;
85 struct ldb_message *search_msg;
91 enum urgent_situation {
92 REPL_URGENT_ON_CREATE = 1,
93 REPL_URGENT_ON_UPDATE = 2,
94 REPL_URGENT_ON_DELETE = 4
99 const char *update_name;
100 enum urgent_situation repl_situation;
101 } urgent_objects[] = {
102 {"nTDSDSA", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE)},
103 {"crossRef", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE)},
104 {"attributeSchema", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
105 {"classSchema", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
106 {"secret", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
107 {"rIDManager", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
111 /* Attributes looked for when updating or deleting, to check for a urgent replication needed */
112 static const char *urgent_attrs[] = {
115 "userAccountControl",
120 static bool replmd_check_urgent_objectclass(const struct ldb_message_element *objectclass_el,
121 enum urgent_situation situation)
124 for (i=0; urgent_objects[i].update_name; i++) {
126 if ((situation & urgent_objects[i].repl_situation) == 0) {
130 for (j=0; j<objectclass_el->num_values; j++) {
131 const struct ldb_val *v = &objectclass_el->values[j];
132 if (ldb_attr_cmp((const char *)v->data, urgent_objects[i].update_name) == 0) {
140 static bool replmd_check_urgent_attribute(const struct ldb_message_element *el)
142 if (ldb_attr_in_list(urgent_attrs, el->name)) {
149 static int replmd_replicated_apply_next(struct replmd_replicated_request *ar);
152 initialise the module
153 allocate the private structure and build the list
154 of partition DNs for use by replmd_notify()
156 static int replmd_init(struct ldb_module *module)
158 struct replmd_private *replmd_private;
159 struct ldb_context *ldb = ldb_module_get_ctx(module);
161 replmd_private = talloc_zero(module, struct replmd_private);
162 if (replmd_private == NULL) {
164 return LDB_ERR_OPERATIONS_ERROR;
166 ldb_module_set_private(module, replmd_private);
168 return ldb_next_init(module);
172 cleanup our per-transaction contexts
174 static void replmd_txn_cleanup(struct replmd_private *replmd_private)
176 talloc_free(replmd_private->la_ctx);
177 replmd_private->la_list = NULL;
178 replmd_private->la_ctx = NULL;
180 talloc_free(replmd_private->bl_ctx);
181 replmd_private->la_backlinks = NULL;
182 replmd_private->bl_ctx = NULL;
187 struct la_backlink *next, *prev;
188 const char *attr_name;
189 struct GUID forward_guid, target_guid;
194 process a backlinks we accumulated during a transaction, adding and
195 deleting the backlinks from the target objects
197 static int replmd_process_backlink(struct ldb_module *module, struct la_backlink *bl, struct ldb_request *parent)
199 struct ldb_dn *target_dn, *source_dn;
201 struct ldb_context *ldb = ldb_module_get_ctx(module);
202 struct ldb_message *msg;
203 TALLOC_CTX *tmp_ctx = talloc_new(bl);
209 - construct ldb_message
210 - either an add or a delete
212 ret = dsdb_module_dn_by_guid(module, tmp_ctx, &bl->target_guid, &target_dn, parent);
213 if (ret != LDB_SUCCESS) {
214 DEBUG(2,(__location__ ": WARNING: Failed to find target DN for linked attribute with GUID %s\n",
215 GUID_string(bl, &bl->target_guid)));
219 ret = dsdb_module_dn_by_guid(module, tmp_ctx, &bl->forward_guid, &source_dn, parent);
220 if (ret != LDB_SUCCESS) {
221 ldb_asprintf_errstring(ldb, "Failed to find source DN for linked attribute with GUID %s\n",
222 GUID_string(bl, &bl->forward_guid));
223 talloc_free(tmp_ctx);
227 msg = ldb_msg_new(tmp_ctx);
229 ldb_module_oom(module);
230 talloc_free(tmp_ctx);
231 return LDB_ERR_OPERATIONS_ERROR;
234 /* construct a ldb_message for adding/deleting the backlink */
236 dn_string = ldb_dn_get_extended_linearized(tmp_ctx, source_dn, 1);
238 ldb_module_oom(module);
239 talloc_free(tmp_ctx);
240 return LDB_ERR_OPERATIONS_ERROR;
242 ret = ldb_msg_add_steal_string(msg, bl->attr_name, dn_string);
243 if (ret != LDB_SUCCESS) {
244 talloc_free(tmp_ctx);
247 msg->elements[0].flags = bl->active?LDB_FLAG_MOD_ADD:LDB_FLAG_MOD_DELETE;
249 /* a backlink should never be single valued. Unfortunately the
250 exchange schema has a attribute
251 msExchBridgeheadedLocalConnectorsDNBL which is single
252 valued and a backlink. We need to cope with that by
253 ignoring the single value flag */
254 msg->elements[0].flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
256 ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE, parent);
257 if (ret != LDB_SUCCESS) {
258 ldb_asprintf_errstring(ldb, "Failed to %s backlink from %s to %s - %s",
259 bl->active?"add":"remove",
260 ldb_dn_get_linearized(source_dn),
261 ldb_dn_get_linearized(target_dn),
263 talloc_free(tmp_ctx);
266 talloc_free(tmp_ctx);
271 add a backlink to the list of backlinks to add/delete in the prepare
274 static int replmd_add_backlink(struct ldb_module *module, const struct dsdb_schema *schema,
275 struct GUID *forward_guid, struct GUID *target_guid,
276 bool active, const struct dsdb_attribute *schema_attr, bool immediate)
278 const struct dsdb_attribute *target_attr;
279 struct la_backlink *bl;
280 struct replmd_private *replmd_private =
281 talloc_get_type_abort(ldb_module_get_private(module), struct replmd_private);
283 target_attr = dsdb_attribute_by_linkID(schema, schema_attr->linkID ^ 1);
286 * windows 2003 has a broken schema where the
287 * definition of msDS-IsDomainFor is missing (which is
288 * supposed to be the backlink of the
289 * msDS-HasDomainNCs attribute
294 /* see if its already in the list */
295 for (bl=replmd_private->la_backlinks; bl; bl=bl->next) {
296 if (GUID_equal(forward_guid, &bl->forward_guid) &&
297 GUID_equal(target_guid, &bl->target_guid) &&
298 (target_attr->lDAPDisplayName == bl->attr_name ||
299 strcmp(target_attr->lDAPDisplayName, bl->attr_name) == 0)) {
305 /* we found an existing one */
306 if (bl->active == active) {
309 DLIST_REMOVE(replmd_private->la_backlinks, bl);
314 if (replmd_private->bl_ctx == NULL) {
315 replmd_private->bl_ctx = talloc_new(replmd_private);
316 if (replmd_private->bl_ctx == NULL) {
317 ldb_module_oom(module);
318 return LDB_ERR_OPERATIONS_ERROR;
323 bl = talloc(replmd_private->bl_ctx, struct la_backlink);
325 ldb_module_oom(module);
326 return LDB_ERR_OPERATIONS_ERROR;
329 /* Ensure the schema does not go away before the bl->attr_name is used */
330 if (!talloc_reference(bl, schema)) {
332 ldb_module_oom(module);
333 return LDB_ERR_OPERATIONS_ERROR;
336 bl->attr_name = target_attr->lDAPDisplayName;
337 bl->forward_guid = *forward_guid;
338 bl->target_guid = *target_guid;
341 /* the caller may ask for this backlink to be processed
344 int ret = replmd_process_backlink(module, bl, NULL);
349 DLIST_ADD(replmd_private->la_backlinks, bl);
356 * Callback for most write operations in this module:
358 * notify the repl task that a object has changed. The notifies are
359 * gathered up in the replmd_private structure then written to the
360 * @REPLCHANGED object in each partition during the prepare_commit
362 static int replmd_op_callback(struct ldb_request *req, struct ldb_reply *ares)
365 struct replmd_replicated_request *ac =
366 talloc_get_type_abort(req->context, struct replmd_replicated_request);
367 struct replmd_private *replmd_private =
368 talloc_get_type_abort(ldb_module_get_private(ac->module), struct replmd_private);
369 struct nc_entry *modified_partition;
370 struct ldb_control *partition_ctrl;
371 const struct dsdb_control_current_partition *partition;
373 struct ldb_control **controls;
375 partition_ctrl = ldb_reply_get_control(ares, DSDB_CONTROL_CURRENT_PARTITION_OID);
377 controls = ares->controls;
378 if (ldb_request_get_control(ac->req,
379 DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
381 * Remove the current partition control from what we pass up
382 * the chain if it hasn't been requested manually.
384 controls = ldb_controls_except_specified(ares->controls, ares,
388 if (ares->error != LDB_SUCCESS) {
389 DEBUG(0,("%s failure. Error is: %s\n", __FUNCTION__, ldb_strerror(ares->error)));
390 return ldb_module_done(ac->req, controls,
391 ares->response, ares->error);
394 if (ares->type != LDB_REPLY_DONE) {
395 ldb_set_errstring(ldb_module_get_ctx(ac->module), "Invalid reply type for notify\n!");
396 return ldb_module_done(ac->req, NULL,
397 NULL, LDB_ERR_OPERATIONS_ERROR);
400 if (!partition_ctrl) {
401 ldb_set_errstring(ldb_module_get_ctx(ac->module),"No partition control on reply");
402 return ldb_module_done(ac->req, NULL,
403 NULL, LDB_ERR_OPERATIONS_ERROR);
406 partition = talloc_get_type_abort(partition_ctrl->data,
407 struct dsdb_control_current_partition);
409 if (ac->seq_num > 0) {
410 for (modified_partition = replmd_private->ncs; modified_partition;
411 modified_partition = modified_partition->next) {
412 if (ldb_dn_compare(modified_partition->dn, partition->dn) == 0) {
417 if (modified_partition == NULL) {
418 modified_partition = talloc_zero(replmd_private, struct nc_entry);
419 if (!modified_partition) {
420 ldb_oom(ldb_module_get_ctx(ac->module));
421 return ldb_module_done(ac->req, NULL,
422 NULL, LDB_ERR_OPERATIONS_ERROR);
424 modified_partition->dn = ldb_dn_copy(modified_partition, partition->dn);
425 if (!modified_partition->dn) {
426 ldb_oom(ldb_module_get_ctx(ac->module));
427 return ldb_module_done(ac->req, NULL,
428 NULL, LDB_ERR_OPERATIONS_ERROR);
430 DLIST_ADD(replmd_private->ncs, modified_partition);
433 if (ac->seq_num > modified_partition->mod_usn) {
434 modified_partition->mod_usn = ac->seq_num;
436 modified_partition->mod_usn_urgent = ac->seq_num;
441 if (ac->apply_mode) {
445 ret = replmd_replicated_apply_next(ac);
446 if (ret != LDB_SUCCESS) {
447 return ldb_module_done(ac->req, NULL, NULL, ret);
451 /* free the partition control container here, for the
452 * common path. Other cases will have it cleaned up
453 * eventually with the ares */
454 talloc_free(partition_ctrl);
455 return ldb_module_done(ac->req, controls,
456 ares->response, LDB_SUCCESS);
462 * update a @REPLCHANGED record in each partition if there have been
463 * any writes of replicated data in the partition
465 static int replmd_notify_store(struct ldb_module *module, struct ldb_request *parent)
467 struct replmd_private *replmd_private =
468 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
470 while (replmd_private->ncs) {
472 struct nc_entry *modified_partition = replmd_private->ncs;
474 ret = dsdb_module_save_partition_usn(module, modified_partition->dn,
475 modified_partition->mod_usn,
476 modified_partition->mod_usn_urgent, parent);
477 if (ret != LDB_SUCCESS) {
478 DEBUG(0,(__location__ ": Failed to save partition uSN for %s\n",
479 ldb_dn_get_linearized(modified_partition->dn)));
482 DLIST_REMOVE(replmd_private->ncs, modified_partition);
483 talloc_free(modified_partition);
491 created a replmd_replicated_request context
493 static struct replmd_replicated_request *replmd_ctx_init(struct ldb_module *module,
494 struct ldb_request *req)
496 struct ldb_context *ldb;
497 struct replmd_replicated_request *ac;
499 ldb = ldb_module_get_ctx(module);
501 ac = talloc_zero(req, struct replmd_replicated_request);
510 ac->schema = dsdb_get_schema(ldb, ac);
512 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
513 "replmd_modify: no dsdb_schema loaded");
514 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
522 add a time element to a record
524 static int add_time_element(struct ldb_message *msg, const char *attr, time_t t)
526 struct ldb_message_element *el;
530 if (ldb_msg_find_element(msg, attr) != NULL) {
534 s = ldb_timestring(msg, t);
536 return LDB_ERR_OPERATIONS_ERROR;
539 ret = ldb_msg_add_string(msg, attr, s);
540 if (ret != LDB_SUCCESS) {
544 el = ldb_msg_find_element(msg, attr);
545 /* always set as replace. This works because on add ops, the flag
547 el->flags = LDB_FLAG_MOD_REPLACE;
553 add a uint64_t element to a record
555 static int add_uint64_element(struct ldb_context *ldb, struct ldb_message *msg,
556 const char *attr, uint64_t v)
558 struct ldb_message_element *el;
561 if (ldb_msg_find_element(msg, attr) != NULL) {
565 ret = samdb_msg_add_uint64(ldb, msg, msg, attr, v);
566 if (ret != LDB_SUCCESS) {
570 el = ldb_msg_find_element(msg, attr);
571 /* always set as replace. This works because on add ops, the flag
573 el->flags = LDB_FLAG_MOD_REPLACE;
578 static int replmd_replPropertyMetaData1_attid_sort(const struct replPropertyMetaData1 *m1,
579 const struct replPropertyMetaData1 *m2,
580 const uint32_t *rdn_attid)
582 if (m1->attid == m2->attid) {
587 * the rdn attribute should be at the end!
588 * so we need to return a value greater than zero
589 * which means m1 is greater than m2
591 if (m1->attid == *rdn_attid) {
596 * the rdn attribute should be at the end!
597 * so we need to return a value less than zero
598 * which means m2 is greater than m1
600 if (m2->attid == *rdn_attid) {
604 return m1->attid > m2->attid ? 1 : -1;
607 static int replmd_replPropertyMetaDataCtr1_sort(struct replPropertyMetaDataCtr1 *ctr1,
608 const struct dsdb_schema *schema,
611 const char *rdn_name;
612 const struct dsdb_attribute *rdn_sa;
614 rdn_name = ldb_dn_get_rdn_name(dn);
616 DEBUG(0,(__location__ ": No rDN for %s?\n", ldb_dn_get_linearized(dn)));
617 return LDB_ERR_OPERATIONS_ERROR;
620 rdn_sa = dsdb_attribute_by_lDAPDisplayName(schema, rdn_name);
621 if (rdn_sa == NULL) {
622 DEBUG(0,(__location__ ": No sa found for rDN %s for %s\n", rdn_name, ldb_dn_get_linearized(dn)));
623 return LDB_ERR_OPERATIONS_ERROR;
626 DEBUG(6,("Sorting rpmd with attid exception %u rDN=%s DN=%s\n",
627 rdn_sa->attributeID_id, rdn_name, ldb_dn_get_linearized(dn)));
629 LDB_TYPESAFE_QSORT(ctr1->array, ctr1->count, &rdn_sa->attributeID_id, replmd_replPropertyMetaData1_attid_sort);
634 static int replmd_ldb_message_element_attid_sort(const struct ldb_message_element *e1,
635 const struct ldb_message_element *e2,
636 const struct dsdb_schema *schema)
638 const struct dsdb_attribute *a1;
639 const struct dsdb_attribute *a2;
642 * TODO: make this faster by caching the dsdb_attribute pointer
643 * on the ldb_messag_element
646 a1 = dsdb_attribute_by_lDAPDisplayName(schema, e1->name);
647 a2 = dsdb_attribute_by_lDAPDisplayName(schema, e2->name);
650 * TODO: remove this check, we should rely on e1 and e2 having valid attribute names
654 return strcasecmp(e1->name, e2->name);
656 if (a1->attributeID_id == a2->attributeID_id) {
659 return a1->attributeID_id > a2->attributeID_id ? 1 : -1;
662 static void replmd_ldb_message_sort(struct ldb_message *msg,
663 const struct dsdb_schema *schema)
665 LDB_TYPESAFE_QSORT(msg->elements, msg->num_elements, schema, replmd_ldb_message_element_attid_sort);
668 static int replmd_build_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
669 const struct GUID *invocation_id, uint64_t seq_num,
670 uint64_t local_usn, NTTIME nttime, uint32_t version, bool deleted);
674 fix up linked attributes in replmd_add.
675 This involves setting up the right meta-data in extended DN
676 components, and creating backlinks to the object
678 static int replmd_add_fix_la(struct ldb_module *module, struct ldb_message_element *el,
679 uint64_t seq_num, const struct GUID *invocationId, time_t t,
680 struct GUID *guid, const struct dsdb_attribute *sa, struct ldb_request *parent)
683 TALLOC_CTX *tmp_ctx = talloc_new(el->values);
684 struct ldb_context *ldb = ldb_module_get_ctx(module);
686 /* We will take a reference to the schema in replmd_add_backlink */
687 const struct dsdb_schema *schema = dsdb_get_schema(ldb, NULL);
690 unix_to_nt_time(&now, t);
692 for (i=0; i<el->num_values; i++) {
693 struct ldb_val *v = &el->values[i];
694 struct dsdb_dn *dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, v, sa->syntax->ldap_oid);
695 struct GUID target_guid;
699 /* note that the DN already has the extended
700 components from the extended_dn_store module */
701 status = dsdb_get_extended_dn_guid(dsdb_dn->dn, &target_guid, "GUID");
702 if (!NT_STATUS_IS_OK(status) || GUID_all_zero(&target_guid)) {
703 ret = dsdb_module_guid_by_dn(module, dsdb_dn->dn, &target_guid, parent);
704 if (ret != LDB_SUCCESS) {
705 talloc_free(tmp_ctx);
708 ret = dsdb_set_extended_dn_guid(dsdb_dn->dn, &target_guid, "GUID");
709 if (ret != LDB_SUCCESS) {
710 talloc_free(tmp_ctx);
715 ret = replmd_build_la_val(el->values, v, dsdb_dn, invocationId,
716 seq_num, seq_num, now, 0, false);
717 if (ret != LDB_SUCCESS) {
718 talloc_free(tmp_ctx);
722 ret = replmd_add_backlink(module, schema, guid, &target_guid, true, sa, false);
723 if (ret != LDB_SUCCESS) {
724 talloc_free(tmp_ctx);
729 talloc_free(tmp_ctx);
735 intercept add requests
737 static int replmd_add(struct ldb_module *module, struct ldb_request *req)
739 struct ldb_context *ldb;
740 struct ldb_control *control;
741 struct replmd_replicated_request *ac;
742 enum ndr_err_code ndr_err;
743 struct ldb_request *down_req;
744 struct ldb_message *msg;
745 const DATA_BLOB *guid_blob;
747 struct replPropertyMetaDataBlob nmd;
748 struct ldb_val nmd_value;
749 const struct GUID *our_invocation_id;
750 time_t t = time(NULL);
755 unsigned int functional_level;
757 bool allow_add_guid = false;
758 bool remove_current_guid = false;
759 bool is_urgent = false;
760 struct ldb_message_element *objectclass_el;
762 /* check if there's a show relax control (used by provision to say 'I know what I'm doing') */
763 control = ldb_request_get_control(req, LDB_CONTROL_RELAX_OID);
765 allow_add_guid = true;
768 /* do not manipulate our control entries */
769 if (ldb_dn_is_special(req->op.add.message->dn)) {
770 return ldb_next_request(module, req);
773 ldb = ldb_module_get_ctx(module);
775 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_add\n");
777 guid_blob = ldb_msg_find_ldb_val(req->op.add.message, "objectGUID");
778 if (guid_blob != NULL) {
779 if (!allow_add_guid) {
780 ldb_set_errstring(ldb,
781 "replmd_add: it's not allowed to add an object with objectGUID!");
782 return LDB_ERR_UNWILLING_TO_PERFORM;
784 NTSTATUS status = GUID_from_data_blob(guid_blob,&guid);
785 if (!NT_STATUS_IS_OK(status)) {
786 ldb_set_errstring(ldb,
787 "replmd_add: Unable to parse the 'objectGUID' as a GUID!");
788 return LDB_ERR_UNWILLING_TO_PERFORM;
790 /* we remove this attribute as it can be a string and
791 * will not be treated correctly and then we will re-add
792 * it later on in the good format */
793 remove_current_guid = true;
797 guid = GUID_random();
800 ac = replmd_ctx_init(module, req);
802 return ldb_module_oom(module);
805 functional_level = dsdb_functional_level(ldb);
807 /* Get a sequence number from the backend */
808 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ac->seq_num);
809 if (ret != LDB_SUCCESS) {
814 /* get our invocationId */
815 our_invocation_id = samdb_ntds_invocation_id(ldb);
816 if (!our_invocation_id) {
817 ldb_debug_set(ldb, LDB_DEBUG_ERROR,
818 "replmd_add: unable to find invocationId\n");
820 return LDB_ERR_OPERATIONS_ERROR;
823 /* we have to copy the message as the caller might have it as a const */
824 msg = ldb_msg_copy_shallow(ac, req->op.add.message);
828 return LDB_ERR_OPERATIONS_ERROR;
831 /* generated times */
832 unix_to_nt_time(&now, t);
833 time_str = ldb_timestring(msg, t);
837 return LDB_ERR_OPERATIONS_ERROR;
839 if (remove_current_guid) {
840 ldb_msg_remove_attr(msg,"objectGUID");
844 * remove autogenerated attributes
846 ldb_msg_remove_attr(msg, "whenCreated");
847 ldb_msg_remove_attr(msg, "whenChanged");
848 ldb_msg_remove_attr(msg, "uSNCreated");
849 ldb_msg_remove_attr(msg, "uSNChanged");
850 ldb_msg_remove_attr(msg, "replPropertyMetaData");
853 * readd replicated attributes
855 ret = ldb_msg_add_string(msg, "whenCreated", time_str);
856 if (ret != LDB_SUCCESS) {
862 /* build the replication meta_data */
865 nmd.ctr.ctr1.count = msg->num_elements;
866 nmd.ctr.ctr1.array = talloc_array(msg,
867 struct replPropertyMetaData1,
869 if (!nmd.ctr.ctr1.array) {
872 return LDB_ERR_OPERATIONS_ERROR;
875 for (i=0; i < msg->num_elements; i++) {
876 struct ldb_message_element *e = &msg->elements[i];
877 struct replPropertyMetaData1 *m = &nmd.ctr.ctr1.array[ni];
878 const struct dsdb_attribute *sa;
880 if (e->name[0] == '@') continue;
882 sa = dsdb_attribute_by_lDAPDisplayName(ac->schema, e->name);
884 ldb_debug_set(ldb, LDB_DEBUG_ERROR,
885 "replmd_add: attribute '%s' not defined in schema\n",
888 return LDB_ERR_NO_SUCH_ATTRIBUTE;
891 if ((sa->systemFlags & DS_FLAG_ATTR_NOT_REPLICATED) || (sa->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED)) {
892 /* if the attribute is not replicated (0x00000001)
893 * or constructed (0x00000004) it has no metadata
898 if (sa->linkID != 0 && functional_level > DS_DOMAIN_FUNCTION_2000) {
899 ret = replmd_add_fix_la(module, e, ac->seq_num, our_invocation_id, t, &guid, sa, req);
900 if (ret != LDB_SUCCESS) {
904 /* linked attributes are not stored in
905 replPropertyMetaData in FL above w2k */
909 m->attid = sa->attributeID_id;
911 m->originating_change_time = now;
912 m->originating_invocation_id = *our_invocation_id;
913 m->originating_usn = ac->seq_num;
914 m->local_usn = ac->seq_num;
918 /* fix meta data count */
919 nmd.ctr.ctr1.count = ni;
922 * sort meta data array, and move the rdn attribute entry to the end
924 ret = replmd_replPropertyMetaDataCtr1_sort(&nmd.ctr.ctr1, ac->schema, msg->dn);
925 if (ret != LDB_SUCCESS) {
930 /* generated NDR encoded values */
931 ndr_err = ndr_push_struct_blob(&nmd_value, msg,
933 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
934 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
937 return LDB_ERR_OPERATIONS_ERROR;
941 * add the autogenerated values
943 ret = dsdb_msg_add_guid(msg, &guid, "objectGUID");
944 if (ret != LDB_SUCCESS) {
949 ret = ldb_msg_add_string(msg, "whenChanged", time_str);
950 if (ret != LDB_SUCCESS) {
955 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", ac->seq_num);
956 if (ret != LDB_SUCCESS) {
961 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ac->seq_num);
962 if (ret != LDB_SUCCESS) {
967 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
968 if (ret != LDB_SUCCESS) {
975 * sort the attributes by attid before storing the object
977 replmd_ldb_message_sort(msg, ac->schema);
979 objectclass_el = ldb_msg_find_element(msg, "objectClass");
980 is_urgent = replmd_check_urgent_objectclass(objectclass_el,
981 REPL_URGENT_ON_CREATE);
983 ac->is_urgent = is_urgent;
984 ret = ldb_build_add_req(&down_req, ldb, ac,
987 ac, replmd_op_callback,
990 LDB_REQ_SET_LOCATION(down_req);
991 if (ret != LDB_SUCCESS) {
996 if (functional_level == DS_DOMAIN_FUNCTION_2000) {
997 ret = ldb_request_add_control(down_req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
998 if (ret != LDB_SUCCESS) {
1004 /* mark the control done */
1006 control->critical = 0;
1009 /* go on with the call chain */
1010 return ldb_next_request(module, down_req);
1015 * update the replPropertyMetaData for one element
1017 static int replmd_update_rpmd_element(struct ldb_context *ldb,
1018 struct ldb_message *msg,
1019 struct ldb_message_element *el,
1020 struct ldb_message_element *old_el,
1021 struct replPropertyMetaDataBlob *omd,
1022 const struct dsdb_schema *schema,
1024 const struct GUID *our_invocation_id,
1028 const struct dsdb_attribute *a;
1029 struct replPropertyMetaData1 *md1;
1031 a = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
1033 DEBUG(0,(__location__ ": Unable to find attribute %s in schema\n",
1035 return LDB_ERR_OPERATIONS_ERROR;
1038 if ((a->systemFlags & DS_FLAG_ATTR_NOT_REPLICATED) || (a->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED)) {
1042 /* if the attribute's value haven't changed then return LDB_SUCCESS */
1043 if (old_el != NULL && ldb_msg_element_compare(el, old_el) == 0) {
1047 for (i=0; i<omd->ctr.ctr1.count; i++) {
1048 if (a->attributeID_id == omd->ctr.ctr1.array[i].attid) break;
1051 if (a->linkID != 0 && dsdb_functional_level(ldb) > DS_DOMAIN_FUNCTION_2000) {
1052 /* linked attributes are not stored in
1053 replPropertyMetaData in FL above w2k, but we do
1054 raise the seqnum for the object */
1055 if (*seq_num == 0 &&
1056 ldb_sequence_number(ldb, LDB_SEQ_NEXT, seq_num) != LDB_SUCCESS) {
1057 return LDB_ERR_OPERATIONS_ERROR;
1062 if (i == omd->ctr.ctr1.count) {
1063 /* we need to add a new one */
1064 omd->ctr.ctr1.array = talloc_realloc(msg, omd->ctr.ctr1.array,
1065 struct replPropertyMetaData1, omd->ctr.ctr1.count+1);
1066 if (omd->ctr.ctr1.array == NULL) {
1068 return LDB_ERR_OPERATIONS_ERROR;
1070 omd->ctr.ctr1.count++;
1071 ZERO_STRUCT(omd->ctr.ctr1.array[i]);
1074 /* Get a new sequence number from the backend. We only do this
1075 * if we have a change that requires a new
1076 * replPropertyMetaData element
1078 if (*seq_num == 0) {
1079 int ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, seq_num);
1080 if (ret != LDB_SUCCESS) {
1081 return LDB_ERR_OPERATIONS_ERROR;
1085 md1 = &omd->ctr.ctr1.array[i];
1087 md1->attid = a->attributeID_id;
1088 md1->originating_change_time = now;
1089 md1->originating_invocation_id = *our_invocation_id;
1090 md1->originating_usn = *seq_num;
1091 md1->local_usn = *seq_num;
1096 static uint64_t find_max_local_usn(struct replPropertyMetaDataBlob omd)
1098 uint32_t count = omd.ctr.ctr1.count;
1101 for (i=0; i < count; i++) {
1102 struct replPropertyMetaData1 m = omd.ctr.ctr1.array[i];
1103 if (max < m.local_usn) {
1111 * update the replPropertyMetaData object each time we modify an
1112 * object. This is needed for DRS replication, as the merge on the
1113 * client is based on this object
1115 static int replmd_update_rpmd(struct ldb_module *module,
1116 const struct dsdb_schema *schema,
1117 struct ldb_request *req,
1118 struct ldb_message *msg, uint64_t *seq_num,
1122 const struct ldb_val *omd_value;
1123 enum ndr_err_code ndr_err;
1124 struct replPropertyMetaDataBlob omd;
1127 const struct GUID *our_invocation_id;
1129 const char *attrs[] = { "replPropertyMetaData", "*", NULL };
1130 const char *attrs2[] = { "uSNChanged", "objectClass", NULL };
1131 struct ldb_result *res;
1132 struct ldb_context *ldb;
1133 struct ldb_message_element *objectclass_el;
1134 enum urgent_situation situation;
1135 bool rodc, rmd_is_provided;
1137 ldb = ldb_module_get_ctx(module);
1139 our_invocation_id = samdb_ntds_invocation_id(ldb);
1140 if (!our_invocation_id) {
1141 /* this happens during an initial vampire while
1142 updating the schema */
1143 DEBUG(5,("No invocationID - skipping replPropertyMetaData update\n"));
1147 unix_to_nt_time(&now, t);
1149 if (ldb_request_get_control(req, DSDB_CONTROL_CHANGEREPLMETADATA_OID)) {
1150 rmd_is_provided = true;
1152 rmd_is_provided = false;
1155 /* if isDeleted is present and is TRUE, then we consider we are deleting,
1156 * otherwise we consider we are updating */
1157 if (ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")) {
1158 situation = REPL_URGENT_ON_DELETE;
1160 situation = REPL_URGENT_ON_UPDATE;
1163 if (rmd_is_provided) {
1164 /* In this case the change_replmetadata control was supplied */
1165 /* We check that it's the only attribute that is provided
1166 * (it's a rare case so it's better to keep the code simplier)
1167 * We also check that the highest local_usn is bigger than
1170 if( msg->num_elements != 1 ||
1171 strncmp(msg->elements[0].name,
1172 "replPropertyMetaData", 20) ) {
1173 DEBUG(0,(__location__ ": changereplmetada control called without "\
1174 "a specified replPropertyMetaData attribute or with others\n"));
1175 return LDB_ERR_OPERATIONS_ERROR;
1177 if (situation == REPL_URGENT_ON_DELETE) {
1178 DEBUG(0,(__location__ ": changereplmetada control can't be called when deleting an object\n"));
1179 return LDB_ERR_OPERATIONS_ERROR;
1181 omd_value = ldb_msg_find_ldb_val(msg, "replPropertyMetaData");
1183 DEBUG(0,(__location__ ": replPropertyMetaData was not specified for Object %s\n",
1184 ldb_dn_get_linearized(msg->dn)));
1185 return LDB_ERR_OPERATIONS_ERROR;
1187 ndr_err = ndr_pull_struct_blob(omd_value, msg, &omd,
1188 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
1189 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1190 DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s\n",
1191 ldb_dn_get_linearized(msg->dn)));
1192 return LDB_ERR_OPERATIONS_ERROR;
1194 *seq_num = find_max_local_usn(omd);
1196 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attrs2,
1197 DSDB_FLAG_NEXT_MODULE |
1198 DSDB_SEARCH_SHOW_RECYCLED |
1199 DSDB_SEARCH_SHOW_EXTENDED_DN |
1200 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
1201 DSDB_SEARCH_REVEAL_INTERNALS, req);
1203 if (ret != LDB_SUCCESS || res->count != 1) {
1204 DEBUG(0,(__location__ ": Object %s failed to find uSNChanged\n",
1205 ldb_dn_get_linearized(msg->dn)));
1206 return LDB_ERR_OPERATIONS_ERROR;
1209 objectclass_el = ldb_msg_find_element(res->msgs[0], "objectClass");
1210 if (is_urgent && replmd_check_urgent_objectclass(objectclass_el,
1215 db_seq = ldb_msg_find_attr_as_uint64(res->msgs[0], "uSNChanged", 0);
1216 if (*seq_num <= db_seq) {
1217 DEBUG(0,(__location__ ": changereplmetada control provided but max(local_usn)"\
1218 " is less or equal to uSNChanged (max = %lld uSNChanged = %lld)\n",
1219 (long long)*seq_num, (long long)db_seq));
1220 return LDB_ERR_OPERATIONS_ERROR;
1224 /* search for the existing replPropertyMetaDataBlob. We need
1225 * to use REVEAL and ask for DNs in storage format to support
1226 * the check for values being the same in
1227 * replmd_update_rpmd_element()
1229 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attrs,
1230 DSDB_FLAG_NEXT_MODULE |
1231 DSDB_SEARCH_SHOW_RECYCLED |
1232 DSDB_SEARCH_SHOW_EXTENDED_DN |
1233 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
1234 DSDB_SEARCH_REVEAL_INTERNALS, req);
1235 if (ret != LDB_SUCCESS || res->count != 1) {
1236 DEBUG(0,(__location__ ": Object %s failed to find replPropertyMetaData\n",
1237 ldb_dn_get_linearized(msg->dn)));
1238 return LDB_ERR_OPERATIONS_ERROR;
1241 objectclass_el = ldb_msg_find_element(res->msgs[0], "objectClass");
1242 if (is_urgent && replmd_check_urgent_objectclass(objectclass_el,
1247 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
1249 DEBUG(0,(__location__ ": Object %s does not have a replPropertyMetaData attribute\n",
1250 ldb_dn_get_linearized(msg->dn)));
1251 return LDB_ERR_OPERATIONS_ERROR;
1254 ndr_err = ndr_pull_struct_blob(omd_value, msg, &omd,
1255 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
1256 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1257 DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s\n",
1258 ldb_dn_get_linearized(msg->dn)));
1259 return LDB_ERR_OPERATIONS_ERROR;
1262 if (omd.version != 1) {
1263 DEBUG(0,(__location__ ": bad version %u in replPropertyMetaData for %s\n",
1264 omd.version, ldb_dn_get_linearized(msg->dn)));
1265 return LDB_ERR_OPERATIONS_ERROR;
1268 for (i=0; i<msg->num_elements; i++) {
1269 struct ldb_message_element *old_el;
1270 old_el = ldb_msg_find_element(res->msgs[0], msg->elements[i].name);
1271 ret = replmd_update_rpmd_element(ldb, msg, &msg->elements[i], old_el, &omd, schema, seq_num,
1272 our_invocation_id, now);
1273 if (ret != LDB_SUCCESS) {
1277 if (is_urgent && !*is_urgent && (situation == REPL_URGENT_ON_UPDATE)) {
1278 *is_urgent = replmd_check_urgent_attribute(&msg->elements[i]);
1284 * replmd_update_rpmd_element has done an update if the
1287 if (*seq_num != 0) {
1288 struct ldb_val *md_value;
1289 struct ldb_message_element *el;
1291 /*if we are RODC and this is a DRSR update then its ok*/
1292 if (!ldb_request_get_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID)) {
1293 ret = samdb_rodc(ldb, &rodc);
1294 if (ret != LDB_SUCCESS) {
1295 DEBUG(4, (__location__ ": unable to tell if we are an RODC\n"));
1297 ldb_asprintf_errstring(ldb, "RODC modify is forbidden\n");
1298 return LDB_ERR_REFERRAL;
1302 md_value = talloc(msg, struct ldb_val);
1303 if (md_value == NULL) {
1305 return LDB_ERR_OPERATIONS_ERROR;
1308 ret = replmd_replPropertyMetaDataCtr1_sort(&omd.ctr.ctr1, schema, msg->dn);
1309 if (ret != LDB_SUCCESS) {
1313 ndr_err = ndr_push_struct_blob(md_value, msg, &omd,
1314 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
1315 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1316 DEBUG(0,(__location__ ": Failed to marshall replPropertyMetaData for %s\n",
1317 ldb_dn_get_linearized(msg->dn)));
1318 return LDB_ERR_OPERATIONS_ERROR;
1321 ret = ldb_msg_add_empty(msg, "replPropertyMetaData", LDB_FLAG_MOD_REPLACE, &el);
1322 if (ret != LDB_SUCCESS) {
1323 DEBUG(0,(__location__ ": Failed to add updated replPropertyMetaData %s\n",
1324 ldb_dn_get_linearized(msg->dn)));
1329 el->values = md_value;
1336 struct dsdb_dn *dsdb_dn;
1341 static int parsed_dn_compare(struct parsed_dn *pdn1, struct parsed_dn *pdn2)
1343 return GUID_compare(pdn1->guid, pdn2->guid);
1346 static struct parsed_dn *parsed_dn_find(struct parsed_dn *pdn,
1347 unsigned int count, struct GUID *guid,
1350 struct parsed_dn *ret;
1352 if (dn && GUID_all_zero(guid)) {
1353 /* when updating a link using DRS, we sometimes get a
1354 NULL GUID. We then need to try and match by DN */
1355 for (i=0; i<count; i++) {
1356 if (ldb_dn_compare(pdn[i].dsdb_dn->dn, dn) == 0) {
1357 dsdb_get_extended_dn_guid(pdn[i].dsdb_dn->dn, guid, "GUID");
1363 BINARY_ARRAY_SEARCH(pdn, count, guid, guid, GUID_compare, ret);
1368 get a series of message element values as an array of DNs and GUIDs
1369 the result is sorted by GUID
1371 static int get_parsed_dns(struct ldb_module *module, TALLOC_CTX *mem_ctx,
1372 struct ldb_message_element *el, struct parsed_dn **pdn,
1373 const char *ldap_oid, struct ldb_request *parent)
1376 struct ldb_context *ldb = ldb_module_get_ctx(module);
1383 (*pdn) = talloc_array(mem_ctx, struct parsed_dn, el->num_values);
1385 ldb_module_oom(module);
1386 return LDB_ERR_OPERATIONS_ERROR;
1389 for (i=0; i<el->num_values; i++) {
1390 struct ldb_val *v = &el->values[i];
1393 struct parsed_dn *p;
1397 p->dsdb_dn = dsdb_dn_parse(*pdn, ldb, v, ldap_oid);
1398 if (p->dsdb_dn == NULL) {
1399 return LDB_ERR_INVALID_DN_SYNTAX;
1402 dn = p->dsdb_dn->dn;
1404 p->guid = talloc(*pdn, struct GUID);
1405 if (p->guid == NULL) {
1406 ldb_module_oom(module);
1407 return LDB_ERR_OPERATIONS_ERROR;
1410 status = dsdb_get_extended_dn_guid(dn, p->guid, "GUID");
1411 if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
1412 /* we got a DN without a GUID - go find the GUID */
1413 int ret = dsdb_module_guid_by_dn(module, dn, p->guid, parent);
1414 if (ret != LDB_SUCCESS) {
1415 ldb_asprintf_errstring(ldb, "Unable to find GUID for DN %s\n",
1416 ldb_dn_get_linearized(dn));
1419 ret = dsdb_set_extended_dn_guid(dn, p->guid, "GUID");
1420 if (ret != LDB_SUCCESS) {
1423 } else if (!NT_STATUS_IS_OK(status)) {
1424 return LDB_ERR_OPERATIONS_ERROR;
1427 /* keep a pointer to the original ldb_val */
1431 TYPESAFE_QSORT(*pdn, el->num_values, parsed_dn_compare);
1437 build a new extended DN, including all meta data fields
1439 RMD_FLAGS = DSDB_RMD_FLAG_* bits
1440 RMD_ADDTIME = originating_add_time
1441 RMD_INVOCID = originating_invocation_id
1442 RMD_CHANGETIME = originating_change_time
1443 RMD_ORIGINATING_USN = originating_usn
1444 RMD_LOCAL_USN = local_usn
1445 RMD_VERSION = version
1447 static int replmd_build_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
1448 const struct GUID *invocation_id, uint64_t seq_num,
1449 uint64_t local_usn, NTTIME nttime, uint32_t version, bool deleted)
1451 struct ldb_dn *dn = dsdb_dn->dn;
1452 const char *tstring, *usn_string, *flags_string;
1453 struct ldb_val tval;
1455 struct ldb_val usnv, local_usnv;
1456 struct ldb_val vers, flagsv;
1459 const char *dnstring;
1461 uint32_t rmd_flags = deleted?DSDB_RMD_FLAG_DELETED:0;
1463 tstring = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)nttime);
1465 return LDB_ERR_OPERATIONS_ERROR;
1467 tval = data_blob_string_const(tstring);
1469 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)seq_num);
1471 return LDB_ERR_OPERATIONS_ERROR;
1473 usnv = data_blob_string_const(usn_string);
1475 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)local_usn);
1477 return LDB_ERR_OPERATIONS_ERROR;
1479 local_usnv = data_blob_string_const(usn_string);
1481 vstring = talloc_asprintf(mem_ctx, "%lu", (unsigned long)version);
1483 return LDB_ERR_OPERATIONS_ERROR;
1485 vers = data_blob_string_const(vstring);
1487 status = GUID_to_ndr_blob(invocation_id, dn, &iid);
1488 if (!NT_STATUS_IS_OK(status)) {
1489 return LDB_ERR_OPERATIONS_ERROR;
1492 flags_string = talloc_asprintf(mem_ctx, "%u", rmd_flags);
1493 if (!flags_string) {
1494 return LDB_ERR_OPERATIONS_ERROR;
1496 flagsv = data_blob_string_const(flags_string);
1498 ret = ldb_dn_set_extended_component(dn, "RMD_FLAGS", &flagsv);
1499 if (ret != LDB_SUCCESS) return ret;
1500 ret = ldb_dn_set_extended_component(dn, "RMD_ADDTIME", &tval);
1501 if (ret != LDB_SUCCESS) return ret;
1502 ret = ldb_dn_set_extended_component(dn, "RMD_INVOCID", &iid);
1503 if (ret != LDB_SUCCESS) return ret;
1504 ret = ldb_dn_set_extended_component(dn, "RMD_CHANGETIME", &tval);
1505 if (ret != LDB_SUCCESS) return ret;
1506 ret = ldb_dn_set_extended_component(dn, "RMD_LOCAL_USN", &local_usnv);
1507 if (ret != LDB_SUCCESS) return ret;
1508 ret = ldb_dn_set_extended_component(dn, "RMD_ORIGINATING_USN", &usnv);
1509 if (ret != LDB_SUCCESS) return ret;
1510 ret = ldb_dn_set_extended_component(dn, "RMD_VERSION", &vers);
1511 if (ret != LDB_SUCCESS) return ret;
1513 dnstring = dsdb_dn_get_extended_linearized(mem_ctx, dsdb_dn, 1);
1514 if (dnstring == NULL) {
1515 return LDB_ERR_OPERATIONS_ERROR;
1517 *v = data_blob_string_const(dnstring);
1522 static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
1523 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
1524 uint64_t seq_num, uint64_t local_usn, NTTIME nttime,
1525 uint32_t version, bool deleted);
1528 check if any links need upgrading from w2k format
1530 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.
1532 static int replmd_check_upgrade_links(struct parsed_dn *dns, uint32_t count, struct ldb_message_element *parent_ctx, const struct GUID *invocation_id)
1535 for (i=0; i<count; i++) {
1540 status = dsdb_get_extended_dn_uint32(dns[i].dsdb_dn->dn, &version, "RMD_VERSION");
1541 if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
1545 /* it's an old one that needs upgrading */
1546 ret = replmd_update_la_val(parent_ctx->values, dns[i].v, dns[i].dsdb_dn, dns[i].dsdb_dn, invocation_id,
1548 if (ret != LDB_SUCCESS) {
1556 update an extended DN, including all meta data fields
1558 see replmd_build_la_val for value names
1560 static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
1561 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
1562 uint64_t seq_num, uint64_t local_usn, NTTIME nttime,
1563 uint32_t version, bool deleted)
1565 struct ldb_dn *dn = dsdb_dn->dn;
1566 const char *tstring, *usn_string, *flags_string;
1567 struct ldb_val tval;
1569 struct ldb_val usnv, local_usnv;
1570 struct ldb_val vers, flagsv;
1571 const struct ldb_val *old_addtime;
1572 uint32_t old_version;
1575 const char *dnstring;
1577 uint32_t rmd_flags = deleted?DSDB_RMD_FLAG_DELETED:0;
1579 tstring = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)nttime);
1581 return LDB_ERR_OPERATIONS_ERROR;
1583 tval = data_blob_string_const(tstring);
1585 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)seq_num);
1587 return LDB_ERR_OPERATIONS_ERROR;
1589 usnv = data_blob_string_const(usn_string);
1591 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)local_usn);
1593 return LDB_ERR_OPERATIONS_ERROR;
1595 local_usnv = data_blob_string_const(usn_string);
1597 status = GUID_to_ndr_blob(invocation_id, dn, &iid);
1598 if (!NT_STATUS_IS_OK(status)) {
1599 return LDB_ERR_OPERATIONS_ERROR;
1602 flags_string = talloc_asprintf(mem_ctx, "%u", rmd_flags);
1603 if (!flags_string) {
1604 return LDB_ERR_OPERATIONS_ERROR;
1606 flagsv = data_blob_string_const(flags_string);
1608 ret = ldb_dn_set_extended_component(dn, "RMD_FLAGS", &flagsv);
1609 if (ret != LDB_SUCCESS) return ret;
1611 /* get the ADDTIME from the original */
1612 old_addtime = ldb_dn_get_extended_component(old_dsdb_dn->dn, "RMD_ADDTIME");
1613 if (old_addtime == NULL) {
1614 old_addtime = &tval;
1616 if (dsdb_dn != old_dsdb_dn) {
1617 ret = ldb_dn_set_extended_component(dn, "RMD_ADDTIME", old_addtime);
1618 if (ret != LDB_SUCCESS) return ret;
1621 /* use our invocation id */
1622 ret = ldb_dn_set_extended_component(dn, "RMD_INVOCID", &iid);
1623 if (ret != LDB_SUCCESS) return ret;
1625 /* changetime is the current time */
1626 ret = ldb_dn_set_extended_component(dn, "RMD_CHANGETIME", &tval);
1627 if (ret != LDB_SUCCESS) return ret;
1629 /* update the USN */
1630 ret = ldb_dn_set_extended_component(dn, "RMD_ORIGINATING_USN", &usnv);
1631 if (ret != LDB_SUCCESS) return ret;
1633 ret = ldb_dn_set_extended_component(dn, "RMD_LOCAL_USN", &local_usnv);
1634 if (ret != LDB_SUCCESS) return ret;
1636 /* increase the version by 1 */
1637 status = dsdb_get_extended_dn_uint32(old_dsdb_dn->dn, &old_version, "RMD_VERSION");
1638 if (NT_STATUS_IS_OK(status) && old_version >= version) {
1639 version = old_version+1;
1641 vstring = talloc_asprintf(dn, "%lu", (unsigned long)version);
1642 vers = data_blob_string_const(vstring);
1643 ret = ldb_dn_set_extended_component(dn, "RMD_VERSION", &vers);
1644 if (ret != LDB_SUCCESS) return ret;
1646 dnstring = dsdb_dn_get_extended_linearized(mem_ctx, dsdb_dn, 1);
1647 if (dnstring == NULL) {
1648 return LDB_ERR_OPERATIONS_ERROR;
1650 *v = data_blob_string_const(dnstring);
1656 handle adding a linked attribute
1658 static int replmd_modify_la_add(struct ldb_module *module,
1659 const struct dsdb_schema *schema,
1660 struct ldb_message *msg,
1661 struct ldb_message_element *el,
1662 struct ldb_message_element *old_el,
1663 const struct dsdb_attribute *schema_attr,
1666 struct GUID *msg_guid,
1667 struct ldb_request *parent)
1670 struct parsed_dn *dns, *old_dns;
1671 TALLOC_CTX *tmp_ctx = talloc_new(msg);
1673 struct ldb_val *new_values = NULL;
1674 unsigned int num_new_values = 0;
1675 unsigned old_num_values = old_el?old_el->num_values:0;
1676 const struct GUID *invocation_id;
1677 struct ldb_context *ldb = ldb_module_get_ctx(module);
1680 unix_to_nt_time(&now, t);
1682 ret = get_parsed_dns(module, tmp_ctx, el, &dns, schema_attr->syntax->ldap_oid, parent);
1683 if (ret != LDB_SUCCESS) {
1684 talloc_free(tmp_ctx);
1688 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns, schema_attr->syntax->ldap_oid, parent);
1689 if (ret != LDB_SUCCESS) {
1690 talloc_free(tmp_ctx);
1694 invocation_id = samdb_ntds_invocation_id(ldb);
1695 if (!invocation_id) {
1696 talloc_free(tmp_ctx);
1697 return LDB_ERR_OPERATIONS_ERROR;
1700 ret = replmd_check_upgrade_links(old_dns, old_num_values, old_el, invocation_id);
1701 if (ret != LDB_SUCCESS) {
1702 talloc_free(tmp_ctx);
1706 /* for each new value, see if it exists already with the same GUID */
1707 for (i=0; i<el->num_values; i++) {
1708 struct parsed_dn *p = parsed_dn_find(old_dns, old_num_values, dns[i].guid, NULL);
1710 /* this is a new linked attribute value */
1711 new_values = talloc_realloc(tmp_ctx, new_values, struct ldb_val, num_new_values+1);
1712 if (new_values == NULL) {
1713 ldb_module_oom(module);
1714 talloc_free(tmp_ctx);
1715 return LDB_ERR_OPERATIONS_ERROR;
1717 ret = replmd_build_la_val(new_values, &new_values[num_new_values], dns[i].dsdb_dn,
1718 invocation_id, seq_num, seq_num, now, 0, false);
1719 if (ret != LDB_SUCCESS) {
1720 talloc_free(tmp_ctx);
1725 /* this is only allowed if the GUID was
1726 previously deleted. */
1727 uint32_t rmd_flags = dsdb_dn_rmd_flags(p->dsdb_dn->dn);
1729 if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
1730 ldb_asprintf_errstring(ldb, "Attribute %s already exists for target GUID %s",
1731 el->name, GUID_string(tmp_ctx, p->guid));
1732 talloc_free(tmp_ctx);
1733 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
1735 ret = replmd_update_la_val(old_el->values, p->v, dns[i].dsdb_dn, p->dsdb_dn,
1736 invocation_id, seq_num, seq_num, now, 0, false);
1737 if (ret != LDB_SUCCESS) {
1738 talloc_free(tmp_ctx);
1743 ret = replmd_add_backlink(module, schema, msg_guid, dns[i].guid, true, schema_attr, true);
1744 if (ret != LDB_SUCCESS) {
1745 talloc_free(tmp_ctx);
1750 /* add the new ones on to the end of the old values, constructing a new el->values */
1751 el->values = talloc_realloc(msg->elements, old_el?old_el->values:NULL,
1753 old_num_values+num_new_values);
1754 if (el->values == NULL) {
1755 ldb_module_oom(module);
1756 return LDB_ERR_OPERATIONS_ERROR;
1759 memcpy(&el->values[old_num_values], new_values, num_new_values*sizeof(struct ldb_val));
1760 el->num_values = old_num_values + num_new_values;
1762 talloc_steal(msg->elements, el->values);
1763 talloc_steal(el->values, new_values);
1765 talloc_free(tmp_ctx);
1767 /* we now tell the backend to replace all existing values
1768 with the one we have constructed */
1769 el->flags = LDB_FLAG_MOD_REPLACE;
1776 handle deleting all active linked attributes
1778 static int replmd_modify_la_delete(struct ldb_module *module,
1779 const struct dsdb_schema *schema,
1780 struct ldb_message *msg,
1781 struct ldb_message_element *el,
1782 struct ldb_message_element *old_el,
1783 const struct dsdb_attribute *schema_attr,
1786 struct GUID *msg_guid,
1787 struct ldb_request *parent)
1790 struct parsed_dn *dns, *old_dns;
1791 TALLOC_CTX *tmp_ctx = talloc_new(msg);
1793 const struct GUID *invocation_id;
1794 struct ldb_context *ldb = ldb_module_get_ctx(module);
1797 unix_to_nt_time(&now, t);
1799 /* check if there is nothing to delete */
1800 if ((!old_el || old_el->num_values == 0) &&
1801 el->num_values == 0) {
1805 if (!old_el || old_el->num_values == 0) {
1806 return LDB_ERR_NO_SUCH_ATTRIBUTE;
1809 ret = get_parsed_dns(module, tmp_ctx, el, &dns, schema_attr->syntax->ldap_oid, parent);
1810 if (ret != LDB_SUCCESS) {
1811 talloc_free(tmp_ctx);
1815 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns, schema_attr->syntax->ldap_oid, parent);
1816 if (ret != LDB_SUCCESS) {
1817 talloc_free(tmp_ctx);
1821 invocation_id = samdb_ntds_invocation_id(ldb);
1822 if (!invocation_id) {
1823 return LDB_ERR_OPERATIONS_ERROR;
1826 ret = replmd_check_upgrade_links(old_dns, old_el->num_values, old_el, invocation_id);
1827 if (ret != LDB_SUCCESS) {
1828 talloc_free(tmp_ctx);
1834 /* see if we are being asked to delete any links that
1835 don't exist or are already deleted */
1836 for (i=0; i<el->num_values; i++) {
1837 struct parsed_dn *p = &dns[i];
1838 struct parsed_dn *p2;
1841 p2 = parsed_dn_find(old_dns, old_el->num_values, p->guid, NULL);
1843 ldb_asprintf_errstring(ldb, "Attribute %s doesn't exist for target GUID %s",
1844 el->name, GUID_string(tmp_ctx, p->guid));
1845 return LDB_ERR_NO_SUCH_ATTRIBUTE;
1847 rmd_flags = dsdb_dn_rmd_flags(p2->dsdb_dn->dn);
1848 if (rmd_flags & DSDB_RMD_FLAG_DELETED) {
1849 ldb_asprintf_errstring(ldb, "Attribute %s already deleted for target GUID %s",
1850 el->name, GUID_string(tmp_ctx, p->guid));
1851 return LDB_ERR_NO_SUCH_ATTRIBUTE;
1855 /* for each new value, see if it exists already with the same GUID
1856 if it is not already deleted and matches the delete list then delete it
1858 for (i=0; i<old_el->num_values; i++) {
1859 struct parsed_dn *p = &old_dns[i];
1862 if (el->num_values && parsed_dn_find(dns, el->num_values, p->guid, NULL) == NULL) {
1866 rmd_flags = dsdb_dn_rmd_flags(p->dsdb_dn->dn);
1867 if (rmd_flags & DSDB_RMD_FLAG_DELETED) continue;
1869 ret = replmd_update_la_val(old_el->values, p->v, p->dsdb_dn, p->dsdb_dn,
1870 invocation_id, seq_num, seq_num, now, 0, true);
1871 if (ret != LDB_SUCCESS) {
1872 talloc_free(tmp_ctx);
1876 ret = replmd_add_backlink(module, schema, msg_guid, old_dns[i].guid, false, schema_attr, true);
1877 if (ret != LDB_SUCCESS) {
1878 talloc_free(tmp_ctx);
1883 el->values = talloc_steal(msg->elements, old_el->values);
1884 el->num_values = old_el->num_values;
1886 talloc_free(tmp_ctx);
1888 /* we now tell the backend to replace all existing values
1889 with the one we have constructed */
1890 el->flags = LDB_FLAG_MOD_REPLACE;
1896 handle replacing a linked attribute
1898 static int replmd_modify_la_replace(struct ldb_module *module,
1899 const struct dsdb_schema *schema,
1900 struct ldb_message *msg,
1901 struct ldb_message_element *el,
1902 struct ldb_message_element *old_el,
1903 const struct dsdb_attribute *schema_attr,
1906 struct GUID *msg_guid,
1907 struct ldb_request *parent)
1910 struct parsed_dn *dns, *old_dns;
1911 TALLOC_CTX *tmp_ctx = talloc_new(msg);
1913 const struct GUID *invocation_id;
1914 struct ldb_context *ldb = ldb_module_get_ctx(module);
1915 struct ldb_val *new_values = NULL;
1916 unsigned int num_new_values = 0;
1917 unsigned int old_num_values = old_el?old_el->num_values:0;
1920 unix_to_nt_time(&now, t);
1922 /* check if there is nothing to replace */
1923 if ((!old_el || old_el->num_values == 0) &&
1924 el->num_values == 0) {
1928 ret = get_parsed_dns(module, tmp_ctx, el, &dns, schema_attr->syntax->ldap_oid, parent);
1929 if (ret != LDB_SUCCESS) {
1930 talloc_free(tmp_ctx);
1934 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns, schema_attr->syntax->ldap_oid, parent);
1935 if (ret != LDB_SUCCESS) {
1936 talloc_free(tmp_ctx);
1940 invocation_id = samdb_ntds_invocation_id(ldb);
1941 if (!invocation_id) {
1942 return LDB_ERR_OPERATIONS_ERROR;
1945 ret = replmd_check_upgrade_links(old_dns, old_num_values, old_el, invocation_id);
1946 if (ret != LDB_SUCCESS) {
1947 talloc_free(tmp_ctx);
1951 /* mark all the old ones as deleted */
1952 for (i=0; i<old_num_values; i++) {
1953 struct parsed_dn *old_p = &old_dns[i];
1954 struct parsed_dn *p;
1955 uint32_t rmd_flags = dsdb_dn_rmd_flags(old_p->dsdb_dn->dn);
1957 if (rmd_flags & DSDB_RMD_FLAG_DELETED) continue;
1959 ret = replmd_add_backlink(module, schema, msg_guid, old_dns[i].guid, false, schema_attr, false);
1960 if (ret != LDB_SUCCESS) {
1961 talloc_free(tmp_ctx);
1965 p = parsed_dn_find(dns, el->num_values, old_p->guid, NULL);
1967 /* we don't delete it if we are re-adding it */
1971 ret = replmd_update_la_val(old_el->values, old_p->v, old_p->dsdb_dn, old_p->dsdb_dn,
1972 invocation_id, seq_num, seq_num, now, 0, true);
1973 if (ret != LDB_SUCCESS) {
1974 talloc_free(tmp_ctx);
1979 /* for each new value, either update its meta-data, or add it
1982 for (i=0; i<el->num_values; i++) {
1983 struct parsed_dn *p = &dns[i], *old_p;
1986 (old_p = parsed_dn_find(old_dns,
1987 old_num_values, p->guid, NULL)) != NULL) {
1988 /* update in place */
1989 ret = replmd_update_la_val(old_el->values, old_p->v, old_p->dsdb_dn,
1990 old_p->dsdb_dn, invocation_id,
1991 seq_num, seq_num, now, 0, false);
1992 if (ret != LDB_SUCCESS) {
1993 talloc_free(tmp_ctx);
1998 new_values = talloc_realloc(tmp_ctx, new_values, struct ldb_val,
2000 if (new_values == NULL) {
2001 ldb_module_oom(module);
2002 talloc_free(tmp_ctx);
2003 return LDB_ERR_OPERATIONS_ERROR;
2005 ret = replmd_build_la_val(new_values, &new_values[num_new_values], dns[i].dsdb_dn,
2006 invocation_id, seq_num, seq_num, now, 0, false);
2007 if (ret != LDB_SUCCESS) {
2008 talloc_free(tmp_ctx);
2014 ret = replmd_add_backlink(module, schema, msg_guid, dns[i].guid, true, schema_attr, false);
2015 if (ret != LDB_SUCCESS) {
2016 talloc_free(tmp_ctx);
2021 /* add the new values to the end of old_el */
2022 if (num_new_values != 0) {
2023 el->values = talloc_realloc(msg->elements, old_el?old_el->values:NULL,
2024 struct ldb_val, old_num_values+num_new_values);
2025 if (el->values == NULL) {
2026 ldb_module_oom(module);
2027 return LDB_ERR_OPERATIONS_ERROR;
2029 memcpy(&el->values[old_num_values], &new_values[0],
2030 sizeof(struct ldb_val)*num_new_values);
2031 el->num_values = old_num_values + num_new_values;
2032 talloc_steal(msg->elements, new_values);
2034 el->values = old_el->values;
2035 el->num_values = old_el->num_values;
2036 talloc_steal(msg->elements, el->values);
2039 talloc_free(tmp_ctx);
2041 /* we now tell the backend to replace all existing values
2042 with the one we have constructed */
2043 el->flags = LDB_FLAG_MOD_REPLACE;
2050 handle linked attributes in modify requests
2052 static int replmd_modify_handle_linked_attribs(struct ldb_module *module,
2053 struct ldb_message *msg,
2054 uint64_t seq_num, time_t t,
2055 struct ldb_request *parent)
2057 struct ldb_result *res;
2060 struct ldb_context *ldb = ldb_module_get_ctx(module);
2061 struct ldb_message *old_msg;
2063 const struct dsdb_schema *schema;
2064 struct GUID old_guid;
2067 /* there the replmd_update_rpmd code has already
2068 * checked and saw that there are no linked
2073 if (dsdb_functional_level(ldb) == DS_DOMAIN_FUNCTION_2000) {
2074 /* don't do anything special for linked attributes */
2078 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, NULL,
2079 DSDB_FLAG_NEXT_MODULE |
2080 DSDB_SEARCH_SHOW_RECYCLED |
2081 DSDB_SEARCH_REVEAL_INTERNALS |
2082 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
2084 if (ret != LDB_SUCCESS) {
2087 schema = dsdb_get_schema(ldb, res);
2089 return LDB_ERR_OPERATIONS_ERROR;
2092 old_msg = res->msgs[0];
2094 old_guid = samdb_result_guid(old_msg, "objectGUID");
2096 for (i=0; i<msg->num_elements; i++) {
2097 struct ldb_message_element *el = &msg->elements[i];
2098 struct ldb_message_element *old_el, *new_el;
2099 const struct dsdb_attribute *schema_attr
2100 = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
2102 ldb_asprintf_errstring(ldb,
2103 "%s: attribute %s is not a valid attribute in schema",
2104 __FUNCTION__, el->name);
2105 return LDB_ERR_OBJECT_CLASS_VIOLATION;
2107 if (schema_attr->linkID == 0) {
2110 if ((schema_attr->linkID & 1) == 1) {
2111 /* Odd is for the target. Illegal to modify */
2112 ldb_asprintf_errstring(ldb,
2113 "attribute %s must not be modified directly, it is a linked attribute", el->name);
2114 return LDB_ERR_UNWILLING_TO_PERFORM;
2116 old_el = ldb_msg_find_element(old_msg, el->name);
2117 switch (el->flags & LDB_FLAG_MOD_MASK) {
2118 case LDB_FLAG_MOD_REPLACE:
2119 ret = replmd_modify_la_replace(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid, parent);
2121 case LDB_FLAG_MOD_DELETE:
2122 ret = replmd_modify_la_delete(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid, parent);
2124 case LDB_FLAG_MOD_ADD:
2125 ret = replmd_modify_la_add(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid, parent);
2128 ldb_asprintf_errstring(ldb,
2129 "invalid flags 0x%x for %s linked attribute",
2130 el->flags, el->name);
2131 return LDB_ERR_UNWILLING_TO_PERFORM;
2133 if (ret != LDB_SUCCESS) {
2137 ldb_msg_remove_attr(old_msg, el->name);
2139 ldb_msg_add_empty(old_msg, el->name, 0, &new_el);
2140 new_el->num_values = el->num_values;
2141 new_el->values = talloc_steal(msg->elements, el->values);
2143 /* TODO: this relises a bit too heavily on the exact
2144 behaviour of ldb_msg_find_element and
2145 ldb_msg_remove_element */
2146 old_el = ldb_msg_find_element(msg, el->name);
2148 ldb_msg_remove_element(msg, old_el);
2159 static int replmd_modify(struct ldb_module *module, struct ldb_request *req)
2161 struct ldb_context *ldb;
2162 struct replmd_replicated_request *ac;
2163 struct ldb_request *down_req;
2164 struct ldb_message *msg;
2165 time_t t = time(NULL);
2167 bool is_urgent = false;
2168 struct loadparm_context *lp_ctx;
2170 unsigned int functional_level;
2171 const DATA_BLOB *guid_blob;
2173 /* do not manipulate our control entries */
2174 if (ldb_dn_is_special(req->op.mod.message->dn)) {
2175 return ldb_next_request(module, req);
2178 ldb = ldb_module_get_ctx(module);
2180 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_modify\n");
2182 guid_blob = ldb_msg_find_ldb_val(req->op.mod.message, "objectGUID");
2183 if ( guid_blob != NULL ) {
2184 ldb_set_errstring(ldb,
2185 "replmd_modify: it's not allowed to change the objectGUID!");
2186 return LDB_ERR_CONSTRAINT_VIOLATION;
2189 ac = replmd_ctx_init(module, req);
2191 return ldb_module_oom(module);
2194 functional_level = dsdb_functional_level(ldb);
2196 lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
2197 struct loadparm_context);
2199 /* we have to copy the message as the caller might have it as a const */
2200 msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
2204 return LDB_ERR_OPERATIONS_ERROR;
2207 ldb_msg_remove_attr(msg, "whenChanged");
2208 ldb_msg_remove_attr(msg, "uSNChanged");
2210 ret = replmd_update_rpmd(module, ac->schema, req, msg, &ac->seq_num, t, &is_urgent);
2211 if (ret == LDB_ERR_REFERRAL) {
2212 referral = talloc_asprintf(req,
2214 lpcfg_dnsdomain(lp_ctx),
2215 ldb_dn_get_linearized(msg->dn));
2216 ret = ldb_module_send_referral(req, referral);
2218 return ldb_module_done(req, NULL, NULL, ret);
2221 if (ret != LDB_SUCCESS) {
2226 ret = replmd_modify_handle_linked_attribs(module, msg, ac->seq_num, t, req);
2227 if (ret != LDB_SUCCESS) {
2233 * - replace the old object with the newly constructed one
2236 ac->is_urgent = is_urgent;
2238 ret = ldb_build_mod_req(&down_req, ldb, ac,
2241 ac, replmd_op_callback,
2243 LDB_REQ_SET_LOCATION(down_req);
2244 if (ret != LDB_SUCCESS) {
2249 /* If we are in functional level 2000, then
2250 * replmd_modify_handle_linked_attribs will have done
2252 if (functional_level == DS_DOMAIN_FUNCTION_2000) {
2253 ret = ldb_request_add_control(down_req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
2254 if (ret != LDB_SUCCESS) {
2260 talloc_steal(down_req, msg);
2262 /* we only change whenChanged and uSNChanged if the seq_num
2264 if (ac->seq_num != 0) {
2265 ret = add_time_element(msg, "whenChanged", t);
2266 if (ret != LDB_SUCCESS) {
2271 ret = add_uint64_element(ldb, msg, "uSNChanged", ac->seq_num);
2272 if (ret != LDB_SUCCESS) {
2278 /* go on with the call chain */
2279 return ldb_next_request(module, down_req);
2282 static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares);
2285 handle a rename request
2287 On a rename we need to do an extra ldb_modify which sets the
2288 whenChanged and uSNChanged attributes. We do this in a callback after the success.
2290 static int replmd_rename(struct ldb_module *module, struct ldb_request *req)
2292 struct ldb_context *ldb;
2293 struct replmd_replicated_request *ac;
2295 struct ldb_request *down_req;
2297 /* do not manipulate our control entries */
2298 if (ldb_dn_is_special(req->op.mod.message->dn)) {
2299 return ldb_next_request(module, req);
2302 ldb = ldb_module_get_ctx(module);
2304 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_rename\n");
2306 ac = replmd_ctx_init(module, req);
2308 return ldb_module_oom(module);
2311 ret = ldb_build_rename_req(&down_req, ldb, ac,
2312 ac->req->op.rename.olddn,
2313 ac->req->op.rename.newdn,
2315 ac, replmd_rename_callback,
2317 LDB_REQ_SET_LOCATION(down_req);
2318 if (ret != LDB_SUCCESS) {
2323 /* go on with the call chain */
2324 return ldb_next_request(module, down_req);
2327 /* After the rename is compleated, update the whenchanged etc */
2328 static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares)
2330 struct ldb_context *ldb;
2331 struct replmd_replicated_request *ac;
2332 struct ldb_request *down_req;
2333 struct ldb_message *msg;
2334 time_t t = time(NULL);
2337 ac = talloc_get_type(req->context, struct replmd_replicated_request);
2338 ldb = ldb_module_get_ctx(ac->module);
2340 if (ares->error != LDB_SUCCESS) {
2341 return ldb_module_done(ac->req, ares->controls,
2342 ares->response, ares->error);
2345 if (ares->type != LDB_REPLY_DONE) {
2346 ldb_set_errstring(ldb,
2347 "invalid ldb_reply_type in callback");
2349 return ldb_module_done(ac->req, NULL, NULL,
2350 LDB_ERR_OPERATIONS_ERROR);
2353 /* Get a sequence number from the backend */
2354 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ac->seq_num);
2355 if (ret != LDB_SUCCESS) {
2360 * - replace the old object with the newly constructed one
2363 msg = ldb_msg_new(ac);
2366 return LDB_ERR_OPERATIONS_ERROR;
2369 msg->dn = ac->req->op.rename.newdn;
2371 ret = ldb_build_mod_req(&down_req, ldb, ac,
2374 ac, replmd_op_callback,
2376 LDB_REQ_SET_LOCATION(down_req);
2377 if (ret != LDB_SUCCESS) {
2381 talloc_steal(down_req, msg);
2383 ret = add_time_element(msg, "whenChanged", t);
2384 if (ret != LDB_SUCCESS) {
2389 ret = add_uint64_element(ldb, msg, "uSNChanged", ac->seq_num);
2390 if (ret != LDB_SUCCESS) {
2395 /* go on with the call chain - do the modify after the rename */
2396 return ldb_next_request(ac->module, down_req);
2400 remove links from objects that point at this object when an object
2403 static int replmd_delete_remove_link(struct ldb_module *module,
2404 const struct dsdb_schema *schema,
2406 struct ldb_message_element *el,
2407 const struct dsdb_attribute *sa,
2408 struct ldb_request *parent)
2411 TALLOC_CTX *tmp_ctx = talloc_new(module);
2412 struct ldb_context *ldb = ldb_module_get_ctx(module);
2414 for (i=0; i<el->num_values; i++) {
2415 struct dsdb_dn *dsdb_dn;
2419 struct ldb_message *msg;
2420 const struct dsdb_attribute *target_attr;
2421 struct ldb_message_element *el2;
2422 struct ldb_val dn_val;
2424 if (dsdb_dn_is_deleted_val(&el->values[i])) {
2428 dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i], sa->syntax->ldap_oid);
2430 talloc_free(tmp_ctx);
2431 return LDB_ERR_OPERATIONS_ERROR;
2434 status = dsdb_get_extended_dn_guid(dsdb_dn->dn, &guid2, "GUID");
2435 if (!NT_STATUS_IS_OK(status)) {
2436 talloc_free(tmp_ctx);
2437 return LDB_ERR_OPERATIONS_ERROR;
2440 /* remove the link */
2441 msg = ldb_msg_new(tmp_ctx);
2443 ldb_module_oom(module);
2444 talloc_free(tmp_ctx);
2445 return LDB_ERR_OPERATIONS_ERROR;
2449 msg->dn = dsdb_dn->dn;
2451 target_attr = dsdb_attribute_by_linkID(schema, sa->linkID ^ 1);
2452 if (target_attr == NULL) {
2456 ret = ldb_msg_add_empty(msg, target_attr->lDAPDisplayName, LDB_FLAG_MOD_DELETE, &el2);
2457 if (ret != LDB_SUCCESS) {
2458 ldb_module_oom(module);
2459 talloc_free(tmp_ctx);
2460 return LDB_ERR_OPERATIONS_ERROR;
2462 dn_val = data_blob_string_const(ldb_dn_get_linearized(dn));
2463 el2->values = &dn_val;
2464 el2->num_values = 1;
2466 ret = dsdb_module_modify(module, msg, DSDB_FLAG_OWN_MODULE, parent);
2467 if (ret != LDB_SUCCESS) {
2468 talloc_free(tmp_ctx);
2472 talloc_free(tmp_ctx);
2478 handle update of replication meta data for deletion of objects
2480 This also handles the mapping of delete to a rename operation
2481 to allow deletes to be replicated.
2483 static int replmd_delete(struct ldb_module *module, struct ldb_request *req)
2485 int ret = LDB_ERR_OTHER;
2486 bool retb, disallow_move_on_delete;
2487 struct ldb_dn *old_dn, *new_dn;
2488 const char *rdn_name;
2489 const struct ldb_val *rdn_value, *new_rdn_value;
2491 struct ldb_context *ldb = ldb_module_get_ctx(module);
2492 const struct dsdb_schema *schema;
2493 struct ldb_message *msg, *old_msg;
2494 struct ldb_message_element *el;
2495 TALLOC_CTX *tmp_ctx;
2496 struct ldb_result *res, *parent_res;
2497 const char *preserved_attrs[] = {
2498 /* yes, this really is a hard coded list. See MS-ADTS
2499 section 3.1.1.5.5.1.1 */
2500 "nTSecurityDescriptor", "attributeID", "attributeSyntax", "dNReferenceUpdate", "dNSHostName",
2501 "flatName", "governsID", "groupType", "instanceType", "lDAPDisplayName", "legacyExchangeDN",
2502 "isDeleted", "isRecycled", "lastKnownParent", "msDS-LastKnownRDN", "mS-DS-CreatorSID",
2503 "mSMQOwnerID", "nCName", "objectClass", "distinguishedName", "objectGUID", "objectSid",
2504 "oMSyntax", "proxiedObjectName", "name", "replPropertyMetaData", "sAMAccountName",
2505 "securityIdentifier", "sIDHistory", "subClassOf", "systemFlags", "trustPartner", "trustDirection",
2506 "trustType", "trustAttributes", "userAccountControl", "uSNChanged", "uSNCreated", "whenCreated",
2507 "whenChanged", NULL};
2508 unsigned int i, el_count = 0;
2509 enum deletion_state { OBJECT_NOT_DELETED=1, OBJECT_DELETED=2, OBJECT_RECYCLED=3,
2510 OBJECT_TOMBSTONE=4, OBJECT_REMOVED=5 };
2511 enum deletion_state deletion_state, next_deletion_state;
2514 if (ldb_dn_is_special(req->op.del.dn)) {
2515 return ldb_next_request(module, req);
2518 tmp_ctx = talloc_new(ldb);
2521 return LDB_ERR_OPERATIONS_ERROR;
2524 schema = dsdb_get_schema(ldb, tmp_ctx);
2526 return LDB_ERR_OPERATIONS_ERROR;
2529 old_dn = ldb_dn_copy(tmp_ctx, req->op.del.dn);
2531 /* we need the complete msg off disk, so we can work out which
2532 attributes need to be removed */
2533 ret = dsdb_module_search_dn(module, tmp_ctx, &res, old_dn, NULL,
2534 DSDB_FLAG_NEXT_MODULE |
2535 DSDB_SEARCH_SHOW_RECYCLED |
2536 DSDB_SEARCH_REVEAL_INTERNALS |
2537 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT, req);
2538 if (ret != LDB_SUCCESS) {
2539 talloc_free(tmp_ctx);
2542 old_msg = res->msgs[0];
2545 ret = dsdb_recyclebin_enabled(module, &enabled);
2546 if (ret != LDB_SUCCESS) {
2547 talloc_free(tmp_ctx);
2551 if (ldb_msg_check_string_attribute(old_msg, "isDeleted", "TRUE")) {
2553 deletion_state = OBJECT_TOMBSTONE;
2554 next_deletion_state = OBJECT_REMOVED;
2555 } else if (ldb_msg_check_string_attribute(old_msg, "isRecycled", "TRUE")) {
2556 deletion_state = OBJECT_RECYCLED;
2557 next_deletion_state = OBJECT_REMOVED;
2559 deletion_state = OBJECT_DELETED;
2560 next_deletion_state = OBJECT_RECYCLED;
2563 deletion_state = OBJECT_NOT_DELETED;
2565 next_deletion_state = OBJECT_DELETED;
2567 next_deletion_state = OBJECT_TOMBSTONE;
2571 if (next_deletion_state == OBJECT_REMOVED) {
2572 struct auth_session_info *session_info =
2573 (struct auth_session_info *)ldb_get_opaque(ldb, "sessionInfo");
2574 if (security_session_user_level(session_info, NULL) != SECURITY_SYSTEM) {
2575 ldb_asprintf_errstring(ldb, "Refusing to delete deleted object %s",
2576 ldb_dn_get_linearized(old_msg->dn));
2577 return LDB_ERR_UNWILLING_TO_PERFORM;
2580 /* it is already deleted - really remove it this time */
2581 talloc_free(tmp_ctx);
2582 return ldb_next_request(module, req);
2585 rdn_name = ldb_dn_get_rdn_name(old_dn);
2586 rdn_value = ldb_dn_get_rdn_val(old_dn);
2587 if ((rdn_name == NULL) || (rdn_value == NULL)) {
2588 talloc_free(tmp_ctx);
2589 return ldb_operr(ldb);
2592 msg = ldb_msg_new(tmp_ctx);
2594 ldb_module_oom(module);
2595 talloc_free(tmp_ctx);
2596 return LDB_ERR_OPERATIONS_ERROR;
2601 if (deletion_state == OBJECT_NOT_DELETED){
2602 /* consider the SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE flag */
2603 disallow_move_on_delete =
2604 (ldb_msg_find_attr_as_int(old_msg, "systemFlags", 0)
2605 & SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE);
2607 /* work out where we will be renaming this object to */
2608 if (!disallow_move_on_delete) {
2609 ret = dsdb_get_deleted_objects_dn(ldb, tmp_ctx, old_dn,
2611 if (ret != LDB_SUCCESS) {
2612 /* this is probably an attempted delete on a partition
2613 * that doesn't allow delete operations, such as the
2614 * schema partition */
2615 ldb_asprintf_errstring(ldb, "No Deleted Objects container for DN %s",
2616 ldb_dn_get_linearized(old_dn));
2617 talloc_free(tmp_ctx);
2618 return LDB_ERR_UNWILLING_TO_PERFORM;
2621 new_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
2622 if (new_dn == NULL) {
2623 ldb_module_oom(module);
2624 talloc_free(tmp_ctx);
2625 return LDB_ERR_OPERATIONS_ERROR;
2629 /* get the objects GUID from the search we just did */
2630 guid = samdb_result_guid(old_msg, "objectGUID");
2632 /* Add a formatted child */
2633 retb = ldb_dn_add_child_fmt(new_dn, "%s=%s\\0ADEL:%s",
2635 ldb_dn_escape_value(tmp_ctx, *rdn_value),
2636 GUID_string(tmp_ctx, &guid));
2638 DEBUG(0,(__location__ ": Unable to add a formatted child to dn: %s",
2639 ldb_dn_get_linearized(new_dn)));
2640 talloc_free(tmp_ctx);
2641 return LDB_ERR_OPERATIONS_ERROR;
2644 ret = ldb_msg_add_string(msg, "isDeleted", "TRUE");
2645 if (ret != LDB_SUCCESS) {
2646 DEBUG(0,(__location__ ": Failed to add isDeleted string to the msg\n"));
2647 ldb_module_oom(module);
2648 talloc_free(tmp_ctx);
2651 msg->elements[el_count++].flags = LDB_FLAG_MOD_REPLACE;
2655 now we need to modify the object in the following ways:
2657 - add isDeleted=TRUE
2658 - update rDN and name, with new rDN
2659 - remove linked attributes
2660 - remove objectCategory and sAMAccountType
2661 - remove attribs not on the preserved list
2662 - preserved if in above list, or is rDN
2663 - remove all linked attribs from this object
2664 - remove all links from other objects to this object
2665 - add lastKnownParent
2666 - update replPropertyMetaData?
2668 see MS-ADTS "Tombstone Requirements" section 3.1.1.5.5.1.1
2671 /* we need the storage form of the parent GUID */
2672 ret = dsdb_module_search_dn(module, tmp_ctx, &parent_res,
2673 ldb_dn_get_parent(tmp_ctx, old_dn), NULL,
2674 DSDB_FLAG_NEXT_MODULE |
2675 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
2676 DSDB_SEARCH_REVEAL_INTERNALS|
2677 DSDB_SEARCH_SHOW_RECYCLED, req);
2678 if (ret != LDB_SUCCESS) {
2679 talloc_free(tmp_ctx);
2683 if (deletion_state == OBJECT_NOT_DELETED){
2684 ret = ldb_msg_add_steal_string(msg, "lastKnownParent",
2685 ldb_dn_get_extended_linearized(tmp_ctx, parent_res->msgs[0]->dn, 1));
2686 if (ret != LDB_SUCCESS) {
2687 DEBUG(0,(__location__ ": Failed to add lastKnownParent string to the msg\n"));
2688 ldb_module_oom(module);
2689 talloc_free(tmp_ctx);
2692 msg->elements[el_count++].flags = LDB_FLAG_MOD_ADD;
2695 switch (next_deletion_state){
2697 case OBJECT_DELETED:
2699 ret = ldb_msg_add_value(msg, "msDS-LastKnownRDN", rdn_value, NULL);
2700 if (ret != LDB_SUCCESS) {
2701 DEBUG(0,(__location__ ": Failed to add msDS-LastKnownRDN string to the msg\n"));
2702 ldb_module_oom(module);
2703 talloc_free(tmp_ctx);
2706 msg->elements[el_count++].flags = LDB_FLAG_MOD_ADD;
2708 ret = ldb_msg_add_empty(msg, "objectCategory", LDB_FLAG_MOD_DELETE, NULL);
2709 if (ret != LDB_SUCCESS) {
2710 talloc_free(tmp_ctx);
2711 ldb_module_oom(module);
2715 ret = ldb_msg_add_empty(msg, "sAMAccountType", LDB_FLAG_MOD_DELETE, NULL);
2716 if (ret != LDB_SUCCESS) {
2717 talloc_free(tmp_ctx);
2718 ldb_module_oom(module);
2724 case OBJECT_RECYCLED:
2725 case OBJECT_TOMBSTONE:
2727 /* we also mark it as recycled, meaning this object can't be
2728 recovered (we are stripping its attributes) */
2729 if (dsdb_functional_level(ldb) >= DS_DOMAIN_FUNCTION_2008_R2) {
2730 ret = ldb_msg_add_string(msg, "isRecycled", "TRUE");
2731 if (ret != LDB_SUCCESS) {
2732 DEBUG(0,(__location__ ": Failed to add isRecycled string to the msg\n"));
2733 ldb_module_oom(module);
2734 talloc_free(tmp_ctx);
2737 msg->elements[el_count++].flags = LDB_FLAG_MOD_ADD;
2740 /* work out which of the old attributes we will be removing */
2741 for (i=0; i<old_msg->num_elements; i++) {
2742 const struct dsdb_attribute *sa;
2743 el = &old_msg->elements[i];
2744 sa = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
2746 talloc_free(tmp_ctx);
2747 return LDB_ERR_OPERATIONS_ERROR;
2749 if (ldb_attr_cmp(el->name, rdn_name) == 0) {
2750 /* don't remove the rDN */
2753 if (sa->linkID && sa->linkID & 1) {
2754 ret = replmd_delete_remove_link(module, schema, old_dn, el, sa, req);
2755 if (ret != LDB_SUCCESS) {
2756 talloc_free(tmp_ctx);
2757 return LDB_ERR_OPERATIONS_ERROR;
2761 if (!sa->linkID && ldb_attr_in_list(preserved_attrs, el->name)) {
2764 ret = ldb_msg_add_empty(msg, el->name, LDB_FLAG_MOD_DELETE, &el);
2765 if (ret != LDB_SUCCESS) {
2766 talloc_free(tmp_ctx);
2767 ldb_module_oom(module);
2777 if (deletion_state == OBJECT_NOT_DELETED) {
2778 const struct dsdb_attribute *sa;
2780 /* work out what the new rdn value is, for updating the
2781 rDN and name fields */
2782 new_rdn_value = ldb_dn_get_rdn_val(new_dn);
2783 if (new_rdn_value == NULL) {
2784 talloc_free(tmp_ctx);
2785 return ldb_operr(ldb);
2788 sa = dsdb_attribute_by_lDAPDisplayName(schema, rdn_name);
2790 talloc_free(tmp_ctx);
2791 return LDB_ERR_OPERATIONS_ERROR;
2794 ret = ldb_msg_add_value(msg, sa->lDAPDisplayName, new_rdn_value,
2796 if (ret != LDB_SUCCESS) {
2797 talloc_free(tmp_ctx);
2800 el->flags = LDB_FLAG_MOD_REPLACE;
2802 el = ldb_msg_find_element(old_msg, "name");
2804 ret = ldb_msg_add_value(msg, "name", new_rdn_value, &el);
2805 if (ret != LDB_SUCCESS) {
2806 talloc_free(tmp_ctx);
2809 el->flags = LDB_FLAG_MOD_REPLACE;
2813 ret = dsdb_module_modify(module, msg, DSDB_FLAG_OWN_MODULE, req);
2814 if (ret != LDB_SUCCESS) {
2815 ldb_asprintf_errstring(ldb, "replmd_delete: Failed to modify object %s in delete - %s",
2816 ldb_dn_get_linearized(old_dn), ldb_errstring(ldb));
2817 talloc_free(tmp_ctx);
2821 if (deletion_state == OBJECT_NOT_DELETED) {
2822 /* now rename onto the new DN */
2823 ret = dsdb_module_rename(module, old_dn, new_dn, DSDB_FLAG_NEXT_MODULE, req);
2824 if (ret != LDB_SUCCESS){
2825 DEBUG(0,(__location__ ": Failed to rename object from '%s' to '%s' - %s\n",
2826 ldb_dn_get_linearized(old_dn),
2827 ldb_dn_get_linearized(new_dn),
2828 ldb_errstring(ldb)));
2829 talloc_free(tmp_ctx);
2834 talloc_free(tmp_ctx);
2836 return ldb_module_done(req, NULL, NULL, LDB_SUCCESS);
2841 static int replmd_replicated_request_error(struct replmd_replicated_request *ar, int ret)
2846 static int replmd_replicated_request_werror(struct replmd_replicated_request *ar, WERROR status)
2848 int ret = LDB_ERR_OTHER;
2849 /* TODO: do some error mapping */
2853 static int replmd_replicated_apply_add(struct replmd_replicated_request *ar)
2855 struct ldb_context *ldb;
2856 struct ldb_request *change_req;
2857 enum ndr_err_code ndr_err;
2858 struct ldb_message *msg;
2859 struct replPropertyMetaDataBlob *md;
2860 struct ldb_val md_value;
2865 * TODO: check if the parent object exist
2869 * TODO: handle the conflict case where an object with the
2873 ldb = ldb_module_get_ctx(ar->module);
2874 msg = ar->objs->objects[ar->index_current].msg;
2875 md = ar->objs->objects[ar->index_current].meta_data;
2877 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
2878 if (ret != LDB_SUCCESS) {
2879 return replmd_replicated_request_error(ar, ret);
2882 ret = ldb_msg_add_value(msg, "objectGUID", &ar->objs->objects[ar->index_current].guid_value, NULL);
2883 if (ret != LDB_SUCCESS) {
2884 return replmd_replicated_request_error(ar, ret);
2887 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
2888 if (ret != LDB_SUCCESS) {
2889 return replmd_replicated_request_error(ar, ret);
2892 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", ar->seq_num);
2893 if (ret != LDB_SUCCESS) {
2894 return replmd_replicated_request_error(ar, ret);
2897 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
2898 if (ret != LDB_SUCCESS) {
2899 return replmd_replicated_request_error(ar, ret);
2902 /* remove any message elements that have zero values */
2903 for (i=0; i<msg->num_elements; i++) {
2904 struct ldb_message_element *el = &msg->elements[i];
2906 if (el->num_values == 0) {
2907 DEBUG(4,(__location__ ": Removing attribute %s with num_values==0\n",
2909 memmove(el, el+1, sizeof(*el)*(msg->num_elements - (i+1)));
2910 msg->num_elements--;
2917 * the meta data array is already sorted by the caller
2919 for (i=0; i < md->ctr.ctr1.count; i++) {
2920 md->ctr.ctr1.array[i].local_usn = ar->seq_num;
2922 ndr_err = ndr_push_struct_blob(&md_value, msg, md,
2923 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
2924 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
2925 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
2926 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
2928 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &md_value, NULL);
2929 if (ret != LDB_SUCCESS) {
2930 return replmd_replicated_request_error(ar, ret);
2933 replmd_ldb_message_sort(msg, ar->schema);
2936 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_ADD, msg);
2937 DEBUG(4, ("DRS replication add message:\n%s\n", s));
2941 ret = ldb_build_add_req(&change_req,
2949 LDB_REQ_SET_LOCATION(change_req);
2950 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
2952 return ldb_next_request(ar->module, change_req);
2956 return true if an update is newer than an existing entry
2957 see section 5.11 of MS-ADTS
2959 static bool replmd_update_is_newer(const struct GUID *current_invocation_id,
2960 const struct GUID *update_invocation_id,
2961 uint32_t current_version,
2962 uint32_t update_version,
2963 NTTIME current_change_time,
2964 NTTIME update_change_time)
2966 if (update_version != current_version) {
2967 return update_version > current_version;
2969 if (update_change_time != current_change_time) {
2970 return update_change_time > current_change_time;
2972 return GUID_compare(update_invocation_id, current_invocation_id) > 0;
2975 static bool replmd_replPropertyMetaData1_is_newer(struct replPropertyMetaData1 *cur_m,
2976 struct replPropertyMetaData1 *new_m)
2978 return replmd_update_is_newer(&cur_m->originating_invocation_id,
2979 &new_m->originating_invocation_id,
2982 cur_m->originating_change_time,
2983 new_m->originating_change_time);
2986 static struct replPropertyMetaData1 *
2987 replmd_replPropertyMetaData1_find_attid(struct replPropertyMetaDataBlob *md_blob,
2988 enum drsuapi_DsAttributeId attid)
2991 struct replPropertyMetaDataCtr1 *rpmd_ctr = &md_blob->ctr.ctr1;
2993 for (i = 0; i < rpmd_ctr->count; i++) {
2994 if (rpmd_ctr->array[i].attid == attid) {
2995 return &rpmd_ctr->array[i];
3003 handle renames that come in over DRS replication
3005 static int replmd_replicated_handle_rename(struct replmd_replicated_request *ar,
3006 struct ldb_message *msg,
3007 struct replPropertyMetaDataBlob *rmd,
3008 struct replPropertyMetaDataBlob *omd,
3009 struct ldb_request *parent)
3011 struct replPropertyMetaData1 *md_remote;
3012 struct replPropertyMetaData1 *md_local;
3014 if (ldb_dn_compare(msg->dn, ar->search_msg->dn) == 0) {
3019 /* now we need to check for double renames. We could have a
3020 * local rename pending which our replication partner hasn't
3021 * received yet. We choose which one wins by looking at the
3022 * attribute stamps on the two objects, the newer one wins
3024 md_remote = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
3025 md_local = replmd_replPropertyMetaData1_find_attid(omd, DRSUAPI_ATTID_name);
3026 /* if there is no name attribute then we have to assume the
3027 object we've received is in fact newer */
3028 if (!md_remote || !md_local ||
3029 replmd_replPropertyMetaData1_is_newer(md_local, md_remote)) {
3030 DEBUG(4,("replmd_replicated_request rename %s => %s\n",
3031 ldb_dn_get_linearized(ar->search_msg->dn),
3032 ldb_dn_get_linearized(msg->dn)));
3033 /* pass rename to the next module
3034 * so it doesn't appear as an originating update */
3035 return dsdb_module_rename(ar->module,
3036 ar->search_msg->dn, msg->dn,
3037 DSDB_FLAG_NEXT_MODULE | DSDB_MODIFY_RELAX, parent);
3040 /* we're going to keep our old object */
3041 DEBUG(4,(__location__ ": Keeping object %s and rejecting older rename to %s\n",
3042 ldb_dn_get_linearized(ar->search_msg->dn),
3043 ldb_dn_get_linearized(msg->dn)));
3048 static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar)
3050 struct ldb_context *ldb;
3051 struct ldb_request *change_req;
3052 enum ndr_err_code ndr_err;
3053 struct ldb_message *msg;
3054 struct replPropertyMetaDataBlob *rmd;
3055 struct replPropertyMetaDataBlob omd;
3056 const struct ldb_val *omd_value;
3057 struct replPropertyMetaDataBlob nmd;
3058 struct ldb_val nmd_value;
3061 unsigned int removed_attrs = 0;
3064 ldb = ldb_module_get_ctx(ar->module);
3065 msg = ar->objs->objects[ar->index_current].msg;
3066 rmd = ar->objs->objects[ar->index_current].meta_data;
3070 /* find existing meta data */
3071 omd_value = ldb_msg_find_ldb_val(ar->search_msg, "replPropertyMetaData");
3073 ndr_err = ndr_pull_struct_blob(omd_value, ar, &omd,
3074 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
3075 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
3076 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
3077 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
3080 if (omd.version != 1) {
3081 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
3085 /* handle renames that come in over DRS */
3086 ret = replmd_replicated_handle_rename(ar, msg, rmd, &omd, ar->req);
3087 if (ret != LDB_SUCCESS) {
3088 ldb_debug(ldb, LDB_DEBUG_FATAL,
3089 "replmd_replicated_request rename %s => %s failed - %s\n",
3090 ldb_dn_get_linearized(ar->search_msg->dn),
3091 ldb_dn_get_linearized(msg->dn),
3092 ldb_errstring(ldb));
3093 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
3098 nmd.ctr.ctr1.count = omd.ctr.ctr1.count + rmd->ctr.ctr1.count;
3099 nmd.ctr.ctr1.array = talloc_array(ar,
3100 struct replPropertyMetaData1,
3101 nmd.ctr.ctr1.count);
3102 if (!nmd.ctr.ctr1.array) return replmd_replicated_request_werror(ar, WERR_NOMEM);
3104 /* first copy the old meta data */
3105 for (i=0; i < omd.ctr.ctr1.count; i++) {
3106 nmd.ctr.ctr1.array[ni] = omd.ctr.ctr1.array[i];
3110 /* now merge in the new meta data */
3111 for (i=0; i < rmd->ctr.ctr1.count; i++) {
3114 for (j=0; j < ni; j++) {
3117 if (rmd->ctr.ctr1.array[i].attid != nmd.ctr.ctr1.array[j].attid) {
3121 cmp = replmd_replPropertyMetaData1_is_newer(&nmd.ctr.ctr1.array[j],
3122 &rmd->ctr.ctr1.array[i]);
3124 /* replace the entry */
3125 nmd.ctr.ctr1.array[j] = rmd->ctr.ctr1.array[i];
3130 if (rmd->ctr.ctr1.array[i].attid != DRSUAPI_ATTID_instanceType) {
3131 DEBUG(3,("Discarding older DRS attribute update to %s on %s from %s\n",
3132 msg->elements[i-removed_attrs].name,
3133 ldb_dn_get_linearized(msg->dn),
3134 GUID_string(ar, &rmd->ctr.ctr1.array[i].originating_invocation_id)));
3137 /* we don't want to apply this change so remove the attribute */
3138 ldb_msg_remove_element(msg, &msg->elements[i-removed_attrs]);
3145 if (found) continue;
3147 nmd.ctr.ctr1.array[ni] = rmd->ctr.ctr1.array[i];
3152 * finally correct the size of the meta_data array
3154 nmd.ctr.ctr1.count = ni;
3157 * the rdn attribute (the alias for the name attribute),
3158 * 'cn' for most objects is the last entry in the meta data array
3161 * sort the new meta data array
3163 ret = replmd_replPropertyMetaDataCtr1_sort(&nmd.ctr.ctr1, ar->schema, msg->dn);
3164 if (ret != LDB_SUCCESS) {
3169 * check if some replicated attributes left, otherwise skip the ldb_modify() call
3171 if (msg->num_elements == 0) {
3172 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: skip replace\n",
3175 ar->index_current++;
3176 return replmd_replicated_apply_next(ar);
3179 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: replace %u attributes\n",
3180 ar->index_current, msg->num_elements);
3182 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
3183 if (ret != LDB_SUCCESS) {
3184 return replmd_replicated_request_error(ar, ret);
3187 for (i=0; i<ni; i++) {
3188 nmd.ctr.ctr1.array[i].local_usn = ar->seq_num;
3191 /* create the meta data value */
3192 ndr_err = ndr_push_struct_blob(&nmd_value, msg, &nmd,
3193 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
3194 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
3195 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
3196 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
3200 * when we know that we'll modify the record, add the whenChanged, uSNChanged
3201 * and replPopertyMetaData attributes
3203 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
3204 if (ret != LDB_SUCCESS) {
3205 return replmd_replicated_request_error(ar, ret);
3207 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
3208 if (ret != LDB_SUCCESS) {
3209 return replmd_replicated_request_error(ar, ret);
3211 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
3212 if (ret != LDB_SUCCESS) {
3213 return replmd_replicated_request_error(ar, ret);
3216 replmd_ldb_message_sort(msg, ar->schema);
3218 /* we want to replace the old values */
3219 for (i=0; i < msg->num_elements; i++) {
3220 msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
3224 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_MODIFY, msg);
3225 DEBUG(4, ("DRS replication modify message:\n%s\n", s));
3229 ret = ldb_build_mod_req(&change_req,
3237 LDB_REQ_SET_LOCATION(change_req);
3238 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
3240 return ldb_next_request(ar->module, change_req);
3243 static int replmd_replicated_apply_search_callback(struct ldb_request *req,
3244 struct ldb_reply *ares)
3246 struct replmd_replicated_request *ar = talloc_get_type(req->context,
3247 struct replmd_replicated_request);
3251 return ldb_module_done(ar->req, NULL, NULL,
3252 LDB_ERR_OPERATIONS_ERROR);
3254 if (ares->error != LDB_SUCCESS &&
3255 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
3256 return ldb_module_done(ar->req, ares->controls,
3257 ares->response, ares->error);
3260 switch (ares->type) {
3261 case LDB_REPLY_ENTRY:
3262 ar->search_msg = talloc_steal(ar, ares->message);
3265 case LDB_REPLY_REFERRAL:
3266 /* we ignore referrals */
3269 case LDB_REPLY_DONE:
3270 if (ar->search_msg != NULL) {
3271 ret = replmd_replicated_apply_merge(ar);
3273 ret = replmd_replicated_apply_add(ar);
3275 if (ret != LDB_SUCCESS) {
3276 return ldb_module_done(ar->req, NULL, NULL, ret);
3284 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar);
3286 static int replmd_replicated_apply_next(struct replmd_replicated_request *ar)
3288 struct ldb_context *ldb;
3292 struct ldb_request *search_req;
3293 struct ldb_search_options_control *options;
3295 if (ar->index_current >= ar->objs->num_objects) {
3296 /* done with it, go to next stage */
3297 return replmd_replicated_uptodate_vector(ar);
3300 ldb = ldb_module_get_ctx(ar->module);
3301 ar->search_msg = NULL;
3303 tmp_str = ldb_binary_encode(ar, ar->objs->objects[ar->index_current].guid_value);
3304 if (!tmp_str) return replmd_replicated_request_werror(ar, WERR_NOMEM);
3306 filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
3307 if (!filter) return replmd_replicated_request_werror(ar, WERR_NOMEM);
3308 talloc_free(tmp_str);
3310 ret = ldb_build_search_req(&search_req,
3319 replmd_replicated_apply_search_callback,
3321 LDB_REQ_SET_LOCATION(search_req);
3323 ret = ldb_request_add_control(search_req, LDB_CONTROL_SHOW_RECYCLED_OID,
3325 if (ret != LDB_SUCCESS) {
3329 /* we need to cope with cross-partition links, so search for
3330 the GUID over all partitions */
3331 options = talloc(search_req, struct ldb_search_options_control);
3332 if (options == NULL) {
3333 DEBUG(0, (__location__ ": out of memory\n"));
3334 return LDB_ERR_OPERATIONS_ERROR;
3336 options->search_options = LDB_SEARCH_OPTION_PHANTOM_ROOT;
3338 ret = ldb_request_add_control(search_req,
3339 LDB_CONTROL_SEARCH_OPTIONS_OID,
3341 if (ret != LDB_SUCCESS) {
3345 return ldb_next_request(ar->module, search_req);
3348 static int replmd_replicated_uptodate_modify_callback(struct ldb_request *req,
3349 struct ldb_reply *ares)
3351 struct ldb_context *ldb;
3352 struct replmd_replicated_request *ar = talloc_get_type(req->context,
3353 struct replmd_replicated_request);
3354 ldb = ldb_module_get_ctx(ar->module);
3357 return ldb_module_done(ar->req, NULL, NULL,
3358 LDB_ERR_OPERATIONS_ERROR);
3360 if (ares->error != LDB_SUCCESS) {
3361 return ldb_module_done(ar->req, ares->controls,
3362 ares->response, ares->error);
3365 if (ares->type != LDB_REPLY_DONE) {
3366 ldb_set_errstring(ldb, "Invalid reply type\n!");
3367 return ldb_module_done(ar->req, NULL, NULL,
3368 LDB_ERR_OPERATIONS_ERROR);
3373 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
3376 static int replmd_replicated_uptodate_modify(struct replmd_replicated_request *ar)
3378 struct ldb_context *ldb;
3379 struct ldb_request *change_req;
3380 enum ndr_err_code ndr_err;
3381 struct ldb_message *msg;
3382 struct replUpToDateVectorBlob ouv;
3383 const struct ldb_val *ouv_value;
3384 const struct drsuapi_DsReplicaCursor2CtrEx *ruv;
3385 struct replUpToDateVectorBlob nuv;
3386 struct ldb_val nuv_value;
3387 struct ldb_message_element *nuv_el = NULL;
3388 const struct GUID *our_invocation_id;
3389 struct ldb_message_element *orf_el = NULL;
3390 struct repsFromToBlob nrf;
3391 struct ldb_val *nrf_value = NULL;
3392 struct ldb_message_element *nrf_el = NULL;
3396 time_t t = time(NULL);
3399 uint32_t instanceType;
3401 ldb = ldb_module_get_ctx(ar->module);
3402 ruv = ar->objs->uptodateness_vector;
3408 unix_to_nt_time(&now, t);
3410 instanceType = ldb_msg_find_attr_as_uint(ar->search_msg, "instanceType", 0);
3411 if (! (instanceType & INSTANCE_TYPE_IS_NC_HEAD)) {
3412 DEBUG(4,(__location__ ": Skipping UDV and repsFrom update as not NC root: %s\n",
3413 ldb_dn_get_linearized(ar->search_msg->dn)));
3414 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
3418 * first create the new replUpToDateVector
3420 ouv_value = ldb_msg_find_ldb_val(ar->search_msg, "replUpToDateVector");
3422 ndr_err = ndr_pull_struct_blob(ouv_value, ar, &ouv,
3423 (ndr_pull_flags_fn_t)ndr_pull_replUpToDateVectorBlob);
3424 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
3425 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
3426 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
3429 if (ouv.version != 2) {
3430 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
3435 * the new uptodateness vector will at least
3436 * contain 1 entry, one for the source_dsa
3438 * plus optional values from our old vector and the one from the source_dsa
3440 nuv.ctr.ctr2.count = 1 + ouv.ctr.ctr2.count;
3441 if (ruv) nuv.ctr.ctr2.count += ruv->count;
3442 nuv.ctr.ctr2.cursors = talloc_array(ar,
3443 struct drsuapi_DsReplicaCursor2,
3444 nuv.ctr.ctr2.count);
3445 if (!nuv.ctr.ctr2.cursors) return replmd_replicated_request_werror(ar, WERR_NOMEM);
3447 /* first copy the old vector */
3448 for (i=0; i < ouv.ctr.ctr2.count; i++) {
3449 nuv.ctr.ctr2.cursors[ni] = ouv.ctr.ctr2.cursors[i];
3453 /* get our invocation_id if we have one already attached to the ldb */
3454 our_invocation_id = samdb_ntds_invocation_id(ldb);
3456 /* merge in the source_dsa vector is available */
3457 for (i=0; (ruv && i < ruv->count); i++) {
3460 if (our_invocation_id &&
3461 GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
3462 our_invocation_id)) {
3466 for (j=0; j < ni; j++) {
3467 if (!GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
3468 &nuv.ctr.ctr2.cursors[j].source_dsa_invocation_id)) {
3475 * we update only the highest_usn and not the latest_sync_success time,
3476 * because the last success stands for direct replication
3478 if (ruv->cursors[i].highest_usn > nuv.ctr.ctr2.cursors[j].highest_usn) {
3479 nuv.ctr.ctr2.cursors[j].highest_usn = ruv->cursors[i].highest_usn;
3484 if (found) continue;
3486 /* if it's not there yet, add it */
3487 nuv.ctr.ctr2.cursors[ni] = ruv->cursors[i];
3492 * merge in the current highwatermark for the source_dsa
3495 for (j=0; j < ni; j++) {
3496 if (!GUID_equal(&ar->objs->source_dsa->source_dsa_invocation_id,
3497 &nuv.ctr.ctr2.cursors[j].source_dsa_invocation_id)) {
3504 * here we update the highest_usn and last_sync_success time
3505 * because we're directly replicating from the source_dsa
3507 * and use the tmp_highest_usn because this is what we have just applied
3510 nuv.ctr.ctr2.cursors[j].highest_usn = ar->objs->source_dsa->highwatermark.tmp_highest_usn;
3511 nuv.ctr.ctr2.cursors[j].last_sync_success = now;
3516 * here we update the highest_usn and last_sync_success time
3517 * because we're directly replicating from the source_dsa
3519 * and use the tmp_highest_usn because this is what we have just applied
3522 nuv.ctr.ctr2.cursors[ni].source_dsa_invocation_id= ar->objs->source_dsa->source_dsa_invocation_id;
3523 nuv.ctr.ctr2.cursors[ni].highest_usn = ar->objs->source_dsa->highwatermark.tmp_highest_usn;
3524 nuv.ctr.ctr2.cursors[ni].last_sync_success = now;
3529 * finally correct the size of the cursors array
3531 nuv.ctr.ctr2.count = ni;
3536 TYPESAFE_QSORT(nuv.ctr.ctr2.cursors, nuv.ctr.ctr2.count, drsuapi_DsReplicaCursor2_compare);
3539 * create the change ldb_message
3541 msg = ldb_msg_new(ar);
3542 if (!msg) return replmd_replicated_request_werror(ar, WERR_NOMEM);
3543 msg->dn = ar->search_msg->dn;
3545 ndr_err = ndr_push_struct_blob(&nuv_value, msg, &nuv,
3546 (ndr_push_flags_fn_t)ndr_push_replUpToDateVectorBlob);
3547 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
3548 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
3549 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
3551 ret = ldb_msg_add_value(msg, "replUpToDateVector", &nuv_value, &nuv_el);
3552 if (ret != LDB_SUCCESS) {
3553 return replmd_replicated_request_error(ar, ret);
3555 nuv_el->flags = LDB_FLAG_MOD_REPLACE;
3558 * now create the new repsFrom value from the given repsFromTo1 structure
3562 nrf.ctr.ctr1 = *ar->objs->source_dsa;
3563 nrf.ctr.ctr1.highwatermark.highest_usn = nrf.ctr.ctr1.highwatermark.tmp_highest_usn;
3566 * first see if we already have a repsFrom value for the current source dsa
3567 * if so we'll later replace this value
3569 orf_el = ldb_msg_find_element(ar->search_msg, "repsFrom");
3571 for (i=0; i < orf_el->num_values; i++) {
3572 struct repsFromToBlob *trf;
3574 trf = talloc(ar, struct repsFromToBlob);
3575 if (!trf) return replmd_replicated_request_werror(ar, WERR_NOMEM);
3577 ndr_err = ndr_pull_struct_blob(&orf_el->values[i], trf, trf,
3578 (ndr_pull_flags_fn_t)ndr_pull_repsFromToBlob);
3579 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
3580 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
3581 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
3584 if (trf->version != 1) {
3585 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
3589 * we compare the source dsa objectGUID not the invocation_id
3590 * because we want only one repsFrom value per source dsa
3591 * and when the invocation_id of the source dsa has changed we don't need
3592 * the old repsFrom with the old invocation_id
3594 if (!GUID_equal(&trf->ctr.ctr1.source_dsa_obj_guid,
3595 &ar->objs->source_dsa->source_dsa_obj_guid)) {
3601 nrf_value = &orf_el->values[i];
3606 * copy over all old values to the new ldb_message
3608 ret = ldb_msg_add_empty(msg, "repsFrom", 0, &nrf_el);
3609 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
3614 * if we haven't found an old repsFrom value for the current source dsa
3615 * we'll add a new value
3618 struct ldb_val zero_value;
3619 ZERO_STRUCT(zero_value);
3620 ret = ldb_msg_add_value(msg, "repsFrom", &zero_value, &nrf_el);
3621 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
3623 nrf_value = &nrf_el->values[nrf_el->num_values - 1];
3626 /* we now fill the value which is already attached to ldb_message */
3627 ndr_err = ndr_push_struct_blob(nrf_value, msg,
3629 (ndr_push_flags_fn_t)ndr_push_repsFromToBlob);
3630 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
3631 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
3632 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
3636 * the ldb_message_element for the attribute, has all the old values and the new one
3637 * so we'll replace the whole attribute with all values
3639 nrf_el->flags = LDB_FLAG_MOD_REPLACE;
3642 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_MODIFY, msg);
3643 DEBUG(4, ("DRS replication uptodate modify message:\n%s\n", s));
3647 /* prepare the ldb_modify() request */
3648 ret = ldb_build_mod_req(&change_req,
3654 replmd_replicated_uptodate_modify_callback,
3656 LDB_REQ_SET_LOCATION(change_req);
3657 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
3659 return ldb_next_request(ar->module, change_req);
3662 static int replmd_replicated_uptodate_search_callback(struct ldb_request *req,
3663 struct ldb_reply *ares)
3665 struct replmd_replicated_request *ar = talloc_get_type(req->context,
3666 struct replmd_replicated_request);
3670 return ldb_module_done(ar->req, NULL, NULL,
3671 LDB_ERR_OPERATIONS_ERROR);
3673 if (ares->error != LDB_SUCCESS &&
3674 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
3675 return ldb_module_done(ar->req, ares->controls,
3676 ares->response, ares->error);
3679 switch (ares->type) {
3680 case LDB_REPLY_ENTRY:
3681 ar->search_msg = talloc_steal(ar, ares->message);
3684 case LDB_REPLY_REFERRAL:
3685 /* we ignore referrals */
3688 case LDB_REPLY_DONE:
3689 if (ar->search_msg == NULL) {
3690 ret = replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
3692 ret = replmd_replicated_uptodate_modify(ar);
3694 if (ret != LDB_SUCCESS) {
3695 return ldb_module_done(ar->req, NULL, NULL, ret);
3704 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar)
3706 struct ldb_context *ldb;
3708 static const char *attrs[] = {
3709 "replUpToDateVector",
3714 struct ldb_request *search_req;
3716 ldb = ldb_module_get_ctx(ar->module);
3717 ar->search_msg = NULL;
3719 ret = ldb_build_search_req(&search_req,
3722 ar->objs->partition_dn,
3728 replmd_replicated_uptodate_search_callback,
3730 LDB_REQ_SET_LOCATION(search_req);
3731 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
3733 return ldb_next_request(ar->module, search_req);
3738 static int replmd_extended_replicated_objects(struct ldb_module *module, struct ldb_request *req)
3740 struct ldb_context *ldb;
3741 struct dsdb_extended_replicated_objects *objs;
3742 struct replmd_replicated_request *ar;
3743 struct ldb_control **ctrls;
3746 struct replmd_private *replmd_private =
3747 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
3749 ldb = ldb_module_get_ctx(module);
3751 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_extended_replicated_objects\n");
3753 objs = talloc_get_type(req->op.extended.data, struct dsdb_extended_replicated_objects);
3755 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: invalid extended data\n");
3756 return LDB_ERR_PROTOCOL_ERROR;
3759 if (objs->version != DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION) {
3760 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: extended data invalid version [%u != %u]\n",
3761 objs->version, DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION);
3762 return LDB_ERR_PROTOCOL_ERROR;
3765 ar = replmd_ctx_init(module, req);
3767 return LDB_ERR_OPERATIONS_ERROR;
3769 /* Set the flags to have the replmd_op_callback run over the full set of objects */
3770 ar->apply_mode = true;
3772 ar->schema = dsdb_get_schema(ldb, ar);
3774 ldb_debug_set(ldb, LDB_DEBUG_FATAL, "replmd_ctx_init: no loaded schema found\n");
3776 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
3777 return LDB_ERR_CONSTRAINT_VIOLATION;
3780 ctrls = req->controls;
3782 if (req->controls) {
3783 req->controls = talloc_memdup(ar, req->controls,
3784 talloc_get_size(req->controls));
3785 if (!req->controls) return replmd_replicated_request_werror(ar, WERR_NOMEM);
3788 /* This allows layers further down to know if a change came in over replication */
3789 ret = ldb_request_add_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID, false, NULL);
3790 if (ret != LDB_SUCCESS) {
3794 /* If this change contained linked attributes in the body
3795 * (rather than in the links section) we need to update
3796 * backlinks in linked_attributes */
3797 ret = ldb_request_add_control(req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
3798 if (ret != LDB_SUCCESS) {
3802 ar->controls = req->controls;
3803 req->controls = ctrls;
3805 DEBUG(4,("linked_attributes_count=%u\n", objs->linked_attributes_count));
3807 /* save away the linked attributes for the end of the
3809 for (i=0; i<ar->objs->linked_attributes_count; i++) {
3810 struct la_entry *la_entry;
3812 if (replmd_private->la_ctx == NULL) {
3813 replmd_private->la_ctx = talloc_new(replmd_private);
3815 la_entry = talloc(replmd_private->la_ctx, struct la_entry);
3816 if (la_entry == NULL) {
3818 return LDB_ERR_OPERATIONS_ERROR;
3820 la_entry->la = talloc(la_entry, struct drsuapi_DsReplicaLinkedAttribute);
3821 if (la_entry->la == NULL) {
3822 talloc_free(la_entry);
3824 return LDB_ERR_OPERATIONS_ERROR;
3826 *la_entry->la = ar->objs->linked_attributes[i];
3828 /* we need to steal the non-scalars so they stay
3829 around until the end of the transaction */
3830 talloc_steal(la_entry->la, la_entry->la->identifier);
3831 talloc_steal(la_entry->la, la_entry->la->value.blob);
3833 DLIST_ADD(replmd_private->la_list, la_entry);
3836 return replmd_replicated_apply_next(ar);
3840 process one linked attribute structure
3842 static int replmd_process_linked_attribute(struct ldb_module *module,
3843 struct la_entry *la_entry,
3844 struct ldb_request *parent)
3846 struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
3847 struct ldb_context *ldb = ldb_module_get_ctx(module);
3848 struct ldb_message *msg;
3849 TALLOC_CTX *tmp_ctx = talloc_new(la_entry);
3850 const struct dsdb_schema *schema = dsdb_get_schema(ldb, tmp_ctx);
3852 const struct dsdb_attribute *attr;
3853 struct dsdb_dn *dsdb_dn;
3854 uint64_t seq_num = 0;
3855 struct ldb_message_element *old_el;
3857 time_t t = time(NULL);
3858 struct ldb_result *res;
3859 const char *attrs[2];
3860 struct parsed_dn *pdn_list, *pdn;
3861 struct GUID guid = GUID_zero();
3863 bool active = (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)?true:false;
3864 const struct GUID *our_invocation_id;
3867 linked_attributes[0]:
3868 &objs->linked_attributes[i]: struct drsuapi_DsReplicaLinkedAttribute
3870 identifier: struct drsuapi_DsReplicaObjectIdentifier
3871 __ndr_size : 0x0000003a (58)
3872 __ndr_size_sid : 0x00000000 (0)
3873 guid : 8e95b6a9-13dd-4158-89db-3220a5be5cc7
3875 __ndr_size_dn : 0x00000000 (0)
3877 attid : DRSUAPI_ATTID_member (0x1F)
3878 value: struct drsuapi_DsAttributeValue
3879 __ndr_size : 0x0000007e (126)
3881 blob : DATA_BLOB length=126
3882 flags : 0x00000001 (1)
3883 1: DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE
3884 originating_add_time : Wed Sep 2 22:20:01 2009 EST
3885 meta_data: struct drsuapi_DsReplicaMetaData
3886 version : 0x00000015 (21)
3887 originating_change_time : Wed Sep 2 23:39:07 2009 EST
3888 originating_invocation_id: 794640f3-18cf-40ee-a211-a93992b67a64
3889 originating_usn : 0x000000000001e19c (123292)
3891 (for cases where the link is to a normal DN)
3892 &target: struct drsuapi_DsReplicaObjectIdentifier3
3893 __ndr_size : 0x0000007e (126)
3894 __ndr_size_sid : 0x0000001c (28)
3895 guid : 7639e594-db75-4086-b0d4-67890ae46031
3896 sid : S-1-5-21-2848215498-2472035911-1947525656-19924
3897 __ndr_size_dn : 0x00000022 (34)
3898 dn : 'CN=UOne,OU=TestOU,DC=vsofs8,DC=com'
3901 /* find the attribute being modified */
3902 attr = dsdb_attribute_by_attributeID_id(schema, la->attid);
3904 DEBUG(0, (__location__ ": Unable to find attributeID 0x%x\n", la->attid));
3905 talloc_free(tmp_ctx);
3906 return LDB_ERR_OPERATIONS_ERROR;
3909 attrs[0] = attr->lDAPDisplayName;
3912 /* get the existing message from the db for the object with
3913 this GUID, returning attribute being modified. We will then
3914 use this msg as the basis for a modify call */
3915 ret = dsdb_module_search(module, tmp_ctx, &res, NULL, LDB_SCOPE_SUBTREE, attrs,
3916 DSDB_FLAG_NEXT_MODULE |
3917 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
3918 DSDB_SEARCH_SHOW_RECYCLED |
3919 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
3920 DSDB_SEARCH_REVEAL_INTERNALS,
3922 "objectGUID=%s", GUID_string(tmp_ctx, &la->identifier->guid));
3923 if (ret != LDB_SUCCESS) {
3924 talloc_free(tmp_ctx);
3927 if (res->count != 1) {
3928 ldb_asprintf_errstring(ldb, "DRS linked attribute for GUID %s - DN not found",
3929 GUID_string(tmp_ctx, &la->identifier->guid));
3930 talloc_free(tmp_ctx);
3931 return LDB_ERR_NO_SUCH_OBJECT;
3935 if (msg->num_elements == 0) {
3936 ret = ldb_msg_add_empty(msg, attr->lDAPDisplayName, LDB_FLAG_MOD_REPLACE, &old_el);
3937 if (ret != LDB_SUCCESS) {
3938 ldb_module_oom(module);
3939 talloc_free(tmp_ctx);
3940 return LDB_ERR_OPERATIONS_ERROR;
3943 old_el = &msg->elements[0];
3944 old_el->flags = LDB_FLAG_MOD_REPLACE;
3947 /* parse the existing links */
3948 ret = get_parsed_dns(module, tmp_ctx, old_el, &pdn_list, attr->syntax->ldap_oid, parent);
3949 if (ret != LDB_SUCCESS) {
3950 talloc_free(tmp_ctx);
3954 /* get our invocationId */
3955 our_invocation_id = samdb_ntds_invocation_id(ldb);
3956 if (!our_invocation_id) {
3957 ldb_debug_set(ldb, LDB_DEBUG_ERROR, __location__ ": unable to find invocationId\n");
3958 talloc_free(tmp_ctx);
3959 return LDB_ERR_OPERATIONS_ERROR;
3962 ret = replmd_check_upgrade_links(pdn_list, old_el->num_values, old_el, our_invocation_id);
3963 if (ret != LDB_SUCCESS) {
3964 talloc_free(tmp_ctx);
3968 status = dsdb_dn_la_from_blob(ldb, attr, schema, tmp_ctx, la->value.blob, &dsdb_dn);
3969 if (!W_ERROR_IS_OK(status)) {
3970 ldb_asprintf_errstring(ldb, "Failed to parsed linked attribute blob for %s on %s - %s\n",
3971 old_el->name, ldb_dn_get_linearized(msg->dn), win_errstr(status));
3972 return LDB_ERR_OPERATIONS_ERROR;
3975 ntstatus = dsdb_get_extended_dn_guid(dsdb_dn->dn, &guid, "GUID");
3976 if (!NT_STATUS_IS_OK(ntstatus) && active) {
3977 ldb_asprintf_errstring(ldb, "Failed to find GUID in linked attribute blob for %s on %s from %s",
3979 ldb_dn_get_linearized(dsdb_dn->dn),
3980 ldb_dn_get_linearized(msg->dn));
3981 return LDB_ERR_OPERATIONS_ERROR;
3984 /* re-resolve the DN by GUID, as the DRS server may give us an
3986 ret = dsdb_module_dn_by_guid(module, dsdb_dn, &guid, &dsdb_dn->dn, parent);
3987 if (ret != LDB_SUCCESS) {
3988 DEBUG(2,(__location__ ": WARNING: Failed to re-resolve GUID %s - using %s",
3989 GUID_string(tmp_ctx, &guid),
3990 ldb_dn_get_linearized(dsdb_dn->dn)));
3993 /* see if this link already exists */
3994 pdn = parsed_dn_find(pdn_list, old_el->num_values, &guid, dsdb_dn->dn);
3996 /* see if this update is newer than what we have already */
3997 struct GUID invocation_id = GUID_zero();
3998 uint32_t version = 0;
3999 uint32_t originating_usn = 0;
4000 NTTIME change_time = 0;
4001 uint32_t rmd_flags = dsdb_dn_rmd_flags(pdn->dsdb_dn->dn);
4003 dsdb_get_extended_dn_guid(pdn->dsdb_dn->dn, &invocation_id, "RMD_INVOCID");
4004 dsdb_get_extended_dn_uint32(pdn->dsdb_dn->dn, &version, "RMD_VERSION");
4005 dsdb_get_extended_dn_uint32(pdn->dsdb_dn->dn, &originating_usn, "RMD_ORIGINATING_USN");
4006 dsdb_get_extended_dn_nttime(pdn->dsdb_dn->dn, &change_time, "RMD_CHANGETIME");
4008 if (!replmd_update_is_newer(&invocation_id,
4009 &la->meta_data.originating_invocation_id,
4011 la->meta_data.version,
4013 la->meta_data.originating_change_time)) {
4014 DEBUG(3,("Discarding older DRS linked attribute update to %s on %s from %s\n",
4015 old_el->name, ldb_dn_get_linearized(msg->dn),
4016 GUID_string(tmp_ctx, &la->meta_data.originating_invocation_id)));
4017 talloc_free(tmp_ctx);
4021 /* get a seq_num for this change */
4022 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
4023 if (ret != LDB_SUCCESS) {
4024 talloc_free(tmp_ctx);
4028 if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
4029 /* remove the existing backlink */
4030 ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid, false, attr, false);
4031 if (ret != LDB_SUCCESS) {
4032 talloc_free(tmp_ctx);
4037 ret = replmd_update_la_val(tmp_ctx, pdn->v, dsdb_dn, pdn->dsdb_dn,
4038 &la->meta_data.originating_invocation_id,
4039 la->meta_data.originating_usn, seq_num,
4040 la->meta_data.originating_change_time,
4041 la->meta_data.version,
4043 if (ret != LDB_SUCCESS) {
4044 talloc_free(tmp_ctx);
4049 /* add the new backlink */
4050 ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid, true, attr, false);
4051 if (ret != LDB_SUCCESS) {
4052 talloc_free(tmp_ctx);
4057 /* get a seq_num for this change */
4058 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
4059 if (ret != LDB_SUCCESS) {
4060 talloc_free(tmp_ctx);
4064 old_el->values = talloc_realloc(msg->elements, old_el->values,
4065 struct ldb_val, old_el->num_values+1);
4066 if (!old_el->values) {
4067 ldb_module_oom(module);
4068 return LDB_ERR_OPERATIONS_ERROR;
4070 old_el->num_values++;
4072 ret = replmd_build_la_val(tmp_ctx, &old_el->values[old_el->num_values-1], dsdb_dn,
4073 &la->meta_data.originating_invocation_id,
4074 la->meta_data.originating_usn, seq_num,
4075 la->meta_data.originating_change_time,
4076 la->meta_data.version,
4077 (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)?false:true);
4078 if (ret != LDB_SUCCESS) {
4079 talloc_free(tmp_ctx);
4084 ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid,
4086 if (ret != LDB_SUCCESS) {
4087 talloc_free(tmp_ctx);
4093 /* we only change whenChanged and uSNChanged if the seq_num
4095 if (add_time_element(msg, "whenChanged", t) != LDB_SUCCESS) {
4096 talloc_free(tmp_ctx);
4097 return ldb_operr(ldb);
4100 if (add_uint64_element(ldb, msg, "uSNChanged",
4101 seq_num) != LDB_SUCCESS) {
4102 talloc_free(tmp_ctx);
4103 return ldb_operr(ldb);
4106 old_el = ldb_msg_find_element(msg, attr->lDAPDisplayName);
4107 if (old_el == NULL) {
4108 talloc_free(tmp_ctx);
4109 return ldb_operr(ldb);
4112 ret = dsdb_check_single_valued_link(attr, old_el);
4113 if (ret != LDB_SUCCESS) {
4114 talloc_free(tmp_ctx);
4118 old_el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
4120 ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE, parent);
4121 if (ret != LDB_SUCCESS) {
4122 ldb_debug(ldb, LDB_DEBUG_WARNING, "Failed to apply linked attribute change '%s'\n%s\n",
4124 ldb_ldif_message_string(ldb, tmp_ctx, LDB_CHANGETYPE_MODIFY, msg));
4125 talloc_free(tmp_ctx);
4129 talloc_free(tmp_ctx);
4134 static int replmd_extended(struct ldb_module *module, struct ldb_request *req)
4136 if (strcmp(req->op.extended.oid, DSDB_EXTENDED_REPLICATED_OBJECTS_OID) == 0) {
4137 return replmd_extended_replicated_objects(module, req);
4140 return ldb_next_request(module, req);
4145 we hook into the transaction operations to allow us to
4146 perform the linked attribute updates at the end of the whole
4147 transaction. This allows a forward linked attribute to be created
4148 before the object is created. During a vampire, w2k8 sends us linked
4149 attributes before the objects they are part of.
4151 static int replmd_start_transaction(struct ldb_module *module)
4153 /* create our private structure for this transaction */
4154 struct replmd_private *replmd_private = talloc_get_type(ldb_module_get_private(module),
4155 struct replmd_private);
4156 replmd_txn_cleanup(replmd_private);
4158 /* free any leftover mod_usn records from cancelled
4160 while (replmd_private->ncs) {
4161 struct nc_entry *e = replmd_private->ncs;
4162 DLIST_REMOVE(replmd_private->ncs, e);
4166 return ldb_next_start_trans(module);
4170 on prepare commit we loop over our queued la_context structures and
4173 static int replmd_prepare_commit(struct ldb_module *module)
4175 struct replmd_private *replmd_private =
4176 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
4177 struct la_entry *la, *prev;
4178 struct la_backlink *bl;
4181 /* walk the list backwards, to do the first entry first, as we
4182 * added the entries with DLIST_ADD() which puts them at the
4183 * start of the list */
4184 for (la = DLIST_TAIL(replmd_private->la_list); la; la=prev) {
4185 prev = DLIST_PREV(la);
4186 DLIST_REMOVE(replmd_private->la_list, la);
4187 ret = replmd_process_linked_attribute(module, la, NULL);
4188 if (ret != LDB_SUCCESS) {
4189 replmd_txn_cleanup(replmd_private);
4194 /* process our backlink list, creating and deleting backlinks
4196 for (bl=replmd_private->la_backlinks; bl; bl=bl->next) {
4197 ret = replmd_process_backlink(module, bl, NULL);
4198 if (ret != LDB_SUCCESS) {
4199 replmd_txn_cleanup(replmd_private);
4204 replmd_txn_cleanup(replmd_private);
4206 /* possibly change @REPLCHANGED */
4207 ret = replmd_notify_store(module, NULL);
4208 if (ret != LDB_SUCCESS) {
4212 return ldb_next_prepare_commit(module);
4215 static int replmd_del_transaction(struct ldb_module *module)
4217 struct replmd_private *replmd_private =
4218 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
4219 replmd_txn_cleanup(replmd_private);
4221 return ldb_next_del_trans(module);
4225 static const struct ldb_module_ops ldb_repl_meta_data_module_ops = {
4226 .name = "repl_meta_data",
4227 .init_context = replmd_init,
4229 .modify = replmd_modify,
4230 .rename = replmd_rename,
4231 .del = replmd_delete,
4232 .extended = replmd_extended,
4233 .start_transaction = replmd_start_transaction,
4234 .prepare_commit = replmd_prepare_commit,
4235 .del_transaction = replmd_del_transaction,
4238 int ldb_repl_meta_data_module_init(const char *version)
4240 LDB_MODULE_CHECK_VERSION(version);
4241 return ldb_register_module(&ldb_repl_meta_data_module_ops);