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",
59 /* An extra layer of indirection because LDB does not allow the original request to be altered */
61 static int extended_final_callback(struct ldb_request *req, struct ldb_reply *ares)
63 int ret = LDB_ERR_OPERATIONS_ERROR;
64 struct extended_search_context *ac;
65 ac = talloc_get_type(req->context, struct extended_search_context);
67 if (ares->error != LDB_SUCCESS) {
68 ret = ldb_module_done(ac->req, ares->controls,
69 ares->response, ares->error);
74 ret = ldb_module_send_entry(ac->req, ares->message, ares->controls);
76 case LDB_REPLY_REFERRAL:
78 ret = ldb_module_send_referral(ac->req, ares->referral);
82 ret = ldb_module_done(ac->req, ares->controls,
83 ares->response, ares->error);
90 static int extended_base_callback(struct ldb_request *req, struct ldb_reply *ares)
92 struct extended_search_context *ac;
93 struct ldb_request *down_req;
94 struct ldb_message_element *el;
99 const char *found = NULL;
101 ac = talloc_get_type(req->context, struct extended_search_context);
104 return ldb_module_done(ac->req, NULL, NULL,
105 LDB_ERR_OPERATIONS_ERROR);
107 if (ares->error != LDB_SUCCESS) {
108 return ldb_module_done(ac->req, ares->controls,
109 ares->response, ares->error);
112 switch (ares->type) {
113 case LDB_REPLY_ENTRY:
115 /* we have more than one match! This can
116 happen as S-1-5-17 appears twice in a
117 normal provision. We need to return
119 const char *str = talloc_asprintf(req, "Duplicate base-DN matches found for '%s'",
120 ldb_dn_get_extended_linearized(req, ac->dn, 1));
121 ldb_set_errstring(ldb_module_get_ctx(ac->module), str);
122 return ldb_module_done(ac->req, NULL, NULL,
123 LDB_ERR_NO_SUCH_OBJECT);
126 if (!ac->wellknown_object) {
127 ac->basedn = talloc_steal(ac, ares->message->dn);
131 wkn_len = strlen(ac->wellknown_object);
133 for (j=0; wkattr[j]; j++) {
135 el = ldb_msg_find_element(ares->message, wkattr[j]);
141 for (i=0; i < el->num_values; i++) {
142 valstr = talloc_strndup(ac,
143 (const char *)el->values[i].data,
144 el->values[i].length);
146 ldb_oom(ldb_module_get_ctx(ac->module));
147 return ldb_module_done(ac->req, NULL, NULL,
148 LDB_ERR_OPERATIONS_ERROR);
151 if (strncasecmp(valstr, ac->wellknown_object, wkn_len) != 0) {
156 found = &valstr[wkn_len];
168 ac->basedn = ldb_dn_new(ac, ldb_module_get_ctx(ac->module), found);
171 ldb_oom(ldb_module_get_ctx(ac->module));
172 return ldb_module_done(ac->req, NULL, NULL,
173 LDB_ERR_OPERATIONS_ERROR);
178 case LDB_REPLY_REFERRAL:
184 const char *str = talloc_asprintf(req, "Base-DN '%s' not found",
185 ldb_dn_get_extended_linearized(req, ac->dn, 1));
186 ldb_set_errstring(ldb_module_get_ctx(ac->module), str);
187 return ldb_module_done(ac->req, NULL, NULL,
188 LDB_ERR_NO_SUCH_OBJECT);
191 switch (ac->req->operation) {
193 ret = ldb_build_search_req_ex(&down_req,
194 ldb_module_get_ctx(ac->module), ac->req,
196 ac->req->op.search.scope,
197 ac->req->op.search.tree,
198 ac->req->op.search.attrs,
200 ac, extended_final_callback,
202 LDB_REQ_SET_LOCATION(down_req);
206 struct ldb_message *add_msg = ldb_msg_copy_shallow(ac, ac->req->op.add.message);
208 ldb_oom(ldb_module_get_ctx(ac->module));
209 return ldb_module_done(ac->req, NULL, NULL,
210 LDB_ERR_OPERATIONS_ERROR);
213 add_msg->dn = ac->basedn;
215 ret = ldb_build_add_req(&down_req,
216 ldb_module_get_ctx(ac->module), ac->req,
219 ac, extended_final_callback,
221 LDB_REQ_SET_LOCATION(down_req);
226 struct ldb_message *mod_msg = ldb_msg_copy_shallow(ac, ac->req->op.mod.message);
228 ldb_oom(ldb_module_get_ctx(ac->module));
229 return ldb_module_done(ac->req, NULL, NULL,
230 LDB_ERR_OPERATIONS_ERROR);
233 mod_msg->dn = ac->basedn;
235 ret = ldb_build_mod_req(&down_req,
236 ldb_module_get_ctx(ac->module), ac->req,
239 ac, extended_final_callback,
241 LDB_REQ_SET_LOCATION(down_req);
245 ret = ldb_build_del_req(&down_req,
246 ldb_module_get_ctx(ac->module), ac->req,
249 ac, extended_final_callback,
251 LDB_REQ_SET_LOCATION(down_req);
254 ret = ldb_build_rename_req(&down_req,
255 ldb_module_get_ctx(ac->module), ac->req,
257 ac->req->op.rename.newdn,
259 ac, extended_final_callback,
261 LDB_REQ_SET_LOCATION(down_req);
264 return ldb_module_done(ac->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
267 if (ret != LDB_SUCCESS) {
268 return ldb_module_done(ac->req, NULL, NULL, ret);
271 return ldb_next_request(ac->module, down_req);
279 windows ldap searchs don't allow a baseDN with more
280 than one extended component, or an extended
281 component and a string DN
283 We only enforce this over ldap, not for internal
284 use, as there are just too many places where we
285 internally want to use a DN that has come from a
286 search with extended DN enabled, or comes from a DRS
289 Enforcing this would also make debugging samba much
290 harder, as we'd need to use ldb_dn_minimise() in a
291 lot of places, and that would lose the DN string
292 which is so useful for working out what a request is
295 static bool ldb_dn_match_allowed(struct ldb_dn *dn, struct ldb_request *req)
297 int num_components = ldb_dn_get_comp_num(dn);
298 int num_ex_components = ldb_dn_get_extended_comp_num(dn);
300 if (num_ex_components == 0) {
304 if ((num_components != 0 || num_ex_components != 1) &&
305 ldb_req_is_untrusted(req)) {
312 struct extended_dn_filter_ctx {
315 struct ldb_module *module;
316 struct ldb_request *req;
317 struct dsdb_schema *schema;
321 create a always non-matching node from a equality node
323 static void set_parse_tree_false(struct ldb_parse_tree *tree)
325 const char *attr = tree->u.equality.attr;
326 struct ldb_val value = tree->u.equality.value;
327 tree->operation = LDB_OP_EXTENDED;
328 tree->u.extended.attr = attr;
329 tree->u.extended.value = value;
330 tree->u.extended.rule_id = SAMBA_LDAP_MATCH_ALWAYS_FALSE;
331 tree->u.extended.dnAttributes = 0;
335 called on all nodes in the parse tree
337 static int extended_dn_filter_callback(struct ldb_parse_tree *tree, void *private_context)
339 struct extended_dn_filter_ctx *filter_ctx;
342 const struct ldb_val *sid_val, *guid_val;
343 const char *no_attrs[] = { NULL };
344 struct ldb_result *res;
345 const struct dsdb_attribute *attribute;
346 bool has_extended_component;
347 enum ldb_scope scope;
348 struct ldb_dn *base_dn;
349 const char *expression;
352 if (tree->operation != LDB_OP_EQUALITY) {
356 filter_ctx = talloc_get_type_abort(private_context, struct extended_dn_filter_ctx);
358 if (filter_ctx->test_only && filter_ctx->matched) {
359 /* the tree already matched */
363 attribute = dsdb_attribute_by_lDAPDisplayName(filter_ctx->schema, tree->u.equality.attr);
364 if (attribute == NULL) {
368 if (attribute->dn_format != DSDB_NORMAL_DN) {
372 has_extended_component = (memchr(tree->u.equality.value.data, '<',
373 tree->u.equality.value.length) != NULL);
375 if (!attribute->one_way_link && !has_extended_component) {
379 dn = ldb_dn_from_ldb_val(filter_ctx, ldb_module_get_ctx(filter_ctx->module), &tree->u.equality.value);
381 /* testing against windows shows that we don't raise
386 guid_val = ldb_dn_get_extended_component(dn, "GUID");
387 sid_val = ldb_dn_get_extended_component(dn, "SID");
389 if (!guid_val && !sid_val && (attribute->searchFlags & SEARCH_FLAG_ATTINDEX)) {
390 /* if it is indexed, then fixing the string DN will do
391 no good here, as we will not find the attribute in
392 the index. So for now fall through to a standard DN
393 component comparison */
397 if (filter_ctx->test_only) {
398 /* we need to copy the tree */
399 filter_ctx->matched = true;
403 if (!ldb_dn_match_allowed(dn, filter_ctx->req)) {
404 /* we need to make this element of the filter always
406 set_parse_tree_false(tree);
410 dsdb_flags = DSDB_FLAG_NEXT_MODULE |
411 DSDB_SEARCH_SHOW_DELETED |
412 DSDB_SEARCH_SHOW_EXTENDED_DN;
415 expression = talloc_asprintf(filter_ctx, "objectGUID=%s", ldb_binary_encode(filter_ctx, *guid_val));
416 scope = LDB_SCOPE_SUBTREE;
418 dsdb_flags |= DSDB_SEARCH_SEARCH_ALL_PARTITIONS;
419 } else if (sid_val) {
420 expression = talloc_asprintf(filter_ctx, "objectSID=%s", ldb_binary_encode(filter_ctx, *sid_val));
421 scope = LDB_SCOPE_SUBTREE;
423 dsdb_flags |= DSDB_SEARCH_SEARCH_ALL_PARTITIONS;
425 /* fallback to searching using the string DN as the base DN */
426 expression = "objectClass=*";
428 scope = LDB_SCOPE_BASE;
431 ret = dsdb_module_search(filter_ctx->module,
440 if (scope == LDB_SCOPE_BASE && ret == LDB_ERR_NO_SUCH_OBJECT) {
441 /* note that this will need to change for multi-domain
443 set_parse_tree_false(tree);
447 if (ret != LDB_SUCCESS) {
452 if (res->count != 1) {
456 /* replace the search expression element with the matching DN */
457 tree->u.equality.value.data = (uint8_t *)talloc_strdup(tree,
458 ldb_dn_get_extended_linearized(tree, res->msgs[0]->dn, 1));
459 if (tree->u.equality.value.data == NULL) {
460 return ldb_oom(ldb_module_get_ctx(filter_ctx->module));
462 tree->u.equality.value.length = strlen((const char *)tree->u.equality.value.data);
465 filter_ctx->matched = true;
470 fix the parse tree to change any extended DN components to their
473 static int extended_dn_fix_filter(struct ldb_module *module, struct ldb_request *req)
475 struct extended_dn_filter_ctx *filter_ctx;
478 filter_ctx = talloc_zero(req, struct extended_dn_filter_ctx);
479 if (filter_ctx == NULL) {
480 return ldb_module_oom(module);
483 /* first pass through the existing tree to see if anything
484 needs to be modified. Filtering DNs on the input side is rare,
485 so this avoids copying the parse tree in most cases */
486 filter_ctx->test_only = true;
487 filter_ctx->matched = false;
488 filter_ctx->module = module;
489 filter_ctx->req = req;
490 filter_ctx->schema = dsdb_get_schema(ldb_module_get_ctx(module), filter_ctx);
492 ret = ldb_parse_tree_walk(req->op.search.tree, extended_dn_filter_callback, filter_ctx);
493 if (ret != LDB_SUCCESS) {
494 talloc_free(filter_ctx);
498 if (!filter_ctx->matched) {
499 /* nothing matched, no need for a new parse tree */
500 talloc_free(filter_ctx);
504 filter_ctx->test_only = false;
505 filter_ctx->matched = false;
507 req->op.search.tree = ldb_parse_tree_copy_shallow(req, req->op.search.tree);
508 if (req->op.search.tree == NULL) {
509 return ldb_oom(ldb_module_get_ctx(module));
512 ret = ldb_parse_tree_walk(req->op.search.tree, extended_dn_filter_callback, filter_ctx);
513 if (ret != LDB_SUCCESS) {
514 talloc_free(filter_ctx);
518 talloc_free(filter_ctx);
523 fix DNs and filter expressions to cope with the semantics of
526 static int extended_dn_in_fix(struct ldb_module *module, struct ldb_request *req, struct ldb_dn *dn)
528 struct extended_search_context *ac;
529 struct ldb_request *down_req;
531 struct ldb_dn *base_dn = NULL;
532 enum ldb_scope base_dn_scope = LDB_SCOPE_BASE;
533 const char *base_dn_filter = NULL;
534 const char * const *base_dn_attrs = NULL;
535 char *wellknown_object = NULL;
536 static const char *no_attr[] = {
539 bool all_partitions = false;
541 if (req->operation == LDB_SEARCH) {
542 ret = extended_dn_fix_filter(module, req);
543 if (ret != LDB_SUCCESS) {
548 if (!ldb_dn_has_extended(dn)) {
549 /* Move along there isn't anything to see here */
550 return ldb_next_request(module, req);
552 /* It looks like we need to map the DN */
553 const struct ldb_val *sid_val, *guid_val, *wkguid_val;
555 if (!ldb_dn_match_allowed(dn, req)) {
556 return ldb_error(ldb_module_get_ctx(module),
557 LDB_ERR_INVALID_DN_SYNTAX, "invalid number of DN components");
560 sid_val = ldb_dn_get_extended_component(dn, "SID");
561 guid_val = ldb_dn_get_extended_component(dn, "GUID");
562 wkguid_val = ldb_dn_get_extended_component(dn, "WKGUID");
565 prioritise the GUID - we have had instances of
566 duplicate SIDs in the database in the
567 ForeignSecurityPrinciples due to provision errors
570 all_partitions = true;
572 base_dn_filter = talloc_asprintf(req, "(objectGUID=%s)",
573 ldb_binary_encode(req, *guid_val));
574 if (!base_dn_filter) {
575 return ldb_oom(ldb_module_get_ctx(module));
577 base_dn_scope = LDB_SCOPE_SUBTREE;
578 base_dn_attrs = no_attr;
580 } else if (sid_val) {
581 all_partitions = true;
583 base_dn_filter = talloc_asprintf(req, "(objectSid=%s)",
584 ldb_binary_encode(req, *sid_val));
585 if (!base_dn_filter) {
586 return ldb_oom(ldb_module_get_ctx(module));
588 base_dn_scope = LDB_SCOPE_SUBTREE;
589 base_dn_attrs = no_attr;
591 } else if (wkguid_val) {
596 wkguid_dup = talloc_strndup(req, (char *)wkguid_val->data, wkguid_val->length);
598 p = strchr(wkguid_dup, ',');
600 return ldb_error(ldb_module_get_ctx(module), LDB_ERR_INVALID_DN_SYNTAX,
601 "Invalid WKGUID format");
607 wellknown_object = talloc_asprintf(req, "B:32:%s:", wkguid_dup);
608 if (!wellknown_object) {
609 return ldb_oom(ldb_module_get_ctx(module));
614 base_dn = ldb_dn_new(req, ldb_module_get_ctx(module), tail_str);
615 talloc_free(wkguid_dup);
617 return ldb_oom(ldb_module_get_ctx(module));
619 base_dn_filter = talloc_strdup(req, "(objectClass=*)");
620 if (!base_dn_filter) {
621 return ldb_oom(ldb_module_get_ctx(module));
623 base_dn_scope = LDB_SCOPE_BASE;
624 base_dn_attrs = wkattr;
626 return ldb_error(ldb_module_get_ctx(module), LDB_ERR_INVALID_DN_SYNTAX,
627 "Invalid extended DN component");
630 ac = talloc_zero(req, struct extended_search_context);
632 return ldb_oom(ldb_module_get_ctx(module));
638 ac->basedn = NULL; /* Filled in if the search finds the DN by SID/GUID etc */
639 ac->wellknown_object = wellknown_object;
641 /* If the base DN was an extended DN (perhaps a well known
642 * GUID) then search for that, so we can proceed with the original operation */
644 ret = ldb_build_search_req(&down_req,
645 ldb_module_get_ctx(module), ac,
651 ac, extended_base_callback,
653 LDB_REQ_SET_LOCATION(down_req);
654 if (ret != LDB_SUCCESS) {
655 return ldb_operr(ldb_module_get_ctx(module));
658 if (all_partitions) {
659 struct ldb_search_options_control *control;
660 control = talloc(down_req, struct ldb_search_options_control);
661 control->search_options = 2;
662 ret = ldb_request_replace_control(down_req,
663 LDB_CONTROL_SEARCH_OPTIONS_OID,
665 if (ret != LDB_SUCCESS) {
666 ldb_oom(ldb_module_get_ctx(module));
671 /* perform the search */
672 return ldb_next_request(module, down_req);
676 static int extended_dn_in_search(struct ldb_module *module, struct ldb_request *req)
678 return extended_dn_in_fix(module, req, req->op.search.base);
681 static int extended_dn_in_modify(struct ldb_module *module, struct ldb_request *req)
683 return extended_dn_in_fix(module, req, req->op.mod.message->dn);
686 static int extended_dn_in_del(struct ldb_module *module, struct ldb_request *req)
688 return extended_dn_in_fix(module, req, req->op.del.dn);
691 static int extended_dn_in_rename(struct ldb_module *module, struct ldb_request *req)
693 return extended_dn_in_fix(module, req, req->op.rename.olddn);
696 static const struct ldb_module_ops ldb_extended_dn_in_module_ops = {
697 .name = "extended_dn_in",
698 .search = extended_dn_in_search,
699 .modify = extended_dn_in_modify,
700 .del = extended_dn_in_del,
701 .rename = extended_dn_in_rename,
704 int ldb_extended_dn_in_module_init(const char *version)
706 LDB_MODULE_CHECK_VERSION(version);
707 return ldb_register_module(&ldb_extended_dn_in_module_ops);