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/>.
24 * Component: ldb extended dn control module
26 * Description: this module interprets DNs of the form <SID=S-1-2-4456> into normal DNs.
34 #include <ldb_errors.h>
35 #include <ldb_module.h>
36 #include "dsdb/samdb/samdb.h"
40 TODO: if relax is not set then we need to reject the fancy RMD_* and
41 DELETED extended DN codes
45 struct extended_search_context {
46 struct ldb_module *module;
47 struct ldb_request *req;
48 struct ldb_dn *basedn;
50 char *wellknown_object;
54 /* An extra layer of indirection because LDB does not allow the original request to be altered */
56 static int extended_final_callback(struct ldb_request *req, struct ldb_reply *ares)
58 int ret = LDB_ERR_OPERATIONS_ERROR;
59 struct extended_search_context *ac;
60 ac = talloc_get_type(req->context, struct extended_search_context);
62 if (ares->error != LDB_SUCCESS) {
63 ret = ldb_module_done(ac->req, ares->controls,
64 ares->response, ares->error);
69 ret = ldb_module_send_entry(ac->req, ares->message, ares->controls);
71 case LDB_REPLY_REFERRAL:
73 ret = ldb_module_send_referral(ac->req, ares->referral);
77 ret = ldb_module_done(ac->req, ares->controls,
78 ares->response, ares->error);
85 static int extended_base_callback(struct ldb_request *req, struct ldb_reply *ares)
87 struct extended_search_context *ac;
88 struct ldb_request *down_req;
89 struct ldb_message_element *el;
94 const char *found = NULL;
96 ac = talloc_get_type(req->context, struct extended_search_context);
99 return ldb_module_done(ac->req, NULL, NULL,
100 LDB_ERR_OPERATIONS_ERROR);
102 if (ares->error != LDB_SUCCESS) {
103 return ldb_module_done(ac->req, ares->controls,
104 ares->response, ares->error);
107 switch (ares->type) {
108 case LDB_REPLY_ENTRY:
110 /* we have more than one match! This can
111 happen as S-1-5-17 appears twice in a
112 normal provision. We need to return
114 const char *str = talloc_asprintf(req, "Duplicate base-DN matches found for '%s'",
115 ldb_dn_get_extended_linearized(req, ac->dn, 1));
116 ldb_set_errstring(ldb_module_get_ctx(ac->module), str);
117 return ldb_module_done(ac->req, NULL, NULL,
118 LDB_ERR_NO_SUCH_OBJECT);
121 if (!ac->wellknown_object) {
122 ac->basedn = talloc_steal(ac, ares->message->dn);
126 wkn_len = strlen(ac->wellknown_object);
128 el = ldb_msg_find_element(ares->message, "wellKnownObjects");
134 for (i=0; i < el->num_values; i++) {
135 valstr = talloc_strndup(ac,
136 (const char *)el->values[i].data,
137 el->values[i].length);
139 ldb_oom(ldb_module_get_ctx(ac->module));
140 return ldb_module_done(ac->req, NULL, NULL,
141 LDB_ERR_OPERATIONS_ERROR);
144 if (strncasecmp(valstr, ac->wellknown_object, wkn_len) != 0) {
149 found = &valstr[wkn_len];
157 ac->basedn = ldb_dn_new(ac, ldb_module_get_ctx(ac->module), found);
160 ldb_oom(ldb_module_get_ctx(ac->module));
161 return ldb_module_done(ac->req, NULL, NULL,
162 LDB_ERR_OPERATIONS_ERROR);
167 case LDB_REPLY_REFERRAL:
173 const char *str = talloc_asprintf(req, "Base-DN '%s' not found",
174 ldb_dn_get_extended_linearized(req, ac->dn, 1));
175 ldb_set_errstring(ldb_module_get_ctx(ac->module), str);
176 return ldb_module_done(ac->req, NULL, NULL,
177 LDB_ERR_NO_SUCH_OBJECT);
180 switch (ac->req->operation) {
182 ret = ldb_build_search_req_ex(&down_req,
183 ldb_module_get_ctx(ac->module), ac->req,
185 ac->req->op.search.scope,
186 ac->req->op.search.tree,
187 ac->req->op.search.attrs,
189 ac, extended_final_callback,
191 LDB_REQ_SET_LOCATION(down_req);
195 struct ldb_message *add_msg = ldb_msg_copy_shallow(ac, ac->req->op.add.message);
197 ldb_oom(ldb_module_get_ctx(ac->module));
198 return ldb_module_done(ac->req, NULL, NULL,
199 LDB_ERR_OPERATIONS_ERROR);
202 add_msg->dn = ac->basedn;
204 ret = ldb_build_add_req(&down_req,
205 ldb_module_get_ctx(ac->module), ac->req,
208 ac, extended_final_callback,
210 LDB_REQ_SET_LOCATION(down_req);
215 struct ldb_message *mod_msg = ldb_msg_copy_shallow(ac, ac->req->op.mod.message);
217 ldb_oom(ldb_module_get_ctx(ac->module));
218 return ldb_module_done(ac->req, NULL, NULL,
219 LDB_ERR_OPERATIONS_ERROR);
222 mod_msg->dn = ac->basedn;
224 ret = ldb_build_mod_req(&down_req,
225 ldb_module_get_ctx(ac->module), ac->req,
228 ac, extended_final_callback,
230 LDB_REQ_SET_LOCATION(down_req);
234 ret = ldb_build_del_req(&down_req,
235 ldb_module_get_ctx(ac->module), ac->req,
238 ac, extended_final_callback,
240 LDB_REQ_SET_LOCATION(down_req);
243 ret = ldb_build_rename_req(&down_req,
244 ldb_module_get_ctx(ac->module), ac->req,
246 ac->req->op.rename.newdn,
248 ac, extended_final_callback,
250 LDB_REQ_SET_LOCATION(down_req);
253 return ldb_module_done(ac->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
256 if (ret != LDB_SUCCESS) {
257 return ldb_module_done(ac->req, NULL, NULL, ret);
260 return ldb_next_request(ac->module, down_req);
268 windows ldap searchs don't allow a baseDN with more
269 than one extended component, or an extended
270 component and a string DN
272 We only enforce this over ldap, not for internal
273 use, as there are just too many places where we
274 internally want to use a DN that has come from a
275 search with extended DN enabled, or comes from a DRS
278 Enforcing this would also make debugging samba much
279 harder, as we'd need to use ldb_dn_minimise() in a
280 lot of places, and that would lose the DN string
281 which is so useful for working out what a request is
284 static bool ldb_dn_match_allowed(struct ldb_dn *dn, struct ldb_request *req)
286 int num_components = ldb_dn_get_comp_num(dn);
287 int num_ex_components = ldb_dn_get_extended_comp_num(dn);
289 if (num_ex_components == 0) {
293 if ((num_components != 0 || num_ex_components != 1) &&
294 ldb_req_is_untrusted(req)) {
301 struct extended_dn_filter_ctx {
304 struct ldb_module *module;
305 struct ldb_request *req;
306 struct dsdb_schema *schema;
310 create a always non-matching node from a equality node
312 static void set_parse_tree_false(struct ldb_parse_tree *tree)
314 const char *attr = tree->u.equality.attr;
315 struct ldb_val value = tree->u.equality.value;
316 tree->operation = LDB_OP_EXTENDED;
317 tree->u.extended.attr = attr;
318 tree->u.extended.value = value;
319 tree->u.extended.rule_id = SAMBA_LDAP_MATCH_ALWAYS_FALSE;
320 tree->u.extended.dnAttributes = 0;
324 called on all nodes in the parse tree
326 static int extended_dn_filter_callback(struct ldb_parse_tree *tree, void *private_context)
328 struct extended_dn_filter_ctx *filter_ctx;
331 const struct ldb_val *sid_val, *guid_val;
332 const char *no_attrs[] = { NULL };
333 struct ldb_result *res;
334 const struct dsdb_attribute *attribute;
335 bool has_extended_component;
336 enum ldb_scope scope;
337 struct ldb_dn *base_dn;
338 const char *expression;
340 if (tree->operation != LDB_OP_EQUALITY) {
344 filter_ctx = talloc_get_type_abort(private_context, struct extended_dn_filter_ctx);
346 attribute = dsdb_attribute_by_lDAPDisplayName(filter_ctx->schema, tree->u.equality.attr);
347 if (attribute == NULL) {
351 if (attribute->dn_format != DSDB_NORMAL_DN) {
355 has_extended_component = (memchr(tree->u.equality.value.data, '<',
356 tree->u.equality.value.length) != NULL);
358 if (!attribute->one_way_link && !has_extended_component) {
362 dn = ldb_dn_from_ldb_val(filter_ctx, ldb_module_get_ctx(filter_ctx->module), &tree->u.equality.value);
364 /* testing against windows shows that we don't raise
369 if (filter_ctx->test_only) {
370 filter_ctx->matched = true;
374 if (!ldb_dn_match_allowed(dn, filter_ctx->req)) {
375 /* we need to make this element of the filter always
377 set_parse_tree_false(tree);
381 guid_val = ldb_dn_get_extended_component(dn, "GUID");
382 sid_val = ldb_dn_get_extended_component(dn, "SID");
385 expression = talloc_asprintf(filter_ctx, "objectGUID=%s", ldb_binary_encode(filter_ctx, *guid_val));
386 scope = LDB_SCOPE_SUBTREE;
388 } else if (sid_val) {
389 expression = talloc_asprintf(filter_ctx, "objectSID=%s", ldb_binary_encode(filter_ctx, *sid_val));
390 scope = LDB_SCOPE_SUBTREE;
392 } else if (attribute->searchFlags & SEARCH_FLAG_ATTINDEX) {
393 /* if it is indexed, then fixing the string DN will do
394 no good here, as we will not find the attribute in
395 the index. So for now fall through to a standard DN
396 component comparison */
399 /* fallback to searching using the string DN as the base DN */
400 expression = "objectClass=*";
402 scope = LDB_SCOPE_BASE;
405 ret = dsdb_module_search(filter_ctx->module,
411 DSDB_FLAG_NEXT_MODULE |
412 DSDB_SEARCH_SHOW_DELETED |
413 DSDB_SEARCH_SHOW_EXTENDED_DN |
414 DSDB_SEARCH_SEARCH_ALL_PARTITIONS,
417 if (scope == LDB_SCOPE_BASE && ret == LDB_ERR_NO_SUCH_OBJECT) {
418 /* note that this will need to change for multi-domain
420 set_parse_tree_false(tree);
424 if (ret != LDB_SUCCESS) {
429 if (res->count != 1) {
433 /* replace the search expression element with the matching DN */
434 tree->u.equality.value.data = (uint8_t *)talloc_strdup(tree,
435 ldb_dn_get_extended_linearized(tree, res->msgs[0]->dn, 1));
436 if (tree->u.equality.value.data == NULL) {
437 return ldb_oom(ldb_module_get_ctx(filter_ctx->module));
439 tree->u.equality.value.length = strlen((const char *)tree->u.equality.value.data);
442 filter_ctx->matched = true;
447 fix the parse tree to change any extended DN components to their
450 static int extended_dn_fix_filter(struct ldb_module *module, struct ldb_request *req)
452 struct extended_dn_filter_ctx *filter_ctx;
455 filter_ctx = talloc_zero(req, struct extended_dn_filter_ctx);
456 if (filter_ctx == NULL) {
457 return ldb_module_oom(module);
460 /* first pass through the existing tree to see if anything
461 needs to be modified. Filtering DNs on the input side is rare,
462 so this avoids copying the parse tree in most cases */
463 filter_ctx->test_only = true;
464 filter_ctx->matched = false;
465 filter_ctx->module = module;
466 filter_ctx->req = req;
467 filter_ctx->schema = dsdb_get_schema(ldb_module_get_ctx(module), filter_ctx);
469 ret = ldb_parse_tree_walk(req->op.search.tree, extended_dn_filter_callback, filter_ctx);
470 if (ret != LDB_SUCCESS) {
471 talloc_free(filter_ctx);
475 if (!filter_ctx->matched) {
476 /* nothing matched, no need for a new parse tree */
477 talloc_free(filter_ctx);
481 filter_ctx->test_only = false;
482 filter_ctx->matched = false;
484 req->op.search.tree = ldb_parse_tree_copy_shallow(req, req->op.search.tree);
485 if (req->op.search.tree == NULL) {
486 return ldb_oom(ldb_module_get_ctx(module));
489 ret = ldb_parse_tree_walk(req->op.search.tree, extended_dn_filter_callback, filter_ctx);
490 if (ret != LDB_SUCCESS) {
491 talloc_free(filter_ctx);
495 talloc_free(filter_ctx);
500 fix DNs and filter expressions to cope with the semantics of
503 static int extended_dn_in_fix(struct ldb_module *module, struct ldb_request *req, struct ldb_dn *dn)
505 struct extended_search_context *ac;
506 struct ldb_request *down_req;
508 struct ldb_dn *base_dn = NULL;
509 enum ldb_scope base_dn_scope = LDB_SCOPE_BASE;
510 const char *base_dn_filter = NULL;
511 const char * const *base_dn_attrs = NULL;
512 char *wellknown_object = NULL;
513 static const char *no_attr[] = {
516 static const char *wkattr[] = {
520 bool all_partitions = false;
522 if (req->operation == LDB_SEARCH) {
523 ret = extended_dn_fix_filter(module, req);
524 if (ret != LDB_SUCCESS) {
529 if (!ldb_dn_has_extended(dn)) {
530 /* Move along there isn't anything to see here */
531 return ldb_next_request(module, req);
533 /* It looks like we need to map the DN */
534 const struct ldb_val *sid_val, *guid_val, *wkguid_val;
536 if (!ldb_dn_match_allowed(dn, req)) {
537 return ldb_error(ldb_module_get_ctx(module),
538 LDB_ERR_INVALID_DN_SYNTAX, "invalid number of DN components");
541 sid_val = ldb_dn_get_extended_component(dn, "SID");
542 guid_val = ldb_dn_get_extended_component(dn, "GUID");
543 wkguid_val = ldb_dn_get_extended_component(dn, "WKGUID");
546 prioritise the GUID - we have had instances of
547 duplicate SIDs in the database in the
548 ForeignSecurityPrinciples due to provision errors
551 all_partitions = true;
552 base_dn = ldb_get_default_basedn(ldb_module_get_ctx(module));
553 base_dn_filter = talloc_asprintf(req, "(objectGUID=%s)",
554 ldb_binary_encode(req, *guid_val));
555 if (!base_dn_filter) {
556 return ldb_oom(ldb_module_get_ctx(module));
558 base_dn_scope = LDB_SCOPE_SUBTREE;
559 base_dn_attrs = no_attr;
561 } else if (sid_val) {
562 all_partitions = true;
563 base_dn = ldb_get_default_basedn(ldb_module_get_ctx(module));
564 base_dn_filter = talloc_asprintf(req, "(objectSid=%s)",
565 ldb_binary_encode(req, *sid_val));
566 if (!base_dn_filter) {
567 return ldb_oom(ldb_module_get_ctx(module));
569 base_dn_scope = LDB_SCOPE_SUBTREE;
570 base_dn_attrs = no_attr;
572 } else if (wkguid_val) {
577 wkguid_dup = talloc_strndup(req, (char *)wkguid_val->data, wkguid_val->length);
579 p = strchr(wkguid_dup, ',');
581 return ldb_error(ldb_module_get_ctx(module), LDB_ERR_INVALID_DN_SYNTAX,
582 "Invalid WKGUID format");
588 wellknown_object = talloc_asprintf(req, "B:32:%s:", wkguid_dup);
589 if (!wellknown_object) {
590 return ldb_oom(ldb_module_get_ctx(module));
595 base_dn = ldb_dn_new(req, ldb_module_get_ctx(module), tail_str);
596 talloc_free(wkguid_dup);
598 return ldb_oom(ldb_module_get_ctx(module));
600 base_dn_filter = talloc_strdup(req, "(objectClass=*)");
601 if (!base_dn_filter) {
602 return ldb_oom(ldb_module_get_ctx(module));
604 base_dn_scope = LDB_SCOPE_BASE;
605 base_dn_attrs = wkattr;
607 return ldb_error(ldb_module_get_ctx(module), LDB_ERR_INVALID_DN_SYNTAX,
608 "Invalid extended DN component");
611 ac = talloc_zero(req, struct extended_search_context);
613 return ldb_oom(ldb_module_get_ctx(module));
619 ac->basedn = NULL; /* Filled in if the search finds the DN by SID/GUID etc */
620 ac->wellknown_object = wellknown_object;
622 /* If the base DN was an extended DN (perhaps a well known
623 * GUID) then search for that, so we can proceed with the original operation */
625 ret = ldb_build_search_req(&down_req,
626 ldb_module_get_ctx(module), ac,
632 ac, extended_base_callback,
634 LDB_REQ_SET_LOCATION(down_req);
635 if (ret != LDB_SUCCESS) {
636 return ldb_operr(ldb_module_get_ctx(module));
639 if (all_partitions) {
640 struct ldb_search_options_control *control;
641 control = talloc(down_req, struct ldb_search_options_control);
642 control->search_options = 2;
643 ret = ldb_request_replace_control(down_req,
644 LDB_CONTROL_SEARCH_OPTIONS_OID,
646 if (ret != LDB_SUCCESS) {
647 ldb_oom(ldb_module_get_ctx(module));
652 /* perform the search */
653 return ldb_next_request(module, down_req);
657 static int extended_dn_in_search(struct ldb_module *module, struct ldb_request *req)
659 return extended_dn_in_fix(module, req, req->op.search.base);
662 static int extended_dn_in_modify(struct ldb_module *module, struct ldb_request *req)
664 return extended_dn_in_fix(module, req, req->op.mod.message->dn);
667 static int extended_dn_in_del(struct ldb_module *module, struct ldb_request *req)
669 return extended_dn_in_fix(module, req, req->op.del.dn);
672 static int extended_dn_in_rename(struct ldb_module *module, struct ldb_request *req)
674 return extended_dn_in_fix(module, req, req->op.rename.olddn);
677 static const struct ldb_module_ops ldb_extended_dn_in_module_ops = {
678 .name = "extended_dn_in",
679 .search = extended_dn_in_search,
680 .modify = extended_dn_in_modify,
681 .del = extended_dn_in_del,
682 .rename = extended_dn_in_rename,
685 int ldb_extended_dn_in_module_init(const char *version)
687 LDB_MODULE_CHECK_VERSION(version);
688 return ldb_register_module(&ldb_extended_dn_in_module_ops);