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;
50 struct replace_context {
51 struct la_context *ac;
52 unsigned int num_elements;
53 struct ldb_message_element *el;
57 struct la_context *next, *prev;
58 const struct dsdb_schema *schema;
59 struct ldb_module *module;
60 struct ldb_request *req;
61 struct ldb_dn *mod_dn;
62 struct replace_context *rc;
63 struct la_op_store *ops;
64 struct ldb_extended *op_response;
65 struct ldb_control **op_controls;
68 static struct la_context *linked_attributes_init(struct ldb_module *module,
69 struct ldb_request *req)
71 struct ldb_context *ldb;
72 struct la_context *ac;
74 ldb = ldb_module_get_ctx(module);
76 ac = talloc_zero(req, struct la_context);
82 ac->schema = dsdb_get_schema(ldb, ac);
92 static int la_guid_from_dn(struct la_context *ac, struct ldb_dn *dn, struct GUID *guid)
97 status = dsdb_get_extended_dn_guid(dn, guid, "GUID");
98 if (NT_STATUS_IS_OK(status)) {
101 if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
102 DEBUG(4,(__location__ ": Unable to parse GUID for dn %s\n",
103 ldb_dn_get_linearized(dn)));
104 return ldb_operr(ldb_module_get_ctx(ac->module));
107 ret = dsdb_find_guid_by_dn(ldb_module_get_ctx(ac->module), dn, guid);
108 if (ret != LDB_SUCCESS) {
109 DEBUG(4,(__location__ ": Failed to find GUID for dn %s\n",
110 ldb_dn_get_linearized(dn)));
117 /* Common routine to handle reading the attributes and creating a
118 * series of modify requests */
119 static int la_store_op(struct la_context *ac,
120 enum la_op op, struct ldb_val *dn,
123 struct ldb_context *ldb;
124 struct la_op_store *os;
125 struct ldb_dn *op_dn;
128 ldb = ldb_module_get_ctx(ac->module);
130 op_dn = ldb_dn_from_ldb_val(ac, ldb, dn);
132 ldb_asprintf_errstring(ldb,
133 "could not parse attribute as a DN");
134 return LDB_ERR_INVALID_DN_SYNTAX;
137 os = talloc_zero(ac, struct la_op_store);
144 ret = la_guid_from_dn(ac, op_dn, &os->guid);
146 if (ret == LDB_ERR_NO_SUCH_OBJECT && ac->req->operation == LDB_DELETE) {
147 /* we are deleting an object, and we've found it has a
148 * forward link to a target that no longer
149 * exists. This is not an error in the delete, and we
150 * should just not do the deferred delete of the
156 if (ret != LDB_SUCCESS) {
160 os->name = talloc_strdup(os, name);
165 /* Do deletes before adds */
166 if (op == LA_OP_ADD) {
167 DLIST_ADD_END(ac->ops, os, struct la_op_store *);
169 /* By adding to the head of the list, we do deletes before
170 * adds when processing a replace */
171 DLIST_ADD(ac->ops, os);
177 static int la_queue_mod_request(struct la_context *ac);
178 static int la_down_req(struct la_context *ac);
183 static int linked_attributes_add(struct ldb_module *module, struct ldb_request *req)
185 struct ldb_context *ldb;
186 const struct dsdb_attribute *target_attr;
187 struct la_context *ac;
188 const char *attr_name;
189 struct ldb_control *ctrl;
193 ldb = ldb_module_get_ctx(module);
195 if (ldb_dn_is_special(req->op.add.message->dn)) {
196 /* do not manipulate our control entries */
197 return ldb_next_request(module, req);
200 if (!(ctrl = ldb_request_get_control(req, DSDB_CONTROL_APPLY_LINKS))) {
201 /* don't do anything special for linked attributes, repl_meta_data has done it */
202 return ldb_next_request(module, req);
204 ctrl->critical = false;
206 ac = linked_attributes_init(module, req);
208 return ldb_operr(ldb);
212 /* without schema, this doesn't make any sense */
214 return ldb_next_request(module, req);
217 /* Need to ensure we only have forward links being specified */
218 for (i=0; i < req->op.add.message->num_elements; i++) {
219 const struct ldb_message_element *el = &req->op.add.message->elements[i];
220 const struct dsdb_attribute *schema_attr
221 = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
223 ldb_asprintf_errstring(ldb,
224 "%s: attribute %s is not a valid attribute in schema",
227 return LDB_ERR_OBJECT_CLASS_VIOLATION;
229 /* We have a valid attribute, now find out if it is a forward link */
230 if ((schema_attr->linkID == 0)) {
234 if ((schema_attr->linkID & 1) == 1) {
235 unsigned int functional_level;
237 functional_level = dsdb_functional_level(ldb);
238 SMB_ASSERT(functional_level > DS_DOMAIN_FUNCTION_2000);
241 /* Even link IDs are for the originating attribute */
242 target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID + 1);
245 * windows 2003 has a broken schema where
246 * the definition of msDS-IsDomainFor
247 * is missing (which is supposed to be
248 * the backlink of the msDS-HasDomainNCs
254 attr_name = target_attr->lDAPDisplayName;
256 for (j = 0; j < el->num_values; j++) {
257 ret = la_store_op(ac, LA_OP_ADD,
260 if (ret != LDB_SUCCESS) {
266 /* if no linked attributes are present continue */
267 if (ac->ops == NULL) {
268 /* nothing to do for this module, proceed */
270 return ldb_next_request(module, req);
273 /* start with the original request */
274 return la_down_req(ac);
277 /* For a delete or rename, we need to find out what linked attributes
278 * are currently on this DN, and then deal with them. This is the
279 * callback to the base search */
281 static int la_mod_search_callback(struct ldb_request *req, struct ldb_reply *ares)
283 struct ldb_context *ldb;
284 const struct dsdb_attribute *schema_attr;
285 const struct dsdb_attribute *target_attr;
286 struct ldb_message_element *search_el;
287 struct replace_context *rc;
288 struct la_context *ac;
289 const char *attr_name;
291 int ret = LDB_SUCCESS;
293 ac = talloc_get_type(req->context, struct la_context);
294 ldb = ldb_module_get_ctx(ac->module);
298 return ldb_module_done(ac->req, NULL, NULL,
299 LDB_ERR_OPERATIONS_ERROR);
301 if (ares->error != LDB_SUCCESS) {
302 return ldb_module_done(ac->req, ares->controls,
303 ares->response, ares->error);
306 /* Only entries are interesting, and we only want the olddn */
307 switch (ares->type) {
308 case LDB_REPLY_ENTRY:
310 if (ldb_dn_compare(ares->message->dn, ac->req->op.mod.message->dn) != 0) {
311 ldb_asprintf_errstring(ldb,
312 "linked_attributes: %s is not the DN we were looking for",
313 ldb_dn_get_linearized(ares->message->dn));
314 /* Guh? We only asked for this DN */
316 return ldb_module_done(ac->req, NULL, NULL,
317 LDB_ERR_OPERATIONS_ERROR);
320 ac->mod_dn = talloc_steal(ac, ares->message->dn);
322 /* We don't populate 'rc' for ADD - it can't be deleting elements anyway */
323 for (i = 0; rc && i < rc->num_elements; i++) {
325 schema_attr = dsdb_attribute_by_lDAPDisplayName(ac->schema, rc->el[i].name);
327 ldb_asprintf_errstring(ldb,
328 "%s: attribute %s is not a valid attribute in schema",
332 return ldb_module_done(ac->req, NULL, NULL,
333 LDB_ERR_OBJECT_CLASS_VIOLATION);
336 search_el = ldb_msg_find_element(ares->message,
339 /* See if this element already exists */
340 /* otherwise just ignore as
341 * the add has already been scheduled */
346 target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID + 1);
349 * windows 2003 has a broken schema where
350 * the definition of msDS-IsDomainFor
351 * is missing (which is supposed to be
352 * the backlink of the msDS-HasDomainNCs
357 attr_name = target_attr->lDAPDisplayName;
359 /* Now we know what was there, we can remove it for the re-add */
360 for (j = 0; j < search_el->num_values; j++) {
361 ret = la_store_op(ac, LA_OP_DEL,
362 &search_el->values[j],
364 if (ret != LDB_SUCCESS) {
366 return ldb_module_done(ac->req,
374 case LDB_REPLY_REFERRAL:
382 if (ac->req->operation == LDB_ADD) {
383 /* Start the modifies to the backlinks */
384 ret = la_queue_mod_request(ac);
386 if (ret != LDB_SUCCESS) {
387 return ldb_module_done(ac->req, NULL, NULL,
391 /* Start with the original request */
392 ret = la_down_req(ac);
393 if (ret != LDB_SUCCESS) {
394 return ldb_module_done(ac->req, NULL, NULL, ret);
406 static int linked_attributes_modify(struct ldb_module *module, struct ldb_request *req)
408 /* Look over list of modifications */
409 /* Find if any are for linked attributes */
410 /* Determine the effect of the modification */
411 /* Apply the modify to the linked entry */
413 struct ldb_context *ldb;
415 struct la_context *ac;
416 struct ldb_request *search_req;
418 struct ldb_control *ctrl;
421 ldb = ldb_module_get_ctx(module);
423 if (ldb_dn_is_special(req->op.mod.message->dn)) {
424 /* do not manipulate our control entries */
425 return ldb_next_request(module, req);
428 if (!(ctrl = ldb_request_get_control(req, DSDB_CONTROL_APPLY_LINKS))) {
429 /* don't do anything special for linked attributes, repl_meta_data has done it */
430 return ldb_next_request(module, req);
432 ctrl->critical = false;
434 ac = linked_attributes_init(module, req);
436 return ldb_operr(ldb);
440 /* without schema, this doesn't make any sense */
441 return ldb_next_request(module, req);
444 ac->rc = talloc_zero(ac, struct replace_context);
449 for (i=0; i < req->op.mod.message->num_elements; i++) {
450 bool store_el = false;
451 const char *attr_name;
452 const struct dsdb_attribute *target_attr;
453 const struct ldb_message_element *el = &req->op.mod.message->elements[i];
454 const struct dsdb_attribute *schema_attr
455 = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
457 ldb_asprintf_errstring(ldb,
458 "%s: attribute %s is not a valid attribute in schema",
461 return LDB_ERR_OBJECT_CLASS_VIOLATION;
463 /* We have a valid attribute, now find out if it is a forward link
464 (Even link IDs are for the originating attribute) */
465 if (schema_attr->linkID == 0) {
469 if ((schema_attr->linkID & 1) == 1) {
470 unsigned int functional_level;
472 functional_level = dsdb_functional_level(ldb);
473 SMB_ASSERT(functional_level > DS_DOMAIN_FUNCTION_2000);
475 /* Now find the target attribute */
476 target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID + 1);
479 * windows 2003 has a broken schema where
480 * the definition of msDS-IsDomainFor
481 * is missing (which is supposed to be
482 * the backlink of the msDS-HasDomainNCs
488 attr_name = target_attr->lDAPDisplayName;
490 switch (el->flags & LDB_FLAG_MOD_MASK) {
491 case LDB_FLAG_MOD_REPLACE:
492 /* treat as just a normal add the delete part is handled by the callback */
495 /* break intentionally missing */
497 case LDB_FLAG_MOD_ADD:
499 /* For each value being added, we need to setup the adds */
500 for (j = 0; j < el->num_values; j++) {
501 ret = la_store_op(ac, LA_OP_ADD,
504 if (ret != LDB_SUCCESS) {
510 case LDB_FLAG_MOD_DELETE:
512 if (el->num_values) {
513 /* For each value being deleted, we need to setup the delete */
514 for (j = 0; j < el->num_values; j++) {
515 ret = la_store_op(ac, LA_OP_DEL,
518 if (ret != LDB_SUCCESS) {
523 /* Flag that there was a DELETE
524 * without a value specified, so we
525 * need to look for the old value */
533 struct ldb_message_element *search_el;
535 search_el = talloc_realloc(ac->rc, ac->rc->el,
536 struct ldb_message_element,
537 ac->rc->num_elements +1);
541 ac->rc->el = search_el;
543 ac->rc->el[ac->rc->num_elements] = *el;
544 ac->rc->num_elements++;
548 if (ac->ops || ac->rc->el) {
549 /* both replace and delete without values are handled in the callback
550 * after the search on the entry to be modified is performed */
552 attrs = talloc_array(ac->rc, const char *, ac->rc->num_elements + 1);
556 for (i = 0; ac->rc && i < ac->rc->num_elements; i++) {
557 attrs[i] = ac->rc->el[i].name;
561 /* The callback does all the hard work here */
562 ret = ldb_build_search_req(&search_req, ldb, ac,
563 req->op.mod.message->dn,
565 "(objectClass=*)", attrs,
567 ac, la_mod_search_callback,
569 LDB_REQ_SET_LOCATION(search_req);
571 /* We need to figure out our own extended DN, to fill in as the backlink target */
572 if (ret == LDB_SUCCESS) {
573 ret = dsdb_request_add_controls(search_req,
574 DSDB_SEARCH_SHOW_DELETED |
575 DSDB_SEARCH_SHOW_EXTENDED_DN);
577 if (ret == LDB_SUCCESS) {
578 talloc_steal(search_req, attrs);
580 ret = ldb_next_request(module, search_req);
584 /* nothing to do for this module, proceed */
586 ret = ldb_next_request(module, req);
592 static int linked_attributes_fix_links(struct ldb_module *module,
593 struct ldb_dn *old_dn, struct ldb_dn *new_dn,
594 struct ldb_message_element *el, struct dsdb_schema *schema,
595 const struct dsdb_attribute *schema_attr,
596 struct ldb_request *parent)
599 TALLOC_CTX *tmp_ctx = talloc_new(module);
600 struct ldb_context *ldb = ldb_module_get_ctx(module);
601 const struct dsdb_attribute *target;
602 const char *attrs[2];
605 target = dsdb_attribute_by_linkID(schema, schema_attr->linkID ^ 1);
606 if (target == NULL) {
607 /* there is no counterpart link to change */
611 attrs[0] = target->lDAPDisplayName;
614 for (i=0; i<el->num_values; i++) {
615 struct dsdb_dn *dsdb_dn;
616 struct ldb_result *res;
617 struct ldb_message *msg;
618 struct ldb_message_element *el2;
620 dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i], schema_attr->syntax->ldap_oid);
621 if (dsdb_dn == NULL) {
622 talloc_free(tmp_ctx);
623 return LDB_ERR_INVALID_DN_SYNTAX;
626 ret = dsdb_module_search_dn(module, tmp_ctx, &res, dsdb_dn->dn,
628 DSDB_FLAG_NEXT_MODULE |
629 DSDB_SEARCH_SHOW_RECYCLED |
630 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
631 DSDB_SEARCH_REVEAL_INTERNALS, parent);
632 if (ret != LDB_SUCCESS) {
633 ldb_asprintf_errstring(ldb, "Linked attribute %s->%s between %s and %s - remote not found - %s",
634 el->name, target->lDAPDisplayName,
635 ldb_dn_get_linearized(old_dn),
636 ldb_dn_get_linearized(dsdb_dn->dn),
638 talloc_free(tmp_ctx);
643 if (msg->num_elements == 0) {
644 /* Forward link without backlink remaining - nothing to do here */
646 } else if (msg->num_elements != 1) {
647 ldb_asprintf_errstring(ldb, "Bad msg elements - got %u elements, expected one element to be returned in linked_attributes_fix_links for %s",
648 msg->num_elements, ldb_dn_get_linearized(msg->dn));
649 talloc_free(tmp_ctx);
650 return LDB_ERR_OPERATIONS_ERROR;
652 if (ldb_attr_cmp(msg->elements[0].name, target->lDAPDisplayName) != 0) {
653 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));
654 talloc_free(tmp_ctx);
655 return LDB_ERR_OPERATIONS_ERROR;
657 el2 = &msg->elements[0];
659 el2->flags = LDB_FLAG_MOD_REPLACE;
661 /* find our DN in the values */
662 for (j=0; j<el2->num_values; j++) {
663 struct dsdb_dn *dsdb_dn2;
664 dsdb_dn2 = dsdb_dn_parse(msg, ldb, &el2->values[j], target->syntax->ldap_oid);
665 if (dsdb_dn2 == NULL) {
666 talloc_free(tmp_ctx);
667 return LDB_ERR_INVALID_DN_SYNTAX;
669 if (ldb_dn_compare(old_dn, dsdb_dn2->dn) != 0) {
672 ret = ldb_dn_update_components(dsdb_dn2->dn, new_dn);
673 if (ret != LDB_SUCCESS) {
674 talloc_free(tmp_ctx);
678 el2->values[j] = data_blob_string_const(
679 dsdb_dn_get_extended_linearized(el2->values, dsdb_dn2, 1));
682 ret = dsdb_check_single_valued_link(target, el2);
683 if (ret != LDB_SUCCESS) {
684 talloc_free(tmp_ctx);
688 /* we may be putting multiple values in an attribute -
689 disable checking for this attribute */
690 el2->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
692 ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE, parent);
693 if (ret != LDB_SUCCESS) {
694 ldb_asprintf_errstring(ldb, "Linked attribute %s->%s between %s and %s - update failed - %s",
695 el->name, target->lDAPDisplayName,
696 ldb_dn_get_linearized(old_dn),
697 ldb_dn_get_linearized(dsdb_dn->dn),
699 talloc_free(tmp_ctx);
704 talloc_free(tmp_ctx);
710 static int linked_attributes_rename(struct ldb_module *module, struct ldb_request *req)
712 struct ldb_result *res;
713 struct ldb_message *msg;
715 struct ldb_context *ldb = ldb_module_get_ctx(module);
716 struct dsdb_schema *schema;
719 - load the current msg
720 - find any linked attributes
721 - if its a link then find the target object
722 - modify the target linked attributes with the new DN
724 ret = dsdb_module_search_dn(module, req, &res, req->op.rename.olddn,
726 DSDB_FLAG_NEXT_MODULE |
727 DSDB_SEARCH_SHOW_RECYCLED, req);
728 if (ret != LDB_SUCCESS) {
732 schema = dsdb_get_schema(ldb, res);
739 for (i=0; i<msg->num_elements; i++) {
740 struct ldb_message_element *el = &msg->elements[i];
741 const struct dsdb_attribute *schema_attr
742 = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
743 if (!schema_attr || schema_attr->linkID == 0) {
746 ret = linked_attributes_fix_links(module, msg->dn, req->op.rename.newdn, el,
747 schema, schema_attr, req);
748 if (ret != LDB_SUCCESS) {
756 return ldb_next_request(module, req);
760 /* queue a linked attributes modify request in the la_private
762 static int la_queue_mod_request(struct la_context *ac)
764 struct la_private *la_private =
765 talloc_get_type(ldb_module_get_private(ac->module), struct la_private);
767 if (la_private == NULL) {
768 ldb_debug(ldb_module_get_ctx(ac->module), LDB_DEBUG_ERROR, __location__ ": No la_private transaction setup\n");
769 return ldb_operr(ldb_module_get_ctx(ac->module));
772 talloc_steal(la_private, ac);
773 DLIST_ADD(la_private->la_list, ac);
775 return ldb_module_done(ac->req, ac->op_controls,
776 ac->op_response, LDB_SUCCESS);
779 /* Having done the original operation, then try to fix up all the linked attributes for modify and delete */
780 static int la_mod_del_callback(struct ldb_request *req, struct ldb_reply *ares)
782 struct la_context *ac;
783 struct ldb_context *ldb;
786 ac = talloc_get_type(req->context, struct la_context);
787 ldb = ldb_module_get_ctx(ac->module);
790 return ldb_module_done(ac->req, NULL, NULL,
791 LDB_ERR_OPERATIONS_ERROR);
793 if (ares->error != LDB_SUCCESS) {
794 return ldb_module_done(ac->req, ares->controls,
795 ares->response, ares->error);
798 if (ares->type != LDB_REPLY_DONE) {
799 ldb_set_errstring(ldb,
800 "invalid ldb_reply_type in callback");
802 return ldb_module_done(ac->req, NULL, NULL,
803 LDB_ERR_OPERATIONS_ERROR);
806 ac->op_controls = talloc_steal(ac, ares->controls);
807 ac->op_response = talloc_steal(ac, ares->response);
809 /* If we have modfies to make, this is the time to do them for modify and delete */
810 ret = la_queue_mod_request(ac);
812 if (ret != LDB_SUCCESS) {
813 return ldb_module_done(ac->req, NULL, NULL, ret);
817 /* la_queue_mod_request has already sent the callbacks */
822 /* Having done the original add, then try to fix up all the linked attributes
824 This is done after the add so the links can get the extended DNs correctly.
826 static int la_add_callback(struct ldb_request *req, struct ldb_reply *ares)
828 struct la_context *ac;
829 struct ldb_context *ldb;
832 ac = talloc_get_type(req->context, struct la_context);
833 ldb = ldb_module_get_ctx(ac->module);
836 return ldb_module_done(ac->req, NULL, NULL,
837 LDB_ERR_OPERATIONS_ERROR);
839 if (ares->error != LDB_SUCCESS) {
840 return ldb_module_done(ac->req, ares->controls,
841 ares->response, ares->error);
844 if (ares->type != LDB_REPLY_DONE) {
845 ldb_set_errstring(ldb,
846 "invalid ldb_reply_type in callback");
848 return ldb_module_done(ac->req, NULL, NULL,
849 LDB_ERR_OPERATIONS_ERROR);
853 struct ldb_request *search_req;
854 static const char *attrs[] = { NULL };
856 /* The callback does all the hard work here - we need
857 * the objectGUID and SID of the added record */
858 ret = ldb_build_search_req(&search_req, ldb, ac,
859 ac->req->op.add.message->dn,
861 "(objectClass=*)", attrs,
863 ac, la_mod_search_callback,
865 LDB_REQ_SET_LOCATION(search_req);
867 if (ret == LDB_SUCCESS) {
868 ret = dsdb_request_add_controls(search_req,
869 DSDB_SEARCH_SHOW_DELETED |
870 DSDB_SEARCH_SHOW_EXTENDED_DN);
872 if (ret != LDB_SUCCESS) {
873 return ldb_module_done(ac->req, NULL, NULL,
877 ac->op_controls = talloc_steal(ac, ares->controls);
878 ac->op_response = talloc_steal(ac, ares->response);
880 return ldb_next_request(ac->module, search_req);
883 return ldb_module_done(ac->req, ares->controls,
884 ares->response, ares->error);
888 /* Reconstruct the original request, but pointing at our local callback to finish things off */
889 static int la_down_req(struct la_context *ac)
891 struct ldb_request *down_req;
892 struct ldb_context *ldb;
895 ldb = ldb_module_get_ctx(ac->module);
897 switch (ac->req->operation) {
899 ret = ldb_build_add_req(&down_req, ldb, ac,
900 ac->req->op.add.message,
904 LDB_REQ_SET_LOCATION(down_req);
907 ret = ldb_build_mod_req(&down_req, ldb, ac,
908 ac->req->op.mod.message,
910 ac, la_mod_del_callback,
912 LDB_REQ_SET_LOCATION(down_req);
915 ret = LDB_ERR_OPERATIONS_ERROR;
917 if (ret != LDB_SUCCESS) {
921 return ldb_next_request(ac->module, down_req);
925 use the GUID part of an extended DN to find the target DN, in case
928 static int la_find_dn_target(struct ldb_module *module, struct la_context *ac,
929 struct GUID *guid, struct ldb_dn **dn)
931 return dsdb_find_dn_by_guid(ldb_module_get_ctx(ac->module), ac, guid, dn);
934 /* apply one la_context op change */
935 static int la_do_op_request(struct ldb_module *module, struct la_context *ac, struct la_op_store *op)
937 struct ldb_message_element *ret_el;
938 struct ldb_message *new_msg;
939 struct ldb_context *ldb;
942 if (ac->mod_dn == NULL) {
943 /* we didn't find the DN that we searched for */
947 ldb = ldb_module_get_ctx(ac->module);
949 /* Create the modify request */
950 new_msg = ldb_msg_new(ac);
955 ret = la_find_dn_target(module, ac, &op->guid, &new_msg->dn);
956 if (ret != LDB_SUCCESS) {
960 if (op->op == LA_OP_ADD) {
961 ret = ldb_msg_add_empty(new_msg, op->name,
962 LDB_FLAG_MOD_ADD, &ret_el);
964 ret = ldb_msg_add_empty(new_msg, op->name,
965 LDB_FLAG_MOD_DELETE, &ret_el);
967 if (ret != LDB_SUCCESS) {
970 ret_el->values = talloc_array(new_msg, struct ldb_val, 1);
971 if (!ret_el->values) {
974 ret_el->num_values = 1;
975 ret_el->values[0] = data_blob_string_const(ldb_dn_get_extended_linearized(new_msg, ac->mod_dn, 1));
977 /* a backlink should never be single valued. Unfortunately the
978 exchange schema has a attribute
979 msExchBridgeheadedLocalConnectorsDNBL which is single
980 valued and a backlink. We need to cope with that by
981 ignoring the single value flag */
982 ret_el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
985 ldb_debug(ldb, LDB_DEBUG_WARNING,
986 "link on %s %s: %s %s\n",
987 ldb_dn_get_linearized(new_msg->dn), ret_el->name,
988 ret_el->values[0].data, ac->ops->op == LA_OP_ADD ? "added" : "deleted");
992 DEBUG(4,("Applying linked attribute change:\n%s\n",
993 ldb_ldif_message_string(ldb, op, LDB_CHANGETYPE_MODIFY, new_msg)));
996 ret = dsdb_module_modify(module, new_msg, DSDB_FLAG_NEXT_MODULE, ac->req);
997 if (ret != LDB_SUCCESS) {
998 ldb_debug(ldb, LDB_DEBUG_WARNING, __location__ ": failed to apply linked attribute change '%s'\n%s\n",
1000 ldb_ldif_message_string(ldb, op, LDB_CHANGETYPE_MODIFY, new_msg));
1006 /* apply one set of la_context changes */
1007 static int la_do_mod_request(struct ldb_module *module, struct la_context *ac)
1009 struct la_op_store *op;
1011 for (op = ac->ops; op; op=op->next) {
1012 int ret = la_do_op_request(module, ac, op);
1013 if (ret != LDB_SUCCESS) {
1014 if (ret != LDB_ERR_NO_SUCH_OBJECT) {
1025 we hook into the transaction operations to allow us to
1026 perform the linked attribute updates at the end of the whole
1027 transaction. This allows a forward linked attribute to be created
1028 before the target is created, as long as the target is created
1029 in the same transaction
1031 static int linked_attributes_start_transaction(struct ldb_module *module)
1033 /* create our private structure for this transaction */
1034 struct la_private *la_private = talloc_get_type(ldb_module_get_private(module),
1036 talloc_free(la_private);
1037 la_private = talloc(module, struct la_private);
1038 if (la_private == NULL) {
1039 return ldb_oom(ldb_module_get_ctx(module));
1041 la_private->la_list = NULL;
1042 ldb_module_set_private(module, la_private);
1043 return ldb_next_start_trans(module);
1047 on prepare commit we loop over our queued la_context structures
1048 and apply each of them
1050 static int linked_attributes_prepare_commit(struct ldb_module *module)
1052 struct la_private *la_private =
1053 talloc_get_type(ldb_module_get_private(module), struct la_private);
1054 struct la_context *ac;
1057 /* prepare commit without begin_transaction - let someone else return the error, just don't segfault */
1058 return ldb_next_prepare_commit(module);
1060 /* walk the list backwards, to do the first entry first, as we
1061 * added the entries with DLIST_ADD() which puts them at the
1062 * start of the list */
1064 /* Start at the end of the list - so we can start
1065 * there, but ensure we don't create a loop by NULLing
1066 * it out in the first element */
1067 ac = DLIST_TAIL(la_private->la_list);
1069 for (; ac; ac=DLIST_PREV(ac)) {
1072 ret = la_do_mod_request(module, ac);
1073 if (ret != LDB_SUCCESS) {
1074 DEBUG(0,(__location__ ": Failed mod request ret=%d\n", ret));
1075 talloc_free(la_private);
1076 ldb_module_set_private(module, NULL);
1081 talloc_free(la_private);
1082 ldb_module_set_private(module, NULL);
1084 return ldb_next_prepare_commit(module);
1087 static int linked_attributes_del_transaction(struct ldb_module *module)
1089 struct la_private *la_private =
1090 talloc_get_type(ldb_module_get_private(module), struct la_private);
1091 talloc_free(la_private);
1092 ldb_module_set_private(module, NULL);
1093 return ldb_next_del_trans(module);
1097 static const struct ldb_module_ops ldb_linked_attributes_module_ops = {
1098 .name = "linked_attributes",
1099 .add = linked_attributes_add,
1100 .modify = linked_attributes_modify,
1101 .rename = linked_attributes_rename,
1102 .start_transaction = linked_attributes_start_transaction,
1103 .prepare_commit = linked_attributes_prepare_commit,
1104 .del_transaction = linked_attributes_del_transaction,
1107 int ldb_linked_attributes_module_init(const char *version)
1109 LDB_MODULE_CHECK_VERSION(version);
1110 return ldb_register_module(&ldb_linked_attributes_module_ops);