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 "attribute %s is not a valid attribute in schema", el->name);
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->add_dn = ac->del_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 "attribute %s is not a valid attribute in schema",
331 return ldb_module_done(ac->req, NULL, NULL,
332 LDB_ERR_OBJECT_CLASS_VIOLATION);
335 search_el = ldb_msg_find_element(ares->message,
338 /* See if this element already exists */
339 /* otherwise just ignore as
340 * the add has already been scheduled */
345 target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID + 1);
348 * windows 2003 has a broken schema where
349 * the definition of msDS-IsDomainFor
350 * is missing (which is supposed to be
351 * the backlink of the msDS-HasDomainNCs
356 attr_name = target_attr->lDAPDisplayName;
358 /* Now we know what was there, we can remove it for the re-add */
359 for (j = 0; j < search_el->num_values; j++) {
360 ret = la_store_op(ac, LA_OP_DEL,
361 &search_el->values[j],
363 if (ret != LDB_SUCCESS) {
365 return ldb_module_done(ac->req,
373 case LDB_REPLY_REFERRAL:
381 if (ac->req->operation == LDB_ADD) {
382 /* Start the modifies to the backlinks */
383 ret = la_queue_mod_request(ac);
385 if (ret != LDB_SUCCESS) {
386 return ldb_module_done(ac->req, NULL, NULL,
390 /* Start with the original request */
391 ret = la_down_req(ac);
392 if (ret != LDB_SUCCESS) {
393 return ldb_module_done(ac->req, NULL, NULL, ret);
405 static int linked_attributes_modify(struct ldb_module *module, struct ldb_request *req)
407 /* Look over list of modifications */
408 /* Find if any are for linked attributes */
409 /* Determine the effect of the modification */
410 /* Apply the modify to the linked entry */
412 struct ldb_context *ldb;
414 struct la_context *ac;
415 struct ldb_request *search_req;
417 struct ldb_control *ctrl;
420 ldb = ldb_module_get_ctx(module);
422 if (ldb_dn_is_special(req->op.mod.message->dn)) {
423 /* do not manipulate our control entries */
424 return ldb_next_request(module, req);
427 if (!(ctrl = ldb_request_get_control(req, DSDB_CONTROL_APPLY_LINKS))) {
428 /* don't do anything special for linked attributes, repl_meta_data has done it */
429 return ldb_next_request(module, req);
431 ctrl->critical = false;
433 ac = linked_attributes_init(module, req);
435 return ldb_operr(ldb);
439 /* without schema, this doesn't make any sense */
440 return ldb_next_request(module, req);
443 ac->rc = talloc_zero(ac, struct replace_context);
448 for (i=0; i < req->op.mod.message->num_elements; i++) {
449 bool store_el = false;
450 const char *attr_name;
451 const struct dsdb_attribute *target_attr;
452 const struct ldb_message_element *el = &req->op.mod.message->elements[i];
453 const struct dsdb_attribute *schema_attr
454 = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
456 ldb_asprintf_errstring(ldb,
457 "attribute %s is not a valid attribute in schema", el->name);
458 return LDB_ERR_OBJECT_CLASS_VIOLATION;
460 /* We have a valid attribute, now find out if it is a forward link
461 (Even link IDs are for the originating attribute) */
462 if (schema_attr->linkID == 0) {
466 if ((schema_attr->linkID & 1) == 1) {
467 unsigned int functional_level;
469 functional_level = dsdb_functional_level(ldb);
470 SMB_ASSERT(functional_level > DS_DOMAIN_FUNCTION_2000);
472 /* Now find the target attribute */
473 target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID + 1);
476 * windows 2003 has a broken schema where
477 * the definition of msDS-IsDomainFor
478 * is missing (which is supposed to be
479 * the backlink of the msDS-HasDomainNCs
485 attr_name = target_attr->lDAPDisplayName;
487 switch (el->flags & LDB_FLAG_MOD_MASK) {
488 case LDB_FLAG_MOD_REPLACE:
489 /* treat as just a normal add the delete part is handled by the callback */
492 /* break intentionally missing */
494 case LDB_FLAG_MOD_ADD:
496 /* For each value being added, we need to setup the adds */
497 for (j = 0; j < el->num_values; j++) {
498 ret = la_store_op(ac, LA_OP_ADD,
501 if (ret != LDB_SUCCESS) {
507 case LDB_FLAG_MOD_DELETE:
509 if (el->num_values) {
510 /* For each value being deleted, we need to setup the delete */
511 for (j = 0; j < el->num_values; j++) {
512 ret = la_store_op(ac, LA_OP_DEL,
515 if (ret != LDB_SUCCESS) {
520 /* Flag that there was a DELETE
521 * without a value specified, so we
522 * need to look for the old value */
530 struct ldb_message_element *search_el;
532 search_el = talloc_realloc(ac->rc, ac->rc->el,
533 struct ldb_message_element,
534 ac->rc->num_elements +1);
538 ac->rc->el = search_el;
540 ac->rc->el[ac->rc->num_elements] = *el;
541 ac->rc->num_elements++;
545 if (ac->ops || ac->rc->el) {
546 /* both replace and delete without values are handled in the callback
547 * after the search on the entry to be modified is performed */
549 attrs = talloc_array(ac->rc, const char *, ac->rc->num_elements + 1);
553 for (i = 0; ac->rc && i < ac->rc->num_elements; i++) {
554 attrs[i] = ac->rc->el[i].name;
558 /* The callback does all the hard work here */
559 ret = ldb_build_search_req(&search_req, ldb, ac,
560 req->op.mod.message->dn,
562 "(objectClass=*)", attrs,
564 ac, la_mod_search_callback,
566 LDB_REQ_SET_LOCATION(search_req);
568 /* We need to figure out our own extended DN, to fill in as the backlink target */
569 if (ret == LDB_SUCCESS) {
570 ret = ldb_request_add_control(search_req,
571 LDB_CONTROL_EXTENDED_DN_OID,
574 if (ret == LDB_SUCCESS) {
575 talloc_steal(search_req, attrs);
577 ret = ldb_next_request(module, search_req);
581 /* nothing to do for this module, proceed */
583 ret = ldb_next_request(module, req);
589 static int linked_attributes_fix_links(struct ldb_module *module,
590 struct ldb_dn *old_dn, struct ldb_dn *new_dn,
591 struct ldb_message_element *el, struct dsdb_schema *schema,
592 const struct dsdb_attribute *schema_attr)
595 TALLOC_CTX *tmp_ctx = talloc_new(module);
596 struct ldb_context *ldb = ldb_module_get_ctx(module);
597 const struct dsdb_attribute *target;
598 const char *attrs[2];
601 target = dsdb_attribute_by_linkID(schema, schema_attr->linkID ^ 1);
602 if (target == NULL) {
603 /* there is no counterpart link to change */
607 attrs[0] = target->lDAPDisplayName;
610 for (i=0; i<el->num_values; i++) {
611 struct dsdb_dn *dsdb_dn;
612 struct ldb_result *res;
613 struct ldb_message *msg;
614 struct ldb_message_element *el2;
616 dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i], schema_attr->syntax->ldap_oid);
617 if (dsdb_dn == NULL) {
618 talloc_free(tmp_ctx);
619 return LDB_ERR_INVALID_DN_SYNTAX;
622 ret = dsdb_module_search_dn(module, tmp_ctx, &res, dsdb_dn->dn,
624 DSDB_FLAG_NEXT_MODULE |
625 DSDB_SEARCH_SHOW_RECYCLED |
626 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
627 DSDB_SEARCH_REVEAL_INTERNALS);
628 if (ret != LDB_SUCCESS) {
629 ldb_asprintf_errstring(ldb, "Linked attribute %s->%s between %s and %s - remote not found - %s",
630 el->name, target->lDAPDisplayName,
631 ldb_dn_get_linearized(old_dn),
632 ldb_dn_get_linearized(dsdb_dn->dn),
634 talloc_free(tmp_ctx);
639 if (msg->num_elements == 0) {
640 /* Forward link without backlink remaining - nothing to do here */
642 } else if (msg->num_elements != 1) {
643 ldb_asprintf_errstring(ldb, "Bad msg elements - got %u elements, expected one element to be returned in linked_attributes_fix_links for %s",
644 msg->num_elements, ldb_dn_get_linearized(msg->dn));
645 talloc_free(tmp_ctx);
646 return LDB_ERR_OPERATIONS_ERROR;
648 if (ldb_attr_cmp(msg->elements[0].name, target->lDAPDisplayName) != 0) {
649 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));
650 talloc_free(tmp_ctx);
651 return LDB_ERR_OPERATIONS_ERROR;
653 el2 = &msg->elements[0];
655 el2->flags = LDB_FLAG_MOD_REPLACE;
657 /* find our DN in the values */
658 for (j=0; j<el2->num_values; j++) {
659 struct dsdb_dn *dsdb_dn2;
660 dsdb_dn2 = dsdb_dn_parse(msg, ldb, &el2->values[j], target->syntax->ldap_oid);
661 if (dsdb_dn2 == NULL) {
662 talloc_free(tmp_ctx);
663 return LDB_ERR_INVALID_DN_SYNTAX;
665 if (ldb_dn_compare(old_dn, dsdb_dn2->dn) != 0) {
668 ret = ldb_dn_update_components(dsdb_dn2->dn, new_dn);
669 if (ret != LDB_SUCCESS) {
670 talloc_free(tmp_ctx);
674 el2->values[j] = data_blob_string_const(
675 dsdb_dn_get_extended_linearized(el2->values, dsdb_dn2, 1));
678 ret = dsdb_check_single_valued_link(target, el2);
679 if (ret != LDB_SUCCESS) {
680 talloc_free(tmp_ctx);
684 ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE | DSDB_MODIFY_RELAX);
685 if (ret != LDB_SUCCESS) {
686 ldb_asprintf_errstring(ldb, "Linked attribute %s->%s between %s and %s - update failed - %s",
687 el->name, target->lDAPDisplayName,
688 ldb_dn_get_linearized(old_dn),
689 ldb_dn_get_linearized(dsdb_dn->dn),
691 talloc_free(tmp_ctx);
696 talloc_free(tmp_ctx);
702 static int linked_attributes_rename(struct ldb_module *module, struct ldb_request *req)
704 struct ldb_result *res;
705 struct ldb_message *msg;
707 struct ldb_context *ldb = ldb_module_get_ctx(module);
708 struct dsdb_schema *schema;
711 - load the current msg
712 - find any linked attributes
713 - if its a link then find the target object
714 - modify the target linked attributes with the new DN
716 ret = dsdb_module_search_dn(module, req, &res, req->op.rename.olddn,
718 DSDB_FLAG_NEXT_MODULE |
719 DSDB_SEARCH_SHOW_RECYCLED);
720 if (ret != LDB_SUCCESS) {
724 schema = dsdb_get_schema(ldb, res);
731 for (i=0; i<msg->num_elements; i++) {
732 struct ldb_message_element *el = &msg->elements[i];
733 const struct dsdb_attribute *schema_attr
734 = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
735 if (!schema_attr || schema_attr->linkID == 0) {
738 ret = linked_attributes_fix_links(module, msg->dn, req->op.rename.newdn, el,
739 schema, schema_attr);
740 if (ret != LDB_SUCCESS) {
748 return ldb_next_request(module, req);
752 /* queue a linked attributes modify request in the la_private
754 static int la_queue_mod_request(struct la_context *ac)
756 struct la_private *la_private =
757 talloc_get_type(ldb_module_get_private(ac->module), struct la_private);
759 if (la_private == NULL) {
760 ldb_debug(ldb_module_get_ctx(ac->module), LDB_DEBUG_ERROR, __location__ ": No la_private transaction setup\n");
761 return ldb_operr(ldb_module_get_ctx(ac->module));
764 talloc_steal(la_private, ac);
765 DLIST_ADD(la_private->la_list, ac);
767 return ldb_module_done(ac->req, ac->op_controls,
768 ac->op_response, LDB_SUCCESS);
771 /* Having done the original operation, then try to fix up all the linked attributes for modify and delete */
772 static int la_mod_del_callback(struct ldb_request *req, struct ldb_reply *ares)
774 struct la_context *ac;
775 struct ldb_context *ldb;
778 ac = talloc_get_type(req->context, struct la_context);
779 ldb = ldb_module_get_ctx(ac->module);
782 return ldb_module_done(ac->req, NULL, NULL,
783 LDB_ERR_OPERATIONS_ERROR);
785 if (ares->error != LDB_SUCCESS) {
786 return ldb_module_done(ac->req, ares->controls,
787 ares->response, ares->error);
790 if (ares->type != LDB_REPLY_DONE) {
791 ldb_set_errstring(ldb,
792 "invalid ldb_reply_type in callback");
794 return ldb_module_done(ac->req, NULL, NULL,
795 LDB_ERR_OPERATIONS_ERROR);
798 ac->op_controls = talloc_steal(ac, ares->controls);
799 ac->op_response = talloc_steal(ac, ares->response);
801 /* If we have modfies to make, this is the time to do them for modify and delete */
802 ret = la_queue_mod_request(ac);
804 if (ret != LDB_SUCCESS) {
805 return ldb_module_done(ac->req, NULL, NULL, ret);
809 /* la_queue_mod_request has already sent the callbacks */
814 /* Having done the original add, then try to fix up all the linked attributes
816 This is done after the add so the links can get the extended DNs correctly.
818 static int la_add_callback(struct ldb_request *req, struct ldb_reply *ares)
820 struct la_context *ac;
821 struct ldb_context *ldb;
824 ac = talloc_get_type(req->context, struct la_context);
825 ldb = ldb_module_get_ctx(ac->module);
828 return ldb_module_done(ac->req, NULL, NULL,
829 LDB_ERR_OPERATIONS_ERROR);
831 if (ares->error != LDB_SUCCESS) {
832 return ldb_module_done(ac->req, ares->controls,
833 ares->response, ares->error);
836 if (ares->type != LDB_REPLY_DONE) {
837 ldb_set_errstring(ldb,
838 "invalid ldb_reply_type in callback");
840 return ldb_module_done(ac->req, NULL, NULL,
841 LDB_ERR_OPERATIONS_ERROR);
845 struct ldb_request *search_req;
846 static const char *attrs[] = { NULL };
848 /* The callback does all the hard work here - we need
849 * the objectGUID and SID of the added record */
850 ret = ldb_build_search_req(&search_req, ldb, ac,
851 ac->req->op.add.message->dn,
853 "(objectClass=*)", attrs,
855 ac, la_mod_search_callback,
857 LDB_REQ_SET_LOCATION(search_req);
859 if (ret == LDB_SUCCESS) {
860 ret = ldb_request_add_control(search_req,
861 LDB_CONTROL_EXTENDED_DN_OID,
864 if (ret != LDB_SUCCESS) {
865 return ldb_module_done(ac->req, NULL, NULL,
869 ac->op_controls = talloc_steal(ac, ares->controls);
870 ac->op_response = talloc_steal(ac, ares->response);
872 return ldb_next_request(ac->module, search_req);
875 return ldb_module_done(ac->req, ares->controls,
876 ares->response, ares->error);
880 /* Reconstruct the original request, but pointing at our local callback to finish things off */
881 static int la_down_req(struct la_context *ac)
883 struct ldb_request *down_req;
884 struct ldb_context *ldb;
887 ldb = ldb_module_get_ctx(ac->module);
889 switch (ac->req->operation) {
891 ret = ldb_build_add_req(&down_req, ldb, ac,
892 ac->req->op.add.message,
896 LDB_REQ_SET_LOCATION(down_req);
899 ret = ldb_build_mod_req(&down_req, ldb, ac,
900 ac->req->op.mod.message,
902 ac, la_mod_del_callback,
904 LDB_REQ_SET_LOCATION(down_req);
907 ret = LDB_ERR_OPERATIONS_ERROR;
909 if (ret != LDB_SUCCESS) {
913 return ldb_next_request(ac->module, down_req);
917 use the GUID part of an extended DN to find the target DN, in case
920 static int la_find_dn_target(struct ldb_module *module, struct la_context *ac,
921 struct GUID *guid, struct ldb_dn **dn)
923 return dsdb_find_dn_by_guid(ldb_module_get_ctx(ac->module), ac, guid, dn);
926 /* apply one la_context op change */
927 static int la_do_op_request(struct ldb_module *module, struct la_context *ac, struct la_op_store *op)
929 struct ldb_message_element *ret_el;
930 struct ldb_message *new_msg;
931 struct ldb_context *ldb;
934 ldb = ldb_module_get_ctx(ac->module);
936 /* Create the modify request */
937 new_msg = ldb_msg_new(ac);
942 ret = la_find_dn_target(module, ac, &op->guid, &new_msg->dn);
943 if (ret != LDB_SUCCESS) {
947 if (op->op == LA_OP_ADD) {
948 ret = ldb_msg_add_empty(new_msg, op->name,
949 LDB_FLAG_MOD_ADD, &ret_el);
951 ret = ldb_msg_add_empty(new_msg, op->name,
952 LDB_FLAG_MOD_DELETE, &ret_el);
954 if (ret != LDB_SUCCESS) {
957 ret_el->values = talloc_array(new_msg, struct ldb_val, 1);
958 if (!ret_el->values) {
961 ret_el->num_values = 1;
962 if (op->op == LA_OP_ADD) {
963 ret_el->values[0] = data_blob_string_const(ldb_dn_get_extended_linearized(new_msg, ac->add_dn, 1));
965 ret_el->values[0] = data_blob_string_const(ldb_dn_get_extended_linearized(new_msg, ac->del_dn, 1));
969 ldb_debug(ldb, LDB_DEBUG_WARNING,
970 "link on %s %s: %s %s\n",
971 ldb_dn_get_linearized(new_msg->dn), ret_el->name,
972 ret_el->values[0].data, ac->ops->op == LA_OP_ADD ? "added" : "deleted");
976 DEBUG(4,("Applying linked attribute change:\n%s\n",
977 ldb_ldif_message_string(ldb, op, LDB_CHANGETYPE_MODIFY, new_msg)));
980 ret = dsdb_module_modify(module, new_msg, DSDB_FLAG_NEXT_MODULE);
981 if (ret != LDB_SUCCESS) {
982 ldb_debug(ldb, LDB_DEBUG_WARNING, "Failed to apply linked attribute change '%s'\n%s\n",
984 ldb_ldif_message_string(ldb, op, LDB_CHANGETYPE_MODIFY, new_msg));
990 /* apply one set of la_context changes */
991 static int la_do_mod_request(struct ldb_module *module, struct la_context *ac)
993 struct la_op_store *op;
995 for (op = ac->ops; op; op=op->next) {
996 int ret = la_do_op_request(module, ac, op);
997 if (ret != LDB_SUCCESS) {
998 if (ret != LDB_ERR_NO_SUCH_OBJECT) {
1009 we hook into the transaction operations to allow us to
1010 perform the linked attribute updates at the end of the whole
1011 transaction. This allows a forward linked attribute to be created
1012 before the target is created, as long as the target is created
1013 in the same transaction
1015 static int linked_attributes_start_transaction(struct ldb_module *module)
1017 /* create our private structure for this transaction */
1018 struct la_private *la_private = talloc_get_type(ldb_module_get_private(module),
1020 talloc_free(la_private);
1021 la_private = talloc(module, struct la_private);
1022 if (la_private == NULL) {
1023 return ldb_oom(ldb_module_get_ctx(module));
1025 la_private->la_list = NULL;
1026 ldb_module_set_private(module, la_private);
1027 return ldb_next_start_trans(module);
1031 on prepare commit we loop over our queued la_context structures
1032 and apply each of them
1034 static int linked_attributes_prepare_commit(struct ldb_module *module)
1036 struct la_private *la_private =
1037 talloc_get_type(ldb_module_get_private(module), struct la_private);
1038 struct la_context *ac;
1041 /* prepare commit without begin_transaction - let someone else return the error, just don't segfault */
1042 return ldb_next_prepare_commit(module);
1044 /* walk the list backwards, to do the first entry first, as we
1045 * added the entries with DLIST_ADD() which puts them at the
1046 * start of the list */
1048 /* Start at the end of the list - so we can start
1049 * there, but ensure we don't create a loop by NULLing
1050 * it out in the first element */
1051 ac = DLIST_TAIL(la_private->la_list);
1053 for (; ac; ac=DLIST_PREV(ac)) {
1056 ret = la_do_mod_request(module, ac);
1057 if (ret != LDB_SUCCESS) {
1058 DEBUG(0,(__location__ ": Failed mod request ret=%d\n", ret));
1059 talloc_free(la_private);
1060 ldb_module_set_private(module, NULL);
1065 talloc_free(la_private);
1066 ldb_module_set_private(module, NULL);
1068 return ldb_next_prepare_commit(module);
1071 static int linked_attributes_del_transaction(struct ldb_module *module)
1073 struct la_private *la_private =
1074 talloc_get_type(ldb_module_get_private(module), struct la_private);
1075 talloc_free(la_private);
1076 ldb_module_set_private(module, NULL);
1077 return ldb_next_del_trans(module);
1081 static const struct ldb_module_ops ldb_linked_attributes_module_ops = {
1082 .name = "linked_attributes",
1083 .add = linked_attributes_add,
1084 .modify = linked_attributes_modify,
1085 .rename = linked_attributes_rename,
1086 .start_transaction = linked_attributes_start_transaction,
1087 .prepare_commit = linked_attributes_prepare_commit,
1088 .del_transaction = linked_attributes_del_transaction,
1091 int ldb_linked_attributes_module_init(const char *version)
1093 return ldb_register_module(&ldb_linked_attributes_module_ops);