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 0 : 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 1963628 : static struct la_context *linked_attributes_init(struct ldb_module *module,
146 : struct ldb_request *req)
147 : {
148 112968 : struct ldb_context *ldb;
149 112968 : struct la_context *ac;
150 :
151 1963628 : ldb = ldb_module_get_ctx(module);
152 :
153 1963628 : ac = talloc_zero(req, struct la_context);
154 1963628 : if (ac == NULL) {
155 0 : ldb_oom(ldb);
156 0 : return NULL;
157 : }
158 :
159 1963628 : ac->schema = dsdb_get_schema(ldb, ac);
160 1963628 : ac->module = module;
161 1963628 : ac->req = req;
162 :
163 1963628 : return ac;
164 : }
165 :
166 : /*
167 : turn a DN into a GUID
168 : */
169 109582 : 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 276 : NTSTATUS status;
174 276 : int ret;
175 :
176 109582 : status = dsdb_get_extended_dn_guid(dn, guid, "GUID");
177 109582 : if (NT_STATUS_IS_OK(status)) {
178 107876 : return LDB_SUCCESS;
179 : }
180 1430 : 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 1430 : ret = dsdb_module_guid_by_dn(module, dn, guid, parent);
187 1430 : 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 1430 : return LDB_SUCCESS;
193 : }
194 :
195 :
196 : /* Common routine to handle reading the attributes and creating a
197 : * series of modify requests */
198 3342 : 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 0 : struct ldb_context *ldb;
205 0 : struct la_op_store *os;
206 0 : struct ldb_dn *op_dn;
207 0 : struct dsdb_dn *dsdb_dn;
208 0 : int ret;
209 :
210 3342 : ldb = ldb_module_get_ctx(ac->module);
211 :
212 :
213 3342 : os = talloc_zero(ac, struct la_op_store);
214 3342 : if (!os) {
215 0 : return ldb_oom(ldb);
216 : }
217 :
218 3342 : dsdb_dn = dsdb_dn_parse(os, ldb, dn, schema_attr->syntax->ldap_oid);
219 :
220 3342 : 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 3342 : op_dn = dsdb_dn->dn;
228 :
229 3342 : os->op = op;
230 :
231 3342 : ret = la_guid_from_dn(ac->module, ac->req, op_dn, &os->guid);
232 3342 : talloc_free(op_dn);
233 3342 : 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 3342 : if (ret != LDB_SUCCESS) {
244 0 : return ret;
245 : }
246 :
247 3342 : os->name = talloc_strdup(os, name);
248 3342 : if (!os->name) {
249 0 : return ldb_oom(ldb);
250 : }
251 :
252 : /* Do deletes before adds */
253 3342 : if (op == LA_OP_ADD) {
254 1787 : 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 1555 : DLIST_ADD(ac->ops, os);
259 : }
260 :
261 3342 : 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 924119 : static int linked_attributes_add(struct ldb_module *module, struct ldb_request *req)
271 : {
272 83787 : struct ldb_context *ldb;
273 83787 : const struct dsdb_attribute *target_attr;
274 83787 : struct la_context *ac;
275 83787 : const char *attr_name;
276 83787 : struct ldb_control *ctrl;
277 83787 : unsigned int i, j;
278 83787 : struct ldb_control *control;
279 83787 : int ret;
280 :
281 924119 : ldb = ldb_module_get_ctx(module);
282 :
283 924119 : if (ldb_dn_is_special(req->op.add.message->dn)) {
284 : /* do not manipulate our control entries */
285 1517 : return ldb_next_request(module, req);
286 : }
287 :
288 922602 : ac = linked_attributes_init(module, req);
289 922602 : if (!ac) {
290 0 : return ldb_operr(ldb);
291 : }
292 :
293 922602 : control = ldb_request_get_control(req, LDB_CONTROL_VERIFY_NAME_OID);
294 922602 : 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 922602 : 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 527300 : talloc_free(ac);
304 527300 : return ldb_next_request(module, req);
305 : }
306 395302 : ctrl->critical = false;
307 :
308 395302 : 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 8090252 : for (i=0; i < req->op.add.message->num_elements; i++) {
317 7694950 : const struct ldb_message_element *el = &req->op.add.message->elements[i];
318 0 : const struct dsdb_attribute *schema_attr
319 7694950 : = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
320 7694950 : 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 7694950 : if (schema_attr->linkID == 0) {
331 7694727 : 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 395302 : if (ac->ops == NULL) {
365 : /* nothing to do for this module, proceed */
366 395141 : talloc_free(ac);
367 395141 : 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 4392 : static int la_mod_search_callback(struct ldb_request *req, struct ldb_reply *ares)
379 : {
380 0 : struct ldb_context *ldb;
381 0 : const struct dsdb_attribute *schema_attr;
382 0 : const struct dsdb_attribute *target_attr;
383 0 : struct ldb_message_element *search_el;
384 0 : struct replace_context *rc;
385 0 : struct la_context *ac;
386 0 : const char *attr_name;
387 0 : unsigned int i, j;
388 4392 : int ret = LDB_SUCCESS;
389 :
390 4392 : ac = talloc_get_type(req->context, struct la_context);
391 4392 : ldb = ldb_module_get_ctx(ac->module);
392 4392 : rc = ac->rc;
393 :
394 4392 : if (!ares) {
395 0 : return ldb_module_done(ac->req, NULL, NULL,
396 : LDB_ERR_OPERATIONS_ERROR);
397 : }
398 4392 : 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 4392 : switch (ares->type) {
405 2196 : case LDB_REPLY_ENTRY:
406 :
407 2196 : 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 2196 : 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 2274 : 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 2196 : break;
471 :
472 0 : case LDB_REPLY_REFERRAL:
473 : /* ignore */
474 0 : break;
475 :
476 2196 : case LDB_REPLY_DONE:
477 :
478 2196 : talloc_free(ares);
479 :
480 2196 : 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 2035 : ret = la_down_req(ac);
491 2035 : if (ret != LDB_SUCCESS) {
492 0 : return ldb_module_done(ac->req, NULL, NULL, ret);
493 : }
494 : }
495 2196 : return LDB_SUCCESS;
496 : }
497 :
498 2196 : talloc_free(ares);
499 2196 : return ret;
500 : }
501 :
502 :
503 : /* modify */
504 1268858 : 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 30521 : struct ldb_control *control;
512 30521 : struct ldb_context *ldb;
513 30521 : unsigned int i, j;
514 30521 : struct la_context *ac;
515 30521 : struct ldb_request *search_req;
516 30521 : const char **attrs;
517 30521 : struct ldb_control *ctrl;
518 30521 : int ret;
519 :
520 1268858 : ldb = ldb_module_get_ctx(module);
521 :
522 1268858 : if (ldb_dn_is_special(req->op.mod.message->dn)) {
523 : /* do not manipulate our control entries */
524 227832 : return ldb_next_request(module, req);
525 : }
526 :
527 1041026 : ac = linked_attributes_init(module, req);
528 1041026 : if (!ac) {
529 0 : return ldb_operr(ldb);
530 : }
531 :
532 1041026 : control = ldb_request_get_control(req, LDB_CONTROL_VERIFY_NAME_OID);
533 1041026 : 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 1041026 : 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 872542 : talloc_free(ac);
543 872542 : return ldb_next_request(module, req);
544 : }
545 168484 : ctrl->critical = false;
546 :
547 168484 : if (!ac->schema) {
548 : /* without schema, this doesn't make any sense */
549 0 : return ldb_next_request(module, req);
550 : }
551 :
552 168484 : ac->rc = talloc_zero(ac, struct replace_context);
553 168484 : if (!ac->rc) {
554 0 : return ldb_oom(ldb);
555 : }
556 :
557 1404108 : for (i=0; i < req->op.mod.message->num_elements; i++) {
558 1235624 : bool store_el = false;
559 0 : const char *attr_name;
560 0 : const struct dsdb_attribute *target_attr;
561 1235624 : const struct ldb_message_element *el = &req->op.mod.message->elements[i];
562 0 : const struct dsdb_attribute *schema_attr
563 1235624 : = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
564 1235624 : 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 1235624 : if (schema_attr->linkID == 0) {
574 1232665 : continue;
575 : }
576 :
577 : /* this part of the code should only be handling forward links */
578 2959 : SMB_ASSERT((schema_attr->linkID & 1) == 0);
579 :
580 : /* Now find the target attribute */
581 2959 : target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID ^ 1);
582 2959 : 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 2935 : attr_name = target_attr->lDAPDisplayName;
594 :
595 2935 : 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 0 : FALL_THROUGH;
601 1438 : case LDB_FLAG_MOD_ADD:
602 :
603 : /* For each value being added, we need to setup the adds */
604 2876 : for (j = 0; j < el->num_values; j++) {
605 1438 : ret = la_store_op(ac, LA_OP_ADD,
606 : schema_attr,
607 1438 : &el->values[j],
608 : attr_name);
609 1438 : if (ret != LDB_SUCCESS) {
610 0 : return ret;
611 : }
612 : }
613 1438 : break;
614 :
615 1497 : case LDB_FLAG_MOD_DELETE:
616 :
617 1497 : if (el->num_values) {
618 : /* For each value being deleted, we need to setup the delete */
619 2866 : for (j = 0; j < el->num_values; j++) {
620 1433 : ret = la_store_op(ac, LA_OP_DEL,
621 : schema_attr,
622 1433 : &el->values[j],
623 : attr_name);
624 1433 : 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 1497 : break;
636 : }
637 :
638 2935 : if (store_el) {
639 0 : 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 168484 : 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 2035 : attrs = talloc_array(ac->rc, const char *, ac->rc->num_elements + 1);
659 2035 : if (!attrs) {
660 0 : return ldb_oom(ldb);
661 : }
662 2113 : for (i = 0; i < ac->rc->num_elements; i++) {
663 78 : attrs[i] = ac->rc->el[i].name;
664 : }
665 2035 : attrs[i] = NULL;
666 :
667 : /* The callback does all the hard work here */
668 2035 : ret = ldb_build_search_req(&search_req, ldb, ac,
669 2035 : req->op.mod.message->dn,
670 : LDB_SCOPE_BASE,
671 : "(objectClass=*)", attrs,
672 : NULL,
673 : ac, la_mod_search_callback,
674 : req);
675 2035 : 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 2035 : if (ret == LDB_SUCCESS) {
679 2035 : ret = dsdb_request_add_controls(search_req,
680 : DSDB_SEARCH_SHOW_RECYCLED |
681 : DSDB_SEARCH_SHOW_EXTENDED_DN);
682 : }
683 2035 : if (ret == LDB_SUCCESS) {
684 2035 : talloc_steal(search_req, attrs);
685 :
686 2035 : ret = ldb_next_request(module, search_req);
687 : }
688 :
689 : } else {
690 : /* nothing to do for this module, proceed */
691 166449 : talloc_free(ac);
692 166449 : ret = ldb_next_request(module, req);
693 : }
694 :
695 168484 : return ret;
696 : }
697 :
698 :
699 6833 : 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 52 : int ret;
708 52 : unsigned int i;
709 52 : struct GUID link_guid;
710 6833 : struct ldb_message_element *el = &msg->elements[0];
711 6833 : struct ldb_context *ldb = ldb_module_get_ctx(module);
712 6833 : bool has_unique_value = strcmp(reverse_syntax_oid, LDB_SYNTAX_DN) == 0;
713 6833 : TALLOC_CTX *tmp_ctx = talloc_new(module);
714 6833 : 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 16304 : for (i = 0; i < el->num_values; i++) {
735 15913 : struct dsdb_dn *dsdb_dn = dsdb_dn_parse(msg,
736 : ldb,
737 15842 : &el->values[i],
738 : syntax_oid);
739 15842 : if (dsdb_dn == NULL) {
740 0 : talloc_free(tmp_ctx);
741 0 : return LDB_ERR_INVALID_DN_SYNTAX;
742 : }
743 :
744 15842 : ret = la_guid_from_dn(module, parent, dsdb_dn->dn, &link_guid);
745 15842 : 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 15842 : if (!GUID_equal(&self_guid, &link_guid)) {
759 8586 : continue;
760 : }
761 :
762 7256 : ret = ldb_dn_update_components(dsdb_dn->dn, new_dn);
763 7256 : if (ret != LDB_SUCCESS) {
764 0 : talloc_free(tmp_ctx);
765 0 : return ret;
766 : }
767 :
768 7256 : el->values[i] = data_blob_string_const(
769 7256 : dsdb_dn_get_extended_linearized(el->values, dsdb_dn, 1));
770 7256 : if (has_unique_value) {
771 6320 : break;
772 : }
773 : }
774 :
775 6833 : talloc_free(tmp_ctx);
776 6833 : return LDB_SUCCESS;
777 : }
778 :
779 :
780 2715 : 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 2 : int ret;
787 2715 : struct ldb_context *ldb = ldb_module_get_ctx(module);
788 2715 : struct parsed_dn *pdn_list = NULL;
789 2715 : struct parsed_dn *exact = NULL;
790 2715 : struct parsed_dn *next = NULL;
791 2 : bool is_plain_dn;
792 2715 : struct ldb_message_element *el = &msg->elements[0];
793 2715 : unsigned int num_parsed_dns = el->num_values;
794 :
795 2715 : TALLOC_CTX *tmp_ctx = talloc_new(module);
796 2715 : 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 2715 : ret = get_parsed_dns_trusted(tmp_ctx, el, &pdn_list);
817 2715 : 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 2715 : 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 2715 : 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 2715 : if (exact == NULL) {
845 : /*
846 : * Our only caller doesn’t want to know about errors finding a
847 : * forward link for which we have a backlink — in particular,
848 : * during the tombstoning of an object, the forward links have
849 : * already been removed when this routine is called by
850 : * dsdb_module_rename() inside replmd_delete_internals().
851 : */
852 1376 : talloc_free(tmp_ctx);
853 1376 : return LDB_SUCCESS;
854 : }
855 :
856 1339 : is_plain_dn = strcmp(syntax_oid, LDB_SYNTAX_DN) == 0;
857 :
858 1339 : if (is_plain_dn) {
859 : /*
860 : * The common case -- we only have to update a single link
861 : */
862 727 : ret = ldb_dn_update_components(exact->dsdb_dn->dn, new_dn);
863 727 : if (ret != LDB_SUCCESS) {
864 0 : DBG_ERR("could not update components %s %s\n",
865 : ldb_dn_get_linearized(exact->dsdb_dn->dn),
866 : ldb_dn_get_linearized(new_dn)
867 : );
868 :
869 0 : talloc_free(tmp_ctx);
870 0 : return ret;
871 : }
872 727 : *(exact->v) = data_blob_string_const(
873 727 : dsdb_dn_get_extended_linearized(el->values,
874 727 : exact->dsdb_dn,
875 : 1));
876 : } else {
877 : /*
878 : * The forward link is a DN+Binary (or in some alternate
879 : * universes, DN+String), which means the parsed_dns are keyed
880 : * on GUID+Binary. We don't know the binary part, which means
881 : * from our point of view the list can have entries with
882 : * duplicate GUIDs that we can't tell apart. We don't know
883 : * which backlink belongs to which GUID+binary, and the binary
884 : * search will always find the same one. That means one link
885 : * link will get fixed n times, whil n-1 links get fixed
886 : * never.
887 : *
888 : * If we instead fixing all the possible links, we end up
889 : * fixing n links n times, which at least works and is
890 : * probably not too costly because n is probably small.
891 : */
892 612 : struct parsed_dn *first = exact;
893 612 : struct parsed_dn *last = exact;
894 612 : struct parsed_dn *p = NULL;
895 : int cmp;
896 612 : while (first > pdn_list) {
897 544 : p = first - 1;
898 544 : if (p->dsdb_dn == NULL) {
899 0 : ret = really_parse_trusted_dn(tmp_ctx,
900 : ldb, p,
901 : syntax_oid);
902 0 : if (ret != LDB_SUCCESS) {
903 0 : talloc_free(tmp_ctx);
904 0 : return ret;
905 : }
906 : }
907 544 : cmp = ndr_guid_compare(&exact->guid, &p->guid);
908 544 : if (cmp != 0) {
909 544 : break;
910 : }
911 0 : first = p;
912 : }
913 :
914 2368 : while (last < pdn_list + num_parsed_dns - 1) {
915 2302 : p = last + 1;
916 2302 : if (p->dsdb_dn == NULL) {
917 1683 : ret = really_parse_trusted_dn(tmp_ctx,
918 : ldb, p,
919 : syntax_oid);
920 1683 : if (ret != LDB_SUCCESS) {
921 0 : talloc_free(tmp_ctx);
922 0 : return ret;
923 : }
924 : }
925 2302 : cmp = ndr_guid_compare(&exact->guid, &p->guid);
926 2302 : if (cmp != 0) {
927 546 : break;
928 : }
929 1756 : last = p;
930 : }
931 :
932 2980 : for (p = first; p <= last; p++) {
933 2368 : ret = ldb_dn_update_components(p->dsdb_dn->dn, new_dn);
934 2368 : if (ret != LDB_SUCCESS) {
935 0 : DBG_ERR("could not update components %s %s\n",
936 : ldb_dn_get_linearized(p->dsdb_dn->dn),
937 : ldb_dn_get_linearized(new_dn)
938 : );
939 0 : talloc_free(tmp_ctx);
940 0 : return ret;
941 : }
942 2368 : *(p->v) = data_blob_string_const(
943 2368 : dsdb_dn_get_extended_linearized(el->values,
944 : p->dsdb_dn,
945 : 1));
946 : }
947 : }
948 :
949 1339 : talloc_free(tmp_ctx);
950 1339 : return LDB_SUCCESS;
951 : }
952 :
953 :
954 6152 : static int linked_attributes_fix_links(struct ldb_module *module,
955 : struct GUID self_guid,
956 : struct ldb_dn *old_dn,
957 : struct ldb_dn *new_dn,
958 : struct ldb_message_element *el,
959 : struct dsdb_schema *schema,
960 : const struct dsdb_attribute *schema_attr,
961 : struct ldb_request *parent)
962 : {
963 27 : unsigned int i;
964 6152 : TALLOC_CTX *tmp_ctx = NULL;
965 6152 : struct ldb_context *ldb = ldb_module_get_ctx(module);
966 6152 : const struct dsdb_attribute *target = NULL;
967 27 : const char *attrs[2];
968 27 : int ret;
969 6152 : struct la_private *la_private = NULL;
970 :
971 6152 : target = dsdb_attribute_by_linkID(schema, schema_attr->linkID ^ 1);
972 6152 : if (target == NULL) {
973 : /* there is no counterpart link to change */
974 496 : return LDB_SUCCESS;
975 : }
976 :
977 5653 : tmp_ctx = talloc_new(module);
978 5653 : if (tmp_ctx == NULL) {
979 0 : return LDB_ERR_OPERATIONS_ERROR;
980 : }
981 :
982 5653 : la_private = talloc_get_type(ldb_module_get_private(module),
983 : struct la_private);
984 5653 : if (la_private == NULL) {
985 0 : talloc_free(tmp_ctx);
986 0 : return LDB_ERR_OPERATIONS_ERROR;
987 : }
988 :
989 5653 : attrs[0] = target->lDAPDisplayName;
990 5653 : attrs[1] = NULL;
991 :
992 15258 : for (i=0; i<el->num_values; i++) {
993 9605 : struct dsdb_dn *dsdb_dn = NULL;
994 9605 : struct ldb_result *res = NULL;
995 9605 : struct ldb_message *msg = NULL;
996 9605 : struct ldb_message_element *el2 = NULL;
997 54 : struct GUID link_guid;
998 9605 : char *link_guid_str = NULL;
999 :
1000 9659 : dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i],
1001 9605 : schema_attr->syntax->ldap_oid);
1002 9605 : if (dsdb_dn == NULL) {
1003 0 : talloc_free(tmp_ctx);
1004 0 : return LDB_ERR_INVALID_DN_SYNTAX;
1005 : }
1006 :
1007 9605 : ret = la_guid_from_dn(module, parent, dsdb_dn->dn, &link_guid);
1008 9605 : if (ret != LDB_SUCCESS) {
1009 0 : ldb_asprintf_errstring(ldb, "Linked attribute %s->%s between %s and %s - GUID not found - %s",
1010 0 : el->name, target->lDAPDisplayName,
1011 : ldb_dn_get_linearized(old_dn),
1012 : ldb_dn_get_linearized(dsdb_dn->dn),
1013 : ldb_errstring(ldb));
1014 0 : talloc_free(tmp_ctx);
1015 0 : return ret;
1016 : }
1017 :
1018 9605 : link_guid_str = GUID_string(tmp_ctx, &link_guid);
1019 9605 : if (link_guid_str == NULL) {
1020 0 : talloc_free(tmp_ctx);
1021 0 : return LDB_ERR_OPERATIONS_ERROR;
1022 : }
1023 :
1024 : /*
1025 : * get the existing message from the db for the object with
1026 : * this GUID, returning attribute being modified. We will then
1027 : * use this msg as the basis for a modify call
1028 : */
1029 :
1030 9605 : ret = dsdb_module_search(module, tmp_ctx, &res, NULL, LDB_SCOPE_SUBTREE, attrs,
1031 : DSDB_FLAG_NEXT_MODULE |
1032 : DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
1033 : DSDB_SEARCH_SHOW_RECYCLED |
1034 : DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
1035 : DSDB_SEARCH_REVEAL_INTERNALS,
1036 : parent,
1037 : "objectGUID=%s", link_guid_str);
1038 9605 : if (ret != LDB_SUCCESS) {
1039 0 : ldb_asprintf_errstring(ldb, "Linked attribute %s->%s between %s and %s - target GUID %s not found - %s",
1040 0 : el->name, target->lDAPDisplayName,
1041 : ldb_dn_get_linearized(old_dn),
1042 : ldb_dn_get_linearized(dsdb_dn->dn),
1043 : link_guid_str,
1044 : ldb_errstring(ldb));
1045 0 : talloc_free(tmp_ctx);
1046 0 : return ret;
1047 : }
1048 9605 : if (res->count == 0) {
1049 : /* Forward link without backlink object remaining - nothing to do here */
1050 57 : continue;
1051 : }
1052 9603 : if (res->count != 1) {
1053 0 : ldb_asprintf_errstring(ldb, "Linked attribute %s->%s between %s and %s - target GUID %s found more than once!",
1054 0 : el->name, target->lDAPDisplayName,
1055 : ldb_dn_get_linearized(old_dn),
1056 : ldb_dn_get_linearized(dsdb_dn->dn),
1057 : link_guid_str);
1058 0 : talloc_free(tmp_ctx);
1059 0 : return LDB_ERR_OPERATIONS_ERROR;
1060 : }
1061 :
1062 9603 : msg = res->msgs[0];
1063 :
1064 9603 : if (msg->num_elements == 0) {
1065 : /* Forward link without backlink remaining - nothing to do here */
1066 55 : continue;
1067 9548 : } else if (msg->num_elements != 1) {
1068 0 : ldb_asprintf_errstring(ldb, "Bad msg elements - got %u elements, expected one element to be returned in linked_attributes_fix_links for %s",
1069 : msg->num_elements, ldb_dn_get_linearized(msg->dn));
1070 0 : talloc_free(tmp_ctx);
1071 0 : return LDB_ERR_OPERATIONS_ERROR;
1072 : }
1073 9548 : if (ldb_attr_cmp(msg->elements[0].name, target->lDAPDisplayName) != 0) {
1074 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));
1075 0 : talloc_free(tmp_ctx);
1076 0 : return LDB_ERR_OPERATIONS_ERROR;
1077 : }
1078 9548 : el2 = &msg->elements[0];
1079 :
1080 9548 : el2->flags = LDB_FLAG_MOD_REPLACE;
1081 :
1082 9548 : if (target->linkID & 1 ||
1083 2720 : ! la_private->sorted_links) {
1084 : /* handle backlinks (which aren't sorted in the DB)
1085 : and forward links in old unsorted databases. */
1086 6833 : ret = linked_attributes_fix_link_slow(
1087 : module,
1088 : parent,
1089 : msg,
1090 : new_dn,
1091 : self_guid,
1092 6833 : target->syntax->ldap_oid,
1093 6833 : schema_attr->syntax->ldap_oid);
1094 : } else {
1095 : /* we can binary search to find forward links */
1096 2715 : ret = linked_attributes_fix_forward_link(
1097 : module,
1098 : msg,
1099 : new_dn,
1100 : self_guid,
1101 2715 : target->syntax->ldap_oid);
1102 : }
1103 9548 : if (ret != LDB_SUCCESS) {
1104 0 : talloc_free(tmp_ctx);
1105 0 : return ret;
1106 : }
1107 9548 : ret = dsdb_check_single_valued_link(target, el2);
1108 9548 : if (ret != LDB_SUCCESS) {
1109 0 : talloc_free(tmp_ctx);
1110 0 : return ret;
1111 : }
1112 :
1113 : /* we may be putting multiple values in an attribute -
1114 : disable checking for this attribute */
1115 9548 : el2->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
1116 :
1117 9548 : ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE, parent);
1118 9548 : if (ret != LDB_SUCCESS) {
1119 0 : ldb_asprintf_errstring(ldb, "Linked attribute %s->%s between %s and %s - update failed - %s",
1120 0 : el->name, target->lDAPDisplayName,
1121 : ldb_dn_get_linearized(old_dn),
1122 : ldb_dn_get_linearized(dsdb_dn->dn),
1123 : ldb_errstring(ldb));
1124 0 : talloc_free(tmp_ctx);
1125 0 : return ret;
1126 : }
1127 : }
1128 :
1129 5653 : talloc_free(tmp_ctx);
1130 5653 : return LDB_SUCCESS;
1131 : }
1132 :
1133 :
1134 : /* rename */
1135 80793 : static int linked_attributes_rename(struct ldb_module *module, struct ldb_request *req)
1136 : {
1137 151 : struct ldb_result *res;
1138 151 : struct ldb_message *msg;
1139 151 : unsigned int i;
1140 80793 : struct ldb_context *ldb = ldb_module_get_ctx(module);
1141 151 : struct dsdb_schema *schema;
1142 151 : int ret;
1143 151 : struct GUID guid;
1144 :
1145 : /*
1146 : - load the current msg
1147 : - find any linked attributes
1148 : - if its a link then find the target object
1149 : - modify the target linked attributes with the new DN
1150 : */
1151 80793 : ret = dsdb_module_search_dn(module, req, &res, req->op.rename.olddn,
1152 : NULL,
1153 : DSDB_FLAG_NEXT_MODULE |
1154 : DSDB_SEARCH_SHOW_EXTENDED_DN |
1155 : DSDB_SEARCH_SHOW_RECYCLED, req);
1156 80793 : if (ret != LDB_SUCCESS) {
1157 0 : return ret;
1158 : }
1159 :
1160 80793 : schema = dsdb_get_schema(ldb, res);
1161 80793 : if (!schema) {
1162 0 : return ldb_oom(ldb);
1163 : }
1164 :
1165 80793 : msg = res->msgs[0];
1166 :
1167 80793 : ret = la_guid_from_dn(module, req, msg->dn, &guid);
1168 80793 : if (ret != LDB_SUCCESS) {
1169 0 : return ret;
1170 : }
1171 :
1172 1741181 : for (i=0; i<msg->num_elements; i++) {
1173 1660388 : struct ldb_message_element *el = &msg->elements[i];
1174 4315 : const struct dsdb_attribute *schema_attr
1175 1660388 : = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
1176 1660388 : if (!schema_attr || schema_attr->linkID == 0) {
1177 1654236 : continue;
1178 : }
1179 6152 : ret = linked_attributes_fix_links(module, guid, msg->dn, req->op.rename.newdn, el,
1180 : schema, schema_attr, req);
1181 6152 : if (ret != LDB_SUCCESS) {
1182 0 : talloc_free(res);
1183 0 : return ret;
1184 : }
1185 : }
1186 :
1187 80793 : talloc_free(res);
1188 :
1189 80793 : return ldb_next_request(module, req);
1190 : }
1191 :
1192 :
1193 : /* queue a linked attributes modify request in the la_private
1194 : structure */
1195 2194 : static int la_queue_mod_request(struct la_context *ac)
1196 : {
1197 0 : struct la_private *la_private =
1198 2194 : talloc_get_type(ldb_module_get_private(ac->module),
1199 : struct la_private);
1200 :
1201 2194 : if (la_private == NULL || la_private->transaction == NULL) {
1202 0 : ldb_debug(ldb_module_get_ctx(ac->module),
1203 : LDB_DEBUG_ERROR,
1204 : __location__ ": No la_private transaction setup\n");
1205 0 : return ldb_operr(ldb_module_get_ctx(ac->module));
1206 : }
1207 :
1208 2194 : talloc_steal(la_private->transaction, ac);
1209 2194 : DLIST_ADD(la_private->transaction->la_list, ac);
1210 :
1211 2194 : return ldb_module_done(ac->req, ac->op_controls,
1212 : ac->op_response, LDB_SUCCESS);
1213 : }
1214 :
1215 : /* Having done the original operation, then try to fix up all the linked attributes for modify and delete */
1216 2035 : static int la_mod_del_callback(struct ldb_request *req, struct ldb_reply *ares)
1217 : {
1218 0 : struct la_context *ac;
1219 0 : struct ldb_context *ldb;
1220 0 : int ret;
1221 :
1222 2035 : ac = talloc_get_type(req->context, struct la_context);
1223 2035 : ldb = ldb_module_get_ctx(ac->module);
1224 :
1225 2035 : if (!ares) {
1226 0 : return ldb_module_done(ac->req, NULL, NULL,
1227 : LDB_ERR_OPERATIONS_ERROR);
1228 : }
1229 2035 : if (ares->error != LDB_SUCCESS) {
1230 2 : return ldb_module_done(ac->req, ares->controls,
1231 : ares->response, ares->error);
1232 : }
1233 :
1234 2033 : if (ares->type != LDB_REPLY_DONE) {
1235 0 : ldb_set_errstring(ldb,
1236 : "invalid reply type in linked attributes delete callback");
1237 0 : talloc_free(ares);
1238 0 : return ldb_module_done(ac->req, NULL, NULL,
1239 : LDB_ERR_OPERATIONS_ERROR);
1240 : }
1241 :
1242 2033 : ac->op_controls = talloc_steal(ac, ares->controls);
1243 2033 : ac->op_response = talloc_steal(ac, ares->response);
1244 :
1245 : /* If we have modifies to make, this is the time to do them for modify and delete */
1246 2033 : ret = la_queue_mod_request(ac);
1247 :
1248 2033 : if (ret != LDB_SUCCESS) {
1249 0 : return ldb_module_done(ac->req, NULL, NULL, ret);
1250 : }
1251 2033 : talloc_free(ares);
1252 :
1253 : /* la_queue_mod_request has already sent the callbacks */
1254 2033 : return LDB_SUCCESS;
1255 :
1256 : }
1257 :
1258 : /* Having done the original add, then try to fix up all the linked attributes
1259 :
1260 : This is done after the add so the links can get the extended DNs correctly.
1261 : */
1262 161 : static int la_add_callback(struct ldb_request *req, struct ldb_reply *ares)
1263 : {
1264 0 : struct la_context *ac;
1265 0 : struct ldb_context *ldb;
1266 0 : int ret;
1267 :
1268 161 : ac = talloc_get_type(req->context, struct la_context);
1269 161 : ldb = ldb_module_get_ctx(ac->module);
1270 :
1271 161 : if (!ares) {
1272 0 : return ldb_module_done(ac->req, NULL, NULL,
1273 : LDB_ERR_OPERATIONS_ERROR);
1274 : }
1275 161 : if (ares->error != LDB_SUCCESS) {
1276 0 : return ldb_module_done(ac->req, ares->controls,
1277 : ares->response, ares->error);
1278 : }
1279 :
1280 161 : if (ares->type != LDB_REPLY_DONE) {
1281 0 : ldb_set_errstring(ldb,
1282 : "invalid reply type in linked attributes add callback");
1283 0 : talloc_free(ares);
1284 0 : return ldb_module_done(ac->req, NULL, NULL,
1285 : LDB_ERR_OPERATIONS_ERROR);
1286 : }
1287 :
1288 161 : if (ac->ops) {
1289 0 : struct ldb_request *search_req;
1290 0 : static const char *attrs[] = { NULL };
1291 :
1292 : /* The callback does all the hard work here - we need
1293 : * the objectGUID and SID of the added record */
1294 161 : ret = ldb_build_search_req(&search_req, ldb, ac,
1295 161 : ac->req->op.add.message->dn,
1296 : LDB_SCOPE_BASE,
1297 : "(objectClass=*)", attrs,
1298 : NULL,
1299 : ac, la_mod_search_callback,
1300 : ac->req);
1301 161 : LDB_REQ_SET_LOCATION(search_req);
1302 :
1303 161 : if (ret == LDB_SUCCESS) {
1304 161 : ret = dsdb_request_add_controls(search_req,
1305 : DSDB_SEARCH_SHOW_RECYCLED |
1306 : DSDB_SEARCH_SHOW_EXTENDED_DN);
1307 : }
1308 161 : if (ret != LDB_SUCCESS) {
1309 0 : return ldb_module_done(ac->req, NULL, NULL,
1310 : ret);
1311 : }
1312 :
1313 161 : ac->op_controls = talloc_steal(ac, ares->controls);
1314 161 : ac->op_response = talloc_steal(ac, ares->response);
1315 :
1316 161 : return ldb_next_request(ac->module, search_req);
1317 :
1318 : } else {
1319 0 : return ldb_module_done(ac->req, ares->controls,
1320 : ares->response, ares->error);
1321 : }
1322 : }
1323 :
1324 : /* Reconstruct the original request, but pointing at our local callback to finish things off */
1325 2196 : static int la_down_req(struct la_context *ac)
1326 : {
1327 0 : struct ldb_request *down_req;
1328 0 : struct ldb_context *ldb;
1329 0 : int ret;
1330 :
1331 2196 : ldb = ldb_module_get_ctx(ac->module);
1332 :
1333 2196 : switch (ac->req->operation) {
1334 161 : case LDB_ADD:
1335 161 : ret = ldb_build_add_req(&down_req, ldb, ac,
1336 161 : ac->req->op.add.message,
1337 161 : ac->req->controls,
1338 : ac, la_add_callback,
1339 : ac->req);
1340 161 : LDB_REQ_SET_LOCATION(down_req);
1341 161 : break;
1342 2035 : case LDB_MODIFY:
1343 2035 : ret = ldb_build_mod_req(&down_req, ldb, ac,
1344 2035 : ac->req->op.mod.message,
1345 2035 : ac->req->controls,
1346 : ac, la_mod_del_callback,
1347 : ac->req);
1348 2035 : LDB_REQ_SET_LOCATION(down_req);
1349 2035 : break;
1350 0 : default:
1351 0 : ret = LDB_ERR_OPERATIONS_ERROR;
1352 : }
1353 2196 : if (ret != LDB_SUCCESS) {
1354 0 : return ret;
1355 : }
1356 :
1357 2196 : return ldb_next_request(ac->module, down_req);
1358 : }
1359 :
1360 : /*
1361 : use the GUID part of an extended DN to find the target DN, in case
1362 : it has moved
1363 : */
1364 3340 : static int la_find_dn_target(struct ldb_module *module, struct la_context *ac,
1365 : struct GUID *guid, struct ldb_dn **dn)
1366 : {
1367 3340 : return dsdb_module_dn_by_guid(ac->module, ac, guid, dn, ac->req);
1368 : }
1369 :
1370 : /* apply one la_context op change */
1371 3340 : static int la_do_op_request(struct ldb_module *module, struct la_context *ac, struct la_op_store *op)
1372 : {
1373 0 : struct ldb_message_element *ret_el;
1374 0 : struct ldb_message *new_msg;
1375 0 : struct ldb_context *ldb;
1376 0 : int ret;
1377 :
1378 3340 : if (ac->mod_dn == NULL) {
1379 : /* we didn't find the DN that we searched for */
1380 0 : return LDB_SUCCESS;
1381 : }
1382 :
1383 3340 : ldb = ldb_module_get_ctx(ac->module);
1384 :
1385 : /* Create the modify request */
1386 3340 : new_msg = ldb_msg_new(ac);
1387 3340 : if (!new_msg) {
1388 0 : return ldb_oom(ldb);
1389 : }
1390 :
1391 3340 : ret = la_find_dn_target(module, ac, &op->guid, &new_msg->dn);
1392 3340 : if (ret != LDB_SUCCESS) {
1393 21 : return ret;
1394 : }
1395 :
1396 3319 : if (op->op == LA_OP_ADD) {
1397 1764 : ret = ldb_msg_add_empty(new_msg, op->name,
1398 : LDB_FLAG_MOD_ADD, &ret_el);
1399 : } else {
1400 1555 : ret = ldb_msg_add_empty(new_msg, op->name,
1401 : LDB_FLAG_MOD_DELETE, &ret_el);
1402 : }
1403 3319 : if (ret != LDB_SUCCESS) {
1404 0 : return ret;
1405 : }
1406 3319 : ret_el->values = talloc_array(new_msg, struct ldb_val, 1);
1407 3319 : if (!ret_el->values) {
1408 0 : return ldb_oom(ldb);
1409 : }
1410 3319 : ret_el->num_values = 1;
1411 3319 : ret_el->values[0] = data_blob_string_const(ldb_dn_get_extended_linearized(new_msg, ac->mod_dn, 1));
1412 :
1413 : /* a backlink should never be single valued. Unfortunately the
1414 : exchange schema has a attribute
1415 : msExchBridgeheadedLocalConnectorsDNBL which is single
1416 : valued and a backlink. We need to cope with that by
1417 : ignoring the single value flag */
1418 3319 : ret_el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
1419 :
1420 : #if 0
1421 : ldb_debug(ldb, LDB_DEBUG_WARNING,
1422 : "link on %s %s: %s %s\n",
1423 : ldb_dn_get_linearized(new_msg->dn), ret_el->name,
1424 : ret_el->values[0].data, ac->ops->op == LA_OP_ADD ? "added" : "deleted");
1425 : #endif
1426 :
1427 3319 : if (DEBUGLVL(4)) {
1428 0 : DEBUG(4,("Applying linked attribute change:\n%s\n",
1429 : ldb_ldif_message_redacted_string(ldb, op,
1430 : LDB_CHANGETYPE_MODIFY,
1431 : new_msg)));
1432 : }
1433 :
1434 3319 : ret = dsdb_module_modify(module, new_msg, DSDB_FLAG_NEXT_MODULE, ac->req);
1435 3319 : if (ret != LDB_SUCCESS) {
1436 0 : ldb_debug(ldb, LDB_DEBUG_WARNING, __location__ ": failed to apply linked attribute change '%s'\n%s\n",
1437 : ldb_errstring(ldb),
1438 : ldb_ldif_message_redacted_string(ldb, op,
1439 : LDB_CHANGETYPE_MODIFY,
1440 : new_msg));
1441 : }
1442 :
1443 3319 : return ret;
1444 : }
1445 :
1446 : /* apply one set of la_context changes */
1447 2194 : static int la_do_mod_request(struct ldb_module *module, struct la_context *ac)
1448 : {
1449 0 : struct la_op_store *op;
1450 :
1451 5534 : for (op = ac->ops; op; op=op->next) {
1452 3340 : int ret = la_do_op_request(module, ac, op);
1453 3340 : if (ret != LDB_SUCCESS) {
1454 21 : if (ret != LDB_ERR_NO_SUCH_OBJECT) {
1455 0 : return ret;
1456 : }
1457 : }
1458 : }
1459 :
1460 2194 : return LDB_SUCCESS;
1461 : }
1462 :
1463 :
1464 : /*
1465 : we hook into the transaction operations to allow us to
1466 : perform the linked attribute updates at the end of the whole
1467 : transaction. This allows a forward linked attribute to be created
1468 : before the target is created, as long as the target is created
1469 : in the same transaction
1470 : */
1471 349735 : static int linked_attributes_start_transaction(struct ldb_module *module)
1472 : {
1473 : /* create our private structure for this transaction */
1474 2179 : struct la_private *la_private =
1475 349735 : talloc_get_type(ldb_module_get_private(module),
1476 : struct la_private);
1477 :
1478 349735 : if (la_private == NULL) {
1479 0 : return ldb_oom(ldb_module_get_ctx(module));
1480 : }
1481 349735 : talloc_free(la_private->transaction);
1482 349735 : la_private->transaction = talloc(module, struct la_private_transaction);
1483 349735 : if (la_private->transaction == NULL) {
1484 0 : return ldb_oom(ldb_module_get_ctx(module));
1485 : }
1486 349735 : la_private->transaction->la_list = NULL;
1487 349735 : return ldb_next_start_trans(module);
1488 : }
1489 :
1490 : /*
1491 : on prepare commit we loop over our queued la_context structures
1492 : and apply each of them
1493 : */
1494 303807 : static int linked_attributes_prepare_commit(struct ldb_module *module)
1495 : {
1496 2174 : struct la_context *ac;
1497 2174 : struct la_private *la_private =
1498 303807 : talloc_get_type(ldb_module_get_private(module),
1499 : struct la_private);
1500 303807 : if (la_private == NULL || la_private->transaction == NULL) {
1501 0 : DBG_ERR("prepare_commit without begin_transaction\n");
1502 : /* prepare commit without begin_transaction - let someone else
1503 : * return the error, just don't segfault */
1504 0 : return ldb_next_prepare_commit(module);
1505 : }
1506 : /* walk the list backwards, to do the first entry first, as we
1507 : * added the entries with DLIST_ADD() which puts them at the
1508 : * start of the list */
1509 :
1510 : /* Start at the end of the list - so we can start
1511 : * there, but ensure we don't create a loop by NULLing
1512 : * it out in the first element */
1513 303807 : ac = DLIST_TAIL(la_private->transaction->la_list);
1514 :
1515 306001 : for (; ac; ac=DLIST_PREV(ac)) {
1516 0 : int ret;
1517 2194 : ac->req = NULL;
1518 2194 : ret = la_do_mod_request(module, ac);
1519 2194 : if (ret != LDB_SUCCESS) {
1520 0 : DEBUG(0,(__location__ ": Failed mod request ret=%d\n", ret));
1521 0 : TALLOC_FREE(la_private->transaction);
1522 0 : return ret;
1523 : }
1524 : }
1525 :
1526 303807 : TALLOC_FREE(la_private->transaction);
1527 :
1528 303807 : return ldb_next_prepare_commit(module);
1529 : }
1530 :
1531 45493 : static int linked_attributes_del_transaction(struct ldb_module *module)
1532 : {
1533 4 : struct la_private *la_private =
1534 45493 : talloc_get_type(ldb_module_get_private(module),
1535 : struct la_private);
1536 45493 : TALLOC_FREE(la_private->transaction);
1537 45493 : return ldb_next_del_trans(module);
1538 : }
1539 :
1540 182004 : static int linked_attributes_ldb_init(struct ldb_module *module)
1541 : {
1542 6016 : int ret;
1543 182004 : struct la_private *la_private = NULL;
1544 182004 : struct ldb_context *ldb = ldb_module_get_ctx(module);
1545 :
1546 182004 : ret = ldb_mod_register_control(module, LDB_CONTROL_VERIFY_NAME_OID);
1547 182004 : if (ret != LDB_SUCCESS) {
1548 0 : ldb_debug(ldb_module_get_ctx(module), LDB_DEBUG_ERROR,
1549 : "verify_name: Unable to register control with rootdse!\n");
1550 0 : return ldb_operr(ldb_module_get_ctx(module));
1551 : }
1552 :
1553 182004 : la_private = talloc_zero(module, struct la_private);
1554 182004 : if (la_private == NULL) {
1555 0 : ldb_oom(ldb);
1556 0 : return LDB_ERR_OPERATIONS_ERROR;
1557 : }
1558 :
1559 182004 : ret = dsdb_check_samba_compatible_feature(module,
1560 : SAMBA_SORTED_LINKS_FEATURE,
1561 : &la_private->sorted_links);
1562 182004 : if (ret != LDB_SUCCESS) {
1563 0 : talloc_free(la_private);
1564 0 : return ret;
1565 : }
1566 :
1567 182004 : ldb_module_set_private(module, la_private);
1568 182004 : return ldb_next_init(module);
1569 : }
1570 :
1571 :
1572 : static const struct ldb_module_ops ldb_linked_attributes_module_ops = {
1573 : .name = "linked_attributes",
1574 : .add = linked_attributes_add,
1575 : .modify = linked_attributes_modify,
1576 : .rename = linked_attributes_rename,
1577 : .init_context = linked_attributes_ldb_init,
1578 : .start_transaction = linked_attributes_start_transaction,
1579 : .prepare_commit = linked_attributes_prepare_commit,
1580 : .del_transaction = linked_attributes_del_transaction,
1581 : };
1582 :
1583 6040 : int ldb_linked_attributes_module_init(const char *version)
1584 : {
1585 6040 : LDB_MODULE_CHECK_VERSION(version);
1586 6040 : return ldb_register_module(&ldb_linked_attributes_module_ops);
1587 : }
|