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"
41 TODO: if relax is not set then we need to reject the fancy RMD_* and
42 DELETED extended DN codes
46 struct extended_search_context {
47 struct ldb_module *module;
48 struct ldb_request *req;
49 struct ldb_dn *basedn;
51 char *wellknown_object;
55 static const char *wkattr[] = {
57 "otherWellKnownObjects",
61 static const struct ldb_module_ops ldb_extended_dn_in_openldap_module_ops;
63 /* An extra layer of indirection because LDB does not allow the original request to be altered */
65 static int extended_final_callback(struct ldb_request *req, struct ldb_reply *ares)
67 int ret = LDB_ERR_OPERATIONS_ERROR;
68 struct extended_search_context *ac;
69 ac = talloc_get_type(req->context, struct extended_search_context);
71 if (ares->error != LDB_SUCCESS) {
72 ret = ldb_module_done(ac->req, ares->controls,
73 ares->response, ares->error);
78 ret = ldb_module_send_entry(ac->req, ares->message, ares->controls);
80 case LDB_REPLY_REFERRAL:
82 ret = ldb_module_send_referral(ac->req, ares->referral);
86 ret = ldb_module_done(ac->req, ares->controls,
87 ares->response, ares->error);
94 static int extended_base_callback(struct ldb_request *req, struct ldb_reply *ares)
96 struct extended_search_context *ac;
97 struct ldb_request *down_req;
98 struct ldb_message_element *el;
103 const char *found = NULL;
105 ac = talloc_get_type(req->context, struct extended_search_context);
108 return ldb_module_done(ac->req, NULL, NULL,
109 LDB_ERR_OPERATIONS_ERROR);
111 if (ares->error != LDB_SUCCESS) {
112 return ldb_module_done(ac->req, ares->controls,
113 ares->response, ares->error);
116 switch (ares->type) {
117 case LDB_REPLY_ENTRY:
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
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);
130 if (!ac->wellknown_object) {
131 ac->basedn = talloc_steal(ac, ares->message->dn);
135 wkn_len = strlen(ac->wellknown_object);
137 for (j=0; wkattr[j]; j++) {
139 el = ldb_msg_find_element(ares->message, wkattr[j]);
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);
150 ldb_oom(ldb_module_get_ctx(ac->module));
151 return ldb_module_done(ac->req, NULL, NULL,
152 LDB_ERR_OPERATIONS_ERROR);
155 if (strncasecmp(valstr, ac->wellknown_object, wkn_len) != 0) {
160 found = &valstr[wkn_len];
172 ac->basedn = ldb_dn_new(ac, ldb_module_get_ctx(ac->module), found);
175 ldb_oom(ldb_module_get_ctx(ac->module));
176 return ldb_module_done(ac->req, NULL, NULL,
177 LDB_ERR_OPERATIONS_ERROR);
182 case LDB_REPLY_REFERRAL:
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);
195 switch (ac->req->operation) {
197 ret = ldb_build_search_req_ex(&down_req,
198 ldb_module_get_ctx(ac->module), ac->req,
200 ac->req->op.search.scope,
201 ac->req->op.search.tree,
202 ac->req->op.search.attrs,
204 ac, extended_final_callback,
206 LDB_REQ_SET_LOCATION(down_req);
210 struct ldb_message *add_msg = ldb_msg_copy_shallow(ac, ac->req->op.add.message);
212 ldb_oom(ldb_module_get_ctx(ac->module));
213 return ldb_module_done(ac->req, NULL, NULL,
214 LDB_ERR_OPERATIONS_ERROR);
217 add_msg->dn = ac->basedn;
219 ret = ldb_build_add_req(&down_req,
220 ldb_module_get_ctx(ac->module), ac->req,
223 ac, extended_final_callback,
225 LDB_REQ_SET_LOCATION(down_req);
230 struct ldb_message *mod_msg = ldb_msg_copy_shallow(ac, ac->req->op.mod.message);
232 ldb_oom(ldb_module_get_ctx(ac->module));
233 return ldb_module_done(ac->req, NULL, NULL,
234 LDB_ERR_OPERATIONS_ERROR);
237 mod_msg->dn = ac->basedn;
239 ret = ldb_build_mod_req(&down_req,
240 ldb_module_get_ctx(ac->module), ac->req,
243 ac, extended_final_callback,
245 LDB_REQ_SET_LOCATION(down_req);
249 ret = ldb_build_del_req(&down_req,
250 ldb_module_get_ctx(ac->module), ac->req,
253 ac, extended_final_callback,
255 LDB_REQ_SET_LOCATION(down_req);
258 ret = ldb_build_rename_req(&down_req,
259 ldb_module_get_ctx(ac->module), ac->req,
261 ac->req->op.rename.newdn,
263 ac, extended_final_callback,
265 LDB_REQ_SET_LOCATION(down_req);
268 return ldb_module_done(ac->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
271 if (ret != LDB_SUCCESS) {
272 return ldb_module_done(ac->req, NULL, NULL, ret);
275 return ldb_next_request(ac->module, down_req);
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
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
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
299 static bool ldb_dn_match_allowed(struct ldb_dn *dn, struct ldb_request *req)
301 int num_components = ldb_dn_get_comp_num(dn);
302 int num_ex_components = ldb_dn_get_extended_comp_num(dn);
304 if (num_ex_components == 0) {
308 if ((num_components != 0 || num_ex_components != 1) &&
309 ldb_req_is_untrusted(req)) {
316 struct extended_dn_filter_ctx {
319 struct ldb_module *module;
320 struct ldb_request *req;
321 struct dsdb_schema *schema;
326 create a always non-matching node from a equality node
328 static void set_parse_tree_false(struct ldb_parse_tree *tree)
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;
340 called on all nodes in the parse tree
342 static int extended_dn_filter_callback(struct ldb_parse_tree *tree, void *private_context)
344 struct extended_dn_filter_ctx *filter_ctx;
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;
357 if (tree->operation != LDB_OP_EQUALITY && tree->operation != LDB_OP_EXTENDED) {
361 filter_ctx = talloc_get_type_abort(private_context, struct extended_dn_filter_ctx);
363 if (filter_ctx->test_only && filter_ctx->matched) {
364 /* the tree already matched */
368 if (!filter_ctx->schema) {
369 /* Schema not setup yet */
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);
377 if (attribute == NULL) {
381 if (attribute->dn_format != DSDB_NORMAL_DN) {
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);
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.
398 if (!has_extended_component) {
399 if (!attribute->one_way_link) {
403 if (ldb_module_get_ops(filter_ctx->module) == &ldb_extended_dn_in_openldap_module_ops) {
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);
415 /* testing against windows shows that we don't raise
420 guid_val = ldb_dn_get_extended_component(dn, "GUID");
421 sid_val = ldb_dn_get_extended_component(dn, "SID");
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 */
431 if (filter_ctx->test_only) {
432 /* we need to copy the tree */
433 filter_ctx->matched = true;
437 if (!ldb_dn_match_allowed(dn, filter_ctx->req)) {
438 /* we need to make this element of the filter always
440 set_parse_tree_false(tree);
444 dsdb_flags = filter_ctx->dsdb_flags | DSDB_FLAG_NEXT_MODULE;
447 expression = talloc_asprintf(filter_ctx, "objectGUID=%s", ldb_binary_encode(filter_ctx, *guid_val));
448 scope = LDB_SCOPE_SUBTREE;
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;
455 dsdb_flags |= DSDB_SEARCH_SEARCH_ALL_PARTITIONS;
457 /* fallback to searching using the string DN as the base DN */
458 expression = "objectClass=*";
460 scope = LDB_SCOPE_BASE;
463 ret = dsdb_module_search(filter_ctx->module,
472 if (scope == LDB_SCOPE_BASE && ret == LDB_ERR_NO_SUCH_OBJECT) {
473 /* note that this will need to change for multi-domain
475 set_parse_tree_false(tree);
479 if (ret != LDB_SUCCESS) {
484 if (res->count != 1) {
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));
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));
502 tree->u.extended.value.length = strlen((const char *)tree->u.extended.value.data);
506 filter_ctx->matched = true;
511 fix the parse tree to change any extended DN components to their
514 static int extended_dn_fix_filter(struct ldb_module *module,
515 struct ldb_request *req,
516 uint32_t default_dsdb_flags)
518 struct extended_dn_filter_ctx *filter_ctx;
521 filter_ctx = talloc_zero(req, struct extended_dn_filter_ctx);
522 if (filter_ctx == NULL) {
523 return ldb_module_oom(module);
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;
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);
542 if (!filter_ctx->matched) {
543 /* nothing matched, no need for a new parse tree */
544 talloc_free(filter_ctx);
548 filter_ctx->test_only = false;
549 filter_ctx->matched = false;
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));
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);
562 talloc_free(filter_ctx);
567 fix DNs and filter expressions to cope with the semantics of
570 static int extended_dn_in_fix(struct ldb_module *module, struct ldb_request *req, struct ldb_dn *dn)
572 struct extended_search_context *ac;
573 struct ldb_request *down_req;
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[] = {
583 uint32_t dsdb_flags = DSDB_FLAG_AS_SYSTEM | DSDB_SEARCH_SHOW_EXTENDED_DN;
585 if (ldb_request_get_control(req, LDB_CONTROL_SHOW_DELETED_OID)) {
586 dsdb_flags |= DSDB_SEARCH_SHOW_DELETED;
588 if (ldb_request_get_control(req, LDB_CONTROL_SHOW_RECYCLED_OID)) {
589 dsdb_flags |= DSDB_SEARCH_SHOW_RECYCLED;
591 if (ldb_request_get_control(req, DSDB_CONTROL_DBCHECK)) {
592 dsdb_flags |= DSDB_SEARCH_SHOW_RECYCLED;
595 if (req->operation == LDB_SEARCH) {
596 ret = extended_dn_fix_filter(module, req, dsdb_flags);
597 if (ret != LDB_SUCCESS) {
602 if (!ldb_dn_has_extended(dn)) {
603 /* Move along there isn't anything to see here */
604 return ldb_next_request(module, req);
606 /* It looks like we need to map the DN */
607 const struct ldb_val *sid_val, *guid_val, *wkguid_val;
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");
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");
619 prioritise the GUID - we have had instances of
620 duplicate SIDs in the database in the
621 ForeignSecurityPrinciples due to provision errors
624 dsdb_flags |= DSDB_SEARCH_SEARCH_ALL_PARTITIONS;
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));
631 base_dn_scope = LDB_SCOPE_SUBTREE;
632 base_dn_attrs = no_attr;
634 } else if (sid_val) {
635 dsdb_flags |= DSDB_SEARCH_SEARCH_ALL_PARTITIONS;
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));
642 base_dn_scope = LDB_SCOPE_SUBTREE;
643 base_dn_attrs = no_attr;
645 } else if (wkguid_val) {
650 wkguid_dup = talloc_strndup(req, (char *)wkguid_val->data, wkguid_val->length);
652 p = strchr(wkguid_dup, ',');
654 return ldb_error(ldb_module_get_ctx(module), LDB_ERR_INVALID_DN_SYNTAX,
655 "Invalid WKGUID format");
661 wellknown_object = talloc_asprintf(req, "B:32:%s:", wkguid_dup);
662 if (!wellknown_object) {
663 return ldb_oom(ldb_module_get_ctx(module));
668 base_dn = ldb_dn_new(req, ldb_module_get_ctx(module), tail_str);
669 talloc_free(wkguid_dup);
671 return ldb_oom(ldb_module_get_ctx(module));
673 base_dn_filter = talloc_strdup(req, "(objectClass=*)");
674 if (!base_dn_filter) {
675 return ldb_oom(ldb_module_get_ctx(module));
677 base_dn_scope = LDB_SCOPE_BASE;
678 base_dn_attrs = wkattr;
680 return ldb_error(ldb_module_get_ctx(module), LDB_ERR_INVALID_DN_SYNTAX,
681 "Invalid extended DN component");
684 ac = talloc_zero(req, struct extended_search_context);
686 return ldb_oom(ldb_module_get_ctx(module));
692 ac->basedn = NULL; /* Filled in if the search finds the DN by SID/GUID etc */
693 ac->wellknown_object = wellknown_object;
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 */
698 ret = ldb_build_search_req(&down_req,
699 ldb_module_get_ctx(module), ac,
705 ac, extended_base_callback,
707 LDB_REQ_SET_LOCATION(down_req);
708 if (ret != LDB_SUCCESS) {
709 return ldb_operr(ldb_module_get_ctx(module));
712 ret = dsdb_request_add_controls(down_req, dsdb_flags);
713 if (ret != LDB_SUCCESS) {
717 /* perform the search */
718 return ldb_next_request(module, down_req);
722 static int extended_dn_in_search(struct ldb_module *module, struct ldb_request *req)
724 return extended_dn_in_fix(module, req, req->op.search.base);
727 static int extended_dn_in_modify(struct ldb_module *module, struct ldb_request *req)
729 return extended_dn_in_fix(module, req, req->op.mod.message->dn);
732 static int extended_dn_in_del(struct ldb_module *module, struct ldb_request *req)
734 return extended_dn_in_fix(module, req, req->op.del.dn);
737 static int extended_dn_in_rename(struct ldb_module *module, struct ldb_request *req)
739 return extended_dn_in_fix(module, req, req->op.rename.olddn);
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,
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,
758 int ldb_extended_dn_in_module_init(const char *version)
761 LDB_MODULE_CHECK_VERSION(version);
762 ret = ldb_register_module(&ldb_extended_dn_in_openldap_module_ops);
763 if (ret != LDB_SUCCESS) {
766 return ldb_register_module(&ldb_extended_dn_in_module_ops);