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 /* 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
);
69 ret
= ldb_module_send_entry(ac
->req
, ares
->message
, ares
->controls
);
71 case LDB_REPLY_REFERRAL
:
73 ret
= ldb_module_send_referral(ac
->req
, ares
->referral
);
77 ret
= ldb_module_done(ac
->req
, ares
->controls
,
78 ares
->response
, ares
->error
);
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
;
94 const char *found
= NULL
;
96 ac
= talloc_get_type(req
->context
, struct extended_search_context
);
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
:
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
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
);
126 wkn_len
= strlen(ac
->wellknown_object
);
128 el
= ldb_msg_find_element(ares
->message
, "wellKnownObjects");
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
);
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) {
149 found
= &valstr
[wkn_len
];
157 ac
->basedn
= ldb_dn_new(ac
, ldb_module_get_ctx(ac
->module
), found
);
160 ldb_oom(ldb_module_get_ctx(ac
->module
));
161 return ldb_module_done(ac
->req
, NULL
, NULL
,
162 LDB_ERR_OPERATIONS_ERROR
);
167 case LDB_REPLY_REFERRAL
:
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
) {
182 ret
= ldb_build_search_req_ex(&down_req
,
183 ldb_module_get_ctx(ac
->module
), ac
->req
,
185 ac
->req
->op
.search
.scope
,
186 ac
->req
->op
.search
.tree
,
187 ac
->req
->op
.search
.attrs
,
189 ac
, extended_final_callback
,
191 LDB_REQ_SET_LOCATION(down_req
);
195 struct ldb_message
*add_msg
= ldb_msg_copy_shallow(ac
, ac
->req
->op
.add
.message
);
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
,
208 ac
, extended_final_callback
,
210 LDB_REQ_SET_LOCATION(down_req
);
215 struct ldb_message
*mod_msg
= ldb_msg_copy_shallow(ac
, ac
->req
->op
.mod
.message
);
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
,
228 ac
, extended_final_callback
,
230 LDB_REQ_SET_LOCATION(down_req
);
234 ret
= ldb_build_del_req(&down_req
,
235 ldb_module_get_ctx(ac
->module
), ac
->req
,
238 ac
, extended_final_callback
,
240 LDB_REQ_SET_LOCATION(down_req
);
243 ret
= ldb_build_rename_req(&down_req
,
244 ldb_module_get_ctx(ac
->module
), ac
->req
,
246 ac
->req
->op
.rename
.newdn
,
248 ac
, extended_final_callback
,
250 LDB_REQ_SET_LOCATION(down_req
);
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
);
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
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) {
293 if ((num_components
!= 0 || num_ex_components
!= 1) &&
294 ldb_req_is_untrusted(req
)) {
301 struct extended_dn_filter_ctx
{
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
;
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
;
341 if (tree
->operation
!= LDB_OP_EQUALITY
) {
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 */
352 attribute
= dsdb_attribute_by_lDAPDisplayName(filter_ctx
->schema
, tree
->u
.equality
.attr
);
353 if (attribute
== NULL
) {
357 if (attribute
->dn_format
!= DSDB_NORMAL_DN
) {
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
) {
368 dn
= ldb_dn_from_ldb_val(filter_ctx
, ldb_module_get_ctx(filter_ctx
->module
), &tree
->u
.equality
.value
);
370 /* testing against windows shows that we don't raise
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 */
386 if (filter_ctx
->test_only
) {
387 /* we need to copy the tree */
388 filter_ctx
->matched
= true;
392 if (!ldb_dn_match_allowed(dn
, filter_ctx
->req
)) {
393 /* we need to make this element of the filter always
395 set_parse_tree_false(tree
);
399 dsdb_flags
= DSDB_FLAG_NEXT_MODULE
|
400 DSDB_SEARCH_SHOW_DELETED
|
401 DSDB_SEARCH_SHOW_EXTENDED_DN
;
404 expression
= talloc_asprintf(filter_ctx
, "objectGUID=%s", ldb_binary_encode(filter_ctx
, *guid_val
));
405 scope
= LDB_SCOPE_SUBTREE
;
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
;
412 dsdb_flags
|= DSDB_SEARCH_SEARCH_ALL_PARTITIONS
;
414 /* fallback to searching using the string DN as the base DN */
415 expression
= "objectClass=*";
417 scope
= LDB_SCOPE_BASE
;
420 ret
= dsdb_module_search(filter_ctx
->module
,
429 if (scope
== LDB_SCOPE_BASE
&& ret
== LDB_ERR_NO_SUCH_OBJECT
) {
430 /* note that this will need to change for multi-domain
432 set_parse_tree_false(tree
);
436 if (ret
!= LDB_SUCCESS
) {
441 if (res
->count
!= 1) {
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
);
454 filter_ctx
->matched
= true;
459 fix the parse tree to change any extended DN components to their
462 static int extended_dn_fix_filter(struct ldb_module
*module
, struct ldb_request
*req
)
464 struct extended_dn_filter_ctx
*filter_ctx
;
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
);
487 if (!filter_ctx
->matched
) {
488 /* nothing matched, no need for a new parse tree */
489 talloc_free(filter_ctx
);
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
);
507 talloc_free(filter_ctx
);
512 fix DNs and filter expressions to cope with the semantics of
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
;
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
[] = {
528 static const char *wkattr
[] = {
532 bool all_partitions
= false;
534 if (req
->operation
== LDB_SEARCH
) {
535 ret
= extended_dn_fix_filter(module
, req
);
536 if (ret
!= LDB_SUCCESS
) {
541 if (!ldb_dn_has_extended(dn
)) {
542 /* Move along there isn't anything to see here */
543 return ldb_next_request(module
, req
);
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
563 all_partitions
= true;
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;
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
) {
589 wkguid_dup
= talloc_strndup(req
, (char *)wkguid_val
->data
, wkguid_val
->length
);
591 p
= strchr(wkguid_dup
, ',');
593 return ldb_error(ldb_module_get_ctx(module
), LDB_ERR_INVALID_DN_SYNTAX
,
594 "Invalid WKGUID format");
600 wellknown_object
= talloc_asprintf(req
, "B:32:%s:", wkguid_dup
);
601 if (!wellknown_object
) {
602 return ldb_oom(ldb_module_get_ctx(module
));
607 base_dn
= ldb_dn_new(req
, ldb_module_get_ctx(module
), tail_str
);
608 talloc_free(wkguid_dup
);
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
;
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
);
625 return ldb_oom(ldb_module_get_ctx(module
));
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
,
644 ac
, extended_base_callback
,
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
,
658 if (ret
!= LDB_SUCCESS
) {
659 ldb_oom(ldb_module_get_ctx(module
));
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
);