s4-drs: isRecycled only exists in FL W2K8-R2
[Samba/fernandojvsilva.git] / source4 / dsdb / samdb / ldb_modules / repl_meta_data.c
blob9994b9566dfa6f227072ddbe7c568f813ad0d2e2
1 /*
2 ldb database library
4 Copyright (C) Simo Sorce 2004-2008
5 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
6 Copyright (C) Andrew Tridgell 2005
7 Copyright (C) Stefan Metzmacher <metze@samba.org> 2007
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/>.
24 * Name: ldb
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
33 * Author: Simo Sorce
34 * Author: Stefan Metzmacher
37 #include "includes.h"
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"
51 #define W2K3_LINKED_ATTRIBUTES 1
53 struct replmd_private {
54 TALLOC_CTX *la_ctx;
55 struct la_entry *la_list;
56 TALLOC_CTX *bl_ctx;
57 struct la_backlink *la_backlinks;
58 struct nc_entry {
59 struct nc_entry *prev, *next;
60 struct ldb_dn *dn;
61 uint64_t mod_usn;
62 } *ncs;
65 struct la_entry {
66 struct la_entry *next, *prev;
67 struct drsuapi_DsReplicaLinkedAttribute *la;
70 struct replmd_replicated_request {
71 struct ldb_module *module;
72 struct ldb_request *req;
74 const struct dsdb_schema *schema;
76 /* the controls we pass down */
77 struct ldb_control **controls;
79 /* details for the mode where we apply a bunch of inbound replication meessages */
80 bool apply_mode;
81 uint32_t index_current;
82 struct dsdb_extended_replicated_objects *objs;
84 struct ldb_message *search_msg;
86 uint64_t seq_num;
90 static int replmd_replicated_apply_next(struct replmd_replicated_request *ar);
94 initialise the module
95 allocate the private structure and build the list
96 of partition DNs for use by replmd_notify()
98 static int replmd_init(struct ldb_module *module)
100 struct replmd_private *replmd_private;
101 struct ldb_context *ldb = ldb_module_get_ctx(module);
103 replmd_private = talloc_zero(module, struct replmd_private);
104 if (replmd_private == NULL) {
105 ldb_oom(ldb);
106 return LDB_ERR_OPERATIONS_ERROR;
108 ldb_module_set_private(module, replmd_private);
110 return ldb_next_init(module);
114 cleanup our per-transaction contexts
116 static void replmd_txn_cleanup(struct replmd_private *replmd_private)
118 talloc_free(replmd_private->la_ctx);
119 replmd_private->la_list = NULL;
120 replmd_private->la_ctx = NULL;
122 talloc_free(replmd_private->bl_ctx);
123 replmd_private->la_backlinks = NULL;
124 replmd_private->bl_ctx = NULL;
128 struct la_backlink {
129 struct la_backlink *next, *prev;
130 const char *attr_name;
131 struct GUID forward_guid, target_guid;
132 bool active;
136 process a backlinks we accumulated during a transaction, adding and
137 deleting the backlinks from the target objects
139 static int replmd_process_backlink(struct ldb_module *module, struct la_backlink *bl)
141 struct ldb_dn *target_dn, *source_dn;
142 int ret;
143 struct ldb_context *ldb = ldb_module_get_ctx(module);
144 struct ldb_message *msg;
145 TALLOC_CTX *tmp_ctx = talloc_new(bl);
146 char *dn_string;
149 - find DN of target
150 - find DN of source
151 - construct ldb_message
152 - either an add or a delete
154 ret = dsdb_module_dn_by_guid(module, tmp_ctx, &bl->target_guid, &target_dn);
155 if (ret != LDB_SUCCESS) {
156 ldb_asprintf_errstring(ldb, "Failed to find target DN for linked attribute with GUID %s\n",
157 GUID_string(bl, &bl->target_guid));
158 talloc_free(tmp_ctx);
159 return ret;
162 ret = dsdb_module_dn_by_guid(module, tmp_ctx, &bl->forward_guid, &source_dn);
163 if (ret != LDB_SUCCESS) {
164 ldb_asprintf_errstring(ldb, "Failed to find source DN for linked attribute with GUID %s\n",
165 GUID_string(bl, &bl->forward_guid));
166 talloc_free(tmp_ctx);
167 return ret;
170 msg = ldb_msg_new(tmp_ctx);
171 if (msg == NULL) {
172 ldb_module_oom(module);
173 talloc_free(tmp_ctx);
174 return LDB_ERR_OPERATIONS_ERROR;
177 /* construct a ldb_message for adding/deleting the backlink */
178 msg->dn = target_dn;
179 dn_string = ldb_dn_get_extended_linearized(tmp_ctx, source_dn, 1);
180 if (!dn_string) {
181 ldb_module_oom(module);
182 talloc_free(tmp_ctx);
183 return LDB_ERR_OPERATIONS_ERROR;
185 ret = ldb_msg_add_steal_string(msg, bl->attr_name, dn_string);
186 if (ret != LDB_SUCCESS) {
187 talloc_free(tmp_ctx);
188 return ret;
190 msg->elements[0].flags = bl->active?LDB_FLAG_MOD_ADD:LDB_FLAG_MOD_DELETE;
192 ret = dsdb_module_modify(module, msg, 0);
193 if (ret != LDB_SUCCESS) {
194 ldb_asprintf_errstring(ldb, "Failed to %s backlink from %s to %s - %s",
195 bl->active?"add":"remove",
196 ldb_dn_get_linearized(source_dn),
197 ldb_dn_get_linearized(target_dn),
198 ldb_errstring(ldb));
199 talloc_free(tmp_ctx);
200 return ret;
202 talloc_free(tmp_ctx);
203 return ret;
207 add a backlink to the list of backlinks to add/delete in the prepare
208 commit
210 static int replmd_add_backlink(struct ldb_module *module, const struct dsdb_schema *schema,
211 struct GUID *forward_guid, struct GUID *target_guid,
212 bool active, const struct dsdb_attribute *schema_attr, bool immediate)
214 const struct dsdb_attribute *target_attr;
215 struct la_backlink *bl;
216 struct replmd_private *replmd_private =
217 talloc_get_type_abort(ldb_module_get_private(module), struct replmd_private);
219 target_attr = dsdb_attribute_by_linkID(schema, schema_attr->linkID ^ 1);
220 if (!target_attr) {
222 * windows 2003 has a broken schema where the
223 * definition of msDS-IsDomainFor is missing (which is
224 * supposed to be the backlink of the
225 * msDS-HasDomainNCs attribute
227 return LDB_SUCCESS;
230 /* see if its already in the list */
231 for (bl=replmd_private->la_backlinks; bl; bl=bl->next) {
232 if (GUID_equal(forward_guid, &bl->forward_guid) &&
233 GUID_equal(target_guid, &bl->target_guid) &&
234 (target_attr->lDAPDisplayName == bl->attr_name ||
235 strcmp(target_attr->lDAPDisplayName, bl->attr_name) == 0)) {
236 break;
240 if (bl) {
241 /* we found an existing one */
242 if (bl->active == active) {
243 return LDB_SUCCESS;
245 DLIST_REMOVE(replmd_private->la_backlinks, bl);
246 talloc_free(bl);
247 return LDB_SUCCESS;
250 if (replmd_private->bl_ctx == NULL) {
251 replmd_private->bl_ctx = talloc_new(replmd_private);
252 if (replmd_private->bl_ctx == NULL) {
253 ldb_module_oom(module);
254 return LDB_ERR_OPERATIONS_ERROR;
258 /* its a new one */
259 bl = talloc(replmd_private->bl_ctx, struct la_backlink);
260 if (bl == NULL) {
261 ldb_module_oom(module);
262 return LDB_ERR_OPERATIONS_ERROR;
265 bl->attr_name = target_attr->lDAPDisplayName;
266 bl->forward_guid = *forward_guid;
267 bl->target_guid = *target_guid;
268 bl->active = active;
270 /* the caller may ask for this backlink to be processed
271 immediately */
272 if (immediate) {
273 int ret = replmd_process_backlink(module, bl);
274 talloc_free(bl);
275 return ret;
278 DLIST_ADD(replmd_private->la_backlinks, bl);
280 return LDB_SUCCESS;
285 * Callback for most write operations in this module:
287 * notify the repl task that a object has changed. The notifies are
288 * gathered up in the replmd_private structure then written to the
289 * @REPLCHANGED object in each partition during the prepare_commit
291 static int replmd_op_callback(struct ldb_request *req, struct ldb_reply *ares)
293 int ret;
294 struct replmd_replicated_request *ac =
295 talloc_get_type_abort(req->context, struct replmd_replicated_request);
296 struct replmd_private *replmd_private =
297 talloc_get_type_abort(ldb_module_get_private(ac->module), struct replmd_private);
298 struct nc_entry *modified_partition;
299 struct ldb_control *partition_ctrl;
300 const struct dsdb_control_current_partition *partition;
302 struct ldb_control **controls;
304 partition_ctrl = ldb_reply_get_control(ares, DSDB_CONTROL_CURRENT_PARTITION_OID);
306 /* Remove the 'partition' control from what we pass up the chain */
307 controls = controls_except_specified(ares->controls, ares, partition_ctrl);
309 if (ares->error != LDB_SUCCESS) {
310 return ldb_module_done(ac->req, controls,
311 ares->response, ares->error);
314 if (ares->type != LDB_REPLY_DONE) {
315 ldb_set_errstring(ldb_module_get_ctx(ac->module), "Invalid reply type for notify\n!");
316 return ldb_module_done(ac->req, NULL,
317 NULL, LDB_ERR_OPERATIONS_ERROR);
320 if (!partition_ctrl) {
321 ldb_set_errstring(ldb_module_get_ctx(ac->module),"No partition control on reply");
322 return ldb_module_done(ac->req, NULL,
323 NULL, LDB_ERR_OPERATIONS_ERROR);
326 partition = talloc_get_type_abort(partition_ctrl->data,
327 struct dsdb_control_current_partition);
329 if (ac->seq_num > 0) {
330 for (modified_partition = replmd_private->ncs; modified_partition;
331 modified_partition = modified_partition->next) {
332 if (ldb_dn_compare(modified_partition->dn, partition->dn) == 0) {
333 break;
337 if (modified_partition == NULL) {
338 modified_partition = talloc_zero(replmd_private, struct nc_entry);
339 if (!modified_partition) {
340 ldb_oom(ldb_module_get_ctx(ac->module));
341 return ldb_module_done(ac->req, NULL,
342 NULL, LDB_ERR_OPERATIONS_ERROR);
344 modified_partition->dn = ldb_dn_copy(modified_partition, partition->dn);
345 if (!modified_partition->dn) {
346 ldb_oom(ldb_module_get_ctx(ac->module));
347 return ldb_module_done(ac->req, NULL,
348 NULL, LDB_ERR_OPERATIONS_ERROR);
350 DLIST_ADD(replmd_private->ncs, modified_partition);
353 if (ac->seq_num > modified_partition->mod_usn) {
354 modified_partition->mod_usn = ac->seq_num;
358 if (ac->apply_mode) {
359 talloc_free(ares);
360 ac->index_current++;
362 ret = replmd_replicated_apply_next(ac);
363 if (ret != LDB_SUCCESS) {
364 return ldb_module_done(ac->req, NULL, NULL, ret);
366 return ret;
367 } else {
368 /* free the partition control container here, for the
369 * common path. Other cases will have it cleaned up
370 * eventually with the ares */
371 talloc_free(partition_ctrl);
372 return ldb_module_done(ac->req,
373 controls_except_specified(controls, ares, partition_ctrl),
374 ares->response, LDB_SUCCESS);
380 * update a @REPLCHANGED record in each partition if there have been
381 * any writes of replicated data in the partition
383 static int replmd_notify_store(struct ldb_module *module)
385 struct replmd_private *replmd_private =
386 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
387 struct ldb_context *ldb = ldb_module_get_ctx(module);
389 while (replmd_private->ncs) {
390 int ret;
391 struct nc_entry *modified_partition = replmd_private->ncs;
393 ret = dsdb_save_partition_usn(ldb, modified_partition->dn, modified_partition->mod_usn);
394 if (ret != LDB_SUCCESS) {
395 DEBUG(0,(__location__ ": Failed to save partition uSN for %s\n",
396 ldb_dn_get_linearized(modified_partition->dn)));
397 return ret;
399 DLIST_REMOVE(replmd_private->ncs, modified_partition);
400 talloc_free(modified_partition);
403 return LDB_SUCCESS;
408 created a replmd_replicated_request context
410 static struct replmd_replicated_request *replmd_ctx_init(struct ldb_module *module,
411 struct ldb_request *req)
413 struct ldb_context *ldb;
414 struct replmd_replicated_request *ac;
416 ldb = ldb_module_get_ctx(module);
418 ac = talloc_zero(req, struct replmd_replicated_request);
419 if (ac == NULL) {
420 ldb_oom(ldb);
421 return NULL;
424 ac->module = module;
425 ac->req = req;
427 ac->schema = dsdb_get_schema(ldb);
428 if (!ac->schema) {
429 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
430 "replmd_modify: no dsdb_schema loaded");
431 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
432 return NULL;
435 return ac;
439 add a time element to a record
441 static int add_time_element(struct ldb_message *msg, const char *attr, time_t t)
443 struct ldb_message_element *el;
444 char *s;
446 if (ldb_msg_find_element(msg, attr) != NULL) {
447 return LDB_SUCCESS;
450 s = ldb_timestring(msg, t);
451 if (s == NULL) {
452 return LDB_ERR_OPERATIONS_ERROR;
455 if (ldb_msg_add_string(msg, attr, s) != LDB_SUCCESS) {
456 return LDB_ERR_OPERATIONS_ERROR;
459 el = ldb_msg_find_element(msg, attr);
460 /* always set as replace. This works because on add ops, the flag
461 is ignored */
462 el->flags = LDB_FLAG_MOD_REPLACE;
464 return LDB_SUCCESS;
468 add a uint64_t element to a record
470 static int add_uint64_element(struct ldb_message *msg, const char *attr, uint64_t v)
472 struct ldb_message_element *el;
474 if (ldb_msg_find_element(msg, attr) != NULL) {
475 return LDB_SUCCESS;
478 if (ldb_msg_add_fmt(msg, attr, "%llu", (unsigned long long)v) != LDB_SUCCESS) {
479 return LDB_ERR_OPERATIONS_ERROR;
482 el = ldb_msg_find_element(msg, attr);
483 /* always set as replace. This works because on add ops, the flag
484 is ignored */
485 el->flags = LDB_FLAG_MOD_REPLACE;
487 return LDB_SUCCESS;
490 static int replmd_replPropertyMetaData1_attid_sort(const struct replPropertyMetaData1 *m1,
491 const struct replPropertyMetaData1 *m2,
492 const uint32_t *rdn_attid)
494 if (m1->attid == m2->attid) {
495 return 0;
499 * the rdn attribute should be at the end!
500 * so we need to return a value greater than zero
501 * which means m1 is greater than m2
503 if (m1->attid == *rdn_attid) {
504 return 1;
508 * the rdn attribute should be at the end!
509 * so we need to return a value less than zero
510 * which means m2 is greater than m1
512 if (m2->attid == *rdn_attid) {
513 return -1;
516 return m1->attid > m2->attid ? 1 : -1;
519 static int replmd_replPropertyMetaDataCtr1_sort(struct replPropertyMetaDataCtr1 *ctr1,
520 const struct dsdb_schema *schema,
521 struct ldb_dn *dn)
523 const char *rdn_name;
524 const struct dsdb_attribute *rdn_sa;
526 rdn_name = ldb_dn_get_rdn_name(dn);
527 if (!rdn_name) {
528 DEBUG(0,(__location__ ": No rDN for %s?\n", ldb_dn_get_linearized(dn)));
529 return LDB_ERR_OPERATIONS_ERROR;
532 rdn_sa = dsdb_attribute_by_lDAPDisplayName(schema, rdn_name);
533 if (rdn_sa == NULL) {
534 DEBUG(0,(__location__ ": No sa found for rDN %s for %s\n", rdn_name, ldb_dn_get_linearized(dn)));
535 return LDB_ERR_OPERATIONS_ERROR;
538 DEBUG(6,("Sorting rpmd with attid exception %u rDN=%s DN=%s\n",
539 rdn_sa->attributeID_id, rdn_name, ldb_dn_get_linearized(dn)));
541 ldb_qsort(ctr1->array, ctr1->count, sizeof(struct replPropertyMetaData1),
542 discard_const_p(void, &rdn_sa->attributeID_id),
543 (ldb_qsort_cmp_fn_t)replmd_replPropertyMetaData1_attid_sort);
545 return LDB_SUCCESS;
548 static int replmd_ldb_message_element_attid_sort(const struct ldb_message_element *e1,
549 const struct ldb_message_element *e2,
550 const struct dsdb_schema *schema)
552 const struct dsdb_attribute *a1;
553 const struct dsdb_attribute *a2;
556 * TODO: make this faster by caching the dsdb_attribute pointer
557 * on the ldb_messag_element
560 a1 = dsdb_attribute_by_lDAPDisplayName(schema, e1->name);
561 a2 = dsdb_attribute_by_lDAPDisplayName(schema, e2->name);
564 * TODO: remove this check, we should rely on e1 and e2 having valid attribute names
565 * in the schema
567 if (!a1 || !a2) {
568 return strcasecmp(e1->name, e2->name);
570 if (a1->attributeID_id == a2->attributeID_id) {
571 return 0;
573 return a1->attributeID_id > a2->attributeID_id ? 1 : -1;
576 static void replmd_ldb_message_sort(struct ldb_message *msg,
577 const struct dsdb_schema *schema)
579 ldb_qsort(msg->elements, msg->num_elements, sizeof(struct ldb_message_element),
580 discard_const_p(void, schema), (ldb_qsort_cmp_fn_t)replmd_ldb_message_element_attid_sort);
583 static int replmd_build_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
584 const struct GUID *invocation_id, uint64_t seq_num,
585 uint64_t local_usn, NTTIME nttime, uint32_t version, bool deleted);
589 fix up linked attributes in replmd_add.
590 This involves setting up the right meta-data in extended DN
591 components, and creating backlinks to the object
593 static int replmd_add_fix_la(struct ldb_module *module, struct ldb_message_element *el,
594 uint64_t seq_num, const struct GUID *invocationId, time_t t,
595 struct GUID *guid, const struct dsdb_attribute *sa)
597 int i;
598 TALLOC_CTX *tmp_ctx = talloc_new(el->values);
599 struct ldb_context *ldb = ldb_module_get_ctx(module);
600 struct dsdb_schema *schema = dsdb_get_schema(ldb);
601 NTTIME now;
603 unix_to_nt_time(&now, t);
605 for (i=0; i<el->num_values; i++) {
606 struct ldb_val *v = &el->values[i];
607 struct dsdb_dn *dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, v, sa->syntax->ldap_oid);
608 struct GUID target_guid;
609 NTSTATUS status;
610 int ret;
612 ret = replmd_build_la_val(el->values, v, dsdb_dn, invocationId,
613 seq_num, seq_num, now, 0, false);
614 if (ret != LDB_SUCCESS) {
615 talloc_free(tmp_ctx);
616 return ret;
619 /* note that the DN already has the extended
620 components from the extended_dn_store module */
621 status = dsdb_get_extended_dn_guid(dsdb_dn->dn, &target_guid, "GUID");
622 if (!NT_STATUS_IS_OK(status) || GUID_all_zero(&target_guid)) {
623 ret = dsdb_module_guid_by_dn(module, dsdb_dn->dn, &target_guid);
624 if (ret != LDB_SUCCESS) {
625 talloc_free(tmp_ctx);
626 return ret;
630 ret = replmd_add_backlink(module, schema, guid, &target_guid, true, sa, false);
631 if (ret != LDB_SUCCESS) {
632 talloc_free(tmp_ctx);
633 return ret;
637 talloc_free(tmp_ctx);
638 return LDB_SUCCESS;
643 intercept add requests
645 static int replmd_add(struct ldb_module *module, struct ldb_request *req)
647 struct ldb_context *ldb;
648 struct ldb_control *control;
649 struct replmd_replicated_request *ac;
650 enum ndr_err_code ndr_err;
651 struct ldb_request *down_req;
652 struct ldb_message *msg;
653 const DATA_BLOB *guid_blob;
654 struct GUID guid;
655 struct replPropertyMetaDataBlob nmd;
656 struct ldb_val nmd_value;
657 const struct GUID *our_invocation_id;
658 time_t t = time(NULL);
659 NTTIME now;
660 char *time_str;
661 int ret;
662 uint32_t i, ni=0;
663 bool allow_add_guid = false;
664 bool remove_current_guid = false;
666 /* check if there's a show relax control (used by provision to say 'I know what I'm doing') */
667 control = ldb_request_get_control(req, LDB_CONTROL_RELAX_OID);
668 if (control) {
669 allow_add_guid = 1;
672 /* do not manipulate our control entries */
673 if (ldb_dn_is_special(req->op.add.message->dn)) {
674 return ldb_next_request(module, req);
677 ldb = ldb_module_get_ctx(module);
679 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_add\n");
681 ac = replmd_ctx_init(module, req);
682 if (!ac) {
683 return LDB_ERR_OPERATIONS_ERROR;
686 guid_blob = ldb_msg_find_ldb_val(req->op.add.message, "objectGUID");
687 if ( guid_blob != NULL ) {
688 if( !allow_add_guid ) {
689 ldb_debug_set(ldb, LDB_DEBUG_ERROR,
690 "replmd_add: it's not allowed to add an object with objectGUID\n");
691 talloc_free(ac);
692 return LDB_ERR_UNWILLING_TO_PERFORM;
693 } else {
694 NTSTATUS status = GUID_from_data_blob(guid_blob,&guid);
695 if ( !NT_STATUS_IS_OK(status)) {
696 ldb_debug_set(ldb, LDB_DEBUG_ERROR,
697 "replmd_add: Unable to parse as a GUID the attribute objectGUID\n");
698 talloc_free(ac);
699 return LDB_ERR_UNWILLING_TO_PERFORM;
701 /* we remove this attribute as it can be a string and will not be treated
702 correctly and then we will readd it latter on in the good format*/
703 remove_current_guid = true;
705 } else {
706 /* a new GUID */
707 guid = GUID_random();
710 /* Get a sequence number from the backend */
711 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ac->seq_num);
712 if (ret != LDB_SUCCESS) {
713 talloc_free(ac);
714 return ret;
717 /* get our invocationId */
718 our_invocation_id = samdb_ntds_invocation_id(ldb);
719 if (!our_invocation_id) {
720 ldb_debug_set(ldb, LDB_DEBUG_ERROR,
721 "replmd_add: unable to find invocationId\n");
722 talloc_free(ac);
723 return LDB_ERR_OPERATIONS_ERROR;
726 /* we have to copy the message as the caller might have it as a const */
727 msg = ldb_msg_copy_shallow(ac, req->op.add.message);
728 if (msg == NULL) {
729 ldb_oom(ldb);
730 talloc_free(ac);
731 return LDB_ERR_OPERATIONS_ERROR;
734 /* generated times */
735 unix_to_nt_time(&now, t);
736 time_str = ldb_timestring(msg, t);
737 if (!time_str) {
738 ldb_oom(ldb);
739 talloc_free(ac);
740 return LDB_ERR_OPERATIONS_ERROR;
742 if (remove_current_guid) {
743 ldb_msg_remove_attr(msg,"objectGUID");
747 * remove autogenerated attributes
749 ldb_msg_remove_attr(msg, "whenCreated");
750 ldb_msg_remove_attr(msg, "whenChanged");
751 ldb_msg_remove_attr(msg, "uSNCreated");
752 ldb_msg_remove_attr(msg, "uSNChanged");
753 ldb_msg_remove_attr(msg, "replPropertyMetaData");
756 * readd replicated attributes
758 ret = ldb_msg_add_string(msg, "whenCreated", time_str);
759 if (ret != LDB_SUCCESS) {
760 ldb_oom(ldb);
761 talloc_free(ac);
762 return ret;
765 /* build the replication meta_data */
766 ZERO_STRUCT(nmd);
767 nmd.version = 1;
768 nmd.ctr.ctr1.count = msg->num_elements;
769 nmd.ctr.ctr1.array = talloc_array(msg,
770 struct replPropertyMetaData1,
771 nmd.ctr.ctr1.count);
772 if (!nmd.ctr.ctr1.array) {
773 ldb_oom(ldb);
774 talloc_free(ac);
775 return LDB_ERR_OPERATIONS_ERROR;
778 for (i=0; i < msg->num_elements; i++) {
779 struct ldb_message_element *e = &msg->elements[i];
780 struct replPropertyMetaData1 *m = &nmd.ctr.ctr1.array[ni];
781 const struct dsdb_attribute *sa;
783 if (e->name[0] == '@') continue;
785 sa = dsdb_attribute_by_lDAPDisplayName(ac->schema, e->name);
786 if (!sa) {
787 ldb_debug_set(ldb, LDB_DEBUG_ERROR,
788 "replmd_add: attribute '%s' not defined in schema\n",
789 e->name);
790 talloc_free(ac);
791 return LDB_ERR_NO_SUCH_ATTRIBUTE;
794 if ((sa->systemFlags & DS_FLAG_ATTR_NOT_REPLICATED) || (sa->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED)) {
795 /* if the attribute is not replicated (0x00000001)
796 * or constructed (0x00000004) it has no metadata
798 continue;
801 #if W2K3_LINKED_ATTRIBUTES
802 if (sa->linkID != 0 && dsdb_functional_level(ldb) > DS_DOMAIN_FUNCTION_2000) {
803 ret = replmd_add_fix_la(module, e, ac->seq_num, our_invocation_id, t, &guid, sa);
804 if (ret != LDB_SUCCESS) {
805 talloc_free(ac);
806 return ret;
808 /* linked attributes are not stored in
809 replPropertyMetaData in FL above w2k */
810 continue;
812 #endif
814 m->attid = sa->attributeID_id;
815 m->version = 1;
816 m->originating_change_time = now;
817 m->originating_invocation_id = *our_invocation_id;
818 m->originating_usn = ac->seq_num;
819 m->local_usn = ac->seq_num;
820 ni++;
823 /* fix meta data count */
824 nmd.ctr.ctr1.count = ni;
827 * sort meta data array, and move the rdn attribute entry to the end
829 ret = replmd_replPropertyMetaDataCtr1_sort(&nmd.ctr.ctr1, ac->schema, msg->dn);
830 if (ret != LDB_SUCCESS) {
831 talloc_free(ac);
832 return ret;
835 /* generated NDR encoded values */
836 ndr_err = ndr_push_struct_blob(&nmd_value, msg,
837 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
838 &nmd,
839 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
840 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
841 ldb_oom(ldb);
842 talloc_free(ac);
843 return LDB_ERR_OPERATIONS_ERROR;
847 * add the autogenerated values
849 ret = dsdb_msg_add_guid(msg, &guid, "objectGUID");
850 if (ret != LDB_SUCCESS) {
851 ldb_oom(ldb);
852 talloc_free(ac);
853 return ret;
855 ret = ldb_msg_add_string(msg, "whenChanged", time_str);
856 if (ret != LDB_SUCCESS) {
857 ldb_oom(ldb);
858 talloc_free(ac);
859 return ret;
861 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", ac->seq_num);
862 if (ret != LDB_SUCCESS) {
863 ldb_oom(ldb);
864 talloc_free(ac);
865 return ret;
867 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ac->seq_num);
868 if (ret != LDB_SUCCESS) {
869 ldb_oom(ldb);
870 talloc_free(ac);
871 return ret;
873 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
874 if (ret != LDB_SUCCESS) {
875 ldb_oom(ldb);
876 talloc_free(ac);
877 return ret;
881 * sort the attributes by attid before storing the object
883 replmd_ldb_message_sort(msg, ac->schema);
885 ret = ldb_build_add_req(&down_req, ldb, ac,
886 msg,
887 req->controls,
888 ac, replmd_op_callback,
889 req);
890 if (ret != LDB_SUCCESS) {
891 talloc_free(ac);
892 return ret;
895 /* mark the control done */
896 if (control) {
897 control->critical = 0;
900 /* go on with the call chain */
901 return ldb_next_request(module, down_req);
906 * update the replPropertyMetaData for one element
908 static int replmd_update_rpmd_element(struct ldb_context *ldb,
909 struct ldb_message *msg,
910 struct ldb_message_element *el,
911 struct replPropertyMetaDataBlob *omd,
912 const struct dsdb_schema *schema,
913 uint64_t *seq_num,
914 const struct GUID *our_invocation_id,
915 NTTIME now)
917 int i;
918 const struct dsdb_attribute *a;
919 struct replPropertyMetaData1 *md1;
921 a = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
922 if (a == NULL) {
923 DEBUG(0,(__location__ ": Unable to find attribute %s in schema\n",
924 el->name));
925 return LDB_ERR_OPERATIONS_ERROR;
928 if ((a->systemFlags & DS_FLAG_ATTR_NOT_REPLICATED) || (a->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED)) {
929 return LDB_SUCCESS;
932 for (i=0; i<omd->ctr.ctr1.count; i++) {
933 if (a->attributeID_id == omd->ctr.ctr1.array[i].attid) break;
936 #if W2K3_LINKED_ATTRIBUTES
937 if (a->linkID != 0 && dsdb_functional_level(ldb) > DS_DOMAIN_FUNCTION_2000) {
938 /* linked attributes are not stored in
939 replPropertyMetaData in FL above w2k, but we do
940 raise the seqnum for the object */
941 if (*seq_num == 0 &&
942 ldb_sequence_number(ldb, LDB_SEQ_NEXT, seq_num) != LDB_SUCCESS) {
943 return LDB_ERR_OPERATIONS_ERROR;
945 return LDB_SUCCESS;
947 #endif
949 if (i == omd->ctr.ctr1.count) {
950 /* we need to add a new one */
951 omd->ctr.ctr1.array = talloc_realloc(msg, omd->ctr.ctr1.array,
952 struct replPropertyMetaData1, omd->ctr.ctr1.count+1);
953 if (omd->ctr.ctr1.array == NULL) {
954 ldb_oom(ldb);
955 return LDB_ERR_OPERATIONS_ERROR;
957 omd->ctr.ctr1.count++;
958 ZERO_STRUCT(omd->ctr.ctr1.array[i]);
961 /* Get a new sequence number from the backend. We only do this
962 * if we have a change that requires a new
963 * replPropertyMetaData element
965 if (*seq_num == 0) {
966 int ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, seq_num);
967 if (ret != LDB_SUCCESS) {
968 return LDB_ERR_OPERATIONS_ERROR;
972 md1 = &omd->ctr.ctr1.array[i];
973 md1->version++;
974 md1->attid = a->attributeID_id;
975 md1->originating_change_time = now;
976 md1->originating_invocation_id = *our_invocation_id;
977 md1->originating_usn = *seq_num;
978 md1->local_usn = *seq_num;
980 return LDB_SUCCESS;
984 * update the replPropertyMetaData object each time we modify an
985 * object. This is needed for DRS replication, as the merge on the
986 * client is based on this object
988 static int replmd_update_rpmd(struct ldb_module *module,
989 const struct dsdb_schema *schema,
990 struct ldb_message *msg, uint64_t *seq_num,
991 time_t t)
993 const struct ldb_val *omd_value;
994 enum ndr_err_code ndr_err;
995 struct replPropertyMetaDataBlob omd;
996 int i;
997 NTTIME now;
998 const struct GUID *our_invocation_id;
999 int ret;
1000 const char *attrs[] = { "replPropertyMetaData" , NULL };
1001 struct ldb_result *res;
1002 struct ldb_context *ldb;
1004 ldb = ldb_module_get_ctx(module);
1006 our_invocation_id = samdb_ntds_invocation_id(ldb);
1007 if (!our_invocation_id) {
1008 /* this happens during an initial vampire while
1009 updating the schema */
1010 DEBUG(5,("No invocationID - skipping replPropertyMetaData update\n"));
1011 return LDB_SUCCESS;
1014 unix_to_nt_time(&now, t);
1016 /* search for the existing replPropertyMetaDataBlob */
1017 ret = dsdb_search_dn_with_deleted(ldb, msg, &res, msg->dn, attrs);
1018 if (ret != LDB_SUCCESS || res->count != 1) {
1019 DEBUG(0,(__location__ ": Object %s failed to find replPropertyMetaData\n",
1020 ldb_dn_get_linearized(msg->dn)));
1021 return LDB_ERR_OPERATIONS_ERROR;
1025 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
1026 if (!omd_value) {
1027 DEBUG(0,(__location__ ": Object %s does not have a replPropertyMetaData attribute\n",
1028 ldb_dn_get_linearized(msg->dn)));
1029 return LDB_ERR_OPERATIONS_ERROR;
1032 ndr_err = ndr_pull_struct_blob(omd_value, msg,
1033 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")), &omd,
1034 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
1035 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1036 DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s\n",
1037 ldb_dn_get_linearized(msg->dn)));
1038 return LDB_ERR_OPERATIONS_ERROR;
1041 if (omd.version != 1) {
1042 DEBUG(0,(__location__ ": bad version %u in replPropertyMetaData for %s\n",
1043 omd.version, ldb_dn_get_linearized(msg->dn)));
1044 return LDB_ERR_OPERATIONS_ERROR;
1047 for (i=0; i<msg->num_elements; i++) {
1048 ret = replmd_update_rpmd_element(ldb, msg, &msg->elements[i], &omd, schema, seq_num,
1049 our_invocation_id, now);
1050 if (ret != LDB_SUCCESS) {
1051 return ret;
1056 * replmd_update_rpmd_element has done an update if the
1057 * seq_num is set
1059 if (*seq_num != 0) {
1060 struct ldb_val *md_value;
1061 struct ldb_message_element *el;
1063 md_value = talloc(msg, struct ldb_val);
1064 if (md_value == NULL) {
1065 ldb_oom(ldb);
1066 return LDB_ERR_OPERATIONS_ERROR;
1069 ret = replmd_replPropertyMetaDataCtr1_sort(&omd.ctr.ctr1, schema, msg->dn);
1070 if (ret != LDB_SUCCESS) {
1071 return ret;
1074 ndr_err = ndr_push_struct_blob(md_value, msg,
1075 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
1076 &omd,
1077 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
1078 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1079 DEBUG(0,(__location__ ": Failed to marshall replPropertyMetaData for %s\n",
1080 ldb_dn_get_linearized(msg->dn)));
1081 return LDB_ERR_OPERATIONS_ERROR;
1084 ret = ldb_msg_add_empty(msg, "replPropertyMetaData", LDB_FLAG_MOD_REPLACE, &el);
1085 if (ret != LDB_SUCCESS) {
1086 DEBUG(0,(__location__ ": Failed to add updated replPropertyMetaData %s\n",
1087 ldb_dn_get_linearized(msg->dn)));
1088 return ret;
1091 el->num_values = 1;
1092 el->values = md_value;
1095 return LDB_SUCCESS;
1099 struct parsed_dn {
1100 struct dsdb_dn *dsdb_dn;
1101 struct GUID *guid;
1102 struct ldb_val *v;
1105 static int parsed_dn_compare(struct parsed_dn *pdn1, struct parsed_dn *pdn2)
1107 return GUID_compare(pdn1->guid, pdn2->guid);
1110 static struct parsed_dn *parsed_dn_find(struct parsed_dn *pdn, int count, struct GUID *guid, struct ldb_dn *dn)
1112 struct parsed_dn *ret;
1113 if (dn && GUID_all_zero(guid)) {
1114 /* when updating a link using DRS, we sometimes get a
1115 NULL GUID. We then need to try and match by DN */
1116 int i;
1117 for (i=0; i<count; i++) {
1118 if (ldb_dn_compare(pdn[i].dsdb_dn->dn, dn) == 0) {
1119 dsdb_get_extended_dn_guid(pdn[i].dsdb_dn->dn, guid, "GUID");
1120 return &pdn[i];
1123 return NULL;
1125 BINARY_ARRAY_SEARCH(pdn, count, guid, guid, GUID_compare, ret);
1126 return ret;
1130 get a series of message element values as an array of DNs and GUIDs
1131 the result is sorted by GUID
1133 static int get_parsed_dns(struct ldb_module *module, TALLOC_CTX *mem_ctx,
1134 struct ldb_message_element *el, struct parsed_dn **pdn,
1135 const char *ldap_oid)
1137 int i;
1138 struct ldb_context *ldb = ldb_module_get_ctx(module);
1140 if (el == NULL) {
1141 *pdn = NULL;
1142 return LDB_SUCCESS;
1145 (*pdn) = talloc_array(mem_ctx, struct parsed_dn, el->num_values);
1146 if (!*pdn) {
1147 ldb_module_oom(module);
1148 return LDB_ERR_OPERATIONS_ERROR;
1151 for (i=0; i<el->num_values; i++) {
1152 struct ldb_val *v = &el->values[i];
1153 NTSTATUS status;
1154 struct ldb_dn *dn;
1155 struct parsed_dn *p;
1157 p = &(*pdn)[i];
1159 p->dsdb_dn = dsdb_dn_parse(*pdn, ldb, v, ldap_oid);
1160 if (p->dsdb_dn == NULL) {
1161 return LDB_ERR_INVALID_DN_SYNTAX;
1164 dn = p->dsdb_dn->dn;
1166 p->guid = talloc(*pdn, struct GUID);
1167 if (p->guid == NULL) {
1168 ldb_module_oom(module);
1169 return LDB_ERR_OPERATIONS_ERROR;
1172 status = dsdb_get_extended_dn_guid(dn, p->guid, "GUID");
1173 if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
1174 /* we got a DN without a GUID - go find the GUID */
1175 int ret = dsdb_module_guid_by_dn(module, dn, p->guid);
1176 if (ret != LDB_SUCCESS) {
1177 ldb_asprintf_errstring(ldb, "Unable to find GUID for DN %s\n",
1178 ldb_dn_get_linearized(dn));
1179 return ret;
1181 } else if (!NT_STATUS_IS_OK(status)) {
1182 return LDB_ERR_OPERATIONS_ERROR;
1185 /* keep a pointer to the original ldb_val */
1186 p->v = v;
1189 qsort(*pdn, el->num_values, sizeof((*pdn)[0]), (comparison_fn_t)parsed_dn_compare);
1191 return LDB_SUCCESS;
1195 build a new extended DN, including all meta data fields
1197 DELETED = 1 or missing
1198 RMD_ADDTIME = originating_add_time
1199 RMD_INVOCID = originating_invocation_id
1200 RMD_CHANGETIME = originating_change_time
1201 RMD_ORIGINATING_USN = originating_usn
1202 RMD_LOCAL_USN = local_usn
1203 RMD_VERSION = version
1205 static int replmd_build_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
1206 const struct GUID *invocation_id, uint64_t seq_num,
1207 uint64_t local_usn, NTTIME nttime, uint32_t version, bool deleted)
1209 struct ldb_dn *dn = dsdb_dn->dn;
1210 const char *tstring, *usn_string;
1211 struct ldb_val tval;
1212 struct ldb_val iid;
1213 struct ldb_val usnv, local_usnv;
1214 struct ldb_val vers;
1215 NTSTATUS status;
1216 int ret;
1217 const char *dnstring;
1218 char *vstring;
1220 tstring = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)nttime);
1221 if (!tstring) {
1222 return LDB_ERR_OPERATIONS_ERROR;
1224 tval = data_blob_string_const(tstring);
1226 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)seq_num);
1227 if (!usn_string) {
1228 return LDB_ERR_OPERATIONS_ERROR;
1230 usnv = data_blob_string_const(usn_string);
1232 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)local_usn);
1233 if (!usn_string) {
1234 return LDB_ERR_OPERATIONS_ERROR;
1236 local_usnv = data_blob_string_const(usn_string);
1238 vstring = talloc_asprintf(mem_ctx, "%lu", (unsigned long)version);
1239 vers = data_blob_string_const(vstring);
1241 status = GUID_to_ndr_blob(invocation_id, dn, &iid);
1242 if (!NT_STATUS_IS_OK(status)) {
1243 return LDB_ERR_OPERATIONS_ERROR;
1246 if (deleted) {
1247 struct ldb_val dv;
1248 dv = data_blob_string_const("1");
1249 ret = ldb_dn_set_extended_component(dn, "DELETED", &dv);
1250 } else {
1251 ret = ldb_dn_set_extended_component(dn, "DELETED", NULL);
1253 if (ret != LDB_SUCCESS) return ret;
1254 ret = ldb_dn_set_extended_component(dn, "RMD_ADDTIME", &tval);
1255 if (ret != LDB_SUCCESS) return ret;
1256 ret = ldb_dn_set_extended_component(dn, "RMD_INVOCID", &iid);
1257 if (ret != LDB_SUCCESS) return ret;
1258 ret = ldb_dn_set_extended_component(dn, "RMD_CHANGETIME", &tval);
1259 if (ret != LDB_SUCCESS) return ret;
1260 ret = ldb_dn_set_extended_component(dn, "RMD_LOCAL_USN", &local_usnv);
1261 if (ret != LDB_SUCCESS) return ret;
1262 ret = ldb_dn_set_extended_component(dn, "RMD_ORIGINATING_USN", &usnv);
1263 if (ret != LDB_SUCCESS) return ret;
1264 ret = ldb_dn_set_extended_component(dn, "RMD_VERSION", &vers);
1265 if (ret != LDB_SUCCESS) return ret;
1267 dnstring = dsdb_dn_get_extended_linearized(mem_ctx, dsdb_dn, 1);
1268 if (dnstring == NULL) {
1269 return LDB_ERR_OPERATIONS_ERROR;
1271 *v = data_blob_string_const(dnstring);
1273 return LDB_SUCCESS;
1276 static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
1277 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
1278 uint64_t seq_num, uint64_t local_usn, NTTIME nttime,
1279 uint32_t version, bool deleted);
1282 check if any links need upgrading from w2k format
1284 static int replmd_check_upgrade_links(struct parsed_dn *dns, uint32_t count, const struct GUID *invocation_id)
1286 int i;
1287 for (i=0; i<count; i++) {
1288 NTSTATUS status;
1289 uint32_t version;
1290 int ret;
1292 status = dsdb_get_extended_dn_uint32(dns[i].dsdb_dn->dn, &version, "RMD_VERSION");
1293 if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
1294 continue;
1297 /* it's an old one that needs upgrading */
1298 ret = replmd_update_la_val(dns, dns[i].v, dns[i].dsdb_dn, dns[i].dsdb_dn, invocation_id,
1299 1, 1, 0, 0, false);
1300 if (ret != LDB_SUCCESS) {
1301 return ret;
1304 return LDB_SUCCESS;
1308 update an extended DN, including all meta data fields
1310 see replmd_build_la_val for value names
1312 static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
1313 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
1314 uint64_t seq_num, uint64_t local_usn, NTTIME nttime,
1315 uint32_t version, bool deleted)
1317 struct ldb_dn *dn = dsdb_dn->dn;
1318 const char *tstring, *usn_string;
1319 struct ldb_val tval;
1320 struct ldb_val iid;
1321 struct ldb_val usnv, local_usnv;
1322 struct ldb_val vers;
1323 const struct ldb_val *old_addtime;
1324 uint32_t old_version;
1325 NTSTATUS status;
1326 int ret;
1327 const char *dnstring;
1328 char *vstring;
1330 tstring = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)nttime);
1331 if (!tstring) {
1332 return LDB_ERR_OPERATIONS_ERROR;
1334 tval = data_blob_string_const(tstring);
1336 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)seq_num);
1337 if (!usn_string) {
1338 return LDB_ERR_OPERATIONS_ERROR;
1340 usnv = data_blob_string_const(usn_string);
1342 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)local_usn);
1343 if (!usn_string) {
1344 return LDB_ERR_OPERATIONS_ERROR;
1346 local_usnv = data_blob_string_const(usn_string);
1348 status = GUID_to_ndr_blob(invocation_id, dn, &iid);
1349 if (!NT_STATUS_IS_OK(status)) {
1350 return LDB_ERR_OPERATIONS_ERROR;
1353 if (deleted) {
1354 struct ldb_val dv;
1355 dv = data_blob_string_const("1");
1356 ret = ldb_dn_set_extended_component(dn, "DELETED", &dv);
1357 } else {
1358 ret = ldb_dn_set_extended_component(dn, "DELETED", NULL);
1360 if (ret != LDB_SUCCESS) return ret;
1362 /* get the ADDTIME from the original */
1363 old_addtime = ldb_dn_get_extended_component(old_dsdb_dn->dn, "RMD_ADDTIME");
1364 if (old_addtime == NULL) {
1365 old_addtime = &tval;
1367 if (dsdb_dn != old_dsdb_dn) {
1368 ret = ldb_dn_set_extended_component(dn, "RMD_ADDTIME", old_addtime);
1369 if (ret != LDB_SUCCESS) return ret;
1372 /* use our invocation id */
1373 ret = ldb_dn_set_extended_component(dn, "RMD_INVOCID", &iid);
1374 if (ret != LDB_SUCCESS) return ret;
1376 /* changetime is the current time */
1377 ret = ldb_dn_set_extended_component(dn, "RMD_CHANGETIME", &tval);
1378 if (ret != LDB_SUCCESS) return ret;
1380 /* update the USN */
1381 ret = ldb_dn_set_extended_component(dn, "RMD_ORIGINATING_USN", &usnv);
1382 if (ret != LDB_SUCCESS) return ret;
1384 ret = ldb_dn_set_extended_component(dn, "RMD_LOCAL_USN", &local_usnv);
1385 if (ret != LDB_SUCCESS) return ret;
1387 /* increase the version by 1 */
1388 status = dsdb_get_extended_dn_uint32(old_dsdb_dn->dn, &old_version, "RMD_VERSION");
1389 if (NT_STATUS_IS_OK(status) && old_version >= version) {
1390 version = old_version+1;
1392 vstring = talloc_asprintf(dn, "%lu", (unsigned long)version);
1393 vers = data_blob_string_const(vstring);
1394 ret = ldb_dn_set_extended_component(dn, "RMD_VERSION", &vers);
1395 if (ret != LDB_SUCCESS) return ret;
1397 dnstring = dsdb_dn_get_extended_linearized(mem_ctx, dsdb_dn, 1);
1398 if (dnstring == NULL) {
1399 return LDB_ERR_OPERATIONS_ERROR;
1401 *v = data_blob_string_const(dnstring);
1403 return LDB_SUCCESS;
1407 handle adding a linked attribute
1409 static int replmd_modify_la_add(struct ldb_module *module,
1410 struct dsdb_schema *schema,
1411 struct ldb_message *msg,
1412 struct ldb_message_element *el,
1413 struct ldb_message_element *old_el,
1414 const struct dsdb_attribute *schema_attr,
1415 uint64_t seq_num,
1416 time_t t,
1417 struct GUID *msg_guid)
1419 int i;
1420 struct parsed_dn *dns, *old_dns;
1421 TALLOC_CTX *tmp_ctx = talloc_new(msg);
1422 int ret;
1423 struct ldb_val *new_values = NULL;
1424 unsigned int num_new_values = 0;
1425 unsigned old_num_values = old_el?old_el->num_values:0;
1426 const struct GUID *invocation_id;
1427 struct ldb_context *ldb = ldb_module_get_ctx(module);
1428 NTTIME now;
1430 unix_to_nt_time(&now, t);
1432 ret = get_parsed_dns(module, tmp_ctx, el, &dns, schema_attr->syntax->ldap_oid);
1433 if (ret != LDB_SUCCESS) {
1434 talloc_free(tmp_ctx);
1435 return ret;
1438 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns, schema_attr->syntax->ldap_oid);
1439 if (ret != LDB_SUCCESS) {
1440 talloc_free(tmp_ctx);
1441 return ret;
1444 invocation_id = samdb_ntds_invocation_id(ldb);
1445 if (!invocation_id) {
1446 talloc_free(tmp_ctx);
1447 return LDB_ERR_OPERATIONS_ERROR;
1450 ret = replmd_check_upgrade_links(old_dns, old_num_values, invocation_id);
1451 if (ret != LDB_SUCCESS) {
1452 talloc_free(tmp_ctx);
1453 return ret;
1456 /* for each new value, see if it exists already with the same GUID */
1457 for (i=0; i<el->num_values; i++) {
1458 struct parsed_dn *p = parsed_dn_find(old_dns, old_num_values, dns[i].guid, NULL);
1459 if (p == NULL) {
1460 /* this is a new linked attribute value */
1461 new_values = talloc_realloc(tmp_ctx, new_values, struct ldb_val, num_new_values+1);
1462 if (new_values == NULL) {
1463 ldb_module_oom(module);
1464 talloc_free(tmp_ctx);
1465 return LDB_ERR_OPERATIONS_ERROR;
1467 ret = replmd_build_la_val(new_values, &new_values[num_new_values], dns[i].dsdb_dn,
1468 invocation_id, seq_num, seq_num, now, 0, false);
1469 if (ret != LDB_SUCCESS) {
1470 talloc_free(tmp_ctx);
1471 return ret;
1473 num_new_values++;
1474 } else {
1475 /* this is only allowed if the GUID was
1476 previously deleted. */
1477 const struct ldb_val *v;
1478 v = ldb_dn_get_extended_component(p->dsdb_dn->dn, "DELETED");
1479 if (v == NULL) {
1480 ldb_asprintf_errstring(ldb, "Attribute %s already exists for target GUID %s",
1481 el->name, GUID_string(tmp_ctx, p->guid));
1482 talloc_free(tmp_ctx);
1483 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
1485 ret = replmd_update_la_val(old_el->values, p->v, dns[i].dsdb_dn, p->dsdb_dn,
1486 invocation_id, seq_num, seq_num, now, 0, false);
1487 if (ret != LDB_SUCCESS) {
1488 talloc_free(tmp_ctx);
1489 return ret;
1493 ret = replmd_add_backlink(module, schema, msg_guid, dns[i].guid, true, schema_attr, true);
1494 if (ret != LDB_SUCCESS) {
1495 talloc_free(tmp_ctx);
1496 return ret;
1500 /* add the new ones on to the end of the old values, constructing a new el->values */
1501 el->values = talloc_realloc(msg->elements, old_el?old_el->values:NULL,
1502 struct ldb_val,
1503 old_num_values+num_new_values);
1504 if (el->values == NULL) {
1505 ldb_module_oom(module);
1506 return LDB_ERR_OPERATIONS_ERROR;
1509 memcpy(&el->values[old_num_values], new_values, num_new_values*sizeof(struct ldb_val));
1510 el->num_values = old_num_values + num_new_values;
1512 talloc_steal(msg->elements, el->values);
1513 talloc_steal(el->values, new_values);
1515 talloc_free(tmp_ctx);
1517 /* we now tell the backend to replace all existing values
1518 with the one we have constructed */
1519 el->flags = LDB_FLAG_MOD_REPLACE;
1521 return LDB_SUCCESS;
1526 handle deleting all active linked attributes
1528 static int replmd_modify_la_delete(struct ldb_module *module,
1529 struct dsdb_schema *schema,
1530 struct ldb_message *msg,
1531 struct ldb_message_element *el,
1532 struct ldb_message_element *old_el,
1533 const struct dsdb_attribute *schema_attr,
1534 uint64_t seq_num,
1535 time_t t,
1536 struct GUID *msg_guid)
1538 int i;
1539 struct parsed_dn *dns, *old_dns;
1540 TALLOC_CTX *tmp_ctx = talloc_new(msg);
1541 int ret;
1542 const struct GUID *invocation_id;
1543 struct ldb_context *ldb = ldb_module_get_ctx(module);
1544 NTTIME now;
1546 unix_to_nt_time(&now, t);
1548 /* check if there is nothing to delete */
1549 if ((!old_el || old_el->num_values == 0) &&
1550 el->num_values == 0) {
1551 return LDB_SUCCESS;
1554 if (!old_el || old_el->num_values == 0) {
1555 return LDB_ERR_NO_SUCH_ATTRIBUTE;
1558 ret = get_parsed_dns(module, tmp_ctx, el, &dns, schema_attr->syntax->ldap_oid);
1559 if (ret != LDB_SUCCESS) {
1560 talloc_free(tmp_ctx);
1561 return ret;
1564 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns, schema_attr->syntax->ldap_oid);
1565 if (ret != LDB_SUCCESS) {
1566 talloc_free(tmp_ctx);
1567 return ret;
1570 invocation_id = samdb_ntds_invocation_id(ldb);
1571 if (!invocation_id) {
1572 return LDB_ERR_OPERATIONS_ERROR;
1575 ret = replmd_check_upgrade_links(old_dns, old_el->num_values, invocation_id);
1576 if (ret != LDB_SUCCESS) {
1577 talloc_free(tmp_ctx);
1578 return ret;
1581 el->values = NULL;
1583 /* see if we are being asked to delete any links that
1584 don't exist or are already deleted */
1585 for (i=0; i<el->num_values; i++) {
1586 struct parsed_dn *p = &dns[i];
1587 struct parsed_dn *p2;
1588 const struct ldb_val *v;
1590 p2 = parsed_dn_find(old_dns, old_el->num_values, p->guid, NULL);
1591 if (!p2) {
1592 ldb_asprintf_errstring(ldb, "Attribute %s doesn't exist for target GUID %s",
1593 el->name, GUID_string(tmp_ctx, p->guid));
1594 return LDB_ERR_NO_SUCH_ATTRIBUTE;
1596 v = ldb_dn_get_extended_component(p2->dsdb_dn->dn, "DELETED");
1597 if (v) {
1598 ldb_asprintf_errstring(ldb, "Attribute %s already deleted for target GUID %s",
1599 el->name, GUID_string(tmp_ctx, p->guid));
1600 return LDB_ERR_NO_SUCH_ATTRIBUTE;
1604 /* for each new value, see if it exists already with the same GUID
1605 if it is not already deleted and matches the delete list then delete it
1607 for (i=0; i<old_el->num_values; i++) {
1608 struct parsed_dn *p = &old_dns[i];
1609 const struct ldb_val *v;
1611 if (el->num_values && parsed_dn_find(dns, el->num_values, p->guid, NULL) == NULL) {
1612 continue;
1615 v = ldb_dn_get_extended_component(p->dsdb_dn->dn, "DELETED");
1616 if (v != NULL) continue;
1618 ret = replmd_update_la_val(old_el->values, p->v, p->dsdb_dn, p->dsdb_dn,
1619 invocation_id, seq_num, seq_num, now, 0, true);
1620 if (ret != LDB_SUCCESS) {
1621 talloc_free(tmp_ctx);
1622 return ret;
1625 ret = replmd_add_backlink(module, schema, msg_guid, old_dns[i].guid, false, schema_attr, true);
1626 if (ret != LDB_SUCCESS) {
1627 talloc_free(tmp_ctx);
1628 return ret;
1632 el->values = talloc_steal(msg->elements, old_el->values);
1633 el->num_values = old_el->num_values;
1635 talloc_free(tmp_ctx);
1637 /* we now tell the backend to replace all existing values
1638 with the one we have constructed */
1639 el->flags = LDB_FLAG_MOD_REPLACE;
1641 return LDB_SUCCESS;
1645 handle replacing a linked attribute
1647 static int replmd_modify_la_replace(struct ldb_module *module,
1648 struct dsdb_schema *schema,
1649 struct ldb_message *msg,
1650 struct ldb_message_element *el,
1651 struct ldb_message_element *old_el,
1652 const struct dsdb_attribute *schema_attr,
1653 uint64_t seq_num,
1654 time_t t,
1655 struct GUID *msg_guid)
1657 int i;
1658 struct parsed_dn *dns, *old_dns;
1659 TALLOC_CTX *tmp_ctx = talloc_new(msg);
1660 int ret;
1661 const struct GUID *invocation_id;
1662 struct ldb_context *ldb = ldb_module_get_ctx(module);
1663 struct ldb_val *new_values = NULL;
1664 uint32_t num_new_values = 0;
1665 unsigned old_num_values = old_el?old_el->num_values:0;
1666 NTTIME now;
1668 unix_to_nt_time(&now, t);
1670 /* check if there is nothing to replace */
1671 if ((!old_el || old_el->num_values == 0) &&
1672 el->num_values == 0) {
1673 return LDB_SUCCESS;
1676 ret = get_parsed_dns(module, tmp_ctx, el, &dns, schema_attr->syntax->ldap_oid);
1677 if (ret != LDB_SUCCESS) {
1678 talloc_free(tmp_ctx);
1679 return ret;
1682 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns, schema_attr->syntax->ldap_oid);
1683 if (ret != LDB_SUCCESS) {
1684 talloc_free(tmp_ctx);
1685 return ret;
1688 invocation_id = samdb_ntds_invocation_id(ldb);
1689 if (!invocation_id) {
1690 return LDB_ERR_OPERATIONS_ERROR;
1693 ret = replmd_check_upgrade_links(old_dns, old_num_values, invocation_id);
1694 if (ret != LDB_SUCCESS) {
1695 talloc_free(tmp_ctx);
1696 return ret;
1699 /* mark all the old ones as deleted */
1700 for (i=0; i<old_num_values; i++) {
1701 struct parsed_dn *old_p = &old_dns[i];
1702 struct parsed_dn *p;
1703 const struct ldb_val *v;
1705 v = ldb_dn_get_extended_component(old_p->dsdb_dn->dn, "DELETED");
1706 if (v) continue;
1708 ret = replmd_add_backlink(module, schema, msg_guid, old_dns[i].guid, false, schema_attr, false);
1709 if (ret != LDB_SUCCESS) {
1710 talloc_free(tmp_ctx);
1711 return ret;
1714 p = parsed_dn_find(dns, el->num_values, old_p->guid, NULL);
1715 if (p) {
1716 /* we don't delete it if we are re-adding it */
1717 continue;
1720 ret = replmd_update_la_val(old_el->values, old_p->v, old_p->dsdb_dn, old_p->dsdb_dn,
1721 invocation_id, seq_num, seq_num, now, 0, true);
1722 if (ret != LDB_SUCCESS) {
1723 talloc_free(tmp_ctx);
1724 return ret;
1728 /* for each new value, either update its meta-data, or add it
1729 * to old_el
1731 for (i=0; i<el->num_values; i++) {
1732 struct parsed_dn *p = &dns[i], *old_p;
1734 if (old_dns &&
1735 (old_p = parsed_dn_find(old_dns,
1736 old_num_values, p->guid, NULL)) != NULL) {
1737 /* update in place */
1738 ret = replmd_update_la_val(old_el->values, old_p->v, old_p->dsdb_dn,
1739 old_p->dsdb_dn, invocation_id,
1740 seq_num, seq_num, now, 0, false);
1741 if (ret != LDB_SUCCESS) {
1742 talloc_free(tmp_ctx);
1743 return ret;
1745 } else {
1746 /* add a new one */
1747 new_values = talloc_realloc(tmp_ctx, new_values, struct ldb_val,
1748 num_new_values+1);
1749 if (new_values == NULL) {
1750 ldb_module_oom(module);
1751 talloc_free(tmp_ctx);
1752 return LDB_ERR_OPERATIONS_ERROR;
1754 ret = replmd_build_la_val(new_values, &new_values[num_new_values], dns[i].dsdb_dn,
1755 invocation_id, seq_num, seq_num, now, 0, false);
1756 if (ret != LDB_SUCCESS) {
1757 talloc_free(tmp_ctx);
1758 return ret;
1760 num_new_values++;
1763 ret = replmd_add_backlink(module, schema, msg_guid, dns[i].guid, true, schema_attr, false);
1764 if (ret != LDB_SUCCESS) {
1765 talloc_free(tmp_ctx);
1766 return ret;
1770 /* add the new values to the end of old_el */
1771 if (num_new_values != 0) {
1772 el->values = talloc_realloc(msg->elements, old_el?old_el->values:NULL,
1773 struct ldb_val, old_num_values+num_new_values);
1774 if (el->values == NULL) {
1775 ldb_module_oom(module);
1776 return LDB_ERR_OPERATIONS_ERROR;
1778 memcpy(&el->values[old_num_values], &new_values[0],
1779 sizeof(struct ldb_val)*num_new_values);
1780 el->num_values = old_num_values + num_new_values;
1781 talloc_steal(msg->elements, new_values);
1782 } else {
1783 el->values = old_el->values;
1784 el->num_values = old_el->num_values;
1785 talloc_steal(msg->elements, el->values);
1788 talloc_free(tmp_ctx);
1790 /* we now tell the backend to replace all existing values
1791 with the one we have constructed */
1792 el->flags = LDB_FLAG_MOD_REPLACE;
1794 return LDB_SUCCESS;
1799 handle linked attributes in modify requests
1801 static int replmd_modify_handle_linked_attribs(struct ldb_module *module,
1802 struct ldb_message *msg,
1803 uint64_t seq_num, time_t t)
1805 struct ldb_result *res;
1806 int ret, i;
1807 struct ldb_context *ldb = ldb_module_get_ctx(module);
1808 struct ldb_message *old_msg;
1809 struct dsdb_schema *schema = dsdb_get_schema(ldb);
1810 struct GUID old_guid;
1812 if (seq_num == 0) {
1813 /* there the replmd_update_rpmd code has already
1814 * checked and saw that there are no linked
1815 * attributes */
1816 return LDB_SUCCESS;
1819 #if !W2K3_LINKED_ATTRIBUTES
1820 return LDB_SUCCESS;
1821 #endif
1823 if (dsdb_functional_level(ldb) == DS_DOMAIN_FUNCTION_2000) {
1824 /* don't do anything special for linked attributes */
1825 return LDB_SUCCESS;
1828 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, NULL,
1829 DSDB_SEARCH_SHOW_DELETED |
1830 DSDB_SEARCH_REVEAL_INTERNALS |
1831 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT);
1832 if (ret != LDB_SUCCESS) {
1833 return ret;
1835 old_msg = res->msgs[0];
1837 old_guid = samdb_result_guid(old_msg, "objectGUID");
1839 for (i=0; i<msg->num_elements; i++) {
1840 struct ldb_message_element *el = &msg->elements[i];
1841 struct ldb_message_element *old_el, *new_el;
1842 const struct dsdb_attribute *schema_attr
1843 = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
1844 if (!schema_attr) {
1845 ldb_asprintf_errstring(ldb,
1846 "attribute %s is not a valid attribute in schema", el->name);
1847 return LDB_ERR_OBJECT_CLASS_VIOLATION;
1849 if (schema_attr->linkID == 0) {
1850 continue;
1852 if ((schema_attr->linkID & 1) == 1) {
1853 /* Odd is for the target. Illegal to modify */
1854 ldb_asprintf_errstring(ldb,
1855 "attribute %s must not be modified directly, it is a linked attribute", el->name);
1856 return LDB_ERR_UNWILLING_TO_PERFORM;
1858 old_el = ldb_msg_find_element(old_msg, el->name);
1859 switch (el->flags & LDB_FLAG_MOD_MASK) {
1860 case LDB_FLAG_MOD_REPLACE:
1861 ret = replmd_modify_la_replace(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid);
1862 break;
1863 case LDB_FLAG_MOD_DELETE:
1864 ret = replmd_modify_la_delete(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid);
1865 break;
1866 case LDB_FLAG_MOD_ADD:
1867 ret = replmd_modify_la_add(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid);
1868 break;
1869 default:
1870 ldb_asprintf_errstring(ldb,
1871 "invalid flags 0x%x for %s linked attribute",
1872 el->flags, el->name);
1873 return LDB_ERR_UNWILLING_TO_PERFORM;
1875 if (ret != LDB_SUCCESS) {
1876 return ret;
1878 if (old_el) {
1879 ldb_msg_remove_attr(old_msg, el->name);
1881 ldb_msg_add_empty(old_msg, el->name, 0, &new_el);
1882 new_el->num_values = el->num_values;
1883 new_el->values = el->values;
1885 /* TODO: this relises a bit too heavily on the exact
1886 behaviour of ldb_msg_find_element and
1887 ldb_msg_remove_element */
1888 old_el = ldb_msg_find_element(msg, el->name);
1889 if (old_el != el) {
1890 ldb_msg_remove_element(msg, old_el);
1891 i--;
1895 talloc_free(res);
1896 return ret;
1901 static int replmd_modify(struct ldb_module *module, struct ldb_request *req)
1903 struct ldb_context *ldb;
1904 struct replmd_replicated_request *ac;
1905 struct ldb_request *down_req;
1906 struct ldb_message *msg;
1907 time_t t = time(NULL);
1908 int ret;
1910 /* do not manipulate our control entries */
1911 if (ldb_dn_is_special(req->op.mod.message->dn)) {
1912 return ldb_next_request(module, req);
1915 ldb = ldb_module_get_ctx(module);
1917 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_modify\n");
1919 ac = replmd_ctx_init(module, req);
1920 if (!ac) {
1921 return LDB_ERR_OPERATIONS_ERROR;
1924 /* we have to copy the message as the caller might have it as a const */
1925 msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
1926 if (msg == NULL) {
1927 ldb_oom(ldb);
1928 talloc_free(ac);
1929 return LDB_ERR_OPERATIONS_ERROR;
1932 ret = replmd_update_rpmd(module, ac->schema, msg, &ac->seq_num, t);
1933 if (ret != LDB_SUCCESS) {
1934 talloc_free(ac);
1935 return ret;
1938 ret = replmd_modify_handle_linked_attribs(module, msg, ac->seq_num, t);
1939 if (ret != LDB_SUCCESS) {
1940 talloc_free(ac);
1941 return ret;
1944 /* TODO:
1945 * - replace the old object with the newly constructed one
1948 ret = ldb_build_mod_req(&down_req, ldb, ac,
1949 msg,
1950 req->controls,
1951 ac, replmd_op_callback,
1952 req);
1953 if (ret != LDB_SUCCESS) {
1954 talloc_free(ac);
1955 return ret;
1957 talloc_steal(down_req, msg);
1959 /* we only change whenChanged and uSNChanged if the seq_num
1960 has changed */
1961 if (ac->seq_num != 0) {
1962 if (add_time_element(msg, "whenChanged", t) != LDB_SUCCESS) {
1963 talloc_free(ac);
1964 return ret;
1967 if (add_uint64_element(msg, "uSNChanged", ac->seq_num) != LDB_SUCCESS) {
1968 talloc_free(ac);
1969 return ret;
1973 /* go on with the call chain */
1974 return ldb_next_request(module, down_req);
1977 static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares);
1980 handle a rename request
1982 On a rename we need to do an extra ldb_modify which sets the
1983 whenChanged and uSNChanged attributes. We do this in a callback after the success.
1985 static int replmd_rename(struct ldb_module *module, struct ldb_request *req)
1987 struct ldb_context *ldb;
1988 struct replmd_replicated_request *ac;
1989 int ret;
1990 struct ldb_request *down_req;
1992 /* do not manipulate our control entries */
1993 if (ldb_dn_is_special(req->op.mod.message->dn)) {
1994 return ldb_next_request(module, req);
1997 ldb = ldb_module_get_ctx(module);
1999 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_rename\n");
2001 ac = replmd_ctx_init(module, req);
2002 if (!ac) {
2003 return LDB_ERR_OPERATIONS_ERROR;
2005 ret = ldb_build_rename_req(&down_req, ldb, ac,
2006 ac->req->op.rename.olddn,
2007 ac->req->op.rename.newdn,
2008 ac->req->controls,
2009 ac, replmd_rename_callback,
2010 ac->req);
2012 if (ret != LDB_SUCCESS) {
2013 talloc_free(ac);
2014 return ret;
2017 /* go on with the call chain */
2018 return ldb_next_request(module, down_req);
2021 /* After the rename is compleated, update the whenchanged etc */
2022 static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares)
2024 struct ldb_context *ldb;
2025 struct replmd_replicated_request *ac;
2026 struct ldb_request *down_req;
2027 struct ldb_message *msg;
2028 time_t t = time(NULL);
2029 int ret;
2031 ac = talloc_get_type(req->context, struct replmd_replicated_request);
2032 ldb = ldb_module_get_ctx(ac->module);
2034 if (ares->error != LDB_SUCCESS) {
2035 return ldb_module_done(ac->req, ares->controls,
2036 ares->response, ares->error);
2039 if (ares->type != LDB_REPLY_DONE) {
2040 ldb_set_errstring(ldb,
2041 "invalid ldb_reply_type in callback");
2042 talloc_free(ares);
2043 return ldb_module_done(ac->req, NULL, NULL,
2044 LDB_ERR_OPERATIONS_ERROR);
2047 /* Get a sequence number from the backend */
2048 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ac->seq_num);
2049 if (ret != LDB_SUCCESS) {
2050 return ret;
2053 /* TODO:
2054 * - replace the old object with the newly constructed one
2057 msg = ldb_msg_new(ac);
2058 if (msg == NULL) {
2059 ldb_oom(ldb);
2060 return LDB_ERR_OPERATIONS_ERROR;
2063 msg->dn = ac->req->op.rename.newdn;
2065 ret = ldb_build_mod_req(&down_req, ldb, ac,
2066 msg,
2067 req->controls,
2068 ac, replmd_op_callback,
2069 req);
2071 if (ret != LDB_SUCCESS) {
2072 talloc_free(ac);
2073 return ret;
2075 talloc_steal(down_req, msg);
2077 if (add_time_element(msg, "whenChanged", t) != LDB_SUCCESS) {
2078 talloc_free(ac);
2079 return ret;
2082 if (add_uint64_element(msg, "uSNChanged", ac->seq_num) != LDB_SUCCESS) {
2083 talloc_free(ac);
2084 return ret;
2087 /* go on with the call chain - do the modify after the rename */
2088 return ldb_next_request(ac->module, down_req);
2092 remove links from objects that point at this object when an object
2093 is deleted
2095 static int replmd_delete_remove_link(struct ldb_module *module,
2096 struct dsdb_schema *schema,
2097 struct ldb_dn *dn,
2098 struct ldb_message_element *el,
2099 const struct dsdb_attribute *sa)
2101 int i;
2102 TALLOC_CTX *tmp_ctx = talloc_new(module);
2103 struct ldb_context *ldb = ldb_module_get_ctx(module);
2105 for (i=0; i<el->num_values; i++) {
2106 struct dsdb_dn *dsdb_dn;
2107 NTSTATUS status;
2108 int ret;
2109 struct GUID guid2;
2110 struct ldb_message *msg;
2111 const struct dsdb_attribute *target_attr;
2112 struct ldb_message_element *el2;
2113 struct ldb_val dn_val;
2115 if (dsdb_dn_is_deleted_val(&el->values[i])) {
2116 continue;
2119 dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i], sa->syntax->ldap_oid);
2120 if (!dsdb_dn) {
2121 talloc_free(tmp_ctx);
2122 return LDB_ERR_OPERATIONS_ERROR;
2125 status = dsdb_get_extended_dn_guid(dsdb_dn->dn, &guid2, "GUID");
2126 if (!NT_STATUS_IS_OK(status)) {
2127 talloc_free(tmp_ctx);
2128 return LDB_ERR_OPERATIONS_ERROR;
2131 /* remove the link */
2132 msg = ldb_msg_new(tmp_ctx);
2133 if (!msg) {
2134 ldb_module_oom(module);
2135 talloc_free(tmp_ctx);
2136 return LDB_ERR_OPERATIONS_ERROR;
2140 msg->dn = dsdb_dn->dn;
2142 target_attr = dsdb_attribute_by_linkID(schema, sa->linkID ^ 1);
2143 if (target_attr == NULL) {
2144 continue;
2147 ret = ldb_msg_add_empty(msg, target_attr->lDAPDisplayName, LDB_FLAG_MOD_DELETE, &el2);
2148 if (ret != LDB_SUCCESS) {
2149 ldb_module_oom(module);
2150 talloc_free(tmp_ctx);
2151 return LDB_ERR_OPERATIONS_ERROR;
2153 dn_val = data_blob_string_const(ldb_dn_get_linearized(dn));
2154 el2->values = &dn_val;
2155 el2->num_values = 1;
2157 ret = dsdb_module_modify(module, msg, DSDB_FLAG_OWN_MODULE);
2158 if (ret != LDB_SUCCESS) {
2159 talloc_free(tmp_ctx);
2160 return ret;
2163 talloc_free(tmp_ctx);
2164 return LDB_SUCCESS;
2169 handle update of replication meta data for deletion of objects
2171 This also handles the mapping of delete to a rename operation
2172 to allow deletes to be replicated.
2174 static int replmd_delete(struct ldb_module *module, struct ldb_request *req)
2176 int ret = LDB_ERR_OTHER;
2177 bool retb;
2178 struct ldb_dn *old_dn, *new_dn;
2179 const char *rdn_name;
2180 const struct ldb_val *rdn_value, *new_rdn_value;
2181 struct GUID guid;
2182 struct ldb_context *ldb = ldb_module_get_ctx(module);
2183 struct dsdb_schema *schema = dsdb_get_schema(ldb);
2184 struct ldb_message *msg, *old_msg;
2185 struct ldb_message_element *el;
2186 TALLOC_CTX *tmp_ctx;
2187 struct ldb_result *res, *parent_res;
2188 const char *preserved_attrs[] = {
2189 /* yes, this really is a hard coded list. See MS-ADTS
2190 section 3.1.1.5.5.1.1 */
2191 "nTSecurityDescriptor", "attributeID", "attributeSyntax", "dNReferenceUpdate", "dNSHostName",
2192 "flatName", "governsID", "groupType", "instanceType", "lDAPDisplayName", "legacyExchangeDN",
2193 "isDeleted", "isRecycled", "lastKnownParent", "msDS-LastKnownRDN", "mS-DS-CreatorSID",
2194 "mSMQOwnerID", "nCName", "objectClass", "distinguishedName", "objectGUID", "objectSid",
2195 "oMSyntax", "proxiedObjectName", "name", "replPropertyMetaData", "sAMAccountName",
2196 "securityIdentifier", "sIDHistory", "subClassOf", "systemFlags", "trustPartner", "trustDirection",
2197 "trustType", "trustAttributes", "userAccountControl", "uSNChanged", "uSNCreated", "whenCreated",
2198 NULL};
2199 uint32_t el_count = 0;
2200 int i;
2202 tmp_ctx = talloc_new(ldb);
2204 old_dn = ldb_dn_copy(tmp_ctx, req->op.del.dn);
2206 /* we need the complete msg off disk, so we can work out which
2207 attributes need to be removed */
2208 ret = dsdb_module_search_dn(module, tmp_ctx, &res, old_dn, NULL,
2209 DSDB_SEARCH_SHOW_DELETED |
2210 DSDB_SEARCH_REVEAL_INTERNALS |
2211 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT);
2212 if (ret != LDB_SUCCESS) {
2213 talloc_free(tmp_ctx);
2214 return ret;
2216 old_msg = res->msgs[0];
2218 /* work out where we will be renaming this object to */
2219 ret = dsdb_get_deleted_objects_dn(ldb, tmp_ctx, old_dn, &new_dn);
2220 if (ret != LDB_SUCCESS) {
2221 /* this is probably an attempted delete on a partition
2222 * that doesn't allow delete operations, such as the
2223 * schema partition */
2224 ldb_asprintf_errstring(ldb, "No Deleted Objects container for DN %s",
2225 ldb_dn_get_linearized(old_dn));
2226 talloc_free(tmp_ctx);
2227 return LDB_ERR_UNWILLING_TO_PERFORM;
2230 rdn_name = ldb_dn_get_rdn_name(old_dn);
2231 rdn_value = ldb_dn_get_rdn_val(old_dn);
2233 /* get the objects GUID from the search we just did */
2234 guid = samdb_result_guid(old_msg, "objectGUID");
2236 /* Add a formatted child */
2237 retb = ldb_dn_add_child_fmt(new_dn, "%s=%s\\0ADEL:%s",
2238 rdn_name,
2239 rdn_value->data,
2240 GUID_string(tmp_ctx, &guid));
2241 if (!retb) {
2242 DEBUG(0,(__location__ ": Unable to add a formatted child to dn: %s",
2243 ldb_dn_get_linearized(new_dn)));
2244 talloc_free(tmp_ctx);
2245 return LDB_ERR_OPERATIONS_ERROR;
2249 now we need to modify the object in the following ways:
2251 - add isDeleted=TRUE
2252 - update rDN and name, with new rDN
2253 - remove linked attributes
2254 - remove objectCategory and sAMAccountType
2255 - remove attribs not on the preserved list
2256 - preserved if in above list, or is rDN
2257 - remove all linked attribs from this object
2258 - remove all links from other objects to this object
2259 - add lastKnownParent
2260 - update replPropertyMetaData?
2262 see MS-ADTS "Tombstone Requirements" section 3.1.1.5.5.1.1
2265 msg = ldb_msg_new(tmp_ctx);
2266 if (msg == NULL) {
2267 ldb_module_oom(module);
2268 talloc_free(tmp_ctx);
2269 return LDB_ERR_OPERATIONS_ERROR;
2272 msg->dn = old_dn;
2274 ret = ldb_msg_add_string(msg, "isDeleted", "TRUE");
2275 if (ret != LDB_SUCCESS) {
2276 DEBUG(0,(__location__ ": Failed to add isDeleted string to the msg\n"));
2277 ldb_module_oom(module);
2278 talloc_free(tmp_ctx);
2279 return ret;
2281 msg->elements[el_count++].flags = LDB_FLAG_MOD_ADD;
2283 /* we also mark it as recycled, meaning this object can't be
2284 recovered (we are stripping its attributes) */
2285 if (dsdb_functional_level(ldb) >= DS_DOMAIN_FUNCTION_2008_R2) {
2286 ret = ldb_msg_add_string(msg, "isRecycled", "TRUE");
2287 if (ret != LDB_SUCCESS) {
2288 DEBUG(0,(__location__ ": Failed to add isRecycled string to the msg\n"));
2289 ldb_module_oom(module);
2290 talloc_free(tmp_ctx);
2291 return ret;
2293 msg->elements[el_count++].flags = LDB_FLAG_MOD_ADD;
2296 /* we need the storage form of the parent GUID */
2297 ret = dsdb_module_search_dn(module, tmp_ctx, &parent_res,
2298 ldb_dn_get_parent(tmp_ctx, old_dn), NULL,
2299 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
2300 DSDB_SEARCH_REVEAL_INTERNALS);
2301 if (ret != LDB_SUCCESS) {
2302 talloc_free(tmp_ctx);
2303 return ret;
2306 ret = ldb_msg_add_steal_string(msg, "lastKnownParent",
2307 ldb_dn_get_extended_linearized(tmp_ctx, parent_res->msgs[0]->dn, 1));
2308 if (ret != LDB_SUCCESS) {
2309 DEBUG(0,(__location__ ": Failed to add lastKnownParent string to the msg\n"));
2310 ldb_module_oom(module);
2311 talloc_free(tmp_ctx);
2312 return ret;
2314 msg->elements[el_count++].flags = LDB_FLAG_MOD_ADD;
2316 /* work out which of the old attributes we will be removing */
2317 for (i=0; i<old_msg->num_elements; i++) {
2318 const struct dsdb_attribute *sa;
2319 el = &old_msg->elements[i];
2320 sa = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
2321 if (!sa) {
2322 talloc_free(tmp_ctx);
2323 return LDB_ERR_OPERATIONS_ERROR;
2325 if (ldb_attr_cmp(el->name, rdn_name) == 0) {
2326 /* don't remove the rDN */
2327 continue;
2330 if (sa->linkID) {
2331 ret = replmd_delete_remove_link(module, schema, old_dn, el, sa);
2332 if (ret != LDB_SUCCESS) {
2333 talloc_free(tmp_ctx);
2334 return LDB_ERR_OPERATIONS_ERROR;
2338 if (!sa->linkID && ldb_attr_in_list(preserved_attrs, el->name)) {
2339 continue;
2342 ret = ldb_msg_add_empty(msg, el->name, LDB_FLAG_MOD_DELETE, &el);
2343 if (ret != LDB_SUCCESS) {
2344 talloc_free(tmp_ctx);
2345 ldb_module_oom(module);
2346 return ret;
2350 /* work out what the new rdn value is, for updating the
2351 rDN and name fields */
2352 new_rdn_value = ldb_dn_get_rdn_val(new_dn);
2353 ret = ldb_msg_add_value(msg, rdn_name, new_rdn_value, &el);
2354 if (ret != LDB_SUCCESS) {
2355 talloc_free(tmp_ctx);
2356 return ret;
2358 el->flags = LDB_FLAG_MOD_REPLACE;
2360 el = ldb_msg_find_element(old_msg, "name");
2361 if (el) {
2362 ret = ldb_msg_add_value(msg, "name", new_rdn_value, &el);
2363 if (ret != LDB_SUCCESS) {
2364 talloc_free(tmp_ctx);
2365 return ret;
2367 el->flags = LDB_FLAG_MOD_REPLACE;
2370 ret = dsdb_module_modify(module, msg, DSDB_FLAG_OWN_MODULE);
2371 if (ret != LDB_SUCCESS) {
2372 ldb_asprintf_errstring(ldb, "replmd_delete: Failed to modify object %s in delete - %s",
2373 ldb_dn_get_linearized(old_dn), ldb_errstring(ldb));
2374 talloc_free(tmp_ctx);
2375 return ret;
2378 /* now rename onto the new DN */
2379 ret = dsdb_module_rename(module, old_dn, new_dn, 0);
2380 if (ret != LDB_SUCCESS){
2381 DEBUG(0,(__location__ ": Failed to rename object from '%s' to '%s' - %s\n",
2382 ldb_dn_get_linearized(old_dn),
2383 ldb_dn_get_linearized(new_dn),
2384 ldb_errstring(ldb)));
2385 talloc_free(tmp_ctx);
2386 return ret;
2389 talloc_free(tmp_ctx);
2391 return ldb_module_done(req, NULL, NULL, LDB_SUCCESS);
2396 static int replmd_replicated_request_error(struct replmd_replicated_request *ar, int ret)
2398 return ret;
2401 static int replmd_replicated_request_werror(struct replmd_replicated_request *ar, WERROR status)
2403 int ret = LDB_ERR_OTHER;
2404 /* TODO: do some error mapping */
2405 return ret;
2408 static int replmd_replicated_apply_add(struct replmd_replicated_request *ar)
2410 struct ldb_context *ldb;
2411 struct ldb_request *change_req;
2412 enum ndr_err_code ndr_err;
2413 struct ldb_message *msg;
2414 struct replPropertyMetaDataBlob *md;
2415 struct ldb_val md_value;
2416 uint32_t i;
2417 int ret;
2420 * TODO: check if the parent object exist
2424 * TODO: handle the conflict case where an object with the
2425 * same name exist
2428 ldb = ldb_module_get_ctx(ar->module);
2429 msg = ar->objs->objects[ar->index_current].msg;
2430 md = ar->objs->objects[ar->index_current].meta_data;
2432 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
2433 if (ret != LDB_SUCCESS) {
2434 return replmd_replicated_request_error(ar, ret);
2437 ret = ldb_msg_add_value(msg, "objectGUID", &ar->objs->objects[ar->index_current].guid_value, NULL);
2438 if (ret != LDB_SUCCESS) {
2439 return replmd_replicated_request_error(ar, ret);
2442 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
2443 if (ret != LDB_SUCCESS) {
2444 return replmd_replicated_request_error(ar, ret);
2447 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", ar->seq_num);
2448 if (ret != LDB_SUCCESS) {
2449 return replmd_replicated_request_error(ar, ret);
2452 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
2453 if (ret != LDB_SUCCESS) {
2454 return replmd_replicated_request_error(ar, ret);
2457 /* remove any message elements that have zero values */
2458 for (i=0; i<msg->num_elements; i++) {
2459 struct ldb_message_element *el = &msg->elements[i];
2461 if (el->num_values == 0) {
2462 DEBUG(4,(__location__ ": Removing attribute %s with num_values==0\n",
2463 el->name));
2464 memmove(el, el+1, sizeof(*el)*(msg->num_elements - (i+1)));
2465 msg->num_elements--;
2466 i--;
2467 continue;
2472 * the meta data array is already sorted by the caller
2474 for (i=0; i < md->ctr.ctr1.count; i++) {
2475 md->ctr.ctr1.array[i].local_usn = ar->seq_num;
2477 ndr_err = ndr_push_struct_blob(&md_value, msg,
2478 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
2480 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
2481 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
2482 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
2483 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
2485 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &md_value, NULL);
2486 if (ret != LDB_SUCCESS) {
2487 return replmd_replicated_request_error(ar, ret);
2490 replmd_ldb_message_sort(msg, ar->schema);
2492 if (DEBUGLVL(4)) {
2493 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_ADD, msg);
2494 DEBUG(4, ("DRS replication add message:\n%s\n", s));
2495 talloc_free(s);
2498 ret = ldb_build_add_req(&change_req,
2499 ldb,
2501 msg,
2502 ar->controls,
2504 replmd_op_callback,
2505 ar->req);
2506 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
2508 return ldb_next_request(ar->module, change_req);
2512 return true if an update is newer than an existing entry
2513 see section 5.11 of MS-ADTS
2515 static bool replmd_update_is_newer(const struct GUID *current_invocation_id,
2516 const struct GUID *update_invocation_id,
2517 uint32_t current_version,
2518 uint32_t update_version,
2519 NTTIME current_change_time,
2520 NTTIME update_change_time)
2522 if (update_version != current_version) {
2523 return update_version > current_version;
2525 if (update_change_time > current_change_time) {
2526 return true;
2528 if (update_change_time == current_change_time) {
2529 return GUID_compare(update_invocation_id, current_invocation_id) > 0;
2531 return false;
2534 static bool replmd_replPropertyMetaData1_is_newer(struct replPropertyMetaData1 *cur_m,
2535 struct replPropertyMetaData1 *new_m)
2537 return replmd_update_is_newer(&cur_m->originating_invocation_id,
2538 &new_m->originating_invocation_id,
2539 cur_m->version,
2540 new_m->version,
2541 cur_m->originating_change_time,
2542 new_m->originating_change_time);
2545 static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar)
2547 struct ldb_context *ldb;
2548 struct ldb_request *change_req;
2549 enum ndr_err_code ndr_err;
2550 struct ldb_message *msg;
2551 struct replPropertyMetaDataBlob *rmd;
2552 struct replPropertyMetaDataBlob omd;
2553 const struct ldb_val *omd_value;
2554 struct replPropertyMetaDataBlob nmd;
2555 struct ldb_val nmd_value;
2556 uint32_t i,j,ni=0;
2557 uint32_t removed_attrs = 0;
2558 int ret;
2560 ldb = ldb_module_get_ctx(ar->module);
2561 msg = ar->objs->objects[ar->index_current].msg;
2562 rmd = ar->objs->objects[ar->index_current].meta_data;
2563 ZERO_STRUCT(omd);
2564 omd.version = 1;
2567 * TODO: check repl data is correct after a rename
2569 if (ldb_dn_compare(msg->dn, ar->search_msg->dn) != 0) {
2570 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_request rename %s => %s\n",
2571 ldb_dn_get_linearized(ar->search_msg->dn),
2572 ldb_dn_get_linearized(msg->dn));
2573 /* we can't use dsdb_module_rename() here as we need
2574 the rename call to be intercepted by this module, to
2575 allow it to process linked attribute changes */
2576 if (ldb_rename(ldb, ar->search_msg->dn, msg->dn) != LDB_SUCCESS) {
2577 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_replicated_request rename %s => %s failed - %s\n",
2578 ldb_dn_get_linearized(ar->search_msg->dn),
2579 ldb_dn_get_linearized(msg->dn),
2580 ldb_errstring(ldb));
2581 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
2585 /* find existing meta data */
2586 omd_value = ldb_msg_find_ldb_val(ar->search_msg, "replPropertyMetaData");
2587 if (omd_value) {
2588 ndr_err = ndr_pull_struct_blob(omd_value, ar,
2589 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")), &omd,
2590 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
2591 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
2592 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
2593 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
2596 if (omd.version != 1) {
2597 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
2601 ZERO_STRUCT(nmd);
2602 nmd.version = 1;
2603 nmd.ctr.ctr1.count = omd.ctr.ctr1.count + rmd->ctr.ctr1.count;
2604 nmd.ctr.ctr1.array = talloc_array(ar,
2605 struct replPropertyMetaData1,
2606 nmd.ctr.ctr1.count);
2607 if (!nmd.ctr.ctr1.array) return replmd_replicated_request_werror(ar, WERR_NOMEM);
2609 /* first copy the old meta data */
2610 for (i=0; i < omd.ctr.ctr1.count; i++) {
2611 nmd.ctr.ctr1.array[ni] = omd.ctr.ctr1.array[i];
2612 ni++;
2615 /* now merge in the new meta data */
2616 for (i=0; i < rmd->ctr.ctr1.count; i++) {
2617 bool found = false;
2619 for (j=0; j < ni; j++) {
2620 bool cmp;
2622 if (rmd->ctr.ctr1.array[i].attid != nmd.ctr.ctr1.array[j].attid) {
2623 continue;
2626 cmp = replmd_replPropertyMetaData1_is_newer(&nmd.ctr.ctr1.array[j],
2627 &rmd->ctr.ctr1.array[i]);
2628 if (cmp) {
2629 /* replace the entry */
2630 nmd.ctr.ctr1.array[j] = rmd->ctr.ctr1.array[i];
2631 found = true;
2632 break;
2635 DEBUG(1,("Discarding older DRS attribute update to %s on %s from %s\n",
2636 msg->elements[i-removed_attrs].name,
2637 ldb_dn_get_linearized(msg->dn),
2638 GUID_string(ar, &rmd->ctr.ctr1.array[i].originating_invocation_id)));
2640 /* we don't want to apply this change so remove the attribute */
2641 ldb_msg_remove_element(msg, &msg->elements[i-removed_attrs]);
2642 removed_attrs++;
2644 found = true;
2645 break;
2648 if (found) continue;
2650 nmd.ctr.ctr1.array[ni] = rmd->ctr.ctr1.array[i];
2651 ni++;
2655 * finally correct the size of the meta_data array
2657 nmd.ctr.ctr1.count = ni;
2660 * the rdn attribute (the alias for the name attribute),
2661 * 'cn' for most objects is the last entry in the meta data array
2662 * we have stored
2664 * sort the new meta data array
2666 ret = replmd_replPropertyMetaDataCtr1_sort(&nmd.ctr.ctr1, ar->schema, msg->dn);
2667 if (ret != LDB_SUCCESS) {
2668 return ret;
2672 * check if some replicated attributes left, otherwise skip the ldb_modify() call
2674 if (msg->num_elements == 0) {
2675 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: skip replace\n",
2676 ar->index_current);
2678 ar->index_current++;
2679 return replmd_replicated_apply_next(ar);
2682 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: replace %u attributes\n",
2683 ar->index_current, msg->num_elements);
2685 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
2686 if (ret != LDB_SUCCESS) {
2687 return replmd_replicated_request_error(ar, ret);
2690 for (i=0; i<ni; i++) {
2691 nmd.ctr.ctr1.array[i].local_usn = ar->seq_num;
2694 /* create the meta data value */
2695 ndr_err = ndr_push_struct_blob(&nmd_value, msg,
2696 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
2697 &nmd,
2698 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
2699 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
2700 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
2701 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
2705 * when we know that we'll modify the record, add the whenChanged, uSNChanged
2706 * and replPopertyMetaData attributes
2708 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
2709 if (ret != LDB_SUCCESS) {
2710 return replmd_replicated_request_error(ar, ret);
2712 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
2713 if (ret != LDB_SUCCESS) {
2714 return replmd_replicated_request_error(ar, ret);
2716 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
2717 if (ret != LDB_SUCCESS) {
2718 return replmd_replicated_request_error(ar, ret);
2721 replmd_ldb_message_sort(msg, ar->schema);
2723 /* we want to replace the old values */
2724 for (i=0; i < msg->num_elements; i++) {
2725 msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
2728 if (DEBUGLVL(4)) {
2729 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_MODIFY, msg);
2730 DEBUG(4, ("DRS replication modify message:\n%s\n", s));
2731 talloc_free(s);
2734 ret = ldb_build_mod_req(&change_req,
2735 ldb,
2737 msg,
2738 ar->controls,
2740 replmd_op_callback,
2741 ar->req);
2742 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
2744 return ldb_next_request(ar->module, change_req);
2747 static int replmd_replicated_apply_search_callback(struct ldb_request *req,
2748 struct ldb_reply *ares)
2750 struct replmd_replicated_request *ar = talloc_get_type(req->context,
2751 struct replmd_replicated_request);
2752 int ret;
2754 if (!ares) {
2755 return ldb_module_done(ar->req, NULL, NULL,
2756 LDB_ERR_OPERATIONS_ERROR);
2758 if (ares->error != LDB_SUCCESS &&
2759 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
2760 return ldb_module_done(ar->req, ares->controls,
2761 ares->response, ares->error);
2764 switch (ares->type) {
2765 case LDB_REPLY_ENTRY:
2766 ar->search_msg = talloc_steal(ar, ares->message);
2767 break;
2769 case LDB_REPLY_REFERRAL:
2770 /* we ignore referrals */
2771 break;
2773 case LDB_REPLY_DONE:
2774 if (ar->search_msg != NULL) {
2775 ret = replmd_replicated_apply_merge(ar);
2776 } else {
2777 ret = replmd_replicated_apply_add(ar);
2779 if (ret != LDB_SUCCESS) {
2780 return ldb_module_done(ar->req, NULL, NULL, ret);
2784 talloc_free(ares);
2785 return LDB_SUCCESS;
2788 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar);
2790 static int replmd_replicated_apply_next(struct replmd_replicated_request *ar)
2792 struct ldb_context *ldb;
2793 int ret;
2794 char *tmp_str;
2795 char *filter;
2796 struct ldb_request *search_req;
2797 struct ldb_search_options_control *options;
2799 if (ar->index_current >= ar->objs->num_objects) {
2800 /* done with it, go to next stage */
2801 return replmd_replicated_uptodate_vector(ar);
2804 ldb = ldb_module_get_ctx(ar->module);
2805 ar->search_msg = NULL;
2807 tmp_str = ldb_binary_encode(ar, ar->objs->objects[ar->index_current].guid_value);
2808 if (!tmp_str) return replmd_replicated_request_werror(ar, WERR_NOMEM);
2810 filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
2811 if (!filter) return replmd_replicated_request_werror(ar, WERR_NOMEM);
2812 talloc_free(tmp_str);
2814 ret = ldb_build_search_req(&search_req,
2815 ldb,
2817 NULL,
2818 LDB_SCOPE_SUBTREE,
2819 filter,
2820 NULL,
2821 NULL,
2823 replmd_replicated_apply_search_callback,
2824 ar->req);
2826 ret = ldb_request_add_control(search_req, LDB_CONTROL_SHOW_DELETED_OID, true, NULL);
2827 if (ret != LDB_SUCCESS) {
2828 return ret;
2831 /* we need to cope with cross-partition links, so search for
2832 the GUID over all partitions */
2833 options = talloc(search_req, struct ldb_search_options_control);
2834 if (options == NULL) {
2835 DEBUG(0, (__location__ ": out of memory\n"));
2836 return LDB_ERR_OPERATIONS_ERROR;
2838 options->search_options = LDB_SEARCH_OPTION_PHANTOM_ROOT;
2840 ret = ldb_request_add_control(search_req,
2841 LDB_CONTROL_SEARCH_OPTIONS_OID,
2842 true, options);
2843 if (ret != LDB_SUCCESS) {
2844 return ret;
2847 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
2849 return ldb_next_request(ar->module, search_req);
2852 static int replmd_replicated_uptodate_modify_callback(struct ldb_request *req,
2853 struct ldb_reply *ares)
2855 struct ldb_context *ldb;
2856 struct replmd_replicated_request *ar = talloc_get_type(req->context,
2857 struct replmd_replicated_request);
2858 ldb = ldb_module_get_ctx(ar->module);
2860 if (!ares) {
2861 return ldb_module_done(ar->req, NULL, NULL,
2862 LDB_ERR_OPERATIONS_ERROR);
2864 if (ares->error != LDB_SUCCESS) {
2865 return ldb_module_done(ar->req, ares->controls,
2866 ares->response, ares->error);
2869 if (ares->type != LDB_REPLY_DONE) {
2870 ldb_set_errstring(ldb, "Invalid reply type\n!");
2871 return ldb_module_done(ar->req, NULL, NULL,
2872 LDB_ERR_OPERATIONS_ERROR);
2875 talloc_free(ares);
2877 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
2880 static int replmd_replicated_uptodate_modify(struct replmd_replicated_request *ar)
2882 struct ldb_context *ldb;
2883 struct ldb_request *change_req;
2884 enum ndr_err_code ndr_err;
2885 struct ldb_message *msg;
2886 struct replUpToDateVectorBlob ouv;
2887 const struct ldb_val *ouv_value;
2888 const struct drsuapi_DsReplicaCursor2CtrEx *ruv;
2889 struct replUpToDateVectorBlob nuv;
2890 struct ldb_val nuv_value;
2891 struct ldb_message_element *nuv_el = NULL;
2892 const struct GUID *our_invocation_id;
2893 struct ldb_message_element *orf_el = NULL;
2894 struct repsFromToBlob nrf;
2895 struct ldb_val *nrf_value = NULL;
2896 struct ldb_message_element *nrf_el = NULL;
2897 uint32_t i,j,ni=0;
2898 bool found = false;
2899 time_t t = time(NULL);
2900 NTTIME now;
2901 int ret;
2903 ldb = ldb_module_get_ctx(ar->module);
2904 ruv = ar->objs->uptodateness_vector;
2905 ZERO_STRUCT(ouv);
2906 ouv.version = 2;
2907 ZERO_STRUCT(nuv);
2908 nuv.version = 2;
2910 unix_to_nt_time(&now, t);
2913 * first create the new replUpToDateVector
2915 ouv_value = ldb_msg_find_ldb_val(ar->search_msg, "replUpToDateVector");
2916 if (ouv_value) {
2917 ndr_err = ndr_pull_struct_blob(ouv_value, ar,
2918 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")), &ouv,
2919 (ndr_pull_flags_fn_t)ndr_pull_replUpToDateVectorBlob);
2920 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
2921 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
2922 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
2925 if (ouv.version != 2) {
2926 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
2931 * the new uptodateness vector will at least
2932 * contain 1 entry, one for the source_dsa
2934 * plus optional values from our old vector and the one from the source_dsa
2936 nuv.ctr.ctr2.count = 1 + ouv.ctr.ctr2.count;
2937 if (ruv) nuv.ctr.ctr2.count += ruv->count;
2938 nuv.ctr.ctr2.cursors = talloc_array(ar,
2939 struct drsuapi_DsReplicaCursor2,
2940 nuv.ctr.ctr2.count);
2941 if (!nuv.ctr.ctr2.cursors) return replmd_replicated_request_werror(ar, WERR_NOMEM);
2943 /* first copy the old vector */
2944 for (i=0; i < ouv.ctr.ctr2.count; i++) {
2945 nuv.ctr.ctr2.cursors[ni] = ouv.ctr.ctr2.cursors[i];
2946 ni++;
2949 /* get our invocation_id if we have one already attached to the ldb */
2950 our_invocation_id = samdb_ntds_invocation_id(ldb);
2952 /* merge in the source_dsa vector is available */
2953 for (i=0; (ruv && i < ruv->count); i++) {
2954 found = false;
2956 if (our_invocation_id &&
2957 GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
2958 our_invocation_id)) {
2959 continue;
2962 for (j=0; j < ni; j++) {
2963 if (!GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
2964 &nuv.ctr.ctr2.cursors[j].source_dsa_invocation_id)) {
2965 continue;
2968 found = true;
2971 * we update only the highest_usn and not the latest_sync_success time,
2972 * because the last success stands for direct replication
2974 if (ruv->cursors[i].highest_usn > nuv.ctr.ctr2.cursors[j].highest_usn) {
2975 nuv.ctr.ctr2.cursors[j].highest_usn = ruv->cursors[i].highest_usn;
2977 break;
2980 if (found) continue;
2982 /* if it's not there yet, add it */
2983 nuv.ctr.ctr2.cursors[ni] = ruv->cursors[i];
2984 ni++;
2988 * merge in the current highwatermark for the source_dsa
2990 found = false;
2991 for (j=0; j < ni; j++) {
2992 if (!GUID_equal(&ar->objs->source_dsa->source_dsa_invocation_id,
2993 &nuv.ctr.ctr2.cursors[j].source_dsa_invocation_id)) {
2994 continue;
2997 found = true;
3000 * here we update the highest_usn and last_sync_success time
3001 * because we're directly replicating from the source_dsa
3003 * and use the tmp_highest_usn because this is what we have just applied
3004 * to our ldb
3006 nuv.ctr.ctr2.cursors[j].highest_usn = ar->objs->source_dsa->highwatermark.tmp_highest_usn;
3007 nuv.ctr.ctr2.cursors[j].last_sync_success = now;
3008 break;
3010 if (!found) {
3012 * here we update the highest_usn and last_sync_success time
3013 * because we're directly replicating from the source_dsa
3015 * and use the tmp_highest_usn because this is what we have just applied
3016 * to our ldb
3018 nuv.ctr.ctr2.cursors[ni].source_dsa_invocation_id= ar->objs->source_dsa->source_dsa_invocation_id;
3019 nuv.ctr.ctr2.cursors[ni].highest_usn = ar->objs->source_dsa->highwatermark.tmp_highest_usn;
3020 nuv.ctr.ctr2.cursors[ni].last_sync_success = now;
3021 ni++;
3025 * finally correct the size of the cursors array
3027 nuv.ctr.ctr2.count = ni;
3030 * sort the cursors
3032 qsort(nuv.ctr.ctr2.cursors, nuv.ctr.ctr2.count,
3033 sizeof(struct drsuapi_DsReplicaCursor2),
3034 (comparison_fn_t)drsuapi_DsReplicaCursor2_compare);
3037 * create the change ldb_message
3039 msg = ldb_msg_new(ar);
3040 if (!msg) return replmd_replicated_request_werror(ar, WERR_NOMEM);
3041 msg->dn = ar->search_msg->dn;
3043 ndr_err = ndr_push_struct_blob(&nuv_value, msg,
3044 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
3045 &nuv,
3046 (ndr_push_flags_fn_t)ndr_push_replUpToDateVectorBlob);
3047 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
3048 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
3049 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
3051 ret = ldb_msg_add_value(msg, "replUpToDateVector", &nuv_value, &nuv_el);
3052 if (ret != LDB_SUCCESS) {
3053 return replmd_replicated_request_error(ar, ret);
3055 nuv_el->flags = LDB_FLAG_MOD_REPLACE;
3058 * now create the new repsFrom value from the given repsFromTo1 structure
3060 ZERO_STRUCT(nrf);
3061 nrf.version = 1;
3062 nrf.ctr.ctr1 = *ar->objs->source_dsa;
3063 /* and fix some values... */
3064 nrf.ctr.ctr1.consecutive_sync_failures = 0;
3065 nrf.ctr.ctr1.last_success = now;
3066 nrf.ctr.ctr1.last_attempt = now;
3067 nrf.ctr.ctr1.result_last_attempt = WERR_OK;
3068 nrf.ctr.ctr1.highwatermark.highest_usn = nrf.ctr.ctr1.highwatermark.tmp_highest_usn;
3071 * first see if we already have a repsFrom value for the current source dsa
3072 * if so we'll later replace this value
3074 orf_el = ldb_msg_find_element(ar->search_msg, "repsFrom");
3075 if (orf_el) {
3076 for (i=0; i < orf_el->num_values; i++) {
3077 struct repsFromToBlob *trf;
3079 trf = talloc(ar, struct repsFromToBlob);
3080 if (!trf) return replmd_replicated_request_werror(ar, WERR_NOMEM);
3082 ndr_err = ndr_pull_struct_blob(&orf_el->values[i], trf, lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")), trf,
3083 (ndr_pull_flags_fn_t)ndr_pull_repsFromToBlob);
3084 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
3085 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
3086 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
3089 if (trf->version != 1) {
3090 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
3094 * we compare the source dsa objectGUID not the invocation_id
3095 * because we want only one repsFrom value per source dsa
3096 * and when the invocation_id of the source dsa has changed we don't need
3097 * the old repsFrom with the old invocation_id
3099 if (!GUID_equal(&trf->ctr.ctr1.source_dsa_obj_guid,
3100 &ar->objs->source_dsa->source_dsa_obj_guid)) {
3101 talloc_free(trf);
3102 continue;
3105 talloc_free(trf);
3106 nrf_value = &orf_el->values[i];
3107 break;
3111 * copy over all old values to the new ldb_message
3113 ret = ldb_msg_add_empty(msg, "repsFrom", 0, &nrf_el);
3114 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
3115 *nrf_el = *orf_el;
3119 * if we haven't found an old repsFrom value for the current source dsa
3120 * we'll add a new value
3122 if (!nrf_value) {
3123 struct ldb_val zero_value;
3124 ZERO_STRUCT(zero_value);
3125 ret = ldb_msg_add_value(msg, "repsFrom", &zero_value, &nrf_el);
3126 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
3128 nrf_value = &nrf_el->values[nrf_el->num_values - 1];
3131 /* we now fill the value which is already attached to ldb_message */
3132 ndr_err = ndr_push_struct_blob(nrf_value, msg,
3133 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
3134 &nrf,
3135 (ndr_push_flags_fn_t)ndr_push_repsFromToBlob);
3136 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
3137 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
3138 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
3142 * the ldb_message_element for the attribute, has all the old values and the new one
3143 * so we'll replace the whole attribute with all values
3145 nrf_el->flags = LDB_FLAG_MOD_REPLACE;
3147 if (DEBUGLVL(4)) {
3148 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_MODIFY, msg);
3149 DEBUG(4, ("DRS replication uptodate modify message:\n%s\n", s));
3150 talloc_free(s);
3153 /* prepare the ldb_modify() request */
3154 ret = ldb_build_mod_req(&change_req,
3155 ldb,
3157 msg,
3158 ar->controls,
3160 replmd_replicated_uptodate_modify_callback,
3161 ar->req);
3162 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
3164 return ldb_next_request(ar->module, change_req);
3167 static int replmd_replicated_uptodate_search_callback(struct ldb_request *req,
3168 struct ldb_reply *ares)
3170 struct replmd_replicated_request *ar = talloc_get_type(req->context,
3171 struct replmd_replicated_request);
3172 int ret;
3174 if (!ares) {
3175 return ldb_module_done(ar->req, NULL, NULL,
3176 LDB_ERR_OPERATIONS_ERROR);
3178 if (ares->error != LDB_SUCCESS &&
3179 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
3180 return ldb_module_done(ar->req, ares->controls,
3181 ares->response, ares->error);
3184 switch (ares->type) {
3185 case LDB_REPLY_ENTRY:
3186 ar->search_msg = talloc_steal(ar, ares->message);
3187 break;
3189 case LDB_REPLY_REFERRAL:
3190 /* we ignore referrals */
3191 break;
3193 case LDB_REPLY_DONE:
3194 if (ar->search_msg == NULL) {
3195 ret = replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
3196 } else {
3197 ret = replmd_replicated_uptodate_modify(ar);
3199 if (ret != LDB_SUCCESS) {
3200 return ldb_module_done(ar->req, NULL, NULL, ret);
3204 talloc_free(ares);
3205 return LDB_SUCCESS;
3209 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar)
3211 struct ldb_context *ldb;
3212 int ret;
3213 static const char *attrs[] = {
3214 "replUpToDateVector",
3215 "repsFrom",
3216 NULL
3218 struct ldb_request *search_req;
3220 ldb = ldb_module_get_ctx(ar->module);
3221 ar->search_msg = NULL;
3223 ret = ldb_build_search_req(&search_req,
3224 ldb,
3226 ar->objs->partition_dn,
3227 LDB_SCOPE_BASE,
3228 "(objectClass=*)",
3229 attrs,
3230 NULL,
3232 replmd_replicated_uptodate_search_callback,
3233 ar->req);
3234 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
3236 return ldb_next_request(ar->module, search_req);
3241 static int replmd_extended_replicated_objects(struct ldb_module *module, struct ldb_request *req)
3243 struct ldb_context *ldb;
3244 struct dsdb_extended_replicated_objects *objs;
3245 struct replmd_replicated_request *ar;
3246 struct ldb_control **ctrls;
3247 int ret, i;
3248 struct replmd_private *replmd_private =
3249 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
3251 ldb = ldb_module_get_ctx(module);
3253 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_extended_replicated_objects\n");
3255 objs = talloc_get_type(req->op.extended.data, struct dsdb_extended_replicated_objects);
3256 if (!objs) {
3257 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: invalid extended data\n");
3258 return LDB_ERR_PROTOCOL_ERROR;
3261 if (objs->version != DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION) {
3262 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: extended data invalid version [%u != %u]\n",
3263 objs->version, DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION);
3264 return LDB_ERR_PROTOCOL_ERROR;
3267 ar = replmd_ctx_init(module, req);
3268 if (!ar)
3269 return LDB_ERR_OPERATIONS_ERROR;
3271 /* Set the flags to have the replmd_op_callback run over the full set of objects */
3272 ar->apply_mode = true;
3273 ar->objs = objs;
3274 ar->schema = dsdb_get_schema(ldb);
3275 if (!ar->schema) {
3276 ldb_debug_set(ldb, LDB_DEBUG_FATAL, "replmd_ctx_init: no loaded schema found\n");
3277 talloc_free(ar);
3278 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
3279 return LDB_ERR_CONSTRAINT_VIOLATION;
3282 ctrls = req->controls;
3284 if (req->controls) {
3285 req->controls = talloc_memdup(ar, req->controls,
3286 talloc_get_size(req->controls));
3287 if (!req->controls) return replmd_replicated_request_werror(ar, WERR_NOMEM);
3290 ret = ldb_request_add_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID, false, NULL);
3291 if (ret != LDB_SUCCESS) {
3292 return ret;
3295 ar->controls = req->controls;
3296 req->controls = ctrls;
3298 DEBUG(4,("linked_attributes_count=%u\n", objs->linked_attributes_count));
3300 /* save away the linked attributes for the end of the
3301 transaction */
3302 for (i=0; i<ar->objs->linked_attributes_count; i++) {
3303 struct la_entry *la_entry;
3305 if (replmd_private->la_ctx == NULL) {
3306 replmd_private->la_ctx = talloc_new(replmd_private);
3308 la_entry = talloc(replmd_private->la_ctx, struct la_entry);
3309 if (la_entry == NULL) {
3310 ldb_oom(ldb);
3311 return LDB_ERR_OPERATIONS_ERROR;
3313 la_entry->la = talloc(la_entry, struct drsuapi_DsReplicaLinkedAttribute);
3314 if (la_entry->la == NULL) {
3315 talloc_free(la_entry);
3316 ldb_oom(ldb);
3317 return LDB_ERR_OPERATIONS_ERROR;
3319 *la_entry->la = ar->objs->linked_attributes[i];
3321 /* we need to steal the non-scalars so they stay
3322 around until the end of the transaction */
3323 talloc_steal(la_entry->la, la_entry->la->identifier);
3324 talloc_steal(la_entry->la, la_entry->la->value.blob);
3326 DLIST_ADD(replmd_private->la_list, la_entry);
3329 return replmd_replicated_apply_next(ar);
3333 process one linked attribute structure
3335 static int replmd_process_linked_attribute(struct ldb_module *module,
3336 struct la_entry *la_entry)
3338 struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
3339 struct ldb_context *ldb = ldb_module_get_ctx(module);
3340 struct dsdb_schema *schema = dsdb_get_schema(ldb);
3341 struct ldb_message *msg;
3342 TALLOC_CTX *tmp_ctx = talloc_new(la_entry);
3343 int ret;
3344 const struct dsdb_attribute *attr;
3345 struct dsdb_dn *dsdb_dn;
3346 uint64_t seq_num = 0;
3347 struct drsuapi_DsReplicaAttribute drs;
3348 struct drsuapi_DsAttributeValue val;
3349 struct ldb_message_element new_el, *old_el;
3350 WERROR status;
3351 time_t t = time(NULL);
3352 struct ldb_result *res;
3353 const char *attrs[2];
3354 struct parsed_dn *pdn_list, *pdn;
3355 struct GUID guid = GUID_zero();
3356 NTSTATUS ntstatus;
3357 bool active = (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)?true:false;
3358 const struct GUID *our_invocation_id;
3360 drs.value_ctr.num_values = 1;
3361 drs.value_ctr.values = &val;
3362 val.blob = la->value.blob;
3365 linked_attributes[0]:
3366 &objs->linked_attributes[i]: struct drsuapi_DsReplicaLinkedAttribute
3367 identifier : *
3368 identifier: struct drsuapi_DsReplicaObjectIdentifier
3369 __ndr_size : 0x0000003a (58)
3370 __ndr_size_sid : 0x00000000 (0)
3371 guid : 8e95b6a9-13dd-4158-89db-3220a5be5cc7
3372 sid : S-0-0
3373 __ndr_size_dn : 0x00000000 (0)
3374 dn : ''
3375 attid : DRSUAPI_ATTRIBUTE_member (0x1F)
3376 value: struct drsuapi_DsAttributeValue
3377 __ndr_size : 0x0000007e (126)
3378 blob : *
3379 blob : DATA_BLOB length=126
3380 flags : 0x00000001 (1)
3381 1: DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE
3382 originating_add_time : Wed Sep 2 22:20:01 2009 EST
3383 meta_data: struct drsuapi_DsReplicaMetaData
3384 version : 0x00000015 (21)
3385 originating_change_time : Wed Sep 2 23:39:07 2009 EST
3386 originating_invocation_id: 794640f3-18cf-40ee-a211-a93992b67a64
3387 originating_usn : 0x000000000001e19c (123292)
3389 (for cases where the link is to a normal DN)
3390 &target: struct drsuapi_DsReplicaObjectIdentifier3
3391 __ndr_size : 0x0000007e (126)
3392 __ndr_size_sid : 0x0000001c (28)
3393 guid : 7639e594-db75-4086-b0d4-67890ae46031
3394 sid : S-1-5-21-2848215498-2472035911-1947525656-19924
3395 __ndr_size_dn : 0x00000022 (34)
3396 dn : 'CN=UOne,OU=TestOU,DC=vsofs8,DC=com'
3399 /* find the attribute being modified */
3400 attr = dsdb_attribute_by_attributeID_id(schema, la->attid);
3401 if (attr == NULL) {
3402 DEBUG(0, (__location__ ": Unable to find attributeID 0x%x\n", la->attid));
3403 talloc_free(tmp_ctx);
3404 return LDB_ERR_OPERATIONS_ERROR;
3407 attrs[0] = attr->lDAPDisplayName;
3408 attrs[1] = NULL;
3410 /* get the existing message from the db for the object with
3411 this GUID, returning attribute being modified. We will then
3412 use this msg as the basis for a modify call */
3413 ret = dsdb_module_search(module, tmp_ctx, &res, NULL, LDB_SCOPE_SUBTREE, attrs,
3414 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
3415 DSDB_SEARCH_SHOW_DELETED |
3416 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
3417 DSDB_SEARCH_REVEAL_INTERNALS,
3418 "objectGUID=%s", GUID_string(tmp_ctx, &la->identifier->guid));
3419 if (ret != LDB_SUCCESS) {
3420 talloc_free(tmp_ctx);
3421 return ret;
3423 if (res->count != 1) {
3424 ldb_asprintf_errstring(ldb, "DRS linked attribute for GUID %s - DN not found",
3425 GUID_string(tmp_ctx, &la->identifier->guid));
3426 talloc_free(tmp_ctx);
3427 return LDB_ERR_NO_SUCH_OBJECT;
3429 msg = res->msgs[0];
3431 if (msg->num_elements == 0) {
3432 ret = ldb_msg_add_empty(msg, attr->lDAPDisplayName, LDB_FLAG_MOD_REPLACE, &old_el);
3433 if (ret != LDB_SUCCESS) {
3434 ldb_module_oom(module);
3435 talloc_free(tmp_ctx);
3436 return LDB_ERR_OPERATIONS_ERROR;
3438 } else {
3439 old_el = &msg->elements[0];
3440 old_el->flags = LDB_FLAG_MOD_REPLACE;
3443 /* parse the existing links */
3444 ret = get_parsed_dns(module, tmp_ctx, old_el, &pdn_list, attr->syntax->ldap_oid);
3445 if (ret != LDB_SUCCESS) {
3446 talloc_free(tmp_ctx);
3447 return ret;
3450 /* get our invocationId */
3451 our_invocation_id = samdb_ntds_invocation_id(ldb);
3452 if (!our_invocation_id) {
3453 ldb_debug_set(ldb, LDB_DEBUG_ERROR, __location__ ": unable to find invocationId\n");
3454 talloc_free(tmp_ctx);
3455 return LDB_ERR_OPERATIONS_ERROR;
3458 ret = replmd_check_upgrade_links(pdn_list, old_el->num_values, our_invocation_id);
3459 if (ret != LDB_SUCCESS) {
3460 talloc_free(tmp_ctx);
3461 return ret;
3464 status = attr->syntax->drsuapi_to_ldb(ldb, schema, attr, &drs, tmp_ctx, &new_el);
3465 if (!W_ERROR_IS_OK(status)) {
3466 ldb_asprintf_errstring(ldb, "Failed to parsed linked attribute blob for %s on %s\n",
3467 old_el->name, ldb_dn_get_linearized(msg->dn));
3468 return LDB_ERR_OPERATIONS_ERROR;
3471 if (new_el.num_values != 1) {
3472 ldb_asprintf_errstring(ldb, "Failed to find value in linked attribute blob for %s on %s\n",
3473 old_el->name, ldb_dn_get_linearized(msg->dn));
3474 return LDB_ERR_OPERATIONS_ERROR;
3477 dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, &new_el.values[0], attr->syntax->ldap_oid);
3478 if (!dsdb_dn) {
3479 ldb_asprintf_errstring(ldb, "Failed to parse DN in linked attribute blob for %s on %s\n",
3480 old_el->name, ldb_dn_get_linearized(msg->dn));
3481 return LDB_ERR_OPERATIONS_ERROR;
3484 ntstatus = dsdb_get_extended_dn_guid(dsdb_dn->dn, &guid, "GUID");
3485 if (!NT_STATUS_IS_OK(ntstatus) && active) {
3486 ldb_asprintf_errstring(ldb, "Failed to find GUID in linked attribute blob for %s on %s from %s",
3487 old_el->name,
3488 ldb_dn_get_linearized(dsdb_dn->dn),
3489 ldb_dn_get_linearized(msg->dn));
3490 return LDB_ERR_OPERATIONS_ERROR;
3493 /* see if this link already exists */
3494 pdn = parsed_dn_find(pdn_list, old_el->num_values, &guid, dsdb_dn->dn);
3495 if (pdn != NULL) {
3496 /* see if this update is newer than what we have already */
3497 struct GUID invocation_id = GUID_zero();
3498 uint32_t version = 0;
3499 NTTIME change_time = 0;
3500 bool was_active = ldb_dn_get_extended_component(pdn->dsdb_dn->dn, "DELETED") == NULL;
3502 dsdb_get_extended_dn_guid(pdn->dsdb_dn->dn, &invocation_id, "RMD_INVOCID");
3503 dsdb_get_extended_dn_uint32(pdn->dsdb_dn->dn, &version, "RMD_VERSION");
3504 dsdb_get_extended_dn_nttime(pdn->dsdb_dn->dn, &change_time, "RMD_CHANGETIME");
3506 if (!replmd_update_is_newer(&invocation_id,
3507 &la->meta_data.originating_invocation_id,
3508 version,
3509 la->meta_data.version,
3510 change_time,
3511 la->meta_data.originating_change_time)) {
3512 DEBUG(1,("Discarding older DRS linked attribute update to %s on %s from %s\n",
3513 old_el->name, ldb_dn_get_linearized(msg->dn),
3514 GUID_string(tmp_ctx, &la->meta_data.originating_invocation_id)));
3515 talloc_free(tmp_ctx);
3516 return LDB_SUCCESS;
3519 /* get a seq_num for this change */
3520 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
3521 if (ret != LDB_SUCCESS) {
3522 talloc_free(tmp_ctx);
3523 return ret;
3526 if (was_active) {
3527 /* remove the existing backlink */
3528 ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid, false, attr, false);
3529 if (ret != LDB_SUCCESS) {
3530 talloc_free(tmp_ctx);
3531 return ret;
3535 ret = replmd_update_la_val(tmp_ctx, pdn->v, dsdb_dn, pdn->dsdb_dn,
3536 &la->meta_data.originating_invocation_id,
3537 la->meta_data.originating_usn, seq_num,
3538 la->meta_data.originating_change_time,
3539 la->meta_data.version,
3540 !active);
3541 if (ret != LDB_SUCCESS) {
3542 talloc_free(tmp_ctx);
3543 return ret;
3546 if (active) {
3547 /* add the new backlink */
3548 ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid, true, attr, false);
3549 if (ret != LDB_SUCCESS) {
3550 talloc_free(tmp_ctx);
3551 return ret;
3554 } else {
3555 /* get a seq_num for this change */
3556 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
3557 if (ret != LDB_SUCCESS) {
3558 talloc_free(tmp_ctx);
3559 return ret;
3562 old_el->values = talloc_realloc(msg->elements, old_el->values,
3563 struct ldb_val, old_el->num_values+1);
3564 if (!old_el->values) {
3565 ldb_module_oom(module);
3566 return LDB_ERR_OPERATIONS_ERROR;
3568 old_el->num_values++;
3570 ret = replmd_build_la_val(tmp_ctx, &old_el->values[old_el->num_values-1], dsdb_dn,
3571 &la->meta_data.originating_invocation_id,
3572 la->meta_data.originating_usn, seq_num,
3573 la->meta_data.originating_change_time,
3574 la->meta_data.version,
3575 (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)?false:true);
3576 if (ret != LDB_SUCCESS) {
3577 talloc_free(tmp_ctx);
3578 return ret;
3581 if (active) {
3582 ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid,
3583 true, attr, false);
3584 if (ret != LDB_SUCCESS) {
3585 talloc_free(tmp_ctx);
3586 return ret;
3591 /* we only change whenChanged and uSNChanged if the seq_num
3592 has changed */
3593 if (add_time_element(msg, "whenChanged", t) != LDB_SUCCESS) {
3594 talloc_free(tmp_ctx);
3595 return LDB_ERR_OPERATIONS_ERROR;
3598 if (add_uint64_element(msg, "uSNChanged", seq_num) != LDB_SUCCESS) {
3599 talloc_free(tmp_ctx);
3600 return LDB_ERR_OPERATIONS_ERROR;
3603 ret = dsdb_check_single_valued_link(attr, old_el);
3604 if (ret != LDB_SUCCESS) {
3605 talloc_free(tmp_ctx);
3606 return ret;
3609 ret = dsdb_module_modify(module, msg, DSDB_MODIFY_RELAX);
3610 if (ret != LDB_SUCCESS) {
3611 ldb_debug(ldb, LDB_DEBUG_WARNING, "Failed to apply linked attribute change '%s'\n%s\n",
3612 ldb_errstring(ldb),
3613 ldb_ldif_message_string(ldb, tmp_ctx, LDB_CHANGETYPE_MODIFY, msg));
3614 talloc_free(tmp_ctx);
3615 return ret;
3618 talloc_free(tmp_ctx);
3620 return ret;
3623 static int replmd_extended(struct ldb_module *module, struct ldb_request *req)
3625 if (strcmp(req->op.extended.oid, DSDB_EXTENDED_REPLICATED_OBJECTS_OID) == 0) {
3626 return replmd_extended_replicated_objects(module, req);
3629 return ldb_next_request(module, req);
3634 we hook into the transaction operations to allow us to
3635 perform the linked attribute updates at the end of the whole
3636 transaction. This allows a forward linked attribute to be created
3637 before the object is created. During a vampire, w2k8 sends us linked
3638 attributes before the objects they are part of.
3640 static int replmd_start_transaction(struct ldb_module *module)
3642 /* create our private structure for this transaction */
3643 struct replmd_private *replmd_private = talloc_get_type(ldb_module_get_private(module),
3644 struct replmd_private);
3645 replmd_txn_cleanup(replmd_private);
3647 /* free any leftover mod_usn records from cancelled
3648 transactions */
3649 while (replmd_private->ncs) {
3650 struct nc_entry *e = replmd_private->ncs;
3651 DLIST_REMOVE(replmd_private->ncs, e);
3652 talloc_free(e);
3655 return ldb_next_start_trans(module);
3659 on prepare commit we loop over our queued la_context structures and
3660 apply each of them
3662 static int replmd_prepare_commit(struct ldb_module *module)
3664 struct replmd_private *replmd_private =
3665 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
3666 struct la_entry *la, *prev;
3667 struct la_backlink *bl;
3668 int ret;
3670 /* walk the list backwards, to do the first entry first, as we
3671 * added the entries with DLIST_ADD() which puts them at the
3672 * start of the list */
3673 for (la = replmd_private->la_list; la && la->next; la=la->next) ;
3675 for (; la; la=prev) {
3676 prev = la->prev;
3677 DLIST_REMOVE(replmd_private->la_list, la);
3678 ret = replmd_process_linked_attribute(module, la);
3679 if (ret != LDB_SUCCESS) {
3680 replmd_txn_cleanup(replmd_private);
3681 return ret;
3685 /* process our backlink list, creating and deleting backlinks
3686 as necessary */
3687 for (bl=replmd_private->la_backlinks; bl; bl=bl->next) {
3688 ret = replmd_process_backlink(module, bl);
3689 if (ret != LDB_SUCCESS) {
3690 replmd_txn_cleanup(replmd_private);
3691 return ret;
3695 replmd_txn_cleanup(replmd_private);
3697 /* possibly change @REPLCHANGED */
3698 ret = replmd_notify_store(module);
3699 if (ret != LDB_SUCCESS) {
3700 return ret;
3703 return ldb_next_prepare_commit(module);
3706 static int replmd_del_transaction(struct ldb_module *module)
3708 struct replmd_private *replmd_private =
3709 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
3710 replmd_txn_cleanup(replmd_private);
3712 return ldb_next_del_trans(module);
3716 _PUBLIC_ const struct ldb_module_ops ldb_repl_meta_data_module_ops = {
3717 .name = "repl_meta_data",
3718 .init_context = replmd_init,
3719 .add = replmd_add,
3720 .modify = replmd_modify,
3721 .rename = replmd_rename,
3722 .del = replmd_delete,
3723 .extended = replmd_extended,
3724 .start_transaction = replmd_start_transaction,
3725 .prepare_commit = replmd_prepare_commit,
3726 .del_transaction = replmd_del_transaction,