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_context *la_list;
61 struct la_op_store *next;
62 struct la_op_store *prev;
63 enum la_op {LA_OP_ADD, LA_OP_DEL} op;
68 struct replace_context {
69 struct la_context *ac;
70 unsigned int num_elements;
71 struct ldb_message_element *el;
75 struct la_context *next, *prev;
76 const struct dsdb_schema *schema;
77 struct ldb_module *module;
78 struct ldb_request *req;
79 struct ldb_dn *mod_dn;
80 struct replace_context *rc;
81 struct la_op_store *ops;
82 struct ldb_extended *op_response;
83 struct ldb_control **op_controls;
86 * will tell which GC to use for resolving links
92 static int handle_verify_name_control(TALLOC_CTX *ctx, struct ldb_context *ldb,
93 struct ldb_control *control, struct la_context *ac)
96 * If we are a GC let's remove the control,
97 * if there is a specified GC check that is us.
99 struct ldb_verify_name_control *lvnc = (struct ldb_verify_name_control *)control->data;
100 if (samdb_is_gc(ldb)) {
101 /* Because we can't easily talloc a struct ldb_dn*/
102 struct ldb_dn **dn = talloc_array(ctx, struct ldb_dn *, 1);
103 int ret = samdb_server_reference_dn(ldb, ctx, dn);
106 if (ret != LDB_SUCCESS) {
107 return ldb_operr(ldb);
110 dns = samdb_dn_to_dnshostname(ldb, ctx, *dn);
112 return ldb_operr(ldb);
114 if (!lvnc->gc || strcasecmp(dns, lvnc->gc) == 0) {
115 if (!ldb_save_controls(control, ctx, NULL)) {
116 return ldb_operr(ldb);
119 control->critical = true;
123 /* For the moment we don't remove the control is this case in order
124 * to fail the request. It's better than having the client thinking
125 * that we honnor its control.
126 * Hopefully only a very small set of usecase should hit this problem.
129 ac->gc_dns_name = talloc_strdup(ac, lvnc->gc);
131 control->critical = true;
137 static struct la_context *linked_attributes_init(struct ldb_module *module,
138 struct ldb_request *req)
140 struct ldb_context *ldb;
141 struct la_context *ac;
143 ldb = ldb_module_get_ctx(module);
145 ac = talloc_zero(req, struct la_context);
151 ac->schema = dsdb_get_schema(ldb, ac);
159 turn a DN into a GUID
161 static int la_guid_from_dn(struct ldb_module *module,
162 struct ldb_request *parent,
163 struct ldb_dn *dn, struct GUID *guid)
168 status = dsdb_get_extended_dn_guid(dn, guid, "GUID");
169 if (NT_STATUS_IS_OK(status)) {
172 if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
173 DEBUG(4,(__location__ ": Unable to parse GUID for dn %s\n",
174 ldb_dn_get_linearized(dn)));
175 return ldb_operr(ldb_module_get_ctx(module));
178 ret = dsdb_module_guid_by_dn(module, dn, guid, parent);
179 if (ret != LDB_SUCCESS) {
180 DEBUG(4,(__location__ ": Failed to find GUID for dn %s\n",
181 ldb_dn_get_linearized(dn)));
188 /* Common routine to handle reading the attributes and creating a
189 * series of modify requests */
190 static int la_store_op(struct la_context *ac,
192 const struct dsdb_attribute *schema_attr,
196 struct ldb_context *ldb;
197 struct la_op_store *os;
198 struct ldb_dn *op_dn;
199 struct dsdb_dn *dsdb_dn;
202 ldb = ldb_module_get_ctx(ac->module);
205 os = talloc_zero(ac, struct la_op_store);
210 dsdb_dn = dsdb_dn_parse(os, ldb, dn, schema_attr->syntax->ldap_oid);
213 ldb_asprintf_errstring(ldb,
214 "could not parse attribute as a DN");
216 return LDB_ERR_INVALID_DN_SYNTAX;
223 ret = la_guid_from_dn(ac->module, ac->req, op_dn, &os->guid);
225 if (ret == LDB_ERR_NO_SUCH_OBJECT && ac->req->operation == LDB_DELETE) {
226 /* we are deleting an object, and we've found it has a
227 * forward link to a target that no longer
228 * exists. This is not an error in the delete, and we
229 * should just not do the deferred delete of the
235 if (ret != LDB_SUCCESS) {
239 os->name = talloc_strdup(os, name);
244 /* Do deletes before adds */
245 if (op == LA_OP_ADD) {
246 DLIST_ADD_END(ac->ops, os);
248 /* By adding to the head of the list, we do deletes before
249 * adds when processing a replace */
250 DLIST_ADD(ac->ops, os);
256 static int la_queue_mod_request(struct la_context *ac);
257 static int la_down_req(struct la_context *ac);
262 static int linked_attributes_add(struct ldb_module *module, struct ldb_request *req)
264 struct ldb_context *ldb;
265 const struct dsdb_attribute *target_attr;
266 struct la_context *ac;
267 const char *attr_name;
268 struct ldb_control *ctrl;
270 struct ldb_control *control;
273 ldb = ldb_module_get_ctx(module);
275 if (ldb_dn_is_special(req->op.add.message->dn)) {
276 /* do not manipulate our control entries */
277 return ldb_next_request(module, req);
280 ac = linked_attributes_init(module, req);
282 return ldb_operr(ldb);
285 control = ldb_request_get_control(req, LDB_CONTROL_VERIFY_NAME_OID);
286 if (control != NULL && control->data != NULL) {
287 ret = handle_verify_name_control(req, ldb, control, ac);
288 if (ret != LDB_SUCCESS) {
289 return ldb_operr(ldb);
293 if (!(ctrl = ldb_request_get_control(req, DSDB_CONTROL_APPLY_LINKS))) {
294 /* don't do anything special for linked attributes, repl_meta_data has done it */
296 return ldb_next_request(module, req);
298 ctrl->critical = false;
301 /* without schema, this doesn't make any sense */
303 return ldb_next_request(module, req);
307 /* Need to ensure we only have forward links being specified */
308 for (i=0; i < req->op.add.message->num_elements; i++) {
309 const struct ldb_message_element *el = &req->op.add.message->elements[i];
310 const struct dsdb_attribute *schema_attr
311 = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
313 ldb_asprintf_errstring(ldb,
314 "%s: attribute %s is not a valid attribute in schema",
317 return LDB_ERR_OBJECT_CLASS_VIOLATION;
320 /* this could be a link with no partner, in which case
321 there is no special work to do */
322 if (schema_attr->linkID == 0) {
326 /* this part of the code should only be handling forward links */
327 SMB_ASSERT((schema_attr->linkID & 1) == 0);
329 /* Even link IDs are for the originating attribute */
330 target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID ^ 1);
333 * windows 2003 has a broken schema where
334 * the definition of msDS-IsDomainFor
335 * is missing (which is supposed to be
336 * the backlink of the msDS-HasDomainNCs
342 attr_name = target_attr->lDAPDisplayName;
344 for (j = 0; j < el->num_values; j++) {
345 ret = la_store_op(ac, LA_OP_ADD,
349 if (ret != LDB_SUCCESS) {
355 /* if no linked attributes are present continue */
356 if (ac->ops == NULL) {
357 /* nothing to do for this module, proceed */
359 return ldb_next_request(module, req);
362 /* start with the original request */
363 return la_down_req(ac);
366 /* For a delete or rename, we need to find out what linked attributes
367 * are currently on this DN, and then deal with them. This is the
368 * callback to the base search */
370 static int la_mod_search_callback(struct ldb_request *req, struct ldb_reply *ares)
372 struct ldb_context *ldb;
373 const struct dsdb_attribute *schema_attr;
374 const struct dsdb_attribute *target_attr;
375 struct ldb_message_element *search_el;
376 struct replace_context *rc;
377 struct la_context *ac;
378 const char *attr_name;
380 int ret = LDB_SUCCESS;
382 ac = talloc_get_type(req->context, struct la_context);
383 ldb = ldb_module_get_ctx(ac->module);
387 return ldb_module_done(ac->req, NULL, NULL,
388 LDB_ERR_OPERATIONS_ERROR);
390 if (ares->error != LDB_SUCCESS) {
391 return ldb_module_done(ac->req, ares->controls,
392 ares->response, ares->error);
395 /* Only entries are interesting, and we only want the olddn */
396 switch (ares->type) {
397 case LDB_REPLY_ENTRY:
399 if (ldb_dn_compare(ares->message->dn, ac->req->op.mod.message->dn) != 0) {
400 ldb_asprintf_errstring(ldb,
401 "linked_attributes: %s is not the DN we were looking for",
402 ldb_dn_get_linearized(ares->message->dn));
403 /* Guh? We only asked for this DN */
405 return ldb_module_done(ac->req, NULL, NULL,
406 LDB_ERR_OPERATIONS_ERROR);
409 ac->mod_dn = talloc_steal(ac, ares->message->dn);
411 /* We don't populate 'rc' for ADD - it can't be deleting elements anyway */
412 for (i = 0; rc && i < rc->num_elements; i++) {
414 schema_attr = dsdb_attribute_by_lDAPDisplayName(ac->schema, rc->el[i].name);
416 ldb_asprintf_errstring(ldb,
417 "%s: attribute %s is not a valid attribute in schema",
421 return ldb_module_done(ac->req, NULL, NULL,
422 LDB_ERR_OBJECT_CLASS_VIOLATION);
425 search_el = ldb_msg_find_element(ares->message,
428 /* See if this element already exists */
429 /* otherwise just ignore as
430 * the add has already been scheduled */
435 target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID ^ 1);
438 * windows 2003 has a broken schema where
439 * the definition of msDS-IsDomainFor
440 * is missing (which is supposed to be
441 * the backlink of the msDS-HasDomainNCs
446 attr_name = target_attr->lDAPDisplayName;
448 /* Now we know what was there, we can remove it for the re-add */
449 for (j = 0; j < search_el->num_values; j++) {
450 ret = la_store_op(ac, LA_OP_DEL,
452 &search_el->values[j],
454 if (ret != LDB_SUCCESS) {
456 return ldb_module_done(ac->req,
464 case LDB_REPLY_REFERRAL:
472 if (ac->req->operation == LDB_ADD) {
473 /* Start the modifies to the backlinks */
474 ret = la_queue_mod_request(ac);
476 if (ret != LDB_SUCCESS) {
477 return ldb_module_done(ac->req, NULL, NULL,
481 /* Start with the original request */
482 ret = la_down_req(ac);
483 if (ret != LDB_SUCCESS) {
484 return ldb_module_done(ac->req, NULL, NULL, ret);
496 static int linked_attributes_modify(struct ldb_module *module, struct ldb_request *req)
498 /* Look over list of modifications */
499 /* Find if any are for linked attributes */
500 /* Determine the effect of the modification */
501 /* Apply the modify to the linked entry */
503 struct ldb_control *control;
504 struct ldb_context *ldb;
506 struct la_context *ac;
507 struct ldb_request *search_req;
509 struct ldb_control *ctrl;
512 ldb = ldb_module_get_ctx(module);
514 if (ldb_dn_is_special(req->op.mod.message->dn)) {
515 /* do not manipulate our control entries */
516 return ldb_next_request(module, req);
519 ac = linked_attributes_init(module, req);
521 return ldb_operr(ldb);
524 control = ldb_request_get_control(req, LDB_CONTROL_VERIFY_NAME_OID);
525 if (control != NULL && control->data != NULL) {
526 ret = handle_verify_name_control(req, ldb, control, ac);
527 if (ret != LDB_SUCCESS) {
528 return ldb_operr(ldb);
532 if (!(ctrl = ldb_request_get_control(req, DSDB_CONTROL_APPLY_LINKS))) {
533 /* don't do anything special for linked attributes, repl_meta_data has done it */
535 return ldb_next_request(module, req);
537 ctrl->critical = false;
540 /* without schema, this doesn't make any sense */
541 return ldb_next_request(module, req);
544 ac->rc = talloc_zero(ac, struct replace_context);
549 for (i=0; i < req->op.mod.message->num_elements; i++) {
550 bool store_el = false;
551 const char *attr_name;
552 const struct dsdb_attribute *target_attr;
553 const struct ldb_message_element *el = &req->op.mod.message->elements[i];
554 const struct dsdb_attribute *schema_attr
555 = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
557 ldb_asprintf_errstring(ldb,
558 "%s: attribute %s is not a valid attribute in schema",
561 return LDB_ERR_OBJECT_CLASS_VIOLATION;
563 /* We have a valid attribute, now find out if it is a forward link
564 (Even link IDs are for the originating attribute) */
565 if (schema_attr->linkID == 0) {
569 /* this part of the code should only be handling forward links */
570 SMB_ASSERT((schema_attr->linkID & 1) == 0);
572 /* Now find the target attribute */
573 target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID ^ 1);
576 * windows 2003 has a broken schema where
577 * the definition of msDS-IsDomainFor
578 * is missing (which is supposed to be
579 * the backlink of the msDS-HasDomainNCs
585 attr_name = target_attr->lDAPDisplayName;
587 switch (el->flags & LDB_FLAG_MOD_MASK) {
588 case LDB_FLAG_MOD_REPLACE:
589 /* treat as just a normal add the delete part is handled by the callback */
593 case LDB_FLAG_MOD_ADD:
595 /* For each value being added, we need to setup the adds */
596 for (j = 0; j < el->num_values; j++) {
597 ret = la_store_op(ac, LA_OP_ADD,
601 if (ret != LDB_SUCCESS) {
607 case LDB_FLAG_MOD_DELETE:
609 if (el->num_values) {
610 /* For each value being deleted, we need to setup the delete */
611 for (j = 0; j < el->num_values; j++) {
612 ret = la_store_op(ac, LA_OP_DEL,
616 if (ret != LDB_SUCCESS) {
621 /* Flag that there was a DELETE
622 * without a value specified, so we
623 * need to look for the old value */
631 struct ldb_message_element *search_el;
633 search_el = talloc_realloc(ac->rc, ac->rc->el,
634 struct ldb_message_element,
635 ac->rc->num_elements +1);
639 ac->rc->el = search_el;
641 ac->rc->el[ac->rc->num_elements] = *el;
642 ac->rc->num_elements++;
646 if (ac->ops || ac->rc->el) {
647 /* both replace and delete without values are handled in the callback
648 * after the search on the entry to be modified is performed */
650 attrs = talloc_array(ac->rc, const char *, ac->rc->num_elements + 1);
654 for (i = 0; ac->rc && i < ac->rc->num_elements; i++) {
655 attrs[i] = ac->rc->el[i].name;
659 /* The callback does all the hard work here */
660 ret = ldb_build_search_req(&search_req, ldb, ac,
661 req->op.mod.message->dn,
663 "(objectClass=*)", attrs,
665 ac, la_mod_search_callback,
667 LDB_REQ_SET_LOCATION(search_req);
669 /* We need to figure out our own extended DN, to fill in as the backlink target */
670 if (ret == LDB_SUCCESS) {
671 ret = dsdb_request_add_controls(search_req,
672 DSDB_SEARCH_SHOW_RECYCLED |
673 DSDB_SEARCH_SHOW_EXTENDED_DN);
675 if (ret == LDB_SUCCESS) {
676 talloc_steal(search_req, attrs);
678 ret = ldb_next_request(module, search_req);
682 /* nothing to do for this module, proceed */
684 ret = ldb_next_request(module, req);
690 static int linked_attributes_fix_links(struct ldb_module *module,
691 struct GUID self_guid,
692 struct ldb_dn *old_dn, struct ldb_dn *new_dn,
693 struct ldb_message_element *el, struct dsdb_schema *schema,
694 const struct dsdb_attribute *schema_attr,
695 struct ldb_request *parent)
698 TALLOC_CTX *tmp_ctx = NULL;
699 struct ldb_context *ldb = ldb_module_get_ctx(module);
700 const struct dsdb_attribute *target;
701 const char *attrs[2];
704 target = dsdb_attribute_by_linkID(schema, schema_attr->linkID ^ 1);
705 if (target == NULL) {
706 /* there is no counterpart link to change */
710 tmp_ctx = talloc_new(module);
711 if (tmp_ctx == NULL) {
712 return LDB_ERR_OPERATIONS_ERROR;
715 attrs[0] = target->lDAPDisplayName;
718 for (i=0; i<el->num_values; i++) {
719 struct dsdb_dn *dsdb_dn;
720 struct ldb_result *res;
721 struct ldb_message *msg;
722 struct ldb_message_element *el2;
723 struct GUID link_guid;
724 char *link_guid_str = NULL;
726 dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i], schema_attr->syntax->ldap_oid);
727 if (dsdb_dn == NULL) {
728 talloc_free(tmp_ctx);
729 return LDB_ERR_INVALID_DN_SYNTAX;
732 ret = la_guid_from_dn(module, parent, dsdb_dn->dn, &link_guid);
733 if (ret != LDB_SUCCESS) {
734 ldb_asprintf_errstring(ldb, "Linked attribute %s->%s between %s and %s - GUID not found - %s",
735 el->name, target->lDAPDisplayName,
736 ldb_dn_get_linearized(old_dn),
737 ldb_dn_get_linearized(dsdb_dn->dn),
739 talloc_free(tmp_ctx);
743 link_guid_str = GUID_string(tmp_ctx, &link_guid);
744 if (link_guid_str == NULL) {
745 talloc_free(tmp_ctx);
746 return LDB_ERR_OPERATIONS_ERROR;
750 * get the existing message from the db for the object with
751 * this GUID, returning attribute being modified. We will then
752 * use this msg as the basis for a modify call
755 ret = dsdb_module_search(module, tmp_ctx, &res, NULL, LDB_SCOPE_SUBTREE, attrs,
756 DSDB_FLAG_NEXT_MODULE |
757 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
758 DSDB_SEARCH_SHOW_RECYCLED |
759 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
760 DSDB_SEARCH_REVEAL_INTERNALS,
762 "objectGUID=%s", link_guid_str);
763 if (ret != LDB_SUCCESS) {
764 ldb_asprintf_errstring(ldb, "Linked attribute %s->%s between %s and %s - target GUID %s not found - %s",
765 el->name, target->lDAPDisplayName,
766 ldb_dn_get_linearized(old_dn),
767 ldb_dn_get_linearized(dsdb_dn->dn),
770 talloc_free(tmp_ctx);
773 if (res->count == 0) {
774 /* Forward link without backlink object remaining - nothing to do here */
777 if (res->count != 1) {
778 ldb_asprintf_errstring(ldb, "Linked attribute %s->%s between %s and %s - target GUID %s found more than once!",
779 el->name, target->lDAPDisplayName,
780 ldb_dn_get_linearized(old_dn),
781 ldb_dn_get_linearized(dsdb_dn->dn),
783 talloc_free(tmp_ctx);
784 return LDB_ERR_OPERATIONS_ERROR;
789 if (msg->num_elements == 0) {
790 /* Forward link without backlink remaining - nothing to do here */
792 } else if (msg->num_elements != 1) {
793 ldb_asprintf_errstring(ldb, "Bad msg elements - got %u elements, expected one element to be returned in linked_attributes_fix_links for %s",
794 msg->num_elements, ldb_dn_get_linearized(msg->dn));
795 talloc_free(tmp_ctx);
796 return LDB_ERR_OPERATIONS_ERROR;
798 if (ldb_attr_cmp(msg->elements[0].name, target->lDAPDisplayName) != 0) {
799 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));
800 talloc_free(tmp_ctx);
801 return LDB_ERR_OPERATIONS_ERROR;
803 el2 = &msg->elements[0];
805 el2->flags = LDB_FLAG_MOD_REPLACE;
807 /* find our DN in the values */
808 for (j=0; j<el2->num_values; j++) {
809 struct dsdb_dn *dsdb_dn2;
810 struct GUID link_guid2;
812 dsdb_dn2 = dsdb_dn_parse(msg, ldb, &el2->values[j], target->syntax->ldap_oid);
813 if (dsdb_dn2 == NULL) {
814 talloc_free(tmp_ctx);
815 return LDB_ERR_INVALID_DN_SYNTAX;
818 ret = la_guid_from_dn(module, parent, dsdb_dn2->dn, &link_guid2);
819 if (ret != LDB_SUCCESS) {
820 talloc_free(tmp_ctx);
825 * By comparing using the GUID we ensure that
826 * even if somehow the name has got out of
827 * sync, this rename will fix it.
829 * If somehow we don't have a GUID on the DN
830 * in the DB, the la_guid_from_dn call will be
831 * more costly, but still give us a GUID.
832 * dbcheck will fix this if run.
834 if (!GUID_equal(&self_guid, &link_guid2)) {
838 ret = ldb_dn_update_components(dsdb_dn2->dn, new_dn);
839 if (ret != LDB_SUCCESS) {
840 talloc_free(tmp_ctx);
844 el2->values[j] = data_blob_string_const(
845 dsdb_dn_get_extended_linearized(el2->values, dsdb_dn2, 1));
848 ret = dsdb_check_single_valued_link(target, el2);
849 if (ret != LDB_SUCCESS) {
850 talloc_free(tmp_ctx);
854 /* we may be putting multiple values in an attribute -
855 disable checking for this attribute */
856 el2->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
858 ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE, parent);
859 if (ret != LDB_SUCCESS) {
860 ldb_asprintf_errstring(ldb, "Linked attribute %s->%s between %s and %s - update failed - %s",
861 el->name, target->lDAPDisplayName,
862 ldb_dn_get_linearized(old_dn),
863 ldb_dn_get_linearized(dsdb_dn->dn),
865 talloc_free(tmp_ctx);
870 talloc_free(tmp_ctx);
876 static int linked_attributes_rename(struct ldb_module *module, struct ldb_request *req)
878 struct ldb_result *res;
879 struct ldb_message *msg;
881 struct ldb_context *ldb = ldb_module_get_ctx(module);
882 struct dsdb_schema *schema;
887 - load the current msg
888 - find any linked attributes
889 - if its a link then find the target object
890 - modify the target linked attributes with the new DN
892 ret = dsdb_module_search_dn(module, req, &res, req->op.rename.olddn,
894 DSDB_FLAG_NEXT_MODULE |
895 DSDB_SEARCH_SHOW_EXTENDED_DN |
896 DSDB_SEARCH_SHOW_RECYCLED, req);
897 if (ret != LDB_SUCCESS) {
901 schema = dsdb_get_schema(ldb, res);
908 ret = la_guid_from_dn(module, req, msg->dn, &guid);
909 if (ret != LDB_SUCCESS) {
913 for (i=0; i<msg->num_elements; i++) {
914 struct ldb_message_element *el = &msg->elements[i];
915 const struct dsdb_attribute *schema_attr
916 = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
917 if (!schema_attr || schema_attr->linkID == 0) {
920 ret = linked_attributes_fix_links(module, guid, msg->dn, req->op.rename.newdn, el,
921 schema, schema_attr, req);
922 if (ret != LDB_SUCCESS) {
930 return ldb_next_request(module, req);
934 /* queue a linked attributes modify request in the la_private
936 static int la_queue_mod_request(struct la_context *ac)
938 struct la_private *la_private =
939 talloc_get_type(ldb_module_get_private(ac->module), struct la_private);
941 if (la_private == NULL) {
942 ldb_debug(ldb_module_get_ctx(ac->module), LDB_DEBUG_ERROR, __location__ ": No la_private transaction setup\n");
943 return ldb_operr(ldb_module_get_ctx(ac->module));
946 talloc_steal(la_private, ac);
947 DLIST_ADD(la_private->la_list, ac);
949 return ldb_module_done(ac->req, ac->op_controls,
950 ac->op_response, LDB_SUCCESS);
953 /* Having done the original operation, then try to fix up all the linked attributes for modify and delete */
954 static int la_mod_del_callback(struct ldb_request *req, struct ldb_reply *ares)
956 struct la_context *ac;
957 struct ldb_context *ldb;
960 ac = talloc_get_type(req->context, struct la_context);
961 ldb = ldb_module_get_ctx(ac->module);
964 return ldb_module_done(ac->req, NULL, NULL,
965 LDB_ERR_OPERATIONS_ERROR);
967 if (ares->error != LDB_SUCCESS) {
968 return ldb_module_done(ac->req, ares->controls,
969 ares->response, ares->error);
972 if (ares->type != LDB_REPLY_DONE) {
973 ldb_set_errstring(ldb,
974 "invalid ldb_reply_type in callback");
976 return ldb_module_done(ac->req, NULL, NULL,
977 LDB_ERR_OPERATIONS_ERROR);
980 ac->op_controls = talloc_steal(ac, ares->controls);
981 ac->op_response = talloc_steal(ac, ares->response);
983 /* If we have modfies to make, this is the time to do them for modify and delete */
984 ret = la_queue_mod_request(ac);
986 if (ret != LDB_SUCCESS) {
987 return ldb_module_done(ac->req, NULL, NULL, ret);
991 /* la_queue_mod_request has already sent the callbacks */
996 /* Having done the original add, then try to fix up all the linked attributes
998 This is done after the add so the links can get the extended DNs correctly.
1000 static int la_add_callback(struct ldb_request *req, struct ldb_reply *ares)
1002 struct la_context *ac;
1003 struct ldb_context *ldb;
1006 ac = talloc_get_type(req->context, struct la_context);
1007 ldb = ldb_module_get_ctx(ac->module);
1010 return ldb_module_done(ac->req, NULL, NULL,
1011 LDB_ERR_OPERATIONS_ERROR);
1013 if (ares->error != LDB_SUCCESS) {
1014 return ldb_module_done(ac->req, ares->controls,
1015 ares->response, ares->error);
1018 if (ares->type != LDB_REPLY_DONE) {
1019 ldb_set_errstring(ldb,
1020 "invalid ldb_reply_type in callback");
1022 return ldb_module_done(ac->req, NULL, NULL,
1023 LDB_ERR_OPERATIONS_ERROR);
1027 struct ldb_request *search_req;
1028 static const char *attrs[] = { NULL };
1030 /* The callback does all the hard work here - we need
1031 * the objectGUID and SID of the added record */
1032 ret = ldb_build_search_req(&search_req, ldb, ac,
1033 ac->req->op.add.message->dn,
1035 "(objectClass=*)", attrs,
1037 ac, la_mod_search_callback,
1039 LDB_REQ_SET_LOCATION(search_req);
1041 if (ret == LDB_SUCCESS) {
1042 ret = dsdb_request_add_controls(search_req,
1043 DSDB_SEARCH_SHOW_RECYCLED |
1044 DSDB_SEARCH_SHOW_EXTENDED_DN);
1046 if (ret != LDB_SUCCESS) {
1047 return ldb_module_done(ac->req, NULL, NULL,
1051 ac->op_controls = talloc_steal(ac, ares->controls);
1052 ac->op_response = talloc_steal(ac, ares->response);
1054 return ldb_next_request(ac->module, search_req);
1057 return ldb_module_done(ac->req, ares->controls,
1058 ares->response, ares->error);
1062 /* Reconstruct the original request, but pointing at our local callback to finish things off */
1063 static int la_down_req(struct la_context *ac)
1065 struct ldb_request *down_req;
1066 struct ldb_context *ldb;
1069 ldb = ldb_module_get_ctx(ac->module);
1071 switch (ac->req->operation) {
1073 ret = ldb_build_add_req(&down_req, ldb, ac,
1074 ac->req->op.add.message,
1076 ac, la_add_callback,
1078 LDB_REQ_SET_LOCATION(down_req);
1081 ret = ldb_build_mod_req(&down_req, ldb, ac,
1082 ac->req->op.mod.message,
1084 ac, la_mod_del_callback,
1086 LDB_REQ_SET_LOCATION(down_req);
1089 ret = LDB_ERR_OPERATIONS_ERROR;
1091 if (ret != LDB_SUCCESS) {
1095 return ldb_next_request(ac->module, down_req);
1099 use the GUID part of an extended DN to find the target DN, in case
1102 static int la_find_dn_target(struct ldb_module *module, struct la_context *ac,
1103 struct GUID *guid, struct ldb_dn **dn)
1105 return dsdb_module_dn_by_guid(ac->module, ac, guid, dn, ac->req);
1108 /* apply one la_context op change */
1109 static int la_do_op_request(struct ldb_module *module, struct la_context *ac, struct la_op_store *op)
1111 struct ldb_message_element *ret_el;
1112 struct ldb_message *new_msg;
1113 struct ldb_context *ldb;
1116 if (ac->mod_dn == NULL) {
1117 /* we didn't find the DN that we searched for */
1121 ldb = ldb_module_get_ctx(ac->module);
1123 /* Create the modify request */
1124 new_msg = ldb_msg_new(ac);
1126 return ldb_oom(ldb);
1129 ret = la_find_dn_target(module, ac, &op->guid, &new_msg->dn);
1130 if (ret != LDB_SUCCESS) {
1134 if (op->op == LA_OP_ADD) {
1135 ret = ldb_msg_add_empty(new_msg, op->name,
1136 LDB_FLAG_MOD_ADD, &ret_el);
1138 ret = ldb_msg_add_empty(new_msg, op->name,
1139 LDB_FLAG_MOD_DELETE, &ret_el);
1141 if (ret != LDB_SUCCESS) {
1144 ret_el->values = talloc_array(new_msg, struct ldb_val, 1);
1145 if (!ret_el->values) {
1146 return ldb_oom(ldb);
1148 ret_el->num_values = 1;
1149 ret_el->values[0] = data_blob_string_const(ldb_dn_get_extended_linearized(new_msg, ac->mod_dn, 1));
1151 /* a backlink should never be single valued. Unfortunately the
1152 exchange schema has a attribute
1153 msExchBridgeheadedLocalConnectorsDNBL which is single
1154 valued and a backlink. We need to cope with that by
1155 ignoring the single value flag */
1156 ret_el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
1159 ldb_debug(ldb, LDB_DEBUG_WARNING,
1160 "link on %s %s: %s %s\n",
1161 ldb_dn_get_linearized(new_msg->dn), ret_el->name,
1162 ret_el->values[0].data, ac->ops->op == LA_OP_ADD ? "added" : "deleted");
1166 DEBUG(4,("Applying linked attribute change:\n%s\n",
1167 ldb_ldif_message_redacted_string(ldb, op,
1168 LDB_CHANGETYPE_MODIFY,
1172 ret = dsdb_module_modify(module, new_msg, DSDB_FLAG_NEXT_MODULE, ac->req);
1173 if (ret != LDB_SUCCESS) {
1174 ldb_debug(ldb, LDB_DEBUG_WARNING, __location__ ": failed to apply linked attribute change '%s'\n%s\n",
1176 ldb_ldif_message_redacted_string(ldb, op,
1177 LDB_CHANGETYPE_MODIFY,
1184 /* apply one set of la_context changes */
1185 static int la_do_mod_request(struct ldb_module *module, struct la_context *ac)
1187 struct la_op_store *op;
1189 for (op = ac->ops; op; op=op->next) {
1190 int ret = la_do_op_request(module, ac, op);
1191 if (ret != LDB_SUCCESS) {
1192 if (ret != LDB_ERR_NO_SUCH_OBJECT) {
1203 we hook into the transaction operations to allow us to
1204 perform the linked attribute updates at the end of the whole
1205 transaction. This allows a forward linked attribute to be created
1206 before the target is created, as long as the target is created
1207 in the same transaction
1209 static int linked_attributes_start_transaction(struct ldb_module *module)
1211 /* create our private structure for this transaction */
1212 struct la_private *la_private = talloc_get_type(ldb_module_get_private(module),
1214 talloc_free(la_private);
1215 la_private = talloc(module, struct la_private);
1216 if (la_private == NULL) {
1217 return ldb_oom(ldb_module_get_ctx(module));
1219 la_private->la_list = NULL;
1220 ldb_module_set_private(module, la_private);
1221 return ldb_next_start_trans(module);
1225 on prepare commit we loop over our queued la_context structures
1226 and apply each of them
1228 static int linked_attributes_prepare_commit(struct ldb_module *module)
1230 struct la_private *la_private =
1231 talloc_get_type(ldb_module_get_private(module), struct la_private);
1232 struct la_context *ac;
1235 /* prepare commit without begin_transaction - let someone else return the error, just don't segfault */
1236 return ldb_next_prepare_commit(module);
1238 /* walk the list backwards, to do the first entry first, as we
1239 * added the entries with DLIST_ADD() which puts them at the
1240 * start of the list */
1242 /* Start at the end of the list - so we can start
1243 * there, but ensure we don't create a loop by NULLing
1244 * it out in the first element */
1245 ac = DLIST_TAIL(la_private->la_list);
1247 for (; ac; ac=DLIST_PREV(ac)) {
1250 ret = la_do_mod_request(module, ac);
1251 if (ret != LDB_SUCCESS) {
1252 DEBUG(0,(__location__ ": Failed mod request ret=%d\n", ret));
1253 talloc_free(la_private);
1254 ldb_module_set_private(module, NULL);
1259 talloc_free(la_private);
1260 ldb_module_set_private(module, NULL);
1262 return ldb_next_prepare_commit(module);
1265 static int linked_attributes_del_transaction(struct ldb_module *module)
1267 struct la_private *la_private =
1268 talloc_get_type(ldb_module_get_private(module), struct la_private);
1269 talloc_free(la_private);
1270 ldb_module_set_private(module, NULL);
1271 return ldb_next_del_trans(module);
1274 static int linked_attributes_ldb_init(struct ldb_module *module)
1278 ret = ldb_mod_register_control(module, LDB_CONTROL_VERIFY_NAME_OID);
1279 if (ret != LDB_SUCCESS) {
1280 ldb_debug(ldb_module_get_ctx(module), LDB_DEBUG_ERROR,
1281 "verify_name: Unable to register control with rootdse!\n");
1282 return ldb_operr(ldb_module_get_ctx(module));
1285 return ldb_next_init(module);
1289 static const struct ldb_module_ops ldb_linked_attributes_module_ops = {
1290 .name = "linked_attributes",
1291 .add = linked_attributes_add,
1292 .modify = linked_attributes_modify,
1293 .rename = linked_attributes_rename,
1294 .init_context = linked_attributes_ldb_init,
1295 .start_transaction = linked_attributes_start_transaction,
1296 .prepare_commit = linked_attributes_prepare_commit,
1297 .del_transaction = linked_attributes_del_transaction,
1300 int ldb_linked_attributes_module_init(const char *version)
1302 LDB_MODULE_CHECK_VERSION(version);
1303 return ldb_register_module(&ldb_linked_attributes_module_ops);