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"
38 #include "lib/ldb-samba/ldb_matching_rules.h"
43 TODO: if relax is not set then we need to reject the fancy RMD_* and
44 DELETED extended DN codes
48 struct extended_search_context {
49 struct ldb_module *module;
50 struct ldb_request *req;
51 struct ldb_dn *basedn;
53 char *wellknown_object;
57 static const char *wkattr[] = {
59 "otherWellKnownObjects",
63 static const struct ldb_module_ops ldb_extended_dn_in_openldap_module_ops;
65 /* An extra layer of indirection because LDB does not allow the original request to be altered */
67 static int extended_final_callback(struct ldb_request *req, struct ldb_reply *ares)
69 int ret = LDB_ERR_OPERATIONS_ERROR;
70 struct extended_search_context *ac;
71 ac = talloc_get_type(req->context, struct extended_search_context);
73 if (ares->error != LDB_SUCCESS) {
74 ret = ldb_module_done(ac->req, ares->controls,
75 ares->response, ares->error);
80 ret = ldb_module_send_entry(ac->req, ares->message, ares->controls);
82 case LDB_REPLY_REFERRAL:
84 ret = ldb_module_send_referral(ac->req, ares->referral);
88 ret = ldb_module_done(ac->req, ares->controls,
89 ares->response, ares->error);
96 static int extended_base_callback(struct ldb_request *req, struct ldb_reply *ares)
98 struct extended_search_context *ac;
99 struct ldb_request *down_req;
100 struct ldb_message_element *el;
105 const char *found = NULL;
107 ac = talloc_get_type(req->context, struct extended_search_context);
110 return ldb_module_done(ac->req, NULL, NULL,
111 LDB_ERR_OPERATIONS_ERROR);
113 if (ares->error != LDB_SUCCESS) {
114 return ldb_module_done(ac->req, ares->controls,
115 ares->response, ares->error);
118 switch (ares->type) {
119 case LDB_REPLY_ENTRY:
121 /* we have more than one match! This can
122 happen as S-1-5-17 appears twice in a
123 normal provision. We need to return
125 const char *str = talloc_asprintf(req, "Duplicate base-DN matches found for '%s'",
126 ldb_dn_get_extended_linearized(req, ac->dn, 1));
127 ldb_set_errstring(ldb_module_get_ctx(ac->module), str);
128 return ldb_module_done(ac->req, NULL, NULL,
129 LDB_ERR_NO_SUCH_OBJECT);
132 if (!ac->wellknown_object) {
133 ac->basedn = talloc_steal(ac, ares->message->dn);
137 wkn_len = strlen(ac->wellknown_object);
139 for (j=0; wkattr[j]; j++) {
141 el = ldb_msg_find_element(ares->message, wkattr[j]);
147 for (i=0; i < el->num_values; i++) {
148 valstr = talloc_strndup(ac,
149 (const char *)el->values[i].data,
150 el->values[i].length);
152 ldb_oom(ldb_module_get_ctx(ac->module));
153 return ldb_module_done(ac->req, NULL, NULL,
154 LDB_ERR_OPERATIONS_ERROR);
157 if (strncasecmp(valstr, ac->wellknown_object, wkn_len) != 0) {
162 found = &valstr[wkn_len];
174 ac->basedn = ldb_dn_new(ac, ldb_module_get_ctx(ac->module), found);
177 ldb_oom(ldb_module_get_ctx(ac->module));
178 return ldb_module_done(ac->req, NULL, NULL,
179 LDB_ERR_OPERATIONS_ERROR);
184 case LDB_REPLY_REFERRAL:
190 const char *str = talloc_asprintf(req, "Base-DN '%s' not found",
191 ldb_dn_get_extended_linearized(req, ac->dn, 1));
192 ldb_set_errstring(ldb_module_get_ctx(ac->module), str);
193 return ldb_module_done(ac->req, NULL, NULL,
194 LDB_ERR_NO_SUCH_OBJECT);
197 switch (ac->req->operation) {
199 ret = ldb_build_search_req_ex(&down_req,
200 ldb_module_get_ctx(ac->module), ac->req,
202 ac->req->op.search.scope,
203 ac->req->op.search.tree,
204 ac->req->op.search.attrs,
206 ac, extended_final_callback,
208 LDB_REQ_SET_LOCATION(down_req);
212 struct ldb_message *add_msg = ldb_msg_copy_shallow(ac, ac->req->op.add.message);
214 ldb_oom(ldb_module_get_ctx(ac->module));
215 return ldb_module_done(ac->req, NULL, NULL,
216 LDB_ERR_OPERATIONS_ERROR);
219 add_msg->dn = ac->basedn;
221 ret = ldb_build_add_req(&down_req,
222 ldb_module_get_ctx(ac->module), ac->req,
225 ac, extended_final_callback,
227 LDB_REQ_SET_LOCATION(down_req);
232 struct ldb_message *mod_msg = ldb_msg_copy_shallow(ac, ac->req->op.mod.message);
234 ldb_oom(ldb_module_get_ctx(ac->module));
235 return ldb_module_done(ac->req, NULL, NULL,
236 LDB_ERR_OPERATIONS_ERROR);
239 mod_msg->dn = ac->basedn;
241 ret = ldb_build_mod_req(&down_req,
242 ldb_module_get_ctx(ac->module), ac->req,
245 ac, extended_final_callback,
247 LDB_REQ_SET_LOCATION(down_req);
251 ret = ldb_build_del_req(&down_req,
252 ldb_module_get_ctx(ac->module), ac->req,
255 ac, extended_final_callback,
257 LDB_REQ_SET_LOCATION(down_req);
260 ret = ldb_build_rename_req(&down_req,
261 ldb_module_get_ctx(ac->module), ac->req,
263 ac->req->op.rename.newdn,
265 ac, extended_final_callback,
267 LDB_REQ_SET_LOCATION(down_req);
270 return ldb_module_done(ac->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
273 if (ret != LDB_SUCCESS) {
274 return ldb_module_done(ac->req, NULL, NULL, ret);
277 return ldb_next_request(ac->module, down_req);
285 windows ldap searchs don't allow a baseDN with more
286 than one extended component, or an extended
287 component and a string DN
289 We only enforce this over ldap, not for internal
290 use, as there are just too many places where we
291 internally want to use a DN that has come from a
292 search with extended DN enabled, or comes from a DRS
295 Enforcing this would also make debugging samba much
296 harder, as we'd need to use ldb_dn_minimise() in a
297 lot of places, and that would lose the DN string
298 which is so useful for working out what a request is
301 static bool ldb_dn_match_allowed(struct ldb_dn *dn, struct ldb_request *req)
303 int num_components = ldb_dn_get_comp_num(dn);
304 int num_ex_components = ldb_dn_get_extended_comp_num(dn);
306 if (num_ex_components == 0) {
310 if ((num_components != 0 || num_ex_components != 1) &&
311 ldb_req_is_untrusted(req)) {
318 struct extended_dn_filter_ctx {
321 struct ldb_module *module;
322 struct ldb_request *req;
323 struct dsdb_schema *schema;
328 create a always non-matching node from a equality node
330 static void set_parse_tree_false(struct ldb_parse_tree *tree)
332 const char *attr = tree->u.equality.attr;
333 struct ldb_val value = tree->u.equality.value;
334 tree->operation = LDB_OP_EXTENDED;
335 tree->u.extended.attr = attr;
336 tree->u.extended.value = value;
337 tree->u.extended.rule_id = SAMBA_LDAP_MATCH_ALWAYS_FALSE;
338 tree->u.extended.dnAttributes = 0;
342 called on all nodes in the parse tree
344 static int extended_dn_filter_callback(struct ldb_parse_tree *tree, void *private_context)
346 struct extended_dn_filter_ctx *filter_ctx;
348 struct ldb_dn *dn = NULL;
349 const struct ldb_val *sid_val, *guid_val;
350 const char *no_attrs[] = { NULL };
351 struct ldb_result *res;
352 const struct dsdb_attribute *attribute = NULL;
353 bool has_extended_component = false;
354 enum ldb_scope scope;
355 struct ldb_dn *base_dn;
356 const char *expression;
359 if (tree->operation != LDB_OP_EQUALITY && tree->operation != LDB_OP_EXTENDED) {
363 filter_ctx = talloc_get_type_abort(private_context, struct extended_dn_filter_ctx);
365 if (filter_ctx->test_only && filter_ctx->matched) {
366 /* the tree already matched */
370 if (!filter_ctx->schema) {
371 /* Schema not setup yet */
374 if (tree->operation == LDB_OP_EQUALITY) {
375 attribute = dsdb_attribute_by_lDAPDisplayName(filter_ctx->schema, tree->u.equality.attr);
376 } else if (tree->operation == LDB_OP_EXTENDED) {
377 attribute = dsdb_attribute_by_lDAPDisplayName(filter_ctx->schema, tree->u.extended.attr);
379 if (attribute == NULL) {
383 if (attribute->dn_format != DSDB_NORMAL_DN) {
387 if (tree->operation == LDB_OP_EQUALITY) {
388 has_extended_component = (memchr(tree->u.equality.value.data, '<',
389 tree->u.equality.value.length) != NULL);
390 } else if (tree->operation == LDB_OP_EXTENDED) {
391 has_extended_component = (memchr(tree->u.extended.value.data, '<',
392 tree->u.extended.value.length) != NULL);
396 * Don't turn it into an extended DN if we're talking to OpenLDAP.
397 * We just check the module_ops pointer instead of adding a private
398 * pointer and a boolean to tell us the exact same thing.
400 if (!has_extended_component) {
401 if (!attribute->one_way_link) {
405 if (ldb_module_get_ops(filter_ctx->module) == &ldb_extended_dn_in_openldap_module_ops) {
410 if (tree->operation == LDB_OP_EQUALITY) {
411 dn = ldb_dn_from_ldb_val(filter_ctx, ldb_module_get_ctx(filter_ctx->module), &tree->u.equality.value);
412 } else if (tree->operation == LDB_OP_EXTENDED
413 && (strcmp(tree->u.extended.rule_id, SAMBA_LDAP_MATCH_RULE_TRANSITIVE_EVAL) == 0)) {
414 dn = ldb_dn_from_ldb_val(filter_ctx, ldb_module_get_ctx(filter_ctx->module), &tree->u.extended.value);
417 /* testing against windows shows that we don't raise
422 guid_val = ldb_dn_get_extended_component(dn, "GUID");
423 sid_val = ldb_dn_get_extended_component(dn, "SID");
425 if (!guid_val && !sid_val && (attribute->searchFlags & SEARCH_FLAG_ATTINDEX)) {
426 /* if it is indexed, then fixing the string DN will do
427 no good here, as we will not find the attribute in
428 the index. So for now fall through to a standard DN
429 component comparison */
433 if (filter_ctx->test_only) {
434 /* we need to copy the tree */
435 filter_ctx->matched = true;
439 if (!ldb_dn_match_allowed(dn, filter_ctx->req)) {
440 /* we need to make this element of the filter always
442 set_parse_tree_false(tree);
446 dsdb_flags = filter_ctx->dsdb_flags | DSDB_FLAG_NEXT_MODULE;
449 expression = talloc_asprintf(filter_ctx, "objectGUID=%s", ldb_binary_encode(filter_ctx, *guid_val));
450 scope = LDB_SCOPE_SUBTREE;
452 dsdb_flags |= DSDB_SEARCH_SEARCH_ALL_PARTITIONS;
453 } else if (sid_val) {
454 expression = talloc_asprintf(filter_ctx, "objectSID=%s", ldb_binary_encode(filter_ctx, *sid_val));
455 scope = LDB_SCOPE_SUBTREE;
457 dsdb_flags |= DSDB_SEARCH_SEARCH_ALL_PARTITIONS;
459 /* fallback to searching using the string DN as the base DN */
460 expression = "objectClass=*";
462 scope = LDB_SCOPE_BASE;
465 ret = dsdb_module_search(filter_ctx->module,
474 if (scope == LDB_SCOPE_BASE && ret == LDB_ERR_NO_SUCH_OBJECT) {
475 /* note that this will need to change for multi-domain
477 set_parse_tree_false(tree);
481 if (ret != LDB_SUCCESS) {
486 if (res->count != 1) {
490 /* replace the search expression element with the matching DN */
491 if (tree->operation == LDB_OP_EQUALITY) {
492 tree->u.equality.value.data =
493 (uint8_t *)talloc_strdup(tree, ldb_dn_get_extended_linearized(tree, res->msgs[0]->dn, 1));
494 if (tree->u.equality.value.data == NULL) {
495 return ldb_oom(ldb_module_get_ctx(filter_ctx->module));
497 tree->u.equality.value.length = strlen((const char *)tree->u.equality.value.data);
498 } else if (tree->operation == LDB_OP_EXTENDED) {
499 tree->u.extended.value.data =
500 (uint8_t *)talloc_strdup(tree, ldb_dn_get_extended_linearized(tree, res->msgs[0]->dn, 1));
501 if (tree->u.extended.value.data == NULL) {
502 return ldb_oom(ldb_module_get_ctx(filter_ctx->module));
504 tree->u.extended.value.length = strlen((const char *)tree->u.extended.value.data);
508 filter_ctx->matched = true;
513 fix the parse tree to change any extended DN components to their
516 static int extended_dn_fix_filter(struct ldb_module *module,
517 struct ldb_request *req,
518 uint32_t default_dsdb_flags)
520 struct extended_dn_filter_ctx *filter_ctx;
523 filter_ctx = talloc_zero(req, struct extended_dn_filter_ctx);
524 if (filter_ctx == NULL) {
525 return ldb_module_oom(module);
528 /* first pass through the existing tree to see if anything
529 needs to be modified. Filtering DNs on the input side is rare,
530 so this avoids copying the parse tree in most cases */
531 filter_ctx->test_only = true;
532 filter_ctx->matched = false;
533 filter_ctx->module = module;
534 filter_ctx->req = req;
535 filter_ctx->schema = dsdb_get_schema(ldb_module_get_ctx(module), filter_ctx);
536 filter_ctx->dsdb_flags= default_dsdb_flags;
538 ret = ldb_parse_tree_walk(req->op.search.tree, extended_dn_filter_callback, filter_ctx);
539 if (ret != LDB_SUCCESS) {
540 talloc_free(filter_ctx);
544 if (!filter_ctx->matched) {
545 /* nothing matched, no need for a new parse tree */
546 talloc_free(filter_ctx);
550 filter_ctx->test_only = false;
551 filter_ctx->matched = false;
553 req->op.search.tree = ldb_parse_tree_copy_shallow(req, req->op.search.tree);
554 if (req->op.search.tree == NULL) {
555 return ldb_oom(ldb_module_get_ctx(module));
558 ret = ldb_parse_tree_walk(req->op.search.tree, extended_dn_filter_callback, filter_ctx);
559 if (ret != LDB_SUCCESS) {
560 talloc_free(filter_ctx);
564 talloc_free(filter_ctx);
569 fix DNs and filter expressions to cope with the semantics of
572 static int extended_dn_in_fix(struct ldb_module *module, struct ldb_request *req, struct ldb_dn *dn)
574 struct extended_search_context *ac;
575 struct ldb_request *down_req;
577 struct ldb_dn *base_dn = NULL;
578 enum ldb_scope base_dn_scope = LDB_SCOPE_BASE;
579 const char *base_dn_filter = NULL;
580 const char * const *base_dn_attrs = NULL;
581 char *wellknown_object = NULL;
582 static const char *no_attr[] = {
585 uint32_t dsdb_flags = DSDB_FLAG_AS_SYSTEM | DSDB_SEARCH_SHOW_EXTENDED_DN;
587 if (ldb_request_get_control(req, LDB_CONTROL_SHOW_DELETED_OID)) {
588 dsdb_flags |= DSDB_SEARCH_SHOW_DELETED;
590 if (ldb_request_get_control(req, LDB_CONTROL_SHOW_RECYCLED_OID)) {
591 dsdb_flags |= DSDB_SEARCH_SHOW_RECYCLED;
593 if (ldb_request_get_control(req, DSDB_CONTROL_DBCHECK)) {
594 dsdb_flags |= DSDB_SEARCH_SHOW_RECYCLED;
597 if (req->operation == LDB_SEARCH) {
598 ret = extended_dn_fix_filter(module, req, dsdb_flags);
599 if (ret != LDB_SUCCESS) {
604 if (!ldb_dn_has_extended(dn)) {
605 /* Move along there isn't anything to see here */
606 return ldb_next_request(module, req);
608 /* It looks like we need to map the DN */
609 const struct ldb_val *sid_val, *guid_val, *wkguid_val;
611 if (!ldb_dn_match_allowed(dn, req)) {
612 return ldb_error(ldb_module_get_ctx(module),
613 LDB_ERR_INVALID_DN_SYNTAX, "invalid number of DN components");
616 sid_val = ldb_dn_get_extended_component(dn, "SID");
617 guid_val = ldb_dn_get_extended_component(dn, "GUID");
618 wkguid_val = ldb_dn_get_extended_component(dn, "WKGUID");
621 prioritise the GUID - we have had instances of
622 duplicate SIDs in the database in the
623 ForeignSecurityPrinciples due to provision errors
626 dsdb_flags |= DSDB_SEARCH_SEARCH_ALL_PARTITIONS;
628 base_dn_filter = talloc_asprintf(req, "(objectGUID=%s)",
629 ldb_binary_encode(req, *guid_val));
630 if (!base_dn_filter) {
631 return ldb_oom(ldb_module_get_ctx(module));
633 base_dn_scope = LDB_SCOPE_SUBTREE;
634 base_dn_attrs = no_attr;
636 } else if (sid_val) {
637 dsdb_flags |= DSDB_SEARCH_SEARCH_ALL_PARTITIONS;
639 base_dn_filter = talloc_asprintf(req, "(objectSid=%s)",
640 ldb_binary_encode(req, *sid_val));
641 if (!base_dn_filter) {
642 return ldb_oom(ldb_module_get_ctx(module));
644 base_dn_scope = LDB_SCOPE_SUBTREE;
645 base_dn_attrs = no_attr;
647 } else if (wkguid_val) {
652 wkguid_dup = talloc_strndup(req, (char *)wkguid_val->data, wkguid_val->length);
654 p = strchr(wkguid_dup, ',');
656 return ldb_error(ldb_module_get_ctx(module), LDB_ERR_INVALID_DN_SYNTAX,
657 "Invalid WKGUID format");
663 wellknown_object = talloc_asprintf(req, "B:32:%s:", wkguid_dup);
664 if (!wellknown_object) {
665 return ldb_oom(ldb_module_get_ctx(module));
670 base_dn = ldb_dn_new(req, ldb_module_get_ctx(module), tail_str);
671 talloc_free(wkguid_dup);
673 return ldb_oom(ldb_module_get_ctx(module));
675 base_dn_filter = talloc_strdup(req, "(objectClass=*)");
676 if (!base_dn_filter) {
677 return ldb_oom(ldb_module_get_ctx(module));
679 base_dn_scope = LDB_SCOPE_BASE;
680 base_dn_attrs = wkattr;
682 return ldb_error(ldb_module_get_ctx(module), LDB_ERR_INVALID_DN_SYNTAX,
683 "Invalid extended DN component");
686 ac = talloc_zero(req, struct extended_search_context);
688 return ldb_oom(ldb_module_get_ctx(module));
694 ac->basedn = NULL; /* Filled in if the search finds the DN by SID/GUID etc */
695 ac->wellknown_object = wellknown_object;
697 /* If the base DN was an extended DN (perhaps a well known
698 * GUID) then search for that, so we can proceed with the original operation */
700 ret = ldb_build_search_req(&down_req,
701 ldb_module_get_ctx(module), ac,
707 ac, extended_base_callback,
709 LDB_REQ_SET_LOCATION(down_req);
710 if (ret != LDB_SUCCESS) {
711 return ldb_operr(ldb_module_get_ctx(module));
714 ret = dsdb_request_add_controls(down_req, dsdb_flags);
715 if (ret != LDB_SUCCESS) {
719 /* perform the search */
720 return ldb_next_request(module, down_req);
724 static int extended_dn_in_search(struct ldb_module *module, struct ldb_request *req)
726 return extended_dn_in_fix(module, req, req->op.search.base);
729 static int extended_dn_in_modify(struct ldb_module *module, struct ldb_request *req)
731 return extended_dn_in_fix(module, req, req->op.mod.message->dn);
734 static int extended_dn_in_del(struct ldb_module *module, struct ldb_request *req)
736 return extended_dn_in_fix(module, req, req->op.del.dn);
739 static int extended_dn_in_rename(struct ldb_module *module, struct ldb_request *req)
741 return extended_dn_in_fix(module, req, req->op.rename.olddn);
744 static const struct ldb_module_ops ldb_extended_dn_in_module_ops = {
745 .name = "extended_dn_in",
746 .search = extended_dn_in_search,
747 .modify = extended_dn_in_modify,
748 .del = extended_dn_in_del,
749 .rename = extended_dn_in_rename,
752 static const struct ldb_module_ops ldb_extended_dn_in_openldap_module_ops = {
753 .name = "extended_dn_in_openldap",
754 .search = extended_dn_in_search,
755 .modify = extended_dn_in_modify,
756 .del = extended_dn_in_del,
757 .rename = extended_dn_in_rename,
760 int ldb_extended_dn_in_module_init(const char *version)
763 LDB_MODULE_CHECK_VERSION(version);
764 ret = ldb_register_module(&ldb_extended_dn_in_openldap_module_ops);
765 if (ret != LDB_SUCCESS) {
768 return ldb_register_module(&ldb_extended_dn_in_module_ops);