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
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 3 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>.
26 * Component: ldb repl_meta_data module
28 * Description: - add a unique objectGUID onto every new record,
29 * - handle whenCreated, whenChanged timestamps
30 * - handle uSNCreated, uSNChanged numbers
31 * - handle replPropertyMetaData attribute
34 * Author: Stefan Metzmacher
38 #include "ldb_module.h"
39 #include "dsdb/samdb/samdb.h"
40 #include "dsdb/common/proto.h"
41 #include "../libds/common/flags.h"
42 #include "librpc/gen_ndr/ndr_misc.h"
43 #include "librpc/gen_ndr/ndr_drsuapi.h"
44 #include "librpc/gen_ndr/ndr_drsblobs.h"
45 #include "param/param.h"
46 #include "libcli/security/dom_sid.h"
47 #include "lib/util/dlinklist.h"
48 #include "dsdb/samdb/ldb_modules/util.h"
49 #include "lib/util/binsearch.h"
50 #include "libcli/security/security.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)
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);
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);
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 ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE);
250 if (ret != LDB_SUCCESS) {
251 ldb_asprintf_errstring(ldb, "Failed to %s backlink from %s to %s - %s",
252 bl->active?"add":"remove",
253 ldb_dn_get_linearized(source_dn),
254 ldb_dn_get_linearized(target_dn),
256 talloc_free(tmp_ctx);
259 talloc_free(tmp_ctx);
264 add a backlink to the list of backlinks to add/delete in the prepare
267 static int replmd_add_backlink(struct ldb_module *module, const struct dsdb_schema *schema,
268 struct GUID *forward_guid, struct GUID *target_guid,
269 bool active, const struct dsdb_attribute *schema_attr, bool immediate)
271 const struct dsdb_attribute *target_attr;
272 struct la_backlink *bl;
273 struct replmd_private *replmd_private =
274 talloc_get_type_abort(ldb_module_get_private(module), struct replmd_private);
276 target_attr = dsdb_attribute_by_linkID(schema, schema_attr->linkID ^ 1);
279 * windows 2003 has a broken schema where the
280 * definition of msDS-IsDomainFor is missing (which is
281 * supposed to be the backlink of the
282 * msDS-HasDomainNCs attribute
287 /* see if its already in the list */
288 for (bl=replmd_private->la_backlinks; bl; bl=bl->next) {
289 if (GUID_equal(forward_guid, &bl->forward_guid) &&
290 GUID_equal(target_guid, &bl->target_guid) &&
291 (target_attr->lDAPDisplayName == bl->attr_name ||
292 strcmp(target_attr->lDAPDisplayName, bl->attr_name) == 0)) {
298 /* we found an existing one */
299 if (bl->active == active) {
302 DLIST_REMOVE(replmd_private->la_backlinks, bl);
307 if (replmd_private->bl_ctx == NULL) {
308 replmd_private->bl_ctx = talloc_new(replmd_private);
309 if (replmd_private->bl_ctx == NULL) {
310 ldb_module_oom(module);
311 return LDB_ERR_OPERATIONS_ERROR;
316 bl = talloc(replmd_private->bl_ctx, struct la_backlink);
318 ldb_module_oom(module);
319 return LDB_ERR_OPERATIONS_ERROR;
322 /* Ensure the schema does not go away before the bl->attr_name is used */
323 if (!talloc_reference(bl, schema)) {
325 ldb_module_oom(module);
326 return LDB_ERR_OPERATIONS_ERROR;
329 bl->attr_name = target_attr->lDAPDisplayName;
330 bl->forward_guid = *forward_guid;
331 bl->target_guid = *target_guid;
334 /* the caller may ask for this backlink to be processed
337 int ret = replmd_process_backlink(module, bl);
342 DLIST_ADD(replmd_private->la_backlinks, bl);
349 * Callback for most write operations in this module:
351 * notify the repl task that a object has changed. The notifies are
352 * gathered up in the replmd_private structure then written to the
353 * @REPLCHANGED object in each partition during the prepare_commit
355 static int replmd_op_callback(struct ldb_request *req, struct ldb_reply *ares)
358 struct replmd_replicated_request *ac =
359 talloc_get_type_abort(req->context, struct replmd_replicated_request);
360 struct replmd_private *replmd_private =
361 talloc_get_type_abort(ldb_module_get_private(ac->module), struct replmd_private);
362 struct nc_entry *modified_partition;
363 struct ldb_control *partition_ctrl;
364 const struct dsdb_control_current_partition *partition;
366 struct ldb_control **controls;
368 partition_ctrl = ldb_reply_get_control(ares, DSDB_CONTROL_CURRENT_PARTITION_OID);
370 /* Remove the 'partition' control from what we pass up the chain */
371 controls = controls_except_specified(ares->controls, ares, partition_ctrl);
373 if (ares->error != LDB_SUCCESS) {
374 return ldb_module_done(ac->req, controls,
375 ares->response, ares->error);
378 if (ares->type != LDB_REPLY_DONE) {
379 ldb_set_errstring(ldb_module_get_ctx(ac->module), "Invalid reply type for notify\n!");
380 return ldb_module_done(ac->req, NULL,
381 NULL, LDB_ERR_OPERATIONS_ERROR);
384 if (!partition_ctrl) {
385 ldb_set_errstring(ldb_module_get_ctx(ac->module),"No partition control on reply");
386 return ldb_module_done(ac->req, NULL,
387 NULL, LDB_ERR_OPERATIONS_ERROR);
390 partition = talloc_get_type_abort(partition_ctrl->data,
391 struct dsdb_control_current_partition);
393 if (ac->seq_num > 0) {
394 for (modified_partition = replmd_private->ncs; modified_partition;
395 modified_partition = modified_partition->next) {
396 if (ldb_dn_compare(modified_partition->dn, partition->dn) == 0) {
401 if (modified_partition == NULL) {
402 modified_partition = talloc_zero(replmd_private, struct nc_entry);
403 if (!modified_partition) {
404 ldb_oom(ldb_module_get_ctx(ac->module));
405 return ldb_module_done(ac->req, NULL,
406 NULL, LDB_ERR_OPERATIONS_ERROR);
408 modified_partition->dn = ldb_dn_copy(modified_partition, partition->dn);
409 if (!modified_partition->dn) {
410 ldb_oom(ldb_module_get_ctx(ac->module));
411 return ldb_module_done(ac->req, NULL,
412 NULL, LDB_ERR_OPERATIONS_ERROR);
414 DLIST_ADD(replmd_private->ncs, modified_partition);
417 if (ac->seq_num > modified_partition->mod_usn) {
418 modified_partition->mod_usn = ac->seq_num;
420 modified_partition->mod_usn_urgent = ac->seq_num;
425 if (ac->apply_mode) {
429 ret = replmd_replicated_apply_next(ac);
430 if (ret != LDB_SUCCESS) {
431 return ldb_module_done(ac->req, NULL, NULL, ret);
435 /* free the partition control container here, for the
436 * common path. Other cases will have it cleaned up
437 * eventually with the ares */
438 talloc_free(partition_ctrl);
439 return ldb_module_done(ac->req,
440 controls_except_specified(controls, ares, partition_ctrl),
441 ares->response, LDB_SUCCESS);
447 * update a @REPLCHANGED record in each partition if there have been
448 * any writes of replicated data in the partition
450 static int replmd_notify_store(struct ldb_module *module)
452 struct replmd_private *replmd_private =
453 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
455 while (replmd_private->ncs) {
457 struct nc_entry *modified_partition = replmd_private->ncs;
459 ret = dsdb_module_save_partition_usn(module, modified_partition->dn,
460 modified_partition->mod_usn,
461 modified_partition->mod_usn_urgent);
462 if (ret != LDB_SUCCESS) {
463 DEBUG(0,(__location__ ": Failed to save partition uSN for %s\n",
464 ldb_dn_get_linearized(modified_partition->dn)));
467 DLIST_REMOVE(replmd_private->ncs, modified_partition);
468 talloc_free(modified_partition);
476 created a replmd_replicated_request context
478 static struct replmd_replicated_request *replmd_ctx_init(struct ldb_module *module,
479 struct ldb_request *req)
481 struct ldb_context *ldb;
482 struct replmd_replicated_request *ac;
484 ldb = ldb_module_get_ctx(module);
486 ac = talloc_zero(req, struct replmd_replicated_request);
495 ac->schema = dsdb_get_schema(ldb, ac);
497 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
498 "replmd_modify: no dsdb_schema loaded");
499 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
507 add a time element to a record
509 static int add_time_element(struct ldb_message *msg, const char *attr, time_t t)
511 struct ldb_message_element *el;
514 if (ldb_msg_find_element(msg, attr) != NULL) {
518 s = ldb_timestring(msg, t);
520 return LDB_ERR_OPERATIONS_ERROR;
523 if (ldb_msg_add_string(msg, attr, s) != LDB_SUCCESS) {
524 return LDB_ERR_OPERATIONS_ERROR;
527 el = ldb_msg_find_element(msg, attr);
528 /* always set as replace. This works because on add ops, the flag
530 el->flags = LDB_FLAG_MOD_REPLACE;
536 add a uint64_t element to a record
538 static int add_uint64_element(struct ldb_message *msg, const char *attr, uint64_t v)
540 struct ldb_message_element *el;
542 if (ldb_msg_find_element(msg, attr) != NULL) {
546 if (ldb_msg_add_fmt(msg, attr, "%llu", (unsigned long long)v) != LDB_SUCCESS) {
547 return LDB_ERR_OPERATIONS_ERROR;
550 el = ldb_msg_find_element(msg, attr);
551 /* always set as replace. This works because on add ops, the flag
553 el->flags = LDB_FLAG_MOD_REPLACE;
558 static int replmd_replPropertyMetaData1_attid_sort(const struct replPropertyMetaData1 *m1,
559 const struct replPropertyMetaData1 *m2,
560 const uint32_t *rdn_attid)
562 if (m1->attid == m2->attid) {
567 * the rdn attribute should be at the end!
568 * so we need to return a value greater than zero
569 * which means m1 is greater than m2
571 if (m1->attid == *rdn_attid) {
576 * the rdn attribute should be at the end!
577 * so we need to return a value less than zero
578 * which means m2 is greater than m1
580 if (m2->attid == *rdn_attid) {
584 return m1->attid > m2->attid ? 1 : -1;
587 static int replmd_replPropertyMetaDataCtr1_sort(struct replPropertyMetaDataCtr1 *ctr1,
588 const struct dsdb_schema *schema,
591 const char *rdn_name;
592 const struct dsdb_attribute *rdn_sa;
594 rdn_name = ldb_dn_get_rdn_name(dn);
596 DEBUG(0,(__location__ ": No rDN for %s?\n", ldb_dn_get_linearized(dn)));
597 return LDB_ERR_OPERATIONS_ERROR;
600 rdn_sa = dsdb_attribute_by_lDAPDisplayName(schema, rdn_name);
601 if (rdn_sa == NULL) {
602 DEBUG(0,(__location__ ": No sa found for rDN %s for %s\n", rdn_name, ldb_dn_get_linearized(dn)));
603 return LDB_ERR_OPERATIONS_ERROR;
606 DEBUG(6,("Sorting rpmd with attid exception %u rDN=%s DN=%s\n",
607 rdn_sa->attributeID_id, rdn_name, ldb_dn_get_linearized(dn)));
609 LDB_TYPESAFE_QSORT(ctr1->array, ctr1->count, &rdn_sa->attributeID_id, replmd_replPropertyMetaData1_attid_sort);
614 static int replmd_ldb_message_element_attid_sort(const struct ldb_message_element *e1,
615 const struct ldb_message_element *e2,
616 const struct dsdb_schema *schema)
618 const struct dsdb_attribute *a1;
619 const struct dsdb_attribute *a2;
622 * TODO: make this faster by caching the dsdb_attribute pointer
623 * on the ldb_messag_element
626 a1 = dsdb_attribute_by_lDAPDisplayName(schema, e1->name);
627 a2 = dsdb_attribute_by_lDAPDisplayName(schema, e2->name);
630 * TODO: remove this check, we should rely on e1 and e2 having valid attribute names
634 return strcasecmp(e1->name, e2->name);
636 if (a1->attributeID_id == a2->attributeID_id) {
639 return a1->attributeID_id > a2->attributeID_id ? 1 : -1;
642 static void replmd_ldb_message_sort(struct ldb_message *msg,
643 const struct dsdb_schema *schema)
645 LDB_TYPESAFE_QSORT(msg->elements, msg->num_elements, schema, replmd_ldb_message_element_attid_sort);
648 static int replmd_build_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
649 const struct GUID *invocation_id, uint64_t seq_num,
650 uint64_t local_usn, NTTIME nttime, uint32_t version, bool deleted);
654 fix up linked attributes in replmd_add.
655 This involves setting up the right meta-data in extended DN
656 components, and creating backlinks to the object
658 static int replmd_add_fix_la(struct ldb_module *module, struct ldb_message_element *el,
659 uint64_t seq_num, const struct GUID *invocationId, time_t t,
660 struct GUID *guid, const struct dsdb_attribute *sa)
663 TALLOC_CTX *tmp_ctx = talloc_new(el->values);
664 struct ldb_context *ldb = ldb_module_get_ctx(module);
666 /* We will take a reference to the schema in replmd_add_backlink */
667 const struct dsdb_schema *schema = dsdb_get_schema(ldb, NULL);
670 unix_to_nt_time(&now, t);
672 for (i=0; i<el->num_values; i++) {
673 struct ldb_val *v = &el->values[i];
674 struct dsdb_dn *dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, v, sa->syntax->ldap_oid);
675 struct GUID target_guid;
679 /* note that the DN already has the extended
680 components from the extended_dn_store module */
681 status = dsdb_get_extended_dn_guid(dsdb_dn->dn, &target_guid, "GUID");
682 if (!NT_STATUS_IS_OK(status) || GUID_all_zero(&target_guid)) {
683 ret = dsdb_module_guid_by_dn(module, dsdb_dn->dn, &target_guid);
684 if (ret != LDB_SUCCESS) {
685 talloc_free(tmp_ctx);
688 ret = dsdb_set_extended_dn_guid(dsdb_dn->dn, &target_guid, "GUID");
689 if (ret != LDB_SUCCESS) {
690 talloc_free(tmp_ctx);
695 ret = replmd_build_la_val(el->values, v, dsdb_dn, invocationId,
696 seq_num, seq_num, now, 0, false);
697 if (ret != LDB_SUCCESS) {
698 talloc_free(tmp_ctx);
702 ret = replmd_add_backlink(module, schema, guid, &target_guid, true, sa, false);
703 if (ret != LDB_SUCCESS) {
704 talloc_free(tmp_ctx);
709 talloc_free(tmp_ctx);
715 intercept add requests
717 static int replmd_add(struct ldb_module *module, struct ldb_request *req)
719 struct ldb_context *ldb;
720 struct ldb_control *control;
721 struct replmd_replicated_request *ac;
722 enum ndr_err_code ndr_err;
723 struct ldb_request *down_req;
724 struct ldb_message *msg;
725 const DATA_BLOB *guid_blob;
727 struct replPropertyMetaDataBlob nmd;
728 struct ldb_val nmd_value;
729 const struct GUID *our_invocation_id;
730 time_t t = time(NULL);
735 unsigned int functional_level;
737 bool allow_add_guid = false;
738 bool remove_current_guid = false;
739 bool is_urgent = false;
740 struct ldb_message_element *objectclass_el;
742 /* check if there's a show relax control (used by provision to say 'I know what I'm doing') */
743 control = ldb_request_get_control(req, LDB_CONTROL_RELAX_OID);
745 allow_add_guid = true;
748 /* do not manipulate our control entries */
749 if (ldb_dn_is_special(req->op.add.message->dn)) {
750 return ldb_next_request(module, req);
753 ldb = ldb_module_get_ctx(module);
755 functional_level = dsdb_functional_level(ldb);
757 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_add\n");
759 ac = replmd_ctx_init(module, req);
761 return LDB_ERR_OPERATIONS_ERROR;
764 guid_blob = ldb_msg_find_ldb_val(req->op.add.message, "objectGUID");
765 if ( guid_blob != NULL ) {
766 if( !allow_add_guid ) {
767 ldb_debug_set(ldb, LDB_DEBUG_ERROR,
768 "replmd_add: it's not allowed to add an object with objectGUID\n");
770 return LDB_ERR_UNWILLING_TO_PERFORM;
772 NTSTATUS status = GUID_from_data_blob(guid_blob,&guid);
773 if ( !NT_STATUS_IS_OK(status)) {
774 ldb_debug_set(ldb, LDB_DEBUG_ERROR,
775 "replmd_add: Unable to parse as a GUID the attribute objectGUID\n");
777 return LDB_ERR_UNWILLING_TO_PERFORM;
779 /* we remove this attribute as it can be a string and will not be treated
780 correctly and then we will readd it latter on in the good format*/
781 remove_current_guid = true;
785 guid = GUID_random();
788 /* Get a sequence number from the backend */
789 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ac->seq_num);
790 if (ret != LDB_SUCCESS) {
795 /* get our invocationId */
796 our_invocation_id = samdb_ntds_invocation_id(ldb);
797 if (!our_invocation_id) {
798 ldb_debug_set(ldb, LDB_DEBUG_ERROR,
799 "replmd_add: unable to find invocationId\n");
801 return LDB_ERR_OPERATIONS_ERROR;
804 /* we have to copy the message as the caller might have it as a const */
805 msg = ldb_msg_copy_shallow(ac, req->op.add.message);
809 return LDB_ERR_OPERATIONS_ERROR;
812 /* generated times */
813 unix_to_nt_time(&now, t);
814 time_str = ldb_timestring(msg, t);
818 return LDB_ERR_OPERATIONS_ERROR;
820 if (remove_current_guid) {
821 ldb_msg_remove_attr(msg,"objectGUID");
825 * remove autogenerated attributes
827 ldb_msg_remove_attr(msg, "whenCreated");
828 ldb_msg_remove_attr(msg, "whenChanged");
829 ldb_msg_remove_attr(msg, "uSNCreated");
830 ldb_msg_remove_attr(msg, "uSNChanged");
831 ldb_msg_remove_attr(msg, "replPropertyMetaData");
834 * readd replicated attributes
836 ret = ldb_msg_add_string(msg, "whenCreated", time_str);
837 if (ret != LDB_SUCCESS) {
843 /* build the replication meta_data */
846 nmd.ctr.ctr1.count = msg->num_elements;
847 nmd.ctr.ctr1.array = talloc_array(msg,
848 struct replPropertyMetaData1,
850 if (!nmd.ctr.ctr1.array) {
853 return LDB_ERR_OPERATIONS_ERROR;
856 for (i=0; i < msg->num_elements; i++) {
857 struct ldb_message_element *e = &msg->elements[i];
858 struct replPropertyMetaData1 *m = &nmd.ctr.ctr1.array[ni];
859 const struct dsdb_attribute *sa;
861 if (e->name[0] == '@') continue;
863 sa = dsdb_attribute_by_lDAPDisplayName(ac->schema, e->name);
865 ldb_debug_set(ldb, LDB_DEBUG_ERROR,
866 "replmd_add: attribute '%s' not defined in schema\n",
869 return LDB_ERR_NO_SUCH_ATTRIBUTE;
872 if ((sa->systemFlags & DS_FLAG_ATTR_NOT_REPLICATED) || (sa->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED)) {
873 /* if the attribute is not replicated (0x00000001)
874 * or constructed (0x00000004) it has no metadata
879 if (sa->linkID != 0 && functional_level > DS_DOMAIN_FUNCTION_2000) {
880 ret = replmd_add_fix_la(module, e, ac->seq_num, our_invocation_id, t, &guid, sa);
881 if (ret != LDB_SUCCESS) {
885 /* linked attributes are not stored in
886 replPropertyMetaData in FL above w2k */
890 m->attid = sa->attributeID_id;
892 m->originating_change_time = now;
893 m->originating_invocation_id = *our_invocation_id;
894 m->originating_usn = ac->seq_num;
895 m->local_usn = ac->seq_num;
899 /* fix meta data count */
900 nmd.ctr.ctr1.count = ni;
903 * sort meta data array, and move the rdn attribute entry to the end
905 ret = replmd_replPropertyMetaDataCtr1_sort(&nmd.ctr.ctr1, ac->schema, msg->dn);
906 if (ret != LDB_SUCCESS) {
911 /* generated NDR encoded values */
912 ndr_err = ndr_push_struct_blob(&nmd_value, msg,
914 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
915 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
918 return LDB_ERR_OPERATIONS_ERROR;
922 * add the autogenerated values
924 ret = dsdb_msg_add_guid(msg, &guid, "objectGUID");
925 if (ret != LDB_SUCCESS) {
930 ret = ldb_msg_add_string(msg, "whenChanged", time_str);
931 if (ret != LDB_SUCCESS) {
936 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", ac->seq_num);
937 if (ret != LDB_SUCCESS) {
942 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ac->seq_num);
943 if (ret != LDB_SUCCESS) {
948 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
949 if (ret != LDB_SUCCESS) {
956 * sort the attributes by attid before storing the object
958 replmd_ldb_message_sort(msg, ac->schema);
960 objectclass_el = ldb_msg_find_element(msg, "objectClass");
961 is_urgent = replmd_check_urgent_objectclass(objectclass_el,
962 REPL_URGENT_ON_CREATE);
964 ac->is_urgent = is_urgent;
965 ret = ldb_build_add_req(&down_req, ldb, ac,
968 ac, replmd_op_callback,
971 if (ret != LDB_SUCCESS) {
976 if (functional_level == DS_DOMAIN_FUNCTION_2000) {
977 ret = ldb_request_add_control(down_req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
978 if (ret != LDB_SUCCESS) {
984 /* mark the control done */
986 control->critical = 0;
989 /* go on with the call chain */
990 return ldb_next_request(module, down_req);
995 * update the replPropertyMetaData for one element
997 static int replmd_update_rpmd_element(struct ldb_context *ldb,
998 struct ldb_message *msg,
999 struct ldb_message_element *el,
1000 struct ldb_message_element *old_el,
1001 struct replPropertyMetaDataBlob *omd,
1002 const struct dsdb_schema *schema,
1004 const struct GUID *our_invocation_id,
1008 const struct dsdb_attribute *a;
1009 struct replPropertyMetaData1 *md1;
1011 a = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
1013 DEBUG(0,(__location__ ": Unable to find attribute %s in schema\n",
1015 return LDB_ERR_OPERATIONS_ERROR;
1018 if ((a->systemFlags & DS_FLAG_ATTR_NOT_REPLICATED) || (a->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED)) {
1022 /* if the attribute's value haven't changed then return LDB_SUCCESS */
1023 if (old_el != NULL && ldb_msg_element_compare(el, old_el) == 0) {
1027 for (i=0; i<omd->ctr.ctr1.count; i++) {
1028 if (a->attributeID_id == omd->ctr.ctr1.array[i].attid) break;
1031 if (a->linkID != 0 && dsdb_functional_level(ldb) > DS_DOMAIN_FUNCTION_2000) {
1032 /* linked attributes are not stored in
1033 replPropertyMetaData in FL above w2k, but we do
1034 raise the seqnum for the object */
1035 if (*seq_num == 0 &&
1036 ldb_sequence_number(ldb, LDB_SEQ_NEXT, seq_num) != LDB_SUCCESS) {
1037 return LDB_ERR_OPERATIONS_ERROR;
1042 if (i == omd->ctr.ctr1.count) {
1043 /* we need to add a new one */
1044 omd->ctr.ctr1.array = talloc_realloc(msg, omd->ctr.ctr1.array,
1045 struct replPropertyMetaData1, omd->ctr.ctr1.count+1);
1046 if (omd->ctr.ctr1.array == NULL) {
1048 return LDB_ERR_OPERATIONS_ERROR;
1050 omd->ctr.ctr1.count++;
1051 ZERO_STRUCT(omd->ctr.ctr1.array[i]);
1054 /* Get a new sequence number from the backend. We only do this
1055 * if we have a change that requires a new
1056 * replPropertyMetaData element
1058 if (*seq_num == 0) {
1059 int ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, seq_num);
1060 if (ret != LDB_SUCCESS) {
1061 return LDB_ERR_OPERATIONS_ERROR;
1065 md1 = &omd->ctr.ctr1.array[i];
1067 md1->attid = a->attributeID_id;
1068 md1->originating_change_time = now;
1069 md1->originating_invocation_id = *our_invocation_id;
1070 md1->originating_usn = *seq_num;
1071 md1->local_usn = *seq_num;
1077 * update the replPropertyMetaData object each time we modify an
1078 * object. This is needed for DRS replication, as the merge on the
1079 * client is based on this object
1081 static int replmd_update_rpmd(struct ldb_module *module,
1082 const struct dsdb_schema *schema,
1083 struct ldb_request *req,
1084 struct ldb_message *msg, uint64_t *seq_num,
1088 const struct ldb_val *omd_value;
1089 enum ndr_err_code ndr_err;
1090 struct replPropertyMetaDataBlob omd;
1093 const struct GUID *our_invocation_id;
1095 const char *attrs[] = { "replPropertyMetaData", "*", NULL };
1096 struct ldb_result *res;
1097 struct ldb_context *ldb;
1098 struct ldb_message_element *objectclass_el;
1099 enum urgent_situation situation;
1102 ldb = ldb_module_get_ctx(module);
1104 our_invocation_id = samdb_ntds_invocation_id(ldb);
1105 if (!our_invocation_id) {
1106 /* this happens during an initial vampire while
1107 updating the schema */
1108 DEBUG(5,("No invocationID - skipping replPropertyMetaData update\n"));
1112 unix_to_nt_time(&now, t);
1114 /* search for the existing replPropertyMetaDataBlob. We need
1115 * to use REVEAL and ask for DNs in storage format to support
1116 * the check for values being the same in
1117 * replmd_update_rpmd_element()
1119 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attrs,
1120 DSDB_SEARCH_SHOW_DELETED |
1121 DSDB_SEARCH_SHOW_EXTENDED_DN |
1122 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
1123 DSDB_SEARCH_REVEAL_INTERNALS);
1124 if (ret != LDB_SUCCESS || res->count != 1) {
1125 DEBUG(0,(__location__ ": Object %s failed to find replPropertyMetaData\n",
1126 ldb_dn_get_linearized(msg->dn)));
1127 return LDB_ERR_OPERATIONS_ERROR;
1130 /* if isDeleted is present and is TRUE, then we consider we are deleting,
1131 * otherwise we consider we are updating */
1132 if (ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")) {
1133 situation = REPL_URGENT_ON_DELETE;
1135 situation = REPL_URGENT_ON_UPDATE;
1138 objectclass_el = ldb_msg_find_element(res->msgs[0], "objectClass");
1139 if (is_urgent && replmd_check_urgent_objectclass(objectclass_el,
1144 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
1146 DEBUG(0,(__location__ ": Object %s does not have a replPropertyMetaData attribute\n",
1147 ldb_dn_get_linearized(msg->dn)));
1148 return LDB_ERR_OPERATIONS_ERROR;
1151 ndr_err = ndr_pull_struct_blob(omd_value, msg, &omd,
1152 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
1153 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1154 DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s\n",
1155 ldb_dn_get_linearized(msg->dn)));
1156 return LDB_ERR_OPERATIONS_ERROR;
1159 if (omd.version != 1) {
1160 DEBUG(0,(__location__ ": bad version %u in replPropertyMetaData for %s\n",
1161 omd.version, ldb_dn_get_linearized(msg->dn)));
1162 return LDB_ERR_OPERATIONS_ERROR;
1165 /*we have elements that will be modified*/
1166 if (msg->num_elements > 0) {
1167 /*if we are RODC and this is a DRSR update then its ok*/
1168 if (!ldb_request_get_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID)) {
1169 ret = samdb_rodc(ldb, &rodc);
1170 if (ret != LDB_SUCCESS) {
1171 DEBUG(4, (__location__ ": unable to tell if we are an RODC\n"));
1173 ldb_asprintf_errstring(ldb, "RODC modify is forbidden\n");
1174 return LDB_ERR_REFERRAL;
1179 for (i=0; i<msg->num_elements; i++) {
1180 struct ldb_message_element *old_el;
1181 old_el = ldb_msg_find_element(res->msgs[0], msg->elements[i].name);
1182 ret = replmd_update_rpmd_element(ldb, msg, &msg->elements[i], old_el, &omd, schema, seq_num,
1183 our_invocation_id, now);
1184 if (ret != LDB_SUCCESS) {
1188 if (is_urgent && !*is_urgent && (situation == REPL_URGENT_ON_UPDATE)) {
1189 *is_urgent = replmd_check_urgent_attribute(&msg->elements[i]);
1195 * replmd_update_rpmd_element has done an update if the
1198 if (*seq_num != 0) {
1199 struct ldb_val *md_value;
1200 struct ldb_message_element *el;
1202 md_value = talloc(msg, struct ldb_val);
1203 if (md_value == NULL) {
1205 return LDB_ERR_OPERATIONS_ERROR;
1208 ret = replmd_replPropertyMetaDataCtr1_sort(&omd.ctr.ctr1, schema, msg->dn);
1209 if (ret != LDB_SUCCESS) {
1213 ndr_err = ndr_push_struct_blob(md_value, msg, &omd,
1214 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
1215 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1216 DEBUG(0,(__location__ ": Failed to marshall replPropertyMetaData for %s\n",
1217 ldb_dn_get_linearized(msg->dn)));
1218 return LDB_ERR_OPERATIONS_ERROR;
1221 ret = ldb_msg_add_empty(msg, "replPropertyMetaData", LDB_FLAG_MOD_REPLACE, &el);
1222 if (ret != LDB_SUCCESS) {
1223 DEBUG(0,(__location__ ": Failed to add updated replPropertyMetaData %s\n",
1224 ldb_dn_get_linearized(msg->dn)));
1229 el->values = md_value;
1236 struct dsdb_dn *dsdb_dn;
1241 static int parsed_dn_compare(struct parsed_dn *pdn1, struct parsed_dn *pdn2)
1243 return GUID_compare(pdn1->guid, pdn2->guid);
1246 static struct parsed_dn *parsed_dn_find(struct parsed_dn *pdn, int count, struct GUID *guid, struct ldb_dn *dn)
1248 struct parsed_dn *ret;
1249 if (dn && GUID_all_zero(guid)) {
1250 /* when updating a link using DRS, we sometimes get a
1251 NULL GUID. We then need to try and match by DN */
1253 for (i=0; i<count; i++) {
1254 if (ldb_dn_compare(pdn[i].dsdb_dn->dn, dn) == 0) {
1255 dsdb_get_extended_dn_guid(pdn[i].dsdb_dn->dn, guid, "GUID");
1261 BINARY_ARRAY_SEARCH(pdn, count, guid, guid, GUID_compare, ret);
1266 get a series of message element values as an array of DNs and GUIDs
1267 the result is sorted by GUID
1269 static int get_parsed_dns(struct ldb_module *module, TALLOC_CTX *mem_ctx,
1270 struct ldb_message_element *el, struct parsed_dn **pdn,
1271 const char *ldap_oid)
1274 struct ldb_context *ldb = ldb_module_get_ctx(module);
1281 (*pdn) = talloc_array(mem_ctx, struct parsed_dn, el->num_values);
1283 ldb_module_oom(module);
1284 return LDB_ERR_OPERATIONS_ERROR;
1287 for (i=0; i<el->num_values; i++) {
1288 struct ldb_val *v = &el->values[i];
1291 struct parsed_dn *p;
1295 p->dsdb_dn = dsdb_dn_parse(*pdn, ldb, v, ldap_oid);
1296 if (p->dsdb_dn == NULL) {
1297 return LDB_ERR_INVALID_DN_SYNTAX;
1300 dn = p->dsdb_dn->dn;
1302 p->guid = talloc(*pdn, struct GUID);
1303 if (p->guid == NULL) {
1304 ldb_module_oom(module);
1305 return LDB_ERR_OPERATIONS_ERROR;
1308 status = dsdb_get_extended_dn_guid(dn, p->guid, "GUID");
1309 if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
1310 /* we got a DN without a GUID - go find the GUID */
1311 int ret = dsdb_module_guid_by_dn(module, dn, p->guid);
1312 if (ret != LDB_SUCCESS) {
1313 ldb_asprintf_errstring(ldb, "Unable to find GUID for DN %s\n",
1314 ldb_dn_get_linearized(dn));
1317 ret = dsdb_set_extended_dn_guid(dn, p->guid, "GUID");
1318 if (ret != LDB_SUCCESS) {
1321 } else if (!NT_STATUS_IS_OK(status)) {
1322 return LDB_ERR_OPERATIONS_ERROR;
1325 /* keep a pointer to the original ldb_val */
1329 TYPESAFE_QSORT(*pdn, el->num_values, parsed_dn_compare);
1335 build a new extended DN, including all meta data fields
1337 RMD_FLAGS = DSDB_RMD_FLAG_* bits
1338 RMD_ADDTIME = originating_add_time
1339 RMD_INVOCID = originating_invocation_id
1340 RMD_CHANGETIME = originating_change_time
1341 RMD_ORIGINATING_USN = originating_usn
1342 RMD_LOCAL_USN = local_usn
1343 RMD_VERSION = version
1345 static int replmd_build_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
1346 const struct GUID *invocation_id, uint64_t seq_num,
1347 uint64_t local_usn, NTTIME nttime, uint32_t version, bool deleted)
1349 struct ldb_dn *dn = dsdb_dn->dn;
1350 const char *tstring, *usn_string, *flags_string;
1351 struct ldb_val tval;
1353 struct ldb_val usnv, local_usnv;
1354 struct ldb_val vers, flagsv;
1357 const char *dnstring;
1359 uint32_t rmd_flags = deleted?DSDB_RMD_FLAG_DELETED:0;
1361 tstring = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)nttime);
1363 return LDB_ERR_OPERATIONS_ERROR;
1365 tval = data_blob_string_const(tstring);
1367 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)seq_num);
1369 return LDB_ERR_OPERATIONS_ERROR;
1371 usnv = data_blob_string_const(usn_string);
1373 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)local_usn);
1375 return LDB_ERR_OPERATIONS_ERROR;
1377 local_usnv = data_blob_string_const(usn_string);
1379 vstring = talloc_asprintf(mem_ctx, "%lu", (unsigned long)version);
1381 return LDB_ERR_OPERATIONS_ERROR;
1383 vers = data_blob_string_const(vstring);
1385 status = GUID_to_ndr_blob(invocation_id, dn, &iid);
1386 if (!NT_STATUS_IS_OK(status)) {
1387 return LDB_ERR_OPERATIONS_ERROR;
1390 flags_string = talloc_asprintf(mem_ctx, "%u", rmd_flags);
1391 if (!flags_string) {
1392 return LDB_ERR_OPERATIONS_ERROR;
1394 flagsv = data_blob_string_const(flags_string);
1396 ret = ldb_dn_set_extended_component(dn, "RMD_FLAGS", &flagsv);
1397 if (ret != LDB_SUCCESS) return ret;
1398 ret = ldb_dn_set_extended_component(dn, "RMD_ADDTIME", &tval);
1399 if (ret != LDB_SUCCESS) return ret;
1400 ret = ldb_dn_set_extended_component(dn, "RMD_INVOCID", &iid);
1401 if (ret != LDB_SUCCESS) return ret;
1402 ret = ldb_dn_set_extended_component(dn, "RMD_CHANGETIME", &tval);
1403 if (ret != LDB_SUCCESS) return ret;
1404 ret = ldb_dn_set_extended_component(dn, "RMD_LOCAL_USN", &local_usnv);
1405 if (ret != LDB_SUCCESS) return ret;
1406 ret = ldb_dn_set_extended_component(dn, "RMD_ORIGINATING_USN", &usnv);
1407 if (ret != LDB_SUCCESS) return ret;
1408 ret = ldb_dn_set_extended_component(dn, "RMD_VERSION", &vers);
1409 if (ret != LDB_SUCCESS) return ret;
1411 dnstring = dsdb_dn_get_extended_linearized(mem_ctx, dsdb_dn, 1);
1412 if (dnstring == NULL) {
1413 return LDB_ERR_OPERATIONS_ERROR;
1415 *v = data_blob_string_const(dnstring);
1420 static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
1421 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
1422 uint64_t seq_num, uint64_t local_usn, NTTIME nttime,
1423 uint32_t version, bool deleted);
1426 check if any links need upgrading from w2k format
1428 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.
1430 static int replmd_check_upgrade_links(struct parsed_dn *dns, uint32_t count, struct ldb_message_element *parent_ctx, const struct GUID *invocation_id)
1433 for (i=0; i<count; i++) {
1438 status = dsdb_get_extended_dn_uint32(dns[i].dsdb_dn->dn, &version, "RMD_VERSION");
1439 if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
1443 /* it's an old one that needs upgrading */
1444 ret = replmd_update_la_val(parent_ctx->values, dns[i].v, dns[i].dsdb_dn, dns[i].dsdb_dn, invocation_id,
1446 if (ret != LDB_SUCCESS) {
1454 update an extended DN, including all meta data fields
1456 see replmd_build_la_val for value names
1458 static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
1459 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
1460 uint64_t seq_num, uint64_t local_usn, NTTIME nttime,
1461 uint32_t version, bool deleted)
1463 struct ldb_dn *dn = dsdb_dn->dn;
1464 const char *tstring, *usn_string, *flags_string;
1465 struct ldb_val tval;
1467 struct ldb_val usnv, local_usnv;
1468 struct ldb_val vers, flagsv;
1469 const struct ldb_val *old_addtime;
1470 uint32_t old_version;
1473 const char *dnstring;
1475 uint32_t rmd_flags = deleted?DSDB_RMD_FLAG_DELETED:0;
1477 tstring = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)nttime);
1479 return LDB_ERR_OPERATIONS_ERROR;
1481 tval = data_blob_string_const(tstring);
1483 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)seq_num);
1485 return LDB_ERR_OPERATIONS_ERROR;
1487 usnv = data_blob_string_const(usn_string);
1489 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)local_usn);
1491 return LDB_ERR_OPERATIONS_ERROR;
1493 local_usnv = data_blob_string_const(usn_string);
1495 status = GUID_to_ndr_blob(invocation_id, dn, &iid);
1496 if (!NT_STATUS_IS_OK(status)) {
1497 return LDB_ERR_OPERATIONS_ERROR;
1500 flags_string = talloc_asprintf(mem_ctx, "%u", rmd_flags);
1501 if (!flags_string) {
1502 return LDB_ERR_OPERATIONS_ERROR;
1504 flagsv = data_blob_string_const(flags_string);
1506 ret = ldb_dn_set_extended_component(dn, "RMD_FLAGS", &flagsv);
1507 if (ret != LDB_SUCCESS) return ret;
1509 /* get the ADDTIME from the original */
1510 old_addtime = ldb_dn_get_extended_component(old_dsdb_dn->dn, "RMD_ADDTIME");
1511 if (old_addtime == NULL) {
1512 old_addtime = &tval;
1514 if (dsdb_dn != old_dsdb_dn) {
1515 ret = ldb_dn_set_extended_component(dn, "RMD_ADDTIME", old_addtime);
1516 if (ret != LDB_SUCCESS) return ret;
1519 /* use our invocation id */
1520 ret = ldb_dn_set_extended_component(dn, "RMD_INVOCID", &iid);
1521 if (ret != LDB_SUCCESS) return ret;
1523 /* changetime is the current time */
1524 ret = ldb_dn_set_extended_component(dn, "RMD_CHANGETIME", &tval);
1525 if (ret != LDB_SUCCESS) return ret;
1527 /* update the USN */
1528 ret = ldb_dn_set_extended_component(dn, "RMD_ORIGINATING_USN", &usnv);
1529 if (ret != LDB_SUCCESS) return ret;
1531 ret = ldb_dn_set_extended_component(dn, "RMD_LOCAL_USN", &local_usnv);
1532 if (ret != LDB_SUCCESS) return ret;
1534 /* increase the version by 1 */
1535 status = dsdb_get_extended_dn_uint32(old_dsdb_dn->dn, &old_version, "RMD_VERSION");
1536 if (NT_STATUS_IS_OK(status) && old_version >= version) {
1537 version = old_version+1;
1539 vstring = talloc_asprintf(dn, "%lu", (unsigned long)version);
1540 vers = data_blob_string_const(vstring);
1541 ret = ldb_dn_set_extended_component(dn, "RMD_VERSION", &vers);
1542 if (ret != LDB_SUCCESS) return ret;
1544 dnstring = dsdb_dn_get_extended_linearized(mem_ctx, dsdb_dn, 1);
1545 if (dnstring == NULL) {
1546 return LDB_ERR_OPERATIONS_ERROR;
1548 *v = data_blob_string_const(dnstring);
1554 handle adding a linked attribute
1556 static int replmd_modify_la_add(struct ldb_module *module,
1557 const struct dsdb_schema *schema,
1558 struct ldb_message *msg,
1559 struct ldb_message_element *el,
1560 struct ldb_message_element *old_el,
1561 const struct dsdb_attribute *schema_attr,
1564 struct GUID *msg_guid)
1567 struct parsed_dn *dns, *old_dns;
1568 TALLOC_CTX *tmp_ctx = talloc_new(msg);
1570 struct ldb_val *new_values = NULL;
1571 unsigned int num_new_values = 0;
1572 unsigned old_num_values = old_el?old_el->num_values:0;
1573 const struct GUID *invocation_id;
1574 struct ldb_context *ldb = ldb_module_get_ctx(module);
1577 unix_to_nt_time(&now, t);
1579 ret = get_parsed_dns(module, tmp_ctx, el, &dns, schema_attr->syntax->ldap_oid);
1580 if (ret != LDB_SUCCESS) {
1581 talloc_free(tmp_ctx);
1585 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns, schema_attr->syntax->ldap_oid);
1586 if (ret != LDB_SUCCESS) {
1587 talloc_free(tmp_ctx);
1591 invocation_id = samdb_ntds_invocation_id(ldb);
1592 if (!invocation_id) {
1593 talloc_free(tmp_ctx);
1594 return LDB_ERR_OPERATIONS_ERROR;
1597 ret = replmd_check_upgrade_links(old_dns, old_num_values, old_el, invocation_id);
1598 if (ret != LDB_SUCCESS) {
1599 talloc_free(tmp_ctx);
1603 /* for each new value, see if it exists already with the same GUID */
1604 for (i=0; i<el->num_values; i++) {
1605 struct parsed_dn *p = parsed_dn_find(old_dns, old_num_values, dns[i].guid, NULL);
1607 /* this is a new linked attribute value */
1608 new_values = talloc_realloc(tmp_ctx, new_values, struct ldb_val, num_new_values+1);
1609 if (new_values == NULL) {
1610 ldb_module_oom(module);
1611 talloc_free(tmp_ctx);
1612 return LDB_ERR_OPERATIONS_ERROR;
1614 ret = replmd_build_la_val(new_values, &new_values[num_new_values], dns[i].dsdb_dn,
1615 invocation_id, seq_num, seq_num, now, 0, false);
1616 if (ret != LDB_SUCCESS) {
1617 talloc_free(tmp_ctx);
1622 /* this is only allowed if the GUID was
1623 previously deleted. */
1624 uint32_t rmd_flags = dsdb_dn_rmd_flags(p->dsdb_dn->dn);
1626 if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
1627 ldb_asprintf_errstring(ldb, "Attribute %s already exists for target GUID %s",
1628 el->name, GUID_string(tmp_ctx, p->guid));
1629 talloc_free(tmp_ctx);
1630 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
1632 ret = replmd_update_la_val(old_el->values, p->v, dns[i].dsdb_dn, p->dsdb_dn,
1633 invocation_id, seq_num, seq_num, now, 0, false);
1634 if (ret != LDB_SUCCESS) {
1635 talloc_free(tmp_ctx);
1640 ret = replmd_add_backlink(module, schema, msg_guid, dns[i].guid, true, schema_attr, true);
1641 if (ret != LDB_SUCCESS) {
1642 talloc_free(tmp_ctx);
1647 /* add the new ones on to the end of the old values, constructing a new el->values */
1648 el->values = talloc_realloc(msg->elements, old_el?old_el->values:NULL,
1650 old_num_values+num_new_values);
1651 if (el->values == NULL) {
1652 ldb_module_oom(module);
1653 return LDB_ERR_OPERATIONS_ERROR;
1656 memcpy(&el->values[old_num_values], new_values, num_new_values*sizeof(struct ldb_val));
1657 el->num_values = old_num_values + num_new_values;
1659 talloc_steal(msg->elements, el->values);
1660 talloc_steal(el->values, new_values);
1662 talloc_free(tmp_ctx);
1664 /* we now tell the backend to replace all existing values
1665 with the one we have constructed */
1666 el->flags = LDB_FLAG_MOD_REPLACE;
1673 handle deleting all active linked attributes
1675 static int replmd_modify_la_delete(struct ldb_module *module,
1676 const struct dsdb_schema *schema,
1677 struct ldb_message *msg,
1678 struct ldb_message_element *el,
1679 struct ldb_message_element *old_el,
1680 const struct dsdb_attribute *schema_attr,
1683 struct GUID *msg_guid)
1686 struct parsed_dn *dns, *old_dns;
1687 TALLOC_CTX *tmp_ctx = talloc_new(msg);
1689 const struct GUID *invocation_id;
1690 struct ldb_context *ldb = ldb_module_get_ctx(module);
1693 unix_to_nt_time(&now, t);
1695 /* check if there is nothing to delete */
1696 if ((!old_el || old_el->num_values == 0) &&
1697 el->num_values == 0) {
1701 if (!old_el || old_el->num_values == 0) {
1702 return LDB_ERR_NO_SUCH_ATTRIBUTE;
1705 ret = get_parsed_dns(module, tmp_ctx, el, &dns, schema_attr->syntax->ldap_oid);
1706 if (ret != LDB_SUCCESS) {
1707 talloc_free(tmp_ctx);
1711 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns, schema_attr->syntax->ldap_oid);
1712 if (ret != LDB_SUCCESS) {
1713 talloc_free(tmp_ctx);
1717 invocation_id = samdb_ntds_invocation_id(ldb);
1718 if (!invocation_id) {
1719 return LDB_ERR_OPERATIONS_ERROR;
1722 ret = replmd_check_upgrade_links(old_dns, old_el->num_values, old_el, invocation_id);
1723 if (ret != LDB_SUCCESS) {
1724 talloc_free(tmp_ctx);
1730 /* see if we are being asked to delete any links that
1731 don't exist or are already deleted */
1732 for (i=0; i<el->num_values; i++) {
1733 struct parsed_dn *p = &dns[i];
1734 struct parsed_dn *p2;
1737 p2 = parsed_dn_find(old_dns, old_el->num_values, p->guid, NULL);
1739 ldb_asprintf_errstring(ldb, "Attribute %s doesn't exist for target GUID %s",
1740 el->name, GUID_string(tmp_ctx, p->guid));
1741 return LDB_ERR_NO_SUCH_ATTRIBUTE;
1743 rmd_flags = dsdb_dn_rmd_flags(p2->dsdb_dn->dn);
1744 if (rmd_flags & DSDB_RMD_FLAG_DELETED) {
1745 ldb_asprintf_errstring(ldb, "Attribute %s already deleted for target GUID %s",
1746 el->name, GUID_string(tmp_ctx, p->guid));
1747 return LDB_ERR_NO_SUCH_ATTRIBUTE;
1751 /* for each new value, see if it exists already with the same GUID
1752 if it is not already deleted and matches the delete list then delete it
1754 for (i=0; i<old_el->num_values; i++) {
1755 struct parsed_dn *p = &old_dns[i];
1758 if (el->num_values && parsed_dn_find(dns, el->num_values, p->guid, NULL) == NULL) {
1762 rmd_flags = dsdb_dn_rmd_flags(p->dsdb_dn->dn);
1763 if (rmd_flags & DSDB_RMD_FLAG_DELETED) continue;
1765 ret = replmd_update_la_val(old_el->values, p->v, p->dsdb_dn, p->dsdb_dn,
1766 invocation_id, seq_num, seq_num, now, 0, true);
1767 if (ret != LDB_SUCCESS) {
1768 talloc_free(tmp_ctx);
1772 ret = replmd_add_backlink(module, schema, msg_guid, old_dns[i].guid, false, schema_attr, true);
1773 if (ret != LDB_SUCCESS) {
1774 talloc_free(tmp_ctx);
1779 el->values = talloc_steal(msg->elements, old_el->values);
1780 el->num_values = old_el->num_values;
1782 talloc_free(tmp_ctx);
1784 /* we now tell the backend to replace all existing values
1785 with the one we have constructed */
1786 el->flags = LDB_FLAG_MOD_REPLACE;
1792 handle replacing a linked attribute
1794 static int replmd_modify_la_replace(struct ldb_module *module,
1795 const struct dsdb_schema *schema,
1796 struct ldb_message *msg,
1797 struct ldb_message_element *el,
1798 struct ldb_message_element *old_el,
1799 const struct dsdb_attribute *schema_attr,
1802 struct GUID *msg_guid)
1805 struct parsed_dn *dns, *old_dns;
1806 TALLOC_CTX *tmp_ctx = talloc_new(msg);
1808 const struct GUID *invocation_id;
1809 struct ldb_context *ldb = ldb_module_get_ctx(module);
1810 struct ldb_val *new_values = NULL;
1811 unsigned int num_new_values = 0;
1812 unsigned int old_num_values = old_el?old_el->num_values:0;
1815 unix_to_nt_time(&now, t);
1817 /* check if there is nothing to replace */
1818 if ((!old_el || old_el->num_values == 0) &&
1819 el->num_values == 0) {
1823 ret = get_parsed_dns(module, tmp_ctx, el, &dns, schema_attr->syntax->ldap_oid);
1824 if (ret != LDB_SUCCESS) {
1825 talloc_free(tmp_ctx);
1829 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns, schema_attr->syntax->ldap_oid);
1830 if (ret != LDB_SUCCESS) {
1831 talloc_free(tmp_ctx);
1835 invocation_id = samdb_ntds_invocation_id(ldb);
1836 if (!invocation_id) {
1837 return LDB_ERR_OPERATIONS_ERROR;
1840 ret = replmd_check_upgrade_links(old_dns, old_num_values, old_el, invocation_id);
1841 if (ret != LDB_SUCCESS) {
1842 talloc_free(tmp_ctx);
1846 /* mark all the old ones as deleted */
1847 for (i=0; i<old_num_values; i++) {
1848 struct parsed_dn *old_p = &old_dns[i];
1849 struct parsed_dn *p;
1850 uint32_t rmd_flags = dsdb_dn_rmd_flags(old_p->dsdb_dn->dn);
1852 if (rmd_flags & DSDB_RMD_FLAG_DELETED) continue;
1854 ret = replmd_add_backlink(module, schema, msg_guid, old_dns[i].guid, false, schema_attr, false);
1855 if (ret != LDB_SUCCESS) {
1856 talloc_free(tmp_ctx);
1860 p = parsed_dn_find(dns, el->num_values, old_p->guid, NULL);
1862 /* we don't delete it if we are re-adding it */
1866 ret = replmd_update_la_val(old_el->values, old_p->v, old_p->dsdb_dn, old_p->dsdb_dn,
1867 invocation_id, seq_num, seq_num, now, 0, true);
1868 if (ret != LDB_SUCCESS) {
1869 talloc_free(tmp_ctx);
1874 /* for each new value, either update its meta-data, or add it
1877 for (i=0; i<el->num_values; i++) {
1878 struct parsed_dn *p = &dns[i], *old_p;
1881 (old_p = parsed_dn_find(old_dns,
1882 old_num_values, p->guid, NULL)) != NULL) {
1883 /* update in place */
1884 ret = replmd_update_la_val(old_el->values, old_p->v, old_p->dsdb_dn,
1885 old_p->dsdb_dn, invocation_id,
1886 seq_num, seq_num, now, 0, false);
1887 if (ret != LDB_SUCCESS) {
1888 talloc_free(tmp_ctx);
1893 new_values = talloc_realloc(tmp_ctx, new_values, struct ldb_val,
1895 if (new_values == NULL) {
1896 ldb_module_oom(module);
1897 talloc_free(tmp_ctx);
1898 return LDB_ERR_OPERATIONS_ERROR;
1900 ret = replmd_build_la_val(new_values, &new_values[num_new_values], dns[i].dsdb_dn,
1901 invocation_id, seq_num, seq_num, now, 0, false);
1902 if (ret != LDB_SUCCESS) {
1903 talloc_free(tmp_ctx);
1909 ret = replmd_add_backlink(module, schema, msg_guid, dns[i].guid, true, schema_attr, false);
1910 if (ret != LDB_SUCCESS) {
1911 talloc_free(tmp_ctx);
1916 /* add the new values to the end of old_el */
1917 if (num_new_values != 0) {
1918 el->values = talloc_realloc(msg->elements, old_el?old_el->values:NULL,
1919 struct ldb_val, old_num_values+num_new_values);
1920 if (el->values == NULL) {
1921 ldb_module_oom(module);
1922 return LDB_ERR_OPERATIONS_ERROR;
1924 memcpy(&el->values[old_num_values], &new_values[0],
1925 sizeof(struct ldb_val)*num_new_values);
1926 el->num_values = old_num_values + num_new_values;
1927 talloc_steal(msg->elements, new_values);
1929 el->values = old_el->values;
1930 el->num_values = old_el->num_values;
1931 talloc_steal(msg->elements, el->values);
1934 talloc_free(tmp_ctx);
1936 /* we now tell the backend to replace all existing values
1937 with the one we have constructed */
1938 el->flags = LDB_FLAG_MOD_REPLACE;
1945 handle linked attributes in modify requests
1947 static int replmd_modify_handle_linked_attribs(struct ldb_module *module,
1948 struct ldb_message *msg,
1949 uint64_t seq_num, time_t t)
1951 struct ldb_result *res;
1954 struct ldb_context *ldb = ldb_module_get_ctx(module);
1955 struct ldb_message *old_msg;
1957 const struct dsdb_schema *schema;
1958 struct GUID old_guid;
1961 /* there the replmd_update_rpmd code has already
1962 * checked and saw that there are no linked
1967 if (dsdb_functional_level(ldb) == DS_DOMAIN_FUNCTION_2000) {
1968 /* don't do anything special for linked attributes */
1972 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, NULL,
1973 DSDB_SEARCH_SHOW_DELETED |
1974 DSDB_SEARCH_REVEAL_INTERNALS |
1975 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT);
1976 if (ret != LDB_SUCCESS) {
1979 schema = dsdb_get_schema(ldb, res);
1981 return LDB_ERR_OPERATIONS_ERROR;
1984 old_msg = res->msgs[0];
1986 old_guid = samdb_result_guid(old_msg, "objectGUID");
1988 for (i=0; i<msg->num_elements; i++) {
1989 struct ldb_message_element *el = &msg->elements[i];
1990 struct ldb_message_element *old_el, *new_el;
1991 const struct dsdb_attribute *schema_attr
1992 = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
1994 ldb_asprintf_errstring(ldb,
1995 "attribute %s is not a valid attribute in schema", el->name);
1996 return LDB_ERR_OBJECT_CLASS_VIOLATION;
1998 if (schema_attr->linkID == 0) {
2001 if ((schema_attr->linkID & 1) == 1) {
2002 /* Odd is for the target. Illegal to modify */
2003 ldb_asprintf_errstring(ldb,
2004 "attribute %s must not be modified directly, it is a linked attribute", el->name);
2005 return LDB_ERR_UNWILLING_TO_PERFORM;
2007 old_el = ldb_msg_find_element(old_msg, el->name);
2008 switch (el->flags & LDB_FLAG_MOD_MASK) {
2009 case LDB_FLAG_MOD_REPLACE:
2010 ret = replmd_modify_la_replace(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid);
2012 case LDB_FLAG_MOD_DELETE:
2013 ret = replmd_modify_la_delete(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid);
2015 case LDB_FLAG_MOD_ADD:
2016 ret = replmd_modify_la_add(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid);
2019 ldb_asprintf_errstring(ldb,
2020 "invalid flags 0x%x for %s linked attribute",
2021 el->flags, el->name);
2022 return LDB_ERR_UNWILLING_TO_PERFORM;
2024 if (ret != LDB_SUCCESS) {
2028 ldb_msg_remove_attr(old_msg, el->name);
2030 ldb_msg_add_empty(old_msg, el->name, 0, &new_el);
2031 new_el->num_values = el->num_values;
2032 new_el->values = talloc_steal(msg->elements, el->values);
2034 /* TODO: this relises a bit too heavily on the exact
2035 behaviour of ldb_msg_find_element and
2036 ldb_msg_remove_element */
2037 old_el = ldb_msg_find_element(msg, el->name);
2039 ldb_msg_remove_element(msg, old_el);
2050 static int replmd_modify(struct ldb_module *module, struct ldb_request *req)
2052 struct ldb_context *ldb;
2053 struct replmd_replicated_request *ac;
2054 struct ldb_request *down_req;
2055 struct ldb_message *msg;
2056 time_t t = time(NULL);
2058 bool is_urgent = false;
2059 struct loadparm_context *lp_ctx;
2061 unsigned int functional_level;
2063 /* do not manipulate our control entries */
2064 if (ldb_dn_is_special(req->op.mod.message->dn)) {
2065 return ldb_next_request(module, req);
2068 ldb = ldb_module_get_ctx(module);
2069 functional_level = dsdb_functional_level(ldb);
2071 lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
2072 struct loadparm_context);
2074 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_modify\n");
2076 ac = replmd_ctx_init(module, req);
2078 return LDB_ERR_OPERATIONS_ERROR;
2081 /* we have to copy the message as the caller might have it as a const */
2082 msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
2086 return LDB_ERR_OPERATIONS_ERROR;
2089 ldb_msg_remove_attr(msg, "whenChanged");
2090 ldb_msg_remove_attr(msg, "uSNChanged");
2092 ret = replmd_update_rpmd(module, ac->schema, req, msg, &ac->seq_num, t, &is_urgent);
2093 if (ret == LDB_ERR_REFERRAL) {
2096 referral = talloc_asprintf(req,
2098 lp_dnsdomain(lp_ctx),
2099 ldb_dn_get_linearized(msg->dn));
2100 ret = ldb_module_send_referral(req, referral);
2101 return ldb_module_done(req, NULL, NULL, ret);
2104 if (ret != LDB_SUCCESS) {
2109 ret = replmd_modify_handle_linked_attribs(module, msg, ac->seq_num, t);
2110 if (ret != LDB_SUCCESS) {
2116 * - replace the old object with the newly constructed one
2119 ac->is_urgent = is_urgent;
2121 ret = ldb_build_mod_req(&down_req, ldb, ac,
2124 ac, replmd_op_callback,
2126 if (ret != LDB_SUCCESS) {
2131 /* If we are in functional level 2000, then
2132 * replmd_modify_handle_linked_attribs will have done
2134 if (functional_level == DS_DOMAIN_FUNCTION_2000) {
2135 ret = ldb_request_add_control(down_req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
2136 if (ret != LDB_SUCCESS) {
2142 talloc_steal(down_req, msg);
2144 /* we only change whenChanged and uSNChanged if the seq_num
2146 if (ac->seq_num != 0) {
2147 if (add_time_element(msg, "whenChanged", t) != LDB_SUCCESS) {
2152 if (add_uint64_element(msg, "uSNChanged", ac->seq_num) != LDB_SUCCESS) {
2158 /* go on with the call chain */
2159 return ldb_next_request(module, down_req);
2162 static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares);
2165 handle a rename request
2167 On a rename we need to do an extra ldb_modify which sets the
2168 whenChanged and uSNChanged attributes. We do this in a callback after the success.
2170 static int replmd_rename(struct ldb_module *module, struct ldb_request *req)
2172 struct ldb_context *ldb;
2173 struct replmd_replicated_request *ac;
2175 struct ldb_request *down_req;
2177 /* do not manipulate our control entries */
2178 if (ldb_dn_is_special(req->op.mod.message->dn)) {
2179 return ldb_next_request(module, req);
2182 ldb = ldb_module_get_ctx(module);
2184 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_rename\n");
2186 ac = replmd_ctx_init(module, req);
2188 return LDB_ERR_OPERATIONS_ERROR;
2190 ret = ldb_build_rename_req(&down_req, ldb, ac,
2191 ac->req->op.rename.olddn,
2192 ac->req->op.rename.newdn,
2194 ac, replmd_rename_callback,
2197 if (ret != LDB_SUCCESS) {
2202 /* go on with the call chain */
2203 return ldb_next_request(module, down_req);
2206 /* After the rename is compleated, update the whenchanged etc */
2207 static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares)
2209 struct ldb_context *ldb;
2210 struct replmd_replicated_request *ac;
2211 struct ldb_request *down_req;
2212 struct ldb_message *msg;
2213 time_t t = time(NULL);
2216 ac = talloc_get_type(req->context, struct replmd_replicated_request);
2217 ldb = ldb_module_get_ctx(ac->module);
2219 if (ares->error != LDB_SUCCESS) {
2220 return ldb_module_done(ac->req, ares->controls,
2221 ares->response, ares->error);
2224 if (ares->type != LDB_REPLY_DONE) {
2225 ldb_set_errstring(ldb,
2226 "invalid ldb_reply_type in callback");
2228 return ldb_module_done(ac->req, NULL, NULL,
2229 LDB_ERR_OPERATIONS_ERROR);
2232 /* Get a sequence number from the backend */
2233 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ac->seq_num);
2234 if (ret != LDB_SUCCESS) {
2239 * - replace the old object with the newly constructed one
2242 msg = ldb_msg_new(ac);
2245 return LDB_ERR_OPERATIONS_ERROR;
2248 msg->dn = ac->req->op.rename.newdn;
2250 ret = ldb_build_mod_req(&down_req, ldb, ac,
2253 ac, replmd_op_callback,
2256 if (ret != LDB_SUCCESS) {
2260 talloc_steal(down_req, msg);
2262 if (add_time_element(msg, "whenChanged", t) != LDB_SUCCESS) {
2267 if (add_uint64_element(msg, "uSNChanged", ac->seq_num) != LDB_SUCCESS) {
2272 /* go on with the call chain - do the modify after the rename */
2273 return ldb_next_request(ac->module, down_req);
2277 remove links from objects that point at this object when an object
2280 static int replmd_delete_remove_link(struct ldb_module *module,
2281 const struct dsdb_schema *schema,
2283 struct ldb_message_element *el,
2284 const struct dsdb_attribute *sa)
2287 TALLOC_CTX *tmp_ctx = talloc_new(module);
2288 struct ldb_context *ldb = ldb_module_get_ctx(module);
2290 for (i=0; i<el->num_values; i++) {
2291 struct dsdb_dn *dsdb_dn;
2295 struct ldb_message *msg;
2296 const struct dsdb_attribute *target_attr;
2297 struct ldb_message_element *el2;
2298 struct ldb_val dn_val;
2300 if (dsdb_dn_is_deleted_val(&el->values[i])) {
2304 dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i], sa->syntax->ldap_oid);
2306 talloc_free(tmp_ctx);
2307 return LDB_ERR_OPERATIONS_ERROR;
2310 status = dsdb_get_extended_dn_guid(dsdb_dn->dn, &guid2, "GUID");
2311 if (!NT_STATUS_IS_OK(status)) {
2312 talloc_free(tmp_ctx);
2313 return LDB_ERR_OPERATIONS_ERROR;
2316 /* remove the link */
2317 msg = ldb_msg_new(tmp_ctx);
2319 ldb_module_oom(module);
2320 talloc_free(tmp_ctx);
2321 return LDB_ERR_OPERATIONS_ERROR;
2325 msg->dn = dsdb_dn->dn;
2327 target_attr = dsdb_attribute_by_linkID(schema, sa->linkID ^ 1);
2328 if (target_attr == NULL) {
2332 ret = ldb_msg_add_empty(msg, target_attr->lDAPDisplayName, LDB_FLAG_MOD_DELETE, &el2);
2333 if (ret != LDB_SUCCESS) {
2334 ldb_module_oom(module);
2335 talloc_free(tmp_ctx);
2336 return LDB_ERR_OPERATIONS_ERROR;
2338 dn_val = data_blob_string_const(ldb_dn_get_linearized(dn));
2339 el2->values = &dn_val;
2340 el2->num_values = 1;
2342 ret = dsdb_module_modify(module, msg, DSDB_FLAG_OWN_MODULE);
2343 if (ret != LDB_SUCCESS) {
2344 talloc_free(tmp_ctx);
2348 talloc_free(tmp_ctx);
2354 handle update of replication meta data for deletion of objects
2356 This also handles the mapping of delete to a rename operation
2357 to allow deletes to be replicated.
2359 static int replmd_delete(struct ldb_module *module, struct ldb_request *req)
2361 int ret = LDB_ERR_OTHER;
2363 struct ldb_dn *old_dn, *new_dn;
2364 const char *rdn_name;
2365 const struct ldb_val *rdn_value, *new_rdn_value;
2367 struct ldb_context *ldb = ldb_module_get_ctx(module);
2368 const struct dsdb_schema *schema;
2369 struct ldb_message *msg, *old_msg;
2370 struct ldb_message_element *el;
2371 TALLOC_CTX *tmp_ctx;
2372 struct ldb_result *res, *parent_res;
2373 const char *preserved_attrs[] = {
2374 /* yes, this really is a hard coded list. See MS-ADTS
2375 section 3.1.1.5.5.1.1 */
2376 "nTSecurityDescriptor", "attributeID", "attributeSyntax", "dNReferenceUpdate", "dNSHostName",
2377 "flatName", "governsID", "groupType", "instanceType", "lDAPDisplayName", "legacyExchangeDN",
2378 "isDeleted", "isRecycled", "lastKnownParent", "msDS-LastKnownRDN", "mS-DS-CreatorSID",
2379 "mSMQOwnerID", "nCName", "objectClass", "distinguishedName", "objectGUID", "objectSid",
2380 "oMSyntax", "proxiedObjectName", "name", "replPropertyMetaData", "sAMAccountName",
2381 "securityIdentifier", "sIDHistory", "subClassOf", "systemFlags", "trustPartner", "trustDirection",
2382 "trustType", "trustAttributes", "userAccountControl", "uSNChanged", "uSNCreated", "whenCreated",
2383 "whenChanged", NULL};
2384 unsigned int i, el_count = 0;
2385 enum deletion_state { OBJECT_NOT_DELETED=1, OBJECT_DELETED=2, OBJECT_RECYCLED=3,
2386 OBJECT_TOMBSTONE=4, OBJECT_REMOVED=5 };
2387 enum deletion_state deletion_state, next_deletion_state;
2390 if (ldb_dn_is_special(req->op.del.dn)) {
2391 return ldb_next_request(module, req);
2394 tmp_ctx = talloc_new(ldb);
2397 return LDB_ERR_OPERATIONS_ERROR;
2400 schema = dsdb_get_schema(ldb, tmp_ctx);
2402 return LDB_ERR_OPERATIONS_ERROR;
2405 old_dn = ldb_dn_copy(tmp_ctx, req->op.del.dn);
2407 /* we need the complete msg off disk, so we can work out which
2408 attributes need to be removed */
2409 ret = dsdb_module_search_dn(module, tmp_ctx, &res, old_dn, NULL,
2410 DSDB_SEARCH_SHOW_DELETED |
2411 DSDB_SEARCH_REVEAL_INTERNALS |
2412 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT);
2413 if (ret != LDB_SUCCESS) {
2414 talloc_free(tmp_ctx);
2417 old_msg = res->msgs[0];
2420 ret = dsdb_recyclebin_enabled(module, &enabled);
2421 if (ret != LDB_SUCCESS) {
2422 talloc_free(tmp_ctx);
2426 if (ldb_msg_check_string_attribute(old_msg, "isDeleted", "TRUE")) {
2428 deletion_state = OBJECT_TOMBSTONE;
2429 next_deletion_state = OBJECT_REMOVED;
2430 } else if (ldb_msg_check_string_attribute(old_msg, "isRecycled", "TRUE")) {
2431 deletion_state = OBJECT_RECYCLED;
2432 next_deletion_state = OBJECT_REMOVED;
2434 deletion_state = OBJECT_DELETED;
2435 next_deletion_state = OBJECT_RECYCLED;
2438 deletion_state = OBJECT_NOT_DELETED;
2440 next_deletion_state = OBJECT_DELETED;
2442 next_deletion_state = OBJECT_TOMBSTONE;
2446 if (next_deletion_state == OBJECT_REMOVED) {
2447 struct auth_session_info *session_info =
2448 (struct auth_session_info *)ldb_get_opaque(ldb, "sessionInfo");
2449 if (security_session_user_level(session_info, NULL) != SECURITY_SYSTEM) {
2450 ldb_asprintf_errstring(ldb, "Refusing to delete deleted object %s",
2451 ldb_dn_get_linearized(old_msg->dn));
2452 return LDB_ERR_UNWILLING_TO_PERFORM;
2455 /* it is already deleted - really remove it this time */
2456 talloc_free(tmp_ctx);
2457 return ldb_next_request(module, req);
2460 rdn_name = ldb_dn_get_rdn_name(old_dn);
2461 rdn_value = ldb_dn_get_rdn_val(old_dn);
2463 msg = ldb_msg_new(tmp_ctx);
2465 ldb_module_oom(module);
2466 talloc_free(tmp_ctx);
2467 return LDB_ERR_OPERATIONS_ERROR;
2472 if (deletion_state == OBJECT_NOT_DELETED){
2473 /* work out where we will be renaming this object to */
2474 ret = dsdb_get_deleted_objects_dn(ldb, tmp_ctx, old_dn, &new_dn);
2475 if (ret != LDB_SUCCESS) {
2476 /* this is probably an attempted delete on a partition
2477 * that doesn't allow delete operations, such as the
2478 * schema partition */
2479 ldb_asprintf_errstring(ldb, "No Deleted Objects container for DN %s",
2480 ldb_dn_get_linearized(old_dn));
2481 talloc_free(tmp_ctx);
2482 return LDB_ERR_UNWILLING_TO_PERFORM;
2485 /* get the objects GUID from the search we just did */
2486 guid = samdb_result_guid(old_msg, "objectGUID");
2488 /* Add a formatted child */
2489 retb = ldb_dn_add_child_fmt(new_dn, "%s=%s\\0ADEL:%s",
2492 GUID_string(tmp_ctx, &guid));
2494 DEBUG(0,(__location__ ": Unable to add a formatted child to dn: %s",
2495 ldb_dn_get_linearized(new_dn)));
2496 talloc_free(tmp_ctx);
2497 return LDB_ERR_OPERATIONS_ERROR;
2500 ret = ldb_msg_add_string(msg, "isDeleted", "TRUE");
2501 if (ret != LDB_SUCCESS) {
2502 DEBUG(0,(__location__ ": Failed to add isDeleted string to the msg\n"));
2503 ldb_module_oom(module);
2504 talloc_free(tmp_ctx);
2507 msg->elements[el_count++].flags = LDB_FLAG_MOD_ADD;
2511 now we need to modify the object in the following ways:
2513 - add isDeleted=TRUE
2514 - update rDN and name, with new rDN
2515 - remove linked attributes
2516 - remove objectCategory and sAMAccountType
2517 - remove attribs not on the preserved list
2518 - preserved if in above list, or is rDN
2519 - remove all linked attribs from this object
2520 - remove all links from other objects to this object
2521 - add lastKnownParent
2522 - update replPropertyMetaData?
2524 see MS-ADTS "Tombstone Requirements" section 3.1.1.5.5.1.1
2527 /* we need the storage form of the parent GUID */
2528 ret = dsdb_module_search_dn(module, tmp_ctx, &parent_res,
2529 ldb_dn_get_parent(tmp_ctx, old_dn), NULL,
2530 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
2531 DSDB_SEARCH_REVEAL_INTERNALS|
2532 DSDB_SEARCH_SHOW_DELETED);
2533 if (ret != LDB_SUCCESS) {
2534 talloc_free(tmp_ctx);
2538 if (deletion_state == OBJECT_NOT_DELETED){
2539 ret = ldb_msg_add_steal_string(msg, "lastKnownParent",
2540 ldb_dn_get_extended_linearized(tmp_ctx, parent_res->msgs[0]->dn, 1));
2541 if (ret != LDB_SUCCESS) {
2542 DEBUG(0,(__location__ ": Failed to add lastKnownParent string to the msg\n"));
2543 ldb_module_oom(module);
2544 talloc_free(tmp_ctx);
2547 msg->elements[el_count++].flags = LDB_FLAG_MOD_ADD;
2550 switch (next_deletion_state){
2552 case OBJECT_DELETED:
2554 ret = ldb_msg_add_value(msg, "msDS-LastKnownRDN", rdn_value, NULL);
2555 if (ret != LDB_SUCCESS) {
2556 DEBUG(0,(__location__ ": Failed to add msDS-LastKnownRDN string to the msg\n"));
2557 ldb_module_oom(module);
2558 talloc_free(tmp_ctx);
2561 msg->elements[el_count++].flags = LDB_FLAG_MOD_ADD;
2563 ret = ldb_msg_add_empty(msg, "objectCategory", LDB_FLAG_MOD_DELETE, NULL);
2564 if (ret != LDB_SUCCESS) {
2565 talloc_free(tmp_ctx);
2566 ldb_module_oom(module);
2570 ret = ldb_msg_add_empty(msg, "sAMAccountType", LDB_FLAG_MOD_DELETE, NULL);
2571 if (ret != LDB_SUCCESS) {
2572 talloc_free(tmp_ctx);
2573 ldb_module_oom(module);
2579 case OBJECT_RECYCLED:
2580 case OBJECT_TOMBSTONE:
2582 /* we also mark it as recycled, meaning this object can't be
2583 recovered (we are stripping its attributes) */
2584 if (dsdb_functional_level(ldb) >= DS_DOMAIN_FUNCTION_2008_R2) {
2585 ret = ldb_msg_add_string(msg, "isRecycled", "TRUE");
2586 if (ret != LDB_SUCCESS) {
2587 DEBUG(0,(__location__ ": Failed to add isRecycled string to the msg\n"));
2588 ldb_module_oom(module);
2589 talloc_free(tmp_ctx);
2592 msg->elements[el_count++].flags = LDB_FLAG_MOD_ADD;
2595 /* work out which of the old attributes we will be removing */
2596 for (i=0; i<old_msg->num_elements; i++) {
2597 const struct dsdb_attribute *sa;
2598 el = &old_msg->elements[i];
2599 sa = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
2601 talloc_free(tmp_ctx);
2602 return LDB_ERR_OPERATIONS_ERROR;
2604 if (ldb_attr_cmp(el->name, rdn_name) == 0) {
2605 /* don't remove the rDN */
2608 if (sa->linkID && sa->linkID & 1) {
2609 ret = replmd_delete_remove_link(module, schema, old_dn, el, sa);
2610 if (ret != LDB_SUCCESS) {
2611 talloc_free(tmp_ctx);
2612 return LDB_ERR_OPERATIONS_ERROR;
2616 if (!sa->linkID && ldb_attr_in_list(preserved_attrs, el->name)) {
2619 ret = ldb_msg_add_empty(msg, el->name, LDB_FLAG_MOD_DELETE, &el);
2620 if (ret != LDB_SUCCESS) {
2621 talloc_free(tmp_ctx);
2622 ldb_module_oom(module);
2632 if (deletion_state == OBJECT_NOT_DELETED) {
2633 /* work out what the new rdn value is, for updating the
2634 rDN and name fields */
2635 new_rdn_value = ldb_dn_get_rdn_val(new_dn);
2637 ret = ldb_msg_add_value(msg, strlower_talloc(tmp_ctx, rdn_name), new_rdn_value, &el);
2638 if (ret != LDB_SUCCESS) {
2639 talloc_free(tmp_ctx);
2642 el->flags = LDB_FLAG_MOD_REPLACE;
2644 el = ldb_msg_find_element(old_msg, "name");
2646 ret = ldb_msg_add_value(msg, "name", new_rdn_value, &el);
2647 if (ret != LDB_SUCCESS) {
2648 talloc_free(tmp_ctx);
2651 el->flags = LDB_FLAG_MOD_REPLACE;
2655 ret = dsdb_module_modify(module, msg, DSDB_FLAG_OWN_MODULE);
2656 if (ret != LDB_SUCCESS) {
2657 ldb_asprintf_errstring(ldb, "replmd_delete: Failed to modify object %s in delete - %s",
2658 ldb_dn_get_linearized(old_dn), ldb_errstring(ldb));
2659 talloc_free(tmp_ctx);
2663 if (deletion_state == OBJECT_NOT_DELETED) {
2664 /* now rename onto the new DN */
2665 ret = dsdb_module_rename(module, old_dn, new_dn, DSDB_FLAG_NEXT_MODULE);
2666 if (ret != LDB_SUCCESS){
2667 DEBUG(0,(__location__ ": Failed to rename object from '%s' to '%s' - %s\n",
2668 ldb_dn_get_linearized(old_dn),
2669 ldb_dn_get_linearized(new_dn),
2670 ldb_errstring(ldb)));
2671 talloc_free(tmp_ctx);
2676 talloc_free(tmp_ctx);
2678 return ldb_module_done(req, NULL, NULL, LDB_SUCCESS);
2683 static int replmd_replicated_request_error(struct replmd_replicated_request *ar, int ret)
2688 static int replmd_replicated_request_werror(struct replmd_replicated_request *ar, WERROR status)
2690 int ret = LDB_ERR_OTHER;
2691 /* TODO: do some error mapping */
2695 static int replmd_replicated_apply_add(struct replmd_replicated_request *ar)
2697 struct ldb_context *ldb;
2698 struct ldb_request *change_req;
2699 enum ndr_err_code ndr_err;
2700 struct ldb_message *msg;
2701 struct replPropertyMetaDataBlob *md;
2702 struct ldb_val md_value;
2707 * TODO: check if the parent object exist
2711 * TODO: handle the conflict case where an object with the
2715 ldb = ldb_module_get_ctx(ar->module);
2716 msg = ar->objs->objects[ar->index_current].msg;
2717 md = ar->objs->objects[ar->index_current].meta_data;
2719 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
2720 if (ret != LDB_SUCCESS) {
2721 return replmd_replicated_request_error(ar, ret);
2724 ret = ldb_msg_add_value(msg, "objectGUID", &ar->objs->objects[ar->index_current].guid_value, NULL);
2725 if (ret != LDB_SUCCESS) {
2726 return replmd_replicated_request_error(ar, ret);
2729 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
2730 if (ret != LDB_SUCCESS) {
2731 return replmd_replicated_request_error(ar, ret);
2734 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", ar->seq_num);
2735 if (ret != LDB_SUCCESS) {
2736 return replmd_replicated_request_error(ar, ret);
2739 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
2740 if (ret != LDB_SUCCESS) {
2741 return replmd_replicated_request_error(ar, ret);
2744 /* remove any message elements that have zero values */
2745 for (i=0; i<msg->num_elements; i++) {
2746 struct ldb_message_element *el = &msg->elements[i];
2748 if (el->num_values == 0) {
2749 DEBUG(4,(__location__ ": Removing attribute %s with num_values==0\n",
2751 memmove(el, el+1, sizeof(*el)*(msg->num_elements - (i+1)));
2752 msg->num_elements--;
2759 * the meta data array is already sorted by the caller
2761 for (i=0; i < md->ctr.ctr1.count; i++) {
2762 md->ctr.ctr1.array[i].local_usn = ar->seq_num;
2764 ndr_err = ndr_push_struct_blob(&md_value, msg, md,
2765 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
2766 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
2767 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
2768 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
2770 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &md_value, NULL);
2771 if (ret != LDB_SUCCESS) {
2772 return replmd_replicated_request_error(ar, ret);
2775 replmd_ldb_message_sort(msg, ar->schema);
2778 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_ADD, msg);
2779 DEBUG(4, ("DRS replication add message:\n%s\n", s));
2783 ret = ldb_build_add_req(&change_req,
2791 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
2793 return ldb_next_request(ar->module, change_req);
2797 return true if an update is newer than an existing entry
2798 see section 5.11 of MS-ADTS
2800 static bool replmd_update_is_newer(const struct GUID *current_invocation_id,
2801 const struct GUID *update_invocation_id,
2802 uint32_t current_version,
2803 uint32_t update_version,
2804 NTTIME current_change_time,
2805 NTTIME update_change_time)
2807 if (update_version != current_version) {
2808 return update_version > current_version;
2810 if (update_change_time > current_change_time) {
2813 if (update_change_time == current_change_time) {
2814 return GUID_compare(update_invocation_id, current_invocation_id) > 0;
2819 static bool replmd_replPropertyMetaData1_is_newer(struct replPropertyMetaData1 *cur_m,
2820 struct replPropertyMetaData1 *new_m)
2822 return replmd_update_is_newer(&cur_m->originating_invocation_id,
2823 &new_m->originating_invocation_id,
2826 cur_m->originating_change_time,
2827 new_m->originating_change_time);
2830 static struct replPropertyMetaData1 *
2831 replmd_replPropertyMetaData1_find_attid(struct replPropertyMetaDataBlob *md_blob,
2832 enum drsuapi_DsAttributeId attid)
2835 struct replPropertyMetaDataCtr1 *rpmd_ctr = &md_blob->ctr.ctr1;
2837 for (i = 0; i < rpmd_ctr->count; i++) {
2838 if (rpmd_ctr->array[i].attid == attid) {
2839 return &rpmd_ctr->array[i];
2845 static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar)
2847 struct ldb_context *ldb;
2848 struct ldb_request *change_req;
2849 enum ndr_err_code ndr_err;
2850 struct ldb_message *msg;
2851 struct replPropertyMetaDataBlob *rmd;
2852 struct replPropertyMetaDataBlob omd;
2853 const struct ldb_val *omd_value;
2854 struct replPropertyMetaDataBlob nmd;
2855 struct ldb_val nmd_value;
2856 struct replPropertyMetaData1 *md_remote;
2857 struct replPropertyMetaData1 *md_local;
2860 unsigned int removed_attrs = 0;
2863 ldb = ldb_module_get_ctx(ar->module);
2864 msg = ar->objs->objects[ar->index_current].msg;
2865 rmd = ar->objs->objects[ar->index_current].meta_data;
2869 /* find existing meta data */
2870 omd_value = ldb_msg_find_ldb_val(ar->search_msg, "replPropertyMetaData");
2872 ndr_err = ndr_pull_struct_blob(omd_value, ar, &omd,
2873 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
2874 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
2875 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
2876 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
2879 if (omd.version != 1) {
2880 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
2884 /* check if remote 'name' has change,
2885 * which indicates a rename operation */
2886 md_remote = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTRIBUTE_name);
2888 md_local = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTRIBUTE_name);
2889 SMB_ASSERT(md_local);
2890 if (replmd_replPropertyMetaData1_is_newer(md_local, md_remote)) {
2891 SMB_ASSERT(ldb_dn_compare(msg->dn, ar->search_msg->dn) != 0);
2892 /* TODO: Find appropriate local name (dn) for the object
2893 * and modify msg->dn appropriately */
2895 DEBUG(4,("replmd_replicated_request rename %s => %s\n",
2896 ldb_dn_get_linearized(ar->search_msg->dn),
2897 ldb_dn_get_linearized(msg->dn)));
2898 /* pass rename to the next module
2899 * so it doesn't appear as an originating update */
2900 ret = dsdb_module_rename(ar->module,
2901 ar->search_msg->dn, msg->dn,
2902 DSDB_FLAG_NEXT_MODULE);
2903 if (ret != LDB_SUCCESS) {
2904 ldb_debug(ldb, LDB_DEBUG_FATAL,
2905 "replmd_replicated_request rename %s => %s failed - %s\n",
2906 ldb_dn_get_linearized(ar->search_msg->dn),
2907 ldb_dn_get_linearized(msg->dn),
2908 ldb_errstring(ldb));
2909 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
2916 nmd.ctr.ctr1.count = omd.ctr.ctr1.count + rmd->ctr.ctr1.count;
2917 nmd.ctr.ctr1.array = talloc_array(ar,
2918 struct replPropertyMetaData1,
2919 nmd.ctr.ctr1.count);
2920 if (!nmd.ctr.ctr1.array) return replmd_replicated_request_werror(ar, WERR_NOMEM);
2922 /* first copy the old meta data */
2923 for (i=0; i < omd.ctr.ctr1.count; i++) {
2924 nmd.ctr.ctr1.array[ni] = omd.ctr.ctr1.array[i];
2928 /* now merge in the new meta data */
2929 for (i=0; i < rmd->ctr.ctr1.count; i++) {
2932 for (j=0; j < ni; j++) {
2935 if (rmd->ctr.ctr1.array[i].attid != nmd.ctr.ctr1.array[j].attid) {
2939 cmp = replmd_replPropertyMetaData1_is_newer(&nmd.ctr.ctr1.array[j],
2940 &rmd->ctr.ctr1.array[i]);
2942 /* replace the entry */
2943 nmd.ctr.ctr1.array[j] = rmd->ctr.ctr1.array[i];
2948 if (rmd->ctr.ctr1.array[i].attid != DRSUAPI_ATTRIBUTE_instanceType) {
2949 DEBUG(3,("Discarding older DRS attribute update to %s on %s from %s\n",
2950 msg->elements[i-removed_attrs].name,
2951 ldb_dn_get_linearized(msg->dn),
2952 GUID_string(ar, &rmd->ctr.ctr1.array[i].originating_invocation_id)));
2955 /* we don't want to apply this change so remove the attribute */
2956 ldb_msg_remove_element(msg, &msg->elements[i-removed_attrs]);
2963 if (found) continue;
2965 nmd.ctr.ctr1.array[ni] = rmd->ctr.ctr1.array[i];
2970 * finally correct the size of the meta_data array
2972 nmd.ctr.ctr1.count = ni;
2975 * the rdn attribute (the alias for the name attribute),
2976 * 'cn' for most objects is the last entry in the meta data array
2979 * sort the new meta data array
2981 ret = replmd_replPropertyMetaDataCtr1_sort(&nmd.ctr.ctr1, ar->schema, msg->dn);
2982 if (ret != LDB_SUCCESS) {
2987 * check if some replicated attributes left, otherwise skip the ldb_modify() call
2989 if (msg->num_elements == 0) {
2990 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: skip replace\n",
2993 ar->index_current++;
2994 return replmd_replicated_apply_next(ar);
2997 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: replace %u attributes\n",
2998 ar->index_current, msg->num_elements);
3000 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
3001 if (ret != LDB_SUCCESS) {
3002 return replmd_replicated_request_error(ar, ret);
3005 for (i=0; i<ni; i++) {
3006 nmd.ctr.ctr1.array[i].local_usn = ar->seq_num;
3009 /* create the meta data value */
3010 ndr_err = ndr_push_struct_blob(&nmd_value, msg, &nmd,
3011 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
3012 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
3013 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
3014 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
3018 * when we know that we'll modify the record, add the whenChanged, uSNChanged
3019 * and replPopertyMetaData attributes
3021 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
3022 if (ret != LDB_SUCCESS) {
3023 return replmd_replicated_request_error(ar, ret);
3025 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
3026 if (ret != LDB_SUCCESS) {
3027 return replmd_replicated_request_error(ar, ret);
3029 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
3030 if (ret != LDB_SUCCESS) {
3031 return replmd_replicated_request_error(ar, ret);
3034 replmd_ldb_message_sort(msg, ar->schema);
3036 /* we want to replace the old values */
3037 for (i=0; i < msg->num_elements; i++) {
3038 msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
3042 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_MODIFY, msg);
3043 DEBUG(4, ("DRS replication modify message:\n%s\n", s));
3047 ret = ldb_build_mod_req(&change_req,
3055 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
3057 return ldb_next_request(ar->module, change_req);
3060 static int replmd_replicated_apply_search_callback(struct ldb_request *req,
3061 struct ldb_reply *ares)
3063 struct replmd_replicated_request *ar = talloc_get_type(req->context,
3064 struct replmd_replicated_request);
3068 return ldb_module_done(ar->req, NULL, NULL,
3069 LDB_ERR_OPERATIONS_ERROR);
3071 if (ares->error != LDB_SUCCESS &&
3072 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
3073 return ldb_module_done(ar->req, ares->controls,
3074 ares->response, ares->error);
3077 switch (ares->type) {
3078 case LDB_REPLY_ENTRY:
3079 ar->search_msg = talloc_steal(ar, ares->message);
3082 case LDB_REPLY_REFERRAL:
3083 /* we ignore referrals */
3086 case LDB_REPLY_DONE:
3087 if (ar->search_msg != NULL) {
3088 ret = replmd_replicated_apply_merge(ar);
3090 ret = replmd_replicated_apply_add(ar);
3092 if (ret != LDB_SUCCESS) {
3093 return ldb_module_done(ar->req, NULL, NULL, ret);
3101 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar);
3103 static int replmd_replicated_apply_next(struct replmd_replicated_request *ar)
3105 struct ldb_context *ldb;
3109 struct ldb_request *search_req;
3110 struct ldb_search_options_control *options;
3112 if (ar->index_current >= ar->objs->num_objects) {
3113 /* done with it, go to next stage */
3114 return replmd_replicated_uptodate_vector(ar);
3117 ldb = ldb_module_get_ctx(ar->module);
3118 ar->search_msg = NULL;
3120 tmp_str = ldb_binary_encode(ar, ar->objs->objects[ar->index_current].guid_value);
3121 if (!tmp_str) return replmd_replicated_request_werror(ar, WERR_NOMEM);
3123 filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
3124 if (!filter) return replmd_replicated_request_werror(ar, WERR_NOMEM);
3125 talloc_free(tmp_str);
3127 ret = ldb_build_search_req(&search_req,
3136 replmd_replicated_apply_search_callback,
3139 ret = ldb_request_add_control(search_req, LDB_CONTROL_SHOW_DELETED_OID, true, NULL);
3140 if (ret != LDB_SUCCESS) {
3144 /* we need to cope with cross-partition links, so search for
3145 the GUID over all partitions */
3146 options = talloc(search_req, struct ldb_search_options_control);
3147 if (options == NULL) {
3148 DEBUG(0, (__location__ ": out of memory\n"));
3149 return LDB_ERR_OPERATIONS_ERROR;
3151 options->search_options = LDB_SEARCH_OPTION_PHANTOM_ROOT;
3153 ret = ldb_request_add_control(search_req,
3154 LDB_CONTROL_SEARCH_OPTIONS_OID,
3156 if (ret != LDB_SUCCESS) {
3160 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
3162 return ldb_next_request(ar->module, search_req);
3165 static int replmd_replicated_uptodate_modify_callback(struct ldb_request *req,
3166 struct ldb_reply *ares)
3168 struct ldb_context *ldb;
3169 struct replmd_replicated_request *ar = talloc_get_type(req->context,
3170 struct replmd_replicated_request);
3171 ldb = ldb_module_get_ctx(ar->module);
3174 return ldb_module_done(ar->req, NULL, NULL,
3175 LDB_ERR_OPERATIONS_ERROR);
3177 if (ares->error != LDB_SUCCESS) {
3178 return ldb_module_done(ar->req, ares->controls,
3179 ares->response, ares->error);
3182 if (ares->type != LDB_REPLY_DONE) {
3183 ldb_set_errstring(ldb, "Invalid reply type\n!");
3184 return ldb_module_done(ar->req, NULL, NULL,
3185 LDB_ERR_OPERATIONS_ERROR);
3190 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
3193 static int replmd_replicated_uptodate_modify(struct replmd_replicated_request *ar)
3195 struct ldb_context *ldb;
3196 struct ldb_request *change_req;
3197 enum ndr_err_code ndr_err;
3198 struct ldb_message *msg;
3199 struct replUpToDateVectorBlob ouv;
3200 const struct ldb_val *ouv_value;
3201 const struct drsuapi_DsReplicaCursor2CtrEx *ruv;
3202 struct replUpToDateVectorBlob nuv;
3203 struct ldb_val nuv_value;
3204 struct ldb_message_element *nuv_el = NULL;
3205 const struct GUID *our_invocation_id;
3206 struct ldb_message_element *orf_el = NULL;
3207 struct repsFromToBlob nrf;
3208 struct ldb_val *nrf_value = NULL;
3209 struct ldb_message_element *nrf_el = NULL;
3213 time_t t = time(NULL);
3217 ldb = ldb_module_get_ctx(ar->module);
3218 ruv = ar->objs->uptodateness_vector;
3224 unix_to_nt_time(&now, t);
3227 * first create the new replUpToDateVector
3229 ouv_value = ldb_msg_find_ldb_val(ar->search_msg, "replUpToDateVector");
3231 ndr_err = ndr_pull_struct_blob(ouv_value, ar, &ouv,
3232 (ndr_pull_flags_fn_t)ndr_pull_replUpToDateVectorBlob);
3233 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
3234 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
3235 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
3238 if (ouv.version != 2) {
3239 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
3244 * the new uptodateness vector will at least
3245 * contain 1 entry, one for the source_dsa
3247 * plus optional values from our old vector and the one from the source_dsa
3249 nuv.ctr.ctr2.count = 1 + ouv.ctr.ctr2.count;
3250 if (ruv) nuv.ctr.ctr2.count += ruv->count;
3251 nuv.ctr.ctr2.cursors = talloc_array(ar,
3252 struct drsuapi_DsReplicaCursor2,
3253 nuv.ctr.ctr2.count);
3254 if (!nuv.ctr.ctr2.cursors) return replmd_replicated_request_werror(ar, WERR_NOMEM);
3256 /* first copy the old vector */
3257 for (i=0; i < ouv.ctr.ctr2.count; i++) {
3258 nuv.ctr.ctr2.cursors[ni] = ouv.ctr.ctr2.cursors[i];
3262 /* get our invocation_id if we have one already attached to the ldb */
3263 our_invocation_id = samdb_ntds_invocation_id(ldb);
3265 /* merge in the source_dsa vector is available */
3266 for (i=0; (ruv && i < ruv->count); i++) {
3269 if (our_invocation_id &&
3270 GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
3271 our_invocation_id)) {
3275 for (j=0; j < ni; j++) {
3276 if (!GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
3277 &nuv.ctr.ctr2.cursors[j].source_dsa_invocation_id)) {
3284 * we update only the highest_usn and not the latest_sync_success time,
3285 * because the last success stands for direct replication
3287 if (ruv->cursors[i].highest_usn > nuv.ctr.ctr2.cursors[j].highest_usn) {
3288 nuv.ctr.ctr2.cursors[j].highest_usn = ruv->cursors[i].highest_usn;
3293 if (found) continue;
3295 /* if it's not there yet, add it */
3296 nuv.ctr.ctr2.cursors[ni] = ruv->cursors[i];
3301 * merge in the current highwatermark for the source_dsa
3304 for (j=0; j < ni; j++) {
3305 if (!GUID_equal(&ar->objs->source_dsa->source_dsa_invocation_id,
3306 &nuv.ctr.ctr2.cursors[j].source_dsa_invocation_id)) {
3313 * here we update the highest_usn and last_sync_success time
3314 * because we're directly replicating from the source_dsa
3316 * and use the tmp_highest_usn because this is what we have just applied
3319 nuv.ctr.ctr2.cursors[j].highest_usn = ar->objs->source_dsa->highwatermark.tmp_highest_usn;
3320 nuv.ctr.ctr2.cursors[j].last_sync_success = now;
3325 * here we update the highest_usn and last_sync_success time
3326 * because we're directly replicating from the source_dsa
3328 * and use the tmp_highest_usn because this is what we have just applied
3331 nuv.ctr.ctr2.cursors[ni].source_dsa_invocation_id= ar->objs->source_dsa->source_dsa_invocation_id;
3332 nuv.ctr.ctr2.cursors[ni].highest_usn = ar->objs->source_dsa->highwatermark.tmp_highest_usn;
3333 nuv.ctr.ctr2.cursors[ni].last_sync_success = now;
3338 * finally correct the size of the cursors array
3340 nuv.ctr.ctr2.count = ni;
3345 TYPESAFE_QSORT(nuv.ctr.ctr2.cursors, nuv.ctr.ctr2.count, drsuapi_DsReplicaCursor2_compare);
3348 * create the change ldb_message
3350 msg = ldb_msg_new(ar);
3351 if (!msg) return replmd_replicated_request_werror(ar, WERR_NOMEM);
3352 msg->dn = ar->search_msg->dn;
3354 ndr_err = ndr_push_struct_blob(&nuv_value, msg, &nuv,
3355 (ndr_push_flags_fn_t)ndr_push_replUpToDateVectorBlob);
3356 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
3357 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
3358 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
3360 ret = ldb_msg_add_value(msg, "replUpToDateVector", &nuv_value, &nuv_el);
3361 if (ret != LDB_SUCCESS) {
3362 return replmd_replicated_request_error(ar, ret);
3364 nuv_el->flags = LDB_FLAG_MOD_REPLACE;
3367 * now create the new repsFrom value from the given repsFromTo1 structure
3371 nrf.ctr.ctr1 = *ar->objs->source_dsa;
3372 /* and fix some values... */
3373 nrf.ctr.ctr1.consecutive_sync_failures = 0;
3374 nrf.ctr.ctr1.last_success = now;
3375 nrf.ctr.ctr1.last_attempt = now;
3376 nrf.ctr.ctr1.result_last_attempt = WERR_OK;
3377 nrf.ctr.ctr1.highwatermark.highest_usn = nrf.ctr.ctr1.highwatermark.tmp_highest_usn;
3380 * first see if we already have a repsFrom value for the current source dsa
3381 * if so we'll later replace this value
3383 orf_el = ldb_msg_find_element(ar->search_msg, "repsFrom");
3385 for (i=0; i < orf_el->num_values; i++) {
3386 struct repsFromToBlob *trf;
3388 trf = talloc(ar, struct repsFromToBlob);
3389 if (!trf) return replmd_replicated_request_werror(ar, WERR_NOMEM);
3391 ndr_err = ndr_pull_struct_blob(&orf_el->values[i], trf, trf,
3392 (ndr_pull_flags_fn_t)ndr_pull_repsFromToBlob);
3393 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
3394 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
3395 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
3398 if (trf->version != 1) {
3399 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
3403 * we compare the source dsa objectGUID not the invocation_id
3404 * because we want only one repsFrom value per source dsa
3405 * and when the invocation_id of the source dsa has changed we don't need
3406 * the old repsFrom with the old invocation_id
3408 if (!GUID_equal(&trf->ctr.ctr1.source_dsa_obj_guid,
3409 &ar->objs->source_dsa->source_dsa_obj_guid)) {
3415 nrf_value = &orf_el->values[i];
3420 * copy over all old values to the new ldb_message
3422 ret = ldb_msg_add_empty(msg, "repsFrom", 0, &nrf_el);
3423 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
3428 * if we haven't found an old repsFrom value for the current source dsa
3429 * we'll add a new value
3432 struct ldb_val zero_value;
3433 ZERO_STRUCT(zero_value);
3434 ret = ldb_msg_add_value(msg, "repsFrom", &zero_value, &nrf_el);
3435 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
3437 nrf_value = &nrf_el->values[nrf_el->num_values - 1];
3440 /* we now fill the value which is already attached to ldb_message */
3441 ndr_err = ndr_push_struct_blob(nrf_value, msg,
3443 (ndr_push_flags_fn_t)ndr_push_repsFromToBlob);
3444 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
3445 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
3446 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
3450 * the ldb_message_element for the attribute, has all the old values and the new one
3451 * so we'll replace the whole attribute with all values
3453 nrf_el->flags = LDB_FLAG_MOD_REPLACE;
3456 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_MODIFY, msg);
3457 DEBUG(4, ("DRS replication uptodate modify message:\n%s\n", s));
3461 /* prepare the ldb_modify() request */
3462 ret = ldb_build_mod_req(&change_req,
3468 replmd_replicated_uptodate_modify_callback,
3470 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
3472 return ldb_next_request(ar->module, change_req);
3475 static int replmd_replicated_uptodate_search_callback(struct ldb_request *req,
3476 struct ldb_reply *ares)
3478 struct replmd_replicated_request *ar = talloc_get_type(req->context,
3479 struct replmd_replicated_request);
3483 return ldb_module_done(ar->req, NULL, NULL,
3484 LDB_ERR_OPERATIONS_ERROR);
3486 if (ares->error != LDB_SUCCESS &&
3487 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
3488 return ldb_module_done(ar->req, ares->controls,
3489 ares->response, ares->error);
3492 switch (ares->type) {
3493 case LDB_REPLY_ENTRY:
3494 ar->search_msg = talloc_steal(ar, ares->message);
3497 case LDB_REPLY_REFERRAL:
3498 /* we ignore referrals */
3501 case LDB_REPLY_DONE:
3502 if (ar->search_msg == NULL) {
3503 ret = replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
3505 ret = replmd_replicated_uptodate_modify(ar);
3507 if (ret != LDB_SUCCESS) {
3508 return ldb_module_done(ar->req, NULL, NULL, ret);
3517 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar)
3519 struct ldb_context *ldb;
3521 static const char *attrs[] = {
3522 "replUpToDateVector",
3526 struct ldb_request *search_req;
3528 ldb = ldb_module_get_ctx(ar->module);
3529 ar->search_msg = NULL;
3531 ret = ldb_build_search_req(&search_req,
3534 ar->objs->partition_dn,
3540 replmd_replicated_uptodate_search_callback,
3542 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
3544 return ldb_next_request(ar->module, search_req);
3549 static int replmd_extended_replicated_objects(struct ldb_module *module, struct ldb_request *req)
3551 struct ldb_context *ldb;
3552 struct dsdb_extended_replicated_objects *objs;
3553 struct replmd_replicated_request *ar;
3554 struct ldb_control **ctrls;
3557 struct replmd_private *replmd_private =
3558 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
3560 ldb = ldb_module_get_ctx(module);
3562 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_extended_replicated_objects\n");
3564 objs = talloc_get_type(req->op.extended.data, struct dsdb_extended_replicated_objects);
3566 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: invalid extended data\n");
3567 return LDB_ERR_PROTOCOL_ERROR;
3570 if (objs->version != DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION) {
3571 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: extended data invalid version [%u != %u]\n",
3572 objs->version, DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION);
3573 return LDB_ERR_PROTOCOL_ERROR;
3576 ar = replmd_ctx_init(module, req);
3578 return LDB_ERR_OPERATIONS_ERROR;
3580 /* Set the flags to have the replmd_op_callback run over the full set of objects */
3581 ar->apply_mode = true;
3583 ar->schema = dsdb_get_schema(ldb, ar);
3585 ldb_debug_set(ldb, LDB_DEBUG_FATAL, "replmd_ctx_init: no loaded schema found\n");
3587 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
3588 return LDB_ERR_CONSTRAINT_VIOLATION;
3591 ctrls = req->controls;
3593 if (req->controls) {
3594 req->controls = talloc_memdup(ar, req->controls,
3595 talloc_get_size(req->controls));
3596 if (!req->controls) return replmd_replicated_request_werror(ar, WERR_NOMEM);
3599 /* This allows layers further down to know if a change came in over replication */
3600 ret = ldb_request_add_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID, false, NULL);
3601 if (ret != LDB_SUCCESS) {
3605 /* If this change contained linked attributes in the body
3606 * (rather than in the links section) we need to update
3607 * backlinks in linked_attributes */
3608 ret = ldb_request_add_control(req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
3609 if (ret != LDB_SUCCESS) {
3613 ar->controls = req->controls;
3614 req->controls = ctrls;
3616 DEBUG(4,("linked_attributes_count=%u\n", objs->linked_attributes_count));
3618 /* save away the linked attributes for the end of the
3620 for (i=0; i<ar->objs->linked_attributes_count; i++) {
3621 struct la_entry *la_entry;
3623 if (replmd_private->la_ctx == NULL) {
3624 replmd_private->la_ctx = talloc_new(replmd_private);
3626 la_entry = talloc(replmd_private->la_ctx, struct la_entry);
3627 if (la_entry == NULL) {
3629 return LDB_ERR_OPERATIONS_ERROR;
3631 la_entry->la = talloc(la_entry, struct drsuapi_DsReplicaLinkedAttribute);
3632 if (la_entry->la == NULL) {
3633 talloc_free(la_entry);
3635 return LDB_ERR_OPERATIONS_ERROR;
3637 *la_entry->la = ar->objs->linked_attributes[i];
3639 /* we need to steal the non-scalars so they stay
3640 around until the end of the transaction */
3641 talloc_steal(la_entry->la, la_entry->la->identifier);
3642 talloc_steal(la_entry->la, la_entry->la->value.blob);
3644 DLIST_ADD(replmd_private->la_list, la_entry);
3647 return replmd_replicated_apply_next(ar);
3651 process one linked attribute structure
3653 static int replmd_process_linked_attribute(struct ldb_module *module,
3654 struct la_entry *la_entry)
3656 struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
3657 struct ldb_context *ldb = ldb_module_get_ctx(module);
3658 struct ldb_message *msg;
3659 TALLOC_CTX *tmp_ctx = talloc_new(la_entry);
3660 const struct dsdb_schema *schema = dsdb_get_schema(ldb, tmp_ctx);
3662 const struct dsdb_attribute *attr;
3663 struct dsdb_dn *dsdb_dn;
3664 uint64_t seq_num = 0;
3665 struct ldb_message_element *old_el;
3667 time_t t = time(NULL);
3668 struct ldb_result *res;
3669 const char *attrs[2];
3670 struct parsed_dn *pdn_list, *pdn;
3671 struct GUID guid = GUID_zero();
3673 bool active = (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)?true:false;
3674 const struct GUID *our_invocation_id;
3677 linked_attributes[0]:
3678 &objs->linked_attributes[i]: struct drsuapi_DsReplicaLinkedAttribute
3680 identifier: struct drsuapi_DsReplicaObjectIdentifier
3681 __ndr_size : 0x0000003a (58)
3682 __ndr_size_sid : 0x00000000 (0)
3683 guid : 8e95b6a9-13dd-4158-89db-3220a5be5cc7
3685 __ndr_size_dn : 0x00000000 (0)
3687 attid : DRSUAPI_ATTRIBUTE_member (0x1F)
3688 value: struct drsuapi_DsAttributeValue
3689 __ndr_size : 0x0000007e (126)
3691 blob : DATA_BLOB length=126
3692 flags : 0x00000001 (1)
3693 1: DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE
3694 originating_add_time : Wed Sep 2 22:20:01 2009 EST
3695 meta_data: struct drsuapi_DsReplicaMetaData
3696 version : 0x00000015 (21)
3697 originating_change_time : Wed Sep 2 23:39:07 2009 EST
3698 originating_invocation_id: 794640f3-18cf-40ee-a211-a93992b67a64
3699 originating_usn : 0x000000000001e19c (123292)
3701 (for cases where the link is to a normal DN)
3702 &target: struct drsuapi_DsReplicaObjectIdentifier3
3703 __ndr_size : 0x0000007e (126)
3704 __ndr_size_sid : 0x0000001c (28)
3705 guid : 7639e594-db75-4086-b0d4-67890ae46031
3706 sid : S-1-5-21-2848215498-2472035911-1947525656-19924
3707 __ndr_size_dn : 0x00000022 (34)
3708 dn : 'CN=UOne,OU=TestOU,DC=vsofs8,DC=com'
3711 /* find the attribute being modified */
3712 attr = dsdb_attribute_by_attributeID_id(schema, la->attid);
3714 DEBUG(0, (__location__ ": Unable to find attributeID 0x%x\n", la->attid));
3715 talloc_free(tmp_ctx);
3716 return LDB_ERR_OPERATIONS_ERROR;
3719 attrs[0] = attr->lDAPDisplayName;
3722 /* get the existing message from the db for the object with
3723 this GUID, returning attribute being modified. We will then
3724 use this msg as the basis for a modify call */
3725 ret = dsdb_module_search(module, tmp_ctx, &res, NULL, LDB_SCOPE_SUBTREE, attrs,
3726 DSDB_FLAG_NEXT_MODULE |
3727 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
3728 DSDB_SEARCH_SHOW_DELETED |
3729 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
3730 DSDB_SEARCH_REVEAL_INTERNALS,
3731 "objectGUID=%s", GUID_string(tmp_ctx, &la->identifier->guid));
3732 if (ret != LDB_SUCCESS) {
3733 talloc_free(tmp_ctx);
3736 if (res->count != 1) {
3737 ldb_asprintf_errstring(ldb, "DRS linked attribute for GUID %s - DN not found",
3738 GUID_string(tmp_ctx, &la->identifier->guid));
3739 talloc_free(tmp_ctx);
3740 return LDB_ERR_NO_SUCH_OBJECT;
3744 if (msg->num_elements == 0) {
3745 ret = ldb_msg_add_empty(msg, attr->lDAPDisplayName, LDB_FLAG_MOD_REPLACE, &old_el);
3746 if (ret != LDB_SUCCESS) {
3747 ldb_module_oom(module);
3748 talloc_free(tmp_ctx);
3749 return LDB_ERR_OPERATIONS_ERROR;
3752 old_el = &msg->elements[0];
3753 old_el->flags = LDB_FLAG_MOD_REPLACE;
3756 /* parse the existing links */
3757 ret = get_parsed_dns(module, tmp_ctx, old_el, &pdn_list, attr->syntax->ldap_oid);
3758 if (ret != LDB_SUCCESS) {
3759 talloc_free(tmp_ctx);
3763 /* get our invocationId */
3764 our_invocation_id = samdb_ntds_invocation_id(ldb);
3765 if (!our_invocation_id) {
3766 ldb_debug_set(ldb, LDB_DEBUG_ERROR, __location__ ": unable to find invocationId\n");
3767 talloc_free(tmp_ctx);
3768 return LDB_ERR_OPERATIONS_ERROR;
3771 ret = replmd_check_upgrade_links(pdn_list, old_el->num_values, old_el, our_invocation_id);
3772 if (ret != LDB_SUCCESS) {
3773 talloc_free(tmp_ctx);
3777 status = dsdb_dn_la_from_blob(ldb, attr, schema, tmp_ctx, la->value.blob, &dsdb_dn);
3778 if (!W_ERROR_IS_OK(status)) {
3779 ldb_asprintf_errstring(ldb, "Failed to parsed linked attribute blob for %s on %s - %s\n",
3780 old_el->name, ldb_dn_get_linearized(msg->dn), win_errstr(status));
3781 return LDB_ERR_OPERATIONS_ERROR;
3784 ntstatus = dsdb_get_extended_dn_guid(dsdb_dn->dn, &guid, "GUID");
3785 if (!NT_STATUS_IS_OK(ntstatus) && active) {
3786 ldb_asprintf_errstring(ldb, "Failed to find GUID in linked attribute blob for %s on %s from %s",
3788 ldb_dn_get_linearized(dsdb_dn->dn),
3789 ldb_dn_get_linearized(msg->dn));
3790 return LDB_ERR_OPERATIONS_ERROR;
3793 /* re-resolve the DN by GUID, as the DRS server may give us an
3795 ret = dsdb_module_dn_by_guid(module, dsdb_dn, &guid, &dsdb_dn->dn);
3796 if (ret != LDB_SUCCESS) {
3797 DEBUG(2,(__location__ ": WARNING: Failed to re-resolve GUID %s - using %s",
3798 GUID_string(tmp_ctx, &guid),
3799 ldb_dn_get_linearized(dsdb_dn->dn)));
3802 /* see if this link already exists */
3803 pdn = parsed_dn_find(pdn_list, old_el->num_values, &guid, dsdb_dn->dn);
3805 /* see if this update is newer than what we have already */
3806 struct GUID invocation_id = GUID_zero();
3807 uint32_t version = 0;
3808 NTTIME change_time = 0;
3809 uint32_t rmd_flags = dsdb_dn_rmd_flags(pdn->dsdb_dn->dn);
3811 dsdb_get_extended_dn_guid(pdn->dsdb_dn->dn, &invocation_id, "RMD_INVOCID");
3812 dsdb_get_extended_dn_uint32(pdn->dsdb_dn->dn, &version, "RMD_VERSION");
3813 dsdb_get_extended_dn_nttime(pdn->dsdb_dn->dn, &change_time, "RMD_CHANGETIME");
3815 if (!replmd_update_is_newer(&invocation_id,
3816 &la->meta_data.originating_invocation_id,
3818 la->meta_data.version,
3820 la->meta_data.originating_change_time)) {
3821 DEBUG(3,("Discarding older DRS linked attribute update to %s on %s from %s\n",
3822 old_el->name, ldb_dn_get_linearized(msg->dn),
3823 GUID_string(tmp_ctx, &la->meta_data.originating_invocation_id)));
3824 talloc_free(tmp_ctx);
3828 /* get a seq_num for this change */
3829 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
3830 if (ret != LDB_SUCCESS) {
3831 talloc_free(tmp_ctx);
3835 if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
3836 /* remove the existing backlink */
3837 ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid, false, attr, false);
3838 if (ret != LDB_SUCCESS) {
3839 talloc_free(tmp_ctx);
3844 ret = replmd_update_la_val(tmp_ctx, pdn->v, dsdb_dn, pdn->dsdb_dn,
3845 &la->meta_data.originating_invocation_id,
3846 la->meta_data.originating_usn, seq_num,
3847 la->meta_data.originating_change_time,
3848 la->meta_data.version,
3850 if (ret != LDB_SUCCESS) {
3851 talloc_free(tmp_ctx);
3856 /* add the new backlink */
3857 ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid, true, attr, false);
3858 if (ret != LDB_SUCCESS) {
3859 talloc_free(tmp_ctx);
3864 /* get a seq_num for this change */
3865 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
3866 if (ret != LDB_SUCCESS) {
3867 talloc_free(tmp_ctx);
3871 old_el->values = talloc_realloc(msg->elements, old_el->values,
3872 struct ldb_val, old_el->num_values+1);
3873 if (!old_el->values) {
3874 ldb_module_oom(module);
3875 return LDB_ERR_OPERATIONS_ERROR;
3877 old_el->num_values++;
3879 ret = replmd_build_la_val(tmp_ctx, &old_el->values[old_el->num_values-1], dsdb_dn,
3880 &la->meta_data.originating_invocation_id,
3881 la->meta_data.originating_usn, seq_num,
3882 la->meta_data.originating_change_time,
3883 la->meta_data.version,
3884 (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)?false:true);
3885 if (ret != LDB_SUCCESS) {
3886 talloc_free(tmp_ctx);
3891 ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid,
3893 if (ret != LDB_SUCCESS) {
3894 talloc_free(tmp_ctx);
3900 /* we only change whenChanged and uSNChanged if the seq_num
3902 if (add_time_element(msg, "whenChanged", t) != LDB_SUCCESS) {
3903 talloc_free(tmp_ctx);
3904 return LDB_ERR_OPERATIONS_ERROR;
3907 if (add_uint64_element(msg, "uSNChanged", seq_num) != LDB_SUCCESS) {
3908 talloc_free(tmp_ctx);
3909 return LDB_ERR_OPERATIONS_ERROR;
3912 ret = dsdb_check_single_valued_link(attr, old_el);
3913 if (ret != LDB_SUCCESS) {
3914 talloc_free(tmp_ctx);
3918 ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE | DSDB_MODIFY_RELAX);
3919 if (ret != LDB_SUCCESS) {
3920 ldb_debug(ldb, LDB_DEBUG_WARNING, "Failed to apply linked attribute change '%s'\n%s\n",
3922 ldb_ldif_message_string(ldb, tmp_ctx, LDB_CHANGETYPE_MODIFY, msg));
3923 talloc_free(tmp_ctx);
3927 talloc_free(tmp_ctx);
3932 static int replmd_extended(struct ldb_module *module, struct ldb_request *req)
3934 if (strcmp(req->op.extended.oid, DSDB_EXTENDED_REPLICATED_OBJECTS_OID) == 0) {
3935 return replmd_extended_replicated_objects(module, req);
3938 return ldb_next_request(module, req);
3943 we hook into the transaction operations to allow us to
3944 perform the linked attribute updates at the end of the whole
3945 transaction. This allows a forward linked attribute to be created
3946 before the object is created. During a vampire, w2k8 sends us linked
3947 attributes before the objects they are part of.
3949 static int replmd_start_transaction(struct ldb_module *module)
3951 /* create our private structure for this transaction */
3952 struct replmd_private *replmd_private = talloc_get_type(ldb_module_get_private(module),
3953 struct replmd_private);
3954 replmd_txn_cleanup(replmd_private);
3956 /* free any leftover mod_usn records from cancelled
3958 while (replmd_private->ncs) {
3959 struct nc_entry *e = replmd_private->ncs;
3960 DLIST_REMOVE(replmd_private->ncs, e);
3964 return ldb_next_start_trans(module);
3968 on prepare commit we loop over our queued la_context structures and
3971 static int replmd_prepare_commit(struct ldb_module *module)
3973 struct replmd_private *replmd_private =
3974 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
3975 struct la_entry *la, *prev;
3976 struct la_backlink *bl;
3979 /* walk the list backwards, to do the first entry first, as we
3980 * added the entries with DLIST_ADD() which puts them at the
3981 * start of the list */
3982 for (la = DLIST_TAIL(replmd_private->la_list); la; la=prev) {
3983 prev = DLIST_PREV(la);
3984 DLIST_REMOVE(replmd_private->la_list, la);
3985 ret = replmd_process_linked_attribute(module, la);
3986 if (ret != LDB_SUCCESS) {
3987 replmd_txn_cleanup(replmd_private);
3992 /* process our backlink list, creating and deleting backlinks
3994 for (bl=replmd_private->la_backlinks; bl; bl=bl->next) {
3995 ret = replmd_process_backlink(module, bl);
3996 if (ret != LDB_SUCCESS) {
3997 replmd_txn_cleanup(replmd_private);
4002 replmd_txn_cleanup(replmd_private);
4004 /* possibly change @REPLCHANGED */
4005 ret = replmd_notify_store(module);
4006 if (ret != LDB_SUCCESS) {
4010 return ldb_next_prepare_commit(module);
4013 static int replmd_del_transaction(struct ldb_module *module)
4015 struct replmd_private *replmd_private =
4016 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
4017 replmd_txn_cleanup(replmd_private);
4019 return ldb_next_del_trans(module);
4023 _PUBLIC_ const struct ldb_module_ops ldb_repl_meta_data_module_ops = {
4024 .name = "repl_meta_data",
4025 .init_context = replmd_init,
4027 .modify = replmd_modify,
4028 .rename = replmd_rename,
4029 .del = replmd_delete,
4030 .extended = replmd_extended,
4031 .start_transaction = replmd_start_transaction,
4032 .prepare_commit = replmd_prepare_commit,
4033 .del_transaction = replmd_del_transaction,