s4:dsdb/extended_dn_in: don't force DSDB_SEARCH_SHOW_RECYCLED
[Samba.git] / source4 / dsdb / samdb / ldb_modules / extended_dn_in.c
blob213b2c2c87b81ad85645e9be33c2d24781db10f3
1 /*
2 ldb database library
4 Copyright (C) Simo Sorce 2005-2008
5 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2007-2008
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.
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.
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/>.
22 * Name: ldb
24 * Component: ldb extended dn control module
26 * Description: this module interprets DNs of the form <SID=S-1-2-4456> into normal DNs.
28 * Authors: Simo Sorce
29 * Andrew Bartlett
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"
40 TODO: if relax is not set then we need to reject the fancy RMD_* and
41 DELETED extended DN codes
44 /* search */
45 struct extended_search_context {
46 struct ldb_module *module;
47 struct ldb_request *req;
48 struct ldb_dn *basedn;
49 struct ldb_dn *dn;
50 char *wellknown_object;
51 int extended_type;
54 static const char *wkattr[] = {
55 "wellKnownObjects",
56 "otherWellKnownObjects",
57 NULL
59 /* An extra layer of indirection because LDB does not allow the original request to be altered */
61 static int extended_final_callback(struct ldb_request *req, struct ldb_reply *ares)
63 int ret = LDB_ERR_OPERATIONS_ERROR;
64 struct extended_search_context *ac;
65 ac = talloc_get_type(req->context, struct extended_search_context);
67 if (ares->error != LDB_SUCCESS) {
68 ret = ldb_module_done(ac->req, ares->controls,
69 ares->response, ares->error);
70 } else {
71 switch (ares->type) {
72 case LDB_REPLY_ENTRY:
74 ret = ldb_module_send_entry(ac->req, ares->message, ares->controls);
75 break;
76 case LDB_REPLY_REFERRAL:
78 ret = ldb_module_send_referral(ac->req, ares->referral);
79 break;
80 case LDB_REPLY_DONE:
82 ret = ldb_module_done(ac->req, ares->controls,
83 ares->response, ares->error);
84 break;
87 return ret;
90 static int extended_base_callback(struct ldb_request *req, struct ldb_reply *ares)
92 struct extended_search_context *ac;
93 struct ldb_request *down_req;
94 struct ldb_message_element *el;
95 int ret;
96 unsigned int i, j;
97 size_t wkn_len = 0;
98 char *valstr = NULL;
99 const char *found = NULL;
101 ac = talloc_get_type(req->context, struct extended_search_context);
103 if (!ares) {
104 return ldb_module_done(ac->req, NULL, NULL,
105 LDB_ERR_OPERATIONS_ERROR);
107 if (ares->error != LDB_SUCCESS) {
108 return ldb_module_done(ac->req, ares->controls,
109 ares->response, ares->error);
112 switch (ares->type) {
113 case LDB_REPLY_ENTRY:
114 if (ac->basedn) {
115 /* we have more than one match! This can
116 happen as S-1-5-17 appears twice in a
117 normal provision. We need to return
118 NO_SUCH_OBJECT */
119 const char *str = talloc_asprintf(req, "Duplicate base-DN matches found for '%s'",
120 ldb_dn_get_extended_linearized(req, ac->dn, 1));
121 ldb_set_errstring(ldb_module_get_ctx(ac->module), str);
122 return ldb_module_done(ac->req, NULL, NULL,
123 LDB_ERR_NO_SUCH_OBJECT);
126 if (!ac->wellknown_object) {
127 ac->basedn = talloc_steal(ac, ares->message->dn);
128 break;
131 wkn_len = strlen(ac->wellknown_object);
133 for (j=0; wkattr[j]; j++) {
135 el = ldb_msg_find_element(ares->message, wkattr[j]);
136 if (!el) {
137 ac->basedn = NULL;
138 continue;
141 for (i=0; i < el->num_values; i++) {
142 valstr = talloc_strndup(ac,
143 (const char *)el->values[i].data,
144 el->values[i].length);
145 if (!valstr) {
146 ldb_oom(ldb_module_get_ctx(ac->module));
147 return ldb_module_done(ac->req, NULL, NULL,
148 LDB_ERR_OPERATIONS_ERROR);
151 if (strncasecmp(valstr, ac->wellknown_object, wkn_len) != 0) {
152 talloc_free(valstr);
153 continue;
156 found = &valstr[wkn_len];
157 break;
159 if (found) {
160 break;
164 if (!found) {
165 break;
168 ac->basedn = ldb_dn_new(ac, ldb_module_get_ctx(ac->module), found);
169 talloc_free(valstr);
170 if (!ac->basedn) {
171 ldb_oom(ldb_module_get_ctx(ac->module));
172 return ldb_module_done(ac->req, NULL, NULL,
173 LDB_ERR_OPERATIONS_ERROR);
176 break;
178 case LDB_REPLY_REFERRAL:
179 break;
181 case LDB_REPLY_DONE:
183 if (!ac->basedn) {
184 const char *str = talloc_asprintf(req, "Base-DN '%s' not found",
185 ldb_dn_get_extended_linearized(req, ac->dn, 1));
186 ldb_set_errstring(ldb_module_get_ctx(ac->module), str);
187 return ldb_module_done(ac->req, NULL, NULL,
188 LDB_ERR_NO_SUCH_OBJECT);
191 switch (ac->req->operation) {
192 case LDB_SEARCH:
193 ret = ldb_build_search_req_ex(&down_req,
194 ldb_module_get_ctx(ac->module), ac->req,
195 ac->basedn,
196 ac->req->op.search.scope,
197 ac->req->op.search.tree,
198 ac->req->op.search.attrs,
199 ac->req->controls,
200 ac, extended_final_callback,
201 ac->req);
202 LDB_REQ_SET_LOCATION(down_req);
203 break;
204 case LDB_ADD:
206 struct ldb_message *add_msg = ldb_msg_copy_shallow(ac, ac->req->op.add.message);
207 if (!add_msg) {
208 ldb_oom(ldb_module_get_ctx(ac->module));
209 return ldb_module_done(ac->req, NULL, NULL,
210 LDB_ERR_OPERATIONS_ERROR);
213 add_msg->dn = ac->basedn;
215 ret = ldb_build_add_req(&down_req,
216 ldb_module_get_ctx(ac->module), ac->req,
217 add_msg,
218 ac->req->controls,
219 ac, extended_final_callback,
220 ac->req);
221 LDB_REQ_SET_LOCATION(down_req);
222 break;
224 case LDB_MODIFY:
226 struct ldb_message *mod_msg = ldb_msg_copy_shallow(ac, ac->req->op.mod.message);
227 if (!mod_msg) {
228 ldb_oom(ldb_module_get_ctx(ac->module));
229 return ldb_module_done(ac->req, NULL, NULL,
230 LDB_ERR_OPERATIONS_ERROR);
233 mod_msg->dn = ac->basedn;
235 ret = ldb_build_mod_req(&down_req,
236 ldb_module_get_ctx(ac->module), ac->req,
237 mod_msg,
238 ac->req->controls,
239 ac, extended_final_callback,
240 ac->req);
241 LDB_REQ_SET_LOCATION(down_req);
242 break;
244 case LDB_DELETE:
245 ret = ldb_build_del_req(&down_req,
246 ldb_module_get_ctx(ac->module), ac->req,
247 ac->basedn,
248 ac->req->controls,
249 ac, extended_final_callback,
250 ac->req);
251 LDB_REQ_SET_LOCATION(down_req);
252 break;
253 case LDB_RENAME:
254 ret = ldb_build_rename_req(&down_req,
255 ldb_module_get_ctx(ac->module), ac->req,
256 ac->basedn,
257 ac->req->op.rename.newdn,
258 ac->req->controls,
259 ac, extended_final_callback,
260 ac->req);
261 LDB_REQ_SET_LOCATION(down_req);
262 break;
263 default:
264 return ldb_module_done(ac->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
267 if (ret != LDB_SUCCESS) {
268 return ldb_module_done(ac->req, NULL, NULL, ret);
271 return ldb_next_request(ac->module, down_req);
273 talloc_free(ares);
274 return LDB_SUCCESS;
279 windows ldap searchs don't allow a baseDN with more
280 than one extended component, or an extended
281 component and a string DN
283 We only enforce this over ldap, not for internal
284 use, as there are just too many places where we
285 internally want to use a DN that has come from a
286 search with extended DN enabled, or comes from a DRS
287 naming context.
289 Enforcing this would also make debugging samba much
290 harder, as we'd need to use ldb_dn_minimise() in a
291 lot of places, and that would lose the DN string
292 which is so useful for working out what a request is
295 static bool ldb_dn_match_allowed(struct ldb_dn *dn, struct ldb_request *req)
297 int num_components = ldb_dn_get_comp_num(dn);
298 int num_ex_components = ldb_dn_get_extended_comp_num(dn);
300 if (num_ex_components == 0) {
301 return true;
304 if ((num_components != 0 || num_ex_components != 1) &&
305 ldb_req_is_untrusted(req)) {
306 return false;
308 return true;
312 struct extended_dn_filter_ctx {
313 bool test_only;
314 bool matched;
315 struct ldb_module *module;
316 struct ldb_request *req;
317 struct dsdb_schema *schema;
318 uint32_t dsdb_flags;
322 create a always non-matching node from a equality node
324 static void set_parse_tree_false(struct ldb_parse_tree *tree)
326 const char *attr = tree->u.equality.attr;
327 struct ldb_val value = tree->u.equality.value;
328 tree->operation = LDB_OP_EXTENDED;
329 tree->u.extended.attr = attr;
330 tree->u.extended.value = value;
331 tree->u.extended.rule_id = SAMBA_LDAP_MATCH_ALWAYS_FALSE;
332 tree->u.extended.dnAttributes = 0;
336 called on all nodes in the parse tree
338 static int extended_dn_filter_callback(struct ldb_parse_tree *tree, void *private_context)
340 struct extended_dn_filter_ctx *filter_ctx;
341 int ret;
342 struct ldb_dn *dn;
343 const struct ldb_val *sid_val, *guid_val;
344 const char *no_attrs[] = { NULL };
345 struct ldb_result *res;
346 const struct dsdb_attribute *attribute;
347 bool has_extended_component;
348 enum ldb_scope scope;
349 struct ldb_dn *base_dn;
350 const char *expression;
351 uint32_t dsdb_flags;
353 if (tree->operation != LDB_OP_EQUALITY) {
354 return LDB_SUCCESS;
357 filter_ctx = talloc_get_type_abort(private_context, struct extended_dn_filter_ctx);
359 if (filter_ctx->test_only && filter_ctx->matched) {
360 /* the tree already matched */
361 return LDB_SUCCESS;
364 if (!filter_ctx->schema) {
365 /* Schema not setup yet */
366 return LDB_SUCCESS;
368 attribute = dsdb_attribute_by_lDAPDisplayName(filter_ctx->schema, tree->u.equality.attr);
369 if (attribute == NULL) {
370 return LDB_SUCCESS;
373 if (attribute->dn_format != DSDB_NORMAL_DN) {
374 return LDB_SUCCESS;
377 has_extended_component = (memchr(tree->u.equality.value.data, '<',
378 tree->u.equality.value.length) != NULL);
380 if (!attribute->one_way_link && !has_extended_component) {
381 return LDB_SUCCESS;
384 dn = ldb_dn_from_ldb_val(filter_ctx, ldb_module_get_ctx(filter_ctx->module), &tree->u.equality.value);
385 if (dn == NULL) {
386 /* testing against windows shows that we don't raise
387 an error here */
388 return LDB_SUCCESS;
391 guid_val = ldb_dn_get_extended_component(dn, "GUID");
392 sid_val = ldb_dn_get_extended_component(dn, "SID");
394 if (!guid_val && !sid_val && (attribute->searchFlags & SEARCH_FLAG_ATTINDEX)) {
395 /* if it is indexed, then fixing the string DN will do
396 no good here, as we will not find the attribute in
397 the index. So for now fall through to a standard DN
398 component comparison */
399 return LDB_SUCCESS;
402 if (filter_ctx->test_only) {
403 /* we need to copy the tree */
404 filter_ctx->matched = true;
405 return LDB_SUCCESS;
408 if (!ldb_dn_match_allowed(dn, filter_ctx->req)) {
409 /* we need to make this element of the filter always
410 be false */
411 set_parse_tree_false(tree);
412 return LDB_SUCCESS;
415 dsdb_flags = filter_ctx->dsdb_flags | DSDB_FLAG_NEXT_MODULE;
417 if (guid_val) {
418 expression = talloc_asprintf(filter_ctx, "objectGUID=%s", ldb_binary_encode(filter_ctx, *guid_val));
419 scope = LDB_SCOPE_SUBTREE;
420 base_dn = NULL;
421 dsdb_flags |= DSDB_SEARCH_SEARCH_ALL_PARTITIONS;
422 } else if (sid_val) {
423 expression = talloc_asprintf(filter_ctx, "objectSID=%s", ldb_binary_encode(filter_ctx, *sid_val));
424 scope = LDB_SCOPE_SUBTREE;
425 base_dn = NULL;
426 dsdb_flags |= DSDB_SEARCH_SEARCH_ALL_PARTITIONS;
427 } else {
428 /* fallback to searching using the string DN as the base DN */
429 expression = "objectClass=*";
430 base_dn = dn;
431 scope = LDB_SCOPE_BASE;
434 ret = dsdb_module_search(filter_ctx->module,
435 filter_ctx,
436 &res,
437 base_dn,
438 scope,
439 no_attrs,
440 dsdb_flags,
441 filter_ctx->req,
442 "%s", expression);
443 if (scope == LDB_SCOPE_BASE && ret == LDB_ERR_NO_SUCH_OBJECT) {
444 /* note that this will need to change for multi-domain
445 support */
446 set_parse_tree_false(tree);
447 return LDB_SUCCESS;
450 if (ret != LDB_SUCCESS) {
451 return LDB_SUCCESS;
455 if (res->count != 1) {
456 return LDB_SUCCESS;
459 /* replace the search expression element with the matching DN */
460 tree->u.equality.value.data = (uint8_t *)talloc_strdup(tree,
461 ldb_dn_get_extended_linearized(tree, res->msgs[0]->dn, 1));
462 if (tree->u.equality.value.data == NULL) {
463 return ldb_oom(ldb_module_get_ctx(filter_ctx->module));
465 tree->u.equality.value.length = strlen((const char *)tree->u.equality.value.data);
466 talloc_free(res);
468 filter_ctx->matched = true;
469 return LDB_SUCCESS;
473 fix the parse tree to change any extended DN components to their
474 caconical form
476 static int extended_dn_fix_filter(struct ldb_module *module,
477 struct ldb_request *req,
478 uint32_t default_dsdb_flags)
480 struct extended_dn_filter_ctx *filter_ctx;
481 int ret;
483 filter_ctx = talloc_zero(req, struct extended_dn_filter_ctx);
484 if (filter_ctx == NULL) {
485 return ldb_module_oom(module);
488 /* first pass through the existing tree to see if anything
489 needs to be modified. Filtering DNs on the input side is rare,
490 so this avoids copying the parse tree in most cases */
491 filter_ctx->test_only = true;
492 filter_ctx->matched = false;
493 filter_ctx->module = module;
494 filter_ctx->req = req;
495 filter_ctx->schema = dsdb_get_schema(ldb_module_get_ctx(module), filter_ctx);
496 filter_ctx->dsdb_flags= default_dsdb_flags;
498 ret = ldb_parse_tree_walk(req->op.search.tree, extended_dn_filter_callback, filter_ctx);
499 if (ret != LDB_SUCCESS) {
500 talloc_free(filter_ctx);
501 return ret;
504 if (!filter_ctx->matched) {
505 /* nothing matched, no need for a new parse tree */
506 talloc_free(filter_ctx);
507 return LDB_SUCCESS;
510 filter_ctx->test_only = false;
511 filter_ctx->matched = false;
513 req->op.search.tree = ldb_parse_tree_copy_shallow(req, req->op.search.tree);
514 if (req->op.search.tree == NULL) {
515 return ldb_oom(ldb_module_get_ctx(module));
518 ret = ldb_parse_tree_walk(req->op.search.tree, extended_dn_filter_callback, filter_ctx);
519 if (ret != LDB_SUCCESS) {
520 talloc_free(filter_ctx);
521 return ret;
524 talloc_free(filter_ctx);
525 return LDB_SUCCESS;
529 fix DNs and filter expressions to cope with the semantics of
530 extended DNs
532 static int extended_dn_in_fix(struct ldb_module *module, struct ldb_request *req, struct ldb_dn *dn)
534 struct extended_search_context *ac;
535 struct ldb_request *down_req;
536 int ret;
537 struct ldb_dn *base_dn = NULL;
538 enum ldb_scope base_dn_scope = LDB_SCOPE_BASE;
539 const char *base_dn_filter = NULL;
540 const char * const *base_dn_attrs = NULL;
541 char *wellknown_object = NULL;
542 static const char *no_attr[] = {
543 NULL
545 uint32_t dsdb_flags = DSDB_FLAG_AS_SYSTEM | DSDB_SEARCH_SHOW_EXTENDED_DN;
547 if (ldb_request_get_control(req, LDB_CONTROL_SHOW_DELETED_OID)) {
548 dsdb_flags |= DSDB_SEARCH_SHOW_DELETED;
550 if (ldb_request_get_control(req, LDB_CONTROL_SHOW_RECYCLED_OID)) {
551 dsdb_flags |= DSDB_SEARCH_SHOW_RECYCLED;
553 if (ldb_request_get_control(req, DSDB_CONTROL_DBCHECK)) {
554 dsdb_flags |= DSDB_SEARCH_SHOW_RECYCLED;
557 if (req->operation == LDB_SEARCH) {
558 ret = extended_dn_fix_filter(module, req, dsdb_flags);
559 if (ret != LDB_SUCCESS) {
560 return ret;
564 if (!ldb_dn_has_extended(dn)) {
565 /* Move along there isn't anything to see here */
566 return ldb_next_request(module, req);
567 } else {
568 /* It looks like we need to map the DN */
569 const struct ldb_val *sid_val, *guid_val, *wkguid_val;
571 if (!ldb_dn_match_allowed(dn, req)) {
572 return ldb_error(ldb_module_get_ctx(module),
573 LDB_ERR_INVALID_DN_SYNTAX, "invalid number of DN components");
576 sid_val = ldb_dn_get_extended_component(dn, "SID");
577 guid_val = ldb_dn_get_extended_component(dn, "GUID");
578 wkguid_val = ldb_dn_get_extended_component(dn, "WKGUID");
581 prioritise the GUID - we have had instances of
582 duplicate SIDs in the database in the
583 ForeignSecurityPrinciples due to provision errors
585 if (guid_val) {
586 dsdb_flags |= DSDB_SEARCH_SEARCH_ALL_PARTITIONS;
587 base_dn = NULL;
588 base_dn_filter = talloc_asprintf(req, "(objectGUID=%s)",
589 ldb_binary_encode(req, *guid_val));
590 if (!base_dn_filter) {
591 return ldb_oom(ldb_module_get_ctx(module));
593 base_dn_scope = LDB_SCOPE_SUBTREE;
594 base_dn_attrs = no_attr;
596 } else if (sid_val) {
597 dsdb_flags |= DSDB_SEARCH_SEARCH_ALL_PARTITIONS;
598 base_dn = NULL;
599 base_dn_filter = talloc_asprintf(req, "(objectSid=%s)",
600 ldb_binary_encode(req, *sid_val));
601 if (!base_dn_filter) {
602 return ldb_oom(ldb_module_get_ctx(module));
604 base_dn_scope = LDB_SCOPE_SUBTREE;
605 base_dn_attrs = no_attr;
607 } else if (wkguid_val) {
608 char *wkguid_dup;
609 char *tail_str;
610 char *p;
612 wkguid_dup = talloc_strndup(req, (char *)wkguid_val->data, wkguid_val->length);
614 p = strchr(wkguid_dup, ',');
615 if (!p) {
616 return ldb_error(ldb_module_get_ctx(module), LDB_ERR_INVALID_DN_SYNTAX,
617 "Invalid WKGUID format");
620 p[0] = '\0';
621 p++;
623 wellknown_object = talloc_asprintf(req, "B:32:%s:", wkguid_dup);
624 if (!wellknown_object) {
625 return ldb_oom(ldb_module_get_ctx(module));
628 tail_str = p;
630 base_dn = ldb_dn_new(req, ldb_module_get_ctx(module), tail_str);
631 talloc_free(wkguid_dup);
632 if (!base_dn) {
633 return ldb_oom(ldb_module_get_ctx(module));
635 base_dn_filter = talloc_strdup(req, "(objectClass=*)");
636 if (!base_dn_filter) {
637 return ldb_oom(ldb_module_get_ctx(module));
639 base_dn_scope = LDB_SCOPE_BASE;
640 base_dn_attrs = wkattr;
641 } else {
642 return ldb_error(ldb_module_get_ctx(module), LDB_ERR_INVALID_DN_SYNTAX,
643 "Invalid extended DN component");
646 ac = talloc_zero(req, struct extended_search_context);
647 if (ac == NULL) {
648 return ldb_oom(ldb_module_get_ctx(module));
651 ac->module = module;
652 ac->req = req;
653 ac->dn = dn;
654 ac->basedn = NULL; /* Filled in if the search finds the DN by SID/GUID etc */
655 ac->wellknown_object = wellknown_object;
657 /* If the base DN was an extended DN (perhaps a well known
658 * GUID) then search for that, so we can proceed with the original operation */
660 ret = ldb_build_search_req(&down_req,
661 ldb_module_get_ctx(module), ac,
662 base_dn,
663 base_dn_scope,
664 base_dn_filter,
665 base_dn_attrs,
666 NULL,
667 ac, extended_base_callback,
668 req);
669 LDB_REQ_SET_LOCATION(down_req);
670 if (ret != LDB_SUCCESS) {
671 return ldb_operr(ldb_module_get_ctx(module));
674 ret = dsdb_request_add_controls(down_req, dsdb_flags);
675 if (ret != LDB_SUCCESS) {
676 return ret;
679 /* perform the search */
680 return ldb_next_request(module, down_req);
684 static int extended_dn_in_search(struct ldb_module *module, struct ldb_request *req)
686 return extended_dn_in_fix(module, req, req->op.search.base);
689 static int extended_dn_in_modify(struct ldb_module *module, struct ldb_request *req)
691 return extended_dn_in_fix(module, req, req->op.mod.message->dn);
694 static int extended_dn_in_del(struct ldb_module *module, struct ldb_request *req)
696 return extended_dn_in_fix(module, req, req->op.del.dn);
699 static int extended_dn_in_rename(struct ldb_module *module, struct ldb_request *req)
701 return extended_dn_in_fix(module, req, req->op.rename.olddn);
704 static const struct ldb_module_ops ldb_extended_dn_in_module_ops = {
705 .name = "extended_dn_in",
706 .search = extended_dn_in_search,
707 .modify = extended_dn_in_modify,
708 .del = extended_dn_in_del,
709 .rename = extended_dn_in_rename,
712 int ldb_extended_dn_in_module_init(const char *version)
714 LDB_MODULE_CHECK_VERSION(version);
715 return ldb_register_module(&ldb_extended_dn_in_module_ops);