dsdb-acl: add acl_check_access_on_objectclass() helper
[Samba/gebeck_regimport.git] / source4 / dsdb / samdb / ldb_modules / linked_attributes.c
blob6e08209bf32e5b1113cd01a13a2c62804afa83cc
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 ldb_module *module,
145 struct ldb_request *parent,
146 struct ldb_dn *dn, struct GUID *guid)
148 NTSTATUS status;
149 int ret;
151 status = dsdb_get_extended_dn_guid(dn, guid, "GUID");
152 if (NT_STATUS_IS_OK(status)) {
153 return LDB_SUCCESS;
155 if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
156 DEBUG(4,(__location__ ": Unable to parse GUID for dn %s\n",
157 ldb_dn_get_linearized(dn)));
158 return ldb_operr(ldb_module_get_ctx(module));
161 ret = dsdb_module_guid_by_dn(module, dn, guid, parent);
162 if (ret != LDB_SUCCESS) {
163 DEBUG(4,(__location__ ": Failed to find GUID for dn %s\n",
164 ldb_dn_get_linearized(dn)));
165 return ret;
167 return LDB_SUCCESS;
171 /* Common routine to handle reading the attributes and creating a
172 * series of modify requests */
173 static int la_store_op(struct la_context *ac,
174 enum la_op op, struct ldb_val *dn,
175 const char *name)
177 struct ldb_context *ldb;
178 struct la_op_store *os;
179 struct ldb_dn *op_dn;
180 int ret;
182 ldb = ldb_module_get_ctx(ac->module);
184 op_dn = ldb_dn_from_ldb_val(ac, ldb, dn);
185 if (!op_dn) {
186 ldb_asprintf_errstring(ldb,
187 "could not parse attribute as a DN");
188 return LDB_ERR_INVALID_DN_SYNTAX;
191 os = talloc_zero(ac, struct la_op_store);
192 if (!os) {
193 return ldb_oom(ldb);
196 os->op = op;
198 ret = la_guid_from_dn(ac->module, ac->req, op_dn, &os->guid);
199 talloc_free(op_dn);
200 if (ret == LDB_ERR_NO_SUCH_OBJECT && ac->req->operation == LDB_DELETE) {
201 /* we are deleting an object, and we've found it has a
202 * forward link to a target that no longer
203 * exists. This is not an error in the delete, and we
204 * should just not do the deferred delete of the
205 * target attribute
207 talloc_free(os);
208 return LDB_SUCCESS;
210 if (ret != LDB_SUCCESS) {
211 return ret;
214 os->name = talloc_strdup(os, name);
215 if (!os->name) {
216 return ldb_oom(ldb);
219 /* Do deletes before adds */
220 if (op == LA_OP_ADD) {
221 DLIST_ADD_END(ac->ops, os, struct la_op_store *);
222 } else {
223 /* By adding to the head of the list, we do deletes before
224 * adds when processing a replace */
225 DLIST_ADD(ac->ops, os);
228 return LDB_SUCCESS;
231 static int la_queue_mod_request(struct la_context *ac);
232 static int la_down_req(struct la_context *ac);
236 /* add */
237 static int linked_attributes_add(struct ldb_module *module, struct ldb_request *req)
239 struct ldb_context *ldb;
240 const struct dsdb_attribute *target_attr;
241 struct la_context *ac;
242 const char *attr_name;
243 struct ldb_control *ctrl;
244 unsigned int i, j;
245 struct ldb_control *control;
246 int ret;
248 ldb = ldb_module_get_ctx(module);
250 if (ldb_dn_is_special(req->op.add.message->dn)) {
251 /* do not manipulate our control entries */
252 return ldb_next_request(module, req);
255 ac = linked_attributes_init(module, req);
256 if (!ac) {
257 return ldb_operr(ldb);
260 control = ldb_request_get_control(req, LDB_CONTROL_VERIFY_NAME_OID);
261 if (control != NULL && control->data != NULL) {
262 ret = handle_verify_name_control(req, ldb, control, ac);
263 if (ret != LDB_SUCCESS) {
264 return ldb_operr(ldb);
268 if (!(ctrl = ldb_request_get_control(req, DSDB_CONTROL_APPLY_LINKS))) {
269 /* don't do anything special for linked attributes, repl_meta_data has done it */
270 talloc_free(ac);
271 return ldb_next_request(module, req);
273 ctrl->critical = false;
275 if (!ac->schema) {
276 /* without schema, this doesn't make any sense */
277 talloc_free(ac);
278 return ldb_next_request(module, req);
282 /* Need to ensure we only have forward links being specified */
283 for (i=0; i < req->op.add.message->num_elements; i++) {
284 const struct ldb_message_element *el = &req->op.add.message->elements[i];
285 const struct dsdb_attribute *schema_attr
286 = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
287 if (!schema_attr) {
288 ldb_asprintf_errstring(ldb,
289 "%s: attribute %s is not a valid attribute in schema",
290 __FUNCTION__,
291 el->name);
292 return LDB_ERR_OBJECT_CLASS_VIOLATION;
295 /* this could be a link with no partner, in which case
296 there is no special work to do */
297 if (schema_attr->linkID == 0) {
298 continue;
301 /* this part of the code should only be handling forward links */
302 SMB_ASSERT((schema_attr->linkID & 1) == 0);
304 /* Even link IDs are for the originating attribute */
305 target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID ^ 1);
306 if (!target_attr) {
308 * windows 2003 has a broken schema where
309 * the definition of msDS-IsDomainFor
310 * is missing (which is supposed to be
311 * the backlink of the msDS-HasDomainNCs
312 * attribute
314 continue;
317 attr_name = target_attr->lDAPDisplayName;
319 for (j = 0; j < el->num_values; j++) {
320 ret = la_store_op(ac, LA_OP_ADD,
321 &el->values[j],
322 attr_name);
323 if (ret != LDB_SUCCESS) {
324 return ret;
329 /* if no linked attributes are present continue */
330 if (ac->ops == NULL) {
331 /* nothing to do for this module, proceed */
332 talloc_free(ac);
333 return ldb_next_request(module, req);
336 /* start with the original request */
337 return la_down_req(ac);
340 /* For a delete or rename, we need to find out what linked attributes
341 * are currently on this DN, and then deal with them. This is the
342 * callback to the base search */
344 static int la_mod_search_callback(struct ldb_request *req, struct ldb_reply *ares)
346 struct ldb_context *ldb;
347 const struct dsdb_attribute *schema_attr;
348 const struct dsdb_attribute *target_attr;
349 struct ldb_message_element *search_el;
350 struct replace_context *rc;
351 struct la_context *ac;
352 const char *attr_name;
353 unsigned int i, j;
354 int ret = LDB_SUCCESS;
356 ac = talloc_get_type(req->context, struct la_context);
357 ldb = ldb_module_get_ctx(ac->module);
358 rc = ac->rc;
360 if (!ares) {
361 return ldb_module_done(ac->req, NULL, NULL,
362 LDB_ERR_OPERATIONS_ERROR);
364 if (ares->error != LDB_SUCCESS) {
365 return ldb_module_done(ac->req, ares->controls,
366 ares->response, ares->error);
369 /* Only entries are interesting, and we only want the olddn */
370 switch (ares->type) {
371 case LDB_REPLY_ENTRY:
373 if (ldb_dn_compare(ares->message->dn, ac->req->op.mod.message->dn) != 0) {
374 ldb_asprintf_errstring(ldb,
375 "linked_attributes: %s is not the DN we were looking for",
376 ldb_dn_get_linearized(ares->message->dn));
377 /* Guh? We only asked for this DN */
378 talloc_free(ares);
379 return ldb_module_done(ac->req, NULL, NULL,
380 LDB_ERR_OPERATIONS_ERROR);
383 ac->mod_dn = talloc_steal(ac, ares->message->dn);
385 /* We don't populate 'rc' for ADD - it can't be deleting elements anyway */
386 for (i = 0; rc && i < rc->num_elements; i++) {
388 schema_attr = dsdb_attribute_by_lDAPDisplayName(ac->schema, rc->el[i].name);
389 if (!schema_attr) {
390 ldb_asprintf_errstring(ldb,
391 "%s: attribute %s is not a valid attribute in schema",
392 __FUNCTION__,
393 rc->el[i].name);
394 talloc_free(ares);
395 return ldb_module_done(ac->req, NULL, NULL,
396 LDB_ERR_OBJECT_CLASS_VIOLATION);
399 search_el = ldb_msg_find_element(ares->message,
400 rc->el[i].name);
402 /* See if this element already exists */
403 /* otherwise just ignore as
404 * the add has already been scheduled */
405 if ( ! search_el) {
406 continue;
409 target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID ^ 1);
410 if (!target_attr) {
412 * windows 2003 has a broken schema where
413 * the definition of msDS-IsDomainFor
414 * is missing (which is supposed to be
415 * the backlink of the msDS-HasDomainNCs
416 * attribute
418 continue;
420 attr_name = target_attr->lDAPDisplayName;
422 /* Now we know what was there, we can remove it for the re-add */
423 for (j = 0; j < search_el->num_values; j++) {
424 ret = la_store_op(ac, LA_OP_DEL,
425 &search_el->values[j],
426 attr_name);
427 if (ret != LDB_SUCCESS) {
428 talloc_free(ares);
429 return ldb_module_done(ac->req,
430 NULL, NULL, ret);
435 break;
437 case LDB_REPLY_REFERRAL:
438 /* ignore */
439 break;
441 case LDB_REPLY_DONE:
443 talloc_free(ares);
445 if (ac->req->operation == LDB_ADD) {
446 /* Start the modifies to the backlinks */
447 ret = la_queue_mod_request(ac);
449 if (ret != LDB_SUCCESS) {
450 return ldb_module_done(ac->req, NULL, NULL,
451 ret);
453 } else {
454 /* Start with the original request */
455 ret = la_down_req(ac);
456 if (ret != LDB_SUCCESS) {
457 return ldb_module_done(ac->req, NULL, NULL, ret);
460 return LDB_SUCCESS;
463 talloc_free(ares);
464 return ret;
468 /* modify */
469 static int linked_attributes_modify(struct ldb_module *module, struct ldb_request *req)
471 /* Look over list of modifications */
472 /* Find if any are for linked attributes */
473 /* Determine the effect of the modification */
474 /* Apply the modify to the linked entry */
476 struct ldb_control *control;
477 struct ldb_context *ldb;
478 unsigned int i, j;
479 struct la_context *ac;
480 struct ldb_request *search_req;
481 const char **attrs;
482 struct ldb_control *ctrl;
483 int ret;
485 ldb = ldb_module_get_ctx(module);
487 if (ldb_dn_is_special(req->op.mod.message->dn)) {
488 /* do not manipulate our control entries */
489 return ldb_next_request(module, req);
492 ac = linked_attributes_init(module, req);
493 if (!ac) {
494 return ldb_operr(ldb);
497 control = ldb_request_get_control(req, LDB_CONTROL_VERIFY_NAME_OID);
498 if (control != NULL && control->data != NULL) {
499 ret = handle_verify_name_control(req, ldb, control, ac);
500 if (ret != LDB_SUCCESS) {
501 return ldb_operr(ldb);
505 if (!(ctrl = ldb_request_get_control(req, DSDB_CONTROL_APPLY_LINKS))) {
506 /* don't do anything special for linked attributes, repl_meta_data has done it */
507 talloc_free(ac);
508 return ldb_next_request(module, req);
510 ctrl->critical = false;
512 if (!ac->schema) {
513 /* without schema, this doesn't make any sense */
514 return ldb_next_request(module, req);
517 ac->rc = talloc_zero(ac, struct replace_context);
518 if (!ac->rc) {
519 return ldb_oom(ldb);
522 for (i=0; i < req->op.mod.message->num_elements; i++) {
523 bool store_el = false;
524 const char *attr_name;
525 const struct dsdb_attribute *target_attr;
526 const struct ldb_message_element *el = &req->op.mod.message->elements[i];
527 const struct dsdb_attribute *schema_attr
528 = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
529 if (!schema_attr) {
530 ldb_asprintf_errstring(ldb,
531 "%s: attribute %s is not a valid attribute in schema",
532 __FUNCTION__,
533 el->name);
534 return LDB_ERR_OBJECT_CLASS_VIOLATION;
536 /* We have a valid attribute, now find out if it is a forward link
537 (Even link IDs are for the originating attribute) */
538 if (schema_attr->linkID == 0) {
539 continue;
542 /* this part of the code should only be handling forward links */
543 SMB_ASSERT((schema_attr->linkID & 1) == 0);
545 /* Now find the target attribute */
546 target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID ^ 1);
547 if (!target_attr) {
549 * windows 2003 has a broken schema where
550 * the definition of msDS-IsDomainFor
551 * is missing (which is supposed to be
552 * the backlink of the msDS-HasDomainNCs
553 * attribute
555 continue;
558 attr_name = target_attr->lDAPDisplayName;
560 switch (el->flags & LDB_FLAG_MOD_MASK) {
561 case LDB_FLAG_MOD_REPLACE:
562 /* treat as just a normal add the delete part is handled by the callback */
563 store_el = true;
565 /* break intentionally missing */
567 case LDB_FLAG_MOD_ADD:
569 /* For each value being added, we need to setup the adds */
570 for (j = 0; j < el->num_values; j++) {
571 ret = la_store_op(ac, LA_OP_ADD,
572 &el->values[j],
573 attr_name);
574 if (ret != LDB_SUCCESS) {
575 return ret;
578 break;
580 case LDB_FLAG_MOD_DELETE:
582 if (el->num_values) {
583 /* For each value being deleted, we need to setup the delete */
584 for (j = 0; j < el->num_values; j++) {
585 ret = la_store_op(ac, LA_OP_DEL,
586 &el->values[j],
587 attr_name);
588 if (ret != LDB_SUCCESS) {
589 return ret;
592 } else {
593 /* Flag that there was a DELETE
594 * without a value specified, so we
595 * need to look for the old value */
596 store_el = true;
599 break;
602 if (store_el) {
603 struct ldb_message_element *search_el;
605 search_el = talloc_realloc(ac->rc, ac->rc->el,
606 struct ldb_message_element,
607 ac->rc->num_elements +1);
608 if (!search_el) {
609 return ldb_oom(ldb);
611 ac->rc->el = search_el;
613 ac->rc->el[ac->rc->num_elements] = *el;
614 ac->rc->num_elements++;
618 if (ac->ops || ac->rc->el) {
619 /* both replace and delete without values are handled in the callback
620 * after the search on the entry to be modified is performed */
622 attrs = talloc_array(ac->rc, const char *, ac->rc->num_elements + 1);
623 if (!attrs) {
624 return ldb_oom(ldb);
626 for (i = 0; ac->rc && i < ac->rc->num_elements; i++) {
627 attrs[i] = ac->rc->el[i].name;
629 attrs[i] = NULL;
631 /* The callback does all the hard work here */
632 ret = ldb_build_search_req(&search_req, ldb, ac,
633 req->op.mod.message->dn,
634 LDB_SCOPE_BASE,
635 "(objectClass=*)", attrs,
636 NULL,
637 ac, la_mod_search_callback,
638 req);
639 LDB_REQ_SET_LOCATION(search_req);
641 /* We need to figure out our own extended DN, to fill in as the backlink target */
642 if (ret == LDB_SUCCESS) {
643 ret = dsdb_request_add_controls(search_req,
644 DSDB_SEARCH_SHOW_DELETED |
645 DSDB_SEARCH_SHOW_EXTENDED_DN);
647 if (ret == LDB_SUCCESS) {
648 talloc_steal(search_req, attrs);
650 ret = ldb_next_request(module, search_req);
653 } else {
654 /* nothing to do for this module, proceed */
655 talloc_free(ac);
656 ret = ldb_next_request(module, req);
659 return ret;
662 static int linked_attributes_fix_links(struct ldb_module *module,
663 struct GUID self_guid,
664 struct ldb_dn *old_dn, struct ldb_dn *new_dn,
665 struct ldb_message_element *el, struct dsdb_schema *schema,
666 const struct dsdb_attribute *schema_attr,
667 struct ldb_request *parent)
669 unsigned int i, j;
670 TALLOC_CTX *tmp_ctx = talloc_new(module);
671 struct ldb_context *ldb = ldb_module_get_ctx(module);
672 const struct dsdb_attribute *target;
673 const char *attrs[2];
674 int ret;
676 target = dsdb_attribute_by_linkID(schema, schema_attr->linkID ^ 1);
677 if (target == NULL) {
678 /* there is no counterpart link to change */
679 return LDB_SUCCESS;
682 attrs[0] = target->lDAPDisplayName;
683 attrs[1] = NULL;
685 for (i=0; i<el->num_values; i++) {
686 struct dsdb_dn *dsdb_dn;
687 struct ldb_result *res;
688 struct ldb_message *msg;
689 struct ldb_message_element *el2;
690 struct GUID link_guid;
692 dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i], schema_attr->syntax->ldap_oid);
693 if (dsdb_dn == NULL) {
694 talloc_free(tmp_ctx);
695 return LDB_ERR_INVALID_DN_SYNTAX;
698 ret = la_guid_from_dn(module, parent, dsdb_dn->dn, &link_guid);
699 if (ret != LDB_SUCCESS) {
700 ldb_asprintf_errstring(ldb, "Linked attribute %s->%s between %s and %s - GUID not found - %s",
701 el->name, target->lDAPDisplayName,
702 ldb_dn_get_linearized(old_dn),
703 ldb_dn_get_linearized(dsdb_dn->dn),
704 ldb_errstring(ldb));
705 talloc_free(tmp_ctx);
706 return ret;
710 * get the existing message from the db for the object with
711 * this GUID, returning attribute being modified. We will then
712 * use this msg as the basis for a modify call
714 ret = dsdb_module_search(module, tmp_ctx, &res, NULL, LDB_SCOPE_SUBTREE, attrs,
715 DSDB_FLAG_NEXT_MODULE |
716 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
717 DSDB_SEARCH_SHOW_RECYCLED |
718 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
719 DSDB_SEARCH_REVEAL_INTERNALS,
720 parent,
721 "objectGUID=%s", GUID_string(tmp_ctx, &link_guid));
722 if (ret != LDB_SUCCESS) {
723 ldb_asprintf_errstring(ldb, "Linked attribute %s->%s between %s and %s - target GUID %s not found - %s",
724 el->name, target->lDAPDisplayName,
725 ldb_dn_get_linearized(old_dn),
726 ldb_dn_get_linearized(dsdb_dn->dn),
727 GUID_string(tmp_ctx, &link_guid),
728 ldb_errstring(ldb));
729 talloc_free(tmp_ctx);
730 return ret;
732 msg = res->msgs[0];
734 if (msg->num_elements == 0) {
735 /* Forward link without backlink remaining - nothing to do here */
736 continue;
737 } else if (msg->num_elements != 1) {
738 ldb_asprintf_errstring(ldb, "Bad msg elements - got %u elements, expected one element to be returned in linked_attributes_fix_links for %s",
739 msg->num_elements, ldb_dn_get_linearized(msg->dn));
740 talloc_free(tmp_ctx);
741 return LDB_ERR_OPERATIONS_ERROR;
743 if (ldb_attr_cmp(msg->elements[0].name, target->lDAPDisplayName) != 0) {
744 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));
745 talloc_free(tmp_ctx);
746 return LDB_ERR_OPERATIONS_ERROR;
748 el2 = &msg->elements[0];
750 el2->flags = LDB_FLAG_MOD_REPLACE;
752 /* find our DN in the values */
753 for (j=0; j<el2->num_values; j++) {
754 struct dsdb_dn *dsdb_dn2;
755 struct GUID link_guid;
757 dsdb_dn2 = dsdb_dn_parse(msg, ldb, &el2->values[j], target->syntax->ldap_oid);
758 if (dsdb_dn2 == NULL) {
759 talloc_free(tmp_ctx);
760 return LDB_ERR_INVALID_DN_SYNTAX;
763 ret = la_guid_from_dn(module, parent, dsdb_dn2->dn, &link_guid);
764 if (ret != LDB_SUCCESS) {
765 talloc_free(tmp_ctx);
766 return ret;
770 * By comparing using the GUID we ensure that
771 * even if somehow the name has got out of
772 * sync, this rename will fix it.
774 * If somehow we don't have a GUID on the DN
775 * in the DB, the la_guid_from_dn call will be
776 * more costly, but still give us a GUID.
777 * dbcheck will fix this if run.
779 if (!GUID_equal(&self_guid, &link_guid)) {
780 continue;
783 ret = ldb_dn_update_components(dsdb_dn2->dn, new_dn);
784 if (ret != LDB_SUCCESS) {
785 talloc_free(tmp_ctx);
786 return ret;
789 el2->values[j] = data_blob_string_const(
790 dsdb_dn_get_extended_linearized(el2->values, dsdb_dn2, 1));
793 ret = dsdb_check_single_valued_link(target, el2);
794 if (ret != LDB_SUCCESS) {
795 talloc_free(tmp_ctx);
796 return ret;
799 /* we may be putting multiple values in an attribute -
800 disable checking for this attribute */
801 el2->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
803 ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE, parent);
804 if (ret != LDB_SUCCESS) {
805 ldb_asprintf_errstring(ldb, "Linked attribute %s->%s between %s and %s - update failed - %s",
806 el->name, target->lDAPDisplayName,
807 ldb_dn_get_linearized(old_dn),
808 ldb_dn_get_linearized(dsdb_dn->dn),
809 ldb_errstring(ldb));
810 talloc_free(tmp_ctx);
811 return ret;
815 talloc_free(tmp_ctx);
816 return LDB_SUCCESS;
820 /* rename */
821 static int linked_attributes_rename(struct ldb_module *module, struct ldb_request *req)
823 struct ldb_result *res;
824 struct ldb_message *msg;
825 unsigned int i;
826 struct ldb_context *ldb = ldb_module_get_ctx(module);
827 struct dsdb_schema *schema;
828 int ret;
829 struct GUID guid;
832 - load the current msg
833 - find any linked attributes
834 - if its a link then find the target object
835 - modify the target linked attributes with the new DN
837 ret = dsdb_module_search_dn(module, req, &res, req->op.rename.olddn,
838 NULL,
839 DSDB_FLAG_NEXT_MODULE |
840 DSDB_SEARCH_SHOW_EXTENDED_DN |
841 DSDB_SEARCH_SHOW_RECYCLED, req);
842 if (ret != LDB_SUCCESS) {
843 return ret;
846 schema = dsdb_get_schema(ldb, res);
847 if (!schema) {
848 return ldb_oom(ldb);
851 msg = res->msgs[0];
853 ret = la_guid_from_dn(module, req, msg->dn, &guid);
854 if (ret != LDB_SUCCESS) {
855 return ret;
858 for (i=0; i<msg->num_elements; i++) {
859 struct ldb_message_element *el = &msg->elements[i];
860 const struct dsdb_attribute *schema_attr
861 = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
862 if (!schema_attr || schema_attr->linkID == 0) {
863 continue;
865 ret = linked_attributes_fix_links(module, guid, msg->dn, req->op.rename.newdn, el,
866 schema, schema_attr, req);
867 if (ret != LDB_SUCCESS) {
868 talloc_free(res);
869 return ret;
873 talloc_free(res);
875 return ldb_next_request(module, req);
879 /* queue a linked attributes modify request in the la_private
880 structure */
881 static int la_queue_mod_request(struct la_context *ac)
883 struct la_private *la_private =
884 talloc_get_type(ldb_module_get_private(ac->module), struct la_private);
886 if (la_private == NULL) {
887 ldb_debug(ldb_module_get_ctx(ac->module), LDB_DEBUG_ERROR, __location__ ": No la_private transaction setup\n");
888 return ldb_operr(ldb_module_get_ctx(ac->module));
891 talloc_steal(la_private, ac);
892 DLIST_ADD(la_private->la_list, ac);
894 return ldb_module_done(ac->req, ac->op_controls,
895 ac->op_response, LDB_SUCCESS);
898 /* Having done the original operation, then try to fix up all the linked attributes for modify and delete */
899 static int la_mod_del_callback(struct ldb_request *req, struct ldb_reply *ares)
901 struct la_context *ac;
902 struct ldb_context *ldb;
903 int ret;
905 ac = talloc_get_type(req->context, struct la_context);
906 ldb = ldb_module_get_ctx(ac->module);
908 if (!ares) {
909 return ldb_module_done(ac->req, NULL, NULL,
910 LDB_ERR_OPERATIONS_ERROR);
912 if (ares->error != LDB_SUCCESS) {
913 return ldb_module_done(ac->req, ares->controls,
914 ares->response, ares->error);
917 if (ares->type != LDB_REPLY_DONE) {
918 ldb_set_errstring(ldb,
919 "invalid ldb_reply_type in callback");
920 talloc_free(ares);
921 return ldb_module_done(ac->req, NULL, NULL,
922 LDB_ERR_OPERATIONS_ERROR);
925 ac->op_controls = talloc_steal(ac, ares->controls);
926 ac->op_response = talloc_steal(ac, ares->response);
928 /* If we have modfies to make, this is the time to do them for modify and delete */
929 ret = la_queue_mod_request(ac);
931 if (ret != LDB_SUCCESS) {
932 return ldb_module_done(ac->req, NULL, NULL, ret);
934 talloc_free(ares);
936 /* la_queue_mod_request has already sent the callbacks */
937 return LDB_SUCCESS;
941 /* Having done the original add, then try to fix up all the linked attributes
943 This is done after the add so the links can get the extended DNs correctly.
945 static int la_add_callback(struct ldb_request *req, struct ldb_reply *ares)
947 struct la_context *ac;
948 struct ldb_context *ldb;
949 int ret;
951 ac = talloc_get_type(req->context, struct la_context);
952 ldb = ldb_module_get_ctx(ac->module);
954 if (!ares) {
955 return ldb_module_done(ac->req, NULL, NULL,
956 LDB_ERR_OPERATIONS_ERROR);
958 if (ares->error != LDB_SUCCESS) {
959 return ldb_module_done(ac->req, ares->controls,
960 ares->response, ares->error);
963 if (ares->type != LDB_REPLY_DONE) {
964 ldb_set_errstring(ldb,
965 "invalid ldb_reply_type in callback");
966 talloc_free(ares);
967 return ldb_module_done(ac->req, NULL, NULL,
968 LDB_ERR_OPERATIONS_ERROR);
971 if (ac->ops) {
972 struct ldb_request *search_req;
973 static const char *attrs[] = { NULL };
975 /* The callback does all the hard work here - we need
976 * the objectGUID and SID of the added record */
977 ret = ldb_build_search_req(&search_req, ldb, ac,
978 ac->req->op.add.message->dn,
979 LDB_SCOPE_BASE,
980 "(objectClass=*)", attrs,
981 NULL,
982 ac, la_mod_search_callback,
983 ac->req);
984 LDB_REQ_SET_LOCATION(search_req);
986 if (ret == LDB_SUCCESS) {
987 ret = dsdb_request_add_controls(search_req,
988 DSDB_SEARCH_SHOW_DELETED |
989 DSDB_SEARCH_SHOW_EXTENDED_DN);
991 if (ret != LDB_SUCCESS) {
992 return ldb_module_done(ac->req, NULL, NULL,
993 ret);
996 ac->op_controls = talloc_steal(ac, ares->controls);
997 ac->op_response = talloc_steal(ac, ares->response);
999 return ldb_next_request(ac->module, search_req);
1001 } else {
1002 return ldb_module_done(ac->req, ares->controls,
1003 ares->response, ares->error);
1007 /* Reconstruct the original request, but pointing at our local callback to finish things off */
1008 static int la_down_req(struct la_context *ac)
1010 struct ldb_request *down_req;
1011 struct ldb_context *ldb;
1012 int ret;
1014 ldb = ldb_module_get_ctx(ac->module);
1016 switch (ac->req->operation) {
1017 case LDB_ADD:
1018 ret = ldb_build_add_req(&down_req, ldb, ac,
1019 ac->req->op.add.message,
1020 ac->req->controls,
1021 ac, la_add_callback,
1022 ac->req);
1023 LDB_REQ_SET_LOCATION(down_req);
1024 break;
1025 case LDB_MODIFY:
1026 ret = ldb_build_mod_req(&down_req, ldb, ac,
1027 ac->req->op.mod.message,
1028 ac->req->controls,
1029 ac, la_mod_del_callback,
1030 ac->req);
1031 LDB_REQ_SET_LOCATION(down_req);
1032 break;
1033 default:
1034 ret = LDB_ERR_OPERATIONS_ERROR;
1036 if (ret != LDB_SUCCESS) {
1037 return ret;
1040 return ldb_next_request(ac->module, down_req);
1044 use the GUID part of an extended DN to find the target DN, in case
1045 it has moved
1047 static int la_find_dn_target(struct ldb_module *module, struct la_context *ac,
1048 struct GUID *guid, struct ldb_dn **dn)
1050 return dsdb_module_dn_by_guid(ac->module, ac, guid, dn, ac->req);
1053 /* apply one la_context op change */
1054 static int la_do_op_request(struct ldb_module *module, struct la_context *ac, struct la_op_store *op)
1056 struct ldb_message_element *ret_el;
1057 struct ldb_message *new_msg;
1058 struct ldb_context *ldb;
1059 int ret;
1061 if (ac->mod_dn == NULL) {
1062 /* we didn't find the DN that we searched for */
1063 return LDB_SUCCESS;
1066 ldb = ldb_module_get_ctx(ac->module);
1068 /* Create the modify request */
1069 new_msg = ldb_msg_new(ac);
1070 if (!new_msg) {
1071 return ldb_oom(ldb);
1074 ret = la_find_dn_target(module, ac, &op->guid, &new_msg->dn);
1075 if (ret != LDB_SUCCESS) {
1076 return ret;
1079 if (op->op == LA_OP_ADD) {
1080 ret = ldb_msg_add_empty(new_msg, op->name,
1081 LDB_FLAG_MOD_ADD, &ret_el);
1082 } else {
1083 ret = ldb_msg_add_empty(new_msg, op->name,
1084 LDB_FLAG_MOD_DELETE, &ret_el);
1086 if (ret != LDB_SUCCESS) {
1087 return ret;
1089 ret_el->values = talloc_array(new_msg, struct ldb_val, 1);
1090 if (!ret_el->values) {
1091 return ldb_oom(ldb);
1093 ret_el->num_values = 1;
1094 ret_el->values[0] = data_blob_string_const(ldb_dn_get_extended_linearized(new_msg, ac->mod_dn, 1));
1096 /* a backlink should never be single valued. Unfortunately the
1097 exchange schema has a attribute
1098 msExchBridgeheadedLocalConnectorsDNBL which is single
1099 valued and a backlink. We need to cope with that by
1100 ignoring the single value flag */
1101 ret_el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
1103 #if 0
1104 ldb_debug(ldb, LDB_DEBUG_WARNING,
1105 "link on %s %s: %s %s\n",
1106 ldb_dn_get_linearized(new_msg->dn), ret_el->name,
1107 ret_el->values[0].data, ac->ops->op == LA_OP_ADD ? "added" : "deleted");
1108 #endif
1110 if (DEBUGLVL(4)) {
1111 DEBUG(4,("Applying linked attribute change:\n%s\n",
1112 ldb_ldif_message_string(ldb, op, LDB_CHANGETYPE_MODIFY, new_msg)));
1115 ret = dsdb_module_modify(module, new_msg, DSDB_FLAG_NEXT_MODULE, ac->req);
1116 if (ret != LDB_SUCCESS) {
1117 ldb_debug(ldb, LDB_DEBUG_WARNING, __location__ ": failed to apply linked attribute change '%s'\n%s\n",
1118 ldb_errstring(ldb),
1119 ldb_ldif_message_string(ldb, op, LDB_CHANGETYPE_MODIFY, new_msg));
1122 return ret;
1125 /* apply one set of la_context changes */
1126 static int la_do_mod_request(struct ldb_module *module, struct la_context *ac)
1128 struct la_op_store *op;
1130 for (op = ac->ops; op; op=op->next) {
1131 int ret = la_do_op_request(module, ac, op);
1132 if (ret != LDB_SUCCESS) {
1133 if (ret != LDB_ERR_NO_SUCH_OBJECT) {
1134 return ret;
1139 return LDB_SUCCESS;
1144 we hook into the transaction operations to allow us to
1145 perform the linked attribute updates at the end of the whole
1146 transaction. This allows a forward linked attribute to be created
1147 before the target is created, as long as the target is created
1148 in the same transaction
1150 static int linked_attributes_start_transaction(struct ldb_module *module)
1152 /* create our private structure for this transaction */
1153 struct la_private *la_private = talloc_get_type(ldb_module_get_private(module),
1154 struct la_private);
1155 talloc_free(la_private);
1156 la_private = talloc(module, struct la_private);
1157 if (la_private == NULL) {
1158 return ldb_oom(ldb_module_get_ctx(module));
1160 la_private->la_list = NULL;
1161 ldb_module_set_private(module, la_private);
1162 return ldb_next_start_trans(module);
1166 on prepare commit we loop over our queued la_context structures
1167 and apply each of them
1169 static int linked_attributes_prepare_commit(struct ldb_module *module)
1171 struct la_private *la_private =
1172 talloc_get_type(ldb_module_get_private(module), struct la_private);
1173 struct la_context *ac;
1175 if (!la_private) {
1176 /* prepare commit without begin_transaction - let someone else return the error, just don't segfault */
1177 return ldb_next_prepare_commit(module);
1179 /* walk the list backwards, to do the first entry first, as we
1180 * added the entries with DLIST_ADD() which puts them at the
1181 * start of the list */
1183 /* Start at the end of the list - so we can start
1184 * there, but ensure we don't create a loop by NULLing
1185 * it out in the first element */
1186 ac = DLIST_TAIL(la_private->la_list);
1188 for (; ac; ac=DLIST_PREV(ac)) {
1189 int ret;
1190 ac->req = NULL;
1191 ret = la_do_mod_request(module, ac);
1192 if (ret != LDB_SUCCESS) {
1193 DEBUG(0,(__location__ ": Failed mod request ret=%d\n", ret));
1194 talloc_free(la_private);
1195 ldb_module_set_private(module, NULL);
1196 return ret;
1200 talloc_free(la_private);
1201 ldb_module_set_private(module, NULL);
1203 return ldb_next_prepare_commit(module);
1206 static int linked_attributes_del_transaction(struct ldb_module *module)
1208 struct la_private *la_private =
1209 talloc_get_type(ldb_module_get_private(module), struct la_private);
1210 talloc_free(la_private);
1211 ldb_module_set_private(module, NULL);
1212 return ldb_next_del_trans(module);
1215 static int linked_attributes_ldb_init(struct ldb_module *module)
1217 int ret;
1219 ret = ldb_mod_register_control(module, LDB_CONTROL_VERIFY_NAME_OID);
1220 if (ret != LDB_SUCCESS) {
1221 ldb_debug(ldb_module_get_ctx(module), LDB_DEBUG_ERROR,
1222 "verify_name: Unable to register control with rootdse!\n");
1223 return ldb_operr(ldb_module_get_ctx(module));
1226 return ldb_next_init(module);
1230 static const struct ldb_module_ops ldb_linked_attributes_module_ops = {
1231 .name = "linked_attributes",
1232 .add = linked_attributes_add,
1233 .modify = linked_attributes_modify,
1234 .rename = linked_attributes_rename,
1235 .init_context = linked_attributes_ldb_init,
1236 .start_transaction = linked_attributes_start_transaction,
1237 .prepare_commit = linked_attributes_prepare_commit,
1238 .del_transaction = linked_attributes_del_transaction,
1241 int ldb_linked_attributes_module_init(const char *version)
1243 LDB_MODULE_CHECK_VERSION(version);
1244 return ldb_register_module(&ldb_linked_attributes_module_ops);