[SAMBA 4] Some cosmetic changes for the LDB modules
[Samba/ekacnet.git] / source4 / dsdb / samdb / ldb_modules / linked_attributes.c
blob9ed06a91309cadf1d9ef16e09e651a18388ca247
1 /*
2 ldb database library
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/>.
22 * Name: ldb
24 * Component: ldb linked_attributes module
26 * Description: Module to ensure linked attribute pairs remain in sync
28 * Author: Andrew Bartlett
31 #include "includes.h"
32 #include "ldb_module.h"
33 #include "dlinklist.h"
34 #include "dsdb/samdb/samdb.h"
36 struct la_op_store {
37 struct la_op_store *next;
38 struct la_op_store *prev;
39 enum la_op {LA_OP_ADD, LA_OP_DEL} op;
40 struct ldb_dn *dn;
41 char *name;
42 char *value;
45 struct replace_context {
46 struct la_context *ac;
47 unsigned int num_elements;
48 struct ldb_message_element *el;
51 struct la_context {
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);
72 if (ac == NULL) {
73 ldb_oom(ldb);
74 return NULL;
77 ac->schema = dsdb_get_schema(ldb);
78 ac->module = module;
79 ac->req = req;
81 return ac;
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,
88 const char *name)
90 struct ldb_context *ldb;
91 struct la_op_store *os;
92 struct ldb_dn *op_dn;
94 ldb = ldb_module_get_ctx(ac->module);
96 op_dn = ldb_dn_from_ldb_val(ac, ldb, dn);
97 if (!op_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);
104 if (!os) {
105 ldb_oom(ldb);
106 return LDB_ERR_OPERATIONS_ERROR;
109 os->op = op;
111 os->dn = talloc_steal(os, op_dn);
113 os->name = talloc_strdup(os, name);
114 if (!os->name) {
115 ldb_oom(ldb);
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 *);
122 } else {
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);
128 return LDB_SUCCESS;
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);
140 /* add */
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;
147 int ret;
148 int i, j;
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);
158 if (!ac) {
159 return LDB_ERR_OPERATIONS_ERROR;
162 if (!ac->schema) {
163 /* without schema, this doesn't make any sense */
164 talloc_free(ac);
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);
173 if (!schema_attr) {
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) {
180 continue;
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);
192 if (!target_attr) {
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
198 * attribute
200 continue;
203 attr_name = target_attr->lDAPDisplayName;
205 for (j = 0; j < el->num_values; j++) {
206 ret = la_store_op(ac, LA_OP_ADD,
207 &el->values[j],
208 attr_name);
209 if (ret != LDB_SUCCESS) {
210 return ret;
215 /* if no linked attributes are present continue */
216 if (ac->ops == NULL) {
217 /* nothing to do for this module, proceed */
218 talloc_free(ac);
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;
239 int i, j;
240 int ret = LDB_SUCCESS;
242 ac = talloc_get_type(req->context, struct la_context);
243 ldb = ldb_module_get_ctx(ac->module);
244 rc = ac->rc;
246 if (!ares) {
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 */
263 talloc_free(ares);
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);
274 if (!schema_attr) {
275 ldb_asprintf_errstring(ldb,
276 "attribute %s is not a valid attribute in schema",
277 rc->el[i].name);
278 talloc_free(ares);
279 return ldb_module_done(ac->req, NULL, NULL,
280 LDB_ERR_OBJECT_CLASS_VIOLATION);
283 search_el = ldb_msg_find_element(ares->message,
284 rc->el[i].name);
286 /* See if this element already exists */
287 /* otherwise just ignore as
288 * the add has already been scheduled */
289 if ( ! search_el) {
290 continue;
293 target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID + 1);
294 if (!target_attr) {
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
300 * attribute
302 continue;
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],
310 attr_name);
311 if (ret != LDB_SUCCESS) {
312 talloc_free(ares);
313 return ldb_module_done(ac->req,
314 NULL, NULL, ret);
319 break;
321 case LDB_REPLY_REFERRAL:
322 /* ignore */
323 break;
325 case LDB_REPLY_DONE:
327 talloc_free(ares);
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,
335 ret);
337 } else {
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);
344 return LDB_SUCCESS;
347 talloc_free(ares);
348 return ret;
352 /* modify */
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;
361 int i, j;
362 struct la_context *ac;
363 struct ldb_request *search_req;
364 const char **attrs;
366 int ret;
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);
376 if (!ac) {
377 return LDB_ERR_OPERATIONS_ERROR;
380 if (!ac->schema) {
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);
386 if (!ac->rc) {
387 ldb_oom(ldb);
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);
398 if (!schema_attr) {
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) {
405 continue;
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);
419 if (!target_attr) {
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
425 * attribute
427 continue;
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 */
435 store_el = true;
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,
444 &el->values[j],
445 attr_name);
446 if (ret != LDB_SUCCESS) {
447 return ret;
450 break;
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,
458 &el->values[j],
459 attr_name);
460 if (ret != LDB_SUCCESS) {
461 return ret;
464 } else {
465 /* Flag that there was a DELETE
466 * without a value specified, so we
467 * need to look for the old value */
468 store_el = true;
471 break;
474 if (store_el) {
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);
480 if (!search_el) {
481 ldb_oom(ldb);
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);
496 if (!attrs) {
497 ldb_oom(ldb);
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;
503 attrs[i] = NULL;
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,
508 LDB_SCOPE_BASE,
509 "(objectClass=*)", attrs,
510 NULL,
511 ac, la_mod_search_callback,
512 req);
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,
518 false, NULL);
520 if (ret == LDB_SUCCESS) {
521 talloc_steal(search_req, attrs);
523 ret = ldb_next_request(module, search_req);
526 } else {
527 /* nothing to do for this module, proceed */
528 talloc_free(ac);
529 ret = ldb_next_request(module, req);
532 return ret;
535 /* delete, rename */
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;
541 const char **attrs;
542 WERROR werr;
543 int ret;
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
551 - Regain our sainity
554 ldb = ldb_module_get_ctx(module);
556 ac = linked_attributes_init(module, req);
557 if (!ac) {
558 return LDB_ERR_OPERATIONS_ERROR;
561 if (!ac->schema) {
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,
574 NULL,
575 ac, la_op_search_callback,
576 req);
578 if (ret != LDB_SUCCESS) {
579 return ret;
582 talloc_steal(search_req, attrs);
584 return ldb_next_request(module, search_req);
587 /* delete, rename */
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
598 - Regain our sainity
601 ac = linked_attributes_init(module, req);
602 if (!ac) {
603 return LDB_ERR_OPERATIONS_ERROR;
606 if (!ac->schema) {
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;
625 int i, j;
626 int ret;
628 ac = talloc_get_type(req->context, struct la_context);
629 ldb = ldb_module_get_ctx(ac->module);
631 if (!ares) {
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);
644 if (ret != 0) {
645 /* Guh? We only asked for this DN */
646 talloc_free(ares);
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 */
653 talloc_free(ares);
654 return LDB_SUCCESS;
657 switch (ac->req->operation) {
658 case LDB_DELETE:
659 ac->del_dn = talloc_steal(ac, ares->message->dn);
660 break;
661 case LDB_RENAME:
662 ac->add_dn = talloc_steal(ac, ares->message->dn);
663 ac->del_dn = talloc_steal(ac, ac->req->op.rename.olddn);
664 break;
665 default:
666 talloc_free(ares);
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);
677 if (!schema_attr) {
678 ldb_asprintf_errstring(ldb,
679 "attribute %s is not a valid attribute"
680 " in schema", el->name);
681 talloc_free(ares);
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 */
689 continue;
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);
695 if (!target_attr) {
696 continue;
698 attr_name = target_attr->lDAPDisplayName;
699 } else {
700 target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID - 1);
701 if (!target_attr) {
702 continue;
704 attr_name = target_attr->lDAPDisplayName;
706 for (j = 0; j < el->num_values; j++) {
707 ret = la_store_op(ac, LA_OP_DEL,
708 &el->values[j],
709 attr_name);
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,
715 &el->values[j],
716 attr_name);
718 if (ret != LDB_SUCCESS) {
719 talloc_free(ares);
720 return ldb_module_done(ac->req,
721 NULL, NULL, ret);
726 break;
728 case LDB_REPLY_REFERRAL:
729 /* ignore */
730 break;
732 case LDB_REPLY_DONE:
734 talloc_free(ares);
737 switch (ac->req->operation) {
738 case LDB_DELETE:
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);
744 return ret;
746 case LDB_RENAME:
747 /* start the mod requests chain */
748 ret = la_do_mod_request(ac);
749 if (ret != LDB_SUCCESS) {
750 return ldb_module_done(ac->req, NULL, NULL,
751 ret);
753 return ret;
755 default:
756 talloc_free(ares);
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);
764 talloc_free(ares);
765 return LDB_SUCCESS;
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;
775 int ret;
777 /* If we have no modifies in the queue, we are done! */
778 if (!ac->ops) {
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);
787 if (!new_msg) {
788 ldb_oom(ldb);
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);
796 } else {
797 ret = ldb_msg_add_empty(new_msg, ac->ops->name,
798 LDB_FLAG_MOD_DELETE, &ret_el);
800 if (ret != LDB_SUCCESS) {
801 return ret;
803 ret_el->values = talloc_array(new_msg, struct ldb_val, 1);
804 if (!ret_el->values) {
805 ldb_oom(ldb);
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));
811 } else {
812 ret_el->values[0] = data_blob_string_const(ldb_dn_get_extended_linearized(new_msg, ac->del_dn, 1));
815 #if 0
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");
820 #endif
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,
825 new_msg,
826 NULL,
827 ac, la_mod_callback,
828 ac->req);
829 if (ret != LDB_SUCCESS) {
830 return ret;
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);
847 if (!ares) {
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");
859 talloc_free(ares);
860 return ldb_module_done(ac->req, NULL, NULL,
861 LDB_ERR_OPERATIONS_ERROR);
864 talloc_free(ares);
866 os = ac->ops;
867 DLIST_REMOVE(ac->ops, os);
869 /* this frees the request too
870 * DO NOT access 'req' after this point */
871 talloc_free(os);
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)
879 int ret;
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);
886 if (!ares) {
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");
898 talloc_free(ares);
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);
912 talloc_free(ares);
914 /* la_do_mod_request has already sent the callbacks */
915 return LDB_SUCCESS;
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)
922 int ret;
923 struct la_context *ac;
924 struct ldb_request *search_req;
925 const char **attrs;
926 WERROR werr;
927 struct ldb_context *ldb;
929 ac = talloc_get_type(req->context, struct la_context);
930 ldb = ldb_module_get_ctx(ac->module);
932 if (!ares) {
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");
944 talloc_free(ares);
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,
957 NULL,
958 ac, la_op_search_callback,
959 req);
961 if (ret != LDB_SUCCESS) {
962 return ret;
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,
970 false, NULL);
972 if (ret != LDB_SUCCESS) {
973 return ldb_module_done(ac->req, NULL, NULL,
974 ret);
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)
989 int ret;
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);
996 if (!ares) {
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");
1008 talloc_free(ares);
1009 return ldb_module_done(ac->req, NULL, NULL,
1010 LDB_ERR_OPERATIONS_ERROR);
1013 if (ac->ops) {
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,
1021 LDB_SCOPE_BASE,
1022 "(objectClass=*)", attrs,
1023 NULL,
1024 ac, la_mod_search_callback,
1025 ac->req);
1027 if (ret == LDB_SUCCESS) {
1028 ret = ldb_request_add_control(search_req,
1029 LDB_CONTROL_EXTENDED_DN_OID,
1030 false, NULL);
1032 if (ret != LDB_SUCCESS) {
1033 return ldb_module_done(ac->req, NULL, NULL,
1034 ret);
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);
1042 } else {
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;
1052 int ret;
1053 struct ldb_context *ldb;
1055 ldb = ldb_module_get_ctx(ac->module);
1057 switch (ac->req->operation) {
1058 case LDB_ADD:
1059 ret = ldb_build_add_req(&down_req, ldb, ac,
1060 ac->req->op.add.message,
1061 ac->req->controls,
1062 ac, la_add_callback,
1063 ac->req);
1064 break;
1065 case LDB_MODIFY:
1066 ret = ldb_build_mod_req(&down_req, ldb, ac,
1067 ac->req->op.mod.message,
1068 ac->req->controls,
1069 ac, la_mod_del_callback,
1070 ac->req);
1071 break;
1072 case LDB_DELETE:
1073 ret = ldb_build_del_req(&down_req, ldb, ac,
1074 ac->req->op.del.dn,
1075 ac->req->controls,
1076 ac, la_mod_del_callback,
1077 ac->req);
1078 break;
1079 case LDB_RENAME:
1080 ret = ldb_build_rename_req(&down_req, ldb, ac,
1081 ac->req->op.rename.olddn,
1082 ac->req->op.rename.newdn,
1083 ac->req->controls,
1084 ac, la_rename_callback,
1085 ac->req);
1086 break;
1087 default:
1088 ret = LDB_ERR_OPERATIONS_ERROR;
1090 if (ret != LDB_SUCCESS) {
1091 return ret;
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,