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"
43 TODO: if relax is not set then we need to reject the fancy RMD_* and
44 DELETED extended DN codes
48 struct extended_search_context
{
49 struct ldb_module
*module
;
50 struct ldb_request
*req
;
51 struct ldb_parse_tree
*tree
;
52 struct ldb_dn
*basedn
;
54 char *wellknown_object
;
58 static const char *wkattr
[] = {
60 "otherWellKnownObjects",
64 static const struct ldb_module_ops ldb_extended_dn_in_openldap_module_ops
;
66 /* An extra layer of indirection because LDB does not allow the original request to be altered */
68 static int extended_final_callback(struct ldb_request
*req
, struct ldb_reply
*ares
)
70 int ret
= LDB_ERR_OPERATIONS_ERROR
;
71 struct extended_search_context
*ac
;
72 ac
= talloc_get_type(req
->context
, struct extended_search_context
);
74 if (ares
->error
!= LDB_SUCCESS
) {
75 ret
= ldb_module_done(ac
->req
, ares
->controls
,
76 ares
->response
, ares
->error
);
81 ret
= ldb_module_send_entry(ac
->req
, ares
->message
, ares
->controls
);
83 case LDB_REPLY_REFERRAL
:
85 ret
= ldb_module_send_referral(ac
->req
, ares
->referral
);
89 ret
= ldb_module_done(ac
->req
, ares
->controls
,
90 ares
->response
, ares
->error
);
97 static int extended_base_callback(struct ldb_request
*req
, struct ldb_reply
*ares
)
99 struct extended_search_context
*ac
;
100 struct ldb_request
*down_req
;
101 struct ldb_message_element
*el
;
106 const char *found
= NULL
;
108 ac
= talloc_get_type(req
->context
, struct extended_search_context
);
111 return ldb_module_done(ac
->req
, NULL
, NULL
,
112 LDB_ERR_OPERATIONS_ERROR
);
114 if (ares
->error
!= LDB_SUCCESS
) {
115 return ldb_module_done(ac
->req
, ares
->controls
,
116 ares
->response
, ares
->error
);
119 switch (ares
->type
) {
120 case LDB_REPLY_ENTRY
:
122 /* we have more than one match! This can
123 happen as S-1-5-17 appears twice in a
124 normal provision. We need to return
126 const char *str
= talloc_asprintf(req
, "Duplicate base-DN matches found for '%s'",
127 ldb_dn_get_extended_linearized(req
, ac
->dn
, 1));
128 ldb_set_errstring(ldb_module_get_ctx(ac
->module
), str
);
129 return ldb_module_done(ac
->req
, NULL
, NULL
,
130 LDB_ERR_NO_SUCH_OBJECT
);
133 if (!ac
->wellknown_object
) {
134 ac
->basedn
= talloc_steal(ac
, ares
->message
->dn
);
138 wkn_len
= strlen(ac
->wellknown_object
);
140 for (j
=0; wkattr
[j
]; j
++) {
142 el
= ldb_msg_find_element(ares
->message
, wkattr
[j
]);
148 for (i
=0; i
< el
->num_values
; i
++) {
149 valstr
= talloc_strndup(ac
,
150 (const char *)el
->values
[i
].data
,
151 el
->values
[i
].length
);
153 ldb_oom(ldb_module_get_ctx(ac
->module
));
154 return ldb_module_done(ac
->req
, NULL
, NULL
,
155 LDB_ERR_OPERATIONS_ERROR
);
158 if (strncasecmp(valstr
, ac
->wellknown_object
, wkn_len
) != 0) {
163 found
= &valstr
[wkn_len
];
175 ac
->basedn
= ldb_dn_new(ac
, ldb_module_get_ctx(ac
->module
), found
);
178 ldb_oom(ldb_module_get_ctx(ac
->module
));
179 return ldb_module_done(ac
->req
, NULL
, NULL
,
180 LDB_ERR_OPERATIONS_ERROR
);
185 case LDB_REPLY_REFERRAL
:
191 const char *str
= talloc_asprintf(req
, "Base-DN '%s' not found",
192 ldb_dn_get_extended_linearized(req
, ac
->dn
, 1));
193 ldb_set_errstring(ldb_module_get_ctx(ac
->module
), str
);
194 return ldb_module_done(ac
->req
, NULL
, NULL
,
195 LDB_ERR_NO_SUCH_OBJECT
);
198 switch (ac
->req
->operation
) {
200 ret
= ldb_build_search_req_ex(&down_req
,
201 ldb_module_get_ctx(ac
->module
), ac
->req
,
203 ac
->req
->op
.search
.scope
,
205 ac
->req
->op
.search
.attrs
,
207 ac
, extended_final_callback
,
209 LDB_REQ_SET_LOCATION(down_req
);
213 struct ldb_message
*add_msg
= ldb_msg_copy_shallow(ac
, ac
->req
->op
.add
.message
);
215 ldb_oom(ldb_module_get_ctx(ac
->module
));
216 return ldb_module_done(ac
->req
, NULL
, NULL
,
217 LDB_ERR_OPERATIONS_ERROR
);
220 add_msg
->dn
= ac
->basedn
;
222 ret
= ldb_build_add_req(&down_req
,
223 ldb_module_get_ctx(ac
->module
), ac
->req
,
226 ac
, extended_final_callback
,
228 LDB_REQ_SET_LOCATION(down_req
);
233 struct ldb_message
*mod_msg
= ldb_msg_copy_shallow(ac
, ac
->req
->op
.mod
.message
);
235 ldb_oom(ldb_module_get_ctx(ac
->module
));
236 return ldb_module_done(ac
->req
, NULL
, NULL
,
237 LDB_ERR_OPERATIONS_ERROR
);
240 mod_msg
->dn
= ac
->basedn
;
242 ret
= ldb_build_mod_req(&down_req
,
243 ldb_module_get_ctx(ac
->module
), ac
->req
,
246 ac
, extended_final_callback
,
248 LDB_REQ_SET_LOCATION(down_req
);
252 ret
= ldb_build_del_req(&down_req
,
253 ldb_module_get_ctx(ac
->module
), ac
->req
,
256 ac
, extended_final_callback
,
258 LDB_REQ_SET_LOCATION(down_req
);
261 ret
= ldb_build_rename_req(&down_req
,
262 ldb_module_get_ctx(ac
->module
), ac
->req
,
264 ac
->req
->op
.rename
.newdn
,
266 ac
, extended_final_callback
,
268 LDB_REQ_SET_LOCATION(down_req
);
271 return ldb_module_done(ac
->req
, NULL
, NULL
, LDB_ERR_OPERATIONS_ERROR
);
274 if (ret
!= LDB_SUCCESS
) {
275 return ldb_module_done(ac
->req
, NULL
, NULL
, ret
);
278 return ldb_next_request(ac
->module
, down_req
);
286 windows ldap searches don't allow a baseDN with more
287 than one extended component, or an extended
288 component and a string DN
290 We only enforce this over ldap, not for internal
291 use, as there are just too many places where we
292 internally want to use a DN that has come from a
293 search with extended DN enabled, or comes from a DRS
296 Enforcing this would also make debugging samba much
297 harder, as we'd need to use ldb_dn_minimise() in a
298 lot of places, and that would lose the DN string
299 which is so useful for working out what a request is
302 static bool ldb_dn_match_allowed(struct ldb_dn
*dn
, struct ldb_request
*req
)
304 int num_components
= ldb_dn_get_comp_num(dn
);
305 int num_ex_components
= ldb_dn_get_extended_comp_num(dn
);
307 if (num_ex_components
== 0) {
311 if ((num_components
!= 0 || num_ex_components
!= 1) &&
312 ldb_req_is_untrusted(req
)) {
319 struct extended_dn_filter_ctx
{
322 struct ldb_module
*module
;
323 struct ldb_request
*req
;
324 struct dsdb_schema
*schema
;
329 create a always non-matching node from a equality node
331 static void set_parse_tree_false(struct ldb_parse_tree
*tree
)
333 const char *attr
= tree
->u
.equality
.attr
;
334 struct ldb_val value
= tree
->u
.equality
.value
;
335 tree
->operation
= LDB_OP_EXTENDED
;
336 tree
->u
.extended
.attr
= attr
;
337 tree
->u
.extended
.value
= value
;
338 tree
->u
.extended
.rule_id
= SAMBA_LDAP_MATCH_ALWAYS_FALSE
;
339 tree
->u
.extended
.dnAttributes
= 0;
343 called on all nodes in the parse tree
345 static int extended_dn_filter_callback(struct ldb_parse_tree
*tree
, void *private_context
)
347 struct extended_dn_filter_ctx
*filter_ctx
;
349 struct ldb_dn
*dn
= NULL
;
350 const struct ldb_val
*sid_val
, *guid_val
;
351 const char *no_attrs
[] = { NULL
};
352 struct ldb_result
*res
;
353 const struct dsdb_attribute
*attribute
= NULL
;
354 bool has_extended_component
= false;
355 enum ldb_scope scope
;
356 struct ldb_dn
*base_dn
;
357 const char *expression
;
360 if (tree
->operation
!= LDB_OP_EQUALITY
&& tree
->operation
!= LDB_OP_EXTENDED
) {
364 filter_ctx
= talloc_get_type_abort(private_context
, struct extended_dn_filter_ctx
);
366 if (filter_ctx
->test_only
&& filter_ctx
->matched
) {
367 /* the tree already matched */
371 if (!filter_ctx
->schema
) {
372 /* Schema not setup yet */
375 if (tree
->operation
== LDB_OP_EQUALITY
) {
376 attribute
= dsdb_attribute_by_lDAPDisplayName(filter_ctx
->schema
, tree
->u
.equality
.attr
);
377 } else if (tree
->operation
== LDB_OP_EXTENDED
) {
378 attribute
= dsdb_attribute_by_lDAPDisplayName(filter_ctx
->schema
, tree
->u
.extended
.attr
);
380 if (attribute
== NULL
) {
384 if (attribute
->dn_format
!= DSDB_NORMAL_DN
) {
388 if (tree
->operation
== LDB_OP_EQUALITY
) {
389 has_extended_component
= (memchr(tree
->u
.equality
.value
.data
, '<',
390 tree
->u
.equality
.value
.length
) != NULL
);
391 } else if (tree
->operation
== LDB_OP_EXTENDED
) {
392 has_extended_component
= (memchr(tree
->u
.extended
.value
.data
, '<',
393 tree
->u
.extended
.value
.length
) != NULL
);
397 * Don't turn it into an extended DN if we're talking to OpenLDAP.
398 * We just check the module_ops pointer instead of adding a private
399 * pointer and a boolean to tell us the exact same thing.
401 if (!has_extended_component
) {
402 if (!attribute
->one_way_link
) {
406 if (ldb_module_get_ops(filter_ctx
->module
) == &ldb_extended_dn_in_openldap_module_ops
) {
411 if (tree
->operation
== LDB_OP_EQUALITY
) {
412 dn
= ldb_dn_from_ldb_val(filter_ctx
, ldb_module_get_ctx(filter_ctx
->module
), &tree
->u
.equality
.value
);
413 } else if (tree
->operation
== LDB_OP_EXTENDED
414 && (strcmp(tree
->u
.extended
.rule_id
, SAMBA_LDAP_MATCH_RULE_TRANSITIVE_EVAL
) == 0)) {
415 dn
= ldb_dn_from_ldb_val(filter_ctx
, ldb_module_get_ctx(filter_ctx
->module
), &tree
->u
.extended
.value
);
418 /* testing against windows shows that we don't raise
423 guid_val
= ldb_dn_get_extended_component(dn
, "GUID");
424 sid_val
= ldb_dn_get_extended_component(dn
, "SID");
427 * Is the attribute indexed? By treating confidential attributes
428 * as unindexed, we force searches to go through the unindexed
429 * search path, avoiding observable timing differences.
431 if (!guid_val
&& !sid_val
&&
432 (attribute
->searchFlags
& SEARCH_FLAG_ATTINDEX
) &&
433 !(attribute
->searchFlags
& SEARCH_FLAG_CONFIDENTIAL
))
435 /* if it is indexed, then fixing the string DN will do
436 no good here, as we will not find the attribute in
437 the index. So for now fall through to a standard DN
438 component comparison */
442 if (filter_ctx
->test_only
) {
443 /* we need to copy the tree */
444 filter_ctx
->matched
= true;
448 if (!ldb_dn_match_allowed(dn
, filter_ctx
->req
)) {
449 /* we need to make this element of the filter always
451 set_parse_tree_false(tree
);
455 dsdb_flags
= filter_ctx
->dsdb_flags
| DSDB_FLAG_NEXT_MODULE
;
458 expression
= talloc_asprintf(filter_ctx
, "objectGUID=%s", ldb_binary_encode(filter_ctx
, *guid_val
));
459 scope
= LDB_SCOPE_SUBTREE
;
461 dsdb_flags
|= DSDB_SEARCH_SEARCH_ALL_PARTITIONS
;
462 } else if (sid_val
) {
463 expression
= talloc_asprintf(filter_ctx
, "objectSID=%s", ldb_binary_encode(filter_ctx
, *sid_val
));
464 scope
= LDB_SCOPE_SUBTREE
;
466 dsdb_flags
|= DSDB_SEARCH_SEARCH_ALL_PARTITIONS
;
468 /* fallback to searching using the string DN as the base DN */
469 expression
= "objectClass=*";
471 scope
= LDB_SCOPE_BASE
;
474 ret
= dsdb_module_search(filter_ctx
->module
,
483 if (scope
== LDB_SCOPE_BASE
&& ret
== LDB_ERR_NO_SUCH_OBJECT
) {
484 /* note that this will need to change for multi-domain
486 set_parse_tree_false(tree
);
490 if (ret
!= LDB_SUCCESS
) {
495 if (res
->count
!= 1) {
499 /* replace the search expression element with the matching DN */
500 if (tree
->operation
== LDB_OP_EQUALITY
) {
501 tree
->u
.equality
.value
.data
=
502 (uint8_t *)talloc_strdup(tree
, ldb_dn_get_extended_linearized(tree
, res
->msgs
[0]->dn
, 1));
503 if (tree
->u
.equality
.value
.data
== NULL
) {
504 return ldb_oom(ldb_module_get_ctx(filter_ctx
->module
));
506 tree
->u
.equality
.value
.length
= strlen((const char *)tree
->u
.equality
.value
.data
);
507 } else if (tree
->operation
== LDB_OP_EXTENDED
) {
508 tree
->u
.extended
.value
.data
=
509 (uint8_t *)talloc_strdup(tree
, ldb_dn_get_extended_linearized(tree
, res
->msgs
[0]->dn
, 1));
510 if (tree
->u
.extended
.value
.data
== NULL
) {
511 return ldb_oom(ldb_module_get_ctx(filter_ctx
->module
));
513 tree
->u
.extended
.value
.length
= strlen((const char *)tree
->u
.extended
.value
.data
);
517 filter_ctx
->matched
= true;
522 fix the parse tree to change any extended DN components to their
525 static int extended_dn_fix_filter(struct ldb_module
*module
,
526 struct ldb_request
*req
,
527 uint32_t default_dsdb_flags
,
528 struct ldb_parse_tree
**down_tree
)
530 struct extended_dn_filter_ctx
*filter_ctx
;
535 filter_ctx
= talloc_zero(req
, struct extended_dn_filter_ctx
);
536 if (filter_ctx
== NULL
) {
537 return ldb_module_oom(module
);
540 /* first pass through the existing tree to see if anything
541 needs to be modified. Filtering DNs on the input side is rare,
542 so this avoids copying the parse tree in most cases */
543 filter_ctx
->test_only
= true;
544 filter_ctx
->matched
= false;
545 filter_ctx
->module
= module
;
546 filter_ctx
->req
= req
;
547 filter_ctx
->schema
= dsdb_get_schema(ldb_module_get_ctx(module
), filter_ctx
);
548 filter_ctx
->dsdb_flags
= default_dsdb_flags
;
550 ret
= ldb_parse_tree_walk(req
->op
.search
.tree
, extended_dn_filter_callback
, filter_ctx
);
551 if (ret
!= LDB_SUCCESS
) {
552 talloc_free(filter_ctx
);
556 if (!filter_ctx
->matched
) {
557 /* nothing matched, no need for a new parse tree */
558 talloc_free(filter_ctx
);
562 filter_ctx
->test_only
= false;
563 filter_ctx
->matched
= false;
565 *down_tree
= ldb_parse_tree_copy_shallow(req
, req
->op
.search
.tree
);
566 if (*down_tree
== NULL
) {
567 return ldb_oom(ldb_module_get_ctx(module
));
570 ret
= ldb_parse_tree_walk(*down_tree
, extended_dn_filter_callback
, filter_ctx
);
571 if (ret
!= LDB_SUCCESS
) {
572 talloc_free(filter_ctx
);
576 talloc_free(filter_ctx
);
581 fix DNs and filter expressions to cope with the semantics of
584 static int extended_dn_in_fix(struct ldb_module
*module
, struct ldb_request
*req
, struct ldb_dn
*dn
)
586 struct extended_search_context
*ac
;
587 struct ldb_request
*down_req
= NULL
;
588 struct ldb_parse_tree
*down_tree
= NULL
;
590 struct ldb_dn
*base_dn
= NULL
;
591 enum ldb_scope base_dn_scope
= LDB_SCOPE_BASE
;
592 const char *base_dn_filter
= NULL
;
593 const char * const *base_dn_attrs
= NULL
;
594 char *wellknown_object
= NULL
;
595 static const char *no_attr
[] = {
598 uint32_t dsdb_flags
= DSDB_FLAG_AS_SYSTEM
| DSDB_SEARCH_SHOW_EXTENDED_DN
;
600 if (ldb_request_get_control(req
, LDB_CONTROL_SHOW_DELETED_OID
)) {
601 dsdb_flags
|= DSDB_SEARCH_SHOW_DELETED
;
603 if (ldb_request_get_control(req
, LDB_CONTROL_SHOW_RECYCLED_OID
)) {
604 dsdb_flags
|= DSDB_SEARCH_SHOW_RECYCLED
;
606 if (ldb_request_get_control(req
, DSDB_CONTROL_DBCHECK
)) {
607 dsdb_flags
|= DSDB_SEARCH_SHOW_RECYCLED
;
610 if (req
->operation
== LDB_SEARCH
) {
611 ret
= extended_dn_fix_filter(module
, req
, dsdb_flags
, &down_tree
);
612 if (ret
!= LDB_SUCCESS
) {
617 if (!ldb_dn_has_extended(dn
)) {
618 /* Move along there isn't anything to see here */
619 if (down_tree
== NULL
) {
622 ret
= ldb_build_search_req_ex(&down_req
,
623 ldb_module_get_ctx(module
), req
,
625 req
->op
.search
.scope
,
627 req
->op
.search
.attrs
,
629 req
, dsdb_next_callback
,
631 if (ret
!= LDB_SUCCESS
) {
634 LDB_REQ_SET_LOCATION(down_req
);
637 return ldb_next_request(module
, down_req
);
639 /* It looks like we need to map the DN */
640 const struct ldb_val
*sid_val
, *guid_val
, *wkguid_val
;
642 if (!ldb_dn_match_allowed(dn
, req
)) {
643 return ldb_error(ldb_module_get_ctx(module
),
644 LDB_ERR_INVALID_DN_SYNTAX
, "invalid number of DN components");
647 sid_val
= ldb_dn_get_extended_component(dn
, "SID");
648 guid_val
= ldb_dn_get_extended_component(dn
, "GUID");
649 wkguid_val
= ldb_dn_get_extended_component(dn
, "WKGUID");
652 prioritise the GUID - we have had instances of
653 duplicate SIDs in the database in the
654 ForeignSecurityPrincipals due to provision errors
657 dsdb_flags
|= DSDB_SEARCH_SEARCH_ALL_PARTITIONS
;
659 base_dn_filter
= talloc_asprintf(req
, "(objectGUID=%s)",
660 ldb_binary_encode(req
, *guid_val
));
661 if (!base_dn_filter
) {
662 return ldb_oom(ldb_module_get_ctx(module
));
664 base_dn_scope
= LDB_SCOPE_SUBTREE
;
665 base_dn_attrs
= no_attr
;
667 } else if (sid_val
) {
668 dsdb_flags
|= DSDB_SEARCH_SEARCH_ALL_PARTITIONS
;
670 base_dn_filter
= talloc_asprintf(req
, "(objectSid=%s)",
671 ldb_binary_encode(req
, *sid_val
));
672 if (!base_dn_filter
) {
673 return ldb_oom(ldb_module_get_ctx(module
));
675 base_dn_scope
= LDB_SCOPE_SUBTREE
;
676 base_dn_attrs
= no_attr
;
678 } else if (wkguid_val
) {
683 wkguid_dup
= talloc_strndup(req
, (char *)wkguid_val
->data
, wkguid_val
->length
);
685 p
= strchr(wkguid_dup
, ',');
687 return ldb_error(ldb_module_get_ctx(module
), LDB_ERR_INVALID_DN_SYNTAX
,
688 "Invalid WKGUID format");
694 wellknown_object
= talloc_asprintf(req
, "B:32:%s:", wkguid_dup
);
695 if (!wellknown_object
) {
696 return ldb_oom(ldb_module_get_ctx(module
));
701 base_dn
= ldb_dn_new(req
, ldb_module_get_ctx(module
), tail_str
);
702 talloc_free(wkguid_dup
);
704 return ldb_oom(ldb_module_get_ctx(module
));
706 base_dn_filter
= talloc_strdup(req
, "(objectClass=*)");
707 if (!base_dn_filter
) {
708 return ldb_oom(ldb_module_get_ctx(module
));
710 base_dn_scope
= LDB_SCOPE_BASE
;
711 base_dn_attrs
= wkattr
;
713 return ldb_error(ldb_module_get_ctx(module
), LDB_ERR_INVALID_DN_SYNTAX
,
714 "Invalid extended DN component");
717 ac
= talloc_zero(req
, struct extended_search_context
);
719 return ldb_oom(ldb_module_get_ctx(module
));
724 ac
->tree
= (down_tree
!= NULL
) ? down_tree
: req
->op
.search
.tree
;
726 ac
->basedn
= NULL
; /* Filled in if the search finds the DN by SID/GUID etc */
727 ac
->wellknown_object
= wellknown_object
;
729 /* If the base DN was an extended DN (perhaps a well known
730 * GUID) then search for that, so we can proceed with the original operation */
732 ret
= ldb_build_search_req(&down_req
,
733 ldb_module_get_ctx(module
), ac
,
739 ac
, extended_base_callback
,
741 LDB_REQ_SET_LOCATION(down_req
);
742 if (ret
!= LDB_SUCCESS
) {
743 return ldb_operr(ldb_module_get_ctx(module
));
746 ret
= dsdb_request_add_controls(down_req
, dsdb_flags
);
747 if (ret
!= LDB_SUCCESS
) {
751 /* perform the search */
752 return ldb_next_request(module
, down_req
);
756 static int extended_dn_in_search(struct ldb_module
*module
, struct ldb_request
*req
)
758 return extended_dn_in_fix(module
, req
, req
->op
.search
.base
);
761 static int extended_dn_in_modify(struct ldb_module
*module
, struct ldb_request
*req
)
763 return extended_dn_in_fix(module
, req
, req
->op
.mod
.message
->dn
);
766 static int extended_dn_in_del(struct ldb_module
*module
, struct ldb_request
*req
)
768 return extended_dn_in_fix(module
, req
, req
->op
.del
.dn
);
771 static int extended_dn_in_rename(struct ldb_module
*module
, struct ldb_request
*req
)
773 return extended_dn_in_fix(module
, req
, req
->op
.rename
.olddn
);
776 static const struct ldb_module_ops ldb_extended_dn_in_module_ops
= {
777 .name
= "extended_dn_in",
778 .search
= extended_dn_in_search
,
779 .modify
= extended_dn_in_modify
,
780 .del
= extended_dn_in_del
,
781 .rename
= extended_dn_in_rename
,
784 static const struct ldb_module_ops ldb_extended_dn_in_openldap_module_ops
= {
785 .name
= "extended_dn_in_openldap",
786 .search
= extended_dn_in_search
,
787 .modify
= extended_dn_in_modify
,
788 .del
= extended_dn_in_del
,
789 .rename
= extended_dn_in_rename
,
792 int ldb_extended_dn_in_module_init(const char *version
)
795 LDB_MODULE_CHECK_VERSION(version
);
796 ret
= ldb_register_module(&ldb_extended_dn_in_openldap_module_ops
);
797 if (ret
!= LDB_SUCCESS
) {
800 return ldb_register_module(&ldb_extended_dn_in_module_ops
);