s4:dsdb use dsdb_module_modify() rather than ldb_next_request()
[Samba.git] / source4 / dsdb / samdb / ldb_modules / linked_attributes.c
blob82fe27ef7f964b7096fee0fe385cdb996d1e1f14
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 int ret;
97 NTSTATUS status;
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_ERR_OPERATIONS_ERROR;
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 ldb_oom(ldb);
142 return LDB_ERR_OPERATIONS_ERROR;
145 os->op = op;
147 ret = la_guid_from_dn(ac, op_dn, &os->guid);
148 talloc_free(op_dn);
149 if (ret == LDB_ERR_NO_SUCH_OBJECT && ac->req->operation == LDB_DELETE) {
150 /* we are deleting an object, and we've found it has a
151 * forward link to a target that no longer
152 * exists. This is not an error in the delete, and we
153 * should just not do the deferred delete of the
154 * target attribute
156 talloc_free(os);
157 return LDB_SUCCESS;
159 if (ret != LDB_SUCCESS) {
160 return ret;
163 os->name = talloc_strdup(os, name);
164 if (!os->name) {
165 ldb_oom(ldb);
166 return LDB_ERR_OPERATIONS_ERROR;
169 /* Do deletes before adds */
170 if (op == LA_OP_ADD) {
171 DLIST_ADD_END(ac->ops, os, struct la_op_store *);
172 } else {
173 /* By adding to the head of the list, we do deletes before
174 * adds when processing a replace */
175 DLIST_ADD(ac->ops, os);
178 return LDB_SUCCESS;
181 static int la_queue_mod_request(struct la_context *ac);
182 static int la_down_req(struct la_context *ac);
186 /* add */
187 static int linked_attributes_add(struct ldb_module *module, struct ldb_request *req)
189 struct ldb_context *ldb;
190 const struct dsdb_attribute *target_attr;
191 struct la_context *ac;
192 const char *attr_name;
193 struct ldb_control *ctrl;
194 int ret;
195 int i, j;
197 ldb = ldb_module_get_ctx(module);
199 if (ldb_dn_is_special(req->op.add.message->dn)) {
200 /* do not manipulate our control entries */
201 return ldb_next_request(module, req);
204 if (!(ctrl = ldb_request_get_control(req, DSDB_CONTROL_APPLY_LINKS))) {
205 /* don't do anything special for linked attributes, repl_meta_data has done it */
206 return ldb_next_request(module, req);
208 ctrl->critical = false;
210 ac = linked_attributes_init(module, req);
211 if (!ac) {
212 return LDB_ERR_OPERATIONS_ERROR;
215 if (!ac->schema) {
216 /* without schema, this doesn't make any sense */
217 talloc_free(ac);
218 return ldb_next_request(module, req);
221 /* Need to ensure we only have forward links being specified */
222 for (i=0; i < req->op.add.message->num_elements; i++) {
223 const struct ldb_message_element *el = &req->op.add.message->elements[i];
224 const struct dsdb_attribute *schema_attr
225 = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
226 if (!schema_attr) {
227 ldb_asprintf_errstring(ldb,
228 "attribute %s is not a valid attribute in schema", 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) || ((schema_attr->linkID & 1) == 1)) {
233 continue;
236 /* Even link IDs are for the originating attribute */
237 target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID + 1);
238 if (!target_attr) {
240 * windows 2003 has a broken schema where
241 * the definition of msDS-IsDomainFor
242 * is missing (which is supposed to be
243 * the backlink of the msDS-HasDomainNCs
244 * attribute
246 continue;
249 attr_name = target_attr->lDAPDisplayName;
251 for (j = 0; j < el->num_values; j++) {
252 ret = la_store_op(ac, LA_OP_ADD,
253 &el->values[j],
254 attr_name);
255 if (ret != LDB_SUCCESS) {
256 return ret;
261 /* if no linked attributes are present continue */
262 if (ac->ops == NULL) {
263 /* nothing to do for this module, proceed */
264 talloc_free(ac);
265 return ldb_next_request(module, req);
268 /* start with the original request */
269 return la_down_req(ac);
272 /* For a delete or rename, we need to find out what linked attributes
273 * are currently on this DN, and then deal with them. This is the
274 * callback to the base search */
276 static int la_mod_search_callback(struct ldb_request *req, struct ldb_reply *ares)
278 struct ldb_context *ldb;
279 const struct dsdb_attribute *schema_attr;
280 const struct dsdb_attribute *target_attr;
281 struct ldb_message_element *search_el;
282 struct replace_context *rc;
283 struct la_context *ac;
284 const char *attr_name;
285 int i, j;
286 int ret = LDB_SUCCESS;
288 ac = talloc_get_type(req->context, struct la_context);
289 ldb = ldb_module_get_ctx(ac->module);
290 rc = ac->rc;
292 if (!ares) {
293 return ldb_module_done(ac->req, NULL, NULL,
294 LDB_ERR_OPERATIONS_ERROR);
296 if (ares->error != LDB_SUCCESS) {
297 return ldb_module_done(ac->req, ares->controls,
298 ares->response, ares->error);
301 /* Only entries are interesting, and we only want the olddn */
302 switch (ares->type) {
303 case LDB_REPLY_ENTRY:
305 if (ldb_dn_compare(ares->message->dn, ac->req->op.mod.message->dn) != 0) {
306 ldb_asprintf_errstring(ldb,
307 "linked_attributes: %s is not the DN we were looking for",
308 ldb_dn_get_linearized(ares->message->dn));
309 /* Guh? We only asked for this DN */
310 talloc_free(ares);
311 return ldb_module_done(ac->req, NULL, NULL,
312 LDB_ERR_OPERATIONS_ERROR);
315 ac->add_dn = ac->del_dn = talloc_steal(ac, ares->message->dn);
317 /* We don't populate 'rc' for ADD - it can't be deleting elements anyway */
318 for (i = 0; rc && i < rc->num_elements; i++) {
320 schema_attr = dsdb_attribute_by_lDAPDisplayName(ac->schema, rc->el[i].name);
321 if (!schema_attr) {
322 ldb_asprintf_errstring(ldb,
323 "attribute %s is not a valid attribute in schema",
324 rc->el[i].name);
325 talloc_free(ares);
326 return ldb_module_done(ac->req, NULL, NULL,
327 LDB_ERR_OBJECT_CLASS_VIOLATION);
330 search_el = ldb_msg_find_element(ares->message,
331 rc->el[i].name);
333 /* See if this element already exists */
334 /* otherwise just ignore as
335 * the add has already been scheduled */
336 if ( ! search_el) {
337 continue;
340 target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID + 1);
341 if (!target_attr) {
343 * windows 2003 has a broken schema where
344 * the definition of msDS-IsDomainFor
345 * is missing (which is supposed to be
346 * the backlink of the msDS-HasDomainNCs
347 * attribute
349 continue;
351 attr_name = target_attr->lDAPDisplayName;
353 /* Now we know what was there, we can remove it for the re-add */
354 for (j = 0; j < search_el->num_values; j++) {
355 ret = la_store_op(ac, LA_OP_DEL,
356 &search_el->values[j],
357 attr_name);
358 if (ret != LDB_SUCCESS) {
359 talloc_free(ares);
360 return ldb_module_done(ac->req,
361 NULL, NULL, ret);
366 break;
368 case LDB_REPLY_REFERRAL:
369 /* ignore */
370 break;
372 case LDB_REPLY_DONE:
374 talloc_free(ares);
376 if (ac->req->operation == LDB_ADD) {
377 /* Start the modifies to the backlinks */
378 ret = la_queue_mod_request(ac);
380 if (ret != LDB_SUCCESS) {
381 return ldb_module_done(ac->req, NULL, NULL,
382 ret);
384 } else {
385 /* Start with the original request */
386 ret = la_down_req(ac);
387 if (ret != LDB_SUCCESS) {
388 return ldb_module_done(ac->req, NULL, NULL, ret);
391 return LDB_SUCCESS;
394 talloc_free(ares);
395 return ret;
399 /* modify */
400 static int linked_attributes_modify(struct ldb_module *module, struct ldb_request *req)
402 /* Look over list of modifications */
403 /* Find if any are for linked attributes */
404 /* Determine the effect of the modification */
405 /* Apply the modify to the linked entry */
407 struct ldb_context *ldb;
408 int i, j;
409 struct la_context *ac;
410 struct ldb_request *search_req;
411 const char **attrs;
412 struct ldb_control *ctrl;
414 int ret;
416 ldb = ldb_module_get_ctx(module);
418 if (ldb_dn_is_special(req->op.mod.message->dn)) {
419 /* do not manipulate our control entries */
420 return ldb_next_request(module, req);
423 if (!(ctrl = ldb_request_get_control(req, DSDB_CONTROL_APPLY_LINKS))) {
424 /* don't do anything special for linked attributes, repl_meta_data has done it */
425 return ldb_next_request(module, req);
427 ctrl->critical = false;
429 ac = linked_attributes_init(module, req);
430 if (!ac) {
431 return LDB_ERR_OPERATIONS_ERROR;
434 if (!ac->schema) {
435 /* without schema, this doesn't make any sense */
436 return ldb_next_request(module, req);
439 ac->rc = talloc_zero(ac, struct replace_context);
440 if (!ac->rc) {
441 ldb_oom(ldb);
442 return LDB_ERR_OPERATIONS_ERROR;
445 for (i=0; i < req->op.mod.message->num_elements; i++) {
446 bool store_el = false;
447 const char *attr_name;
448 const struct dsdb_attribute *target_attr;
449 const struct ldb_message_element *el = &req->op.mod.message->elements[i];
450 const struct dsdb_attribute *schema_attr
451 = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
452 if (!schema_attr) {
453 ldb_asprintf_errstring(ldb,
454 "attribute %s is not a valid attribute in schema", el->name);
455 return LDB_ERR_OBJECT_CLASS_VIOLATION;
457 /* We have a valid attribute, now find out if it is a forward link
458 (Even link IDs are for the originating attribute) */
459 if ((schema_attr->linkID == 0) || ((schema_attr->linkID & 1) == 1)) {
460 continue;
463 /* Now find the target attribute */
464 target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID + 1);
465 if (!target_attr) {
467 * windows 2003 has a broken schema where
468 * the definition of msDS-IsDomainFor
469 * is missing (which is supposed to be
470 * the backlink of the msDS-HasDomainNCs
471 * attribute
473 continue;
476 attr_name = target_attr->lDAPDisplayName;
478 switch (el->flags & LDB_FLAG_MOD_MASK) {
479 case LDB_FLAG_MOD_REPLACE:
480 /* treat as just a normal add the delete part is handled by the callback */
481 store_el = true;
483 /* break intentionally missing */
485 case LDB_FLAG_MOD_ADD:
487 /* For each value being added, we need to setup the adds */
488 for (j = 0; j < el->num_values; j++) {
489 ret = la_store_op(ac, LA_OP_ADD,
490 &el->values[j],
491 attr_name);
492 if (ret != LDB_SUCCESS) {
493 return ret;
496 break;
498 case LDB_FLAG_MOD_DELETE:
500 if (el->num_values) {
501 /* For each value being deleted, we need to setup the delete */
502 for (j = 0; j < el->num_values; j++) {
503 ret = la_store_op(ac, LA_OP_DEL,
504 &el->values[j],
505 attr_name);
506 if (ret != LDB_SUCCESS) {
507 return ret;
510 } else {
511 /* Flag that there was a DELETE
512 * without a value specified, so we
513 * need to look for the old value */
514 store_el = true;
517 break;
520 if (store_el) {
521 struct ldb_message_element *search_el;
523 search_el = talloc_realloc(ac->rc, ac->rc->el,
524 struct ldb_message_element,
525 ac->rc->num_elements +1);
526 if (!search_el) {
527 ldb_oom(ldb);
528 return LDB_ERR_OPERATIONS_ERROR;
530 ac->rc->el = search_el;
532 ac->rc->el[ac->rc->num_elements] = *el;
533 ac->rc->num_elements++;
537 if (ac->ops || ac->rc->el) {
538 /* both replace and delete without values are handled in the callback
539 * after the search on the entry to be modified is performed */
541 attrs = talloc_array(ac->rc, const char *, ac->rc->num_elements + 1);
542 if (!attrs) {
543 ldb_oom(ldb);
544 return LDB_ERR_OPERATIONS_ERROR;
546 for (i = 0; ac->rc && i < ac->rc->num_elements; i++) {
547 attrs[i] = ac->rc->el[i].name;
549 attrs[i] = NULL;
551 /* The callback does all the hard work here */
552 ret = ldb_build_search_req(&search_req, ldb, ac,
553 req->op.mod.message->dn,
554 LDB_SCOPE_BASE,
555 "(objectClass=*)", attrs,
556 NULL,
557 ac, la_mod_search_callback,
558 req);
560 /* We need to figure out our own extended DN, to fill in as the backlink target */
561 if (ret == LDB_SUCCESS) {
562 ret = ldb_request_add_control(search_req,
563 LDB_CONTROL_EXTENDED_DN_OID,
564 false, NULL);
566 if (ret == LDB_SUCCESS) {
567 talloc_steal(search_req, attrs);
569 ret = ldb_next_request(module, search_req);
572 } else {
573 /* nothing to do for this module, proceed */
574 talloc_free(ac);
575 ret = ldb_next_request(module, req);
578 return ret;
581 static int linked_attributes_fix_links(struct ldb_module *module,
582 struct ldb_dn *old_dn, struct ldb_dn *new_dn,
583 struct ldb_message_element *el, struct dsdb_schema *schema,
584 const struct dsdb_attribute *schema_attr)
586 unsigned int i;
587 TALLOC_CTX *tmp_ctx = talloc_new(module);
588 struct ldb_context *ldb = ldb_module_get_ctx(module);
589 const struct dsdb_attribute *target;
590 const char *attrs[2];
592 target = dsdb_attribute_by_linkID(schema, schema_attr->linkID ^ 1);
593 if (target == NULL) {
594 /* there is no counterpart link to change */
595 return LDB_SUCCESS;
598 attrs[0] = target->lDAPDisplayName;
599 attrs[1] = NULL;
601 for (i=0; i<el->num_values; i++) {
602 struct dsdb_dn *dsdb_dn;
603 unsigned int j;
604 int ret;
605 struct ldb_result *res;
606 struct ldb_message *msg;
607 struct ldb_message_element *el2;
609 dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i], schema_attr->syntax->ldap_oid);
610 if (dsdb_dn == NULL) {
611 talloc_free(tmp_ctx);
612 return LDB_ERR_INVALID_DN_SYNTAX;
615 ret = dsdb_module_search_dn(module, tmp_ctx, &res, dsdb_dn->dn,
616 attrs,
617 DSDB_SEARCH_SHOW_DELETED |
618 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
619 DSDB_SEARCH_REVEAL_INTERNALS);
620 if (ret != LDB_SUCCESS) {
621 ldb_asprintf_errstring(ldb, "Linked attribute %s->%s between %s and %s - remote not found - %s",
622 el->name, target->lDAPDisplayName,
623 ldb_dn_get_linearized(old_dn),
624 ldb_dn_get_linearized(dsdb_dn->dn),
625 ldb_errstring(ldb));
626 talloc_free(tmp_ctx);
627 return ret;
629 msg = res->msgs[0];
631 if (msg->num_elements != 1 ||
632 ldb_attr_cmp(msg->elements[0].name, target->lDAPDisplayName) != 0) {
633 ldb_set_errstring(ldb, "Bad msg elements in linked_attributes_fix_links");
634 talloc_free(tmp_ctx);
635 return LDB_ERR_OPERATIONS_ERROR;
637 el2 = &msg->elements[0];
639 el2->flags = LDB_FLAG_MOD_REPLACE;
641 /* find our DN in the values */
642 for (j=0; j<el2->num_values; j++) {
643 struct dsdb_dn *dsdb_dn2;
644 dsdb_dn2 = dsdb_dn_parse(msg, ldb, &el2->values[j], target->syntax->ldap_oid);
645 if (dsdb_dn2 == NULL) {
646 talloc_free(tmp_ctx);
647 return LDB_ERR_INVALID_DN_SYNTAX;
649 if (ldb_dn_compare(old_dn, dsdb_dn2->dn) != 0) {
650 continue;
652 ret = ldb_dn_update_components(dsdb_dn2->dn, new_dn);
653 if (ret != LDB_SUCCESS) {
654 talloc_free(tmp_ctx);
655 return ret;
658 el2->values[j] = data_blob_string_const(
659 dsdb_dn_get_extended_linearized(el2->values, dsdb_dn2, 1));
662 ret = dsdb_check_single_valued_link(target, el2);
663 if (ret != LDB_SUCCESS) {
664 talloc_free(tmp_ctx);
665 return ret;
668 ret = dsdb_module_modify(module, msg, DSDB_MODIFY_RELAX);
669 if (ret != LDB_SUCCESS) {
670 ldb_asprintf_errstring(ldb, "Linked attribute %s->%s between %s and %s - update failed - %s",
671 el->name, target->lDAPDisplayName,
672 ldb_dn_get_linearized(old_dn),
673 ldb_dn_get_linearized(dsdb_dn->dn),
674 ldb_errstring(ldb));
675 talloc_free(tmp_ctx);
676 return ret;
680 talloc_free(tmp_ctx);
681 return LDB_SUCCESS;
685 /* rename */
686 static int linked_attributes_rename(struct ldb_module *module, struct ldb_request *req)
688 struct ldb_result *res;
689 struct ldb_message *msg;
690 unsigned int i;
691 int ret;
692 struct ldb_context *ldb = ldb_module_get_ctx(module);
693 struct dsdb_schema *schema;
695 - load the current msg
696 - find any linked attributes
697 - if its a link then find the target object
698 - modify the target linked attributes with the new DN
700 ret = dsdb_module_search_dn(module, req, &res, req->op.rename.olddn,
701 NULL, DSDB_SEARCH_SHOW_DELETED);
702 if (ret != LDB_SUCCESS) {
703 return ret;
706 schema = dsdb_get_schema(ldb, res);
707 if (!schema) {
708 ldb_oom(ldb);
709 return LDB_ERR_OPERATIONS_ERROR;
712 msg = res->msgs[0];
714 for (i=0; i<msg->num_elements; i++) {
715 struct ldb_message_element *el = &msg->elements[i];
716 const struct dsdb_attribute *schema_attr
717 = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
718 if (!schema_attr || schema_attr->linkID == 0) {
719 continue;
721 ret = linked_attributes_fix_links(module, msg->dn, req->op.rename.newdn, el,
722 schema, schema_attr);
723 if (ret != LDB_SUCCESS) {
724 talloc_free(res);
725 return ret;
729 talloc_free(res);
731 return ldb_next_request(module, req);
735 /* queue a linked attributes modify request in the la_private
736 structure */
737 static int la_queue_mod_request(struct la_context *ac)
739 struct la_private *la_private =
740 talloc_get_type(ldb_module_get_private(ac->module), struct la_private);
742 if (la_private == NULL) {
743 ldb_debug(ldb_module_get_ctx(ac->module), LDB_DEBUG_ERROR, __location__ ": No la_private transaction setup\n");
744 return LDB_ERR_OPERATIONS_ERROR;
747 talloc_steal(la_private, ac);
748 DLIST_ADD(la_private->la_list, ac);
750 return ldb_module_done(ac->req, ac->op_controls,
751 ac->op_response, LDB_SUCCESS);
754 /* Having done the original operation, then try to fix up all the linked attributes for modify and delete */
755 static int la_mod_del_callback(struct ldb_request *req, struct ldb_reply *ares)
757 int ret;
758 struct la_context *ac;
759 struct ldb_context *ldb;
761 ac = talloc_get_type(req->context, struct la_context);
762 ldb = ldb_module_get_ctx(ac->module);
764 if (!ares) {
765 return ldb_module_done(ac->req, NULL, NULL,
766 LDB_ERR_OPERATIONS_ERROR);
768 if (ares->error != LDB_SUCCESS) {
769 return ldb_module_done(ac->req, ares->controls,
770 ares->response, ares->error);
773 if (ares->type != LDB_REPLY_DONE) {
774 ldb_set_errstring(ldb,
775 "invalid ldb_reply_type in callback");
776 talloc_free(ares);
777 return ldb_module_done(ac->req, NULL, NULL,
778 LDB_ERR_OPERATIONS_ERROR);
781 ac->op_controls = talloc_steal(ac, ares->controls);
782 ac->op_response = talloc_steal(ac, ares->response);
784 /* If we have modfies to make, this is the time to do them for modify and delete */
785 ret = la_queue_mod_request(ac);
787 if (ret != LDB_SUCCESS) {
788 return ldb_module_done(ac->req, NULL, NULL, ret);
790 talloc_free(ares);
792 /* la_queue_mod_request has already sent the callbacks */
793 return LDB_SUCCESS;
797 /* Having done the original add, then try to fix up all the linked attributes
799 This is done after the add so the links can get the extended DNs correctly.
801 static int la_add_callback(struct ldb_request *req, struct ldb_reply *ares)
803 int ret;
804 struct la_context *ac;
805 struct ldb_context *ldb;
807 ac = talloc_get_type(req->context, struct la_context);
808 ldb = ldb_module_get_ctx(ac->module);
810 if (!ares) {
811 return ldb_module_done(ac->req, NULL, NULL,
812 LDB_ERR_OPERATIONS_ERROR);
814 if (ares->error != LDB_SUCCESS) {
815 return ldb_module_done(ac->req, ares->controls,
816 ares->response, ares->error);
819 if (ares->type != LDB_REPLY_DONE) {
820 ldb_set_errstring(ldb,
821 "invalid ldb_reply_type in callback");
822 talloc_free(ares);
823 return ldb_module_done(ac->req, NULL, NULL,
824 LDB_ERR_OPERATIONS_ERROR);
827 if (ac->ops) {
828 struct ldb_request *search_req;
829 static const char *attrs[] = { NULL };
831 /* The callback does all the hard work here - we need
832 * the objectGUID and SID of the added record */
833 ret = ldb_build_search_req(&search_req, ldb, ac,
834 ac->req->op.add.message->dn,
835 LDB_SCOPE_BASE,
836 "(objectClass=*)", attrs,
837 NULL,
838 ac, la_mod_search_callback,
839 ac->req);
841 if (ret == LDB_SUCCESS) {
842 ret = ldb_request_add_control(search_req,
843 LDB_CONTROL_EXTENDED_DN_OID,
844 false, NULL);
846 if (ret != LDB_SUCCESS) {
847 return ldb_module_done(ac->req, NULL, NULL,
848 ret);
851 ac->op_controls = talloc_steal(ac, ares->controls);
852 ac->op_response = talloc_steal(ac, ares->response);
854 return ldb_next_request(ac->module, search_req);
856 } else {
857 return ldb_module_done(ac->req, ares->controls,
858 ares->response, ares->error);
862 /* Reconstruct the original request, but pointing at our local callback to finish things off */
863 static int la_down_req(struct la_context *ac)
865 struct ldb_request *down_req;
866 int ret;
867 struct ldb_context *ldb;
869 ldb = ldb_module_get_ctx(ac->module);
871 switch (ac->req->operation) {
872 case LDB_ADD:
873 ret = ldb_build_add_req(&down_req, ldb, ac,
874 ac->req->op.add.message,
875 ac->req->controls,
876 ac, la_add_callback,
877 ac->req);
878 break;
879 case LDB_MODIFY:
880 ret = ldb_build_mod_req(&down_req, ldb, ac,
881 ac->req->op.mod.message,
882 ac->req->controls,
883 ac, la_mod_del_callback,
884 ac->req);
885 break;
886 default:
887 ret = LDB_ERR_OPERATIONS_ERROR;
889 if (ret != LDB_SUCCESS) {
890 return ret;
893 return ldb_next_request(ac->module, down_req);
897 use the GUID part of an extended DN to find the target DN, in case
898 it has moved
900 static int la_find_dn_target(struct ldb_module *module, struct la_context *ac,
901 struct GUID *guid, struct ldb_dn **dn)
903 return dsdb_find_dn_by_guid(ldb_module_get_ctx(ac->module), ac, guid, dn);
906 /* apply one la_context op change */
907 static int la_do_op_request(struct ldb_module *module, struct la_context *ac, struct la_op_store *op)
909 struct ldb_message_element *ret_el;
910 struct ldb_message *new_msg;
911 struct ldb_context *ldb;
912 int ret;
914 ldb = ldb_module_get_ctx(ac->module);
916 /* Create the modify request */
917 new_msg = ldb_msg_new(ac);
918 if (!new_msg) {
919 ldb_oom(ldb);
920 return LDB_ERR_OPERATIONS_ERROR;
923 ret = la_find_dn_target(module, ac, &op->guid, &new_msg->dn);
924 if (ret != LDB_SUCCESS) {
925 return ret;
928 if (op->op == LA_OP_ADD) {
929 ret = ldb_msg_add_empty(new_msg, op->name,
930 LDB_FLAG_MOD_ADD, &ret_el);
931 } else {
932 ret = ldb_msg_add_empty(new_msg, op->name,
933 LDB_FLAG_MOD_DELETE, &ret_el);
935 if (ret != LDB_SUCCESS) {
936 return ret;
938 ret_el->values = talloc_array(new_msg, struct ldb_val, 1);
939 if (!ret_el->values) {
940 ldb_oom(ldb);
941 return LDB_ERR_OPERATIONS_ERROR;
943 ret_el->num_values = 1;
944 if (op->op == LA_OP_ADD) {
945 ret_el->values[0] = data_blob_string_const(ldb_dn_get_extended_linearized(new_msg, ac->add_dn, 1));
946 } else {
947 ret_el->values[0] = data_blob_string_const(ldb_dn_get_extended_linearized(new_msg, ac->del_dn, 1));
950 #if 0
951 ldb_debug(ldb, LDB_DEBUG_WARNING,
952 "link on %s %s: %s %s\n",
953 ldb_dn_get_linearized(new_msg->dn), ret_el->name,
954 ret_el->values[0].data, ac->ops->op == LA_OP_ADD ? "added" : "deleted");
955 #endif
957 if (DEBUGLVL(4)) {
958 DEBUG(4,("Applying linked attribute change:\n%s\n",
959 ldb_ldif_message_string(ldb, op, LDB_CHANGETYPE_MODIFY, new_msg)));
962 ret = dsdb_module_modify(module, new_msg, 0);
963 if (ret != LDB_SUCCESS) {
964 ldb_debug(ldb, LDB_DEBUG_WARNING, "Failed to apply linked attribute change '%s'\n%s\n",
965 ldb_errstring(ldb),
966 ldb_ldif_message_string(ldb, op, LDB_CHANGETYPE_MODIFY, new_msg));
969 return ret;
972 /* apply one set of la_context changes */
973 static int la_do_mod_request(struct ldb_module *module, struct la_context *ac)
975 struct la_op_store *op;
977 for (op = ac->ops; op; op=op->next) {
978 int ret = la_do_op_request(module, ac, op);
979 if (ret != LDB_SUCCESS) {
980 if (ret != LDB_ERR_NO_SUCH_OBJECT) {
981 return ret;
986 return LDB_SUCCESS;
991 we hook into the transaction operations to allow us to
992 perform the linked attribute updates at the end of the whole
993 transaction. This allows a forward linked attribute to be created
994 before the target is created, as long as the target is created
995 in the same transaction
997 static int linked_attributes_start_transaction(struct ldb_module *module)
999 /* create our private structure for this transaction */
1000 struct la_private *la_private = talloc_get_type(ldb_module_get_private(module),
1001 struct la_private);
1002 talloc_free(la_private);
1003 la_private = talloc(module, struct la_private);
1004 if (la_private == NULL) {
1005 return LDB_ERR_OPERATIONS_ERROR;
1007 la_private->la_list = NULL;
1008 ldb_module_set_private(module, la_private);
1009 return ldb_next_start_trans(module);
1013 on prepare commit we loop over our queued la_context structures
1014 and apply each of them
1016 static int linked_attributes_prepare_commit(struct ldb_module *module)
1018 struct la_private *la_private =
1019 talloc_get_type(ldb_module_get_private(module), struct la_private);
1020 struct la_context *ac;
1022 if (!la_private) {
1023 /* prepare commit without begin_transaction - let someone else return the error, just don't segfault */
1024 return ldb_next_prepare_commit(module);
1026 /* walk the list backwards, to do the first entry first, as we
1027 * added the entries with DLIST_ADD() which puts them at the
1028 * start of the list */
1029 for (ac = la_private->la_list; ac && ac->next; ac=ac->next) ;
1031 for (; ac; ac=ac->prev) {
1032 int ret;
1033 ac->req = NULL;
1034 ret = la_do_mod_request(module, ac);
1035 if (ret != LDB_SUCCESS) {
1036 DEBUG(0,(__location__ ": Failed mod request ret=%d\n", ret));
1037 talloc_free(la_private);
1038 ldb_module_set_private(module, NULL);
1039 return ret;
1043 talloc_free(la_private);
1044 ldb_module_set_private(module, NULL);
1046 return ldb_next_prepare_commit(module);
1049 static int linked_attributes_del_transaction(struct ldb_module *module)
1051 struct la_private *la_private =
1052 talloc_get_type(ldb_module_get_private(module), struct la_private);
1053 talloc_free(la_private);
1054 ldb_module_set_private(module, NULL);
1055 return ldb_next_del_trans(module);
1059 _PUBLIC_ const struct ldb_module_ops ldb_linked_attributes_module_ops = {
1060 .name = "linked_attributes",
1061 .add = linked_attributes_add,
1062 .modify = linked_attributes_modify,
1063 .rename = linked_attributes_rename,
1064 .start_transaction = linked_attributes_start_transaction,
1065 .prepare_commit = linked_attributes_prepare_commit,
1066 .del_transaction = linked_attributes_del_transaction,