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; 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,
704 const char *reverse_syntax_oid)
708 struct GUID link_guid;
709 struct ldb_message_element *el = &msg->elements[0];
710 struct ldb_context *ldb = ldb_module_get_ctx(module);
711 bool has_unique_value = strcmp(reverse_syntax_oid, LDB_SYNTAX_DN) == 0;
712 TALLOC_CTX *tmp_ctx = talloc_new(module);
713 if (tmp_ctx == NULL) {
714 return LDB_ERR_OPERATIONS_ERROR;
717 * The msg has one element (el) containing links of one particular
718 * type from the remote object. We know that at least one of those
719 * links points to the object being renamed (identified by self_guid,
720 * renamed to new_dn). Usually only one of the links will point back
721 * to renamed object, but there can be more when the reverse link is a
724 * This is used for unsorted links, which is to say back links and
725 * forward links on old databases. It necessarily involves a linear
726 * search, though when the link is a plain DN link, we can skip
727 * checking as soon as we find it.
729 * NOTE: if there are duplicate links, the extra ones will end up as
730 * dangling links to the old DN. This may or may not be better.
732 for (i = 0; i < el->num_values; i++) {
733 struct dsdb_dn *dsdb_dn = dsdb_dn_parse(msg,
737 if (dsdb_dn == NULL) {
738 talloc_free(tmp_ctx);
739 return LDB_ERR_INVALID_DN_SYNTAX;
742 ret = la_guid_from_dn(module, parent, dsdb_dn->dn, &link_guid);
743 if (ret != LDB_SUCCESS) {
744 talloc_free(tmp_ctx);
749 * By comparing using the GUID we ensure that even if somehow
750 * the name has got out of sync, this rename will fix it.
752 * If somehow we don't have a GUID on the DN in the DB, the
753 * la_guid_from_dn call will be more costly, but still give us
754 * a GUID. dbcheck will fix this if run.
756 if (!GUID_equal(&self_guid, &link_guid)) {
760 ret = ldb_dn_update_components(dsdb_dn->dn, new_dn);
761 if (ret != LDB_SUCCESS) {
762 talloc_free(tmp_ctx);
766 el->values[i] = data_blob_string_const(
767 dsdb_dn_get_extended_linearized(el->values, dsdb_dn, 1));
768 if (has_unique_value) {
773 talloc_free(tmp_ctx);
778 static int linked_attributes_fix_forward_link(struct ldb_module *module,
779 struct ldb_message *msg,
780 struct ldb_dn *new_dn,
781 struct GUID self_guid,
782 const char *syntax_oid)
785 struct ldb_context *ldb = ldb_module_get_ctx(module);
786 struct parsed_dn *pdn_list = NULL;
787 struct parsed_dn *exact = NULL;
788 struct parsed_dn *next = NULL;
790 struct ldb_message_element *el = &msg->elements[0];
791 unsigned int num_parsed_dns = el->num_values;
793 TALLOC_CTX *tmp_ctx = talloc_new(module);
794 if (tmp_ctx == NULL) {
795 return LDB_ERR_OPERATIONS_ERROR;
799 * The msg has a single element (el) containing forward links which we
800 * trust are sorted in GUID order. We know that at least one of those
801 * links points to the object being renamed (identified by self_guid,
802 * renamed to new_dn), because that object has a backlink pointing
805 * In most cases we assume there will only be one forward link, which
806 * is found by parsed_dn_find(), but in the case of DN+Binary links
807 * (e.g. msDS-RevealedUsers) there may be many forward links that
808 * share the same DN/GUID but differ in the binary part. For those we
809 * need to look around the link found by parsed_dn_find() and convert
810 * them all -- there is no way to know which forward link belongs to
814 ret = get_parsed_dns_trusted(tmp_ctx, el, &pdn_list);
815 if (ret != LDB_SUCCESS) {
816 ldb_asprintf_errstring(ldb, "get_parsed_dn_trusted() "
817 "error fixing %s links for %s",
819 ldb_dn_get_linearized(msg->dn));
820 talloc_free(tmp_ctx);
824 /* find our DN in the values */
825 ret = parsed_dn_find(ldb, pdn_list, num_parsed_dns,
833 if (ret != LDB_SUCCESS) {
834 ldb_asprintf_errstring(ldb, "parsed_dn_find() "
835 "error fixing %s links for %s",
837 ldb_dn_get_linearized(msg->dn));
838 talloc_free(tmp_ctx);
843 ldb_asprintf_errstring(
845 "parsed_dn_find could not find %s link for %s",
847 ldb_dn_get_linearized(msg->dn));
848 talloc_free(tmp_ctx);
849 return LDB_ERR_OPERATIONS_ERROR;
852 is_plain_dn = strcmp(syntax_oid, LDB_SYNTAX_DN) == 0;
856 * The common case -- we only have to update a single link
858 ret = ldb_dn_update_components(exact->dsdb_dn->dn, new_dn);
859 if (ret != LDB_SUCCESS) {
860 DBG_ERR("could not update components %s %s\n",
861 ldb_dn_get_linearized(exact->dsdb_dn->dn),
862 ldb_dn_get_linearized(new_dn)
865 talloc_free(tmp_ctx);
868 *(exact->v) = data_blob_string_const(
869 dsdb_dn_get_extended_linearized(el->values,
874 * The forward link is a DN+Binary (or in some alternate
875 * universes, DN+String), which means the parsed_dns are keyed
876 * on GUID+Binary. We don't know the binary part, which means
877 * from our point of view the list can have entries with
878 * duplicate GUIDs that we can't tell apart. We don't know
879 * which backlink belongs to which GUID+binary, and the binary
880 * search will always find the same one. That means one link
881 * link will get fixed n times, whil n-1 links get fixed
884 * If we instead fixing all the possible links, we end up
885 * fixing n links n times, which at least works and is
886 * probably not too costly because n is probably small.
888 struct parsed_dn *first = exact;
889 struct parsed_dn *last = exact;
890 struct parsed_dn *p = NULL;
892 while (first > pdn_list) {
894 if (p->dsdb_dn == NULL) {
895 ret = really_parse_trusted_dn(tmp_ctx,
898 if (ret != LDB_SUCCESS) {
899 talloc_free(tmp_ctx);
903 cmp = ndr_guid_compare(&exact->guid, &p->guid);
910 while (last < pdn_list + num_parsed_dns - 1) {
912 if (p->dsdb_dn == NULL) {
913 ret = really_parse_trusted_dn(tmp_ctx,
916 if (ret != LDB_SUCCESS) {
917 talloc_free(tmp_ctx);
921 cmp = ndr_guid_compare(&exact->guid, &p->guid);
928 for (p = first; p <= last; p++) {
929 ret = ldb_dn_update_components(p->dsdb_dn->dn, new_dn);
930 if (ret != LDB_SUCCESS) {
931 DBG_ERR("could not update components %s %s\n",
932 ldb_dn_get_linearized(p->dsdb_dn->dn),
933 ldb_dn_get_linearized(new_dn)
935 talloc_free(tmp_ctx);
938 *(p->v) = data_blob_string_const(
939 dsdb_dn_get_extended_linearized(el->values,
945 talloc_free(tmp_ctx);
950 static int linked_attributes_fix_links(struct ldb_module *module,
951 struct GUID self_guid,
952 struct ldb_dn *old_dn,
953 struct ldb_dn *new_dn,
954 struct ldb_message_element *el,
955 struct dsdb_schema *schema,
956 const struct dsdb_attribute *schema_attr,
957 struct ldb_request *parent)
960 TALLOC_CTX *tmp_ctx = NULL;
961 struct ldb_context *ldb = ldb_module_get_ctx(module);
962 const struct dsdb_attribute *target = NULL;
963 const char *attrs[2];
965 struct la_private *la_private = NULL;
967 target = dsdb_attribute_by_linkID(schema, schema_attr->linkID ^ 1);
968 if (target == NULL) {
969 /* there is no counterpart link to change */
973 tmp_ctx = talloc_new(module);
974 if (tmp_ctx == NULL) {
975 return LDB_ERR_OPERATIONS_ERROR;
978 la_private = talloc_get_type(ldb_module_get_private(module),
980 if (la_private == NULL) {
981 talloc_free(tmp_ctx);
982 return LDB_ERR_OPERATIONS_ERROR;
985 attrs[0] = target->lDAPDisplayName;
988 for (i=0; i<el->num_values; i++) {
989 struct dsdb_dn *dsdb_dn = NULL;
990 struct ldb_result *res = NULL;
991 struct ldb_message *msg = NULL;
992 struct ldb_message_element *el2 = NULL;
993 struct GUID link_guid;
994 char *link_guid_str = NULL;
996 dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i],
997 schema_attr->syntax->ldap_oid);
998 if (dsdb_dn == NULL) {
999 talloc_free(tmp_ctx);
1000 return LDB_ERR_INVALID_DN_SYNTAX;
1003 ret = la_guid_from_dn(module, parent, dsdb_dn->dn, &link_guid);
1004 if (ret != LDB_SUCCESS) {
1005 ldb_asprintf_errstring(ldb, "Linked attribute %s->%s between %s and %s - GUID not found - %s",
1006 el->name, target->lDAPDisplayName,
1007 ldb_dn_get_linearized(old_dn),
1008 ldb_dn_get_linearized(dsdb_dn->dn),
1009 ldb_errstring(ldb));
1010 talloc_free(tmp_ctx);
1014 link_guid_str = GUID_string(tmp_ctx, &link_guid);
1015 if (link_guid_str == NULL) {
1016 talloc_free(tmp_ctx);
1017 return LDB_ERR_OPERATIONS_ERROR;
1021 * get the existing message from the db for the object with
1022 * this GUID, returning attribute being modified. We will then
1023 * use this msg as the basis for a modify call
1026 ret = dsdb_module_search(module, tmp_ctx, &res, NULL, LDB_SCOPE_SUBTREE, attrs,
1027 DSDB_FLAG_NEXT_MODULE |
1028 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
1029 DSDB_SEARCH_SHOW_RECYCLED |
1030 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
1031 DSDB_SEARCH_REVEAL_INTERNALS,
1033 "objectGUID=%s", link_guid_str);
1034 if (ret != LDB_SUCCESS) {
1035 ldb_asprintf_errstring(ldb, "Linked attribute %s->%s between %s and %s - target GUID %s not found - %s",
1036 el->name, target->lDAPDisplayName,
1037 ldb_dn_get_linearized(old_dn),
1038 ldb_dn_get_linearized(dsdb_dn->dn),
1040 ldb_errstring(ldb));
1041 talloc_free(tmp_ctx);
1044 if (res->count == 0) {
1045 /* Forward link without backlink object remaining - nothing to do here */
1048 if (res->count != 1) {
1049 ldb_asprintf_errstring(ldb, "Linked attribute %s->%s between %s and %s - target GUID %s found more than once!",
1050 el->name, target->lDAPDisplayName,
1051 ldb_dn_get_linearized(old_dn),
1052 ldb_dn_get_linearized(dsdb_dn->dn),
1054 talloc_free(tmp_ctx);
1055 return LDB_ERR_OPERATIONS_ERROR;
1060 if (msg->num_elements == 0) {
1061 /* Forward link without backlink remaining - nothing to do here */
1063 } else if (msg->num_elements != 1) {
1064 ldb_asprintf_errstring(ldb, "Bad msg elements - got %u elements, expected one element to be returned in linked_attributes_fix_links for %s",
1065 msg->num_elements, ldb_dn_get_linearized(msg->dn));
1066 talloc_free(tmp_ctx);
1067 return LDB_ERR_OPERATIONS_ERROR;
1069 if (ldb_attr_cmp(msg->elements[0].name, target->lDAPDisplayName) != 0) {
1070 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));
1071 talloc_free(tmp_ctx);
1072 return LDB_ERR_OPERATIONS_ERROR;
1074 el2 = &msg->elements[0];
1076 el2->flags = LDB_FLAG_MOD_REPLACE;
1078 if (target->linkID & 1 ||
1079 ! la_private->sorted_links) {
1080 /* handle backlinks (which aren't sorted in the DB)
1081 and forward links in old unsorted databases. */
1082 ret = linked_attributes_fix_link_slow(
1088 target->syntax->ldap_oid,
1089 schema_attr->syntax->ldap_oid);
1091 /* we can binary search to find forward links */
1092 ret = linked_attributes_fix_forward_link(
1097 target->syntax->ldap_oid);
1099 ret = dsdb_check_single_valued_link(target, el2);
1100 if (ret != LDB_SUCCESS) {
1101 talloc_free(tmp_ctx);
1105 /* we may be putting multiple values in an attribute -
1106 disable checking for this attribute */
1107 el2->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
1109 ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE, parent);
1110 if (ret != LDB_SUCCESS) {
1111 ldb_asprintf_errstring(ldb, "Linked attribute %s->%s between %s and %s - update failed - %s",
1112 el->name, target->lDAPDisplayName,
1113 ldb_dn_get_linearized(old_dn),
1114 ldb_dn_get_linearized(dsdb_dn->dn),
1115 ldb_errstring(ldb));
1116 talloc_free(tmp_ctx);
1121 talloc_free(tmp_ctx);
1127 static int linked_attributes_rename(struct ldb_module *module, struct ldb_request *req)
1129 struct ldb_result *res;
1130 struct ldb_message *msg;
1132 struct ldb_context *ldb = ldb_module_get_ctx(module);
1133 struct dsdb_schema *schema;
1138 - load the current msg
1139 - find any linked attributes
1140 - if its a link then find the target object
1141 - modify the target linked attributes with the new DN
1143 ret = dsdb_module_search_dn(module, req, &res, req->op.rename.olddn,
1145 DSDB_FLAG_NEXT_MODULE |
1146 DSDB_SEARCH_SHOW_EXTENDED_DN |
1147 DSDB_SEARCH_SHOW_RECYCLED, req);
1148 if (ret != LDB_SUCCESS) {
1152 schema = dsdb_get_schema(ldb, res);
1154 return ldb_oom(ldb);
1159 ret = la_guid_from_dn(module, req, msg->dn, &guid);
1160 if (ret != LDB_SUCCESS) {
1164 for (i=0; i<msg->num_elements; i++) {
1165 struct ldb_message_element *el = &msg->elements[i];
1166 const struct dsdb_attribute *schema_attr
1167 = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
1168 if (!schema_attr || schema_attr->linkID == 0) {
1171 ret = linked_attributes_fix_links(module, guid, msg->dn, req->op.rename.newdn, el,
1172 schema, schema_attr, req);
1173 if (ret != LDB_SUCCESS) {
1181 return ldb_next_request(module, req);
1185 /* queue a linked attributes modify request in the la_private
1187 static int la_queue_mod_request(struct la_context *ac)
1189 struct la_private *la_private =
1190 talloc_get_type(ldb_module_get_private(ac->module),
1193 if (la_private == NULL || la_private->transaction == NULL) {
1194 ldb_debug(ldb_module_get_ctx(ac->module),
1196 __location__ ": No la_private transaction setup\n");
1197 return ldb_operr(ldb_module_get_ctx(ac->module));
1200 talloc_steal(la_private->transaction, ac);
1201 DLIST_ADD(la_private->transaction->la_list, ac);
1203 return ldb_module_done(ac->req, ac->op_controls,
1204 ac->op_response, LDB_SUCCESS);
1207 /* Having done the original operation, then try to fix up all the linked attributes for modify and delete */
1208 static int la_mod_del_callback(struct ldb_request *req, struct ldb_reply *ares)
1210 struct la_context *ac;
1211 struct ldb_context *ldb;
1214 ac = talloc_get_type(req->context, struct la_context);
1215 ldb = ldb_module_get_ctx(ac->module);
1218 return ldb_module_done(ac->req, NULL, NULL,
1219 LDB_ERR_OPERATIONS_ERROR);
1221 if (ares->error != LDB_SUCCESS) {
1222 return ldb_module_done(ac->req, ares->controls,
1223 ares->response, ares->error);
1226 if (ares->type != LDB_REPLY_DONE) {
1227 ldb_set_errstring(ldb,
1228 "invalid reply type in linked attributes delete callback");
1230 return ldb_module_done(ac->req, NULL, NULL,
1231 LDB_ERR_OPERATIONS_ERROR);
1234 ac->op_controls = talloc_steal(ac, ares->controls);
1235 ac->op_response = talloc_steal(ac, ares->response);
1237 /* If we have modfies to make, this is the time to do them for modify and delete */
1238 ret = la_queue_mod_request(ac);
1240 if (ret != LDB_SUCCESS) {
1241 return ldb_module_done(ac->req, NULL, NULL, ret);
1245 /* la_queue_mod_request has already sent the callbacks */
1250 /* Having done the original add, then try to fix up all the linked attributes
1252 This is done after the add so the links can get the extended DNs correctly.
1254 static int la_add_callback(struct ldb_request *req, struct ldb_reply *ares)
1256 struct la_context *ac;
1257 struct ldb_context *ldb;
1260 ac = talloc_get_type(req->context, struct la_context);
1261 ldb = ldb_module_get_ctx(ac->module);
1264 return ldb_module_done(ac->req, NULL, NULL,
1265 LDB_ERR_OPERATIONS_ERROR);
1267 if (ares->error != LDB_SUCCESS) {
1268 return ldb_module_done(ac->req, ares->controls,
1269 ares->response, ares->error);
1272 if (ares->type != LDB_REPLY_DONE) {
1273 ldb_set_errstring(ldb,
1274 "invalid reply type in linked attributes add callback");
1276 return ldb_module_done(ac->req, NULL, NULL,
1277 LDB_ERR_OPERATIONS_ERROR);
1281 struct ldb_request *search_req;
1282 static const char *attrs[] = { NULL };
1284 /* The callback does all the hard work here - we need
1285 * the objectGUID and SID of the added record */
1286 ret = ldb_build_search_req(&search_req, ldb, ac,
1287 ac->req->op.add.message->dn,
1289 "(objectClass=*)", attrs,
1291 ac, la_mod_search_callback,
1293 LDB_REQ_SET_LOCATION(search_req);
1295 if (ret == LDB_SUCCESS) {
1296 ret = dsdb_request_add_controls(search_req,
1297 DSDB_SEARCH_SHOW_RECYCLED |
1298 DSDB_SEARCH_SHOW_EXTENDED_DN);
1300 if (ret != LDB_SUCCESS) {
1301 return ldb_module_done(ac->req, NULL, NULL,
1305 ac->op_controls = talloc_steal(ac, ares->controls);
1306 ac->op_response = talloc_steal(ac, ares->response);
1308 return ldb_next_request(ac->module, search_req);
1311 return ldb_module_done(ac->req, ares->controls,
1312 ares->response, ares->error);
1316 /* Reconstruct the original request, but pointing at our local callback to finish things off */
1317 static int la_down_req(struct la_context *ac)
1319 struct ldb_request *down_req;
1320 struct ldb_context *ldb;
1323 ldb = ldb_module_get_ctx(ac->module);
1325 switch (ac->req->operation) {
1327 ret = ldb_build_add_req(&down_req, ldb, ac,
1328 ac->req->op.add.message,
1330 ac, la_add_callback,
1332 LDB_REQ_SET_LOCATION(down_req);
1335 ret = ldb_build_mod_req(&down_req, ldb, ac,
1336 ac->req->op.mod.message,
1338 ac, la_mod_del_callback,
1340 LDB_REQ_SET_LOCATION(down_req);
1343 ret = LDB_ERR_OPERATIONS_ERROR;
1345 if (ret != LDB_SUCCESS) {
1349 return ldb_next_request(ac->module, down_req);
1353 use the GUID part of an extended DN to find the target DN, in case
1356 static int la_find_dn_target(struct ldb_module *module, struct la_context *ac,
1357 struct GUID *guid, struct ldb_dn **dn)
1359 return dsdb_module_dn_by_guid(ac->module, ac, guid, dn, ac->req);
1362 /* apply one la_context op change */
1363 static int la_do_op_request(struct ldb_module *module, struct la_context *ac, struct la_op_store *op)
1365 struct ldb_message_element *ret_el;
1366 struct ldb_message *new_msg;
1367 struct ldb_context *ldb;
1370 if (ac->mod_dn == NULL) {
1371 /* we didn't find the DN that we searched for */
1375 ldb = ldb_module_get_ctx(ac->module);
1377 /* Create the modify request */
1378 new_msg = ldb_msg_new(ac);
1380 return ldb_oom(ldb);
1383 ret = la_find_dn_target(module, ac, &op->guid, &new_msg->dn);
1384 if (ret != LDB_SUCCESS) {
1388 if (op->op == LA_OP_ADD) {
1389 ret = ldb_msg_add_empty(new_msg, op->name,
1390 LDB_FLAG_MOD_ADD, &ret_el);
1392 ret = ldb_msg_add_empty(new_msg, op->name,
1393 LDB_FLAG_MOD_DELETE, &ret_el);
1395 if (ret != LDB_SUCCESS) {
1398 ret_el->values = talloc_array(new_msg, struct ldb_val, 1);
1399 if (!ret_el->values) {
1400 return ldb_oom(ldb);
1402 ret_el->num_values = 1;
1403 ret_el->values[0] = data_blob_string_const(ldb_dn_get_extended_linearized(new_msg, ac->mod_dn, 1));
1405 /* a backlink should never be single valued. Unfortunately the
1406 exchange schema has a attribute
1407 msExchBridgeheadedLocalConnectorsDNBL which is single
1408 valued and a backlink. We need to cope with that by
1409 ignoring the single value flag */
1410 ret_el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
1413 ldb_debug(ldb, LDB_DEBUG_WARNING,
1414 "link on %s %s: %s %s\n",
1415 ldb_dn_get_linearized(new_msg->dn), ret_el->name,
1416 ret_el->values[0].data, ac->ops->op == LA_OP_ADD ? "added" : "deleted");
1420 DEBUG(4,("Applying linked attribute change:\n%s\n",
1421 ldb_ldif_message_redacted_string(ldb, op,
1422 LDB_CHANGETYPE_MODIFY,
1426 ret = dsdb_module_modify(module, new_msg, DSDB_FLAG_NEXT_MODULE, ac->req);
1427 if (ret != LDB_SUCCESS) {
1428 ldb_debug(ldb, LDB_DEBUG_WARNING, __location__ ": failed to apply linked attribute change '%s'\n%s\n",
1430 ldb_ldif_message_redacted_string(ldb, op,
1431 LDB_CHANGETYPE_MODIFY,
1438 /* apply one set of la_context changes */
1439 static int la_do_mod_request(struct ldb_module *module, struct la_context *ac)
1441 struct la_op_store *op;
1443 for (op = ac->ops; op; op=op->next) {
1444 int ret = la_do_op_request(module, ac, op);
1445 if (ret != LDB_SUCCESS) {
1446 if (ret != LDB_ERR_NO_SUCH_OBJECT) {
1457 we hook into the transaction operations to allow us to
1458 perform the linked attribute updates at the end of the whole
1459 transaction. This allows a forward linked attribute to be created
1460 before the target is created, as long as the target is created
1461 in the same transaction
1463 static int linked_attributes_start_transaction(struct ldb_module *module)
1465 /* create our private structure for this transaction */
1466 struct la_private *la_private =
1467 talloc_get_type(ldb_module_get_private(module),
1470 if (la_private == NULL) {
1471 return ldb_oom(ldb_module_get_ctx(module));
1473 talloc_free(la_private->transaction);
1474 la_private->transaction = talloc(module, struct la_private_transaction);
1475 if (la_private->transaction == NULL) {
1476 return ldb_oom(ldb_module_get_ctx(module));
1478 la_private->transaction->la_list = NULL;
1479 return ldb_next_start_trans(module);
1483 on prepare commit we loop over our queued la_context structures
1484 and apply each of them
1486 static int linked_attributes_prepare_commit(struct ldb_module *module)
1488 struct la_context *ac;
1489 struct la_private *la_private =
1490 talloc_get_type(ldb_module_get_private(module),
1492 if (la_private == NULL || la_private->transaction == NULL) {
1493 DBG_ERR("prepare_commit without begin_transaction\n");
1494 /* prepare commit without begin_transaction - let someone else
1495 * return the error, just don't segfault */
1496 return ldb_next_prepare_commit(module);
1498 /* walk the list backwards, to do the first entry first, as we
1499 * added the entries with DLIST_ADD() which puts them at the
1500 * start of the list */
1502 /* Start at the end of the list - so we can start
1503 * there, but ensure we don't create a loop by NULLing
1504 * it out in the first element */
1505 ac = DLIST_TAIL(la_private->transaction->la_list);
1507 for (; ac; ac=DLIST_PREV(ac)) {
1510 ret = la_do_mod_request(module, ac);
1511 if (ret != LDB_SUCCESS) {
1512 DEBUG(0,(__location__ ": Failed mod request ret=%d\n", ret));
1513 TALLOC_FREE(la_private->transaction);
1518 TALLOC_FREE(la_private->transaction);
1520 return ldb_next_prepare_commit(module);
1523 static int linked_attributes_del_transaction(struct ldb_module *module)
1525 struct la_private *la_private =
1526 talloc_get_type(ldb_module_get_private(module),
1528 TALLOC_FREE(la_private->transaction);
1529 return ldb_next_del_trans(module);
1532 static int linked_attributes_ldb_init(struct ldb_module *module)
1535 struct la_private *la_private = NULL;
1536 struct ldb_context *ldb = ldb_module_get_ctx(module);
1538 ret = ldb_mod_register_control(module, LDB_CONTROL_VERIFY_NAME_OID);
1539 if (ret != LDB_SUCCESS) {
1540 ldb_debug(ldb_module_get_ctx(module), LDB_DEBUG_ERROR,
1541 "verify_name: Unable to register control with rootdse!\n");
1542 return ldb_operr(ldb_module_get_ctx(module));
1545 la_private = talloc_zero(module, struct la_private);
1546 if (la_private == NULL) {
1548 return LDB_ERR_OPERATIONS_ERROR;
1551 ret = dsdb_check_samba_compatible_feature(module,
1552 SAMBA_SORTED_LINKS_FEATURE,
1553 &la_private->sorted_links);
1554 if (ret != LDB_SUCCESS) {
1555 talloc_free(la_private);
1559 ldb_module_set_private(module, la_private);
1560 return ldb_next_init(module);
1564 static const struct ldb_module_ops ldb_linked_attributes_module_ops = {
1565 .name = "linked_attributes",
1566 .add = linked_attributes_add,
1567 .modify = linked_attributes_modify,
1568 .rename = linked_attributes_rename,
1569 .init_context = linked_attributes_ldb_init,
1570 .start_transaction = linked_attributes_start_transaction,
1571 .prepare_commit = linked_attributes_prepare_commit,
1572 .del_transaction = linked_attributes_del_transaction,
1575 int ldb_linked_attributes_module_init(const char *version)
1577 LDB_MODULE_CHECK_VERSION(version);
1578 return ldb_register_module(&ldb_linked_attributes_module_ops);