998634f334ad0fb6244349f67d6c4a1cf6d0e070
[sfrench/samba-autobuild/.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 "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 /* An extra layer of indirection because LDB does not allow the original request to be altered */
55
56 static int extended_final_callback(struct ldb_request *req, struct ldb_reply *ares)
57 {
58         int ret = LDB_ERR_OPERATIONS_ERROR;
59         struct extended_search_context *ac;
60         ac = talloc_get_type(req->context, struct extended_search_context);
61
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:
68                         
69                         ret = ldb_module_send_entry(ac->req, ares->message, ares->controls);
70                         break;
71                 case LDB_REPLY_REFERRAL:
72                         
73                         ret = ldb_module_send_referral(ac->req, ares->referral);
74                         break;
75                 case LDB_REPLY_DONE:
76                         
77                         ret = ldb_module_done(ac->req, ares->controls,
78                                               ares->response, ares->error);
79                         break;
80                 }
81         }
82         return ret;
83 }
84
85 static int extended_base_callback(struct ldb_request *req, struct ldb_reply *ares)
86 {
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;
95
96         ac = talloc_get_type(req->context, struct extended_search_context);
97
98         if (!ares) {
99                 return ldb_module_done(ac->req, NULL, NULL,
100                                         LDB_ERR_OPERATIONS_ERROR);
101         }
102         if (ares->error != LDB_SUCCESS) {
103                 return ldb_module_done(ac->req, ares->controls,
104                                         ares->response, ares->error);
105         }
106
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);
119                 }
120
121                 if (!ac->wellknown_object) {
122                         ac->basedn = talloc_steal(ac, ares->message->dn);
123                         break;
124                 }
125
126                 wkn_len = strlen(ac->wellknown_object);
127
128                 el = ldb_msg_find_element(ares->message, "wellKnownObjects");
129                 if (!el) {
130                         ac->basedn = NULL;
131                         break;
132                 }
133
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);
142                         }
143
144                         if (strncasecmp(valstr, ac->wellknown_object, wkn_len) != 0) {
145                                 talloc_free(valstr);
146                                 continue;
147                         }
148
149                         found = &valstr[wkn_len];
150                         break;
151                 }
152
153                 if (!found) {
154                         break;
155                 }
156
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);
163                 }
164
165                 break;
166
167         case LDB_REPLY_REFERRAL:
168                 break;
169
170         case LDB_REPLY_DONE:
171
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);
178                 }
179
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:
194                 {
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);
200                         }
201                         
202                         add_msg->dn = ac->basedn;
203
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;
212                 }
213                 case LDB_MODIFY:
214                 {
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);
220                         }
221                         
222                         mod_msg->dn = ac->basedn;
223
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;
232                 }
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);
254                 }
255                 
256                 if (ret != LDB_SUCCESS) {
257                         return ldb_module_done(ac->req, NULL, NULL, ret);
258                 }
259
260                 return ldb_next_request(ac->module, down_req);
261         }
262         talloc_free(ares);
263         return LDB_SUCCESS;
264 }
265
266
267 /*
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
271
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.
277
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
282   for
283 */
284 static bool ldb_dn_match_allowed(struct ldb_dn *dn, struct ldb_request *req)
285 {
286         int num_components = ldb_dn_get_comp_num(dn);
287         int num_ex_components = ldb_dn_get_extended_comp_num(dn);
288
289         if (num_ex_components == 0) {
290                 return true;
291         }
292
293         if ((num_components != 0 || num_ex_components != 1) &&
294             ldb_req_is_untrusted(req)) {
295                 return false;
296         }
297         return true;
298 }
299
300
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;
307 };
308
309 /*
310   create a always non-matching node from a equality node
311  */
312 static void set_parse_tree_false(struct ldb_parse_tree *tree)
313 {
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;
321 }
322
323 /*
324   called on all nodes in the parse tree
325  */
326 static int extended_dn_filter_callback(struct ldb_parse_tree *tree, void *private_context)
327 {
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;
340
341         if (tree->operation != LDB_OP_EQUALITY) {
342                 return LDB_SUCCESS;
343         }
344
345         filter_ctx = talloc_get_type_abort(private_context, struct extended_dn_filter_ctx);
346
347         if (filter_ctx->test_only && filter_ctx->matched) {
348                 /* the tree already matched */
349                 return LDB_SUCCESS;
350         }
351
352         attribute = dsdb_attribute_by_lDAPDisplayName(filter_ctx->schema, tree->u.equality.attr);
353         if (attribute == NULL) {
354                 return LDB_SUCCESS;
355         }
356
357         if (attribute->dn_format != DSDB_NORMAL_DN) {
358                 return LDB_SUCCESS;
359         }
360
361         has_extended_component = (memchr(tree->u.equality.value.data, '<',
362                                          tree->u.equality.value.length) != NULL);
363
364         if (!attribute->one_way_link && !has_extended_component) {
365                 return LDB_SUCCESS;
366         }
367
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;
373         }
374
375         guid_val = ldb_dn_get_extended_component(dn, "GUID");
376         sid_val  = ldb_dn_get_extended_component(dn, "SID");
377
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;
384         }
385
386         if (filter_ctx->test_only) {
387                 /* we need to copy the tree */
388                 filter_ctx->matched = true;
389                 return LDB_SUCCESS;
390         }
391
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;
397         }
398
399         dsdb_flags = DSDB_FLAG_NEXT_MODULE |
400                 DSDB_SEARCH_SHOW_DELETED |
401                 DSDB_SEARCH_SHOW_EXTENDED_DN;
402
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;
418         }
419
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;
434         }
435
436         if (ret != LDB_SUCCESS) {
437                 return LDB_SUCCESS;
438         }
439
440
441         if (res->count != 1) {
442                 return LDB_SUCCESS;
443         }
444
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));
450         }
451         tree->u.equality.value.length = strlen((const char *)tree->u.equality.value.data);
452         talloc_free(res);
453
454         filter_ctx->matched = true;
455         return LDB_SUCCESS;
456 }
457
458 /*
459   fix the parse tree to change any extended DN components to their
460   caconical form
461  */
462 static int extended_dn_fix_filter(struct ldb_module *module, struct ldb_request *req)
463 {
464         struct extended_dn_filter_ctx *filter_ctx;
465         int ret;
466
467         filter_ctx = talloc_zero(req, struct extended_dn_filter_ctx);
468         if (filter_ctx == NULL) {
469                 return ldb_module_oom(module);
470         }
471
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);
480
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;
485         }
486
487         if (!filter_ctx->matched) {
488                 /* nothing matched, no need for a new parse tree */
489                 talloc_free(filter_ctx);
490                 return LDB_SUCCESS;
491         }
492
493         filter_ctx->test_only = false;
494         filter_ctx->matched   = false;
495
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));
499         }
500
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;
505         }
506
507         talloc_free(filter_ctx);
508         return LDB_SUCCESS;
509 }
510
511 /*
512   fix DNs and filter expressions to cope with the semantics of
513   extended DNs
514  */
515 static int extended_dn_in_fix(struct ldb_module *module, struct ldb_request *req, struct ldb_dn *dn)
516 {
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
527         };
528         static const char *wkattr[] = {
529                 "wellKnownObjects",
530                 NULL
531         };
532         bool all_partitions = false;
533
534         if (req->operation == LDB_SEARCH) {
535                 ret = extended_dn_fix_filter(module, req);
536                 if (ret != LDB_SUCCESS) {
537                         return ret;
538                 }
539         }
540
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;
547
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");
551                 }
552
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");
556
557                 /*
558                   prioritise the GUID - we have had instances of
559                   duplicate SIDs in the database in the
560                   ForeignSecurityPrinciples due to provision errors
561                  */
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));
569                         }
570                         base_dn_scope = LDB_SCOPE_SUBTREE;
571                         base_dn_attrs = no_attr;
572
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));
580                         }
581                         base_dn_scope = LDB_SCOPE_SUBTREE;
582                         base_dn_attrs = no_attr;
583
584                 } else if (wkguid_val) {
585                         char *wkguid_dup;
586                         char *tail_str;
587                         char *p;
588
589                         wkguid_dup = talloc_strndup(req, (char *)wkguid_val->data, wkguid_val->length);
590
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");
595                         }
596
597                         p[0] = '\0';
598                         p++;
599
600                         wellknown_object = talloc_asprintf(req, "B:32:%s:", wkguid_dup);
601                         if (!wellknown_object) {
602                                 return ldb_oom(ldb_module_get_ctx(module));
603                         }
604
605                         tail_str = p;
606
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));
611                         }
612                         base_dn_filter = talloc_strdup(req, "(objectClass=*)");
613                         if (!base_dn_filter) {
614                                 return ldb_oom(ldb_module_get_ctx(module));
615                         }
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");
621                 }
622
623                 ac = talloc_zero(req, struct extended_search_context);
624                 if (ac == NULL) {
625                         return ldb_oom(ldb_module_get_ctx(module));
626                 }
627                 
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;
633                 
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 */
636
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));
649                 }
650
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;
661                         }
662                 }
663
664                 /* perform the search */
665                 return ldb_next_request(module, down_req);
666         }
667 }
668
669 static int extended_dn_in_search(struct ldb_module *module, struct ldb_request *req)
670 {
671         return extended_dn_in_fix(module, req, req->op.search.base);
672 }
673
674 static int extended_dn_in_modify(struct ldb_module *module, struct ldb_request *req)
675 {
676         return extended_dn_in_fix(module, req, req->op.mod.message->dn);
677 }
678
679 static int extended_dn_in_del(struct ldb_module *module, struct ldb_request *req)
680 {
681         return extended_dn_in_fix(module, req, req->op.del.dn);
682 }
683
684 static int extended_dn_in_rename(struct ldb_module *module, struct ldb_request *req)
685 {
686         return extended_dn_in_fix(module, req, req->op.rename.olddn);
687 }
688
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,
695 };
696
697 int ldb_extended_dn_in_module_init(const char *version)
698 {
699         LDB_MODULE_CHECK_VERSION(version);
700         return ldb_register_module(&ldb_extended_dn_in_module_ops);
701 }