s3:registry: do not use regdb functions during db upgrade
[Samba/gebeck_regimport.git] / source4 / dsdb / samdb / ldb_modules / linked_attributes.c
blob3ad6f0ca8c42ea338f91e1feb0c88cfeabf581a4
1 /*
2 ldb database library
4 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2007
5 Copyright (C) Simo Sorce <idra@samba.org> 2008
6 Copyright (C) Matthieu Patou <mat@matws.net> 2011
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
23 * Name: ldb
25 * Component: ldb linked_attributes module
27 * Description: Module to ensure linked attribute pairs remain in sync
29 * Author: Andrew Bartlett
32 #include "includes.h"
33 #include "ldb_module.h"
34 #include "util/dlinklist.h"
35 #include "dsdb/samdb/samdb.h"
36 #include "librpc/gen_ndr/ndr_misc.h"
37 #include "dsdb/samdb/ldb_modules/util.h"
39 struct la_private {
40 struct la_context *la_list;
43 struct la_op_store {
44 struct la_op_store *next;
45 struct la_op_store *prev;
46 enum la_op {LA_OP_ADD, LA_OP_DEL} op;
47 struct GUID guid;
48 char *name;
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 *mod_dn;
63 struct replace_context *rc;
64 struct la_op_store *ops;
65 struct ldb_extended *op_response;
66 struct ldb_control **op_controls;
69 static struct la_context *linked_attributes_init(struct ldb_module *module,
70 struct ldb_request *req)
72 struct ldb_context *ldb;
73 struct la_context *ac;
75 ldb = ldb_module_get_ctx(module);
77 ac = talloc_zero(req, struct la_context);
78 if (ac == NULL) {
79 ldb_oom(ldb);
80 return NULL;
83 ac->schema = dsdb_get_schema(ldb, ac);
84 ac->module = module;
85 ac->req = req;
87 return ac;
91 turn a DN into a GUID
93 static int la_guid_from_dn(struct la_context *ac, struct ldb_dn *dn, struct GUID *guid)
95 NTSTATUS status;
96 int ret;
98 status = dsdb_get_extended_dn_guid(dn, guid, "GUID");
99 if (NT_STATUS_IS_OK(status)) {
100 return LDB_SUCCESS;
102 if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
103 DEBUG(4,(__location__ ": Unable to parse GUID for dn %s\n",
104 ldb_dn_get_linearized(dn)));
105 return ldb_operr(ldb_module_get_ctx(ac->module));
108 ret = dsdb_module_guid_by_dn(ac->module, dn, guid, ac->req);
109 if (ret != LDB_SUCCESS) {
110 DEBUG(4,(__location__ ": Failed to find GUID for dn %s\n",
111 ldb_dn_get_linearized(dn)));
112 return ret;
114 return LDB_SUCCESS;
118 /* Common routine to handle reading the attributes and creating a
119 * series of modify requests */
120 static int la_store_op(struct la_context *ac,
121 enum la_op op, struct ldb_val *dn,
122 const char *name)
124 struct ldb_context *ldb;
125 struct la_op_store *os;
126 struct ldb_dn *op_dn;
127 int ret;
129 ldb = ldb_module_get_ctx(ac->module);
131 op_dn = ldb_dn_from_ldb_val(ac, ldb, dn);
132 if (!op_dn) {
133 ldb_asprintf_errstring(ldb,
134 "could not parse attribute as a DN");
135 return LDB_ERR_INVALID_DN_SYNTAX;
138 os = talloc_zero(ac, struct la_op_store);
139 if (!os) {
140 return ldb_oom(ldb);
143 os->op = op;
145 ret = la_guid_from_dn(ac, op_dn, &os->guid);
146 talloc_free(op_dn);
147 if (ret == LDB_ERR_NO_SUCH_OBJECT && ac->req->operation == LDB_DELETE) {
148 /* we are deleting an object, and we've found it has a
149 * forward link to a target that no longer
150 * exists. This is not an error in the delete, and we
151 * should just not do the deferred delete of the
152 * target attribute
154 talloc_free(os);
155 return LDB_SUCCESS;
157 if (ret != LDB_SUCCESS) {
158 return ret;
161 os->name = talloc_strdup(os, name);
162 if (!os->name) {
163 return ldb_oom(ldb);
166 /* Do deletes before adds */
167 if (op == LA_OP_ADD) {
168 DLIST_ADD_END(ac->ops, os, struct la_op_store *);
169 } else {
170 /* By adding to the head of the list, we do deletes before
171 * adds when processing a replace */
172 DLIST_ADD(ac->ops, os);
175 return LDB_SUCCESS;
178 static int la_queue_mod_request(struct la_context *ac);
179 static int la_down_req(struct la_context *ac);
183 /* add */
184 static int linked_attributes_add(struct ldb_module *module, struct ldb_request *req)
186 struct ldb_context *ldb;
187 const struct dsdb_attribute *target_attr;
188 struct la_context *ac;
189 const char *attr_name;
190 struct ldb_control *ctrl;
191 unsigned int i, j;
192 int ret;
194 ldb = ldb_module_get_ctx(module);
196 if (ldb_dn_is_special(req->op.add.message->dn)) {
197 /* do not manipulate our control entries */
198 return ldb_next_request(module, req);
201 if (!(ctrl = ldb_request_get_control(req, DSDB_CONTROL_APPLY_LINKS))) {
202 /* don't do anything special for linked attributes, repl_meta_data has done it */
203 return ldb_next_request(module, req);
205 ctrl->critical = false;
207 ac = linked_attributes_init(module, req);
208 if (!ac) {
209 return ldb_operr(ldb);
212 if (!ac->schema) {
213 /* without schema, this doesn't make any sense */
214 talloc_free(ac);
215 return ldb_next_request(module, req);
218 /* Need to ensure we only have forward links being specified */
219 for (i=0; i < req->op.add.message->num_elements; i++) {
220 const struct ldb_message_element *el = &req->op.add.message->elements[i];
221 const struct dsdb_attribute *schema_attr
222 = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
223 if (!schema_attr) {
224 ldb_asprintf_errstring(ldb,
225 "%s: attribute %s is not a valid attribute in schema",
226 __FUNCTION__,
227 el->name);
228 return LDB_ERR_OBJECT_CLASS_VIOLATION;
231 /* this could be a link with no partner, in which case
232 there is no special work to do */
233 if (schema_attr->linkID == 0) {
234 continue;
237 /* this part of the code should only be handling forward links */
238 SMB_ASSERT((schema_attr->linkID & 1) == 0);
240 /* Even link IDs are for the originating attribute */
241 target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID ^ 1);
242 if (!target_attr) {
244 * windows 2003 has a broken schema where
245 * the definition of msDS-IsDomainFor
246 * is missing (which is supposed to be
247 * the backlink of the msDS-HasDomainNCs
248 * attribute
250 continue;
253 attr_name = target_attr->lDAPDisplayName;
255 for (j = 0; j < el->num_values; j++) {
256 ret = la_store_op(ac, LA_OP_ADD,
257 &el->values[j],
258 attr_name);
259 if (ret != LDB_SUCCESS) {
260 return ret;
265 /* if no linked attributes are present continue */
266 if (ac->ops == NULL) {
267 /* nothing to do for this module, proceed */
268 talloc_free(ac);
269 return ldb_next_request(module, req);
272 /* start with the original request */
273 return la_down_req(ac);
276 /* For a delete or rename, we need to find out what linked attributes
277 * are currently on this DN, and then deal with them. This is the
278 * callback to the base search */
280 static int la_mod_search_callback(struct ldb_request *req, struct ldb_reply *ares)
282 struct ldb_context *ldb;
283 const struct dsdb_attribute *schema_attr;
284 const struct dsdb_attribute *target_attr;
285 struct ldb_message_element *search_el;
286 struct replace_context *rc;
287 struct la_context *ac;
288 const char *attr_name;
289 unsigned int i, j;
290 int ret = LDB_SUCCESS;
292 ac = talloc_get_type(req->context, struct la_context);
293 ldb = ldb_module_get_ctx(ac->module);
294 rc = ac->rc;
296 if (!ares) {
297 return ldb_module_done(ac->req, NULL, NULL,
298 LDB_ERR_OPERATIONS_ERROR);
300 if (ares->error != LDB_SUCCESS) {
301 return ldb_module_done(ac->req, ares->controls,
302 ares->response, ares->error);
305 /* Only entries are interesting, and we only want the olddn */
306 switch (ares->type) {
307 case LDB_REPLY_ENTRY:
309 if (ldb_dn_compare(ares->message->dn, ac->req->op.mod.message->dn) != 0) {
310 ldb_asprintf_errstring(ldb,
311 "linked_attributes: %s is not the DN we were looking for",
312 ldb_dn_get_linearized(ares->message->dn));
313 /* Guh? We only asked for this DN */
314 talloc_free(ares);
315 return ldb_module_done(ac->req, NULL, NULL,
316 LDB_ERR_OPERATIONS_ERROR);
319 ac->mod_dn = talloc_steal(ac, ares->message->dn);
321 /* We don't populate 'rc' for ADD - it can't be deleting elements anyway */
322 for (i = 0; rc && i < rc->num_elements; i++) {
324 schema_attr = dsdb_attribute_by_lDAPDisplayName(ac->schema, rc->el[i].name);
325 if (!schema_attr) {
326 ldb_asprintf_errstring(ldb,
327 "%s: attribute %s is not a valid attribute in schema",
328 __FUNCTION__,
329 rc->el[i].name);
330 talloc_free(ares);
331 return ldb_module_done(ac->req, NULL, NULL,
332 LDB_ERR_OBJECT_CLASS_VIOLATION);
335 search_el = ldb_msg_find_element(ares->message,
336 rc->el[i].name);
338 /* See if this element already exists */
339 /* otherwise just ignore as
340 * the add has already been scheduled */
341 if ( ! search_el) {
342 continue;
345 target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID ^ 1);
346 if (!target_attr) {
348 * windows 2003 has a broken schema where
349 * the definition of msDS-IsDomainFor
350 * is missing (which is supposed to be
351 * the backlink of the msDS-HasDomainNCs
352 * attribute
354 continue;
356 attr_name = target_attr->lDAPDisplayName;
358 /* Now we know what was there, we can remove it for the re-add */
359 for (j = 0; j < search_el->num_values; j++) {
360 ret = la_store_op(ac, LA_OP_DEL,
361 &search_el->values[j],
362 attr_name);
363 if (ret != LDB_SUCCESS) {
364 talloc_free(ares);
365 return ldb_module_done(ac->req,
366 NULL, NULL, ret);
371 break;
373 case LDB_REPLY_REFERRAL:
374 /* ignore */
375 break;
377 case LDB_REPLY_DONE:
379 talloc_free(ares);
381 if (ac->req->operation == LDB_ADD) {
382 /* Start the modifies to the backlinks */
383 ret = la_queue_mod_request(ac);
385 if (ret != LDB_SUCCESS) {
386 return ldb_module_done(ac->req, NULL, NULL,
387 ret);
389 } else {
390 /* Start with the original request */
391 ret = la_down_req(ac);
392 if (ret != LDB_SUCCESS) {
393 return ldb_module_done(ac->req, NULL, NULL, ret);
396 return LDB_SUCCESS;
399 talloc_free(ares);
400 return ret;
404 /* modify */
405 static int linked_attributes_modify(struct ldb_module *module, struct ldb_request *req)
407 /* Look over list of modifications */
408 /* Find if any are for linked attributes */
409 /* Determine the effect of the modification */
410 /* Apply the modify to the linked entry */
412 struct ldb_context *ldb;
413 unsigned int i, j;
414 struct la_context *ac;
415 struct ldb_request *search_req;
416 const char **attrs;
417 struct ldb_control *ctrl;
418 int ret;
420 ldb = ldb_module_get_ctx(module);
422 if (ldb_dn_is_special(req->op.mod.message->dn)) {
423 /* do not manipulate our control entries */
424 return ldb_next_request(module, req);
427 if (!(ctrl = ldb_request_get_control(req, DSDB_CONTROL_APPLY_LINKS))) {
428 /* don't do anything special for linked attributes, repl_meta_data has done it */
429 return ldb_next_request(module, req);
431 ctrl->critical = false;
433 ac = linked_attributes_init(module, req);
434 if (!ac) {
435 return ldb_operr(ldb);
438 if (!ac->schema) {
439 /* without schema, this doesn't make any sense */
440 return ldb_next_request(module, req);
443 ac->rc = talloc_zero(ac, struct replace_context);
444 if (!ac->rc) {
445 return ldb_oom(ldb);
448 for (i=0; i < req->op.mod.message->num_elements; i++) {
449 bool store_el = false;
450 const char *attr_name;
451 const struct dsdb_attribute *target_attr;
452 const struct ldb_message_element *el = &req->op.mod.message->elements[i];
453 const struct dsdb_attribute *schema_attr
454 = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
455 if (!schema_attr) {
456 ldb_asprintf_errstring(ldb,
457 "%s: attribute %s is not a valid attribute in schema",
458 __FUNCTION__,
459 el->name);
460 return LDB_ERR_OBJECT_CLASS_VIOLATION;
462 /* We have a valid attribute, now find out if it is a forward link
463 (Even link IDs are for the originating attribute) */
464 if (schema_attr->linkID == 0) {
465 continue;
468 /* this part of the code should only be handling forward links */
469 SMB_ASSERT((schema_attr->linkID & 1) == 0);
471 /* Now find the target attribute */
472 target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID ^ 1);
473 if (!target_attr) {
475 * windows 2003 has a broken schema where
476 * the definition of msDS-IsDomainFor
477 * is missing (which is supposed to be
478 * the backlink of the msDS-HasDomainNCs
479 * attribute
481 continue;
484 attr_name = target_attr->lDAPDisplayName;
486 switch (el->flags & LDB_FLAG_MOD_MASK) {
487 case LDB_FLAG_MOD_REPLACE:
488 /* treat as just a normal add the delete part is handled by the callback */
489 store_el = true;
491 /* break intentionally missing */
493 case LDB_FLAG_MOD_ADD:
495 /* For each value being added, we need to setup the adds */
496 for (j = 0; j < el->num_values; j++) {
497 ret = la_store_op(ac, LA_OP_ADD,
498 &el->values[j],
499 attr_name);
500 if (ret != LDB_SUCCESS) {
501 return ret;
504 break;
506 case LDB_FLAG_MOD_DELETE:
508 if (el->num_values) {
509 /* For each value being deleted, we need to setup the delete */
510 for (j = 0; j < el->num_values; j++) {
511 ret = la_store_op(ac, LA_OP_DEL,
512 &el->values[j],
513 attr_name);
514 if (ret != LDB_SUCCESS) {
515 return ret;
518 } else {
519 /* Flag that there was a DELETE
520 * without a value specified, so we
521 * need to look for the old value */
522 store_el = true;
525 break;
528 if (store_el) {
529 struct ldb_message_element *search_el;
531 search_el = talloc_realloc(ac->rc, ac->rc->el,
532 struct ldb_message_element,
533 ac->rc->num_elements +1);
534 if (!search_el) {
535 return ldb_oom(ldb);
537 ac->rc->el = search_el;
539 ac->rc->el[ac->rc->num_elements] = *el;
540 ac->rc->num_elements++;
544 if (ac->ops || ac->rc->el) {
545 /* both replace and delete without values are handled in the callback
546 * after the search on the entry to be modified is performed */
548 attrs = talloc_array(ac->rc, const char *, ac->rc->num_elements + 1);
549 if (!attrs) {
550 return ldb_oom(ldb);
552 for (i = 0; ac->rc && i < ac->rc->num_elements; i++) {
553 attrs[i] = ac->rc->el[i].name;
555 attrs[i] = NULL;
557 /* The callback does all the hard work here */
558 ret = ldb_build_search_req(&search_req, ldb, ac,
559 req->op.mod.message->dn,
560 LDB_SCOPE_BASE,
561 "(objectClass=*)", attrs,
562 NULL,
563 ac, la_mod_search_callback,
564 req);
565 LDB_REQ_SET_LOCATION(search_req);
567 /* We need to figure out our own extended DN, to fill in as the backlink target */
568 if (ret == LDB_SUCCESS) {
569 ret = dsdb_request_add_controls(search_req,
570 DSDB_SEARCH_SHOW_DELETED |
571 DSDB_SEARCH_SHOW_EXTENDED_DN);
573 if (ret == LDB_SUCCESS) {
574 talloc_steal(search_req, attrs);
576 ret = ldb_next_request(module, search_req);
579 } else {
580 /* nothing to do for this module, proceed */
581 talloc_free(ac);
582 ret = ldb_next_request(module, req);
585 return ret;
588 static int linked_attributes_fix_links(struct ldb_module *module,
589 struct ldb_dn *old_dn, struct ldb_dn *new_dn,
590 struct ldb_message_element *el, struct dsdb_schema *schema,
591 const struct dsdb_attribute *schema_attr,
592 struct ldb_request *parent)
594 unsigned int i, j;
595 TALLOC_CTX *tmp_ctx = talloc_new(module);
596 struct ldb_context *ldb = ldb_module_get_ctx(module);
597 const struct dsdb_attribute *target;
598 const char *attrs[2];
599 int ret;
601 target = dsdb_attribute_by_linkID(schema, schema_attr->linkID ^ 1);
602 if (target == NULL) {
603 /* there is no counterpart link to change */
604 return LDB_SUCCESS;
607 attrs[0] = target->lDAPDisplayName;
608 attrs[1] = NULL;
610 for (i=0; i<el->num_values; i++) {
611 struct dsdb_dn *dsdb_dn;
612 struct ldb_result *res;
613 struct ldb_message *msg;
614 struct ldb_message_element *el2;
616 dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i], schema_attr->syntax->ldap_oid);
617 if (dsdb_dn == NULL) {
618 talloc_free(tmp_ctx);
619 return LDB_ERR_INVALID_DN_SYNTAX;
622 ret = dsdb_module_search_dn(module, tmp_ctx, &res, dsdb_dn->dn,
623 attrs,
624 DSDB_FLAG_NEXT_MODULE |
625 DSDB_SEARCH_SHOW_RECYCLED |
626 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
627 DSDB_SEARCH_REVEAL_INTERNALS, parent);
628 if (ret != LDB_SUCCESS) {
629 ldb_asprintf_errstring(ldb, "Linked attribute %s->%s between %s and %s - remote not found - %s",
630 el->name, target->lDAPDisplayName,
631 ldb_dn_get_linearized(old_dn),
632 ldb_dn_get_linearized(dsdb_dn->dn),
633 ldb_errstring(ldb));
634 talloc_free(tmp_ctx);
635 return ret;
637 msg = res->msgs[0];
639 if (msg->num_elements == 0) {
640 /* Forward link without backlink remaining - nothing to do here */
641 continue;
642 } else if (msg->num_elements != 1) {
643 ldb_asprintf_errstring(ldb, "Bad msg elements - got %u elements, expected one element to be returned in linked_attributes_fix_links for %s",
644 msg->num_elements, ldb_dn_get_linearized(msg->dn));
645 talloc_free(tmp_ctx);
646 return LDB_ERR_OPERATIONS_ERROR;
648 if (ldb_attr_cmp(msg->elements[0].name, target->lDAPDisplayName) != 0) {
649 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));
650 talloc_free(tmp_ctx);
651 return LDB_ERR_OPERATIONS_ERROR;
653 el2 = &msg->elements[0];
655 el2->flags = LDB_FLAG_MOD_REPLACE;
657 /* find our DN in the values */
658 for (j=0; j<el2->num_values; j++) {
659 struct dsdb_dn *dsdb_dn2;
660 dsdb_dn2 = dsdb_dn_parse(msg, ldb, &el2->values[j], target->syntax->ldap_oid);
661 if (dsdb_dn2 == NULL) {
662 talloc_free(tmp_ctx);
663 return LDB_ERR_INVALID_DN_SYNTAX;
665 if (ldb_dn_compare(old_dn, dsdb_dn2->dn) != 0) {
666 continue;
668 ret = ldb_dn_update_components(dsdb_dn2->dn, new_dn);
669 if (ret != LDB_SUCCESS) {
670 talloc_free(tmp_ctx);
671 return ret;
674 el2->values[j] = data_blob_string_const(
675 dsdb_dn_get_extended_linearized(el2->values, dsdb_dn2, 1));
678 ret = dsdb_check_single_valued_link(target, el2);
679 if (ret != LDB_SUCCESS) {
680 talloc_free(tmp_ctx);
681 return ret;
684 /* we may be putting multiple values in an attribute -
685 disable checking for this attribute */
686 el2->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
688 ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE, parent);
689 if (ret != LDB_SUCCESS) {
690 ldb_asprintf_errstring(ldb, "Linked attribute %s->%s between %s and %s - update failed - %s",
691 el->name, target->lDAPDisplayName,
692 ldb_dn_get_linearized(old_dn),
693 ldb_dn_get_linearized(dsdb_dn->dn),
694 ldb_errstring(ldb));
695 talloc_free(tmp_ctx);
696 return ret;
700 talloc_free(tmp_ctx);
701 return LDB_SUCCESS;
705 /* rename */
706 static int linked_attributes_rename(struct ldb_module *module, struct ldb_request *req)
708 struct ldb_result *res;
709 struct ldb_message *msg;
710 unsigned int i;
711 struct ldb_context *ldb = ldb_module_get_ctx(module);
712 struct dsdb_schema *schema;
713 int ret;
715 - load the current msg
716 - find any linked attributes
717 - if its a link then find the target object
718 - modify the target linked attributes with the new DN
720 ret = dsdb_module_search_dn(module, req, &res, req->op.rename.olddn,
721 NULL,
722 DSDB_FLAG_NEXT_MODULE |
723 DSDB_SEARCH_SHOW_RECYCLED, req);
724 if (ret != LDB_SUCCESS) {
725 return ret;
728 schema = dsdb_get_schema(ldb, res);
729 if (!schema) {
730 return ldb_oom(ldb);
733 msg = res->msgs[0];
735 for (i=0; i<msg->num_elements; i++) {
736 struct ldb_message_element *el = &msg->elements[i];
737 const struct dsdb_attribute *schema_attr
738 = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
739 if (!schema_attr || schema_attr->linkID == 0) {
740 continue;
742 ret = linked_attributes_fix_links(module, msg->dn, req->op.rename.newdn, el,
743 schema, schema_attr, req);
744 if (ret != LDB_SUCCESS) {
745 talloc_free(res);
746 return ret;
750 talloc_free(res);
752 return ldb_next_request(module, req);
756 /* queue a linked attributes modify request in the la_private
757 structure */
758 static int la_queue_mod_request(struct la_context *ac)
760 struct la_private *la_private =
761 talloc_get_type(ldb_module_get_private(ac->module), struct la_private);
763 if (la_private == NULL) {
764 ldb_debug(ldb_module_get_ctx(ac->module), LDB_DEBUG_ERROR, __location__ ": No la_private transaction setup\n");
765 return ldb_operr(ldb_module_get_ctx(ac->module));
768 talloc_steal(la_private, ac);
769 DLIST_ADD(la_private->la_list, ac);
771 return ldb_module_done(ac->req, ac->op_controls,
772 ac->op_response, LDB_SUCCESS);
775 /* Having done the original operation, then try to fix up all the linked attributes for modify and delete */
776 static int la_mod_del_callback(struct ldb_request *req, struct ldb_reply *ares)
778 struct la_context *ac;
779 struct ldb_context *ldb;
780 int ret;
782 ac = talloc_get_type(req->context, struct la_context);
783 ldb = ldb_module_get_ctx(ac->module);
785 if (!ares) {
786 return ldb_module_done(ac->req, NULL, NULL,
787 LDB_ERR_OPERATIONS_ERROR);
789 if (ares->error != LDB_SUCCESS) {
790 return ldb_module_done(ac->req, ares->controls,
791 ares->response, ares->error);
794 if (ares->type != LDB_REPLY_DONE) {
795 ldb_set_errstring(ldb,
796 "invalid ldb_reply_type in callback");
797 talloc_free(ares);
798 return ldb_module_done(ac->req, NULL, NULL,
799 LDB_ERR_OPERATIONS_ERROR);
802 ac->op_controls = talloc_steal(ac, ares->controls);
803 ac->op_response = talloc_steal(ac, ares->response);
805 /* If we have modfies to make, this is the time to do them for modify and delete */
806 ret = la_queue_mod_request(ac);
808 if (ret != LDB_SUCCESS) {
809 return ldb_module_done(ac->req, NULL, NULL, ret);
811 talloc_free(ares);
813 /* la_queue_mod_request has already sent the callbacks */
814 return LDB_SUCCESS;
818 /* Having done the original add, then try to fix up all the linked attributes
820 This is done after the add so the links can get the extended DNs correctly.
822 static int la_add_callback(struct ldb_request *req, struct ldb_reply *ares)
824 struct la_context *ac;
825 struct ldb_context *ldb;
826 int ret;
828 ac = talloc_get_type(req->context, struct la_context);
829 ldb = ldb_module_get_ctx(ac->module);
831 if (!ares) {
832 return ldb_module_done(ac->req, NULL, NULL,
833 LDB_ERR_OPERATIONS_ERROR);
835 if (ares->error != LDB_SUCCESS) {
836 return ldb_module_done(ac->req, ares->controls,
837 ares->response, ares->error);
840 if (ares->type != LDB_REPLY_DONE) {
841 ldb_set_errstring(ldb,
842 "invalid ldb_reply_type in callback");
843 talloc_free(ares);
844 return ldb_module_done(ac->req, NULL, NULL,
845 LDB_ERR_OPERATIONS_ERROR);
848 if (ac->ops) {
849 struct ldb_request *search_req;
850 static const char *attrs[] = { NULL };
852 /* The callback does all the hard work here - we need
853 * the objectGUID and SID of the added record */
854 ret = ldb_build_search_req(&search_req, ldb, ac,
855 ac->req->op.add.message->dn,
856 LDB_SCOPE_BASE,
857 "(objectClass=*)", attrs,
858 NULL,
859 ac, la_mod_search_callback,
860 ac->req);
861 LDB_REQ_SET_LOCATION(search_req);
863 if (ret == LDB_SUCCESS) {
864 ret = dsdb_request_add_controls(search_req,
865 DSDB_SEARCH_SHOW_DELETED |
866 DSDB_SEARCH_SHOW_EXTENDED_DN);
868 if (ret != LDB_SUCCESS) {
869 return ldb_module_done(ac->req, NULL, NULL,
870 ret);
873 ac->op_controls = talloc_steal(ac, ares->controls);
874 ac->op_response = talloc_steal(ac, ares->response);
876 return ldb_next_request(ac->module, search_req);
878 } else {
879 return ldb_module_done(ac->req, ares->controls,
880 ares->response, ares->error);
884 /* Reconstruct the original request, but pointing at our local callback to finish things off */
885 static int la_down_req(struct la_context *ac)
887 struct ldb_request *down_req;
888 struct ldb_context *ldb;
889 int ret;
891 ldb = ldb_module_get_ctx(ac->module);
893 switch (ac->req->operation) {
894 case LDB_ADD:
895 ret = ldb_build_add_req(&down_req, ldb, ac,
896 ac->req->op.add.message,
897 ac->req->controls,
898 ac, la_add_callback,
899 ac->req);
900 LDB_REQ_SET_LOCATION(down_req);
901 break;
902 case LDB_MODIFY:
903 ret = ldb_build_mod_req(&down_req, ldb, ac,
904 ac->req->op.mod.message,
905 ac->req->controls,
906 ac, la_mod_del_callback,
907 ac->req);
908 LDB_REQ_SET_LOCATION(down_req);
909 break;
910 default:
911 ret = LDB_ERR_OPERATIONS_ERROR;
913 if (ret != LDB_SUCCESS) {
914 return ret;
917 return ldb_next_request(ac->module, down_req);
921 use the GUID part of an extended DN to find the target DN, in case
922 it has moved
924 static int la_find_dn_target(struct ldb_module *module, struct la_context *ac,
925 struct GUID *guid, struct ldb_dn **dn)
927 return dsdb_module_dn_by_guid(ac->module, ac, guid, dn, ac->req);
930 /* apply one la_context op change */
931 static int la_do_op_request(struct ldb_module *module, struct la_context *ac, struct la_op_store *op)
933 struct ldb_message_element *ret_el;
934 struct ldb_message *new_msg;
935 struct ldb_context *ldb;
936 int ret;
938 if (ac->mod_dn == NULL) {
939 /* we didn't find the DN that we searched for */
940 return LDB_SUCCESS;
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 ret_el->values[0] = data_blob_string_const(ldb_dn_get_extended_linearized(new_msg, ac->mod_dn, 1));
973 /* a backlink should never be single valued. Unfortunately the
974 exchange schema has a attribute
975 msExchBridgeheadedLocalConnectorsDNBL which is single
976 valued and a backlink. We need to cope with that by
977 ignoring the single value flag */
978 ret_el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
980 #if 0
981 ldb_debug(ldb, LDB_DEBUG_WARNING,
982 "link on %s %s: %s %s\n",
983 ldb_dn_get_linearized(new_msg->dn), ret_el->name,
984 ret_el->values[0].data, ac->ops->op == LA_OP_ADD ? "added" : "deleted");
985 #endif
987 if (DEBUGLVL(4)) {
988 DEBUG(4,("Applying linked attribute change:\n%s\n",
989 ldb_ldif_message_string(ldb, op, LDB_CHANGETYPE_MODIFY, new_msg)));
992 ret = dsdb_module_modify(module, new_msg, DSDB_FLAG_NEXT_MODULE, ac->req);
993 if (ret != LDB_SUCCESS) {
994 ldb_debug(ldb, LDB_DEBUG_WARNING, __location__ ": failed to apply linked attribute change '%s'\n%s\n",
995 ldb_errstring(ldb),
996 ldb_ldif_message_string(ldb, op, LDB_CHANGETYPE_MODIFY, new_msg));
999 return ret;
1002 /* apply one set of la_context changes */
1003 static int la_do_mod_request(struct ldb_module *module, struct la_context *ac)
1005 struct la_op_store *op;
1007 for (op = ac->ops; op; op=op->next) {
1008 int ret = la_do_op_request(module, ac, op);
1009 if (ret != LDB_SUCCESS) {
1010 if (ret != LDB_ERR_NO_SUCH_OBJECT) {
1011 return ret;
1016 return LDB_SUCCESS;
1021 we hook into the transaction operations to allow us to
1022 perform the linked attribute updates at the end of the whole
1023 transaction. This allows a forward linked attribute to be created
1024 before the target is created, as long as the target is created
1025 in the same transaction
1027 static int linked_attributes_start_transaction(struct ldb_module *module)
1029 /* create our private structure for this transaction */
1030 struct la_private *la_private = talloc_get_type(ldb_module_get_private(module),
1031 struct la_private);
1032 talloc_free(la_private);
1033 la_private = talloc(module, struct la_private);
1034 if (la_private == NULL) {
1035 return ldb_oom(ldb_module_get_ctx(module));
1037 la_private->la_list = NULL;
1038 ldb_module_set_private(module, la_private);
1039 return ldb_next_start_trans(module);
1043 on prepare commit we loop over our queued la_context structures
1044 and apply each of them
1046 static int linked_attributes_prepare_commit(struct ldb_module *module)
1048 struct la_private *la_private =
1049 talloc_get_type(ldb_module_get_private(module), struct la_private);
1050 struct la_context *ac;
1052 if (!la_private) {
1053 /* prepare commit without begin_transaction - let someone else return the error, just don't segfault */
1054 return ldb_next_prepare_commit(module);
1056 /* walk the list backwards, to do the first entry first, as we
1057 * added the entries with DLIST_ADD() which puts them at the
1058 * start of the list */
1060 /* Start at the end of the list - so we can start
1061 * there, but ensure we don't create a loop by NULLing
1062 * it out in the first element */
1063 ac = DLIST_TAIL(la_private->la_list);
1065 for (; ac; ac=DLIST_PREV(ac)) {
1066 int ret;
1067 ac->req = NULL;
1068 ret = la_do_mod_request(module, ac);
1069 if (ret != LDB_SUCCESS) {
1070 DEBUG(0,(__location__ ": Failed mod request ret=%d\n", ret));
1071 talloc_free(la_private);
1072 ldb_module_set_private(module, NULL);
1073 return ret;
1077 talloc_free(la_private);
1078 ldb_module_set_private(module, NULL);
1080 return ldb_next_prepare_commit(module);
1083 static int linked_attributes_del_transaction(struct ldb_module *module)
1085 struct la_private *la_private =
1086 talloc_get_type(ldb_module_get_private(module), struct la_private);
1087 talloc_free(la_private);
1088 ldb_module_set_private(module, NULL);
1089 return ldb_next_del_trans(module);
1093 static const struct ldb_module_ops ldb_linked_attributes_module_ops = {
1094 .name = "linked_attributes",
1095 .add = linked_attributes_add,
1096 .modify = linked_attributes_modify,
1097 .rename = linked_attributes_rename,
1098 .start_transaction = linked_attributes_start_transaction,
1099 .prepare_commit = linked_attributes_prepare_commit,
1100 .del_transaction = linked_attributes_del_transaction,
1103 int ldb_linked_attributes_module_init(const char *version)
1105 LDB_MODULE_CHECK_VERSION(version);
1106 return ldb_register_module(&ldb_linked_attributes_module_ops);