s4:dsdb Don't segfault with ldb_transaction_prepare_commit() without begin()
[Samba/cd1.git] / source4 / dsdb / samdb / ldb_modules / linked_attributes.c
blobbd9af5545e3c8533034cae97d5d5205d82bd417e
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"
35 #include "librpc/gen_ndr/ndr_misc.h"
37 struct la_private {
38 struct la_context *la_list;
41 struct la_op_store {
42 struct la_op_store *next;
43 struct la_op_store *prev;
44 enum la_op {LA_OP_ADD, LA_OP_DEL} op;
45 struct GUID guid;
46 char *name;
47 char *value;
50 struct replace_context {
51 struct la_context *ac;
52 unsigned int num_elements;
53 struct ldb_message_element *el;
56 struct la_context {
57 struct la_context *next, *prev;
58 const struct dsdb_schema *schema;
59 struct ldb_module *module;
60 struct ldb_request *req;
61 struct ldb_dn *partition_dn;
62 struct ldb_dn *add_dn;
63 struct ldb_dn *del_dn;
64 struct replace_context *rc;
65 struct la_op_store *ops;
66 struct ldb_extended *op_response;
67 struct ldb_control **op_controls;
70 static struct la_context *linked_attributes_init(struct ldb_module *module,
71 struct ldb_request *req)
73 struct ldb_context *ldb;
74 struct la_context *ac;
75 const struct ldb_control *partition_ctrl;
77 ldb = ldb_module_get_ctx(module);
79 ac = talloc_zero(req, struct la_context);
80 if (ac == NULL) {
81 ldb_oom(ldb);
82 return NULL;
85 ac->schema = dsdb_get_schema(ldb);
86 ac->module = module;
87 ac->req = req;
89 /* remember the partition DN that came in, if given */
90 partition_ctrl = ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID);
91 if (partition_ctrl) {
92 const struct dsdb_control_current_partition *partition;
93 partition = talloc_get_type(partition_ctrl->data,
94 struct dsdb_control_current_partition);
95 SMB_ASSERT(partition && partition->version == DSDB_CONTROL_CURRENT_PARTITION_VERSION);
97 ac->partition_dn = ldb_dn_copy(ac, partition->dn);
100 return ac;
104 turn a DN into a GUID
106 static int la_guid_from_dn(struct la_context *ac, struct ldb_dn *dn, struct GUID *guid)
108 const struct ldb_val *guid_val;
109 int ret;
111 guid_val = ldb_dn_get_extended_component(dn, "GUID");
112 if (guid_val) {
113 /* there is a GUID embedded in the DN */
114 enum ndr_err_code ndr_err;
115 ndr_err = ndr_pull_struct_blob(guid_val, ac, NULL, guid,
116 (ndr_pull_flags_fn_t)ndr_pull_GUID);
117 if (ndr_err != NDR_ERR_SUCCESS) {
118 DEBUG(0,(__location__ ": Failed to parse GUID\n"));
119 return LDB_ERR_OPERATIONS_ERROR;
121 } else {
122 ret = dsdb_find_guid_by_dn(ldb_module_get_ctx(ac->module), dn, guid);
123 if (ret != LDB_SUCCESS) {
124 DEBUG(4,(__location__ ": Failed to find GUID for dn %s\n",
125 ldb_dn_get_linearized(dn)));
126 return ret;
129 return LDB_SUCCESS;
133 /* Common routine to handle reading the attributes and creating a
134 * series of modify requests */
135 static int la_store_op(struct la_context *ac,
136 enum la_op op, struct ldb_val *dn,
137 const char *name)
139 struct ldb_context *ldb;
140 struct la_op_store *os;
141 struct ldb_dn *op_dn;
142 int ret;
144 ldb = ldb_module_get_ctx(ac->module);
146 op_dn = ldb_dn_from_ldb_val(ac, ldb, dn);
147 if (!op_dn) {
148 ldb_asprintf_errstring(ldb,
149 "could not parse attribute as a DN");
150 return LDB_ERR_INVALID_DN_SYNTAX;
153 os = talloc_zero(ac, struct la_op_store);
154 if (!os) {
155 ldb_oom(ldb);
156 return LDB_ERR_OPERATIONS_ERROR;
159 os->op = op;
161 ret = la_guid_from_dn(ac, op_dn, &os->guid);
162 if (ret == LDB_ERR_NO_SUCH_OBJECT && ac->req->operation == LDB_DELETE) {
163 /* we are deleting an object, and we've found it has a
164 * forward link to a target that no longer
165 * exists. This is not an error in the delete, and we
166 * should just not do the deferred delete of the
167 * target attribute
169 talloc_free(os);
170 return LDB_SUCCESS;
172 if (ret != LDB_SUCCESS) {
173 return ret;
176 os->name = talloc_strdup(os, name);
177 if (!os->name) {
178 ldb_oom(ldb);
179 return LDB_ERR_OPERATIONS_ERROR;
182 /* Do deletes before adds */
183 if (op == LA_OP_ADD) {
184 DLIST_ADD_END(ac->ops, os, struct la_op_store *);
185 } else {
186 /* By adding to the head of the list, we do deletes before
187 * adds when processing a replace */
188 DLIST_ADD(ac->ops, os);
191 return LDB_SUCCESS;
194 static int la_op_search_callback(struct ldb_request *req,
195 struct ldb_reply *ares);
196 static int la_queue_mod_request(struct la_context *ac);
197 static int la_down_req(struct la_context *ac);
201 /* add */
202 static int linked_attributes_add(struct ldb_module *module, struct ldb_request *req)
204 struct ldb_context *ldb;
205 const struct dsdb_attribute *target_attr;
206 struct la_context *ac;
207 const char *attr_name;
208 int ret;
209 int i, j;
211 ldb = ldb_module_get_ctx(module);
213 if (ldb_dn_is_special(req->op.add.message->dn)) {
214 /* do not manipulate our control entries */
215 return ldb_next_request(module, req);
218 ac = linked_attributes_init(module, req);
219 if (!ac) {
220 return LDB_ERR_OPERATIONS_ERROR;
223 if (!ac->schema) {
224 /* without schema, this doesn't make any sense */
225 talloc_free(ac);
226 return ldb_next_request(module, req);
229 /* Need to ensure we only have forward links being specified */
230 for (i=0; i < req->op.add.message->num_elements; i++) {
231 const struct ldb_message_element *el = &req->op.add.message->elements[i];
232 const struct dsdb_attribute *schema_attr
233 = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
234 if (!schema_attr) {
235 ldb_asprintf_errstring(ldb,
236 "attribute %s is not a valid attribute in schema", el->name);
237 return LDB_ERR_OBJECT_CLASS_VIOLATION;
239 /* We have a valid attribute, now find out if it is linked */
240 if (schema_attr->linkID == 0) {
241 continue;
244 if ((schema_attr->linkID & 1) == 1) {
245 /* Odd is for the target. Illegal to modify */
246 ldb_asprintf_errstring(ldb,
247 "attribute %s must not be modified directly, it is a linked attribute", el->name);
248 return LDB_ERR_UNWILLING_TO_PERFORM;
251 /* Even link IDs are for the originating attribute */
252 target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID + 1);
253 if (!target_attr) {
255 * windows 2003 has a broken schema where
256 * the definition of msDS-IsDomainFor
257 * is missing (which is supposed to be
258 * the backlink of the msDS-HasDomainNCs
259 * attribute
261 continue;
264 attr_name = target_attr->lDAPDisplayName;
266 for (j = 0; j < el->num_values; j++) {
267 ret = la_store_op(ac, LA_OP_ADD,
268 &el->values[j],
269 attr_name);
270 if (ret != LDB_SUCCESS) {
271 return ret;
276 /* if no linked attributes are present continue */
277 if (ac->ops == NULL) {
278 /* nothing to do for this module, proceed */
279 talloc_free(ac);
280 return ldb_next_request(module, req);
283 /* start with the original request */
284 return la_down_req(ac);
287 /* For a delete or rename, we need to find out what linked attributes
288 * are currently on this DN, and then deal with them. This is the
289 * callback to the base search */
291 static int la_mod_search_callback(struct ldb_request *req, struct ldb_reply *ares)
293 struct ldb_context *ldb;
294 const struct dsdb_attribute *schema_attr;
295 const struct dsdb_attribute *target_attr;
296 struct ldb_message_element *search_el;
297 struct replace_context *rc;
298 struct la_context *ac;
299 const char *attr_name;
300 int i, j;
301 int ret = LDB_SUCCESS;
303 ac = talloc_get_type(req->context, struct la_context);
304 ldb = ldb_module_get_ctx(ac->module);
305 rc = ac->rc;
307 if (!ares) {
308 return ldb_module_done(ac->req, NULL, NULL,
309 LDB_ERR_OPERATIONS_ERROR);
311 if (ares->error != LDB_SUCCESS) {
312 return ldb_module_done(ac->req, ares->controls,
313 ares->response, ares->error);
316 /* Only entries are interesting, and we only want the olddn */
317 switch (ares->type) {
318 case LDB_REPLY_ENTRY:
320 if (ldb_dn_compare(ares->message->dn, ac->req->op.mod.message->dn) != 0) {
321 ldb_asprintf_errstring(ldb,
322 "linked_attributes: %s is not the DN we were looking for", ldb_dn_get_linearized(ares->message->dn));
323 /* Guh? We only asked for this DN */
324 talloc_free(ares);
325 return ldb_module_done(ac->req, NULL, NULL,
326 LDB_ERR_OPERATIONS_ERROR);
329 ac->add_dn = ac->del_dn = talloc_steal(ac, ares->message->dn);
331 /* We don't populate 'rc' for ADD - it can't be deleting elements anyway */
332 for (i = 0; rc && i < rc->num_elements; i++) {
334 schema_attr = dsdb_attribute_by_lDAPDisplayName(ac->schema, rc->el[i].name);
335 if (!schema_attr) {
336 ldb_asprintf_errstring(ldb,
337 "attribute %s is not a valid attribute in schema",
338 rc->el[i].name);
339 talloc_free(ares);
340 return ldb_module_done(ac->req, NULL, NULL,
341 LDB_ERR_OBJECT_CLASS_VIOLATION);
344 search_el = ldb_msg_find_element(ares->message,
345 rc->el[i].name);
347 /* See if this element already exists */
348 /* otherwise just ignore as
349 * the add has already been scheduled */
350 if ( ! search_el) {
351 continue;
354 target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID + 1);
355 if (!target_attr) {
357 * windows 2003 has a broken schema where
358 * the definition of msDS-IsDomainFor
359 * is missing (which is supposed to be
360 * the backlink of the msDS-HasDomainNCs
361 * attribute
363 continue;
365 attr_name = target_attr->lDAPDisplayName;
367 /* Now we know what was there, we can remove it for the re-add */
368 for (j = 0; j < search_el->num_values; j++) {
369 ret = la_store_op(ac, LA_OP_DEL,
370 &search_el->values[j],
371 attr_name);
372 if (ret != LDB_SUCCESS) {
373 talloc_free(ares);
374 return ldb_module_done(ac->req,
375 NULL, NULL, ret);
380 break;
382 case LDB_REPLY_REFERRAL:
383 /* ignore */
384 break;
386 case LDB_REPLY_DONE:
388 talloc_free(ares);
390 if (ac->req->operation == LDB_ADD) {
391 /* Start the modifies to the backlinks */
392 ret = la_queue_mod_request(ac);
394 if (ret != LDB_SUCCESS) {
395 return ldb_module_done(ac->req, NULL, NULL,
396 ret);
398 } else {
399 /* Start with the original request */
400 ret = la_down_req(ac);
401 if (ret != LDB_SUCCESS) {
402 return ldb_module_done(ac->req, NULL, NULL, ret);
405 return LDB_SUCCESS;
408 talloc_free(ares);
409 return ret;
413 /* modify */
414 static int linked_attributes_modify(struct ldb_module *module, struct ldb_request *req)
416 /* Look over list of modifications */
417 /* Find if any are for linked attributes */
418 /* Determine the effect of the modification */
419 /* Apply the modify to the linked entry */
421 struct ldb_context *ldb;
422 int i, j;
423 struct la_context *ac;
424 struct ldb_request *search_req;
425 const char **attrs;
427 int ret;
429 ldb = ldb_module_get_ctx(module);
431 if (ldb_dn_is_special(req->op.mod.message->dn)) {
432 /* do not manipulate our control entries */
433 return ldb_next_request(module, req);
436 ac = linked_attributes_init(module, req);
437 if (!ac) {
438 return LDB_ERR_OPERATIONS_ERROR;
441 if (!ac->schema) {
442 /* without schema, this doesn't make any sense */
443 return ldb_next_request(module, req);
446 ac->rc = talloc_zero(ac, struct replace_context);
447 if (!ac->rc) {
448 ldb_oom(ldb);
449 return LDB_ERR_OPERATIONS_ERROR;
452 for (i=0; i < req->op.mod.message->num_elements; i++) {
453 bool store_el = false;
454 const char *attr_name;
455 const struct dsdb_attribute *target_attr;
456 const struct ldb_message_element *el = &req->op.mod.message->elements[i];
457 const struct dsdb_attribute *schema_attr
458 = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
459 if (!schema_attr) {
460 ldb_asprintf_errstring(ldb,
461 "attribute %s is not a valid attribute in schema", el->name);
462 return LDB_ERR_OBJECT_CLASS_VIOLATION;
464 /* We have a valid attribute, now find out if it is linked */
465 if (schema_attr->linkID == 0) {
466 continue;
469 if ((schema_attr->linkID & 1) == 1) {
470 /* Odd is for the target. Illegal to modify */
471 ldb_asprintf_errstring(ldb,
472 "attribute %s must not be modified directly, it is a linked attribute", el->name);
473 return LDB_ERR_UNWILLING_TO_PERFORM;
476 /* Even link IDs are for the originating attribute */
478 /* Now find the target attribute */
479 target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID + 1);
480 if (!target_attr) {
482 * windows 2003 has a broken schema where
483 * the definition of msDS-IsDomainFor
484 * is missing (which is supposed to be
485 * the backlink of the msDS-HasDomainNCs
486 * attribute
488 continue;
491 attr_name = target_attr->lDAPDisplayName;
493 switch (el->flags & LDB_FLAG_MOD_MASK) {
494 case LDB_FLAG_MOD_REPLACE:
495 /* treat as just a normal add the delete part is handled by the callback */
496 store_el = true;
498 /* break intentionally missing */
500 case LDB_FLAG_MOD_ADD:
502 /* For each value being added, we need to setup the adds */
503 for (j = 0; j < el->num_values; j++) {
504 ret = la_store_op(ac, LA_OP_ADD,
505 &el->values[j],
506 attr_name);
507 if (ret != LDB_SUCCESS) {
508 return ret;
511 break;
513 case LDB_FLAG_MOD_DELETE:
515 if (el->num_values) {
516 /* For each value being deleted, we need to setup the delete */
517 for (j = 0; j < el->num_values; j++) {
518 ret = la_store_op(ac, LA_OP_DEL,
519 &el->values[j],
520 attr_name);
521 if (ret != LDB_SUCCESS) {
522 return ret;
525 } else {
526 /* Flag that there was a DELETE
527 * without a value specified, so we
528 * need to look for the old value */
529 store_el = true;
532 break;
535 if (store_el) {
536 struct ldb_message_element *search_el;
538 search_el = talloc_realloc(ac->rc, ac->rc->el,
539 struct ldb_message_element,
540 ac->rc->num_elements +1);
541 if (!search_el) {
542 ldb_oom(ldb);
543 return LDB_ERR_OPERATIONS_ERROR;
545 ac->rc->el = search_el;
547 ac->rc->el[ac->rc->num_elements] = *el;
548 ac->rc->num_elements++;
552 if (ac->ops || ac->rc->el) {
553 /* both replace and delete without values are handled in the callback
554 * after the search on the entry to be modified is performed */
556 attrs = talloc_array(ac->rc, const char *, ac->rc->num_elements + 1);
557 if (!attrs) {
558 ldb_oom(ldb);
559 return LDB_ERR_OPERATIONS_ERROR;
561 for (i = 0; ac->rc && i < ac->rc->num_elements; i++) {
562 attrs[i] = ac->rc->el[i].name;
564 attrs[i] = NULL;
566 /* The callback does all the hard work here */
567 ret = ldb_build_search_req(&search_req, ldb, ac,
568 req->op.mod.message->dn,
569 LDB_SCOPE_BASE,
570 "(objectClass=*)", attrs,
571 NULL,
572 ac, la_mod_search_callback,
573 req);
575 /* We need to figure out our own extended DN, to fill in as the backlink target */
576 if (ret == LDB_SUCCESS) {
577 ret = ldb_request_add_control(search_req,
578 LDB_CONTROL_EXTENDED_DN_OID,
579 false, NULL);
581 if (ret == LDB_SUCCESS) {
582 talloc_steal(search_req, attrs);
584 ret = ldb_next_request(module, search_req);
587 } else {
588 /* nothing to do for this module, proceed */
589 talloc_free(ac);
590 ret = ldb_next_request(module, req);
593 return ret;
596 /* delete */
597 static int linked_attributes_del(struct ldb_module *module, struct ldb_request *req)
599 struct ldb_context *ldb;
600 struct ldb_request *search_req;
601 struct la_context *ac;
602 const char **attrs;
603 WERROR werr;
604 int ret;
606 /* This gets complex: We need to:
607 - Do a search for the entry
608 - Wait for these result to appear
609 - In the callback for the result, issue a modify
610 request based on the linked attributes found
611 - Wait for each modify result
612 - Regain our sainity
615 ldb = ldb_module_get_ctx(module);
617 ac = linked_attributes_init(module, req);
618 if (!ac) {
619 return LDB_ERR_OPERATIONS_ERROR;
622 if (!ac->schema) {
623 /* without schema, this doesn't make any sense */
624 return ldb_next_request(module, req);
627 werr = dsdb_linked_attribute_lDAPDisplayName_list(ac->schema, ac, &attrs);
628 if (!W_ERROR_IS_OK(werr)) {
629 return LDB_ERR_OPERATIONS_ERROR;
632 ret = ldb_build_search_req(&search_req, ldb, req,
633 req->op.del.dn, LDB_SCOPE_BASE,
634 "(objectClass=*)", attrs,
635 NULL,
636 ac, la_op_search_callback,
637 req);
639 if (ret != LDB_SUCCESS) {
640 return ret;
643 talloc_steal(search_req, attrs);
645 return ldb_next_request(module, search_req);
648 /* rename */
649 static int linked_attributes_rename(struct ldb_module *module, struct ldb_request *req)
651 struct la_context *ac;
653 /* This gets complex: We need to:
654 - Do a search for the entry
655 - Wait for these result to appear
656 - In the callback for the result, issue a modify
657 request based on the linked attributes found
658 - Wait for each modify result
659 - Regain our sainity
662 ac = linked_attributes_init(module, req);
663 if (!ac) {
664 return LDB_ERR_OPERATIONS_ERROR;
667 if (!ac->schema) {
668 /* without schema, this doesn't make any sense */
669 return ldb_next_request(module, req);
672 /* start with the original request */
673 return la_down_req(ac);
677 static int la_op_search_callback(struct ldb_request *req,
678 struct ldb_reply *ares)
680 struct ldb_context *ldb;
681 struct la_context *ac;
682 const struct dsdb_attribute *schema_attr;
683 const struct dsdb_attribute *target_attr;
684 const struct ldb_message_element *el;
685 const char *attr_name;
686 int i, j;
687 int ret;
689 ac = talloc_get_type(req->context, struct la_context);
690 ldb = ldb_module_get_ctx(ac->module);
692 if (!ares) {
693 return ldb_module_done(ac->req, NULL, NULL,
694 LDB_ERR_OPERATIONS_ERROR);
696 if (ares->error != LDB_SUCCESS) {
697 return ldb_module_done(ac->req, ares->controls,
698 ares->response, ares->error);
701 /* Only entries are interesting, and we only want the olddn */
702 switch (ares->type) {
703 case LDB_REPLY_ENTRY:
704 ret = ldb_dn_compare(ares->message->dn, req->op.search.base);
705 if (ret != 0) {
706 /* Guh? We only asked for this DN */
707 talloc_free(ares);
708 return ldb_module_done(ac->req, NULL, NULL,
709 LDB_ERR_OPERATIONS_ERROR);
711 if (ares->message->num_elements == 0) {
712 /* only bother at all if there were some
713 * linked attributes found */
714 talloc_free(ares);
715 return LDB_SUCCESS;
718 switch (ac->req->operation) {
719 case LDB_DELETE:
720 ac->del_dn = talloc_steal(ac, ares->message->dn);
721 break;
722 case LDB_RENAME:
723 ac->add_dn = talloc_steal(ac, ares->message->dn);
724 ac->del_dn = talloc_steal(ac, ac->req->op.rename.olddn);
725 break;
726 default:
727 talloc_free(ares);
728 ldb_set_errstring(ldb,
729 "operations must be delete or rename");
730 return ldb_module_done(ac->req, NULL, NULL,
731 LDB_ERR_OPERATIONS_ERROR);
734 for (i = 0; i < ares->message->num_elements; i++) {
735 el = &ares->message->elements[i];
737 schema_attr = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
738 if (!schema_attr) {
739 ldb_asprintf_errstring(ldb,
740 "attribute %s is not a valid attribute"
741 " in schema", el->name);
742 talloc_free(ares);
743 return ldb_module_done(ac->req, NULL, NULL,
744 LDB_ERR_OBJECT_CLASS_VIOLATION);
747 /* Valid attribute, now find out if it is linked */
748 if (schema_attr->linkID == 0) {
749 /* Not a linked attribute, skip */
750 continue;
753 if ((schema_attr->linkID & 1) == 0) {
754 /* Odd is for the target. */
755 target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID + 1);
756 if (!target_attr) {
757 continue;
759 attr_name = target_attr->lDAPDisplayName;
760 } else {
761 target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID - 1);
762 if (!target_attr) {
763 continue;
765 attr_name = target_attr->lDAPDisplayName;
767 for (j = 0; j < el->num_values; j++) {
768 ret = la_store_op(ac, LA_OP_DEL,
769 &el->values[j],
770 attr_name);
772 /* for renames, ensure we add it back */
773 if (ret == LDB_SUCCESS
774 && ac->req->operation == LDB_RENAME) {
775 ret = la_store_op(ac, LA_OP_ADD,
776 &el->values[j],
777 attr_name);
779 if (ret != LDB_SUCCESS) {
780 talloc_free(ares);
781 return ldb_module_done(ac->req,
782 NULL, NULL, ret);
787 break;
789 case LDB_REPLY_REFERRAL:
790 /* ignore */
791 break;
793 case LDB_REPLY_DONE:
795 talloc_free(ares);
798 switch (ac->req->operation) {
799 case LDB_DELETE:
800 /* start the mod requests chain */
801 ret = la_down_req(ac);
802 if (ret != LDB_SUCCESS) {
803 return ldb_module_done(ac->req, NULL, NULL, ret);
805 return ret;
807 case LDB_RENAME:
808 /* start the mod requests chain */
809 ret = la_queue_mod_request(ac);
810 if (ret != LDB_SUCCESS) {
811 return ldb_module_done(ac->req, NULL, NULL,
812 ret);
814 return ret;
816 default:
817 talloc_free(ares);
818 ldb_set_errstring(ldb,
819 "operations must be delete or rename");
820 return ldb_module_done(ac->req, NULL, NULL,
821 LDB_ERR_OPERATIONS_ERROR);
825 talloc_free(ares);
826 return LDB_SUCCESS;
829 /* queue a linked attributes modify request in the la_private
830 structure */
831 static int la_queue_mod_request(struct la_context *ac)
833 struct la_private *la_private =
834 talloc_get_type(ldb_module_get_private(ac->module), struct la_private);
836 if (la_private == NULL) {
837 ldb_debug(ldb_module_get_ctx(ac->module), LDB_DEBUG_ERROR, __location__ ": No la_private transaction setup\n");
838 return LDB_ERR_OPERATIONS_ERROR;
841 talloc_steal(la_private, ac);
842 DLIST_ADD(la_private->la_list, ac);
844 return ldb_module_done(ac->req, ac->op_controls,
845 ac->op_response, LDB_SUCCESS);
848 /* Having done the original operation, then try to fix up all the linked attributes for modify and delete */
849 static int la_mod_del_callback(struct ldb_request *req, struct ldb_reply *ares)
851 int ret;
852 struct la_context *ac;
853 struct ldb_context *ldb;
855 ac = talloc_get_type(req->context, struct la_context);
856 ldb = ldb_module_get_ctx(ac->module);
858 if (!ares) {
859 return ldb_module_done(ac->req, NULL, NULL,
860 LDB_ERR_OPERATIONS_ERROR);
862 if (ares->error != LDB_SUCCESS) {
863 return ldb_module_done(ac->req, ares->controls,
864 ares->response, ares->error);
867 if (ares->type != LDB_REPLY_DONE) {
868 ldb_set_errstring(ldb,
869 "invalid ldb_reply_type in callback");
870 talloc_free(ares);
871 return ldb_module_done(ac->req, NULL, NULL,
872 LDB_ERR_OPERATIONS_ERROR);
875 ac->op_controls = talloc_steal(ac, ares->controls);
876 ac->op_response = talloc_steal(ac, ares->response);
878 /* If we have modfies to make, this is the time to do them for modify and delete */
879 ret = la_queue_mod_request(ac);
881 if (ret != LDB_SUCCESS) {
882 return ldb_module_done(ac->req, NULL, NULL, ret);
884 talloc_free(ares);
886 /* la_queue_mod_request has already sent the callbacks */
887 return LDB_SUCCESS;
891 /* Having done the original rename try to fix up all the linked attributes */
892 static int la_rename_callback(struct ldb_request *req, struct ldb_reply *ares)
894 int ret;
895 struct la_context *ac;
896 struct ldb_request *search_req;
897 const char **attrs;
898 WERROR werr;
899 struct ldb_context *ldb;
901 ac = talloc_get_type(req->context, struct la_context);
902 ldb = ldb_module_get_ctx(ac->module);
904 if (!ares) {
905 return ldb_module_done(ac->req, NULL, NULL,
906 LDB_ERR_OPERATIONS_ERROR);
908 if (ares->error != LDB_SUCCESS) {
909 return ldb_module_done(ac->req, ares->controls,
910 ares->response, ares->error);
913 if (ares->type != LDB_REPLY_DONE) {
914 ldb_set_errstring(ldb,
915 "invalid ldb_reply_type in callback");
916 talloc_free(ares);
917 return ldb_module_done(ac->req, NULL, NULL,
918 LDB_ERR_OPERATIONS_ERROR);
921 werr = dsdb_linked_attribute_lDAPDisplayName_list(ac->schema, ac, &attrs);
922 if (!W_ERROR_IS_OK(werr)) {
923 return LDB_ERR_OPERATIONS_ERROR;
926 ret = ldb_build_search_req(&search_req, ldb, req,
927 ac->req->op.rename.newdn, LDB_SCOPE_BASE,
928 "(objectClass=*)", attrs,
929 NULL,
930 ac, la_op_search_callback,
931 req);
933 if (ret != LDB_SUCCESS) {
934 return ret;
937 talloc_steal(search_req, attrs);
939 if (ret == LDB_SUCCESS) {
940 ret = ldb_request_add_control(search_req,
941 LDB_CONTROL_EXTENDED_DN_OID,
942 false, NULL);
944 if (ret != LDB_SUCCESS) {
945 return ldb_module_done(ac->req, NULL, NULL,
946 ret);
949 ac->op_controls = talloc_steal(ac, ares->controls);
950 ac->op_response = talloc_steal(ac, ares->response);
952 return ldb_next_request(ac->module, search_req);
955 /* Having done the original add, then try to fix up all the linked attributes
957 This is done after the add so the links can get the extended DNs correctly.
959 static int la_add_callback(struct ldb_request *req, struct ldb_reply *ares)
961 int ret;
962 struct la_context *ac;
963 struct ldb_context *ldb;
965 ac = talloc_get_type(req->context, struct la_context);
966 ldb = ldb_module_get_ctx(ac->module);
968 if (!ares) {
969 return ldb_module_done(ac->req, NULL, NULL,
970 LDB_ERR_OPERATIONS_ERROR);
972 if (ares->error != LDB_SUCCESS) {
973 return ldb_module_done(ac->req, ares->controls,
974 ares->response, ares->error);
977 if (ares->type != LDB_REPLY_DONE) {
978 ldb_set_errstring(ldb,
979 "invalid ldb_reply_type in callback");
980 talloc_free(ares);
981 return ldb_module_done(ac->req, NULL, NULL,
982 LDB_ERR_OPERATIONS_ERROR);
985 if (ac->ops) {
986 struct ldb_request *search_req;
987 static const char *attrs[] = { NULL };
989 /* The callback does all the hard work here - we need
990 * the objectGUID and SID of the added record */
991 ret = ldb_build_search_req(&search_req, ldb, ac,
992 ac->req->op.add.message->dn,
993 LDB_SCOPE_BASE,
994 "(objectClass=*)", attrs,
995 NULL,
996 ac, la_mod_search_callback,
997 ac->req);
999 if (ret == LDB_SUCCESS) {
1000 ret = ldb_request_add_control(search_req,
1001 LDB_CONTROL_EXTENDED_DN_OID,
1002 false, NULL);
1004 if (ret != LDB_SUCCESS) {
1005 return ldb_module_done(ac->req, NULL, NULL,
1006 ret);
1009 ac->op_controls = talloc_steal(ac, ares->controls);
1010 ac->op_response = talloc_steal(ac, ares->response);
1012 return ldb_next_request(ac->module, search_req);
1014 } else {
1015 return ldb_module_done(ac->req, ares->controls,
1016 ares->response, ares->error);
1020 /* Reconstruct the original request, but pointing at our local callback to finish things off */
1021 static int la_down_req(struct la_context *ac)
1023 struct ldb_request *down_req;
1024 int ret;
1025 struct ldb_context *ldb;
1027 ldb = ldb_module_get_ctx(ac->module);
1029 switch (ac->req->operation) {
1030 case LDB_ADD:
1031 ret = ldb_build_add_req(&down_req, ldb, ac,
1032 ac->req->op.add.message,
1033 ac->req->controls,
1034 ac, la_add_callback,
1035 ac->req);
1036 break;
1037 case LDB_MODIFY:
1038 ret = ldb_build_mod_req(&down_req, ldb, ac,
1039 ac->req->op.mod.message,
1040 ac->req->controls,
1041 ac, la_mod_del_callback,
1042 ac->req);
1043 break;
1044 case LDB_DELETE:
1045 ret = ldb_build_del_req(&down_req, ldb, ac,
1046 ac->req->op.del.dn,
1047 ac->req->controls,
1048 ac, la_mod_del_callback,
1049 ac->req);
1050 break;
1051 case LDB_RENAME:
1052 ret = ldb_build_rename_req(&down_req, ldb, ac,
1053 ac->req->op.rename.olddn,
1054 ac->req->op.rename.newdn,
1055 ac->req->controls,
1056 ac, la_rename_callback,
1057 ac->req);
1058 break;
1059 default:
1060 ret = LDB_ERR_OPERATIONS_ERROR;
1062 if (ret != LDB_SUCCESS) {
1063 return ret;
1066 return ldb_next_request(ac->module, down_req);
1070 use the GUID part of an extended DN to find the target DN, in case
1071 it has moved
1073 static int la_find_dn_target(struct ldb_module *module, struct la_context *ac,
1074 struct GUID *guid, struct ldb_dn **dn)
1076 return dsdb_find_dn_by_guid(ldb_module_get_ctx(ac->module), ac, GUID_string(ac, guid), dn);
1079 /* apply one la_context op change */
1080 static int la_do_op_request(struct ldb_module *module, struct la_context *ac, struct la_op_store *op)
1082 struct ldb_message_element *ret_el;
1083 struct ldb_request *mod_req;
1084 struct ldb_message *new_msg;
1085 struct ldb_context *ldb;
1086 int ret;
1088 ldb = ldb_module_get_ctx(ac->module);
1090 /* Create the modify request */
1091 new_msg = ldb_msg_new(ac);
1092 if (!new_msg) {
1093 ldb_oom(ldb);
1094 return LDB_ERR_OPERATIONS_ERROR;
1097 ret = la_find_dn_target(module, ac, &op->guid, &new_msg->dn);
1098 if (ret != LDB_SUCCESS) {
1099 return ret;
1102 if (op->op == LA_OP_ADD) {
1103 ret = ldb_msg_add_empty(new_msg, op->name,
1104 LDB_FLAG_MOD_ADD, &ret_el);
1105 } else {
1106 ret = ldb_msg_add_empty(new_msg, op->name,
1107 LDB_FLAG_MOD_DELETE, &ret_el);
1109 if (ret != LDB_SUCCESS) {
1110 return ret;
1112 ret_el->values = talloc_array(new_msg, struct ldb_val, 1);
1113 if (!ret_el->values) {
1114 ldb_oom(ldb);
1115 return LDB_ERR_OPERATIONS_ERROR;
1117 ret_el->num_values = 1;
1118 if (op->op == LA_OP_ADD) {
1119 ret_el->values[0] = data_blob_string_const(ldb_dn_get_extended_linearized(new_msg, ac->add_dn, 1));
1120 } else {
1121 ret_el->values[0] = data_blob_string_const(ldb_dn_get_extended_linearized(new_msg, ac->del_dn, 1));
1124 #if 0
1125 ldb_debug(ldb, LDB_DEBUG_WARNING,
1126 "link on %s %s: %s %s\n",
1127 ldb_dn_get_linearized(new_msg->dn), ret_el->name,
1128 ret_el->values[0].data, ac->ops->op == LA_OP_ADD ? "added" : "deleted");
1129 #endif
1131 ret = ldb_build_mod_req(&mod_req, ldb, op,
1132 new_msg,
1133 NULL,
1134 NULL,
1135 ldb_op_default_callback,
1136 NULL);
1137 if (ret != LDB_SUCCESS) {
1138 return ret;
1140 talloc_steal(mod_req, new_msg);
1142 if (DEBUGLVL(4)) {
1143 DEBUG(4,("Applying linked attribute change:\n%s\n",
1144 ldb_ldif_message_string(ldb, op, LDB_CHANGETYPE_MODIFY, new_msg)));
1147 /* Run the new request */
1148 ret = ldb_next_request(module, mod_req);
1150 /* we need to wait for this to finish, as we are being called
1151 from the synchronous end_transaction hook of this module */
1152 if (ret == LDB_SUCCESS) {
1153 ret = ldb_wait(mod_req->handle, LDB_WAIT_ALL);
1156 if (ret != LDB_SUCCESS) {
1157 ldb_debug(ldb, LDB_DEBUG_WARNING, "Failed to apply linked attribute change '%s' %s\n",
1158 ldb_errstring(ldb),
1159 ldb_ldif_message_string(ldb, op, LDB_CHANGETYPE_MODIFY, new_msg));
1162 return ret;
1165 /* apply one set of la_context changes */
1166 static int la_do_mod_request(struct ldb_module *module, struct la_context *ac)
1168 struct la_op_store *op;
1170 for (op = ac->ops; op; op=op->next) {
1171 int ret = la_do_op_request(module, ac, op);
1172 if (ret != LDB_SUCCESS) {
1173 if (ret != LDB_ERR_NO_SUCH_OBJECT) {
1174 return ret;
1179 return LDB_SUCCESS;
1184 we hook into the transaction operations to allow us to
1185 perform the linked attribute updates at the end of the whole
1186 transaction. This allows a forward linked attribute to be created
1187 before the target is created, as long as the target is created
1188 in the same transaction
1190 static int linked_attributes_start_transaction(struct ldb_module *module)
1192 /* create our private structure for this transaction */
1193 struct la_private *la_private = talloc_get_type(ldb_module_get_private(module),
1194 struct la_private);
1195 talloc_free(la_private);
1196 la_private = talloc(module, struct la_private);
1197 if (la_private == NULL) {
1198 return LDB_ERR_OPERATIONS_ERROR;
1200 la_private->la_list = NULL;
1201 ldb_module_set_private(module, la_private);
1202 return ldb_next_start_trans(module);
1206 on prepare commit we loop over our queued la_context structures
1207 and apply each of them
1209 static int linked_attributes_prepare_commit(struct ldb_module *module)
1211 struct la_private *la_private =
1212 talloc_get_type(ldb_module_get_private(module), struct la_private);
1213 struct la_context *ac;
1215 if (!la_private) {
1216 /* prepare commit without begin_transaction - let someone else return the error, just don't segfault */
1217 return ldb_next_prepare_commit(module);
1219 /* walk the list backwards, to do the first entry first, as we
1220 * added the entries with DLIST_ADD() which puts them at the
1221 * start of the list */
1222 for (ac = la_private->la_list; ac && ac->next; ac=ac->next) ;
1224 for (; ac; ac=ac->prev) {
1225 int ret;
1226 ac->req = NULL;
1227 ret = la_do_mod_request(module, ac);
1228 if (ret != LDB_SUCCESS) {
1229 DEBUG(0,(__location__ ": Failed mod request ret=%d\n", ret));
1230 talloc_free(la_private);
1231 ldb_module_set_private(module, NULL);
1232 return ret;
1236 talloc_free(la_private);
1237 ldb_module_set_private(module, NULL);
1239 return ldb_next_prepare_commit(module);
1242 static int linked_attributes_del_transaction(struct ldb_module *module)
1244 struct la_private *la_private =
1245 talloc_get_type(ldb_module_get_private(module), struct la_private);
1246 talloc_free(la_private);
1247 ldb_module_set_private(module, NULL);
1248 return ldb_next_del_trans(module);
1252 _PUBLIC_ const struct ldb_module_ops ldb_linked_attributes_module_ops = {
1253 .name = "linked_attributes",
1254 .add = linked_attributes_add,
1255 .modify = linked_attributes_modify,
1256 .del = linked_attributes_del,
1257 .rename = linked_attributes_rename,
1258 .start_transaction = linked_attributes_start_transaction,
1259 .prepare_commit = linked_attributes_prepare_commit,
1260 .del_transaction = linked_attributes_del_transaction,