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 "dlinklist.h"
34 #include "dsdb/samdb/samdb.h"
37 struct la_op_store *next;
38 struct la_op_store *prev;
39 enum la_op {LA_OP_ADD, LA_OP_DEL} op;
45 struct replace_context {
46 struct la_context *ac;
47 unsigned int num_elements;
48 struct ldb_message_element *el;
52 const struct dsdb_schema *schema;
53 struct ldb_module *module;
54 struct ldb_request *req;
55 struct ldb_dn *add_dn;
56 struct ldb_dn *del_dn;
57 struct replace_context *rc;
58 struct la_op_store *ops;
59 struct ldb_extended *op_response;
60 struct ldb_control **op_controls;
63 static struct la_context *linked_attributes_init(struct ldb_module *module,
64 struct ldb_request *req)
66 struct ldb_context *ldb;
67 struct la_context *ac;
69 ldb = ldb_module_get_ctx(module);
71 ac = talloc_zero(req, struct la_context);
77 ac->schema = dsdb_get_schema(ldb);
84 /* Common routine to handle reading the attributes and creating a
85 * series of modify requests */
86 static int la_store_op(struct la_context *ac,
87 enum la_op op, struct ldb_val *dn,
90 struct ldb_context *ldb;
91 struct la_op_store *os;
94 ldb = ldb_module_get_ctx(ac->module);
96 op_dn = ldb_dn_from_ldb_val(ac, ldb, dn);
98 ldb_asprintf_errstring(ldb,
99 "could not parse attribute as a DN");
100 return LDB_ERR_INVALID_DN_SYNTAX;
103 os = talloc_zero(ac, struct la_op_store);
106 return LDB_ERR_OPERATIONS_ERROR;
111 os->dn = talloc_steal(os, op_dn);
113 os->name = talloc_strdup(os, name);
116 return LDB_ERR_OPERATIONS_ERROR;
119 /* Do deletes before adds */
120 if (op == LA_OP_ADD) {
121 DLIST_ADD_END(ac->ops, os, struct la_op_store *);
123 /* By adding to the head of the list, we do deletes before
124 * adds when processing a replace */
125 DLIST_ADD(ac->ops, os);
131 static int la_op_search_callback(struct ldb_request *req,
132 struct ldb_reply *ares);
133 static int la_do_mod_request(struct la_context *ac);
134 static int la_mod_callback(struct ldb_request *req,
135 struct ldb_reply *ares);
136 static int la_down_req(struct la_context *ac);
141 static int linked_attributes_add(struct ldb_module *module, struct ldb_request *req)
143 struct ldb_context *ldb;
144 const struct dsdb_attribute *target_attr;
145 struct la_context *ac;
146 const char *attr_name;
150 ldb = ldb_module_get_ctx(module);
152 if (ldb_dn_is_special(req->op.add.message->dn)) {
153 /* do not manipulate our control entries */
154 return ldb_next_request(module, req);
157 ac = linked_attributes_init(module, req);
159 return LDB_ERR_OPERATIONS_ERROR;
163 /* without schema, this doesn't make any sense */
165 return ldb_next_request(module, req);
168 /* Need to ensure we only have forward links being specified */
169 for (i=0; i < req->op.add.message->num_elements; i++) {
170 const struct ldb_message_element *el = &req->op.add.message->elements[i];
171 const struct dsdb_attribute *schema_attr
172 = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
174 ldb_asprintf_errstring(ldb,
175 "attribute %s is not a valid attribute in schema", el->name);
176 return LDB_ERR_OBJECT_CLASS_VIOLATION;
178 /* We have a valid attribute, now find out if it is linked */
179 if (schema_attr->linkID == 0) {
183 if ((schema_attr->linkID & 1) == 1) {
184 /* Odd is for the target. Illigal to modify */
185 ldb_asprintf_errstring(ldb,
186 "attribute %s must not be modified directly, it is a linked attribute", el->name);
187 return LDB_ERR_UNWILLING_TO_PERFORM;
190 /* Even link IDs are for the originating attribute */
191 target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID + 1);
194 * windows 2003 has a broken schema where
195 * the definition of msDS-IsDomainFor
196 * is missing (which is supposed to be
197 * the backlink of the msDS-HasDomainNCs
203 attr_name = target_attr->lDAPDisplayName;
205 for (j = 0; j < el->num_values; j++) {
206 ret = la_store_op(ac, LA_OP_ADD,
209 if (ret != LDB_SUCCESS) {
215 /* if no linked attributes are present continue */
216 if (ac->ops == NULL) {
217 /* nothing to do for this module, proceed */
219 return ldb_next_request(module, req);
222 /* start with the original request */
223 return la_down_req(ac);
226 /* For a delete or rename, we need to find out what linked attributes
227 * are currently on this DN, and then deal with them. This is the
228 * callback to the base search */
230 static int la_mod_search_callback(struct ldb_request *req, struct ldb_reply *ares)
232 struct ldb_context *ldb;
233 const struct dsdb_attribute *schema_attr;
234 const struct dsdb_attribute *target_attr;
235 struct ldb_message_element *search_el;
236 struct replace_context *rc;
237 struct la_context *ac;
238 const char *attr_name;
240 int ret = LDB_SUCCESS;
242 ac = talloc_get_type(req->context, struct la_context);
243 ldb = ldb_module_get_ctx(ac->module);
247 return ldb_module_done(ac->req, NULL, NULL,
248 LDB_ERR_OPERATIONS_ERROR);
250 if (ares->error != LDB_SUCCESS) {
251 return ldb_module_done(ac->req, ares->controls,
252 ares->response, ares->error);
255 /* Only entries are interesting, and we only want the olddn */
256 switch (ares->type) {
257 case LDB_REPLY_ENTRY:
259 if (ldb_dn_compare(ares->message->dn, ac->req->op.mod.message->dn) != 0) {
260 ldb_asprintf_errstring(ldb,
261 "linked_attributes: %s is not the DN we were looking for", ldb_dn_get_linearized(ares->message->dn));
262 /* Guh? We only asked for this DN */
264 return ldb_module_done(ac->req, NULL, NULL,
265 LDB_ERR_OPERATIONS_ERROR);
268 ac->add_dn = ac->del_dn = talloc_steal(ac, ares->message->dn);
270 /* We don't populate 'rc' for ADD - it can't be deleting elements anyway */
271 for (i = 0; rc && i < rc->num_elements; i++) {
273 schema_attr = dsdb_attribute_by_lDAPDisplayName(ac->schema, rc->el[i].name);
275 ldb_asprintf_errstring(ldb,
276 "attribute %s is not a valid attribute in schema",
279 return ldb_module_done(ac->req, NULL, NULL,
280 LDB_ERR_OBJECT_CLASS_VIOLATION);
283 search_el = ldb_msg_find_element(ares->message,
286 /* See if this element already exists */
287 /* otherwise just ignore as
288 * the add has already been scheduled */
293 target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID + 1);
296 * windows 2003 has a broken schema where
297 * the definition of msDS-IsDomainFor
298 * is missing (which is supposed to be
299 * the backlink of the msDS-HasDomainNCs
304 attr_name = target_attr->lDAPDisplayName;
306 /* Now we know what was there, we can remove it for the re-add */
307 for (j = 0; j < search_el->num_values; j++) {
308 ret = la_store_op(ac, LA_OP_DEL,
309 &search_el->values[j],
311 if (ret != LDB_SUCCESS) {
313 return ldb_module_done(ac->req,
321 case LDB_REPLY_REFERRAL:
329 if (ac->req->operation == LDB_ADD) {
330 /* Start the modifies to the backlinks */
331 ret = la_do_mod_request(ac);
333 if (ret != LDB_SUCCESS) {
334 return ldb_module_done(ac->req, NULL, NULL,
338 /* Start with the original request */
339 ret = la_down_req(ac);
340 if (ret != LDB_SUCCESS) {
341 return ldb_module_done(ac->req, NULL, NULL, ret);
353 static int linked_attributes_modify(struct ldb_module *module, struct ldb_request *req)
355 /* Look over list of modifications */
356 /* Find if any are for linked attributes */
357 /* Determine the effect of the modification */
358 /* Apply the modify to the linked entry */
360 struct ldb_context *ldb;
362 struct la_context *ac;
363 struct ldb_request *search_req;
368 ldb = ldb_module_get_ctx(module);
370 if (ldb_dn_is_special(req->op.mod.message->dn)) {
371 /* do not manipulate our control entries */
372 return ldb_next_request(module, req);
375 ac = linked_attributes_init(module, req);
377 return LDB_ERR_OPERATIONS_ERROR;
381 /* without schema, this doesn't make any sense */
382 return ldb_next_request(module, req);
385 ac->rc = talloc_zero(ac, struct replace_context);
388 return LDB_ERR_OPERATIONS_ERROR;
391 for (i=0; i < req->op.mod.message->num_elements; i++) {
392 bool store_el = false;
393 const char *attr_name;
394 const struct dsdb_attribute *target_attr;
395 const struct ldb_message_element *el = &req->op.mod.message->elements[i];
396 const struct dsdb_attribute *schema_attr
397 = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
399 ldb_asprintf_errstring(ldb,
400 "attribute %s is not a valid attribute in schema", el->name);
401 return LDB_ERR_OBJECT_CLASS_VIOLATION;
403 /* We have a valid attribute, now find out if it is linked */
404 if (schema_attr->linkID == 0) {
408 if ((schema_attr->linkID & 1) == 1) {
409 /* Odd is for the target. Illegal to modify */
410 ldb_asprintf_errstring(ldb,
411 "attribute %s must not be modified directly, it is a linked attribute", el->name);
412 return LDB_ERR_UNWILLING_TO_PERFORM;
415 /* Even link IDs are for the originating attribute */
417 /* Now find the target attribute */
418 target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID + 1);
421 * windows 2003 has a broken schema where
422 * the definition of msDS-IsDomainFor
423 * is missing (which is supposed to be
424 * the backlink of the msDS-HasDomainNCs
430 attr_name = target_attr->lDAPDisplayName;
432 switch (el->flags & LDB_FLAG_MOD_MASK) {
433 case LDB_FLAG_MOD_REPLACE:
434 /* treat as just a normal add the delete part is handled by the callback */
437 /* break intentionally missing */
439 case LDB_FLAG_MOD_ADD:
441 /* For each value being added, we need to setup the adds */
442 for (j = 0; j < el->num_values; j++) {
443 ret = la_store_op(ac, LA_OP_ADD,
446 if (ret != LDB_SUCCESS) {
452 case LDB_FLAG_MOD_DELETE:
454 if (el->num_values) {
455 /* For each value being deleted, we need to setup the delete */
456 for (j = 0; j < el->num_values; j++) {
457 ret = la_store_op(ac, LA_OP_DEL,
460 if (ret != LDB_SUCCESS) {
465 /* Flag that there was a DELETE
466 * without a value specified, so we
467 * need to look for the old value */
475 struct ldb_message_element *search_el;
477 search_el = talloc_realloc(ac->rc, ac->rc->el,
478 struct ldb_message_element,
479 ac->rc->num_elements +1);
482 return LDB_ERR_OPERATIONS_ERROR;
484 ac->rc->el = search_el;
486 ac->rc->el[ac->rc->num_elements] = *el;
487 ac->rc->num_elements++;
491 if (ac->ops || ac->rc->el) {
492 /* both replace and delete without values are handled in the callback
493 * after the search on the entry to be modified is performed */
495 attrs = talloc_array(ac->rc, const char *, ac->rc->num_elements + 1);
498 return LDB_ERR_OPERATIONS_ERROR;
500 for (i = 0; ac->rc && i < ac->rc->num_elements; i++) {
501 attrs[i] = ac->rc->el[i].name;
505 /* The callback does all the hard work here */
506 ret = ldb_build_search_req(&search_req, ldb, ac,
507 req->op.mod.message->dn,
509 "(objectClass=*)", attrs,
511 ac, la_mod_search_callback,
514 /* We need to figure out our own extended DN, to fill in as the backlink target */
515 if (ret == LDB_SUCCESS) {
516 ret = ldb_request_add_control(search_req,
517 LDB_CONTROL_EXTENDED_DN_OID,
520 if (ret == LDB_SUCCESS) {
521 talloc_steal(search_req, attrs);
523 ret = ldb_next_request(module, search_req);
527 /* nothing to do for this module, proceed */
529 ret = ldb_next_request(module, req);
536 static int linked_attributes_del(struct ldb_module *module, struct ldb_request *req)
538 struct ldb_context *ldb;
539 struct ldb_request *search_req;
540 struct la_context *ac;
545 /* This gets complex: We need to:
546 - Do a search for the entry
547 - Wait for these result to appear
548 - In the callback for the result, issue a modify
549 request based on the linked attributes found
550 - Wait for each modify result
554 ldb = ldb_module_get_ctx(module);
556 ac = linked_attributes_init(module, req);
558 return LDB_ERR_OPERATIONS_ERROR;
562 /* without schema, this doesn't make any sense */
563 return ldb_next_request(module, req);
566 werr = dsdb_linked_attribute_lDAPDisplayName_list(ac->schema, ac, &attrs);
567 if (!W_ERROR_IS_OK(werr)) {
568 return LDB_ERR_OPERATIONS_ERROR;
571 ret = ldb_build_search_req(&search_req, ldb, req,
572 req->op.del.dn, LDB_SCOPE_BASE,
573 "(objectClass=*)", attrs,
575 ac, la_op_search_callback,
578 if (ret != LDB_SUCCESS) {
582 talloc_steal(search_req, attrs);
584 return ldb_next_request(module, search_req);
588 static int linked_attributes_rename(struct ldb_module *module, struct ldb_request *req)
590 struct la_context *ac;
592 /* This gets complex: We need to:
593 - Do a search for the entry
594 - Wait for these result to appear
595 - In the callback for the result, issue a modify
596 request based on the linked attributes found
597 - Wait for each modify result
601 ac = linked_attributes_init(module, req);
603 return LDB_ERR_OPERATIONS_ERROR;
607 /* without schema, this doesn't make any sense */
608 return ldb_next_request(module, req);
611 /* start with the original request */
612 return la_down_req(ac);
616 static int la_op_search_callback(struct ldb_request *req,
617 struct ldb_reply *ares)
619 struct ldb_context *ldb;
620 struct la_context *ac;
621 const struct dsdb_attribute *schema_attr;
622 const struct dsdb_attribute *target_attr;
623 const struct ldb_message_element *el;
624 const char *attr_name;
628 ac = talloc_get_type(req->context, struct la_context);
629 ldb = ldb_module_get_ctx(ac->module);
632 return ldb_module_done(ac->req, NULL, NULL,
633 LDB_ERR_OPERATIONS_ERROR);
635 if (ares->error != LDB_SUCCESS) {
636 return ldb_module_done(ac->req, ares->controls,
637 ares->response, ares->error);
640 /* Only entries are interesting, and we only want the olddn */
641 switch (ares->type) {
642 case LDB_REPLY_ENTRY:
643 ret = ldb_dn_compare(ares->message->dn, req->op.search.base);
645 /* Guh? We only asked for this DN */
647 return ldb_module_done(ac->req, NULL, NULL,
648 LDB_ERR_OPERATIONS_ERROR);
650 if (ares->message->num_elements == 0) {
651 /* only bother at all if there were some
652 * linked attributes found */
657 switch (ac->req->operation) {
659 ac->del_dn = talloc_steal(ac, ares->message->dn);
662 ac->add_dn = talloc_steal(ac, ares->message->dn);
663 ac->del_dn = talloc_steal(ac, ac->req->op.rename.olddn);
667 ldb_set_errstring(ldb,
668 "operations must be delete or rename");
669 return ldb_module_done(ac->req, NULL, NULL,
670 LDB_ERR_OPERATIONS_ERROR);
673 for (i = 0; i < ares->message->num_elements; i++) {
674 el = &ares->message->elements[i];
676 schema_attr = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
678 ldb_asprintf_errstring(ldb,
679 "attribute %s is not a valid attribute"
680 " in schema", el->name);
682 return ldb_module_done(ac->req, NULL, NULL,
683 LDB_ERR_OBJECT_CLASS_VIOLATION);
686 /* Valid attribute, now find out if it is linked */
687 if (schema_attr->linkID == 0) {
688 /* Not a linked attribute, skip */
692 if ((schema_attr->linkID & 1) == 0) {
693 /* Odd is for the target. */
694 target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID + 1);
698 attr_name = target_attr->lDAPDisplayName;
700 target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID - 1);
704 attr_name = target_attr->lDAPDisplayName;
706 for (j = 0; j < el->num_values; j++) {
707 ret = la_store_op(ac, LA_OP_DEL,
711 /* for renames, ensure we add it back */
712 if (ret == LDB_SUCCESS
713 && ac->req->operation == LDB_RENAME) {
714 ret = la_store_op(ac, LA_OP_ADD,
718 if (ret != LDB_SUCCESS) {
720 return ldb_module_done(ac->req,
728 case LDB_REPLY_REFERRAL:
737 switch (ac->req->operation) {
739 /* start the mod requests chain */
740 ret = la_down_req(ac);
741 if (ret != LDB_SUCCESS) {
742 return ldb_module_done(ac->req, NULL, NULL, ret);
747 ret = la_do_mod_request(ac);
748 if (ret != LDB_SUCCESS) {
749 return ldb_module_done(ac->req, NULL, NULL,
757 ldb_set_errstring(ldb,
758 "operations must be delete or rename");
759 return ldb_module_done(ac->req, NULL, NULL,
760 LDB_ERR_OPERATIONS_ERROR);
769 /* do a linked attributes modify request */
770 static int la_do_mod_request(struct la_context *ac)
772 struct ldb_message_element *ret_el;
773 struct ldb_request *mod_req;
774 struct ldb_message *new_msg;
775 struct ldb_context *ldb;
778 /* If we have no modifies in the queue, we are done! */
780 return ldb_module_done(ac->req, ac->op_controls,
781 ac->op_response, LDB_SUCCESS);
784 ldb = ldb_module_get_ctx(ac->module);
786 /* Create the modify request */
787 new_msg = ldb_msg_new(ac);
790 return LDB_ERR_OPERATIONS_ERROR;
792 new_msg->dn = ac->ops->dn;
794 if (ac->ops->op == LA_OP_ADD) {
795 ret = ldb_msg_add_empty(new_msg, ac->ops->name,
796 LDB_FLAG_MOD_ADD, &ret_el);
798 ret = ldb_msg_add_empty(new_msg, ac->ops->name,
799 LDB_FLAG_MOD_DELETE, &ret_el);
801 if (ret != LDB_SUCCESS) {
804 ret_el->values = talloc_array(new_msg, struct ldb_val, 1);
805 if (!ret_el->values) {
807 return LDB_ERR_OPERATIONS_ERROR;
809 ret_el->num_values = 1;
810 if (ac->ops->op == LA_OP_ADD) {
811 ret_el->values[0] = data_blob_string_const(ldb_dn_get_extended_linearized(new_msg, ac->add_dn, 1));
813 ret_el->values[0] = data_blob_string_const(ldb_dn_get_extended_linearized(new_msg, ac->del_dn, 1));
817 ldb_debug(ldb, LDB_DEBUG_WARNING,
818 "link on %s %s: %s %s\n",
819 ldb_dn_get_linearized(new_msg->dn), ret_el->name,
820 ret_el->values[0].data, ac->ops->op == LA_OP_ADD ? "added" : "deleted");
823 /* use ac->ops as the mem_ctx so that the request will be freed
824 * in the callback as soon as completed */
825 ret = ldb_build_mod_req(&mod_req, ldb, ac->ops,
830 if (ret != LDB_SUCCESS) {
833 talloc_steal(mod_req, new_msg);
835 /* Run the new request */
836 return ldb_next_request(ac->module, mod_req);
839 static int la_mod_callback(struct ldb_request *req, struct ldb_reply *ares)
841 struct la_context *ac;
842 struct ldb_context *ldb;
843 struct la_op_store *os;
845 ac = talloc_get_type(req->context, struct la_context);
846 ldb = ldb_module_get_ctx(ac->module);
849 return ldb_module_done(ac->req, NULL, NULL,
850 LDB_ERR_OPERATIONS_ERROR);
852 if (ares->error != LDB_SUCCESS) {
853 return ldb_module_done(ac->req, ares->controls,
854 ares->response, ares->error);
857 if (ares->type != LDB_REPLY_DONE) {
858 ldb_set_errstring(ldb,
859 "invalid ldb_reply_type in callback");
861 return ldb_module_done(ac->req, NULL, NULL,
862 LDB_ERR_OPERATIONS_ERROR);
868 DLIST_REMOVE(ac->ops, os);
870 /* this frees the request too
871 * DO NOT access 'req' after this point */
874 return la_do_mod_request(ac);
877 /* Having done the original operation, then try to fix up all the linked attributes for modify and delete */
878 static int la_mod_del_callback(struct ldb_request *req, struct ldb_reply *ares)
881 struct la_context *ac;
882 struct ldb_context *ldb;
884 ac = talloc_get_type(req->context, struct la_context);
885 ldb = ldb_module_get_ctx(ac->module);
888 return ldb_module_done(ac->req, NULL, NULL,
889 LDB_ERR_OPERATIONS_ERROR);
891 if (ares->error != LDB_SUCCESS) {
892 return ldb_module_done(ac->req, ares->controls,
893 ares->response, ares->error);
896 if (ares->type != LDB_REPLY_DONE) {
897 ldb_set_errstring(ldb,
898 "invalid ldb_reply_type in callback");
900 return ldb_module_done(ac->req, NULL, NULL,
901 LDB_ERR_OPERATIONS_ERROR);
904 ac->op_controls = talloc_steal(ac, ares->controls);
905 ac->op_response = talloc_steal(ac, ares->response);
907 /* If we have modfies to make, this is the time to do them for modify and delete */
908 ret = la_do_mod_request(ac);
910 if (ret != LDB_SUCCESS) {
911 return ldb_module_done(ac->req, NULL, NULL, ret);
915 /* la_do_mod_request has already sent the callbacks */
920 /* Having done the original rename try to fix up all the linked attributes */
921 static int la_rename_callback(struct ldb_request *req, struct ldb_reply *ares)
924 struct la_context *ac;
925 struct ldb_request *search_req;
928 struct ldb_context *ldb;
930 ac = talloc_get_type(req->context, struct la_context);
931 ldb = ldb_module_get_ctx(ac->module);
934 return ldb_module_done(ac->req, NULL, NULL,
935 LDB_ERR_OPERATIONS_ERROR);
937 if (ares->error != LDB_SUCCESS) {
938 return ldb_module_done(ac->req, ares->controls,
939 ares->response, ares->error);
942 if (ares->type != LDB_REPLY_DONE) {
943 ldb_set_errstring(ldb,
944 "invalid ldb_reply_type in callback");
946 return ldb_module_done(ac->req, NULL, NULL,
947 LDB_ERR_OPERATIONS_ERROR);
950 werr = dsdb_linked_attribute_lDAPDisplayName_list(ac->schema, ac, &attrs);
951 if (!W_ERROR_IS_OK(werr)) {
952 return LDB_ERR_OPERATIONS_ERROR;
955 ret = ldb_build_search_req(&search_req, ldb, req,
956 ac->req->op.rename.newdn, LDB_SCOPE_BASE,
957 "(objectClass=*)", attrs,
959 ac, la_op_search_callback,
962 if (ret != LDB_SUCCESS) {
966 talloc_steal(search_req, attrs);
968 if (ret == LDB_SUCCESS) {
969 ret = ldb_request_add_control(search_req,
970 LDB_CONTROL_EXTENDED_DN_OID,
973 if (ret != LDB_SUCCESS) {
974 return ldb_module_done(ac->req, NULL, NULL,
978 ac->op_controls = talloc_steal(ac, ares->controls);
979 ac->op_response = talloc_steal(ac, ares->response);
981 return ldb_next_request(ac->module, search_req);
984 /* Having done the original add, then try to fix up all the linked attributes
986 This is done after the add so the links can get the extended DNs correctly.
988 static int la_add_callback(struct ldb_request *req, struct ldb_reply *ares)
991 struct la_context *ac;
992 struct ldb_context *ldb;
994 ac = talloc_get_type(req->context, struct la_context);
995 ldb = ldb_module_get_ctx(ac->module);
998 return ldb_module_done(ac->req, NULL, NULL,
999 LDB_ERR_OPERATIONS_ERROR);
1001 if (ares->error != LDB_SUCCESS) {
1002 return ldb_module_done(ac->req, ares->controls,
1003 ares->response, ares->error);
1006 if (ares->type != LDB_REPLY_DONE) {
1007 ldb_set_errstring(ldb,
1008 "invalid ldb_reply_type in callback");
1010 return ldb_module_done(ac->req, NULL, NULL,
1011 LDB_ERR_OPERATIONS_ERROR);
1015 struct ldb_request *search_req;
1016 static const char *attrs[] = { NULL };
1018 /* The callback does all the hard work here - we need
1019 * the objectGUID and SID of the added record */
1020 ret = ldb_build_search_req(&search_req, ldb, ac,
1021 ac->req->op.add.message->dn,
1023 "(objectClass=*)", attrs,
1025 ac, la_mod_search_callback,
1028 if (ret == LDB_SUCCESS) {
1029 ret = ldb_request_add_control(search_req,
1030 LDB_CONTROL_EXTENDED_DN_OID,
1033 if (ret != LDB_SUCCESS) {
1034 return ldb_module_done(ac->req, NULL, NULL,
1038 ac->op_controls = talloc_steal(ac, ares->controls);
1039 ac->op_response = talloc_steal(ac, ares->response);
1041 return ldb_next_request(ac->module, search_req);
1044 return ldb_module_done(ac->req, ares->controls,
1045 ares->response, ares->error);
1049 /* Reconstruct the original request, but pointing at our local callback to finish things off */
1050 static int la_down_req(struct la_context *ac)
1052 struct ldb_request *down_req;
1054 struct ldb_context *ldb;
1056 ldb = ldb_module_get_ctx(ac->module);
1058 switch (ac->req->operation) {
1060 ret = ldb_build_add_req(&down_req, ldb, ac,
1061 ac->req->op.add.message,
1063 ac, la_add_callback,
1067 ret = ldb_build_mod_req(&down_req, ldb, ac,
1068 ac->req->op.mod.message,
1070 ac, la_mod_del_callback,
1074 ret = ldb_build_del_req(&down_req, ldb, ac,
1077 ac, la_mod_del_callback,
1081 ret = ldb_build_rename_req(&down_req, ldb, ac,
1082 ac->req->op.rename.olddn,
1083 ac->req->op.rename.newdn,
1085 ac, la_rename_callback,
1089 ret = LDB_ERR_OPERATIONS_ERROR;
1091 if (ret != LDB_SUCCESS) {
1095 return ldb_next_request(ac->module, down_req);
1099 _PUBLIC_ const struct ldb_module_ops ldb_linked_attributes_module_ops = {
1100 .name = "linked_attributes",
1101 .add = linked_attributes_add,
1102 .modify = linked_attributes_modify,
1103 .del = linked_attributes_del,
1104 .rename = linked_attributes_rename,