s4:dsdb/subtree_delete: do the recursive delete AS_SYSTEM/TRUSTED (bug #7711)
[Samba/gebeck_regimport.git] / source4 / dsdb / samdb / ldb_modules / linked_attributes.c
blob5ebf4efed41e0868323bb6fe5f97a1fb3d7fe335
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;
68 * For futur use
69 * will tell which GC to use for resolving links
71 char *gc_dns_name;
75 static int handle_verify_name_control(TALLOC_CTX *ctx, struct ldb_context *ldb,
76 struct ldb_control *control, struct la_context *ac)
79 * If we are a GC let's remove the control,
80 * if there is a specified GC check that is us.
82 struct ldb_verify_name_control *lvnc = (struct ldb_verify_name_control *)control->data;
83 if (samdb_is_gc(ldb)) {
84 /* Because we can't easily talloc a struct ldb_dn*/
85 struct ldb_dn **dn = talloc_array(ctx, struct ldb_dn *, 1);
86 int ret = samdb_server_reference_dn(ldb, ctx, dn);
87 const char *dns;
89 if (ret != LDB_SUCCESS) {
90 return ldb_operr(ldb);
93 dns = samdb_dn_to_dnshostname(ldb, ctx, *dn);
94 if (!dns) {
95 return ldb_operr(ldb);
97 if (!lvnc->gc || strcasecmp(dns, lvnc->gc) == 0) {
98 if (!ldb_save_controls(control, ctx, NULL)) {
99 return ldb_operr(ldb);
101 } else {
102 control->critical = true;
104 talloc_free(dn);
105 } else {
106 /* For the moment we don't remove the control is this case in order
107 * to fail the request. It's better than having the client thinking
108 * that we honnor its control.
109 * Hopefully only a very small set of usecase should hit this problem.
111 if (lvnc->gc) {
112 ac->gc_dns_name = talloc_strdup(ac, lvnc->gc);
114 control->critical = true;
117 return LDB_SUCCESS;
120 static struct la_context *linked_attributes_init(struct ldb_module *module,
121 struct ldb_request *req)
123 struct ldb_context *ldb;
124 struct la_context *ac;
126 ldb = ldb_module_get_ctx(module);
128 ac = talloc_zero(req, struct la_context);
129 if (ac == NULL) {
130 ldb_oom(ldb);
131 return NULL;
134 ac->schema = dsdb_get_schema(ldb, ac);
135 ac->module = module;
136 ac->req = req;
138 return ac;
142 turn a DN into a GUID
144 static int la_guid_from_dn(struct la_context *ac, struct ldb_dn *dn, struct GUID *guid)
146 NTSTATUS status;
147 int ret;
149 status = dsdb_get_extended_dn_guid(dn, guid, "GUID");
150 if (NT_STATUS_IS_OK(status)) {
151 return LDB_SUCCESS;
153 if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
154 DEBUG(4,(__location__ ": Unable to parse GUID for dn %s\n",
155 ldb_dn_get_linearized(dn)));
156 return ldb_operr(ldb_module_get_ctx(ac->module));
159 ret = dsdb_module_guid_by_dn(ac->module, dn, guid, ac->req);
160 if (ret != LDB_SUCCESS) {
161 DEBUG(4,(__location__ ": Failed to find GUID for dn %s\n",
162 ldb_dn_get_linearized(dn)));
163 return ret;
165 return LDB_SUCCESS;
169 /* Common routine to handle reading the attributes and creating a
170 * series of modify requests */
171 static int la_store_op(struct la_context *ac,
172 enum la_op op, struct ldb_val *dn,
173 const char *name)
175 struct ldb_context *ldb;
176 struct la_op_store *os;
177 struct ldb_dn *op_dn;
178 int ret;
180 ldb = ldb_module_get_ctx(ac->module);
182 op_dn = ldb_dn_from_ldb_val(ac, ldb, dn);
183 if (!op_dn) {
184 ldb_asprintf_errstring(ldb,
185 "could not parse attribute as a DN");
186 return LDB_ERR_INVALID_DN_SYNTAX;
189 os = talloc_zero(ac, struct la_op_store);
190 if (!os) {
191 return ldb_oom(ldb);
194 os->op = op;
196 ret = la_guid_from_dn(ac, op_dn, &os->guid);
197 talloc_free(op_dn);
198 if (ret == LDB_ERR_NO_SUCH_OBJECT && ac->req->operation == LDB_DELETE) {
199 /* we are deleting an object, and we've found it has a
200 * forward link to a target that no longer
201 * exists. This is not an error in the delete, and we
202 * should just not do the deferred delete of the
203 * target attribute
205 talloc_free(os);
206 return LDB_SUCCESS;
208 if (ret != LDB_SUCCESS) {
209 return ret;
212 os->name = talloc_strdup(os, name);
213 if (!os->name) {
214 return ldb_oom(ldb);
217 /* Do deletes before adds */
218 if (op == LA_OP_ADD) {
219 DLIST_ADD_END(ac->ops, os, struct la_op_store *);
220 } else {
221 /* By adding to the head of the list, we do deletes before
222 * adds when processing a replace */
223 DLIST_ADD(ac->ops, os);
226 return LDB_SUCCESS;
229 static int la_queue_mod_request(struct la_context *ac);
230 static int la_down_req(struct la_context *ac);
234 /* add */
235 static int linked_attributes_add(struct ldb_module *module, struct ldb_request *req)
237 struct ldb_context *ldb;
238 const struct dsdb_attribute *target_attr;
239 struct la_context *ac;
240 const char *attr_name;
241 struct ldb_control *ctrl;
242 unsigned int i, j;
243 struct ldb_control *control;
244 int ret;
246 ldb = ldb_module_get_ctx(module);
248 if (ldb_dn_is_special(req->op.add.message->dn)) {
249 /* do not manipulate our control entries */
250 return ldb_next_request(module, req);
253 ac = linked_attributes_init(module, req);
254 if (!ac) {
255 return ldb_operr(ldb);
258 control = ldb_request_get_control(req, LDB_CONTROL_VERIFY_NAME_OID);
259 if (control != NULL && control->data != NULL) {
260 ret = handle_verify_name_control(req, ldb, control, ac);
261 if (ret != LDB_SUCCESS) {
262 return ldb_operr(ldb);
266 if (!(ctrl = ldb_request_get_control(req, DSDB_CONTROL_APPLY_LINKS))) {
267 /* don't do anything special for linked attributes, repl_meta_data has done it */
268 talloc_free(ac);
269 return ldb_next_request(module, req);
271 ctrl->critical = false;
273 if (!ac->schema) {
274 /* without schema, this doesn't make any sense */
275 talloc_free(ac);
276 return ldb_next_request(module, req);
280 /* Need to ensure we only have forward links being specified */
281 for (i=0; i < req->op.add.message->num_elements; i++) {
282 const struct ldb_message_element *el = &req->op.add.message->elements[i];
283 const struct dsdb_attribute *schema_attr
284 = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
285 if (!schema_attr) {
286 ldb_asprintf_errstring(ldb,
287 "%s: attribute %s is not a valid attribute in schema",
288 __FUNCTION__,
289 el->name);
290 return LDB_ERR_OBJECT_CLASS_VIOLATION;
293 /* this could be a link with no partner, in which case
294 there is no special work to do */
295 if (schema_attr->linkID == 0) {
296 continue;
299 /* this part of the code should only be handling forward links */
300 SMB_ASSERT((schema_attr->linkID & 1) == 0);
302 /* Even link IDs are for the originating attribute */
303 target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID ^ 1);
304 if (!target_attr) {
306 * windows 2003 has a broken schema where
307 * the definition of msDS-IsDomainFor
308 * is missing (which is supposed to be
309 * the backlink of the msDS-HasDomainNCs
310 * attribute
312 continue;
315 attr_name = target_attr->lDAPDisplayName;
317 for (j = 0; j < el->num_values; j++) {
318 ret = la_store_op(ac, LA_OP_ADD,
319 &el->values[j],
320 attr_name);
321 if (ret != LDB_SUCCESS) {
322 return ret;
327 /* if no linked attributes are present continue */
328 if (ac->ops == NULL) {
329 /* nothing to do for this module, proceed */
330 talloc_free(ac);
331 return ldb_next_request(module, req);
334 /* start with the original request */
335 return la_down_req(ac);
338 /* For a delete or rename, we need to find out what linked attributes
339 * are currently on this DN, and then deal with them. This is the
340 * callback to the base search */
342 static int la_mod_search_callback(struct ldb_request *req, struct ldb_reply *ares)
344 struct ldb_context *ldb;
345 const struct dsdb_attribute *schema_attr;
346 const struct dsdb_attribute *target_attr;
347 struct ldb_message_element *search_el;
348 struct replace_context *rc;
349 struct la_context *ac;
350 const char *attr_name;
351 unsigned int i, j;
352 int ret = LDB_SUCCESS;
354 ac = talloc_get_type(req->context, struct la_context);
355 ldb = ldb_module_get_ctx(ac->module);
356 rc = ac->rc;
358 if (!ares) {
359 return ldb_module_done(ac->req, NULL, NULL,
360 LDB_ERR_OPERATIONS_ERROR);
362 if (ares->error != LDB_SUCCESS) {
363 return ldb_module_done(ac->req, ares->controls,
364 ares->response, ares->error);
367 /* Only entries are interesting, and we only want the olddn */
368 switch (ares->type) {
369 case LDB_REPLY_ENTRY:
371 if (ldb_dn_compare(ares->message->dn, ac->req->op.mod.message->dn) != 0) {
372 ldb_asprintf_errstring(ldb,
373 "linked_attributes: %s is not the DN we were looking for",
374 ldb_dn_get_linearized(ares->message->dn));
375 /* Guh? We only asked for this DN */
376 talloc_free(ares);
377 return ldb_module_done(ac->req, NULL, NULL,
378 LDB_ERR_OPERATIONS_ERROR);
381 ac->mod_dn = talloc_steal(ac, ares->message->dn);
383 /* We don't populate 'rc' for ADD - it can't be deleting elements anyway */
384 for (i = 0; rc && i < rc->num_elements; i++) {
386 schema_attr = dsdb_attribute_by_lDAPDisplayName(ac->schema, rc->el[i].name);
387 if (!schema_attr) {
388 ldb_asprintf_errstring(ldb,
389 "%s: attribute %s is not a valid attribute in schema",
390 __FUNCTION__,
391 rc->el[i].name);
392 talloc_free(ares);
393 return ldb_module_done(ac->req, NULL, NULL,
394 LDB_ERR_OBJECT_CLASS_VIOLATION);
397 search_el = ldb_msg_find_element(ares->message,
398 rc->el[i].name);
400 /* See if this element already exists */
401 /* otherwise just ignore as
402 * the add has already been scheduled */
403 if ( ! search_el) {
404 continue;
407 target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID ^ 1);
408 if (!target_attr) {
410 * windows 2003 has a broken schema where
411 * the definition of msDS-IsDomainFor
412 * is missing (which is supposed to be
413 * the backlink of the msDS-HasDomainNCs
414 * attribute
416 continue;
418 attr_name = target_attr->lDAPDisplayName;
420 /* Now we know what was there, we can remove it for the re-add */
421 for (j = 0; j < search_el->num_values; j++) {
422 ret = la_store_op(ac, LA_OP_DEL,
423 &search_el->values[j],
424 attr_name);
425 if (ret != LDB_SUCCESS) {
426 talloc_free(ares);
427 return ldb_module_done(ac->req,
428 NULL, NULL, ret);
433 break;
435 case LDB_REPLY_REFERRAL:
436 /* ignore */
437 break;
439 case LDB_REPLY_DONE:
441 talloc_free(ares);
443 if (ac->req->operation == LDB_ADD) {
444 /* Start the modifies to the backlinks */
445 ret = la_queue_mod_request(ac);
447 if (ret != LDB_SUCCESS) {
448 return ldb_module_done(ac->req, NULL, NULL,
449 ret);
451 } else {
452 /* Start with the original request */
453 ret = la_down_req(ac);
454 if (ret != LDB_SUCCESS) {
455 return ldb_module_done(ac->req, NULL, NULL, ret);
458 return LDB_SUCCESS;
461 talloc_free(ares);
462 return ret;
466 /* modify */
467 static int linked_attributes_modify(struct ldb_module *module, struct ldb_request *req)
469 /* Look over list of modifications */
470 /* Find if any are for linked attributes */
471 /* Determine the effect of the modification */
472 /* Apply the modify to the linked entry */
474 struct ldb_control *control;
475 struct ldb_context *ldb;
476 unsigned int i, j;
477 struct la_context *ac;
478 struct ldb_request *search_req;
479 const char **attrs;
480 struct ldb_control *ctrl;
481 int ret;
483 ldb = ldb_module_get_ctx(module);
485 if (ldb_dn_is_special(req->op.mod.message->dn)) {
486 /* do not manipulate our control entries */
487 return ldb_next_request(module, req);
490 ac = linked_attributes_init(module, req);
491 if (!ac) {
492 return ldb_operr(ldb);
495 control = ldb_request_get_control(req, LDB_CONTROL_VERIFY_NAME_OID);
496 if (control != NULL && control->data != NULL) {
497 ret = handle_verify_name_control(req, ldb, control, ac);
498 if (ret != LDB_SUCCESS) {
499 return ldb_operr(ldb);
503 if (!(ctrl = ldb_request_get_control(req, DSDB_CONTROL_APPLY_LINKS))) {
504 /* don't do anything special for linked attributes, repl_meta_data has done it */
505 talloc_free(ac);
506 return ldb_next_request(module, req);
508 ctrl->critical = false;
510 if (!ac->schema) {
511 /* without schema, this doesn't make any sense */
512 return ldb_next_request(module, req);
515 ac->rc = talloc_zero(ac, struct replace_context);
516 if (!ac->rc) {
517 return ldb_oom(ldb);
520 for (i=0; i < req->op.mod.message->num_elements; i++) {
521 bool store_el = false;
522 const char *attr_name;
523 const struct dsdb_attribute *target_attr;
524 const struct ldb_message_element *el = &req->op.mod.message->elements[i];
525 const struct dsdb_attribute *schema_attr
526 = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
527 if (!schema_attr) {
528 ldb_asprintf_errstring(ldb,
529 "%s: attribute %s is not a valid attribute in schema",
530 __FUNCTION__,
531 el->name);
532 return LDB_ERR_OBJECT_CLASS_VIOLATION;
534 /* We have a valid attribute, now find out if it is a forward link
535 (Even link IDs are for the originating attribute) */
536 if (schema_attr->linkID == 0) {
537 continue;
540 /* this part of the code should only be handling forward links */
541 SMB_ASSERT((schema_attr->linkID & 1) == 0);
543 /* Now find the target attribute */
544 target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID ^ 1);
545 if (!target_attr) {
547 * windows 2003 has a broken schema where
548 * the definition of msDS-IsDomainFor
549 * is missing (which is supposed to be
550 * the backlink of the msDS-HasDomainNCs
551 * attribute
553 continue;
556 attr_name = target_attr->lDAPDisplayName;
558 switch (el->flags & LDB_FLAG_MOD_MASK) {
559 case LDB_FLAG_MOD_REPLACE:
560 /* treat as just a normal add the delete part is handled by the callback */
561 store_el = true;
563 /* break intentionally missing */
565 case LDB_FLAG_MOD_ADD:
567 /* For each value being added, we need to setup the adds */
568 for (j = 0; j < el->num_values; j++) {
569 ret = la_store_op(ac, LA_OP_ADD,
570 &el->values[j],
571 attr_name);
572 if (ret != LDB_SUCCESS) {
573 return ret;
576 break;
578 case LDB_FLAG_MOD_DELETE:
580 if (el->num_values) {
581 /* For each value being deleted, we need to setup the delete */
582 for (j = 0; j < el->num_values; j++) {
583 ret = la_store_op(ac, LA_OP_DEL,
584 &el->values[j],
585 attr_name);
586 if (ret != LDB_SUCCESS) {
587 return ret;
590 } else {
591 /* Flag that there was a DELETE
592 * without a value specified, so we
593 * need to look for the old value */
594 store_el = true;
597 break;
600 if (store_el) {
601 struct ldb_message_element *search_el;
603 search_el = talloc_realloc(ac->rc, ac->rc->el,
604 struct ldb_message_element,
605 ac->rc->num_elements +1);
606 if (!search_el) {
607 return ldb_oom(ldb);
609 ac->rc->el = search_el;
611 ac->rc->el[ac->rc->num_elements] = *el;
612 ac->rc->num_elements++;
616 if (ac->ops || ac->rc->el) {
617 /* both replace and delete without values are handled in the callback
618 * after the search on the entry to be modified is performed */
620 attrs = talloc_array(ac->rc, const char *, ac->rc->num_elements + 1);
621 if (!attrs) {
622 return ldb_oom(ldb);
624 for (i = 0; ac->rc && i < ac->rc->num_elements; i++) {
625 attrs[i] = ac->rc->el[i].name;
627 attrs[i] = NULL;
629 /* The callback does all the hard work here */
630 ret = ldb_build_search_req(&search_req, ldb, ac,
631 req->op.mod.message->dn,
632 LDB_SCOPE_BASE,
633 "(objectClass=*)", attrs,
634 NULL,
635 ac, la_mod_search_callback,
636 req);
637 LDB_REQ_SET_LOCATION(search_req);
639 /* We need to figure out our own extended DN, to fill in as the backlink target */
640 if (ret == LDB_SUCCESS) {
641 ret = dsdb_request_add_controls(search_req,
642 DSDB_SEARCH_SHOW_DELETED |
643 DSDB_SEARCH_SHOW_EXTENDED_DN);
645 if (ret == LDB_SUCCESS) {
646 talloc_steal(search_req, attrs);
648 ret = ldb_next_request(module, search_req);
651 } else {
652 /* nothing to do for this module, proceed */
653 talloc_free(ac);
654 ret = ldb_next_request(module, req);
657 return ret;
660 static int linked_attributes_fix_links(struct ldb_module *module,
661 struct ldb_dn *old_dn, struct ldb_dn *new_dn,
662 struct ldb_message_element *el, struct dsdb_schema *schema,
663 const struct dsdb_attribute *schema_attr,
664 struct ldb_request *parent)
666 unsigned int i, j;
667 TALLOC_CTX *tmp_ctx = talloc_new(module);
668 struct ldb_context *ldb = ldb_module_get_ctx(module);
669 const struct dsdb_attribute *target;
670 const char *attrs[2];
671 int ret;
673 target = dsdb_attribute_by_linkID(schema, schema_attr->linkID ^ 1);
674 if (target == NULL) {
675 /* there is no counterpart link to change */
676 return LDB_SUCCESS;
679 attrs[0] = target->lDAPDisplayName;
680 attrs[1] = NULL;
682 for (i=0; i<el->num_values; i++) {
683 struct dsdb_dn *dsdb_dn;
684 struct ldb_result *res;
685 struct ldb_message *msg;
686 struct ldb_message_element *el2;
688 dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i], schema_attr->syntax->ldap_oid);
689 if (dsdb_dn == NULL) {
690 talloc_free(tmp_ctx);
691 return LDB_ERR_INVALID_DN_SYNTAX;
694 ret = dsdb_module_search_dn(module, tmp_ctx, &res, dsdb_dn->dn,
695 attrs,
696 DSDB_FLAG_NEXT_MODULE |
697 DSDB_SEARCH_SHOW_RECYCLED |
698 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
699 DSDB_SEARCH_REVEAL_INTERNALS, parent);
700 if (ret != LDB_SUCCESS) {
701 ldb_asprintf_errstring(ldb, "Linked attribute %s->%s between %s and %s - remote not found - %s",
702 el->name, target->lDAPDisplayName,
703 ldb_dn_get_linearized(old_dn),
704 ldb_dn_get_linearized(dsdb_dn->dn),
705 ldb_errstring(ldb));
706 talloc_free(tmp_ctx);
707 return ret;
709 msg = res->msgs[0];
711 if (msg->num_elements == 0) {
712 /* Forward link without backlink remaining - nothing to do here */
713 continue;
714 } else if (msg->num_elements != 1) {
715 ldb_asprintf_errstring(ldb, "Bad msg elements - got %u elements, expected one element to be returned in linked_attributes_fix_links for %s",
716 msg->num_elements, ldb_dn_get_linearized(msg->dn));
717 talloc_free(tmp_ctx);
718 return LDB_ERR_OPERATIONS_ERROR;
720 if (ldb_attr_cmp(msg->elements[0].name, target->lDAPDisplayName) != 0) {
721 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));
722 talloc_free(tmp_ctx);
723 return LDB_ERR_OPERATIONS_ERROR;
725 el2 = &msg->elements[0];
727 el2->flags = LDB_FLAG_MOD_REPLACE;
729 /* find our DN in the values */
730 for (j=0; j<el2->num_values; j++) {
731 struct dsdb_dn *dsdb_dn2;
732 dsdb_dn2 = dsdb_dn_parse(msg, ldb, &el2->values[j], target->syntax->ldap_oid);
733 if (dsdb_dn2 == NULL) {
734 talloc_free(tmp_ctx);
735 return LDB_ERR_INVALID_DN_SYNTAX;
737 if (ldb_dn_compare(old_dn, dsdb_dn2->dn) != 0) {
738 continue;
740 ret = ldb_dn_update_components(dsdb_dn2->dn, new_dn);
741 if (ret != LDB_SUCCESS) {
742 talloc_free(tmp_ctx);
743 return ret;
746 el2->values[j] = data_blob_string_const(
747 dsdb_dn_get_extended_linearized(el2->values, dsdb_dn2, 1));
750 ret = dsdb_check_single_valued_link(target, el2);
751 if (ret != LDB_SUCCESS) {
752 talloc_free(tmp_ctx);
753 return ret;
756 /* we may be putting multiple values in an attribute -
757 disable checking for this attribute */
758 el2->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
760 ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE, parent);
761 if (ret != LDB_SUCCESS) {
762 ldb_asprintf_errstring(ldb, "Linked attribute %s->%s between %s and %s - update failed - %s",
763 el->name, target->lDAPDisplayName,
764 ldb_dn_get_linearized(old_dn),
765 ldb_dn_get_linearized(dsdb_dn->dn),
766 ldb_errstring(ldb));
767 talloc_free(tmp_ctx);
768 return ret;
772 talloc_free(tmp_ctx);
773 return LDB_SUCCESS;
777 /* rename */
778 static int linked_attributes_rename(struct ldb_module *module, struct ldb_request *req)
780 struct ldb_result *res;
781 struct ldb_message *msg;
782 unsigned int i;
783 struct ldb_context *ldb = ldb_module_get_ctx(module);
784 struct dsdb_schema *schema;
785 int ret;
787 - load the current msg
788 - find any linked attributes
789 - if its a link then find the target object
790 - modify the target linked attributes with the new DN
792 ret = dsdb_module_search_dn(module, req, &res, req->op.rename.olddn,
793 NULL,
794 DSDB_FLAG_NEXT_MODULE |
795 DSDB_SEARCH_SHOW_RECYCLED, req);
796 if (ret != LDB_SUCCESS) {
797 return ret;
800 schema = dsdb_get_schema(ldb, res);
801 if (!schema) {
802 return ldb_oom(ldb);
805 msg = res->msgs[0];
807 for (i=0; i<msg->num_elements; i++) {
808 struct ldb_message_element *el = &msg->elements[i];
809 const struct dsdb_attribute *schema_attr
810 = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
811 if (!schema_attr || schema_attr->linkID == 0) {
812 continue;
814 ret = linked_attributes_fix_links(module, msg->dn, req->op.rename.newdn, el,
815 schema, schema_attr, req);
816 if (ret != LDB_SUCCESS) {
817 talloc_free(res);
818 return ret;
822 talloc_free(res);
824 return ldb_next_request(module, req);
828 /* queue a linked attributes modify request in the la_private
829 structure */
830 static int la_queue_mod_request(struct la_context *ac)
832 struct la_private *la_private =
833 talloc_get_type(ldb_module_get_private(ac->module), struct la_private);
835 if (la_private == NULL) {
836 ldb_debug(ldb_module_get_ctx(ac->module), LDB_DEBUG_ERROR, __location__ ": No la_private transaction setup\n");
837 return ldb_operr(ldb_module_get_ctx(ac->module));
840 talloc_steal(la_private, ac);
841 DLIST_ADD(la_private->la_list, ac);
843 return ldb_module_done(ac->req, ac->op_controls,
844 ac->op_response, LDB_SUCCESS);
847 /* Having done the original operation, then try to fix up all the linked attributes for modify and delete */
848 static int la_mod_del_callback(struct ldb_request *req, struct ldb_reply *ares)
850 struct la_context *ac;
851 struct ldb_context *ldb;
852 int ret;
854 ac = talloc_get_type(req->context, struct la_context);
855 ldb = ldb_module_get_ctx(ac->module);
857 if (!ares) {
858 return ldb_module_done(ac->req, NULL, NULL,
859 LDB_ERR_OPERATIONS_ERROR);
861 if (ares->error != LDB_SUCCESS) {
862 return ldb_module_done(ac->req, ares->controls,
863 ares->response, ares->error);
866 if (ares->type != LDB_REPLY_DONE) {
867 ldb_set_errstring(ldb,
868 "invalid ldb_reply_type in callback");
869 talloc_free(ares);
870 return ldb_module_done(ac->req, NULL, NULL,
871 LDB_ERR_OPERATIONS_ERROR);
874 ac->op_controls = talloc_steal(ac, ares->controls);
875 ac->op_response = talloc_steal(ac, ares->response);
877 /* If we have modfies to make, this is the time to do them for modify and delete */
878 ret = la_queue_mod_request(ac);
880 if (ret != LDB_SUCCESS) {
881 return ldb_module_done(ac->req, NULL, NULL, ret);
883 talloc_free(ares);
885 /* la_queue_mod_request has already sent the callbacks */
886 return LDB_SUCCESS;
890 /* Having done the original add, then try to fix up all the linked attributes
892 This is done after the add so the links can get the extended DNs correctly.
894 static int la_add_callback(struct ldb_request *req, struct ldb_reply *ares)
896 struct la_context *ac;
897 struct ldb_context *ldb;
898 int ret;
900 ac = talloc_get_type(req->context, struct la_context);
901 ldb = ldb_module_get_ctx(ac->module);
903 if (!ares) {
904 return ldb_module_done(ac->req, NULL, NULL,
905 LDB_ERR_OPERATIONS_ERROR);
907 if (ares->error != LDB_SUCCESS) {
908 return ldb_module_done(ac->req, ares->controls,
909 ares->response, ares->error);
912 if (ares->type != LDB_REPLY_DONE) {
913 ldb_set_errstring(ldb,
914 "invalid ldb_reply_type in callback");
915 talloc_free(ares);
916 return ldb_module_done(ac->req, NULL, NULL,
917 LDB_ERR_OPERATIONS_ERROR);
920 if (ac->ops) {
921 struct ldb_request *search_req;
922 static const char *attrs[] = { NULL };
924 /* The callback does all the hard work here - we need
925 * the objectGUID and SID of the added record */
926 ret = ldb_build_search_req(&search_req, ldb, ac,
927 ac->req->op.add.message->dn,
928 LDB_SCOPE_BASE,
929 "(objectClass=*)", attrs,
930 NULL,
931 ac, la_mod_search_callback,
932 ac->req);
933 LDB_REQ_SET_LOCATION(search_req);
935 if (ret == LDB_SUCCESS) {
936 ret = dsdb_request_add_controls(search_req,
937 DSDB_SEARCH_SHOW_DELETED |
938 DSDB_SEARCH_SHOW_EXTENDED_DN);
940 if (ret != LDB_SUCCESS) {
941 return ldb_module_done(ac->req, NULL, NULL,
942 ret);
945 ac->op_controls = talloc_steal(ac, ares->controls);
946 ac->op_response = talloc_steal(ac, ares->response);
948 return ldb_next_request(ac->module, search_req);
950 } else {
951 return ldb_module_done(ac->req, ares->controls,
952 ares->response, ares->error);
956 /* Reconstruct the original request, but pointing at our local callback to finish things off */
957 static int la_down_req(struct la_context *ac)
959 struct ldb_request *down_req;
960 struct ldb_context *ldb;
961 int ret;
963 ldb = ldb_module_get_ctx(ac->module);
965 switch (ac->req->operation) {
966 case LDB_ADD:
967 ret = ldb_build_add_req(&down_req, ldb, ac,
968 ac->req->op.add.message,
969 ac->req->controls,
970 ac, la_add_callback,
971 ac->req);
972 LDB_REQ_SET_LOCATION(down_req);
973 break;
974 case LDB_MODIFY:
975 ret = ldb_build_mod_req(&down_req, ldb, ac,
976 ac->req->op.mod.message,
977 ac->req->controls,
978 ac, la_mod_del_callback,
979 ac->req);
980 LDB_REQ_SET_LOCATION(down_req);
981 break;
982 default:
983 ret = LDB_ERR_OPERATIONS_ERROR;
985 if (ret != LDB_SUCCESS) {
986 return ret;
989 return ldb_next_request(ac->module, down_req);
993 use the GUID part of an extended DN to find the target DN, in case
994 it has moved
996 static int la_find_dn_target(struct ldb_module *module, struct la_context *ac,
997 struct GUID *guid, struct ldb_dn **dn)
999 return dsdb_module_dn_by_guid(ac->module, ac, guid, dn, ac->req);
1002 /* apply one la_context op change */
1003 static int la_do_op_request(struct ldb_module *module, struct la_context *ac, struct la_op_store *op)
1005 struct ldb_message_element *ret_el;
1006 struct ldb_message *new_msg;
1007 struct ldb_context *ldb;
1008 int ret;
1010 if (ac->mod_dn == NULL) {
1011 /* we didn't find the DN that we searched for */
1012 return LDB_SUCCESS;
1015 ldb = ldb_module_get_ctx(ac->module);
1017 /* Create the modify request */
1018 new_msg = ldb_msg_new(ac);
1019 if (!new_msg) {
1020 return ldb_oom(ldb);
1023 ret = la_find_dn_target(module, ac, &op->guid, &new_msg->dn);
1024 if (ret != LDB_SUCCESS) {
1025 return ret;
1028 if (op->op == LA_OP_ADD) {
1029 ret = ldb_msg_add_empty(new_msg, op->name,
1030 LDB_FLAG_MOD_ADD, &ret_el);
1031 } else {
1032 ret = ldb_msg_add_empty(new_msg, op->name,
1033 LDB_FLAG_MOD_DELETE, &ret_el);
1035 if (ret != LDB_SUCCESS) {
1036 return ret;
1038 ret_el->values = talloc_array(new_msg, struct ldb_val, 1);
1039 if (!ret_el->values) {
1040 return ldb_oom(ldb);
1042 ret_el->num_values = 1;
1043 ret_el->values[0] = data_blob_string_const(ldb_dn_get_extended_linearized(new_msg, ac->mod_dn, 1));
1045 /* a backlink should never be single valued. Unfortunately the
1046 exchange schema has a attribute
1047 msExchBridgeheadedLocalConnectorsDNBL which is single
1048 valued and a backlink. We need to cope with that by
1049 ignoring the single value flag */
1050 ret_el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
1052 #if 0
1053 ldb_debug(ldb, LDB_DEBUG_WARNING,
1054 "link on %s %s: %s %s\n",
1055 ldb_dn_get_linearized(new_msg->dn), ret_el->name,
1056 ret_el->values[0].data, ac->ops->op == LA_OP_ADD ? "added" : "deleted");
1057 #endif
1059 if (DEBUGLVL(4)) {
1060 DEBUG(4,("Applying linked attribute change:\n%s\n",
1061 ldb_ldif_message_string(ldb, op, LDB_CHANGETYPE_MODIFY, new_msg)));
1064 ret = dsdb_module_modify(module, new_msg, DSDB_FLAG_NEXT_MODULE, ac->req);
1065 if (ret != LDB_SUCCESS) {
1066 ldb_debug(ldb, LDB_DEBUG_WARNING, __location__ ": failed to apply linked attribute change '%s'\n%s\n",
1067 ldb_errstring(ldb),
1068 ldb_ldif_message_string(ldb, op, LDB_CHANGETYPE_MODIFY, new_msg));
1071 return ret;
1074 /* apply one set of la_context changes */
1075 static int la_do_mod_request(struct ldb_module *module, struct la_context *ac)
1077 struct la_op_store *op;
1079 for (op = ac->ops; op; op=op->next) {
1080 int ret = la_do_op_request(module, ac, op);
1081 if (ret != LDB_SUCCESS) {
1082 if (ret != LDB_ERR_NO_SUCH_OBJECT) {
1083 return ret;
1088 return LDB_SUCCESS;
1093 we hook into the transaction operations to allow us to
1094 perform the linked attribute updates at the end of the whole
1095 transaction. This allows a forward linked attribute to be created
1096 before the target is created, as long as the target is created
1097 in the same transaction
1099 static int linked_attributes_start_transaction(struct ldb_module *module)
1101 /* create our private structure for this transaction */
1102 struct la_private *la_private = talloc_get_type(ldb_module_get_private(module),
1103 struct la_private);
1104 talloc_free(la_private);
1105 la_private = talloc(module, struct la_private);
1106 if (la_private == NULL) {
1107 return ldb_oom(ldb_module_get_ctx(module));
1109 la_private->la_list = NULL;
1110 ldb_module_set_private(module, la_private);
1111 return ldb_next_start_trans(module);
1115 on prepare commit we loop over our queued la_context structures
1116 and apply each of them
1118 static int linked_attributes_prepare_commit(struct ldb_module *module)
1120 struct la_private *la_private =
1121 talloc_get_type(ldb_module_get_private(module), struct la_private);
1122 struct la_context *ac;
1124 if (!la_private) {
1125 /* prepare commit without begin_transaction - let someone else return the error, just don't segfault */
1126 return ldb_next_prepare_commit(module);
1128 /* walk the list backwards, to do the first entry first, as we
1129 * added the entries with DLIST_ADD() which puts them at the
1130 * start of the list */
1132 /* Start at the end of the list - so we can start
1133 * there, but ensure we don't create a loop by NULLing
1134 * it out in the first element */
1135 ac = DLIST_TAIL(la_private->la_list);
1137 for (; ac; ac=DLIST_PREV(ac)) {
1138 int ret;
1139 ac->req = NULL;
1140 ret = la_do_mod_request(module, ac);
1141 if (ret != LDB_SUCCESS) {
1142 DEBUG(0,(__location__ ": Failed mod request ret=%d\n", ret));
1143 talloc_free(la_private);
1144 ldb_module_set_private(module, NULL);
1145 return ret;
1149 talloc_free(la_private);
1150 ldb_module_set_private(module, NULL);
1152 return ldb_next_prepare_commit(module);
1155 static int linked_attributes_del_transaction(struct ldb_module *module)
1157 struct la_private *la_private =
1158 talloc_get_type(ldb_module_get_private(module), struct la_private);
1159 talloc_free(la_private);
1160 ldb_module_set_private(module, NULL);
1161 return ldb_next_del_trans(module);
1164 static int linked_attributes_ldb_init(struct ldb_module *module)
1166 int ret;
1168 ret = ldb_mod_register_control(module, LDB_CONTROL_VERIFY_NAME_OID);
1169 if (ret != LDB_SUCCESS) {
1170 ldb_debug(ldb_module_get_ctx(module), LDB_DEBUG_ERROR,
1171 "verify_name: Unable to register control with rootdse!\n");
1172 return ldb_operr(ldb_module_get_ctx(module));
1175 return ldb_next_init(module);
1179 static const struct ldb_module_ops ldb_linked_attributes_module_ops = {
1180 .name = "linked_attributes",
1181 .add = linked_attributes_add,
1182 .modify = linked_attributes_modify,
1183 .rename = linked_attributes_rename,
1184 .init_context = linked_attributes_ldb_init,
1185 .start_transaction = linked_attributes_start_transaction,
1186 .prepare_commit = linked_attributes_prepare_commit,
1187 .del_transaction = linked_attributes_del_transaction,
1190 int ldb_linked_attributes_module_init(const char *version)
1192 LDB_MODULE_CHECK_VERSION(version);
1193 return ldb_register_module(&ldb_linked_attributes_module_ops);