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;
324 create a always non-matching node from a equality node
326 static void set_parse_tree_false(struct ldb_parse_tree *tree)
328 const char *attr = tree->u.equality.attr;
329 struct ldb_val value = tree->u.equality.value;
330 tree->operation = LDB_OP_EXTENDED;
331 tree->u.extended.attr = attr;
332 tree->u.extended.value = value;
333 tree->u.extended.rule_id = SAMBA_LDAP_MATCH_ALWAYS_FALSE;
334 tree->u.extended.dnAttributes = 0;
338 called on all nodes in the parse tree
340 static int extended_dn_filter_callback(struct ldb_parse_tree *tree, void *private_context)
342 struct extended_dn_filter_ctx *filter_ctx;
345 const struct ldb_val *sid_val, *guid_val;
346 const char *no_attrs[] = { NULL };
347 struct ldb_result *res;
348 const struct dsdb_attribute *attribute;
349 bool has_extended_component;
350 enum ldb_scope scope;
351 struct ldb_dn *base_dn;
352 const char *expression;
355 if (tree->operation != LDB_OP_EQUALITY) {
359 filter_ctx = talloc_get_type_abort(private_context, struct extended_dn_filter_ctx);
361 if (filter_ctx->test_only && filter_ctx->matched) {
362 /* the tree already matched */
366 if (!filter_ctx->schema) {
367 /* Schema not setup yet */
370 attribute = dsdb_attribute_by_lDAPDisplayName(filter_ctx->schema, tree->u.equality.attr);
371 if (attribute == NULL) {
375 if (attribute->dn_format != DSDB_NORMAL_DN) {
379 has_extended_component = (memchr(tree->u.equality.value.data, '<',
380 tree->u.equality.value.length) != NULL);
383 * Don't turn it into an extended DN if we're talking to OpenLDAP.
384 * We just check the module_ops pointer instead of adding a private
385 * pointer and a boolean to tell us the exact same thing.
387 if (!has_extended_component) {
388 if (!attribute->one_way_link ||
389 ldb_module_get_ops(filter_ctx->module) == &ldb_extended_dn_in_openldap_module_ops)
393 dn = ldb_dn_from_ldb_val(filter_ctx, ldb_module_get_ctx(filter_ctx->module), &tree->u.equality.value);
395 /* testing against windows shows that we don't raise
400 guid_val = ldb_dn_get_extended_component(dn, "GUID");
401 sid_val = ldb_dn_get_extended_component(dn, "SID");
403 if (!guid_val && !sid_val && (attribute->searchFlags & SEARCH_FLAG_ATTINDEX)) {
404 /* if it is indexed, then fixing the string DN will do
405 no good here, as we will not find the attribute in
406 the index. So for now fall through to a standard DN
407 component comparison */
411 if (filter_ctx->test_only) {
412 /* we need to copy the tree */
413 filter_ctx->matched = true;
417 if (!ldb_dn_match_allowed(dn, filter_ctx->req)) {
418 /* we need to make this element of the filter always
420 set_parse_tree_false(tree);
424 dsdb_flags = DSDB_FLAG_NEXT_MODULE |
425 DSDB_FLAG_AS_SYSTEM |
426 DSDB_SEARCH_SHOW_RECYCLED |
427 DSDB_SEARCH_SHOW_EXTENDED_DN;
430 expression = talloc_asprintf(filter_ctx, "objectGUID=%s", ldb_binary_encode(filter_ctx, *guid_val));
431 scope = LDB_SCOPE_SUBTREE;
433 dsdb_flags |= DSDB_SEARCH_SEARCH_ALL_PARTITIONS;
434 } else if (sid_val) {
435 expression = talloc_asprintf(filter_ctx, "objectSID=%s", ldb_binary_encode(filter_ctx, *sid_val));
436 scope = LDB_SCOPE_SUBTREE;
438 dsdb_flags |= DSDB_SEARCH_SEARCH_ALL_PARTITIONS;
440 /* fallback to searching using the string DN as the base DN */
441 expression = "objectClass=*";
443 scope = LDB_SCOPE_BASE;
446 ret = dsdb_module_search(filter_ctx->module,
455 if (scope == LDB_SCOPE_BASE && ret == LDB_ERR_NO_SUCH_OBJECT) {
456 /* note that this will need to change for multi-domain
458 set_parse_tree_false(tree);
462 if (ret != LDB_SUCCESS) {
467 if (res->count != 1) {
471 /* replace the search expression element with the matching DN */
472 tree->u.equality.value.data = (uint8_t *)talloc_strdup(tree,
473 ldb_dn_get_extended_linearized(tree, res->msgs[0]->dn, 1));
474 if (tree->u.equality.value.data == NULL) {
475 return ldb_oom(ldb_module_get_ctx(filter_ctx->module));
477 tree->u.equality.value.length = strlen((const char *)tree->u.equality.value.data);
480 filter_ctx->matched = true;
485 fix the parse tree to change any extended DN components to their
488 static int extended_dn_fix_filter(struct ldb_module *module, struct ldb_request *req)
490 struct extended_dn_filter_ctx *filter_ctx;
493 filter_ctx = talloc_zero(req, struct extended_dn_filter_ctx);
494 if (filter_ctx == NULL) {
495 return ldb_module_oom(module);
498 /* first pass through the existing tree to see if anything
499 needs to be modified. Filtering DNs on the input side is rare,
500 so this avoids copying the parse tree in most cases */
501 filter_ctx->test_only = true;
502 filter_ctx->matched = false;
503 filter_ctx->module = module;
504 filter_ctx->req = req;
505 filter_ctx->schema = dsdb_get_schema(ldb_module_get_ctx(module), filter_ctx);
507 ret = ldb_parse_tree_walk(req->op.search.tree, extended_dn_filter_callback, filter_ctx);
508 if (ret != LDB_SUCCESS) {
509 talloc_free(filter_ctx);
513 if (!filter_ctx->matched) {
514 /* nothing matched, no need for a new parse tree */
515 talloc_free(filter_ctx);
519 filter_ctx->test_only = false;
520 filter_ctx->matched = false;
522 req->op.search.tree = ldb_parse_tree_copy_shallow(req, req->op.search.tree);
523 if (req->op.search.tree == NULL) {
524 return ldb_oom(ldb_module_get_ctx(module));
527 ret = ldb_parse_tree_walk(req->op.search.tree, extended_dn_filter_callback, filter_ctx);
528 if (ret != LDB_SUCCESS) {
529 talloc_free(filter_ctx);
533 talloc_free(filter_ctx);
538 fix DNs and filter expressions to cope with the semantics of
541 static int extended_dn_in_fix(struct ldb_module *module, struct ldb_request *req, struct ldb_dn *dn)
543 struct extended_search_context *ac;
544 struct ldb_request *down_req;
546 struct ldb_dn *base_dn = NULL;
547 enum ldb_scope base_dn_scope = LDB_SCOPE_BASE;
548 const char *base_dn_filter = NULL;
549 const char * const *base_dn_attrs = NULL;
550 char *wellknown_object = NULL;
551 static const char *no_attr[] = {
554 bool all_partitions = false;
556 if (req->operation == LDB_SEARCH) {
557 ret = extended_dn_fix_filter(module, req);
558 if (ret != LDB_SUCCESS) {
563 if (!ldb_dn_has_extended(dn)) {
564 /* Move along there isn't anything to see here */
565 return ldb_next_request(module, req);
567 /* It looks like we need to map the DN */
568 const struct ldb_val *sid_val, *guid_val, *wkguid_val;
569 uint32_t dsdb_flags = 0;
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
586 all_partitions = true;
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 all_partitions = true;
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) {
612 wkguid_dup = talloc_strndup(req, (char *)wkguid_val->data, wkguid_val->length);
614 p = strchr(wkguid_dup, ',');
616 return ldb_error(ldb_module_get_ctx(module), LDB_ERR_INVALID_DN_SYNTAX,
617 "Invalid WKGUID format");
623 wellknown_object = talloc_asprintf(req, "B:32:%s:", wkguid_dup);
624 if (!wellknown_object) {
625 return ldb_oom(ldb_module_get_ctx(module));
630 base_dn = ldb_dn_new(req, ldb_module_get_ctx(module), tail_str);
631 talloc_free(wkguid_dup);
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;
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);
648 return ldb_oom(ldb_module_get_ctx(module));
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,
667 ac, extended_base_callback,
669 LDB_REQ_SET_LOCATION(down_req);
670 if (ret != LDB_SUCCESS) {
671 return ldb_operr(ldb_module_get_ctx(module));
674 dsdb_flags = DSDB_FLAG_AS_SYSTEM |
675 DSDB_SEARCH_SHOW_RECYCLED |
676 DSDB_SEARCH_SHOW_EXTENDED_DN;
677 if (all_partitions) {
678 dsdb_flags |= DSDB_SEARCH_SEARCH_ALL_PARTITIONS;
681 ret = dsdb_request_add_controls(down_req, dsdb_flags);
682 if (ret != LDB_SUCCESS) {
686 /* perform the search */
687 return ldb_next_request(module, down_req);
691 static int extended_dn_in_search(struct ldb_module *module, struct ldb_request *req)
693 return extended_dn_in_fix(module, req, req->op.search.base);
696 static int extended_dn_in_modify(struct ldb_module *module, struct ldb_request *req)
698 return extended_dn_in_fix(module, req, req->op.mod.message->dn);
701 static int extended_dn_in_del(struct ldb_module *module, struct ldb_request *req)
703 return extended_dn_in_fix(module, req, req->op.del.dn);
706 static int extended_dn_in_rename(struct ldb_module *module, struct ldb_request *req)
708 return extended_dn_in_fix(module, req, req->op.rename.olddn);
711 static const struct ldb_module_ops ldb_extended_dn_in_module_ops = {
712 .name = "extended_dn_in",
713 .search = extended_dn_in_search,
714 .modify = extended_dn_in_modify,
715 .del = extended_dn_in_del,
716 .rename = extended_dn_in_rename,
719 static const struct ldb_module_ops ldb_extended_dn_in_openldap_module_ops = {
720 .name = "extended_dn_in_openldap",
721 .search = extended_dn_in_search,
722 .modify = extended_dn_in_modify,
723 .del = extended_dn_in_del,
724 .rename = extended_dn_in_rename,
727 int ldb_extended_dn_in_module_init(const char *version)
730 LDB_MODULE_CHECK_VERSION(version);
731 ret = ldb_register_module(&ldb_extended_dn_in_openldap_module_ops);
732 if (ret != LDB_SUCCESS) {
735 return ldb_register_module(&ldb_extended_dn_in_module_ops);