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