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"
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
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 static const char *wkattr[] = {
56 "otherWellKnownObjects",
60 static const struct ldb_module_ops ldb_extended_dn_in_openldap_module_ops;
62 /* An extra layer of indirection because LDB does not allow the original request to be altered */
64 static int extended_final_callback(struct ldb_request *req, struct ldb_reply *ares)
66 int ret = LDB_ERR_OPERATIONS_ERROR;
67 struct extended_search_context *ac;
68 ac = talloc_get_type(req->context, struct extended_search_context);
70 if (ares->error != LDB_SUCCESS) {
71 ret = ldb_module_done(ac->req, ares->controls,
72 ares->response, ares->error);
77 ret = ldb_module_send_entry(ac->req, ares->message, ares->controls);
79 case LDB_REPLY_REFERRAL:
81 ret = ldb_module_send_referral(ac->req, ares->referral);
85 ret = ldb_module_done(ac->req, ares->controls,
86 ares->response, ares->error);
93 static int extended_base_callback(struct ldb_request *req, struct ldb_reply *ares)
95 struct extended_search_context *ac;
96 struct ldb_request *down_req;
97 struct ldb_message_element *el;
102 const char *found = NULL;
104 ac = talloc_get_type(req->context, struct extended_search_context);
107 return ldb_module_done(ac->req, NULL, NULL,
108 LDB_ERR_OPERATIONS_ERROR);
110 if (ares->error != LDB_SUCCESS) {
111 return ldb_module_done(ac->req, ares->controls,
112 ares->response, ares->error);
115 switch (ares->type) {
116 case LDB_REPLY_ENTRY:
118 /* we have more than one match! This can
119 happen as S-1-5-17 appears twice in a
120 normal provision. We need to return
122 const char *str = talloc_asprintf(req, "Duplicate base-DN matches found for '%s'",
123 ldb_dn_get_extended_linearized(req, ac->dn, 1));
124 ldb_set_errstring(ldb_module_get_ctx(ac->module), str);
125 return ldb_module_done(ac->req, NULL, NULL,
126 LDB_ERR_NO_SUCH_OBJECT);
129 if (!ac->wellknown_object) {
130 ac->basedn = talloc_steal(ac, ares->message->dn);
134 wkn_len = strlen(ac->wellknown_object);
136 for (j=0; wkattr[j]; j++) {
138 el = ldb_msg_find_element(ares->message, wkattr[j]);
144 for (i=0; i < el->num_values; i++) {
145 valstr = talloc_strndup(ac,
146 (const char *)el->values[i].data,
147 el->values[i].length);
149 ldb_oom(ldb_module_get_ctx(ac->module));
150 return ldb_module_done(ac->req, NULL, NULL,
151 LDB_ERR_OPERATIONS_ERROR);
154 if (strncasecmp(valstr, ac->wellknown_object, wkn_len) != 0) {
159 found = &valstr[wkn_len];
171 ac->basedn = ldb_dn_new(ac, ldb_module_get_ctx(ac->module), found);
174 ldb_oom(ldb_module_get_ctx(ac->module));
175 return ldb_module_done(ac->req, NULL, NULL,
176 LDB_ERR_OPERATIONS_ERROR);
181 case LDB_REPLY_REFERRAL:
187 const char *str = talloc_asprintf(req, "Base-DN '%s' not found",
188 ldb_dn_get_extended_linearized(req, ac->dn, 1));
189 ldb_set_errstring(ldb_module_get_ctx(ac->module), str);
190 return ldb_module_done(ac->req, NULL, NULL,
191 LDB_ERR_NO_SUCH_OBJECT);
194 switch (ac->req->operation) {
196 ret = ldb_build_search_req_ex(&down_req,
197 ldb_module_get_ctx(ac->module), ac->req,
199 ac->req->op.search.scope,
200 ac->req->op.search.tree,
201 ac->req->op.search.attrs,
203 ac, extended_final_callback,
205 LDB_REQ_SET_LOCATION(down_req);
209 struct ldb_message *add_msg = ldb_msg_copy_shallow(ac, ac->req->op.add.message);
211 ldb_oom(ldb_module_get_ctx(ac->module));
212 return ldb_module_done(ac->req, NULL, NULL,
213 LDB_ERR_OPERATIONS_ERROR);
216 add_msg->dn = ac->basedn;
218 ret = ldb_build_add_req(&down_req,
219 ldb_module_get_ctx(ac->module), ac->req,
222 ac, extended_final_callback,
224 LDB_REQ_SET_LOCATION(down_req);
229 struct ldb_message *mod_msg = ldb_msg_copy_shallow(ac, ac->req->op.mod.message);
231 ldb_oom(ldb_module_get_ctx(ac->module));
232 return ldb_module_done(ac->req, NULL, NULL,
233 LDB_ERR_OPERATIONS_ERROR);
236 mod_msg->dn = ac->basedn;
238 ret = ldb_build_mod_req(&down_req,
239 ldb_module_get_ctx(ac->module), ac->req,
242 ac, extended_final_callback,
244 LDB_REQ_SET_LOCATION(down_req);
248 ret = ldb_build_del_req(&down_req,
249 ldb_module_get_ctx(ac->module), ac->req,
252 ac, extended_final_callback,
254 LDB_REQ_SET_LOCATION(down_req);
257 ret = ldb_build_rename_req(&down_req,
258 ldb_module_get_ctx(ac->module), ac->req,
260 ac->req->op.rename.newdn,
262 ac, extended_final_callback,
264 LDB_REQ_SET_LOCATION(down_req);
267 return ldb_module_done(ac->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
270 if (ret != LDB_SUCCESS) {
271 return ldb_module_done(ac->req, NULL, NULL, ret);
274 return ldb_next_request(ac->module, down_req);
282 windows ldap searchs don't allow a baseDN with more
283 than one extended component, or an extended
284 component and a string DN
286 We only enforce this over ldap, not for internal
287 use, as there are just too many places where we
288 internally want to use a DN that has come from a
289 search with extended DN enabled, or comes from a DRS
292 Enforcing this would also make debugging samba much
293 harder, as we'd need to use ldb_dn_minimise() in a
294 lot of places, and that would lose the DN string
295 which is so useful for working out what a request is
298 static bool ldb_dn_match_allowed(struct ldb_dn *dn, struct ldb_request *req)
300 int num_components = ldb_dn_get_comp_num(dn);
301 int num_ex_components = ldb_dn_get_extended_comp_num(dn);
303 if (num_ex_components == 0) {
307 if ((num_components != 0 || num_ex_components != 1) &&
308 ldb_req_is_untrusted(req)) {
315 struct extended_dn_filter_ctx {
318 struct ldb_module *module;
319 struct ldb_request *req;
320 struct dsdb_schema *schema;
325 create a always non-matching node from a equality node
327 static void set_parse_tree_false(struct ldb_parse_tree *tree)
329 const char *attr = tree->u.equality.attr;
330 struct ldb_val value = tree->u.equality.value;
331 tree->operation = LDB_OP_EXTENDED;
332 tree->u.extended.attr = attr;
333 tree->u.extended.value = value;
334 tree->u.extended.rule_id = SAMBA_LDAP_MATCH_ALWAYS_FALSE;
335 tree->u.extended.dnAttributes = 0;
339 called on all nodes in the parse tree
341 static int extended_dn_filter_callback(struct ldb_parse_tree *tree, void *private_context)
343 struct extended_dn_filter_ctx *filter_ctx;
345 struct ldb_dn *dn = NULL;
346 const struct ldb_val *sid_val, *guid_val;
347 const char *no_attrs[] = { NULL };
348 struct ldb_result *res;
349 const struct dsdb_attribute *attribute = NULL;
350 bool has_extended_component = false;
351 enum ldb_scope scope;
352 struct ldb_dn *base_dn;
353 const char *expression;
356 if (tree->operation != LDB_OP_EQUALITY && tree->operation != LDB_OP_EXTENDED) {
360 filter_ctx = talloc_get_type_abort(private_context, struct extended_dn_filter_ctx);
362 if (filter_ctx->test_only && filter_ctx->matched) {
363 /* the tree already matched */
367 if (!filter_ctx->schema) {
368 /* Schema not setup yet */
371 if (tree->operation == LDB_OP_EQUALITY) {
372 attribute = dsdb_attribute_by_lDAPDisplayName(filter_ctx->schema, tree->u.equality.attr);
373 } else if (tree->operation == LDB_OP_EXTENDED) {
374 attribute = dsdb_attribute_by_lDAPDisplayName(filter_ctx->schema, tree->u.extended.attr);
376 if (attribute == NULL) {
380 if (attribute->dn_format != DSDB_NORMAL_DN) {
384 if (tree->operation == LDB_OP_EQUALITY) {
385 has_extended_component = (memchr(tree->u.equality.value.data, '<',
386 tree->u.equality.value.length) != NULL);
387 } else if (tree->operation == LDB_OP_EXTENDED) {
388 has_extended_component = (memchr(tree->u.extended.value.data, '<',
389 tree->u.extended.value.length) != NULL);
393 * Don't turn it into an extended DN if we're talking to OpenLDAP.
394 * We just check the module_ops pointer instead of adding a private
395 * pointer and a boolean to tell us the exact same thing.
397 if (!has_extended_component) {
398 if (!attribute->one_way_link) {
402 if (ldb_module_get_ops(filter_ctx->module) == &ldb_extended_dn_in_openldap_module_ops) {
407 if (tree->operation == LDB_OP_EQUALITY) {
408 dn = ldb_dn_from_ldb_val(filter_ctx, ldb_module_get_ctx(filter_ctx->module), &tree->u.equality.value);
409 } else if (tree->operation == LDB_OP_EXTENDED) {
410 dn = ldb_dn_from_ldb_val(filter_ctx, ldb_module_get_ctx(filter_ctx->module), &tree->u.extended.value);
413 /* testing against windows shows that we don't raise
418 guid_val = ldb_dn_get_extended_component(dn, "GUID");
419 sid_val = ldb_dn_get_extended_component(dn, "SID");
421 if (!guid_val && !sid_val && (attribute->searchFlags & SEARCH_FLAG_ATTINDEX)) {
422 /* if it is indexed, then fixing the string DN will do
423 no good here, as we will not find the attribute in
424 the index. So for now fall through to a standard DN
425 component comparison */
429 if (filter_ctx->test_only) {
430 /* we need to copy the tree */
431 filter_ctx->matched = true;
435 if (!ldb_dn_match_allowed(dn, filter_ctx->req)) {
436 /* we need to make this element of the filter always
438 set_parse_tree_false(tree);
442 dsdb_flags = filter_ctx->dsdb_flags | DSDB_FLAG_NEXT_MODULE;
445 expression = talloc_asprintf(filter_ctx, "objectGUID=%s", ldb_binary_encode(filter_ctx, *guid_val));
446 scope = LDB_SCOPE_SUBTREE;
448 dsdb_flags |= DSDB_SEARCH_SEARCH_ALL_PARTITIONS;
449 } else if (sid_val) {
450 expression = talloc_asprintf(filter_ctx, "objectSID=%s", ldb_binary_encode(filter_ctx, *sid_val));
451 scope = LDB_SCOPE_SUBTREE;
453 dsdb_flags |= DSDB_SEARCH_SEARCH_ALL_PARTITIONS;
455 /* fallback to searching using the string DN as the base DN */
456 expression = "objectClass=*";
458 scope = LDB_SCOPE_BASE;
461 ret = dsdb_module_search(filter_ctx->module,
470 if (scope == LDB_SCOPE_BASE && ret == LDB_ERR_NO_SUCH_OBJECT) {
471 /* note that this will need to change for multi-domain
473 set_parse_tree_false(tree);
477 if (ret != LDB_SUCCESS) {
482 if (res->count != 1) {
486 /* replace the search expression element with the matching DN */
487 if (tree->operation == LDB_OP_EQUALITY) {
488 tree->u.equality.value.data =
489 (uint8_t *)talloc_strdup(tree, ldb_dn_get_extended_linearized(tree, res->msgs[0]->dn, 1));
490 if (tree->u.equality.value.data == NULL) {
491 return ldb_oom(ldb_module_get_ctx(filter_ctx->module));
493 tree->u.equality.value.length = strlen((const char *)tree->u.equality.value.data);
494 } else if (tree->operation == LDB_OP_EXTENDED) {
495 tree->u.extended.value.data =
496 (uint8_t *)talloc_strdup(tree, ldb_dn_get_extended_linearized(tree, res->msgs[0]->dn, 1));
497 if (tree->u.extended.value.data == NULL) {
498 return ldb_oom(ldb_module_get_ctx(filter_ctx->module));
500 tree->u.extended.value.length = strlen((const char *)tree->u.extended.value.data);
504 filter_ctx->matched = true;
509 fix the parse tree to change any extended DN components to their
512 static int extended_dn_fix_filter(struct ldb_module *module,
513 struct ldb_request *req,
514 uint32_t default_dsdb_flags)
516 struct extended_dn_filter_ctx *filter_ctx;
519 filter_ctx = talloc_zero(req, struct extended_dn_filter_ctx);
520 if (filter_ctx == NULL) {
521 return ldb_module_oom(module);
524 /* first pass through the existing tree to see if anything
525 needs to be modified. Filtering DNs on the input side is rare,
526 so this avoids copying the parse tree in most cases */
527 filter_ctx->test_only = true;
528 filter_ctx->matched = false;
529 filter_ctx->module = module;
530 filter_ctx->req = req;
531 filter_ctx->schema = dsdb_get_schema(ldb_module_get_ctx(module), filter_ctx);
532 filter_ctx->dsdb_flags= default_dsdb_flags;
534 ret = ldb_parse_tree_walk(req->op.search.tree, extended_dn_filter_callback, filter_ctx);
535 if (ret != LDB_SUCCESS) {
536 talloc_free(filter_ctx);
540 if (!filter_ctx->matched) {
541 /* nothing matched, no need for a new parse tree */
542 talloc_free(filter_ctx);
546 filter_ctx->test_only = false;
547 filter_ctx->matched = false;
549 req->op.search.tree = ldb_parse_tree_copy_shallow(req, req->op.search.tree);
550 if (req->op.search.tree == NULL) {
551 return ldb_oom(ldb_module_get_ctx(module));
554 ret = ldb_parse_tree_walk(req->op.search.tree, extended_dn_filter_callback, filter_ctx);
555 if (ret != LDB_SUCCESS) {
556 talloc_free(filter_ctx);
560 talloc_free(filter_ctx);
565 fix DNs and filter expressions to cope with the semantics of
568 static int extended_dn_in_fix(struct ldb_module *module, struct ldb_request *req, struct ldb_dn *dn)
570 struct extended_search_context *ac;
571 struct ldb_request *down_req;
573 struct ldb_dn *base_dn = NULL;
574 enum ldb_scope base_dn_scope = LDB_SCOPE_BASE;
575 const char *base_dn_filter = NULL;
576 const char * const *base_dn_attrs = NULL;
577 char *wellknown_object = NULL;
578 static const char *no_attr[] = {
581 uint32_t dsdb_flags = DSDB_FLAG_AS_SYSTEM | DSDB_SEARCH_SHOW_EXTENDED_DN;
583 if (ldb_request_get_control(req, LDB_CONTROL_SHOW_DELETED_OID)) {
584 dsdb_flags |= DSDB_SEARCH_SHOW_DELETED;
586 if (ldb_request_get_control(req, LDB_CONTROL_SHOW_RECYCLED_OID)) {
587 dsdb_flags |= DSDB_SEARCH_SHOW_RECYCLED;
589 if (ldb_request_get_control(req, DSDB_CONTROL_DBCHECK)) {
590 dsdb_flags |= DSDB_SEARCH_SHOW_RECYCLED;
593 if (req->operation == LDB_SEARCH) {
594 ret = extended_dn_fix_filter(module, req, dsdb_flags);
595 if (ret != LDB_SUCCESS) {
600 if (!ldb_dn_has_extended(dn)) {
601 /* Move along there isn't anything to see here */
602 return ldb_next_request(module, req);
604 /* It looks like we need to map the DN */
605 const struct ldb_val *sid_val, *guid_val, *wkguid_val;
607 if (!ldb_dn_match_allowed(dn, req)) {
608 return ldb_error(ldb_module_get_ctx(module),
609 LDB_ERR_INVALID_DN_SYNTAX, "invalid number of DN components");
612 sid_val = ldb_dn_get_extended_component(dn, "SID");
613 guid_val = ldb_dn_get_extended_component(dn, "GUID");
614 wkguid_val = ldb_dn_get_extended_component(dn, "WKGUID");
617 prioritise the GUID - we have had instances of
618 duplicate SIDs in the database in the
619 ForeignSecurityPrinciples due to provision errors
622 dsdb_flags |= DSDB_SEARCH_SEARCH_ALL_PARTITIONS;
624 base_dn_filter = talloc_asprintf(req, "(objectGUID=%s)",
625 ldb_binary_encode(req, *guid_val));
626 if (!base_dn_filter) {
627 return ldb_oom(ldb_module_get_ctx(module));
629 base_dn_scope = LDB_SCOPE_SUBTREE;
630 base_dn_attrs = no_attr;
632 } else if (sid_val) {
633 dsdb_flags |= DSDB_SEARCH_SEARCH_ALL_PARTITIONS;
635 base_dn_filter = talloc_asprintf(req, "(objectSid=%s)",
636 ldb_binary_encode(req, *sid_val));
637 if (!base_dn_filter) {
638 return ldb_oom(ldb_module_get_ctx(module));
640 base_dn_scope = LDB_SCOPE_SUBTREE;
641 base_dn_attrs = no_attr;
643 } else if (wkguid_val) {
648 wkguid_dup = talloc_strndup(req, (char *)wkguid_val->data, wkguid_val->length);
650 p = strchr(wkguid_dup, ',');
652 return ldb_error(ldb_module_get_ctx(module), LDB_ERR_INVALID_DN_SYNTAX,
653 "Invalid WKGUID format");
659 wellknown_object = talloc_asprintf(req, "B:32:%s:", wkguid_dup);
660 if (!wellknown_object) {
661 return ldb_oom(ldb_module_get_ctx(module));
666 base_dn = ldb_dn_new(req, ldb_module_get_ctx(module), tail_str);
667 talloc_free(wkguid_dup);
669 return ldb_oom(ldb_module_get_ctx(module));
671 base_dn_filter = talloc_strdup(req, "(objectClass=*)");
672 if (!base_dn_filter) {
673 return ldb_oom(ldb_module_get_ctx(module));
675 base_dn_scope = LDB_SCOPE_BASE;
676 base_dn_attrs = wkattr;
678 return ldb_error(ldb_module_get_ctx(module), LDB_ERR_INVALID_DN_SYNTAX,
679 "Invalid extended DN component");
682 ac = talloc_zero(req, struct extended_search_context);
684 return ldb_oom(ldb_module_get_ctx(module));
690 ac->basedn = NULL; /* Filled in if the search finds the DN by SID/GUID etc */
691 ac->wellknown_object = wellknown_object;
693 /* If the base DN was an extended DN (perhaps a well known
694 * GUID) then search for that, so we can proceed with the original operation */
696 ret = ldb_build_search_req(&down_req,
697 ldb_module_get_ctx(module), ac,
703 ac, extended_base_callback,
705 LDB_REQ_SET_LOCATION(down_req);
706 if (ret != LDB_SUCCESS) {
707 return ldb_operr(ldb_module_get_ctx(module));
710 ret = dsdb_request_add_controls(down_req, dsdb_flags);
711 if (ret != LDB_SUCCESS) {
715 /* perform the search */
716 return ldb_next_request(module, down_req);
720 static int extended_dn_in_search(struct ldb_module *module, struct ldb_request *req)
722 return extended_dn_in_fix(module, req, req->op.search.base);
725 static int extended_dn_in_modify(struct ldb_module *module, struct ldb_request *req)
727 return extended_dn_in_fix(module, req, req->op.mod.message->dn);
730 static int extended_dn_in_del(struct ldb_module *module, struct ldb_request *req)
732 return extended_dn_in_fix(module, req, req->op.del.dn);
735 static int extended_dn_in_rename(struct ldb_module *module, struct ldb_request *req)
737 return extended_dn_in_fix(module, req, req->op.rename.olddn);
740 static const struct ldb_module_ops ldb_extended_dn_in_module_ops = {
741 .name = "extended_dn_in",
742 .search = extended_dn_in_search,
743 .modify = extended_dn_in_modify,
744 .del = extended_dn_in_del,
745 .rename = extended_dn_in_rename,
748 static const struct ldb_module_ops ldb_extended_dn_in_openldap_module_ops = {
749 .name = "extended_dn_in_openldap",
750 .search = extended_dn_in_search,
751 .modify = extended_dn_in_modify,
752 .del = extended_dn_in_del,
753 .rename = extended_dn_in_rename,
756 int ldb_extended_dn_in_module_init(const char *version)
759 LDB_MODULE_CHECK_VERSION(version);
760 ret = ldb_register_module(&ldb_extended_dn_in_openldap_module_ops);
761 if (ret != LDB_SUCCESS) {
764 return ldb_register_module(&ldb_extended_dn_in_module_ops);