s4:dsdb/password_hash: implement DSDB_CONTROL_BYPASS_PASSWORD_HASH_OID
[Samba/ita.git] / source4 / dsdb / samdb / ldb_modules / linked_attributes.c
blob8648ab3bfd44a7b4ce43f0f447626a8d98971f59
1 /*
2 ldb database library
4 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2007
5 Copyright (C) Simo Sorce <idra@samba.org> 2008
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
22 * Name: ldb
24 * Component: ldb linked_attributes module
26 * Description: Module to ensure linked attribute pairs remain in sync
28 * Author: Andrew Bartlett
31 #include "includes.h"
32 #include "ldb_module.h"
33 #include "util/dlinklist.h"
34 #include "dsdb/samdb/samdb.h"
35 #include "librpc/gen_ndr/ndr_misc.h"
36 #include "dsdb/samdb/ldb_modules/util.h"
38 struct la_private {
39 struct la_context *la_list;
42 struct la_op_store {
43 struct la_op_store *next;
44 struct la_op_store *prev;
45 enum la_op {LA_OP_ADD, LA_OP_DEL} op;
46 struct GUID guid;
47 char *name;
48 char *value;
51 struct replace_context {
52 struct la_context *ac;
53 unsigned int num_elements;
54 struct ldb_message_element *el;
57 struct la_context {
58 struct la_context *next, *prev;
59 const struct dsdb_schema *schema;
60 struct ldb_module *module;
61 struct ldb_request *req;
62 struct ldb_dn *add_dn;
63 struct ldb_dn *del_dn;
64 struct replace_context *rc;
65 struct la_op_store *ops;
66 struct ldb_extended *op_response;
67 struct ldb_control **op_controls;
70 static struct la_context *linked_attributes_init(struct ldb_module *module,
71 struct ldb_request *req)
73 struct ldb_context *ldb;
74 struct la_context *ac;
76 ldb = ldb_module_get_ctx(module);
78 ac = talloc_zero(req, struct la_context);
79 if (ac == NULL) {
80 ldb_oom(ldb);
81 return NULL;
84 ac->schema = dsdb_get_schema(ldb, ac);
85 ac->module = module;
86 ac->req = req;
88 return ac;
92 turn a DN into a GUID
94 static int la_guid_from_dn(struct la_context *ac, struct ldb_dn *dn, struct GUID *guid)
96 NTSTATUS status;
97 int ret;
99 status = dsdb_get_extended_dn_guid(dn, guid, "GUID");
100 if (NT_STATUS_IS_OK(status)) {
101 return LDB_SUCCESS;
103 if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
104 DEBUG(4,(__location__ ": Unable to parse GUID for dn %s\n",
105 ldb_dn_get_linearized(dn)));
106 return LDB_ERR_OPERATIONS_ERROR;
109 ret = dsdb_find_guid_by_dn(ldb_module_get_ctx(ac->module), dn, guid);
110 if (ret != LDB_SUCCESS) {
111 DEBUG(4,(__location__ ": Failed to find GUID for dn %s\n",
112 ldb_dn_get_linearized(dn)));
113 return ret;
115 return LDB_SUCCESS;
119 /* Common routine to handle reading the attributes and creating a
120 * series of modify requests */
121 static int la_store_op(struct la_context *ac,
122 enum la_op op, struct ldb_val *dn,
123 const char *name)
125 struct ldb_context *ldb;
126 struct la_op_store *os;
127 struct ldb_dn *op_dn;
128 int ret;
130 ldb = ldb_module_get_ctx(ac->module);
132 op_dn = ldb_dn_from_ldb_val(ac, ldb, dn);
133 if (!op_dn) {
134 ldb_asprintf_errstring(ldb,
135 "could not parse attribute as a DN");
136 return LDB_ERR_INVALID_DN_SYNTAX;
139 os = talloc_zero(ac, struct la_op_store);
140 if (!os) {
141 ldb_oom(ldb);
142 return LDB_ERR_OPERATIONS_ERROR;
145 os->op = op;
147 ret = la_guid_from_dn(ac, op_dn, &os->guid);
148 talloc_free(op_dn);
149 if (ret == LDB_ERR_NO_SUCH_OBJECT && ac->req->operation == LDB_DELETE) {
150 /* we are deleting an object, and we've found it has a
151 * forward link to a target that no longer
152 * exists. This is not an error in the delete, and we
153 * should just not do the deferred delete of the
154 * target attribute
156 talloc_free(os);
157 return LDB_SUCCESS;
159 if (ret != LDB_SUCCESS) {
160 return ret;
163 os->name = talloc_strdup(os, name);
164 if (!os->name) {
165 ldb_oom(ldb);
166 return LDB_ERR_OPERATIONS_ERROR;
169 /* Do deletes before adds */
170 if (op == LA_OP_ADD) {
171 DLIST_ADD_END(ac->ops, os, struct la_op_store *);
172 } else {
173 /* By adding to the head of the list, we do deletes before
174 * adds when processing a replace */
175 DLIST_ADD(ac->ops, os);
178 return LDB_SUCCESS;
181 static int la_queue_mod_request(struct la_context *ac);
182 static int la_down_req(struct la_context *ac);
186 /* add */
187 static int linked_attributes_add(struct ldb_module *module, struct ldb_request *req)
189 struct ldb_context *ldb;
190 const struct dsdb_attribute *target_attr;
191 struct la_context *ac;
192 const char *attr_name;
193 struct ldb_control *ctrl;
194 unsigned int i, j;
195 int ret;
197 ldb = ldb_module_get_ctx(module);
199 if (ldb_dn_is_special(req->op.add.message->dn)) {
200 /* do not manipulate our control entries */
201 return ldb_next_request(module, req);
204 if (!(ctrl = ldb_request_get_control(req, DSDB_CONTROL_APPLY_LINKS))) {
205 /* don't do anything special for linked attributes, repl_meta_data has done it */
206 return ldb_next_request(module, req);
208 ctrl->critical = false;
210 ac = linked_attributes_init(module, req);
211 if (!ac) {
212 return LDB_ERR_OPERATIONS_ERROR;
215 if (!ac->schema) {
216 /* without schema, this doesn't make any sense */
217 talloc_free(ac);
218 return ldb_next_request(module, req);
221 /* Need to ensure we only have forward links being specified */
222 for (i=0; i < req->op.add.message->num_elements; i++) {
223 const struct ldb_message_element *el = &req->op.add.message->elements[i];
224 const struct dsdb_attribute *schema_attr
225 = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
226 if (!schema_attr) {
227 ldb_asprintf_errstring(ldb,
228 "attribute %s is not a valid attribute in schema", el->name);
229 return LDB_ERR_OBJECT_CLASS_VIOLATION;
231 /* We have a valid attribute, now find out if it is a forward link */
232 if ((schema_attr->linkID == 0)) {
233 continue;
236 if ((schema_attr->linkID & 1) == 1) {
237 unsigned int functional_level;
239 functional_level = dsdb_functional_level(ldb);
240 SMB_ASSERT(functional_level > DS_DOMAIN_FUNCTION_2000);
243 /* Even link IDs are for the originating attribute */
244 target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID + 1);
245 if (!target_attr) {
247 * windows 2003 has a broken schema where
248 * the definition of msDS-IsDomainFor
249 * is missing (which is supposed to be
250 * the backlink of the msDS-HasDomainNCs
251 * attribute
253 continue;
256 attr_name = target_attr->lDAPDisplayName;
258 for (j = 0; j < el->num_values; j++) {
259 ret = la_store_op(ac, LA_OP_ADD,
260 &el->values[j],
261 attr_name);
262 if (ret != LDB_SUCCESS) {
263 return ret;
268 /* if no linked attributes are present continue */
269 if (ac->ops == NULL) {
270 /* nothing to do for this module, proceed */
271 talloc_free(ac);
272 return ldb_next_request(module, req);
275 /* start with the original request */
276 return la_down_req(ac);
279 /* For a delete or rename, we need to find out what linked attributes
280 * are currently on this DN, and then deal with them. This is the
281 * callback to the base search */
283 static int la_mod_search_callback(struct ldb_request *req, struct ldb_reply *ares)
285 struct ldb_context *ldb;
286 const struct dsdb_attribute *schema_attr;
287 const struct dsdb_attribute *target_attr;
288 struct ldb_message_element *search_el;
289 struct replace_context *rc;
290 struct la_context *ac;
291 const char *attr_name;
292 unsigned int i, j;
293 int ret = LDB_SUCCESS;
295 ac = talloc_get_type(req->context, struct la_context);
296 ldb = ldb_module_get_ctx(ac->module);
297 rc = ac->rc;
299 if (!ares) {
300 return ldb_module_done(ac->req, NULL, NULL,
301 LDB_ERR_OPERATIONS_ERROR);
303 if (ares->error != LDB_SUCCESS) {
304 return ldb_module_done(ac->req, ares->controls,
305 ares->response, ares->error);
308 /* Only entries are interesting, and we only want the olddn */
309 switch (ares->type) {
310 case LDB_REPLY_ENTRY:
312 if (ldb_dn_compare(ares->message->dn, ac->req->op.mod.message->dn) != 0) {
313 ldb_asprintf_errstring(ldb,
314 "linked_attributes: %s is not the DN we were looking for",
315 ldb_dn_get_linearized(ares->message->dn));
316 /* Guh? We only asked for this DN */
317 talloc_free(ares);
318 return ldb_module_done(ac->req, NULL, NULL,
319 LDB_ERR_OPERATIONS_ERROR);
322 ac->add_dn = ac->del_dn = talloc_steal(ac, ares->message->dn);
324 /* We don't populate 'rc' for ADD - it can't be deleting elements anyway */
325 for (i = 0; rc && i < rc->num_elements; i++) {
327 schema_attr = dsdb_attribute_by_lDAPDisplayName(ac->schema, rc->el[i].name);
328 if (!schema_attr) {
329 ldb_asprintf_errstring(ldb,
330 "attribute %s is not a valid attribute in schema",
331 rc->el[i].name);
332 talloc_free(ares);
333 return ldb_module_done(ac->req, NULL, NULL,
334 LDB_ERR_OBJECT_CLASS_VIOLATION);
337 search_el = ldb_msg_find_element(ares->message,
338 rc->el[i].name);
340 /* See if this element already exists */
341 /* otherwise just ignore as
342 * the add has already been scheduled */
343 if ( ! search_el) {
344 continue;
347 target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID + 1);
348 if (!target_attr) {
350 * windows 2003 has a broken schema where
351 * the definition of msDS-IsDomainFor
352 * is missing (which is supposed to be
353 * the backlink of the msDS-HasDomainNCs
354 * attribute
356 continue;
358 attr_name = target_attr->lDAPDisplayName;
360 /* Now we know what was there, we can remove it for the re-add */
361 for (j = 0; j < search_el->num_values; j++) {
362 ret = la_store_op(ac, LA_OP_DEL,
363 &search_el->values[j],
364 attr_name);
365 if (ret != LDB_SUCCESS) {
366 talloc_free(ares);
367 return ldb_module_done(ac->req,
368 NULL, NULL, ret);
373 break;
375 case LDB_REPLY_REFERRAL:
376 /* ignore */
377 break;
379 case LDB_REPLY_DONE:
381 talloc_free(ares);
383 if (ac->req->operation == LDB_ADD) {
384 /* Start the modifies to the backlinks */
385 ret = la_queue_mod_request(ac);
387 if (ret != LDB_SUCCESS) {
388 return ldb_module_done(ac->req, NULL, NULL,
389 ret);
391 } else {
392 /* Start with the original request */
393 ret = la_down_req(ac);
394 if (ret != LDB_SUCCESS) {
395 return ldb_module_done(ac->req, NULL, NULL, ret);
398 return LDB_SUCCESS;
401 talloc_free(ares);
402 return ret;
406 /* modify */
407 static int linked_attributes_modify(struct ldb_module *module, struct ldb_request *req)
409 /* Look over list of modifications */
410 /* Find if any are for linked attributes */
411 /* Determine the effect of the modification */
412 /* Apply the modify to the linked entry */
414 struct ldb_context *ldb;
415 unsigned int i, j;
416 struct la_context *ac;
417 struct ldb_request *search_req;
418 const char **attrs;
419 struct ldb_control *ctrl;
420 int ret;
422 ldb = ldb_module_get_ctx(module);
424 if (ldb_dn_is_special(req->op.mod.message->dn)) {
425 /* do not manipulate our control entries */
426 return ldb_next_request(module, req);
429 if (!(ctrl = ldb_request_get_control(req, DSDB_CONTROL_APPLY_LINKS))) {
430 /* don't do anything special for linked attributes, repl_meta_data has done it */
431 return ldb_next_request(module, req);
433 ctrl->critical = false;
435 ac = linked_attributes_init(module, req);
436 if (!ac) {
437 return LDB_ERR_OPERATIONS_ERROR;
440 if (!ac->schema) {
441 /* without schema, this doesn't make any sense */
442 return ldb_next_request(module, req);
445 ac->rc = talloc_zero(ac, struct replace_context);
446 if (!ac->rc) {
447 ldb_oom(ldb);
448 return LDB_ERR_OPERATIONS_ERROR;
451 for (i=0; i < req->op.mod.message->num_elements; i++) {
452 bool store_el = false;
453 const char *attr_name;
454 const struct dsdb_attribute *target_attr;
455 const struct ldb_message_element *el = &req->op.mod.message->elements[i];
456 const struct dsdb_attribute *schema_attr
457 = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
458 if (!schema_attr) {
459 ldb_asprintf_errstring(ldb,
460 "attribute %s is not a valid attribute in schema", el->name);
461 return LDB_ERR_OBJECT_CLASS_VIOLATION;
463 /* We have a valid attribute, now find out if it is a forward link
464 (Even link IDs are for the originating attribute) */
465 if (schema_attr->linkID == 0) {
466 continue;
469 if ((schema_attr->linkID & 1) == 1) {
470 unsigned int functional_level;
472 functional_level = dsdb_functional_level(ldb);
473 SMB_ASSERT(functional_level > DS_DOMAIN_FUNCTION_2000);
475 /* Now find the target attribute */
476 target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID + 1);
477 if (!target_attr) {
479 * windows 2003 has a broken schema where
480 * the definition of msDS-IsDomainFor
481 * is missing (which is supposed to be
482 * the backlink of the msDS-HasDomainNCs
483 * attribute
485 continue;
488 attr_name = target_attr->lDAPDisplayName;
490 switch (el->flags & LDB_FLAG_MOD_MASK) {
491 case LDB_FLAG_MOD_REPLACE:
492 /* treat as just a normal add the delete part is handled by the callback */
493 store_el = true;
495 /* break intentionally missing */
497 case LDB_FLAG_MOD_ADD:
499 /* For each value being added, we need to setup the adds */
500 for (j = 0; j < el->num_values; j++) {
501 ret = la_store_op(ac, LA_OP_ADD,
502 &el->values[j],
503 attr_name);
504 if (ret != LDB_SUCCESS) {
505 return ret;
508 break;
510 case LDB_FLAG_MOD_DELETE:
512 if (el->num_values) {
513 /* For each value being deleted, we need to setup the delete */
514 for (j = 0; j < el->num_values; j++) {
515 ret = la_store_op(ac, LA_OP_DEL,
516 &el->values[j],
517 attr_name);
518 if (ret != LDB_SUCCESS) {
519 return ret;
522 } else {
523 /* Flag that there was a DELETE
524 * without a value specified, so we
525 * need to look for the old value */
526 store_el = true;
529 break;
532 if (store_el) {
533 struct ldb_message_element *search_el;
535 search_el = talloc_realloc(ac->rc, ac->rc->el,
536 struct ldb_message_element,
537 ac->rc->num_elements +1);
538 if (!search_el) {
539 ldb_oom(ldb);
540 return LDB_ERR_OPERATIONS_ERROR;
542 ac->rc->el = search_el;
544 ac->rc->el[ac->rc->num_elements] = *el;
545 ac->rc->num_elements++;
549 if (ac->ops || ac->rc->el) {
550 /* both replace and delete without values are handled in the callback
551 * after the search on the entry to be modified is performed */
553 attrs = talloc_array(ac->rc, const char *, ac->rc->num_elements + 1);
554 if (!attrs) {
555 ldb_oom(ldb);
556 return LDB_ERR_OPERATIONS_ERROR;
558 for (i = 0; ac->rc && i < ac->rc->num_elements; i++) {
559 attrs[i] = ac->rc->el[i].name;
561 attrs[i] = NULL;
563 /* The callback does all the hard work here */
564 ret = ldb_build_search_req(&search_req, ldb, ac,
565 req->op.mod.message->dn,
566 LDB_SCOPE_BASE,
567 "(objectClass=*)", attrs,
568 NULL,
569 ac, la_mod_search_callback,
570 req);
572 /* We need to figure out our own extended DN, to fill in as the backlink target */
573 if (ret == LDB_SUCCESS) {
574 ret = ldb_request_add_control(search_req,
575 LDB_CONTROL_EXTENDED_DN_OID,
576 false, NULL);
578 if (ret == LDB_SUCCESS) {
579 talloc_steal(search_req, attrs);
581 ret = ldb_next_request(module, search_req);
584 } else {
585 /* nothing to do for this module, proceed */
586 talloc_free(ac);
587 ret = ldb_next_request(module, req);
590 return ret;
593 static int linked_attributes_fix_links(struct ldb_module *module,
594 struct ldb_dn *old_dn, struct ldb_dn *new_dn,
595 struct ldb_message_element *el, struct dsdb_schema *schema,
596 const struct dsdb_attribute *schema_attr)
598 unsigned int i, j;
599 TALLOC_CTX *tmp_ctx = talloc_new(module);
600 struct ldb_context *ldb = ldb_module_get_ctx(module);
601 const struct dsdb_attribute *target;
602 const char *attrs[2];
603 int ret;
605 target = dsdb_attribute_by_linkID(schema, schema_attr->linkID ^ 1);
606 if (target == NULL) {
607 /* there is no counterpart link to change */
608 return LDB_SUCCESS;
611 attrs[0] = target->lDAPDisplayName;
612 attrs[1] = NULL;
614 for (i=0; i<el->num_values; i++) {
615 struct dsdb_dn *dsdb_dn;
616 struct ldb_result *res;
617 struct ldb_message *msg;
618 struct ldb_message_element *el2;
620 dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i], schema_attr->syntax->ldap_oid);
621 if (dsdb_dn == NULL) {
622 talloc_free(tmp_ctx);
623 return LDB_ERR_INVALID_DN_SYNTAX;
626 ret = dsdb_module_search_dn(module, tmp_ctx, &res, dsdb_dn->dn,
627 attrs,
628 DSDB_SEARCH_SHOW_DELETED |
629 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
630 DSDB_SEARCH_REVEAL_INTERNALS);
631 if (ret != LDB_SUCCESS) {
632 ldb_asprintf_errstring(ldb, "Linked attribute %s->%s between %s and %s - remote not found - %s",
633 el->name, target->lDAPDisplayName,
634 ldb_dn_get_linearized(old_dn),
635 ldb_dn_get_linearized(dsdb_dn->dn),
636 ldb_errstring(ldb));
637 talloc_free(tmp_ctx);
638 return ret;
640 msg = res->msgs[0];
642 if (msg->num_elements == 0) {
643 /* Forward link without backlink remaining - nothing to do here */
644 continue;
645 } else if (msg->num_elements != 1) {
646 ldb_asprintf_errstring(ldb, "Bad msg elements - got %u elements, expected one element to be returned in linked_attributes_fix_links for %s",
647 msg->num_elements, ldb_dn_get_linearized(msg->dn));
648 talloc_free(tmp_ctx);
649 return LDB_ERR_OPERATIONS_ERROR;
651 if (ldb_attr_cmp(msg->elements[0].name, target->lDAPDisplayName) != 0) {
652 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));
653 talloc_free(tmp_ctx);
654 return LDB_ERR_OPERATIONS_ERROR;
656 el2 = &msg->elements[0];
658 el2->flags = LDB_FLAG_MOD_REPLACE;
660 /* find our DN in the values */
661 for (j=0; j<el2->num_values; j++) {
662 struct dsdb_dn *dsdb_dn2;
663 dsdb_dn2 = dsdb_dn_parse(msg, ldb, &el2->values[j], target->syntax->ldap_oid);
664 if (dsdb_dn2 == NULL) {
665 talloc_free(tmp_ctx);
666 return LDB_ERR_INVALID_DN_SYNTAX;
668 if (ldb_dn_compare(old_dn, dsdb_dn2->dn) != 0) {
669 continue;
671 ret = ldb_dn_update_components(dsdb_dn2->dn, new_dn);
672 if (ret != LDB_SUCCESS) {
673 talloc_free(tmp_ctx);
674 return ret;
677 el2->values[j] = data_blob_string_const(
678 dsdb_dn_get_extended_linearized(el2->values, dsdb_dn2, 1));
681 ret = dsdb_check_single_valued_link(target, el2);
682 if (ret != LDB_SUCCESS) {
683 talloc_free(tmp_ctx);
684 return ret;
687 ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE | DSDB_MODIFY_RELAX);
688 if (ret != LDB_SUCCESS) {
689 ldb_asprintf_errstring(ldb, "Linked attribute %s->%s between %s and %s - update failed - %s",
690 el->name, target->lDAPDisplayName,
691 ldb_dn_get_linearized(old_dn),
692 ldb_dn_get_linearized(dsdb_dn->dn),
693 ldb_errstring(ldb));
694 talloc_free(tmp_ctx);
695 return ret;
699 talloc_free(tmp_ctx);
700 return LDB_SUCCESS;
704 /* rename */
705 static int linked_attributes_rename(struct ldb_module *module, struct ldb_request *req)
707 struct ldb_result *res;
708 struct ldb_message *msg;
709 unsigned int i;
710 struct ldb_context *ldb = ldb_module_get_ctx(module);
711 struct dsdb_schema *schema;
712 int ret;
714 - load the current msg
715 - find any linked attributes
716 - if its a link then find the target object
717 - modify the target linked attributes with the new DN
719 ret = dsdb_module_search_dn(module, req, &res, req->op.rename.olddn,
720 NULL, DSDB_SEARCH_SHOW_DELETED);
721 if (ret != LDB_SUCCESS) {
722 return ret;
725 schema = dsdb_get_schema(ldb, res);
726 if (!schema) {
727 ldb_oom(ldb);
728 return LDB_ERR_OPERATIONS_ERROR;
731 msg = res->msgs[0];
733 for (i=0; i<msg->num_elements; i++) {
734 struct ldb_message_element *el = &msg->elements[i];
735 const struct dsdb_attribute *schema_attr
736 = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
737 if (!schema_attr || schema_attr->linkID == 0) {
738 continue;
740 ret = linked_attributes_fix_links(module, msg->dn, req->op.rename.newdn, el,
741 schema, schema_attr);
742 if (ret != LDB_SUCCESS) {
743 talloc_free(res);
744 return ret;
748 talloc_free(res);
750 return ldb_next_request(module, req);
754 /* queue a linked attributes modify request in the la_private
755 structure */
756 static int la_queue_mod_request(struct la_context *ac)
758 struct la_private *la_private =
759 talloc_get_type(ldb_module_get_private(ac->module), struct la_private);
761 if (la_private == NULL) {
762 ldb_debug(ldb_module_get_ctx(ac->module), LDB_DEBUG_ERROR, __location__ ": No la_private transaction setup\n");
763 return LDB_ERR_OPERATIONS_ERROR;
766 talloc_steal(la_private, ac);
767 DLIST_ADD(la_private->la_list, ac);
769 return ldb_module_done(ac->req, ac->op_controls,
770 ac->op_response, LDB_SUCCESS);
773 /* Having done the original operation, then try to fix up all the linked attributes for modify and delete */
774 static int la_mod_del_callback(struct ldb_request *req, struct ldb_reply *ares)
776 struct la_context *ac;
777 struct ldb_context *ldb;
778 int ret;
780 ac = talloc_get_type(req->context, struct la_context);
781 ldb = ldb_module_get_ctx(ac->module);
783 if (!ares) {
784 return ldb_module_done(ac->req, NULL, NULL,
785 LDB_ERR_OPERATIONS_ERROR);
787 if (ares->error != LDB_SUCCESS) {
788 return ldb_module_done(ac->req, ares->controls,
789 ares->response, ares->error);
792 if (ares->type != LDB_REPLY_DONE) {
793 ldb_set_errstring(ldb,
794 "invalid ldb_reply_type in callback");
795 talloc_free(ares);
796 return ldb_module_done(ac->req, NULL, NULL,
797 LDB_ERR_OPERATIONS_ERROR);
800 ac->op_controls = talloc_steal(ac, ares->controls);
801 ac->op_response = talloc_steal(ac, ares->response);
803 /* If we have modfies to make, this is the time to do them for modify and delete */
804 ret = la_queue_mod_request(ac);
806 if (ret != LDB_SUCCESS) {
807 return ldb_module_done(ac->req, NULL, NULL, ret);
809 talloc_free(ares);
811 /* la_queue_mod_request has already sent the callbacks */
812 return LDB_SUCCESS;
816 /* Having done the original add, then try to fix up all the linked attributes
818 This is done after the add so the links can get the extended DNs correctly.
820 static int la_add_callback(struct ldb_request *req, struct ldb_reply *ares)
822 struct la_context *ac;
823 struct ldb_context *ldb;
824 int ret;
826 ac = talloc_get_type(req->context, struct la_context);
827 ldb = ldb_module_get_ctx(ac->module);
829 if (!ares) {
830 return ldb_module_done(ac->req, NULL, NULL,
831 LDB_ERR_OPERATIONS_ERROR);
833 if (ares->error != LDB_SUCCESS) {
834 return ldb_module_done(ac->req, ares->controls,
835 ares->response, ares->error);
838 if (ares->type != LDB_REPLY_DONE) {
839 ldb_set_errstring(ldb,
840 "invalid ldb_reply_type in callback");
841 talloc_free(ares);
842 return ldb_module_done(ac->req, NULL, NULL,
843 LDB_ERR_OPERATIONS_ERROR);
846 if (ac->ops) {
847 struct ldb_request *search_req;
848 static const char *attrs[] = { NULL };
850 /* The callback does all the hard work here - we need
851 * the objectGUID and SID of the added record */
852 ret = ldb_build_search_req(&search_req, ldb, ac,
853 ac->req->op.add.message->dn,
854 LDB_SCOPE_BASE,
855 "(objectClass=*)", attrs,
856 NULL,
857 ac, la_mod_search_callback,
858 ac->req);
860 if (ret == LDB_SUCCESS) {
861 ret = ldb_request_add_control(search_req,
862 LDB_CONTROL_EXTENDED_DN_OID,
863 false, NULL);
865 if (ret != LDB_SUCCESS) {
866 return ldb_module_done(ac->req, NULL, NULL,
867 ret);
870 ac->op_controls = talloc_steal(ac, ares->controls);
871 ac->op_response = talloc_steal(ac, ares->response);
873 return ldb_next_request(ac->module, search_req);
875 } else {
876 return ldb_module_done(ac->req, ares->controls,
877 ares->response, ares->error);
881 /* Reconstruct the original request, but pointing at our local callback to finish things off */
882 static int la_down_req(struct la_context *ac)
884 struct ldb_request *down_req;
885 struct ldb_context *ldb;
886 int ret;
888 ldb = ldb_module_get_ctx(ac->module);
890 switch (ac->req->operation) {
891 case LDB_ADD:
892 ret = ldb_build_add_req(&down_req, ldb, ac,
893 ac->req->op.add.message,
894 ac->req->controls,
895 ac, la_add_callback,
896 ac->req);
897 break;
898 case LDB_MODIFY:
899 ret = ldb_build_mod_req(&down_req, ldb, ac,
900 ac->req->op.mod.message,
901 ac->req->controls,
902 ac, la_mod_del_callback,
903 ac->req);
904 break;
905 default:
906 ret = LDB_ERR_OPERATIONS_ERROR;
908 if (ret != LDB_SUCCESS) {
909 return ret;
912 return ldb_next_request(ac->module, down_req);
916 use the GUID part of an extended DN to find the target DN, in case
917 it has moved
919 static int la_find_dn_target(struct ldb_module *module, struct la_context *ac,
920 struct GUID *guid, struct ldb_dn **dn)
922 return dsdb_find_dn_by_guid(ldb_module_get_ctx(ac->module), ac, guid, dn);
925 /* apply one la_context op change */
926 static int la_do_op_request(struct ldb_module *module, struct la_context *ac, struct la_op_store *op)
928 struct ldb_message_element *ret_el;
929 struct ldb_message *new_msg;
930 struct ldb_context *ldb;
931 int ret;
933 ldb = ldb_module_get_ctx(ac->module);
935 /* Create the modify request */
936 new_msg = ldb_msg_new(ac);
937 if (!new_msg) {
938 ldb_oom(ldb);
939 return LDB_ERR_OPERATIONS_ERROR;
942 ret = la_find_dn_target(module, ac, &op->guid, &new_msg->dn);
943 if (ret != LDB_SUCCESS) {
944 return ret;
947 if (op->op == LA_OP_ADD) {
948 ret = ldb_msg_add_empty(new_msg, op->name,
949 LDB_FLAG_MOD_ADD, &ret_el);
950 } else {
951 ret = ldb_msg_add_empty(new_msg, op->name,
952 LDB_FLAG_MOD_DELETE, &ret_el);
954 if (ret != LDB_SUCCESS) {
955 return ret;
957 ret_el->values = talloc_array(new_msg, struct ldb_val, 1);
958 if (!ret_el->values) {
959 ldb_oom(ldb);
960 return LDB_ERR_OPERATIONS_ERROR;
962 ret_el->num_values = 1;
963 if (op->op == LA_OP_ADD) {
964 ret_el->values[0] = data_blob_string_const(ldb_dn_get_extended_linearized(new_msg, ac->add_dn, 1));
965 } else {
966 ret_el->values[0] = data_blob_string_const(ldb_dn_get_extended_linearized(new_msg, ac->del_dn, 1));
969 #if 0
970 ldb_debug(ldb, LDB_DEBUG_WARNING,
971 "link on %s %s: %s %s\n",
972 ldb_dn_get_linearized(new_msg->dn), ret_el->name,
973 ret_el->values[0].data, ac->ops->op == LA_OP_ADD ? "added" : "deleted");
974 #endif
976 if (DEBUGLVL(4)) {
977 DEBUG(4,("Applying linked attribute change:\n%s\n",
978 ldb_ldif_message_string(ldb, op, LDB_CHANGETYPE_MODIFY, new_msg)));
981 ret = dsdb_module_modify(module, new_msg, DSDB_FLAG_NEXT_MODULE);
982 if (ret != LDB_SUCCESS) {
983 ldb_debug(ldb, LDB_DEBUG_WARNING, "Failed to apply linked attribute change '%s'\n%s\n",
984 ldb_errstring(ldb),
985 ldb_ldif_message_string(ldb, op, LDB_CHANGETYPE_MODIFY, new_msg));
988 return ret;
991 /* apply one set of la_context changes */
992 static int la_do_mod_request(struct ldb_module *module, struct la_context *ac)
994 struct la_op_store *op;
996 for (op = ac->ops; op; op=op->next) {
997 int ret = la_do_op_request(module, ac, op);
998 if (ret != LDB_SUCCESS) {
999 if (ret != LDB_ERR_NO_SUCH_OBJECT) {
1000 return ret;
1005 return LDB_SUCCESS;
1010 we hook into the transaction operations to allow us to
1011 perform the linked attribute updates at the end of the whole
1012 transaction. This allows a forward linked attribute to be created
1013 before the target is created, as long as the target is created
1014 in the same transaction
1016 static int linked_attributes_start_transaction(struct ldb_module *module)
1018 /* create our private structure for this transaction */
1019 struct la_private *la_private = talloc_get_type(ldb_module_get_private(module),
1020 struct la_private);
1021 talloc_free(la_private);
1022 la_private = talloc(module, struct la_private);
1023 if (la_private == NULL) {
1024 return LDB_ERR_OPERATIONS_ERROR;
1026 la_private->la_list = NULL;
1027 ldb_module_set_private(module, la_private);
1028 return ldb_next_start_trans(module);
1032 on prepare commit we loop over our queued la_context structures
1033 and apply each of them
1035 static int linked_attributes_prepare_commit(struct ldb_module *module)
1037 struct la_private *la_private =
1038 talloc_get_type(ldb_module_get_private(module), struct la_private);
1039 struct la_context *ac;
1041 if (!la_private) {
1042 /* prepare commit without begin_transaction - let someone else return the error, just don't segfault */
1043 return ldb_next_prepare_commit(module);
1045 /* walk the list backwards, to do the first entry first, as we
1046 * added the entries with DLIST_ADD() which puts them at the
1047 * start of the list */
1049 /* Start at the end of the list - so we can start
1050 * there, but ensure we don't create a loop by NULLing
1051 * it out in the first element */
1052 ac = DLIST_TAIL(la_private->la_list);
1054 for (; ac; ac=DLIST_PREV(ac)) {
1055 int ret;
1056 ac->req = NULL;
1057 ret = la_do_mod_request(module, ac);
1058 if (ret != LDB_SUCCESS) {
1059 DEBUG(0,(__location__ ": Failed mod request ret=%d\n", ret));
1060 talloc_free(la_private);
1061 ldb_module_set_private(module, NULL);
1062 return ret;
1066 talloc_free(la_private);
1067 ldb_module_set_private(module, NULL);
1069 return ldb_next_prepare_commit(module);
1072 static int linked_attributes_del_transaction(struct ldb_module *module)
1074 struct la_private *la_private =
1075 talloc_get_type(ldb_module_get_private(module), struct la_private);
1076 talloc_free(la_private);
1077 ldb_module_set_private(module, NULL);
1078 return ldb_next_del_trans(module);
1082 _PUBLIC_ const struct ldb_module_ops ldb_linked_attributes_module_ops = {
1083 .name = "linked_attributes",
1084 .add = linked_attributes_add,
1085 .modify = linked_attributes_modify,
1086 .rename = linked_attributes_rename,
1087 .start_transaction = linked_attributes_start_transaction,
1088 .prepare_commit = linked_attributes_prepare_commit,
1089 .del_transaction = linked_attributes_del_transaction,