use unsigned instead of uint32_t for LDB counters.
[Samba/cd1.git] / source4 / dsdb / samdb / ldb_modules / repl_meta_data.c
blobe96a2059a66a45554c2fdfdf98c53c04a13fb8b0
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 bl->attr_name = target_attr->lDAPDisplayName;
326 bl->forward_guid = *forward_guid;
327 bl->target_guid = *target_guid;
328 bl->active = active;
330 /* the caller may ask for this backlink to be processed
331 immediately */
332 if (immediate) {
333 int ret = replmd_process_backlink(module, bl);
334 talloc_free(bl);
335 return ret;
338 DLIST_ADD(replmd_private->la_backlinks, bl);
340 return LDB_SUCCESS;
345 * Callback for most write operations in this module:
347 * notify the repl task that a object has changed. The notifies are
348 * gathered up in the replmd_private structure then written to the
349 * @REPLCHANGED object in each partition during the prepare_commit
351 static int replmd_op_callback(struct ldb_request *req, struct ldb_reply *ares)
353 int ret;
354 struct replmd_replicated_request *ac =
355 talloc_get_type_abort(req->context, struct replmd_replicated_request);
356 struct replmd_private *replmd_private =
357 talloc_get_type_abort(ldb_module_get_private(ac->module), struct replmd_private);
358 struct nc_entry *modified_partition;
359 struct ldb_control *partition_ctrl;
360 const struct dsdb_control_current_partition *partition;
362 struct ldb_control **controls;
364 partition_ctrl = ldb_reply_get_control(ares, DSDB_CONTROL_CURRENT_PARTITION_OID);
366 /* Remove the 'partition' control from what we pass up the chain */
367 controls = controls_except_specified(ares->controls, ares, partition_ctrl);
369 if (ares->error != LDB_SUCCESS) {
370 return ldb_module_done(ac->req, controls,
371 ares->response, ares->error);
374 if (ares->type != LDB_REPLY_DONE) {
375 ldb_set_errstring(ldb_module_get_ctx(ac->module), "Invalid reply type for notify\n!");
376 return ldb_module_done(ac->req, NULL,
377 NULL, LDB_ERR_OPERATIONS_ERROR);
380 if (!partition_ctrl) {
381 ldb_set_errstring(ldb_module_get_ctx(ac->module),"No partition control on reply");
382 return ldb_module_done(ac->req, NULL,
383 NULL, LDB_ERR_OPERATIONS_ERROR);
386 partition = talloc_get_type_abort(partition_ctrl->data,
387 struct dsdb_control_current_partition);
389 if (ac->seq_num > 0) {
390 for (modified_partition = replmd_private->ncs; modified_partition;
391 modified_partition = modified_partition->next) {
392 if (ldb_dn_compare(modified_partition->dn, partition->dn) == 0) {
393 break;
397 if (modified_partition == NULL) {
398 modified_partition = talloc_zero(replmd_private, struct nc_entry);
399 if (!modified_partition) {
400 ldb_oom(ldb_module_get_ctx(ac->module));
401 return ldb_module_done(ac->req, NULL,
402 NULL, LDB_ERR_OPERATIONS_ERROR);
404 modified_partition->dn = ldb_dn_copy(modified_partition, partition->dn);
405 if (!modified_partition->dn) {
406 ldb_oom(ldb_module_get_ctx(ac->module));
407 return ldb_module_done(ac->req, NULL,
408 NULL, LDB_ERR_OPERATIONS_ERROR);
410 DLIST_ADD(replmd_private->ncs, modified_partition);
413 if (ac->seq_num > modified_partition->mod_usn) {
414 modified_partition->mod_usn = ac->seq_num;
415 if (ac->is_urgent) {
416 modified_partition->mod_usn_urgent = ac->seq_num;
421 if (ac->apply_mode) {
422 talloc_free(ares);
423 ac->index_current++;
425 ret = replmd_replicated_apply_next(ac);
426 if (ret != LDB_SUCCESS) {
427 return ldb_module_done(ac->req, NULL, NULL, ret);
429 return ret;
430 } else {
431 /* free the partition control container here, for the
432 * common path. Other cases will have it cleaned up
433 * eventually with the ares */
434 talloc_free(partition_ctrl);
435 return ldb_module_done(ac->req,
436 controls_except_specified(controls, ares, partition_ctrl),
437 ares->response, LDB_SUCCESS);
443 * update a @REPLCHANGED record in each partition if there have been
444 * any writes of replicated data in the partition
446 static int replmd_notify_store(struct ldb_module *module)
448 struct replmd_private *replmd_private =
449 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
450 struct ldb_context *ldb = ldb_module_get_ctx(module);
452 while (replmd_private->ncs) {
453 int ret;
454 struct nc_entry *modified_partition = replmd_private->ncs;
456 ret = dsdb_save_partition_usn(ldb, modified_partition->dn,
457 modified_partition->mod_usn,
458 modified_partition->mod_usn_urgent);
459 if (ret != LDB_SUCCESS) {
460 DEBUG(0,(__location__ ": Failed to save partition uSN for %s\n",
461 ldb_dn_get_linearized(modified_partition->dn)));
462 return ret;
464 DLIST_REMOVE(replmd_private->ncs, modified_partition);
465 talloc_free(modified_partition);
468 return LDB_SUCCESS;
473 created a replmd_replicated_request context
475 static struct replmd_replicated_request *replmd_ctx_init(struct ldb_module *module,
476 struct ldb_request *req)
478 struct ldb_context *ldb;
479 struct replmd_replicated_request *ac;
481 ldb = ldb_module_get_ctx(module);
483 ac = talloc_zero(req, struct replmd_replicated_request);
484 if (ac == NULL) {
485 ldb_oom(ldb);
486 return NULL;
489 ac->module = module;
490 ac->req = req;
492 ac->schema = dsdb_get_schema(ldb);
493 if (!ac->schema) {
494 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
495 "replmd_modify: no dsdb_schema loaded");
496 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
497 return NULL;
500 return ac;
504 add a time element to a record
506 static int add_time_element(struct ldb_message *msg, const char *attr, time_t t)
508 struct ldb_message_element *el;
509 char *s;
511 if (ldb_msg_find_element(msg, attr) != NULL) {
512 return LDB_SUCCESS;
515 s = ldb_timestring(msg, t);
516 if (s == NULL) {
517 return LDB_ERR_OPERATIONS_ERROR;
520 if (ldb_msg_add_string(msg, attr, s) != LDB_SUCCESS) {
521 return LDB_ERR_OPERATIONS_ERROR;
524 el = ldb_msg_find_element(msg, attr);
525 /* always set as replace. This works because on add ops, the flag
526 is ignored */
527 el->flags = LDB_FLAG_MOD_REPLACE;
529 return LDB_SUCCESS;
533 add a uint64_t element to a record
535 static int add_uint64_element(struct ldb_message *msg, const char *attr, uint64_t v)
537 struct ldb_message_element *el;
539 if (ldb_msg_find_element(msg, attr) != NULL) {
540 return LDB_SUCCESS;
543 if (ldb_msg_add_fmt(msg, attr, "%llu", (unsigned long long)v) != LDB_SUCCESS) {
544 return LDB_ERR_OPERATIONS_ERROR;
547 el = ldb_msg_find_element(msg, attr);
548 /* always set as replace. This works because on add ops, the flag
549 is ignored */
550 el->flags = LDB_FLAG_MOD_REPLACE;
552 return LDB_SUCCESS;
555 static int replmd_replPropertyMetaData1_attid_sort(const struct replPropertyMetaData1 *m1,
556 const struct replPropertyMetaData1 *m2,
557 const uint32_t *rdn_attid)
559 if (m1->attid == m2->attid) {
560 return 0;
564 * the rdn attribute should be at the end!
565 * so we need to return a value greater than zero
566 * which means m1 is greater than m2
568 if (m1->attid == *rdn_attid) {
569 return 1;
573 * the rdn attribute should be at the end!
574 * so we need to return a value less than zero
575 * which means m2 is greater than m1
577 if (m2->attid == *rdn_attid) {
578 return -1;
581 return m1->attid > m2->attid ? 1 : -1;
584 static int replmd_replPropertyMetaDataCtr1_sort(struct replPropertyMetaDataCtr1 *ctr1,
585 const struct dsdb_schema *schema,
586 struct ldb_dn *dn)
588 const char *rdn_name;
589 const struct dsdb_attribute *rdn_sa;
591 rdn_name = ldb_dn_get_rdn_name(dn);
592 if (!rdn_name) {
593 DEBUG(0,(__location__ ": No rDN for %s?\n", ldb_dn_get_linearized(dn)));
594 return LDB_ERR_OPERATIONS_ERROR;
597 rdn_sa = dsdb_attribute_by_lDAPDisplayName(schema, rdn_name);
598 if (rdn_sa == NULL) {
599 DEBUG(0,(__location__ ": No sa found for rDN %s for %s\n", rdn_name, ldb_dn_get_linearized(dn)));
600 return LDB_ERR_OPERATIONS_ERROR;
603 DEBUG(6,("Sorting rpmd with attid exception %u rDN=%s DN=%s\n",
604 rdn_sa->attributeID_id, rdn_name, ldb_dn_get_linearized(dn)));
606 LDB_TYPESAFE_QSORT(ctr1->array, ctr1->count, &rdn_sa->attributeID_id, replmd_replPropertyMetaData1_attid_sort);
608 return LDB_SUCCESS;
611 static int replmd_ldb_message_element_attid_sort(const struct ldb_message_element *e1,
612 const struct ldb_message_element *e2,
613 const struct dsdb_schema *schema)
615 const struct dsdb_attribute *a1;
616 const struct dsdb_attribute *a2;
619 * TODO: make this faster by caching the dsdb_attribute pointer
620 * on the ldb_messag_element
623 a1 = dsdb_attribute_by_lDAPDisplayName(schema, e1->name);
624 a2 = dsdb_attribute_by_lDAPDisplayName(schema, e2->name);
627 * TODO: remove this check, we should rely on e1 and e2 having valid attribute names
628 * in the schema
630 if (!a1 || !a2) {
631 return strcasecmp(e1->name, e2->name);
633 if (a1->attributeID_id == a2->attributeID_id) {
634 return 0;
636 return a1->attributeID_id > a2->attributeID_id ? 1 : -1;
639 static void replmd_ldb_message_sort(struct ldb_message *msg,
640 const struct dsdb_schema *schema)
642 LDB_TYPESAFE_QSORT(msg->elements, msg->num_elements, schema, replmd_ldb_message_element_attid_sort);
645 static int replmd_build_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
646 const struct GUID *invocation_id, uint64_t seq_num,
647 uint64_t local_usn, NTTIME nttime, uint32_t version, bool deleted);
651 fix up linked attributes in replmd_add.
652 This involves setting up the right meta-data in extended DN
653 components, and creating backlinks to the object
655 static int replmd_add_fix_la(struct ldb_module *module, struct ldb_message_element *el,
656 uint64_t seq_num, const struct GUID *invocationId, time_t t,
657 struct GUID *guid, const struct dsdb_attribute *sa)
659 unsigned int i;
660 TALLOC_CTX *tmp_ctx = talloc_new(el->values);
661 struct ldb_context *ldb = ldb_module_get_ctx(module);
662 struct dsdb_schema *schema = dsdb_get_schema(ldb);
663 NTTIME now;
665 unix_to_nt_time(&now, t);
667 for (i=0; i<el->num_values; i++) {
668 struct ldb_val *v = &el->values[i];
669 struct dsdb_dn *dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, v, sa->syntax->ldap_oid);
670 struct GUID target_guid;
671 NTSTATUS status;
672 int ret;
674 /* note that the DN already has the extended
675 components from the extended_dn_store module */
676 status = dsdb_get_extended_dn_guid(dsdb_dn->dn, &target_guid, "GUID");
677 if (!NT_STATUS_IS_OK(status) || GUID_all_zero(&target_guid)) {
678 ret = dsdb_module_guid_by_dn(module, dsdb_dn->dn, &target_guid);
679 if (ret != LDB_SUCCESS) {
680 talloc_free(tmp_ctx);
681 return ret;
683 ret = dsdb_set_extended_dn_guid(dsdb_dn->dn, &target_guid, "GUID");
684 if (ret != LDB_SUCCESS) {
685 talloc_free(tmp_ctx);
686 return ret;
690 ret = replmd_build_la_val(el->values, v, dsdb_dn, invocationId,
691 seq_num, seq_num, now, 0, false);
692 if (ret != LDB_SUCCESS) {
693 talloc_free(tmp_ctx);
694 return ret;
697 ret = replmd_add_backlink(module, schema, guid, &target_guid, true, sa, false);
698 if (ret != LDB_SUCCESS) {
699 talloc_free(tmp_ctx);
700 return ret;
704 talloc_free(tmp_ctx);
705 return LDB_SUCCESS;
710 intercept add requests
712 static int replmd_add(struct ldb_module *module, struct ldb_request *req)
714 struct ldb_context *ldb;
715 struct ldb_control *control;
716 struct replmd_replicated_request *ac;
717 enum ndr_err_code ndr_err;
718 struct ldb_request *down_req;
719 struct ldb_message *msg;
720 const DATA_BLOB *guid_blob;
721 struct GUID guid;
722 struct replPropertyMetaDataBlob nmd;
723 struct ldb_val nmd_value;
724 const struct GUID *our_invocation_id;
725 time_t t = time(NULL);
726 NTTIME now;
727 char *time_str;
728 int ret;
729 unsigned int i;
730 uint32_t ni=0;
731 bool allow_add_guid = false;
732 bool remove_current_guid = false;
733 bool is_urgent = false;
734 struct ldb_message_element *objectclass_el;
736 /* check if there's a show relax control (used by provision to say 'I know what I'm doing') */
737 control = ldb_request_get_control(req, LDB_CONTROL_RELAX_OID);
738 if (control) {
739 allow_add_guid = true;
742 /* do not manipulate our control entries */
743 if (ldb_dn_is_special(req->op.add.message->dn)) {
744 return ldb_next_request(module, req);
747 ldb = ldb_module_get_ctx(module);
749 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_add\n");
751 ac = replmd_ctx_init(module, req);
752 if (!ac) {
753 return LDB_ERR_OPERATIONS_ERROR;
756 guid_blob = ldb_msg_find_ldb_val(req->op.add.message, "objectGUID");
757 if ( guid_blob != NULL ) {
758 if( !allow_add_guid ) {
759 ldb_debug_set(ldb, LDB_DEBUG_ERROR,
760 "replmd_add: it's not allowed to add an object with objectGUID\n");
761 talloc_free(ac);
762 return LDB_ERR_UNWILLING_TO_PERFORM;
763 } else {
764 NTSTATUS status = GUID_from_data_blob(guid_blob,&guid);
765 if ( !NT_STATUS_IS_OK(status)) {
766 ldb_debug_set(ldb, LDB_DEBUG_ERROR,
767 "replmd_add: Unable to parse as a GUID the attribute objectGUID\n");
768 talloc_free(ac);
769 return LDB_ERR_UNWILLING_TO_PERFORM;
771 /* we remove this attribute as it can be a string and will not be treated
772 correctly and then we will readd it latter on in the good format*/
773 remove_current_guid = true;
775 } else {
776 /* a new GUID */
777 guid = GUID_random();
780 /* Get a sequence number from the backend */
781 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ac->seq_num);
782 if (ret != LDB_SUCCESS) {
783 talloc_free(ac);
784 return ret;
787 /* get our invocationId */
788 our_invocation_id = samdb_ntds_invocation_id(ldb);
789 if (!our_invocation_id) {
790 ldb_debug_set(ldb, LDB_DEBUG_ERROR,
791 "replmd_add: unable to find invocationId\n");
792 talloc_free(ac);
793 return LDB_ERR_OPERATIONS_ERROR;
796 /* we have to copy the message as the caller might have it as a const */
797 msg = ldb_msg_copy_shallow(ac, req->op.add.message);
798 if (msg == NULL) {
799 ldb_oom(ldb);
800 talloc_free(ac);
801 return LDB_ERR_OPERATIONS_ERROR;
804 /* generated times */
805 unix_to_nt_time(&now, t);
806 time_str = ldb_timestring(msg, t);
807 if (!time_str) {
808 ldb_oom(ldb);
809 talloc_free(ac);
810 return LDB_ERR_OPERATIONS_ERROR;
812 if (remove_current_guid) {
813 ldb_msg_remove_attr(msg,"objectGUID");
817 * remove autogenerated attributes
819 ldb_msg_remove_attr(msg, "whenCreated");
820 ldb_msg_remove_attr(msg, "whenChanged");
821 ldb_msg_remove_attr(msg, "uSNCreated");
822 ldb_msg_remove_attr(msg, "uSNChanged");
823 ldb_msg_remove_attr(msg, "replPropertyMetaData");
826 * readd replicated attributes
828 ret = ldb_msg_add_string(msg, "whenCreated", time_str);
829 if (ret != LDB_SUCCESS) {
830 ldb_oom(ldb);
831 talloc_free(ac);
832 return ret;
835 /* build the replication meta_data */
836 ZERO_STRUCT(nmd);
837 nmd.version = 1;
838 nmd.ctr.ctr1.count = msg->num_elements;
839 nmd.ctr.ctr1.array = talloc_array(msg,
840 struct replPropertyMetaData1,
841 nmd.ctr.ctr1.count);
842 if (!nmd.ctr.ctr1.array) {
843 ldb_oom(ldb);
844 talloc_free(ac);
845 return LDB_ERR_OPERATIONS_ERROR;
848 for (i=0; i < msg->num_elements; i++) {
849 struct ldb_message_element *e = &msg->elements[i];
850 struct replPropertyMetaData1 *m = &nmd.ctr.ctr1.array[ni];
851 const struct dsdb_attribute *sa;
853 if (e->name[0] == '@') continue;
855 sa = dsdb_attribute_by_lDAPDisplayName(ac->schema, e->name);
856 if (!sa) {
857 ldb_debug_set(ldb, LDB_DEBUG_ERROR,
858 "replmd_add: attribute '%s' not defined in schema\n",
859 e->name);
860 talloc_free(ac);
861 return LDB_ERR_NO_SUCH_ATTRIBUTE;
864 if ((sa->systemFlags & DS_FLAG_ATTR_NOT_REPLICATED) || (sa->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED)) {
865 /* if the attribute is not replicated (0x00000001)
866 * or constructed (0x00000004) it has no metadata
868 continue;
871 #if W2K3_LINKED_ATTRIBUTES
872 if (sa->linkID != 0 && dsdb_functional_level(ldb) > DS_DOMAIN_FUNCTION_2000) {
873 ret = replmd_add_fix_la(module, e, ac->seq_num, our_invocation_id, t, &guid, sa);
874 if (ret != LDB_SUCCESS) {
875 talloc_free(ac);
876 return ret;
878 /* linked attributes are not stored in
879 replPropertyMetaData in FL above w2k */
880 continue;
882 #endif
884 m->attid = sa->attributeID_id;
885 m->version = 1;
886 m->originating_change_time = now;
887 m->originating_invocation_id = *our_invocation_id;
888 m->originating_usn = ac->seq_num;
889 m->local_usn = ac->seq_num;
890 ni++;
893 /* fix meta data count */
894 nmd.ctr.ctr1.count = ni;
897 * sort meta data array, and move the rdn attribute entry to the end
899 ret = replmd_replPropertyMetaDataCtr1_sort(&nmd.ctr.ctr1, ac->schema, msg->dn);
900 if (ret != LDB_SUCCESS) {
901 talloc_free(ac);
902 return ret;
905 /* generated NDR encoded values */
906 ndr_err = ndr_push_struct_blob(&nmd_value, msg,
907 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
908 &nmd,
909 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
910 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
911 ldb_oom(ldb);
912 talloc_free(ac);
913 return LDB_ERR_OPERATIONS_ERROR;
917 * add the autogenerated values
919 ret = dsdb_msg_add_guid(msg, &guid, "objectGUID");
920 if (ret != LDB_SUCCESS) {
921 ldb_oom(ldb);
922 talloc_free(ac);
923 return ret;
925 ret = ldb_msg_add_string(msg, "whenChanged", time_str);
926 if (ret != LDB_SUCCESS) {
927 ldb_oom(ldb);
928 talloc_free(ac);
929 return ret;
931 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", ac->seq_num);
932 if (ret != LDB_SUCCESS) {
933 ldb_oom(ldb);
934 talloc_free(ac);
935 return ret;
937 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ac->seq_num);
938 if (ret != LDB_SUCCESS) {
939 ldb_oom(ldb);
940 talloc_free(ac);
941 return ret;
943 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
944 if (ret != LDB_SUCCESS) {
945 ldb_oom(ldb);
946 talloc_free(ac);
947 return ret;
951 * sort the attributes by attid before storing the object
953 replmd_ldb_message_sort(msg, ac->schema);
955 objectclass_el = ldb_msg_find_element(msg, "objectClass");
956 is_urgent = replmd_check_urgent_objectclass(objectclass_el,
957 REPL_URGENT_ON_CREATE);
959 ac->is_urgent = is_urgent;
960 ret = ldb_build_add_req(&down_req, ldb, ac,
961 msg,
962 req->controls,
963 ac, replmd_op_callback,
964 req);
966 if (ret != LDB_SUCCESS) {
967 talloc_free(ac);
968 return ret;
971 /* mark the control done */
972 if (control) {
973 control->critical = 0;
976 /* go on with the call chain */
977 return ldb_next_request(module, down_req);
982 * update the replPropertyMetaData for one element
984 static int replmd_update_rpmd_element(struct ldb_context *ldb,
985 struct ldb_message *msg,
986 struct ldb_message_element *el,
987 struct ldb_message_element *old_el,
988 struct replPropertyMetaDataBlob *omd,
989 const struct dsdb_schema *schema,
990 uint64_t *seq_num,
991 const struct GUID *our_invocation_id,
992 NTTIME now)
994 uint32_t i;
995 const struct dsdb_attribute *a;
996 struct replPropertyMetaData1 *md1;
998 a = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
999 if (a == NULL) {
1000 DEBUG(0,(__location__ ": Unable to find attribute %s in schema\n",
1001 el->name));
1002 return LDB_ERR_OPERATIONS_ERROR;
1005 if ((a->systemFlags & DS_FLAG_ATTR_NOT_REPLICATED) || (a->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED)) {
1006 return LDB_SUCCESS;
1009 /* if the attribute's value haven't changed then return LDB_SUCCESS */
1010 if (old_el != NULL && ldb_msg_element_compare(el, old_el) == 0) {
1011 return LDB_SUCCESS;
1014 for (i=0; i<omd->ctr.ctr1.count; i++) {
1015 if (a->attributeID_id == omd->ctr.ctr1.array[i].attid) break;
1018 #if W2K3_LINKED_ATTRIBUTES
1019 if (a->linkID != 0 && dsdb_functional_level(ldb) > DS_DOMAIN_FUNCTION_2000) {
1020 /* linked attributes are not stored in
1021 replPropertyMetaData in FL above w2k, but we do
1022 raise the seqnum for the object */
1023 if (*seq_num == 0 &&
1024 ldb_sequence_number(ldb, LDB_SEQ_NEXT, seq_num) != LDB_SUCCESS) {
1025 return LDB_ERR_OPERATIONS_ERROR;
1027 return LDB_SUCCESS;
1029 #endif
1031 if (i == omd->ctr.ctr1.count) {
1032 /* we need to add a new one */
1033 omd->ctr.ctr1.array = talloc_realloc(msg, omd->ctr.ctr1.array,
1034 struct replPropertyMetaData1, omd->ctr.ctr1.count+1);
1035 if (omd->ctr.ctr1.array == NULL) {
1036 ldb_oom(ldb);
1037 return LDB_ERR_OPERATIONS_ERROR;
1039 omd->ctr.ctr1.count++;
1040 ZERO_STRUCT(omd->ctr.ctr1.array[i]);
1043 /* Get a new sequence number from the backend. We only do this
1044 * if we have a change that requires a new
1045 * replPropertyMetaData element
1047 if (*seq_num == 0) {
1048 int ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, seq_num);
1049 if (ret != LDB_SUCCESS) {
1050 return LDB_ERR_OPERATIONS_ERROR;
1054 md1 = &omd->ctr.ctr1.array[i];
1055 md1->version++;
1056 md1->attid = a->attributeID_id;
1057 md1->originating_change_time = now;
1058 md1->originating_invocation_id = *our_invocation_id;
1059 md1->originating_usn = *seq_num;
1060 md1->local_usn = *seq_num;
1062 return LDB_SUCCESS;
1066 * update the replPropertyMetaData object each time we modify an
1067 * object. This is needed for DRS replication, as the merge on the
1068 * client is based on this object
1070 static int replmd_update_rpmd(struct ldb_module *module,
1071 const struct dsdb_schema *schema,
1072 struct ldb_message *msg, uint64_t *seq_num,
1073 time_t t,
1074 bool *is_urgent)
1076 const struct ldb_val *omd_value;
1077 enum ndr_err_code ndr_err;
1078 struct replPropertyMetaDataBlob omd;
1079 unsigned int i;
1080 NTTIME now;
1081 const struct GUID *our_invocation_id;
1082 int ret;
1083 const char *attrs[] = { "replPropertyMetaData", "*", NULL };
1084 struct ldb_result *res;
1085 struct ldb_context *ldb;
1086 struct ldb_message_element *objectclass_el;
1087 enum urgent_situation situation;
1089 ldb = ldb_module_get_ctx(module);
1091 our_invocation_id = samdb_ntds_invocation_id(ldb);
1092 if (!our_invocation_id) {
1093 /* this happens during an initial vampire while
1094 updating the schema */
1095 DEBUG(5,("No invocationID - skipping replPropertyMetaData update\n"));
1096 return LDB_SUCCESS;
1099 unix_to_nt_time(&now, t);
1101 /* search for the existing replPropertyMetaDataBlob. We need
1102 * to use REVEAL and ask for DNs in storage format to support
1103 * the check for values being the same in
1104 * replmd_update_rpmd_element()
1106 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attrs,
1107 DSDB_SEARCH_SHOW_DELETED |
1108 DSDB_SEARCH_SHOW_EXTENDED_DN |
1109 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
1110 DSDB_SEARCH_REVEAL_INTERNALS);
1111 if (ret != LDB_SUCCESS || res->count != 1) {
1112 DEBUG(0,(__location__ ": Object %s failed to find replPropertyMetaData\n",
1113 ldb_dn_get_linearized(msg->dn)));
1114 return LDB_ERR_OPERATIONS_ERROR;
1117 /* if isDeleted is present and is TRUE, then we consider we are deleting,
1118 * otherwise we consider we are updating */
1119 if (ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")) {
1120 situation = REPL_URGENT_ON_DELETE;
1121 } else {
1122 situation = REPL_URGENT_ON_UPDATE;
1125 objectclass_el = ldb_msg_find_element(res->msgs[0], "objectClass");
1126 if (is_urgent && replmd_check_urgent_objectclass(objectclass_el,
1127 situation)) {
1128 *is_urgent = true;
1131 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
1132 if (!omd_value) {
1133 DEBUG(0,(__location__ ": Object %s does not have a replPropertyMetaData attribute\n",
1134 ldb_dn_get_linearized(msg->dn)));
1135 return LDB_ERR_OPERATIONS_ERROR;
1138 ndr_err = ndr_pull_struct_blob(omd_value, msg,
1139 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")), &omd,
1140 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
1141 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1142 DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s\n",
1143 ldb_dn_get_linearized(msg->dn)));
1144 return LDB_ERR_OPERATIONS_ERROR;
1147 if (omd.version != 1) {
1148 DEBUG(0,(__location__ ": bad version %u in replPropertyMetaData for %s\n",
1149 omd.version, ldb_dn_get_linearized(msg->dn)));
1150 return LDB_ERR_OPERATIONS_ERROR;
1153 for (i=0; i<msg->num_elements; i++) {
1154 struct ldb_message_element *old_el;
1155 old_el = ldb_msg_find_element(res->msgs[0], msg->elements[i].name);
1156 ret = replmd_update_rpmd_element(ldb, msg, &msg->elements[i], old_el, &omd, schema, seq_num,
1157 our_invocation_id, now);
1158 if (ret != LDB_SUCCESS) {
1159 return ret;
1162 if (is_urgent && !*is_urgent && (situation == REPL_URGENT_ON_UPDATE)) {
1163 *is_urgent = replmd_check_urgent_attribute(&msg->elements[i]);
1169 * replmd_update_rpmd_element has done an update if the
1170 * seq_num is set
1172 if (*seq_num != 0) {
1173 struct ldb_val *md_value;
1174 struct ldb_message_element *el;
1176 md_value = talloc(msg, struct ldb_val);
1177 if (md_value == NULL) {
1178 ldb_oom(ldb);
1179 return LDB_ERR_OPERATIONS_ERROR;
1182 ret = replmd_replPropertyMetaDataCtr1_sort(&omd.ctr.ctr1, schema, msg->dn);
1183 if (ret != LDB_SUCCESS) {
1184 return ret;
1187 ndr_err = ndr_push_struct_blob(md_value, msg,
1188 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
1189 &omd,
1190 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
1191 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1192 DEBUG(0,(__location__ ": Failed to marshall replPropertyMetaData for %s\n",
1193 ldb_dn_get_linearized(msg->dn)));
1194 return LDB_ERR_OPERATIONS_ERROR;
1197 ret = ldb_msg_add_empty(msg, "replPropertyMetaData", LDB_FLAG_MOD_REPLACE, &el);
1198 if (ret != LDB_SUCCESS) {
1199 DEBUG(0,(__location__ ": Failed to add updated replPropertyMetaData %s\n",
1200 ldb_dn_get_linearized(msg->dn)));
1201 return ret;
1204 el->num_values = 1;
1205 el->values = md_value;
1208 return LDB_SUCCESS;
1211 struct parsed_dn {
1212 struct dsdb_dn *dsdb_dn;
1213 struct GUID *guid;
1214 struct ldb_val *v;
1217 static int parsed_dn_compare(struct parsed_dn *pdn1, struct parsed_dn *pdn2)
1219 return GUID_compare(pdn1->guid, pdn2->guid);
1222 static struct parsed_dn *parsed_dn_find(struct parsed_dn *pdn, int count, struct GUID *guid, struct ldb_dn *dn)
1224 struct parsed_dn *ret;
1225 if (dn && GUID_all_zero(guid)) {
1226 /* when updating a link using DRS, we sometimes get a
1227 NULL GUID. We then need to try and match by DN */
1228 int i;
1229 for (i=0; i<count; i++) {
1230 if (ldb_dn_compare(pdn[i].dsdb_dn->dn, dn) == 0) {
1231 dsdb_get_extended_dn_guid(pdn[i].dsdb_dn->dn, guid, "GUID");
1232 return &pdn[i];
1235 return NULL;
1237 BINARY_ARRAY_SEARCH(pdn, count, guid, guid, GUID_compare, ret);
1238 return ret;
1242 get a series of message element values as an array of DNs and GUIDs
1243 the result is sorted by GUID
1245 static int get_parsed_dns(struct ldb_module *module, TALLOC_CTX *mem_ctx,
1246 struct ldb_message_element *el, struct parsed_dn **pdn,
1247 const char *ldap_oid)
1249 unsigned int i;
1250 struct ldb_context *ldb = ldb_module_get_ctx(module);
1252 if (el == NULL) {
1253 *pdn = NULL;
1254 return LDB_SUCCESS;
1257 (*pdn) = talloc_array(mem_ctx, struct parsed_dn, el->num_values);
1258 if (!*pdn) {
1259 ldb_module_oom(module);
1260 return LDB_ERR_OPERATIONS_ERROR;
1263 for (i=0; i<el->num_values; i++) {
1264 struct ldb_val *v = &el->values[i];
1265 NTSTATUS status;
1266 struct ldb_dn *dn;
1267 struct parsed_dn *p;
1269 p = &(*pdn)[i];
1271 p->dsdb_dn = dsdb_dn_parse(*pdn, ldb, v, ldap_oid);
1272 if (p->dsdb_dn == NULL) {
1273 return LDB_ERR_INVALID_DN_SYNTAX;
1276 dn = p->dsdb_dn->dn;
1278 p->guid = talloc(*pdn, struct GUID);
1279 if (p->guid == NULL) {
1280 ldb_module_oom(module);
1281 return LDB_ERR_OPERATIONS_ERROR;
1284 status = dsdb_get_extended_dn_guid(dn, p->guid, "GUID");
1285 if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
1286 /* we got a DN without a GUID - go find the GUID */
1287 int ret = dsdb_module_guid_by_dn(module, dn, p->guid);
1288 if (ret != LDB_SUCCESS) {
1289 ldb_asprintf_errstring(ldb, "Unable to find GUID for DN %s\n",
1290 ldb_dn_get_linearized(dn));
1291 return ret;
1293 ret = dsdb_set_extended_dn_guid(dn, p->guid, "GUID");
1294 if (ret != LDB_SUCCESS) {
1295 return ret;
1297 } else if (!NT_STATUS_IS_OK(status)) {
1298 return LDB_ERR_OPERATIONS_ERROR;
1301 /* keep a pointer to the original ldb_val */
1302 p->v = v;
1305 TYPESAFE_QSORT(*pdn, el->num_values, parsed_dn_compare);
1307 return LDB_SUCCESS;
1311 build a new extended DN, including all meta data fields
1313 RMD_FLAGS = DSDB_RMD_FLAG_* bits
1314 RMD_ADDTIME = originating_add_time
1315 RMD_INVOCID = originating_invocation_id
1316 RMD_CHANGETIME = originating_change_time
1317 RMD_ORIGINATING_USN = originating_usn
1318 RMD_LOCAL_USN = local_usn
1319 RMD_VERSION = version
1321 static int replmd_build_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
1322 const struct GUID *invocation_id, uint64_t seq_num,
1323 uint64_t local_usn, NTTIME nttime, uint32_t version, bool deleted)
1325 struct ldb_dn *dn = dsdb_dn->dn;
1326 const char *tstring, *usn_string, *flags_string;
1327 struct ldb_val tval;
1328 struct ldb_val iid;
1329 struct ldb_val usnv, local_usnv;
1330 struct ldb_val vers, flagsv;
1331 NTSTATUS status;
1332 int ret;
1333 const char *dnstring;
1334 char *vstring;
1335 uint32_t rmd_flags = deleted?DSDB_RMD_FLAG_DELETED:0;
1337 tstring = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)nttime);
1338 if (!tstring) {
1339 return LDB_ERR_OPERATIONS_ERROR;
1341 tval = data_blob_string_const(tstring);
1343 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)seq_num);
1344 if (!usn_string) {
1345 return LDB_ERR_OPERATIONS_ERROR;
1347 usnv = data_blob_string_const(usn_string);
1349 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)local_usn);
1350 if (!usn_string) {
1351 return LDB_ERR_OPERATIONS_ERROR;
1353 local_usnv = data_blob_string_const(usn_string);
1355 vstring = talloc_asprintf(mem_ctx, "%lu", (unsigned long)version);
1356 if (!vstring) {
1357 return LDB_ERR_OPERATIONS_ERROR;
1359 vers = data_blob_string_const(vstring);
1361 status = GUID_to_ndr_blob(invocation_id, dn, &iid);
1362 if (!NT_STATUS_IS_OK(status)) {
1363 return LDB_ERR_OPERATIONS_ERROR;
1366 flags_string = talloc_asprintf(mem_ctx, "%u", rmd_flags);
1367 if (!flags_string) {
1368 return LDB_ERR_OPERATIONS_ERROR;
1370 flagsv = data_blob_string_const(flags_string);
1372 ret = ldb_dn_set_extended_component(dn, "RMD_FLAGS", &flagsv);
1373 if (ret != LDB_SUCCESS) return ret;
1374 ret = ldb_dn_set_extended_component(dn, "RMD_ADDTIME", &tval);
1375 if (ret != LDB_SUCCESS) return ret;
1376 ret = ldb_dn_set_extended_component(dn, "RMD_INVOCID", &iid);
1377 if (ret != LDB_SUCCESS) return ret;
1378 ret = ldb_dn_set_extended_component(dn, "RMD_CHANGETIME", &tval);
1379 if (ret != LDB_SUCCESS) return ret;
1380 ret = ldb_dn_set_extended_component(dn, "RMD_LOCAL_USN", &local_usnv);
1381 if (ret != LDB_SUCCESS) return ret;
1382 ret = ldb_dn_set_extended_component(dn, "RMD_ORIGINATING_USN", &usnv);
1383 if (ret != LDB_SUCCESS) return ret;
1384 ret = ldb_dn_set_extended_component(dn, "RMD_VERSION", &vers);
1385 if (ret != LDB_SUCCESS) return ret;
1387 dnstring = dsdb_dn_get_extended_linearized(mem_ctx, dsdb_dn, 1);
1388 if (dnstring == NULL) {
1389 return LDB_ERR_OPERATIONS_ERROR;
1391 *v = data_blob_string_const(dnstring);
1393 return LDB_SUCCESS;
1396 static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
1397 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
1398 uint64_t seq_num, uint64_t local_usn, NTTIME nttime,
1399 uint32_t version, bool deleted);
1402 check if any links need upgrading from w2k format
1404 static int replmd_check_upgrade_links(struct parsed_dn *dns, uint32_t count, const struct GUID *invocation_id)
1406 uint32_t i;
1407 for (i=0; i<count; i++) {
1408 NTSTATUS status;
1409 uint32_t version;
1410 int ret;
1412 status = dsdb_get_extended_dn_uint32(dns[i].dsdb_dn->dn, &version, "RMD_VERSION");
1413 if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
1414 continue;
1417 /* it's an old one that needs upgrading */
1418 ret = replmd_update_la_val(dns, dns[i].v, dns[i].dsdb_dn, dns[i].dsdb_dn, invocation_id,
1419 1, 1, 0, 0, false);
1420 if (ret != LDB_SUCCESS) {
1421 return ret;
1424 return LDB_SUCCESS;
1428 update an extended DN, including all meta data fields
1430 see replmd_build_la_val for value names
1432 static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
1433 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
1434 uint64_t seq_num, uint64_t local_usn, NTTIME nttime,
1435 uint32_t version, bool deleted)
1437 struct ldb_dn *dn = dsdb_dn->dn;
1438 const char *tstring, *usn_string, *flags_string;
1439 struct ldb_val tval;
1440 struct ldb_val iid;
1441 struct ldb_val usnv, local_usnv;
1442 struct ldb_val vers, flagsv;
1443 const struct ldb_val *old_addtime;
1444 uint32_t old_version;
1445 NTSTATUS status;
1446 int ret;
1447 const char *dnstring;
1448 char *vstring;
1449 uint32_t rmd_flags = deleted?DSDB_RMD_FLAG_DELETED:0;
1451 tstring = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)nttime);
1452 if (!tstring) {
1453 return LDB_ERR_OPERATIONS_ERROR;
1455 tval = data_blob_string_const(tstring);
1457 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)seq_num);
1458 if (!usn_string) {
1459 return LDB_ERR_OPERATIONS_ERROR;
1461 usnv = data_blob_string_const(usn_string);
1463 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)local_usn);
1464 if (!usn_string) {
1465 return LDB_ERR_OPERATIONS_ERROR;
1467 local_usnv = data_blob_string_const(usn_string);
1469 status = GUID_to_ndr_blob(invocation_id, dn, &iid);
1470 if (!NT_STATUS_IS_OK(status)) {
1471 return LDB_ERR_OPERATIONS_ERROR;
1474 flags_string = talloc_asprintf(mem_ctx, "%u", rmd_flags);
1475 if (!flags_string) {
1476 return LDB_ERR_OPERATIONS_ERROR;
1478 flagsv = data_blob_string_const(flags_string);
1480 ret = ldb_dn_set_extended_component(dn, "RMD_FLAGS", &flagsv);
1481 if (ret != LDB_SUCCESS) return ret;
1483 /* get the ADDTIME from the original */
1484 old_addtime = ldb_dn_get_extended_component(old_dsdb_dn->dn, "RMD_ADDTIME");
1485 if (old_addtime == NULL) {
1486 old_addtime = &tval;
1488 if (dsdb_dn != old_dsdb_dn) {
1489 ret = ldb_dn_set_extended_component(dn, "RMD_ADDTIME", old_addtime);
1490 if (ret != LDB_SUCCESS) return ret;
1493 /* use our invocation id */
1494 ret = ldb_dn_set_extended_component(dn, "RMD_INVOCID", &iid);
1495 if (ret != LDB_SUCCESS) return ret;
1497 /* changetime is the current time */
1498 ret = ldb_dn_set_extended_component(dn, "RMD_CHANGETIME", &tval);
1499 if (ret != LDB_SUCCESS) return ret;
1501 /* update the USN */
1502 ret = ldb_dn_set_extended_component(dn, "RMD_ORIGINATING_USN", &usnv);
1503 if (ret != LDB_SUCCESS) return ret;
1505 ret = ldb_dn_set_extended_component(dn, "RMD_LOCAL_USN", &local_usnv);
1506 if (ret != LDB_SUCCESS) return ret;
1508 /* increase the version by 1 */
1509 status = dsdb_get_extended_dn_uint32(old_dsdb_dn->dn, &old_version, "RMD_VERSION");
1510 if (NT_STATUS_IS_OK(status) && old_version >= version) {
1511 version = old_version+1;
1513 vstring = talloc_asprintf(dn, "%lu", (unsigned long)version);
1514 vers = data_blob_string_const(vstring);
1515 ret = ldb_dn_set_extended_component(dn, "RMD_VERSION", &vers);
1516 if (ret != LDB_SUCCESS) return ret;
1518 dnstring = dsdb_dn_get_extended_linearized(mem_ctx, dsdb_dn, 1);
1519 if (dnstring == NULL) {
1520 return LDB_ERR_OPERATIONS_ERROR;
1522 *v = data_blob_string_const(dnstring);
1524 return LDB_SUCCESS;
1528 handle adding a linked attribute
1530 static int replmd_modify_la_add(struct ldb_module *module,
1531 struct dsdb_schema *schema,
1532 struct ldb_message *msg,
1533 struct ldb_message_element *el,
1534 struct ldb_message_element *old_el,
1535 const struct dsdb_attribute *schema_attr,
1536 uint64_t seq_num,
1537 time_t t,
1538 struct GUID *msg_guid)
1540 unsigned int i;
1541 struct parsed_dn *dns, *old_dns;
1542 TALLOC_CTX *tmp_ctx = talloc_new(msg);
1543 int ret;
1544 struct ldb_val *new_values = NULL;
1545 unsigned int num_new_values = 0;
1546 unsigned old_num_values = old_el?old_el->num_values:0;
1547 const struct GUID *invocation_id;
1548 struct ldb_context *ldb = ldb_module_get_ctx(module);
1549 NTTIME now;
1551 unix_to_nt_time(&now, t);
1553 ret = get_parsed_dns(module, tmp_ctx, el, &dns, schema_attr->syntax->ldap_oid);
1554 if (ret != LDB_SUCCESS) {
1555 talloc_free(tmp_ctx);
1556 return ret;
1559 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns, schema_attr->syntax->ldap_oid);
1560 if (ret != LDB_SUCCESS) {
1561 talloc_free(tmp_ctx);
1562 return ret;
1565 invocation_id = samdb_ntds_invocation_id(ldb);
1566 if (!invocation_id) {
1567 talloc_free(tmp_ctx);
1568 return LDB_ERR_OPERATIONS_ERROR;
1571 ret = replmd_check_upgrade_links(old_dns, old_num_values, invocation_id);
1572 if (ret != LDB_SUCCESS) {
1573 talloc_free(tmp_ctx);
1574 return ret;
1577 /* for each new value, see if it exists already with the same GUID */
1578 for (i=0; i<el->num_values; i++) {
1579 struct parsed_dn *p = parsed_dn_find(old_dns, old_num_values, dns[i].guid, NULL);
1580 if (p == NULL) {
1581 /* this is a new linked attribute value */
1582 new_values = talloc_realloc(tmp_ctx, new_values, struct ldb_val, num_new_values+1);
1583 if (new_values == NULL) {
1584 ldb_module_oom(module);
1585 talloc_free(tmp_ctx);
1586 return LDB_ERR_OPERATIONS_ERROR;
1588 ret = replmd_build_la_val(new_values, &new_values[num_new_values], dns[i].dsdb_dn,
1589 invocation_id, seq_num, seq_num, now, 0, false);
1590 if (ret != LDB_SUCCESS) {
1591 talloc_free(tmp_ctx);
1592 return ret;
1594 num_new_values++;
1595 } else {
1596 /* this is only allowed if the GUID was
1597 previously deleted. */
1598 uint32_t rmd_flags = dsdb_dn_rmd_flags(p->dsdb_dn->dn);
1600 if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
1601 ldb_asprintf_errstring(ldb, "Attribute %s already exists for target GUID %s",
1602 el->name, GUID_string(tmp_ctx, p->guid));
1603 talloc_free(tmp_ctx);
1604 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
1606 ret = replmd_update_la_val(old_el->values, p->v, dns[i].dsdb_dn, p->dsdb_dn,
1607 invocation_id, seq_num, seq_num, now, 0, false);
1608 if (ret != LDB_SUCCESS) {
1609 talloc_free(tmp_ctx);
1610 return ret;
1614 ret = replmd_add_backlink(module, schema, msg_guid, dns[i].guid, true, schema_attr, true);
1615 if (ret != LDB_SUCCESS) {
1616 talloc_free(tmp_ctx);
1617 return ret;
1621 /* add the new ones on to the end of the old values, constructing a new el->values */
1622 el->values = talloc_realloc(msg->elements, old_el?old_el->values:NULL,
1623 struct ldb_val,
1624 old_num_values+num_new_values);
1625 if (el->values == NULL) {
1626 ldb_module_oom(module);
1627 return LDB_ERR_OPERATIONS_ERROR;
1630 memcpy(&el->values[old_num_values], new_values, num_new_values*sizeof(struct ldb_val));
1631 el->num_values = old_num_values + num_new_values;
1633 talloc_steal(msg->elements, el->values);
1634 talloc_steal(el->values, new_values);
1636 talloc_free(tmp_ctx);
1638 /* we now tell the backend to replace all existing values
1639 with the one we have constructed */
1640 el->flags = LDB_FLAG_MOD_REPLACE;
1642 return LDB_SUCCESS;
1647 handle deleting all active linked attributes
1649 static int replmd_modify_la_delete(struct ldb_module *module,
1650 struct dsdb_schema *schema,
1651 struct ldb_message *msg,
1652 struct ldb_message_element *el,
1653 struct ldb_message_element *old_el,
1654 const struct dsdb_attribute *schema_attr,
1655 uint64_t seq_num,
1656 time_t t,
1657 struct GUID *msg_guid)
1659 unsigned int i;
1660 struct parsed_dn *dns, *old_dns;
1661 TALLOC_CTX *tmp_ctx = talloc_new(msg);
1662 int ret;
1663 const struct GUID *invocation_id;
1664 struct ldb_context *ldb = ldb_module_get_ctx(module);
1665 NTTIME now;
1667 unix_to_nt_time(&now, t);
1669 /* check if there is nothing to delete */
1670 if ((!old_el || old_el->num_values == 0) &&
1671 el->num_values == 0) {
1672 return LDB_SUCCESS;
1675 if (!old_el || old_el->num_values == 0) {
1676 return LDB_ERR_NO_SUCH_ATTRIBUTE;
1679 ret = get_parsed_dns(module, tmp_ctx, el, &dns, schema_attr->syntax->ldap_oid);
1680 if (ret != LDB_SUCCESS) {
1681 talloc_free(tmp_ctx);
1682 return ret;
1685 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns, schema_attr->syntax->ldap_oid);
1686 if (ret != LDB_SUCCESS) {
1687 talloc_free(tmp_ctx);
1688 return ret;
1691 invocation_id = samdb_ntds_invocation_id(ldb);
1692 if (!invocation_id) {
1693 return LDB_ERR_OPERATIONS_ERROR;
1696 ret = replmd_check_upgrade_links(old_dns, old_el->num_values, invocation_id);
1697 if (ret != LDB_SUCCESS) {
1698 talloc_free(tmp_ctx);
1699 return ret;
1702 el->values = NULL;
1704 /* see if we are being asked to delete any links that
1705 don't exist or are already deleted */
1706 for (i=0; i<el->num_values; i++) {
1707 struct parsed_dn *p = &dns[i];
1708 struct parsed_dn *p2;
1709 uint32_t rmd_flags;
1711 p2 = parsed_dn_find(old_dns, old_el->num_values, p->guid, NULL);
1712 if (!p2) {
1713 ldb_asprintf_errstring(ldb, "Attribute %s doesn't exist for target GUID %s",
1714 el->name, GUID_string(tmp_ctx, p->guid));
1715 return LDB_ERR_NO_SUCH_ATTRIBUTE;
1717 rmd_flags = dsdb_dn_rmd_flags(p2->dsdb_dn->dn);
1718 if (rmd_flags & DSDB_RMD_FLAG_DELETED) {
1719 ldb_asprintf_errstring(ldb, "Attribute %s already deleted for target GUID %s",
1720 el->name, GUID_string(tmp_ctx, p->guid));
1721 return LDB_ERR_NO_SUCH_ATTRIBUTE;
1725 /* for each new value, see if it exists already with the same GUID
1726 if it is not already deleted and matches the delete list then delete it
1728 for (i=0; i<old_el->num_values; i++) {
1729 struct parsed_dn *p = &old_dns[i];
1730 uint32_t rmd_flags;
1732 if (el->num_values && parsed_dn_find(dns, el->num_values, p->guid, NULL) == NULL) {
1733 continue;
1736 rmd_flags = dsdb_dn_rmd_flags(p->dsdb_dn->dn);
1737 if (rmd_flags & DSDB_RMD_FLAG_DELETED) continue;
1739 ret = replmd_update_la_val(old_el->values, p->v, p->dsdb_dn, p->dsdb_dn,
1740 invocation_id, seq_num, seq_num, now, 0, true);
1741 if (ret != LDB_SUCCESS) {
1742 talloc_free(tmp_ctx);
1743 return ret;
1746 ret = replmd_add_backlink(module, schema, msg_guid, old_dns[i].guid, false, schema_attr, true);
1747 if (ret != LDB_SUCCESS) {
1748 talloc_free(tmp_ctx);
1749 return ret;
1753 el->values = talloc_steal(msg->elements, old_el->values);
1754 el->num_values = old_el->num_values;
1756 talloc_free(tmp_ctx);
1758 /* we now tell the backend to replace all existing values
1759 with the one we have constructed */
1760 el->flags = LDB_FLAG_MOD_REPLACE;
1762 return LDB_SUCCESS;
1766 handle replacing a linked attribute
1768 static int replmd_modify_la_replace(struct ldb_module *module,
1769 struct dsdb_schema *schema,
1770 struct ldb_message *msg,
1771 struct ldb_message_element *el,
1772 struct ldb_message_element *old_el,
1773 const struct dsdb_attribute *schema_attr,
1774 uint64_t seq_num,
1775 time_t t,
1776 struct GUID *msg_guid)
1778 unsigned int i;
1779 struct parsed_dn *dns, *old_dns;
1780 TALLOC_CTX *tmp_ctx = talloc_new(msg);
1781 int ret;
1782 const struct GUID *invocation_id;
1783 struct ldb_context *ldb = ldb_module_get_ctx(module);
1784 struct ldb_val *new_values = NULL;
1785 unsigned int num_new_values = 0;
1786 unsigned int old_num_values = old_el?old_el->num_values:0;
1787 NTTIME now;
1789 unix_to_nt_time(&now, t);
1791 /* check if there is nothing to replace */
1792 if ((!old_el || old_el->num_values == 0) &&
1793 el->num_values == 0) {
1794 return LDB_SUCCESS;
1797 ret = get_parsed_dns(module, tmp_ctx, el, &dns, schema_attr->syntax->ldap_oid);
1798 if (ret != LDB_SUCCESS) {
1799 talloc_free(tmp_ctx);
1800 return ret;
1803 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns, schema_attr->syntax->ldap_oid);
1804 if (ret != LDB_SUCCESS) {
1805 talloc_free(tmp_ctx);
1806 return ret;
1809 invocation_id = samdb_ntds_invocation_id(ldb);
1810 if (!invocation_id) {
1811 return LDB_ERR_OPERATIONS_ERROR;
1814 ret = replmd_check_upgrade_links(old_dns, old_num_values, invocation_id);
1815 if (ret != LDB_SUCCESS) {
1816 talloc_free(tmp_ctx);
1817 return ret;
1820 /* mark all the old ones as deleted */
1821 for (i=0; i<old_num_values; i++) {
1822 struct parsed_dn *old_p = &old_dns[i];
1823 struct parsed_dn *p;
1824 uint32_t rmd_flags = dsdb_dn_rmd_flags(old_p->dsdb_dn->dn);
1826 if (rmd_flags & DSDB_RMD_FLAG_DELETED) continue;
1828 ret = replmd_add_backlink(module, schema, msg_guid, old_dns[i].guid, false, schema_attr, false);
1829 if (ret != LDB_SUCCESS) {
1830 talloc_free(tmp_ctx);
1831 return ret;
1834 p = parsed_dn_find(dns, el->num_values, old_p->guid, NULL);
1835 if (p) {
1836 /* we don't delete it if we are re-adding it */
1837 continue;
1840 ret = replmd_update_la_val(old_el->values, old_p->v, old_p->dsdb_dn, old_p->dsdb_dn,
1841 invocation_id, seq_num, seq_num, now, 0, true);
1842 if (ret != LDB_SUCCESS) {
1843 talloc_free(tmp_ctx);
1844 return ret;
1848 /* for each new value, either update its meta-data, or add it
1849 * to old_el
1851 for (i=0; i<el->num_values; i++) {
1852 struct parsed_dn *p = &dns[i], *old_p;
1854 if (old_dns &&
1855 (old_p = parsed_dn_find(old_dns,
1856 old_num_values, p->guid, NULL)) != NULL) {
1857 /* update in place */
1858 ret = replmd_update_la_val(old_el->values, old_p->v, old_p->dsdb_dn,
1859 old_p->dsdb_dn, invocation_id,
1860 seq_num, seq_num, now, 0, false);
1861 if (ret != LDB_SUCCESS) {
1862 talloc_free(tmp_ctx);
1863 return ret;
1865 } else {
1866 /* add a new one */
1867 new_values = talloc_realloc(tmp_ctx, new_values, struct ldb_val,
1868 num_new_values+1);
1869 if (new_values == NULL) {
1870 ldb_module_oom(module);
1871 talloc_free(tmp_ctx);
1872 return LDB_ERR_OPERATIONS_ERROR;
1874 ret = replmd_build_la_val(new_values, &new_values[num_new_values], dns[i].dsdb_dn,
1875 invocation_id, seq_num, seq_num, now, 0, false);
1876 if (ret != LDB_SUCCESS) {
1877 talloc_free(tmp_ctx);
1878 return ret;
1880 num_new_values++;
1883 ret = replmd_add_backlink(module, schema, msg_guid, dns[i].guid, true, schema_attr, false);
1884 if (ret != LDB_SUCCESS) {
1885 talloc_free(tmp_ctx);
1886 return ret;
1890 /* add the new values to the end of old_el */
1891 if (num_new_values != 0) {
1892 el->values = talloc_realloc(msg->elements, old_el?old_el->values:NULL,
1893 struct ldb_val, old_num_values+num_new_values);
1894 if (el->values == NULL) {
1895 ldb_module_oom(module);
1896 return LDB_ERR_OPERATIONS_ERROR;
1898 memcpy(&el->values[old_num_values], &new_values[0],
1899 sizeof(struct ldb_val)*num_new_values);
1900 el->num_values = old_num_values + num_new_values;
1901 talloc_steal(msg->elements, new_values);
1902 } else {
1903 el->values = old_el->values;
1904 el->num_values = old_el->num_values;
1905 talloc_steal(msg->elements, el->values);
1908 talloc_free(tmp_ctx);
1910 /* we now tell the backend to replace all existing values
1911 with the one we have constructed */
1912 el->flags = LDB_FLAG_MOD_REPLACE;
1914 return LDB_SUCCESS;
1919 handle linked attributes in modify requests
1921 static int replmd_modify_handle_linked_attribs(struct ldb_module *module,
1922 struct ldb_message *msg,
1923 uint64_t seq_num, time_t t)
1925 struct ldb_result *res;
1926 unsigned int i;
1927 int ret;
1928 struct ldb_context *ldb = ldb_module_get_ctx(module);
1929 struct ldb_message *old_msg;
1930 struct dsdb_schema *schema = dsdb_get_schema(ldb);
1931 struct GUID old_guid;
1933 if (seq_num == 0) {
1934 /* there the replmd_update_rpmd code has already
1935 * checked and saw that there are no linked
1936 * attributes */
1937 return LDB_SUCCESS;
1940 #if !W2K3_LINKED_ATTRIBUTES
1941 return LDB_SUCCESS;
1942 #endif
1944 if (dsdb_functional_level(ldb) == DS_DOMAIN_FUNCTION_2000) {
1945 /* don't do anything special for linked attributes */
1946 return LDB_SUCCESS;
1949 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, NULL,
1950 DSDB_SEARCH_SHOW_DELETED |
1951 DSDB_SEARCH_REVEAL_INTERNALS |
1952 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT);
1953 if (ret != LDB_SUCCESS) {
1954 return ret;
1956 old_msg = res->msgs[0];
1958 old_guid = samdb_result_guid(old_msg, "objectGUID");
1960 for (i=0; i<msg->num_elements; i++) {
1961 struct ldb_message_element *el = &msg->elements[i];
1962 struct ldb_message_element *old_el, *new_el;
1963 const struct dsdb_attribute *schema_attr
1964 = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
1965 if (!schema_attr) {
1966 ldb_asprintf_errstring(ldb,
1967 "attribute %s is not a valid attribute in schema", el->name);
1968 return LDB_ERR_OBJECT_CLASS_VIOLATION;
1970 if (schema_attr->linkID == 0) {
1971 continue;
1973 if ((schema_attr->linkID & 1) == 1) {
1974 /* Odd is for the target. Illegal to modify */
1975 ldb_asprintf_errstring(ldb,
1976 "attribute %s must not be modified directly, it is a linked attribute", el->name);
1977 return LDB_ERR_UNWILLING_TO_PERFORM;
1979 old_el = ldb_msg_find_element(old_msg, el->name);
1980 switch (el->flags & LDB_FLAG_MOD_MASK) {
1981 case LDB_FLAG_MOD_REPLACE:
1982 ret = replmd_modify_la_replace(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid);
1983 break;
1984 case LDB_FLAG_MOD_DELETE:
1985 ret = replmd_modify_la_delete(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid);
1986 break;
1987 case LDB_FLAG_MOD_ADD:
1988 ret = replmd_modify_la_add(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid);
1989 break;
1990 default:
1991 ldb_asprintf_errstring(ldb,
1992 "invalid flags 0x%x for %s linked attribute",
1993 el->flags, el->name);
1994 return LDB_ERR_UNWILLING_TO_PERFORM;
1996 if (ret != LDB_SUCCESS) {
1997 return ret;
1999 if (old_el) {
2000 ldb_msg_remove_attr(old_msg, el->name);
2002 ldb_msg_add_empty(old_msg, el->name, 0, &new_el);
2003 new_el->num_values = el->num_values;
2004 new_el->values = talloc_steal(msg->elements, el->values);
2006 /* TODO: this relises a bit too heavily on the exact
2007 behaviour of ldb_msg_find_element and
2008 ldb_msg_remove_element */
2009 old_el = ldb_msg_find_element(msg, el->name);
2010 if (old_el != el) {
2011 ldb_msg_remove_element(msg, old_el);
2012 i--;
2016 talloc_free(res);
2017 return ret;
2022 static int replmd_modify(struct ldb_module *module, struct ldb_request *req)
2024 struct ldb_context *ldb;
2025 struct replmd_replicated_request *ac;
2026 struct ldb_request *down_req;
2027 struct ldb_message *msg;
2028 time_t t = time(NULL);
2029 int ret;
2030 bool is_urgent = false;
2032 /* do not manipulate our control entries */
2033 if (ldb_dn_is_special(req->op.mod.message->dn)) {
2034 return ldb_next_request(module, req);
2037 ldb = ldb_module_get_ctx(module);
2039 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_modify\n");
2041 ac = replmd_ctx_init(module, req);
2042 if (!ac) {
2043 return LDB_ERR_OPERATIONS_ERROR;
2046 /* we have to copy the message as the caller might have it as a const */
2047 msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
2048 if (msg == NULL) {
2049 ldb_oom(ldb);
2050 talloc_free(ac);
2051 return LDB_ERR_OPERATIONS_ERROR;
2054 ldb_msg_remove_attr(msg, "whenChanged");
2055 ldb_msg_remove_attr(msg, "uSNChanged");
2057 ret = replmd_update_rpmd(module, ac->schema, msg, &ac->seq_num, t, &is_urgent);
2058 if (ret != LDB_SUCCESS) {
2059 talloc_free(ac);
2060 return ret;
2063 ret = replmd_modify_handle_linked_attribs(module, msg, ac->seq_num, t);
2064 if (ret != LDB_SUCCESS) {
2065 talloc_free(ac);
2066 return ret;
2069 /* TODO:
2070 * - replace the old object with the newly constructed one
2073 ac->is_urgent = is_urgent;
2075 ret = ldb_build_mod_req(&down_req, ldb, ac,
2076 msg,
2077 req->controls,
2078 ac, replmd_op_callback,
2079 req);
2080 if (ret != LDB_SUCCESS) {
2081 talloc_free(ac);
2082 return ret;
2084 talloc_steal(down_req, msg);
2086 /* we only change whenChanged and uSNChanged if the seq_num
2087 has changed */
2088 if (ac->seq_num != 0) {
2089 if (add_time_element(msg, "whenChanged", t) != LDB_SUCCESS) {
2090 talloc_free(ac);
2091 return ret;
2094 if (add_uint64_element(msg, "uSNChanged", ac->seq_num) != LDB_SUCCESS) {
2095 talloc_free(ac);
2096 return ret;
2100 /* go on with the call chain */
2101 return ldb_next_request(module, down_req);
2104 static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares);
2107 handle a rename request
2109 On a rename we need to do an extra ldb_modify which sets the
2110 whenChanged and uSNChanged attributes. We do this in a callback after the success.
2112 static int replmd_rename(struct ldb_module *module, struct ldb_request *req)
2114 struct ldb_context *ldb;
2115 struct replmd_replicated_request *ac;
2116 int ret;
2117 struct ldb_request *down_req;
2119 /* do not manipulate our control entries */
2120 if (ldb_dn_is_special(req->op.mod.message->dn)) {
2121 return ldb_next_request(module, req);
2124 ldb = ldb_module_get_ctx(module);
2126 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_rename\n");
2128 ac = replmd_ctx_init(module, req);
2129 if (!ac) {
2130 return LDB_ERR_OPERATIONS_ERROR;
2132 ret = ldb_build_rename_req(&down_req, ldb, ac,
2133 ac->req->op.rename.olddn,
2134 ac->req->op.rename.newdn,
2135 ac->req->controls,
2136 ac, replmd_rename_callback,
2137 ac->req);
2139 if (ret != LDB_SUCCESS) {
2140 talloc_free(ac);
2141 return ret;
2144 /* go on with the call chain */
2145 return ldb_next_request(module, down_req);
2148 /* After the rename is compleated, update the whenchanged etc */
2149 static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares)
2151 struct ldb_context *ldb;
2152 struct replmd_replicated_request *ac;
2153 struct ldb_request *down_req;
2154 struct ldb_message *msg;
2155 time_t t = time(NULL);
2156 int ret;
2158 ac = talloc_get_type(req->context, struct replmd_replicated_request);
2159 ldb = ldb_module_get_ctx(ac->module);
2161 if (ares->error != LDB_SUCCESS) {
2162 return ldb_module_done(ac->req, ares->controls,
2163 ares->response, ares->error);
2166 if (ares->type != LDB_REPLY_DONE) {
2167 ldb_set_errstring(ldb,
2168 "invalid ldb_reply_type in callback");
2169 talloc_free(ares);
2170 return ldb_module_done(ac->req, NULL, NULL,
2171 LDB_ERR_OPERATIONS_ERROR);
2174 /* Get a sequence number from the backend */
2175 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ac->seq_num);
2176 if (ret != LDB_SUCCESS) {
2177 return ret;
2180 /* TODO:
2181 * - replace the old object with the newly constructed one
2184 msg = ldb_msg_new(ac);
2185 if (msg == NULL) {
2186 ldb_oom(ldb);
2187 return LDB_ERR_OPERATIONS_ERROR;
2190 msg->dn = ac->req->op.rename.newdn;
2192 ret = ldb_build_mod_req(&down_req, ldb, ac,
2193 msg,
2194 req->controls,
2195 ac, replmd_op_callback,
2196 req);
2198 if (ret != LDB_SUCCESS) {
2199 talloc_free(ac);
2200 return ret;
2202 talloc_steal(down_req, msg);
2204 if (add_time_element(msg, "whenChanged", t) != LDB_SUCCESS) {
2205 talloc_free(ac);
2206 return ret;
2209 if (add_uint64_element(msg, "uSNChanged", ac->seq_num) != LDB_SUCCESS) {
2210 talloc_free(ac);
2211 return ret;
2214 /* go on with the call chain - do the modify after the rename */
2215 return ldb_next_request(ac->module, down_req);
2219 remove links from objects that point at this object when an object
2220 is deleted
2222 static int replmd_delete_remove_link(struct ldb_module *module,
2223 struct dsdb_schema *schema,
2224 struct ldb_dn *dn,
2225 struct ldb_message_element *el,
2226 const struct dsdb_attribute *sa)
2228 unsigned int i;
2229 TALLOC_CTX *tmp_ctx = talloc_new(module);
2230 struct ldb_context *ldb = ldb_module_get_ctx(module);
2232 for (i=0; i<el->num_values; i++) {
2233 struct dsdb_dn *dsdb_dn;
2234 NTSTATUS status;
2235 int ret;
2236 struct GUID guid2;
2237 struct ldb_message *msg;
2238 const struct dsdb_attribute *target_attr;
2239 struct ldb_message_element *el2;
2240 struct ldb_val dn_val;
2242 if (dsdb_dn_is_deleted_val(&el->values[i])) {
2243 continue;
2246 dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i], sa->syntax->ldap_oid);
2247 if (!dsdb_dn) {
2248 talloc_free(tmp_ctx);
2249 return LDB_ERR_OPERATIONS_ERROR;
2252 status = dsdb_get_extended_dn_guid(dsdb_dn->dn, &guid2, "GUID");
2253 if (!NT_STATUS_IS_OK(status)) {
2254 talloc_free(tmp_ctx);
2255 return LDB_ERR_OPERATIONS_ERROR;
2258 /* remove the link */
2259 msg = ldb_msg_new(tmp_ctx);
2260 if (!msg) {
2261 ldb_module_oom(module);
2262 talloc_free(tmp_ctx);
2263 return LDB_ERR_OPERATIONS_ERROR;
2267 msg->dn = dsdb_dn->dn;
2269 target_attr = dsdb_attribute_by_linkID(schema, sa->linkID ^ 1);
2270 if (target_attr == NULL) {
2271 continue;
2274 ret = ldb_msg_add_empty(msg, target_attr->lDAPDisplayName, LDB_FLAG_MOD_DELETE, &el2);
2275 if (ret != LDB_SUCCESS) {
2276 ldb_module_oom(module);
2277 talloc_free(tmp_ctx);
2278 return LDB_ERR_OPERATIONS_ERROR;
2280 dn_val = data_blob_string_const(ldb_dn_get_linearized(dn));
2281 el2->values = &dn_val;
2282 el2->num_values = 1;
2284 ret = dsdb_module_modify(module, msg, DSDB_FLAG_OWN_MODULE);
2285 if (ret != LDB_SUCCESS) {
2286 talloc_free(tmp_ctx);
2287 return ret;
2290 talloc_free(tmp_ctx);
2291 return LDB_SUCCESS;
2296 handle update of replication meta data for deletion of objects
2298 This also handles the mapping of delete to a rename operation
2299 to allow deletes to be replicated.
2301 static int replmd_delete(struct ldb_module *module, struct ldb_request *req)
2303 int ret = LDB_ERR_OTHER;
2304 bool retb;
2305 struct ldb_dn *old_dn, *new_dn;
2306 const char *rdn_name;
2307 const struct ldb_val *rdn_value, *new_rdn_value;
2308 struct GUID guid;
2309 struct ldb_context *ldb = ldb_module_get_ctx(module);
2310 struct dsdb_schema *schema = dsdb_get_schema(ldb);
2311 struct ldb_message *msg, *old_msg;
2312 struct ldb_message_element *el;
2313 TALLOC_CTX *tmp_ctx;
2314 struct ldb_result *res, *parent_res;
2315 const char *preserved_attrs[] = {
2316 /* yes, this really is a hard coded list. See MS-ADTS
2317 section 3.1.1.5.5.1.1 */
2318 "nTSecurityDescriptor", "attributeID", "attributeSyntax", "dNReferenceUpdate", "dNSHostName",
2319 "flatName", "governsID", "groupType", "instanceType", "lDAPDisplayName", "legacyExchangeDN",
2320 "isDeleted", "isRecycled", "lastKnownParent", "msDS-LastKnownRDN", "mS-DS-CreatorSID",
2321 "mSMQOwnerID", "nCName", "objectClass", "distinguishedName", "objectGUID", "objectSid",
2322 "oMSyntax", "proxiedObjectName", "name", "replPropertyMetaData", "sAMAccountName",
2323 "securityIdentifier", "sIDHistory", "subClassOf", "systemFlags", "trustPartner", "trustDirection",
2324 "trustType", "trustAttributes", "userAccountControl", "uSNChanged", "uSNCreated", "whenCreated",
2325 "whenChanged", NULL};
2326 unsigned int i, el_count = 0;
2328 if (ldb_dn_is_special(req->op.del.dn)) {
2329 return ldb_next_request(module, req);
2332 tmp_ctx = talloc_new(ldb);
2334 old_dn = ldb_dn_copy(tmp_ctx, req->op.del.dn);
2336 /* we need the complete msg off disk, so we can work out which
2337 attributes need to be removed */
2338 ret = dsdb_module_search_dn(module, tmp_ctx, &res, old_dn, NULL,
2339 DSDB_SEARCH_SHOW_DELETED |
2340 DSDB_SEARCH_REVEAL_INTERNALS |
2341 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT);
2342 if (ret != LDB_SUCCESS) {
2343 talloc_free(tmp_ctx);
2344 return ret;
2346 old_msg = res->msgs[0];
2348 if (ldb_msg_check_string_attribute(old_msg, "isDeleted", "TRUE")) {
2349 struct auth_session_info *session_info =
2350 (struct auth_session_info *)ldb_get_opaque(ldb, "sessionInfo");
2351 if (security_session_user_level(session_info) != SECURITY_SYSTEM) {
2352 ldb_asprintf_errstring(ldb, "Refusing to delete deleted object %s",
2353 ldb_dn_get_linearized(old_msg->dn));
2354 return LDB_ERR_UNWILLING_TO_PERFORM;
2357 /* it is already deleted - really remove it this time */
2358 talloc_free(tmp_ctx);
2359 return ldb_next_request(module, req);
2362 /* work out where we will be renaming this object to */
2363 ret = dsdb_get_deleted_objects_dn(ldb, tmp_ctx, old_dn, &new_dn);
2364 if (ret != LDB_SUCCESS) {
2365 /* this is probably an attempted delete on a partition
2366 * that doesn't allow delete operations, such as the
2367 * schema partition */
2368 ldb_asprintf_errstring(ldb, "No Deleted Objects container for DN %s",
2369 ldb_dn_get_linearized(old_dn));
2370 talloc_free(tmp_ctx);
2371 return LDB_ERR_UNWILLING_TO_PERFORM;
2374 rdn_name = ldb_dn_get_rdn_name(old_dn);
2375 rdn_value = ldb_dn_get_rdn_val(old_dn);
2377 /* get the objects GUID from the search we just did */
2378 guid = samdb_result_guid(old_msg, "objectGUID");
2380 /* Add a formatted child */
2381 retb = ldb_dn_add_child_fmt(new_dn, "%s=%s\\0ADEL:%s",
2382 rdn_name,
2383 rdn_value->data,
2384 GUID_string(tmp_ctx, &guid));
2385 if (!retb) {
2386 DEBUG(0,(__location__ ": Unable to add a formatted child to dn: %s",
2387 ldb_dn_get_linearized(new_dn)));
2388 talloc_free(tmp_ctx);
2389 return LDB_ERR_OPERATIONS_ERROR;
2393 now we need to modify the object in the following ways:
2395 - add isDeleted=TRUE
2396 - update rDN and name, with new rDN
2397 - remove linked attributes
2398 - remove objectCategory and sAMAccountType
2399 - remove attribs not on the preserved list
2400 - preserved if in above list, or is rDN
2401 - remove all linked attribs from this object
2402 - remove all links from other objects to this object
2403 - add lastKnownParent
2404 - update replPropertyMetaData?
2406 see MS-ADTS "Tombstone Requirements" section 3.1.1.5.5.1.1
2409 msg = ldb_msg_new(tmp_ctx);
2410 if (msg == NULL) {
2411 ldb_module_oom(module);
2412 talloc_free(tmp_ctx);
2413 return LDB_ERR_OPERATIONS_ERROR;
2416 msg->dn = old_dn;
2418 ret = ldb_msg_add_string(msg, "isDeleted", "TRUE");
2419 if (ret != LDB_SUCCESS) {
2420 DEBUG(0,(__location__ ": Failed to add isDeleted string to the msg\n"));
2421 ldb_module_oom(module);
2422 talloc_free(tmp_ctx);
2423 return ret;
2425 msg->elements[el_count++].flags = LDB_FLAG_MOD_ADD;
2427 /* we also mark it as recycled, meaning this object can't be
2428 recovered (we are stripping its attributes) */
2429 if (dsdb_functional_level(ldb) >= DS_DOMAIN_FUNCTION_2008_R2) {
2430 ret = ldb_msg_add_string(msg, "isRecycled", "TRUE");
2431 if (ret != LDB_SUCCESS) {
2432 DEBUG(0,(__location__ ": Failed to add isRecycled string to the msg\n"));
2433 ldb_module_oom(module);
2434 talloc_free(tmp_ctx);
2435 return ret;
2437 msg->elements[el_count++].flags = LDB_FLAG_MOD_ADD;
2440 /* we need the storage form of the parent GUID */
2441 ret = dsdb_module_search_dn(module, tmp_ctx, &parent_res,
2442 ldb_dn_get_parent(tmp_ctx, old_dn), NULL,
2443 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
2444 DSDB_SEARCH_REVEAL_INTERNALS);
2445 if (ret != LDB_SUCCESS) {
2446 talloc_free(tmp_ctx);
2447 return ret;
2450 ret = ldb_msg_add_steal_string(msg, "lastKnownParent",
2451 ldb_dn_get_extended_linearized(tmp_ctx, parent_res->msgs[0]->dn, 1));
2452 if (ret != LDB_SUCCESS) {
2453 DEBUG(0,(__location__ ": Failed to add lastKnownParent string to the msg\n"));
2454 ldb_module_oom(module);
2455 talloc_free(tmp_ctx);
2456 return ret;
2458 msg->elements[el_count++].flags = LDB_FLAG_MOD_ADD;
2460 /* work out which of the old attributes we will be removing */
2461 for (i=0; i<old_msg->num_elements; i++) {
2462 const struct dsdb_attribute *sa;
2463 el = &old_msg->elements[i];
2464 sa = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
2465 if (!sa) {
2466 talloc_free(tmp_ctx);
2467 return LDB_ERR_OPERATIONS_ERROR;
2469 if (ldb_attr_cmp(el->name, rdn_name) == 0) {
2470 /* don't remove the rDN */
2471 continue;
2474 if (sa->linkID && sa->linkID & 1) {
2475 ret = replmd_delete_remove_link(module, schema, old_dn, el, sa);
2476 if (ret != LDB_SUCCESS) {
2477 talloc_free(tmp_ctx);
2478 return LDB_ERR_OPERATIONS_ERROR;
2480 continue;
2483 if (!sa->linkID && ldb_attr_in_list(preserved_attrs, el->name)) {
2484 continue;
2487 ret = ldb_msg_add_empty(msg, el->name, LDB_FLAG_MOD_DELETE, &el);
2488 if (ret != LDB_SUCCESS) {
2489 talloc_free(tmp_ctx);
2490 ldb_module_oom(module);
2491 return ret;
2495 /* work out what the new rdn value is, for updating the
2496 rDN and name fields */
2497 new_rdn_value = ldb_dn_get_rdn_val(new_dn);
2498 ret = ldb_msg_add_value(msg, rdn_name, new_rdn_value, &el);
2499 if (ret != LDB_SUCCESS) {
2500 talloc_free(tmp_ctx);
2501 return ret;
2503 el->flags = LDB_FLAG_MOD_REPLACE;
2505 el = ldb_msg_find_element(old_msg, "name");
2506 if (el) {
2507 ret = ldb_msg_add_value(msg, "name", new_rdn_value, &el);
2508 if (ret != LDB_SUCCESS) {
2509 talloc_free(tmp_ctx);
2510 return ret;
2512 el->flags = LDB_FLAG_MOD_REPLACE;
2515 ret = dsdb_module_modify(module, msg, DSDB_FLAG_OWN_MODULE);
2516 if (ret != LDB_SUCCESS) {
2517 ldb_asprintf_errstring(ldb, "replmd_delete: Failed to modify object %s in delete - %s",
2518 ldb_dn_get_linearized(old_dn), ldb_errstring(ldb));
2519 talloc_free(tmp_ctx);
2520 return ret;
2523 /* now rename onto the new DN */
2524 ret = dsdb_module_rename(module, old_dn, new_dn, 0);
2525 if (ret != LDB_SUCCESS){
2526 DEBUG(0,(__location__ ": Failed to rename object from '%s' to '%s' - %s\n",
2527 ldb_dn_get_linearized(old_dn),
2528 ldb_dn_get_linearized(new_dn),
2529 ldb_errstring(ldb)));
2530 talloc_free(tmp_ctx);
2531 return ret;
2534 talloc_free(tmp_ctx);
2536 return ldb_module_done(req, NULL, NULL, LDB_SUCCESS);
2541 static int replmd_replicated_request_error(struct replmd_replicated_request *ar, int ret)
2543 return ret;
2546 static int replmd_replicated_request_werror(struct replmd_replicated_request *ar, WERROR status)
2548 int ret = LDB_ERR_OTHER;
2549 /* TODO: do some error mapping */
2550 return ret;
2553 static int replmd_replicated_apply_add(struct replmd_replicated_request *ar)
2555 struct ldb_context *ldb;
2556 struct ldb_request *change_req;
2557 enum ndr_err_code ndr_err;
2558 struct ldb_message *msg;
2559 struct replPropertyMetaDataBlob *md;
2560 struct ldb_val md_value;
2561 unsigned int i;
2562 int ret;
2565 * TODO: check if the parent object exist
2569 * TODO: handle the conflict case where an object with the
2570 * same name exist
2573 ldb = ldb_module_get_ctx(ar->module);
2574 msg = ar->objs->objects[ar->index_current].msg;
2575 md = ar->objs->objects[ar->index_current].meta_data;
2577 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
2578 if (ret != LDB_SUCCESS) {
2579 return replmd_replicated_request_error(ar, ret);
2582 ret = ldb_msg_add_value(msg, "objectGUID", &ar->objs->objects[ar->index_current].guid_value, NULL);
2583 if (ret != LDB_SUCCESS) {
2584 return replmd_replicated_request_error(ar, ret);
2587 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
2588 if (ret != LDB_SUCCESS) {
2589 return replmd_replicated_request_error(ar, ret);
2592 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", ar->seq_num);
2593 if (ret != LDB_SUCCESS) {
2594 return replmd_replicated_request_error(ar, ret);
2597 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
2598 if (ret != LDB_SUCCESS) {
2599 return replmd_replicated_request_error(ar, ret);
2602 /* remove any message elements that have zero values */
2603 for (i=0; i<msg->num_elements; i++) {
2604 struct ldb_message_element *el = &msg->elements[i];
2606 if (el->num_values == 0) {
2607 DEBUG(4,(__location__ ": Removing attribute %s with num_values==0\n",
2608 el->name));
2609 memmove(el, el+1, sizeof(*el)*(msg->num_elements - (i+1)));
2610 msg->num_elements--;
2611 i--;
2612 continue;
2617 * the meta data array is already sorted by the caller
2619 for (i=0; i < md->ctr.ctr1.count; i++) {
2620 md->ctr.ctr1.array[i].local_usn = ar->seq_num;
2622 ndr_err = ndr_push_struct_blob(&md_value, msg,
2623 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
2625 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
2626 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
2627 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
2628 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
2630 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &md_value, NULL);
2631 if (ret != LDB_SUCCESS) {
2632 return replmd_replicated_request_error(ar, ret);
2635 replmd_ldb_message_sort(msg, ar->schema);
2637 if (DEBUGLVL(4)) {
2638 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_ADD, msg);
2639 DEBUG(4, ("DRS replication add message:\n%s\n", s));
2640 talloc_free(s);
2643 ret = ldb_build_add_req(&change_req,
2644 ldb,
2646 msg,
2647 ar->controls,
2649 replmd_op_callback,
2650 ar->req);
2651 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
2653 return ldb_next_request(ar->module, change_req);
2657 return true if an update is newer than an existing entry
2658 see section 5.11 of MS-ADTS
2660 static bool replmd_update_is_newer(const struct GUID *current_invocation_id,
2661 const struct GUID *update_invocation_id,
2662 uint32_t current_version,
2663 uint32_t update_version,
2664 NTTIME current_change_time,
2665 NTTIME update_change_time)
2667 if (update_version != current_version) {
2668 return update_version > current_version;
2670 if (update_change_time > current_change_time) {
2671 return true;
2673 if (update_change_time == current_change_time) {
2674 return GUID_compare(update_invocation_id, current_invocation_id) > 0;
2676 return false;
2679 static bool replmd_replPropertyMetaData1_is_newer(struct replPropertyMetaData1 *cur_m,
2680 struct replPropertyMetaData1 *new_m)
2682 return replmd_update_is_newer(&cur_m->originating_invocation_id,
2683 &new_m->originating_invocation_id,
2684 cur_m->version,
2685 new_m->version,
2686 cur_m->originating_change_time,
2687 new_m->originating_change_time);
2690 static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar)
2692 struct ldb_context *ldb;
2693 struct ldb_request *change_req;
2694 enum ndr_err_code ndr_err;
2695 struct ldb_message *msg;
2696 struct replPropertyMetaDataBlob *rmd;
2697 struct replPropertyMetaDataBlob omd;
2698 const struct ldb_val *omd_value;
2699 struct replPropertyMetaDataBlob nmd;
2700 struct ldb_val nmd_value;
2701 unsigned int i;
2702 uint32_t j,ni=0;
2703 unsigned int removed_attrs = 0;
2704 int ret;
2706 ldb = ldb_module_get_ctx(ar->module);
2707 msg = ar->objs->objects[ar->index_current].msg;
2708 rmd = ar->objs->objects[ar->index_current].meta_data;
2709 ZERO_STRUCT(omd);
2710 omd.version = 1;
2713 * TODO: check repl data is correct after a rename
2715 if (ldb_dn_compare(msg->dn, ar->search_msg->dn) != 0) {
2716 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_request rename %s => %s\n",
2717 ldb_dn_get_linearized(ar->search_msg->dn),
2718 ldb_dn_get_linearized(msg->dn));
2719 if (dsdb_module_rename(ar->module,
2720 ar->search_msg->dn, msg->dn,
2721 DSDB_FLAG_OWN_MODULE) != LDB_SUCCESS) {
2722 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_replicated_request rename %s => %s failed - %s\n",
2723 ldb_dn_get_linearized(ar->search_msg->dn),
2724 ldb_dn_get_linearized(msg->dn),
2725 ldb_errstring(ldb));
2726 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
2730 /* find existing meta data */
2731 omd_value = ldb_msg_find_ldb_val(ar->search_msg, "replPropertyMetaData");
2732 if (omd_value) {
2733 ndr_err = ndr_pull_struct_blob(omd_value, ar,
2734 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")), &omd,
2735 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
2736 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
2737 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
2738 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
2741 if (omd.version != 1) {
2742 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
2746 ZERO_STRUCT(nmd);
2747 nmd.version = 1;
2748 nmd.ctr.ctr1.count = omd.ctr.ctr1.count + rmd->ctr.ctr1.count;
2749 nmd.ctr.ctr1.array = talloc_array(ar,
2750 struct replPropertyMetaData1,
2751 nmd.ctr.ctr1.count);
2752 if (!nmd.ctr.ctr1.array) return replmd_replicated_request_werror(ar, WERR_NOMEM);
2754 /* first copy the old meta data */
2755 for (i=0; i < omd.ctr.ctr1.count; i++) {
2756 nmd.ctr.ctr1.array[ni] = omd.ctr.ctr1.array[i];
2757 ni++;
2760 /* now merge in the new meta data */
2761 for (i=0; i < rmd->ctr.ctr1.count; i++) {
2762 bool found = false;
2764 for (j=0; j < ni; j++) {
2765 bool cmp;
2767 if (rmd->ctr.ctr1.array[i].attid != nmd.ctr.ctr1.array[j].attid) {
2768 continue;
2771 cmp = replmd_replPropertyMetaData1_is_newer(&nmd.ctr.ctr1.array[j],
2772 &rmd->ctr.ctr1.array[i]);
2773 if (cmp) {
2774 /* replace the entry */
2775 nmd.ctr.ctr1.array[j] = rmd->ctr.ctr1.array[i];
2776 found = true;
2777 break;
2780 if (rmd->ctr.ctr1.array[i].attid != DRSUAPI_ATTRIBUTE_instanceType) {
2781 DEBUG(1,("Discarding older DRS attribute update to %s on %s from %s\n",
2782 msg->elements[i-removed_attrs].name,
2783 ldb_dn_get_linearized(msg->dn),
2784 GUID_string(ar, &rmd->ctr.ctr1.array[i].originating_invocation_id)));
2787 /* we don't want to apply this change so remove the attribute */
2788 ldb_msg_remove_element(msg, &msg->elements[i-removed_attrs]);
2789 removed_attrs++;
2791 found = true;
2792 break;
2795 if (found) continue;
2797 nmd.ctr.ctr1.array[ni] = rmd->ctr.ctr1.array[i];
2798 ni++;
2802 * finally correct the size of the meta_data array
2804 nmd.ctr.ctr1.count = ni;
2807 * the rdn attribute (the alias for the name attribute),
2808 * 'cn' for most objects is the last entry in the meta data array
2809 * we have stored
2811 * sort the new meta data array
2813 ret = replmd_replPropertyMetaDataCtr1_sort(&nmd.ctr.ctr1, ar->schema, msg->dn);
2814 if (ret != LDB_SUCCESS) {
2815 return ret;
2819 * check if some replicated attributes left, otherwise skip the ldb_modify() call
2821 if (msg->num_elements == 0) {
2822 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: skip replace\n",
2823 ar->index_current);
2825 ar->index_current++;
2826 return replmd_replicated_apply_next(ar);
2829 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: replace %u attributes\n",
2830 ar->index_current, msg->num_elements);
2832 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
2833 if (ret != LDB_SUCCESS) {
2834 return replmd_replicated_request_error(ar, ret);
2837 for (i=0; i<ni; i++) {
2838 nmd.ctr.ctr1.array[i].local_usn = ar->seq_num;
2841 /* create the meta data value */
2842 ndr_err = ndr_push_struct_blob(&nmd_value, msg,
2843 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
2844 &nmd,
2845 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
2846 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
2847 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
2848 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
2852 * when we know that we'll modify the record, add the whenChanged, uSNChanged
2853 * and replPopertyMetaData attributes
2855 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
2856 if (ret != LDB_SUCCESS) {
2857 return replmd_replicated_request_error(ar, ret);
2859 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
2860 if (ret != LDB_SUCCESS) {
2861 return replmd_replicated_request_error(ar, ret);
2863 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
2864 if (ret != LDB_SUCCESS) {
2865 return replmd_replicated_request_error(ar, ret);
2868 replmd_ldb_message_sort(msg, ar->schema);
2870 /* we want to replace the old values */
2871 for (i=0; i < msg->num_elements; i++) {
2872 msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
2875 if (DEBUGLVL(4)) {
2876 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_MODIFY, msg);
2877 DEBUG(4, ("DRS replication modify message:\n%s\n", s));
2878 talloc_free(s);
2881 ret = ldb_build_mod_req(&change_req,
2882 ldb,
2884 msg,
2885 ar->controls,
2887 replmd_op_callback,
2888 ar->req);
2889 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
2891 return ldb_next_request(ar->module, change_req);
2894 static int replmd_replicated_apply_search_callback(struct ldb_request *req,
2895 struct ldb_reply *ares)
2897 struct replmd_replicated_request *ar = talloc_get_type(req->context,
2898 struct replmd_replicated_request);
2899 int ret;
2901 if (!ares) {
2902 return ldb_module_done(ar->req, NULL, NULL,
2903 LDB_ERR_OPERATIONS_ERROR);
2905 if (ares->error != LDB_SUCCESS &&
2906 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
2907 return ldb_module_done(ar->req, ares->controls,
2908 ares->response, ares->error);
2911 switch (ares->type) {
2912 case LDB_REPLY_ENTRY:
2913 ar->search_msg = talloc_steal(ar, ares->message);
2914 break;
2916 case LDB_REPLY_REFERRAL:
2917 /* we ignore referrals */
2918 break;
2920 case LDB_REPLY_DONE:
2921 if (ar->search_msg != NULL) {
2922 ret = replmd_replicated_apply_merge(ar);
2923 } else {
2924 ret = replmd_replicated_apply_add(ar);
2926 if (ret != LDB_SUCCESS) {
2927 return ldb_module_done(ar->req, NULL, NULL, ret);
2931 talloc_free(ares);
2932 return LDB_SUCCESS;
2935 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar);
2937 static int replmd_replicated_apply_next(struct replmd_replicated_request *ar)
2939 struct ldb_context *ldb;
2940 int ret;
2941 char *tmp_str;
2942 char *filter;
2943 struct ldb_request *search_req;
2944 struct ldb_search_options_control *options;
2946 if (ar->index_current >= ar->objs->num_objects) {
2947 /* done with it, go to next stage */
2948 return replmd_replicated_uptodate_vector(ar);
2951 ldb = ldb_module_get_ctx(ar->module);
2952 ar->search_msg = NULL;
2954 tmp_str = ldb_binary_encode(ar, ar->objs->objects[ar->index_current].guid_value);
2955 if (!tmp_str) return replmd_replicated_request_werror(ar, WERR_NOMEM);
2957 filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
2958 if (!filter) return replmd_replicated_request_werror(ar, WERR_NOMEM);
2959 talloc_free(tmp_str);
2961 ret = ldb_build_search_req(&search_req,
2962 ldb,
2964 NULL,
2965 LDB_SCOPE_SUBTREE,
2966 filter,
2967 NULL,
2968 NULL,
2970 replmd_replicated_apply_search_callback,
2971 ar->req);
2973 ret = ldb_request_add_control(search_req, LDB_CONTROL_SHOW_DELETED_OID, true, NULL);
2974 if (ret != LDB_SUCCESS) {
2975 return ret;
2978 /* we need to cope with cross-partition links, so search for
2979 the GUID over all partitions */
2980 options = talloc(search_req, struct ldb_search_options_control);
2981 if (options == NULL) {
2982 DEBUG(0, (__location__ ": out of memory\n"));
2983 return LDB_ERR_OPERATIONS_ERROR;
2985 options->search_options = LDB_SEARCH_OPTION_PHANTOM_ROOT;
2987 ret = ldb_request_add_control(search_req,
2988 LDB_CONTROL_SEARCH_OPTIONS_OID,
2989 true, options);
2990 if (ret != LDB_SUCCESS) {
2991 return ret;
2994 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
2996 return ldb_next_request(ar->module, search_req);
2999 static int replmd_replicated_uptodate_modify_callback(struct ldb_request *req,
3000 struct ldb_reply *ares)
3002 struct ldb_context *ldb;
3003 struct replmd_replicated_request *ar = talloc_get_type(req->context,
3004 struct replmd_replicated_request);
3005 ldb = ldb_module_get_ctx(ar->module);
3007 if (!ares) {
3008 return ldb_module_done(ar->req, NULL, NULL,
3009 LDB_ERR_OPERATIONS_ERROR);
3011 if (ares->error != LDB_SUCCESS) {
3012 return ldb_module_done(ar->req, ares->controls,
3013 ares->response, ares->error);
3016 if (ares->type != LDB_REPLY_DONE) {
3017 ldb_set_errstring(ldb, "Invalid reply type\n!");
3018 return ldb_module_done(ar->req, NULL, NULL,
3019 LDB_ERR_OPERATIONS_ERROR);
3022 talloc_free(ares);
3024 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
3027 static int replmd_replicated_uptodate_modify(struct replmd_replicated_request *ar)
3029 struct ldb_context *ldb;
3030 struct ldb_request *change_req;
3031 enum ndr_err_code ndr_err;
3032 struct ldb_message *msg;
3033 struct replUpToDateVectorBlob ouv;
3034 const struct ldb_val *ouv_value;
3035 const struct drsuapi_DsReplicaCursor2CtrEx *ruv;
3036 struct replUpToDateVectorBlob nuv;
3037 struct ldb_val nuv_value;
3038 struct ldb_message_element *nuv_el = NULL;
3039 const struct GUID *our_invocation_id;
3040 struct ldb_message_element *orf_el = NULL;
3041 struct repsFromToBlob nrf;
3042 struct ldb_val *nrf_value = NULL;
3043 struct ldb_message_element *nrf_el = NULL;
3044 unsigned int i;
3045 uint32_t j,ni=0;
3046 bool found = false;
3047 time_t t = time(NULL);
3048 NTTIME now;
3049 int ret;
3051 ldb = ldb_module_get_ctx(ar->module);
3052 ruv = ar->objs->uptodateness_vector;
3053 ZERO_STRUCT(ouv);
3054 ouv.version = 2;
3055 ZERO_STRUCT(nuv);
3056 nuv.version = 2;
3058 unix_to_nt_time(&now, t);
3061 * first create the new replUpToDateVector
3063 ouv_value = ldb_msg_find_ldb_val(ar->search_msg, "replUpToDateVector");
3064 if (ouv_value) {
3065 ndr_err = ndr_pull_struct_blob(ouv_value, ar,
3066 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")), &ouv,
3067 (ndr_pull_flags_fn_t)ndr_pull_replUpToDateVectorBlob);
3068 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
3069 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
3070 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
3073 if (ouv.version != 2) {
3074 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
3079 * the new uptodateness vector will at least
3080 * contain 1 entry, one for the source_dsa
3082 * plus optional values from our old vector and the one from the source_dsa
3084 nuv.ctr.ctr2.count = 1 + ouv.ctr.ctr2.count;
3085 if (ruv) nuv.ctr.ctr2.count += ruv->count;
3086 nuv.ctr.ctr2.cursors = talloc_array(ar,
3087 struct drsuapi_DsReplicaCursor2,
3088 nuv.ctr.ctr2.count);
3089 if (!nuv.ctr.ctr2.cursors) return replmd_replicated_request_werror(ar, WERR_NOMEM);
3091 /* first copy the old vector */
3092 for (i=0; i < ouv.ctr.ctr2.count; i++) {
3093 nuv.ctr.ctr2.cursors[ni] = ouv.ctr.ctr2.cursors[i];
3094 ni++;
3097 /* get our invocation_id if we have one already attached to the ldb */
3098 our_invocation_id = samdb_ntds_invocation_id(ldb);
3100 /* merge in the source_dsa vector is available */
3101 for (i=0; (ruv && i < ruv->count); i++) {
3102 found = false;
3104 if (our_invocation_id &&
3105 GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
3106 our_invocation_id)) {
3107 continue;
3110 for (j=0; j < ni; j++) {
3111 if (!GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
3112 &nuv.ctr.ctr2.cursors[j].source_dsa_invocation_id)) {
3113 continue;
3116 found = true;
3119 * we update only the highest_usn and not the latest_sync_success time,
3120 * because the last success stands for direct replication
3122 if (ruv->cursors[i].highest_usn > nuv.ctr.ctr2.cursors[j].highest_usn) {
3123 nuv.ctr.ctr2.cursors[j].highest_usn = ruv->cursors[i].highest_usn;
3125 break;
3128 if (found) continue;
3130 /* if it's not there yet, add it */
3131 nuv.ctr.ctr2.cursors[ni] = ruv->cursors[i];
3132 ni++;
3136 * merge in the current highwatermark for the source_dsa
3138 found = false;
3139 for (j=0; j < ni; j++) {
3140 if (!GUID_equal(&ar->objs->source_dsa->source_dsa_invocation_id,
3141 &nuv.ctr.ctr2.cursors[j].source_dsa_invocation_id)) {
3142 continue;
3145 found = true;
3148 * here we update the highest_usn and last_sync_success time
3149 * because we're directly replicating from the source_dsa
3151 * and use the tmp_highest_usn because this is what we have just applied
3152 * to our ldb
3154 nuv.ctr.ctr2.cursors[j].highest_usn = ar->objs->source_dsa->highwatermark.tmp_highest_usn;
3155 nuv.ctr.ctr2.cursors[j].last_sync_success = now;
3156 break;
3158 if (!found) {
3160 * here we update the highest_usn and last_sync_success time
3161 * because we're directly replicating from the source_dsa
3163 * and use the tmp_highest_usn because this is what we have just applied
3164 * to our ldb
3166 nuv.ctr.ctr2.cursors[ni].source_dsa_invocation_id= ar->objs->source_dsa->source_dsa_invocation_id;
3167 nuv.ctr.ctr2.cursors[ni].highest_usn = ar->objs->source_dsa->highwatermark.tmp_highest_usn;
3168 nuv.ctr.ctr2.cursors[ni].last_sync_success = now;
3169 ni++;
3173 * finally correct the size of the cursors array
3175 nuv.ctr.ctr2.count = ni;
3178 * sort the cursors
3180 TYPESAFE_QSORT(nuv.ctr.ctr2.cursors, nuv.ctr.ctr2.count, drsuapi_DsReplicaCursor2_compare);
3183 * create the change ldb_message
3185 msg = ldb_msg_new(ar);
3186 if (!msg) return replmd_replicated_request_werror(ar, WERR_NOMEM);
3187 msg->dn = ar->search_msg->dn;
3189 ndr_err = ndr_push_struct_blob(&nuv_value, msg,
3190 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
3191 &nuv,
3192 (ndr_push_flags_fn_t)ndr_push_replUpToDateVectorBlob);
3193 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
3194 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
3195 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
3197 ret = ldb_msg_add_value(msg, "replUpToDateVector", &nuv_value, &nuv_el);
3198 if (ret != LDB_SUCCESS) {
3199 return replmd_replicated_request_error(ar, ret);
3201 nuv_el->flags = LDB_FLAG_MOD_REPLACE;
3204 * now create the new repsFrom value from the given repsFromTo1 structure
3206 ZERO_STRUCT(nrf);
3207 nrf.version = 1;
3208 nrf.ctr.ctr1 = *ar->objs->source_dsa;
3209 /* and fix some values... */
3210 nrf.ctr.ctr1.consecutive_sync_failures = 0;
3211 nrf.ctr.ctr1.last_success = now;
3212 nrf.ctr.ctr1.last_attempt = now;
3213 nrf.ctr.ctr1.result_last_attempt = WERR_OK;
3214 nrf.ctr.ctr1.highwatermark.highest_usn = nrf.ctr.ctr1.highwatermark.tmp_highest_usn;
3217 * first see if we already have a repsFrom value for the current source dsa
3218 * if so we'll later replace this value
3220 orf_el = ldb_msg_find_element(ar->search_msg, "repsFrom");
3221 if (orf_el) {
3222 for (i=0; i < orf_el->num_values; i++) {
3223 struct repsFromToBlob *trf;
3225 trf = talloc(ar, struct repsFromToBlob);
3226 if (!trf) return replmd_replicated_request_werror(ar, WERR_NOMEM);
3228 ndr_err = ndr_pull_struct_blob(&orf_el->values[i], trf, lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")), trf,
3229 (ndr_pull_flags_fn_t)ndr_pull_repsFromToBlob);
3230 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
3231 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
3232 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
3235 if (trf->version != 1) {
3236 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
3240 * we compare the source dsa objectGUID not the invocation_id
3241 * because we want only one repsFrom value per source dsa
3242 * and when the invocation_id of the source dsa has changed we don't need
3243 * the old repsFrom with the old invocation_id
3245 if (!GUID_equal(&trf->ctr.ctr1.source_dsa_obj_guid,
3246 &ar->objs->source_dsa->source_dsa_obj_guid)) {
3247 talloc_free(trf);
3248 continue;
3251 talloc_free(trf);
3252 nrf_value = &orf_el->values[i];
3253 break;
3257 * copy over all old values to the new ldb_message
3259 ret = ldb_msg_add_empty(msg, "repsFrom", 0, &nrf_el);
3260 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
3261 *nrf_el = *orf_el;
3265 * if we haven't found an old repsFrom value for the current source dsa
3266 * we'll add a new value
3268 if (!nrf_value) {
3269 struct ldb_val zero_value;
3270 ZERO_STRUCT(zero_value);
3271 ret = ldb_msg_add_value(msg, "repsFrom", &zero_value, &nrf_el);
3272 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
3274 nrf_value = &nrf_el->values[nrf_el->num_values - 1];
3277 /* we now fill the value which is already attached to ldb_message */
3278 ndr_err = ndr_push_struct_blob(nrf_value, msg,
3279 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
3280 &nrf,
3281 (ndr_push_flags_fn_t)ndr_push_repsFromToBlob);
3282 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
3283 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
3284 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
3288 * the ldb_message_element for the attribute, has all the old values and the new one
3289 * so we'll replace the whole attribute with all values
3291 nrf_el->flags = LDB_FLAG_MOD_REPLACE;
3293 if (DEBUGLVL(4)) {
3294 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_MODIFY, msg);
3295 DEBUG(4, ("DRS replication uptodate modify message:\n%s\n", s));
3296 talloc_free(s);
3299 /* prepare the ldb_modify() request */
3300 ret = ldb_build_mod_req(&change_req,
3301 ldb,
3303 msg,
3304 ar->controls,
3306 replmd_replicated_uptodate_modify_callback,
3307 ar->req);
3308 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
3310 return ldb_next_request(ar->module, change_req);
3313 static int replmd_replicated_uptodate_search_callback(struct ldb_request *req,
3314 struct ldb_reply *ares)
3316 struct replmd_replicated_request *ar = talloc_get_type(req->context,
3317 struct replmd_replicated_request);
3318 int ret;
3320 if (!ares) {
3321 return ldb_module_done(ar->req, NULL, NULL,
3322 LDB_ERR_OPERATIONS_ERROR);
3324 if (ares->error != LDB_SUCCESS &&
3325 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
3326 return ldb_module_done(ar->req, ares->controls,
3327 ares->response, ares->error);
3330 switch (ares->type) {
3331 case LDB_REPLY_ENTRY:
3332 ar->search_msg = talloc_steal(ar, ares->message);
3333 break;
3335 case LDB_REPLY_REFERRAL:
3336 /* we ignore referrals */
3337 break;
3339 case LDB_REPLY_DONE:
3340 if (ar->search_msg == NULL) {
3341 ret = replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
3342 } else {
3343 ret = replmd_replicated_uptodate_modify(ar);
3345 if (ret != LDB_SUCCESS) {
3346 return ldb_module_done(ar->req, NULL, NULL, ret);
3350 talloc_free(ares);
3351 return LDB_SUCCESS;
3355 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar)
3357 struct ldb_context *ldb;
3358 int ret;
3359 static const char *attrs[] = {
3360 "replUpToDateVector",
3361 "repsFrom",
3362 NULL
3364 struct ldb_request *search_req;
3366 ldb = ldb_module_get_ctx(ar->module);
3367 ar->search_msg = NULL;
3369 ret = ldb_build_search_req(&search_req,
3370 ldb,
3372 ar->objs->partition_dn,
3373 LDB_SCOPE_BASE,
3374 "(objectClass=*)",
3375 attrs,
3376 NULL,
3378 replmd_replicated_uptodate_search_callback,
3379 ar->req);
3380 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
3382 return ldb_next_request(ar->module, search_req);
3387 static int replmd_extended_replicated_objects(struct ldb_module *module, struct ldb_request *req)
3389 struct ldb_context *ldb;
3390 struct dsdb_extended_replicated_objects *objs;
3391 struct replmd_replicated_request *ar;
3392 struct ldb_control **ctrls;
3393 int ret;
3394 uint32_t i;
3395 struct replmd_private *replmd_private =
3396 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
3398 ldb = ldb_module_get_ctx(module);
3400 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_extended_replicated_objects\n");
3402 objs = talloc_get_type(req->op.extended.data, struct dsdb_extended_replicated_objects);
3403 if (!objs) {
3404 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: invalid extended data\n");
3405 return LDB_ERR_PROTOCOL_ERROR;
3408 if (objs->version != DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION) {
3409 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: extended data invalid version [%u != %u]\n",
3410 objs->version, DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION);
3411 return LDB_ERR_PROTOCOL_ERROR;
3414 ar = replmd_ctx_init(module, req);
3415 if (!ar)
3416 return LDB_ERR_OPERATIONS_ERROR;
3418 /* Set the flags to have the replmd_op_callback run over the full set of objects */
3419 ar->apply_mode = true;
3420 ar->objs = objs;
3421 ar->schema = dsdb_get_schema(ldb);
3422 if (!ar->schema) {
3423 ldb_debug_set(ldb, LDB_DEBUG_FATAL, "replmd_ctx_init: no loaded schema found\n");
3424 talloc_free(ar);
3425 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
3426 return LDB_ERR_CONSTRAINT_VIOLATION;
3429 ctrls = req->controls;
3431 if (req->controls) {
3432 req->controls = talloc_memdup(ar, req->controls,
3433 talloc_get_size(req->controls));
3434 if (!req->controls) return replmd_replicated_request_werror(ar, WERR_NOMEM);
3437 ret = ldb_request_add_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID, false, NULL);
3438 if (ret != LDB_SUCCESS) {
3439 return ret;
3442 ar->controls = req->controls;
3443 req->controls = ctrls;
3445 DEBUG(4,("linked_attributes_count=%u\n", objs->linked_attributes_count));
3447 /* save away the linked attributes for the end of the
3448 transaction */
3449 for (i=0; i<ar->objs->linked_attributes_count; i++) {
3450 struct la_entry *la_entry;
3452 if (replmd_private->la_ctx == NULL) {
3453 replmd_private->la_ctx = talloc_new(replmd_private);
3455 la_entry = talloc(replmd_private->la_ctx, struct la_entry);
3456 if (la_entry == NULL) {
3457 ldb_oom(ldb);
3458 return LDB_ERR_OPERATIONS_ERROR;
3460 la_entry->la = talloc(la_entry, struct drsuapi_DsReplicaLinkedAttribute);
3461 if (la_entry->la == NULL) {
3462 talloc_free(la_entry);
3463 ldb_oom(ldb);
3464 return LDB_ERR_OPERATIONS_ERROR;
3466 *la_entry->la = ar->objs->linked_attributes[i];
3468 /* we need to steal the non-scalars so they stay
3469 around until the end of the transaction */
3470 talloc_steal(la_entry->la, la_entry->la->identifier);
3471 talloc_steal(la_entry->la, la_entry->la->value.blob);
3473 DLIST_ADD(replmd_private->la_list, la_entry);
3476 return replmd_replicated_apply_next(ar);
3480 process one linked attribute structure
3482 static int replmd_process_linked_attribute(struct ldb_module *module,
3483 struct la_entry *la_entry)
3485 struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
3486 struct ldb_context *ldb = ldb_module_get_ctx(module);
3487 struct dsdb_schema *schema = dsdb_get_schema(ldb);
3488 struct ldb_message *msg;
3489 TALLOC_CTX *tmp_ctx = talloc_new(la_entry);
3490 int ret;
3491 const struct dsdb_attribute *attr;
3492 struct dsdb_dn *dsdb_dn;
3493 uint64_t seq_num = 0;
3494 struct ldb_message_element *old_el;
3495 WERROR status;
3496 time_t t = time(NULL);
3497 struct ldb_result *res;
3498 const char *attrs[2];
3499 struct parsed_dn *pdn_list, *pdn;
3500 struct GUID guid = GUID_zero();
3501 NTSTATUS ntstatus;
3502 bool active = (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)?true:false;
3503 const struct GUID *our_invocation_id;
3506 linked_attributes[0]:
3507 &objs->linked_attributes[i]: struct drsuapi_DsReplicaLinkedAttribute
3508 identifier : *
3509 identifier: struct drsuapi_DsReplicaObjectIdentifier
3510 __ndr_size : 0x0000003a (58)
3511 __ndr_size_sid : 0x00000000 (0)
3512 guid : 8e95b6a9-13dd-4158-89db-3220a5be5cc7
3513 sid : S-0-0
3514 __ndr_size_dn : 0x00000000 (0)
3515 dn : ''
3516 attid : DRSUAPI_ATTRIBUTE_member (0x1F)
3517 value: struct drsuapi_DsAttributeValue
3518 __ndr_size : 0x0000007e (126)
3519 blob : *
3520 blob : DATA_BLOB length=126
3521 flags : 0x00000001 (1)
3522 1: DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE
3523 originating_add_time : Wed Sep 2 22:20:01 2009 EST
3524 meta_data: struct drsuapi_DsReplicaMetaData
3525 version : 0x00000015 (21)
3526 originating_change_time : Wed Sep 2 23:39:07 2009 EST
3527 originating_invocation_id: 794640f3-18cf-40ee-a211-a93992b67a64
3528 originating_usn : 0x000000000001e19c (123292)
3530 (for cases where the link is to a normal DN)
3531 &target: struct drsuapi_DsReplicaObjectIdentifier3
3532 __ndr_size : 0x0000007e (126)
3533 __ndr_size_sid : 0x0000001c (28)
3534 guid : 7639e594-db75-4086-b0d4-67890ae46031
3535 sid : S-1-5-21-2848215498-2472035911-1947525656-19924
3536 __ndr_size_dn : 0x00000022 (34)
3537 dn : 'CN=UOne,OU=TestOU,DC=vsofs8,DC=com'
3540 /* find the attribute being modified */
3541 attr = dsdb_attribute_by_attributeID_id(schema, la->attid);
3542 if (attr == NULL) {
3543 DEBUG(0, (__location__ ": Unable to find attributeID 0x%x\n", la->attid));
3544 talloc_free(tmp_ctx);
3545 return LDB_ERR_OPERATIONS_ERROR;
3548 attrs[0] = attr->lDAPDisplayName;
3549 attrs[1] = NULL;
3551 /* get the existing message from the db for the object with
3552 this GUID, returning attribute being modified. We will then
3553 use this msg as the basis for a modify call */
3554 ret = dsdb_module_search(module, tmp_ctx, &res, NULL, LDB_SCOPE_SUBTREE, attrs,
3555 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
3556 DSDB_SEARCH_SHOW_DELETED |
3557 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
3558 DSDB_SEARCH_REVEAL_INTERNALS,
3559 "objectGUID=%s", GUID_string(tmp_ctx, &la->identifier->guid));
3560 if (ret != LDB_SUCCESS) {
3561 talloc_free(tmp_ctx);
3562 return ret;
3564 if (res->count != 1) {
3565 ldb_asprintf_errstring(ldb, "DRS linked attribute for GUID %s - DN not found",
3566 GUID_string(tmp_ctx, &la->identifier->guid));
3567 talloc_free(tmp_ctx);
3568 return LDB_ERR_NO_SUCH_OBJECT;
3570 msg = res->msgs[0];
3572 if (msg->num_elements == 0) {
3573 ret = ldb_msg_add_empty(msg, attr->lDAPDisplayName, LDB_FLAG_MOD_REPLACE, &old_el);
3574 if (ret != LDB_SUCCESS) {
3575 ldb_module_oom(module);
3576 talloc_free(tmp_ctx);
3577 return LDB_ERR_OPERATIONS_ERROR;
3579 } else {
3580 old_el = &msg->elements[0];
3581 old_el->flags = LDB_FLAG_MOD_REPLACE;
3584 /* parse the existing links */
3585 ret = get_parsed_dns(module, tmp_ctx, old_el, &pdn_list, attr->syntax->ldap_oid);
3586 if (ret != LDB_SUCCESS) {
3587 talloc_free(tmp_ctx);
3588 return ret;
3591 /* get our invocationId */
3592 our_invocation_id = samdb_ntds_invocation_id(ldb);
3593 if (!our_invocation_id) {
3594 ldb_debug_set(ldb, LDB_DEBUG_ERROR, __location__ ": unable to find invocationId\n");
3595 talloc_free(tmp_ctx);
3596 return LDB_ERR_OPERATIONS_ERROR;
3599 ret = replmd_check_upgrade_links(pdn_list, old_el->num_values, our_invocation_id);
3600 if (ret != LDB_SUCCESS) {
3601 talloc_free(tmp_ctx);
3602 return ret;
3605 status = dsdb_dn_la_from_blob(ldb, attr, schema, tmp_ctx, la->value.blob, &dsdb_dn);
3606 if (!W_ERROR_IS_OK(status)) {
3607 ldb_asprintf_errstring(ldb, "Failed to parsed linked attribute blob for %s on %s - %s\n",
3608 old_el->name, ldb_dn_get_linearized(msg->dn), win_errstr(status));
3609 return LDB_ERR_OPERATIONS_ERROR;
3612 ntstatus = dsdb_get_extended_dn_guid(dsdb_dn->dn, &guid, "GUID");
3613 if (!NT_STATUS_IS_OK(ntstatus) && active) {
3614 ldb_asprintf_errstring(ldb, "Failed to find GUID in linked attribute blob for %s on %s from %s",
3615 old_el->name,
3616 ldb_dn_get_linearized(dsdb_dn->dn),
3617 ldb_dn_get_linearized(msg->dn));
3618 return LDB_ERR_OPERATIONS_ERROR;
3621 /* re-resolve the DN by GUID, as the DRS server may give us an
3622 old DN value */
3623 ret = dsdb_module_dn_by_guid(module, dsdb_dn, &guid, &dsdb_dn->dn);
3624 if (ret != LDB_SUCCESS) {
3625 ldb_asprintf_errstring(ldb, __location__ ": Failed to re-resolve GUID %s",
3626 GUID_string(tmp_ctx, &guid));
3627 talloc_free(tmp_ctx);
3628 return ret;
3631 /* see if this link already exists */
3632 pdn = parsed_dn_find(pdn_list, old_el->num_values, &guid, dsdb_dn->dn);
3633 if (pdn != NULL) {
3634 /* see if this update is newer than what we have already */
3635 struct GUID invocation_id = GUID_zero();
3636 uint32_t version = 0;
3637 NTTIME change_time = 0;
3638 uint32_t rmd_flags = dsdb_dn_rmd_flags(pdn->dsdb_dn->dn);
3640 dsdb_get_extended_dn_guid(pdn->dsdb_dn->dn, &invocation_id, "RMD_INVOCID");
3641 dsdb_get_extended_dn_uint32(pdn->dsdb_dn->dn, &version, "RMD_VERSION");
3642 dsdb_get_extended_dn_nttime(pdn->dsdb_dn->dn, &change_time, "RMD_CHANGETIME");
3644 if (!replmd_update_is_newer(&invocation_id,
3645 &la->meta_data.originating_invocation_id,
3646 version,
3647 la->meta_data.version,
3648 change_time,
3649 la->meta_data.originating_change_time)) {
3650 DEBUG(1,("Discarding older DRS linked attribute update to %s on %s from %s\n",
3651 old_el->name, ldb_dn_get_linearized(msg->dn),
3652 GUID_string(tmp_ctx, &la->meta_data.originating_invocation_id)));
3653 talloc_free(tmp_ctx);
3654 return LDB_SUCCESS;
3657 /* get a seq_num for this change */
3658 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
3659 if (ret != LDB_SUCCESS) {
3660 talloc_free(tmp_ctx);
3661 return ret;
3664 if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
3665 /* remove the existing backlink */
3666 ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid, false, attr, false);
3667 if (ret != LDB_SUCCESS) {
3668 talloc_free(tmp_ctx);
3669 return ret;
3673 ret = replmd_update_la_val(tmp_ctx, pdn->v, dsdb_dn, pdn->dsdb_dn,
3674 &la->meta_data.originating_invocation_id,
3675 la->meta_data.originating_usn, seq_num,
3676 la->meta_data.originating_change_time,
3677 la->meta_data.version,
3678 !active);
3679 if (ret != LDB_SUCCESS) {
3680 talloc_free(tmp_ctx);
3681 return ret;
3684 if (active) {
3685 /* add the new backlink */
3686 ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid, true, attr, false);
3687 if (ret != LDB_SUCCESS) {
3688 talloc_free(tmp_ctx);
3689 return ret;
3692 } else {
3693 /* get a seq_num for this change */
3694 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
3695 if (ret != LDB_SUCCESS) {
3696 talloc_free(tmp_ctx);
3697 return ret;
3700 old_el->values = talloc_realloc(msg->elements, old_el->values,
3701 struct ldb_val, old_el->num_values+1);
3702 if (!old_el->values) {
3703 ldb_module_oom(module);
3704 return LDB_ERR_OPERATIONS_ERROR;
3706 old_el->num_values++;
3708 ret = replmd_build_la_val(tmp_ctx, &old_el->values[old_el->num_values-1], dsdb_dn,
3709 &la->meta_data.originating_invocation_id,
3710 la->meta_data.originating_usn, seq_num,
3711 la->meta_data.originating_change_time,
3712 la->meta_data.version,
3713 (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)?false:true);
3714 if (ret != LDB_SUCCESS) {
3715 talloc_free(tmp_ctx);
3716 return ret;
3719 if (active) {
3720 ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid,
3721 true, attr, false);
3722 if (ret != LDB_SUCCESS) {
3723 talloc_free(tmp_ctx);
3724 return ret;
3729 /* we only change whenChanged and uSNChanged if the seq_num
3730 has changed */
3731 if (add_time_element(msg, "whenChanged", t) != LDB_SUCCESS) {
3732 talloc_free(tmp_ctx);
3733 return LDB_ERR_OPERATIONS_ERROR;
3736 if (add_uint64_element(msg, "uSNChanged", seq_num) != LDB_SUCCESS) {
3737 talloc_free(tmp_ctx);
3738 return LDB_ERR_OPERATIONS_ERROR;
3741 ret = dsdb_check_single_valued_link(attr, old_el);
3742 if (ret != LDB_SUCCESS) {
3743 talloc_free(tmp_ctx);
3744 return ret;
3747 ret = dsdb_module_modify(module, msg, DSDB_MODIFY_RELAX);
3748 if (ret != LDB_SUCCESS) {
3749 ldb_debug(ldb, LDB_DEBUG_WARNING, "Failed to apply linked attribute change '%s'\n%s\n",
3750 ldb_errstring(ldb),
3751 ldb_ldif_message_string(ldb, tmp_ctx, LDB_CHANGETYPE_MODIFY, msg));
3752 talloc_free(tmp_ctx);
3753 return ret;
3756 talloc_free(tmp_ctx);
3758 return ret;
3761 static int replmd_extended(struct ldb_module *module, struct ldb_request *req)
3763 if (strcmp(req->op.extended.oid, DSDB_EXTENDED_REPLICATED_OBJECTS_OID) == 0) {
3764 return replmd_extended_replicated_objects(module, req);
3767 return ldb_next_request(module, req);
3772 we hook into the transaction operations to allow us to
3773 perform the linked attribute updates at the end of the whole
3774 transaction. This allows a forward linked attribute to be created
3775 before the object is created. During a vampire, w2k8 sends us linked
3776 attributes before the objects they are part of.
3778 static int replmd_start_transaction(struct ldb_module *module)
3780 /* create our private structure for this transaction */
3781 struct replmd_private *replmd_private = talloc_get_type(ldb_module_get_private(module),
3782 struct replmd_private);
3783 replmd_txn_cleanup(replmd_private);
3785 /* free any leftover mod_usn records from cancelled
3786 transactions */
3787 while (replmd_private->ncs) {
3788 struct nc_entry *e = replmd_private->ncs;
3789 DLIST_REMOVE(replmd_private->ncs, e);
3790 talloc_free(e);
3793 return ldb_next_start_trans(module);
3797 on prepare commit we loop over our queued la_context structures and
3798 apply each of them
3800 static int replmd_prepare_commit(struct ldb_module *module)
3802 struct replmd_private *replmd_private =
3803 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
3804 struct la_entry *la, *prev;
3805 struct la_backlink *bl;
3806 int ret;
3808 /* walk the list backwards, to do the first entry first, as we
3809 * added the entries with DLIST_ADD() which puts them at the
3810 * start of the list */
3811 for (la = DLIST_TAIL(replmd_private->la_list); la; la=prev) {
3812 prev = DLIST_PREV(la);
3813 DLIST_REMOVE(replmd_private->la_list, la);
3814 ret = replmd_process_linked_attribute(module, la);
3815 if (ret != LDB_SUCCESS) {
3816 replmd_txn_cleanup(replmd_private);
3817 return ret;
3821 /* process our backlink list, creating and deleting backlinks
3822 as necessary */
3823 for (bl=replmd_private->la_backlinks; bl; bl=bl->next) {
3824 ret = replmd_process_backlink(module, bl);
3825 if (ret != LDB_SUCCESS) {
3826 replmd_txn_cleanup(replmd_private);
3827 return ret;
3831 replmd_txn_cleanup(replmd_private);
3833 /* possibly change @REPLCHANGED */
3834 ret = replmd_notify_store(module);
3835 if (ret != LDB_SUCCESS) {
3836 return ret;
3839 return ldb_next_prepare_commit(module);
3842 static int replmd_del_transaction(struct ldb_module *module)
3844 struct replmd_private *replmd_private =
3845 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
3846 replmd_txn_cleanup(replmd_private);
3848 return ldb_next_del_trans(module);
3852 _PUBLIC_ const struct ldb_module_ops ldb_repl_meta_data_module_ops = {
3853 .name = "repl_meta_data",
3854 .init_context = replmd_init,
3855 .add = replmd_add,
3856 .modify = replmd_modify,
3857 .rename = replmd_rename,
3858 .del = replmd_delete,
3859 .extended = replmd_extended,
3860 .start_transaction = replmd_start_transaction,
3861 .prepare_commit = replmd_prepare_commit,
3862 .del_transaction = replmd_del_transaction,