s4:dsdb Add 'const' to some struct dsdb_schema variables
[Samba/ekacnet.git] / source4 / dsdb / samdb / ldb_modules / repl_meta_data.c
blob91cd825a8467896e3a77be2c3dee354dcf5b046b
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"
50 #include "libcli/security/security.h"
51 #include "lib/util/tsort.h"
53 #define W2K3_LINKED_ATTRIBUTES 1
55 struct replmd_private {
56 TALLOC_CTX *la_ctx;
57 struct la_entry *la_list;
58 TALLOC_CTX *bl_ctx;
59 struct la_backlink *la_backlinks;
60 struct nc_entry {
61 struct nc_entry *prev, *next;
62 struct ldb_dn *dn;
63 uint64_t mod_usn;
64 uint64_t mod_usn_urgent;
65 } *ncs;
68 struct la_entry {
69 struct la_entry *next, *prev;
70 struct drsuapi_DsReplicaLinkedAttribute *la;
73 struct replmd_replicated_request {
74 struct ldb_module *module;
75 struct ldb_request *req;
77 const struct dsdb_schema *schema;
79 /* the controls we pass down */
80 struct ldb_control **controls;
82 /* details for the mode where we apply a bunch of inbound replication meessages */
83 bool apply_mode;
84 uint32_t index_current;
85 struct dsdb_extended_replicated_objects *objs;
87 struct ldb_message *search_msg;
89 uint64_t seq_num;
90 bool is_urgent;
93 enum urgent_situation {
94 REPL_URGENT_ON_CREATE = 1,
95 REPL_URGENT_ON_UPDATE = 2,
96 REPL_URGENT_ON_DELETE = 4
100 static const struct {
101 const char *update_name;
102 enum urgent_situation repl_situation;
103 } urgent_objects[] = {
104 {"nTDSDSA", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE)},
105 {"crossRef", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE)},
106 {"attributeSchema", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
107 {"classSchema", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
108 {"secret", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
109 {"rIDManager", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
110 {NULL, 0}
113 /* Attributes looked for when updating or deleting, to check for a urgent replication needed */
114 static const char *urgent_attrs[] = {
115 "lockoutTime",
116 "pwdLastSet",
117 "userAccountControl",
118 NULL
122 static bool replmd_check_urgent_objectclass(const struct ldb_message_element *objectclass_el,
123 enum urgent_situation situation)
125 int i, j;
126 for (i=0; urgent_objects[i].update_name; i++) {
128 if ((situation & urgent_objects[i].repl_situation) == 0) {
129 continue;
132 for (j=0; j<objectclass_el->num_values; j++) {
133 const struct ldb_val *v = &objectclass_el->values[j];
134 if (ldb_attr_cmp((const char *)v->data, urgent_objects[i].update_name) == 0) {
135 return true;
139 return false;
142 static bool replmd_check_urgent_attribute(const struct ldb_message_element *el)
144 if (ldb_attr_in_list(urgent_attrs, el->name)) {
145 return true;
147 return false;
151 static int replmd_replicated_apply_next(struct replmd_replicated_request *ar);
154 initialise the module
155 allocate the private structure and build the list
156 of partition DNs for use by replmd_notify()
158 static int replmd_init(struct ldb_module *module)
160 struct replmd_private *replmd_private;
161 struct ldb_context *ldb = ldb_module_get_ctx(module);
163 replmd_private = talloc_zero(module, struct replmd_private);
164 if (replmd_private == NULL) {
165 ldb_oom(ldb);
166 return LDB_ERR_OPERATIONS_ERROR;
168 ldb_module_set_private(module, replmd_private);
170 return ldb_next_init(module);
174 cleanup our per-transaction contexts
176 static void replmd_txn_cleanup(struct replmd_private *replmd_private)
178 talloc_free(replmd_private->la_ctx);
179 replmd_private->la_list = NULL;
180 replmd_private->la_ctx = NULL;
182 talloc_free(replmd_private->bl_ctx);
183 replmd_private->la_backlinks = NULL;
184 replmd_private->bl_ctx = NULL;
188 struct la_backlink {
189 struct la_backlink *next, *prev;
190 const char *attr_name;
191 struct GUID forward_guid, target_guid;
192 bool active;
196 process a backlinks we accumulated during a transaction, adding and
197 deleting the backlinks from the target objects
199 static int replmd_process_backlink(struct ldb_module *module, struct la_backlink *bl)
201 struct ldb_dn *target_dn, *source_dn;
202 int ret;
203 struct ldb_context *ldb = ldb_module_get_ctx(module);
204 struct ldb_message *msg;
205 TALLOC_CTX *tmp_ctx = talloc_new(bl);
206 char *dn_string;
209 - find DN of target
210 - find DN of source
211 - construct ldb_message
212 - either an add or a delete
214 ret = dsdb_module_dn_by_guid(module, tmp_ctx, &bl->target_guid, &target_dn);
215 if (ret != LDB_SUCCESS) {
216 ldb_asprintf_errstring(ldb, "Failed to find target DN for linked attribute with GUID %s\n",
217 GUID_string(bl, &bl->target_guid));
218 talloc_free(tmp_ctx);
219 return ret;
222 ret = dsdb_module_dn_by_guid(module, tmp_ctx, &bl->forward_guid, &source_dn);
223 if (ret != LDB_SUCCESS) {
224 ldb_asprintf_errstring(ldb, "Failed to find source DN for linked attribute with GUID %s\n",
225 GUID_string(bl, &bl->forward_guid));
226 talloc_free(tmp_ctx);
227 return ret;
230 msg = ldb_msg_new(tmp_ctx);
231 if (msg == NULL) {
232 ldb_module_oom(module);
233 talloc_free(tmp_ctx);
234 return LDB_ERR_OPERATIONS_ERROR;
237 /* construct a ldb_message for adding/deleting the backlink */
238 msg->dn = target_dn;
239 dn_string = ldb_dn_get_extended_linearized(tmp_ctx, source_dn, 1);
240 if (!dn_string) {
241 ldb_module_oom(module);
242 talloc_free(tmp_ctx);
243 return LDB_ERR_OPERATIONS_ERROR;
245 ret = ldb_msg_add_steal_string(msg, bl->attr_name, dn_string);
246 if (ret != LDB_SUCCESS) {
247 talloc_free(tmp_ctx);
248 return ret;
250 msg->elements[0].flags = bl->active?LDB_FLAG_MOD_ADD:LDB_FLAG_MOD_DELETE;
252 ret = dsdb_module_modify(module, msg, 0);
253 if (ret != LDB_SUCCESS) {
254 ldb_asprintf_errstring(ldb, "Failed to %s backlink from %s to %s - %s",
255 bl->active?"add":"remove",
256 ldb_dn_get_linearized(source_dn),
257 ldb_dn_get_linearized(target_dn),
258 ldb_errstring(ldb));
259 talloc_free(tmp_ctx);
260 return ret;
262 talloc_free(tmp_ctx);
263 return ret;
267 add a backlink to the list of backlinks to add/delete in the prepare
268 commit
270 static int replmd_add_backlink(struct ldb_module *module, const struct dsdb_schema *schema,
271 struct GUID *forward_guid, struct GUID *target_guid,
272 bool active, const struct dsdb_attribute *schema_attr, bool immediate)
274 const struct dsdb_attribute *target_attr;
275 struct la_backlink *bl;
276 struct replmd_private *replmd_private =
277 talloc_get_type_abort(ldb_module_get_private(module), struct replmd_private);
279 target_attr = dsdb_attribute_by_linkID(schema, schema_attr->linkID ^ 1);
280 if (!target_attr) {
282 * windows 2003 has a broken schema where the
283 * definition of msDS-IsDomainFor is missing (which is
284 * supposed to be the backlink of the
285 * msDS-HasDomainNCs attribute
287 return LDB_SUCCESS;
290 /* see if its already in the list */
291 for (bl=replmd_private->la_backlinks; bl; bl=bl->next) {
292 if (GUID_equal(forward_guid, &bl->forward_guid) &&
293 GUID_equal(target_guid, &bl->target_guid) &&
294 (target_attr->lDAPDisplayName == bl->attr_name ||
295 strcmp(target_attr->lDAPDisplayName, bl->attr_name) == 0)) {
296 break;
300 if (bl) {
301 /* we found an existing one */
302 if (bl->active == active) {
303 return LDB_SUCCESS;
305 DLIST_REMOVE(replmd_private->la_backlinks, bl);
306 talloc_free(bl);
307 return LDB_SUCCESS;
310 if (replmd_private->bl_ctx == NULL) {
311 replmd_private->bl_ctx = talloc_new(replmd_private);
312 if (replmd_private->bl_ctx == NULL) {
313 ldb_module_oom(module);
314 return LDB_ERR_OPERATIONS_ERROR;
318 /* its a new one */
319 bl = talloc(replmd_private->bl_ctx, struct la_backlink);
320 if (bl == NULL) {
321 ldb_module_oom(module);
322 return LDB_ERR_OPERATIONS_ERROR;
325 /* Ensure the schema does not go away before the bl->attr_name is used */
326 if (!talloc_reference(bl, schema)) {
327 talloc_free(bl);
328 ldb_module_oom(module);
329 return LDB_ERR_OPERATIONS_ERROR;
332 bl->attr_name = target_attr->lDAPDisplayName;
333 bl->forward_guid = *forward_guid;
334 bl->target_guid = *target_guid;
335 bl->active = active;
337 /* the caller may ask for this backlink to be processed
338 immediately */
339 if (immediate) {
340 int ret = replmd_process_backlink(module, bl);
341 talloc_free(bl);
342 return ret;
345 DLIST_ADD(replmd_private->la_backlinks, bl);
347 return LDB_SUCCESS;
352 * Callback for most write operations in this module:
354 * notify the repl task that a object has changed. The notifies are
355 * gathered up in the replmd_private structure then written to the
356 * @REPLCHANGED object in each partition during the prepare_commit
358 static int replmd_op_callback(struct ldb_request *req, struct ldb_reply *ares)
360 int ret;
361 struct replmd_replicated_request *ac =
362 talloc_get_type_abort(req->context, struct replmd_replicated_request);
363 struct replmd_private *replmd_private =
364 talloc_get_type_abort(ldb_module_get_private(ac->module), struct replmd_private);
365 struct nc_entry *modified_partition;
366 struct ldb_control *partition_ctrl;
367 const struct dsdb_control_current_partition *partition;
369 struct ldb_control **controls;
371 partition_ctrl = ldb_reply_get_control(ares, DSDB_CONTROL_CURRENT_PARTITION_OID);
373 /* Remove the 'partition' control from what we pass up the chain */
374 controls = controls_except_specified(ares->controls, ares, partition_ctrl);
376 if (ares->error != LDB_SUCCESS) {
377 return ldb_module_done(ac->req, controls,
378 ares->response, ares->error);
381 if (ares->type != LDB_REPLY_DONE) {
382 ldb_set_errstring(ldb_module_get_ctx(ac->module), "Invalid reply type for notify\n!");
383 return ldb_module_done(ac->req, NULL,
384 NULL, LDB_ERR_OPERATIONS_ERROR);
387 if (!partition_ctrl) {
388 ldb_set_errstring(ldb_module_get_ctx(ac->module),"No partition control on reply");
389 return ldb_module_done(ac->req, NULL,
390 NULL, LDB_ERR_OPERATIONS_ERROR);
393 partition = talloc_get_type_abort(partition_ctrl->data,
394 struct dsdb_control_current_partition);
396 if (ac->seq_num > 0) {
397 for (modified_partition = replmd_private->ncs; modified_partition;
398 modified_partition = modified_partition->next) {
399 if (ldb_dn_compare(modified_partition->dn, partition->dn) == 0) {
400 break;
404 if (modified_partition == NULL) {
405 modified_partition = talloc_zero(replmd_private, struct nc_entry);
406 if (!modified_partition) {
407 ldb_oom(ldb_module_get_ctx(ac->module));
408 return ldb_module_done(ac->req, NULL,
409 NULL, LDB_ERR_OPERATIONS_ERROR);
411 modified_partition->dn = ldb_dn_copy(modified_partition, partition->dn);
412 if (!modified_partition->dn) {
413 ldb_oom(ldb_module_get_ctx(ac->module));
414 return ldb_module_done(ac->req, NULL,
415 NULL, LDB_ERR_OPERATIONS_ERROR);
417 DLIST_ADD(replmd_private->ncs, modified_partition);
420 if (ac->seq_num > modified_partition->mod_usn) {
421 modified_partition->mod_usn = ac->seq_num;
422 if (ac->is_urgent) {
423 modified_partition->mod_usn_urgent = ac->seq_num;
428 if (ac->apply_mode) {
429 talloc_free(ares);
430 ac->index_current++;
432 ret = replmd_replicated_apply_next(ac);
433 if (ret != LDB_SUCCESS) {
434 return ldb_module_done(ac->req, NULL, NULL, ret);
436 return ret;
437 } else {
438 /* free the partition control container here, for the
439 * common path. Other cases will have it cleaned up
440 * eventually with the ares */
441 talloc_free(partition_ctrl);
442 return ldb_module_done(ac->req,
443 controls_except_specified(controls, ares, partition_ctrl),
444 ares->response, LDB_SUCCESS);
450 * update a @REPLCHANGED record in each partition if there have been
451 * any writes of replicated data in the partition
453 static int replmd_notify_store(struct ldb_module *module)
455 struct replmd_private *replmd_private =
456 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
457 struct ldb_context *ldb = ldb_module_get_ctx(module);
459 while (replmd_private->ncs) {
460 int ret;
461 struct nc_entry *modified_partition = replmd_private->ncs;
463 ret = dsdb_save_partition_usn(ldb, modified_partition->dn,
464 modified_partition->mod_usn,
465 modified_partition->mod_usn_urgent);
466 if (ret != LDB_SUCCESS) {
467 DEBUG(0,(__location__ ": Failed to save partition uSN for %s\n",
468 ldb_dn_get_linearized(modified_partition->dn)));
469 return ret;
471 DLIST_REMOVE(replmd_private->ncs, modified_partition);
472 talloc_free(modified_partition);
475 return LDB_SUCCESS;
480 created a replmd_replicated_request context
482 static struct replmd_replicated_request *replmd_ctx_init(struct ldb_module *module,
483 struct ldb_request *req)
485 struct ldb_context *ldb;
486 struct replmd_replicated_request *ac;
488 ldb = ldb_module_get_ctx(module);
490 ac = talloc_zero(req, struct replmd_replicated_request);
491 if (ac == NULL) {
492 ldb_oom(ldb);
493 return NULL;
496 ac->module = module;
497 ac->req = req;
499 ac->schema = dsdb_get_schema(ldb, ac);
500 if (!ac->schema) {
501 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
502 "replmd_modify: no dsdb_schema loaded");
503 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
504 return NULL;
507 return ac;
511 add a time element to a record
513 static int add_time_element(struct ldb_message *msg, const char *attr, time_t t)
515 struct ldb_message_element *el;
516 char *s;
518 if (ldb_msg_find_element(msg, attr) != NULL) {
519 return LDB_SUCCESS;
522 s = ldb_timestring(msg, t);
523 if (s == NULL) {
524 return LDB_ERR_OPERATIONS_ERROR;
527 if (ldb_msg_add_string(msg, attr, s) != LDB_SUCCESS) {
528 return LDB_ERR_OPERATIONS_ERROR;
531 el = ldb_msg_find_element(msg, attr);
532 /* always set as replace. This works because on add ops, the flag
533 is ignored */
534 el->flags = LDB_FLAG_MOD_REPLACE;
536 return LDB_SUCCESS;
540 add a uint64_t element to a record
542 static int add_uint64_element(struct ldb_message *msg, const char *attr, uint64_t v)
544 struct ldb_message_element *el;
546 if (ldb_msg_find_element(msg, attr) != NULL) {
547 return LDB_SUCCESS;
550 if (ldb_msg_add_fmt(msg, attr, "%llu", (unsigned long long)v) != LDB_SUCCESS) {
551 return LDB_ERR_OPERATIONS_ERROR;
554 el = ldb_msg_find_element(msg, attr);
555 /* always set as replace. This works because on add ops, the flag
556 is ignored */
557 el->flags = LDB_FLAG_MOD_REPLACE;
559 return LDB_SUCCESS;
562 static int replmd_replPropertyMetaData1_attid_sort(const struct replPropertyMetaData1 *m1,
563 const struct replPropertyMetaData1 *m2,
564 const uint32_t *rdn_attid)
566 if (m1->attid == m2->attid) {
567 return 0;
571 * the rdn attribute should be at the end!
572 * so we need to return a value greater than zero
573 * which means m1 is greater than m2
575 if (m1->attid == *rdn_attid) {
576 return 1;
580 * the rdn attribute should be at the end!
581 * so we need to return a value less than zero
582 * which means m2 is greater than m1
584 if (m2->attid == *rdn_attid) {
585 return -1;
588 return m1->attid > m2->attid ? 1 : -1;
591 static int replmd_replPropertyMetaDataCtr1_sort(struct replPropertyMetaDataCtr1 *ctr1,
592 const struct dsdb_schema *schema,
593 struct ldb_dn *dn)
595 const char *rdn_name;
596 const struct dsdb_attribute *rdn_sa;
598 rdn_name = ldb_dn_get_rdn_name(dn);
599 if (!rdn_name) {
600 DEBUG(0,(__location__ ": No rDN for %s?\n", ldb_dn_get_linearized(dn)));
601 return LDB_ERR_OPERATIONS_ERROR;
604 rdn_sa = dsdb_attribute_by_lDAPDisplayName(schema, rdn_name);
605 if (rdn_sa == NULL) {
606 DEBUG(0,(__location__ ": No sa found for rDN %s for %s\n", rdn_name, ldb_dn_get_linearized(dn)));
607 return LDB_ERR_OPERATIONS_ERROR;
610 DEBUG(6,("Sorting rpmd with attid exception %u rDN=%s DN=%s\n",
611 rdn_sa->attributeID_id, rdn_name, ldb_dn_get_linearized(dn)));
613 LDB_TYPESAFE_QSORT(ctr1->array, ctr1->count, &rdn_sa->attributeID_id, replmd_replPropertyMetaData1_attid_sort);
615 return LDB_SUCCESS;
618 static int replmd_ldb_message_element_attid_sort(const struct ldb_message_element *e1,
619 const struct ldb_message_element *e2,
620 const struct dsdb_schema *schema)
622 const struct dsdb_attribute *a1;
623 const struct dsdb_attribute *a2;
626 * TODO: make this faster by caching the dsdb_attribute pointer
627 * on the ldb_messag_element
630 a1 = dsdb_attribute_by_lDAPDisplayName(schema, e1->name);
631 a2 = dsdb_attribute_by_lDAPDisplayName(schema, e2->name);
634 * TODO: remove this check, we should rely on e1 and e2 having valid attribute names
635 * in the schema
637 if (!a1 || !a2) {
638 return strcasecmp(e1->name, e2->name);
640 if (a1->attributeID_id == a2->attributeID_id) {
641 return 0;
643 return a1->attributeID_id > a2->attributeID_id ? 1 : -1;
646 static void replmd_ldb_message_sort(struct ldb_message *msg,
647 const struct dsdb_schema *schema)
649 LDB_TYPESAFE_QSORT(msg->elements, msg->num_elements, schema, replmd_ldb_message_element_attid_sort);
652 static int replmd_build_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
653 const struct GUID *invocation_id, uint64_t seq_num,
654 uint64_t local_usn, NTTIME nttime, uint32_t version, bool deleted);
658 fix up linked attributes in replmd_add.
659 This involves setting up the right meta-data in extended DN
660 components, and creating backlinks to the object
662 static int replmd_add_fix_la(struct ldb_module *module, struct ldb_message_element *el,
663 uint64_t seq_num, const struct GUID *invocationId, time_t t,
664 struct GUID *guid, const struct dsdb_attribute *sa)
666 unsigned int i;
667 TALLOC_CTX *tmp_ctx = talloc_new(el->values);
668 struct ldb_context *ldb = ldb_module_get_ctx(module);
670 /* We will take a reference to the schema in replmd_add_backlink */
671 const struct dsdb_schema *schema = dsdb_get_schema(ldb, NULL);
672 NTTIME now;
674 unix_to_nt_time(&now, t);
676 for (i=0; i<el->num_values; i++) {
677 struct ldb_val *v = &el->values[i];
678 struct dsdb_dn *dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, v, sa->syntax->ldap_oid);
679 struct GUID target_guid;
680 NTSTATUS status;
681 int ret;
683 /* note that the DN already has the extended
684 components from the extended_dn_store module */
685 status = dsdb_get_extended_dn_guid(dsdb_dn->dn, &target_guid, "GUID");
686 if (!NT_STATUS_IS_OK(status) || GUID_all_zero(&target_guid)) {
687 ret = dsdb_module_guid_by_dn(module, dsdb_dn->dn, &target_guid);
688 if (ret != LDB_SUCCESS) {
689 talloc_free(tmp_ctx);
690 return ret;
692 ret = dsdb_set_extended_dn_guid(dsdb_dn->dn, &target_guid, "GUID");
693 if (ret != LDB_SUCCESS) {
694 talloc_free(tmp_ctx);
695 return ret;
699 ret = replmd_build_la_val(el->values, v, dsdb_dn, invocationId,
700 seq_num, seq_num, now, 0, false);
701 if (ret != LDB_SUCCESS) {
702 talloc_free(tmp_ctx);
703 return ret;
706 ret = replmd_add_backlink(module, schema, guid, &target_guid, true, sa, false);
707 if (ret != LDB_SUCCESS) {
708 talloc_free(tmp_ctx);
709 return ret;
713 talloc_free(tmp_ctx);
714 return LDB_SUCCESS;
719 intercept add requests
721 static int replmd_add(struct ldb_module *module, struct ldb_request *req)
723 struct ldb_context *ldb;
724 struct ldb_control *control;
725 struct replmd_replicated_request *ac;
726 enum ndr_err_code ndr_err;
727 struct ldb_request *down_req;
728 struct ldb_message *msg;
729 const DATA_BLOB *guid_blob;
730 struct GUID guid;
731 struct replPropertyMetaDataBlob nmd;
732 struct ldb_val nmd_value;
733 const struct GUID *our_invocation_id;
734 time_t t = time(NULL);
735 NTTIME now;
736 char *time_str;
737 int ret;
738 unsigned int i;
739 uint32_t ni=0;
740 bool allow_add_guid = false;
741 bool remove_current_guid = false;
742 bool is_urgent = false;
743 struct ldb_message_element *objectclass_el;
745 /* check if there's a show relax control (used by provision to say 'I know what I'm doing') */
746 control = ldb_request_get_control(req, LDB_CONTROL_RELAX_OID);
747 if (control) {
748 allow_add_guid = true;
751 /* do not manipulate our control entries */
752 if (ldb_dn_is_special(req->op.add.message->dn)) {
753 return ldb_next_request(module, req);
756 ldb = ldb_module_get_ctx(module);
758 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_add\n");
760 ac = replmd_ctx_init(module, req);
761 if (!ac) {
762 return LDB_ERR_OPERATIONS_ERROR;
765 guid_blob = ldb_msg_find_ldb_val(req->op.add.message, "objectGUID");
766 if ( guid_blob != NULL ) {
767 if( !allow_add_guid ) {
768 ldb_debug_set(ldb, LDB_DEBUG_ERROR,
769 "replmd_add: it's not allowed to add an object with objectGUID\n");
770 talloc_free(ac);
771 return LDB_ERR_UNWILLING_TO_PERFORM;
772 } else {
773 NTSTATUS status = GUID_from_data_blob(guid_blob,&guid);
774 if ( !NT_STATUS_IS_OK(status)) {
775 ldb_debug_set(ldb, LDB_DEBUG_ERROR,
776 "replmd_add: Unable to parse as a GUID the attribute objectGUID\n");
777 talloc_free(ac);
778 return LDB_ERR_UNWILLING_TO_PERFORM;
780 /* we remove this attribute as it can be a string and will not be treated
781 correctly and then we will readd it latter on in the good format*/
782 remove_current_guid = true;
784 } else {
785 /* a new GUID */
786 guid = GUID_random();
789 /* Get a sequence number from the backend */
790 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ac->seq_num);
791 if (ret != LDB_SUCCESS) {
792 talloc_free(ac);
793 return ret;
796 /* get our invocationId */
797 our_invocation_id = samdb_ntds_invocation_id(ldb);
798 if (!our_invocation_id) {
799 ldb_debug_set(ldb, LDB_DEBUG_ERROR,
800 "replmd_add: unable to find invocationId\n");
801 talloc_free(ac);
802 return LDB_ERR_OPERATIONS_ERROR;
805 /* we have to copy the message as the caller might have it as a const */
806 msg = ldb_msg_copy_shallow(ac, req->op.add.message);
807 if (msg == NULL) {
808 ldb_oom(ldb);
809 talloc_free(ac);
810 return LDB_ERR_OPERATIONS_ERROR;
813 /* generated times */
814 unix_to_nt_time(&now, t);
815 time_str = ldb_timestring(msg, t);
816 if (!time_str) {
817 ldb_oom(ldb);
818 talloc_free(ac);
819 return LDB_ERR_OPERATIONS_ERROR;
821 if (remove_current_guid) {
822 ldb_msg_remove_attr(msg,"objectGUID");
826 * remove autogenerated attributes
828 ldb_msg_remove_attr(msg, "whenCreated");
829 ldb_msg_remove_attr(msg, "whenChanged");
830 ldb_msg_remove_attr(msg, "uSNCreated");
831 ldb_msg_remove_attr(msg, "uSNChanged");
832 ldb_msg_remove_attr(msg, "replPropertyMetaData");
835 * readd replicated attributes
837 ret = ldb_msg_add_string(msg, "whenCreated", time_str);
838 if (ret != LDB_SUCCESS) {
839 ldb_oom(ldb);
840 talloc_free(ac);
841 return ret;
844 /* build the replication meta_data */
845 ZERO_STRUCT(nmd);
846 nmd.version = 1;
847 nmd.ctr.ctr1.count = msg->num_elements;
848 nmd.ctr.ctr1.array = talloc_array(msg,
849 struct replPropertyMetaData1,
850 nmd.ctr.ctr1.count);
851 if (!nmd.ctr.ctr1.array) {
852 ldb_oom(ldb);
853 talloc_free(ac);
854 return LDB_ERR_OPERATIONS_ERROR;
857 for (i=0; i < msg->num_elements; i++) {
858 struct ldb_message_element *e = &msg->elements[i];
859 struct replPropertyMetaData1 *m = &nmd.ctr.ctr1.array[ni];
860 const struct dsdb_attribute *sa;
862 if (e->name[0] == '@') continue;
864 sa = dsdb_attribute_by_lDAPDisplayName(ac->schema, e->name);
865 if (!sa) {
866 ldb_debug_set(ldb, LDB_DEBUG_ERROR,
867 "replmd_add: attribute '%s' not defined in schema\n",
868 e->name);
869 talloc_free(ac);
870 return LDB_ERR_NO_SUCH_ATTRIBUTE;
873 if ((sa->systemFlags & DS_FLAG_ATTR_NOT_REPLICATED) || (sa->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED)) {
874 /* if the attribute is not replicated (0x00000001)
875 * or constructed (0x00000004) it has no metadata
877 continue;
880 #if W2K3_LINKED_ATTRIBUTES
881 if (sa->linkID != 0 && dsdb_functional_level(ldb) > DS_DOMAIN_FUNCTION_2000) {
882 ret = replmd_add_fix_la(module, e, ac->seq_num, our_invocation_id, t, &guid, sa);
883 if (ret != LDB_SUCCESS) {
884 talloc_free(ac);
885 return ret;
887 /* linked attributes are not stored in
888 replPropertyMetaData in FL above w2k */
889 continue;
891 #endif
893 m->attid = sa->attributeID_id;
894 m->version = 1;
895 m->originating_change_time = now;
896 m->originating_invocation_id = *our_invocation_id;
897 m->originating_usn = ac->seq_num;
898 m->local_usn = ac->seq_num;
899 ni++;
902 /* fix meta data count */
903 nmd.ctr.ctr1.count = ni;
906 * sort meta data array, and move the rdn attribute entry to the end
908 ret = replmd_replPropertyMetaDataCtr1_sort(&nmd.ctr.ctr1, ac->schema, msg->dn);
909 if (ret != LDB_SUCCESS) {
910 talloc_free(ac);
911 return ret;
914 /* generated NDR encoded values */
915 ndr_err = ndr_push_struct_blob(&nmd_value, msg,
916 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
917 &nmd,
918 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
919 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
920 ldb_oom(ldb);
921 talloc_free(ac);
922 return LDB_ERR_OPERATIONS_ERROR;
926 * add the autogenerated values
928 ret = dsdb_msg_add_guid(msg, &guid, "objectGUID");
929 if (ret != LDB_SUCCESS) {
930 ldb_oom(ldb);
931 talloc_free(ac);
932 return ret;
934 ret = ldb_msg_add_string(msg, "whenChanged", time_str);
935 if (ret != LDB_SUCCESS) {
936 ldb_oom(ldb);
937 talloc_free(ac);
938 return ret;
940 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", ac->seq_num);
941 if (ret != LDB_SUCCESS) {
942 ldb_oom(ldb);
943 talloc_free(ac);
944 return ret;
946 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ac->seq_num);
947 if (ret != LDB_SUCCESS) {
948 ldb_oom(ldb);
949 talloc_free(ac);
950 return ret;
952 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
953 if (ret != LDB_SUCCESS) {
954 ldb_oom(ldb);
955 talloc_free(ac);
956 return ret;
960 * sort the attributes by attid before storing the object
962 replmd_ldb_message_sort(msg, ac->schema);
964 objectclass_el = ldb_msg_find_element(msg, "objectClass");
965 is_urgent = replmd_check_urgent_objectclass(objectclass_el,
966 REPL_URGENT_ON_CREATE);
968 ac->is_urgent = is_urgent;
969 ret = ldb_build_add_req(&down_req, ldb, ac,
970 msg,
971 req->controls,
972 ac, replmd_op_callback,
973 req);
975 if (ret != LDB_SUCCESS) {
976 talloc_free(ac);
977 return ret;
980 /* mark the control done */
981 if (control) {
982 control->critical = 0;
985 /* go on with the call chain */
986 return ldb_next_request(module, down_req);
991 * update the replPropertyMetaData for one element
993 static int replmd_update_rpmd_element(struct ldb_context *ldb,
994 struct ldb_message *msg,
995 struct ldb_message_element *el,
996 struct ldb_message_element *old_el,
997 struct replPropertyMetaDataBlob *omd,
998 const struct dsdb_schema *schema,
999 uint64_t *seq_num,
1000 const struct GUID *our_invocation_id,
1001 NTTIME now)
1003 uint32_t i;
1004 const struct dsdb_attribute *a;
1005 struct replPropertyMetaData1 *md1;
1007 a = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
1008 if (a == NULL) {
1009 DEBUG(0,(__location__ ": Unable to find attribute %s in schema\n",
1010 el->name));
1011 return LDB_ERR_OPERATIONS_ERROR;
1014 if ((a->systemFlags & DS_FLAG_ATTR_NOT_REPLICATED) || (a->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED)) {
1015 return LDB_SUCCESS;
1018 /* if the attribute's value haven't changed then return LDB_SUCCESS */
1019 if (old_el != NULL && ldb_msg_element_compare(el, old_el) == 0) {
1020 return LDB_SUCCESS;
1023 for (i=0; i<omd->ctr.ctr1.count; i++) {
1024 if (a->attributeID_id == omd->ctr.ctr1.array[i].attid) break;
1027 #if W2K3_LINKED_ATTRIBUTES
1028 if (a->linkID != 0 && dsdb_functional_level(ldb) > DS_DOMAIN_FUNCTION_2000) {
1029 /* linked attributes are not stored in
1030 replPropertyMetaData in FL above w2k, but we do
1031 raise the seqnum for the object */
1032 if (*seq_num == 0 &&
1033 ldb_sequence_number(ldb, LDB_SEQ_NEXT, seq_num) != LDB_SUCCESS) {
1034 return LDB_ERR_OPERATIONS_ERROR;
1036 return LDB_SUCCESS;
1038 #endif
1040 if (i == omd->ctr.ctr1.count) {
1041 /* we need to add a new one */
1042 omd->ctr.ctr1.array = talloc_realloc(msg, omd->ctr.ctr1.array,
1043 struct replPropertyMetaData1, omd->ctr.ctr1.count+1);
1044 if (omd->ctr.ctr1.array == NULL) {
1045 ldb_oom(ldb);
1046 return LDB_ERR_OPERATIONS_ERROR;
1048 omd->ctr.ctr1.count++;
1049 ZERO_STRUCT(omd->ctr.ctr1.array[i]);
1052 /* Get a new sequence number from the backend. We only do this
1053 * if we have a change that requires a new
1054 * replPropertyMetaData element
1056 if (*seq_num == 0) {
1057 int ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, seq_num);
1058 if (ret != LDB_SUCCESS) {
1059 return LDB_ERR_OPERATIONS_ERROR;
1063 md1 = &omd->ctr.ctr1.array[i];
1064 md1->version++;
1065 md1->attid = a->attributeID_id;
1066 md1->originating_change_time = now;
1067 md1->originating_invocation_id = *our_invocation_id;
1068 md1->originating_usn = *seq_num;
1069 md1->local_usn = *seq_num;
1071 return LDB_SUCCESS;
1075 * update the replPropertyMetaData object each time we modify an
1076 * object. This is needed for DRS replication, as the merge on the
1077 * client is based on this object
1079 static int replmd_update_rpmd(struct ldb_module *module,
1080 const struct dsdb_schema *schema,
1081 struct ldb_message *msg, uint64_t *seq_num,
1082 time_t t,
1083 bool *is_urgent)
1085 const struct ldb_val *omd_value;
1086 enum ndr_err_code ndr_err;
1087 struct replPropertyMetaDataBlob omd;
1088 unsigned int i;
1089 NTTIME now;
1090 const struct GUID *our_invocation_id;
1091 int ret;
1092 const char *attrs[] = { "replPropertyMetaData", "*", NULL };
1093 struct ldb_result *res;
1094 struct ldb_context *ldb;
1095 struct ldb_message_element *objectclass_el;
1096 enum urgent_situation situation;
1098 ldb = ldb_module_get_ctx(module);
1100 our_invocation_id = samdb_ntds_invocation_id(ldb);
1101 if (!our_invocation_id) {
1102 /* this happens during an initial vampire while
1103 updating the schema */
1104 DEBUG(5,("No invocationID - skipping replPropertyMetaData update\n"));
1105 return LDB_SUCCESS;
1108 unix_to_nt_time(&now, t);
1110 /* search for the existing replPropertyMetaDataBlob. We need
1111 * to use REVEAL and ask for DNs in storage format to support
1112 * the check for values being the same in
1113 * replmd_update_rpmd_element()
1115 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attrs,
1116 DSDB_SEARCH_SHOW_DELETED |
1117 DSDB_SEARCH_SHOW_EXTENDED_DN |
1118 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
1119 DSDB_SEARCH_REVEAL_INTERNALS);
1120 if (ret != LDB_SUCCESS || res->count != 1) {
1121 DEBUG(0,(__location__ ": Object %s failed to find replPropertyMetaData\n",
1122 ldb_dn_get_linearized(msg->dn)));
1123 return LDB_ERR_OPERATIONS_ERROR;
1126 /* if isDeleted is present and is TRUE, then we consider we are deleting,
1127 * otherwise we consider we are updating */
1128 if (ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")) {
1129 situation = REPL_URGENT_ON_DELETE;
1130 } else {
1131 situation = REPL_URGENT_ON_UPDATE;
1134 objectclass_el = ldb_msg_find_element(res->msgs[0], "objectClass");
1135 if (is_urgent && replmd_check_urgent_objectclass(objectclass_el,
1136 situation)) {
1137 *is_urgent = true;
1140 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
1141 if (!omd_value) {
1142 DEBUG(0,(__location__ ": Object %s does not have a replPropertyMetaData attribute\n",
1143 ldb_dn_get_linearized(msg->dn)));
1144 return LDB_ERR_OPERATIONS_ERROR;
1147 ndr_err = ndr_pull_struct_blob(omd_value, msg,
1148 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")), &omd,
1149 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
1150 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1151 DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s\n",
1152 ldb_dn_get_linearized(msg->dn)));
1153 return LDB_ERR_OPERATIONS_ERROR;
1156 if (omd.version != 1) {
1157 DEBUG(0,(__location__ ": bad version %u in replPropertyMetaData for %s\n",
1158 omd.version, ldb_dn_get_linearized(msg->dn)));
1159 return LDB_ERR_OPERATIONS_ERROR;
1162 for (i=0; i<msg->num_elements; i++) {
1163 struct ldb_message_element *old_el;
1164 old_el = ldb_msg_find_element(res->msgs[0], msg->elements[i].name);
1165 ret = replmd_update_rpmd_element(ldb, msg, &msg->elements[i], old_el, &omd, schema, seq_num,
1166 our_invocation_id, now);
1167 if (ret != LDB_SUCCESS) {
1168 return ret;
1171 if (is_urgent && !*is_urgent && (situation == REPL_URGENT_ON_UPDATE)) {
1172 *is_urgent = replmd_check_urgent_attribute(&msg->elements[i]);
1178 * replmd_update_rpmd_element has done an update if the
1179 * seq_num is set
1181 if (*seq_num != 0) {
1182 struct ldb_val *md_value;
1183 struct ldb_message_element *el;
1185 md_value = talloc(msg, struct ldb_val);
1186 if (md_value == NULL) {
1187 ldb_oom(ldb);
1188 return LDB_ERR_OPERATIONS_ERROR;
1191 ret = replmd_replPropertyMetaDataCtr1_sort(&omd.ctr.ctr1, schema, msg->dn);
1192 if (ret != LDB_SUCCESS) {
1193 return ret;
1196 ndr_err = ndr_push_struct_blob(md_value, msg,
1197 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
1198 &omd,
1199 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
1200 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1201 DEBUG(0,(__location__ ": Failed to marshall replPropertyMetaData for %s\n",
1202 ldb_dn_get_linearized(msg->dn)));
1203 return LDB_ERR_OPERATIONS_ERROR;
1206 ret = ldb_msg_add_empty(msg, "replPropertyMetaData", LDB_FLAG_MOD_REPLACE, &el);
1207 if (ret != LDB_SUCCESS) {
1208 DEBUG(0,(__location__ ": Failed to add updated replPropertyMetaData %s\n",
1209 ldb_dn_get_linearized(msg->dn)));
1210 return ret;
1213 el->num_values = 1;
1214 el->values = md_value;
1217 return LDB_SUCCESS;
1220 struct parsed_dn {
1221 struct dsdb_dn *dsdb_dn;
1222 struct GUID *guid;
1223 struct ldb_val *v;
1226 static int parsed_dn_compare(struct parsed_dn *pdn1, struct parsed_dn *pdn2)
1228 return GUID_compare(pdn1->guid, pdn2->guid);
1231 static struct parsed_dn *parsed_dn_find(struct parsed_dn *pdn, int count, struct GUID *guid, struct ldb_dn *dn)
1233 struct parsed_dn *ret;
1234 if (dn && GUID_all_zero(guid)) {
1235 /* when updating a link using DRS, we sometimes get a
1236 NULL GUID. We then need to try and match by DN */
1237 int i;
1238 for (i=0; i<count; i++) {
1239 if (ldb_dn_compare(pdn[i].dsdb_dn->dn, dn) == 0) {
1240 dsdb_get_extended_dn_guid(pdn[i].dsdb_dn->dn, guid, "GUID");
1241 return &pdn[i];
1244 return NULL;
1246 BINARY_ARRAY_SEARCH(pdn, count, guid, guid, GUID_compare, ret);
1247 return ret;
1251 get a series of message element values as an array of DNs and GUIDs
1252 the result is sorted by GUID
1254 static int get_parsed_dns(struct ldb_module *module, TALLOC_CTX *mem_ctx,
1255 struct ldb_message_element *el, struct parsed_dn **pdn,
1256 const char *ldap_oid)
1258 unsigned int i;
1259 struct ldb_context *ldb = ldb_module_get_ctx(module);
1261 if (el == NULL) {
1262 *pdn = NULL;
1263 return LDB_SUCCESS;
1266 (*pdn) = talloc_array(mem_ctx, struct parsed_dn, el->num_values);
1267 if (!*pdn) {
1268 ldb_module_oom(module);
1269 return LDB_ERR_OPERATIONS_ERROR;
1272 for (i=0; i<el->num_values; i++) {
1273 struct ldb_val *v = &el->values[i];
1274 NTSTATUS status;
1275 struct ldb_dn *dn;
1276 struct parsed_dn *p;
1278 p = &(*pdn)[i];
1280 p->dsdb_dn = dsdb_dn_parse(*pdn, ldb, v, ldap_oid);
1281 if (p->dsdb_dn == NULL) {
1282 return LDB_ERR_INVALID_DN_SYNTAX;
1285 dn = p->dsdb_dn->dn;
1287 p->guid = talloc(*pdn, struct GUID);
1288 if (p->guid == NULL) {
1289 ldb_module_oom(module);
1290 return LDB_ERR_OPERATIONS_ERROR;
1293 status = dsdb_get_extended_dn_guid(dn, p->guid, "GUID");
1294 if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
1295 /* we got a DN without a GUID - go find the GUID */
1296 int ret = dsdb_module_guid_by_dn(module, dn, p->guid);
1297 if (ret != LDB_SUCCESS) {
1298 ldb_asprintf_errstring(ldb, "Unable to find GUID for DN %s\n",
1299 ldb_dn_get_linearized(dn));
1300 return ret;
1302 ret = dsdb_set_extended_dn_guid(dn, p->guid, "GUID");
1303 if (ret != LDB_SUCCESS) {
1304 return ret;
1306 } else if (!NT_STATUS_IS_OK(status)) {
1307 return LDB_ERR_OPERATIONS_ERROR;
1310 /* keep a pointer to the original ldb_val */
1311 p->v = v;
1314 TYPESAFE_QSORT(*pdn, el->num_values, parsed_dn_compare);
1316 return LDB_SUCCESS;
1320 build a new extended DN, including all meta data fields
1322 RMD_FLAGS = DSDB_RMD_FLAG_* bits
1323 RMD_ADDTIME = originating_add_time
1324 RMD_INVOCID = originating_invocation_id
1325 RMD_CHANGETIME = originating_change_time
1326 RMD_ORIGINATING_USN = originating_usn
1327 RMD_LOCAL_USN = local_usn
1328 RMD_VERSION = version
1330 static int replmd_build_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
1331 const struct GUID *invocation_id, uint64_t seq_num,
1332 uint64_t local_usn, NTTIME nttime, uint32_t version, bool deleted)
1334 struct ldb_dn *dn = dsdb_dn->dn;
1335 const char *tstring, *usn_string, *flags_string;
1336 struct ldb_val tval;
1337 struct ldb_val iid;
1338 struct ldb_val usnv, local_usnv;
1339 struct ldb_val vers, flagsv;
1340 NTSTATUS status;
1341 int ret;
1342 const char *dnstring;
1343 char *vstring;
1344 uint32_t rmd_flags = deleted?DSDB_RMD_FLAG_DELETED:0;
1346 tstring = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)nttime);
1347 if (!tstring) {
1348 return LDB_ERR_OPERATIONS_ERROR;
1350 tval = data_blob_string_const(tstring);
1352 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)seq_num);
1353 if (!usn_string) {
1354 return LDB_ERR_OPERATIONS_ERROR;
1356 usnv = data_blob_string_const(usn_string);
1358 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)local_usn);
1359 if (!usn_string) {
1360 return LDB_ERR_OPERATIONS_ERROR;
1362 local_usnv = data_blob_string_const(usn_string);
1364 vstring = talloc_asprintf(mem_ctx, "%lu", (unsigned long)version);
1365 if (!vstring) {
1366 return LDB_ERR_OPERATIONS_ERROR;
1368 vers = data_blob_string_const(vstring);
1370 status = GUID_to_ndr_blob(invocation_id, dn, &iid);
1371 if (!NT_STATUS_IS_OK(status)) {
1372 return LDB_ERR_OPERATIONS_ERROR;
1375 flags_string = talloc_asprintf(mem_ctx, "%u", rmd_flags);
1376 if (!flags_string) {
1377 return LDB_ERR_OPERATIONS_ERROR;
1379 flagsv = data_blob_string_const(flags_string);
1381 ret = ldb_dn_set_extended_component(dn, "RMD_FLAGS", &flagsv);
1382 if (ret != LDB_SUCCESS) return ret;
1383 ret = ldb_dn_set_extended_component(dn, "RMD_ADDTIME", &tval);
1384 if (ret != LDB_SUCCESS) return ret;
1385 ret = ldb_dn_set_extended_component(dn, "RMD_INVOCID", &iid);
1386 if (ret != LDB_SUCCESS) return ret;
1387 ret = ldb_dn_set_extended_component(dn, "RMD_CHANGETIME", &tval);
1388 if (ret != LDB_SUCCESS) return ret;
1389 ret = ldb_dn_set_extended_component(dn, "RMD_LOCAL_USN", &local_usnv);
1390 if (ret != LDB_SUCCESS) return ret;
1391 ret = ldb_dn_set_extended_component(dn, "RMD_ORIGINATING_USN", &usnv);
1392 if (ret != LDB_SUCCESS) return ret;
1393 ret = ldb_dn_set_extended_component(dn, "RMD_VERSION", &vers);
1394 if (ret != LDB_SUCCESS) return ret;
1396 dnstring = dsdb_dn_get_extended_linearized(mem_ctx, dsdb_dn, 1);
1397 if (dnstring == NULL) {
1398 return LDB_ERR_OPERATIONS_ERROR;
1400 *v = data_blob_string_const(dnstring);
1402 return LDB_SUCCESS;
1405 static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
1406 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
1407 uint64_t seq_num, uint64_t local_usn, NTTIME nttime,
1408 uint32_t version, bool deleted);
1411 check if any links need upgrading from w2k format
1413 static int replmd_check_upgrade_links(struct parsed_dn *dns, uint32_t count, const struct GUID *invocation_id)
1415 uint32_t i;
1416 for (i=0; i<count; i++) {
1417 NTSTATUS status;
1418 uint32_t version;
1419 int ret;
1421 status = dsdb_get_extended_dn_uint32(dns[i].dsdb_dn->dn, &version, "RMD_VERSION");
1422 if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
1423 continue;
1426 /* it's an old one that needs upgrading */
1427 ret = replmd_update_la_val(dns, dns[i].v, dns[i].dsdb_dn, dns[i].dsdb_dn, invocation_id,
1428 1, 1, 0, 0, false);
1429 if (ret != LDB_SUCCESS) {
1430 return ret;
1433 return LDB_SUCCESS;
1437 update an extended DN, including all meta data fields
1439 see replmd_build_la_val for value names
1441 static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
1442 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
1443 uint64_t seq_num, uint64_t local_usn, NTTIME nttime,
1444 uint32_t version, bool deleted)
1446 struct ldb_dn *dn = dsdb_dn->dn;
1447 const char *tstring, *usn_string, *flags_string;
1448 struct ldb_val tval;
1449 struct ldb_val iid;
1450 struct ldb_val usnv, local_usnv;
1451 struct ldb_val vers, flagsv;
1452 const struct ldb_val *old_addtime;
1453 uint32_t old_version;
1454 NTSTATUS status;
1455 int ret;
1456 const char *dnstring;
1457 char *vstring;
1458 uint32_t rmd_flags = deleted?DSDB_RMD_FLAG_DELETED:0;
1460 tstring = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)nttime);
1461 if (!tstring) {
1462 return LDB_ERR_OPERATIONS_ERROR;
1464 tval = data_blob_string_const(tstring);
1466 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)seq_num);
1467 if (!usn_string) {
1468 return LDB_ERR_OPERATIONS_ERROR;
1470 usnv = data_blob_string_const(usn_string);
1472 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)local_usn);
1473 if (!usn_string) {
1474 return LDB_ERR_OPERATIONS_ERROR;
1476 local_usnv = data_blob_string_const(usn_string);
1478 status = GUID_to_ndr_blob(invocation_id, dn, &iid);
1479 if (!NT_STATUS_IS_OK(status)) {
1480 return LDB_ERR_OPERATIONS_ERROR;
1483 flags_string = talloc_asprintf(mem_ctx, "%u", rmd_flags);
1484 if (!flags_string) {
1485 return LDB_ERR_OPERATIONS_ERROR;
1487 flagsv = data_blob_string_const(flags_string);
1489 ret = ldb_dn_set_extended_component(dn, "RMD_FLAGS", &flagsv);
1490 if (ret != LDB_SUCCESS) return ret;
1492 /* get the ADDTIME from the original */
1493 old_addtime = ldb_dn_get_extended_component(old_dsdb_dn->dn, "RMD_ADDTIME");
1494 if (old_addtime == NULL) {
1495 old_addtime = &tval;
1497 if (dsdb_dn != old_dsdb_dn) {
1498 ret = ldb_dn_set_extended_component(dn, "RMD_ADDTIME", old_addtime);
1499 if (ret != LDB_SUCCESS) return ret;
1502 /* use our invocation id */
1503 ret = ldb_dn_set_extended_component(dn, "RMD_INVOCID", &iid);
1504 if (ret != LDB_SUCCESS) return ret;
1506 /* changetime is the current time */
1507 ret = ldb_dn_set_extended_component(dn, "RMD_CHANGETIME", &tval);
1508 if (ret != LDB_SUCCESS) return ret;
1510 /* update the USN */
1511 ret = ldb_dn_set_extended_component(dn, "RMD_ORIGINATING_USN", &usnv);
1512 if (ret != LDB_SUCCESS) return ret;
1514 ret = ldb_dn_set_extended_component(dn, "RMD_LOCAL_USN", &local_usnv);
1515 if (ret != LDB_SUCCESS) return ret;
1517 /* increase the version by 1 */
1518 status = dsdb_get_extended_dn_uint32(old_dsdb_dn->dn, &old_version, "RMD_VERSION");
1519 if (NT_STATUS_IS_OK(status) && old_version >= version) {
1520 version = old_version+1;
1522 vstring = talloc_asprintf(dn, "%lu", (unsigned long)version);
1523 vers = data_blob_string_const(vstring);
1524 ret = ldb_dn_set_extended_component(dn, "RMD_VERSION", &vers);
1525 if (ret != LDB_SUCCESS) return ret;
1527 dnstring = dsdb_dn_get_extended_linearized(mem_ctx, dsdb_dn, 1);
1528 if (dnstring == NULL) {
1529 return LDB_ERR_OPERATIONS_ERROR;
1531 *v = data_blob_string_const(dnstring);
1533 return LDB_SUCCESS;
1537 handle adding a linked attribute
1539 static int replmd_modify_la_add(struct ldb_module *module,
1540 const struct dsdb_schema *schema,
1541 struct ldb_message *msg,
1542 struct ldb_message_element *el,
1543 struct ldb_message_element *old_el,
1544 const struct dsdb_attribute *schema_attr,
1545 uint64_t seq_num,
1546 time_t t,
1547 struct GUID *msg_guid)
1549 unsigned int i;
1550 struct parsed_dn *dns, *old_dns;
1551 TALLOC_CTX *tmp_ctx = talloc_new(msg);
1552 int ret;
1553 struct ldb_val *new_values = NULL;
1554 unsigned int num_new_values = 0;
1555 unsigned old_num_values = old_el?old_el->num_values:0;
1556 const struct GUID *invocation_id;
1557 struct ldb_context *ldb = ldb_module_get_ctx(module);
1558 NTTIME now;
1560 unix_to_nt_time(&now, t);
1562 ret = get_parsed_dns(module, tmp_ctx, el, &dns, schema_attr->syntax->ldap_oid);
1563 if (ret != LDB_SUCCESS) {
1564 talloc_free(tmp_ctx);
1565 return ret;
1568 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns, schema_attr->syntax->ldap_oid);
1569 if (ret != LDB_SUCCESS) {
1570 talloc_free(tmp_ctx);
1571 return ret;
1574 invocation_id = samdb_ntds_invocation_id(ldb);
1575 if (!invocation_id) {
1576 talloc_free(tmp_ctx);
1577 return LDB_ERR_OPERATIONS_ERROR;
1580 ret = replmd_check_upgrade_links(old_dns, old_num_values, invocation_id);
1581 if (ret != LDB_SUCCESS) {
1582 talloc_free(tmp_ctx);
1583 return ret;
1586 /* for each new value, see if it exists already with the same GUID */
1587 for (i=0; i<el->num_values; i++) {
1588 struct parsed_dn *p = parsed_dn_find(old_dns, old_num_values, dns[i].guid, NULL);
1589 if (p == NULL) {
1590 /* this is a new linked attribute value */
1591 new_values = talloc_realloc(tmp_ctx, new_values, struct ldb_val, num_new_values+1);
1592 if (new_values == NULL) {
1593 ldb_module_oom(module);
1594 talloc_free(tmp_ctx);
1595 return LDB_ERR_OPERATIONS_ERROR;
1597 ret = replmd_build_la_val(new_values, &new_values[num_new_values], dns[i].dsdb_dn,
1598 invocation_id, seq_num, seq_num, now, 0, false);
1599 if (ret != LDB_SUCCESS) {
1600 talloc_free(tmp_ctx);
1601 return ret;
1603 num_new_values++;
1604 } else {
1605 /* this is only allowed if the GUID was
1606 previously deleted. */
1607 uint32_t rmd_flags = dsdb_dn_rmd_flags(p->dsdb_dn->dn);
1609 if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
1610 ldb_asprintf_errstring(ldb, "Attribute %s already exists for target GUID %s",
1611 el->name, GUID_string(tmp_ctx, p->guid));
1612 talloc_free(tmp_ctx);
1613 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
1615 ret = replmd_update_la_val(old_el->values, p->v, dns[i].dsdb_dn, p->dsdb_dn,
1616 invocation_id, seq_num, seq_num, now, 0, false);
1617 if (ret != LDB_SUCCESS) {
1618 talloc_free(tmp_ctx);
1619 return ret;
1623 ret = replmd_add_backlink(module, schema, msg_guid, dns[i].guid, true, schema_attr, true);
1624 if (ret != LDB_SUCCESS) {
1625 talloc_free(tmp_ctx);
1626 return ret;
1630 /* add the new ones on to the end of the old values, constructing a new el->values */
1631 el->values = talloc_realloc(msg->elements, old_el?old_el->values:NULL,
1632 struct ldb_val,
1633 old_num_values+num_new_values);
1634 if (el->values == NULL) {
1635 ldb_module_oom(module);
1636 return LDB_ERR_OPERATIONS_ERROR;
1639 memcpy(&el->values[old_num_values], new_values, num_new_values*sizeof(struct ldb_val));
1640 el->num_values = old_num_values + num_new_values;
1642 talloc_steal(msg->elements, el->values);
1643 talloc_steal(el->values, new_values);
1645 talloc_free(tmp_ctx);
1647 /* we now tell the backend to replace all existing values
1648 with the one we have constructed */
1649 el->flags = LDB_FLAG_MOD_REPLACE;
1651 return LDB_SUCCESS;
1656 handle deleting all active linked attributes
1658 static int replmd_modify_la_delete(struct ldb_module *module,
1659 const struct dsdb_schema *schema,
1660 struct ldb_message *msg,
1661 struct ldb_message_element *el,
1662 struct ldb_message_element *old_el,
1663 const struct dsdb_attribute *schema_attr,
1664 uint64_t seq_num,
1665 time_t t,
1666 struct GUID *msg_guid)
1668 unsigned int i;
1669 struct parsed_dn *dns, *old_dns;
1670 TALLOC_CTX *tmp_ctx = talloc_new(msg);
1671 int ret;
1672 const struct GUID *invocation_id;
1673 struct ldb_context *ldb = ldb_module_get_ctx(module);
1674 NTTIME now;
1676 unix_to_nt_time(&now, t);
1678 /* check if there is nothing to delete */
1679 if ((!old_el || old_el->num_values == 0) &&
1680 el->num_values == 0) {
1681 return LDB_SUCCESS;
1684 if (!old_el || old_el->num_values == 0) {
1685 return LDB_ERR_NO_SUCH_ATTRIBUTE;
1688 ret = get_parsed_dns(module, tmp_ctx, el, &dns, schema_attr->syntax->ldap_oid);
1689 if (ret != LDB_SUCCESS) {
1690 talloc_free(tmp_ctx);
1691 return ret;
1694 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns, schema_attr->syntax->ldap_oid);
1695 if (ret != LDB_SUCCESS) {
1696 talloc_free(tmp_ctx);
1697 return ret;
1700 invocation_id = samdb_ntds_invocation_id(ldb);
1701 if (!invocation_id) {
1702 return LDB_ERR_OPERATIONS_ERROR;
1705 ret = replmd_check_upgrade_links(old_dns, old_el->num_values, invocation_id);
1706 if (ret != LDB_SUCCESS) {
1707 talloc_free(tmp_ctx);
1708 return ret;
1711 el->values = NULL;
1713 /* see if we are being asked to delete any links that
1714 don't exist or are already deleted */
1715 for (i=0; i<el->num_values; i++) {
1716 struct parsed_dn *p = &dns[i];
1717 struct parsed_dn *p2;
1718 uint32_t rmd_flags;
1720 p2 = parsed_dn_find(old_dns, old_el->num_values, p->guid, NULL);
1721 if (!p2) {
1722 ldb_asprintf_errstring(ldb, "Attribute %s doesn't exist for target GUID %s",
1723 el->name, GUID_string(tmp_ctx, p->guid));
1724 return LDB_ERR_NO_SUCH_ATTRIBUTE;
1726 rmd_flags = dsdb_dn_rmd_flags(p2->dsdb_dn->dn);
1727 if (rmd_flags & DSDB_RMD_FLAG_DELETED) {
1728 ldb_asprintf_errstring(ldb, "Attribute %s already deleted for target GUID %s",
1729 el->name, GUID_string(tmp_ctx, p->guid));
1730 return LDB_ERR_NO_SUCH_ATTRIBUTE;
1734 /* for each new value, see if it exists already with the same GUID
1735 if it is not already deleted and matches the delete list then delete it
1737 for (i=0; i<old_el->num_values; i++) {
1738 struct parsed_dn *p = &old_dns[i];
1739 uint32_t rmd_flags;
1741 if (el->num_values && parsed_dn_find(dns, el->num_values, p->guid, NULL) == NULL) {
1742 continue;
1745 rmd_flags = dsdb_dn_rmd_flags(p->dsdb_dn->dn);
1746 if (rmd_flags & DSDB_RMD_FLAG_DELETED) continue;
1748 ret = replmd_update_la_val(old_el->values, p->v, p->dsdb_dn, p->dsdb_dn,
1749 invocation_id, seq_num, seq_num, now, 0, true);
1750 if (ret != LDB_SUCCESS) {
1751 talloc_free(tmp_ctx);
1752 return ret;
1755 ret = replmd_add_backlink(module, schema, msg_guid, old_dns[i].guid, false, schema_attr, true);
1756 if (ret != LDB_SUCCESS) {
1757 talloc_free(tmp_ctx);
1758 return ret;
1762 el->values = talloc_steal(msg->elements, old_el->values);
1763 el->num_values = old_el->num_values;
1765 talloc_free(tmp_ctx);
1767 /* we now tell the backend to replace all existing values
1768 with the one we have constructed */
1769 el->flags = LDB_FLAG_MOD_REPLACE;
1771 return LDB_SUCCESS;
1775 handle replacing a linked attribute
1777 static int replmd_modify_la_replace(struct ldb_module *module,
1778 const struct dsdb_schema *schema,
1779 struct ldb_message *msg,
1780 struct ldb_message_element *el,
1781 struct ldb_message_element *old_el,
1782 const struct dsdb_attribute *schema_attr,
1783 uint64_t seq_num,
1784 time_t t,
1785 struct GUID *msg_guid)
1787 unsigned int i;
1788 struct parsed_dn *dns, *old_dns;
1789 TALLOC_CTX *tmp_ctx = talloc_new(msg);
1790 int ret;
1791 const struct GUID *invocation_id;
1792 struct ldb_context *ldb = ldb_module_get_ctx(module);
1793 struct ldb_val *new_values = NULL;
1794 unsigned int num_new_values = 0;
1795 unsigned int old_num_values = old_el?old_el->num_values:0;
1796 NTTIME now;
1798 unix_to_nt_time(&now, t);
1800 /* check if there is nothing to replace */
1801 if ((!old_el || old_el->num_values == 0) &&
1802 el->num_values == 0) {
1803 return LDB_SUCCESS;
1806 ret = get_parsed_dns(module, tmp_ctx, el, &dns, schema_attr->syntax->ldap_oid);
1807 if (ret != LDB_SUCCESS) {
1808 talloc_free(tmp_ctx);
1809 return ret;
1812 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns, schema_attr->syntax->ldap_oid);
1813 if (ret != LDB_SUCCESS) {
1814 talloc_free(tmp_ctx);
1815 return ret;
1818 invocation_id = samdb_ntds_invocation_id(ldb);
1819 if (!invocation_id) {
1820 return LDB_ERR_OPERATIONS_ERROR;
1823 ret = replmd_check_upgrade_links(old_dns, old_num_values, invocation_id);
1824 if (ret != LDB_SUCCESS) {
1825 talloc_free(tmp_ctx);
1826 return ret;
1829 /* mark all the old ones as deleted */
1830 for (i=0; i<old_num_values; i++) {
1831 struct parsed_dn *old_p = &old_dns[i];
1832 struct parsed_dn *p;
1833 uint32_t rmd_flags = dsdb_dn_rmd_flags(old_p->dsdb_dn->dn);
1835 if (rmd_flags & DSDB_RMD_FLAG_DELETED) continue;
1837 ret = replmd_add_backlink(module, schema, msg_guid, old_dns[i].guid, false, schema_attr, false);
1838 if (ret != LDB_SUCCESS) {
1839 talloc_free(tmp_ctx);
1840 return ret;
1843 p = parsed_dn_find(dns, el->num_values, old_p->guid, NULL);
1844 if (p) {
1845 /* we don't delete it if we are re-adding it */
1846 continue;
1849 ret = replmd_update_la_val(old_el->values, old_p->v, old_p->dsdb_dn, old_p->dsdb_dn,
1850 invocation_id, seq_num, seq_num, now, 0, true);
1851 if (ret != LDB_SUCCESS) {
1852 talloc_free(tmp_ctx);
1853 return ret;
1857 /* for each new value, either update its meta-data, or add it
1858 * to old_el
1860 for (i=0; i<el->num_values; i++) {
1861 struct parsed_dn *p = &dns[i], *old_p;
1863 if (old_dns &&
1864 (old_p = parsed_dn_find(old_dns,
1865 old_num_values, p->guid, NULL)) != NULL) {
1866 /* update in place */
1867 ret = replmd_update_la_val(old_el->values, old_p->v, old_p->dsdb_dn,
1868 old_p->dsdb_dn, invocation_id,
1869 seq_num, seq_num, now, 0, false);
1870 if (ret != LDB_SUCCESS) {
1871 talloc_free(tmp_ctx);
1872 return ret;
1874 } else {
1875 /* add a new one */
1876 new_values = talloc_realloc(tmp_ctx, new_values, struct ldb_val,
1877 num_new_values+1);
1878 if (new_values == NULL) {
1879 ldb_module_oom(module);
1880 talloc_free(tmp_ctx);
1881 return LDB_ERR_OPERATIONS_ERROR;
1883 ret = replmd_build_la_val(new_values, &new_values[num_new_values], dns[i].dsdb_dn,
1884 invocation_id, seq_num, seq_num, now, 0, false);
1885 if (ret != LDB_SUCCESS) {
1886 talloc_free(tmp_ctx);
1887 return ret;
1889 num_new_values++;
1892 ret = replmd_add_backlink(module, schema, msg_guid, dns[i].guid, true, schema_attr, false);
1893 if (ret != LDB_SUCCESS) {
1894 talloc_free(tmp_ctx);
1895 return ret;
1899 /* add the new values to the end of old_el */
1900 if (num_new_values != 0) {
1901 el->values = talloc_realloc(msg->elements, old_el?old_el->values:NULL,
1902 struct ldb_val, old_num_values+num_new_values);
1903 if (el->values == NULL) {
1904 ldb_module_oom(module);
1905 return LDB_ERR_OPERATIONS_ERROR;
1907 memcpy(&el->values[old_num_values], &new_values[0],
1908 sizeof(struct ldb_val)*num_new_values);
1909 el->num_values = old_num_values + num_new_values;
1910 talloc_steal(msg->elements, new_values);
1911 } else {
1912 el->values = old_el->values;
1913 el->num_values = old_el->num_values;
1914 talloc_steal(msg->elements, el->values);
1917 talloc_free(tmp_ctx);
1919 /* we now tell the backend to replace all existing values
1920 with the one we have constructed */
1921 el->flags = LDB_FLAG_MOD_REPLACE;
1923 return LDB_SUCCESS;
1928 handle linked attributes in modify requests
1930 static int replmd_modify_handle_linked_attribs(struct ldb_module *module,
1931 struct ldb_message *msg,
1932 uint64_t seq_num, time_t t)
1934 struct ldb_result *res;
1935 unsigned int i;
1936 int ret;
1937 struct ldb_context *ldb = ldb_module_get_ctx(module);
1938 struct ldb_message *old_msg;
1940 const struct dsdb_schema *schema;
1941 struct GUID old_guid;
1943 if (seq_num == 0) {
1944 /* there the replmd_update_rpmd code has already
1945 * checked and saw that there are no linked
1946 * attributes */
1947 return LDB_SUCCESS;
1950 #if !W2K3_LINKED_ATTRIBUTES
1951 return LDB_SUCCESS;
1952 #endif
1954 if (dsdb_functional_level(ldb) == DS_DOMAIN_FUNCTION_2000) {
1955 /* don't do anything special for linked attributes */
1956 return LDB_SUCCESS;
1959 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, NULL,
1960 DSDB_SEARCH_SHOW_DELETED |
1961 DSDB_SEARCH_REVEAL_INTERNALS |
1962 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT);
1963 if (ret != LDB_SUCCESS) {
1964 return ret;
1966 schema = dsdb_get_schema(ldb, res);
1967 if (!schema) {
1968 return LDB_ERR_OPERATIONS_ERROR;
1971 old_msg = res->msgs[0];
1973 old_guid = samdb_result_guid(old_msg, "objectGUID");
1975 for (i=0; i<msg->num_elements; i++) {
1976 struct ldb_message_element *el = &msg->elements[i];
1977 struct ldb_message_element *old_el, *new_el;
1978 const struct dsdb_attribute *schema_attr
1979 = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
1980 if (!schema_attr) {
1981 ldb_asprintf_errstring(ldb,
1982 "attribute %s is not a valid attribute in schema", el->name);
1983 return LDB_ERR_OBJECT_CLASS_VIOLATION;
1985 if (schema_attr->linkID == 0) {
1986 continue;
1988 if ((schema_attr->linkID & 1) == 1) {
1989 /* Odd is for the target. Illegal to modify */
1990 ldb_asprintf_errstring(ldb,
1991 "attribute %s must not be modified directly, it is a linked attribute", el->name);
1992 return LDB_ERR_UNWILLING_TO_PERFORM;
1994 old_el = ldb_msg_find_element(old_msg, el->name);
1995 switch (el->flags & LDB_FLAG_MOD_MASK) {
1996 case LDB_FLAG_MOD_REPLACE:
1997 ret = replmd_modify_la_replace(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid);
1998 break;
1999 case LDB_FLAG_MOD_DELETE:
2000 ret = replmd_modify_la_delete(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid);
2001 break;
2002 case LDB_FLAG_MOD_ADD:
2003 ret = replmd_modify_la_add(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid);
2004 break;
2005 default:
2006 ldb_asprintf_errstring(ldb,
2007 "invalid flags 0x%x for %s linked attribute",
2008 el->flags, el->name);
2009 return LDB_ERR_UNWILLING_TO_PERFORM;
2011 if (ret != LDB_SUCCESS) {
2012 return ret;
2014 if (old_el) {
2015 ldb_msg_remove_attr(old_msg, el->name);
2017 ldb_msg_add_empty(old_msg, el->name, 0, &new_el);
2018 new_el->num_values = el->num_values;
2019 new_el->values = talloc_steal(msg->elements, el->values);
2021 /* TODO: this relises a bit too heavily on the exact
2022 behaviour of ldb_msg_find_element and
2023 ldb_msg_remove_element */
2024 old_el = ldb_msg_find_element(msg, el->name);
2025 if (old_el != el) {
2026 ldb_msg_remove_element(msg, old_el);
2027 i--;
2031 talloc_free(res);
2032 return ret;
2037 static int replmd_modify(struct ldb_module *module, struct ldb_request *req)
2039 struct ldb_context *ldb;
2040 struct replmd_replicated_request *ac;
2041 struct ldb_request *down_req;
2042 struct ldb_message *msg;
2043 time_t t = time(NULL);
2044 int ret;
2045 bool is_urgent = false;
2047 /* do not manipulate our control entries */
2048 if (ldb_dn_is_special(req->op.mod.message->dn)) {
2049 return ldb_next_request(module, req);
2052 ldb = ldb_module_get_ctx(module);
2054 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_modify\n");
2056 ac = replmd_ctx_init(module, req);
2057 if (!ac) {
2058 return LDB_ERR_OPERATIONS_ERROR;
2061 /* we have to copy the message as the caller might have it as a const */
2062 msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
2063 if (msg == NULL) {
2064 ldb_oom(ldb);
2065 talloc_free(ac);
2066 return LDB_ERR_OPERATIONS_ERROR;
2069 ldb_msg_remove_attr(msg, "whenChanged");
2070 ldb_msg_remove_attr(msg, "uSNChanged");
2072 ret = replmd_update_rpmd(module, ac->schema, msg, &ac->seq_num, t, &is_urgent);
2073 if (ret != LDB_SUCCESS) {
2074 talloc_free(ac);
2075 return ret;
2078 ret = replmd_modify_handle_linked_attribs(module, msg, ac->seq_num, t);
2079 if (ret != LDB_SUCCESS) {
2080 talloc_free(ac);
2081 return ret;
2084 /* TODO:
2085 * - replace the old object with the newly constructed one
2088 ac->is_urgent = is_urgent;
2090 ret = ldb_build_mod_req(&down_req, ldb, ac,
2091 msg,
2092 req->controls,
2093 ac, replmd_op_callback,
2094 req);
2095 if (ret != LDB_SUCCESS) {
2096 talloc_free(ac);
2097 return ret;
2099 talloc_steal(down_req, msg);
2101 /* we only change whenChanged and uSNChanged if the seq_num
2102 has changed */
2103 if (ac->seq_num != 0) {
2104 if (add_time_element(msg, "whenChanged", t) != LDB_SUCCESS) {
2105 talloc_free(ac);
2106 return ret;
2109 if (add_uint64_element(msg, "uSNChanged", ac->seq_num) != LDB_SUCCESS) {
2110 talloc_free(ac);
2111 return ret;
2115 /* go on with the call chain */
2116 return ldb_next_request(module, down_req);
2119 static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares);
2122 handle a rename request
2124 On a rename we need to do an extra ldb_modify which sets the
2125 whenChanged and uSNChanged attributes. We do this in a callback after the success.
2127 static int replmd_rename(struct ldb_module *module, struct ldb_request *req)
2129 struct ldb_context *ldb;
2130 struct replmd_replicated_request *ac;
2131 int ret;
2132 struct ldb_request *down_req;
2134 /* do not manipulate our control entries */
2135 if (ldb_dn_is_special(req->op.mod.message->dn)) {
2136 return ldb_next_request(module, req);
2139 ldb = ldb_module_get_ctx(module);
2141 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_rename\n");
2143 ac = replmd_ctx_init(module, req);
2144 if (!ac) {
2145 return LDB_ERR_OPERATIONS_ERROR;
2147 ret = ldb_build_rename_req(&down_req, ldb, ac,
2148 ac->req->op.rename.olddn,
2149 ac->req->op.rename.newdn,
2150 ac->req->controls,
2151 ac, replmd_rename_callback,
2152 ac->req);
2154 if (ret != LDB_SUCCESS) {
2155 talloc_free(ac);
2156 return ret;
2159 /* go on with the call chain */
2160 return ldb_next_request(module, down_req);
2163 /* After the rename is compleated, update the whenchanged etc */
2164 static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares)
2166 struct ldb_context *ldb;
2167 struct replmd_replicated_request *ac;
2168 struct ldb_request *down_req;
2169 struct ldb_message *msg;
2170 time_t t = time(NULL);
2171 int ret;
2173 ac = talloc_get_type(req->context, struct replmd_replicated_request);
2174 ldb = ldb_module_get_ctx(ac->module);
2176 if (ares->error != LDB_SUCCESS) {
2177 return ldb_module_done(ac->req, ares->controls,
2178 ares->response, ares->error);
2181 if (ares->type != LDB_REPLY_DONE) {
2182 ldb_set_errstring(ldb,
2183 "invalid ldb_reply_type in callback");
2184 talloc_free(ares);
2185 return ldb_module_done(ac->req, NULL, NULL,
2186 LDB_ERR_OPERATIONS_ERROR);
2189 /* Get a sequence number from the backend */
2190 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ac->seq_num);
2191 if (ret != LDB_SUCCESS) {
2192 return ret;
2195 /* TODO:
2196 * - replace the old object with the newly constructed one
2199 msg = ldb_msg_new(ac);
2200 if (msg == NULL) {
2201 ldb_oom(ldb);
2202 return LDB_ERR_OPERATIONS_ERROR;
2205 msg->dn = ac->req->op.rename.newdn;
2207 ret = ldb_build_mod_req(&down_req, ldb, ac,
2208 msg,
2209 req->controls,
2210 ac, replmd_op_callback,
2211 req);
2213 if (ret != LDB_SUCCESS) {
2214 talloc_free(ac);
2215 return ret;
2217 talloc_steal(down_req, msg);
2219 if (add_time_element(msg, "whenChanged", t) != LDB_SUCCESS) {
2220 talloc_free(ac);
2221 return ret;
2224 if (add_uint64_element(msg, "uSNChanged", ac->seq_num) != LDB_SUCCESS) {
2225 talloc_free(ac);
2226 return ret;
2229 /* go on with the call chain - do the modify after the rename */
2230 return ldb_next_request(ac->module, down_req);
2234 remove links from objects that point at this object when an object
2235 is deleted
2237 static int replmd_delete_remove_link(struct ldb_module *module,
2238 const struct dsdb_schema *schema,
2239 struct ldb_dn *dn,
2240 struct ldb_message_element *el,
2241 const struct dsdb_attribute *sa)
2243 unsigned int i;
2244 TALLOC_CTX *tmp_ctx = talloc_new(module);
2245 struct ldb_context *ldb = ldb_module_get_ctx(module);
2247 for (i=0; i<el->num_values; i++) {
2248 struct dsdb_dn *dsdb_dn;
2249 NTSTATUS status;
2250 int ret;
2251 struct GUID guid2;
2252 struct ldb_message *msg;
2253 const struct dsdb_attribute *target_attr;
2254 struct ldb_message_element *el2;
2255 struct ldb_val dn_val;
2257 if (dsdb_dn_is_deleted_val(&el->values[i])) {
2258 continue;
2261 dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i], sa->syntax->ldap_oid);
2262 if (!dsdb_dn) {
2263 talloc_free(tmp_ctx);
2264 return LDB_ERR_OPERATIONS_ERROR;
2267 status = dsdb_get_extended_dn_guid(dsdb_dn->dn, &guid2, "GUID");
2268 if (!NT_STATUS_IS_OK(status)) {
2269 talloc_free(tmp_ctx);
2270 return LDB_ERR_OPERATIONS_ERROR;
2273 /* remove the link */
2274 msg = ldb_msg_new(tmp_ctx);
2275 if (!msg) {
2276 ldb_module_oom(module);
2277 talloc_free(tmp_ctx);
2278 return LDB_ERR_OPERATIONS_ERROR;
2282 msg->dn = dsdb_dn->dn;
2284 target_attr = dsdb_attribute_by_linkID(schema, sa->linkID ^ 1);
2285 if (target_attr == NULL) {
2286 continue;
2289 ret = ldb_msg_add_empty(msg, target_attr->lDAPDisplayName, LDB_FLAG_MOD_DELETE, &el2);
2290 if (ret != LDB_SUCCESS) {
2291 ldb_module_oom(module);
2292 talloc_free(tmp_ctx);
2293 return LDB_ERR_OPERATIONS_ERROR;
2295 dn_val = data_blob_string_const(ldb_dn_get_linearized(dn));
2296 el2->values = &dn_val;
2297 el2->num_values = 1;
2299 ret = dsdb_module_modify(module, msg, DSDB_FLAG_OWN_MODULE);
2300 if (ret != LDB_SUCCESS) {
2301 talloc_free(tmp_ctx);
2302 return ret;
2305 talloc_free(tmp_ctx);
2306 return LDB_SUCCESS;
2311 handle update of replication meta data for deletion of objects
2313 This also handles the mapping of delete to a rename operation
2314 to allow deletes to be replicated.
2316 static int replmd_delete(struct ldb_module *module, struct ldb_request *req)
2318 int ret = LDB_ERR_OTHER;
2319 bool retb;
2320 struct ldb_dn *old_dn, *new_dn;
2321 const char *rdn_name;
2322 const struct ldb_val *rdn_value, *new_rdn_value;
2323 struct GUID guid;
2324 struct ldb_context *ldb = ldb_module_get_ctx(module);
2325 const struct dsdb_schema *schema;
2326 struct ldb_message *msg, *old_msg;
2327 struct ldb_message_element *el;
2328 TALLOC_CTX *tmp_ctx;
2329 struct ldb_result *res, *parent_res;
2330 const char *preserved_attrs[] = {
2331 /* yes, this really is a hard coded list. See MS-ADTS
2332 section 3.1.1.5.5.1.1 */
2333 "nTSecurityDescriptor", "attributeID", "attributeSyntax", "dNReferenceUpdate", "dNSHostName",
2334 "flatName", "governsID", "groupType", "instanceType", "lDAPDisplayName", "legacyExchangeDN",
2335 "isDeleted", "isRecycled", "lastKnownParent", "msDS-LastKnownRDN", "mS-DS-CreatorSID",
2336 "mSMQOwnerID", "nCName", "objectClass", "distinguishedName", "objectGUID", "objectSid",
2337 "oMSyntax", "proxiedObjectName", "name", "replPropertyMetaData", "sAMAccountName",
2338 "securityIdentifier", "sIDHistory", "subClassOf", "systemFlags", "trustPartner", "trustDirection",
2339 "trustType", "trustAttributes", "userAccountControl", "uSNChanged", "uSNCreated", "whenCreated",
2340 "whenChanged", NULL};
2341 unsigned int i, el_count = 0;
2343 if (ldb_dn_is_special(req->op.del.dn)) {
2344 return ldb_next_request(module, req);
2347 tmp_ctx = talloc_new(ldb);
2348 if (!tmp_ctx) {
2349 ldb_oom(ldb);
2350 return LDB_ERR_OPERATIONS_ERROR;
2353 schema = dsdb_get_schema(ldb, tmp_ctx);
2354 if (!schema) {
2355 return LDB_ERR_OPERATIONS_ERROR;
2358 old_dn = ldb_dn_copy(tmp_ctx, req->op.del.dn);
2360 /* we need the complete msg off disk, so we can work out which
2361 attributes need to be removed */
2362 ret = dsdb_module_search_dn(module, tmp_ctx, &res, old_dn, NULL,
2363 DSDB_SEARCH_SHOW_DELETED |
2364 DSDB_SEARCH_REVEAL_INTERNALS |
2365 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT);
2366 if (ret != LDB_SUCCESS) {
2367 talloc_free(tmp_ctx);
2368 return ret;
2370 old_msg = res->msgs[0];
2372 if (ldb_msg_check_string_attribute(old_msg, "isDeleted", "TRUE")) {
2373 struct auth_session_info *session_info =
2374 (struct auth_session_info *)ldb_get_opaque(ldb, "sessionInfo");
2375 if (security_session_user_level(session_info) != SECURITY_SYSTEM) {
2376 ldb_asprintf_errstring(ldb, "Refusing to delete deleted object %s",
2377 ldb_dn_get_linearized(old_msg->dn));
2378 return LDB_ERR_UNWILLING_TO_PERFORM;
2381 /* it is already deleted - really remove it this time */
2382 talloc_free(tmp_ctx);
2383 return ldb_next_request(module, req);
2386 /* work out where we will be renaming this object to */
2387 ret = dsdb_get_deleted_objects_dn(ldb, tmp_ctx, old_dn, &new_dn);
2388 if (ret != LDB_SUCCESS) {
2389 /* this is probably an attempted delete on a partition
2390 * that doesn't allow delete operations, such as the
2391 * schema partition */
2392 ldb_asprintf_errstring(ldb, "No Deleted Objects container for DN %s",
2393 ldb_dn_get_linearized(old_dn));
2394 talloc_free(tmp_ctx);
2395 return LDB_ERR_UNWILLING_TO_PERFORM;
2398 rdn_name = ldb_dn_get_rdn_name(old_dn);
2399 rdn_value = ldb_dn_get_rdn_val(old_dn);
2401 /* get the objects GUID from the search we just did */
2402 guid = samdb_result_guid(old_msg, "objectGUID");
2404 /* Add a formatted child */
2405 retb = ldb_dn_add_child_fmt(new_dn, "%s=%s\\0ADEL:%s",
2406 rdn_name,
2407 rdn_value->data,
2408 GUID_string(tmp_ctx, &guid));
2409 if (!retb) {
2410 DEBUG(0,(__location__ ": Unable to add a formatted child to dn: %s",
2411 ldb_dn_get_linearized(new_dn)));
2412 talloc_free(tmp_ctx);
2413 return LDB_ERR_OPERATIONS_ERROR;
2417 now we need to modify the object in the following ways:
2419 - add isDeleted=TRUE
2420 - update rDN and name, with new rDN
2421 - remove linked attributes
2422 - remove objectCategory and sAMAccountType
2423 - remove attribs not on the preserved list
2424 - preserved if in above list, or is rDN
2425 - remove all linked attribs from this object
2426 - remove all links from other objects to this object
2427 - add lastKnownParent
2428 - update replPropertyMetaData?
2430 see MS-ADTS "Tombstone Requirements" section 3.1.1.5.5.1.1
2433 msg = ldb_msg_new(tmp_ctx);
2434 if (msg == NULL) {
2435 ldb_module_oom(module);
2436 talloc_free(tmp_ctx);
2437 return LDB_ERR_OPERATIONS_ERROR;
2440 msg->dn = old_dn;
2442 ret = ldb_msg_add_string(msg, "isDeleted", "TRUE");
2443 if (ret != LDB_SUCCESS) {
2444 DEBUG(0,(__location__ ": Failed to add isDeleted string to the msg\n"));
2445 ldb_module_oom(module);
2446 talloc_free(tmp_ctx);
2447 return ret;
2449 msg->elements[el_count++].flags = LDB_FLAG_MOD_ADD;
2451 /* we also mark it as recycled, meaning this object can't be
2452 recovered (we are stripping its attributes) */
2453 if (dsdb_functional_level(ldb) >= DS_DOMAIN_FUNCTION_2008_R2) {
2454 ret = ldb_msg_add_string(msg, "isRecycled", "TRUE");
2455 if (ret != LDB_SUCCESS) {
2456 DEBUG(0,(__location__ ": Failed to add isRecycled string to the msg\n"));
2457 ldb_module_oom(module);
2458 talloc_free(tmp_ctx);
2459 return ret;
2461 msg->elements[el_count++].flags = LDB_FLAG_MOD_ADD;
2464 /* we need the storage form of the parent GUID */
2465 ret = dsdb_module_search_dn(module, tmp_ctx, &parent_res,
2466 ldb_dn_get_parent(tmp_ctx, old_dn), NULL,
2467 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
2468 DSDB_SEARCH_REVEAL_INTERNALS);
2469 if (ret != LDB_SUCCESS) {
2470 talloc_free(tmp_ctx);
2471 return ret;
2474 ret = ldb_msg_add_steal_string(msg, "lastKnownParent",
2475 ldb_dn_get_extended_linearized(tmp_ctx, parent_res->msgs[0]->dn, 1));
2476 if (ret != LDB_SUCCESS) {
2477 DEBUG(0,(__location__ ": Failed to add lastKnownParent string to the msg\n"));
2478 ldb_module_oom(module);
2479 talloc_free(tmp_ctx);
2480 return ret;
2482 msg->elements[el_count++].flags = LDB_FLAG_MOD_ADD;
2484 /* work out which of the old attributes we will be removing */
2485 for (i=0; i<old_msg->num_elements; i++) {
2486 const struct dsdb_attribute *sa;
2487 el = &old_msg->elements[i];
2488 sa = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
2489 if (!sa) {
2490 talloc_free(tmp_ctx);
2491 return LDB_ERR_OPERATIONS_ERROR;
2493 if (ldb_attr_cmp(el->name, rdn_name) == 0) {
2494 /* don't remove the rDN */
2495 continue;
2498 if (sa->linkID && sa->linkID & 1) {
2499 ret = replmd_delete_remove_link(module, schema, old_dn, el, sa);
2500 if (ret != LDB_SUCCESS) {
2501 talloc_free(tmp_ctx);
2502 return LDB_ERR_OPERATIONS_ERROR;
2504 continue;
2507 if (!sa->linkID && ldb_attr_in_list(preserved_attrs, el->name)) {
2508 continue;
2511 ret = ldb_msg_add_empty(msg, el->name, LDB_FLAG_MOD_DELETE, &el);
2512 if (ret != LDB_SUCCESS) {
2513 talloc_free(tmp_ctx);
2514 ldb_module_oom(module);
2515 return ret;
2519 /* work out what the new rdn value is, for updating the
2520 rDN and name fields */
2521 new_rdn_value = ldb_dn_get_rdn_val(new_dn);
2522 ret = ldb_msg_add_value(msg, rdn_name, new_rdn_value, &el);
2523 if (ret != LDB_SUCCESS) {
2524 talloc_free(tmp_ctx);
2525 return ret;
2527 el->flags = LDB_FLAG_MOD_REPLACE;
2529 el = ldb_msg_find_element(old_msg, "name");
2530 if (el) {
2531 ret = ldb_msg_add_value(msg, "name", new_rdn_value, &el);
2532 if (ret != LDB_SUCCESS) {
2533 talloc_free(tmp_ctx);
2534 return ret;
2536 el->flags = LDB_FLAG_MOD_REPLACE;
2539 ret = dsdb_module_modify(module, msg, DSDB_FLAG_OWN_MODULE);
2540 if (ret != LDB_SUCCESS) {
2541 ldb_asprintf_errstring(ldb, "replmd_delete: Failed to modify object %s in delete - %s",
2542 ldb_dn_get_linearized(old_dn), ldb_errstring(ldb));
2543 talloc_free(tmp_ctx);
2544 return ret;
2547 /* now rename onto the new DN */
2548 ret = dsdb_module_rename(module, old_dn, new_dn, 0);
2549 if (ret != LDB_SUCCESS){
2550 DEBUG(0,(__location__ ": Failed to rename object from '%s' to '%s' - %s\n",
2551 ldb_dn_get_linearized(old_dn),
2552 ldb_dn_get_linearized(new_dn),
2553 ldb_errstring(ldb)));
2554 talloc_free(tmp_ctx);
2555 return ret;
2558 talloc_free(tmp_ctx);
2560 return ldb_module_done(req, NULL, NULL, LDB_SUCCESS);
2565 static int replmd_replicated_request_error(struct replmd_replicated_request *ar, int ret)
2567 return ret;
2570 static int replmd_replicated_request_werror(struct replmd_replicated_request *ar, WERROR status)
2572 int ret = LDB_ERR_OTHER;
2573 /* TODO: do some error mapping */
2574 return ret;
2577 static int replmd_replicated_apply_add(struct replmd_replicated_request *ar)
2579 struct ldb_context *ldb;
2580 struct ldb_request *change_req;
2581 enum ndr_err_code ndr_err;
2582 struct ldb_message *msg;
2583 struct replPropertyMetaDataBlob *md;
2584 struct ldb_val md_value;
2585 unsigned int i;
2586 int ret;
2589 * TODO: check if the parent object exist
2593 * TODO: handle the conflict case where an object with the
2594 * same name exist
2597 ldb = ldb_module_get_ctx(ar->module);
2598 msg = ar->objs->objects[ar->index_current].msg;
2599 md = ar->objs->objects[ar->index_current].meta_data;
2601 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
2602 if (ret != LDB_SUCCESS) {
2603 return replmd_replicated_request_error(ar, ret);
2606 ret = ldb_msg_add_value(msg, "objectGUID", &ar->objs->objects[ar->index_current].guid_value, NULL);
2607 if (ret != LDB_SUCCESS) {
2608 return replmd_replicated_request_error(ar, ret);
2611 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
2612 if (ret != LDB_SUCCESS) {
2613 return replmd_replicated_request_error(ar, ret);
2616 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", ar->seq_num);
2617 if (ret != LDB_SUCCESS) {
2618 return replmd_replicated_request_error(ar, ret);
2621 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
2622 if (ret != LDB_SUCCESS) {
2623 return replmd_replicated_request_error(ar, ret);
2626 /* remove any message elements that have zero values */
2627 for (i=0; i<msg->num_elements; i++) {
2628 struct ldb_message_element *el = &msg->elements[i];
2630 if (el->num_values == 0) {
2631 DEBUG(4,(__location__ ": Removing attribute %s with num_values==0\n",
2632 el->name));
2633 memmove(el, el+1, sizeof(*el)*(msg->num_elements - (i+1)));
2634 msg->num_elements--;
2635 i--;
2636 continue;
2641 * the meta data array is already sorted by the caller
2643 for (i=0; i < md->ctr.ctr1.count; i++) {
2644 md->ctr.ctr1.array[i].local_usn = ar->seq_num;
2646 ndr_err = ndr_push_struct_blob(&md_value, msg,
2647 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
2649 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
2650 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
2651 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
2652 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
2654 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &md_value, NULL);
2655 if (ret != LDB_SUCCESS) {
2656 return replmd_replicated_request_error(ar, ret);
2659 replmd_ldb_message_sort(msg, ar->schema);
2661 if (DEBUGLVL(4)) {
2662 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_ADD, msg);
2663 DEBUG(4, ("DRS replication add message:\n%s\n", s));
2664 talloc_free(s);
2667 ret = ldb_build_add_req(&change_req,
2668 ldb,
2670 msg,
2671 ar->controls,
2673 replmd_op_callback,
2674 ar->req);
2675 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
2677 return ldb_next_request(ar->module, change_req);
2681 return true if an update is newer than an existing entry
2682 see section 5.11 of MS-ADTS
2684 static bool replmd_update_is_newer(const struct GUID *current_invocation_id,
2685 const struct GUID *update_invocation_id,
2686 uint32_t current_version,
2687 uint32_t update_version,
2688 NTTIME current_change_time,
2689 NTTIME update_change_time)
2691 if (update_version != current_version) {
2692 return update_version > current_version;
2694 if (update_change_time > current_change_time) {
2695 return true;
2697 if (update_change_time == current_change_time) {
2698 return GUID_compare(update_invocation_id, current_invocation_id) > 0;
2700 return false;
2703 static bool replmd_replPropertyMetaData1_is_newer(struct replPropertyMetaData1 *cur_m,
2704 struct replPropertyMetaData1 *new_m)
2706 return replmd_update_is_newer(&cur_m->originating_invocation_id,
2707 &new_m->originating_invocation_id,
2708 cur_m->version,
2709 new_m->version,
2710 cur_m->originating_change_time,
2711 new_m->originating_change_time);
2714 static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar)
2716 struct ldb_context *ldb;
2717 struct ldb_request *change_req;
2718 enum ndr_err_code ndr_err;
2719 struct ldb_message *msg;
2720 struct replPropertyMetaDataBlob *rmd;
2721 struct replPropertyMetaDataBlob omd;
2722 const struct ldb_val *omd_value;
2723 struct replPropertyMetaDataBlob nmd;
2724 struct ldb_val nmd_value;
2725 unsigned int i;
2726 uint32_t j,ni=0;
2727 unsigned int removed_attrs = 0;
2728 int ret;
2730 ldb = ldb_module_get_ctx(ar->module);
2731 msg = ar->objs->objects[ar->index_current].msg;
2732 rmd = ar->objs->objects[ar->index_current].meta_data;
2733 ZERO_STRUCT(omd);
2734 omd.version = 1;
2737 * TODO: check repl data is correct after a rename
2739 if (ldb_dn_compare(msg->dn, ar->search_msg->dn) != 0) {
2740 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_request rename %s => %s\n",
2741 ldb_dn_get_linearized(ar->search_msg->dn),
2742 ldb_dn_get_linearized(msg->dn));
2743 if (dsdb_module_rename(ar->module,
2744 ar->search_msg->dn, msg->dn,
2745 DSDB_FLAG_OWN_MODULE) != LDB_SUCCESS) {
2746 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_replicated_request rename %s => %s failed - %s\n",
2747 ldb_dn_get_linearized(ar->search_msg->dn),
2748 ldb_dn_get_linearized(msg->dn),
2749 ldb_errstring(ldb));
2750 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
2754 /* find existing meta data */
2755 omd_value = ldb_msg_find_ldb_val(ar->search_msg, "replPropertyMetaData");
2756 if (omd_value) {
2757 ndr_err = ndr_pull_struct_blob(omd_value, ar,
2758 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")), &omd,
2759 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
2760 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
2761 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
2762 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
2765 if (omd.version != 1) {
2766 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
2770 ZERO_STRUCT(nmd);
2771 nmd.version = 1;
2772 nmd.ctr.ctr1.count = omd.ctr.ctr1.count + rmd->ctr.ctr1.count;
2773 nmd.ctr.ctr1.array = talloc_array(ar,
2774 struct replPropertyMetaData1,
2775 nmd.ctr.ctr1.count);
2776 if (!nmd.ctr.ctr1.array) return replmd_replicated_request_werror(ar, WERR_NOMEM);
2778 /* first copy the old meta data */
2779 for (i=0; i < omd.ctr.ctr1.count; i++) {
2780 nmd.ctr.ctr1.array[ni] = omd.ctr.ctr1.array[i];
2781 ni++;
2784 /* now merge in the new meta data */
2785 for (i=0; i < rmd->ctr.ctr1.count; i++) {
2786 bool found = false;
2788 for (j=0; j < ni; j++) {
2789 bool cmp;
2791 if (rmd->ctr.ctr1.array[i].attid != nmd.ctr.ctr1.array[j].attid) {
2792 continue;
2795 cmp = replmd_replPropertyMetaData1_is_newer(&nmd.ctr.ctr1.array[j],
2796 &rmd->ctr.ctr1.array[i]);
2797 if (cmp) {
2798 /* replace the entry */
2799 nmd.ctr.ctr1.array[j] = rmd->ctr.ctr1.array[i];
2800 found = true;
2801 break;
2804 if (rmd->ctr.ctr1.array[i].attid != DRSUAPI_ATTRIBUTE_instanceType) {
2805 DEBUG(1,("Discarding older DRS attribute update to %s on %s from %s\n",
2806 msg->elements[i-removed_attrs].name,
2807 ldb_dn_get_linearized(msg->dn),
2808 GUID_string(ar, &rmd->ctr.ctr1.array[i].originating_invocation_id)));
2811 /* we don't want to apply this change so remove the attribute */
2812 ldb_msg_remove_element(msg, &msg->elements[i-removed_attrs]);
2813 removed_attrs++;
2815 found = true;
2816 break;
2819 if (found) continue;
2821 nmd.ctr.ctr1.array[ni] = rmd->ctr.ctr1.array[i];
2822 ni++;
2826 * finally correct the size of the meta_data array
2828 nmd.ctr.ctr1.count = ni;
2831 * the rdn attribute (the alias for the name attribute),
2832 * 'cn' for most objects is the last entry in the meta data array
2833 * we have stored
2835 * sort the new meta data array
2837 ret = replmd_replPropertyMetaDataCtr1_sort(&nmd.ctr.ctr1, ar->schema, msg->dn);
2838 if (ret != LDB_SUCCESS) {
2839 return ret;
2843 * check if some replicated attributes left, otherwise skip the ldb_modify() call
2845 if (msg->num_elements == 0) {
2846 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: skip replace\n",
2847 ar->index_current);
2849 ar->index_current++;
2850 return replmd_replicated_apply_next(ar);
2853 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: replace %u attributes\n",
2854 ar->index_current, msg->num_elements);
2856 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
2857 if (ret != LDB_SUCCESS) {
2858 return replmd_replicated_request_error(ar, ret);
2861 for (i=0; i<ni; i++) {
2862 nmd.ctr.ctr1.array[i].local_usn = ar->seq_num;
2865 /* create the meta data value */
2866 ndr_err = ndr_push_struct_blob(&nmd_value, msg,
2867 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
2868 &nmd,
2869 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
2870 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
2871 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
2872 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
2876 * when we know that we'll modify the record, add the whenChanged, uSNChanged
2877 * and replPopertyMetaData attributes
2879 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
2880 if (ret != LDB_SUCCESS) {
2881 return replmd_replicated_request_error(ar, ret);
2883 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
2884 if (ret != LDB_SUCCESS) {
2885 return replmd_replicated_request_error(ar, ret);
2887 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
2888 if (ret != LDB_SUCCESS) {
2889 return replmd_replicated_request_error(ar, ret);
2892 replmd_ldb_message_sort(msg, ar->schema);
2894 /* we want to replace the old values */
2895 for (i=0; i < msg->num_elements; i++) {
2896 msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
2899 if (DEBUGLVL(4)) {
2900 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_MODIFY, msg);
2901 DEBUG(4, ("DRS replication modify message:\n%s\n", s));
2902 talloc_free(s);
2905 ret = ldb_build_mod_req(&change_req,
2906 ldb,
2908 msg,
2909 ar->controls,
2911 replmd_op_callback,
2912 ar->req);
2913 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
2915 return ldb_next_request(ar->module, change_req);
2918 static int replmd_replicated_apply_search_callback(struct ldb_request *req,
2919 struct ldb_reply *ares)
2921 struct replmd_replicated_request *ar = talloc_get_type(req->context,
2922 struct replmd_replicated_request);
2923 int ret;
2925 if (!ares) {
2926 return ldb_module_done(ar->req, NULL, NULL,
2927 LDB_ERR_OPERATIONS_ERROR);
2929 if (ares->error != LDB_SUCCESS &&
2930 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
2931 return ldb_module_done(ar->req, ares->controls,
2932 ares->response, ares->error);
2935 switch (ares->type) {
2936 case LDB_REPLY_ENTRY:
2937 ar->search_msg = talloc_steal(ar, ares->message);
2938 break;
2940 case LDB_REPLY_REFERRAL:
2941 /* we ignore referrals */
2942 break;
2944 case LDB_REPLY_DONE:
2945 if (ar->search_msg != NULL) {
2946 ret = replmd_replicated_apply_merge(ar);
2947 } else {
2948 ret = replmd_replicated_apply_add(ar);
2950 if (ret != LDB_SUCCESS) {
2951 return ldb_module_done(ar->req, NULL, NULL, ret);
2955 talloc_free(ares);
2956 return LDB_SUCCESS;
2959 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar);
2961 static int replmd_replicated_apply_next(struct replmd_replicated_request *ar)
2963 struct ldb_context *ldb;
2964 int ret;
2965 char *tmp_str;
2966 char *filter;
2967 struct ldb_request *search_req;
2968 struct ldb_search_options_control *options;
2970 if (ar->index_current >= ar->objs->num_objects) {
2971 /* done with it, go to next stage */
2972 return replmd_replicated_uptodate_vector(ar);
2975 ldb = ldb_module_get_ctx(ar->module);
2976 ar->search_msg = NULL;
2978 tmp_str = ldb_binary_encode(ar, ar->objs->objects[ar->index_current].guid_value);
2979 if (!tmp_str) return replmd_replicated_request_werror(ar, WERR_NOMEM);
2981 filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
2982 if (!filter) return replmd_replicated_request_werror(ar, WERR_NOMEM);
2983 talloc_free(tmp_str);
2985 ret = ldb_build_search_req(&search_req,
2986 ldb,
2988 NULL,
2989 LDB_SCOPE_SUBTREE,
2990 filter,
2991 NULL,
2992 NULL,
2994 replmd_replicated_apply_search_callback,
2995 ar->req);
2997 ret = ldb_request_add_control(search_req, LDB_CONTROL_SHOW_DELETED_OID, true, NULL);
2998 if (ret != LDB_SUCCESS) {
2999 return ret;
3002 /* we need to cope with cross-partition links, so search for
3003 the GUID over all partitions */
3004 options = talloc(search_req, struct ldb_search_options_control);
3005 if (options == NULL) {
3006 DEBUG(0, (__location__ ": out of memory\n"));
3007 return LDB_ERR_OPERATIONS_ERROR;
3009 options->search_options = LDB_SEARCH_OPTION_PHANTOM_ROOT;
3011 ret = ldb_request_add_control(search_req,
3012 LDB_CONTROL_SEARCH_OPTIONS_OID,
3013 true, options);
3014 if (ret != LDB_SUCCESS) {
3015 return ret;
3018 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
3020 return ldb_next_request(ar->module, search_req);
3023 static int replmd_replicated_uptodate_modify_callback(struct ldb_request *req,
3024 struct ldb_reply *ares)
3026 struct ldb_context *ldb;
3027 struct replmd_replicated_request *ar = talloc_get_type(req->context,
3028 struct replmd_replicated_request);
3029 ldb = ldb_module_get_ctx(ar->module);
3031 if (!ares) {
3032 return ldb_module_done(ar->req, NULL, NULL,
3033 LDB_ERR_OPERATIONS_ERROR);
3035 if (ares->error != LDB_SUCCESS) {
3036 return ldb_module_done(ar->req, ares->controls,
3037 ares->response, ares->error);
3040 if (ares->type != LDB_REPLY_DONE) {
3041 ldb_set_errstring(ldb, "Invalid reply type\n!");
3042 return ldb_module_done(ar->req, NULL, NULL,
3043 LDB_ERR_OPERATIONS_ERROR);
3046 talloc_free(ares);
3048 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
3051 static int replmd_replicated_uptodate_modify(struct replmd_replicated_request *ar)
3053 struct ldb_context *ldb;
3054 struct ldb_request *change_req;
3055 enum ndr_err_code ndr_err;
3056 struct ldb_message *msg;
3057 struct replUpToDateVectorBlob ouv;
3058 const struct ldb_val *ouv_value;
3059 const struct drsuapi_DsReplicaCursor2CtrEx *ruv;
3060 struct replUpToDateVectorBlob nuv;
3061 struct ldb_val nuv_value;
3062 struct ldb_message_element *nuv_el = NULL;
3063 const struct GUID *our_invocation_id;
3064 struct ldb_message_element *orf_el = NULL;
3065 struct repsFromToBlob nrf;
3066 struct ldb_val *nrf_value = NULL;
3067 struct ldb_message_element *nrf_el = NULL;
3068 unsigned int i;
3069 uint32_t j,ni=0;
3070 bool found = false;
3071 time_t t = time(NULL);
3072 NTTIME now;
3073 int ret;
3075 ldb = ldb_module_get_ctx(ar->module);
3076 ruv = ar->objs->uptodateness_vector;
3077 ZERO_STRUCT(ouv);
3078 ouv.version = 2;
3079 ZERO_STRUCT(nuv);
3080 nuv.version = 2;
3082 unix_to_nt_time(&now, t);
3085 * first create the new replUpToDateVector
3087 ouv_value = ldb_msg_find_ldb_val(ar->search_msg, "replUpToDateVector");
3088 if (ouv_value) {
3089 ndr_err = ndr_pull_struct_blob(ouv_value, ar,
3090 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")), &ouv,
3091 (ndr_pull_flags_fn_t)ndr_pull_replUpToDateVectorBlob);
3092 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
3093 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
3094 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
3097 if (ouv.version != 2) {
3098 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
3103 * the new uptodateness vector will at least
3104 * contain 1 entry, one for the source_dsa
3106 * plus optional values from our old vector and the one from the source_dsa
3108 nuv.ctr.ctr2.count = 1 + ouv.ctr.ctr2.count;
3109 if (ruv) nuv.ctr.ctr2.count += ruv->count;
3110 nuv.ctr.ctr2.cursors = talloc_array(ar,
3111 struct drsuapi_DsReplicaCursor2,
3112 nuv.ctr.ctr2.count);
3113 if (!nuv.ctr.ctr2.cursors) return replmd_replicated_request_werror(ar, WERR_NOMEM);
3115 /* first copy the old vector */
3116 for (i=0; i < ouv.ctr.ctr2.count; i++) {
3117 nuv.ctr.ctr2.cursors[ni] = ouv.ctr.ctr2.cursors[i];
3118 ni++;
3121 /* get our invocation_id if we have one already attached to the ldb */
3122 our_invocation_id = samdb_ntds_invocation_id(ldb);
3124 /* merge in the source_dsa vector is available */
3125 for (i=0; (ruv && i < ruv->count); i++) {
3126 found = false;
3128 if (our_invocation_id &&
3129 GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
3130 our_invocation_id)) {
3131 continue;
3134 for (j=0; j < ni; j++) {
3135 if (!GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
3136 &nuv.ctr.ctr2.cursors[j].source_dsa_invocation_id)) {
3137 continue;
3140 found = true;
3143 * we update only the highest_usn and not the latest_sync_success time,
3144 * because the last success stands for direct replication
3146 if (ruv->cursors[i].highest_usn > nuv.ctr.ctr2.cursors[j].highest_usn) {
3147 nuv.ctr.ctr2.cursors[j].highest_usn = ruv->cursors[i].highest_usn;
3149 break;
3152 if (found) continue;
3154 /* if it's not there yet, add it */
3155 nuv.ctr.ctr2.cursors[ni] = ruv->cursors[i];
3156 ni++;
3160 * merge in the current highwatermark for the source_dsa
3162 found = false;
3163 for (j=0; j < ni; j++) {
3164 if (!GUID_equal(&ar->objs->source_dsa->source_dsa_invocation_id,
3165 &nuv.ctr.ctr2.cursors[j].source_dsa_invocation_id)) {
3166 continue;
3169 found = true;
3172 * here we update the highest_usn and last_sync_success time
3173 * because we're directly replicating from the source_dsa
3175 * and use the tmp_highest_usn because this is what we have just applied
3176 * to our ldb
3178 nuv.ctr.ctr2.cursors[j].highest_usn = ar->objs->source_dsa->highwatermark.tmp_highest_usn;
3179 nuv.ctr.ctr2.cursors[j].last_sync_success = now;
3180 break;
3182 if (!found) {
3184 * here we update the highest_usn and last_sync_success time
3185 * because we're directly replicating from the source_dsa
3187 * and use the tmp_highest_usn because this is what we have just applied
3188 * to our ldb
3190 nuv.ctr.ctr2.cursors[ni].source_dsa_invocation_id= ar->objs->source_dsa->source_dsa_invocation_id;
3191 nuv.ctr.ctr2.cursors[ni].highest_usn = ar->objs->source_dsa->highwatermark.tmp_highest_usn;
3192 nuv.ctr.ctr2.cursors[ni].last_sync_success = now;
3193 ni++;
3197 * finally correct the size of the cursors array
3199 nuv.ctr.ctr2.count = ni;
3202 * sort the cursors
3204 TYPESAFE_QSORT(nuv.ctr.ctr2.cursors, nuv.ctr.ctr2.count, drsuapi_DsReplicaCursor2_compare);
3207 * create the change ldb_message
3209 msg = ldb_msg_new(ar);
3210 if (!msg) return replmd_replicated_request_werror(ar, WERR_NOMEM);
3211 msg->dn = ar->search_msg->dn;
3213 ndr_err = ndr_push_struct_blob(&nuv_value, msg,
3214 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
3215 &nuv,
3216 (ndr_push_flags_fn_t)ndr_push_replUpToDateVectorBlob);
3217 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
3218 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
3219 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
3221 ret = ldb_msg_add_value(msg, "replUpToDateVector", &nuv_value, &nuv_el);
3222 if (ret != LDB_SUCCESS) {
3223 return replmd_replicated_request_error(ar, ret);
3225 nuv_el->flags = LDB_FLAG_MOD_REPLACE;
3228 * now create the new repsFrom value from the given repsFromTo1 structure
3230 ZERO_STRUCT(nrf);
3231 nrf.version = 1;
3232 nrf.ctr.ctr1 = *ar->objs->source_dsa;
3233 /* and fix some values... */
3234 nrf.ctr.ctr1.consecutive_sync_failures = 0;
3235 nrf.ctr.ctr1.last_success = now;
3236 nrf.ctr.ctr1.last_attempt = now;
3237 nrf.ctr.ctr1.result_last_attempt = WERR_OK;
3238 nrf.ctr.ctr1.highwatermark.highest_usn = nrf.ctr.ctr1.highwatermark.tmp_highest_usn;
3241 * first see if we already have a repsFrom value for the current source dsa
3242 * if so we'll later replace this value
3244 orf_el = ldb_msg_find_element(ar->search_msg, "repsFrom");
3245 if (orf_el) {
3246 for (i=0; i < orf_el->num_values; i++) {
3247 struct repsFromToBlob *trf;
3249 trf = talloc(ar, struct repsFromToBlob);
3250 if (!trf) return replmd_replicated_request_werror(ar, WERR_NOMEM);
3252 ndr_err = ndr_pull_struct_blob(&orf_el->values[i], trf, lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")), trf,
3253 (ndr_pull_flags_fn_t)ndr_pull_repsFromToBlob);
3254 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
3255 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
3256 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
3259 if (trf->version != 1) {
3260 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
3264 * we compare the source dsa objectGUID not the invocation_id
3265 * because we want only one repsFrom value per source dsa
3266 * and when the invocation_id of the source dsa has changed we don't need
3267 * the old repsFrom with the old invocation_id
3269 if (!GUID_equal(&trf->ctr.ctr1.source_dsa_obj_guid,
3270 &ar->objs->source_dsa->source_dsa_obj_guid)) {
3271 talloc_free(trf);
3272 continue;
3275 talloc_free(trf);
3276 nrf_value = &orf_el->values[i];
3277 break;
3281 * copy over all old values to the new ldb_message
3283 ret = ldb_msg_add_empty(msg, "repsFrom", 0, &nrf_el);
3284 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
3285 *nrf_el = *orf_el;
3289 * if we haven't found an old repsFrom value for the current source dsa
3290 * we'll add a new value
3292 if (!nrf_value) {
3293 struct ldb_val zero_value;
3294 ZERO_STRUCT(zero_value);
3295 ret = ldb_msg_add_value(msg, "repsFrom", &zero_value, &nrf_el);
3296 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
3298 nrf_value = &nrf_el->values[nrf_el->num_values - 1];
3301 /* we now fill the value which is already attached to ldb_message */
3302 ndr_err = ndr_push_struct_blob(nrf_value, msg,
3303 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
3304 &nrf,
3305 (ndr_push_flags_fn_t)ndr_push_repsFromToBlob);
3306 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
3307 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
3308 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
3312 * the ldb_message_element for the attribute, has all the old values and the new one
3313 * so we'll replace the whole attribute with all values
3315 nrf_el->flags = LDB_FLAG_MOD_REPLACE;
3317 if (DEBUGLVL(4)) {
3318 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_MODIFY, msg);
3319 DEBUG(4, ("DRS replication uptodate modify message:\n%s\n", s));
3320 talloc_free(s);
3323 /* prepare the ldb_modify() request */
3324 ret = ldb_build_mod_req(&change_req,
3325 ldb,
3327 msg,
3328 ar->controls,
3330 replmd_replicated_uptodate_modify_callback,
3331 ar->req);
3332 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
3334 return ldb_next_request(ar->module, change_req);
3337 static int replmd_replicated_uptodate_search_callback(struct ldb_request *req,
3338 struct ldb_reply *ares)
3340 struct replmd_replicated_request *ar = talloc_get_type(req->context,
3341 struct replmd_replicated_request);
3342 int ret;
3344 if (!ares) {
3345 return ldb_module_done(ar->req, NULL, NULL,
3346 LDB_ERR_OPERATIONS_ERROR);
3348 if (ares->error != LDB_SUCCESS &&
3349 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
3350 return ldb_module_done(ar->req, ares->controls,
3351 ares->response, ares->error);
3354 switch (ares->type) {
3355 case LDB_REPLY_ENTRY:
3356 ar->search_msg = talloc_steal(ar, ares->message);
3357 break;
3359 case LDB_REPLY_REFERRAL:
3360 /* we ignore referrals */
3361 break;
3363 case LDB_REPLY_DONE:
3364 if (ar->search_msg == NULL) {
3365 ret = replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
3366 } else {
3367 ret = replmd_replicated_uptodate_modify(ar);
3369 if (ret != LDB_SUCCESS) {
3370 return ldb_module_done(ar->req, NULL, NULL, ret);
3374 talloc_free(ares);
3375 return LDB_SUCCESS;
3379 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar)
3381 struct ldb_context *ldb;
3382 int ret;
3383 static const char *attrs[] = {
3384 "replUpToDateVector",
3385 "repsFrom",
3386 NULL
3388 struct ldb_request *search_req;
3390 ldb = ldb_module_get_ctx(ar->module);
3391 ar->search_msg = NULL;
3393 ret = ldb_build_search_req(&search_req,
3394 ldb,
3396 ar->objs->partition_dn,
3397 LDB_SCOPE_BASE,
3398 "(objectClass=*)",
3399 attrs,
3400 NULL,
3402 replmd_replicated_uptodate_search_callback,
3403 ar->req);
3404 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
3406 return ldb_next_request(ar->module, search_req);
3411 static int replmd_extended_replicated_objects(struct ldb_module *module, struct ldb_request *req)
3413 struct ldb_context *ldb;
3414 struct dsdb_extended_replicated_objects *objs;
3415 struct replmd_replicated_request *ar;
3416 struct ldb_control **ctrls;
3417 int ret;
3418 uint32_t i;
3419 struct replmd_private *replmd_private =
3420 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
3422 ldb = ldb_module_get_ctx(module);
3424 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_extended_replicated_objects\n");
3426 objs = talloc_get_type(req->op.extended.data, struct dsdb_extended_replicated_objects);
3427 if (!objs) {
3428 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: invalid extended data\n");
3429 return LDB_ERR_PROTOCOL_ERROR;
3432 if (objs->version != DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION) {
3433 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: extended data invalid version [%u != %u]\n",
3434 objs->version, DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION);
3435 return LDB_ERR_PROTOCOL_ERROR;
3438 ar = replmd_ctx_init(module, req);
3439 if (!ar)
3440 return LDB_ERR_OPERATIONS_ERROR;
3442 /* Set the flags to have the replmd_op_callback run over the full set of objects */
3443 ar->apply_mode = true;
3444 ar->objs = objs;
3445 ar->schema = dsdb_get_schema(ldb, ar);
3446 if (!ar->schema) {
3447 ldb_debug_set(ldb, LDB_DEBUG_FATAL, "replmd_ctx_init: no loaded schema found\n");
3448 talloc_free(ar);
3449 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
3450 return LDB_ERR_CONSTRAINT_VIOLATION;
3453 ctrls = req->controls;
3455 if (req->controls) {
3456 req->controls = talloc_memdup(ar, req->controls,
3457 talloc_get_size(req->controls));
3458 if (!req->controls) return replmd_replicated_request_werror(ar, WERR_NOMEM);
3461 ret = ldb_request_add_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID, false, NULL);
3462 if (ret != LDB_SUCCESS) {
3463 return ret;
3466 ar->controls = req->controls;
3467 req->controls = ctrls;
3469 DEBUG(4,("linked_attributes_count=%u\n", objs->linked_attributes_count));
3471 /* save away the linked attributes for the end of the
3472 transaction */
3473 for (i=0; i<ar->objs->linked_attributes_count; i++) {
3474 struct la_entry *la_entry;
3476 if (replmd_private->la_ctx == NULL) {
3477 replmd_private->la_ctx = talloc_new(replmd_private);
3479 la_entry = talloc(replmd_private->la_ctx, struct la_entry);
3480 if (la_entry == NULL) {
3481 ldb_oom(ldb);
3482 return LDB_ERR_OPERATIONS_ERROR;
3484 la_entry->la = talloc(la_entry, struct drsuapi_DsReplicaLinkedAttribute);
3485 if (la_entry->la == NULL) {
3486 talloc_free(la_entry);
3487 ldb_oom(ldb);
3488 return LDB_ERR_OPERATIONS_ERROR;
3490 *la_entry->la = ar->objs->linked_attributes[i];
3492 /* we need to steal the non-scalars so they stay
3493 around until the end of the transaction */
3494 talloc_steal(la_entry->la, la_entry->la->identifier);
3495 talloc_steal(la_entry->la, la_entry->la->value.blob);
3497 DLIST_ADD(replmd_private->la_list, la_entry);
3500 return replmd_replicated_apply_next(ar);
3504 process one linked attribute structure
3506 static int replmd_process_linked_attribute(struct ldb_module *module,
3507 struct la_entry *la_entry)
3509 struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
3510 struct ldb_context *ldb = ldb_module_get_ctx(module);
3511 struct ldb_message *msg;
3512 TALLOC_CTX *tmp_ctx = talloc_new(la_entry);
3513 const struct dsdb_schema *schema = dsdb_get_schema(ldb, tmp_ctx);
3514 int ret;
3515 const struct dsdb_attribute *attr;
3516 struct dsdb_dn *dsdb_dn;
3517 uint64_t seq_num = 0;
3518 struct ldb_message_element *old_el;
3519 WERROR status;
3520 time_t t = time(NULL);
3521 struct ldb_result *res;
3522 const char *attrs[2];
3523 struct parsed_dn *pdn_list, *pdn;
3524 struct GUID guid = GUID_zero();
3525 NTSTATUS ntstatus;
3526 bool active = (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)?true:false;
3527 const struct GUID *our_invocation_id;
3530 linked_attributes[0]:
3531 &objs->linked_attributes[i]: struct drsuapi_DsReplicaLinkedAttribute
3532 identifier : *
3533 identifier: struct drsuapi_DsReplicaObjectIdentifier
3534 __ndr_size : 0x0000003a (58)
3535 __ndr_size_sid : 0x00000000 (0)
3536 guid : 8e95b6a9-13dd-4158-89db-3220a5be5cc7
3537 sid : S-0-0
3538 __ndr_size_dn : 0x00000000 (0)
3539 dn : ''
3540 attid : DRSUAPI_ATTRIBUTE_member (0x1F)
3541 value: struct drsuapi_DsAttributeValue
3542 __ndr_size : 0x0000007e (126)
3543 blob : *
3544 blob : DATA_BLOB length=126
3545 flags : 0x00000001 (1)
3546 1: DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE
3547 originating_add_time : Wed Sep 2 22:20:01 2009 EST
3548 meta_data: struct drsuapi_DsReplicaMetaData
3549 version : 0x00000015 (21)
3550 originating_change_time : Wed Sep 2 23:39:07 2009 EST
3551 originating_invocation_id: 794640f3-18cf-40ee-a211-a93992b67a64
3552 originating_usn : 0x000000000001e19c (123292)
3554 (for cases where the link is to a normal DN)
3555 &target: struct drsuapi_DsReplicaObjectIdentifier3
3556 __ndr_size : 0x0000007e (126)
3557 __ndr_size_sid : 0x0000001c (28)
3558 guid : 7639e594-db75-4086-b0d4-67890ae46031
3559 sid : S-1-5-21-2848215498-2472035911-1947525656-19924
3560 __ndr_size_dn : 0x00000022 (34)
3561 dn : 'CN=UOne,OU=TestOU,DC=vsofs8,DC=com'
3564 /* find the attribute being modified */
3565 attr = dsdb_attribute_by_attributeID_id(schema, la->attid);
3566 if (attr == NULL) {
3567 DEBUG(0, (__location__ ": Unable to find attributeID 0x%x\n", la->attid));
3568 talloc_free(tmp_ctx);
3569 return LDB_ERR_OPERATIONS_ERROR;
3572 attrs[0] = attr->lDAPDisplayName;
3573 attrs[1] = NULL;
3575 /* get the existing message from the db for the object with
3576 this GUID, returning attribute being modified. We will then
3577 use this msg as the basis for a modify call */
3578 ret = dsdb_module_search(module, tmp_ctx, &res, NULL, LDB_SCOPE_SUBTREE, attrs,
3579 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
3580 DSDB_SEARCH_SHOW_DELETED |
3581 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
3582 DSDB_SEARCH_REVEAL_INTERNALS,
3583 "objectGUID=%s", GUID_string(tmp_ctx, &la->identifier->guid));
3584 if (ret != LDB_SUCCESS) {
3585 talloc_free(tmp_ctx);
3586 return ret;
3588 if (res->count != 1) {
3589 ldb_asprintf_errstring(ldb, "DRS linked attribute for GUID %s - DN not found",
3590 GUID_string(tmp_ctx, &la->identifier->guid));
3591 talloc_free(tmp_ctx);
3592 return LDB_ERR_NO_SUCH_OBJECT;
3594 msg = res->msgs[0];
3596 if (msg->num_elements == 0) {
3597 ret = ldb_msg_add_empty(msg, attr->lDAPDisplayName, LDB_FLAG_MOD_REPLACE, &old_el);
3598 if (ret != LDB_SUCCESS) {
3599 ldb_module_oom(module);
3600 talloc_free(tmp_ctx);
3601 return LDB_ERR_OPERATIONS_ERROR;
3603 } else {
3604 old_el = &msg->elements[0];
3605 old_el->flags = LDB_FLAG_MOD_REPLACE;
3608 /* parse the existing links */
3609 ret = get_parsed_dns(module, tmp_ctx, old_el, &pdn_list, attr->syntax->ldap_oid);
3610 if (ret != LDB_SUCCESS) {
3611 talloc_free(tmp_ctx);
3612 return ret;
3615 /* get our invocationId */
3616 our_invocation_id = samdb_ntds_invocation_id(ldb);
3617 if (!our_invocation_id) {
3618 ldb_debug_set(ldb, LDB_DEBUG_ERROR, __location__ ": unable to find invocationId\n");
3619 talloc_free(tmp_ctx);
3620 return LDB_ERR_OPERATIONS_ERROR;
3623 ret = replmd_check_upgrade_links(pdn_list, old_el->num_values, our_invocation_id);
3624 if (ret != LDB_SUCCESS) {
3625 talloc_free(tmp_ctx);
3626 return ret;
3629 status = dsdb_dn_la_from_blob(ldb, attr, schema, tmp_ctx, la->value.blob, &dsdb_dn);
3630 if (!W_ERROR_IS_OK(status)) {
3631 ldb_asprintf_errstring(ldb, "Failed to parsed linked attribute blob for %s on %s - %s\n",
3632 old_el->name, ldb_dn_get_linearized(msg->dn), win_errstr(status));
3633 return LDB_ERR_OPERATIONS_ERROR;
3636 ntstatus = dsdb_get_extended_dn_guid(dsdb_dn->dn, &guid, "GUID");
3637 if (!NT_STATUS_IS_OK(ntstatus) && active) {
3638 ldb_asprintf_errstring(ldb, "Failed to find GUID in linked attribute blob for %s on %s from %s",
3639 old_el->name,
3640 ldb_dn_get_linearized(dsdb_dn->dn),
3641 ldb_dn_get_linearized(msg->dn));
3642 return LDB_ERR_OPERATIONS_ERROR;
3645 /* re-resolve the DN by GUID, as the DRS server may give us an
3646 old DN value */
3647 ret = dsdb_module_dn_by_guid(module, dsdb_dn, &guid, &dsdb_dn->dn);
3648 if (ret != LDB_SUCCESS) {
3649 ldb_asprintf_errstring(ldb, __location__ ": Failed to re-resolve GUID %s",
3650 GUID_string(tmp_ctx, &guid));
3651 talloc_free(tmp_ctx);
3652 return ret;
3655 /* see if this link already exists */
3656 pdn = parsed_dn_find(pdn_list, old_el->num_values, &guid, dsdb_dn->dn);
3657 if (pdn != NULL) {
3658 /* see if this update is newer than what we have already */
3659 struct GUID invocation_id = GUID_zero();
3660 uint32_t version = 0;
3661 NTTIME change_time = 0;
3662 uint32_t rmd_flags = dsdb_dn_rmd_flags(pdn->dsdb_dn->dn);
3664 dsdb_get_extended_dn_guid(pdn->dsdb_dn->dn, &invocation_id, "RMD_INVOCID");
3665 dsdb_get_extended_dn_uint32(pdn->dsdb_dn->dn, &version, "RMD_VERSION");
3666 dsdb_get_extended_dn_nttime(pdn->dsdb_dn->dn, &change_time, "RMD_CHANGETIME");
3668 if (!replmd_update_is_newer(&invocation_id,
3669 &la->meta_data.originating_invocation_id,
3670 version,
3671 la->meta_data.version,
3672 change_time,
3673 la->meta_data.originating_change_time)) {
3674 DEBUG(1,("Discarding older DRS linked attribute update to %s on %s from %s\n",
3675 old_el->name, ldb_dn_get_linearized(msg->dn),
3676 GUID_string(tmp_ctx, &la->meta_data.originating_invocation_id)));
3677 talloc_free(tmp_ctx);
3678 return LDB_SUCCESS;
3681 /* get a seq_num for this change */
3682 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
3683 if (ret != LDB_SUCCESS) {
3684 talloc_free(tmp_ctx);
3685 return ret;
3688 if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
3689 /* remove the existing backlink */
3690 ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid, false, attr, false);
3691 if (ret != LDB_SUCCESS) {
3692 talloc_free(tmp_ctx);
3693 return ret;
3697 ret = replmd_update_la_val(tmp_ctx, pdn->v, dsdb_dn, pdn->dsdb_dn,
3698 &la->meta_data.originating_invocation_id,
3699 la->meta_data.originating_usn, seq_num,
3700 la->meta_data.originating_change_time,
3701 la->meta_data.version,
3702 !active);
3703 if (ret != LDB_SUCCESS) {
3704 talloc_free(tmp_ctx);
3705 return ret;
3708 if (active) {
3709 /* add the new backlink */
3710 ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid, true, attr, false);
3711 if (ret != LDB_SUCCESS) {
3712 talloc_free(tmp_ctx);
3713 return ret;
3716 } else {
3717 /* get a seq_num for this change */
3718 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
3719 if (ret != LDB_SUCCESS) {
3720 talloc_free(tmp_ctx);
3721 return ret;
3724 old_el->values = talloc_realloc(msg->elements, old_el->values,
3725 struct ldb_val, old_el->num_values+1);
3726 if (!old_el->values) {
3727 ldb_module_oom(module);
3728 return LDB_ERR_OPERATIONS_ERROR;
3730 old_el->num_values++;
3732 ret = replmd_build_la_val(tmp_ctx, &old_el->values[old_el->num_values-1], dsdb_dn,
3733 &la->meta_data.originating_invocation_id,
3734 la->meta_data.originating_usn, seq_num,
3735 la->meta_data.originating_change_time,
3736 la->meta_data.version,
3737 (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)?false:true);
3738 if (ret != LDB_SUCCESS) {
3739 talloc_free(tmp_ctx);
3740 return ret;
3743 if (active) {
3744 ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid,
3745 true, attr, false);
3746 if (ret != LDB_SUCCESS) {
3747 talloc_free(tmp_ctx);
3748 return ret;
3753 /* we only change whenChanged and uSNChanged if the seq_num
3754 has changed */
3755 if (add_time_element(msg, "whenChanged", t) != LDB_SUCCESS) {
3756 talloc_free(tmp_ctx);
3757 return LDB_ERR_OPERATIONS_ERROR;
3760 if (add_uint64_element(msg, "uSNChanged", seq_num) != LDB_SUCCESS) {
3761 talloc_free(tmp_ctx);
3762 return LDB_ERR_OPERATIONS_ERROR;
3765 ret = dsdb_check_single_valued_link(attr, old_el);
3766 if (ret != LDB_SUCCESS) {
3767 talloc_free(tmp_ctx);
3768 return ret;
3771 ret = dsdb_module_modify(module, msg, DSDB_MODIFY_RELAX);
3772 if (ret != LDB_SUCCESS) {
3773 ldb_debug(ldb, LDB_DEBUG_WARNING, "Failed to apply linked attribute change '%s'\n%s\n",
3774 ldb_errstring(ldb),
3775 ldb_ldif_message_string(ldb, tmp_ctx, LDB_CHANGETYPE_MODIFY, msg));
3776 talloc_free(tmp_ctx);
3777 return ret;
3780 talloc_free(tmp_ctx);
3782 return ret;
3785 static int replmd_extended(struct ldb_module *module, struct ldb_request *req)
3787 if (strcmp(req->op.extended.oid, DSDB_EXTENDED_REPLICATED_OBJECTS_OID) == 0) {
3788 return replmd_extended_replicated_objects(module, req);
3791 return ldb_next_request(module, req);
3796 we hook into the transaction operations to allow us to
3797 perform the linked attribute updates at the end of the whole
3798 transaction. This allows a forward linked attribute to be created
3799 before the object is created. During a vampire, w2k8 sends us linked
3800 attributes before the objects they are part of.
3802 static int replmd_start_transaction(struct ldb_module *module)
3804 /* create our private structure for this transaction */
3805 struct replmd_private *replmd_private = talloc_get_type(ldb_module_get_private(module),
3806 struct replmd_private);
3807 replmd_txn_cleanup(replmd_private);
3809 /* free any leftover mod_usn records from cancelled
3810 transactions */
3811 while (replmd_private->ncs) {
3812 struct nc_entry *e = replmd_private->ncs;
3813 DLIST_REMOVE(replmd_private->ncs, e);
3814 talloc_free(e);
3817 return ldb_next_start_trans(module);
3821 on prepare commit we loop over our queued la_context structures and
3822 apply each of them
3824 static int replmd_prepare_commit(struct ldb_module *module)
3826 struct replmd_private *replmd_private =
3827 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
3828 struct la_entry *la, *prev;
3829 struct la_backlink *bl;
3830 int ret;
3832 /* walk the list backwards, to do the first entry first, as we
3833 * added the entries with DLIST_ADD() which puts them at the
3834 * start of the list */
3835 for (la = DLIST_TAIL(replmd_private->la_list); la; la=prev) {
3836 prev = DLIST_PREV(la);
3837 DLIST_REMOVE(replmd_private->la_list, la);
3838 ret = replmd_process_linked_attribute(module, la);
3839 if (ret != LDB_SUCCESS) {
3840 replmd_txn_cleanup(replmd_private);
3841 return ret;
3845 /* process our backlink list, creating and deleting backlinks
3846 as necessary */
3847 for (bl=replmd_private->la_backlinks; bl; bl=bl->next) {
3848 ret = replmd_process_backlink(module, bl);
3849 if (ret != LDB_SUCCESS) {
3850 replmd_txn_cleanup(replmd_private);
3851 return ret;
3855 replmd_txn_cleanup(replmd_private);
3857 /* possibly change @REPLCHANGED */
3858 ret = replmd_notify_store(module);
3859 if (ret != LDB_SUCCESS) {
3860 return ret;
3863 return ldb_next_prepare_commit(module);
3866 static int replmd_del_transaction(struct ldb_module *module)
3868 struct replmd_private *replmd_private =
3869 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
3870 replmd_txn_cleanup(replmd_private);
3872 return ldb_next_del_trans(module);
3876 _PUBLIC_ const struct ldb_module_ops ldb_repl_meta_data_module_ops = {
3877 .name = "repl_meta_data",
3878 .init_context = replmd_init,
3879 .add = replmd_add,
3880 .modify = replmd_modify,
3881 .rename = replmd_rename,
3882 .del = replmd_delete,
3883 .extended = replmd_extended,
3884 .start_transaction = replmd_start_transaction,
3885 .prepare_commit = replmd_prepare_commit,
3886 .del_transaction = replmd_del_transaction,