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_FLAG_NEXT_MODULE |
1121 DSDB_SEARCH_SHOW_DELETED |
1122 DSDB_SEARCH_SHOW_EXTENDED_DN |
1123 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
1124 DSDB_SEARCH_REVEAL_INTERNALS);
1125 if (ret != LDB_SUCCESS || res->count != 1) {
1126 DEBUG(0,(__location__ ": Object %s failed to find replPropertyMetaData\n",
1127 ldb_dn_get_linearized(msg->dn)));
1128 return LDB_ERR_OPERATIONS_ERROR;
1131 /* if isDeleted is present and is TRUE, then we consider we are deleting,
1132 * otherwise we consider we are updating */
1133 if (ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")) {
1134 situation = REPL_URGENT_ON_DELETE;
1136 situation = REPL_URGENT_ON_UPDATE;
1139 objectclass_el = ldb_msg_find_element(res->msgs[0], "objectClass");
1140 if (is_urgent && replmd_check_urgent_objectclass(objectclass_el,
1145 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
1147 DEBUG(0,(__location__ ": Object %s does not have a replPropertyMetaData attribute\n",
1148 ldb_dn_get_linearized(msg->dn)));
1149 return LDB_ERR_OPERATIONS_ERROR;
1152 ndr_err = ndr_pull_struct_blob(omd_value, msg, &omd,
1153 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
1154 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1155 DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s\n",
1156 ldb_dn_get_linearized(msg->dn)));
1157 return LDB_ERR_OPERATIONS_ERROR;
1160 if (omd.version != 1) {
1161 DEBUG(0,(__location__ ": bad version %u in replPropertyMetaData for %s\n",
1162 omd.version, ldb_dn_get_linearized(msg->dn)));
1163 return LDB_ERR_OPERATIONS_ERROR;
1166 /*we have elements that will be modified*/
1167 if (msg->num_elements > 0) {
1168 /*if we are RODC and this is a DRSR update then its ok*/
1169 if (!ldb_request_get_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID)) {
1170 ret = samdb_rodc(ldb, &rodc);
1171 if (ret != LDB_SUCCESS) {
1172 DEBUG(4, (__location__ ": unable to tell if we are an RODC\n"));
1174 ldb_asprintf_errstring(ldb, "RODC modify is forbidden\n");
1175 return LDB_ERR_REFERRAL;
1180 for (i=0; i<msg->num_elements; i++) {
1181 struct ldb_message_element *old_el;
1182 old_el = ldb_msg_find_element(res->msgs[0], msg->elements[i].name);
1183 ret = replmd_update_rpmd_element(ldb, msg, &msg->elements[i], old_el, &omd, schema, seq_num,
1184 our_invocation_id, now);
1185 if (ret != LDB_SUCCESS) {
1189 if (is_urgent && !*is_urgent && (situation == REPL_URGENT_ON_UPDATE)) {
1190 *is_urgent = replmd_check_urgent_attribute(&msg->elements[i]);
1196 * replmd_update_rpmd_element has done an update if the
1199 if (*seq_num != 0) {
1200 struct ldb_val *md_value;
1201 struct ldb_message_element *el;
1203 md_value = talloc(msg, struct ldb_val);
1204 if (md_value == NULL) {
1206 return LDB_ERR_OPERATIONS_ERROR;
1209 ret = replmd_replPropertyMetaDataCtr1_sort(&omd.ctr.ctr1, schema, msg->dn);
1210 if (ret != LDB_SUCCESS) {
1214 ndr_err = ndr_push_struct_blob(md_value, msg, &omd,
1215 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
1216 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1217 DEBUG(0,(__location__ ": Failed to marshall replPropertyMetaData for %s\n",
1218 ldb_dn_get_linearized(msg->dn)));
1219 return LDB_ERR_OPERATIONS_ERROR;
1222 ret = ldb_msg_add_empty(msg, "replPropertyMetaData", LDB_FLAG_MOD_REPLACE, &el);
1223 if (ret != LDB_SUCCESS) {
1224 DEBUG(0,(__location__ ": Failed to add updated replPropertyMetaData %s\n",
1225 ldb_dn_get_linearized(msg->dn)));
1230 el->values = md_value;
1237 struct dsdb_dn *dsdb_dn;
1242 static int parsed_dn_compare(struct parsed_dn *pdn1, struct parsed_dn *pdn2)
1244 return GUID_compare(pdn1->guid, pdn2->guid);
1247 static struct parsed_dn *parsed_dn_find(struct parsed_dn *pdn, int count, struct GUID *guid, struct ldb_dn *dn)
1249 struct parsed_dn *ret;
1250 if (dn && GUID_all_zero(guid)) {
1251 /* when updating a link using DRS, we sometimes get a
1252 NULL GUID. We then need to try and match by DN */
1254 for (i=0; i<count; i++) {
1255 if (ldb_dn_compare(pdn[i].dsdb_dn->dn, dn) == 0) {
1256 dsdb_get_extended_dn_guid(pdn[i].dsdb_dn->dn, guid, "GUID");
1262 BINARY_ARRAY_SEARCH(pdn, count, guid, guid, GUID_compare, ret);
1267 get a series of message element values as an array of DNs and GUIDs
1268 the result is sorted by GUID
1270 static int get_parsed_dns(struct ldb_module *module, TALLOC_CTX *mem_ctx,
1271 struct ldb_message_element *el, struct parsed_dn **pdn,
1272 const char *ldap_oid)
1275 struct ldb_context *ldb = ldb_module_get_ctx(module);
1282 (*pdn) = talloc_array(mem_ctx, struct parsed_dn, el->num_values);
1284 ldb_module_oom(module);
1285 return LDB_ERR_OPERATIONS_ERROR;
1288 for (i=0; i<el->num_values; i++) {
1289 struct ldb_val *v = &el->values[i];
1292 struct parsed_dn *p;
1296 p->dsdb_dn = dsdb_dn_parse(*pdn, ldb, v, ldap_oid);
1297 if (p->dsdb_dn == NULL) {
1298 return LDB_ERR_INVALID_DN_SYNTAX;
1301 dn = p->dsdb_dn->dn;
1303 p->guid = talloc(*pdn, struct GUID);
1304 if (p->guid == NULL) {
1305 ldb_module_oom(module);
1306 return LDB_ERR_OPERATIONS_ERROR;
1309 status = dsdb_get_extended_dn_guid(dn, p->guid, "GUID");
1310 if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
1311 /* we got a DN without a GUID - go find the GUID */
1312 int ret = dsdb_module_guid_by_dn(module, dn, p->guid);
1313 if (ret != LDB_SUCCESS) {
1314 ldb_asprintf_errstring(ldb, "Unable to find GUID for DN %s\n",
1315 ldb_dn_get_linearized(dn));
1318 ret = dsdb_set_extended_dn_guid(dn, p->guid, "GUID");
1319 if (ret != LDB_SUCCESS) {
1322 } else if (!NT_STATUS_IS_OK(status)) {
1323 return LDB_ERR_OPERATIONS_ERROR;
1326 /* keep a pointer to the original ldb_val */
1330 TYPESAFE_QSORT(*pdn, el->num_values, parsed_dn_compare);
1336 build a new extended DN, including all meta data fields
1338 RMD_FLAGS = DSDB_RMD_FLAG_* bits
1339 RMD_ADDTIME = originating_add_time
1340 RMD_INVOCID = originating_invocation_id
1341 RMD_CHANGETIME = originating_change_time
1342 RMD_ORIGINATING_USN = originating_usn
1343 RMD_LOCAL_USN = local_usn
1344 RMD_VERSION = version
1346 static int replmd_build_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
1347 const struct GUID *invocation_id, uint64_t seq_num,
1348 uint64_t local_usn, NTTIME nttime, uint32_t version, bool deleted)
1350 struct ldb_dn *dn = dsdb_dn->dn;
1351 const char *tstring, *usn_string, *flags_string;
1352 struct ldb_val tval;
1354 struct ldb_val usnv, local_usnv;
1355 struct ldb_val vers, flagsv;
1358 const char *dnstring;
1360 uint32_t rmd_flags = deleted?DSDB_RMD_FLAG_DELETED:0;
1362 tstring = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)nttime);
1364 return LDB_ERR_OPERATIONS_ERROR;
1366 tval = data_blob_string_const(tstring);
1368 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)seq_num);
1370 return LDB_ERR_OPERATIONS_ERROR;
1372 usnv = data_blob_string_const(usn_string);
1374 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)local_usn);
1376 return LDB_ERR_OPERATIONS_ERROR;
1378 local_usnv = data_blob_string_const(usn_string);
1380 vstring = talloc_asprintf(mem_ctx, "%lu", (unsigned long)version);
1382 return LDB_ERR_OPERATIONS_ERROR;
1384 vers = data_blob_string_const(vstring);
1386 status = GUID_to_ndr_blob(invocation_id, dn, &iid);
1387 if (!NT_STATUS_IS_OK(status)) {
1388 return LDB_ERR_OPERATIONS_ERROR;
1391 flags_string = talloc_asprintf(mem_ctx, "%u", rmd_flags);
1392 if (!flags_string) {
1393 return LDB_ERR_OPERATIONS_ERROR;
1395 flagsv = data_blob_string_const(flags_string);
1397 ret = ldb_dn_set_extended_component(dn, "RMD_FLAGS", &flagsv);
1398 if (ret != LDB_SUCCESS) return ret;
1399 ret = ldb_dn_set_extended_component(dn, "RMD_ADDTIME", &tval);
1400 if (ret != LDB_SUCCESS) return ret;
1401 ret = ldb_dn_set_extended_component(dn, "RMD_INVOCID", &iid);
1402 if (ret != LDB_SUCCESS) return ret;
1403 ret = ldb_dn_set_extended_component(dn, "RMD_CHANGETIME", &tval);
1404 if (ret != LDB_SUCCESS) return ret;
1405 ret = ldb_dn_set_extended_component(dn, "RMD_LOCAL_USN", &local_usnv);
1406 if (ret != LDB_SUCCESS) return ret;
1407 ret = ldb_dn_set_extended_component(dn, "RMD_ORIGINATING_USN", &usnv);
1408 if (ret != LDB_SUCCESS) return ret;
1409 ret = ldb_dn_set_extended_component(dn, "RMD_VERSION", &vers);
1410 if (ret != LDB_SUCCESS) return ret;
1412 dnstring = dsdb_dn_get_extended_linearized(mem_ctx, dsdb_dn, 1);
1413 if (dnstring == NULL) {
1414 return LDB_ERR_OPERATIONS_ERROR;
1416 *v = data_blob_string_const(dnstring);
1421 static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
1422 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
1423 uint64_t seq_num, uint64_t local_usn, NTTIME nttime,
1424 uint32_t version, bool deleted);
1427 check if any links need upgrading from w2k format
1429 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.
1431 static int replmd_check_upgrade_links(struct parsed_dn *dns, uint32_t count, struct ldb_message_element *parent_ctx, const struct GUID *invocation_id)
1434 for (i=0; i<count; i++) {
1439 status = dsdb_get_extended_dn_uint32(dns[i].dsdb_dn->dn, &version, "RMD_VERSION");
1440 if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
1444 /* it's an old one that needs upgrading */
1445 ret = replmd_update_la_val(parent_ctx->values, dns[i].v, dns[i].dsdb_dn, dns[i].dsdb_dn, invocation_id,
1447 if (ret != LDB_SUCCESS) {
1455 update an extended DN, including all meta data fields
1457 see replmd_build_la_val for value names
1459 static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
1460 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
1461 uint64_t seq_num, uint64_t local_usn, NTTIME nttime,
1462 uint32_t version, bool deleted)
1464 struct ldb_dn *dn = dsdb_dn->dn;
1465 const char *tstring, *usn_string, *flags_string;
1466 struct ldb_val tval;
1468 struct ldb_val usnv, local_usnv;
1469 struct ldb_val vers, flagsv;
1470 const struct ldb_val *old_addtime;
1471 uint32_t old_version;
1474 const char *dnstring;
1476 uint32_t rmd_flags = deleted?DSDB_RMD_FLAG_DELETED:0;
1478 tstring = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)nttime);
1480 return LDB_ERR_OPERATIONS_ERROR;
1482 tval = data_blob_string_const(tstring);
1484 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)seq_num);
1486 return LDB_ERR_OPERATIONS_ERROR;
1488 usnv = data_blob_string_const(usn_string);
1490 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)local_usn);
1492 return LDB_ERR_OPERATIONS_ERROR;
1494 local_usnv = data_blob_string_const(usn_string);
1496 status = GUID_to_ndr_blob(invocation_id, dn, &iid);
1497 if (!NT_STATUS_IS_OK(status)) {
1498 return LDB_ERR_OPERATIONS_ERROR;
1501 flags_string = talloc_asprintf(mem_ctx, "%u", rmd_flags);
1502 if (!flags_string) {
1503 return LDB_ERR_OPERATIONS_ERROR;
1505 flagsv = data_blob_string_const(flags_string);
1507 ret = ldb_dn_set_extended_component(dn, "RMD_FLAGS", &flagsv);
1508 if (ret != LDB_SUCCESS) return ret;
1510 /* get the ADDTIME from the original */
1511 old_addtime = ldb_dn_get_extended_component(old_dsdb_dn->dn, "RMD_ADDTIME");
1512 if (old_addtime == NULL) {
1513 old_addtime = &tval;
1515 if (dsdb_dn != old_dsdb_dn) {
1516 ret = ldb_dn_set_extended_component(dn, "RMD_ADDTIME", old_addtime);
1517 if (ret != LDB_SUCCESS) return ret;
1520 /* use our invocation id */
1521 ret = ldb_dn_set_extended_component(dn, "RMD_INVOCID", &iid);
1522 if (ret != LDB_SUCCESS) return ret;
1524 /* changetime is the current time */
1525 ret = ldb_dn_set_extended_component(dn, "RMD_CHANGETIME", &tval);
1526 if (ret != LDB_SUCCESS) return ret;
1528 /* update the USN */
1529 ret = ldb_dn_set_extended_component(dn, "RMD_ORIGINATING_USN", &usnv);
1530 if (ret != LDB_SUCCESS) return ret;
1532 ret = ldb_dn_set_extended_component(dn, "RMD_LOCAL_USN", &local_usnv);
1533 if (ret != LDB_SUCCESS) return ret;
1535 /* increase the version by 1 */
1536 status = dsdb_get_extended_dn_uint32(old_dsdb_dn->dn, &old_version, "RMD_VERSION");
1537 if (NT_STATUS_IS_OK(status) && old_version >= version) {
1538 version = old_version+1;
1540 vstring = talloc_asprintf(dn, "%lu", (unsigned long)version);
1541 vers = data_blob_string_const(vstring);
1542 ret = ldb_dn_set_extended_component(dn, "RMD_VERSION", &vers);
1543 if (ret != LDB_SUCCESS) return ret;
1545 dnstring = dsdb_dn_get_extended_linearized(mem_ctx, dsdb_dn, 1);
1546 if (dnstring == NULL) {
1547 return LDB_ERR_OPERATIONS_ERROR;
1549 *v = data_blob_string_const(dnstring);
1555 handle adding a linked attribute
1557 static int replmd_modify_la_add(struct ldb_module *module,
1558 const struct dsdb_schema *schema,
1559 struct ldb_message *msg,
1560 struct ldb_message_element *el,
1561 struct ldb_message_element *old_el,
1562 const struct dsdb_attribute *schema_attr,
1565 struct GUID *msg_guid)
1568 struct parsed_dn *dns, *old_dns;
1569 TALLOC_CTX *tmp_ctx = talloc_new(msg);
1571 struct ldb_val *new_values = NULL;
1572 unsigned int num_new_values = 0;
1573 unsigned old_num_values = old_el?old_el->num_values:0;
1574 const struct GUID *invocation_id;
1575 struct ldb_context *ldb = ldb_module_get_ctx(module);
1578 unix_to_nt_time(&now, t);
1580 ret = get_parsed_dns(module, tmp_ctx, el, &dns, schema_attr->syntax->ldap_oid);
1581 if (ret != LDB_SUCCESS) {
1582 talloc_free(tmp_ctx);
1586 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns, schema_attr->syntax->ldap_oid);
1587 if (ret != LDB_SUCCESS) {
1588 talloc_free(tmp_ctx);
1592 invocation_id = samdb_ntds_invocation_id(ldb);
1593 if (!invocation_id) {
1594 talloc_free(tmp_ctx);
1595 return LDB_ERR_OPERATIONS_ERROR;
1598 ret = replmd_check_upgrade_links(old_dns, old_num_values, old_el, invocation_id);
1599 if (ret != LDB_SUCCESS) {
1600 talloc_free(tmp_ctx);
1604 /* for each new value, see if it exists already with the same GUID */
1605 for (i=0; i<el->num_values; i++) {
1606 struct parsed_dn *p = parsed_dn_find(old_dns, old_num_values, dns[i].guid, NULL);
1608 /* this is a new linked attribute value */
1609 new_values = talloc_realloc(tmp_ctx, new_values, struct ldb_val, num_new_values+1);
1610 if (new_values == NULL) {
1611 ldb_module_oom(module);
1612 talloc_free(tmp_ctx);
1613 return LDB_ERR_OPERATIONS_ERROR;
1615 ret = replmd_build_la_val(new_values, &new_values[num_new_values], dns[i].dsdb_dn,
1616 invocation_id, seq_num, seq_num, now, 0, false);
1617 if (ret != LDB_SUCCESS) {
1618 talloc_free(tmp_ctx);
1623 /* this is only allowed if the GUID was
1624 previously deleted. */
1625 uint32_t rmd_flags = dsdb_dn_rmd_flags(p->dsdb_dn->dn);
1627 if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
1628 ldb_asprintf_errstring(ldb, "Attribute %s already exists for target GUID %s",
1629 el->name, GUID_string(tmp_ctx, p->guid));
1630 talloc_free(tmp_ctx);
1631 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
1633 ret = replmd_update_la_val(old_el->values, p->v, dns[i].dsdb_dn, p->dsdb_dn,
1634 invocation_id, seq_num, seq_num, now, 0, false);
1635 if (ret != LDB_SUCCESS) {
1636 talloc_free(tmp_ctx);
1641 ret = replmd_add_backlink(module, schema, msg_guid, dns[i].guid, true, schema_attr, true);
1642 if (ret != LDB_SUCCESS) {
1643 talloc_free(tmp_ctx);
1648 /* add the new ones on to the end of the old values, constructing a new el->values */
1649 el->values = talloc_realloc(msg->elements, old_el?old_el->values:NULL,
1651 old_num_values+num_new_values);
1652 if (el->values == NULL) {
1653 ldb_module_oom(module);
1654 return LDB_ERR_OPERATIONS_ERROR;
1657 memcpy(&el->values[old_num_values], new_values, num_new_values*sizeof(struct ldb_val));
1658 el->num_values = old_num_values + num_new_values;
1660 talloc_steal(msg->elements, el->values);
1661 talloc_steal(el->values, new_values);
1663 talloc_free(tmp_ctx);
1665 /* we now tell the backend to replace all existing values
1666 with the one we have constructed */
1667 el->flags = LDB_FLAG_MOD_REPLACE;
1674 handle deleting all active linked attributes
1676 static int replmd_modify_la_delete(struct ldb_module *module,
1677 const struct dsdb_schema *schema,
1678 struct ldb_message *msg,
1679 struct ldb_message_element *el,
1680 struct ldb_message_element *old_el,
1681 const struct dsdb_attribute *schema_attr,
1684 struct GUID *msg_guid)
1687 struct parsed_dn *dns, *old_dns;
1688 TALLOC_CTX *tmp_ctx = talloc_new(msg);
1690 const struct GUID *invocation_id;
1691 struct ldb_context *ldb = ldb_module_get_ctx(module);
1694 unix_to_nt_time(&now, t);
1696 /* check if there is nothing to delete */
1697 if ((!old_el || old_el->num_values == 0) &&
1698 el->num_values == 0) {
1702 if (!old_el || old_el->num_values == 0) {
1703 return LDB_ERR_NO_SUCH_ATTRIBUTE;
1706 ret = get_parsed_dns(module, tmp_ctx, el, &dns, schema_attr->syntax->ldap_oid);
1707 if (ret != LDB_SUCCESS) {
1708 talloc_free(tmp_ctx);
1712 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns, schema_attr->syntax->ldap_oid);
1713 if (ret != LDB_SUCCESS) {
1714 talloc_free(tmp_ctx);
1718 invocation_id = samdb_ntds_invocation_id(ldb);
1719 if (!invocation_id) {
1720 return LDB_ERR_OPERATIONS_ERROR;
1723 ret = replmd_check_upgrade_links(old_dns, old_el->num_values, old_el, invocation_id);
1724 if (ret != LDB_SUCCESS) {
1725 talloc_free(tmp_ctx);
1731 /* see if we are being asked to delete any links that
1732 don't exist or are already deleted */
1733 for (i=0; i<el->num_values; i++) {
1734 struct parsed_dn *p = &dns[i];
1735 struct parsed_dn *p2;
1738 p2 = parsed_dn_find(old_dns, old_el->num_values, p->guid, NULL);
1740 ldb_asprintf_errstring(ldb, "Attribute %s doesn't exist for target GUID %s",
1741 el->name, GUID_string(tmp_ctx, p->guid));
1742 return LDB_ERR_NO_SUCH_ATTRIBUTE;
1744 rmd_flags = dsdb_dn_rmd_flags(p2->dsdb_dn->dn);
1745 if (rmd_flags & DSDB_RMD_FLAG_DELETED) {
1746 ldb_asprintf_errstring(ldb, "Attribute %s already deleted for target GUID %s",
1747 el->name, GUID_string(tmp_ctx, p->guid));
1748 return LDB_ERR_NO_SUCH_ATTRIBUTE;
1752 /* for each new value, see if it exists already with the same GUID
1753 if it is not already deleted and matches the delete list then delete it
1755 for (i=0; i<old_el->num_values; i++) {
1756 struct parsed_dn *p = &old_dns[i];
1759 if (el->num_values && parsed_dn_find(dns, el->num_values, p->guid, NULL) == NULL) {
1763 rmd_flags = dsdb_dn_rmd_flags(p->dsdb_dn->dn);
1764 if (rmd_flags & DSDB_RMD_FLAG_DELETED) continue;
1766 ret = replmd_update_la_val(old_el->values, p->v, p->dsdb_dn, p->dsdb_dn,
1767 invocation_id, seq_num, seq_num, now, 0, true);
1768 if (ret != LDB_SUCCESS) {
1769 talloc_free(tmp_ctx);
1773 ret = replmd_add_backlink(module, schema, msg_guid, old_dns[i].guid, false, schema_attr, true);
1774 if (ret != LDB_SUCCESS) {
1775 talloc_free(tmp_ctx);
1780 el->values = talloc_steal(msg->elements, old_el->values);
1781 el->num_values = old_el->num_values;
1783 talloc_free(tmp_ctx);
1785 /* we now tell the backend to replace all existing values
1786 with the one we have constructed */
1787 el->flags = LDB_FLAG_MOD_REPLACE;
1793 handle replacing a linked attribute
1795 static int replmd_modify_la_replace(struct ldb_module *module,
1796 const struct dsdb_schema *schema,
1797 struct ldb_message *msg,
1798 struct ldb_message_element *el,
1799 struct ldb_message_element *old_el,
1800 const struct dsdb_attribute *schema_attr,
1803 struct GUID *msg_guid)
1806 struct parsed_dn *dns, *old_dns;
1807 TALLOC_CTX *tmp_ctx = talloc_new(msg);
1809 const struct GUID *invocation_id;
1810 struct ldb_context *ldb = ldb_module_get_ctx(module);
1811 struct ldb_val *new_values = NULL;
1812 unsigned int num_new_values = 0;
1813 unsigned int old_num_values = old_el?old_el->num_values:0;
1816 unix_to_nt_time(&now, t);
1818 /* check if there is nothing to replace */
1819 if ((!old_el || old_el->num_values == 0) &&
1820 el->num_values == 0) {
1824 ret = get_parsed_dns(module, tmp_ctx, el, &dns, schema_attr->syntax->ldap_oid);
1825 if (ret != LDB_SUCCESS) {
1826 talloc_free(tmp_ctx);
1830 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns, schema_attr->syntax->ldap_oid);
1831 if (ret != LDB_SUCCESS) {
1832 talloc_free(tmp_ctx);
1836 invocation_id = samdb_ntds_invocation_id(ldb);
1837 if (!invocation_id) {
1838 return LDB_ERR_OPERATIONS_ERROR;
1841 ret = replmd_check_upgrade_links(old_dns, old_num_values, old_el, invocation_id);
1842 if (ret != LDB_SUCCESS) {
1843 talloc_free(tmp_ctx);
1847 /* mark all the old ones as deleted */
1848 for (i=0; i<old_num_values; i++) {
1849 struct parsed_dn *old_p = &old_dns[i];
1850 struct parsed_dn *p;
1851 uint32_t rmd_flags = dsdb_dn_rmd_flags(old_p->dsdb_dn->dn);
1853 if (rmd_flags & DSDB_RMD_FLAG_DELETED) continue;
1855 ret = replmd_add_backlink(module, schema, msg_guid, old_dns[i].guid, false, schema_attr, false);
1856 if (ret != LDB_SUCCESS) {
1857 talloc_free(tmp_ctx);
1861 p = parsed_dn_find(dns, el->num_values, old_p->guid, NULL);
1863 /* we don't delete it if we are re-adding it */
1867 ret = replmd_update_la_val(old_el->values, old_p->v, old_p->dsdb_dn, old_p->dsdb_dn,
1868 invocation_id, seq_num, seq_num, now, 0, true);
1869 if (ret != LDB_SUCCESS) {
1870 talloc_free(tmp_ctx);
1875 /* for each new value, either update its meta-data, or add it
1878 for (i=0; i<el->num_values; i++) {
1879 struct parsed_dn *p = &dns[i], *old_p;
1882 (old_p = parsed_dn_find(old_dns,
1883 old_num_values, p->guid, NULL)) != NULL) {
1884 /* update in place */
1885 ret = replmd_update_la_val(old_el->values, old_p->v, old_p->dsdb_dn,
1886 old_p->dsdb_dn, invocation_id,
1887 seq_num, seq_num, now, 0, false);
1888 if (ret != LDB_SUCCESS) {
1889 talloc_free(tmp_ctx);
1894 new_values = talloc_realloc(tmp_ctx, new_values, struct ldb_val,
1896 if (new_values == NULL) {
1897 ldb_module_oom(module);
1898 talloc_free(tmp_ctx);
1899 return LDB_ERR_OPERATIONS_ERROR;
1901 ret = replmd_build_la_val(new_values, &new_values[num_new_values], dns[i].dsdb_dn,
1902 invocation_id, seq_num, seq_num, now, 0, false);
1903 if (ret != LDB_SUCCESS) {
1904 talloc_free(tmp_ctx);
1910 ret = replmd_add_backlink(module, schema, msg_guid, dns[i].guid, true, schema_attr, false);
1911 if (ret != LDB_SUCCESS) {
1912 talloc_free(tmp_ctx);
1917 /* add the new values to the end of old_el */
1918 if (num_new_values != 0) {
1919 el->values = talloc_realloc(msg->elements, old_el?old_el->values:NULL,
1920 struct ldb_val, old_num_values+num_new_values);
1921 if (el->values == NULL) {
1922 ldb_module_oom(module);
1923 return LDB_ERR_OPERATIONS_ERROR;
1925 memcpy(&el->values[old_num_values], &new_values[0],
1926 sizeof(struct ldb_val)*num_new_values);
1927 el->num_values = old_num_values + num_new_values;
1928 talloc_steal(msg->elements, new_values);
1930 el->values = old_el->values;
1931 el->num_values = old_el->num_values;
1932 talloc_steal(msg->elements, el->values);
1935 talloc_free(tmp_ctx);
1937 /* we now tell the backend to replace all existing values
1938 with the one we have constructed */
1939 el->flags = LDB_FLAG_MOD_REPLACE;
1946 handle linked attributes in modify requests
1948 static int replmd_modify_handle_linked_attribs(struct ldb_module *module,
1949 struct ldb_message *msg,
1950 uint64_t seq_num, time_t t)
1952 struct ldb_result *res;
1955 struct ldb_context *ldb = ldb_module_get_ctx(module);
1956 struct ldb_message *old_msg;
1958 const struct dsdb_schema *schema;
1959 struct GUID old_guid;
1962 /* there the replmd_update_rpmd code has already
1963 * checked and saw that there are no linked
1968 if (dsdb_functional_level(ldb) == DS_DOMAIN_FUNCTION_2000) {
1969 /* don't do anything special for linked attributes */
1973 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, NULL,
1974 DSDB_FLAG_NEXT_MODULE |
1975 DSDB_SEARCH_SHOW_DELETED |
1976 DSDB_SEARCH_REVEAL_INTERNALS |
1977 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT);
1978 if (ret != LDB_SUCCESS) {
1981 schema = dsdb_get_schema(ldb, res);
1983 return LDB_ERR_OPERATIONS_ERROR;
1986 old_msg = res->msgs[0];
1988 old_guid = samdb_result_guid(old_msg, "objectGUID");
1990 for (i=0; i<msg->num_elements; i++) {
1991 struct ldb_message_element *el = &msg->elements[i];
1992 struct ldb_message_element *old_el, *new_el;
1993 const struct dsdb_attribute *schema_attr
1994 = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
1996 ldb_asprintf_errstring(ldb,
1997 "attribute %s is not a valid attribute in schema", el->name);
1998 return LDB_ERR_OBJECT_CLASS_VIOLATION;
2000 if (schema_attr->linkID == 0) {
2003 if ((schema_attr->linkID & 1) == 1) {
2004 /* Odd is for the target. Illegal to modify */
2005 ldb_asprintf_errstring(ldb,
2006 "attribute %s must not be modified directly, it is a linked attribute", el->name);
2007 return LDB_ERR_UNWILLING_TO_PERFORM;
2009 old_el = ldb_msg_find_element(old_msg, el->name);
2010 switch (el->flags & LDB_FLAG_MOD_MASK) {
2011 case LDB_FLAG_MOD_REPLACE:
2012 ret = replmd_modify_la_replace(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid);
2014 case LDB_FLAG_MOD_DELETE:
2015 ret = replmd_modify_la_delete(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid);
2017 case LDB_FLAG_MOD_ADD:
2018 ret = replmd_modify_la_add(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid);
2021 ldb_asprintf_errstring(ldb,
2022 "invalid flags 0x%x for %s linked attribute",
2023 el->flags, el->name);
2024 return LDB_ERR_UNWILLING_TO_PERFORM;
2026 if (ret != LDB_SUCCESS) {
2030 ldb_msg_remove_attr(old_msg, el->name);
2032 ldb_msg_add_empty(old_msg, el->name, 0, &new_el);
2033 new_el->num_values = el->num_values;
2034 new_el->values = talloc_steal(msg->elements, el->values);
2036 /* TODO: this relises a bit too heavily on the exact
2037 behaviour of ldb_msg_find_element and
2038 ldb_msg_remove_element */
2039 old_el = ldb_msg_find_element(msg, el->name);
2041 ldb_msg_remove_element(msg, old_el);
2052 static int replmd_modify(struct ldb_module *module, struct ldb_request *req)
2054 struct ldb_context *ldb;
2055 struct replmd_replicated_request *ac;
2056 struct ldb_request *down_req;
2057 struct ldb_message *msg;
2058 time_t t = time(NULL);
2060 bool is_urgent = false;
2061 struct loadparm_context *lp_ctx;
2063 unsigned int functional_level;
2065 /* do not manipulate our control entries */
2066 if (ldb_dn_is_special(req->op.mod.message->dn)) {
2067 return ldb_next_request(module, req);
2070 ldb = ldb_module_get_ctx(module);
2071 functional_level = dsdb_functional_level(ldb);
2073 lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
2074 struct loadparm_context);
2076 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_modify\n");
2078 ac = replmd_ctx_init(module, req);
2080 return LDB_ERR_OPERATIONS_ERROR;
2083 /* we have to copy the message as the caller might have it as a const */
2084 msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
2088 return LDB_ERR_OPERATIONS_ERROR;
2091 ldb_msg_remove_attr(msg, "whenChanged");
2092 ldb_msg_remove_attr(msg, "uSNChanged");
2094 ret = replmd_update_rpmd(module, ac->schema, req, msg, &ac->seq_num, t, &is_urgent);
2095 if (ret == LDB_ERR_REFERRAL) {
2098 referral = talloc_asprintf(req,
2100 lp_dnsdomain(lp_ctx),
2101 ldb_dn_get_linearized(msg->dn));
2102 ret = ldb_module_send_referral(req, referral);
2103 return ldb_module_done(req, NULL, NULL, ret);
2106 if (ret != LDB_SUCCESS) {
2111 ret = replmd_modify_handle_linked_attribs(module, msg, ac->seq_num, t);
2112 if (ret != LDB_SUCCESS) {
2118 * - replace the old object with the newly constructed one
2121 ac->is_urgent = is_urgent;
2123 ret = ldb_build_mod_req(&down_req, ldb, ac,
2126 ac, replmd_op_callback,
2128 if (ret != LDB_SUCCESS) {
2133 /* If we are in functional level 2000, then
2134 * replmd_modify_handle_linked_attribs will have done
2136 if (functional_level == DS_DOMAIN_FUNCTION_2000) {
2137 ret = ldb_request_add_control(down_req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
2138 if (ret != LDB_SUCCESS) {
2144 talloc_steal(down_req, msg);
2146 /* we only change whenChanged and uSNChanged if the seq_num
2148 if (ac->seq_num != 0) {
2149 if (add_time_element(msg, "whenChanged", t) != LDB_SUCCESS) {
2154 if (add_uint64_element(msg, "uSNChanged", ac->seq_num) != LDB_SUCCESS) {
2160 /* go on with the call chain */
2161 return ldb_next_request(module, down_req);
2164 static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares);
2167 handle a rename request
2169 On a rename we need to do an extra ldb_modify which sets the
2170 whenChanged and uSNChanged attributes. We do this in a callback after the success.
2172 static int replmd_rename(struct ldb_module *module, struct ldb_request *req)
2174 struct ldb_context *ldb;
2175 struct replmd_replicated_request *ac;
2177 struct ldb_request *down_req;
2179 /* do not manipulate our control entries */
2180 if (ldb_dn_is_special(req->op.mod.message->dn)) {
2181 return ldb_next_request(module, req);
2184 ldb = ldb_module_get_ctx(module);
2186 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_rename\n");
2188 ac = replmd_ctx_init(module, req);
2190 return LDB_ERR_OPERATIONS_ERROR;
2192 ret = ldb_build_rename_req(&down_req, ldb, ac,
2193 ac->req->op.rename.olddn,
2194 ac->req->op.rename.newdn,
2196 ac, replmd_rename_callback,
2199 if (ret != LDB_SUCCESS) {
2204 /* go on with the call chain */
2205 return ldb_next_request(module, down_req);
2208 /* After the rename is compleated, update the whenchanged etc */
2209 static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares)
2211 struct ldb_context *ldb;
2212 struct replmd_replicated_request *ac;
2213 struct ldb_request *down_req;
2214 struct ldb_message *msg;
2215 time_t t = time(NULL);
2218 ac = talloc_get_type(req->context, struct replmd_replicated_request);
2219 ldb = ldb_module_get_ctx(ac->module);
2221 if (ares->error != LDB_SUCCESS) {
2222 return ldb_module_done(ac->req, ares->controls,
2223 ares->response, ares->error);
2226 if (ares->type != LDB_REPLY_DONE) {
2227 ldb_set_errstring(ldb,
2228 "invalid ldb_reply_type in callback");
2230 return ldb_module_done(ac->req, NULL, NULL,
2231 LDB_ERR_OPERATIONS_ERROR);
2234 /* Get a sequence number from the backend */
2235 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ac->seq_num);
2236 if (ret != LDB_SUCCESS) {
2241 * - replace the old object with the newly constructed one
2244 msg = ldb_msg_new(ac);
2247 return LDB_ERR_OPERATIONS_ERROR;
2250 msg->dn = ac->req->op.rename.newdn;
2252 ret = ldb_build_mod_req(&down_req, ldb, ac,
2255 ac, replmd_op_callback,
2258 if (ret != LDB_SUCCESS) {
2262 talloc_steal(down_req, msg);
2264 if (add_time_element(msg, "whenChanged", t) != LDB_SUCCESS) {
2269 if (add_uint64_element(msg, "uSNChanged", ac->seq_num) != LDB_SUCCESS) {
2274 /* go on with the call chain - do the modify after the rename */
2275 return ldb_next_request(ac->module, down_req);
2279 remove links from objects that point at this object when an object
2282 static int replmd_delete_remove_link(struct ldb_module *module,
2283 const struct dsdb_schema *schema,
2285 struct ldb_message_element *el,
2286 const struct dsdb_attribute *sa)
2289 TALLOC_CTX *tmp_ctx = talloc_new(module);
2290 struct ldb_context *ldb = ldb_module_get_ctx(module);
2292 for (i=0; i<el->num_values; i++) {
2293 struct dsdb_dn *dsdb_dn;
2297 struct ldb_message *msg;
2298 const struct dsdb_attribute *target_attr;
2299 struct ldb_message_element *el2;
2300 struct ldb_val dn_val;
2302 if (dsdb_dn_is_deleted_val(&el->values[i])) {
2306 dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i], sa->syntax->ldap_oid);
2308 talloc_free(tmp_ctx);
2309 return LDB_ERR_OPERATIONS_ERROR;
2312 status = dsdb_get_extended_dn_guid(dsdb_dn->dn, &guid2, "GUID");
2313 if (!NT_STATUS_IS_OK(status)) {
2314 talloc_free(tmp_ctx);
2315 return LDB_ERR_OPERATIONS_ERROR;
2318 /* remove the link */
2319 msg = ldb_msg_new(tmp_ctx);
2321 ldb_module_oom(module);
2322 talloc_free(tmp_ctx);
2323 return LDB_ERR_OPERATIONS_ERROR;
2327 msg->dn = dsdb_dn->dn;
2329 target_attr = dsdb_attribute_by_linkID(schema, sa->linkID ^ 1);
2330 if (target_attr == NULL) {
2334 ret = ldb_msg_add_empty(msg, target_attr->lDAPDisplayName, LDB_FLAG_MOD_DELETE, &el2);
2335 if (ret != LDB_SUCCESS) {
2336 ldb_module_oom(module);
2337 talloc_free(tmp_ctx);
2338 return LDB_ERR_OPERATIONS_ERROR;
2340 dn_val = data_blob_string_const(ldb_dn_get_linearized(dn));
2341 el2->values = &dn_val;
2342 el2->num_values = 1;
2344 ret = dsdb_module_modify(module, msg, DSDB_FLAG_OWN_MODULE);
2345 if (ret != LDB_SUCCESS) {
2346 talloc_free(tmp_ctx);
2350 talloc_free(tmp_ctx);
2356 handle update of replication meta data for deletion of objects
2358 This also handles the mapping of delete to a rename operation
2359 to allow deletes to be replicated.
2361 static int replmd_delete(struct ldb_module *module, struct ldb_request *req)
2363 int ret = LDB_ERR_OTHER;
2365 struct ldb_dn *old_dn, *new_dn;
2366 const char *rdn_name;
2367 const struct ldb_val *rdn_value, *new_rdn_value;
2369 struct ldb_context *ldb = ldb_module_get_ctx(module);
2370 const struct dsdb_schema *schema;
2371 struct ldb_message *msg, *old_msg;
2372 struct ldb_message_element *el;
2373 TALLOC_CTX *tmp_ctx;
2374 struct ldb_result *res, *parent_res;
2375 const char *preserved_attrs[] = {
2376 /* yes, this really is a hard coded list. See MS-ADTS
2377 section 3.1.1.5.5.1.1 */
2378 "nTSecurityDescriptor", "attributeID", "attributeSyntax", "dNReferenceUpdate", "dNSHostName",
2379 "flatName", "governsID", "groupType", "instanceType", "lDAPDisplayName", "legacyExchangeDN",
2380 "isDeleted", "isRecycled", "lastKnownParent", "msDS-LastKnownRDN", "mS-DS-CreatorSID",
2381 "mSMQOwnerID", "nCName", "objectClass", "distinguishedName", "objectGUID", "objectSid",
2382 "oMSyntax", "proxiedObjectName", "name", "replPropertyMetaData", "sAMAccountName",
2383 "securityIdentifier", "sIDHistory", "subClassOf", "systemFlags", "trustPartner", "trustDirection",
2384 "trustType", "trustAttributes", "userAccountControl", "uSNChanged", "uSNCreated", "whenCreated",
2385 "whenChanged", NULL};
2386 unsigned int i, el_count = 0;
2387 enum deletion_state { OBJECT_NOT_DELETED=1, OBJECT_DELETED=2, OBJECT_RECYCLED=3,
2388 OBJECT_TOMBSTONE=4, OBJECT_REMOVED=5 };
2389 enum deletion_state deletion_state, next_deletion_state;
2392 if (ldb_dn_is_special(req->op.del.dn)) {
2393 return ldb_next_request(module, req);
2396 tmp_ctx = talloc_new(ldb);
2399 return LDB_ERR_OPERATIONS_ERROR;
2402 schema = dsdb_get_schema(ldb, tmp_ctx);
2404 return LDB_ERR_OPERATIONS_ERROR;
2407 old_dn = ldb_dn_copy(tmp_ctx, req->op.del.dn);
2409 /* we need the complete msg off disk, so we can work out which
2410 attributes need to be removed */
2411 ret = dsdb_module_search_dn(module, tmp_ctx, &res, old_dn, NULL,
2412 DSDB_FLAG_NEXT_MODULE |
2413 DSDB_SEARCH_SHOW_DELETED |
2414 DSDB_SEARCH_REVEAL_INTERNALS |
2415 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT);
2416 if (ret != LDB_SUCCESS) {
2417 talloc_free(tmp_ctx);
2420 old_msg = res->msgs[0];
2423 ret = dsdb_recyclebin_enabled(module, &enabled);
2424 if (ret != LDB_SUCCESS) {
2425 talloc_free(tmp_ctx);
2429 if (ldb_msg_check_string_attribute(old_msg, "isDeleted", "TRUE")) {
2431 deletion_state = OBJECT_TOMBSTONE;
2432 next_deletion_state = OBJECT_REMOVED;
2433 } else if (ldb_msg_check_string_attribute(old_msg, "isRecycled", "TRUE")) {
2434 deletion_state = OBJECT_RECYCLED;
2435 next_deletion_state = OBJECT_REMOVED;
2437 deletion_state = OBJECT_DELETED;
2438 next_deletion_state = OBJECT_RECYCLED;
2441 deletion_state = OBJECT_NOT_DELETED;
2443 next_deletion_state = OBJECT_DELETED;
2445 next_deletion_state = OBJECT_TOMBSTONE;
2449 if (next_deletion_state == OBJECT_REMOVED) {
2450 struct auth_session_info *session_info =
2451 (struct auth_session_info *)ldb_get_opaque(ldb, "sessionInfo");
2452 if (security_session_user_level(session_info, NULL) != SECURITY_SYSTEM) {
2453 ldb_asprintf_errstring(ldb, "Refusing to delete deleted object %s",
2454 ldb_dn_get_linearized(old_msg->dn));
2455 return LDB_ERR_UNWILLING_TO_PERFORM;
2458 /* it is already deleted - really remove it this time */
2459 talloc_free(tmp_ctx);
2460 return ldb_next_request(module, req);
2463 rdn_name = ldb_dn_get_rdn_name(old_dn);
2464 rdn_value = ldb_dn_get_rdn_val(old_dn);
2466 msg = ldb_msg_new(tmp_ctx);
2468 ldb_module_oom(module);
2469 talloc_free(tmp_ctx);
2470 return LDB_ERR_OPERATIONS_ERROR;
2475 if (deletion_state == OBJECT_NOT_DELETED){
2476 /* work out where we will be renaming this object to */
2477 ret = dsdb_get_deleted_objects_dn(ldb, tmp_ctx, old_dn, &new_dn);
2478 if (ret != LDB_SUCCESS) {
2479 /* this is probably an attempted delete on a partition
2480 * that doesn't allow delete operations, such as the
2481 * schema partition */
2482 ldb_asprintf_errstring(ldb, "No Deleted Objects container for DN %s",
2483 ldb_dn_get_linearized(old_dn));
2484 talloc_free(tmp_ctx);
2485 return LDB_ERR_UNWILLING_TO_PERFORM;
2488 /* get the objects GUID from the search we just did */
2489 guid = samdb_result_guid(old_msg, "objectGUID");
2491 /* Add a formatted child */
2492 retb = ldb_dn_add_child_fmt(new_dn, "%s=%s\\0ADEL:%s",
2495 GUID_string(tmp_ctx, &guid));
2497 DEBUG(0,(__location__ ": Unable to add a formatted child to dn: %s",
2498 ldb_dn_get_linearized(new_dn)));
2499 talloc_free(tmp_ctx);
2500 return LDB_ERR_OPERATIONS_ERROR;
2503 ret = ldb_msg_add_string(msg, "isDeleted", "TRUE");
2504 if (ret != LDB_SUCCESS) {
2505 DEBUG(0,(__location__ ": Failed to add isDeleted string to the msg\n"));
2506 ldb_module_oom(module);
2507 talloc_free(tmp_ctx);
2510 msg->elements[el_count++].flags = LDB_FLAG_MOD_ADD;
2514 now we need to modify the object in the following ways:
2516 - add isDeleted=TRUE
2517 - update rDN and name, with new rDN
2518 - remove linked attributes
2519 - remove objectCategory and sAMAccountType
2520 - remove attribs not on the preserved list
2521 - preserved if in above list, or is rDN
2522 - remove all linked attribs from this object
2523 - remove all links from other objects to this object
2524 - add lastKnownParent
2525 - update replPropertyMetaData?
2527 see MS-ADTS "Tombstone Requirements" section 3.1.1.5.5.1.1
2530 /* we need the storage form of the parent GUID */
2531 ret = dsdb_module_search_dn(module, tmp_ctx, &parent_res,
2532 ldb_dn_get_parent(tmp_ctx, old_dn), NULL,
2533 DSDB_FLAG_NEXT_MODULE |
2534 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
2535 DSDB_SEARCH_REVEAL_INTERNALS|
2536 DSDB_SEARCH_SHOW_DELETED);
2537 if (ret != LDB_SUCCESS) {
2538 talloc_free(tmp_ctx);
2542 if (deletion_state == OBJECT_NOT_DELETED){
2543 ret = ldb_msg_add_steal_string(msg, "lastKnownParent",
2544 ldb_dn_get_extended_linearized(tmp_ctx, parent_res->msgs[0]->dn, 1));
2545 if (ret != LDB_SUCCESS) {
2546 DEBUG(0,(__location__ ": Failed to add lastKnownParent string to the msg\n"));
2547 ldb_module_oom(module);
2548 talloc_free(tmp_ctx);
2551 msg->elements[el_count++].flags = LDB_FLAG_MOD_ADD;
2554 switch (next_deletion_state){
2556 case OBJECT_DELETED:
2558 ret = ldb_msg_add_value(msg, "msDS-LastKnownRDN", rdn_value, NULL);
2559 if (ret != LDB_SUCCESS) {
2560 DEBUG(0,(__location__ ": Failed to add msDS-LastKnownRDN string to the msg\n"));
2561 ldb_module_oom(module);
2562 talloc_free(tmp_ctx);
2565 msg->elements[el_count++].flags = LDB_FLAG_MOD_ADD;
2567 ret = ldb_msg_add_empty(msg, "objectCategory", LDB_FLAG_MOD_DELETE, NULL);
2568 if (ret != LDB_SUCCESS) {
2569 talloc_free(tmp_ctx);
2570 ldb_module_oom(module);
2574 ret = ldb_msg_add_empty(msg, "sAMAccountType", LDB_FLAG_MOD_DELETE, NULL);
2575 if (ret != LDB_SUCCESS) {
2576 talloc_free(tmp_ctx);
2577 ldb_module_oom(module);
2583 case OBJECT_RECYCLED:
2584 case OBJECT_TOMBSTONE:
2586 /* we also mark it as recycled, meaning this object can't be
2587 recovered (we are stripping its attributes) */
2588 if (dsdb_functional_level(ldb) >= DS_DOMAIN_FUNCTION_2008_R2) {
2589 ret = ldb_msg_add_string(msg, "isRecycled", "TRUE");
2590 if (ret != LDB_SUCCESS) {
2591 DEBUG(0,(__location__ ": Failed to add isRecycled string to the msg\n"));
2592 ldb_module_oom(module);
2593 talloc_free(tmp_ctx);
2596 msg->elements[el_count++].flags = LDB_FLAG_MOD_ADD;
2599 /* work out which of the old attributes we will be removing */
2600 for (i=0; i<old_msg->num_elements; i++) {
2601 const struct dsdb_attribute *sa;
2602 el = &old_msg->elements[i];
2603 sa = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
2605 talloc_free(tmp_ctx);
2606 return LDB_ERR_OPERATIONS_ERROR;
2608 if (ldb_attr_cmp(el->name, rdn_name) == 0) {
2609 /* don't remove the rDN */
2612 if (sa->linkID && sa->linkID & 1) {
2613 ret = replmd_delete_remove_link(module, schema, old_dn, el, sa);
2614 if (ret != LDB_SUCCESS) {
2615 talloc_free(tmp_ctx);
2616 return LDB_ERR_OPERATIONS_ERROR;
2620 if (!sa->linkID && ldb_attr_in_list(preserved_attrs, el->name)) {
2623 ret = ldb_msg_add_empty(msg, el->name, LDB_FLAG_MOD_DELETE, &el);
2624 if (ret != LDB_SUCCESS) {
2625 talloc_free(tmp_ctx);
2626 ldb_module_oom(module);
2636 if (deletion_state == OBJECT_NOT_DELETED) {
2637 /* work out what the new rdn value is, for updating the
2638 rDN and name fields */
2639 new_rdn_value = ldb_dn_get_rdn_val(new_dn);
2641 ret = ldb_msg_add_value(msg, strlower_talloc(tmp_ctx, rdn_name), new_rdn_value, &el);
2642 if (ret != LDB_SUCCESS) {
2643 talloc_free(tmp_ctx);
2646 el->flags = LDB_FLAG_MOD_REPLACE;
2648 el = ldb_msg_find_element(old_msg, "name");
2650 ret = ldb_msg_add_value(msg, "name", new_rdn_value, &el);
2651 if (ret != LDB_SUCCESS) {
2652 talloc_free(tmp_ctx);
2655 el->flags = LDB_FLAG_MOD_REPLACE;
2659 ret = dsdb_module_modify(module, msg, DSDB_FLAG_OWN_MODULE);
2660 if (ret != LDB_SUCCESS) {
2661 ldb_asprintf_errstring(ldb, "replmd_delete: Failed to modify object %s in delete - %s",
2662 ldb_dn_get_linearized(old_dn), ldb_errstring(ldb));
2663 talloc_free(tmp_ctx);
2667 if (deletion_state == OBJECT_NOT_DELETED) {
2668 /* now rename onto the new DN */
2669 ret = dsdb_module_rename(module, old_dn, new_dn, DSDB_FLAG_NEXT_MODULE);
2670 if (ret != LDB_SUCCESS){
2671 DEBUG(0,(__location__ ": Failed to rename object from '%s' to '%s' - %s\n",
2672 ldb_dn_get_linearized(old_dn),
2673 ldb_dn_get_linearized(new_dn),
2674 ldb_errstring(ldb)));
2675 talloc_free(tmp_ctx);
2680 talloc_free(tmp_ctx);
2682 return ldb_module_done(req, NULL, NULL, LDB_SUCCESS);
2687 static int replmd_replicated_request_error(struct replmd_replicated_request *ar, int ret)
2692 static int replmd_replicated_request_werror(struct replmd_replicated_request *ar, WERROR status)
2694 int ret = LDB_ERR_OTHER;
2695 /* TODO: do some error mapping */
2699 static int replmd_replicated_apply_add(struct replmd_replicated_request *ar)
2701 struct ldb_context *ldb;
2702 struct ldb_request *change_req;
2703 enum ndr_err_code ndr_err;
2704 struct ldb_message *msg;
2705 struct replPropertyMetaDataBlob *md;
2706 struct ldb_val md_value;
2711 * TODO: check if the parent object exist
2715 * TODO: handle the conflict case where an object with the
2719 ldb = ldb_module_get_ctx(ar->module);
2720 msg = ar->objs->objects[ar->index_current].msg;
2721 md = ar->objs->objects[ar->index_current].meta_data;
2723 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
2724 if (ret != LDB_SUCCESS) {
2725 return replmd_replicated_request_error(ar, ret);
2728 ret = ldb_msg_add_value(msg, "objectGUID", &ar->objs->objects[ar->index_current].guid_value, NULL);
2729 if (ret != LDB_SUCCESS) {
2730 return replmd_replicated_request_error(ar, ret);
2733 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
2734 if (ret != LDB_SUCCESS) {
2735 return replmd_replicated_request_error(ar, ret);
2738 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", ar->seq_num);
2739 if (ret != LDB_SUCCESS) {
2740 return replmd_replicated_request_error(ar, ret);
2743 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
2744 if (ret != LDB_SUCCESS) {
2745 return replmd_replicated_request_error(ar, ret);
2748 /* remove any message elements that have zero values */
2749 for (i=0; i<msg->num_elements; i++) {
2750 struct ldb_message_element *el = &msg->elements[i];
2752 if (el->num_values == 0) {
2753 DEBUG(4,(__location__ ": Removing attribute %s with num_values==0\n",
2755 memmove(el, el+1, sizeof(*el)*(msg->num_elements - (i+1)));
2756 msg->num_elements--;
2763 * the meta data array is already sorted by the caller
2765 for (i=0; i < md->ctr.ctr1.count; i++) {
2766 md->ctr.ctr1.array[i].local_usn = ar->seq_num;
2768 ndr_err = ndr_push_struct_blob(&md_value, msg, md,
2769 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
2770 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
2771 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
2772 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
2774 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &md_value, NULL);
2775 if (ret != LDB_SUCCESS) {
2776 return replmd_replicated_request_error(ar, ret);
2779 replmd_ldb_message_sort(msg, ar->schema);
2782 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_ADD, msg);
2783 DEBUG(4, ("DRS replication add message:\n%s\n", s));
2787 ret = ldb_build_add_req(&change_req,
2795 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
2797 return ldb_next_request(ar->module, change_req);
2801 return true if an update is newer than an existing entry
2802 see section 5.11 of MS-ADTS
2804 static bool replmd_update_is_newer(const struct GUID *current_invocation_id,
2805 const struct GUID *update_invocation_id,
2806 uint32_t current_version,
2807 uint32_t update_version,
2808 NTTIME current_change_time,
2809 NTTIME update_change_time)
2811 if (update_version != current_version) {
2812 return update_version > current_version;
2814 if (update_change_time > current_change_time) {
2817 if (update_change_time == current_change_time) {
2818 return GUID_compare(update_invocation_id, current_invocation_id) > 0;
2823 static bool replmd_replPropertyMetaData1_is_newer(struct replPropertyMetaData1 *cur_m,
2824 struct replPropertyMetaData1 *new_m)
2826 return replmd_update_is_newer(&cur_m->originating_invocation_id,
2827 &new_m->originating_invocation_id,
2830 cur_m->originating_change_time,
2831 new_m->originating_change_time);
2834 static struct replPropertyMetaData1 *
2835 replmd_replPropertyMetaData1_find_attid(struct replPropertyMetaDataBlob *md_blob,
2836 enum drsuapi_DsAttributeId attid)
2839 struct replPropertyMetaDataCtr1 *rpmd_ctr = &md_blob->ctr.ctr1;
2841 for (i = 0; i < rpmd_ctr->count; i++) {
2842 if (rpmd_ctr->array[i].attid == attid) {
2843 return &rpmd_ctr->array[i];
2849 static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar)
2851 struct ldb_context *ldb;
2852 struct ldb_request *change_req;
2853 enum ndr_err_code ndr_err;
2854 struct ldb_message *msg;
2855 struct replPropertyMetaDataBlob *rmd;
2856 struct replPropertyMetaDataBlob omd;
2857 const struct ldb_val *omd_value;
2858 struct replPropertyMetaDataBlob nmd;
2859 struct ldb_val nmd_value;
2860 struct replPropertyMetaData1 *md_remote;
2861 struct replPropertyMetaData1 *md_local;
2864 unsigned int removed_attrs = 0;
2867 ldb = ldb_module_get_ctx(ar->module);
2868 msg = ar->objs->objects[ar->index_current].msg;
2869 rmd = ar->objs->objects[ar->index_current].meta_data;
2873 /* find existing meta data */
2874 omd_value = ldb_msg_find_ldb_val(ar->search_msg, "replPropertyMetaData");
2876 ndr_err = ndr_pull_struct_blob(omd_value, ar, &omd,
2877 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
2878 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
2879 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
2880 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
2883 if (omd.version != 1) {
2884 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
2888 /* check if remote 'name' has change,
2889 * which indicates a rename operation */
2890 md_remote = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTRIBUTE_name);
2892 md_local = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTRIBUTE_name);
2893 SMB_ASSERT(md_local);
2894 if (replmd_replPropertyMetaData1_is_newer(md_local, md_remote)) {
2895 SMB_ASSERT(ldb_dn_compare(msg->dn, ar->search_msg->dn) != 0);
2896 /* TODO: Find appropriate local name (dn) for the object
2897 * and modify msg->dn appropriately */
2899 DEBUG(4,("replmd_replicated_request rename %s => %s\n",
2900 ldb_dn_get_linearized(ar->search_msg->dn),
2901 ldb_dn_get_linearized(msg->dn)));
2902 /* pass rename to the next module
2903 * so it doesn't appear as an originating update */
2904 ret = dsdb_module_rename(ar->module,
2905 ar->search_msg->dn, msg->dn,
2906 DSDB_FLAG_NEXT_MODULE);
2907 if (ret != LDB_SUCCESS) {
2908 ldb_debug(ldb, LDB_DEBUG_FATAL,
2909 "replmd_replicated_request rename %s => %s failed - %s\n",
2910 ldb_dn_get_linearized(ar->search_msg->dn),
2911 ldb_dn_get_linearized(msg->dn),
2912 ldb_errstring(ldb));
2913 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
2920 nmd.ctr.ctr1.count = omd.ctr.ctr1.count + rmd->ctr.ctr1.count;
2921 nmd.ctr.ctr1.array = talloc_array(ar,
2922 struct replPropertyMetaData1,
2923 nmd.ctr.ctr1.count);
2924 if (!nmd.ctr.ctr1.array) return replmd_replicated_request_werror(ar, WERR_NOMEM);
2926 /* first copy the old meta data */
2927 for (i=0; i < omd.ctr.ctr1.count; i++) {
2928 nmd.ctr.ctr1.array[ni] = omd.ctr.ctr1.array[i];
2932 /* now merge in the new meta data */
2933 for (i=0; i < rmd->ctr.ctr1.count; i++) {
2936 for (j=0; j < ni; j++) {
2939 if (rmd->ctr.ctr1.array[i].attid != nmd.ctr.ctr1.array[j].attid) {
2943 cmp = replmd_replPropertyMetaData1_is_newer(&nmd.ctr.ctr1.array[j],
2944 &rmd->ctr.ctr1.array[i]);
2946 /* replace the entry */
2947 nmd.ctr.ctr1.array[j] = rmd->ctr.ctr1.array[i];
2952 if (rmd->ctr.ctr1.array[i].attid != DRSUAPI_ATTRIBUTE_instanceType) {
2953 DEBUG(3,("Discarding older DRS attribute update to %s on %s from %s\n",
2954 msg->elements[i-removed_attrs].name,
2955 ldb_dn_get_linearized(msg->dn),
2956 GUID_string(ar, &rmd->ctr.ctr1.array[i].originating_invocation_id)));
2959 /* we don't want to apply this change so remove the attribute */
2960 ldb_msg_remove_element(msg, &msg->elements[i-removed_attrs]);
2967 if (found) continue;
2969 nmd.ctr.ctr1.array[ni] = rmd->ctr.ctr1.array[i];
2974 * finally correct the size of the meta_data array
2976 nmd.ctr.ctr1.count = ni;
2979 * the rdn attribute (the alias for the name attribute),
2980 * 'cn' for most objects is the last entry in the meta data array
2983 * sort the new meta data array
2985 ret = replmd_replPropertyMetaDataCtr1_sort(&nmd.ctr.ctr1, ar->schema, msg->dn);
2986 if (ret != LDB_SUCCESS) {
2991 * check if some replicated attributes left, otherwise skip the ldb_modify() call
2993 if (msg->num_elements == 0) {
2994 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: skip replace\n",
2997 ar->index_current++;
2998 return replmd_replicated_apply_next(ar);
3001 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: replace %u attributes\n",
3002 ar->index_current, msg->num_elements);
3004 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
3005 if (ret != LDB_SUCCESS) {
3006 return replmd_replicated_request_error(ar, ret);
3009 for (i=0; i<ni; i++) {
3010 nmd.ctr.ctr1.array[i].local_usn = ar->seq_num;
3013 /* create the meta data value */
3014 ndr_err = ndr_push_struct_blob(&nmd_value, msg, &nmd,
3015 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
3016 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
3017 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
3018 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
3022 * when we know that we'll modify the record, add the whenChanged, uSNChanged
3023 * and replPopertyMetaData attributes
3025 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
3026 if (ret != LDB_SUCCESS) {
3027 return replmd_replicated_request_error(ar, ret);
3029 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
3030 if (ret != LDB_SUCCESS) {
3031 return replmd_replicated_request_error(ar, ret);
3033 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
3034 if (ret != LDB_SUCCESS) {
3035 return replmd_replicated_request_error(ar, ret);
3038 replmd_ldb_message_sort(msg, ar->schema);
3040 /* we want to replace the old values */
3041 for (i=0; i < msg->num_elements; i++) {
3042 msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
3046 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_MODIFY, msg);
3047 DEBUG(4, ("DRS replication modify message:\n%s\n", s));
3051 ret = ldb_build_mod_req(&change_req,
3059 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
3061 return ldb_next_request(ar->module, change_req);
3064 static int replmd_replicated_apply_search_callback(struct ldb_request *req,
3065 struct ldb_reply *ares)
3067 struct replmd_replicated_request *ar = talloc_get_type(req->context,
3068 struct replmd_replicated_request);
3072 return ldb_module_done(ar->req, NULL, NULL,
3073 LDB_ERR_OPERATIONS_ERROR);
3075 if (ares->error != LDB_SUCCESS &&
3076 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
3077 return ldb_module_done(ar->req, ares->controls,
3078 ares->response, ares->error);
3081 switch (ares->type) {
3082 case LDB_REPLY_ENTRY:
3083 ar->search_msg = talloc_steal(ar, ares->message);
3086 case LDB_REPLY_REFERRAL:
3087 /* we ignore referrals */
3090 case LDB_REPLY_DONE:
3091 if (ar->search_msg != NULL) {
3092 ret = replmd_replicated_apply_merge(ar);
3094 ret = replmd_replicated_apply_add(ar);
3096 if (ret != LDB_SUCCESS) {
3097 return ldb_module_done(ar->req, NULL, NULL, ret);
3105 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar);
3107 static int replmd_replicated_apply_next(struct replmd_replicated_request *ar)
3109 struct ldb_context *ldb;
3113 struct ldb_request *search_req;
3114 struct ldb_search_options_control *options;
3116 if (ar->index_current >= ar->objs->num_objects) {
3117 /* done with it, go to next stage */
3118 return replmd_replicated_uptodate_vector(ar);
3121 ldb = ldb_module_get_ctx(ar->module);
3122 ar->search_msg = NULL;
3124 tmp_str = ldb_binary_encode(ar, ar->objs->objects[ar->index_current].guid_value);
3125 if (!tmp_str) return replmd_replicated_request_werror(ar, WERR_NOMEM);
3127 filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
3128 if (!filter) return replmd_replicated_request_werror(ar, WERR_NOMEM);
3129 talloc_free(tmp_str);
3131 ret = ldb_build_search_req(&search_req,
3140 replmd_replicated_apply_search_callback,
3143 ret = ldb_request_add_control(search_req, LDB_CONTROL_SHOW_DELETED_OID, true, NULL);
3144 if (ret != LDB_SUCCESS) {
3148 /* we need to cope with cross-partition links, so search for
3149 the GUID over all partitions */
3150 options = talloc(search_req, struct ldb_search_options_control);
3151 if (options == NULL) {
3152 DEBUG(0, (__location__ ": out of memory\n"));
3153 return LDB_ERR_OPERATIONS_ERROR;
3155 options->search_options = LDB_SEARCH_OPTION_PHANTOM_ROOT;
3157 ret = ldb_request_add_control(search_req,
3158 LDB_CONTROL_SEARCH_OPTIONS_OID,
3160 if (ret != LDB_SUCCESS) {
3164 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
3166 return ldb_next_request(ar->module, search_req);
3169 static int replmd_replicated_uptodate_modify_callback(struct ldb_request *req,
3170 struct ldb_reply *ares)
3172 struct ldb_context *ldb;
3173 struct replmd_replicated_request *ar = talloc_get_type(req->context,
3174 struct replmd_replicated_request);
3175 ldb = ldb_module_get_ctx(ar->module);
3178 return ldb_module_done(ar->req, NULL, NULL,
3179 LDB_ERR_OPERATIONS_ERROR);
3181 if (ares->error != LDB_SUCCESS) {
3182 return ldb_module_done(ar->req, ares->controls,
3183 ares->response, ares->error);
3186 if (ares->type != LDB_REPLY_DONE) {
3187 ldb_set_errstring(ldb, "Invalid reply type\n!");
3188 return ldb_module_done(ar->req, NULL, NULL,
3189 LDB_ERR_OPERATIONS_ERROR);
3194 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
3197 static int replmd_replicated_uptodate_modify(struct replmd_replicated_request *ar)
3199 struct ldb_context *ldb;
3200 struct ldb_request *change_req;
3201 enum ndr_err_code ndr_err;
3202 struct ldb_message *msg;
3203 struct replUpToDateVectorBlob ouv;
3204 const struct ldb_val *ouv_value;
3205 const struct drsuapi_DsReplicaCursor2CtrEx *ruv;
3206 struct replUpToDateVectorBlob nuv;
3207 struct ldb_val nuv_value;
3208 struct ldb_message_element *nuv_el = NULL;
3209 const struct GUID *our_invocation_id;
3210 struct ldb_message_element *orf_el = NULL;
3211 struct repsFromToBlob nrf;
3212 struct ldb_val *nrf_value = NULL;
3213 struct ldb_message_element *nrf_el = NULL;
3217 time_t t = time(NULL);
3221 ldb = ldb_module_get_ctx(ar->module);
3222 ruv = ar->objs->uptodateness_vector;
3228 unix_to_nt_time(&now, t);
3231 * first create the new replUpToDateVector
3233 ouv_value = ldb_msg_find_ldb_val(ar->search_msg, "replUpToDateVector");
3235 ndr_err = ndr_pull_struct_blob(ouv_value, ar, &ouv,
3236 (ndr_pull_flags_fn_t)ndr_pull_replUpToDateVectorBlob);
3237 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
3238 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
3239 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
3242 if (ouv.version != 2) {
3243 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
3248 * the new uptodateness vector will at least
3249 * contain 1 entry, one for the source_dsa
3251 * plus optional values from our old vector and the one from the source_dsa
3253 nuv.ctr.ctr2.count = 1 + ouv.ctr.ctr2.count;
3254 if (ruv) nuv.ctr.ctr2.count += ruv->count;
3255 nuv.ctr.ctr2.cursors = talloc_array(ar,
3256 struct drsuapi_DsReplicaCursor2,
3257 nuv.ctr.ctr2.count);
3258 if (!nuv.ctr.ctr2.cursors) return replmd_replicated_request_werror(ar, WERR_NOMEM);
3260 /* first copy the old vector */
3261 for (i=0; i < ouv.ctr.ctr2.count; i++) {
3262 nuv.ctr.ctr2.cursors[ni] = ouv.ctr.ctr2.cursors[i];
3266 /* get our invocation_id if we have one already attached to the ldb */
3267 our_invocation_id = samdb_ntds_invocation_id(ldb);
3269 /* merge in the source_dsa vector is available */
3270 for (i=0; (ruv && i < ruv->count); i++) {
3273 if (our_invocation_id &&
3274 GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
3275 our_invocation_id)) {
3279 for (j=0; j < ni; j++) {
3280 if (!GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
3281 &nuv.ctr.ctr2.cursors[j].source_dsa_invocation_id)) {
3288 * we update only the highest_usn and not the latest_sync_success time,
3289 * because the last success stands for direct replication
3291 if (ruv->cursors[i].highest_usn > nuv.ctr.ctr2.cursors[j].highest_usn) {
3292 nuv.ctr.ctr2.cursors[j].highest_usn = ruv->cursors[i].highest_usn;
3297 if (found) continue;
3299 /* if it's not there yet, add it */
3300 nuv.ctr.ctr2.cursors[ni] = ruv->cursors[i];
3305 * merge in the current highwatermark for the source_dsa
3308 for (j=0; j < ni; j++) {
3309 if (!GUID_equal(&ar->objs->source_dsa->source_dsa_invocation_id,
3310 &nuv.ctr.ctr2.cursors[j].source_dsa_invocation_id)) {
3317 * here we update the highest_usn and last_sync_success time
3318 * because we're directly replicating from the source_dsa
3320 * and use the tmp_highest_usn because this is what we have just applied
3323 nuv.ctr.ctr2.cursors[j].highest_usn = ar->objs->source_dsa->highwatermark.tmp_highest_usn;
3324 nuv.ctr.ctr2.cursors[j].last_sync_success = now;
3329 * here we update the highest_usn and last_sync_success time
3330 * because we're directly replicating from the source_dsa
3332 * and use the tmp_highest_usn because this is what we have just applied
3335 nuv.ctr.ctr2.cursors[ni].source_dsa_invocation_id= ar->objs->source_dsa->source_dsa_invocation_id;
3336 nuv.ctr.ctr2.cursors[ni].highest_usn = ar->objs->source_dsa->highwatermark.tmp_highest_usn;
3337 nuv.ctr.ctr2.cursors[ni].last_sync_success = now;
3342 * finally correct the size of the cursors array
3344 nuv.ctr.ctr2.count = ni;
3349 TYPESAFE_QSORT(nuv.ctr.ctr2.cursors, nuv.ctr.ctr2.count, drsuapi_DsReplicaCursor2_compare);
3352 * create the change ldb_message
3354 msg = ldb_msg_new(ar);
3355 if (!msg) return replmd_replicated_request_werror(ar, WERR_NOMEM);
3356 msg->dn = ar->search_msg->dn;
3358 ndr_err = ndr_push_struct_blob(&nuv_value, msg, &nuv,
3359 (ndr_push_flags_fn_t)ndr_push_replUpToDateVectorBlob);
3360 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
3361 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
3362 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
3364 ret = ldb_msg_add_value(msg, "replUpToDateVector", &nuv_value, &nuv_el);
3365 if (ret != LDB_SUCCESS) {
3366 return replmd_replicated_request_error(ar, ret);
3368 nuv_el->flags = LDB_FLAG_MOD_REPLACE;
3371 * now create the new repsFrom value from the given repsFromTo1 structure
3375 nrf.ctr.ctr1 = *ar->objs->source_dsa;
3376 /* and fix some values... */
3377 nrf.ctr.ctr1.consecutive_sync_failures = 0;
3378 nrf.ctr.ctr1.last_success = now;
3379 nrf.ctr.ctr1.last_attempt = now;
3380 nrf.ctr.ctr1.result_last_attempt = WERR_OK;
3381 nrf.ctr.ctr1.highwatermark.highest_usn = nrf.ctr.ctr1.highwatermark.tmp_highest_usn;
3384 * first see if we already have a repsFrom value for the current source dsa
3385 * if so we'll later replace this value
3387 orf_el = ldb_msg_find_element(ar->search_msg, "repsFrom");
3389 for (i=0; i < orf_el->num_values; i++) {
3390 struct repsFromToBlob *trf;
3392 trf = talloc(ar, struct repsFromToBlob);
3393 if (!trf) return replmd_replicated_request_werror(ar, WERR_NOMEM);
3395 ndr_err = ndr_pull_struct_blob(&orf_el->values[i], trf, trf,
3396 (ndr_pull_flags_fn_t)ndr_pull_repsFromToBlob);
3397 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
3398 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
3399 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
3402 if (trf->version != 1) {
3403 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
3407 * we compare the source dsa objectGUID not the invocation_id
3408 * because we want only one repsFrom value per source dsa
3409 * and when the invocation_id of the source dsa has changed we don't need
3410 * the old repsFrom with the old invocation_id
3412 if (!GUID_equal(&trf->ctr.ctr1.source_dsa_obj_guid,
3413 &ar->objs->source_dsa->source_dsa_obj_guid)) {
3419 nrf_value = &orf_el->values[i];
3424 * copy over all old values to the new ldb_message
3426 ret = ldb_msg_add_empty(msg, "repsFrom", 0, &nrf_el);
3427 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
3432 * if we haven't found an old repsFrom value for the current source dsa
3433 * we'll add a new value
3436 struct ldb_val zero_value;
3437 ZERO_STRUCT(zero_value);
3438 ret = ldb_msg_add_value(msg, "repsFrom", &zero_value, &nrf_el);
3439 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
3441 nrf_value = &nrf_el->values[nrf_el->num_values - 1];
3444 /* we now fill the value which is already attached to ldb_message */
3445 ndr_err = ndr_push_struct_blob(nrf_value, msg,
3447 (ndr_push_flags_fn_t)ndr_push_repsFromToBlob);
3448 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
3449 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
3450 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
3454 * the ldb_message_element for the attribute, has all the old values and the new one
3455 * so we'll replace the whole attribute with all values
3457 nrf_el->flags = LDB_FLAG_MOD_REPLACE;
3460 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_MODIFY, msg);
3461 DEBUG(4, ("DRS replication uptodate modify message:\n%s\n", s));
3465 /* prepare the ldb_modify() request */
3466 ret = ldb_build_mod_req(&change_req,
3472 replmd_replicated_uptodate_modify_callback,
3474 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
3476 return ldb_next_request(ar->module, change_req);
3479 static int replmd_replicated_uptodate_search_callback(struct ldb_request *req,
3480 struct ldb_reply *ares)
3482 struct replmd_replicated_request *ar = talloc_get_type(req->context,
3483 struct replmd_replicated_request);
3487 return ldb_module_done(ar->req, NULL, NULL,
3488 LDB_ERR_OPERATIONS_ERROR);
3490 if (ares->error != LDB_SUCCESS &&
3491 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
3492 return ldb_module_done(ar->req, ares->controls,
3493 ares->response, ares->error);
3496 switch (ares->type) {
3497 case LDB_REPLY_ENTRY:
3498 ar->search_msg = talloc_steal(ar, ares->message);
3501 case LDB_REPLY_REFERRAL:
3502 /* we ignore referrals */
3505 case LDB_REPLY_DONE:
3506 if (ar->search_msg == NULL) {
3507 ret = replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
3509 ret = replmd_replicated_uptodate_modify(ar);
3511 if (ret != LDB_SUCCESS) {
3512 return ldb_module_done(ar->req, NULL, NULL, ret);
3521 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar)
3523 struct ldb_context *ldb;
3525 static const char *attrs[] = {
3526 "replUpToDateVector",
3530 struct ldb_request *search_req;
3532 ldb = ldb_module_get_ctx(ar->module);
3533 ar->search_msg = NULL;
3535 ret = ldb_build_search_req(&search_req,
3538 ar->objs->partition_dn,
3544 replmd_replicated_uptodate_search_callback,
3546 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
3548 return ldb_next_request(ar->module, search_req);
3553 static int replmd_extended_replicated_objects(struct ldb_module *module, struct ldb_request *req)
3555 struct ldb_context *ldb;
3556 struct dsdb_extended_replicated_objects *objs;
3557 struct replmd_replicated_request *ar;
3558 struct ldb_control **ctrls;
3561 struct replmd_private *replmd_private =
3562 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
3564 ldb = ldb_module_get_ctx(module);
3566 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_extended_replicated_objects\n");
3568 objs = talloc_get_type(req->op.extended.data, struct dsdb_extended_replicated_objects);
3570 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: invalid extended data\n");
3571 return LDB_ERR_PROTOCOL_ERROR;
3574 if (objs->version != DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION) {
3575 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: extended data invalid version [%u != %u]\n",
3576 objs->version, DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION);
3577 return LDB_ERR_PROTOCOL_ERROR;
3580 ar = replmd_ctx_init(module, req);
3582 return LDB_ERR_OPERATIONS_ERROR;
3584 /* Set the flags to have the replmd_op_callback run over the full set of objects */
3585 ar->apply_mode = true;
3587 ar->schema = dsdb_get_schema(ldb, ar);
3589 ldb_debug_set(ldb, LDB_DEBUG_FATAL, "replmd_ctx_init: no loaded schema found\n");
3591 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
3592 return LDB_ERR_CONSTRAINT_VIOLATION;
3595 ctrls = req->controls;
3597 if (req->controls) {
3598 req->controls = talloc_memdup(ar, req->controls,
3599 talloc_get_size(req->controls));
3600 if (!req->controls) return replmd_replicated_request_werror(ar, WERR_NOMEM);
3603 /* This allows layers further down to know if a change came in over replication */
3604 ret = ldb_request_add_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID, false, NULL);
3605 if (ret != LDB_SUCCESS) {
3609 /* If this change contained linked attributes in the body
3610 * (rather than in the links section) we need to update
3611 * backlinks in linked_attributes */
3612 ret = ldb_request_add_control(req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
3613 if (ret != LDB_SUCCESS) {
3617 ar->controls = req->controls;
3618 req->controls = ctrls;
3620 DEBUG(4,("linked_attributes_count=%u\n", objs->linked_attributes_count));
3622 /* save away the linked attributes for the end of the
3624 for (i=0; i<ar->objs->linked_attributes_count; i++) {
3625 struct la_entry *la_entry;
3627 if (replmd_private->la_ctx == NULL) {
3628 replmd_private->la_ctx = talloc_new(replmd_private);
3630 la_entry = talloc(replmd_private->la_ctx, struct la_entry);
3631 if (la_entry == NULL) {
3633 return LDB_ERR_OPERATIONS_ERROR;
3635 la_entry->la = talloc(la_entry, struct drsuapi_DsReplicaLinkedAttribute);
3636 if (la_entry->la == NULL) {
3637 talloc_free(la_entry);
3639 return LDB_ERR_OPERATIONS_ERROR;
3641 *la_entry->la = ar->objs->linked_attributes[i];
3643 /* we need to steal the non-scalars so they stay
3644 around until the end of the transaction */
3645 talloc_steal(la_entry->la, la_entry->la->identifier);
3646 talloc_steal(la_entry->la, la_entry->la->value.blob);
3648 DLIST_ADD(replmd_private->la_list, la_entry);
3651 return replmd_replicated_apply_next(ar);
3655 process one linked attribute structure
3657 static int replmd_process_linked_attribute(struct ldb_module *module,
3658 struct la_entry *la_entry)
3660 struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
3661 struct ldb_context *ldb = ldb_module_get_ctx(module);
3662 struct ldb_message *msg;
3663 TALLOC_CTX *tmp_ctx = talloc_new(la_entry);
3664 const struct dsdb_schema *schema = dsdb_get_schema(ldb, tmp_ctx);
3666 const struct dsdb_attribute *attr;
3667 struct dsdb_dn *dsdb_dn;
3668 uint64_t seq_num = 0;
3669 struct ldb_message_element *old_el;
3671 time_t t = time(NULL);
3672 struct ldb_result *res;
3673 const char *attrs[2];
3674 struct parsed_dn *pdn_list, *pdn;
3675 struct GUID guid = GUID_zero();
3677 bool active = (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)?true:false;
3678 const struct GUID *our_invocation_id;
3681 linked_attributes[0]:
3682 &objs->linked_attributes[i]: struct drsuapi_DsReplicaLinkedAttribute
3684 identifier: struct drsuapi_DsReplicaObjectIdentifier
3685 __ndr_size : 0x0000003a (58)
3686 __ndr_size_sid : 0x00000000 (0)
3687 guid : 8e95b6a9-13dd-4158-89db-3220a5be5cc7
3689 __ndr_size_dn : 0x00000000 (0)
3691 attid : DRSUAPI_ATTRIBUTE_member (0x1F)
3692 value: struct drsuapi_DsAttributeValue
3693 __ndr_size : 0x0000007e (126)
3695 blob : DATA_BLOB length=126
3696 flags : 0x00000001 (1)
3697 1: DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE
3698 originating_add_time : Wed Sep 2 22:20:01 2009 EST
3699 meta_data: struct drsuapi_DsReplicaMetaData
3700 version : 0x00000015 (21)
3701 originating_change_time : Wed Sep 2 23:39:07 2009 EST
3702 originating_invocation_id: 794640f3-18cf-40ee-a211-a93992b67a64
3703 originating_usn : 0x000000000001e19c (123292)
3705 (for cases where the link is to a normal DN)
3706 &target: struct drsuapi_DsReplicaObjectIdentifier3
3707 __ndr_size : 0x0000007e (126)
3708 __ndr_size_sid : 0x0000001c (28)
3709 guid : 7639e594-db75-4086-b0d4-67890ae46031
3710 sid : S-1-5-21-2848215498-2472035911-1947525656-19924
3711 __ndr_size_dn : 0x00000022 (34)
3712 dn : 'CN=UOne,OU=TestOU,DC=vsofs8,DC=com'
3715 /* find the attribute being modified */
3716 attr = dsdb_attribute_by_attributeID_id(schema, la->attid);
3718 DEBUG(0, (__location__ ": Unable to find attributeID 0x%x\n", la->attid));
3719 talloc_free(tmp_ctx);
3720 return LDB_ERR_OPERATIONS_ERROR;
3723 attrs[0] = attr->lDAPDisplayName;
3726 /* get the existing message from the db for the object with
3727 this GUID, returning attribute being modified. We will then
3728 use this msg as the basis for a modify call */
3729 ret = dsdb_module_search(module, tmp_ctx, &res, NULL, LDB_SCOPE_SUBTREE, attrs,
3730 DSDB_FLAG_NEXT_MODULE |
3731 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
3732 DSDB_SEARCH_SHOW_DELETED |
3733 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
3734 DSDB_SEARCH_REVEAL_INTERNALS,
3735 "objectGUID=%s", GUID_string(tmp_ctx, &la->identifier->guid));
3736 if (ret != LDB_SUCCESS) {
3737 talloc_free(tmp_ctx);
3740 if (res->count != 1) {
3741 ldb_asprintf_errstring(ldb, "DRS linked attribute for GUID %s - DN not found",
3742 GUID_string(tmp_ctx, &la->identifier->guid));
3743 talloc_free(tmp_ctx);
3744 return LDB_ERR_NO_SUCH_OBJECT;
3748 if (msg->num_elements == 0) {
3749 ret = ldb_msg_add_empty(msg, attr->lDAPDisplayName, LDB_FLAG_MOD_REPLACE, &old_el);
3750 if (ret != LDB_SUCCESS) {
3751 ldb_module_oom(module);
3752 talloc_free(tmp_ctx);
3753 return LDB_ERR_OPERATIONS_ERROR;
3756 old_el = &msg->elements[0];
3757 old_el->flags = LDB_FLAG_MOD_REPLACE;
3760 /* parse the existing links */
3761 ret = get_parsed_dns(module, tmp_ctx, old_el, &pdn_list, attr->syntax->ldap_oid);
3762 if (ret != LDB_SUCCESS) {
3763 talloc_free(tmp_ctx);
3767 /* get our invocationId */
3768 our_invocation_id = samdb_ntds_invocation_id(ldb);
3769 if (!our_invocation_id) {
3770 ldb_debug_set(ldb, LDB_DEBUG_ERROR, __location__ ": unable to find invocationId\n");
3771 talloc_free(tmp_ctx);
3772 return LDB_ERR_OPERATIONS_ERROR;
3775 ret = replmd_check_upgrade_links(pdn_list, old_el->num_values, old_el, our_invocation_id);
3776 if (ret != LDB_SUCCESS) {
3777 talloc_free(tmp_ctx);
3781 status = dsdb_dn_la_from_blob(ldb, attr, schema, tmp_ctx, la->value.blob, &dsdb_dn);
3782 if (!W_ERROR_IS_OK(status)) {
3783 ldb_asprintf_errstring(ldb, "Failed to parsed linked attribute blob for %s on %s - %s\n",
3784 old_el->name, ldb_dn_get_linearized(msg->dn), win_errstr(status));
3785 return LDB_ERR_OPERATIONS_ERROR;
3788 ntstatus = dsdb_get_extended_dn_guid(dsdb_dn->dn, &guid, "GUID");
3789 if (!NT_STATUS_IS_OK(ntstatus) && active) {
3790 ldb_asprintf_errstring(ldb, "Failed to find GUID in linked attribute blob for %s on %s from %s",
3792 ldb_dn_get_linearized(dsdb_dn->dn),
3793 ldb_dn_get_linearized(msg->dn));
3794 return LDB_ERR_OPERATIONS_ERROR;
3797 /* re-resolve the DN by GUID, as the DRS server may give us an
3799 ret = dsdb_module_dn_by_guid(module, dsdb_dn, &guid, &dsdb_dn->dn);
3800 if (ret != LDB_SUCCESS) {
3801 DEBUG(2,(__location__ ": WARNING: Failed to re-resolve GUID %s - using %s",
3802 GUID_string(tmp_ctx, &guid),
3803 ldb_dn_get_linearized(dsdb_dn->dn)));
3806 /* see if this link already exists */
3807 pdn = parsed_dn_find(pdn_list, old_el->num_values, &guid, dsdb_dn->dn);
3809 /* see if this update is newer than what we have already */
3810 struct GUID invocation_id = GUID_zero();
3811 uint32_t version = 0;
3812 NTTIME change_time = 0;
3813 uint32_t rmd_flags = dsdb_dn_rmd_flags(pdn->dsdb_dn->dn);
3815 dsdb_get_extended_dn_guid(pdn->dsdb_dn->dn, &invocation_id, "RMD_INVOCID");
3816 dsdb_get_extended_dn_uint32(pdn->dsdb_dn->dn, &version, "RMD_VERSION");
3817 dsdb_get_extended_dn_nttime(pdn->dsdb_dn->dn, &change_time, "RMD_CHANGETIME");
3819 if (!replmd_update_is_newer(&invocation_id,
3820 &la->meta_data.originating_invocation_id,
3822 la->meta_data.version,
3824 la->meta_data.originating_change_time)) {
3825 DEBUG(3,("Discarding older DRS linked attribute update to %s on %s from %s\n",
3826 old_el->name, ldb_dn_get_linearized(msg->dn),
3827 GUID_string(tmp_ctx, &la->meta_data.originating_invocation_id)));
3828 talloc_free(tmp_ctx);
3832 /* get a seq_num for this change */
3833 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
3834 if (ret != LDB_SUCCESS) {
3835 talloc_free(tmp_ctx);
3839 if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
3840 /* remove the existing backlink */
3841 ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid, false, attr, false);
3842 if (ret != LDB_SUCCESS) {
3843 talloc_free(tmp_ctx);
3848 ret = replmd_update_la_val(tmp_ctx, pdn->v, dsdb_dn, pdn->dsdb_dn,
3849 &la->meta_data.originating_invocation_id,
3850 la->meta_data.originating_usn, seq_num,
3851 la->meta_data.originating_change_time,
3852 la->meta_data.version,
3854 if (ret != LDB_SUCCESS) {
3855 talloc_free(tmp_ctx);
3860 /* add the new backlink */
3861 ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid, true, attr, false);
3862 if (ret != LDB_SUCCESS) {
3863 talloc_free(tmp_ctx);
3868 /* get a seq_num for this change */
3869 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
3870 if (ret != LDB_SUCCESS) {
3871 talloc_free(tmp_ctx);
3875 old_el->values = talloc_realloc(msg->elements, old_el->values,
3876 struct ldb_val, old_el->num_values+1);
3877 if (!old_el->values) {
3878 ldb_module_oom(module);
3879 return LDB_ERR_OPERATIONS_ERROR;
3881 old_el->num_values++;
3883 ret = replmd_build_la_val(tmp_ctx, &old_el->values[old_el->num_values-1], dsdb_dn,
3884 &la->meta_data.originating_invocation_id,
3885 la->meta_data.originating_usn, seq_num,
3886 la->meta_data.originating_change_time,
3887 la->meta_data.version,
3888 (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)?false:true);
3889 if (ret != LDB_SUCCESS) {
3890 talloc_free(tmp_ctx);
3895 ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid,
3897 if (ret != LDB_SUCCESS) {
3898 talloc_free(tmp_ctx);
3904 /* we only change whenChanged and uSNChanged if the seq_num
3906 if (add_time_element(msg, "whenChanged", t) != LDB_SUCCESS) {
3907 talloc_free(tmp_ctx);
3908 return LDB_ERR_OPERATIONS_ERROR;
3911 if (add_uint64_element(msg, "uSNChanged", seq_num) != LDB_SUCCESS) {
3912 talloc_free(tmp_ctx);
3913 return LDB_ERR_OPERATIONS_ERROR;
3916 ret = dsdb_check_single_valued_link(attr, old_el);
3917 if (ret != LDB_SUCCESS) {
3918 talloc_free(tmp_ctx);
3922 ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE | DSDB_MODIFY_RELAX);
3923 if (ret != LDB_SUCCESS) {
3924 ldb_debug(ldb, LDB_DEBUG_WARNING, "Failed to apply linked attribute change '%s'\n%s\n",
3926 ldb_ldif_message_string(ldb, tmp_ctx, LDB_CHANGETYPE_MODIFY, msg));
3927 talloc_free(tmp_ctx);
3931 talloc_free(tmp_ctx);
3936 static int replmd_extended(struct ldb_module *module, struct ldb_request *req)
3938 if (strcmp(req->op.extended.oid, DSDB_EXTENDED_REPLICATED_OBJECTS_OID) == 0) {
3939 return replmd_extended_replicated_objects(module, req);
3942 return ldb_next_request(module, req);
3947 we hook into the transaction operations to allow us to
3948 perform the linked attribute updates at the end of the whole
3949 transaction. This allows a forward linked attribute to be created
3950 before the object is created. During a vampire, w2k8 sends us linked
3951 attributes before the objects they are part of.
3953 static int replmd_start_transaction(struct ldb_module *module)
3955 /* create our private structure for this transaction */
3956 struct replmd_private *replmd_private = talloc_get_type(ldb_module_get_private(module),
3957 struct replmd_private);
3958 replmd_txn_cleanup(replmd_private);
3960 /* free any leftover mod_usn records from cancelled
3962 while (replmd_private->ncs) {
3963 struct nc_entry *e = replmd_private->ncs;
3964 DLIST_REMOVE(replmd_private->ncs, e);
3968 return ldb_next_start_trans(module);
3972 on prepare commit we loop over our queued la_context structures and
3975 static int replmd_prepare_commit(struct ldb_module *module)
3977 struct replmd_private *replmd_private =
3978 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
3979 struct la_entry *la, *prev;
3980 struct la_backlink *bl;
3983 /* walk the list backwards, to do the first entry first, as we
3984 * added the entries with DLIST_ADD() which puts them at the
3985 * start of the list */
3986 for (la = DLIST_TAIL(replmd_private->la_list); la; la=prev) {
3987 prev = DLIST_PREV(la);
3988 DLIST_REMOVE(replmd_private->la_list, la);
3989 ret = replmd_process_linked_attribute(module, la);
3990 if (ret != LDB_SUCCESS) {
3991 replmd_txn_cleanup(replmd_private);
3996 /* process our backlink list, creating and deleting backlinks
3998 for (bl=replmd_private->la_backlinks; bl; bl=bl->next) {
3999 ret = replmd_process_backlink(module, bl);
4000 if (ret != LDB_SUCCESS) {
4001 replmd_txn_cleanup(replmd_private);
4006 replmd_txn_cleanup(replmd_private);
4008 /* possibly change @REPLCHANGED */
4009 ret = replmd_notify_store(module);
4010 if (ret != LDB_SUCCESS) {
4014 return ldb_next_prepare_commit(module);
4017 static int replmd_del_transaction(struct ldb_module *module)
4019 struct replmd_private *replmd_private =
4020 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
4021 replmd_txn_cleanup(replmd_private);
4023 return ldb_next_del_trans(module);
4027 _PUBLIC_ const struct ldb_module_ops ldb_repl_meta_data_module_ops = {
4028 .name = "repl_meta_data",
4029 .init_context = replmd_init,
4031 .modify = replmd_modify,
4032 .rename = replmd_rename,
4033 .del = replmd_delete,
4034 .extended = replmd_extended,
4035 .start_transaction = replmd_start_transaction,
4036 .prepare_commit = replmd_prepare_commit,
4037 .del_transaction = replmd_del_transaction,