s3: fix compile of krb5 locator on Solaris
[Samba/gebeck_regimport.git] / source4 / dsdb / samdb / ldb_modules / extended_dn_in.c
blob541a2593f176c367952ccba62a9f05a32c1a6443
1 /*
2 ldb database library
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/>.
22 * Name: ldb
24 * Component: ldb extended dn control module
26 * Description: this module interprets DNs of the form <SID=S-1-2-4456> into normal DNs.
28 * Authors: Simo Sorce
29 * Andrew Bartlett
32 #include "includes.h"
33 #include <ldb.h>
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
44 /* search */
45 struct extended_search_context {
46 struct ldb_module *module;
47 struct ldb_request *req;
48 struct ldb_dn *basedn;
49 struct ldb_dn *dn;
50 char *wellknown_object;
51 int extended_type;
54 /* An extra layer of indirection because LDB does not allow the original request to be altered */
56 static int extended_final_callback(struct ldb_request *req, struct ldb_reply *ares)
58 int ret = LDB_ERR_OPERATIONS_ERROR;
59 struct extended_search_context *ac;
60 ac = talloc_get_type(req->context, struct extended_search_context);
62 if (ares->error != LDB_SUCCESS) {
63 ret = ldb_module_done(ac->req, ares->controls,
64 ares->response, ares->error);
65 } else {
66 switch (ares->type) {
67 case LDB_REPLY_ENTRY:
69 ret = ldb_module_send_entry(ac->req, ares->message, ares->controls);
70 break;
71 case LDB_REPLY_REFERRAL:
73 ret = ldb_module_send_referral(ac->req, ares->referral);
74 break;
75 case LDB_REPLY_DONE:
77 ret = ldb_module_done(ac->req, ares->controls,
78 ares->response, ares->error);
79 break;
82 return ret;
85 static int extended_base_callback(struct ldb_request *req, struct ldb_reply *ares)
87 struct extended_search_context *ac;
88 struct ldb_request *down_req;
89 struct ldb_message_element *el;
90 int ret;
91 unsigned int i;
92 size_t wkn_len = 0;
93 char *valstr = NULL;
94 const char *found = NULL;
96 ac = talloc_get_type(req->context, struct extended_search_context);
98 if (!ares) {
99 return ldb_module_done(ac->req, NULL, NULL,
100 LDB_ERR_OPERATIONS_ERROR);
102 if (ares->error != LDB_SUCCESS) {
103 return ldb_module_done(ac->req, ares->controls,
104 ares->response, ares->error);
107 switch (ares->type) {
108 case LDB_REPLY_ENTRY:
109 if (ac->basedn) {
110 /* we have more than one match! This can
111 happen as S-1-5-17 appears twice in a
112 normal provision. We need to return
113 NO_SUCH_OBJECT */
114 const char *str = talloc_asprintf(req, "Duplicate base-DN matches found for '%s'",
115 ldb_dn_get_extended_linearized(req, ac->dn, 1));
116 ldb_set_errstring(ldb_module_get_ctx(ac->module), str);
117 return ldb_module_done(ac->req, NULL, NULL,
118 LDB_ERR_NO_SUCH_OBJECT);
121 if (!ac->wellknown_object) {
122 ac->basedn = talloc_steal(ac, ares->message->dn);
123 break;
126 wkn_len = strlen(ac->wellknown_object);
128 el = ldb_msg_find_element(ares->message, "wellKnownObjects");
129 if (!el) {
130 ac->basedn = NULL;
131 break;
134 for (i=0; i < el->num_values; i++) {
135 valstr = talloc_strndup(ac,
136 (const char *)el->values[i].data,
137 el->values[i].length);
138 if (!valstr) {
139 ldb_oom(ldb_module_get_ctx(ac->module));
140 return ldb_module_done(ac->req, NULL, NULL,
141 LDB_ERR_OPERATIONS_ERROR);
144 if (strncasecmp(valstr, ac->wellknown_object, wkn_len) != 0) {
145 talloc_free(valstr);
146 continue;
149 found = &valstr[wkn_len];
150 break;
153 if (!found) {
154 break;
157 ac->basedn = ldb_dn_new(ac, ldb_module_get_ctx(ac->module), found);
158 talloc_free(valstr);
159 if (!ac->basedn) {
160 ldb_oom(ldb_module_get_ctx(ac->module));
161 return ldb_module_done(ac->req, NULL, NULL,
162 LDB_ERR_OPERATIONS_ERROR);
165 break;
167 case LDB_REPLY_REFERRAL:
168 break;
170 case LDB_REPLY_DONE:
172 if (!ac->basedn) {
173 const char *str = talloc_asprintf(req, "Base-DN '%s' not found",
174 ldb_dn_get_extended_linearized(req, ac->dn, 1));
175 ldb_set_errstring(ldb_module_get_ctx(ac->module), str);
176 return ldb_module_done(ac->req, NULL, NULL,
177 LDB_ERR_NO_SUCH_OBJECT);
180 switch (ac->req->operation) {
181 case LDB_SEARCH:
182 ret = ldb_build_search_req_ex(&down_req,
183 ldb_module_get_ctx(ac->module), ac->req,
184 ac->basedn,
185 ac->req->op.search.scope,
186 ac->req->op.search.tree,
187 ac->req->op.search.attrs,
188 ac->req->controls,
189 ac, extended_final_callback,
190 ac->req);
191 LDB_REQ_SET_LOCATION(down_req);
192 break;
193 case LDB_ADD:
195 struct ldb_message *add_msg = ldb_msg_copy_shallow(ac, ac->req->op.add.message);
196 if (!add_msg) {
197 ldb_oom(ldb_module_get_ctx(ac->module));
198 return ldb_module_done(ac->req, NULL, NULL,
199 LDB_ERR_OPERATIONS_ERROR);
202 add_msg->dn = ac->basedn;
204 ret = ldb_build_add_req(&down_req,
205 ldb_module_get_ctx(ac->module), ac->req,
206 add_msg,
207 ac->req->controls,
208 ac, extended_final_callback,
209 ac->req);
210 LDB_REQ_SET_LOCATION(down_req);
211 break;
213 case LDB_MODIFY:
215 struct ldb_message *mod_msg = ldb_msg_copy_shallow(ac, ac->req->op.mod.message);
216 if (!mod_msg) {
217 ldb_oom(ldb_module_get_ctx(ac->module));
218 return ldb_module_done(ac->req, NULL, NULL,
219 LDB_ERR_OPERATIONS_ERROR);
222 mod_msg->dn = ac->basedn;
224 ret = ldb_build_mod_req(&down_req,
225 ldb_module_get_ctx(ac->module), ac->req,
226 mod_msg,
227 ac->req->controls,
228 ac, extended_final_callback,
229 ac->req);
230 LDB_REQ_SET_LOCATION(down_req);
231 break;
233 case LDB_DELETE:
234 ret = ldb_build_del_req(&down_req,
235 ldb_module_get_ctx(ac->module), ac->req,
236 ac->basedn,
237 ac->req->controls,
238 ac, extended_final_callback,
239 ac->req);
240 LDB_REQ_SET_LOCATION(down_req);
241 break;
242 case LDB_RENAME:
243 ret = ldb_build_rename_req(&down_req,
244 ldb_module_get_ctx(ac->module), ac->req,
245 ac->basedn,
246 ac->req->op.rename.newdn,
247 ac->req->controls,
248 ac, extended_final_callback,
249 ac->req);
250 LDB_REQ_SET_LOCATION(down_req);
251 break;
252 default:
253 return ldb_module_done(ac->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
256 if (ret != LDB_SUCCESS) {
257 return ldb_module_done(ac->req, NULL, NULL, ret);
260 return ldb_next_request(ac->module, down_req);
262 talloc_free(ares);
263 return LDB_SUCCESS;
268 windows ldap searchs don't allow a baseDN with more
269 than one extended component, or an extended
270 component and a string DN
272 We only enforce this over ldap, not for internal
273 use, as there are just too many places where we
274 internally want to use a DN that has come from a
275 search with extended DN enabled, or comes from a DRS
276 naming context.
278 Enforcing this would also make debugging samba much
279 harder, as we'd need to use ldb_dn_minimise() in a
280 lot of places, and that would lose the DN string
281 which is so useful for working out what a request is
284 static bool ldb_dn_match_allowed(struct ldb_dn *dn, struct ldb_request *req)
286 int num_components = ldb_dn_get_comp_num(dn);
287 int num_ex_components = ldb_dn_get_extended_comp_num(dn);
289 if (num_ex_components == 0) {
290 return true;
293 if ((num_components != 0 || num_ex_components != 1) &&
294 ldb_req_is_untrusted(req)) {
295 return false;
297 return true;
301 struct extended_dn_filter_ctx {
302 bool test_only;
303 bool matched;
304 struct ldb_module *module;
305 struct ldb_request *req;
306 struct dsdb_schema *schema;
310 create a always non-matching node from a equality node
312 static void set_parse_tree_false(struct ldb_parse_tree *tree)
314 const char *attr = tree->u.equality.attr;
315 struct ldb_val value = tree->u.equality.value;
316 tree->operation = LDB_OP_EXTENDED;
317 tree->u.extended.attr = attr;
318 tree->u.extended.value = value;
319 tree->u.extended.rule_id = SAMBA_LDAP_MATCH_ALWAYS_FALSE;
320 tree->u.extended.dnAttributes = 0;
324 called on all nodes in the parse tree
326 static int extended_dn_filter_callback(struct ldb_parse_tree *tree, void *private_context)
328 struct extended_dn_filter_ctx *filter_ctx;
329 int ret;
330 struct ldb_dn *dn;
331 const struct ldb_val *sid_val, *guid_val;
332 const char *no_attrs[] = { NULL };
333 struct ldb_result *res;
334 const struct dsdb_attribute *attribute;
335 bool has_extended_component;
336 enum ldb_scope scope;
337 struct ldb_dn *base_dn;
338 const char *expression;
339 uint32_t dsdb_flags;
341 if (tree->operation != LDB_OP_EQUALITY) {
342 return LDB_SUCCESS;
345 filter_ctx = talloc_get_type_abort(private_context, struct extended_dn_filter_ctx);
347 if (filter_ctx->test_only && filter_ctx->matched) {
348 /* the tree already matched */
349 return LDB_SUCCESS;
352 attribute = dsdb_attribute_by_lDAPDisplayName(filter_ctx->schema, tree->u.equality.attr);
353 if (attribute == NULL) {
354 return LDB_SUCCESS;
357 if (attribute->dn_format != DSDB_NORMAL_DN) {
358 return LDB_SUCCESS;
361 has_extended_component = (memchr(tree->u.equality.value.data, '<',
362 tree->u.equality.value.length) != NULL);
364 if (!attribute->one_way_link && !has_extended_component) {
365 return LDB_SUCCESS;
368 dn = ldb_dn_from_ldb_val(filter_ctx, ldb_module_get_ctx(filter_ctx->module), &tree->u.equality.value);
369 if (dn == NULL) {
370 /* testing against windows shows that we don't raise
371 an error here */
372 return LDB_SUCCESS;
375 guid_val = ldb_dn_get_extended_component(dn, "GUID");
376 sid_val = ldb_dn_get_extended_component(dn, "SID");
378 if (!guid_val && !sid_val && (attribute->searchFlags & SEARCH_FLAG_ATTINDEX)) {
379 /* if it is indexed, then fixing the string DN will do
380 no good here, as we will not find the attribute in
381 the index. So for now fall through to a standard DN
382 component comparison */
383 return LDB_SUCCESS;
386 if (filter_ctx->test_only) {
387 /* we need to copy the tree */
388 filter_ctx->matched = true;
389 return LDB_SUCCESS;
392 if (!ldb_dn_match_allowed(dn, filter_ctx->req)) {
393 /* we need to make this element of the filter always
394 be false */
395 set_parse_tree_false(tree);
396 return LDB_SUCCESS;
399 dsdb_flags = DSDB_FLAG_NEXT_MODULE |
400 DSDB_SEARCH_SHOW_DELETED |
401 DSDB_SEARCH_SHOW_EXTENDED_DN;
403 if (guid_val) {
404 expression = talloc_asprintf(filter_ctx, "objectGUID=%s", ldb_binary_encode(filter_ctx, *guid_val));
405 scope = LDB_SCOPE_SUBTREE;
406 base_dn = NULL;
407 dsdb_flags |= DSDB_SEARCH_SEARCH_ALL_PARTITIONS;
408 } else if (sid_val) {
409 expression = talloc_asprintf(filter_ctx, "objectSID=%s", ldb_binary_encode(filter_ctx, *sid_val));
410 scope = LDB_SCOPE_SUBTREE;
411 base_dn = NULL;
412 dsdb_flags |= DSDB_SEARCH_SEARCH_ALL_PARTITIONS;
413 } else {
414 /* fallback to searching using the string DN as the base DN */
415 expression = "objectClass=*";
416 base_dn = dn;
417 scope = LDB_SCOPE_BASE;
420 ret = dsdb_module_search(filter_ctx->module,
421 filter_ctx,
422 &res,
423 base_dn,
424 scope,
425 no_attrs,
426 dsdb_flags,
427 filter_ctx->req,
428 "%s", expression);
429 if (scope == LDB_SCOPE_BASE && ret == LDB_ERR_NO_SUCH_OBJECT) {
430 /* note that this will need to change for multi-domain
431 support */
432 set_parse_tree_false(tree);
433 return LDB_SUCCESS;
436 if (ret != LDB_SUCCESS) {
437 return LDB_SUCCESS;
441 if (res->count != 1) {
442 return LDB_SUCCESS;
445 /* replace the search expression element with the matching DN */
446 tree->u.equality.value.data = (uint8_t *)talloc_strdup(tree,
447 ldb_dn_get_extended_linearized(tree, res->msgs[0]->dn, 1));
448 if (tree->u.equality.value.data == NULL) {
449 return ldb_oom(ldb_module_get_ctx(filter_ctx->module));
451 tree->u.equality.value.length = strlen((const char *)tree->u.equality.value.data);
452 talloc_free(res);
454 filter_ctx->matched = true;
455 return LDB_SUCCESS;
459 fix the parse tree to change any extended DN components to their
460 caconical form
462 static int extended_dn_fix_filter(struct ldb_module *module, struct ldb_request *req)
464 struct extended_dn_filter_ctx *filter_ctx;
465 int ret;
467 filter_ctx = talloc_zero(req, struct extended_dn_filter_ctx);
468 if (filter_ctx == NULL) {
469 return ldb_module_oom(module);
472 /* first pass through the existing tree to see if anything
473 needs to be modified. Filtering DNs on the input side is rare,
474 so this avoids copying the parse tree in most cases */
475 filter_ctx->test_only = true;
476 filter_ctx->matched = false;
477 filter_ctx->module = module;
478 filter_ctx->req = req;
479 filter_ctx->schema = dsdb_get_schema(ldb_module_get_ctx(module), filter_ctx);
481 ret = ldb_parse_tree_walk(req->op.search.tree, extended_dn_filter_callback, filter_ctx);
482 if (ret != LDB_SUCCESS) {
483 talloc_free(filter_ctx);
484 return ret;
487 if (!filter_ctx->matched) {
488 /* nothing matched, no need for a new parse tree */
489 talloc_free(filter_ctx);
490 return LDB_SUCCESS;
493 filter_ctx->test_only = false;
494 filter_ctx->matched = false;
496 req->op.search.tree = ldb_parse_tree_copy_shallow(req, req->op.search.tree);
497 if (req->op.search.tree == NULL) {
498 return ldb_oom(ldb_module_get_ctx(module));
501 ret = ldb_parse_tree_walk(req->op.search.tree, extended_dn_filter_callback, filter_ctx);
502 if (ret != LDB_SUCCESS) {
503 talloc_free(filter_ctx);
504 return ret;
507 talloc_free(filter_ctx);
508 return LDB_SUCCESS;
512 fix DNs and filter expressions to cope with the semantics of
513 extended DNs
515 static int extended_dn_in_fix(struct ldb_module *module, struct ldb_request *req, struct ldb_dn *dn)
517 struct extended_search_context *ac;
518 struct ldb_request *down_req;
519 int ret;
520 struct ldb_dn *base_dn = NULL;
521 enum ldb_scope base_dn_scope = LDB_SCOPE_BASE;
522 const char *base_dn_filter = NULL;
523 const char * const *base_dn_attrs = NULL;
524 char *wellknown_object = NULL;
525 static const char *no_attr[] = {
526 NULL
528 static const char *wkattr[] = {
529 "wellKnownObjects",
530 NULL
532 bool all_partitions = false;
534 if (req->operation == LDB_SEARCH) {
535 ret = extended_dn_fix_filter(module, req);
536 if (ret != LDB_SUCCESS) {
537 return ret;
541 if (!ldb_dn_has_extended(dn)) {
542 /* Move along there isn't anything to see here */
543 return ldb_next_request(module, req);
544 } else {
545 /* It looks like we need to map the DN */
546 const struct ldb_val *sid_val, *guid_val, *wkguid_val;
548 if (!ldb_dn_match_allowed(dn, req)) {
549 return ldb_error(ldb_module_get_ctx(module),
550 LDB_ERR_INVALID_DN_SYNTAX, "invalid number of DN components");
553 sid_val = ldb_dn_get_extended_component(dn, "SID");
554 guid_val = ldb_dn_get_extended_component(dn, "GUID");
555 wkguid_val = ldb_dn_get_extended_component(dn, "WKGUID");
558 prioritise the GUID - we have had instances of
559 duplicate SIDs in the database in the
560 ForeignSecurityPrinciples due to provision errors
562 if (guid_val) {
563 all_partitions = true;
564 base_dn = NULL;
565 base_dn_filter = talloc_asprintf(req, "(objectGUID=%s)",
566 ldb_binary_encode(req, *guid_val));
567 if (!base_dn_filter) {
568 return ldb_oom(ldb_module_get_ctx(module));
570 base_dn_scope = LDB_SCOPE_SUBTREE;
571 base_dn_attrs = no_attr;
573 } else if (sid_val) {
574 all_partitions = true;
575 base_dn = NULL;
576 base_dn_filter = talloc_asprintf(req, "(objectSid=%s)",
577 ldb_binary_encode(req, *sid_val));
578 if (!base_dn_filter) {
579 return ldb_oom(ldb_module_get_ctx(module));
581 base_dn_scope = LDB_SCOPE_SUBTREE;
582 base_dn_attrs = no_attr;
584 } else if (wkguid_val) {
585 char *wkguid_dup;
586 char *tail_str;
587 char *p;
589 wkguid_dup = talloc_strndup(req, (char *)wkguid_val->data, wkguid_val->length);
591 p = strchr(wkguid_dup, ',');
592 if (!p) {
593 return ldb_error(ldb_module_get_ctx(module), LDB_ERR_INVALID_DN_SYNTAX,
594 "Invalid WKGUID format");
597 p[0] = '\0';
598 p++;
600 wellknown_object = talloc_asprintf(req, "B:32:%s:", wkguid_dup);
601 if (!wellknown_object) {
602 return ldb_oom(ldb_module_get_ctx(module));
605 tail_str = p;
607 base_dn = ldb_dn_new(req, ldb_module_get_ctx(module), tail_str);
608 talloc_free(wkguid_dup);
609 if (!base_dn) {
610 return ldb_oom(ldb_module_get_ctx(module));
612 base_dn_filter = talloc_strdup(req, "(objectClass=*)");
613 if (!base_dn_filter) {
614 return ldb_oom(ldb_module_get_ctx(module));
616 base_dn_scope = LDB_SCOPE_BASE;
617 base_dn_attrs = wkattr;
618 } else {
619 return ldb_error(ldb_module_get_ctx(module), LDB_ERR_INVALID_DN_SYNTAX,
620 "Invalid extended DN component");
623 ac = talloc_zero(req, struct extended_search_context);
624 if (ac == NULL) {
625 return ldb_oom(ldb_module_get_ctx(module));
628 ac->module = module;
629 ac->req = req;
630 ac->dn = dn;
631 ac->basedn = NULL; /* Filled in if the search finds the DN by SID/GUID etc */
632 ac->wellknown_object = wellknown_object;
634 /* If the base DN was an extended DN (perhaps a well known
635 * GUID) then search for that, so we can proceed with the original operation */
637 ret = ldb_build_search_req(&down_req,
638 ldb_module_get_ctx(module), ac,
639 base_dn,
640 base_dn_scope,
641 base_dn_filter,
642 base_dn_attrs,
643 req->controls,
644 ac, extended_base_callback,
645 req);
646 LDB_REQ_SET_LOCATION(down_req);
647 if (ret != LDB_SUCCESS) {
648 return ldb_operr(ldb_module_get_ctx(module));
651 if (all_partitions) {
652 struct ldb_search_options_control *control;
653 control = talloc(down_req, struct ldb_search_options_control);
654 control->search_options = 2;
655 ret = ldb_request_replace_control(down_req,
656 LDB_CONTROL_SEARCH_OPTIONS_OID,
657 true, control);
658 if (ret != LDB_SUCCESS) {
659 ldb_oom(ldb_module_get_ctx(module));
660 return ret;
664 /* perform the search */
665 return ldb_next_request(module, down_req);
669 static int extended_dn_in_search(struct ldb_module *module, struct ldb_request *req)
671 return extended_dn_in_fix(module, req, req->op.search.base);
674 static int extended_dn_in_modify(struct ldb_module *module, struct ldb_request *req)
676 return extended_dn_in_fix(module, req, req->op.mod.message->dn);
679 static int extended_dn_in_del(struct ldb_module *module, struct ldb_request *req)
681 return extended_dn_in_fix(module, req, req->op.del.dn);
684 static int extended_dn_in_rename(struct ldb_module *module, struct ldb_request *req)
686 return extended_dn_in_fix(module, req, req->op.rename.olddn);
689 static const struct ldb_module_ops ldb_extended_dn_in_module_ops = {
690 .name = "extended_dn_in",
691 .search = extended_dn_in_search,
692 .modify = extended_dn_in_modify,
693 .del = extended_dn_in_del,
694 .rename = extended_dn_in_rename,
697 int ldb_extended_dn_in_module_init(const char *version)
699 LDB_MODULE_CHECK_VERSION(version);
700 return ldb_register_module(&ldb_extended_dn_in_module_ops);