4 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2007
5 Copyright (C) Simo Sorce <idra@samba.org> 2008
6 Copyright (C) Matthieu Patou <mat@matws.net> 2011
7 Copyright (C) Andrew Tridgell 2009
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 3 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>.
26 * Component: ldb linked_attributes module
28 * Description: Module to ensure linked attribute pairs (i.e. forward-links
29 * and backlinks) remain in sync.
31 * Backlinks are 'plain' links (without extra metadata). When the link target
32 * object is modified (e.g. renamed), we use the backlinks to keep the link
33 * source object updated. Note there are some cases where we can't do this:
34 * - one-way links, which don't have a corresponding backlink
35 * - two-way deactivated links, i.e. when a user is removed from a group,
36 * the forward 'member' link still exists (but is inactive), however, the
37 * 'memberOf' backlink is deleted.
38 * In these cases, we can end up with a dangling forward link which is
39 * incorrect (i.e. the target has been renamed or deleted). We have dbcheck
40 * rules to detect and fix this, and cope otherwise by filtering at runtime
41 * (i.e. in the extended_dn module).
43 * See also repl_meta_data.c, which handles updating links for deleted
44 * objects, as well as link changes received from another DC.
46 * Author: Andrew Bartlett
50 #include "ldb_module.h"
51 #include "util/dlinklist.h"
52 #include "dsdb/samdb/samdb.h"
53 #include "librpc/gen_ndr/ndr_misc.h"
54 #include "dsdb/samdb/ldb_modules/util.h"
57 struct la_private_transaction {
58 struct la_context *la_list;
63 struct la_private_transaction *transaction;
68 struct la_op_store *next;
69 struct la_op_store *prev;
70 enum la_op {LA_OP_ADD, LA_OP_DEL} op;
75 struct replace_context {
76 struct la_context *ac;
77 unsigned int num_elements;
78 struct ldb_message_element *el;
82 struct la_context *next, *prev;
83 const struct dsdb_schema *schema;
84 struct ldb_module *module;
85 struct ldb_request *req;
86 struct ldb_dn *mod_dn;
87 struct replace_context *rc;
88 struct la_op_store *ops;
89 struct ldb_extended *op_response;
90 struct ldb_control **op_controls;
93 * will tell which GC to use for resolving links
99 static int handle_verify_name_control(TALLOC_CTX *ctx, struct ldb_context *ldb,
100 struct ldb_control *control, struct la_context *ac)
103 * If we are a GC let's remove the control,
104 * if there is a specified GC check that is us.
106 struct ldb_verify_name_control *lvnc = (struct ldb_verify_name_control *)control->data;
107 if (samdb_is_gc(ldb)) {
108 /* Because we can't easily talloc a struct ldb_dn*/
109 struct ldb_dn **dn = talloc_array(ctx, struct ldb_dn *, 1);
110 int ret = samdb_server_reference_dn(ldb, ctx, dn);
113 if (ret != LDB_SUCCESS) {
114 return ldb_operr(ldb);
117 dns = samdb_dn_to_dnshostname(ldb, ctx, *dn);
119 return ldb_operr(ldb);
121 if (!lvnc->gc || strcasecmp(dns, lvnc->gc) == 0) {
122 if (!ldb_save_controls(control, ctx, NULL)) {
123 return ldb_operr(ldb);
126 control->critical = true;
130 /* For the moment we don't remove the control is this case in order
131 * to fail the request. It's better than having the client thinking
132 * that we honnor its control.
133 * Hopefully only a very small set of usecase should hit this problem.
136 ac->gc_dns_name = talloc_strdup(ac, lvnc->gc);
138 control->critical = true;
144 static struct la_context *linked_attributes_init(struct ldb_module *module,
145 struct ldb_request *req)
147 struct ldb_context *ldb;
148 struct la_context *ac;
150 ldb = ldb_module_get_ctx(module);
152 ac = talloc_zero(req, struct la_context);
158 ac->schema = dsdb_get_schema(ldb, ac);
166 turn a DN into a GUID
168 static int la_guid_from_dn(struct ldb_module *module,
169 struct ldb_request *parent,
170 struct ldb_dn *dn, struct GUID *guid)
175 status = dsdb_get_extended_dn_guid(dn, guid, "GUID");
176 if (NT_STATUS_IS_OK(status)) {
179 if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
180 DEBUG(4,(__location__ ": Unable to parse GUID for dn %s\n",
181 ldb_dn_get_linearized(dn)));
182 return ldb_operr(ldb_module_get_ctx(module));
185 ret = dsdb_module_guid_by_dn(module, dn, guid, parent);
186 if (ret != LDB_SUCCESS) {
187 DEBUG(4,(__location__ ": Failed to find GUID for dn %s\n",
188 ldb_dn_get_linearized(dn)));
195 /* Common routine to handle reading the attributes and creating a
196 * series of modify requests */
197 static int la_store_op(struct la_context *ac,
199 const struct dsdb_attribute *schema_attr,
203 struct ldb_context *ldb;
204 struct la_op_store *os;
205 struct ldb_dn *op_dn;
206 struct dsdb_dn *dsdb_dn;
209 ldb = ldb_module_get_ctx(ac->module);
212 os = talloc_zero(ac, struct la_op_store);
217 dsdb_dn = dsdb_dn_parse(os, ldb, dn, schema_attr->syntax->ldap_oid);
220 ldb_asprintf_errstring(ldb,
221 "could not parse attribute as a DN");
223 return LDB_ERR_INVALID_DN_SYNTAX;
230 ret = la_guid_from_dn(ac->module, ac->req, op_dn, &os->guid);
232 if (ret == LDB_ERR_NO_SUCH_OBJECT && ac->req->operation == LDB_DELETE) {
233 /* we are deleting an object, and we've found it has a
234 * forward link to a target that no longer
235 * exists. This is not an error in the delete, and we
236 * should just not do the deferred delete of the
242 if (ret != LDB_SUCCESS) {
246 os->name = talloc_strdup(os, name);
251 /* Do deletes before adds */
252 if (op == LA_OP_ADD) {
253 DLIST_ADD_END(ac->ops, os);
255 /* By adding to the head of the list, we do deletes before
256 * adds when processing a replace */
257 DLIST_ADD(ac->ops, os);
263 static int la_queue_mod_request(struct la_context *ac);
264 static int la_down_req(struct la_context *ac);
269 static int linked_attributes_add(struct ldb_module *module, struct ldb_request *req)
271 struct ldb_context *ldb;
272 const struct dsdb_attribute *target_attr;
273 struct la_context *ac;
274 const char *attr_name;
275 struct ldb_control *ctrl;
277 struct ldb_control *control;
280 ldb = ldb_module_get_ctx(module);
282 if (ldb_dn_is_special(req->op.add.message->dn)) {
283 /* do not manipulate our control entries */
284 return ldb_next_request(module, req);
287 ac = linked_attributes_init(module, req);
289 return ldb_operr(ldb);
292 control = ldb_request_get_control(req, LDB_CONTROL_VERIFY_NAME_OID);
293 if (control != NULL && control->data != NULL) {
294 ret = handle_verify_name_control(req, ldb, control, ac);
295 if (ret != LDB_SUCCESS) {
296 return ldb_operr(ldb);
300 if (!(ctrl = ldb_request_get_control(req, DSDB_CONTROL_APPLY_LINKS))) {
301 /* don't do anything special for linked attributes, repl_meta_data has done it */
303 return ldb_next_request(module, req);
305 ctrl->critical = false;
308 /* without schema, this doesn't make any sense */
310 return ldb_next_request(module, req);
314 /* Need to ensure we only have forward links being specified */
315 for (i=0; i < req->op.add.message->num_elements; i++) {
316 const struct ldb_message_element *el = &req->op.add.message->elements[i];
317 const struct dsdb_attribute *schema_attr
318 = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
320 ldb_asprintf_errstring(ldb,
321 "%s: attribute %s is not a valid attribute in schema",
324 return LDB_ERR_OBJECT_CLASS_VIOLATION;
327 /* this could be a link with no partner, in which case
328 there is no special work to do */
329 if (schema_attr->linkID == 0) {
333 /* this part of the code should only be handling forward links */
334 SMB_ASSERT((schema_attr->linkID & 1) == 0);
336 /* Even link IDs are for the originating attribute */
337 target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID ^ 1);
340 * windows 2003 has a broken schema where
341 * the definition of msDS-IsDomainFor
342 * is missing (which is supposed to be
343 * the backlink of the msDS-HasDomainNCs
349 attr_name = target_attr->lDAPDisplayName;
351 for (j = 0; j < el->num_values; j++) {
352 ret = la_store_op(ac, LA_OP_ADD,
356 if (ret != LDB_SUCCESS) {
362 /* if no linked attributes are present continue */
363 if (ac->ops == NULL) {
364 /* nothing to do for this module, proceed */
366 return ldb_next_request(module, req);
369 /* start with the original request */
370 return la_down_req(ac);
373 /* For a delete or rename, we need to find out what linked attributes
374 * are currently on this DN, and then deal with them. This is the
375 * callback to the base search */
377 static int la_mod_search_callback(struct ldb_request *req, struct ldb_reply *ares)
379 struct ldb_context *ldb;
380 const struct dsdb_attribute *schema_attr;
381 const struct dsdb_attribute *target_attr;
382 struct ldb_message_element *search_el;
383 struct replace_context *rc;
384 struct la_context *ac;
385 const char *attr_name;
387 int ret = LDB_SUCCESS;
389 ac = talloc_get_type(req->context, struct la_context);
390 ldb = ldb_module_get_ctx(ac->module);
394 return ldb_module_done(ac->req, NULL, NULL,
395 LDB_ERR_OPERATIONS_ERROR);
397 if (ares->error != LDB_SUCCESS) {
398 return ldb_module_done(ac->req, ares->controls,
399 ares->response, ares->error);
402 /* Only entries are interesting, and we only want the olddn */
403 switch (ares->type) {
404 case LDB_REPLY_ENTRY:
406 if (ldb_dn_compare(ares->message->dn, ac->req->op.mod.message->dn) != 0) {
407 ldb_asprintf_errstring(ldb,
408 "linked_attributes: %s is not the DN we were looking for",
409 ldb_dn_get_linearized(ares->message->dn));
410 /* Guh? We only asked for this DN */
412 return ldb_module_done(ac->req, NULL, NULL,
413 LDB_ERR_OPERATIONS_ERROR);
416 ac->mod_dn = talloc_steal(ac, ares->message->dn);
418 /* We don't populate 'rc' for ADD - it can't be deleting elements anyway */
419 for (i = 0; rc && i < rc->num_elements; i++) {
421 schema_attr = dsdb_attribute_by_lDAPDisplayName(ac->schema, rc->el[i].name);
423 ldb_asprintf_errstring(ldb,
424 "%s: attribute %s is not a valid attribute in schema",
428 return ldb_module_done(ac->req, NULL, NULL,
429 LDB_ERR_OBJECT_CLASS_VIOLATION);
432 search_el = ldb_msg_find_element(ares->message,
435 /* See if this element already exists */
436 /* otherwise just ignore as
437 * the add has already been scheduled */
442 target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID ^ 1);
445 * windows 2003 has a broken schema where
446 * the definition of msDS-IsDomainFor
447 * is missing (which is supposed to be
448 * the backlink of the msDS-HasDomainNCs
453 attr_name = target_attr->lDAPDisplayName;
455 /* Now we know what was there, we can remove it for the re-add */
456 for (j = 0; j < search_el->num_values; j++) {
457 ret = la_store_op(ac, LA_OP_DEL,
459 &search_el->values[j],
461 if (ret != LDB_SUCCESS) {
463 return ldb_module_done(ac->req,
471 case LDB_REPLY_REFERRAL:
479 if (ac->req->operation == LDB_ADD) {
480 /* Start the modifies to the backlinks */
481 ret = la_queue_mod_request(ac);
483 if (ret != LDB_SUCCESS) {
484 return ldb_module_done(ac->req, NULL, NULL,
488 /* Start with the original request */
489 ret = la_down_req(ac);
490 if (ret != LDB_SUCCESS) {
491 return ldb_module_done(ac->req, NULL, NULL, ret);
503 static int linked_attributes_modify(struct ldb_module *module, struct ldb_request *req)
505 /* Look over list of modifications */
506 /* Find if any are for linked attributes */
507 /* Determine the effect of the modification */
508 /* Apply the modify to the linked entry */
510 struct ldb_control *control;
511 struct ldb_context *ldb;
513 struct la_context *ac;
514 struct ldb_request *search_req;
516 struct ldb_control *ctrl;
519 ldb = ldb_module_get_ctx(module);
521 if (ldb_dn_is_special(req->op.mod.message->dn)) {
522 /* do not manipulate our control entries */
523 return ldb_next_request(module, req);
526 ac = linked_attributes_init(module, req);
528 return ldb_operr(ldb);
531 control = ldb_request_get_control(req, LDB_CONTROL_VERIFY_NAME_OID);
532 if (control != NULL && control->data != NULL) {
533 ret = handle_verify_name_control(req, ldb, control, ac);
534 if (ret != LDB_SUCCESS) {
535 return ldb_operr(ldb);
539 if (!(ctrl = ldb_request_get_control(req, DSDB_CONTROL_APPLY_LINKS))) {
540 /* don't do anything special for linked attributes, repl_meta_data has done it */
542 return ldb_next_request(module, req);
544 ctrl->critical = false;
547 /* without schema, this doesn't make any sense */
548 return ldb_next_request(module, req);
551 ac->rc = talloc_zero(ac, struct replace_context);
556 for (i=0; i < req->op.mod.message->num_elements; i++) {
557 bool store_el = false;
558 const char *attr_name;
559 const struct dsdb_attribute *target_attr;
560 const struct ldb_message_element *el = &req->op.mod.message->elements[i];
561 const struct dsdb_attribute *schema_attr
562 = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
564 ldb_asprintf_errstring(ldb,
565 "%s: attribute %s is not a valid attribute in schema",
568 return LDB_ERR_OBJECT_CLASS_VIOLATION;
570 /* We have a valid attribute, now find out if it is a forward link
571 (Even link IDs are for the originating attribute) */
572 if (schema_attr->linkID == 0) {
576 /* this part of the code should only be handling forward links */
577 SMB_ASSERT((schema_attr->linkID & 1) == 0);
579 /* Now find the target attribute */
580 target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID ^ 1);
583 * windows 2003 has a broken schema where
584 * the definition of msDS-IsDomainFor
585 * is missing (which is supposed to be
586 * the backlink of the msDS-HasDomainNCs
592 attr_name = target_attr->lDAPDisplayName;
594 switch (el->flags & LDB_FLAG_MOD_MASK) {
595 case LDB_FLAG_MOD_REPLACE:
596 /* treat as just a normal add the delete part is handled by the callback */
600 case LDB_FLAG_MOD_ADD:
602 /* For each value being added, we need to setup the adds */
603 for (j = 0; j < el->num_values; j++) {
604 ret = la_store_op(ac, LA_OP_ADD,
608 if (ret != LDB_SUCCESS) {
614 case LDB_FLAG_MOD_DELETE:
616 if (el->num_values) {
617 /* For each value being deleted, we need to setup the delete */
618 for (j = 0; j < el->num_values; j++) {
619 ret = la_store_op(ac, LA_OP_DEL,
623 if (ret != LDB_SUCCESS) {
628 /* Flag that there was a DELETE
629 * without a value specified, so we
630 * need to look for the old value */
638 struct ldb_message_element *search_el;
640 search_el = talloc_realloc(ac->rc, ac->rc->el,
641 struct ldb_message_element,
642 ac->rc->num_elements +1);
646 ac->rc->el = search_el;
648 ac->rc->el[ac->rc->num_elements] = *el;
649 ac->rc->num_elements++;
653 if (ac->ops || ac->rc->el) {
654 /* both replace and delete without values are handled in the callback
655 * after the search on the entry to be modified is performed */
657 attrs = talloc_array(ac->rc, const char *, ac->rc->num_elements + 1);
661 for (i = 0; ac->rc && i < ac->rc->num_elements; i++) {
662 attrs[i] = ac->rc->el[i].name;
666 /* The callback does all the hard work here */
667 ret = ldb_build_search_req(&search_req, ldb, ac,
668 req->op.mod.message->dn,
670 "(objectClass=*)", attrs,
672 ac, la_mod_search_callback,
674 LDB_REQ_SET_LOCATION(search_req);
676 /* We need to figure out our own extended DN, to fill in as the backlink target */
677 if (ret == LDB_SUCCESS) {
678 ret = dsdb_request_add_controls(search_req,
679 DSDB_SEARCH_SHOW_RECYCLED |
680 DSDB_SEARCH_SHOW_EXTENDED_DN);
682 if (ret == LDB_SUCCESS) {
683 talloc_steal(search_req, attrs);
685 ret = ldb_next_request(module, search_req);
689 /* nothing to do for this module, proceed */
691 ret = ldb_next_request(module, req);
698 static int linked_attributes_fix_link_slow(struct ldb_module *module,
699 struct ldb_request *parent,
700 struct ldb_message *msg,
701 struct ldb_dn *new_dn,
702 struct GUID self_guid,
703 const char *syntax_oid)
707 struct GUID link_guid;
708 struct ldb_message_element *el = &msg->elements[0];
709 struct ldb_context *ldb = ldb_module_get_ctx(module);
710 TALLOC_CTX *tmp_ctx = talloc_new(module);
711 if (tmp_ctx == NULL) {
712 return LDB_ERR_OPERATIONS_ERROR;
715 * The msg has one element (el) containing links of one particular
716 * type from the remote object. We know that at least one of those
717 * links points to the object being renamed (identified by self_guid,
718 * renamed to new_dn). Usually only one of the links will point back
719 * to renamed object, but there can be more when the reverse link is a
722 * This is used for unsorted links, which is to say back links and
723 * forward links on old databases.
725 for (i = 0; i < el->num_values; i++) {
726 struct dsdb_dn *dsdb_dn = dsdb_dn_parse(msg,
730 if (dsdb_dn == NULL) {
731 talloc_free(tmp_ctx);
732 return LDB_ERR_INVALID_DN_SYNTAX;
735 ret = la_guid_from_dn(module, parent, dsdb_dn->dn, &link_guid);
736 if (ret != LDB_SUCCESS) {
737 talloc_free(tmp_ctx);
742 * By comparing using the GUID we ensure that even if somehow
743 * the name has got out of sync, this rename will fix it.
745 * If somehow we don't have a GUID on the DN in the DB, the
746 * la_guid_from_dn call will be more costly, but still give us
747 * a GUID. dbcheck will fix this if run.
749 if (!GUID_equal(&self_guid, &link_guid)) {
753 ret = ldb_dn_update_components(dsdb_dn->dn, new_dn);
754 if (ret != LDB_SUCCESS) {
755 talloc_free(tmp_ctx);
759 el->values[i] = data_blob_string_const(
760 dsdb_dn_get_extended_linearized(el->values, dsdb_dn, 1));
763 talloc_free(tmp_ctx);
768 static int linked_attributes_fix_forward_link(struct ldb_module *module,
769 struct ldb_message *msg,
770 struct ldb_dn *new_dn,
771 struct GUID self_guid,
772 const char *syntax_oid)
775 struct ldb_context *ldb = ldb_module_get_ctx(module);
776 struct parsed_dn *pdn_list = NULL;
777 struct parsed_dn *exact = NULL;
778 struct parsed_dn *next = NULL;
780 struct ldb_message_element *el = &msg->elements[0];
781 unsigned int num_parsed_dns = el->num_values;
783 TALLOC_CTX *tmp_ctx = talloc_new(module);
784 if (tmp_ctx == NULL) {
785 return LDB_ERR_OPERATIONS_ERROR;
789 * The msg has a single element (el) containing forward links which we
790 * trust are sorted in GUID order. We know that at least one of those
791 * links points to the object being renamed (identified by self_guid,
792 * renamed to new_dn), because that object has a backlink pointing
795 * In most cases we assume there will only be one forward link, which
796 * is found by parsed_dn_find(), but in the case of DN+Binary links
797 * (e.g. msDS-RevealedUsers) there may be many forward links that
798 * share the same DN/GUID but differ in the binary part. For those we
799 * need to look around the link found by parsed_dn_find() and convert
800 * them all -- there is no way to know which forward link belongs to
804 ret = get_parsed_dns_trusted(tmp_ctx, el, &pdn_list);
805 if (ret != LDB_SUCCESS) {
806 ldb_asprintf_errstring(ldb, "get_parsed_dn_trusted() "
807 "error fixing %s links for %s",
809 ldb_dn_get_linearized(msg->dn));
810 talloc_free(tmp_ctx);
814 /* find our DN in the values */
815 ret = parsed_dn_find(ldb, pdn_list, num_parsed_dns,
823 if (ret != LDB_SUCCESS) {
824 ldb_asprintf_errstring(ldb, "parsed_dn_find() "
825 "error fixing %s links for %s",
827 ldb_dn_get_linearized(msg->dn));
828 talloc_free(tmp_ctx);
833 ldb_asprintf_errstring(
835 "parsed_dn_find could not find %s link for %s",
837 ldb_dn_get_linearized(msg->dn));
838 talloc_free(tmp_ctx);
839 return LDB_ERR_OPERATIONS_ERROR;
842 is_plain_dn = strcmp(syntax_oid, LDB_SYNTAX_DN) == 0;
846 * The common case -- we only have to update a single link
848 ret = ldb_dn_update_components(exact->dsdb_dn->dn, new_dn);
849 if (ret != LDB_SUCCESS) {
850 DBG_ERR("could not update components %s %s\n",
851 ldb_dn_get_linearized(exact->dsdb_dn->dn),
852 ldb_dn_get_linearized(new_dn)
855 talloc_free(tmp_ctx);
858 *(exact->v) = data_blob_string_const(
859 dsdb_dn_get_extended_linearized(el->values,
864 * The forward link is a DN+Binary (or in some alternate
865 * universes, DN+String), which means the parsed_dns are keyed
866 * on GUID+Binary. We don't know the binary part, which means
867 * from our point of view the list can have entries with
868 * duplicate GUIDs that we can't tell apart. We don't know
869 * which backlink belongs to which GUID+binary, and the binary
870 * search will always find the same one. That means one link
871 * link will get fixed n times, whil n-1 links get fixed
874 * If we instead fixing all the possible links, we end up
875 * fixing n links n times, which at least works and is
876 * probably not too costly because n is probably small.
878 struct parsed_dn *first = exact;
879 struct parsed_dn *last = exact;
880 struct parsed_dn *p = NULL;
882 while (first > pdn_list) {
884 if (p->dsdb_dn == NULL) {
885 ret = really_parse_trusted_dn(tmp_ctx,
888 if (ret != LDB_SUCCESS) {
889 talloc_free(tmp_ctx);
893 cmp = ndr_guid_compare(&exact->guid, &p->guid);
900 while (last < pdn_list + num_parsed_dns - 1) {
902 if (p->dsdb_dn == NULL) {
903 ret = really_parse_trusted_dn(tmp_ctx,
906 if (ret != LDB_SUCCESS) {
907 talloc_free(tmp_ctx);
911 cmp = ndr_guid_compare(&exact->guid, &p->guid);
918 for (p = first; p <= last; p++) {
919 ret = ldb_dn_update_components(p->dsdb_dn->dn, new_dn);
920 if (ret != LDB_SUCCESS) {
921 DBG_ERR("could not update components %s %s\n",
922 ldb_dn_get_linearized(p->dsdb_dn->dn),
923 ldb_dn_get_linearized(new_dn)
925 talloc_free(tmp_ctx);
928 *(p->v) = data_blob_string_const(
929 dsdb_dn_get_extended_linearized(el->values,
935 talloc_free(tmp_ctx);
940 static int linked_attributes_fix_links(struct ldb_module *module,
941 struct GUID self_guid,
942 struct ldb_dn *old_dn, struct ldb_dn *new_dn,
943 struct ldb_message_element *el, struct dsdb_schema *schema,
944 const struct dsdb_attribute *schema_attr,
945 struct ldb_request *parent)
948 TALLOC_CTX *tmp_ctx = NULL;
949 struct ldb_context *ldb = ldb_module_get_ctx(module);
950 const struct dsdb_attribute *target = NULL;
951 const char *attrs[2];
953 struct la_private *la_private = NULL;
955 target = dsdb_attribute_by_linkID(schema, schema_attr->linkID ^ 1);
956 if (target == NULL) {
957 /* there is no counterpart link to change */
961 tmp_ctx = talloc_new(module);
962 if (tmp_ctx == NULL) {
963 return LDB_ERR_OPERATIONS_ERROR;
966 la_private = talloc_get_type(ldb_module_get_private(module),
968 if (la_private == NULL) {
969 talloc_free(tmp_ctx);
970 return LDB_ERR_OPERATIONS_ERROR;
973 attrs[0] = target->lDAPDisplayName;
976 for (i=0; i<el->num_values; i++) {
977 struct dsdb_dn *dsdb_dn = NULL;
978 struct ldb_result *res = NULL;
979 struct ldb_message *msg = NULL;
980 struct ldb_message_element *el2 = NULL;
981 struct GUID link_guid;
982 char *link_guid_str = NULL;
984 dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i], schema_attr->syntax->ldap_oid);
985 if (dsdb_dn == NULL) {
986 talloc_free(tmp_ctx);
987 return LDB_ERR_INVALID_DN_SYNTAX;
990 ret = la_guid_from_dn(module, parent, dsdb_dn->dn, &link_guid);
991 if (ret != LDB_SUCCESS) {
992 ldb_asprintf_errstring(ldb, "Linked attribute %s->%s between %s and %s - GUID not found - %s",
993 el->name, target->lDAPDisplayName,
994 ldb_dn_get_linearized(old_dn),
995 ldb_dn_get_linearized(dsdb_dn->dn),
997 talloc_free(tmp_ctx);
1001 link_guid_str = GUID_string(tmp_ctx, &link_guid);
1002 if (link_guid_str == NULL) {
1003 talloc_free(tmp_ctx);
1004 return LDB_ERR_OPERATIONS_ERROR;
1008 * get the existing message from the db for the object with
1009 * this GUID, returning attribute being modified. We will then
1010 * use this msg as the basis for a modify call
1013 ret = dsdb_module_search(module, tmp_ctx, &res, NULL, LDB_SCOPE_SUBTREE, attrs,
1014 DSDB_FLAG_NEXT_MODULE |
1015 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
1016 DSDB_SEARCH_SHOW_RECYCLED |
1017 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
1018 DSDB_SEARCH_REVEAL_INTERNALS,
1020 "objectGUID=%s", link_guid_str);
1021 if (ret != LDB_SUCCESS) {
1022 ldb_asprintf_errstring(ldb, "Linked attribute %s->%s between %s and %s - target GUID %s not found - %s",
1023 el->name, target->lDAPDisplayName,
1024 ldb_dn_get_linearized(old_dn),
1025 ldb_dn_get_linearized(dsdb_dn->dn),
1027 ldb_errstring(ldb));
1028 talloc_free(tmp_ctx);
1031 if (res->count == 0) {
1032 /* Forward link without backlink object remaining - nothing to do here */
1035 if (res->count != 1) {
1036 ldb_asprintf_errstring(ldb, "Linked attribute %s->%s between %s and %s - target GUID %s found more than once!",
1037 el->name, target->lDAPDisplayName,
1038 ldb_dn_get_linearized(old_dn),
1039 ldb_dn_get_linearized(dsdb_dn->dn),
1041 talloc_free(tmp_ctx);
1042 return LDB_ERR_OPERATIONS_ERROR;
1047 if (msg->num_elements == 0) {
1048 /* Forward link without backlink remaining - nothing to do here */
1050 } else if (msg->num_elements != 1) {
1051 ldb_asprintf_errstring(ldb, "Bad msg elements - got %u elements, expected one element to be returned in linked_attributes_fix_links for %s",
1052 msg->num_elements, ldb_dn_get_linearized(msg->dn));
1053 talloc_free(tmp_ctx);
1054 return LDB_ERR_OPERATIONS_ERROR;
1056 if (ldb_attr_cmp(msg->elements[0].name, target->lDAPDisplayName) != 0) {
1057 ldb_asprintf_errstring(ldb, "Bad returned attribute in linked_attributes_fix_links: got %s, expected %s for %s", msg->elements[0].name, target->lDAPDisplayName, ldb_dn_get_linearized(msg->dn));
1058 talloc_free(tmp_ctx);
1059 return LDB_ERR_OPERATIONS_ERROR;
1061 el2 = &msg->elements[0];
1063 el2->flags = LDB_FLAG_MOD_REPLACE;
1065 if (target->linkID & 1 ||
1066 ! la_private->sorted_links) {
1067 /* handle backlinks (which aren't sorted in the DB)
1068 and forward links in old unsorted databases. */
1069 ret = linked_attributes_fix_link_slow(
1075 target->syntax->ldap_oid);
1077 /* we can binary search to find forward links */
1078 ret = linked_attributes_fix_forward_link(
1083 target->syntax->ldap_oid);
1085 ret = dsdb_check_single_valued_link(target, el2);
1086 if (ret != LDB_SUCCESS) {
1087 talloc_free(tmp_ctx);
1091 /* we may be putting multiple values in an attribute -
1092 disable checking for this attribute */
1093 el2->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
1095 ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE, parent);
1096 if (ret != LDB_SUCCESS) {
1097 ldb_asprintf_errstring(ldb, "Linked attribute %s->%s between %s and %s - update failed - %s",
1098 el->name, target->lDAPDisplayName,
1099 ldb_dn_get_linearized(old_dn),
1100 ldb_dn_get_linearized(dsdb_dn->dn),
1101 ldb_errstring(ldb));
1102 talloc_free(tmp_ctx);
1107 talloc_free(tmp_ctx);
1113 static int linked_attributes_rename(struct ldb_module *module, struct ldb_request *req)
1115 struct ldb_result *res;
1116 struct ldb_message *msg;
1118 struct ldb_context *ldb = ldb_module_get_ctx(module);
1119 struct dsdb_schema *schema;
1124 - load the current msg
1125 - find any linked attributes
1126 - if its a link then find the target object
1127 - modify the target linked attributes with the new DN
1129 ret = dsdb_module_search_dn(module, req, &res, req->op.rename.olddn,
1131 DSDB_FLAG_NEXT_MODULE |
1132 DSDB_SEARCH_SHOW_EXTENDED_DN |
1133 DSDB_SEARCH_SHOW_RECYCLED, req);
1134 if (ret != LDB_SUCCESS) {
1138 schema = dsdb_get_schema(ldb, res);
1140 return ldb_oom(ldb);
1145 ret = la_guid_from_dn(module, req, msg->dn, &guid);
1146 if (ret != LDB_SUCCESS) {
1150 for (i=0; i<msg->num_elements; i++) {
1151 struct ldb_message_element *el = &msg->elements[i];
1152 const struct dsdb_attribute *schema_attr
1153 = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
1154 if (!schema_attr || schema_attr->linkID == 0) {
1157 ret = linked_attributes_fix_links(module, guid, msg->dn, req->op.rename.newdn, el,
1158 schema, schema_attr, req);
1159 if (ret != LDB_SUCCESS) {
1167 return ldb_next_request(module, req);
1171 /* queue a linked attributes modify request in the la_private
1173 static int la_queue_mod_request(struct la_context *ac)
1175 struct la_private *la_private =
1176 talloc_get_type(ldb_module_get_private(ac->module),
1179 if (la_private == NULL || la_private->transaction == NULL) {
1180 ldb_debug(ldb_module_get_ctx(ac->module),
1182 __location__ ": No la_private transaction setup\n");
1183 return ldb_operr(ldb_module_get_ctx(ac->module));
1186 talloc_steal(la_private->transaction, ac);
1187 DLIST_ADD(la_private->transaction->la_list, ac);
1189 return ldb_module_done(ac->req, ac->op_controls,
1190 ac->op_response, LDB_SUCCESS);
1193 /* Having done the original operation, then try to fix up all the linked attributes for modify and delete */
1194 static int la_mod_del_callback(struct ldb_request *req, struct ldb_reply *ares)
1196 struct la_context *ac;
1197 struct ldb_context *ldb;
1200 ac = talloc_get_type(req->context, struct la_context);
1201 ldb = ldb_module_get_ctx(ac->module);
1204 return ldb_module_done(ac->req, NULL, NULL,
1205 LDB_ERR_OPERATIONS_ERROR);
1207 if (ares->error != LDB_SUCCESS) {
1208 return ldb_module_done(ac->req, ares->controls,
1209 ares->response, ares->error);
1212 if (ares->type != LDB_REPLY_DONE) {
1213 ldb_set_errstring(ldb,
1214 "invalid reply type in linked attributes delete callback");
1216 return ldb_module_done(ac->req, NULL, NULL,
1217 LDB_ERR_OPERATIONS_ERROR);
1220 ac->op_controls = talloc_steal(ac, ares->controls);
1221 ac->op_response = talloc_steal(ac, ares->response);
1223 /* If we have modfies to make, this is the time to do them for modify and delete */
1224 ret = la_queue_mod_request(ac);
1226 if (ret != LDB_SUCCESS) {
1227 return ldb_module_done(ac->req, NULL, NULL, ret);
1231 /* la_queue_mod_request has already sent the callbacks */
1236 /* Having done the original add, then try to fix up all the linked attributes
1238 This is done after the add so the links can get the extended DNs correctly.
1240 static int la_add_callback(struct ldb_request *req, struct ldb_reply *ares)
1242 struct la_context *ac;
1243 struct ldb_context *ldb;
1246 ac = talloc_get_type(req->context, struct la_context);
1247 ldb = ldb_module_get_ctx(ac->module);
1250 return ldb_module_done(ac->req, NULL, NULL,
1251 LDB_ERR_OPERATIONS_ERROR);
1253 if (ares->error != LDB_SUCCESS) {
1254 return ldb_module_done(ac->req, ares->controls,
1255 ares->response, ares->error);
1258 if (ares->type != LDB_REPLY_DONE) {
1259 ldb_set_errstring(ldb,
1260 "invalid reply type in linked attributes add callback");
1262 return ldb_module_done(ac->req, NULL, NULL,
1263 LDB_ERR_OPERATIONS_ERROR);
1267 struct ldb_request *search_req;
1268 static const char *attrs[] = { NULL };
1270 /* The callback does all the hard work here - we need
1271 * the objectGUID and SID of the added record */
1272 ret = ldb_build_search_req(&search_req, ldb, ac,
1273 ac->req->op.add.message->dn,
1275 "(objectClass=*)", attrs,
1277 ac, la_mod_search_callback,
1279 LDB_REQ_SET_LOCATION(search_req);
1281 if (ret == LDB_SUCCESS) {
1282 ret = dsdb_request_add_controls(search_req,
1283 DSDB_SEARCH_SHOW_RECYCLED |
1284 DSDB_SEARCH_SHOW_EXTENDED_DN);
1286 if (ret != LDB_SUCCESS) {
1287 return ldb_module_done(ac->req, NULL, NULL,
1291 ac->op_controls = talloc_steal(ac, ares->controls);
1292 ac->op_response = talloc_steal(ac, ares->response);
1294 return ldb_next_request(ac->module, search_req);
1297 return ldb_module_done(ac->req, ares->controls,
1298 ares->response, ares->error);
1302 /* Reconstruct the original request, but pointing at our local callback to finish things off */
1303 static int la_down_req(struct la_context *ac)
1305 struct ldb_request *down_req;
1306 struct ldb_context *ldb;
1309 ldb = ldb_module_get_ctx(ac->module);
1311 switch (ac->req->operation) {
1313 ret = ldb_build_add_req(&down_req, ldb, ac,
1314 ac->req->op.add.message,
1316 ac, la_add_callback,
1318 LDB_REQ_SET_LOCATION(down_req);
1321 ret = ldb_build_mod_req(&down_req, ldb, ac,
1322 ac->req->op.mod.message,
1324 ac, la_mod_del_callback,
1326 LDB_REQ_SET_LOCATION(down_req);
1329 ret = LDB_ERR_OPERATIONS_ERROR;
1331 if (ret != LDB_SUCCESS) {
1335 return ldb_next_request(ac->module, down_req);
1339 use the GUID part of an extended DN to find the target DN, in case
1342 static int la_find_dn_target(struct ldb_module *module, struct la_context *ac,
1343 struct GUID *guid, struct ldb_dn **dn)
1345 return dsdb_module_dn_by_guid(ac->module, ac, guid, dn, ac->req);
1348 /* apply one la_context op change */
1349 static int la_do_op_request(struct ldb_module *module, struct la_context *ac, struct la_op_store *op)
1351 struct ldb_message_element *ret_el;
1352 struct ldb_message *new_msg;
1353 struct ldb_context *ldb;
1356 if (ac->mod_dn == NULL) {
1357 /* we didn't find the DN that we searched for */
1361 ldb = ldb_module_get_ctx(ac->module);
1363 /* Create the modify request */
1364 new_msg = ldb_msg_new(ac);
1366 return ldb_oom(ldb);
1369 ret = la_find_dn_target(module, ac, &op->guid, &new_msg->dn);
1370 if (ret != LDB_SUCCESS) {
1374 if (op->op == LA_OP_ADD) {
1375 ret = ldb_msg_add_empty(new_msg, op->name,
1376 LDB_FLAG_MOD_ADD, &ret_el);
1378 ret = ldb_msg_add_empty(new_msg, op->name,
1379 LDB_FLAG_MOD_DELETE, &ret_el);
1381 if (ret != LDB_SUCCESS) {
1384 ret_el->values = talloc_array(new_msg, struct ldb_val, 1);
1385 if (!ret_el->values) {
1386 return ldb_oom(ldb);
1388 ret_el->num_values = 1;
1389 ret_el->values[0] = data_blob_string_const(ldb_dn_get_extended_linearized(new_msg, ac->mod_dn, 1));
1391 /* a backlink should never be single valued. Unfortunately the
1392 exchange schema has a attribute
1393 msExchBridgeheadedLocalConnectorsDNBL which is single
1394 valued and a backlink. We need to cope with that by
1395 ignoring the single value flag */
1396 ret_el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
1399 ldb_debug(ldb, LDB_DEBUG_WARNING,
1400 "link on %s %s: %s %s\n",
1401 ldb_dn_get_linearized(new_msg->dn), ret_el->name,
1402 ret_el->values[0].data, ac->ops->op == LA_OP_ADD ? "added" : "deleted");
1406 DEBUG(4,("Applying linked attribute change:\n%s\n",
1407 ldb_ldif_message_redacted_string(ldb, op,
1408 LDB_CHANGETYPE_MODIFY,
1412 ret = dsdb_module_modify(module, new_msg, DSDB_FLAG_NEXT_MODULE, ac->req);
1413 if (ret != LDB_SUCCESS) {
1414 ldb_debug(ldb, LDB_DEBUG_WARNING, __location__ ": failed to apply linked attribute change '%s'\n%s\n",
1416 ldb_ldif_message_redacted_string(ldb, op,
1417 LDB_CHANGETYPE_MODIFY,
1424 /* apply one set of la_context changes */
1425 static int la_do_mod_request(struct ldb_module *module, struct la_context *ac)
1427 struct la_op_store *op;
1429 for (op = ac->ops; op; op=op->next) {
1430 int ret = la_do_op_request(module, ac, op);
1431 if (ret != LDB_SUCCESS) {
1432 if (ret != LDB_ERR_NO_SUCH_OBJECT) {
1443 we hook into the transaction operations to allow us to
1444 perform the linked attribute updates at the end of the whole
1445 transaction. This allows a forward linked attribute to be created
1446 before the target is created, as long as the target is created
1447 in the same transaction
1449 static int linked_attributes_start_transaction(struct ldb_module *module)
1451 /* create our private structure for this transaction */
1452 struct la_private *la_private =
1453 talloc_get_type(ldb_module_get_private(module),
1456 if (la_private == NULL) {
1457 return ldb_oom(ldb_module_get_ctx(module));
1459 talloc_free(la_private->transaction);
1460 la_private->transaction = talloc(module, struct la_private_transaction);
1461 if (la_private->transaction == NULL) {
1462 return ldb_oom(ldb_module_get_ctx(module));
1464 la_private->transaction->la_list = NULL;
1465 return ldb_next_start_trans(module);
1469 on prepare commit we loop over our queued la_context structures
1470 and apply each of them
1472 static int linked_attributes_prepare_commit(struct ldb_module *module)
1474 struct la_context *ac;
1475 struct la_private *la_private =
1476 talloc_get_type(ldb_module_get_private(module),
1478 if (la_private == NULL || la_private->transaction == NULL) {
1479 DBG_ERR("prepare_commit without begin_transaction\n");
1480 /* prepare commit without begin_transaction - let someone else
1481 * return the error, just don't segfault */
1482 return ldb_next_prepare_commit(module);
1484 /* walk the list backwards, to do the first entry first, as we
1485 * added the entries with DLIST_ADD() which puts them at the
1486 * start of the list */
1488 /* Start at the end of the list - so we can start
1489 * there, but ensure we don't create a loop by NULLing
1490 * it out in the first element */
1491 ac = DLIST_TAIL(la_private->transaction->la_list);
1493 for (; ac; ac=DLIST_PREV(ac)) {
1496 ret = la_do_mod_request(module, ac);
1497 if (ret != LDB_SUCCESS) {
1498 DEBUG(0,(__location__ ": Failed mod request ret=%d\n", ret));
1499 TALLOC_FREE(la_private->transaction);
1504 TALLOC_FREE(la_private->transaction);
1506 return ldb_next_prepare_commit(module);
1509 static int linked_attributes_del_transaction(struct ldb_module *module)
1511 struct la_private *la_private =
1512 talloc_get_type(ldb_module_get_private(module),
1514 TALLOC_FREE(la_private->transaction);
1515 return ldb_next_del_trans(module);
1518 static int linked_attributes_ldb_init(struct ldb_module *module)
1521 struct la_private *la_private = NULL;
1522 struct ldb_context *ldb = ldb_module_get_ctx(module);
1524 ret = ldb_mod_register_control(module, LDB_CONTROL_VERIFY_NAME_OID);
1525 if (ret != LDB_SUCCESS) {
1526 ldb_debug(ldb_module_get_ctx(module), LDB_DEBUG_ERROR,
1527 "verify_name: Unable to register control with rootdse!\n");
1528 return ldb_operr(ldb_module_get_ctx(module));
1531 la_private = talloc_zero(module, struct la_private);
1532 if (la_private == NULL) {
1534 return LDB_ERR_OPERATIONS_ERROR;
1537 ret = dsdb_check_samba_compatible_feature(module,
1538 SAMBA_SORTED_LINKS_FEATURE,
1539 &la_private->sorted_links);
1540 if (ret != LDB_SUCCESS) {
1541 talloc_free(la_private);
1545 ldb_module_set_private(module, la_private);
1546 return ldb_next_init(module);
1550 static const struct ldb_module_ops ldb_linked_attributes_module_ops = {
1551 .name = "linked_attributes",
1552 .add = linked_attributes_add,
1553 .modify = linked_attributes_modify,
1554 .rename = linked_attributes_rename,
1555 .init_context = linked_attributes_ldb_init,
1556 .start_transaction = linked_attributes_start_transaction,
1557 .prepare_commit = linked_attributes_prepare_commit,
1558 .del_transaction = linked_attributes_del_transaction,
1561 int ldb_linked_attributes_module_init(const char *version)
1563 LDB_MODULE_CHECK_VERSION(version);
1564 return ldb_register_module(&ldb_linked_attributes_module_ops);