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/include/ldb.h"
33 #include "ldb/include/ldb_errors.h"
34 #include "ldb/include/ldb_private.h"
35 #include "ldb/include/dlinklist.h"
36 #include "dsdb/samdb/samdb.h"
39 struct la_op_store *next;
40 struct la_op_store *prev;
41 enum la_op {LA_OP_ADD, LA_OP_DEL} op;
47 struct replace_context {
48 struct la_context *ac;
49 unsigned int num_elements;
50 struct ldb_message_element *el;
54 const struct dsdb_schema *schema;
55 struct ldb_module *module;
56 struct ldb_request *req;
57 struct ldb_dn *add_dn;
58 struct ldb_dn *del_dn;
59 struct replace_context *rc;
60 struct la_op_store *ops;
61 struct ldb_extended *op_response;
62 struct ldb_control **op_controls;
65 static struct la_context *linked_attributes_init(struct ldb_module *module,
66 struct ldb_request *req)
68 struct la_context *ac;
70 ac = talloc_zero(req, struct la_context);
76 ac->schema = dsdb_get_schema(module->ldb);
83 /* Common routine to handle reading the attributes and creating a
84 * series of modify requests */
85 static int la_store_op(struct la_context *ac,
86 enum la_op op, struct ldb_val *dn,
89 struct la_op_store *os;
92 op_dn = ldb_dn_from_ldb_val(ac, ac->module->ldb, dn);
94 ldb_asprintf_errstring(ac->module->ldb,
95 "could not parse attribute as a DN");
96 return LDB_ERR_INVALID_DN_SYNTAX;
99 os = talloc_zero(ac, struct la_op_store);
101 ldb_oom(ac->module->ldb);
102 return LDB_ERR_OPERATIONS_ERROR;
107 os->dn = talloc_steal(os, op_dn);
109 os->name = talloc_strdup(os, name);
111 ldb_oom(ac->module->ldb);
112 return LDB_ERR_OPERATIONS_ERROR;
115 /* Do deletes before adds */
116 if (op == LA_OP_ADD) {
117 DLIST_ADD_END(ac->ops, os, struct la_op_store *);
119 /* By adding to the head of the list, we do deletes before
120 * adds when processing a replace */
121 DLIST_ADD(ac->ops, os);
127 static int la_op_search_callback(struct ldb_request *req,
128 struct ldb_reply *ares);
129 static int la_do_mod_request(struct la_context *ac);
130 static int la_mod_callback(struct ldb_request *req,
131 struct ldb_reply *ares);
132 static int la_down_req(struct la_context *ac);
137 static int linked_attributes_add(struct ldb_module *module, struct ldb_request *req)
139 const struct dsdb_attribute *target_attr;
140 struct la_context *ac;
141 const char *attr_name;
145 if (ldb_dn_is_special(req->op.add.message->dn)) {
146 /* do not manipulate our control entries */
147 return ldb_next_request(module, req);
150 ac = linked_attributes_init(module, req);
152 return LDB_ERR_OPERATIONS_ERROR;
156 /* without schema, this doesn't make any sense */
158 return ldb_next_request(module, req);
161 /* Need to ensure we only have forward links being specified */
162 for (i=0; i < req->op.add.message->num_elements; i++) {
163 const struct ldb_message_element *el = &req->op.add.message->elements[i];
164 const struct dsdb_attribute *schema_attr
165 = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
167 ldb_asprintf_errstring(module->ldb,
168 "attribute %s is not a valid attribute in schema", el->name);
169 return LDB_ERR_OBJECT_CLASS_VIOLATION;
171 /* We have a valid attribute, now find out if it is linked */
172 if (schema_attr->linkID == 0) {
176 if ((schema_attr->linkID & 1) == 1) {
177 /* Odd is for the target. Illigal to modify */
178 ldb_asprintf_errstring(module->ldb,
179 "attribute %s must not be modified directly, it is a linked attribute", el->name);
180 return LDB_ERR_UNWILLING_TO_PERFORM;
183 /* Even link IDs are for the originating attribute */
184 target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID + 1);
187 * windows 2003 has a broken schema where
188 * the definition of msDS-IsDomainFor
189 * is missing (which is supposed to be
190 * the backlink of the msDS-HasDomainNCs
196 attr_name = target_attr->lDAPDisplayName;
198 for (j = 0; j < el->num_values; j++) {
199 ret = la_store_op(ac, LA_OP_ADD,
202 if (ret != LDB_SUCCESS) {
208 /* if no linked attributes are present continue */
209 if (ac->ops == NULL) {
210 /* nothing to do for this module, proceed */
212 return ldb_next_request(module, req);
215 /* start with the original request */
216 return la_down_req(ac);
219 /* For a delete or rename, we need to find out what linked attributes
220 * are currently on this DN, and then deal with them. This is the
221 * callback to the base search */
223 static int la_mod_search_callback(struct ldb_request *req, struct ldb_reply *ares)
225 const struct dsdb_attribute *schema_attr;
226 const struct dsdb_attribute *target_attr;
227 struct ldb_message_element *search_el;
228 struct replace_context *rc;
229 struct la_context *ac;
230 const char *attr_name;
232 int ret = LDB_SUCCESS;
234 ac = talloc_get_type(req->context, struct la_context);
238 return ldb_module_done(ac->req, NULL, NULL,
239 LDB_ERR_OPERATIONS_ERROR);
241 if (ares->error != LDB_SUCCESS) {
242 return ldb_module_done(ac->req, ares->controls,
243 ares->response, ares->error);
246 /* Only entries are interesting, and we only want the olddn */
247 switch (ares->type) {
248 case LDB_REPLY_ENTRY:
250 if (ldb_dn_compare(ares->message->dn, ac->req->op.mod.message->dn) != 0) {
251 ldb_asprintf_errstring(ac->module->ldb,
252 "linked_attributes: %s is not the DN we were looking for", ldb_dn_get_linearized(ares->message->dn));
253 /* Guh? We only asked for this DN */
255 return ldb_module_done(ac->req, NULL, NULL,
256 LDB_ERR_OPERATIONS_ERROR);
259 ac->add_dn = ac->del_dn = talloc_steal(ac, ares->message->dn);
261 /* We don't populate 'rc' for ADD - it can't be deleting elements anyway */
262 for (i = 0; rc && i < rc->num_elements; i++) {
264 schema_attr = dsdb_attribute_by_lDAPDisplayName(ac->schema, rc->el[i].name);
266 ldb_asprintf_errstring(ac->module->ldb,
267 "attribute %s is not a valid attribute in schema",
270 return ldb_module_done(ac->req, NULL, NULL,
271 LDB_ERR_OBJECT_CLASS_VIOLATION);
274 search_el = ldb_msg_find_element(ares->message,
277 /* See if this element already exists */
278 /* otherwise just ignore as
279 * the add has already been scheduled */
284 target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID + 1);
287 * windows 2003 has a broken schema where
288 * the definition of msDS-IsDomainFor
289 * is missing (which is supposed to be
290 * the backlink of the msDS-HasDomainNCs
295 attr_name = target_attr->lDAPDisplayName;
297 /* Now we know what was there, we can remove it for the re-add */
298 for (j = 0; j < search_el->num_values; j++) {
299 ret = la_store_op(ac, LA_OP_DEL,
300 &search_el->values[j],
302 if (ret != LDB_SUCCESS) {
304 return ldb_module_done(ac->req,
312 case LDB_REPLY_REFERRAL:
320 if (ac->req->operation == LDB_ADD) {
321 /* Start the modifies to the backlinks */
322 ret = la_do_mod_request(ac);
324 if (ret != LDB_SUCCESS) {
325 return ldb_module_done(ac->req, NULL, NULL,
329 /* Start with the original request */
330 ret = la_down_req(ac);
331 if (ret != LDB_SUCCESS) {
332 return ldb_module_done(ac->req, NULL, NULL, ret);
344 static int linked_attributes_modify(struct ldb_module *module, struct ldb_request *req)
346 /* Look over list of modifications */
347 /* Find if any are for linked attributes */
348 /* Determine the effect of the modification */
349 /* Apply the modify to the linked entry */
352 struct la_context *ac;
353 struct ldb_request *search_req;
358 if (ldb_dn_is_special(req->op.mod.message->dn)) {
359 /* do not manipulate our control entries */
360 return ldb_next_request(module, req);
363 ac = linked_attributes_init(module, req);
365 return LDB_ERR_OPERATIONS_ERROR;
369 /* without schema, this doesn't make any sense */
370 return ldb_next_request(module, req);
373 ac->rc = talloc_zero(ac, struct replace_context);
375 ldb_oom(module->ldb);
376 return LDB_ERR_OPERATIONS_ERROR;
379 for (i=0; i < req->op.mod.message->num_elements; i++) {
380 bool store_el = false;
381 const char *attr_name;
382 const struct dsdb_attribute *target_attr;
383 const struct ldb_message_element *el = &req->op.mod.message->elements[i];
384 const struct dsdb_attribute *schema_attr
385 = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
387 ldb_asprintf_errstring(module->ldb,
388 "attribute %s is not a valid attribute in schema", el->name);
389 return LDB_ERR_OBJECT_CLASS_VIOLATION;
391 /* We have a valid attribute, now find out if it is linked */
392 if (schema_attr->linkID == 0) {
396 if ((schema_attr->linkID & 1) == 1) {
397 /* Odd is for the target. Illegal to modify */
398 ldb_asprintf_errstring(module->ldb,
399 "attribute %s must not be modified directly, it is a linked attribute", el->name);
400 return LDB_ERR_UNWILLING_TO_PERFORM;
403 /* Even link IDs are for the originating attribute */
405 /* Now find the target attribute */
406 target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID + 1);
409 * windows 2003 has a broken schema where
410 * the definition of msDS-IsDomainFor
411 * is missing (which is supposed to be
412 * the backlink of the msDS-HasDomainNCs
418 attr_name = target_attr->lDAPDisplayName;
420 switch (el->flags & LDB_FLAG_MOD_MASK) {
421 case LDB_FLAG_MOD_REPLACE:
422 /* treat as just a normal add the delete part is handled by the callback */
425 /* break intentionally missing */
427 case LDB_FLAG_MOD_ADD:
429 /* For each value being added, we need to setup the adds */
430 for (j = 0; j < el->num_values; j++) {
431 ret = la_store_op(ac, LA_OP_ADD,
434 if (ret != LDB_SUCCESS) {
440 case LDB_FLAG_MOD_DELETE:
442 if (el->num_values) {
443 /* For each value being deleted, we need to setup the delete */
444 for (j = 0; j < el->num_values; j++) {
445 ret = la_store_op(ac, LA_OP_DEL,
448 if (ret != LDB_SUCCESS) {
453 /* Flag that there was a DELETE
454 * without a value specified, so we
455 * need to look for the old value */
463 struct ldb_message_element *search_el;
465 search_el = talloc_realloc(ac->rc, ac->rc->el,
466 struct ldb_message_element,
467 ac->rc->num_elements +1);
469 ldb_oom(module->ldb);
470 return LDB_ERR_OPERATIONS_ERROR;
472 ac->rc->el = search_el;
474 ac->rc->el[ac->rc->num_elements] = *el;
475 ac->rc->num_elements++;
479 if (ac->ops || ac->rc->el) {
480 /* both replace and delete without values are handled in the callback
481 * after the search on the entry to be modified is performed */
483 attrs = talloc_array(ac->rc, const char *, ac->rc->num_elements + 1);
485 ldb_oom(module->ldb);
486 return LDB_ERR_OPERATIONS_ERROR;
488 for (i = 0; ac->rc && i < ac->rc->num_elements; i++) {
489 attrs[i] = ac->rc->el[i].name;
493 /* The callback does all the hard work here */
494 ret = ldb_build_search_req(&search_req, module->ldb, ac,
495 req->op.mod.message->dn,
497 "(objectClass=*)", attrs,
499 ac, la_mod_search_callback,
502 /* We need to figure out our own extended DN, to fill in as the backlink target */
503 if (ret == LDB_SUCCESS) {
504 ret = ldb_request_add_control(search_req,
505 LDB_CONTROL_EXTENDED_DN_OID,
508 if (ret == LDB_SUCCESS) {
509 talloc_steal(search_req, attrs);
511 ret = ldb_next_request(module, search_req);
515 /* nothing to do for this module, proceed */
517 ret = ldb_next_request(module, req);
524 static int linked_attributes_del(struct ldb_module *module, struct ldb_request *req)
526 struct ldb_request *search_req;
527 struct la_context *ac;
532 /* This gets complex: We need to:
533 - Do a search for the entry
534 - Wait for these result to appear
535 - In the callback for the result, issue a modify
536 request based on the linked attributes found
537 - Wait for each modify result
541 ac = linked_attributes_init(module, req);
543 return LDB_ERR_OPERATIONS_ERROR;
547 /* without schema, this doesn't make any sense */
548 return ldb_next_request(module, req);
551 werr = dsdb_linked_attribute_lDAPDisplayName_list(ac->schema, ac, &attrs);
552 if (!W_ERROR_IS_OK(werr)) {
553 return LDB_ERR_OPERATIONS_ERROR;
556 ret = ldb_build_search_req(&search_req, module->ldb, req,
557 req->op.del.dn, LDB_SCOPE_BASE,
558 "(objectClass=*)", attrs,
560 ac, la_op_search_callback,
563 if (ret != LDB_SUCCESS) {
567 talloc_steal(search_req, attrs);
569 return ldb_next_request(module, search_req);
573 static int linked_attributes_rename(struct ldb_module *module, struct ldb_request *req)
575 struct la_context *ac;
577 /* This gets complex: We need to:
578 - Do a search for the entry
579 - Wait for these result to appear
580 - In the callback for the result, issue a modify
581 request based on the linked attributes found
582 - Wait for each modify result
586 ac = linked_attributes_init(module, req);
588 return LDB_ERR_OPERATIONS_ERROR;
592 /* without schema, this doesn't make any sense */
593 return ldb_next_request(module, req);
596 /* start with the original request */
597 return la_down_req(ac);
601 static int la_op_search_callback(struct ldb_request *req,
602 struct ldb_reply *ares)
604 struct la_context *ac;
605 const struct dsdb_attribute *schema_attr;
606 const struct dsdb_attribute *target_attr;
607 const struct ldb_message_element *el;
608 const char *attr_name;
612 ac = talloc_get_type(req->context, struct la_context);
615 return ldb_module_done(ac->req, NULL, NULL,
616 LDB_ERR_OPERATIONS_ERROR);
618 if (ares->error != LDB_SUCCESS) {
619 return ldb_module_done(ac->req, ares->controls,
620 ares->response, ares->error);
623 /* Only entries are interesting, and we only want the olddn */
624 switch (ares->type) {
625 case LDB_REPLY_ENTRY:
626 ret = ldb_dn_compare(ares->message->dn, req->op.search.base);
628 /* Guh? We only asked for this DN */
630 return ldb_module_done(ac->req, NULL, NULL,
631 LDB_ERR_OPERATIONS_ERROR);
633 if (ares->message->num_elements == 0) {
634 /* only bother at all if there were some
635 * linked attributes found */
640 switch (ac->req->operation) {
642 ac->del_dn = talloc_steal(ac, ares->message->dn);
645 ac->add_dn = talloc_steal(ac, ares->message->dn);
646 ac->del_dn = talloc_steal(ac, ac->req->op.rename.olddn);
650 ldb_set_errstring(ac->module->ldb,
651 "operations must be delete or rename");
652 return ldb_module_done(ac->req, NULL, NULL,
653 LDB_ERR_OPERATIONS_ERROR);
656 for (i = 0; i < ares->message->num_elements; i++) {
657 el = &ares->message->elements[i];
659 schema_attr = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
661 ldb_asprintf_errstring(ac->module->ldb,
662 "attribute %s is not a valid attribute"
663 " in schema", el->name);
665 return ldb_module_done(ac->req, NULL, NULL,
666 LDB_ERR_OBJECT_CLASS_VIOLATION);
669 /* Valid attribute, now find out if it is linked */
670 if (schema_attr->linkID == 0) {
671 /* Not a linked attribute, skip */
675 if ((schema_attr->linkID & 1) == 0) {
676 /* Odd is for the target. */
677 target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID + 1);
681 attr_name = target_attr->lDAPDisplayName;
683 target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID - 1);
687 attr_name = target_attr->lDAPDisplayName;
689 for (j = 0; j < el->num_values; j++) {
690 ret = la_store_op(ac, LA_OP_DEL,
694 /* for renames, ensure we add it back */
695 if (ret == LDB_SUCCESS
696 && ac->req->operation == LDB_RENAME) {
697 ret = la_store_op(ac, LA_OP_ADD,
701 if (ret != LDB_SUCCESS) {
703 return ldb_module_done(ac->req,
711 case LDB_REPLY_REFERRAL:
720 switch (ac->req->operation) {
722 /* start the mod requests chain */
723 ret = la_down_req(ac);
724 if (ret != LDB_SUCCESS) {
725 return ldb_module_done(ac->req, NULL, NULL, ret);
730 ret = la_do_mod_request(ac);
731 if (ret != LDB_SUCCESS) {
732 return ldb_module_done(ac->req, NULL, NULL,
740 ldb_set_errstring(ac->module->ldb,
741 "operations must be delete or rename");
742 return ldb_module_done(ac->req, NULL, NULL,
743 LDB_ERR_OPERATIONS_ERROR);
752 /* do a linked attributes modify request */
753 static int la_do_mod_request(struct la_context *ac)
755 struct ldb_message_element *ret_el;
756 struct ldb_request *mod_req;
757 struct ldb_message *new_msg;
758 struct ldb_context *ldb;
761 /* If we have no modifies in the queue, we are done! */
763 return ldb_module_done(ac->req, ac->op_controls,
764 ac->op_response, LDB_SUCCESS);
767 ldb = ac->module->ldb;
769 /* Create the modify request */
770 new_msg = ldb_msg_new(ac);
773 return LDB_ERR_OPERATIONS_ERROR;
775 new_msg->dn = ac->ops->dn;
777 if (ac->ops->op == LA_OP_ADD) {
778 ret = ldb_msg_add_empty(new_msg, ac->ops->name,
779 LDB_FLAG_MOD_ADD, &ret_el);
781 ret = ldb_msg_add_empty(new_msg, ac->ops->name,
782 LDB_FLAG_MOD_DELETE, &ret_el);
784 if (ret != LDB_SUCCESS) {
787 ret_el->values = talloc_array(new_msg, struct ldb_val, 1);
788 if (!ret_el->values) {
790 return LDB_ERR_OPERATIONS_ERROR;
792 ret_el->num_values = 1;
793 if (ac->ops->op == LA_OP_ADD) {
794 ret_el->values[0] = data_blob_string_const(ldb_dn_get_extended_linearized(new_msg, ac->add_dn, 1));
796 ret_el->values[0] = data_blob_string_const(ldb_dn_get_extended_linearized(new_msg, ac->del_dn, 1));
800 ldb_debug(ac->module->ldb, LDB_DEBUG_WARNING,
801 "link on %s %s: %s %s\n",
802 ldb_dn_get_linearized(new_msg->dn), ret_el->name,
803 ret_el->values[0].data, ac->ops->op == LA_OP_ADD ? "added" : "deleted");
806 /* use ac->ops as the mem_ctx so that the request will be freed
807 * in the callback as soon as completed */
808 ret = ldb_build_mod_req(&mod_req, ldb, ac->ops,
813 if (ret != LDB_SUCCESS) {
816 talloc_steal(mod_req, new_msg);
818 /* Run the new request */
819 return ldb_next_request(ac->module, mod_req);
822 static int la_mod_callback(struct ldb_request *req, struct ldb_reply *ares)
824 struct la_context *ac;
825 struct la_op_store *os;
827 ac = talloc_get_type(req->context, struct la_context);
830 return ldb_module_done(ac->req, NULL, NULL,
831 LDB_ERR_OPERATIONS_ERROR);
833 if (ares->error != LDB_SUCCESS) {
834 return ldb_module_done(ac->req, ares->controls,
835 ares->response, ares->error);
838 if (ares->type != LDB_REPLY_DONE) {
839 ldb_set_errstring(ac->module->ldb,
840 "invalid ldb_reply_type in callback");
842 return ldb_module_done(ac->req, NULL, NULL,
843 LDB_ERR_OPERATIONS_ERROR);
849 DLIST_REMOVE(ac->ops, os);
851 /* this frees the request too
852 * DO NOT access 'req' after this point */
855 return la_do_mod_request(ac);
858 /* Having done the original operation, then try to fix up all the linked attributes for modify and delete */
859 static int la_mod_del_callback(struct ldb_request *req, struct ldb_reply *ares)
862 struct la_context *ac;
863 ac = talloc_get_type(req->context, struct la_context);
866 return ldb_module_done(ac->req, NULL, NULL,
867 LDB_ERR_OPERATIONS_ERROR);
869 if (ares->error != LDB_SUCCESS) {
870 return ldb_module_done(ac->req, ares->controls,
871 ares->response, ares->error);
874 if (ares->type != LDB_REPLY_DONE) {
875 ldb_set_errstring(ac->module->ldb,
876 "invalid ldb_reply_type in callback");
878 return ldb_module_done(ac->req, NULL, NULL,
879 LDB_ERR_OPERATIONS_ERROR);
882 ac->op_controls = talloc_steal(ac, ares->controls);
883 ac->op_response = talloc_steal(ac, ares->response);
885 /* If we have modfies to make, this is the time to do them for modify and delete */
886 ret = la_do_mod_request(ac);
888 if (ret != LDB_SUCCESS) {
889 return ldb_module_done(ac->req, NULL, NULL, ret);
893 /* la_do_mod_request has already sent the callbacks */
898 /* Having done the original rename try to fix up all the linked attributes */
899 static int la_rename_callback(struct ldb_request *req, struct ldb_reply *ares)
902 struct la_context *ac;
903 struct ldb_request *search_req;
906 ac = talloc_get_type(req->context, struct la_context);
909 return ldb_module_done(ac->req, NULL, NULL,
910 LDB_ERR_OPERATIONS_ERROR);
912 if (ares->error != LDB_SUCCESS) {
913 return ldb_module_done(ac->req, ares->controls,
914 ares->response, ares->error);
917 if (ares->type != LDB_REPLY_DONE) {
918 ldb_set_errstring(ac->module->ldb,
919 "invalid ldb_reply_type in callback");
921 return ldb_module_done(ac->req, NULL, NULL,
922 LDB_ERR_OPERATIONS_ERROR);
925 werr = dsdb_linked_attribute_lDAPDisplayName_list(ac->schema, ac, &attrs);
926 if (!W_ERROR_IS_OK(werr)) {
927 return LDB_ERR_OPERATIONS_ERROR;
930 ret = ldb_build_search_req(&search_req, ac->module->ldb, req,
931 ac->req->op.rename.newdn, LDB_SCOPE_BASE,
932 "(objectClass=*)", attrs,
934 ac, la_op_search_callback,
937 if (ret != LDB_SUCCESS) {
941 talloc_steal(search_req, attrs);
943 if (ret == LDB_SUCCESS) {
944 ret = ldb_request_add_control(search_req,
945 LDB_CONTROL_EXTENDED_DN_OID,
948 if (ret != LDB_SUCCESS) {
949 return ldb_module_done(ac->req, NULL, NULL,
953 ac->op_controls = talloc_steal(ac, ares->controls);
954 ac->op_response = talloc_steal(ac, ares->response);
956 return ldb_next_request(ac->module, search_req);
959 /* Having done the original add, then try to fix up all the linked attributes
961 This is done after the add so the links can get the extended DNs correctly.
963 static int la_add_callback(struct ldb_request *req, struct ldb_reply *ares)
966 struct la_context *ac;
967 ac = talloc_get_type(req->context, struct la_context);
970 return ldb_module_done(ac->req, NULL, NULL,
971 LDB_ERR_OPERATIONS_ERROR);
973 if (ares->error != LDB_SUCCESS) {
974 return ldb_module_done(ac->req, ares->controls,
975 ares->response, ares->error);
978 if (ares->type != LDB_REPLY_DONE) {
979 ldb_set_errstring(ac->module->ldb,
980 "invalid ldb_reply_type in callback");
982 return ldb_module_done(ac->req, NULL, NULL,
983 LDB_ERR_OPERATIONS_ERROR);
987 struct ldb_request *search_req;
988 static const char *attrs[] = { NULL };
990 /* The callback does all the hard work here - we need
991 * the objectGUID and SID of the added record */
992 ret = ldb_build_search_req(&search_req, ac->module->ldb, ac,
993 ac->req->op.add.message->dn,
995 "(objectClass=*)", attrs,
997 ac, la_mod_search_callback,
1000 if (ret == LDB_SUCCESS) {
1001 ret = ldb_request_add_control(search_req,
1002 LDB_CONTROL_EXTENDED_DN_OID,
1005 if (ret != LDB_SUCCESS) {
1006 return ldb_module_done(ac->req, NULL, NULL,
1010 ac->op_controls = talloc_steal(ac, ares->controls);
1011 ac->op_response = talloc_steal(ac, ares->response);
1013 return ldb_next_request(ac->module, search_req);
1016 return ldb_module_done(ac->req, ares->controls,
1017 ares->response, ares->error);
1021 /* Reconstruct the original request, but pointing at our local callback to finish things off */
1022 static int la_down_req(struct la_context *ac)
1024 struct ldb_request *down_req;
1027 switch (ac->req->operation) {
1029 ret = ldb_build_add_req(&down_req, ac->module->ldb, ac,
1030 ac->req->op.add.message,
1032 ac, la_add_callback,
1036 ret = ldb_build_mod_req(&down_req, ac->module->ldb, ac,
1037 ac->req->op.mod.message,
1039 ac, la_mod_del_callback,
1043 ret = ldb_build_del_req(&down_req, ac->module->ldb, ac,
1046 ac, la_mod_del_callback,
1050 ret = ldb_build_rename_req(&down_req, ac->module->ldb, ac,
1051 ac->req->op.rename.olddn,
1052 ac->req->op.rename.newdn,
1054 ac, la_rename_callback,
1058 ret = LDB_ERR_OPERATIONS_ERROR;
1060 if (ret != LDB_SUCCESS) {
1064 return ldb_next_request(ac->module, down_req);
1068 _PUBLIC_ const struct ldb_module_ops ldb_linked_attributes_module_ops = {
1069 .name = "linked_attributes",
1070 .add = linked_attributes_add,
1071 .modify = linked_attributes_modify,
1072 .del = linked_attributes_del,
1073 .rename = linked_attributes_rename,