1 /*
2 ldb database library
3
4 Copyright (C) Simo Sorce 2005-2008
5 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2007-2008
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 /*
22 * Name: ldb
23 *
24 * Component: ldb extended dn control module
25 *
26 * Description: this module interprets DNs of the form <SID=S-1-2-4456> into normal DNs.
27 *
28 * Authors: Simo Sorce
29 * Andrew Bartlett
30 */
31
32 #include "includes.h"
33 #include <ldb.h>
34 #include <ldb_errors.h>
35 #include <ldb_module.h>
36 #include "dsdb/samdb/samdb.h"
37 #include "dsdb/samdb/ldb_modules/util.h"
38 #include "lib/ldb-samba/ldb_matching_rules.h"
39
40 /*
41 TODO: if relax is not set then we need to reject the fancy RMD_* and
42 DELETED extended DN codes
43 */
44
45 /* search */
46 struct extended_search_context {
47 struct ldb_module *module;
48 struct ldb_request *req;
49 struct ldb_dn *basedn;
50 struct ldb_dn *dn;
51 char *wellknown_object;
52 int extended_type;
53 };
54
55 static const char *wkattr[] = {
56 "wellKnownObjects",
57 "otherWellKnownObjects",
58 NULL
59 };
60
61 static const struct ldb_module_ops ldb_extended_dn_in_openldap_module_ops;
62
63 /* An extra layer of indirection because LDB does not allow the original request to be altered */
64
extended_final_callback(struct ldb_request * req,struct ldb_reply * ares)65 static int extended_final_callback(struct ldb_request *req, struct ldb_reply *ares)
66 {
67 int ret = LDB_ERR_OPERATIONS_ERROR;
68 struct extended_search_context *ac;
69 ac = talloc_get_type(req->context, struct extended_search_context);
70
71 if (ares->error != LDB_SUCCESS) {
72 ret = ldb_module_done(ac->req, ares->controls,
73 ares->response, ares->error);
74 } else {
75 switch (ares->type) {
76 case LDB_REPLY_ENTRY:
77
78 ret = ldb_module_send_entry(ac->req, ares->message, ares->controls);
79 break;
80 case LDB_REPLY_REFERRAL:
81
82 ret = ldb_module_send_referral(ac->req, ares->referral);
83 break;
84 case LDB_REPLY_DONE:
85
86 ret = ldb_module_done(ac->req, ares->controls,
87 ares->response, ares->error);
88 break;
89 }
90 }
91 return ret;
92 }
93
extended_base_callback(struct ldb_request * req,struct ldb_reply * ares)94 static int extended_base_callback(struct ldb_request *req, struct ldb_reply *ares)
95 {
96 struct extended_search_context *ac;
97 struct ldb_request *down_req;
98 struct ldb_message_element *el;
99 int ret;
100 unsigned int i, j;
101 size_t wkn_len = 0;
102 char *valstr = NULL;
103 const char *found = NULL;
104
105 ac = talloc_get_type(req->context, struct extended_search_context);
106
107 if (!ares) {
108 return ldb_module_done(ac->req, NULL, NULL,
109 LDB_ERR_OPERATIONS_ERROR);
110 }
111 if (ares->error != LDB_SUCCESS) {
112 return ldb_module_done(ac->req, ares->controls,
113 ares->response, ares->error);
114 }
115
116 switch (ares->type) {
117 case LDB_REPLY_ENTRY:
118 if (ac->basedn) {
119 /* we have more than one match! This can
120 happen as S-1-5-17 appears twice in a
121 normal provision. We need to return
122 NO_SUCH_OBJECT */
123 const char *str = talloc_asprintf(req, "Duplicate base-DN matches found for '%s'",
124 ldb_dn_get_extended_linearized(req, ac->dn, 1));
125 ldb_set_errstring(ldb_module_get_ctx(ac->module), str);
126 return ldb_module_done(ac->req, NULL, NULL,
127 LDB_ERR_NO_SUCH_OBJECT);
128 }
129
130 if (!ac->wellknown_object) {
131 ac->basedn = talloc_steal(ac, ares->message->dn);
132 break;
133 }
134
135 wkn_len = strlen(ac->wellknown_object);
136
137 for (j=0; wkattr[j]; j++) {
138
139 el = ldb_msg_find_element(ares->message, wkattr[j]);
140 if (!el) {
141 ac->basedn = NULL;
142 continue;
143 }
144
145 for (i=0; i < el->num_values; i++) {
146 valstr = talloc_strndup(ac,
147 (const char *)el->values[i].data,
148 el->values[i].length);
149 if (!valstr) {
150 ldb_oom(ldb_module_get_ctx(ac->module));
151 return ldb_module_done(ac->req, NULL, NULL,
152 LDB_ERR_OPERATIONS_ERROR);
153 }
154
155 if (strncasecmp(valstr, ac->wellknown_object, wkn_len) != 0) {
156 talloc_free(valstr);
157 continue;
158 }
159
160 found = &valstr[wkn_len];
161 break;
162 }
163 if (found) {
164 break;
165 }
166 }
167
168 if (!found) {
169 break;
170 }
171
172 ac->basedn = ldb_dn_new(ac, ldb_module_get_ctx(ac->module), found);
173 talloc_free(valstr);
174 if (!ac->basedn) {
175 ldb_oom(ldb_module_get_ctx(ac->module));
176 return ldb_module_done(ac->req, NULL, NULL,
177 LDB_ERR_OPERATIONS_ERROR);
178 }
179
180 break;
181
182 case LDB_REPLY_REFERRAL:
183 break;
184
185 case LDB_REPLY_DONE:
186
187 if (!ac->basedn) {
188 const char *str = talloc_asprintf(req, "Base-DN '%s' not found",
189 ldb_dn_get_extended_linearized(req, ac->dn, 1));
190 ldb_set_errstring(ldb_module_get_ctx(ac->module), str);
191 return ldb_module_done(ac->req, NULL, NULL,
192 LDB_ERR_NO_SUCH_OBJECT);
193 }
194
195 switch (ac->req->operation) {
196 case LDB_SEARCH:
197 ret = ldb_build_search_req_ex(&down_req,
198 ldb_module_get_ctx(ac->module), ac->req,
199 ac->basedn,
200 ac->req->op.search.scope,
201 ac->req->op.search.tree,
202 ac->req->op.search.attrs,
203 ac->req->controls,
204 ac, extended_final_callback,
205 ac->req);
206 LDB_REQ_SET_LOCATION(down_req);
207 break;
208 case LDB_ADD:
209 {
210 struct ldb_message *add_msg = ldb_msg_copy_shallow(ac, ac->req->op.add.message);
211 if (!add_msg) {
212 ldb_oom(ldb_module_get_ctx(ac->module));
213 return ldb_module_done(ac->req, NULL, NULL,
214 LDB_ERR_OPERATIONS_ERROR);
215 }
216
217 add_msg->dn = ac->basedn;
218
219 ret = ldb_build_add_req(&down_req,
220 ldb_module_get_ctx(ac->module), ac->req,
221 add_msg,
222 ac->req->controls,
223 ac, extended_final_callback,
224 ac->req);
225 LDB_REQ_SET_LOCATION(down_req);
226 break;
227 }
228 case LDB_MODIFY:
229 {
230 struct ldb_message *mod_msg = ldb_msg_copy_shallow(ac, ac->req->op.mod.message);
231 if (!mod_msg) {
232 ldb_oom(ldb_module_get_ctx(ac->module));
233 return ldb_module_done(ac->req, NULL, NULL,
234 LDB_ERR_OPERATIONS_ERROR);
235 }
236
237 mod_msg->dn = ac->basedn;
238
239 ret = ldb_build_mod_req(&down_req,
240 ldb_module_get_ctx(ac->module), ac->req,
241 mod_msg,
242 ac->req->controls,
243 ac, extended_final_callback,
244 ac->req);
245 LDB_REQ_SET_LOCATION(down_req);
246 break;
247 }
248 case LDB_DELETE:
249 ret = ldb_build_del_req(&down_req,
250 ldb_module_get_ctx(ac->module), ac->req,
251 ac->basedn,
252 ac->req->controls,
253 ac, extended_final_callback,
254 ac->req);
255 LDB_REQ_SET_LOCATION(down_req);
256 break;
257 case LDB_RENAME:
258 ret = ldb_build_rename_req(&down_req,
259 ldb_module_get_ctx(ac->module), ac->req,
260 ac->basedn,
261 ac->req->op.rename.newdn,
262 ac->req->controls,
263 ac, extended_final_callback,
264 ac->req);
265 LDB_REQ_SET_LOCATION(down_req);
266 break;
267 default:
268 return ldb_module_done(ac->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
269 }
270
271 if (ret != LDB_SUCCESS) {
272 return ldb_module_done(ac->req, NULL, NULL, ret);
273 }
274
275 return ldb_next_request(ac->module, down_req);
276 }
277 talloc_free(ares);
278 return LDB_SUCCESS;
279 }
280
281
282 /*
283 windows ldap searchs don't allow a baseDN with more
284 than one extended component, or an extended
285 component and a string DN
286
287 We only enforce this over ldap, not for internal
288 use, as there are just too many places where we
289 internally want to use a DN that has come from a
290 search with extended DN enabled, or comes from a DRS
291 naming context.
292
293 Enforcing this would also make debugging samba much
294 harder, as we'd need to use ldb_dn_minimise() in a
295 lot of places, and that would lose the DN string
296 which is so useful for working out what a request is
297 for
298 */
ldb_dn_match_allowed(struct ldb_dn * dn,struct ldb_request * req)299 static bool ldb_dn_match_allowed(struct ldb_dn *dn, struct ldb_request *req)
300 {
301 int num_components = ldb_dn_get_comp_num(dn);
302 int num_ex_components = ldb_dn_get_extended_comp_num(dn);
303
304 if (num_ex_components == 0) {
305 return true;
306 }
307
308 if ((num_components != 0 || num_ex_components != 1) &&
309 ldb_req_is_untrusted(req)) {
310 return false;
311 }
312 return true;
313 }
314
315
316 struct extended_dn_filter_ctx {
317 bool test_only;
318 bool matched;
319 struct ldb_module *module;
320 struct ldb_request *req;
321 struct dsdb_schema *schema;
322 uint32_t dsdb_flags;
323 };
324
325 /*
326 create a always non-matching node from a equality node
327 */
set_parse_tree_false(struct ldb_parse_tree * tree)328 static void set_parse_tree_false(struct ldb_parse_tree *tree)
329 {
330 const char *attr = tree->u.equality.attr;
331 struct ldb_val value = tree->u.equality.value;
332 tree->operation = LDB_OP_EXTENDED;
333 tree->u.extended.attr = attr;
334 tree->u.extended.value = value;
335 tree->u.extended.rule_id = SAMBA_LDAP_MATCH_ALWAYS_FALSE;
336 tree->u.extended.dnAttributes = 0;
337 }
338
339 /*
340 called on all nodes in the parse tree
341 */
extended_dn_filter_callback(struct ldb_parse_tree * tree,void * private_context)342 static int extended_dn_filter_callback(struct ldb_parse_tree *tree, void *private_context)
343 {
344 struct extended_dn_filter_ctx *filter_ctx;
345 int ret;
346 struct ldb_dn *dn = NULL;
347 const struct ldb_val *sid_val, *guid_val;
348 const char *no_attrs[] = { NULL };
349 struct ldb_result *res;
350 const struct dsdb_attribute *attribute = NULL;
351 bool has_extended_component = false;
352 enum ldb_scope scope;
353 struct ldb_dn *base_dn;
354 const char *expression;
355 uint32_t dsdb_flags;
356
357 if (tree->operation != LDB_OP_EQUALITY && tree->operation != LDB_OP_EXTENDED) {
358 return LDB_SUCCESS;
359 }
360
361 filter_ctx = talloc_get_type_abort(private_context, struct extended_dn_filter_ctx);
362
363 if (filter_ctx->test_only && filter_ctx->matched) {
364 /* the tree already matched */
365 return LDB_SUCCESS;
366 }
367
368 if (!filter_ctx->schema) {
369 /* Schema not setup yet */
370 return LDB_SUCCESS;
371 }
372 if (tree->operation == LDB_OP_EQUALITY) {
373 attribute = dsdb_attribute_by_lDAPDisplayName(filter_ctx->schema, tree->u.equality.attr);
374 } else if (tree->operation == LDB_OP_EXTENDED) {
375 attribute = dsdb_attribute_by_lDAPDisplayName(filter_ctx->schema, tree->u.extended.attr);
376 }
377 if (attribute == NULL) {
378 return LDB_SUCCESS;
379 }
380
381 if (attribute->dn_format != DSDB_NORMAL_DN) {
382 return LDB_SUCCESS;
383 }
384
385 if (tree->operation == LDB_OP_EQUALITY) {
386 has_extended_component = (memchr(tree->u.equality.value.data, '<',
387 tree->u.equality.value.length) != NULL);
388 } else if (tree->operation == LDB_OP_EXTENDED) {
389 has_extended_component = (memchr(tree->u.extended.value.data, '<',
390 tree->u.extended.value.length) != NULL);
391 }
392
393 /*
394 * Don't turn it into an extended DN if we're talking to OpenLDAP.
395 * We just check the module_ops pointer instead of adding a private
396 * pointer and a boolean to tell us the exact same thing.
397 */
398 if (!has_extended_component) {
399 if (!attribute->one_way_link) {
400 return LDB_SUCCESS;
401 }
402
403 if (ldb_module_get_ops(filter_ctx->module) == &ldb_extended_dn_in_openldap_module_ops) {
404 return LDB_SUCCESS;
405 }
406 }
407
408 if (tree->operation == LDB_OP_EQUALITY) {
409 dn = ldb_dn_from_ldb_val(filter_ctx, ldb_module_get_ctx(filter_ctx->module), &tree->u.equality.value);
410 } else if (tree->operation == LDB_OP_EXTENDED
411 && (strcmp(tree->u.extended.rule_id, SAMBA_LDAP_MATCH_RULE_TRANSITIVE_EVAL) == 0)) {
412 dn = ldb_dn_from_ldb_val(filter_ctx, ldb_module_get_ctx(filter_ctx->module), &tree->u.extended.value);
413 }
414 if (dn == NULL) {
415 /* testing against windows shows that we don't raise
416 an error here */
417 return LDB_SUCCESS;
418 }
419
420 guid_val = ldb_dn_get_extended_component(dn, "GUID");
421 sid_val = ldb_dn_get_extended_component(dn, "SID");
422
423 if (!guid_val && !sid_val && (attribute->searchFlags & SEARCH_FLAG_ATTINDEX)) {
424 /* if it is indexed, then fixing the string DN will do
425 no good here, as we will not find the attribute in
426 the index. So for now fall through to a standard DN
427 component comparison */
428 return LDB_SUCCESS;
429 }
430
431 if (filter_ctx->test_only) {
432 /* we need to copy the tree */
433 filter_ctx->matched = true;
434 return LDB_SUCCESS;
435 }
436
437 if (!ldb_dn_match_allowed(dn, filter_ctx->req)) {
438 /* we need to make this element of the filter always
439 be false */
440 set_parse_tree_false(tree);
441 return LDB_SUCCESS;
442 }
443
444 dsdb_flags = filter_ctx->dsdb_flags | DSDB_FLAG_NEXT_MODULE;
445
446 if (guid_val) {
447 expression = talloc_asprintf(filter_ctx, "objectGUID=%s", ldb_binary_encode(filter_ctx, *guid_val));
448 scope = LDB_SCOPE_SUBTREE;
449 base_dn = NULL;
450 dsdb_flags |= DSDB_SEARCH_SEARCH_ALL_PARTITIONS;
451 } else if (sid_val) {
452 expression = talloc_asprintf(filter_ctx, "objectSID=%s", ldb_binary_encode(filter_ctx, *sid_val));
453 scope = LDB_SCOPE_SUBTREE;
454 base_dn = NULL;
455 dsdb_flags |= DSDB_SEARCH_SEARCH_ALL_PARTITIONS;
456 } else {
457 /* fallback to searching using the string DN as the base DN */
458 expression = "objectClass=*";
459 base_dn = dn;
460 scope = LDB_SCOPE_BASE;
461 }
462
463 ret = dsdb_module_search(filter_ctx->module,
464 filter_ctx,
465 &res,
466 base_dn,
467 scope,
468 no_attrs,
469 dsdb_flags,
470 filter_ctx->req,
471 "%s", expression);
472 if (scope == LDB_SCOPE_BASE && ret == LDB_ERR_NO_SUCH_OBJECT) {
473 /* note that this will need to change for multi-domain
474 support */
475 set_parse_tree_false(tree);
476 return LDB_SUCCESS;
477 }
478
479 if (ret != LDB_SUCCESS) {
480 return LDB_SUCCESS;
481 }
482
483
484 if (res->count != 1) {
485 return LDB_SUCCESS;
486 }
487
488 /* replace the search expression element with the matching DN */
489 if (tree->operation == LDB_OP_EQUALITY) {
490 tree->u.equality.value.data =
491 (uint8_t *)talloc_strdup(tree, ldb_dn_get_extended_linearized(tree, res->msgs[0]->dn, 1));
492 if (tree->u.equality.value.data == NULL) {
493 return ldb_oom(ldb_module_get_ctx(filter_ctx->module));
494 }
495 tree->u.equality.value.length = strlen((const char *)tree->u.equality.value.data);
496 } else if (tree->operation == LDB_OP_EXTENDED) {
497 tree->u.extended.value.data =
498 (uint8_t *)talloc_strdup(tree, ldb_dn_get_extended_linearized(tree, res->msgs[0]->dn, 1));
499 if (tree->u.extended.value.data == NULL) {
500 return ldb_oom(ldb_module_get_ctx(filter_ctx->module));
501 }
502 tree->u.extended.value.length = strlen((const char *)tree->u.extended.value.data);
503 }
504 talloc_free(res);
505
506 filter_ctx->matched = true;
507 return LDB_SUCCESS;
508 }
509
510 /*
511 fix the parse tree to change any extended DN components to their
512 caconical form
513 */
extended_dn_fix_filter(struct ldb_module * module,struct ldb_request * req,uint32_t default_dsdb_flags)514 static int extended_dn_fix_filter(struct ldb_module *module,
515 struct ldb_request *req,
516 uint32_t default_dsdb_flags)
517 {
518 struct extended_dn_filter_ctx *filter_ctx;
519 int ret;
520
521 filter_ctx = talloc_zero(req, struct extended_dn_filter_ctx);
522 if (filter_ctx == NULL) {
523 return ldb_module_oom(module);
524 }
525
526 /* first pass through the existing tree to see if anything
527 needs to be modified. Filtering DNs on the input side is rare,
528 so this avoids copying the parse tree in most cases */
529 filter_ctx->test_only = true;
530 filter_ctx->matched = false;
531 filter_ctx->module = module;
532 filter_ctx->req = req;
533 filter_ctx->schema = dsdb_get_schema(ldb_module_get_ctx(module), filter_ctx);
534 filter_ctx->dsdb_flags= default_dsdb_flags;
535
536 ret = ldb_parse_tree_walk(req->op.search.tree, extended_dn_filter_callback, filter_ctx);
537 if (ret != LDB_SUCCESS) {
538 talloc_free(filter_ctx);
539 return ret;
540 }
541
542 if (!filter_ctx->matched) {
543 /* nothing matched, no need for a new parse tree */
544 talloc_free(filter_ctx);
545 return LDB_SUCCESS;
546 }
547
548 filter_ctx->test_only = false;
549 filter_ctx->matched = false;
550
551 req->op.search.tree = ldb_parse_tree_copy_shallow(req, req->op.search.tree);
552 if (req->op.search.tree == NULL) {
553 return ldb_oom(ldb_module_get_ctx(module));
554 }
555
556 ret = ldb_parse_tree_walk(req->op.search.tree, extended_dn_filter_callback, filter_ctx);
557 if (ret != LDB_SUCCESS) {
558 talloc_free(filter_ctx);
559 return ret;
560 }
561
562 talloc_free(filter_ctx);
563 return LDB_SUCCESS;
564 }
565
566 /*
567 fix DNs and filter expressions to cope with the semantics of
568 extended DNs
569 */
extended_dn_in_fix(struct ldb_module * module,struct ldb_request * req,struct ldb_dn * dn)570 static int extended_dn_in_fix(struct ldb_module *module, struct ldb_request *req, struct ldb_dn *dn)
571 {
572 struct extended_search_context *ac;
573 struct ldb_request *down_req;
574 int ret;
575 struct ldb_dn *base_dn = NULL;
576 enum ldb_scope base_dn_scope = LDB_SCOPE_BASE;
577 const char *base_dn_filter = NULL;
578 const char * const *base_dn_attrs = NULL;
579 char *wellknown_object = NULL;
580 static const char *no_attr[] = {
581 NULL
582 };
583 uint32_t dsdb_flags = DSDB_FLAG_AS_SYSTEM | DSDB_SEARCH_SHOW_EXTENDED_DN;
584
585 if (ldb_request_get_control(req, LDB_CONTROL_SHOW_DELETED_OID)) {
586 dsdb_flags |= DSDB_SEARCH_SHOW_DELETED;
587 }
588 if (ldb_request_get_control(req, LDB_CONTROL_SHOW_RECYCLED_OID)) {
589 dsdb_flags |= DSDB_SEARCH_SHOW_RECYCLED;
590 }
591 if (ldb_request_get_control(req, DSDB_CONTROL_DBCHECK)) {
592 dsdb_flags |= DSDB_SEARCH_SHOW_RECYCLED;
593 }
594
595 if (req->operation == LDB_SEARCH) {
596 ret = extended_dn_fix_filter(module, req, dsdb_flags);
597 if (ret != LDB_SUCCESS) {
598 return ret;
599 }
600 }
601
602 if (!ldb_dn_has_extended(dn)) {
603 /* Move along there isn't anything to see here */
604 return ldb_next_request(module, req);
605 } else {
606 /* It looks like we need to map the DN */
607 const struct ldb_val *sid_val, *guid_val, *wkguid_val;
608
609 if (!ldb_dn_match_allowed(dn, req)) {
610 return ldb_error(ldb_module_get_ctx(module),
611 LDB_ERR_INVALID_DN_SYNTAX, "invalid number of DN components");
612 }
613
614 sid_val = ldb_dn_get_extended_component(dn, "SID");
615 guid_val = ldb_dn_get_extended_component(dn, "GUID");
616 wkguid_val = ldb_dn_get_extended_component(dn, "WKGUID");
617
618 /*
619 prioritise the GUID - we have had instances of
620 duplicate SIDs in the database in the
621 ForeignSecurityPrinciples due to provision errors
622 */
623 if (guid_val) {
624 dsdb_flags |= DSDB_SEARCH_SEARCH_ALL_PARTITIONS;
625 base_dn = NULL;
626 base_dn_filter = talloc_asprintf(req, "(objectGUID=%s)",
627 ldb_binary_encode(req, *guid_val));
628 if (!base_dn_filter) {
629 return ldb_oom(ldb_module_get_ctx(module));
630 }
631 base_dn_scope = LDB_SCOPE_SUBTREE;
632 base_dn_attrs = no_attr;
633
634 } else if (sid_val) {
635 dsdb_flags |= DSDB_SEARCH_SEARCH_ALL_PARTITIONS;
636 base_dn = NULL;
637 base_dn_filter = talloc_asprintf(req, "(objectSid=%s)",
638 ldb_binary_encode(req, *sid_val));
639 if (!base_dn_filter) {
640 return ldb_oom(ldb_module_get_ctx(module));
641 }
642 base_dn_scope = LDB_SCOPE_SUBTREE;
643 base_dn_attrs = no_attr;
644
645 } else if (wkguid_val) {
646 char *wkguid_dup;
647 char *tail_str;
648 char *p;
649
650 wkguid_dup = talloc_strndup(req, (char *)wkguid_val->data, wkguid_val->length);
651
652 p = strchr(wkguid_dup, ',');
653 if (!p) {
654 return ldb_error(ldb_module_get_ctx(module), LDB_ERR_INVALID_DN_SYNTAX,
655 "Invalid WKGUID format");
656 }
657
658 p[0] = '\0';
659 p++;
660
661 wellknown_object = talloc_asprintf(req, "B:32:%s:", wkguid_dup);
662 if (!wellknown_object) {
663 return ldb_oom(ldb_module_get_ctx(module));
664 }
665
666 tail_str = p;
667
668 base_dn = ldb_dn_new(req, ldb_module_get_ctx(module), tail_str);
669 talloc_free(wkguid_dup);
670 if (!base_dn) {
671 return ldb_oom(ldb_module_get_ctx(module));
672 }
673 base_dn_filter = talloc_strdup(req, "(objectClass=*)");
674 if (!base_dn_filter) {
675 return ldb_oom(ldb_module_get_ctx(module));
676 }
677 base_dn_scope = LDB_SCOPE_BASE;
678 base_dn_attrs = wkattr;
679 } else {
680 return ldb_error(ldb_module_get_ctx(module), LDB_ERR_INVALID_DN_SYNTAX,
681 "Invalid extended DN component");
682 }
683
684 ac = talloc_zero(req, struct extended_search_context);
685 if (ac == NULL) {
686 return ldb_oom(ldb_module_get_ctx(module));
687 }
688
689 ac->module = module;
690 ac->req = req;
691 ac->dn = dn;
692 ac->basedn = NULL; /* Filled in if the search finds the DN by SID/GUID etc */
693 ac->wellknown_object = wellknown_object;
694
695 /* If the base DN was an extended DN (perhaps a well known
696 * GUID) then search for that, so we can proceed with the original operation */
697
698 ret = ldb_build_search_req(&down_req,
699 ldb_module_get_ctx(module), ac,
700 base_dn,
701 base_dn_scope,
702 base_dn_filter,
703 base_dn_attrs,
704 NULL,
705 ac, extended_base_callback,
706 req);
707 LDB_REQ_SET_LOCATION(down_req);
708 if (ret != LDB_SUCCESS) {
709 return ldb_operr(ldb_module_get_ctx(module));
710 }
711
712 ret = dsdb_request_add_controls(down_req, dsdb_flags);
713 if (ret != LDB_SUCCESS) {
714 return ret;
715 }
716
717 /* perform the search */
718 return ldb_next_request(module, down_req);
719 }
720 }
721
extended_dn_in_search(struct ldb_module * module,struct ldb_request * req)722 static int extended_dn_in_search(struct ldb_module *module, struct ldb_request *req)
723 {
724 return extended_dn_in_fix(module, req, req->op.search.base);
725 }
726
extended_dn_in_modify(struct ldb_module * module,struct ldb_request * req)727 static int extended_dn_in_modify(struct ldb_module *module, struct ldb_request *req)
728 {
729 return extended_dn_in_fix(module, req, req->op.mod.message->dn);
730 }
731
extended_dn_in_del(struct ldb_module * module,struct ldb_request * req)732 static int extended_dn_in_del(struct ldb_module *module, struct ldb_request *req)
733 {
734 return extended_dn_in_fix(module, req, req->op.del.dn);
735 }
736
extended_dn_in_rename(struct ldb_module * module,struct ldb_request * req)737 static int extended_dn_in_rename(struct ldb_module *module, struct ldb_request *req)
738 {
739 return extended_dn_in_fix(module, req, req->op.rename.olddn);
740 }
741
742 static const struct ldb_module_ops ldb_extended_dn_in_module_ops = {
743 .name = "extended_dn_in",
744 .search = extended_dn_in_search,
745 .modify = extended_dn_in_modify,
746 .del = extended_dn_in_del,
747 .rename = extended_dn_in_rename,
748 };
749
750 static const struct ldb_module_ops ldb_extended_dn_in_openldap_module_ops = {
751 .name = "extended_dn_in_openldap",
752 .search = extended_dn_in_search,
753 .modify = extended_dn_in_modify,
754 .del = extended_dn_in_del,
755 .rename = extended_dn_in_rename,
756 };
757
ldb_extended_dn_in_module_init(const char * version)758 int ldb_extended_dn_in_module_init(const char *version)
759 {
760 int ret;
761 LDB_MODULE_CHECK_VERSION(version);
762 ret = ldb_register_module(&ldb_extended_dn_in_openldap_module_ops);
763 if (ret != LDB_SUCCESS) {
764 return ret;
765 }
766 return ldb_register_module(&ldb_extended_dn_in_module_ops);
767 }
768