72acf071921a32c577c527c867ef843c538705c0
[ddiss/samba.git] / source4 / dsdb / samdb / ldb_modules / extended_dn_in.c
1 /* 
2    ldb database library
3
4    Copyright (C) Simo Sorce 2005-2008
5    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2007-2008
6
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.
11    
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.
16    
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/>.
19 */
20
21 /*
22  *  Name: ldb
23  *
24  *  Component: ldb extended dn control module
25  *
26  *  Description: this module interprets DNs of the form <SID=S-1-2-4456> into normal DNs.
27  *
28  *  Authors: Simo Sorce
29  *           Andrew Bartlett
30  */
31
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"
38
39 /*
40   TODO: if relax is not set then we need to reject the fancy RMD_* and
41   DELETED extended DN codes
42  */
43
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;
52 };
53
54 static const char *wkattr[] = {
55         "wellKnownObjects",
56         "otherWellKnownObjects",
57         NULL
58 };
59 /* An extra layer of indirection because LDB does not allow the original request to be altered */
60
61 static int extended_final_callback(struct ldb_request *req, struct ldb_reply *ares)
62 {
63         int ret = LDB_ERR_OPERATIONS_ERROR;
64         struct extended_search_context *ac;
65         ac = talloc_get_type(req->context, struct extended_search_context);
66
67         if (ares->error != LDB_SUCCESS) {
68                 ret = ldb_module_done(ac->req, ares->controls,
69                                       ares->response, ares->error);
70         } else {
71                 switch (ares->type) {
72                 case LDB_REPLY_ENTRY:
73                         
74                         ret = ldb_module_send_entry(ac->req, ares->message, ares->controls);
75                         break;
76                 case LDB_REPLY_REFERRAL:
77                         
78                         ret = ldb_module_send_referral(ac->req, ares->referral);
79                         break;
80                 case LDB_REPLY_DONE:
81                         
82                         ret = ldb_module_done(ac->req, ares->controls,
83                                               ares->response, ares->error);
84                         break;
85                 }
86         }
87         return ret;
88 }
89
90 static int extended_base_callback(struct ldb_request *req, struct ldb_reply *ares)
91 {
92         struct extended_search_context *ac;
93         struct ldb_request *down_req;
94         struct ldb_message_element *el;
95         int ret;
96         unsigned int i, j;
97         size_t wkn_len = 0;
98         char *valstr = NULL;
99         const char *found = NULL;
100
101         ac = talloc_get_type(req->context, struct extended_search_context);
102
103         if (!ares) {
104                 return ldb_module_done(ac->req, NULL, NULL,
105                                         LDB_ERR_OPERATIONS_ERROR);
106         }
107         if (ares->error != LDB_SUCCESS) {
108                 return ldb_module_done(ac->req, ares->controls,
109                                         ares->response, ares->error);
110         }
111
112         switch (ares->type) {
113         case LDB_REPLY_ENTRY:
114                 if (ac->basedn) {
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
118                            NO_SUCH_OBJECT */
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);
124                 }
125
126                 if (!ac->wellknown_object) {
127                         ac->basedn = talloc_steal(ac, ares->message->dn);
128                         break;
129                 }
130
131                 wkn_len = strlen(ac->wellknown_object);
132
133                 for (j=0; wkattr[j]; j++) {
134
135                         el = ldb_msg_find_element(ares->message, wkattr[j]);
136                         if (!el) {
137                                 ac->basedn = NULL;
138                                 continue;
139                         }
140
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);
145                                 if (!valstr) {
146                                         ldb_oom(ldb_module_get_ctx(ac->module));
147                                         return ldb_module_done(ac->req, NULL, NULL,
148                                                         LDB_ERR_OPERATIONS_ERROR);
149                                 }
150
151                                 if (strncasecmp(valstr, ac->wellknown_object, wkn_len) != 0) {
152                                         talloc_free(valstr);
153                                         continue;
154                                 }
155
156                                 found = &valstr[wkn_len];
157                                 break;
158                         }
159                         if (found) {
160                                 break;
161                         }
162                 }
163
164                 if (!found) {
165                         break;
166                 }
167
168                 ac->basedn = ldb_dn_new(ac, ldb_module_get_ctx(ac->module), found);
169                 talloc_free(valstr);
170                 if (!ac->basedn) {
171                         ldb_oom(ldb_module_get_ctx(ac->module));
172                         return ldb_module_done(ac->req, NULL, NULL,
173                                                LDB_ERR_OPERATIONS_ERROR);
174                 }
175
176                 break;
177
178         case LDB_REPLY_REFERRAL:
179                 break;
180
181         case LDB_REPLY_DONE:
182
183                 if (!ac->basedn) {
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);
189                 }
190
191                 switch (ac->req->operation) {
192                 case LDB_SEARCH:
193                         ret = ldb_build_search_req_ex(&down_req,
194                                                       ldb_module_get_ctx(ac->module), ac->req,
195                                                       ac->basedn,
196                                                       ac->req->op.search.scope,
197                                                       ac->req->op.search.tree,
198                                                       ac->req->op.search.attrs,
199                                                       ac->req->controls,
200                                                       ac, extended_final_callback, 
201                                                       ac->req);
202                         LDB_REQ_SET_LOCATION(down_req);
203                         break;
204                 case LDB_ADD:
205                 {
206                         struct ldb_message *add_msg = ldb_msg_copy_shallow(ac, ac->req->op.add.message);
207                         if (!add_msg) {
208                                 ldb_oom(ldb_module_get_ctx(ac->module));
209                                 return ldb_module_done(ac->req, NULL, NULL,
210                                                        LDB_ERR_OPERATIONS_ERROR);
211                         }
212                         
213                         add_msg->dn = ac->basedn;
214
215                         ret = ldb_build_add_req(&down_req,
216                                                 ldb_module_get_ctx(ac->module), ac->req,
217                                                 add_msg, 
218                                                 ac->req->controls,
219                                                 ac, extended_final_callback, 
220                                                 ac->req);
221                         LDB_REQ_SET_LOCATION(down_req);
222                         break;
223                 }
224                 case LDB_MODIFY:
225                 {
226                         struct ldb_message *mod_msg = ldb_msg_copy_shallow(ac, ac->req->op.mod.message);
227                         if (!mod_msg) {
228                                 ldb_oom(ldb_module_get_ctx(ac->module));
229                                 return ldb_module_done(ac->req, NULL, NULL,
230                                                        LDB_ERR_OPERATIONS_ERROR);
231                         }
232                         
233                         mod_msg->dn = ac->basedn;
234
235                         ret = ldb_build_mod_req(&down_req,
236                                                 ldb_module_get_ctx(ac->module), ac->req,
237                                                 mod_msg, 
238                                                 ac->req->controls,
239                                                 ac, extended_final_callback, 
240                                                 ac->req);
241                         LDB_REQ_SET_LOCATION(down_req);
242                         break;
243                 }
244                 case LDB_DELETE:
245                         ret = ldb_build_del_req(&down_req,
246                                                 ldb_module_get_ctx(ac->module), ac->req,
247                                                 ac->basedn, 
248                                                 ac->req->controls,
249                                                 ac, extended_final_callback, 
250                                                 ac->req);
251                         LDB_REQ_SET_LOCATION(down_req);
252                         break;
253                 case LDB_RENAME:
254                         ret = ldb_build_rename_req(&down_req,
255                                                    ldb_module_get_ctx(ac->module), ac->req,
256                                                    ac->basedn, 
257                                                    ac->req->op.rename.newdn,
258                                                    ac->req->controls,
259                                                    ac, extended_final_callback, 
260                                                    ac->req);
261                         LDB_REQ_SET_LOCATION(down_req);
262                         break;
263                 default:
264                         return ldb_module_done(ac->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
265                 }
266                 
267                 if (ret != LDB_SUCCESS) {
268                         return ldb_module_done(ac->req, NULL, NULL, ret);
269                 }
270
271                 return ldb_next_request(ac->module, down_req);
272         }
273         talloc_free(ares);
274         return LDB_SUCCESS;
275 }
276
277
278 /*
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
282
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
287   naming context.
288
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
293   for
294 */
295 static bool ldb_dn_match_allowed(struct ldb_dn *dn, struct ldb_request *req)
296 {
297         int num_components = ldb_dn_get_comp_num(dn);
298         int num_ex_components = ldb_dn_get_extended_comp_num(dn);
299
300         if (num_ex_components == 0) {
301                 return true;
302         }
303
304         if ((num_components != 0 || num_ex_components != 1) &&
305             ldb_req_is_untrusted(req)) {
306                 return false;
307         }
308         return true;
309 }
310
311
312 struct extended_dn_filter_ctx {
313         bool test_only;
314         bool matched;
315         struct ldb_module *module;
316         struct ldb_request *req;
317         struct dsdb_schema *schema;
318 };
319
320 /*
321   create a always non-matching node from a equality node
322  */
323 static void set_parse_tree_false(struct ldb_parse_tree *tree)
324 {
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;
332 }
333
334 /*
335   called on all nodes in the parse tree
336  */
337 static int extended_dn_filter_callback(struct ldb_parse_tree *tree, void *private_context)
338 {
339         struct extended_dn_filter_ctx *filter_ctx;
340         int ret;
341         struct ldb_dn *dn;
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;
350         uint32_t dsdb_flags;
351
352         if (tree->operation != LDB_OP_EQUALITY) {
353                 return LDB_SUCCESS;
354         }
355
356         filter_ctx = talloc_get_type_abort(private_context, struct extended_dn_filter_ctx);
357
358         if (filter_ctx->test_only && filter_ctx->matched) {
359                 /* the tree already matched */
360                 return LDB_SUCCESS;
361         }
362
363         attribute = dsdb_attribute_by_lDAPDisplayName(filter_ctx->schema, tree->u.equality.attr);
364         if (attribute == NULL) {
365                 return LDB_SUCCESS;
366         }
367
368         if (attribute->dn_format != DSDB_NORMAL_DN) {
369                 return LDB_SUCCESS;
370         }
371
372         has_extended_component = (memchr(tree->u.equality.value.data, '<',
373                                          tree->u.equality.value.length) != NULL);
374
375         if (!attribute->one_way_link && !has_extended_component) {
376                 return LDB_SUCCESS;
377         }
378
379         dn = ldb_dn_from_ldb_val(filter_ctx, ldb_module_get_ctx(filter_ctx->module), &tree->u.equality.value);
380         if (dn == NULL) {
381                 /* testing against windows shows that we don't raise
382                    an error here */
383                 return LDB_SUCCESS;
384         }
385
386         guid_val = ldb_dn_get_extended_component(dn, "GUID");
387         sid_val  = ldb_dn_get_extended_component(dn, "SID");
388
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 */
394                 return LDB_SUCCESS;
395         }
396
397         if (filter_ctx->test_only) {
398                 /* we need to copy the tree */
399                 filter_ctx->matched = true;
400                 return LDB_SUCCESS;
401         }
402
403         if (!ldb_dn_match_allowed(dn, filter_ctx->req)) {
404                 /* we need to make this element of the filter always
405                    be false */
406                 set_parse_tree_false(tree);
407                 return LDB_SUCCESS;
408         }
409
410         dsdb_flags = DSDB_FLAG_NEXT_MODULE |
411                 DSDB_SEARCH_SHOW_DELETED |
412                 DSDB_SEARCH_SHOW_EXTENDED_DN;
413
414         if (guid_val) {
415                 expression = talloc_asprintf(filter_ctx, "objectGUID=%s", ldb_binary_encode(filter_ctx, *guid_val));
416                 scope = LDB_SCOPE_SUBTREE;
417                 base_dn = NULL;
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;
422                 base_dn = NULL;
423                 dsdb_flags |= DSDB_SEARCH_SEARCH_ALL_PARTITIONS;
424         } else {
425                 /* fallback to searching using the string DN as the base DN */
426                 expression = "objectClass=*";
427                 base_dn = dn;
428                 scope = LDB_SCOPE_BASE;
429         }
430
431         ret = dsdb_module_search(filter_ctx->module,
432                                  filter_ctx,
433                                  &res,
434                                  base_dn,
435                                  scope,
436                                  no_attrs,
437                                  dsdb_flags,
438                                  filter_ctx->req,
439                                  "%s", expression);
440         if (scope == LDB_SCOPE_BASE && ret == LDB_ERR_NO_SUCH_OBJECT) {
441                 /* note that this will need to change for multi-domain
442                    support */
443                 set_parse_tree_false(tree);
444                 return LDB_SUCCESS;
445         }
446
447         if (ret != LDB_SUCCESS) {
448                 return LDB_SUCCESS;
449         }
450
451
452         if (res->count != 1) {
453                 return LDB_SUCCESS;
454         }
455
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));
461         }
462         tree->u.equality.value.length = strlen((const char *)tree->u.equality.value.data);
463         talloc_free(res);
464
465         filter_ctx->matched = true;
466         return LDB_SUCCESS;
467 }
468
469 /*
470   fix the parse tree to change any extended DN components to their
471   caconical form
472  */
473 static int extended_dn_fix_filter(struct ldb_module *module, struct ldb_request *req)
474 {
475         struct extended_dn_filter_ctx *filter_ctx;
476         int ret;
477
478         filter_ctx = talloc_zero(req, struct extended_dn_filter_ctx);
479         if (filter_ctx == NULL) {
480                 return ldb_module_oom(module);
481         }
482
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);
491
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);
495                 return ret;
496         }
497
498         if (!filter_ctx->matched) {
499                 /* nothing matched, no need for a new parse tree */
500                 talloc_free(filter_ctx);
501                 return LDB_SUCCESS;
502         }
503
504         filter_ctx->test_only = false;
505         filter_ctx->matched   = false;
506
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));
510         }
511
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);
515                 return ret;
516         }
517
518         talloc_free(filter_ctx);
519         return LDB_SUCCESS;
520 }
521
522 /*
523   fix DNs and filter expressions to cope with the semantics of
524   extended DNs
525  */
526 static int extended_dn_in_fix(struct ldb_module *module, struct ldb_request *req, struct ldb_dn *dn)
527 {
528         struct extended_search_context *ac;
529         struct ldb_request *down_req;
530         int ret;
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[] = {
537                 NULL
538         };
539         bool all_partitions = false;
540
541         if (req->operation == LDB_SEARCH) {
542                 ret = extended_dn_fix_filter(module, req);
543                 if (ret != LDB_SUCCESS) {
544                         return ret;
545                 }
546         }
547
548         if (!ldb_dn_has_extended(dn)) {
549                 /* Move along there isn't anything to see here */
550                 return ldb_next_request(module, req);
551         } else {
552                 /* It looks like we need to map the DN */
553                 const struct ldb_val *sid_val, *guid_val, *wkguid_val;
554
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");
558                 }
559
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");
563
564                 /*
565                   prioritise the GUID - we have had instances of
566                   duplicate SIDs in the database in the
567                   ForeignSecurityPrinciples due to provision errors
568                  */
569                 if (guid_val) {
570                         all_partitions = true;
571                         base_dn = NULL;
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));
576                         }
577                         base_dn_scope = LDB_SCOPE_SUBTREE;
578                         base_dn_attrs = no_attr;
579
580                 } else if (sid_val) {
581                         all_partitions = true;
582                         base_dn = NULL;
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));
587                         }
588                         base_dn_scope = LDB_SCOPE_SUBTREE;
589                         base_dn_attrs = no_attr;
590
591                 } else if (wkguid_val) {
592                         char *wkguid_dup;
593                         char *tail_str;
594                         char *p;
595
596                         wkguid_dup = talloc_strndup(req, (char *)wkguid_val->data, wkguid_val->length);
597
598                         p = strchr(wkguid_dup, ',');
599                         if (!p) {
600                                 return ldb_error(ldb_module_get_ctx(module), LDB_ERR_INVALID_DN_SYNTAX,
601                                                  "Invalid WKGUID format");
602                         }
603
604                         p[0] = '\0';
605                         p++;
606
607                         wellknown_object = talloc_asprintf(req, "B:32:%s:", wkguid_dup);
608                         if (!wellknown_object) {
609                                 return ldb_oom(ldb_module_get_ctx(module));
610                         }
611
612                         tail_str = p;
613
614                         base_dn = ldb_dn_new(req, ldb_module_get_ctx(module), tail_str);
615                         talloc_free(wkguid_dup);
616                         if (!base_dn) {
617                                 return ldb_oom(ldb_module_get_ctx(module));
618                         }
619                         base_dn_filter = talloc_strdup(req, "(objectClass=*)");
620                         if (!base_dn_filter) {
621                                 return ldb_oom(ldb_module_get_ctx(module));
622                         }
623                         base_dn_scope = LDB_SCOPE_BASE;
624                         base_dn_attrs = wkattr;
625                 } else {
626                         return ldb_error(ldb_module_get_ctx(module), LDB_ERR_INVALID_DN_SYNTAX,
627                                          "Invalid extended DN component");
628                 }
629
630                 ac = talloc_zero(req, struct extended_search_context);
631                 if (ac == NULL) {
632                         return ldb_oom(ldb_module_get_ctx(module));
633                 }
634                 
635                 ac->module = module;
636                 ac->req = req;
637                 ac->dn = dn;
638                 ac->basedn = NULL;  /* Filled in if the search finds the DN by SID/GUID etc */
639                 ac->wellknown_object = wellknown_object;
640                 
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 */
643
644                 ret = ldb_build_search_req(&down_req,
645                                            ldb_module_get_ctx(module), ac,
646                                            base_dn,
647                                            base_dn_scope,
648                                            base_dn_filter,
649                                            base_dn_attrs,
650                                            req->controls,
651                                            ac, extended_base_callback,
652                                            req);
653                 LDB_REQ_SET_LOCATION(down_req);
654                 if (ret != LDB_SUCCESS) {
655                         return ldb_operr(ldb_module_get_ctx(module));
656                 }
657
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,
664                                                       true, control);
665                         if (ret != LDB_SUCCESS) {
666                                 ldb_oom(ldb_module_get_ctx(module));
667                                 return ret;
668                         }
669                 }
670
671                 /* perform the search */
672                 return ldb_next_request(module, down_req);
673         }
674 }
675
676 static int extended_dn_in_search(struct ldb_module *module, struct ldb_request *req)
677 {
678         return extended_dn_in_fix(module, req, req->op.search.base);
679 }
680
681 static int extended_dn_in_modify(struct ldb_module *module, struct ldb_request *req)
682 {
683         return extended_dn_in_fix(module, req, req->op.mod.message->dn);
684 }
685
686 static int extended_dn_in_del(struct ldb_module *module, struct ldb_request *req)
687 {
688         return extended_dn_in_fix(module, req, req->op.del.dn);
689 }
690
691 static int extended_dn_in_rename(struct ldb_module *module, struct ldb_request *req)
692 {
693         return extended_dn_in_fix(module, req, req->op.rename.olddn);
694 }
695
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,
702 };
703
704 int ldb_extended_dn_in_module_init(const char *version)
705 {
706         LDB_MODULE_CHECK_VERSION(version);
707         return ldb_register_module(&ldb_extended_dn_in_module_ops);
708 }