s4-linked_attributes: Give more info where an error occured
[Samba.git] / source4 / dsdb / samdb / ldb_modules / linked_attributes.c
blobebbc7c609cdd917fd70a70e7a54fcf259cbd066c
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 "util/dlinklist.h"
34 #include "dsdb/samdb/samdb.h"
35 #include "librpc/gen_ndr/ndr_misc.h"
36 #include "dsdb/samdb/ldb_modules/util.h"
38 struct la_private {
39 struct la_context *la_list;
42 struct la_op_store {
43 struct la_op_store *next;
44 struct la_op_store *prev;
45 enum la_op {LA_OP_ADD, LA_OP_DEL} op;
46 struct GUID guid;
47 char *name;
48 char *value;
51 struct replace_context {
52 struct la_context *ac;
53 unsigned int num_elements;
54 struct ldb_message_element *el;
57 struct la_context {
58 struct la_context *next, *prev;
59 const struct dsdb_schema *schema;
60 struct ldb_module *module;
61 struct ldb_request *req;
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;
76 ldb = ldb_module_get_ctx(module);
78 ac = talloc_zero(req, struct la_context);
79 if (ac == NULL) {
80 ldb_oom(ldb);
81 return NULL;
84 ac->schema = dsdb_get_schema(ldb, ac);
85 ac->module = module;
86 ac->req = req;
88 return ac;
92 turn a DN into a GUID
94 static int la_guid_from_dn(struct la_context *ac, struct ldb_dn *dn, struct GUID *guid)
96 NTSTATUS status;
97 int ret;
99 status = dsdb_get_extended_dn_guid(dn, guid, "GUID");
100 if (NT_STATUS_IS_OK(status)) {
101 return LDB_SUCCESS;
103 if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
104 DEBUG(4,(__location__ ": Unable to parse GUID for dn %s\n",
105 ldb_dn_get_linearized(dn)));
106 return ldb_operr(ldb_module_get_ctx(ac->module));
109 ret = dsdb_find_guid_by_dn(ldb_module_get_ctx(ac->module), dn, guid);
110 if (ret != LDB_SUCCESS) {
111 DEBUG(4,(__location__ ": Failed to find GUID for dn %s\n",
112 ldb_dn_get_linearized(dn)));
113 return ret;
115 return LDB_SUCCESS;
119 /* Common routine to handle reading the attributes and creating a
120 * series of modify requests */
121 static int la_store_op(struct la_context *ac,
122 enum la_op op, struct ldb_val *dn,
123 const char *name)
125 struct ldb_context *ldb;
126 struct la_op_store *os;
127 struct ldb_dn *op_dn;
128 int ret;
130 ldb = ldb_module_get_ctx(ac->module);
132 op_dn = ldb_dn_from_ldb_val(ac, ldb, dn);
133 if (!op_dn) {
134 ldb_asprintf_errstring(ldb,
135 "could not parse attribute as a DN");
136 return LDB_ERR_INVALID_DN_SYNTAX;
139 os = talloc_zero(ac, struct la_op_store);
140 if (!os) {
141 return ldb_oom(ldb);
144 os->op = op;
146 ret = la_guid_from_dn(ac, op_dn, &os->guid);
147 talloc_free(op_dn);
148 if (ret == LDB_ERR_NO_SUCH_OBJECT && ac->req->operation == LDB_DELETE) {
149 /* we are deleting an object, and we've found it has a
150 * forward link to a target that no longer
151 * exists. This is not an error in the delete, and we
152 * should just not do the deferred delete of the
153 * target attribute
155 talloc_free(os);
156 return LDB_SUCCESS;
158 if (ret != LDB_SUCCESS) {
159 return ret;
162 os->name = talloc_strdup(os, name);
163 if (!os->name) {
164 return ldb_oom(ldb);
167 /* Do deletes before adds */
168 if (op == LA_OP_ADD) {
169 DLIST_ADD_END(ac->ops, os, struct la_op_store *);
170 } else {
171 /* By adding to the head of the list, we do deletes before
172 * adds when processing a replace */
173 DLIST_ADD(ac->ops, os);
176 return LDB_SUCCESS;
179 static int la_queue_mod_request(struct la_context *ac);
180 static int la_down_req(struct la_context *ac);
184 /* add */
185 static int linked_attributes_add(struct ldb_module *module, struct ldb_request *req)
187 struct ldb_context *ldb;
188 const struct dsdb_attribute *target_attr;
189 struct la_context *ac;
190 const char *attr_name;
191 struct ldb_control *ctrl;
192 unsigned int i, j;
193 int ret;
195 ldb = ldb_module_get_ctx(module);
197 if (ldb_dn_is_special(req->op.add.message->dn)) {
198 /* do not manipulate our control entries */
199 return ldb_next_request(module, req);
202 if (!(ctrl = ldb_request_get_control(req, DSDB_CONTROL_APPLY_LINKS))) {
203 /* don't do anything special for linked attributes, repl_meta_data has done it */
204 return ldb_next_request(module, req);
206 ctrl->critical = false;
208 ac = linked_attributes_init(module, req);
209 if (!ac) {
210 return ldb_operr(ldb);
213 if (!ac->schema) {
214 /* without schema, this doesn't make any sense */
215 talloc_free(ac);
216 return ldb_next_request(module, req);
219 /* Need to ensure we only have forward links being specified */
220 for (i=0; i < req->op.add.message->num_elements; i++) {
221 const struct ldb_message_element *el = &req->op.add.message->elements[i];
222 const struct dsdb_attribute *schema_attr
223 = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
224 if (!schema_attr) {
225 ldb_asprintf_errstring(ldb,
226 "%s: attribute %s is not a valid attribute in schema",
227 __FUNCTION__,
228 el->name);
229 return LDB_ERR_OBJECT_CLASS_VIOLATION;
231 /* We have a valid attribute, now find out if it is a forward link */
232 if ((schema_attr->linkID == 0)) {
233 continue;
236 if ((schema_attr->linkID & 1) == 1) {
237 unsigned int functional_level;
239 functional_level = dsdb_functional_level(ldb);
240 SMB_ASSERT(functional_level > DS_DOMAIN_FUNCTION_2000);
243 /* Even link IDs are for the originating attribute */
244 target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID + 1);
245 if (!target_attr) {
247 * windows 2003 has a broken schema where
248 * the definition of msDS-IsDomainFor
249 * is missing (which is supposed to be
250 * the backlink of the msDS-HasDomainNCs
251 * attribute
253 continue;
256 attr_name = target_attr->lDAPDisplayName;
258 for (j = 0; j < el->num_values; j++) {
259 ret = la_store_op(ac, LA_OP_ADD,
260 &el->values[j],
261 attr_name);
262 if (ret != LDB_SUCCESS) {
263 return ret;
268 /* if no linked attributes are present continue */
269 if (ac->ops == NULL) {
270 /* nothing to do for this module, proceed */
271 talloc_free(ac);
272 return ldb_next_request(module, req);
275 /* start with the original request */
276 return la_down_req(ac);
279 /* For a delete or rename, we need to find out what linked attributes
280 * are currently on this DN, and then deal with them. This is the
281 * callback to the base search */
283 static int la_mod_search_callback(struct ldb_request *req, struct ldb_reply *ares)
285 struct ldb_context *ldb;
286 const struct dsdb_attribute *schema_attr;
287 const struct dsdb_attribute *target_attr;
288 struct ldb_message_element *search_el;
289 struct replace_context *rc;
290 struct la_context *ac;
291 const char *attr_name;
292 unsigned int i, j;
293 int ret = LDB_SUCCESS;
295 ac = talloc_get_type(req->context, struct la_context);
296 ldb = ldb_module_get_ctx(ac->module);
297 rc = ac->rc;
299 if (!ares) {
300 return ldb_module_done(ac->req, NULL, NULL,
301 LDB_ERR_OPERATIONS_ERROR);
303 if (ares->error != LDB_SUCCESS) {
304 return ldb_module_done(ac->req, ares->controls,
305 ares->response, ares->error);
308 /* Only entries are interesting, and we only want the olddn */
309 switch (ares->type) {
310 case LDB_REPLY_ENTRY:
312 if (ldb_dn_compare(ares->message->dn, ac->req->op.mod.message->dn) != 0) {
313 ldb_asprintf_errstring(ldb,
314 "linked_attributes: %s is not the DN we were looking for",
315 ldb_dn_get_linearized(ares->message->dn));
316 /* Guh? We only asked for this DN */
317 talloc_free(ares);
318 return ldb_module_done(ac->req, NULL, NULL,
319 LDB_ERR_OPERATIONS_ERROR);
322 ac->add_dn = ac->del_dn = talloc_steal(ac, ares->message->dn);
324 /* We don't populate 'rc' for ADD - it can't be deleting elements anyway */
325 for (i = 0; rc && i < rc->num_elements; i++) {
327 schema_attr = dsdb_attribute_by_lDAPDisplayName(ac->schema, rc->el[i].name);
328 if (!schema_attr) {
329 ldb_asprintf_errstring(ldb,
330 "%s: attribute %s is not a valid attribute in schema",
331 __FUNCTION__,
332 rc->el[i].name);
333 talloc_free(ares);
334 return ldb_module_done(ac->req, NULL, NULL,
335 LDB_ERR_OBJECT_CLASS_VIOLATION);
338 search_el = ldb_msg_find_element(ares->message,
339 rc->el[i].name);
341 /* See if this element already exists */
342 /* otherwise just ignore as
343 * the add has already been scheduled */
344 if ( ! search_el) {
345 continue;
348 target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID + 1);
349 if (!target_attr) {
351 * windows 2003 has a broken schema where
352 * the definition of msDS-IsDomainFor
353 * is missing (which is supposed to be
354 * the backlink of the msDS-HasDomainNCs
355 * attribute
357 continue;
359 attr_name = target_attr->lDAPDisplayName;
361 /* Now we know what was there, we can remove it for the re-add */
362 for (j = 0; j < search_el->num_values; j++) {
363 ret = la_store_op(ac, LA_OP_DEL,
364 &search_el->values[j],
365 attr_name);
366 if (ret != LDB_SUCCESS) {
367 talloc_free(ares);
368 return ldb_module_done(ac->req,
369 NULL, NULL, ret);
374 break;
376 case LDB_REPLY_REFERRAL:
377 /* ignore */
378 break;
380 case LDB_REPLY_DONE:
382 talloc_free(ares);
384 if (ac->req->operation == LDB_ADD) {
385 /* Start the modifies to the backlinks */
386 ret = la_queue_mod_request(ac);
388 if (ret != LDB_SUCCESS) {
389 return ldb_module_done(ac->req, NULL, NULL,
390 ret);
392 } else {
393 /* Start with the original request */
394 ret = la_down_req(ac);
395 if (ret != LDB_SUCCESS) {
396 return ldb_module_done(ac->req, NULL, NULL, ret);
399 return LDB_SUCCESS;
402 talloc_free(ares);
403 return ret;
407 /* modify */
408 static int linked_attributes_modify(struct ldb_module *module, struct ldb_request *req)
410 /* Look over list of modifications */
411 /* Find if any are for linked attributes */
412 /* Determine the effect of the modification */
413 /* Apply the modify to the linked entry */
415 struct ldb_context *ldb;
416 unsigned int i, j;
417 struct la_context *ac;
418 struct ldb_request *search_req;
419 const char **attrs;
420 struct ldb_control *ctrl;
421 int ret;
423 ldb = ldb_module_get_ctx(module);
425 if (ldb_dn_is_special(req->op.mod.message->dn)) {
426 /* do not manipulate our control entries */
427 return ldb_next_request(module, req);
430 if (!(ctrl = ldb_request_get_control(req, DSDB_CONTROL_APPLY_LINKS))) {
431 /* don't do anything special for linked attributes, repl_meta_data has done it */
432 return ldb_next_request(module, req);
434 ctrl->critical = false;
436 ac = linked_attributes_init(module, req);
437 if (!ac) {
438 return ldb_operr(ldb);
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 return ldb_oom(ldb);
451 for (i=0; i < req->op.mod.message->num_elements; i++) {
452 bool store_el = false;
453 const char *attr_name;
454 const struct dsdb_attribute *target_attr;
455 const struct ldb_message_element *el = &req->op.mod.message->elements[i];
456 const struct dsdb_attribute *schema_attr
457 = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
458 if (!schema_attr) {
459 ldb_asprintf_errstring(ldb,
460 "%s: attribute %s is not a valid attribute in schema",
461 __FUNCTION__,
462 el->name);
463 return LDB_ERR_OBJECT_CLASS_VIOLATION;
465 /* We have a valid attribute, now find out if it is a forward link
466 (Even link IDs are for the originating attribute) */
467 if (schema_attr->linkID == 0) {
468 continue;
471 if ((schema_attr->linkID & 1) == 1) {
472 unsigned int functional_level;
474 functional_level = dsdb_functional_level(ldb);
475 SMB_ASSERT(functional_level > DS_DOMAIN_FUNCTION_2000);
477 /* Now find the target attribute */
478 target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID + 1);
479 if (!target_attr) {
481 * windows 2003 has a broken schema where
482 * the definition of msDS-IsDomainFor
483 * is missing (which is supposed to be
484 * the backlink of the msDS-HasDomainNCs
485 * attribute
487 continue;
490 attr_name = target_attr->lDAPDisplayName;
492 switch (el->flags & LDB_FLAG_MOD_MASK) {
493 case LDB_FLAG_MOD_REPLACE:
494 /* treat as just a normal add the delete part is handled by the callback */
495 store_el = true;
497 /* break intentionally missing */
499 case LDB_FLAG_MOD_ADD:
501 /* For each value being added, we need to setup the adds */
502 for (j = 0; j < el->num_values; j++) {
503 ret = la_store_op(ac, LA_OP_ADD,
504 &el->values[j],
505 attr_name);
506 if (ret != LDB_SUCCESS) {
507 return ret;
510 break;
512 case LDB_FLAG_MOD_DELETE:
514 if (el->num_values) {
515 /* For each value being deleted, we need to setup the delete */
516 for (j = 0; j < el->num_values; j++) {
517 ret = la_store_op(ac, LA_OP_DEL,
518 &el->values[j],
519 attr_name);
520 if (ret != LDB_SUCCESS) {
521 return ret;
524 } else {
525 /* Flag that there was a DELETE
526 * without a value specified, so we
527 * need to look for the old value */
528 store_el = true;
531 break;
534 if (store_el) {
535 struct ldb_message_element *search_el;
537 search_el = talloc_realloc(ac->rc, ac->rc->el,
538 struct ldb_message_element,
539 ac->rc->num_elements +1);
540 if (!search_el) {
541 return ldb_oom(ldb);
543 ac->rc->el = search_el;
545 ac->rc->el[ac->rc->num_elements] = *el;
546 ac->rc->num_elements++;
550 if (ac->ops || ac->rc->el) {
551 /* both replace and delete without values are handled in the callback
552 * after the search on the entry to be modified is performed */
554 attrs = talloc_array(ac->rc, const char *, ac->rc->num_elements + 1);
555 if (!attrs) {
556 return ldb_oom(ldb);
558 for (i = 0; ac->rc && i < ac->rc->num_elements; i++) {
559 attrs[i] = ac->rc->el[i].name;
561 attrs[i] = NULL;
563 /* The callback does all the hard work here */
564 ret = ldb_build_search_req(&search_req, ldb, ac,
565 req->op.mod.message->dn,
566 LDB_SCOPE_BASE,
567 "(objectClass=*)", attrs,
568 NULL,
569 ac, la_mod_search_callback,
570 req);
571 LDB_REQ_SET_LOCATION(search_req);
573 /* We need to figure out our own extended DN, to fill in as the backlink target */
574 if (ret == LDB_SUCCESS) {
575 ret = ldb_request_add_control(search_req,
576 LDB_CONTROL_EXTENDED_DN_OID,
577 false, NULL);
579 if (ret == LDB_SUCCESS) {
580 talloc_steal(search_req, attrs);
582 ret = ldb_next_request(module, search_req);
585 } else {
586 /* nothing to do for this module, proceed */
587 talloc_free(ac);
588 ret = ldb_next_request(module, req);
591 return ret;
594 static int linked_attributes_fix_links(struct ldb_module *module,
595 struct ldb_dn *old_dn, struct ldb_dn *new_dn,
596 struct ldb_message_element *el, struct dsdb_schema *schema,
597 const struct dsdb_attribute *schema_attr)
599 unsigned int i, j;
600 TALLOC_CTX *tmp_ctx = talloc_new(module);
601 struct ldb_context *ldb = ldb_module_get_ctx(module);
602 const struct dsdb_attribute *target;
603 const char *attrs[2];
604 int ret;
606 target = dsdb_attribute_by_linkID(schema, schema_attr->linkID ^ 1);
607 if (target == NULL) {
608 /* there is no counterpart link to change */
609 return LDB_SUCCESS;
612 attrs[0] = target->lDAPDisplayName;
613 attrs[1] = NULL;
615 for (i=0; i<el->num_values; i++) {
616 struct dsdb_dn *dsdb_dn;
617 struct ldb_result *res;
618 struct ldb_message *msg;
619 struct ldb_message_element *el2;
621 dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i], schema_attr->syntax->ldap_oid);
622 if (dsdb_dn == NULL) {
623 talloc_free(tmp_ctx);
624 return LDB_ERR_INVALID_DN_SYNTAX;
627 ret = dsdb_module_search_dn(module, tmp_ctx, &res, dsdb_dn->dn,
628 attrs,
629 DSDB_FLAG_NEXT_MODULE |
630 DSDB_SEARCH_SHOW_RECYCLED |
631 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
632 DSDB_SEARCH_REVEAL_INTERNALS);
633 if (ret != LDB_SUCCESS) {
634 ldb_asprintf_errstring(ldb, "Linked attribute %s->%s between %s and %s - remote not found - %s",
635 el->name, target->lDAPDisplayName,
636 ldb_dn_get_linearized(old_dn),
637 ldb_dn_get_linearized(dsdb_dn->dn),
638 ldb_errstring(ldb));
639 talloc_free(tmp_ctx);
640 return ret;
642 msg = res->msgs[0];
644 if (msg->num_elements == 0) {
645 /* Forward link without backlink remaining - nothing to do here */
646 continue;
647 } else if (msg->num_elements != 1) {
648 ldb_asprintf_errstring(ldb, "Bad msg elements - got %u elements, expected one element to be returned in linked_attributes_fix_links for %s",
649 msg->num_elements, ldb_dn_get_linearized(msg->dn));
650 talloc_free(tmp_ctx);
651 return LDB_ERR_OPERATIONS_ERROR;
653 if (ldb_attr_cmp(msg->elements[0].name, target->lDAPDisplayName) != 0) {
654 ldb_asprintf_errstring(ldb, "Bad returned attribute in linked_attributes_fix_links: got %s, expected %s for %s", msg->elements[0].name, target->lDAPDisplayName, ldb_dn_get_linearized(msg->dn));
655 talloc_free(tmp_ctx);
656 return LDB_ERR_OPERATIONS_ERROR;
658 el2 = &msg->elements[0];
660 el2->flags = LDB_FLAG_MOD_REPLACE;
662 /* find our DN in the values */
663 for (j=0; j<el2->num_values; j++) {
664 struct dsdb_dn *dsdb_dn2;
665 dsdb_dn2 = dsdb_dn_parse(msg, ldb, &el2->values[j], target->syntax->ldap_oid);
666 if (dsdb_dn2 == NULL) {
667 talloc_free(tmp_ctx);
668 return LDB_ERR_INVALID_DN_SYNTAX;
670 if (ldb_dn_compare(old_dn, dsdb_dn2->dn) != 0) {
671 continue;
673 ret = ldb_dn_update_components(dsdb_dn2->dn, new_dn);
674 if (ret != LDB_SUCCESS) {
675 talloc_free(tmp_ctx);
676 return ret;
679 el2->values[j] = data_blob_string_const(
680 dsdb_dn_get_extended_linearized(el2->values, dsdb_dn2, 1));
683 ret = dsdb_check_single_valued_link(target, el2);
684 if (ret != LDB_SUCCESS) {
685 talloc_free(tmp_ctx);
686 return ret;
689 /* we may be putting multiple values in an attribute -
690 disable checking for this attribute */
691 el2->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
693 ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE);
694 if (ret != LDB_SUCCESS) {
695 ldb_asprintf_errstring(ldb, "Linked attribute %s->%s between %s and %s - update failed - %s",
696 el->name, target->lDAPDisplayName,
697 ldb_dn_get_linearized(old_dn),
698 ldb_dn_get_linearized(dsdb_dn->dn),
699 ldb_errstring(ldb));
700 talloc_free(tmp_ctx);
701 return ret;
705 talloc_free(tmp_ctx);
706 return LDB_SUCCESS;
710 /* rename */
711 static int linked_attributes_rename(struct ldb_module *module, struct ldb_request *req)
713 struct ldb_result *res;
714 struct ldb_message *msg;
715 unsigned int i;
716 struct ldb_context *ldb = ldb_module_get_ctx(module);
717 struct dsdb_schema *schema;
718 int ret;
720 - load the current msg
721 - find any linked attributes
722 - if its a link then find the target object
723 - modify the target linked attributes with the new DN
725 ret = dsdb_module_search_dn(module, req, &res, req->op.rename.olddn,
726 NULL,
727 DSDB_FLAG_NEXT_MODULE |
728 DSDB_SEARCH_SHOW_RECYCLED);
729 if (ret != LDB_SUCCESS) {
730 return ret;
733 schema = dsdb_get_schema(ldb, res);
734 if (!schema) {
735 return ldb_oom(ldb);
738 msg = res->msgs[0];
740 for (i=0; i<msg->num_elements; i++) {
741 struct ldb_message_element *el = &msg->elements[i];
742 const struct dsdb_attribute *schema_attr
743 = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
744 if (!schema_attr || schema_attr->linkID == 0) {
745 continue;
747 ret = linked_attributes_fix_links(module, msg->dn, req->op.rename.newdn, el,
748 schema, schema_attr);
749 if (ret != LDB_SUCCESS) {
750 talloc_free(res);
751 return ret;
755 talloc_free(res);
757 return ldb_next_request(module, req);
761 /* queue a linked attributes modify request in the la_private
762 structure */
763 static int la_queue_mod_request(struct la_context *ac)
765 struct la_private *la_private =
766 talloc_get_type(ldb_module_get_private(ac->module), struct la_private);
768 if (la_private == NULL) {
769 ldb_debug(ldb_module_get_ctx(ac->module), LDB_DEBUG_ERROR, __location__ ": No la_private transaction setup\n");
770 return ldb_operr(ldb_module_get_ctx(ac->module));
773 talloc_steal(la_private, ac);
774 DLIST_ADD(la_private->la_list, ac);
776 return ldb_module_done(ac->req, ac->op_controls,
777 ac->op_response, LDB_SUCCESS);
780 /* Having done the original operation, then try to fix up all the linked attributes for modify and delete */
781 static int la_mod_del_callback(struct ldb_request *req, struct ldb_reply *ares)
783 struct la_context *ac;
784 struct ldb_context *ldb;
785 int ret;
787 ac = talloc_get_type(req->context, struct la_context);
788 ldb = ldb_module_get_ctx(ac->module);
790 if (!ares) {
791 return ldb_module_done(ac->req, NULL, NULL,
792 LDB_ERR_OPERATIONS_ERROR);
794 if (ares->error != LDB_SUCCESS) {
795 return ldb_module_done(ac->req, ares->controls,
796 ares->response, ares->error);
799 if (ares->type != LDB_REPLY_DONE) {
800 ldb_set_errstring(ldb,
801 "invalid ldb_reply_type in callback");
802 talloc_free(ares);
803 return ldb_module_done(ac->req, NULL, NULL,
804 LDB_ERR_OPERATIONS_ERROR);
807 ac->op_controls = talloc_steal(ac, ares->controls);
808 ac->op_response = talloc_steal(ac, ares->response);
810 /* If we have modfies to make, this is the time to do them for modify and delete */
811 ret = la_queue_mod_request(ac);
813 if (ret != LDB_SUCCESS) {
814 return ldb_module_done(ac->req, NULL, NULL, ret);
816 talloc_free(ares);
818 /* la_queue_mod_request has already sent the callbacks */
819 return LDB_SUCCESS;
823 /* Having done the original add, then try to fix up all the linked attributes
825 This is done after the add so the links can get the extended DNs correctly.
827 static int la_add_callback(struct ldb_request *req, struct ldb_reply *ares)
829 struct la_context *ac;
830 struct ldb_context *ldb;
831 int ret;
833 ac = talloc_get_type(req->context, struct la_context);
834 ldb = ldb_module_get_ctx(ac->module);
836 if (!ares) {
837 return ldb_module_done(ac->req, NULL, NULL,
838 LDB_ERR_OPERATIONS_ERROR);
840 if (ares->error != LDB_SUCCESS) {
841 return ldb_module_done(ac->req, ares->controls,
842 ares->response, ares->error);
845 if (ares->type != LDB_REPLY_DONE) {
846 ldb_set_errstring(ldb,
847 "invalid ldb_reply_type in callback");
848 talloc_free(ares);
849 return ldb_module_done(ac->req, NULL, NULL,
850 LDB_ERR_OPERATIONS_ERROR);
853 if (ac->ops) {
854 struct ldb_request *search_req;
855 static const char *attrs[] = { NULL };
857 /* The callback does all the hard work here - we need
858 * the objectGUID and SID of the added record */
859 ret = ldb_build_search_req(&search_req, ldb, ac,
860 ac->req->op.add.message->dn,
861 LDB_SCOPE_BASE,
862 "(objectClass=*)", attrs,
863 NULL,
864 ac, la_mod_search_callback,
865 ac->req);
866 LDB_REQ_SET_LOCATION(search_req);
868 if (ret == LDB_SUCCESS) {
869 ret = ldb_request_add_control(search_req,
870 LDB_CONTROL_EXTENDED_DN_OID,
871 false, NULL);
873 if (ret != LDB_SUCCESS) {
874 return ldb_module_done(ac->req, NULL, NULL,
875 ret);
878 ac->op_controls = talloc_steal(ac, ares->controls);
879 ac->op_response = talloc_steal(ac, ares->response);
881 return ldb_next_request(ac->module, search_req);
883 } else {
884 return ldb_module_done(ac->req, ares->controls,
885 ares->response, ares->error);
889 /* Reconstruct the original request, but pointing at our local callback to finish things off */
890 static int la_down_req(struct la_context *ac)
892 struct ldb_request *down_req;
893 struct ldb_context *ldb;
894 int ret;
896 ldb = ldb_module_get_ctx(ac->module);
898 switch (ac->req->operation) {
899 case LDB_ADD:
900 ret = ldb_build_add_req(&down_req, ldb, ac,
901 ac->req->op.add.message,
902 ac->req->controls,
903 ac, la_add_callback,
904 ac->req);
905 LDB_REQ_SET_LOCATION(down_req);
906 break;
907 case LDB_MODIFY:
908 ret = ldb_build_mod_req(&down_req, ldb, ac,
909 ac->req->op.mod.message,
910 ac->req->controls,
911 ac, la_mod_del_callback,
912 ac->req);
913 LDB_REQ_SET_LOCATION(down_req);
914 break;
915 default:
916 ret = LDB_ERR_OPERATIONS_ERROR;
918 if (ret != LDB_SUCCESS) {
919 return ret;
922 return ldb_next_request(ac->module, down_req);
926 use the GUID part of an extended DN to find the target DN, in case
927 it has moved
929 static int la_find_dn_target(struct ldb_module *module, struct la_context *ac,
930 struct GUID *guid, struct ldb_dn **dn)
932 return dsdb_find_dn_by_guid(ldb_module_get_ctx(ac->module), ac, guid, dn);
935 /* apply one la_context op change */
936 static int la_do_op_request(struct ldb_module *module, struct la_context *ac, struct la_op_store *op)
938 struct ldb_message_element *ret_el;
939 struct ldb_message *new_msg;
940 struct ldb_context *ldb;
941 int ret;
943 ldb = ldb_module_get_ctx(ac->module);
945 /* Create the modify request */
946 new_msg = ldb_msg_new(ac);
947 if (!new_msg) {
948 return ldb_oom(ldb);
951 ret = la_find_dn_target(module, ac, &op->guid, &new_msg->dn);
952 if (ret != LDB_SUCCESS) {
953 return ret;
956 if (op->op == LA_OP_ADD) {
957 ret = ldb_msg_add_empty(new_msg, op->name,
958 LDB_FLAG_MOD_ADD, &ret_el);
959 } else {
960 ret = ldb_msg_add_empty(new_msg, op->name,
961 LDB_FLAG_MOD_DELETE, &ret_el);
963 if (ret != LDB_SUCCESS) {
964 return ret;
966 ret_el->values = talloc_array(new_msg, struct ldb_val, 1);
967 if (!ret_el->values) {
968 return ldb_oom(ldb);
970 ret_el->num_values = 1;
971 if (op->op == LA_OP_ADD) {
972 ret_el->values[0] = data_blob_string_const(ldb_dn_get_extended_linearized(new_msg, ac->add_dn, 1));
973 } else {
974 ret_el->values[0] = data_blob_string_const(ldb_dn_get_extended_linearized(new_msg, ac->del_dn, 1));
977 #if 0
978 ldb_debug(ldb, LDB_DEBUG_WARNING,
979 "link on %s %s: %s %s\n",
980 ldb_dn_get_linearized(new_msg->dn), ret_el->name,
981 ret_el->values[0].data, ac->ops->op == LA_OP_ADD ? "added" : "deleted");
982 #endif
984 if (DEBUGLVL(4)) {
985 DEBUG(4,("Applying linked attribute change:\n%s\n",
986 ldb_ldif_message_string(ldb, op, LDB_CHANGETYPE_MODIFY, new_msg)));
989 ret = dsdb_module_modify(module, new_msg, DSDB_FLAG_NEXT_MODULE);
990 if (ret != LDB_SUCCESS) {
991 ldb_debug(ldb, LDB_DEBUG_WARNING, "Failed to apply linked attribute change '%s'\n%s\n",
992 ldb_errstring(ldb),
993 ldb_ldif_message_string(ldb, op, LDB_CHANGETYPE_MODIFY, new_msg));
996 return ret;
999 /* apply one set of la_context changes */
1000 static int la_do_mod_request(struct ldb_module *module, struct la_context *ac)
1002 struct la_op_store *op;
1004 for (op = ac->ops; op; op=op->next) {
1005 int ret = la_do_op_request(module, ac, op);
1006 if (ret != LDB_SUCCESS) {
1007 if (ret != LDB_ERR_NO_SUCH_OBJECT) {
1008 return ret;
1013 return LDB_SUCCESS;
1018 we hook into the transaction operations to allow us to
1019 perform the linked attribute updates at the end of the whole
1020 transaction. This allows a forward linked attribute to be created
1021 before the target is created, as long as the target is created
1022 in the same transaction
1024 static int linked_attributes_start_transaction(struct ldb_module *module)
1026 /* create our private structure for this transaction */
1027 struct la_private *la_private = talloc_get_type(ldb_module_get_private(module),
1028 struct la_private);
1029 talloc_free(la_private);
1030 la_private = talloc(module, struct la_private);
1031 if (la_private == NULL) {
1032 return ldb_oom(ldb_module_get_ctx(module));
1034 la_private->la_list = NULL;
1035 ldb_module_set_private(module, la_private);
1036 return ldb_next_start_trans(module);
1040 on prepare commit we loop over our queued la_context structures
1041 and apply each of them
1043 static int linked_attributes_prepare_commit(struct ldb_module *module)
1045 struct la_private *la_private =
1046 talloc_get_type(ldb_module_get_private(module), struct la_private);
1047 struct la_context *ac;
1049 if (!la_private) {
1050 /* prepare commit without begin_transaction - let someone else return the error, just don't segfault */
1051 return ldb_next_prepare_commit(module);
1053 /* walk the list backwards, to do the first entry first, as we
1054 * added the entries with DLIST_ADD() which puts them at the
1055 * start of the list */
1057 /* Start at the end of the list - so we can start
1058 * there, but ensure we don't create a loop by NULLing
1059 * it out in the first element */
1060 ac = DLIST_TAIL(la_private->la_list);
1062 for (; ac; ac=DLIST_PREV(ac)) {
1063 int ret;
1064 ac->req = NULL;
1065 ret = la_do_mod_request(module, ac);
1066 if (ret != LDB_SUCCESS) {
1067 DEBUG(0,(__location__ ": Failed mod request ret=%d\n", ret));
1068 talloc_free(la_private);
1069 ldb_module_set_private(module, NULL);
1070 return ret;
1074 talloc_free(la_private);
1075 ldb_module_set_private(module, NULL);
1077 return ldb_next_prepare_commit(module);
1080 static int linked_attributes_del_transaction(struct ldb_module *module)
1082 struct la_private *la_private =
1083 talloc_get_type(ldb_module_get_private(module), struct la_private);
1084 talloc_free(la_private);
1085 ldb_module_set_private(module, NULL);
1086 return ldb_next_del_trans(module);
1090 static const struct ldb_module_ops ldb_linked_attributes_module_ops = {
1091 .name = "linked_attributes",
1092 .add = linked_attributes_add,
1093 .modify = linked_attributes_modify,
1094 .rename = linked_attributes_rename,
1095 .start_transaction = linked_attributes_start_transaction,
1096 .prepare_commit = linked_attributes_prepare_commit,
1097 .del_transaction = linked_attributes_del_transaction,
1100 int ldb_linked_attributes_module_init(const char *version)
1102 LDB_MODULE_CHECK_VERSION(version);
1103 return ldb_register_module(&ldb_linked_attributes_module_ops);