/* [<][>][^][v][top][bottom][index][help] */
DEFINITIONS
This source file includes following definitions.
- linked_attributes_init
- la_store_op
- linked_attributes_add
- la_mod_search_callback
- linked_attributes_modify
- linked_attributes_del
- linked_attributes_rename
- la_op_search_callback
- la_do_mod_request
- la_mod_callback
- la_mod_del_callback
- la_rename_callback
- la_add_callback
- la_down_req
1 /*
2 ldb database library
3
4 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2007
5 Copyright (C) Simo Sorce <idra@samba.org> 2008
6
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.
11
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.
16
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/>.
19 */
20
21 /*
22 * Name: ldb
23 *
24 * Component: ldb linked_attributes module
25 *
26 * Description: Module to ensure linked attribute pairs remain in sync
27 *
28 * Author: Andrew Bartlett
29 */
30
31 #include "includes.h"
32 #include "ldb_module.h"
33 #include "dlinklist.h"
34 #include "dsdb/samdb/samdb.h"
35
36 struct la_op_store {
37 struct la_op_store *next;
38 struct la_op_store *prev;
39 enum la_op {LA_OP_ADD, LA_OP_DEL} op;
40 struct ldb_dn *dn;
41 char *name;
42 char *value;
43 };
44
45 struct replace_context {
46 struct la_context *ac;
47 unsigned int num_elements;
48 struct ldb_message_element *el;
49 };
50
51 struct la_context {
52 const struct dsdb_schema *schema;
53 struct ldb_module *module;
54 struct ldb_request *req;
55 struct ldb_dn *add_dn;
56 struct ldb_dn *del_dn;
57 struct replace_context *rc;
58 struct la_op_store *ops;
59 struct ldb_extended *op_response;
60 struct ldb_control **op_controls;
61 };
62
63 static struct la_context *linked_attributes_init(struct ldb_module *module,
/* [<][>][^][v][top][bottom][index][help] */
64 struct ldb_request *req)
65 {
66 struct ldb_context *ldb;
67 struct la_context *ac;
68
69 ldb = ldb_module_get_ctx(module);
70
71 ac = talloc_zero(req, struct la_context);
72 if (ac == NULL) {
73 ldb_oom(ldb);
74 return NULL;
75 }
76
77 ac->schema = dsdb_get_schema(ldb);
78 ac->module = module;
79 ac->req = req;
80
81 return ac;
82 }
83
84 /* Common routine to handle reading the attributes and creating a
85 * series of modify requests */
86 static int la_store_op(struct la_context *ac,
/* [<][>][^][v][top][bottom][index][help] */
87 enum la_op op, struct ldb_val *dn,
88 const char *name)
89 {
90 struct ldb_context *ldb;
91 struct la_op_store *os;
92 struct ldb_dn *op_dn;
93
94 ldb = ldb_module_get_ctx(ac->module);
95
96 op_dn = ldb_dn_from_ldb_val(ac, ldb, dn);
97 if (!op_dn) {
98 ldb_asprintf_errstring(ldb,
99 "could not parse attribute as a DN");
100 return LDB_ERR_INVALID_DN_SYNTAX;
101 }
102
103 os = talloc_zero(ac, struct la_op_store);
104 if (!os) {
105 ldb_oom(ldb);
106 return LDB_ERR_OPERATIONS_ERROR;
107 }
108
109 os->op = op;
110
111 os->dn = talloc_steal(os, op_dn);
112
113 os->name = talloc_strdup(os, name);
114 if (!os->name) {
115 ldb_oom(ldb);
116 return LDB_ERR_OPERATIONS_ERROR;
117 }
118
119 /* Do deletes before adds */
120 if (op == LA_OP_ADD) {
121 DLIST_ADD_END(ac->ops, os, struct la_op_store *);
122 } else {
123 /* By adding to the head of the list, we do deletes before
124 * adds when processing a replace */
125 DLIST_ADD(ac->ops, os);
126 }
127
128 return LDB_SUCCESS;
129 }
130
131 static int la_op_search_callback(struct ldb_request *req,
132 struct ldb_reply *ares);
133 static int la_do_mod_request(struct la_context *ac);
134 static int la_mod_callback(struct ldb_request *req,
135 struct ldb_reply *ares);
136 static int la_down_req(struct la_context *ac);
137
138
139
140 /* add */
141 static int linked_attributes_add(struct ldb_module *module, struct ldb_request *req)
/* [<][>][^][v][top][bottom][index][help] */
142 {
143 struct ldb_context *ldb;
144 const struct dsdb_attribute *target_attr;
145 struct la_context *ac;
146 const char *attr_name;
147 int ret;
148 int i, j;
149
150 ldb = ldb_module_get_ctx(module);
151
152 if (ldb_dn_is_special(req->op.add.message->dn)) {
153 /* do not manipulate our control entries */
154 return ldb_next_request(module, req);
155 }
156
157 ac = linked_attributes_init(module, req);
158 if (!ac) {
159 return LDB_ERR_OPERATIONS_ERROR;
160 }
161
162 if (!ac->schema) {
163 /* without schema, this doesn't make any sense */
164 talloc_free(ac);
165 return ldb_next_request(module, req);
166 }
167
168 /* Need to ensure we only have forward links being specified */
169 for (i=0; i < req->op.add.message->num_elements; i++) {
170 const struct ldb_message_element *el = &req->op.add.message->elements[i];
171 const struct dsdb_attribute *schema_attr
172 = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
173 if (!schema_attr) {
174 ldb_asprintf_errstring(ldb,
175 "attribute %s is not a valid attribute in schema", el->name);
176 return LDB_ERR_OBJECT_CLASS_VIOLATION;
177 }
178 /* We have a valid attribute, now find out if it is linked */
179 if (schema_attr->linkID == 0) {
180 continue;
181 }
182
183 if ((schema_attr->linkID & 1) == 1) {
184 /* Odd is for the target. Illigal to modify */
185 ldb_asprintf_errstring(ldb,
186 "attribute %s must not be modified directly, it is a linked attribute", el->name);
187 return LDB_ERR_UNWILLING_TO_PERFORM;
188 }
189
190 /* Even link IDs are for the originating attribute */
191 target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID + 1);
192 if (!target_attr) {
193 /*
194 * windows 2003 has a broken schema where
195 * the definition of msDS-IsDomainFor
196 * is missing (which is supposed to be
197 * the backlink of the msDS-HasDomainNCs
198 * attribute
199 */
200 continue;
201 }
202
203 attr_name = target_attr->lDAPDisplayName;
204
205 for (j = 0; j < el->num_values; j++) {
206 ret = la_store_op(ac, LA_OP_ADD,
207 &el->values[j],
208 attr_name);
209 if (ret != LDB_SUCCESS) {
210 return ret;
211 }
212 }
213 }
214
215 /* if no linked attributes are present continue */
216 if (ac->ops == NULL) {
217 /* nothing to do for this module, proceed */
218 talloc_free(ac);
219 return ldb_next_request(module, req);
220 }
221
222 /* start with the original request */
223 return la_down_req(ac);
224 }
225
226 /* For a delete or rename, we need to find out what linked attributes
227 * are currently on this DN, and then deal with them. This is the
228 * callback to the base search */
229
230 static int la_mod_search_callback(struct ldb_request *req, struct ldb_reply *ares)
/* [<][>][^][v][top][bottom][index][help] */
231 {
232 struct ldb_context *ldb;
233 const struct dsdb_attribute *schema_attr;
234 const struct dsdb_attribute *target_attr;
235 struct ldb_message_element *search_el;
236 struct replace_context *rc;
237 struct la_context *ac;
238 const char *attr_name;
239 int i, j;
240 int ret = LDB_SUCCESS;
241
242 ac = talloc_get_type(req->context, struct la_context);
243 ldb = ldb_module_get_ctx(ac->module);
244 rc = ac->rc;
245
246 if (!ares) {
247 return ldb_module_done(ac->req, NULL, NULL,
248 LDB_ERR_OPERATIONS_ERROR);
249 }
250 if (ares->error != LDB_SUCCESS) {
251 return ldb_module_done(ac->req, ares->controls,
252 ares->response, ares->error);
253 }
254
255 /* Only entries are interesting, and we only want the olddn */
256 switch (ares->type) {
257 case LDB_REPLY_ENTRY:
258
259 if (ldb_dn_compare(ares->message->dn, ac->req->op.mod.message->dn) != 0) {
260 ldb_asprintf_errstring(ldb,
261 "linked_attributes: %s is not the DN we were looking for", ldb_dn_get_linearized(ares->message->dn));
262 /* Guh? We only asked for this DN */
263 talloc_free(ares);
264 return ldb_module_done(ac->req, NULL, NULL,
265 LDB_ERR_OPERATIONS_ERROR);
266 }
267
268 ac->add_dn = ac->del_dn = talloc_steal(ac, ares->message->dn);
269
270 /* We don't populate 'rc' for ADD - it can't be deleting elements anyway */
271 for (i = 0; rc && i < rc->num_elements; i++) {
272
273 schema_attr = dsdb_attribute_by_lDAPDisplayName(ac->schema, rc->el[i].name);
274 if (!schema_attr) {
275 ldb_asprintf_errstring(ldb,
276 "attribute %s is not a valid attribute in schema",
277 rc->el[i].name);
278 talloc_free(ares);
279 return ldb_module_done(ac->req, NULL, NULL,
280 LDB_ERR_OBJECT_CLASS_VIOLATION);
281 }
282
283 search_el = ldb_msg_find_element(ares->message,
284 rc->el[i].name);
285
286 /* See if this element already exists */
287 /* otherwise just ignore as
288 * the add has already been scheduled */
289 if ( ! search_el) {
290 continue;
291 }
292
293 target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID + 1);
294 if (!target_attr) {
295 /*
296 * windows 2003 has a broken schema where
297 * the definition of msDS-IsDomainFor
298 * is missing (which is supposed to be
299 * the backlink of the msDS-HasDomainNCs
300 * attribute
301 */
302 continue;
303 }
304 attr_name = target_attr->lDAPDisplayName;
305
306 /* Now we know what was there, we can remove it for the re-add */
307 for (j = 0; j < search_el->num_values; j++) {
308 ret = la_store_op(ac, LA_OP_DEL,
309 &search_el->values[j],
310 attr_name);
311 if (ret != LDB_SUCCESS) {
312 talloc_free(ares);
313 return ldb_module_done(ac->req,
314 NULL, NULL, ret);
315 }
316 }
317 }
318
319 break;
320
321 case LDB_REPLY_REFERRAL:
322 /* ignore */
323 break;
324
325 case LDB_REPLY_DONE:
326
327 talloc_free(ares);
328
329 if (ac->req->operation == LDB_ADD) {
330 /* Start the modifies to the backlinks */
331 ret = la_do_mod_request(ac);
332
333 if (ret != LDB_SUCCESS) {
334 return ldb_module_done(ac->req, NULL, NULL,
335 ret);
336 }
337 } else {
338 /* Start with the original request */
339 ret = la_down_req(ac);
340 if (ret != LDB_SUCCESS) {
341 return ldb_module_done(ac->req, NULL, NULL, ret);
342 }
343 }
344 return LDB_SUCCESS;
345 }
346
347 talloc_free(ares);
348 return ret;
349 }
350
351
352 /* modify */
353 static int linked_attributes_modify(struct ldb_module *module, struct ldb_request *req)
/* [<][>][^][v][top][bottom][index][help] */
354 {
355 /* Look over list of modifications */
356 /* Find if any are for linked attributes */
357 /* Determine the effect of the modification */
358 /* Apply the modify to the linked entry */
359
360 struct ldb_context *ldb;
361 int i, j;
362 struct la_context *ac;
363 struct ldb_request *search_req;
364 const char **attrs;
365
366 int ret;
367
368 ldb = ldb_module_get_ctx(module);
369
370 if (ldb_dn_is_special(req->op.mod.message->dn)) {
371 /* do not manipulate our control entries */
372 return ldb_next_request(module, req);
373 }
374
375 ac = linked_attributes_init(module, req);
376 if (!ac) {
377 return LDB_ERR_OPERATIONS_ERROR;
378 }
379
380 if (!ac->schema) {
381 /* without schema, this doesn't make any sense */
382 return ldb_next_request(module, req);
383 }
384
385 ac->rc = talloc_zero(ac, struct replace_context);
386 if (!ac->rc) {
387 ldb_oom(ldb);
388 return LDB_ERR_OPERATIONS_ERROR;
389 }
390
391 for (i=0; i < req->op.mod.message->num_elements; i++) {
392 bool store_el = false;
393 const char *attr_name;
394 const struct dsdb_attribute *target_attr;
395 const struct ldb_message_element *el = &req->op.mod.message->elements[i];
396 const struct dsdb_attribute *schema_attr
397 = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
398 if (!schema_attr) {
399 ldb_asprintf_errstring(ldb,
400 "attribute %s is not a valid attribute in schema", el->name);
401 return LDB_ERR_OBJECT_CLASS_VIOLATION;
402 }
403 /* We have a valid attribute, now find out if it is linked */
404 if (schema_attr->linkID == 0) {
405 continue;
406 }
407
408 if ((schema_attr->linkID & 1) == 1) {
409 /* Odd is for the target. Illegal to modify */
410 ldb_asprintf_errstring(ldb,
411 "attribute %s must not be modified directly, it is a linked attribute", el->name);
412 return LDB_ERR_UNWILLING_TO_PERFORM;
413 }
414
415 /* Even link IDs are for the originating attribute */
416
417 /* Now find the target attribute */
418 target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID + 1);
419 if (!target_attr) {
420 /*
421 * windows 2003 has a broken schema where
422 * the definition of msDS-IsDomainFor
423 * is missing (which is supposed to be
424 * the backlink of the msDS-HasDomainNCs
425 * attribute
426 */
427 continue;
428 }
429
430 attr_name = target_attr->lDAPDisplayName;
431
432 switch (el->flags & LDB_FLAG_MOD_MASK) {
433 case LDB_FLAG_MOD_REPLACE:
434 /* treat as just a normal add the delete part is handled by the callback */
435 store_el = true;
436
437 /* break intentionally missing */
438
439 case LDB_FLAG_MOD_ADD:
440
441 /* For each value being added, we need to setup the adds */
442 for (j = 0; j < el->num_values; j++) {
443 ret = la_store_op(ac, LA_OP_ADD,
444 &el->values[j],
445 attr_name);
446 if (ret != LDB_SUCCESS) {
447 return ret;
448 }
449 }
450 break;
451
452 case LDB_FLAG_MOD_DELETE:
453
454 if (el->num_values) {
455 /* For each value being deleted, we need to setup the delete */
456 for (j = 0; j < el->num_values; j++) {
457 ret = la_store_op(ac, LA_OP_DEL,
458 &el->values[j],
459 attr_name);
460 if (ret != LDB_SUCCESS) {
461 return ret;
462 }
463 }
464 } else {
465 /* Flag that there was a DELETE
466 * without a value specified, so we
467 * need to look for the old value */
468 store_el = true;
469 }
470
471 break;
472 }
473
474 if (store_el) {
475 struct ldb_message_element *search_el;
476
477 search_el = talloc_realloc(ac->rc, ac->rc->el,
478 struct ldb_message_element,
479 ac->rc->num_elements +1);
480 if (!search_el) {
481 ldb_oom(ldb);
482 return LDB_ERR_OPERATIONS_ERROR;
483 }
484 ac->rc->el = search_el;
485
486 ac->rc->el[ac->rc->num_elements] = *el;
487 ac->rc->num_elements++;
488 }
489 }
490
491 if (ac->ops || ac->rc->el) {
492 /* both replace and delete without values are handled in the callback
493 * after the search on the entry to be modified is performed */
494
495 attrs = talloc_array(ac->rc, const char *, ac->rc->num_elements + 1);
496 if (!attrs) {
497 ldb_oom(ldb);
498 return LDB_ERR_OPERATIONS_ERROR;
499 }
500 for (i = 0; ac->rc && i < ac->rc->num_elements; i++) {
501 attrs[i] = ac->rc->el[i].name;
502 }
503 attrs[i] = NULL;
504
505 /* The callback does all the hard work here */
506 ret = ldb_build_search_req(&search_req, ldb, ac,
507 req->op.mod.message->dn,
508 LDB_SCOPE_BASE,
509 "(objectClass=*)", attrs,
510 NULL,
511 ac, la_mod_search_callback,
512 req);
513
514 /* We need to figure out our own extended DN, to fill in as the backlink target */
515 if (ret == LDB_SUCCESS) {
516 ret = ldb_request_add_control(search_req,
517 LDB_CONTROL_EXTENDED_DN_OID,
518 false, NULL);
519 }
520 if (ret == LDB_SUCCESS) {
521 talloc_steal(search_req, attrs);
522
523 ret = ldb_next_request(module, search_req);
524 }
525
526 } else {
527 /* nothing to do for this module, proceed */
528 talloc_free(ac);
529 ret = ldb_next_request(module, req);
530 }
531
532 return ret;
533 }
534
535 /* delete, rename */
536 static int linked_attributes_del(struct ldb_module *module, struct ldb_request *req)
/* [<][>][^][v][top][bottom][index][help] */
537 {
538 struct ldb_context *ldb;
539 struct ldb_request *search_req;
540 struct la_context *ac;
541 const char **attrs;
542 WERROR werr;
543 int ret;
544
545 /* This gets complex: We need to:
546 - Do a search for the entry
547 - Wait for these result to appear
548 - In the callback for the result, issue a modify
549 request based on the linked attributes found
550 - Wait for each modify result
551 - Regain our sainity
552 */
553
554 ldb = ldb_module_get_ctx(module);
555
556 ac = linked_attributes_init(module, req);
557 if (!ac) {
558 return LDB_ERR_OPERATIONS_ERROR;
559 }
560
561 if (!ac->schema) {
562 /* without schema, this doesn't make any sense */
563 return ldb_next_request(module, req);
564 }
565
566 werr = dsdb_linked_attribute_lDAPDisplayName_list(ac->schema, ac, &attrs);
567 if (!W_ERROR_IS_OK(werr)) {
568 return LDB_ERR_OPERATIONS_ERROR;
569 }
570
571 ret = ldb_build_search_req(&search_req, ldb, req,
572 req->op.del.dn, LDB_SCOPE_BASE,
573 "(objectClass=*)", attrs,
574 NULL,
575 ac, la_op_search_callback,
576 req);
577
578 if (ret != LDB_SUCCESS) {
579 return ret;
580 }
581
582 talloc_steal(search_req, attrs);
583
584 return ldb_next_request(module, search_req);
585 }
586
587 /* delete, rename */
588 static int linked_attributes_rename(struct ldb_module *module, struct ldb_request *req)
/* [<][>][^][v][top][bottom][index][help] */
589 {
590 struct la_context *ac;
591
592 /* This gets complex: We need to:
593 - Do a search for the entry
594 - Wait for these result to appear
595 - In the callback for the result, issue a modify
596 request based on the linked attributes found
597 - Wait for each modify result
598 - Regain our sainity
599 */
600
601 ac = linked_attributes_init(module, req);
602 if (!ac) {
603 return LDB_ERR_OPERATIONS_ERROR;
604 }
605
606 if (!ac->schema) {
607 /* without schema, this doesn't make any sense */
608 return ldb_next_request(module, req);
609 }
610
611 /* start with the original request */
612 return la_down_req(ac);
613 }
614
615
616 static int la_op_search_callback(struct ldb_request *req,
/* [<][>][^][v][top][bottom][index][help] */
617 struct ldb_reply *ares)
618 {
619 struct ldb_context *ldb;
620 struct la_context *ac;
621 const struct dsdb_attribute *schema_attr;
622 const struct dsdb_attribute *target_attr;
623 const struct ldb_message_element *el;
624 const char *attr_name;
625 int i, j;
626 int ret;
627
628 ac = talloc_get_type(req->context, struct la_context);
629 ldb = ldb_module_get_ctx(ac->module);
630
631 if (!ares) {
632 return ldb_module_done(ac->req, NULL, NULL,
633 LDB_ERR_OPERATIONS_ERROR);
634 }
635 if (ares->error != LDB_SUCCESS) {
636 return ldb_module_done(ac->req, ares->controls,
637 ares->response, ares->error);
638 }
639
640 /* Only entries are interesting, and we only want the olddn */
641 switch (ares->type) {
642 case LDB_REPLY_ENTRY:
643 ret = ldb_dn_compare(ares->message->dn, req->op.search.base);
644 if (ret != 0) {
645 /* Guh? We only asked for this DN */
646 talloc_free(ares);
647 return ldb_module_done(ac->req, NULL, NULL,
648 LDB_ERR_OPERATIONS_ERROR);
649 }
650 if (ares->message->num_elements == 0) {
651 /* only bother at all if there were some
652 * linked attributes found */
653 talloc_free(ares);
654 return LDB_SUCCESS;
655 }
656
657 switch (ac->req->operation) {
658 case LDB_DELETE:
659 ac->del_dn = talloc_steal(ac, ares->message->dn);
660 break;
661 case LDB_RENAME:
662 ac->add_dn = talloc_steal(ac, ares->message->dn);
663 ac->del_dn = talloc_steal(ac, ac->req->op.rename.olddn);
664 break;
665 default:
666 talloc_free(ares);
667 ldb_set_errstring(ldb,
668 "operations must be delete or rename");
669 return ldb_module_done(ac->req, NULL, NULL,
670 LDB_ERR_OPERATIONS_ERROR);
671 }
672
673 for (i = 0; i < ares->message->num_elements; i++) {
674 el = &ares->message->elements[i];
675
676 schema_attr = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
677 if (!schema_attr) {
678 ldb_asprintf_errstring(ldb,
679 "attribute %s is not a valid attribute"
680 " in schema", el->name);
681 talloc_free(ares);
682 return ldb_module_done(ac->req, NULL, NULL,
683 LDB_ERR_OBJECT_CLASS_VIOLATION);
684 }
685
686 /* Valid attribute, now find out if it is linked */
687 if (schema_attr->linkID == 0) {
688 /* Not a linked attribute, skip */
689 continue;
690 }
691
692 if ((schema_attr->linkID & 1) == 0) {
693 /* Odd is for the target. */
694 target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID + 1);
695 if (!target_attr) {
696 continue;
697 }
698 attr_name = target_attr->lDAPDisplayName;
699 } else {
700 target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID - 1);
701 if (!target_attr) {
702 continue;
703 }
704 attr_name = target_attr->lDAPDisplayName;
705 }
706 for (j = 0; j < el->num_values; j++) {
707 ret = la_store_op(ac, LA_OP_DEL,
708 &el->values[j],
709 attr_name);
710
711 /* for renames, ensure we add it back */
712 if (ret == LDB_SUCCESS
713 && ac->req->operation == LDB_RENAME) {
714 ret = la_store_op(ac, LA_OP_ADD,
715 &el->values[j],
716 attr_name);
717 }
718 if (ret != LDB_SUCCESS) {
719 talloc_free(ares);
720 return ldb_module_done(ac->req,
721 NULL, NULL, ret);
722 }
723 }
724 }
725
726 break;
727
728 case LDB_REPLY_REFERRAL:
729 /* ignore */
730 break;
731
732 case LDB_REPLY_DONE:
733
734 talloc_free(ares);
735
736
737 switch (ac->req->operation) {
738 case LDB_DELETE:
739 /* start the mod requests chain */
740 ret = la_down_req(ac);
741 if (ret != LDB_SUCCESS) {
742 return ldb_module_done(ac->req, NULL, NULL, ret);
743 }
744 break;
745 case LDB_RENAME:
746
747 ret = la_do_mod_request(ac);
748 if (ret != LDB_SUCCESS) {
749 return ldb_module_done(ac->req, NULL, NULL,
750 ret);
751 }
752
753 return ret;
754
755 default:
756 talloc_free(ares);
757 ldb_set_errstring(ldb,
758 "operations must be delete or rename");
759 return ldb_module_done(ac->req, NULL, NULL,
760 LDB_ERR_OPERATIONS_ERROR);
761 }
762 return LDB_SUCCESS;
763 }
764
765 talloc_free(ares);
766 return LDB_SUCCESS;
767 }
768
769 /* do a linked attributes modify request */
770 static int la_do_mod_request(struct la_context *ac)
/* [<][>][^][v][top][bottom][index][help] */
771 {
772 struct ldb_message_element *ret_el;
773 struct ldb_request *mod_req;
774 struct ldb_message *new_msg;
775 struct ldb_context *ldb;
776 int ret;
777
778 /* If we have no modifies in the queue, we are done! */
779 if (!ac->ops) {
780 return ldb_module_done(ac->req, ac->op_controls,
781 ac->op_response, LDB_SUCCESS);
782 }
783
784 ldb = ldb_module_get_ctx(ac->module);
785
786 /* Create the modify request */
787 new_msg = ldb_msg_new(ac);
788 if (!new_msg) {
789 ldb_oom(ldb);
790 return LDB_ERR_OPERATIONS_ERROR;
791 }
792 new_msg->dn = ac->ops->dn;
793
794 if (ac->ops->op == LA_OP_ADD) {
795 ret = ldb_msg_add_empty(new_msg, ac->ops->name,
796 LDB_FLAG_MOD_ADD, &ret_el);
797 } else {
798 ret = ldb_msg_add_empty(new_msg, ac->ops->name,
799 LDB_FLAG_MOD_DELETE, &ret_el);
800 }
801 if (ret != LDB_SUCCESS) {
802 return ret;
803 }
804 ret_el->values = talloc_array(new_msg, struct ldb_val, 1);
805 if (!ret_el->values) {
806 ldb_oom(ldb);
807 return LDB_ERR_OPERATIONS_ERROR;
808 }
809 ret_el->num_values = 1;
810 if (ac->ops->op == LA_OP_ADD) {
811 ret_el->values[0] = data_blob_string_const(ldb_dn_get_extended_linearized(new_msg, ac->add_dn, 1));
812 } else {
813 ret_el->values[0] = data_blob_string_const(ldb_dn_get_extended_linearized(new_msg, ac->del_dn, 1));
814 }
815
816 #if 0
817 ldb_debug(ldb, LDB_DEBUG_WARNING,
818 "link on %s %s: %s %s\n",
819 ldb_dn_get_linearized(new_msg->dn), ret_el->name,
820 ret_el->values[0].data, ac->ops->op == LA_OP_ADD ? "added" : "deleted");
821 #endif
822
823 /* use ac->ops as the mem_ctx so that the request will be freed
824 * in the callback as soon as completed */
825 ret = ldb_build_mod_req(&mod_req, ldb, ac->ops,
826 new_msg,
827 NULL,
828 ac, la_mod_callback,
829 ac->req);
830 if (ret != LDB_SUCCESS) {
831 return ret;
832 }
833 talloc_steal(mod_req, new_msg);
834
835 /* Run the new request */
836 return ldb_next_request(ac->module, mod_req);
837 }
838
839 static int la_mod_callback(struct ldb_request *req, struct ldb_reply *ares)
/* [<][>][^][v][top][bottom][index][help] */
840 {
841 struct la_context *ac;
842 struct ldb_context *ldb;
843 struct la_op_store *os;
844
845 ac = talloc_get_type(req->context, struct la_context);
846 ldb = ldb_module_get_ctx(ac->module);
847
848 if (!ares) {
849 return ldb_module_done(ac->req, NULL, NULL,
850 LDB_ERR_OPERATIONS_ERROR);
851 }
852 if (ares->error != LDB_SUCCESS) {
853 return ldb_module_done(ac->req, ares->controls,
854 ares->response, ares->error);
855 }
856
857 if (ares->type != LDB_REPLY_DONE) {
858 ldb_set_errstring(ldb,
859 "invalid ldb_reply_type in callback");
860 talloc_free(ares);
861 return ldb_module_done(ac->req, NULL, NULL,
862 LDB_ERR_OPERATIONS_ERROR);
863 }
864
865 talloc_free(ares);
866
867 os = ac->ops;
868 DLIST_REMOVE(ac->ops, os);
869
870 /* this frees the request too
871 * DO NOT access 'req' after this point */
872 talloc_free(os);
873
874 return la_do_mod_request(ac);
875 }
876
877 /* Having done the original operation, then try to fix up all the linked attributes for modify and delete */
878 static int la_mod_del_callback(struct ldb_request *req, struct ldb_reply *ares)
/* [<][>][^][v][top][bottom][index][help] */
879 {
880 int ret;
881 struct la_context *ac;
882 struct ldb_context *ldb;
883
884 ac = talloc_get_type(req->context, struct la_context);
885 ldb = ldb_module_get_ctx(ac->module);
886
887 if (!ares) {
888 return ldb_module_done(ac->req, NULL, NULL,
889 LDB_ERR_OPERATIONS_ERROR);
890 }
891 if (ares->error != LDB_SUCCESS) {
892 return ldb_module_done(ac->req, ares->controls,
893 ares->response, ares->error);
894 }
895
896 if (ares->type != LDB_REPLY_DONE) {
897 ldb_set_errstring(ldb,
898 "invalid ldb_reply_type in callback");
899 talloc_free(ares);
900 return ldb_module_done(ac->req, NULL, NULL,
901 LDB_ERR_OPERATIONS_ERROR);
902 }
903
904 ac->op_controls = talloc_steal(ac, ares->controls);
905 ac->op_response = talloc_steal(ac, ares->response);
906
907 /* If we have modfies to make, this is the time to do them for modify and delete */
908 ret = la_do_mod_request(ac);
909
910 if (ret != LDB_SUCCESS) {
911 return ldb_module_done(ac->req, NULL, NULL, ret);
912 }
913 talloc_free(ares);
914
915 /* la_do_mod_request has already sent the callbacks */
916 return LDB_SUCCESS;
917
918 }
919
920 /* Having done the original rename try to fix up all the linked attributes */
921 static int la_rename_callback(struct ldb_request *req, struct ldb_reply *ares)
/* [<][>][^][v][top][bottom][index][help] */
922 {
923 int ret;
924 struct la_context *ac;
925 struct ldb_request *search_req;
926 const char **attrs;
927 WERROR werr;
928 struct ldb_context *ldb;
929
930 ac = talloc_get_type(req->context, struct la_context);
931 ldb = ldb_module_get_ctx(ac->module);
932
933 if (!ares) {
934 return ldb_module_done(ac->req, NULL, NULL,
935 LDB_ERR_OPERATIONS_ERROR);
936 }
937 if (ares->error != LDB_SUCCESS) {
938 return ldb_module_done(ac->req, ares->controls,
939 ares->response, ares->error);
940 }
941
942 if (ares->type != LDB_REPLY_DONE) {
943 ldb_set_errstring(ldb,
944 "invalid ldb_reply_type in callback");
945 talloc_free(ares);
946 return ldb_module_done(ac->req, NULL, NULL,
947 LDB_ERR_OPERATIONS_ERROR);
948 }
949
950 werr = dsdb_linked_attribute_lDAPDisplayName_list(ac->schema, ac, &attrs);
951 if (!W_ERROR_IS_OK(werr)) {
952 return LDB_ERR_OPERATIONS_ERROR;
953 }
954
955 ret = ldb_build_search_req(&search_req, ldb, req,
956 ac->req->op.rename.newdn, LDB_SCOPE_BASE,
957 "(objectClass=*)", attrs,
958 NULL,
959 ac, la_op_search_callback,
960 req);
961
962 if (ret != LDB_SUCCESS) {
963 return ret;
964 }
965
966 talloc_steal(search_req, attrs);
967
968 if (ret == LDB_SUCCESS) {
969 ret = ldb_request_add_control(search_req,
970 LDB_CONTROL_EXTENDED_DN_OID,
971 false, NULL);
972 }
973 if (ret != LDB_SUCCESS) {
974 return ldb_module_done(ac->req, NULL, NULL,
975 ret);
976 }
977
978 ac->op_controls = talloc_steal(ac, ares->controls);
979 ac->op_response = talloc_steal(ac, ares->response);
980
981 return ldb_next_request(ac->module, search_req);
982 }
983
984 /* Having done the original add, then try to fix up all the linked attributes
985
986 This is done after the add so the links can get the extended DNs correctly.
987 */
988 static int la_add_callback(struct ldb_request *req, struct ldb_reply *ares)
/* [<][>][^][v][top][bottom][index][help] */
989 {
990 int ret;
991 struct la_context *ac;
992 struct ldb_context *ldb;
993
994 ac = talloc_get_type(req->context, struct la_context);
995 ldb = ldb_module_get_ctx(ac->module);
996
997 if (!ares) {
998 return ldb_module_done(ac->req, NULL, NULL,
999 LDB_ERR_OPERATIONS_ERROR);
1000 }
1001 if (ares->error != LDB_SUCCESS) {
1002 return ldb_module_done(ac->req, ares->controls,
1003 ares->response, ares->error);
1004 }
1005
1006 if (ares->type != LDB_REPLY_DONE) {
1007 ldb_set_errstring(ldb,
1008 "invalid ldb_reply_type in callback");
1009 talloc_free(ares);
1010 return ldb_module_done(ac->req, NULL, NULL,
1011 LDB_ERR_OPERATIONS_ERROR);
1012 }
1013
1014 if (ac->ops) {
1015 struct ldb_request *search_req;
1016 static const char *attrs[] = { NULL };
1017
1018 /* The callback does all the hard work here - we need
1019 * the objectGUID and SID of the added record */
1020 ret = ldb_build_search_req(&search_req, ldb, ac,
1021 ac->req->op.add.message->dn,
1022 LDB_SCOPE_BASE,
1023 "(objectClass=*)", attrs,
1024 NULL,
1025 ac, la_mod_search_callback,
1026 ac->req);
1027
1028 if (ret == LDB_SUCCESS) {
1029 ret = ldb_request_add_control(search_req,
1030 LDB_CONTROL_EXTENDED_DN_OID,
1031 false, NULL);
1032 }
1033 if (ret != LDB_SUCCESS) {
1034 return ldb_module_done(ac->req, NULL, NULL,
1035 ret);
1036 }
1037
1038 ac->op_controls = talloc_steal(ac, ares->controls);
1039 ac->op_response = talloc_steal(ac, ares->response);
1040
1041 return ldb_next_request(ac->module, search_req);
1042
1043 } else {
1044 return ldb_module_done(ac->req, ares->controls,
1045 ares->response, ares->error);
1046 }
1047 }
1048
1049 /* Reconstruct the original request, but pointing at our local callback to finish things off */
1050 static int la_down_req(struct la_context *ac)
/* [<][>][^][v][top][bottom][index][help] */
1051 {
1052 struct ldb_request *down_req;
1053 int ret;
1054 struct ldb_context *ldb;
1055
1056 ldb = ldb_module_get_ctx(ac->module);
1057
1058 switch (ac->req->operation) {
1059 case LDB_ADD:
1060 ret = ldb_build_add_req(&down_req, ldb, ac,
1061 ac->req->op.add.message,
1062 ac->req->controls,
1063 ac, la_add_callback,
1064 ac->req);
1065 break;
1066 case LDB_MODIFY:
1067 ret = ldb_build_mod_req(&down_req, ldb, ac,
1068 ac->req->op.mod.message,
1069 ac->req->controls,
1070 ac, la_mod_del_callback,
1071 ac->req);
1072 break;
1073 case LDB_DELETE:
1074 ret = ldb_build_del_req(&down_req, ldb, ac,
1075 ac->req->op.del.dn,
1076 ac->req->controls,
1077 ac, la_mod_del_callback,
1078 ac->req);
1079 break;
1080 case LDB_RENAME:
1081 ret = ldb_build_rename_req(&down_req, ldb, ac,
1082 ac->req->op.rename.olddn,
1083 ac->req->op.rename.newdn,
1084 ac->req->controls,
1085 ac, la_rename_callback,
1086 ac->req);
1087 break;
1088 default:
1089 ret = LDB_ERR_OPERATIONS_ERROR;
1090 }
1091 if (ret != LDB_SUCCESS) {
1092 return ret;
1093 }
1094
1095 return ldb_next_request(ac->module, down_req);
1096 }
1097
1098
1099 _PUBLIC_ const struct ldb_module_ops ldb_linked_attributes_module_ops = {
1100 .name = "linked_attributes",
1101 .add = linked_attributes_add,
1102 .modify = linked_attributes_modify,
1103 .del = linked_attributes_del,
1104 .rename = linked_attributes_rename,
1105 };