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 "dsdb/samdb/samdb.h"
36 struct la_op_store *next;
37 struct la_op_store *prev;
38 enum la_op {LA_OP_ADD, LA_OP_DEL} op;
44 struct replace_context {
45 struct la_context *ac;
46 unsigned int num_elements;
47 struct ldb_message_element *el;
51 const struct dsdb_schema *schema;
52 struct ldb_module *module;
53 struct ldb_request *req;
54 struct ldb_dn *add_dn;
55 struct ldb_dn *del_dn;
56 struct replace_context *rc;
57 struct la_op_store *ops;
58 struct ldb_extended *op_response;
59 struct ldb_control **op_controls;
62 static struct la_context *linked_attributes_init(struct ldb_module *module,
63 struct ldb_request *req)
65 struct ldb_context *ldb;
66 struct la_context *ac;
68 ldb = ldb_module_get_ctx(module);
70 ac = talloc_zero(req, struct la_context);
76 ac->schema = dsdb_get_schema(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 ldb_context *ldb;
90 struct la_op_store *os;
93 ldb = ldb_module_get_ctx(ac->module);
95 op_dn = ldb_dn_from_ldb_val(ac, ldb, dn);
97 ldb_asprintf_errstring(ldb,
98 "could not parse attribute as a DN");
99 return LDB_ERR_INVALID_DN_SYNTAX;
102 os = talloc_zero(ac, struct la_op_store);
105 return LDB_ERR_OPERATIONS_ERROR;
110 os->dn = talloc_steal(os, op_dn);
112 os->name = talloc_strdup(os, name);
115 return LDB_ERR_OPERATIONS_ERROR;
118 /* Do deletes before adds */
119 if (op == LA_OP_ADD) {
120 DLIST_ADD_END(ac->ops, os, struct la_op_store *);
122 /* By adding to the head of the list, we do deletes before
123 * adds when processing a replace */
124 DLIST_ADD(ac->ops, os);
130 static int la_op_search_callback(struct ldb_request *req,
131 struct ldb_reply *ares);
132 static int la_do_mod_request(struct la_context *ac);
133 static int la_mod_callback(struct ldb_request *req,
134 struct ldb_reply *ares);
135 static int la_down_req(struct la_context *ac);
140 static int linked_attributes_add(struct ldb_module *module, struct ldb_request *req)
142 struct ldb_context *ldb;
143 const struct dsdb_attribute *target_attr;
144 struct la_context *ac;
145 const char *attr_name;
149 ldb = ldb_module_get_ctx(module);
151 if (ldb_dn_is_special(req->op.add.message->dn)) {
152 /* do not manipulate our control entries */
153 return ldb_next_request(module, req);
156 ac = linked_attributes_init(module, req);
158 return LDB_ERR_OPERATIONS_ERROR;
162 /* without schema, this doesn't make any sense */
164 return ldb_next_request(module, req);
167 /* Need to ensure we only have forward links being specified */
168 for (i=0; i < req->op.add.message->num_elements; i++) {
169 const struct ldb_message_element *el = &req->op.add.message->elements[i];
170 const struct dsdb_attribute *schema_attr
171 = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
173 ldb_asprintf_errstring(ldb,
174 "attribute %s is not a valid attribute in schema", el->name);
175 return LDB_ERR_OBJECT_CLASS_VIOLATION;
177 /* We have a valid attribute, now find out if it is linked */
178 if (schema_attr->linkID == 0) {
182 if ((schema_attr->linkID & 1) == 1) {
183 /* Odd is for the target. Illigal to modify */
184 ldb_asprintf_errstring(ldb,
185 "attribute %s must not be modified directly, it is a linked attribute", el->name);
186 return LDB_ERR_UNWILLING_TO_PERFORM;
189 /* Even link IDs are for the originating attribute */
190 target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID + 1);
193 * windows 2003 has a broken schema where
194 * the definition of msDS-IsDomainFor
195 * is missing (which is supposed to be
196 * the backlink of the msDS-HasDomainNCs
202 attr_name = target_attr->lDAPDisplayName;
204 for (j = 0; j < el->num_values; j++) {
205 ret = la_store_op(ac, LA_OP_ADD,
208 if (ret != LDB_SUCCESS) {
214 /* if no linked attributes are present continue */
215 if (ac->ops == NULL) {
216 /* nothing to do for this module, proceed */
218 return ldb_next_request(module, req);
221 /* start with the original request */
222 return la_down_req(ac);
225 /* For a delete or rename, we need to find out what linked attributes
226 * are currently on this DN, and then deal with them. This is the
227 * callback to the base search */
229 static int la_mod_search_callback(struct ldb_request *req, struct ldb_reply *ares)
231 struct ldb_context *ldb;
232 const struct dsdb_attribute *schema_attr;
233 const struct dsdb_attribute *target_attr;
234 struct ldb_message_element *search_el;
235 struct replace_context *rc;
236 struct la_context *ac;
237 const char *attr_name;
239 int ret = LDB_SUCCESS;
241 ac = talloc_get_type(req->context, struct la_context);
242 ldb = ldb_module_get_ctx(ac->module);
246 return ldb_module_done(ac->req, NULL, NULL,
247 LDB_ERR_OPERATIONS_ERROR);
249 if (ares->error != LDB_SUCCESS) {
250 return ldb_module_done(ac->req, ares->controls,
251 ares->response, ares->error);
254 /* Only entries are interesting, and we only want the olddn */
255 switch (ares->type) {
256 case LDB_REPLY_ENTRY:
258 if (ldb_dn_compare(ares->message->dn, ac->req->op.mod.message->dn) != 0) {
259 ldb_asprintf_errstring(ldb,
260 "linked_attributes: %s is not the DN we were looking for", ldb_dn_get_linearized(ares->message->dn));
261 /* Guh? We only asked for this DN */
263 return ldb_module_done(ac->req, NULL, NULL,
264 LDB_ERR_OPERATIONS_ERROR);
267 ac->add_dn = ac->del_dn = talloc_steal(ac, ares->message->dn);
269 /* We don't populate 'rc' for ADD - it can't be deleting elements anyway */
270 for (i = 0; rc && i < rc->num_elements; i++) {
272 schema_attr = dsdb_attribute_by_lDAPDisplayName(ac->schema, rc->el[i].name);
274 ldb_asprintf_errstring(ldb,
275 "attribute %s is not a valid attribute in schema",
278 return ldb_module_done(ac->req, NULL, NULL,
279 LDB_ERR_OBJECT_CLASS_VIOLATION);
282 search_el = ldb_msg_find_element(ares->message,
285 /* See if this element already exists */
286 /* otherwise just ignore as
287 * the add has already been scheduled */
292 target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID + 1);
295 * windows 2003 has a broken schema where
296 * the definition of msDS-IsDomainFor
297 * is missing (which is supposed to be
298 * the backlink of the msDS-HasDomainNCs
303 attr_name = target_attr->lDAPDisplayName;
305 /* Now we know what was there, we can remove it for the re-add */
306 for (j = 0; j < search_el->num_values; j++) {
307 ret = la_store_op(ac, LA_OP_DEL,
308 &search_el->values[j],
310 if (ret != LDB_SUCCESS) {
312 return ldb_module_done(ac->req,
320 case LDB_REPLY_REFERRAL:
328 if (ac->req->operation == LDB_ADD) {
329 /* Start the modifies to the backlinks */
330 ret = la_do_mod_request(ac);
332 if (ret != LDB_SUCCESS) {
333 return ldb_module_done(ac->req, NULL, NULL,
337 /* Start with the original request */
338 ret = la_down_req(ac);
339 if (ret != LDB_SUCCESS) {
340 return ldb_module_done(ac->req, NULL, NULL, ret);
352 static int linked_attributes_modify(struct ldb_module *module, struct ldb_request *req)
354 /* Look over list of modifications */
355 /* Find if any are for linked attributes */
356 /* Determine the effect of the modification */
357 /* Apply the modify to the linked entry */
359 struct ldb_context *ldb;
361 struct la_context *ac;
362 struct ldb_request *search_req;
367 ldb = ldb_module_get_ctx(module);
369 if (ldb_dn_is_special(req->op.mod.message->dn)) {
370 /* do not manipulate our control entries */
371 return ldb_next_request(module, req);
374 ac = linked_attributes_init(module, req);
376 return LDB_ERR_OPERATIONS_ERROR;
380 /* without schema, this doesn't make any sense */
381 return ldb_next_request(module, req);
384 ac->rc = talloc_zero(ac, struct replace_context);
387 return LDB_ERR_OPERATIONS_ERROR;
390 for (i=0; i < req->op.mod.message->num_elements; i++) {
391 bool store_el = false;
392 const char *attr_name;
393 const struct dsdb_attribute *target_attr;
394 const struct ldb_message_element *el = &req->op.mod.message->elements[i];
395 const struct dsdb_attribute *schema_attr
396 = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
398 ldb_asprintf_errstring(ldb,
399 "attribute %s is not a valid attribute in schema", el->name);
400 return LDB_ERR_OBJECT_CLASS_VIOLATION;
402 /* We have a valid attribute, now find out if it is linked */
403 if (schema_attr->linkID == 0) {
407 if ((schema_attr->linkID & 1) == 1) {
408 /* Odd is for the target. Illegal to modify */
409 ldb_asprintf_errstring(ldb,
410 "attribute %s must not be modified directly, it is a linked attribute", el->name);
411 return LDB_ERR_UNWILLING_TO_PERFORM;
414 /* Even link IDs are for the originating attribute */
416 /* Now find the target attribute */
417 target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID + 1);
420 * windows 2003 has a broken schema where
421 * the definition of msDS-IsDomainFor
422 * is missing (which is supposed to be
423 * the backlink of the msDS-HasDomainNCs
429 attr_name = target_attr->lDAPDisplayName;
431 switch (el->flags & LDB_FLAG_MOD_MASK) {
432 case LDB_FLAG_MOD_REPLACE:
433 /* treat as just a normal add the delete part is handled by the callback */
436 /* break intentionally missing */
438 case LDB_FLAG_MOD_ADD:
440 /* For each value being added, we need to setup the adds */
441 for (j = 0; j < el->num_values; j++) {
442 ret = la_store_op(ac, LA_OP_ADD,
445 if (ret != LDB_SUCCESS) {
451 case LDB_FLAG_MOD_DELETE:
453 if (el->num_values) {
454 /* For each value being deleted, we need to setup the delete */
455 for (j = 0; j < el->num_values; j++) {
456 ret = la_store_op(ac, LA_OP_DEL,
459 if (ret != LDB_SUCCESS) {
464 /* Flag that there was a DELETE
465 * without a value specified, so we
466 * need to look for the old value */
474 struct ldb_message_element *search_el;
476 search_el = talloc_realloc(ac->rc, ac->rc->el,
477 struct ldb_message_element,
478 ac->rc->num_elements +1);
481 return LDB_ERR_OPERATIONS_ERROR;
483 ac->rc->el = search_el;
485 ac->rc->el[ac->rc->num_elements] = *el;
486 ac->rc->num_elements++;
490 if (ac->ops || ac->rc->el) {
491 /* both replace and delete without values are handled in the callback
492 * after the search on the entry to be modified is performed */
494 attrs = talloc_array(ac->rc, const char *, ac->rc->num_elements + 1);
497 return LDB_ERR_OPERATIONS_ERROR;
499 for (i = 0; ac->rc && i < ac->rc->num_elements; i++) {
500 attrs[i] = ac->rc->el[i].name;
504 /* The callback does all the hard work here */
505 ret = ldb_build_search_req(&search_req, ldb, ac,
506 req->op.mod.message->dn,
508 "(objectClass=*)", attrs,
510 ac, la_mod_search_callback,
513 /* We need to figure out our own extended DN, to fill in as the backlink target */
514 if (ret == LDB_SUCCESS) {
515 ret = ldb_request_add_control(search_req,
516 LDB_CONTROL_EXTENDED_DN_OID,
519 if (ret == LDB_SUCCESS) {
520 talloc_steal(search_req, attrs);
522 ret = ldb_next_request(module, search_req);
526 /* nothing to do for this module, proceed */
528 ret = ldb_next_request(module, req);
535 static int linked_attributes_del(struct ldb_module *module, struct ldb_request *req)
537 struct ldb_context *ldb;
538 struct ldb_request *search_req;
539 struct la_context *ac;
544 /* This gets complex: We need to:
545 - Do a search for the entry
546 - Wait for these result to appear
547 - In the callback for the result, issue a modify
548 request based on the linked attributes found
549 - Wait for each modify result
553 ldb = ldb_module_get_ctx(module);
555 ac = linked_attributes_init(module, req);
557 return LDB_ERR_OPERATIONS_ERROR;
561 /* without schema, this doesn't make any sense */
562 return ldb_next_request(module, req);
565 werr = dsdb_linked_attribute_lDAPDisplayName_list(ac->schema, ac, &attrs);
566 if (!W_ERROR_IS_OK(werr)) {
567 return LDB_ERR_OPERATIONS_ERROR;
570 ret = ldb_build_search_req(&search_req, ldb, req,
571 req->op.del.dn, LDB_SCOPE_BASE,
572 "(objectClass=*)", attrs,
574 ac, la_op_search_callback,
577 if (ret != LDB_SUCCESS) {
581 talloc_steal(search_req, attrs);
583 return ldb_next_request(module, search_req);
587 static int linked_attributes_rename(struct ldb_module *module, struct ldb_request *req)
589 struct la_context *ac;
591 /* This gets complex: We need to:
592 - Do a search for the entry
593 - Wait for these result to appear
594 - In the callback for the result, issue a modify
595 request based on the linked attributes found
596 - Wait for each modify result
600 ac = linked_attributes_init(module, req);
602 return LDB_ERR_OPERATIONS_ERROR;
606 /* without schema, this doesn't make any sense */
607 return ldb_next_request(module, req);
610 /* start with the original request */
611 return la_down_req(ac);
615 static int la_op_search_callback(struct ldb_request *req,
616 struct ldb_reply *ares)
618 struct ldb_context *ldb;
619 struct la_context *ac;
620 const struct dsdb_attribute *schema_attr;
621 const struct dsdb_attribute *target_attr;
622 const struct ldb_message_element *el;
623 const char *attr_name;
627 ac = talloc_get_type(req->context, struct la_context);
628 ldb = ldb_module_get_ctx(ac->module);
631 return ldb_module_done(ac->req, NULL, NULL,
632 LDB_ERR_OPERATIONS_ERROR);
634 if (ares->error != LDB_SUCCESS) {
635 return ldb_module_done(ac->req, ares->controls,
636 ares->response, ares->error);
639 /* Only entries are interesting, and we only want the olddn */
640 switch (ares->type) {
641 case LDB_REPLY_ENTRY:
642 ret = ldb_dn_compare(ares->message->dn, req->op.search.base);
644 /* Guh? We only asked for this DN */
646 return ldb_module_done(ac->req, NULL, NULL,
647 LDB_ERR_OPERATIONS_ERROR);
649 if (ares->message->num_elements == 0) {
650 /* only bother at all if there were some
651 * linked attributes found */
656 switch (ac->req->operation) {
658 ac->del_dn = talloc_steal(ac, ares->message->dn);
661 ac->add_dn = talloc_steal(ac, ares->message->dn);
662 ac->del_dn = talloc_steal(ac, ac->req->op.rename.olddn);
666 ldb_set_errstring(ldb,
667 "operations must be delete or rename");
668 return ldb_module_done(ac->req, NULL, NULL,
669 LDB_ERR_OPERATIONS_ERROR);
672 for (i = 0; i < ares->message->num_elements; i++) {
673 el = &ares->message->elements[i];
675 schema_attr = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
677 ldb_asprintf_errstring(ldb,
678 "attribute %s is not a valid attribute"
679 " in schema", el->name);
681 return ldb_module_done(ac->req, NULL, NULL,
682 LDB_ERR_OBJECT_CLASS_VIOLATION);
685 /* Valid attribute, now find out if it is linked */
686 if (schema_attr->linkID == 0) {
687 /* Not a linked attribute, skip */
691 if ((schema_attr->linkID & 1) == 0) {
692 /* Odd is for the target. */
693 target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID + 1);
697 attr_name = target_attr->lDAPDisplayName;
699 target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID - 1);
703 attr_name = target_attr->lDAPDisplayName;
705 for (j = 0; j < el->num_values; j++) {
706 ret = la_store_op(ac, LA_OP_DEL,
710 /* for renames, ensure we add it back */
711 if (ret == LDB_SUCCESS
712 && ac->req->operation == LDB_RENAME) {
713 ret = la_store_op(ac, LA_OP_ADD,
717 if (ret != LDB_SUCCESS) {
719 return ldb_module_done(ac->req,
727 case LDB_REPLY_REFERRAL:
736 switch (ac->req->operation) {
738 /* start the mod requests chain */
739 ret = la_down_req(ac);
740 if (ret != LDB_SUCCESS) {
741 return ldb_module_done(ac->req, NULL, NULL, ret);
746 ret = la_do_mod_request(ac);
747 if (ret != LDB_SUCCESS) {
748 return ldb_module_done(ac->req, NULL, NULL,
756 ldb_set_errstring(ldb,
757 "operations must be delete or rename");
758 return ldb_module_done(ac->req, NULL, NULL,
759 LDB_ERR_OPERATIONS_ERROR);
768 /* do a linked attributes modify request */
769 static int la_do_mod_request(struct la_context *ac)
771 struct ldb_message_element *ret_el;
772 struct ldb_request *mod_req;
773 struct ldb_message *new_msg;
774 struct ldb_context *ldb;
777 /* If we have no modifies in the queue, we are done! */
779 return ldb_module_done(ac->req, ac->op_controls,
780 ac->op_response, LDB_SUCCESS);
783 ldb = ldb_module_get_ctx(ac->module);
785 /* Create the modify request */
786 new_msg = ldb_msg_new(ac);
789 return LDB_ERR_OPERATIONS_ERROR;
791 new_msg->dn = ac->ops->dn;
793 if (ac->ops->op == LA_OP_ADD) {
794 ret = ldb_msg_add_empty(new_msg, ac->ops->name,
795 LDB_FLAG_MOD_ADD, &ret_el);
797 ret = ldb_msg_add_empty(new_msg, ac->ops->name,
798 LDB_FLAG_MOD_DELETE, &ret_el);
800 if (ret != LDB_SUCCESS) {
803 ret_el->values = talloc_array(new_msg, struct ldb_val, 1);
804 if (!ret_el->values) {
806 return LDB_ERR_OPERATIONS_ERROR;
808 ret_el->num_values = 1;
809 if (ac->ops->op == LA_OP_ADD) {
810 ret_el->values[0] = data_blob_string_const(ldb_dn_get_extended_linearized(new_msg, ac->add_dn, 1));
812 ret_el->values[0] = data_blob_string_const(ldb_dn_get_extended_linearized(new_msg, ac->del_dn, 1));
816 ldb_debug(ldb, LDB_DEBUG_WARNING,
817 "link on %s %s: %s %s\n",
818 ldb_dn_get_linearized(new_msg->dn), ret_el->name,
819 ret_el->values[0].data, ac->ops->op == LA_OP_ADD ? "added" : "deleted");
822 /* use ac->ops as the mem_ctx so that the request will be freed
823 * in the callback as soon as completed */
824 ret = ldb_build_mod_req(&mod_req, ldb, ac->ops,
829 if (ret != LDB_SUCCESS) {
832 talloc_steal(mod_req, new_msg);
834 /* Run the new request */
835 return ldb_next_request(ac->module, mod_req);
838 static int la_mod_callback(struct ldb_request *req, struct ldb_reply *ares)
840 struct la_context *ac;
841 struct ldb_context *ldb;
842 struct la_op_store *os;
844 ac = talloc_get_type(req->context, struct la_context);
845 ldb = ldb_module_get_ctx(ac->module);
848 return ldb_module_done(ac->req, NULL, NULL,
849 LDB_ERR_OPERATIONS_ERROR);
851 if (ares->error != LDB_SUCCESS) {
852 return ldb_module_done(ac->req, ares->controls,
853 ares->response, ares->error);
856 if (ares->type != LDB_REPLY_DONE) {
857 ldb_set_errstring(ldb,
858 "invalid ldb_reply_type in callback");
860 return ldb_module_done(ac->req, NULL, NULL,
861 LDB_ERR_OPERATIONS_ERROR);
867 DLIST_REMOVE(ac->ops, os);
869 /* this frees the request too
870 * DO NOT access 'req' after this point */
873 return la_do_mod_request(ac);
876 /* Having done the original operation, then try to fix up all the linked attributes for modify and delete */
877 static int la_mod_del_callback(struct ldb_request *req, struct ldb_reply *ares)
880 struct la_context *ac;
881 struct ldb_context *ldb;
883 ac = talloc_get_type(req->context, struct la_context);
884 ldb = ldb_module_get_ctx(ac->module);
887 return ldb_module_done(ac->req, NULL, NULL,
888 LDB_ERR_OPERATIONS_ERROR);
890 if (ares->error != LDB_SUCCESS) {
891 return ldb_module_done(ac->req, ares->controls,
892 ares->response, ares->error);
895 if (ares->type != LDB_REPLY_DONE) {
896 ldb_set_errstring(ldb,
897 "invalid ldb_reply_type in callback");
899 return ldb_module_done(ac->req, NULL, NULL,
900 LDB_ERR_OPERATIONS_ERROR);
903 ac->op_controls = talloc_steal(ac, ares->controls);
904 ac->op_response = talloc_steal(ac, ares->response);
906 /* If we have modfies to make, this is the time to do them for modify and delete */
907 ret = la_do_mod_request(ac);
909 if (ret != LDB_SUCCESS) {
910 return ldb_module_done(ac->req, NULL, NULL, ret);
914 /* la_do_mod_request has already sent the callbacks */
919 /* Having done the original rename try to fix up all the linked attributes */
920 static int la_rename_callback(struct ldb_request *req, struct ldb_reply *ares)
923 struct la_context *ac;
924 struct ldb_request *search_req;
927 struct ldb_context *ldb;
929 ac = talloc_get_type(req->context, struct la_context);
930 ldb = ldb_module_get_ctx(ac->module);
933 return ldb_module_done(ac->req, NULL, NULL,
934 LDB_ERR_OPERATIONS_ERROR);
936 if (ares->error != LDB_SUCCESS) {
937 return ldb_module_done(ac->req, ares->controls,
938 ares->response, ares->error);
941 if (ares->type != LDB_REPLY_DONE) {
942 ldb_set_errstring(ldb,
943 "invalid ldb_reply_type in callback");
945 return ldb_module_done(ac->req, NULL, NULL,
946 LDB_ERR_OPERATIONS_ERROR);
949 werr = dsdb_linked_attribute_lDAPDisplayName_list(ac->schema, ac, &attrs);
950 if (!W_ERROR_IS_OK(werr)) {
951 return LDB_ERR_OPERATIONS_ERROR;
954 ret = ldb_build_search_req(&search_req, ldb, req,
955 ac->req->op.rename.newdn, LDB_SCOPE_BASE,
956 "(objectClass=*)", attrs,
958 ac, la_op_search_callback,
961 if (ret != LDB_SUCCESS) {
965 talloc_steal(search_req, attrs);
967 if (ret == LDB_SUCCESS) {
968 ret = ldb_request_add_control(search_req,
969 LDB_CONTROL_EXTENDED_DN_OID,
972 if (ret != LDB_SUCCESS) {
973 return ldb_module_done(ac->req, NULL, NULL,
977 ac->op_controls = talloc_steal(ac, ares->controls);
978 ac->op_response = talloc_steal(ac, ares->response);
980 return ldb_next_request(ac->module, search_req);
983 /* Having done the original add, then try to fix up all the linked attributes
985 This is done after the add so the links can get the extended DNs correctly.
987 static int la_add_callback(struct ldb_request *req, struct ldb_reply *ares)
990 struct la_context *ac;
991 struct ldb_context *ldb;
993 ac = talloc_get_type(req->context, struct la_context);
994 ldb = ldb_module_get_ctx(ac->module);
997 return ldb_module_done(ac->req, NULL, NULL,
998 LDB_ERR_OPERATIONS_ERROR);
1000 if (ares->error != LDB_SUCCESS) {
1001 return ldb_module_done(ac->req, ares->controls,
1002 ares->response, ares->error);
1005 if (ares->type != LDB_REPLY_DONE) {
1006 ldb_set_errstring(ldb,
1007 "invalid ldb_reply_type in callback");
1009 return ldb_module_done(ac->req, NULL, NULL,
1010 LDB_ERR_OPERATIONS_ERROR);
1014 struct ldb_request *search_req;
1015 static const char *attrs[] = { NULL };
1017 /* The callback does all the hard work here - we need
1018 * the objectGUID and SID of the added record */
1019 ret = ldb_build_search_req(&search_req, ldb, ac,
1020 ac->req->op.add.message->dn,
1022 "(objectClass=*)", attrs,
1024 ac, la_mod_search_callback,
1027 if (ret == LDB_SUCCESS) {
1028 ret = ldb_request_add_control(search_req,
1029 LDB_CONTROL_EXTENDED_DN_OID,
1032 if (ret != LDB_SUCCESS) {
1033 return ldb_module_done(ac->req, NULL, NULL,
1037 ac->op_controls = talloc_steal(ac, ares->controls);
1038 ac->op_response = talloc_steal(ac, ares->response);
1040 return ldb_next_request(ac->module, search_req);
1043 return ldb_module_done(ac->req, ares->controls,
1044 ares->response, ares->error);
1048 /* Reconstruct the original request, but pointing at our local callback to finish things off */
1049 static int la_down_req(struct la_context *ac)
1051 struct ldb_request *down_req;
1053 struct ldb_context *ldb;
1055 ldb = ldb_module_get_ctx(ac->module);
1057 switch (ac->req->operation) {
1059 ret = ldb_build_add_req(&down_req, ldb, ac,
1060 ac->req->op.add.message,
1062 ac, la_add_callback,
1066 ret = ldb_build_mod_req(&down_req, ldb, ac,
1067 ac->req->op.mod.message,
1069 ac, la_mod_del_callback,
1073 ret = ldb_build_del_req(&down_req, ldb, ac,
1076 ac, la_mod_del_callback,
1080 ret = ldb_build_rename_req(&down_req, ldb, ac,
1081 ac->req->op.rename.olddn,
1082 ac->req->op.rename.newdn,
1084 ac, la_rename_callback,
1088 ret = LDB_ERR_OPERATIONS_ERROR;
1090 if (ret != LDB_SUCCESS) {
1094 return ldb_next_request(ac->module, down_req);
1098 _PUBLIC_ 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 .del = linked_attributes_del,
1103 .rename = linked_attributes_rename,