Line data Source code
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 : Copyright (C) Matthieu Patou <mat@matws.net> 2011
7 : Copyright (C) Andrew Tridgell 2009
8 :
9 : This program is free software; you can redistribute it and/or modify
10 : it under the terms of the GNU General Public License as published by
11 : the Free Software Foundation; either version 3 of the License, or
12 : (at your option) any later version.
13 :
14 : This program is distributed in the hope that it will be useful,
15 : but WITHOUT ANY WARRANTY; without even the implied warranty of
16 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 : GNU General Public License for more details.
18 :
19 : You should have received a copy of the GNU General Public License
20 : along with this program. If not, see <http://www.gnu.org/licenses/>.
21 : */
22 :
23 : /*
24 : * Name: ldb
25 : *
26 : * Component: ldb linked_attributes module
27 : *
28 : * Description: Module to ensure linked attribute pairs (i.e. forward-links
29 : * and backlinks) remain in sync.
30 : *
31 : * Backlinks are 'plain' links (without extra metadata). When the link target
32 : * object is modified (e.g. renamed), we use the backlinks to keep the link
33 : * source object updated. Note there are some cases where we can't do this:
34 : * - one-way links, which don't have a corresponding backlink
35 : * - two-way deactivated links, i.e. when a user is removed from a group,
36 : * the forward 'member' link still exists (but is inactive), however, the
37 : * 'memberOf' backlink is deleted.
38 : * In these cases, we can end up with a dangling forward link which is
39 : * incorrect (i.e. the target has been renamed or deleted). We have dbcheck
40 : * rules to detect and fix this, and cope otherwise by filtering at runtime
41 : * (i.e. in the extended_dn module).
42 : *
43 : * See also repl_meta_data.c, which handles updating links for deleted
44 : * objects, as well as link changes received from another DC.
45 : *
46 : * Author: Andrew Bartlett
47 : */
48 :
49 : #include "includes.h"
50 : #include "ldb_module.h"
51 : #include "util/dlinklist.h"
52 : #include "dsdb/samdb/samdb.h"
53 : #include "librpc/gen_ndr/ndr_misc.h"
54 : #include "dsdb/samdb/ldb_modules/util.h"
55 :
56 : #undef strcasecmp
57 :
58 : struct la_private_transaction {
59 : struct la_context *la_list;
60 : };
61 :
62 :
63 : struct la_private {
64 : struct la_private_transaction *transaction;
65 : bool sorted_links;
66 : };
67 :
68 : struct la_op_store {
69 : struct la_op_store *next;
70 : struct la_op_store *prev;
71 : enum la_op {LA_OP_ADD, LA_OP_DEL} op;
72 : struct GUID guid;
73 : char *name;
74 : };
75 :
76 : struct replace_context {
77 : struct la_context *ac;
78 : unsigned int num_elements;
79 : struct ldb_message_element *el;
80 : };
81 :
82 : struct la_context {
83 : struct la_context *next, *prev;
84 : const struct dsdb_schema *schema;
85 : struct ldb_module *module;
86 : struct ldb_request *req;
87 : struct ldb_dn *mod_dn;
88 : struct replace_context *rc;
89 : struct la_op_store *ops;
90 : struct ldb_extended *op_response;
91 : struct ldb_control **op_controls;
92 : /*
93 : * For futur use
94 : * will tell which GC to use for resolving links
95 : */
96 : char *gc_dns_name;
97 : };
98 :
99 :
100 0 : static int handle_verify_name_control(TALLOC_CTX *ctx, struct ldb_context *ldb,
101 : struct ldb_control *control, struct la_context *ac)
102 : {
103 : /*
104 : * If we are a GC let's remove the control,
105 : * if there is a specified GC check that is us.
106 : */
107 0 : struct ldb_verify_name_control *lvnc = talloc_get_type_abort(control->data, struct ldb_verify_name_control);
108 0 : if (samdb_is_gc(ldb)) {
109 : /* Because we can't easily talloc a struct ldb_dn*/
110 0 : struct ldb_dn **dn = talloc_array(ctx, struct ldb_dn *, 1);
111 0 : int ret = samdb_server_reference_dn(ldb, ctx, dn);
112 : const char *dns;
113 :
114 0 : if (ret != LDB_SUCCESS) {
115 0 : return ldb_operr(ldb);
116 : }
117 :
118 0 : dns = samdb_dn_to_dnshostname(ldb, ctx, *dn);
119 0 : if (!dns) {
120 0 : return ldb_operr(ldb);
121 : }
122 0 : if (!lvnc->gc || strcasecmp(dns, lvnc->gc) == 0) {
123 0 : if (!ldb_save_controls(control, ctx, NULL)) {
124 0 : return ldb_operr(ldb);
125 : }
126 : } else {
127 0 : control->critical = true;
128 : }
129 0 : talloc_free(dn);
130 : } else {
131 : /* For the moment we don't remove the control is this case in order
132 : * to fail the request. It's better than having the client thinking
133 : * that we honnor its control.
134 : * Hopefully only a very small set of usecase should hit this problem.
135 : */
136 0 : if (lvnc->gc) {
137 0 : ac->gc_dns_name = talloc_strdup(ac, lvnc->gc);
138 : }
139 0 : control->critical = true;
140 : }
141 :
142 0 : return LDB_SUCCESS;
143 : }
144 :
145 1286536 : static struct la_context *linked_attributes_init(struct ldb_module *module,
146 : struct ldb_request *req)
147 : {
148 : struct ldb_context *ldb;
149 : struct la_context *ac;
150 :
151 1286536 : ldb = ldb_module_get_ctx(module);
152 :
153 1286536 : ac = talloc_zero(req, struct la_context);
154 1286536 : if (ac == NULL) {
155 0 : ldb_oom(ldb);
156 0 : return NULL;
157 : }
158 :
159 1286536 : ac->schema = dsdb_get_schema(ldb, ac);
160 1286536 : ac->module = module;
161 1286536 : ac->req = req;
162 :
163 1286536 : return ac;
164 : }
165 :
166 : /*
167 : turn a DN into a GUID
168 : */
169 80219 : static int la_guid_from_dn(struct ldb_module *module,
170 : struct ldb_request *parent,
171 : struct ldb_dn *dn, struct GUID *guid)
172 : {
173 : NTSTATUS status;
174 : int ret;
175 :
176 80219 : status = dsdb_get_extended_dn_guid(dn, guid, "GUID");
177 80219 : if (NT_STATUS_IS_OK(status)) {
178 78961 : return LDB_SUCCESS;
179 : }
180 1258 : if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
181 0 : DEBUG(4,(__location__ ": Unable to parse GUID for dn %s\n",
182 : ldb_dn_get_linearized(dn)));
183 0 : return ldb_operr(ldb_module_get_ctx(module));
184 : }
185 :
186 1258 : ret = dsdb_module_guid_by_dn(module, dn, guid, parent);
187 1258 : if (ret != LDB_SUCCESS) {
188 0 : DEBUG(4,(__location__ ": Failed to find GUID for dn %s\n",
189 : ldb_dn_get_linearized(dn)));
190 0 : return ret;
191 : }
192 1258 : return LDB_SUCCESS;
193 : }
194 :
195 :
196 : /* Common routine to handle reading the attributes and creating a
197 : * series of modify requests */
198 2998 : static int la_store_op(struct la_context *ac,
199 : enum la_op op,
200 : const struct dsdb_attribute *schema_attr,
201 : struct ldb_val *dn,
202 : const char *name)
203 : {
204 : struct ldb_context *ldb;
205 : struct la_op_store *os;
206 : struct ldb_dn *op_dn;
207 : struct dsdb_dn *dsdb_dn;
208 : int ret;
209 :
210 2998 : ldb = ldb_module_get_ctx(ac->module);
211 :
212 :
213 2998 : os = talloc_zero(ac, struct la_op_store);
214 2998 : if (!os) {
215 0 : return ldb_oom(ldb);
216 : }
217 :
218 2998 : dsdb_dn = dsdb_dn_parse(os, ldb, dn, schema_attr->syntax->ldap_oid);
219 :
220 2998 : if (!dsdb_dn) {
221 0 : ldb_asprintf_errstring(ldb,
222 : "could not parse attribute as a DN");
223 0 : TALLOC_FREE(os);
224 0 : return LDB_ERR_INVALID_DN_SYNTAX;
225 : }
226 :
227 2998 : op_dn = dsdb_dn->dn;
228 :
229 2998 : os->op = op;
230 :
231 2998 : ret = la_guid_from_dn(ac->module, ac->req, op_dn, &os->guid);
232 2998 : talloc_free(op_dn);
233 2998 : if (ret == LDB_ERR_NO_SUCH_OBJECT && ac->req->operation == LDB_DELETE) {
234 : /* we are deleting an object, and we've found it has a
235 : * forward link to a target that no longer
236 : * exists. This is not an error in the delete, and we
237 : * should just not do the deferred delete of the
238 : * target attribute
239 : */
240 0 : talloc_free(os);
241 0 : return LDB_SUCCESS;
242 : }
243 2998 : if (ret != LDB_SUCCESS) {
244 0 : return ret;
245 : }
246 :
247 2998 : os->name = talloc_strdup(os, name);
248 2998 : if (!os->name) {
249 0 : return ldb_oom(ldb);
250 : }
251 :
252 : /* Do deletes before adds */
253 2998 : if (op == LA_OP_ADD) {
254 1615 : DLIST_ADD_END(ac->ops, os);
255 : } else {
256 : /* By adding to the head of the list, we do deletes before
257 : * adds when processing a replace */
258 1383 : DLIST_ADD(ac->ops, os);
259 : }
260 :
261 2998 : return LDB_SUCCESS;
262 : }
263 :
264 : static int la_queue_mod_request(struct la_context *ac);
265 : static int la_down_req(struct la_context *ac);
266 :
267 :
268 :
269 : /* add */
270 637678 : static int linked_attributes_add(struct ldb_module *module, struct ldb_request *req)
271 : {
272 : struct ldb_context *ldb;
273 : const struct dsdb_attribute *target_attr;
274 : struct la_context *ac;
275 : const char *attr_name;
276 : struct ldb_control *ctrl;
277 : unsigned int i, j;
278 : struct ldb_control *control;
279 : int ret;
280 :
281 637678 : ldb = ldb_module_get_ctx(module);
282 :
283 637678 : if (ldb_dn_is_special(req->op.add.message->dn)) {
284 : /* do not manipulate our control entries */
285 1067 : return ldb_next_request(module, req);
286 : }
287 :
288 636611 : ac = linked_attributes_init(module, req);
289 636611 : if (!ac) {
290 0 : return ldb_operr(ldb);
291 : }
292 :
293 636611 : control = ldb_request_get_control(req, LDB_CONTROL_VERIFY_NAME_OID);
294 636611 : if (control != NULL && control->data != NULL) {
295 0 : ret = handle_verify_name_control(req, ldb, control, ac);
296 0 : if (ret != LDB_SUCCESS) {
297 0 : return ldb_operr(ldb);
298 : }
299 : }
300 :
301 636611 : if (!(ctrl = ldb_request_get_control(req, DSDB_CONTROL_APPLY_LINKS))) {
302 : /* don't do anything special for linked attributes, repl_meta_data has done it */
303 304993 : talloc_free(ac);
304 304993 : return ldb_next_request(module, req);
305 : }
306 331618 : ctrl->critical = false;
307 :
308 331618 : if (!ac->schema) {
309 : /* without schema, this doesn't make any sense */
310 0 : talloc_free(ac);
311 0 : return ldb_next_request(module, req);
312 : }
313 :
314 :
315 : /* Need to ensure we only have forward links being specified */
316 6947822 : for (i=0; i < req->op.add.message->num_elements; i++) {
317 6616204 : const struct ldb_message_element *el = &req->op.add.message->elements[i];
318 6367684 : const struct dsdb_attribute *schema_attr
319 6616204 : = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
320 6616204 : if (!schema_attr) {
321 0 : ldb_asprintf_errstring(ldb,
322 : "%s: attribute %s is not a valid attribute in schema",
323 : __FUNCTION__,
324 0 : el->name);
325 0 : return LDB_ERR_OBJECT_CLASS_VIOLATION;
326 : }
327 :
328 : /* this could be a link with no partner, in which case
329 : there is no special work to do */
330 6616204 : if (schema_attr->linkID == 0) {
331 6615981 : continue;
332 : }
333 :
334 : /* this part of the code should only be handling forward links */
335 223 : SMB_ASSERT((schema_attr->linkID & 1) == 0);
336 :
337 : /* Even link IDs are for the originating attribute */
338 223 : target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID ^ 1);
339 223 : if (!target_attr) {
340 : /*
341 : * windows 2003 has a broken schema where
342 : * the definition of msDS-IsDomainFor
343 : * is missing (which is supposed to be
344 : * the backlink of the msDS-HasDomainNCs
345 : * attribute
346 : */
347 38 : continue;
348 : }
349 :
350 185 : attr_name = target_attr->lDAPDisplayName;
351 :
352 534 : for (j = 0; j < el->num_values; j++) {
353 349 : ret = la_store_op(ac, LA_OP_ADD,
354 : schema_attr,
355 349 : &el->values[j],
356 : attr_name);
357 349 : if (ret != LDB_SUCCESS) {
358 0 : return ret;
359 : }
360 : }
361 : }
362 :
363 : /* if no linked attributes are present continue */
364 331618 : if (ac->ops == NULL) {
365 : /* nothing to do for this module, proceed */
366 331457 : talloc_free(ac);
367 331457 : return ldb_next_request(module, req);
368 : }
369 :
370 : /* start with the original request */
371 161 : return la_down_req(ac);
372 : }
373 :
374 : /* For a delete or rename, we need to find out what linked attributes
375 : * are currently on this DN, and then deal with them. This is the
376 : * callback to the base search */
377 :
378 3704 : static int la_mod_search_callback(struct ldb_request *req, struct ldb_reply *ares)
379 : {
380 : struct ldb_context *ldb;
381 : const struct dsdb_attribute *schema_attr;
382 : const struct dsdb_attribute *target_attr;
383 : struct ldb_message_element *search_el;
384 : struct replace_context *rc;
385 : struct la_context *ac;
386 : const char *attr_name;
387 : unsigned int i, j;
388 3704 : int ret = LDB_SUCCESS;
389 :
390 3704 : ac = talloc_get_type(req->context, struct la_context);
391 3704 : ldb = ldb_module_get_ctx(ac->module);
392 3704 : rc = ac->rc;
393 :
394 3704 : if (!ares) {
395 0 : return ldb_module_done(ac->req, NULL, NULL,
396 : LDB_ERR_OPERATIONS_ERROR);
397 : }
398 3704 : if (ares->error != LDB_SUCCESS) {
399 0 : return ldb_module_done(ac->req, ares->controls,
400 : ares->response, ares->error);
401 : }
402 :
403 : /* Only entries are interesting, and we only want the olddn */
404 3704 : switch (ares->type) {
405 1852 : case LDB_REPLY_ENTRY:
406 :
407 1852 : if (ldb_dn_compare(ares->message->dn, ac->req->op.mod.message->dn) != 0) {
408 0 : ldb_asprintf_errstring(ldb,
409 : "linked_attributes: %s is not the DN we were looking for",
410 0 : ldb_dn_get_linearized(ares->message->dn));
411 : /* Guh? We only asked for this DN */
412 0 : talloc_free(ares);
413 0 : return ldb_module_done(ac->req, NULL, NULL,
414 : LDB_ERR_OPERATIONS_ERROR);
415 : }
416 :
417 1852 : ac->mod_dn = talloc_steal(ac, ares->message->dn);
418 :
419 : /* We don't populate 'rc' for ADD - it can't be deleting elements anyway */
420 1930 : for (i = 0; rc && i < rc->num_elements; i++) {
421 :
422 78 : schema_attr = dsdb_attribute_by_lDAPDisplayName(ac->schema, rc->el[i].name);
423 78 : if (!schema_attr) {
424 0 : ldb_asprintf_errstring(ldb,
425 : "%s: attribute %s is not a valid attribute in schema",
426 : __FUNCTION__,
427 0 : rc->el[i].name);
428 0 : talloc_free(ares);
429 0 : return ldb_module_done(ac->req, NULL, NULL,
430 : LDB_ERR_OBJECT_CLASS_VIOLATION);
431 : }
432 :
433 78 : search_el = ldb_msg_find_element(ares->message,
434 78 : rc->el[i].name);
435 :
436 : /* See if this element already exists */
437 : /* otherwise just ignore as
438 : * the add has already been scheduled */
439 78 : if ( ! search_el) {
440 12 : continue;
441 : }
442 :
443 66 : target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID ^ 1);
444 66 : if (!target_attr) {
445 : /*
446 : * windows 2003 has a broken schema where
447 : * the definition of msDS-IsDomainFor
448 : * is missing (which is supposed to be
449 : * the backlink of the msDS-HasDomainNCs
450 : * attribute
451 : */
452 0 : continue;
453 : }
454 66 : attr_name = target_attr->lDAPDisplayName;
455 :
456 : /* Now we know what was there, we can remove it for the re-add */
457 188 : for (j = 0; j < search_el->num_values; j++) {
458 122 : ret = la_store_op(ac, LA_OP_DEL,
459 : schema_attr,
460 122 : &search_el->values[j],
461 : attr_name);
462 122 : if (ret != LDB_SUCCESS) {
463 0 : talloc_free(ares);
464 0 : return ldb_module_done(ac->req,
465 : NULL, NULL, ret);
466 : }
467 : }
468 : }
469 :
470 1852 : break;
471 :
472 0 : case LDB_REPLY_REFERRAL:
473 : /* ignore */
474 0 : break;
475 :
476 1852 : case LDB_REPLY_DONE:
477 :
478 1852 : talloc_free(ares);
479 :
480 1852 : if (ac->req->operation == LDB_ADD) {
481 : /* Start the modifies to the backlinks */
482 161 : ret = la_queue_mod_request(ac);
483 :
484 161 : if (ret != LDB_SUCCESS) {
485 0 : return ldb_module_done(ac->req, NULL, NULL,
486 : ret);
487 : }
488 : } else {
489 : /* Start with the original request */
490 1691 : ret = la_down_req(ac);
491 1691 : if (ret != LDB_SUCCESS) {
492 0 : return ldb_module_done(ac->req, NULL, NULL, ret);
493 : }
494 : }
495 1852 : return LDB_SUCCESS;
496 : }
497 :
498 1852 : talloc_free(ares);
499 1852 : return ret;
500 : }
501 :
502 :
503 : /* modify */
504 805811 : static int linked_attributes_modify(struct ldb_module *module, struct ldb_request *req)
505 : {
506 : /* Look over list of modifications */
507 : /* Find if any are for linked attributes */
508 : /* Determine the effect of the modification */
509 : /* Apply the modify to the linked entry */
510 :
511 : struct ldb_control *control;
512 : struct ldb_context *ldb;
513 : unsigned int i, j;
514 : struct la_context *ac;
515 : struct ldb_request *search_req;
516 : const char **attrs;
517 : struct ldb_control *ctrl;
518 : int ret;
519 :
520 805811 : ldb = ldb_module_get_ctx(module);
521 :
522 805811 : if (ldb_dn_is_special(req->op.mod.message->dn)) {
523 : /* do not manipulate our control entries */
524 155886 : return ldb_next_request(module, req);
525 : }
526 :
527 649925 : ac = linked_attributes_init(module, req);
528 649925 : if (!ac) {
529 0 : return ldb_operr(ldb);
530 : }
531 :
532 649925 : control = ldb_request_get_control(req, LDB_CONTROL_VERIFY_NAME_OID);
533 649925 : if (control != NULL && control->data != NULL) {
534 0 : ret = handle_verify_name_control(req, ldb, control, ac);
535 0 : if (ret != LDB_SUCCESS) {
536 0 : return ldb_operr(ldb);
537 : }
538 : }
539 :
540 649925 : if (!(ctrl = ldb_request_get_control(req, DSDB_CONTROL_APPLY_LINKS))) {
541 : /* don't do anything special for linked attributes, repl_meta_data has done it */
542 531744 : talloc_free(ac);
543 531744 : return ldb_next_request(module, req);
544 : }
545 118181 : ctrl->critical = false;
546 :
547 118181 : if (!ac->schema) {
548 : /* without schema, this doesn't make any sense */
549 0 : return ldb_next_request(module, req);
550 : }
551 :
552 118181 : ac->rc = talloc_zero(ac, struct replace_context);
553 118181 : if (!ac->rc) {
554 0 : return ldb_oom(ldb);
555 : }
556 :
557 1107692 : for (i=0; i < req->op.mod.message->num_elements; i++) {
558 989511 : bool store_el = false;
559 : const char *attr_name;
560 : const struct dsdb_attribute *target_attr;
561 989511 : const struct ldb_message_element *el = &req->op.mod.message->elements[i];
562 925273 : const struct dsdb_attribute *schema_attr
563 989511 : = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
564 989511 : if (!schema_attr) {
565 0 : ldb_asprintf_errstring(ldb,
566 : "%s: attribute %s is not a valid attribute in schema",
567 : __FUNCTION__,
568 0 : el->name);
569 0 : return LDB_ERR_OBJECT_CLASS_VIOLATION;
570 : }
571 : /* We have a valid attribute, now find out if it is a forward link
572 : (Even link IDs are for the originating attribute) */
573 989511 : if (schema_attr->linkID == 0) {
574 986896 : continue;
575 : }
576 :
577 : /* this part of the code should only be handling forward links */
578 2615 : SMB_ASSERT((schema_attr->linkID & 1) == 0);
579 :
580 : /* Now find the target attribute */
581 2615 : target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID ^ 1);
582 2615 : if (!target_attr) {
583 : /*
584 : * windows 2003 has a broken schema where
585 : * the definition of msDS-IsDomainFor
586 : * is missing (which is supposed to be
587 : * the backlink of the msDS-HasDomainNCs
588 : * attribute
589 : */
590 24 : continue;
591 : }
592 :
593 2591 : attr_name = target_attr->lDAPDisplayName;
594 :
595 2591 : switch (el->flags & LDB_FLAG_MOD_MASK) {
596 14 : case LDB_FLAG_MOD_REPLACE:
597 : /* treat as just a normal add the delete part is handled by the callback */
598 14 : store_el = true;
599 :
600 : FALL_THROUGH;
601 1266 : case LDB_FLAG_MOD_ADD:
602 :
603 : /* For each value being added, we need to setup the adds */
604 2532 : for (j = 0; j < el->num_values; j++) {
605 1266 : ret = la_store_op(ac, LA_OP_ADD,
606 : schema_attr,
607 1266 : &el->values[j],
608 : attr_name);
609 1266 : if (ret != LDB_SUCCESS) {
610 0 : return ret;
611 : }
612 : }
613 1266 : break;
614 :
615 1325 : case LDB_FLAG_MOD_DELETE:
616 :
617 1325 : if (el->num_values) {
618 : /* For each value being deleted, we need to setup the delete */
619 2522 : for (j = 0; j < el->num_values; j++) {
620 1261 : ret = la_store_op(ac, LA_OP_DEL,
621 : schema_attr,
622 1261 : &el->values[j],
623 : attr_name);
624 1261 : if (ret != LDB_SUCCESS) {
625 0 : return ret;
626 : }
627 : }
628 : } else {
629 : /* Flag that there was a DELETE
630 : * without a value specified, so we
631 : * need to look for the old value */
632 64 : store_el = true;
633 : }
634 :
635 1325 : break;
636 : }
637 :
638 2591 : if (store_el) {
639 : struct ldb_message_element *search_el;
640 :
641 78 : search_el = talloc_realloc(ac->rc, ac->rc->el,
642 : struct ldb_message_element,
643 : ac->rc->num_elements +1);
644 78 : if (!search_el) {
645 0 : return ldb_oom(ldb);
646 : }
647 78 : ac->rc->el = search_el;
648 :
649 78 : ac->rc->el[ac->rc->num_elements] = *el;
650 78 : ac->rc->num_elements++;
651 : }
652 : }
653 :
654 118181 : if (ac->ops || ac->rc->el) {
655 : /* both replace and delete without values are handled in the callback
656 : * after the search on the entry to be modified is performed */
657 :
658 1691 : attrs = talloc_array(ac->rc, const char *, ac->rc->num_elements + 1);
659 1691 : if (!attrs) {
660 0 : return ldb_oom(ldb);
661 : }
662 1769 : for (i = 0; i < ac->rc->num_elements; i++) {
663 78 : attrs[i] = ac->rc->el[i].name;
664 : }
665 1691 : attrs[i] = NULL;
666 :
667 : /* The callback does all the hard work here */
668 1691 : ret = ldb_build_search_req(&search_req, ldb, ac,
669 1691 : req->op.mod.message->dn,
670 : LDB_SCOPE_BASE,
671 : "(objectClass=*)", attrs,
672 : NULL,
673 : ac, la_mod_search_callback,
674 : req);
675 1691 : LDB_REQ_SET_LOCATION(search_req);
676 :
677 : /* We need to figure out our own extended DN, to fill in as the backlink target */
678 1691 : if (ret == LDB_SUCCESS) {
679 1691 : ret = dsdb_request_add_controls(search_req,
680 : DSDB_SEARCH_SHOW_RECYCLED |
681 : DSDB_SEARCH_SHOW_EXTENDED_DN);
682 : }
683 2542 : if (ret == LDB_SUCCESS) {
684 1691 : talloc_steal(search_req, attrs);
685 :
686 1691 : ret = ldb_next_request(module, search_req);
687 : }
688 :
689 : } else {
690 : /* nothing to do for this module, proceed */
691 116490 : talloc_free(ac);
692 116490 : ret = ldb_next_request(module, req);
693 : }
694 :
695 118181 : return ret;
696 : }
697 :
698 :
699 4927 : static int linked_attributes_fix_link_slow(struct ldb_module *module,
700 : struct ldb_request *parent,
701 : struct ldb_message *msg,
702 : struct ldb_dn *new_dn,
703 : struct GUID self_guid,
704 : const char *syntax_oid,
705 : const char *reverse_syntax_oid)
706 : {
707 : int ret;
708 : unsigned int i;
709 : struct GUID link_guid;
710 4927 : struct ldb_message_element *el = &msg->elements[0];
711 4927 : struct ldb_context *ldb = ldb_module_get_ctx(module);
712 4927 : bool has_unique_value = strcmp(reverse_syntax_oid, LDB_SYNTAX_DN) == 0;
713 4927 : TALLOC_CTX *tmp_ctx = talloc_new(module);
714 4927 : if (tmp_ctx == NULL) {
715 0 : return LDB_ERR_OPERATIONS_ERROR;
716 : }
717 : /*
718 : * The msg has one element (el) containing links of one particular
719 : * type from the remote object. We know that at least one of those
720 : * links points to the object being renamed (identified by self_guid,
721 : * renamed to new_dn). Usually only one of the links will point back
722 : * to renamed object, but there can be more when the reverse link is a
723 : * DN+Binary link.
724 : *
725 : * This is used for unsorted links, which is to say back links and
726 : * forward links on old databases. It necessarily involves a linear
727 : * search, though when the link is a plain DN link, we can skip
728 : * checking as soon as we find it.
729 : *
730 : * NOTE: if there are duplicate links, the extra ones will end up as
731 : * dangling links to the old DN. This may or may not be worse than
732 : * leaving them as duplicate links.
733 : */
734 14199 : for (i = 0; i < el->num_values; i++) {
735 13751 : struct dsdb_dn *dsdb_dn = dsdb_dn_parse(msg,
736 : ldb,
737 13751 : &el->values[i],
738 : syntax_oid);
739 13751 : if (dsdb_dn == NULL) {
740 0 : talloc_free(tmp_ctx);
741 0 : return LDB_ERR_INVALID_DN_SYNTAX;
742 : }
743 :
744 13751 : ret = la_guid_from_dn(module, parent, dsdb_dn->dn, &link_guid);
745 13751 : if (ret != LDB_SUCCESS) {
746 0 : talloc_free(tmp_ctx);
747 0 : return ret;
748 : }
749 :
750 : /*
751 : * By comparing using the GUID we ensure that even if somehow
752 : * the name has got out of sync, this rename will fix it.
753 : *
754 : * If somehow we don't have a GUID on the DN in the DB, the
755 : * la_guid_from_dn call will be more costly, but still give us
756 : * a GUID. dbcheck will fix this if run.
757 : */
758 13751 : if (!GUID_equal(&self_guid, &link_guid)) {
759 8406 : continue;
760 : }
761 :
762 5345 : ret = ldb_dn_update_components(dsdb_dn->dn, new_dn);
763 5345 : if (ret != LDB_SUCCESS) {
764 0 : talloc_free(tmp_ctx);
765 0 : return ret;
766 : }
767 :
768 5345 : el->values[i] = data_blob_string_const(
769 5345 : dsdb_dn_get_extended_linearized(el->values, dsdb_dn, 1));
770 5345 : if (has_unique_value) {
771 4479 : break;
772 : }
773 : }
774 :
775 4927 : talloc_free(tmp_ctx);
776 4927 : return LDB_SUCCESS;
777 : }
778 :
779 :
780 2556 : static int linked_attributes_fix_forward_link(struct ldb_module *module,
781 : struct ldb_message *msg,
782 : struct ldb_dn *new_dn,
783 : struct GUID self_guid,
784 : const char *syntax_oid)
785 : {
786 : int ret;
787 2556 : struct ldb_context *ldb = ldb_module_get_ctx(module);
788 2556 : struct parsed_dn *pdn_list = NULL;
789 2556 : struct parsed_dn *exact = NULL;
790 2556 : struct parsed_dn *next = NULL;
791 : bool is_plain_dn;
792 2556 : struct ldb_message_element *el = &msg->elements[0];
793 2556 : unsigned int num_parsed_dns = el->num_values;
794 :
795 2556 : TALLOC_CTX *tmp_ctx = talloc_new(module);
796 2556 : if (tmp_ctx == NULL) {
797 0 : return LDB_ERR_OPERATIONS_ERROR;
798 : }
799 :
800 : /*
801 : * The msg has a single element (el) containing forward links which we
802 : * trust are sorted in GUID order. We know that at least one of those
803 : * links points to the object being renamed (identified by self_guid,
804 : * renamed to new_dn), because that object has a backlink pointing
805 : * here.
806 : *
807 : * In most cases we assume there will only be one forward link, which
808 : * is found by parsed_dn_find(), but in the case of DN+Binary links
809 : * (e.g. msDS-RevealedUsers) there may be many forward links that
810 : * share the same DN/GUID but differ in the binary part. For those we
811 : * need to look around the link found by parsed_dn_find() and convert
812 : * them all -- there is no way to know which forward link belongs to
813 : * which backlink.
814 : */
815 :
816 2556 : ret = get_parsed_dns_trusted(tmp_ctx, el, &pdn_list);
817 2556 : if (ret != LDB_SUCCESS) {
818 0 : ldb_asprintf_errstring(ldb, "get_parsed_dn_trusted() "
819 : "error fixing %s links for %s",
820 : el->name,
821 : ldb_dn_get_linearized(msg->dn));
822 0 : talloc_free(tmp_ctx);
823 0 : return ret;
824 : }
825 :
826 : /* find our DN in the values */
827 2556 : ret = parsed_dn_find(ldb, pdn_list, num_parsed_dns,
828 : &self_guid,
829 : NULL,
830 : data_blob_null, 0,
831 : &exact, &next,
832 : syntax_oid,
833 : false);
834 :
835 2556 : if (ret != LDB_SUCCESS) {
836 0 : ldb_asprintf_errstring(ldb, "parsed_dn_find() "
837 : "error fixing %s links for %s",
838 : el->name,
839 : ldb_dn_get_linearized(msg->dn));
840 0 : talloc_free(tmp_ctx);
841 0 : return ret;
842 : }
843 :
844 2556 : if (exact == NULL) {
845 1204 : ldb_asprintf_errstring(
846 : ldb,
847 : "parsed_dn_find could not find %s link for %s",
848 : el->name,
849 : ldb_dn_get_linearized(msg->dn));
850 1204 : talloc_free(tmp_ctx);
851 1204 : return LDB_ERR_OPERATIONS_ERROR;
852 : }
853 :
854 1352 : is_plain_dn = strcmp(syntax_oid, LDB_SYNTAX_DN) == 0;
855 :
856 1352 : if (is_plain_dn) {
857 : /*
858 : * The common case -- we only have to update a single link
859 : */
860 730 : ret = ldb_dn_update_components(exact->dsdb_dn->dn, new_dn);
861 730 : if (ret != LDB_SUCCESS) {
862 0 : DBG_ERR("could not update components %s %s\n",
863 : ldb_dn_get_linearized(exact->dsdb_dn->dn),
864 : ldb_dn_get_linearized(new_dn)
865 : );
866 :
867 0 : talloc_free(tmp_ctx);
868 0 : return ret;
869 : }
870 730 : *(exact->v) = data_blob_string_const(
871 730 : dsdb_dn_get_extended_linearized(el->values,
872 730 : exact->dsdb_dn,
873 : 1));
874 : } else {
875 : /*
876 : * The forward link is a DN+Binary (or in some alternate
877 : * universes, DN+String), which means the parsed_dns are keyed
878 : * on GUID+Binary. We don't know the binary part, which means
879 : * from our point of view the list can have entries with
880 : * duplicate GUIDs that we can't tell apart. We don't know
881 : * which backlink belongs to which GUID+binary, and the binary
882 : * search will always find the same one. That means one link
883 : * link will get fixed n times, whil n-1 links get fixed
884 : * never.
885 : *
886 : * If we instead fixing all the possible links, we end up
887 : * fixing n links n times, which at least works and is
888 : * probably not too costly because n is probably small.
889 : */
890 622 : struct parsed_dn *first = exact;
891 622 : struct parsed_dn *last = exact;
892 622 : struct parsed_dn *p = NULL;
893 : int cmp;
894 1244 : while (first > pdn_list) {
895 559 : p = first - 1;
896 559 : if (p->dsdb_dn == NULL) {
897 0 : ret = really_parse_trusted_dn(tmp_ctx,
898 : ldb, p,
899 : syntax_oid);
900 0 : if (ret != LDB_SUCCESS) {
901 0 : talloc_free(tmp_ctx);
902 0 : return ret;
903 : }
904 : }
905 559 : cmp = ndr_guid_compare(&exact->guid, &p->guid);
906 559 : if (cmp != 0) {
907 559 : break;
908 : }
909 0 : first = p;
910 : }
911 :
912 3040 : while (last < pdn_list + num_parsed_dns - 1) {
913 2361 : p = last + 1;
914 2361 : if (p->dsdb_dn == NULL) {
915 1806 : ret = really_parse_trusted_dn(tmp_ctx,
916 : ldb, p,
917 : syntax_oid);
918 1806 : if (ret != LDB_SUCCESS) {
919 0 : talloc_free(tmp_ctx);
920 0 : return ret;
921 : }
922 : }
923 2361 : cmp = ndr_guid_compare(&exact->guid, &p->guid);
924 2361 : if (cmp != 0) {
925 565 : break;
926 : }
927 1796 : last = p;
928 : }
929 :
930 3040 : for (p = first; p <= last; p++) {
931 2418 : ret = ldb_dn_update_components(p->dsdb_dn->dn, new_dn);
932 2418 : if (ret != LDB_SUCCESS) {
933 0 : DBG_ERR("could not update components %s %s\n",
934 : ldb_dn_get_linearized(p->dsdb_dn->dn),
935 : ldb_dn_get_linearized(new_dn)
936 : );
937 0 : talloc_free(tmp_ctx);
938 0 : return ret;
939 : }
940 2418 : *(p->v) = data_blob_string_const(
941 2418 : dsdb_dn_get_extended_linearized(el->values,
942 : p->dsdb_dn,
943 : 1));
944 : }
945 : }
946 :
947 1352 : talloc_free(tmp_ctx);
948 1352 : return LDB_SUCCESS;
949 : }
950 :
951 :
952 4049 : static int linked_attributes_fix_links(struct ldb_module *module,
953 : struct GUID self_guid,
954 : struct ldb_dn *old_dn,
955 : struct ldb_dn *new_dn,
956 : struct ldb_message_element *el,
957 : struct dsdb_schema *schema,
958 : const struct dsdb_attribute *schema_attr,
959 : struct ldb_request *parent)
960 : {
961 : unsigned int i;
962 4049 : TALLOC_CTX *tmp_ctx = NULL;
963 4049 : struct ldb_context *ldb = ldb_module_get_ctx(module);
964 4049 : const struct dsdb_attribute *target = NULL;
965 : const char *attrs[2];
966 : int ret;
967 4049 : struct la_private *la_private = NULL;
968 :
969 4049 : target = dsdb_attribute_by_linkID(schema, schema_attr->linkID ^ 1);
970 4049 : if (target == NULL) {
971 : /* there is no counterpart link to change */
972 183 : return LDB_SUCCESS;
973 : }
974 :
975 3866 : tmp_ctx = talloc_new(module);
976 3866 : if (tmp_ctx == NULL) {
977 0 : return LDB_ERR_OPERATIONS_ERROR;
978 : }
979 :
980 3866 : la_private = talloc_get_type(ldb_module_get_private(module),
981 : struct la_private);
982 3866 : if (la_private == NULL) {
983 0 : talloc_free(tmp_ctx);
984 0 : return LDB_ERR_OPERATIONS_ERROR;
985 : }
986 :
987 3866 : attrs[0] = target->lDAPDisplayName;
988 3866 : attrs[1] = NULL;
989 :
990 11407 : for (i=0; i<el->num_values; i++) {
991 7541 : struct dsdb_dn *dsdb_dn = NULL;
992 7541 : struct ldb_result *res = NULL;
993 7541 : struct ldb_message *msg = NULL;
994 7541 : struct ldb_message_element *el2 = NULL;
995 : struct GUID link_guid;
996 7541 : char *link_guid_str = NULL;
997 :
998 7541 : dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i],
999 7541 : schema_attr->syntax->ldap_oid);
1000 7541 : if (dsdb_dn == NULL) {
1001 0 : talloc_free(tmp_ctx);
1002 0 : return LDB_ERR_INVALID_DN_SYNTAX;
1003 : }
1004 :
1005 7541 : ret = la_guid_from_dn(module, parent, dsdb_dn->dn, &link_guid);
1006 7541 : if (ret != LDB_SUCCESS) {
1007 0 : ldb_asprintf_errstring(ldb, "Linked attribute %s->%s between %s and %s - GUID not found - %s",
1008 0 : el->name, target->lDAPDisplayName,
1009 : ldb_dn_get_linearized(old_dn),
1010 : ldb_dn_get_linearized(dsdb_dn->dn),
1011 : ldb_errstring(ldb));
1012 0 : talloc_free(tmp_ctx);
1013 0 : return ret;
1014 : }
1015 :
1016 7541 : link_guid_str = GUID_string(tmp_ctx, &link_guid);
1017 7541 : if (link_guid_str == NULL) {
1018 0 : talloc_free(tmp_ctx);
1019 0 : return LDB_ERR_OPERATIONS_ERROR;
1020 : }
1021 :
1022 : /*
1023 : * get the existing message from the db for the object with
1024 : * this GUID, returning attribute being modified. We will then
1025 : * use this msg as the basis for a modify call
1026 : */
1027 :
1028 7541 : ret = dsdb_module_search(module, tmp_ctx, &res, NULL, LDB_SCOPE_SUBTREE, attrs,
1029 : DSDB_FLAG_NEXT_MODULE |
1030 : DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
1031 : DSDB_SEARCH_SHOW_RECYCLED |
1032 : DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
1033 : DSDB_SEARCH_REVEAL_INTERNALS,
1034 : parent,
1035 : "objectGUID=%s", link_guid_str);
1036 7541 : if (ret != LDB_SUCCESS) {
1037 0 : ldb_asprintf_errstring(ldb, "Linked attribute %s->%s between %s and %s - target GUID %s not found - %s",
1038 0 : el->name, target->lDAPDisplayName,
1039 : ldb_dn_get_linearized(old_dn),
1040 : ldb_dn_get_linearized(dsdb_dn->dn),
1041 : link_guid_str,
1042 : ldb_errstring(ldb));
1043 0 : talloc_free(tmp_ctx);
1044 0 : return ret;
1045 : }
1046 7541 : if (res->count == 0) {
1047 : /* Forward link without backlink object remaining - nothing to do here */
1048 60 : continue;
1049 : }
1050 7539 : if (res->count != 1) {
1051 0 : ldb_asprintf_errstring(ldb, "Linked attribute %s->%s between %s and %s - target GUID %s found more than once!",
1052 0 : el->name, target->lDAPDisplayName,
1053 : ldb_dn_get_linearized(old_dn),
1054 : ldb_dn_get_linearized(dsdb_dn->dn),
1055 : link_guid_str);
1056 0 : talloc_free(tmp_ctx);
1057 0 : return LDB_ERR_OPERATIONS_ERROR;
1058 : }
1059 :
1060 7539 : msg = res->msgs[0];
1061 :
1062 7539 : if (msg->num_elements == 0) {
1063 : /* Forward link without backlink remaining - nothing to do here */
1064 56 : continue;
1065 7483 : } else if (msg->num_elements != 1) {
1066 0 : ldb_asprintf_errstring(ldb, "Bad msg elements - got %u elements, expected one element to be returned in linked_attributes_fix_links for %s",
1067 : msg->num_elements, ldb_dn_get_linearized(msg->dn));
1068 0 : talloc_free(tmp_ctx);
1069 0 : return LDB_ERR_OPERATIONS_ERROR;
1070 : }
1071 7483 : if (ldb_attr_cmp(msg->elements[0].name, target->lDAPDisplayName) != 0) {
1072 0 : 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));
1073 0 : talloc_free(tmp_ctx);
1074 0 : return LDB_ERR_OPERATIONS_ERROR;
1075 : }
1076 7483 : el2 = &msg->elements[0];
1077 :
1078 7483 : el2->flags = LDB_FLAG_MOD_REPLACE;
1079 :
1080 9386 : if (target->linkID & 1 ||
1081 2559 : ! la_private->sorted_links) {
1082 : /* handle backlinks (which aren't sorted in the DB)
1083 : and forward links in old unsorted databases. */
1084 9511 : ret = linked_attributes_fix_link_slow(
1085 : module,
1086 : parent,
1087 : msg,
1088 : new_dn,
1089 : self_guid,
1090 4927 : target->syntax->ldap_oid,
1091 4927 : schema_attr->syntax->ldap_oid);
1092 : } else {
1093 : /* we can binary search to find forward links */
1094 2556 : ret = linked_attributes_fix_forward_link(
1095 : module,
1096 : msg,
1097 : new_dn,
1098 : self_guid,
1099 2556 : target->syntax->ldap_oid);
1100 : }
1101 7483 : ret = dsdb_check_single_valued_link(target, el2);
1102 7483 : if (ret != LDB_SUCCESS) {
1103 0 : talloc_free(tmp_ctx);
1104 0 : return ret;
1105 : }
1106 :
1107 : /* we may be putting multiple values in an attribute -
1108 : disable checking for this attribute */
1109 7483 : el2->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
1110 :
1111 7483 : ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE, parent);
1112 7483 : if (ret != LDB_SUCCESS) {
1113 0 : ldb_asprintf_errstring(ldb, "Linked attribute %s->%s between %s and %s - update failed - %s",
1114 0 : el->name, target->lDAPDisplayName,
1115 : ldb_dn_get_linearized(old_dn),
1116 : ldb_dn_get_linearized(dsdb_dn->dn),
1117 : ldb_errstring(ldb));
1118 0 : talloc_free(tmp_ctx);
1119 0 : return ret;
1120 : }
1121 : }
1122 :
1123 3866 : talloc_free(tmp_ctx);
1124 3866 : return LDB_SUCCESS;
1125 : }
1126 :
1127 :
1128 : /* rename */
1129 55929 : static int linked_attributes_rename(struct ldb_module *module, struct ldb_request *req)
1130 : {
1131 : struct ldb_result *res;
1132 : struct ldb_message *msg;
1133 : unsigned int i;
1134 55929 : struct ldb_context *ldb = ldb_module_get_ctx(module);
1135 : struct dsdb_schema *schema;
1136 : int ret;
1137 : struct GUID guid;
1138 :
1139 : /*
1140 : - load the current msg
1141 : - find any linked attributes
1142 : - if its a link then find the target object
1143 : - modify the target linked attributes with the new DN
1144 : */
1145 55929 : ret = dsdb_module_search_dn(module, req, &res, req->op.rename.olddn,
1146 : NULL,
1147 : DSDB_FLAG_NEXT_MODULE |
1148 : DSDB_SEARCH_SHOW_EXTENDED_DN |
1149 : DSDB_SEARCH_SHOW_RECYCLED, req);
1150 55929 : if (ret != LDB_SUCCESS) {
1151 0 : return ret;
1152 : }
1153 :
1154 55929 : schema = dsdb_get_schema(ldb, res);
1155 55929 : if (!schema) {
1156 0 : return ldb_oom(ldb);
1157 : }
1158 :
1159 55929 : msg = res->msgs[0];
1160 :
1161 55929 : ret = la_guid_from_dn(module, req, msg->dn, &guid);
1162 55929 : if (ret != LDB_SUCCESS) {
1163 0 : return ret;
1164 : }
1165 :
1166 1176610 : for (i=0; i<msg->num_elements; i++) {
1167 1120681 : struct ldb_message_element *el = &msg->elements[i];
1168 919642 : const struct dsdb_attribute *schema_attr
1169 1120681 : = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
1170 1120681 : if (!schema_attr || schema_attr->linkID == 0) {
1171 1116632 : continue;
1172 : }
1173 4049 : ret = linked_attributes_fix_links(module, guid, msg->dn, req->op.rename.newdn, el,
1174 : schema, schema_attr, req);
1175 4049 : if (ret != LDB_SUCCESS) {
1176 0 : talloc_free(res);
1177 0 : return ret;
1178 : }
1179 : }
1180 :
1181 55929 : talloc_free(res);
1182 :
1183 55929 : return ldb_next_request(module, req);
1184 : }
1185 :
1186 :
1187 : /* queue a linked attributes modify request in the la_private
1188 : structure */
1189 1850 : static int la_queue_mod_request(struct la_context *ac)
1190 : {
1191 948 : struct la_private *la_private =
1192 1850 : talloc_get_type(ldb_module_get_private(ac->module),
1193 : struct la_private);
1194 :
1195 1850 : if (la_private == NULL || la_private->transaction == NULL) {
1196 0 : ldb_debug(ldb_module_get_ctx(ac->module),
1197 : LDB_DEBUG_ERROR,
1198 : __location__ ": No la_private transaction setup\n");
1199 0 : return ldb_operr(ldb_module_get_ctx(ac->module));
1200 : }
1201 :
1202 1850 : talloc_steal(la_private->transaction, ac);
1203 1850 : DLIST_ADD(la_private->transaction->la_list, ac);
1204 :
1205 1850 : return ldb_module_done(ac->req, ac->op_controls,
1206 : ac->op_response, LDB_SUCCESS);
1207 : }
1208 :
1209 : /* Having done the original operation, then try to fix up all the linked attributes for modify and delete */
1210 1691 : static int la_mod_del_callback(struct ldb_request *req, struct ldb_reply *ares)
1211 : {
1212 : struct la_context *ac;
1213 : struct ldb_context *ldb;
1214 : int ret;
1215 :
1216 1691 : ac = talloc_get_type(req->context, struct la_context);
1217 1691 : ldb = ldb_module_get_ctx(ac->module);
1218 :
1219 1691 : if (!ares) {
1220 0 : return ldb_module_done(ac->req, NULL, NULL,
1221 : LDB_ERR_OPERATIONS_ERROR);
1222 : }
1223 1691 : if (ares->error != LDB_SUCCESS) {
1224 2 : return ldb_module_done(ac->req, ares->controls,
1225 : ares->response, ares->error);
1226 : }
1227 :
1228 1689 : if (ares->type != LDB_REPLY_DONE) {
1229 0 : ldb_set_errstring(ldb,
1230 : "invalid reply type in linked attributes delete callback");
1231 0 : talloc_free(ares);
1232 0 : return ldb_module_done(ac->req, NULL, NULL,
1233 : LDB_ERR_OPERATIONS_ERROR);
1234 : }
1235 :
1236 1689 : ac->op_controls = talloc_steal(ac, ares->controls);
1237 1689 : ac->op_response = talloc_steal(ac, ares->response);
1238 :
1239 : /* If we have modfies to make, this is the time to do them for modify and delete */
1240 1689 : ret = la_queue_mod_request(ac);
1241 :
1242 1689 : if (ret != LDB_SUCCESS) {
1243 0 : return ldb_module_done(ac->req, NULL, NULL, ret);
1244 : }
1245 1689 : talloc_free(ares);
1246 :
1247 : /* la_queue_mod_request has already sent the callbacks */
1248 1689 : return LDB_SUCCESS;
1249 :
1250 : }
1251 :
1252 : /* Having done the original add, then try to fix up all the linked attributes
1253 :
1254 : This is done after the add so the links can get the extended DNs correctly.
1255 : */
1256 161 : static int la_add_callback(struct ldb_request *req, struct ldb_reply *ares)
1257 : {
1258 : struct la_context *ac;
1259 : struct ldb_context *ldb;
1260 : int ret;
1261 :
1262 161 : ac = talloc_get_type(req->context, struct la_context);
1263 161 : ldb = ldb_module_get_ctx(ac->module);
1264 :
1265 161 : if (!ares) {
1266 0 : return ldb_module_done(ac->req, NULL, NULL,
1267 : LDB_ERR_OPERATIONS_ERROR);
1268 : }
1269 161 : if (ares->error != LDB_SUCCESS) {
1270 0 : return ldb_module_done(ac->req, ares->controls,
1271 : ares->response, ares->error);
1272 : }
1273 :
1274 161 : if (ares->type != LDB_REPLY_DONE) {
1275 0 : ldb_set_errstring(ldb,
1276 : "invalid reply type in linked attributes add callback");
1277 0 : talloc_free(ares);
1278 0 : return ldb_module_done(ac->req, NULL, NULL,
1279 : LDB_ERR_OPERATIONS_ERROR);
1280 : }
1281 :
1282 161 : if (ac->ops) {
1283 : struct ldb_request *search_req;
1284 : static const char *attrs[] = { NULL };
1285 :
1286 : /* The callback does all the hard work here - we need
1287 : * the objectGUID and SID of the added record */
1288 259 : ret = ldb_build_search_req(&search_req, ldb, ac,
1289 161 : ac->req->op.add.message->dn,
1290 : LDB_SCOPE_BASE,
1291 : "(objectClass=*)", attrs,
1292 : NULL,
1293 : ac, la_mod_search_callback,
1294 : ac->req);
1295 161 : LDB_REQ_SET_LOCATION(search_req);
1296 :
1297 161 : if (ret == LDB_SUCCESS) {
1298 161 : ret = dsdb_request_add_controls(search_req,
1299 : DSDB_SEARCH_SHOW_RECYCLED |
1300 : DSDB_SEARCH_SHOW_EXTENDED_DN);
1301 : }
1302 161 : if (ret != LDB_SUCCESS) {
1303 0 : return ldb_module_done(ac->req, NULL, NULL,
1304 : ret);
1305 : }
1306 :
1307 161 : ac->op_controls = talloc_steal(ac, ares->controls);
1308 161 : ac->op_response = talloc_steal(ac, ares->response);
1309 :
1310 161 : return ldb_next_request(ac->module, search_req);
1311 :
1312 : } else {
1313 0 : return ldb_module_done(ac->req, ares->controls,
1314 : ares->response, ares->error);
1315 : }
1316 : }
1317 :
1318 : /* Reconstruct the original request, but pointing at our local callback to finish things off */
1319 1852 : static int la_down_req(struct la_context *ac)
1320 : {
1321 : struct ldb_request *down_req;
1322 : struct ldb_context *ldb;
1323 : int ret;
1324 :
1325 1852 : ldb = ldb_module_get_ctx(ac->module);
1326 :
1327 1852 : switch (ac->req->operation) {
1328 161 : case LDB_ADD:
1329 357 : ret = ldb_build_add_req(&down_req, ldb, ac,
1330 161 : ac->req->op.add.message,
1331 161 : ac->req->controls,
1332 : ac, la_add_callback,
1333 : ac->req);
1334 161 : LDB_REQ_SET_LOCATION(down_req);
1335 161 : break;
1336 1691 : case LDB_MODIFY:
1337 3393 : ret = ldb_build_mod_req(&down_req, ldb, ac,
1338 1691 : ac->req->op.mod.message,
1339 1691 : ac->req->controls,
1340 : ac, la_mod_del_callback,
1341 : ac->req);
1342 1691 : LDB_REQ_SET_LOCATION(down_req);
1343 1691 : break;
1344 0 : default:
1345 0 : ret = LDB_ERR_OPERATIONS_ERROR;
1346 : }
1347 1852 : if (ret != LDB_SUCCESS) {
1348 0 : return ret;
1349 : }
1350 :
1351 1852 : return ldb_next_request(ac->module, down_req);
1352 : }
1353 :
1354 : /*
1355 : use the GUID part of an extended DN to find the target DN, in case
1356 : it has moved
1357 : */
1358 2996 : static int la_find_dn_target(struct ldb_module *module, struct la_context *ac,
1359 : struct GUID *guid, struct ldb_dn **dn)
1360 : {
1361 2996 : return dsdb_module_dn_by_guid(ac->module, ac, guid, dn, ac->req);
1362 : }
1363 :
1364 : /* apply one la_context op change */
1365 2996 : static int la_do_op_request(struct ldb_module *module, struct la_context *ac, struct la_op_store *op)
1366 : {
1367 : struct ldb_message_element *ret_el;
1368 : struct ldb_message *new_msg;
1369 : struct ldb_context *ldb;
1370 : int ret;
1371 :
1372 2996 : if (ac->mod_dn == NULL) {
1373 : /* we didn't find the DN that we searched for */
1374 0 : return LDB_SUCCESS;
1375 : }
1376 :
1377 2996 : ldb = ldb_module_get_ctx(ac->module);
1378 :
1379 : /* Create the modify request */
1380 2996 : new_msg = ldb_msg_new(ac);
1381 2996 : if (!new_msg) {
1382 0 : return ldb_oom(ldb);
1383 : }
1384 :
1385 2996 : ret = la_find_dn_target(module, ac, &op->guid, &new_msg->dn);
1386 2996 : if (ret != LDB_SUCCESS) {
1387 21 : return ret;
1388 : }
1389 :
1390 2975 : if (op->op == LA_OP_ADD) {
1391 1592 : ret = ldb_msg_add_empty(new_msg, op->name,
1392 : LDB_FLAG_MOD_ADD, &ret_el);
1393 : } else {
1394 1383 : ret = ldb_msg_add_empty(new_msg, op->name,
1395 : LDB_FLAG_MOD_DELETE, &ret_el);
1396 : }
1397 2975 : if (ret != LDB_SUCCESS) {
1398 0 : return ret;
1399 : }
1400 2975 : ret_el->values = talloc_array(new_msg, struct ldb_val, 1);
1401 2975 : if (!ret_el->values) {
1402 0 : return ldb_oom(ldb);
1403 : }
1404 2975 : ret_el->num_values = 1;
1405 2975 : ret_el->values[0] = data_blob_string_const(ldb_dn_get_extended_linearized(new_msg, ac->mod_dn, 1));
1406 :
1407 : /* a backlink should never be single valued. Unfortunately the
1408 : exchange schema has a attribute
1409 : msExchBridgeheadedLocalConnectorsDNBL which is single
1410 : valued and a backlink. We need to cope with that by
1411 : ignoring the single value flag */
1412 2975 : ret_el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
1413 :
1414 : #if 0
1415 : ldb_debug(ldb, LDB_DEBUG_WARNING,
1416 : "link on %s %s: %s %s\n",
1417 : ldb_dn_get_linearized(new_msg->dn), ret_el->name,
1418 : ret_el->values[0].data, ac->ops->op == LA_OP_ADD ? "added" : "deleted");
1419 : #endif
1420 :
1421 2975 : if (DEBUGLVL(4)) {
1422 0 : DEBUG(4,("Applying linked attribute change:\n%s\n",
1423 : ldb_ldif_message_redacted_string(ldb, op,
1424 : LDB_CHANGETYPE_MODIFY,
1425 : new_msg)));
1426 : }
1427 :
1428 2975 : ret = dsdb_module_modify(module, new_msg, DSDB_FLAG_NEXT_MODULE, ac->req);
1429 2975 : if (ret != LDB_SUCCESS) {
1430 0 : ldb_debug(ldb, LDB_DEBUG_WARNING, __location__ ": failed to apply linked attribute change '%s'\n%s\n",
1431 : ldb_errstring(ldb),
1432 : ldb_ldif_message_redacted_string(ldb, op,
1433 : LDB_CHANGETYPE_MODIFY,
1434 : new_msg));
1435 : }
1436 :
1437 2975 : return ret;
1438 : }
1439 :
1440 : /* apply one set of la_context changes */
1441 1850 : static int la_do_mod_request(struct ldb_module *module, struct la_context *ac)
1442 : {
1443 : struct la_op_store *op;
1444 :
1445 4846 : for (op = ac->ops; op; op=op->next) {
1446 2996 : int ret = la_do_op_request(module, ac, op);
1447 2996 : if (ret != LDB_SUCCESS) {
1448 21 : if (ret != LDB_ERR_NO_SUCH_OBJECT) {
1449 0 : return ret;
1450 : }
1451 : }
1452 : }
1453 :
1454 1850 : return LDB_SUCCESS;
1455 : }
1456 :
1457 :
1458 : /*
1459 : we hook into the transaction operations to allow us to
1460 : perform the linked attribute updates at the end of the whole
1461 : transaction. This allows a forward linked attribute to be created
1462 : before the target is created, as long as the target is created
1463 : in the same transaction
1464 : */
1465 244616 : static int linked_attributes_start_transaction(struct ldb_module *module)
1466 : {
1467 : /* create our private structure for this transaction */
1468 187794 : struct la_private *la_private =
1469 244616 : talloc_get_type(ldb_module_get_private(module),
1470 : struct la_private);
1471 :
1472 244616 : if (la_private == NULL) {
1473 0 : return ldb_oom(ldb_module_get_ctx(module));
1474 : }
1475 244616 : talloc_free(la_private->transaction);
1476 244616 : la_private->transaction = talloc(module, struct la_private_transaction);
1477 244616 : if (la_private->transaction == NULL) {
1478 0 : return ldb_oom(ldb_module_get_ctx(module));
1479 : }
1480 244616 : la_private->transaction->la_list = NULL;
1481 244616 : return ldb_next_start_trans(module);
1482 : }
1483 :
1484 : /*
1485 : on prepare commit we loop over our queued la_context structures
1486 : and apply each of them
1487 : */
1488 212233 : static int linked_attributes_prepare_commit(struct ldb_module *module)
1489 : {
1490 : struct la_context *ac;
1491 167038 : struct la_private *la_private =
1492 212233 : talloc_get_type(ldb_module_get_private(module),
1493 : struct la_private);
1494 212233 : if (la_private == NULL || la_private->transaction == NULL) {
1495 0 : DBG_ERR("prepare_commit without begin_transaction\n");
1496 : /* prepare commit without begin_transaction - let someone else
1497 : * return the error, just don't segfault */
1498 0 : return ldb_next_prepare_commit(module);
1499 : }
1500 : /* walk the list backwards, to do the first entry first, as we
1501 : * added the entries with DLIST_ADD() which puts them at the
1502 : * start of the list */
1503 :
1504 : /* Start at the end of the list - so we can start
1505 : * there, but ensure we don't create a loop by NULLing
1506 : * it out in the first element */
1507 212233 : ac = DLIST_TAIL(la_private->transaction->la_list);
1508 :
1509 214083 : for (; ac; ac=DLIST_PREV(ac)) {
1510 : int ret;
1511 1850 : ac->req = NULL;
1512 1850 : ret = la_do_mod_request(module, ac);
1513 1850 : if (ret != LDB_SUCCESS) {
1514 0 : DEBUG(0,(__location__ ": Failed mod request ret=%d\n", ret));
1515 0 : TALLOC_FREE(la_private->transaction);
1516 0 : return ret;
1517 : }
1518 : }
1519 :
1520 212233 : TALLOC_FREE(la_private->transaction);
1521 :
1522 212233 : return ldb_next_prepare_commit(module);
1523 : }
1524 :
1525 32114 : static int linked_attributes_del_transaction(struct ldb_module *module)
1526 : {
1527 20574 : struct la_private *la_private =
1528 32114 : talloc_get_type(ldb_module_get_private(module),
1529 : struct la_private);
1530 32114 : TALLOC_FREE(la_private->transaction);
1531 32114 : return ldb_next_del_trans(module);
1532 : }
1533 :
1534 108013 : static int linked_attributes_ldb_init(struct ldb_module *module)
1535 : {
1536 : int ret;
1537 108013 : struct la_private *la_private = NULL;
1538 108013 : struct ldb_context *ldb = ldb_module_get_ctx(module);
1539 :
1540 108013 : ret = ldb_mod_register_control(module, LDB_CONTROL_VERIFY_NAME_OID);
1541 108013 : if (ret != LDB_SUCCESS) {
1542 0 : ldb_debug(ldb_module_get_ctx(module), LDB_DEBUG_ERROR,
1543 : "verify_name: Unable to register control with rootdse!\n");
1544 0 : return ldb_operr(ldb_module_get_ctx(module));
1545 : }
1546 :
1547 108013 : la_private = talloc_zero(module, struct la_private);
1548 108013 : if (la_private == NULL) {
1549 0 : ldb_oom(ldb);
1550 0 : return LDB_ERR_OPERATIONS_ERROR;
1551 : }
1552 :
1553 108013 : ret = dsdb_check_samba_compatible_feature(module,
1554 : SAMBA_SORTED_LINKS_FEATURE,
1555 : &la_private->sorted_links);
1556 108013 : if (ret != LDB_SUCCESS) {
1557 0 : talloc_free(la_private);
1558 0 : return ret;
1559 : }
1560 :
1561 108013 : ldb_module_set_private(module, la_private);
1562 108013 : return ldb_next_init(module);
1563 : }
1564 :
1565 :
1566 : static const struct ldb_module_ops ldb_linked_attributes_module_ops = {
1567 : .name = "linked_attributes",
1568 : .add = linked_attributes_add,
1569 : .modify = linked_attributes_modify,
1570 : .rename = linked_attributes_rename,
1571 : .init_context = linked_attributes_ldb_init,
1572 : .start_transaction = linked_attributes_start_transaction,
1573 : .prepare_commit = linked_attributes_prepare_commit,
1574 : .del_transaction = linked_attributes_del_transaction,
1575 : };
1576 :
1577 4310 : int ldb_linked_attributes_module_init(const char *version)
1578 : {
1579 4310 : LDB_MODULE_CHECK_VERSION(version);
1580 4310 : return ldb_register_module(&ldb_linked_attributes_module_ops);
1581 : }
|