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"
58 struct la_private_transaction {
59 struct la_context *la_list;
64 struct la_private_transaction *transaction;
69 struct la_op_store *next;
70 struct la_op_store *prev;
71 enum la_op {LA_OP_ADD, LA_OP_DEL} op;
76 struct replace_context {
77 struct la_context *ac;
78 unsigned int num_elements;
79 struct ldb_message_element *el;
83 struct la_context *next, *prev;
84 const struct dsdb_schema *schema;
85 struct ldb_module *module;
86 struct ldb_request *req;
87 struct ldb_dn *mod_dn;
88 struct replace_context *rc;
89 struct la_op_store *ops;
90 struct ldb_extended *op_response;
91 struct ldb_control **op_controls;
94 * will tell which GC to use for resolving links
100 static int handle_verify_name_control(TALLOC_CTX *ctx, struct ldb_context *ldb,
101 struct ldb_control *control, struct la_context *ac)
104 * If we are a GC let's remove the control,
105 * if there is a specified GC check that is us.
107 struct ldb_verify_name_control *lvnc = (struct ldb_verify_name_control *)control->data;
108 if (samdb_is_gc(ldb)) {
109 /* Because we can't easily talloc a struct ldb_dn*/
110 struct ldb_dn **dn = talloc_array(ctx, struct ldb_dn *, 1);
111 int ret = samdb_server_reference_dn(ldb, ctx, dn);
114 if (ret != LDB_SUCCESS) {
115 return ldb_operr(ldb);
118 dns = samdb_dn_to_dnshostname(ldb, ctx, *dn);
120 return ldb_operr(ldb);
122 if (!lvnc->gc || strcasecmp(dns, lvnc->gc) == 0) {
123 if (!ldb_save_controls(control, ctx, NULL)) {
124 return ldb_operr(ldb);
127 control->critical = true;
131 /* For the moment we don't remove the control is this case in order
132 * to fail the request. It's better than having the client thinking
133 * that we honnor its control.
134 * Hopefully only a very small set of usecase should hit this problem.
137 ac->gc_dns_name = talloc_strdup(ac, lvnc->gc);
139 control->critical = true;
145 static struct la_context *linked_attributes_init(struct ldb_module *module,
146 struct ldb_request *req)
148 struct ldb_context *ldb;
149 struct la_context *ac;
151 ldb = ldb_module_get_ctx(module);
153 ac = talloc_zero(req, struct la_context);
159 ac->schema = dsdb_get_schema(ldb, ac);
167 turn a DN into a GUID
169 static int la_guid_from_dn(struct ldb_module *module,
170 struct ldb_request *parent,
171 struct ldb_dn *dn, struct GUID *guid)
176 status = dsdb_get_extended_dn_guid(dn, guid, "GUID");
177 if (NT_STATUS_IS_OK(status)) {
180 if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
181 DEBUG(4,(__location__ ": Unable to parse GUID for dn %s\n",
182 ldb_dn_get_linearized(dn)));
183 return ldb_operr(ldb_module_get_ctx(module));
186 ret = dsdb_module_guid_by_dn(module, dn, guid, parent);
187 if (ret != LDB_SUCCESS) {
188 DEBUG(4,(__location__ ": Failed to find GUID for dn %s\n",
189 ldb_dn_get_linearized(dn)));
196 /* Common routine to handle reading the attributes and creating a
197 * series of modify requests */
198 static int la_store_op(struct la_context *ac,
200 const struct dsdb_attribute *schema_attr,
204 struct ldb_context *ldb;
205 struct la_op_store *os;
206 struct ldb_dn *op_dn;
207 struct dsdb_dn *dsdb_dn;
210 ldb = ldb_module_get_ctx(ac->module);
213 os = talloc_zero(ac, struct la_op_store);
218 dsdb_dn = dsdb_dn_parse(os, ldb, dn, schema_attr->syntax->ldap_oid);
221 ldb_asprintf_errstring(ldb,
222 "could not parse attribute as a DN");
224 return LDB_ERR_INVALID_DN_SYNTAX;
231 ret = la_guid_from_dn(ac->module, ac->req, op_dn, &os->guid);
233 if (ret == LDB_ERR_NO_SUCH_OBJECT && ac->req->operation == LDB_DELETE) {
234 /* we are deleting an object, and we've found it has a
235 * forward link to a target that no longer
236 * exists. This is not an error in the delete, and we
237 * should just not do the deferred delete of the
243 if (ret != LDB_SUCCESS) {
247 os->name = talloc_strdup(os, name);
252 /* Do deletes before adds */
253 if (op == LA_OP_ADD) {
254 DLIST_ADD_END(ac->ops, os);
256 /* By adding to the head of the list, we do deletes before
257 * adds when processing a replace */
258 DLIST_ADD(ac->ops, os);
264 static int la_queue_mod_request(struct la_context *ac);
265 static int la_down_req(struct la_context *ac);
270 static int linked_attributes_add(struct ldb_module *module, struct ldb_request *req)
272 struct ldb_context *ldb;
273 const struct dsdb_attribute *target_attr;
274 struct la_context *ac;
275 const char *attr_name;
276 struct ldb_control *ctrl;
278 struct ldb_control *control;
281 ldb = ldb_module_get_ctx(module);
283 if (ldb_dn_is_special(req->op.add.message->dn)) {
284 /* do not manipulate our control entries */
285 return ldb_next_request(module, req);
288 ac = linked_attributes_init(module, req);
290 return ldb_operr(ldb);
293 control = ldb_request_get_control(req, LDB_CONTROL_VERIFY_NAME_OID);
294 if (control != NULL && control->data != NULL) {
295 ret = handle_verify_name_control(req, ldb, control, ac);
296 if (ret != LDB_SUCCESS) {
297 return ldb_operr(ldb);
301 if (!(ctrl = ldb_request_get_control(req, DSDB_CONTROL_APPLY_LINKS))) {
302 /* don't do anything special for linked attributes, repl_meta_data has done it */
304 return ldb_next_request(module, req);
306 ctrl->critical = false;
309 /* without schema, this doesn't make any sense */
311 return ldb_next_request(module, req);
315 /* Need to ensure we only have forward links being specified */
316 for (i=0; i < req->op.add.message->num_elements; i++) {
317 const struct ldb_message_element *el = &req->op.add.message->elements[i];
318 const struct dsdb_attribute *schema_attr
319 = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
321 ldb_asprintf_errstring(ldb,
322 "%s: attribute %s is not a valid attribute in schema",
325 return LDB_ERR_OBJECT_CLASS_VIOLATION;
328 /* this could be a link with no partner, in which case
329 there is no special work to do */
330 if (schema_attr->linkID == 0) {
334 /* this part of the code should only be handling forward links */
335 SMB_ASSERT((schema_attr->linkID & 1) == 0);
337 /* Even link IDs are for the originating attribute */
338 target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID ^ 1);
341 * windows 2003 has a broken schema where
342 * the definition of msDS-IsDomainFor
343 * is missing (which is supposed to be
344 * the backlink of the msDS-HasDomainNCs
350 attr_name = target_attr->lDAPDisplayName;
352 for (j = 0; j < el->num_values; j++) {
353 ret = la_store_op(ac, LA_OP_ADD,
357 if (ret != LDB_SUCCESS) {
363 /* if no linked attributes are present continue */
364 if (ac->ops == NULL) {
365 /* nothing to do for this module, proceed */
367 return ldb_next_request(module, req);
370 /* start with the original request */
371 return la_down_req(ac);
374 /* For a delete or rename, we need to find out what linked attributes
375 * are currently on this DN, and then deal with them. This is the
376 * callback to the base search */
378 static int la_mod_search_callback(struct ldb_request *req, struct ldb_reply *ares)
380 struct ldb_context *ldb;
381 const struct dsdb_attribute *schema_attr;
382 const struct dsdb_attribute *target_attr;
383 struct ldb_message_element *search_el;
384 struct replace_context *rc;
385 struct la_context *ac;
386 const char *attr_name;
388 int ret = LDB_SUCCESS;
390 ac = talloc_get_type(req->context, struct la_context);
391 ldb = ldb_module_get_ctx(ac->module);
395 return ldb_module_done(ac->req, NULL, NULL,
396 LDB_ERR_OPERATIONS_ERROR);
398 if (ares->error != LDB_SUCCESS) {
399 return ldb_module_done(ac->req, ares->controls,
400 ares->response, ares->error);
403 /* Only entries are interesting, and we only want the olddn */
404 switch (ares->type) {
405 case LDB_REPLY_ENTRY:
407 if (ldb_dn_compare(ares->message->dn, ac->req->op.mod.message->dn) != 0) {
408 ldb_asprintf_errstring(ldb,
409 "linked_attributes: %s is not the DN we were looking for",
410 ldb_dn_get_linearized(ares->message->dn));
411 /* Guh? We only asked for this DN */
413 return ldb_module_done(ac->req, NULL, NULL,
414 LDB_ERR_OPERATIONS_ERROR);
417 ac->mod_dn = talloc_steal(ac, ares->message->dn);
419 /* We don't populate 'rc' for ADD - it can't be deleting elements anyway */
420 for (i = 0; rc && i < rc->num_elements; i++) {
422 schema_attr = dsdb_attribute_by_lDAPDisplayName(ac->schema, rc->el[i].name);
424 ldb_asprintf_errstring(ldb,
425 "%s: attribute %s is not a valid attribute in schema",
429 return ldb_module_done(ac->req, NULL, NULL,
430 LDB_ERR_OBJECT_CLASS_VIOLATION);
433 search_el = ldb_msg_find_element(ares->message,
436 /* See if this element already exists */
437 /* otherwise just ignore as
438 * the add has already been scheduled */
443 target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID ^ 1);
446 * windows 2003 has a broken schema where
447 * the definition of msDS-IsDomainFor
448 * is missing (which is supposed to be
449 * the backlink of the msDS-HasDomainNCs
454 attr_name = target_attr->lDAPDisplayName;
456 /* Now we know what was there, we can remove it for the re-add */
457 for (j = 0; j < search_el->num_values; j++) {
458 ret = la_store_op(ac, LA_OP_DEL,
460 &search_el->values[j],
462 if (ret != LDB_SUCCESS) {
464 return ldb_module_done(ac->req,
472 case LDB_REPLY_REFERRAL:
480 if (ac->req->operation == LDB_ADD) {
481 /* Start the modifies to the backlinks */
482 ret = la_queue_mod_request(ac);
484 if (ret != LDB_SUCCESS) {
485 return ldb_module_done(ac->req, NULL, NULL,
489 /* Start with the original request */
490 ret = la_down_req(ac);
491 if (ret != LDB_SUCCESS) {
492 return ldb_module_done(ac->req, NULL, NULL, ret);
504 static int linked_attributes_modify(struct ldb_module *module, struct ldb_request *req)
506 /* Look over list of modifications */
507 /* Find if any are for linked attributes */
508 /* Determine the effect of the modification */
509 /* Apply the modify to the linked entry */
511 struct ldb_control *control;
512 struct ldb_context *ldb;
514 struct la_context *ac;
515 struct ldb_request *search_req;
517 struct ldb_control *ctrl;
520 ldb = ldb_module_get_ctx(module);
522 if (ldb_dn_is_special(req->op.mod.message->dn)) {
523 /* do not manipulate our control entries */
524 return ldb_next_request(module, req);
527 ac = linked_attributes_init(module, req);
529 return ldb_operr(ldb);
532 control = ldb_request_get_control(req, LDB_CONTROL_VERIFY_NAME_OID);
533 if (control != NULL && control->data != NULL) {
534 ret = handle_verify_name_control(req, ldb, control, ac);
535 if (ret != LDB_SUCCESS) {
536 return ldb_operr(ldb);
540 if (!(ctrl = ldb_request_get_control(req, DSDB_CONTROL_APPLY_LINKS))) {
541 /* don't do anything special for linked attributes, repl_meta_data has done it */
543 return ldb_next_request(module, req);
545 ctrl->critical = false;
548 /* without schema, this doesn't make any sense */
549 return ldb_next_request(module, req);
552 ac->rc = talloc_zero(ac, struct replace_context);
557 for (i=0; i < req->op.mod.message->num_elements; i++) {
558 bool store_el = false;
559 const char *attr_name;
560 const struct dsdb_attribute *target_attr;
561 const struct ldb_message_element *el = &req->op.mod.message->elements[i];
562 const struct dsdb_attribute *schema_attr
563 = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
565 ldb_asprintf_errstring(ldb,
566 "%s: attribute %s is not a valid attribute in schema",
569 return LDB_ERR_OBJECT_CLASS_VIOLATION;
571 /* We have a valid attribute, now find out if it is a forward link
572 (Even link IDs are for the originating attribute) */
573 if (schema_attr->linkID == 0) {
577 /* this part of the code should only be handling forward links */
578 SMB_ASSERT((schema_attr->linkID & 1) == 0);
580 /* Now find the target attribute */
581 target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID ^ 1);
584 * windows 2003 has a broken schema where
585 * the definition of msDS-IsDomainFor
586 * is missing (which is supposed to be
587 * the backlink of the msDS-HasDomainNCs
593 attr_name = target_attr->lDAPDisplayName;
595 switch (el->flags & LDB_FLAG_MOD_MASK) {
596 case LDB_FLAG_MOD_REPLACE:
597 /* treat as just a normal add the delete part is handled by the callback */
601 case LDB_FLAG_MOD_ADD:
603 /* For each value being added, we need to setup the adds */
604 for (j = 0; j < el->num_values; j++) {
605 ret = la_store_op(ac, LA_OP_ADD,
609 if (ret != LDB_SUCCESS) {
615 case LDB_FLAG_MOD_DELETE:
617 if (el->num_values) {
618 /* For each value being deleted, we need to setup the delete */
619 for (j = 0; j < el->num_values; j++) {
620 ret = la_store_op(ac, LA_OP_DEL,
624 if (ret != LDB_SUCCESS) {
629 /* Flag that there was a DELETE
630 * without a value specified, so we
631 * need to look for the old value */
639 struct ldb_message_element *search_el;
641 search_el = talloc_realloc(ac->rc, ac->rc->el,
642 struct ldb_message_element,
643 ac->rc->num_elements +1);
647 ac->rc->el = search_el;
649 ac->rc->el[ac->rc->num_elements] = *el;
650 ac->rc->num_elements++;
654 if (ac->ops || ac->rc->el) {
655 /* both replace and delete without values are handled in the callback
656 * after the search on the entry to be modified is performed */
658 attrs = talloc_array(ac->rc, const char *, ac->rc->num_elements + 1);
662 for (i = 0; i < ac->rc->num_elements; i++) {
663 attrs[i] = ac->rc->el[i].name;
667 /* The callback does all the hard work here */
668 ret = ldb_build_search_req(&search_req, ldb, ac,
669 req->op.mod.message->dn,
671 "(objectClass=*)", attrs,
673 ac, la_mod_search_callback,
675 LDB_REQ_SET_LOCATION(search_req);
677 /* We need to figure out our own extended DN, to fill in as the backlink target */
678 if (ret == LDB_SUCCESS) {
679 ret = dsdb_request_add_controls(search_req,
680 DSDB_SEARCH_SHOW_RECYCLED |
681 DSDB_SEARCH_SHOW_EXTENDED_DN);
683 if (ret == LDB_SUCCESS) {
684 talloc_steal(search_req, attrs);
686 ret = ldb_next_request(module, search_req);
690 /* nothing to do for this module, proceed */
692 ret = ldb_next_request(module, req);
699 static int linked_attributes_fix_link_slow(struct ldb_module *module,
700 struct ldb_request *parent,
701 struct ldb_message *msg,
702 struct ldb_dn *new_dn,
703 struct GUID self_guid,
704 const char *syntax_oid,
705 const char *reverse_syntax_oid)
709 struct GUID link_guid;
710 struct ldb_message_element *el = &msg->elements[0];
711 struct ldb_context *ldb = ldb_module_get_ctx(module);
712 bool has_unique_value = strcmp(reverse_syntax_oid, LDB_SYNTAX_DN) == 0;
713 TALLOC_CTX *tmp_ctx = talloc_new(module);
714 if (tmp_ctx == NULL) {
715 return LDB_ERR_OPERATIONS_ERROR;
718 * The msg has one element (el) containing links of one particular
719 * type from the remote object. We know that at least one of those
720 * links points to the object being renamed (identified by self_guid,
721 * renamed to new_dn). Usually only one of the links will point back
722 * to renamed object, but there can be more when the reverse link is a
725 * This is used for unsorted links, which is to say back links and
726 * forward links on old databases. It necessarily involves a linear
727 * search, though when the link is a plain DN link, we can skip
728 * checking as soon as we find it.
730 * NOTE: if there are duplicate links, the extra ones will end up as
731 * dangling links to the old DN. This may or may not be worse than
732 * leaving them as duplicate links.
734 for (i = 0; i < el->num_values; i++) {
735 struct dsdb_dn *dsdb_dn = dsdb_dn_parse(msg,
739 if (dsdb_dn == NULL) {
740 talloc_free(tmp_ctx);
741 return LDB_ERR_INVALID_DN_SYNTAX;
744 ret = la_guid_from_dn(module, parent, dsdb_dn->dn, &link_guid);
745 if (ret != LDB_SUCCESS) {
746 talloc_free(tmp_ctx);
751 * By comparing using the GUID we ensure that even if somehow
752 * the name has got out of sync, this rename will fix it.
754 * If somehow we don't have a GUID on the DN in the DB, the
755 * la_guid_from_dn call will be more costly, but still give us
756 * a GUID. dbcheck will fix this if run.
758 if (!GUID_equal(&self_guid, &link_guid)) {
762 ret = ldb_dn_update_components(dsdb_dn->dn, new_dn);
763 if (ret != LDB_SUCCESS) {
764 talloc_free(tmp_ctx);
768 el->values[i] = data_blob_string_const(
769 dsdb_dn_get_extended_linearized(el->values, dsdb_dn, 1));
770 if (has_unique_value) {
775 talloc_free(tmp_ctx);
780 static int linked_attributes_fix_forward_link(struct ldb_module *module,
781 struct ldb_message *msg,
782 struct ldb_dn *new_dn,
783 struct GUID self_guid,
784 const char *syntax_oid)
787 struct ldb_context *ldb = ldb_module_get_ctx(module);
788 struct parsed_dn *pdn_list = NULL;
789 struct parsed_dn *exact = NULL;
790 struct parsed_dn *next = NULL;
792 struct ldb_message_element *el = &msg->elements[0];
793 unsigned int num_parsed_dns = el->num_values;
795 TALLOC_CTX *tmp_ctx = talloc_new(module);
796 if (tmp_ctx == NULL) {
797 return LDB_ERR_OPERATIONS_ERROR;
801 * The msg has a single element (el) containing forward links which we
802 * trust are sorted in GUID order. We know that at least one of those
803 * links points to the object being renamed (identified by self_guid,
804 * renamed to new_dn), because that object has a backlink pointing
807 * In most cases we assume there will only be one forward link, which
808 * is found by parsed_dn_find(), but in the case of DN+Binary links
809 * (e.g. msDS-RevealedUsers) there may be many forward links that
810 * share the same DN/GUID but differ in the binary part. For those we
811 * need to look around the link found by parsed_dn_find() and convert
812 * them all -- there is no way to know which forward link belongs to
816 ret = get_parsed_dns_trusted(tmp_ctx, el, &pdn_list);
817 if (ret != LDB_SUCCESS) {
818 ldb_asprintf_errstring(ldb, "get_parsed_dn_trusted() "
819 "error fixing %s links for %s",
821 ldb_dn_get_linearized(msg->dn));
822 talloc_free(tmp_ctx);
826 /* find our DN in the values */
827 ret = parsed_dn_find(ldb, pdn_list, num_parsed_dns,
835 if (ret != LDB_SUCCESS) {
836 ldb_asprintf_errstring(ldb, "parsed_dn_find() "
837 "error fixing %s links for %s",
839 ldb_dn_get_linearized(msg->dn));
840 talloc_free(tmp_ctx);
845 ldb_asprintf_errstring(
847 "parsed_dn_find could not find %s link for %s",
849 ldb_dn_get_linearized(msg->dn));
850 talloc_free(tmp_ctx);
851 return LDB_ERR_OPERATIONS_ERROR;
854 is_plain_dn = strcmp(syntax_oid, LDB_SYNTAX_DN) == 0;
858 * The common case -- we only have to update a single link
860 ret = ldb_dn_update_components(exact->dsdb_dn->dn, new_dn);
861 if (ret != LDB_SUCCESS) {
862 DBG_ERR("could not update components %s %s\n",
863 ldb_dn_get_linearized(exact->dsdb_dn->dn),
864 ldb_dn_get_linearized(new_dn)
867 talloc_free(tmp_ctx);
870 *(exact->v) = data_blob_string_const(
871 dsdb_dn_get_extended_linearized(el->values,
876 * The forward link is a DN+Binary (or in some alternate
877 * universes, DN+String), which means the parsed_dns are keyed
878 * on GUID+Binary. We don't know the binary part, which means
879 * from our point of view the list can have entries with
880 * duplicate GUIDs that we can't tell apart. We don't know
881 * which backlink belongs to which GUID+binary, and the binary
882 * search will always find the same one. That means one link
883 * link will get fixed n times, whil n-1 links get fixed
886 * If we instead fixing all the possible links, we end up
887 * fixing n links n times, which at least works and is
888 * probably not too costly because n is probably small.
890 struct parsed_dn *first = exact;
891 struct parsed_dn *last = exact;
892 struct parsed_dn *p = NULL;
894 while (first > pdn_list) {
896 if (p->dsdb_dn == NULL) {
897 ret = really_parse_trusted_dn(tmp_ctx,
900 if (ret != LDB_SUCCESS) {
901 talloc_free(tmp_ctx);
905 cmp = ndr_guid_compare(&exact->guid, &p->guid);
912 while (last < pdn_list + num_parsed_dns - 1) {
914 if (p->dsdb_dn == NULL) {
915 ret = really_parse_trusted_dn(tmp_ctx,
918 if (ret != LDB_SUCCESS) {
919 talloc_free(tmp_ctx);
923 cmp = ndr_guid_compare(&exact->guid, &p->guid);
930 for (p = first; p <= last; p++) {
931 ret = ldb_dn_update_components(p->dsdb_dn->dn, new_dn);
932 if (ret != LDB_SUCCESS) {
933 DBG_ERR("could not update components %s %s\n",
934 ldb_dn_get_linearized(p->dsdb_dn->dn),
935 ldb_dn_get_linearized(new_dn)
937 talloc_free(tmp_ctx);
940 *(p->v) = data_blob_string_const(
941 dsdb_dn_get_extended_linearized(el->values,
947 talloc_free(tmp_ctx);
952 static int linked_attributes_fix_links(struct ldb_module *module,
953 struct GUID self_guid,
954 struct ldb_dn *old_dn,
955 struct ldb_dn *new_dn,
956 struct ldb_message_element *el,
957 struct dsdb_schema *schema,
958 const struct dsdb_attribute *schema_attr,
959 struct ldb_request *parent)
962 TALLOC_CTX *tmp_ctx = NULL;
963 struct ldb_context *ldb = ldb_module_get_ctx(module);
964 const struct dsdb_attribute *target = NULL;
965 const char *attrs[2];
967 struct la_private *la_private = NULL;
969 target = dsdb_attribute_by_linkID(schema, schema_attr->linkID ^ 1);
970 if (target == NULL) {
971 /* there is no counterpart link to change */
975 tmp_ctx = talloc_new(module);
976 if (tmp_ctx == NULL) {
977 return LDB_ERR_OPERATIONS_ERROR;
980 la_private = talloc_get_type(ldb_module_get_private(module),
982 if (la_private == NULL) {
983 talloc_free(tmp_ctx);
984 return LDB_ERR_OPERATIONS_ERROR;
987 attrs[0] = target->lDAPDisplayName;
990 for (i=0; i<el->num_values; i++) {
991 struct dsdb_dn *dsdb_dn = NULL;
992 struct ldb_result *res = NULL;
993 struct ldb_message *msg = NULL;
994 struct ldb_message_element *el2 = NULL;
995 struct GUID link_guid;
996 char *link_guid_str = NULL;
998 dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i],
999 schema_attr->syntax->ldap_oid);
1000 if (dsdb_dn == NULL) {
1001 talloc_free(tmp_ctx);
1002 return LDB_ERR_INVALID_DN_SYNTAX;
1005 ret = la_guid_from_dn(module, parent, dsdb_dn->dn, &link_guid);
1006 if (ret != LDB_SUCCESS) {
1007 ldb_asprintf_errstring(ldb, "Linked attribute %s->%s between %s and %s - GUID not found - %s",
1008 el->name, target->lDAPDisplayName,
1009 ldb_dn_get_linearized(old_dn),
1010 ldb_dn_get_linearized(dsdb_dn->dn),
1011 ldb_errstring(ldb));
1012 talloc_free(tmp_ctx);
1016 link_guid_str = GUID_string(tmp_ctx, &link_guid);
1017 if (link_guid_str == NULL) {
1018 talloc_free(tmp_ctx);
1019 return LDB_ERR_OPERATIONS_ERROR;
1023 * get the existing message from the db for the object with
1024 * this GUID, returning attribute being modified. We will then
1025 * use this msg as the basis for a modify call
1028 ret = dsdb_module_search(module, tmp_ctx, &res, NULL, LDB_SCOPE_SUBTREE, attrs,
1029 DSDB_FLAG_NEXT_MODULE |
1030 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
1031 DSDB_SEARCH_SHOW_RECYCLED |
1032 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
1033 DSDB_SEARCH_REVEAL_INTERNALS,
1035 "objectGUID=%s", link_guid_str);
1036 if (ret != LDB_SUCCESS) {
1037 ldb_asprintf_errstring(ldb, "Linked attribute %s->%s between %s and %s - target GUID %s not found - %s",
1038 el->name, target->lDAPDisplayName,
1039 ldb_dn_get_linearized(old_dn),
1040 ldb_dn_get_linearized(dsdb_dn->dn),
1042 ldb_errstring(ldb));
1043 talloc_free(tmp_ctx);
1046 if (res->count == 0) {
1047 /* Forward link without backlink object remaining - nothing to do here */
1050 if (res->count != 1) {
1051 ldb_asprintf_errstring(ldb, "Linked attribute %s->%s between %s and %s - target GUID %s found more than once!",
1052 el->name, target->lDAPDisplayName,
1053 ldb_dn_get_linearized(old_dn),
1054 ldb_dn_get_linearized(dsdb_dn->dn),
1056 talloc_free(tmp_ctx);
1057 return LDB_ERR_OPERATIONS_ERROR;
1062 if (msg->num_elements == 0) {
1063 /* Forward link without backlink remaining - nothing to do here */
1065 } else if (msg->num_elements != 1) {
1066 ldb_asprintf_errstring(ldb, "Bad msg elements - got %u elements, expected one element to be returned in linked_attributes_fix_links for %s",
1067 msg->num_elements, ldb_dn_get_linearized(msg->dn));
1068 talloc_free(tmp_ctx);
1069 return LDB_ERR_OPERATIONS_ERROR;
1071 if (ldb_attr_cmp(msg->elements[0].name, target->lDAPDisplayName) != 0) {
1072 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));
1073 talloc_free(tmp_ctx);
1074 return LDB_ERR_OPERATIONS_ERROR;
1076 el2 = &msg->elements[0];
1078 el2->flags = LDB_FLAG_MOD_REPLACE;
1080 if (target->linkID & 1 ||
1081 ! la_private->sorted_links) {
1082 /* handle backlinks (which aren't sorted in the DB)
1083 and forward links in old unsorted databases. */
1084 ret = linked_attributes_fix_link_slow(
1090 target->syntax->ldap_oid,
1091 schema_attr->syntax->ldap_oid);
1093 /* we can binary search to find forward links */
1094 ret = linked_attributes_fix_forward_link(
1099 target->syntax->ldap_oid);
1101 ret = dsdb_check_single_valued_link(target, el2);
1102 if (ret != LDB_SUCCESS) {
1103 talloc_free(tmp_ctx);
1107 /* we may be putting multiple values in an attribute -
1108 disable checking for this attribute */
1109 el2->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
1111 ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE, parent);
1112 if (ret != LDB_SUCCESS) {
1113 ldb_asprintf_errstring(ldb, "Linked attribute %s->%s between %s and %s - update failed - %s",
1114 el->name, target->lDAPDisplayName,
1115 ldb_dn_get_linearized(old_dn),
1116 ldb_dn_get_linearized(dsdb_dn->dn),
1117 ldb_errstring(ldb));
1118 talloc_free(tmp_ctx);
1123 talloc_free(tmp_ctx);
1129 static int linked_attributes_rename(struct ldb_module *module, struct ldb_request *req)
1131 struct ldb_result *res;
1132 struct ldb_message *msg;
1134 struct ldb_context *ldb = ldb_module_get_ctx(module);
1135 struct dsdb_schema *schema;
1140 - load the current msg
1141 - find any linked attributes
1142 - if its a link then find the target object
1143 - modify the target linked attributes with the new DN
1145 ret = dsdb_module_search_dn(module, req, &res, req->op.rename.olddn,
1147 DSDB_FLAG_NEXT_MODULE |
1148 DSDB_SEARCH_SHOW_EXTENDED_DN |
1149 DSDB_SEARCH_SHOW_RECYCLED, req);
1150 if (ret != LDB_SUCCESS) {
1154 schema = dsdb_get_schema(ldb, res);
1156 return ldb_oom(ldb);
1161 ret = la_guid_from_dn(module, req, msg->dn, &guid);
1162 if (ret != LDB_SUCCESS) {
1166 for (i=0; i<msg->num_elements; i++) {
1167 struct ldb_message_element *el = &msg->elements[i];
1168 const struct dsdb_attribute *schema_attr
1169 = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
1170 if (!schema_attr || schema_attr->linkID == 0) {
1173 ret = linked_attributes_fix_links(module, guid, msg->dn, req->op.rename.newdn, el,
1174 schema, schema_attr, req);
1175 if (ret != LDB_SUCCESS) {
1183 return ldb_next_request(module, req);
1187 /* queue a linked attributes modify request in the la_private
1189 static int la_queue_mod_request(struct la_context *ac)
1191 struct la_private *la_private =
1192 talloc_get_type(ldb_module_get_private(ac->module),
1195 if (la_private == NULL || la_private->transaction == NULL) {
1196 ldb_debug(ldb_module_get_ctx(ac->module),
1198 __location__ ": No la_private transaction setup\n");
1199 return ldb_operr(ldb_module_get_ctx(ac->module));
1202 talloc_steal(la_private->transaction, ac);
1203 DLIST_ADD(la_private->transaction->la_list, ac);
1205 return ldb_module_done(ac->req, ac->op_controls,
1206 ac->op_response, LDB_SUCCESS);
1209 /* Having done the original operation, then try to fix up all the linked attributes for modify and delete */
1210 static int la_mod_del_callback(struct ldb_request *req, struct ldb_reply *ares)
1212 struct la_context *ac;
1213 struct ldb_context *ldb;
1216 ac = talloc_get_type(req->context, struct la_context);
1217 ldb = ldb_module_get_ctx(ac->module);
1220 return ldb_module_done(ac->req, NULL, NULL,
1221 LDB_ERR_OPERATIONS_ERROR);
1223 if (ares->error != LDB_SUCCESS) {
1224 return ldb_module_done(ac->req, ares->controls,
1225 ares->response, ares->error);
1228 if (ares->type != LDB_REPLY_DONE) {
1229 ldb_set_errstring(ldb,
1230 "invalid reply type in linked attributes delete callback");
1232 return ldb_module_done(ac->req, NULL, NULL,
1233 LDB_ERR_OPERATIONS_ERROR);
1236 ac->op_controls = talloc_steal(ac, ares->controls);
1237 ac->op_response = talloc_steal(ac, ares->response);
1239 /* If we have modfies to make, this is the time to do them for modify and delete */
1240 ret = la_queue_mod_request(ac);
1242 if (ret != LDB_SUCCESS) {
1243 return ldb_module_done(ac->req, NULL, NULL, ret);
1247 /* la_queue_mod_request has already sent the callbacks */
1252 /* Having done the original add, then try to fix up all the linked attributes
1254 This is done after the add so the links can get the extended DNs correctly.
1256 static int la_add_callback(struct ldb_request *req, struct ldb_reply *ares)
1258 struct la_context *ac;
1259 struct ldb_context *ldb;
1262 ac = talloc_get_type(req->context, struct la_context);
1263 ldb = ldb_module_get_ctx(ac->module);
1266 return ldb_module_done(ac->req, NULL, NULL,
1267 LDB_ERR_OPERATIONS_ERROR);
1269 if (ares->error != LDB_SUCCESS) {
1270 return ldb_module_done(ac->req, ares->controls,
1271 ares->response, ares->error);
1274 if (ares->type != LDB_REPLY_DONE) {
1275 ldb_set_errstring(ldb,
1276 "invalid reply type in linked attributes add callback");
1278 return ldb_module_done(ac->req, NULL, NULL,
1279 LDB_ERR_OPERATIONS_ERROR);
1283 struct ldb_request *search_req;
1284 static const char *attrs[] = { NULL };
1286 /* The callback does all the hard work here - we need
1287 * the objectGUID and SID of the added record */
1288 ret = ldb_build_search_req(&search_req, ldb, ac,
1289 ac->req->op.add.message->dn,
1291 "(objectClass=*)", attrs,
1293 ac, la_mod_search_callback,
1295 LDB_REQ_SET_LOCATION(search_req);
1297 if (ret == LDB_SUCCESS) {
1298 ret = dsdb_request_add_controls(search_req,
1299 DSDB_SEARCH_SHOW_RECYCLED |
1300 DSDB_SEARCH_SHOW_EXTENDED_DN);
1302 if (ret != LDB_SUCCESS) {
1303 return ldb_module_done(ac->req, NULL, NULL,
1307 ac->op_controls = talloc_steal(ac, ares->controls);
1308 ac->op_response = talloc_steal(ac, ares->response);
1310 return ldb_next_request(ac->module, search_req);
1313 return ldb_module_done(ac->req, ares->controls,
1314 ares->response, ares->error);
1318 /* Reconstruct the original request, but pointing at our local callback to finish things off */
1319 static int la_down_req(struct la_context *ac)
1321 struct ldb_request *down_req;
1322 struct ldb_context *ldb;
1325 ldb = ldb_module_get_ctx(ac->module);
1327 switch (ac->req->operation) {
1329 ret = ldb_build_add_req(&down_req, ldb, ac,
1330 ac->req->op.add.message,
1332 ac, la_add_callback,
1334 LDB_REQ_SET_LOCATION(down_req);
1337 ret = ldb_build_mod_req(&down_req, ldb, ac,
1338 ac->req->op.mod.message,
1340 ac, la_mod_del_callback,
1342 LDB_REQ_SET_LOCATION(down_req);
1345 ret = LDB_ERR_OPERATIONS_ERROR;
1347 if (ret != LDB_SUCCESS) {
1351 return ldb_next_request(ac->module, down_req);
1355 use the GUID part of an extended DN to find the target DN, in case
1358 static int la_find_dn_target(struct ldb_module *module, struct la_context *ac,
1359 struct GUID *guid, struct ldb_dn **dn)
1361 return dsdb_module_dn_by_guid(ac->module, ac, guid, dn, ac->req);
1364 /* apply one la_context op change */
1365 static int la_do_op_request(struct ldb_module *module, struct la_context *ac, struct la_op_store *op)
1367 struct ldb_message_element *ret_el;
1368 struct ldb_message *new_msg;
1369 struct ldb_context *ldb;
1372 if (ac->mod_dn == NULL) {
1373 /* we didn't find the DN that we searched for */
1377 ldb = ldb_module_get_ctx(ac->module);
1379 /* Create the modify request */
1380 new_msg = ldb_msg_new(ac);
1382 return ldb_oom(ldb);
1385 ret = la_find_dn_target(module, ac, &op->guid, &new_msg->dn);
1386 if (ret != LDB_SUCCESS) {
1390 if (op->op == LA_OP_ADD) {
1391 ret = ldb_msg_add_empty(new_msg, op->name,
1392 LDB_FLAG_MOD_ADD, &ret_el);
1394 ret = ldb_msg_add_empty(new_msg, op->name,
1395 LDB_FLAG_MOD_DELETE, &ret_el);
1397 if (ret != LDB_SUCCESS) {
1400 ret_el->values = talloc_array(new_msg, struct ldb_val, 1);
1401 if (!ret_el->values) {
1402 return ldb_oom(ldb);
1404 ret_el->num_values = 1;
1405 ret_el->values[0] = data_blob_string_const(ldb_dn_get_extended_linearized(new_msg, ac->mod_dn, 1));
1407 /* a backlink should never be single valued. Unfortunately the
1408 exchange schema has a attribute
1409 msExchBridgeheadedLocalConnectorsDNBL which is single
1410 valued and a backlink. We need to cope with that by
1411 ignoring the single value flag */
1412 ret_el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
1415 ldb_debug(ldb, LDB_DEBUG_WARNING,
1416 "link on %s %s: %s %s\n",
1417 ldb_dn_get_linearized(new_msg->dn), ret_el->name,
1418 ret_el->values[0].data, ac->ops->op == LA_OP_ADD ? "added" : "deleted");
1422 DEBUG(4,("Applying linked attribute change:\n%s\n",
1423 ldb_ldif_message_redacted_string(ldb, op,
1424 LDB_CHANGETYPE_MODIFY,
1428 ret = dsdb_module_modify(module, new_msg, DSDB_FLAG_NEXT_MODULE, ac->req);
1429 if (ret != LDB_SUCCESS) {
1430 ldb_debug(ldb, LDB_DEBUG_WARNING, __location__ ": failed to apply linked attribute change '%s'\n%s\n",
1432 ldb_ldif_message_redacted_string(ldb, op,
1433 LDB_CHANGETYPE_MODIFY,
1440 /* apply one set of la_context changes */
1441 static int la_do_mod_request(struct ldb_module *module, struct la_context *ac)
1443 struct la_op_store *op;
1445 for (op = ac->ops; op; op=op->next) {
1446 int ret = la_do_op_request(module, ac, op);
1447 if (ret != LDB_SUCCESS) {
1448 if (ret != LDB_ERR_NO_SUCH_OBJECT) {
1459 we hook into the transaction operations to allow us to
1460 perform the linked attribute updates at the end of the whole
1461 transaction. This allows a forward linked attribute to be created
1462 before the target is created, as long as the target is created
1463 in the same transaction
1465 static int linked_attributes_start_transaction(struct ldb_module *module)
1467 /* create our private structure for this transaction */
1468 struct la_private *la_private =
1469 talloc_get_type(ldb_module_get_private(module),
1472 if (la_private == NULL) {
1473 return ldb_oom(ldb_module_get_ctx(module));
1475 talloc_free(la_private->transaction);
1476 la_private->transaction = talloc(module, struct la_private_transaction);
1477 if (la_private->transaction == NULL) {
1478 return ldb_oom(ldb_module_get_ctx(module));
1480 la_private->transaction->la_list = NULL;
1481 return ldb_next_start_trans(module);
1485 on prepare commit we loop over our queued la_context structures
1486 and apply each of them
1488 static int linked_attributes_prepare_commit(struct ldb_module *module)
1490 struct la_context *ac;
1491 struct la_private *la_private =
1492 talloc_get_type(ldb_module_get_private(module),
1494 if (la_private == NULL || la_private->transaction == NULL) {
1495 DBG_ERR("prepare_commit without begin_transaction\n");
1496 /* prepare commit without begin_transaction - let someone else
1497 * return the error, just don't segfault */
1498 return ldb_next_prepare_commit(module);
1500 /* walk the list backwards, to do the first entry first, as we
1501 * added the entries with DLIST_ADD() which puts them at the
1502 * start of the list */
1504 /* Start at the end of the list - so we can start
1505 * there, but ensure we don't create a loop by NULLing
1506 * it out in the first element */
1507 ac = DLIST_TAIL(la_private->transaction->la_list);
1509 for (; ac; ac=DLIST_PREV(ac)) {
1512 ret = la_do_mod_request(module, ac);
1513 if (ret != LDB_SUCCESS) {
1514 DEBUG(0,(__location__ ": Failed mod request ret=%d\n", ret));
1515 TALLOC_FREE(la_private->transaction);
1520 TALLOC_FREE(la_private->transaction);
1522 return ldb_next_prepare_commit(module);
1525 static int linked_attributes_del_transaction(struct ldb_module *module)
1527 struct la_private *la_private =
1528 talloc_get_type(ldb_module_get_private(module),
1530 TALLOC_FREE(la_private->transaction);
1531 return ldb_next_del_trans(module);
1534 static int linked_attributes_ldb_init(struct ldb_module *module)
1537 struct la_private *la_private = NULL;
1538 struct ldb_context *ldb = ldb_module_get_ctx(module);
1540 ret = ldb_mod_register_control(module, LDB_CONTROL_VERIFY_NAME_OID);
1541 if (ret != LDB_SUCCESS) {
1542 ldb_debug(ldb_module_get_ctx(module), LDB_DEBUG_ERROR,
1543 "verify_name: Unable to register control with rootdse!\n");
1544 return ldb_operr(ldb_module_get_ctx(module));
1547 la_private = talloc_zero(module, struct la_private);
1548 if (la_private == NULL) {
1550 return LDB_ERR_OPERATIONS_ERROR;
1553 ret = dsdb_check_samba_compatible_feature(module,
1554 SAMBA_SORTED_LINKS_FEATURE,
1555 &la_private->sorted_links);
1556 if (ret != LDB_SUCCESS) {
1557 talloc_free(la_private);
1561 ldb_module_set_private(module, la_private);
1562 return ldb_next_init(module);
1566 static const struct ldb_module_ops ldb_linked_attributes_module_ops = {
1567 .name = "linked_attributes",
1568 .add = linked_attributes_add,
1569 .modify = linked_attributes_modify,
1570 .rename = linked_attributes_rename,
1571 .init_context = linked_attributes_ldb_init,
1572 .start_transaction = linked_attributes_start_transaction,
1573 .prepare_commit = linked_attributes_prepare_commit,
1574 .del_transaction = linked_attributes_del_transaction,
1577 int ldb_linked_attributes_module_init(const char *version)
1579 LDB_MODULE_CHECK_VERSION(version);
1580 return ldb_register_module(&ldb_linked_attributes_module_ops);