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
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
25 * Component: ldb linked_attributes module
27 * Description: Module to ensure linked attribute pairs remain in sync
29 * Author: Andrew Bartlett
33 #include "ldb_module.h"
34 #include "util/dlinklist.h"
35 #include "dsdb/samdb/samdb.h"
36 #include "librpc/gen_ndr/ndr_misc.h"
37 #include "dsdb/samdb/ldb_modules/util.h"
40 struct la_context *la_list;
44 struct la_op_store *next;
45 struct la_op_store *prev;
46 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 *mod_dn;
63 struct replace_context *rc;
64 struct la_op_store *ops;
65 struct ldb_extended *op_response;
66 struct ldb_control **op_controls;
69 static struct la_context *linked_attributes_init(struct ldb_module *module,
70 struct ldb_request *req)
72 struct ldb_context *ldb;
73 struct la_context *ac;
75 ldb = ldb_module_get_ctx(module);
77 ac = talloc_zero(req, struct la_context);
83 ac->schema = dsdb_get_schema(ldb, ac);
93 static int la_guid_from_dn(struct la_context *ac, struct ldb_dn *dn, struct GUID *guid)
98 status = dsdb_get_extended_dn_guid(dn, guid, "GUID");
99 if (NT_STATUS_IS_OK(status)) {
102 if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
103 DEBUG(4,(__location__ ": Unable to parse GUID for dn %s\n",
104 ldb_dn_get_linearized(dn)));
105 return ldb_operr(ldb_module_get_ctx(ac->module));
108 ret = dsdb_find_guid_by_dn(ldb_module_get_ctx(ac->module), dn, guid);
109 if (ret != LDB_SUCCESS) {
110 DEBUG(4,(__location__ ": Failed to find GUID for dn %s\n",
111 ldb_dn_get_linearized(dn)));
118 /* Common routine to handle reading the attributes and creating a
119 * series of modify requests */
120 static int la_store_op(struct la_context *ac,
121 enum la_op op, struct ldb_val *dn,
124 struct ldb_context *ldb;
125 struct la_op_store *os;
126 struct ldb_dn *op_dn;
129 ldb = ldb_module_get_ctx(ac->module);
131 op_dn = ldb_dn_from_ldb_val(ac, ldb, dn);
133 ldb_asprintf_errstring(ldb,
134 "could not parse attribute as a DN");
135 return LDB_ERR_INVALID_DN_SYNTAX;
138 os = talloc_zero(ac, struct la_op_store);
145 ret = la_guid_from_dn(ac, op_dn, &os->guid);
147 if (ret == LDB_ERR_NO_SUCH_OBJECT && ac->req->operation == LDB_DELETE) {
148 /* we are deleting an object, and we've found it has a
149 * forward link to a target that no longer
150 * exists. This is not an error in the delete, and we
151 * should just not do the deferred delete of the
157 if (ret != LDB_SUCCESS) {
161 os->name = talloc_strdup(os, name);
166 /* Do deletes before adds */
167 if (op == LA_OP_ADD) {
168 DLIST_ADD_END(ac->ops, os, struct la_op_store *);
170 /* By adding to the head of the list, we do deletes before
171 * adds when processing a replace */
172 DLIST_ADD(ac->ops, os);
178 static int la_queue_mod_request(struct la_context *ac);
179 static int la_down_req(struct la_context *ac);
184 static int linked_attributes_add(struct ldb_module *module, struct ldb_request *req)
186 struct ldb_context *ldb;
187 const struct dsdb_attribute *target_attr;
188 struct la_context *ac;
189 const char *attr_name;
190 struct ldb_control *ctrl;
194 ldb = ldb_module_get_ctx(module);
196 if (ldb_dn_is_special(req->op.add.message->dn)) {
197 /* do not manipulate our control entries */
198 return ldb_next_request(module, req);
201 if (!(ctrl = ldb_request_get_control(req, DSDB_CONTROL_APPLY_LINKS))) {
202 /* don't do anything special for linked attributes, repl_meta_data has done it */
203 return ldb_next_request(module, req);
205 ctrl->critical = false;
207 ac = linked_attributes_init(module, req);
209 return ldb_operr(ldb);
213 /* without schema, this doesn't make any sense */
215 return ldb_next_request(module, req);
218 /* Need to ensure we only have forward links being specified */
219 for (i=0; i < req->op.add.message->num_elements; i++) {
220 const struct ldb_message_element *el = &req->op.add.message->elements[i];
221 const struct dsdb_attribute *schema_attr
222 = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
224 ldb_asprintf_errstring(ldb,
225 "%s: attribute %s is not a valid attribute in schema",
228 return LDB_ERR_OBJECT_CLASS_VIOLATION;
230 /* We have a valid attribute, now find out if it is a forward link */
231 if ((schema_attr->linkID == 0)) {
235 if ((schema_attr->linkID & 1) == 1) {
236 unsigned int functional_level;
238 functional_level = dsdb_functional_level(ldb);
239 SMB_ASSERT(functional_level > DS_DOMAIN_FUNCTION_2000);
242 /* Even link IDs are for the originating attribute */
243 target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID + 1);
246 * windows 2003 has a broken schema where
247 * the definition of msDS-IsDomainFor
248 * is missing (which is supposed to be
249 * the backlink of the msDS-HasDomainNCs
255 attr_name = target_attr->lDAPDisplayName;
257 for (j = 0; j < el->num_values; j++) {
258 ret = la_store_op(ac, LA_OP_ADD,
261 if (ret != LDB_SUCCESS) {
267 /* if no linked attributes are present continue */
268 if (ac->ops == NULL) {
269 /* nothing to do for this module, proceed */
271 return ldb_next_request(module, req);
274 /* start with the original request */
275 return la_down_req(ac);
278 /* For a delete or rename, we need to find out what linked attributes
279 * are currently on this DN, and then deal with them. This is the
280 * callback to the base search */
282 static int la_mod_search_callback(struct ldb_request *req, struct ldb_reply *ares)
284 struct ldb_context *ldb;
285 const struct dsdb_attribute *schema_attr;
286 const struct dsdb_attribute *target_attr;
287 struct ldb_message_element *search_el;
288 struct replace_context *rc;
289 struct la_context *ac;
290 const char *attr_name;
292 int ret = LDB_SUCCESS;
294 ac = talloc_get_type(req->context, struct la_context);
295 ldb = ldb_module_get_ctx(ac->module);
299 return ldb_module_done(ac->req, NULL, NULL,
300 LDB_ERR_OPERATIONS_ERROR);
302 if (ares->error != LDB_SUCCESS) {
303 return ldb_module_done(ac->req, ares->controls,
304 ares->response, ares->error);
307 /* Only entries are interesting, and we only want the olddn */
308 switch (ares->type) {
309 case LDB_REPLY_ENTRY:
311 if (ldb_dn_compare(ares->message->dn, ac->req->op.mod.message->dn) != 0) {
312 ldb_asprintf_errstring(ldb,
313 "linked_attributes: %s is not the DN we were looking for",
314 ldb_dn_get_linearized(ares->message->dn));
315 /* Guh? We only asked for this DN */
317 return ldb_module_done(ac->req, NULL, NULL,
318 LDB_ERR_OPERATIONS_ERROR);
321 ac->mod_dn = talloc_steal(ac, ares->message->dn);
323 /* We don't populate 'rc' for ADD - it can't be deleting elements anyway */
324 for (i = 0; rc && i < rc->num_elements; i++) {
326 schema_attr = dsdb_attribute_by_lDAPDisplayName(ac->schema, rc->el[i].name);
328 ldb_asprintf_errstring(ldb,
329 "%s: attribute %s is not a valid attribute in schema",
333 return ldb_module_done(ac->req, NULL, NULL,
334 LDB_ERR_OBJECT_CLASS_VIOLATION);
337 search_el = ldb_msg_find_element(ares->message,
340 /* See if this element already exists */
341 /* otherwise just ignore as
342 * the add has already been scheduled */
347 target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID + 1);
350 * windows 2003 has a broken schema where
351 * the definition of msDS-IsDomainFor
352 * is missing (which is supposed to be
353 * the backlink of the msDS-HasDomainNCs
358 attr_name = target_attr->lDAPDisplayName;
360 /* Now we know what was there, we can remove it for the re-add */
361 for (j = 0; j < search_el->num_values; j++) {
362 ret = la_store_op(ac, LA_OP_DEL,
363 &search_el->values[j],
365 if (ret != LDB_SUCCESS) {
367 return ldb_module_done(ac->req,
375 case LDB_REPLY_REFERRAL:
383 if (ac->req->operation == LDB_ADD) {
384 /* Start the modifies to the backlinks */
385 ret = la_queue_mod_request(ac);
387 if (ret != LDB_SUCCESS) {
388 return ldb_module_done(ac->req, NULL, NULL,
392 /* Start with the original request */
393 ret = la_down_req(ac);
394 if (ret != LDB_SUCCESS) {
395 return ldb_module_done(ac->req, NULL, NULL, ret);
407 static int linked_attributes_modify(struct ldb_module *module, struct ldb_request *req)
409 /* Look over list of modifications */
410 /* Find if any are for linked attributes */
411 /* Determine the effect of the modification */
412 /* Apply the modify to the linked entry */
414 struct ldb_context *ldb;
416 struct la_context *ac;
417 struct ldb_request *search_req;
419 struct ldb_control *ctrl;
422 ldb = ldb_module_get_ctx(module);
424 if (ldb_dn_is_special(req->op.mod.message->dn)) {
425 /* do not manipulate our control entries */
426 return ldb_next_request(module, req);
429 if (!(ctrl = ldb_request_get_control(req, DSDB_CONTROL_APPLY_LINKS))) {
430 /* don't do anything special for linked attributes, repl_meta_data has done it */
431 return ldb_next_request(module, req);
433 ctrl->critical = false;
435 ac = linked_attributes_init(module, req);
437 return ldb_operr(ldb);
441 /* without schema, this doesn't make any sense */
442 return ldb_next_request(module, req);
445 ac->rc = talloc_zero(ac, struct replace_context);
450 for (i=0; i < req->op.mod.message->num_elements; i++) {
451 bool store_el = false;
452 const char *attr_name;
453 const struct dsdb_attribute *target_attr;
454 const struct ldb_message_element *el = &req->op.mod.message->elements[i];
455 const struct dsdb_attribute *schema_attr
456 = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
458 ldb_asprintf_errstring(ldb,
459 "%s: attribute %s is not a valid attribute in schema",
462 return LDB_ERR_OBJECT_CLASS_VIOLATION;
464 /* We have a valid attribute, now find out if it is a forward link
465 (Even link IDs are for the originating attribute) */
466 if (schema_attr->linkID == 0) {
470 if ((schema_attr->linkID & 1) == 1) {
471 unsigned int functional_level;
473 functional_level = dsdb_functional_level(ldb);
474 SMB_ASSERT(functional_level > DS_DOMAIN_FUNCTION_2000);
476 /* Now find the target attribute */
477 target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID + 1);
480 * windows 2003 has a broken schema where
481 * the definition of msDS-IsDomainFor
482 * is missing (which is supposed to be
483 * the backlink of the msDS-HasDomainNCs
489 attr_name = target_attr->lDAPDisplayName;
491 switch (el->flags & LDB_FLAG_MOD_MASK) {
492 case LDB_FLAG_MOD_REPLACE:
493 /* treat as just a normal add the delete part is handled by the callback */
496 /* break intentionally missing */
498 case LDB_FLAG_MOD_ADD:
500 /* For each value being added, we need to setup the adds */
501 for (j = 0; j < el->num_values; j++) {
502 ret = la_store_op(ac, LA_OP_ADD,
505 if (ret != LDB_SUCCESS) {
511 case LDB_FLAG_MOD_DELETE:
513 if (el->num_values) {
514 /* For each value being deleted, we need to setup the delete */
515 for (j = 0; j < el->num_values; j++) {
516 ret = la_store_op(ac, LA_OP_DEL,
519 if (ret != LDB_SUCCESS) {
524 /* Flag that there was a DELETE
525 * without a value specified, so we
526 * need to look for the old value */
534 struct ldb_message_element *search_el;
536 search_el = talloc_realloc(ac->rc, ac->rc->el,
537 struct ldb_message_element,
538 ac->rc->num_elements +1);
542 ac->rc->el = search_el;
544 ac->rc->el[ac->rc->num_elements] = *el;
545 ac->rc->num_elements++;
549 if (ac->ops || ac->rc->el) {
550 /* both replace and delete without values are handled in the callback
551 * after the search on the entry to be modified is performed */
553 attrs = talloc_array(ac->rc, const char *, ac->rc->num_elements + 1);
557 for (i = 0; ac->rc && i < ac->rc->num_elements; i++) {
558 attrs[i] = ac->rc->el[i].name;
562 /* The callback does all the hard work here */
563 ret = ldb_build_search_req(&search_req, ldb, ac,
564 req->op.mod.message->dn,
566 "(objectClass=*)", attrs,
568 ac, la_mod_search_callback,
570 LDB_REQ_SET_LOCATION(search_req);
572 /* We need to figure out our own extended DN, to fill in as the backlink target */
573 if (ret == LDB_SUCCESS) {
574 ret = dsdb_request_add_controls(search_req,
575 DSDB_SEARCH_SHOW_DELETED |
576 DSDB_SEARCH_SHOW_EXTENDED_DN);
578 if (ret == LDB_SUCCESS) {
579 talloc_steal(search_req, attrs);
581 ret = ldb_next_request(module, search_req);
585 /* nothing to do for this module, proceed */
587 ret = ldb_next_request(module, req);
593 static int linked_attributes_fix_links(struct ldb_module *module,
594 struct ldb_dn *old_dn, struct ldb_dn *new_dn,
595 struct ldb_message_element *el, struct dsdb_schema *schema,
596 const struct dsdb_attribute *schema_attr,
597 struct ldb_request *parent)
600 TALLOC_CTX *tmp_ctx = talloc_new(module);
601 struct ldb_context *ldb = ldb_module_get_ctx(module);
602 const struct dsdb_attribute *target;
603 const char *attrs[2];
606 target = dsdb_attribute_by_linkID(schema, schema_attr->linkID ^ 1);
607 if (target == NULL) {
608 /* there is no counterpart link to change */
612 attrs[0] = target->lDAPDisplayName;
615 for (i=0; i<el->num_values; i++) {
616 struct dsdb_dn *dsdb_dn;
617 struct ldb_result *res;
618 struct ldb_message *msg;
619 struct ldb_message_element *el2;
621 dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i], schema_attr->syntax->ldap_oid);
622 if (dsdb_dn == NULL) {
623 talloc_free(tmp_ctx);
624 return LDB_ERR_INVALID_DN_SYNTAX;
627 ret = dsdb_module_search_dn(module, tmp_ctx, &res, dsdb_dn->dn,
629 DSDB_FLAG_NEXT_MODULE |
630 DSDB_SEARCH_SHOW_RECYCLED |
631 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
632 DSDB_SEARCH_REVEAL_INTERNALS, parent);
633 if (ret != LDB_SUCCESS) {
634 ldb_asprintf_errstring(ldb, "Linked attribute %s->%s between %s and %s - remote not found - %s",
635 el->name, target->lDAPDisplayName,
636 ldb_dn_get_linearized(old_dn),
637 ldb_dn_get_linearized(dsdb_dn->dn),
639 talloc_free(tmp_ctx);
644 if (msg->num_elements == 0) {
645 /* Forward link without backlink remaining - nothing to do here */
647 } else if (msg->num_elements != 1) {
648 ldb_asprintf_errstring(ldb, "Bad msg elements - got %u elements, expected one element to be returned in linked_attributes_fix_links for %s",
649 msg->num_elements, ldb_dn_get_linearized(msg->dn));
650 talloc_free(tmp_ctx);
651 return LDB_ERR_OPERATIONS_ERROR;
653 if (ldb_attr_cmp(msg->elements[0].name, target->lDAPDisplayName) != 0) {
654 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));
655 talloc_free(tmp_ctx);
656 return LDB_ERR_OPERATIONS_ERROR;
658 el2 = &msg->elements[0];
660 el2->flags = LDB_FLAG_MOD_REPLACE;
662 /* find our DN in the values */
663 for (j=0; j<el2->num_values; j++) {
664 struct dsdb_dn *dsdb_dn2;
665 dsdb_dn2 = dsdb_dn_parse(msg, ldb, &el2->values[j], target->syntax->ldap_oid);
666 if (dsdb_dn2 == NULL) {
667 talloc_free(tmp_ctx);
668 return LDB_ERR_INVALID_DN_SYNTAX;
670 if (ldb_dn_compare(old_dn, dsdb_dn2->dn) != 0) {
673 ret = ldb_dn_update_components(dsdb_dn2->dn, new_dn);
674 if (ret != LDB_SUCCESS) {
675 talloc_free(tmp_ctx);
679 el2->values[j] = data_blob_string_const(
680 dsdb_dn_get_extended_linearized(el2->values, dsdb_dn2, 1));
683 ret = dsdb_check_single_valued_link(target, el2);
684 if (ret != LDB_SUCCESS) {
685 talloc_free(tmp_ctx);
689 /* we may be putting multiple values in an attribute -
690 disable checking for this attribute */
691 el2->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
693 ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE, parent);
694 if (ret != LDB_SUCCESS) {
695 ldb_asprintf_errstring(ldb, "Linked attribute %s->%s between %s and %s - update failed - %s",
696 el->name, target->lDAPDisplayName,
697 ldb_dn_get_linearized(old_dn),
698 ldb_dn_get_linearized(dsdb_dn->dn),
700 talloc_free(tmp_ctx);
705 talloc_free(tmp_ctx);
711 static int linked_attributes_rename(struct ldb_module *module, struct ldb_request *req)
713 struct ldb_result *res;
714 struct ldb_message *msg;
716 struct ldb_context *ldb = ldb_module_get_ctx(module);
717 struct dsdb_schema *schema;
720 - load the current msg
721 - find any linked attributes
722 - if its a link then find the target object
723 - modify the target linked attributes with the new DN
725 ret = dsdb_module_search_dn(module, req, &res, req->op.rename.olddn,
727 DSDB_FLAG_NEXT_MODULE |
728 DSDB_SEARCH_SHOW_RECYCLED, req);
729 if (ret != LDB_SUCCESS) {
733 schema = dsdb_get_schema(ldb, res);
740 for (i=0; i<msg->num_elements; i++) {
741 struct ldb_message_element *el = &msg->elements[i];
742 const struct dsdb_attribute *schema_attr
743 = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
744 if (!schema_attr || schema_attr->linkID == 0) {
747 ret = linked_attributes_fix_links(module, msg->dn, req->op.rename.newdn, el,
748 schema, schema_attr, req);
749 if (ret != LDB_SUCCESS) {
757 return ldb_next_request(module, req);
761 /* queue a linked attributes modify request in the la_private
763 static int la_queue_mod_request(struct la_context *ac)
765 struct la_private *la_private =
766 talloc_get_type(ldb_module_get_private(ac->module), struct la_private);
768 if (la_private == NULL) {
769 ldb_debug(ldb_module_get_ctx(ac->module), LDB_DEBUG_ERROR, __location__ ": No la_private transaction setup\n");
770 return ldb_operr(ldb_module_get_ctx(ac->module));
773 talloc_steal(la_private, ac);
774 DLIST_ADD(la_private->la_list, ac);
776 return ldb_module_done(ac->req, ac->op_controls,
777 ac->op_response, LDB_SUCCESS);
780 /* Having done the original operation, then try to fix up all the linked attributes for modify and delete */
781 static int la_mod_del_callback(struct ldb_request *req, struct ldb_reply *ares)
783 struct la_context *ac;
784 struct ldb_context *ldb;
787 ac = talloc_get_type(req->context, struct la_context);
788 ldb = ldb_module_get_ctx(ac->module);
791 return ldb_module_done(ac->req, NULL, NULL,
792 LDB_ERR_OPERATIONS_ERROR);
794 if (ares->error != LDB_SUCCESS) {
795 return ldb_module_done(ac->req, ares->controls,
796 ares->response, ares->error);
799 if (ares->type != LDB_REPLY_DONE) {
800 ldb_set_errstring(ldb,
801 "invalid ldb_reply_type in callback");
803 return ldb_module_done(ac->req, NULL, NULL,
804 LDB_ERR_OPERATIONS_ERROR);
807 ac->op_controls = talloc_steal(ac, ares->controls);
808 ac->op_response = talloc_steal(ac, ares->response);
810 /* If we have modfies to make, this is the time to do them for modify and delete */
811 ret = la_queue_mod_request(ac);
813 if (ret != LDB_SUCCESS) {
814 return ldb_module_done(ac->req, NULL, NULL, ret);
818 /* la_queue_mod_request has already sent the callbacks */
823 /* Having done the original add, then try to fix up all the linked attributes
825 This is done after the add so the links can get the extended DNs correctly.
827 static int la_add_callback(struct ldb_request *req, struct ldb_reply *ares)
829 struct la_context *ac;
830 struct ldb_context *ldb;
833 ac = talloc_get_type(req->context, struct la_context);
834 ldb = ldb_module_get_ctx(ac->module);
837 return ldb_module_done(ac->req, NULL, NULL,
838 LDB_ERR_OPERATIONS_ERROR);
840 if (ares->error != LDB_SUCCESS) {
841 return ldb_module_done(ac->req, ares->controls,
842 ares->response, ares->error);
845 if (ares->type != LDB_REPLY_DONE) {
846 ldb_set_errstring(ldb,
847 "invalid ldb_reply_type in callback");
849 return ldb_module_done(ac->req, NULL, NULL,
850 LDB_ERR_OPERATIONS_ERROR);
854 struct ldb_request *search_req;
855 static const char *attrs[] = { NULL };
857 /* The callback does all the hard work here - we need
858 * the objectGUID and SID of the added record */
859 ret = ldb_build_search_req(&search_req, ldb, ac,
860 ac->req->op.add.message->dn,
862 "(objectClass=*)", attrs,
864 ac, la_mod_search_callback,
866 LDB_REQ_SET_LOCATION(search_req);
868 if (ret == LDB_SUCCESS) {
869 ret = dsdb_request_add_controls(search_req,
870 DSDB_SEARCH_SHOW_DELETED |
871 DSDB_SEARCH_SHOW_EXTENDED_DN);
873 if (ret != LDB_SUCCESS) {
874 return ldb_module_done(ac->req, NULL, NULL,
878 ac->op_controls = talloc_steal(ac, ares->controls);
879 ac->op_response = talloc_steal(ac, ares->response);
881 return ldb_next_request(ac->module, search_req);
884 return ldb_module_done(ac->req, ares->controls,
885 ares->response, ares->error);
889 /* Reconstruct the original request, but pointing at our local callback to finish things off */
890 static int la_down_req(struct la_context *ac)
892 struct ldb_request *down_req;
893 struct ldb_context *ldb;
896 ldb = ldb_module_get_ctx(ac->module);
898 switch (ac->req->operation) {
900 ret = ldb_build_add_req(&down_req, ldb, ac,
901 ac->req->op.add.message,
905 LDB_REQ_SET_LOCATION(down_req);
908 ret = ldb_build_mod_req(&down_req, ldb, ac,
909 ac->req->op.mod.message,
911 ac, la_mod_del_callback,
913 LDB_REQ_SET_LOCATION(down_req);
916 ret = LDB_ERR_OPERATIONS_ERROR;
918 if (ret != LDB_SUCCESS) {
922 return ldb_next_request(ac->module, down_req);
926 use the GUID part of an extended DN to find the target DN, in case
929 static int la_find_dn_target(struct ldb_module *module, struct la_context *ac,
930 struct GUID *guid, struct ldb_dn **dn)
932 return dsdb_find_dn_by_guid(ldb_module_get_ctx(ac->module), ac, guid, dn);
935 /* apply one la_context op change */
936 static int la_do_op_request(struct ldb_module *module, struct la_context *ac, struct la_op_store *op)
938 struct ldb_message_element *ret_el;
939 struct ldb_message *new_msg;
940 struct ldb_context *ldb;
943 if (ac->mod_dn == NULL) {
944 /* we didn't find the DN that we searched for */
948 ldb = ldb_module_get_ctx(ac->module);
950 /* Create the modify request */
951 new_msg = ldb_msg_new(ac);
956 ret = la_find_dn_target(module, ac, &op->guid, &new_msg->dn);
957 if (ret != LDB_SUCCESS) {
961 if (op->op == LA_OP_ADD) {
962 ret = ldb_msg_add_empty(new_msg, op->name,
963 LDB_FLAG_MOD_ADD, &ret_el);
965 ret = ldb_msg_add_empty(new_msg, op->name,
966 LDB_FLAG_MOD_DELETE, &ret_el);
968 if (ret != LDB_SUCCESS) {
971 ret_el->values = talloc_array(new_msg, struct ldb_val, 1);
972 if (!ret_el->values) {
975 ret_el->num_values = 1;
976 ret_el->values[0] = data_blob_string_const(ldb_dn_get_extended_linearized(new_msg, ac->mod_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, __location__ ": 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);