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/>.
24 * Component: ldb linked_attributes module
26 * Description: Module to ensure linked attribute pairs remain in sync
28 * Author: Andrew Bartlett
32 #include "ldb_module.h"
33 #include "dlinklist.h"
34 #include "dsdb/samdb/samdb.h"
35 #include "librpc/gen_ndr/ndr_misc.h"
38 struct la_context *la_list;
42 struct la_op_store *next;
43 struct la_op_store *prev;
44 enum la_op {LA_OP_ADD, LA_OP_DEL} op;
50 struct replace_context {
51 struct la_context *ac;
52 unsigned int num_elements;
53 struct ldb_message_element *el;
57 struct la_context *next, *prev;
58 const struct dsdb_schema *schema;
59 struct ldb_module *module;
60 struct ldb_request *req;
61 struct ldb_dn *partition_dn;
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;
75 const struct ldb_control *partition_ctrl;
77 ldb = ldb_module_get_ctx(module);
79 ac = talloc_zero(req, struct la_context);
85 ac->schema = dsdb_get_schema(ldb);
89 /* remember the partition DN that came in, if given */
90 partition_ctrl = ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID);
92 const struct dsdb_control_current_partition *partition;
93 partition = talloc_get_type(partition_ctrl->data,
94 struct dsdb_control_current_partition);
95 SMB_ASSERT(partition && partition->version == DSDB_CONTROL_CURRENT_PARTITION_VERSION);
97 ac->partition_dn = ldb_dn_copy(ac, partition->dn);
104 turn a DN into a GUID
106 static int la_guid_from_dn(struct la_context *ac, struct ldb_dn *dn, struct GUID *guid)
108 const struct ldb_val *guid_val;
111 guid_val = ldb_dn_get_extended_component(dn, "GUID");
113 /* there is a GUID embedded in the DN */
114 enum ndr_err_code ndr_err;
115 ndr_err = ndr_pull_struct_blob(guid_val, ac, NULL, guid,
116 (ndr_pull_flags_fn_t)ndr_pull_GUID);
117 if (ndr_err != NDR_ERR_SUCCESS) {
118 DEBUG(0,(__location__ ": Failed to parse GUID\n"));
119 return LDB_ERR_OPERATIONS_ERROR;
122 ret = dsdb_find_guid_by_dn(ldb_module_get_ctx(ac->module), dn, guid);
123 if (ret != LDB_SUCCESS) {
124 DEBUG(4,(__location__ ": Failed to find GUID for dn %s\n",
125 ldb_dn_get_linearized(dn)));
133 /* Common routine to handle reading the attributes and creating a
134 * series of modify requests */
135 static int la_store_op(struct la_context *ac,
136 enum la_op op, struct ldb_val *dn,
139 struct ldb_context *ldb;
140 struct la_op_store *os;
141 struct ldb_dn *op_dn;
144 ldb = ldb_module_get_ctx(ac->module);
146 op_dn = ldb_dn_from_ldb_val(ac, ldb, dn);
148 ldb_asprintf_errstring(ldb,
149 "could not parse attribute as a DN");
150 return LDB_ERR_INVALID_DN_SYNTAX;
153 os = talloc_zero(ac, struct la_op_store);
156 return LDB_ERR_OPERATIONS_ERROR;
161 ret = la_guid_from_dn(ac, op_dn, &os->guid);
162 if (ret == LDB_ERR_NO_SUCH_OBJECT && ac->req->operation == LDB_DELETE) {
163 /* we are deleting an object, and we've found it has a
164 * forward link to a target that no longer
165 * exists. This is not an error in the delete, and we
166 * should just not do the deferred delete of the
172 if (ret != LDB_SUCCESS) {
176 os->name = talloc_strdup(os, name);
179 return LDB_ERR_OPERATIONS_ERROR;
182 /* Do deletes before adds */
183 if (op == LA_OP_ADD) {
184 DLIST_ADD_END(ac->ops, os, struct la_op_store *);
186 /* By adding to the head of the list, we do deletes before
187 * adds when processing a replace */
188 DLIST_ADD(ac->ops, os);
194 static int la_op_search_callback(struct ldb_request *req,
195 struct ldb_reply *ares);
196 static int la_queue_mod_request(struct la_context *ac);
197 static int la_down_req(struct la_context *ac);
202 static int linked_attributes_add(struct ldb_module *module, struct ldb_request *req)
204 struct ldb_context *ldb;
205 const struct dsdb_attribute *target_attr;
206 struct la_context *ac;
207 const char *attr_name;
211 ldb = ldb_module_get_ctx(module);
213 if (ldb_dn_is_special(req->op.add.message->dn)) {
214 /* do not manipulate our control entries */
215 return ldb_next_request(module, req);
218 ac = linked_attributes_init(module, req);
220 return LDB_ERR_OPERATIONS_ERROR;
224 /* without schema, this doesn't make any sense */
226 return ldb_next_request(module, req);
229 /* Need to ensure we only have forward links being specified */
230 for (i=0; i < req->op.add.message->num_elements; i++) {
231 const struct ldb_message_element *el = &req->op.add.message->elements[i];
232 const struct dsdb_attribute *schema_attr
233 = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
235 ldb_asprintf_errstring(ldb,
236 "attribute %s is not a valid attribute in schema", el->name);
237 return LDB_ERR_OBJECT_CLASS_VIOLATION;
239 /* We have a valid attribute, now find out if it is linked */
240 if (schema_attr->linkID == 0) {
244 if ((schema_attr->linkID & 1) == 1) {
245 /* Odd is for the target. Illegal to modify */
246 ldb_asprintf_errstring(ldb,
247 "attribute %s must not be modified directly, it is a linked attribute", el->name);
248 return LDB_ERR_UNWILLING_TO_PERFORM;
251 /* Even link IDs are for the originating attribute */
252 target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID + 1);
255 * windows 2003 has a broken schema where
256 * the definition of msDS-IsDomainFor
257 * is missing (which is supposed to be
258 * the backlink of the msDS-HasDomainNCs
264 attr_name = target_attr->lDAPDisplayName;
266 for (j = 0; j < el->num_values; j++) {
267 ret = la_store_op(ac, LA_OP_ADD,
270 if (ret != LDB_SUCCESS) {
276 /* if no linked attributes are present continue */
277 if (ac->ops == NULL) {
278 /* nothing to do for this module, proceed */
280 return ldb_next_request(module, req);
283 /* start with the original request */
284 return la_down_req(ac);
287 /* For a delete or rename, we need to find out what linked attributes
288 * are currently on this DN, and then deal with them. This is the
289 * callback to the base search */
291 static int la_mod_search_callback(struct ldb_request *req, struct ldb_reply *ares)
293 struct ldb_context *ldb;
294 const struct dsdb_attribute *schema_attr;
295 const struct dsdb_attribute *target_attr;
296 struct ldb_message_element *search_el;
297 struct replace_context *rc;
298 struct la_context *ac;
299 const char *attr_name;
301 int ret = LDB_SUCCESS;
303 ac = talloc_get_type(req->context, struct la_context);
304 ldb = ldb_module_get_ctx(ac->module);
308 return ldb_module_done(ac->req, NULL, NULL,
309 LDB_ERR_OPERATIONS_ERROR);
311 if (ares->error != LDB_SUCCESS) {
312 return ldb_module_done(ac->req, ares->controls,
313 ares->response, ares->error);
316 /* Only entries are interesting, and we only want the olddn */
317 switch (ares->type) {
318 case LDB_REPLY_ENTRY:
320 if (ldb_dn_compare(ares->message->dn, ac->req->op.mod.message->dn) != 0) {
321 ldb_asprintf_errstring(ldb,
322 "linked_attributes: %s is not the DN we were looking for", ldb_dn_get_linearized(ares->message->dn));
323 /* Guh? We only asked for this DN */
325 return ldb_module_done(ac->req, NULL, NULL,
326 LDB_ERR_OPERATIONS_ERROR);
329 ac->add_dn = ac->del_dn = talloc_steal(ac, ares->message->dn);
331 /* We don't populate 'rc' for ADD - it can't be deleting elements anyway */
332 for (i = 0; rc && i < rc->num_elements; i++) {
334 schema_attr = dsdb_attribute_by_lDAPDisplayName(ac->schema, rc->el[i].name);
336 ldb_asprintf_errstring(ldb,
337 "attribute %s is not a valid attribute in schema",
340 return ldb_module_done(ac->req, NULL, NULL,
341 LDB_ERR_OBJECT_CLASS_VIOLATION);
344 search_el = ldb_msg_find_element(ares->message,
347 /* See if this element already exists */
348 /* otherwise just ignore as
349 * the add has already been scheduled */
354 target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID + 1);
357 * windows 2003 has a broken schema where
358 * the definition of msDS-IsDomainFor
359 * is missing (which is supposed to be
360 * the backlink of the msDS-HasDomainNCs
365 attr_name = target_attr->lDAPDisplayName;
367 /* Now we know what was there, we can remove it for the re-add */
368 for (j = 0; j < search_el->num_values; j++) {
369 ret = la_store_op(ac, LA_OP_DEL,
370 &search_el->values[j],
372 if (ret != LDB_SUCCESS) {
374 return ldb_module_done(ac->req,
382 case LDB_REPLY_REFERRAL:
390 if (ac->req->operation == LDB_ADD) {
391 /* Start the modifies to the backlinks */
392 ret = la_queue_mod_request(ac);
394 if (ret != LDB_SUCCESS) {
395 return ldb_module_done(ac->req, NULL, NULL,
399 /* Start with the original request */
400 ret = la_down_req(ac);
401 if (ret != LDB_SUCCESS) {
402 return ldb_module_done(ac->req, NULL, NULL, ret);
414 static int linked_attributes_modify(struct ldb_module *module, struct ldb_request *req)
416 /* Look over list of modifications */
417 /* Find if any are for linked attributes */
418 /* Determine the effect of the modification */
419 /* Apply the modify to the linked entry */
421 struct ldb_context *ldb;
423 struct la_context *ac;
424 struct ldb_request *search_req;
429 ldb = ldb_module_get_ctx(module);
431 if (ldb_dn_is_special(req->op.mod.message->dn)) {
432 /* do not manipulate our control entries */
433 return ldb_next_request(module, req);
436 ac = linked_attributes_init(module, req);
438 return LDB_ERR_OPERATIONS_ERROR;
442 /* without schema, this doesn't make any sense */
443 return ldb_next_request(module, req);
446 ac->rc = talloc_zero(ac, struct replace_context);
449 return LDB_ERR_OPERATIONS_ERROR;
452 for (i=0; i < req->op.mod.message->num_elements; i++) {
453 bool store_el = false;
454 const char *attr_name;
455 const struct dsdb_attribute *target_attr;
456 const struct ldb_message_element *el = &req->op.mod.message->elements[i];
457 const struct dsdb_attribute *schema_attr
458 = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
460 ldb_asprintf_errstring(ldb,
461 "attribute %s is not a valid attribute in schema", el->name);
462 return LDB_ERR_OBJECT_CLASS_VIOLATION;
464 /* We have a valid attribute, now find out if it is linked */
465 if (schema_attr->linkID == 0) {
469 if ((schema_attr->linkID & 1) == 1) {
470 /* Odd is for the target. Illegal to modify */
471 ldb_asprintf_errstring(ldb,
472 "attribute %s must not be modified directly, it is a linked attribute", el->name);
473 return LDB_ERR_UNWILLING_TO_PERFORM;
476 /* Even link IDs are for the originating attribute */
478 /* Now find the target attribute */
479 target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID + 1);
482 * windows 2003 has a broken schema where
483 * the definition of msDS-IsDomainFor
484 * is missing (which is supposed to be
485 * the backlink of the msDS-HasDomainNCs
491 attr_name = target_attr->lDAPDisplayName;
493 switch (el->flags & LDB_FLAG_MOD_MASK) {
494 case LDB_FLAG_MOD_REPLACE:
495 /* treat as just a normal add the delete part is handled by the callback */
498 /* break intentionally missing */
500 case LDB_FLAG_MOD_ADD:
502 /* For each value being added, we need to setup the adds */
503 for (j = 0; j < el->num_values; j++) {
504 ret = la_store_op(ac, LA_OP_ADD,
507 if (ret != LDB_SUCCESS) {
513 case LDB_FLAG_MOD_DELETE:
515 if (el->num_values) {
516 /* For each value being deleted, we need to setup the delete */
517 for (j = 0; j < el->num_values; j++) {
518 ret = la_store_op(ac, LA_OP_DEL,
521 if (ret != LDB_SUCCESS) {
526 /* Flag that there was a DELETE
527 * without a value specified, so we
528 * need to look for the old value */
536 struct ldb_message_element *search_el;
538 search_el = talloc_realloc(ac->rc, ac->rc->el,
539 struct ldb_message_element,
540 ac->rc->num_elements +1);
543 return LDB_ERR_OPERATIONS_ERROR;
545 ac->rc->el = search_el;
547 ac->rc->el[ac->rc->num_elements] = *el;
548 ac->rc->num_elements++;
552 if (ac->ops || ac->rc->el) {
553 /* both replace and delete without values are handled in the callback
554 * after the search on the entry to be modified is performed */
556 attrs = talloc_array(ac->rc, const char *, ac->rc->num_elements + 1);
559 return LDB_ERR_OPERATIONS_ERROR;
561 for (i = 0; ac->rc && i < ac->rc->num_elements; i++) {
562 attrs[i] = ac->rc->el[i].name;
566 /* The callback does all the hard work here */
567 ret = ldb_build_search_req(&search_req, ldb, ac,
568 req->op.mod.message->dn,
570 "(objectClass=*)", attrs,
572 ac, la_mod_search_callback,
575 /* We need to figure out our own extended DN, to fill in as the backlink target */
576 if (ret == LDB_SUCCESS) {
577 ret = ldb_request_add_control(search_req,
578 LDB_CONTROL_EXTENDED_DN_OID,
581 if (ret == LDB_SUCCESS) {
582 talloc_steal(search_req, attrs);
584 ret = ldb_next_request(module, search_req);
588 /* nothing to do for this module, proceed */
590 ret = ldb_next_request(module, req);
597 static int linked_attributes_del(struct ldb_module *module, struct ldb_request *req)
599 struct ldb_context *ldb;
600 struct ldb_request *search_req;
601 struct la_context *ac;
606 /* This gets complex: We need to:
607 - Do a search for the entry
608 - Wait for these result to appear
609 - In the callback for the result, issue a modify
610 request based on the linked attributes found
611 - Wait for each modify result
615 ldb = ldb_module_get_ctx(module);
617 ac = linked_attributes_init(module, req);
619 return LDB_ERR_OPERATIONS_ERROR;
623 /* without schema, this doesn't make any sense */
624 return ldb_next_request(module, req);
627 werr = dsdb_linked_attribute_lDAPDisplayName_list(ac->schema, ac, &attrs);
628 if (!W_ERROR_IS_OK(werr)) {
629 return LDB_ERR_OPERATIONS_ERROR;
632 ret = ldb_build_search_req(&search_req, ldb, req,
633 req->op.del.dn, LDB_SCOPE_BASE,
634 "(objectClass=*)", attrs,
636 ac, la_op_search_callback,
639 if (ret != LDB_SUCCESS) {
643 talloc_steal(search_req, attrs);
645 return ldb_next_request(module, search_req);
649 static int linked_attributes_rename(struct ldb_module *module, struct ldb_request *req)
651 struct la_context *ac;
653 /* This gets complex: We need to:
654 - Do a search for the entry
655 - Wait for these result to appear
656 - In the callback for the result, issue a modify
657 request based on the linked attributes found
658 - Wait for each modify result
662 ac = linked_attributes_init(module, req);
664 return LDB_ERR_OPERATIONS_ERROR;
668 /* without schema, this doesn't make any sense */
669 return ldb_next_request(module, req);
672 /* start with the original request */
673 return la_down_req(ac);
677 static int la_op_search_callback(struct ldb_request *req,
678 struct ldb_reply *ares)
680 struct ldb_context *ldb;
681 struct la_context *ac;
682 const struct dsdb_attribute *schema_attr;
683 const struct dsdb_attribute *target_attr;
684 const struct ldb_message_element *el;
685 const char *attr_name;
689 ac = talloc_get_type(req->context, struct la_context);
690 ldb = ldb_module_get_ctx(ac->module);
693 return ldb_module_done(ac->req, NULL, NULL,
694 LDB_ERR_OPERATIONS_ERROR);
696 if (ares->error != LDB_SUCCESS) {
697 return ldb_module_done(ac->req, ares->controls,
698 ares->response, ares->error);
701 /* Only entries are interesting, and we only want the olddn */
702 switch (ares->type) {
703 case LDB_REPLY_ENTRY:
704 ret = ldb_dn_compare(ares->message->dn, req->op.search.base);
706 /* Guh? We only asked for this DN */
708 return ldb_module_done(ac->req, NULL, NULL,
709 LDB_ERR_OPERATIONS_ERROR);
711 if (ares->message->num_elements == 0) {
712 /* only bother at all if there were some
713 * linked attributes found */
718 switch (ac->req->operation) {
720 ac->del_dn = talloc_steal(ac, ares->message->dn);
723 ac->add_dn = talloc_steal(ac, ares->message->dn);
724 ac->del_dn = talloc_steal(ac, ac->req->op.rename.olddn);
728 ldb_set_errstring(ldb,
729 "operations must be delete or rename");
730 return ldb_module_done(ac->req, NULL, NULL,
731 LDB_ERR_OPERATIONS_ERROR);
734 for (i = 0; i < ares->message->num_elements; i++) {
735 el = &ares->message->elements[i];
737 schema_attr = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
739 ldb_asprintf_errstring(ldb,
740 "attribute %s is not a valid attribute"
741 " in schema", el->name);
743 return ldb_module_done(ac->req, NULL, NULL,
744 LDB_ERR_OBJECT_CLASS_VIOLATION);
747 /* Valid attribute, now find out if it is linked */
748 if (schema_attr->linkID == 0) {
749 /* Not a linked attribute, skip */
753 if ((schema_attr->linkID & 1) == 0) {
754 /* Odd is for the target. */
755 target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID + 1);
759 attr_name = target_attr->lDAPDisplayName;
761 target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID - 1);
765 attr_name = target_attr->lDAPDisplayName;
767 for (j = 0; j < el->num_values; j++) {
768 ret = la_store_op(ac, LA_OP_DEL,
772 /* for renames, ensure we add it back */
773 if (ret == LDB_SUCCESS
774 && ac->req->operation == LDB_RENAME) {
775 ret = la_store_op(ac, LA_OP_ADD,
779 if (ret != LDB_SUCCESS) {
781 return ldb_module_done(ac->req,
789 case LDB_REPLY_REFERRAL:
798 switch (ac->req->operation) {
800 /* start the mod requests chain */
801 ret = la_down_req(ac);
802 if (ret != LDB_SUCCESS) {
803 return ldb_module_done(ac->req, NULL, NULL, ret);
808 /* start the mod requests chain */
809 ret = la_queue_mod_request(ac);
810 if (ret != LDB_SUCCESS) {
811 return ldb_module_done(ac->req, NULL, NULL,
818 ldb_set_errstring(ldb,
819 "operations must be delete or rename");
820 return ldb_module_done(ac->req, NULL, NULL,
821 LDB_ERR_OPERATIONS_ERROR);
829 /* queue a linked attributes modify request in the la_private
831 static int la_queue_mod_request(struct la_context *ac)
833 struct la_private *la_private =
834 talloc_get_type(ldb_module_get_private(ac->module), struct la_private);
836 if (la_private == NULL) {
837 ldb_debug(ldb_module_get_ctx(ac->module), LDB_DEBUG_ERROR, __location__ ": No la_private transaction setup\n");
838 return LDB_ERR_OPERATIONS_ERROR;
841 talloc_steal(la_private, ac);
842 DLIST_ADD(la_private->la_list, ac);
844 return ldb_module_done(ac->req, ac->op_controls,
845 ac->op_response, LDB_SUCCESS);
848 /* Having done the original operation, then try to fix up all the linked attributes for modify and delete */
849 static int la_mod_del_callback(struct ldb_request *req, struct ldb_reply *ares)
852 struct la_context *ac;
853 struct ldb_context *ldb;
855 ac = talloc_get_type(req->context, struct la_context);
856 ldb = ldb_module_get_ctx(ac->module);
859 return ldb_module_done(ac->req, NULL, NULL,
860 LDB_ERR_OPERATIONS_ERROR);
862 if (ares->error != LDB_SUCCESS) {
863 return ldb_module_done(ac->req, ares->controls,
864 ares->response, ares->error);
867 if (ares->type != LDB_REPLY_DONE) {
868 ldb_set_errstring(ldb,
869 "invalid ldb_reply_type in callback");
871 return ldb_module_done(ac->req, NULL, NULL,
872 LDB_ERR_OPERATIONS_ERROR);
875 ac->op_controls = talloc_steal(ac, ares->controls);
876 ac->op_response = talloc_steal(ac, ares->response);
878 /* If we have modfies to make, this is the time to do them for modify and delete */
879 ret = la_queue_mod_request(ac);
881 if (ret != LDB_SUCCESS) {
882 return ldb_module_done(ac->req, NULL, NULL, ret);
886 /* la_queue_mod_request has already sent the callbacks */
891 /* Having done the original rename try to fix up all the linked attributes */
892 static int la_rename_callback(struct ldb_request *req, struct ldb_reply *ares)
895 struct la_context *ac;
896 struct ldb_request *search_req;
899 struct ldb_context *ldb;
901 ac = talloc_get_type(req->context, struct la_context);
902 ldb = ldb_module_get_ctx(ac->module);
905 return ldb_module_done(ac->req, NULL, NULL,
906 LDB_ERR_OPERATIONS_ERROR);
908 if (ares->error != LDB_SUCCESS) {
909 return ldb_module_done(ac->req, ares->controls,
910 ares->response, ares->error);
913 if (ares->type != LDB_REPLY_DONE) {
914 ldb_set_errstring(ldb,
915 "invalid ldb_reply_type in callback");
917 return ldb_module_done(ac->req, NULL, NULL,
918 LDB_ERR_OPERATIONS_ERROR);
921 werr = dsdb_linked_attribute_lDAPDisplayName_list(ac->schema, ac, &attrs);
922 if (!W_ERROR_IS_OK(werr)) {
923 return LDB_ERR_OPERATIONS_ERROR;
926 ret = ldb_build_search_req(&search_req, ldb, req,
927 ac->req->op.rename.newdn, LDB_SCOPE_BASE,
928 "(objectClass=*)", attrs,
930 ac, la_op_search_callback,
933 if (ret != LDB_SUCCESS) {
937 talloc_steal(search_req, attrs);
939 if (ret == LDB_SUCCESS) {
940 ret = ldb_request_add_control(search_req,
941 LDB_CONTROL_EXTENDED_DN_OID,
944 if (ret != LDB_SUCCESS) {
945 return ldb_module_done(ac->req, NULL, NULL,
949 ac->op_controls = talloc_steal(ac, ares->controls);
950 ac->op_response = talloc_steal(ac, ares->response);
952 return ldb_next_request(ac->module, search_req);
955 /* Having done the original add, then try to fix up all the linked attributes
957 This is done after the add so the links can get the extended DNs correctly.
959 static int la_add_callback(struct ldb_request *req, struct ldb_reply *ares)
962 struct la_context *ac;
963 struct ldb_context *ldb;
965 ac = talloc_get_type(req->context, struct la_context);
966 ldb = ldb_module_get_ctx(ac->module);
969 return ldb_module_done(ac->req, NULL, NULL,
970 LDB_ERR_OPERATIONS_ERROR);
972 if (ares->error != LDB_SUCCESS) {
973 return ldb_module_done(ac->req, ares->controls,
974 ares->response, ares->error);
977 if (ares->type != LDB_REPLY_DONE) {
978 ldb_set_errstring(ldb,
979 "invalid ldb_reply_type in callback");
981 return ldb_module_done(ac->req, NULL, NULL,
982 LDB_ERR_OPERATIONS_ERROR);
986 struct ldb_request *search_req;
987 static const char *attrs[] = { NULL };
989 /* The callback does all the hard work here - we need
990 * the objectGUID and SID of the added record */
991 ret = ldb_build_search_req(&search_req, ldb, ac,
992 ac->req->op.add.message->dn,
994 "(objectClass=*)", attrs,
996 ac, la_mod_search_callback,
999 if (ret == LDB_SUCCESS) {
1000 ret = ldb_request_add_control(search_req,
1001 LDB_CONTROL_EXTENDED_DN_OID,
1004 if (ret != LDB_SUCCESS) {
1005 return ldb_module_done(ac->req, NULL, NULL,
1009 ac->op_controls = talloc_steal(ac, ares->controls);
1010 ac->op_response = talloc_steal(ac, ares->response);
1012 return ldb_next_request(ac->module, search_req);
1015 return ldb_module_done(ac->req, ares->controls,
1016 ares->response, ares->error);
1020 /* Reconstruct the original request, but pointing at our local callback to finish things off */
1021 static int la_down_req(struct la_context *ac)
1023 struct ldb_request *down_req;
1025 struct ldb_context *ldb;
1027 ldb = ldb_module_get_ctx(ac->module);
1029 switch (ac->req->operation) {
1031 ret = ldb_build_add_req(&down_req, ldb, ac,
1032 ac->req->op.add.message,
1034 ac, la_add_callback,
1038 ret = ldb_build_mod_req(&down_req, ldb, ac,
1039 ac->req->op.mod.message,
1041 ac, la_mod_del_callback,
1045 ret = ldb_build_del_req(&down_req, ldb, ac,
1048 ac, la_mod_del_callback,
1052 ret = ldb_build_rename_req(&down_req, ldb, ac,
1053 ac->req->op.rename.olddn,
1054 ac->req->op.rename.newdn,
1056 ac, la_rename_callback,
1060 ret = LDB_ERR_OPERATIONS_ERROR;
1062 if (ret != LDB_SUCCESS) {
1066 return ldb_next_request(ac->module, down_req);
1070 use the GUID part of an extended DN to find the target DN, in case
1073 static int la_find_dn_target(struct ldb_module *module, struct la_context *ac,
1074 struct GUID *guid, struct ldb_dn **dn)
1076 return dsdb_find_dn_by_guid(ldb_module_get_ctx(ac->module), ac, GUID_string(ac, guid), dn);
1079 /* apply one la_context op change */
1080 static int la_do_op_request(struct ldb_module *module, struct la_context *ac, struct la_op_store *op)
1082 struct ldb_message_element *ret_el;
1083 struct ldb_request *mod_req;
1084 struct ldb_message *new_msg;
1085 struct ldb_context *ldb;
1088 ldb = ldb_module_get_ctx(ac->module);
1090 /* Create the modify request */
1091 new_msg = ldb_msg_new(ac);
1094 return LDB_ERR_OPERATIONS_ERROR;
1097 ret = la_find_dn_target(module, ac, &op->guid, &new_msg->dn);
1098 if (ret != LDB_SUCCESS) {
1102 if (op->op == LA_OP_ADD) {
1103 ret = ldb_msg_add_empty(new_msg, op->name,
1104 LDB_FLAG_MOD_ADD, &ret_el);
1106 ret = ldb_msg_add_empty(new_msg, op->name,
1107 LDB_FLAG_MOD_DELETE, &ret_el);
1109 if (ret != LDB_SUCCESS) {
1112 ret_el->values = talloc_array(new_msg, struct ldb_val, 1);
1113 if (!ret_el->values) {
1115 return LDB_ERR_OPERATIONS_ERROR;
1117 ret_el->num_values = 1;
1118 if (op->op == LA_OP_ADD) {
1119 ret_el->values[0] = data_blob_string_const(ldb_dn_get_extended_linearized(new_msg, ac->add_dn, 1));
1121 ret_el->values[0] = data_blob_string_const(ldb_dn_get_extended_linearized(new_msg, ac->del_dn, 1));
1125 ldb_debug(ldb, LDB_DEBUG_WARNING,
1126 "link on %s %s: %s %s\n",
1127 ldb_dn_get_linearized(new_msg->dn), ret_el->name,
1128 ret_el->values[0].data, ac->ops->op == LA_OP_ADD ? "added" : "deleted");
1131 ret = ldb_build_mod_req(&mod_req, ldb, op,
1135 ldb_op_default_callback,
1137 if (ret != LDB_SUCCESS) {
1140 talloc_steal(mod_req, new_msg);
1143 DEBUG(4,("Applying linked attribute change:\n%s\n",
1144 ldb_ldif_message_string(ldb, op, LDB_CHANGETYPE_MODIFY, new_msg)));
1147 /* Run the new request */
1148 ret = ldb_next_request(module, mod_req);
1150 /* we need to wait for this to finish, as we are being called
1151 from the synchronous end_transaction hook of this module */
1152 if (ret == LDB_SUCCESS) {
1153 ret = ldb_wait(mod_req->handle, LDB_WAIT_ALL);
1156 if (ret != LDB_SUCCESS) {
1157 ldb_debug(ldb, LDB_DEBUG_WARNING, "Failed to apply linked attribute change '%s' %s\n",
1159 ldb_ldif_message_string(ldb, op, LDB_CHANGETYPE_MODIFY, new_msg));
1165 /* apply one set of la_context changes */
1166 static int la_do_mod_request(struct ldb_module *module, struct la_context *ac)
1168 struct la_op_store *op;
1170 for (op = ac->ops; op; op=op->next) {
1171 int ret = la_do_op_request(module, ac, op);
1172 if (ret != LDB_SUCCESS) {
1173 if (ret != LDB_ERR_NO_SUCH_OBJECT) {
1184 we hook into the transaction operations to allow us to
1185 perform the linked attribute updates at the end of the whole
1186 transaction. This allows a forward linked attribute to be created
1187 before the target is created, as long as the target is created
1188 in the same transaction
1190 static int linked_attributes_start_transaction(struct ldb_module *module)
1192 /* create our private structure for this transaction */
1193 struct la_private *la_private = talloc_get_type(ldb_module_get_private(module),
1195 talloc_free(la_private);
1196 la_private = talloc(module, struct la_private);
1197 if (la_private == NULL) {
1198 return LDB_ERR_OPERATIONS_ERROR;
1200 la_private->la_list = NULL;
1201 ldb_module_set_private(module, la_private);
1202 return ldb_next_start_trans(module);
1206 on prepare commit we loop over our queued la_context structures
1207 and apply each of them
1209 static int linked_attributes_prepare_commit(struct ldb_module *module)
1211 struct la_private *la_private =
1212 talloc_get_type(ldb_module_get_private(module), struct la_private);
1213 struct la_context *ac;
1216 /* prepare commit without begin_transaction - let someone else return the error, just don't segfault */
1217 return ldb_next_prepare_commit(module);
1219 /* walk the list backwards, to do the first entry first, as we
1220 * added the entries with DLIST_ADD() which puts them at the
1221 * start of the list */
1222 for (ac = la_private->la_list; ac && ac->next; ac=ac->next) ;
1224 for (; ac; ac=ac->prev) {
1227 ret = la_do_mod_request(module, ac);
1228 if (ret != LDB_SUCCESS) {
1229 DEBUG(0,(__location__ ": Failed mod request ret=%d\n", ret));
1230 talloc_free(la_private);
1231 ldb_module_set_private(module, NULL);
1236 talloc_free(la_private);
1237 ldb_module_set_private(module, NULL);
1239 return ldb_next_prepare_commit(module);
1242 static int linked_attributes_del_transaction(struct ldb_module *module)
1244 struct la_private *la_private =
1245 talloc_get_type(ldb_module_get_private(module), struct la_private);
1246 talloc_free(la_private);
1247 ldb_module_set_private(module, NULL);
1248 return ldb_next_del_trans(module);
1252 _PUBLIC_ const struct ldb_module_ops ldb_linked_attributes_module_ops = {
1253 .name = "linked_attributes",
1254 .add = linked_attributes_add,
1255 .modify = linked_attributes_modify,
1256 .del = linked_attributes_del,
1257 .rename = linked_attributes_rename,
1258 .start_transaction = linked_attributes_start_transaction,
1259 .prepare_commit = linked_attributes_prepare_commit,
1260 .del_transaction = linked_attributes_del_transaction,