4 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2007
5 Copyright (C) Simo Sorce <idra@samba.org> 2008
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
24 * Component: ldb linked_attributes module
26 * Description: Module to ensure linked attribute pairs remain in sync
28 * Author: Andrew Bartlett
32 #include "ldb_module.h"
33 #include "util/dlinklist.h"
34 #include "dsdb/samdb/samdb.h"
35 #include "librpc/gen_ndr/ndr_misc.h"
36 #include "dsdb/samdb/ldb_modules/util.h"
39 struct la_context *la_list;
43 struct la_op_store *next;
44 struct la_op_store *prev;
45 enum la_op {LA_OP_ADD, LA_OP_DEL} op;
51 struct replace_context {
52 struct la_context *ac;
53 unsigned int num_elements;
54 struct ldb_message_element *el;
58 struct la_context *next, *prev;
59 const struct dsdb_schema *schema;
60 struct ldb_module *module;
61 struct ldb_request *req;
62 struct ldb_dn *add_dn;
63 struct ldb_dn *del_dn;
64 struct replace_context *rc;
65 struct la_op_store *ops;
66 struct ldb_extended *op_response;
67 struct ldb_control **op_controls;
70 static struct la_context *linked_attributes_init(struct ldb_module *module,
71 struct ldb_request *req)
73 struct ldb_context *ldb;
74 struct la_context *ac;
76 ldb = ldb_module_get_ctx(module);
78 ac = talloc_zero(req, struct la_context);
84 ac->schema = dsdb_get_schema(ldb, ac);
94 static int la_guid_from_dn(struct la_context *ac, struct ldb_dn *dn, struct GUID *guid)
99 status = dsdb_get_extended_dn_guid(dn, guid, "GUID");
100 if (NT_STATUS_IS_OK(status)) {
103 if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
104 DEBUG(4,(__location__ ": Unable to parse GUID for dn %s\n",
105 ldb_dn_get_linearized(dn)));
106 return ldb_operr(ldb_module_get_ctx(ac->module));
109 ret = dsdb_find_guid_by_dn(ldb_module_get_ctx(ac->module), dn, guid);
110 if (ret != LDB_SUCCESS) {
111 DEBUG(4,(__location__ ": Failed to find GUID for dn %s\n",
112 ldb_dn_get_linearized(dn)));
119 /* Common routine to handle reading the attributes and creating a
120 * series of modify requests */
121 static int la_store_op(struct la_context *ac,
122 enum la_op op, struct ldb_val *dn,
125 struct ldb_context *ldb;
126 struct la_op_store *os;
127 struct ldb_dn *op_dn;
130 ldb = ldb_module_get_ctx(ac->module);
132 op_dn = ldb_dn_from_ldb_val(ac, ldb, dn);
134 ldb_asprintf_errstring(ldb,
135 "could not parse attribute as a DN");
136 return LDB_ERR_INVALID_DN_SYNTAX;
139 os = talloc_zero(ac, struct la_op_store);
146 ret = la_guid_from_dn(ac, op_dn, &os->guid);
148 if (ret == LDB_ERR_NO_SUCH_OBJECT && ac->req->operation == LDB_DELETE) {
149 /* we are deleting an object, and we've found it has a
150 * forward link to a target that no longer
151 * exists. This is not an error in the delete, and we
152 * should just not do the deferred delete of the
158 if (ret != LDB_SUCCESS) {
162 os->name = talloc_strdup(os, name);
167 /* Do deletes before adds */
168 if (op == LA_OP_ADD) {
169 DLIST_ADD_END(ac->ops, os, struct la_op_store *);
171 /* By adding to the head of the list, we do deletes before
172 * adds when processing a replace */
173 DLIST_ADD(ac->ops, os);
179 static int la_queue_mod_request(struct la_context *ac);
180 static int la_down_req(struct la_context *ac);
185 static int linked_attributes_add(struct ldb_module *module, struct ldb_request *req)
187 struct ldb_context *ldb;
188 const struct dsdb_attribute *target_attr;
189 struct la_context *ac;
190 const char *attr_name;
191 struct ldb_control *ctrl;
195 ldb = ldb_module_get_ctx(module);
197 if (ldb_dn_is_special(req->op.add.message->dn)) {
198 /* do not manipulate our control entries */
199 return ldb_next_request(module, req);
202 if (!(ctrl = ldb_request_get_control(req, DSDB_CONTROL_APPLY_LINKS))) {
203 /* don't do anything special for linked attributes, repl_meta_data has done it */
204 return ldb_next_request(module, req);
206 ctrl->critical = false;
208 ac = linked_attributes_init(module, req);
210 return ldb_operr(ldb);
214 /* without schema, this doesn't make any sense */
216 return ldb_next_request(module, req);
219 /* Need to ensure we only have forward links being specified */
220 for (i=0; i < req->op.add.message->num_elements; i++) {
221 const struct ldb_message_element *el = &req->op.add.message->elements[i];
222 const struct dsdb_attribute *schema_attr
223 = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
225 ldb_asprintf_errstring(ldb,
226 "%s: attribute %s is not a valid attribute in schema",
229 return LDB_ERR_OBJECT_CLASS_VIOLATION;
231 /* We have a valid attribute, now find out if it is a forward link */
232 if ((schema_attr->linkID == 0)) {
236 if ((schema_attr->linkID & 1) == 1) {
237 unsigned int functional_level;
239 functional_level = dsdb_functional_level(ldb);
240 SMB_ASSERT(functional_level > DS_DOMAIN_FUNCTION_2000);
243 /* Even link IDs are for the originating attribute */
244 target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID + 1);
247 * windows 2003 has a broken schema where
248 * the definition of msDS-IsDomainFor
249 * is missing (which is supposed to be
250 * the backlink of the msDS-HasDomainNCs
256 attr_name = target_attr->lDAPDisplayName;
258 for (j = 0; j < el->num_values; j++) {
259 ret = la_store_op(ac, LA_OP_ADD,
262 if (ret != LDB_SUCCESS) {
268 /* if no linked attributes are present continue */
269 if (ac->ops == NULL) {
270 /* nothing to do for this module, proceed */
272 return ldb_next_request(module, req);
275 /* start with the original request */
276 return la_down_req(ac);
279 /* For a delete or rename, we need to find out what linked attributes
280 * are currently on this DN, and then deal with them. This is the
281 * callback to the base search */
283 static int la_mod_search_callback(struct ldb_request *req, struct ldb_reply *ares)
285 struct ldb_context *ldb;
286 const struct dsdb_attribute *schema_attr;
287 const struct dsdb_attribute *target_attr;
288 struct ldb_message_element *search_el;
289 struct replace_context *rc;
290 struct la_context *ac;
291 const char *attr_name;
293 int ret = LDB_SUCCESS;
295 ac = talloc_get_type(req->context, struct la_context);
296 ldb = ldb_module_get_ctx(ac->module);
300 return ldb_module_done(ac->req, NULL, NULL,
301 LDB_ERR_OPERATIONS_ERROR);
303 if (ares->error != LDB_SUCCESS) {
304 return ldb_module_done(ac->req, ares->controls,
305 ares->response, ares->error);
308 /* Only entries are interesting, and we only want the olddn */
309 switch (ares->type) {
310 case LDB_REPLY_ENTRY:
312 if (ldb_dn_compare(ares->message->dn, ac->req->op.mod.message->dn) != 0) {
313 ldb_asprintf_errstring(ldb,
314 "linked_attributes: %s is not the DN we were looking for",
315 ldb_dn_get_linearized(ares->message->dn));
316 /* Guh? We only asked for this DN */
318 return ldb_module_done(ac->req, NULL, NULL,
319 LDB_ERR_OPERATIONS_ERROR);
322 ac->add_dn = ac->del_dn = talloc_steal(ac, ares->message->dn);
324 /* We don't populate 'rc' for ADD - it can't be deleting elements anyway */
325 for (i = 0; rc && i < rc->num_elements; i++) {
327 schema_attr = dsdb_attribute_by_lDAPDisplayName(ac->schema, rc->el[i].name);
329 ldb_asprintf_errstring(ldb,
330 "%s: attribute %s is not a valid attribute in schema",
334 return ldb_module_done(ac->req, NULL, NULL,
335 LDB_ERR_OBJECT_CLASS_VIOLATION);
338 search_el = ldb_msg_find_element(ares->message,
341 /* See if this element already exists */
342 /* otherwise just ignore as
343 * the add has already been scheduled */
348 target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID + 1);
351 * windows 2003 has a broken schema where
352 * the definition of msDS-IsDomainFor
353 * is missing (which is supposed to be
354 * the backlink of the msDS-HasDomainNCs
359 attr_name = target_attr->lDAPDisplayName;
361 /* Now we know what was there, we can remove it for the re-add */
362 for (j = 0; j < search_el->num_values; j++) {
363 ret = la_store_op(ac, LA_OP_DEL,
364 &search_el->values[j],
366 if (ret != LDB_SUCCESS) {
368 return ldb_module_done(ac->req,
376 case LDB_REPLY_REFERRAL:
384 if (ac->req->operation == LDB_ADD) {
385 /* Start the modifies to the backlinks */
386 ret = la_queue_mod_request(ac);
388 if (ret != LDB_SUCCESS) {
389 return ldb_module_done(ac->req, NULL, NULL,
393 /* Start with the original request */
394 ret = la_down_req(ac);
395 if (ret != LDB_SUCCESS) {
396 return ldb_module_done(ac->req, NULL, NULL, ret);
408 static int linked_attributes_modify(struct ldb_module *module, struct ldb_request *req)
410 /* Look over list of modifications */
411 /* Find if any are for linked attributes */
412 /* Determine the effect of the modification */
413 /* Apply the modify to the linked entry */
415 struct ldb_context *ldb;
417 struct la_context *ac;
418 struct ldb_request *search_req;
420 struct ldb_control *ctrl;
423 ldb = ldb_module_get_ctx(module);
425 if (ldb_dn_is_special(req->op.mod.message->dn)) {
426 /* do not manipulate our control entries */
427 return ldb_next_request(module, req);
430 if (!(ctrl = ldb_request_get_control(req, DSDB_CONTROL_APPLY_LINKS))) {
431 /* don't do anything special for linked attributes, repl_meta_data has done it */
432 return ldb_next_request(module, req);
434 ctrl->critical = false;
436 ac = linked_attributes_init(module, req);
438 return ldb_operr(ldb);
442 /* without schema, this doesn't make any sense */
443 return ldb_next_request(module, req);
446 ac->rc = talloc_zero(ac, struct replace_context);
451 for (i=0; i < req->op.mod.message->num_elements; i++) {
452 bool store_el = false;
453 const char *attr_name;
454 const struct dsdb_attribute *target_attr;
455 const struct ldb_message_element *el = &req->op.mod.message->elements[i];
456 const struct dsdb_attribute *schema_attr
457 = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
459 ldb_asprintf_errstring(ldb,
460 "%s: attribute %s is not a valid attribute in schema",
463 return LDB_ERR_OBJECT_CLASS_VIOLATION;
465 /* We have a valid attribute, now find out if it is a forward link
466 (Even link IDs are for the originating attribute) */
467 if (schema_attr->linkID == 0) {
471 if ((schema_attr->linkID & 1) == 1) {
472 unsigned int functional_level;
474 functional_level = dsdb_functional_level(ldb);
475 SMB_ASSERT(functional_level > DS_DOMAIN_FUNCTION_2000);
477 /* Now find the target attribute */
478 target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID + 1);
481 * windows 2003 has a broken schema where
482 * the definition of msDS-IsDomainFor
483 * is missing (which is supposed to be
484 * the backlink of the msDS-HasDomainNCs
490 attr_name = target_attr->lDAPDisplayName;
492 switch (el->flags & LDB_FLAG_MOD_MASK) {
493 case LDB_FLAG_MOD_REPLACE:
494 /* treat as just a normal add the delete part is handled by the callback */
497 /* break intentionally missing */
499 case LDB_FLAG_MOD_ADD:
501 /* For each value being added, we need to setup the adds */
502 for (j = 0; j < el->num_values; j++) {
503 ret = la_store_op(ac, LA_OP_ADD,
506 if (ret != LDB_SUCCESS) {
512 case LDB_FLAG_MOD_DELETE:
514 if (el->num_values) {
515 /* For each value being deleted, we need to setup the delete */
516 for (j = 0; j < el->num_values; j++) {
517 ret = la_store_op(ac, LA_OP_DEL,
520 if (ret != LDB_SUCCESS) {
525 /* Flag that there was a DELETE
526 * without a value specified, so we
527 * need to look for the old value */
535 struct ldb_message_element *search_el;
537 search_el = talloc_realloc(ac->rc, ac->rc->el,
538 struct ldb_message_element,
539 ac->rc->num_elements +1);
543 ac->rc->el = search_el;
545 ac->rc->el[ac->rc->num_elements] = *el;
546 ac->rc->num_elements++;
550 if (ac->ops || ac->rc->el) {
551 /* both replace and delete without values are handled in the callback
552 * after the search on the entry to be modified is performed */
554 attrs = talloc_array(ac->rc, const char *, ac->rc->num_elements + 1);
558 for (i = 0; ac->rc && i < ac->rc->num_elements; i++) {
559 attrs[i] = ac->rc->el[i].name;
563 /* The callback does all the hard work here */
564 ret = ldb_build_search_req(&search_req, ldb, ac,
565 req->op.mod.message->dn,
567 "(objectClass=*)", attrs,
569 ac, la_mod_search_callback,
571 LDB_REQ_SET_LOCATION(search_req);
573 /* We need to figure out our own extended DN, to fill in as the backlink target */
574 if (ret == LDB_SUCCESS) {
575 ret = ldb_request_add_control(search_req,
576 LDB_CONTROL_EXTENDED_DN_OID,
579 if (ret == LDB_SUCCESS) {
580 talloc_steal(search_req, attrs);
582 ret = ldb_next_request(module, search_req);
586 /* nothing to do for this module, proceed */
588 ret = ldb_next_request(module, req);
594 static int linked_attributes_fix_links(struct ldb_module *module,
595 struct ldb_dn *old_dn, struct ldb_dn *new_dn,
596 struct ldb_message_element *el, struct dsdb_schema *schema,
597 const struct dsdb_attribute *schema_attr,
598 struct ldb_request *parent)
601 TALLOC_CTX *tmp_ctx = talloc_new(module);
602 struct ldb_context *ldb = ldb_module_get_ctx(module);
603 const struct dsdb_attribute *target;
604 const char *attrs[2];
607 target = dsdb_attribute_by_linkID(schema, schema_attr->linkID ^ 1);
608 if (target == NULL) {
609 /* there is no counterpart link to change */
613 attrs[0] = target->lDAPDisplayName;
616 for (i=0; i<el->num_values; i++) {
617 struct dsdb_dn *dsdb_dn;
618 struct ldb_result *res;
619 struct ldb_message *msg;
620 struct ldb_message_element *el2;
622 dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i], schema_attr->syntax->ldap_oid);
623 if (dsdb_dn == NULL) {
624 talloc_free(tmp_ctx);
625 return LDB_ERR_INVALID_DN_SYNTAX;
628 ret = dsdb_module_search_dn(module, tmp_ctx, &res, dsdb_dn->dn,
630 DSDB_FLAG_NEXT_MODULE |
631 DSDB_SEARCH_SHOW_RECYCLED |
632 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
633 DSDB_SEARCH_REVEAL_INTERNALS, parent);
634 if (ret != LDB_SUCCESS) {
635 ldb_asprintf_errstring(ldb, "Linked attribute %s->%s between %s and %s - remote not found - %s",
636 el->name, target->lDAPDisplayName,
637 ldb_dn_get_linearized(old_dn),
638 ldb_dn_get_linearized(dsdb_dn->dn),
640 talloc_free(tmp_ctx);
645 if (msg->num_elements == 0) {
646 /* Forward link without backlink remaining - nothing to do here */
648 } else if (msg->num_elements != 1) {
649 ldb_asprintf_errstring(ldb, "Bad msg elements - got %u elements, expected one element to be returned in linked_attributes_fix_links for %s",
650 msg->num_elements, ldb_dn_get_linearized(msg->dn));
651 talloc_free(tmp_ctx);
652 return LDB_ERR_OPERATIONS_ERROR;
654 if (ldb_attr_cmp(msg->elements[0].name, target->lDAPDisplayName) != 0) {
655 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));
656 talloc_free(tmp_ctx);
657 return LDB_ERR_OPERATIONS_ERROR;
659 el2 = &msg->elements[0];
661 el2->flags = LDB_FLAG_MOD_REPLACE;
663 /* find our DN in the values */
664 for (j=0; j<el2->num_values; j++) {
665 struct dsdb_dn *dsdb_dn2;
666 dsdb_dn2 = dsdb_dn_parse(msg, ldb, &el2->values[j], target->syntax->ldap_oid);
667 if (dsdb_dn2 == NULL) {
668 talloc_free(tmp_ctx);
669 return LDB_ERR_INVALID_DN_SYNTAX;
671 if (ldb_dn_compare(old_dn, dsdb_dn2->dn) != 0) {
674 ret = ldb_dn_update_components(dsdb_dn2->dn, new_dn);
675 if (ret != LDB_SUCCESS) {
676 talloc_free(tmp_ctx);
680 el2->values[j] = data_blob_string_const(
681 dsdb_dn_get_extended_linearized(el2->values, dsdb_dn2, 1));
684 ret = dsdb_check_single_valued_link(target, el2);
685 if (ret != LDB_SUCCESS) {
686 talloc_free(tmp_ctx);
690 /* we may be putting multiple values in an attribute -
691 disable checking for this attribute */
692 el2->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
694 ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE, parent);
695 if (ret != LDB_SUCCESS) {
696 ldb_asprintf_errstring(ldb, "Linked attribute %s->%s between %s and %s - update failed - %s",
697 el->name, target->lDAPDisplayName,
698 ldb_dn_get_linearized(old_dn),
699 ldb_dn_get_linearized(dsdb_dn->dn),
701 talloc_free(tmp_ctx);
706 talloc_free(tmp_ctx);
712 static int linked_attributes_rename(struct ldb_module *module, struct ldb_request *req)
714 struct ldb_result *res;
715 struct ldb_message *msg;
717 struct ldb_context *ldb = ldb_module_get_ctx(module);
718 struct dsdb_schema *schema;
721 - load the current msg
722 - find any linked attributes
723 - if its a link then find the target object
724 - modify the target linked attributes with the new DN
726 ret = dsdb_module_search_dn(module, req, &res, req->op.rename.olddn,
728 DSDB_FLAG_NEXT_MODULE |
729 DSDB_SEARCH_SHOW_RECYCLED, req);
730 if (ret != LDB_SUCCESS) {
734 schema = dsdb_get_schema(ldb, res);
741 for (i=0; i<msg->num_elements; i++) {
742 struct ldb_message_element *el = &msg->elements[i];
743 const struct dsdb_attribute *schema_attr
744 = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
745 if (!schema_attr || schema_attr->linkID == 0) {
748 ret = linked_attributes_fix_links(module, msg->dn, req->op.rename.newdn, el,
749 schema, schema_attr, req);
750 if (ret != LDB_SUCCESS) {
758 return ldb_next_request(module, req);
762 /* queue a linked attributes modify request in the la_private
764 static int la_queue_mod_request(struct la_context *ac)
766 struct la_private *la_private =
767 talloc_get_type(ldb_module_get_private(ac->module), struct la_private);
769 if (la_private == NULL) {
770 ldb_debug(ldb_module_get_ctx(ac->module), LDB_DEBUG_ERROR, __location__ ": No la_private transaction setup\n");
771 return ldb_operr(ldb_module_get_ctx(ac->module));
774 talloc_steal(la_private, ac);
775 DLIST_ADD(la_private->la_list, ac);
777 return ldb_module_done(ac->req, ac->op_controls,
778 ac->op_response, LDB_SUCCESS);
781 /* Having done the original operation, then try to fix up all the linked attributes for modify and delete */
782 static int la_mod_del_callback(struct ldb_request *req, struct ldb_reply *ares)
784 struct la_context *ac;
785 struct ldb_context *ldb;
788 ac = talloc_get_type(req->context, struct la_context);
789 ldb = ldb_module_get_ctx(ac->module);
792 return ldb_module_done(ac->req, NULL, NULL,
793 LDB_ERR_OPERATIONS_ERROR);
795 if (ares->error != LDB_SUCCESS) {
796 return ldb_module_done(ac->req, ares->controls,
797 ares->response, ares->error);
800 if (ares->type != LDB_REPLY_DONE) {
801 ldb_set_errstring(ldb,
802 "invalid ldb_reply_type in callback");
804 return ldb_module_done(ac->req, NULL, NULL,
805 LDB_ERR_OPERATIONS_ERROR);
808 ac->op_controls = talloc_steal(ac, ares->controls);
809 ac->op_response = talloc_steal(ac, ares->response);
811 /* If we have modfies to make, this is the time to do them for modify and delete */
812 ret = la_queue_mod_request(ac);
814 if (ret != LDB_SUCCESS) {
815 return ldb_module_done(ac->req, NULL, NULL, ret);
819 /* la_queue_mod_request has already sent the callbacks */
824 /* Having done the original add, then try to fix up all the linked attributes
826 This is done after the add so the links can get the extended DNs correctly.
828 static int la_add_callback(struct ldb_request *req, struct ldb_reply *ares)
830 struct la_context *ac;
831 struct ldb_context *ldb;
834 ac = talloc_get_type(req->context, struct la_context);
835 ldb = ldb_module_get_ctx(ac->module);
838 return ldb_module_done(ac->req, NULL, NULL,
839 LDB_ERR_OPERATIONS_ERROR);
841 if (ares->error != LDB_SUCCESS) {
842 return ldb_module_done(ac->req, ares->controls,
843 ares->response, ares->error);
846 if (ares->type != LDB_REPLY_DONE) {
847 ldb_set_errstring(ldb,
848 "invalid ldb_reply_type in callback");
850 return ldb_module_done(ac->req, NULL, NULL,
851 LDB_ERR_OPERATIONS_ERROR);
855 struct ldb_request *search_req;
856 static const char *attrs[] = { NULL };
858 /* The callback does all the hard work here - we need
859 * the objectGUID and SID of the added record */
860 ret = ldb_build_search_req(&search_req, ldb, ac,
861 ac->req->op.add.message->dn,
863 "(objectClass=*)", attrs,
865 ac, la_mod_search_callback,
867 LDB_REQ_SET_LOCATION(search_req);
869 if (ret == LDB_SUCCESS) {
870 ret = ldb_request_add_control(search_req,
871 LDB_CONTROL_EXTENDED_DN_OID,
874 if (ret != LDB_SUCCESS) {
875 return ldb_module_done(ac->req, NULL, NULL,
879 ac->op_controls = talloc_steal(ac, ares->controls);
880 ac->op_response = talloc_steal(ac, ares->response);
882 return ldb_next_request(ac->module, search_req);
885 return ldb_module_done(ac->req, ares->controls,
886 ares->response, ares->error);
890 /* Reconstruct the original request, but pointing at our local callback to finish things off */
891 static int la_down_req(struct la_context *ac)
893 struct ldb_request *down_req;
894 struct ldb_context *ldb;
897 ldb = ldb_module_get_ctx(ac->module);
899 switch (ac->req->operation) {
901 ret = ldb_build_add_req(&down_req, ldb, ac,
902 ac->req->op.add.message,
906 LDB_REQ_SET_LOCATION(down_req);
909 ret = ldb_build_mod_req(&down_req, ldb, ac,
910 ac->req->op.mod.message,
912 ac, la_mod_del_callback,
914 LDB_REQ_SET_LOCATION(down_req);
917 ret = LDB_ERR_OPERATIONS_ERROR;
919 if (ret != LDB_SUCCESS) {
923 return ldb_next_request(ac->module, down_req);
927 use the GUID part of an extended DN to find the target DN, in case
930 static int la_find_dn_target(struct ldb_module *module, struct la_context *ac,
931 struct GUID *guid, struct ldb_dn **dn)
933 return dsdb_find_dn_by_guid(ldb_module_get_ctx(ac->module), ac, guid, dn);
936 /* apply one la_context op change */
937 static int la_do_op_request(struct ldb_module *module, struct la_context *ac, struct la_op_store *op)
939 struct ldb_message_element *ret_el;
940 struct ldb_message *new_msg;
941 struct ldb_context *ldb;
944 ldb = ldb_module_get_ctx(ac->module);
946 /* Create the modify request */
947 new_msg = ldb_msg_new(ac);
952 ret = la_find_dn_target(module, ac, &op->guid, &new_msg->dn);
953 if (ret != LDB_SUCCESS) {
957 if (op->op == LA_OP_ADD) {
958 ret = ldb_msg_add_empty(new_msg, op->name,
959 LDB_FLAG_MOD_ADD, &ret_el);
961 ret = ldb_msg_add_empty(new_msg, op->name,
962 LDB_FLAG_MOD_DELETE, &ret_el);
964 if (ret != LDB_SUCCESS) {
967 ret_el->values = talloc_array(new_msg, struct ldb_val, 1);
968 if (!ret_el->values) {
971 ret_el->num_values = 1;
972 if (op->op == LA_OP_ADD) {
973 ret_el->values[0] = data_blob_string_const(ldb_dn_get_extended_linearized(new_msg, ac->add_dn, 1));
975 ret_el->values[0] = data_blob_string_const(ldb_dn_get_extended_linearized(new_msg, ac->del_dn, 1));
978 /* a backlink should never be single valued. Unfortunately the
979 exchange schema has a attribute
980 msExchBridgeheadedLocalConnectorsDNBL which is single
981 valued and a backlink. We need to cope with that by
982 ignoring the single value flag */
983 ret_el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
986 ldb_debug(ldb, LDB_DEBUG_WARNING,
987 "link on %s %s: %s %s\n",
988 ldb_dn_get_linearized(new_msg->dn), ret_el->name,
989 ret_el->values[0].data, ac->ops->op == LA_OP_ADD ? "added" : "deleted");
993 DEBUG(4,("Applying linked attribute change:\n%s\n",
994 ldb_ldif_message_string(ldb, op, LDB_CHANGETYPE_MODIFY, new_msg)));
997 ret = dsdb_module_modify(module, new_msg, DSDB_FLAG_NEXT_MODULE, ac->req);
998 if (ret != LDB_SUCCESS) {
999 ldb_debug(ldb, LDB_DEBUG_WARNING, "Failed to apply linked attribute change '%s'\n%s\n",
1001 ldb_ldif_message_string(ldb, op, LDB_CHANGETYPE_MODIFY, new_msg));
1007 /* apply one set of la_context changes */
1008 static int la_do_mod_request(struct ldb_module *module, struct la_context *ac)
1010 struct la_op_store *op;
1012 for (op = ac->ops; op; op=op->next) {
1013 int ret = la_do_op_request(module, ac, op);
1014 if (ret != LDB_SUCCESS) {
1015 if (ret != LDB_ERR_NO_SUCH_OBJECT) {
1026 we hook into the transaction operations to allow us to
1027 perform the linked attribute updates at the end of the whole
1028 transaction. This allows a forward linked attribute to be created
1029 before the target is created, as long as the target is created
1030 in the same transaction
1032 static int linked_attributes_start_transaction(struct ldb_module *module)
1034 /* create our private structure for this transaction */
1035 struct la_private *la_private = talloc_get_type(ldb_module_get_private(module),
1037 talloc_free(la_private);
1038 la_private = talloc(module, struct la_private);
1039 if (la_private == NULL) {
1040 return ldb_oom(ldb_module_get_ctx(module));
1042 la_private->la_list = NULL;
1043 ldb_module_set_private(module, la_private);
1044 return ldb_next_start_trans(module);
1048 on prepare commit we loop over our queued la_context structures
1049 and apply each of them
1051 static int linked_attributes_prepare_commit(struct ldb_module *module)
1053 struct la_private *la_private =
1054 talloc_get_type(ldb_module_get_private(module), struct la_private);
1055 struct la_context *ac;
1058 /* prepare commit without begin_transaction - let someone else return the error, just don't segfault */
1059 return ldb_next_prepare_commit(module);
1061 /* walk the list backwards, to do the first entry first, as we
1062 * added the entries with DLIST_ADD() which puts them at the
1063 * start of the list */
1065 /* Start at the end of the list - so we can start
1066 * there, but ensure we don't create a loop by NULLing
1067 * it out in the first element */
1068 ac = DLIST_TAIL(la_private->la_list);
1070 for (; ac; ac=DLIST_PREV(ac)) {
1073 ret = la_do_mod_request(module, ac);
1074 if (ret != LDB_SUCCESS) {
1075 DEBUG(0,(__location__ ": Failed mod request ret=%d\n", ret));
1076 talloc_free(la_private);
1077 ldb_module_set_private(module, NULL);
1082 talloc_free(la_private);
1083 ldb_module_set_private(module, NULL);
1085 return ldb_next_prepare_commit(module);
1088 static int linked_attributes_del_transaction(struct ldb_module *module)
1090 struct la_private *la_private =
1091 talloc_get_type(ldb_module_get_private(module), struct la_private);
1092 talloc_free(la_private);
1093 ldb_module_set_private(module, NULL);
1094 return ldb_next_del_trans(module);
1098 static const struct ldb_module_ops ldb_linked_attributes_module_ops = {
1099 .name = "linked_attributes",
1100 .add = linked_attributes_add,
1101 .modify = linked_attributes_modify,
1102 .rename = linked_attributes_rename,
1103 .start_transaction = linked_attributes_start_transaction,
1104 .prepare_commit = linked_attributes_prepare_commit,
1105 .del_transaction = linked_attributes_del_transaction,
1108 int ldb_linked_attributes_module_init(const char *version)
1110 LDB_MODULE_CHECK_VERSION(version);
1111 return ldb_register_module(&ldb_linked_attributes_module_ops);