s4-dsdb: extend the extended_dn_in module to handle DN links
[amitay/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 "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
340         if (tree->operation != LDB_OP_EQUALITY) {
341                 return LDB_SUCCESS;
342         }
343
344         filter_ctx = talloc_get_type_abort(private_context, struct extended_dn_filter_ctx);
345
346         attribute = dsdb_attribute_by_lDAPDisplayName(filter_ctx->schema, tree->u.equality.attr);
347         if (attribute == NULL) {
348                 return LDB_SUCCESS;
349         }
350
351         if (attribute->dn_format != DSDB_NORMAL_DN) {
352                 return LDB_SUCCESS;
353         }
354
355         has_extended_component = (memchr(tree->u.equality.value.data, '<',
356                                          tree->u.equality.value.length) != NULL);
357
358         if (!attribute->one_way_link && !has_extended_component) {
359                 return LDB_SUCCESS;
360         }
361
362         dn = ldb_dn_from_ldb_val(filter_ctx, ldb_module_get_ctx(filter_ctx->module), &tree->u.equality.value);
363         if (dn == NULL) {
364                 /* testing against windows shows that we don't raise
365                    an error here */
366                 return LDB_SUCCESS;
367         }
368
369         if (filter_ctx->test_only) {
370                 filter_ctx->matched = true;
371                 return LDB_SUCCESS;
372         }
373
374         if (!ldb_dn_match_allowed(dn, filter_ctx->req)) {
375                 /* we need to make this element of the filter always
376                    be false */
377                 set_parse_tree_false(tree);
378                 return LDB_SUCCESS;
379         }
380
381         guid_val = ldb_dn_get_extended_component(dn, "GUID");
382         sid_val  = ldb_dn_get_extended_component(dn, "SID");
383
384         if (guid_val) {
385                 expression = talloc_asprintf(filter_ctx, "objectGUID=%s", ldb_binary_encode(filter_ctx, *guid_val));
386                 scope = LDB_SCOPE_SUBTREE;
387                 base_dn = NULL;
388         } else if (sid_val) {
389                 expression = talloc_asprintf(filter_ctx, "objectSID=%s", ldb_binary_encode(filter_ctx, *sid_val));
390                 scope = LDB_SCOPE_SUBTREE;
391                 base_dn = NULL;
392         } else if (attribute->searchFlags & SEARCH_FLAG_ATTINDEX) {
393                 /* if it is indexed, then fixing the string DN will do
394                    no good here, as we will not find the attribute in
395                    the index. So for now fall through to a standard DN
396                    component comparison */
397                 return LDB_SUCCESS;
398         } else {
399                 /* fallback to searching using the string DN as the base DN */
400                 expression = "objectClass=*";
401                 base_dn = dn;
402                 scope = LDB_SCOPE_BASE;
403         }
404
405         ret = dsdb_module_search(filter_ctx->module,
406                                  filter_ctx,
407                                  &res,
408                                  base_dn,
409                                  scope,
410                                  no_attrs,
411                                  DSDB_FLAG_NEXT_MODULE |
412                                  DSDB_SEARCH_SHOW_DELETED |
413                                  DSDB_SEARCH_SHOW_EXTENDED_DN |
414                                  DSDB_SEARCH_SEARCH_ALL_PARTITIONS,
415                                  filter_ctx->req,
416                                  "%s", expression);
417         if (scope == LDB_SCOPE_BASE && ret == LDB_ERR_NO_SUCH_OBJECT) {
418                 /* note that this will need to change for multi-domain
419                    support */
420                 set_parse_tree_false(tree);
421                 return LDB_SUCCESS;
422         }
423
424         if (ret != LDB_SUCCESS) {
425                 return LDB_SUCCESS;
426         }
427
428
429         if (res->count != 1) {
430                 return LDB_SUCCESS;
431         }
432
433         /* replace the search expression element with the matching DN */
434         tree->u.equality.value.data = (uint8_t *)talloc_strdup(tree,
435                                                                ldb_dn_get_extended_linearized(tree, res->msgs[0]->dn, 1));
436         if (tree->u.equality.value.data == NULL) {
437                 return ldb_oom(ldb_module_get_ctx(filter_ctx->module));
438         }
439         tree->u.equality.value.length = strlen((const char *)tree->u.equality.value.data);
440         talloc_free(res);
441
442         filter_ctx->matched = true;
443         return LDB_SUCCESS;
444 }
445
446 /*
447   fix the parse tree to change any extended DN components to their
448   caconical form
449  */
450 static int extended_dn_fix_filter(struct ldb_module *module, struct ldb_request *req)
451 {
452         struct extended_dn_filter_ctx *filter_ctx;
453         int ret;
454
455         filter_ctx = talloc_zero(req, struct extended_dn_filter_ctx);
456         if (filter_ctx == NULL) {
457                 return ldb_module_oom(module);
458         }
459
460         /* first pass through the existing tree to see if anything
461            needs to be modified. Filtering DNs on the input side is rare,
462            so this avoids copying the parse tree in most cases */
463         filter_ctx->test_only = true;
464         filter_ctx->matched   = false;
465         filter_ctx->module    = module;
466         filter_ctx->req       = req;
467         filter_ctx->schema    = dsdb_get_schema(ldb_module_get_ctx(module), filter_ctx);
468
469         ret = ldb_parse_tree_walk(req->op.search.tree, extended_dn_filter_callback, filter_ctx);
470         if (ret != LDB_SUCCESS) {
471                 talloc_free(filter_ctx);
472                 return ret;
473         }
474
475         if (!filter_ctx->matched) {
476                 /* nothing matched, no need for a new parse tree */
477                 talloc_free(filter_ctx);
478                 return LDB_SUCCESS;
479         }
480
481         filter_ctx->test_only = false;
482         filter_ctx->matched   = false;
483
484         req->op.search.tree = ldb_parse_tree_copy_shallow(req, req->op.search.tree);
485         if (req->op.search.tree == NULL) {
486                 return ldb_oom(ldb_module_get_ctx(module));
487         }
488
489         ret = ldb_parse_tree_walk(req->op.search.tree, extended_dn_filter_callback, filter_ctx);
490         if (ret != LDB_SUCCESS) {
491                 talloc_free(filter_ctx);
492                 return ret;
493         }
494
495         talloc_free(filter_ctx);
496         return LDB_SUCCESS;
497 }
498
499 /*
500   fix DNs and filter expressions to cope with the semantics of
501   extended DNs
502  */
503 static int extended_dn_in_fix(struct ldb_module *module, struct ldb_request *req, struct ldb_dn *dn)
504 {
505         struct extended_search_context *ac;
506         struct ldb_request *down_req;
507         int ret;
508         struct ldb_dn *base_dn = NULL;
509         enum ldb_scope base_dn_scope = LDB_SCOPE_BASE;
510         const char *base_dn_filter = NULL;
511         const char * const *base_dn_attrs = NULL;
512         char *wellknown_object = NULL;
513         static const char *no_attr[] = {
514                 NULL
515         };
516         static const char *wkattr[] = {
517                 "wellKnownObjects",
518                 NULL
519         };
520         bool all_partitions = false;
521
522         if (req->operation == LDB_SEARCH) {
523                 ret = extended_dn_fix_filter(module, req);
524                 if (ret != LDB_SUCCESS) {
525                         return ret;
526                 }
527         }
528
529         if (!ldb_dn_has_extended(dn)) {
530                 /* Move along there isn't anything to see here */
531                 return ldb_next_request(module, req);
532         } else {
533                 /* It looks like we need to map the DN */
534                 const struct ldb_val *sid_val, *guid_val, *wkguid_val;
535
536                 if (!ldb_dn_match_allowed(dn, req)) {
537                         return ldb_error(ldb_module_get_ctx(module),
538                                          LDB_ERR_INVALID_DN_SYNTAX, "invalid number of DN components");
539                 }
540
541                 sid_val = ldb_dn_get_extended_component(dn, "SID");
542                 guid_val = ldb_dn_get_extended_component(dn, "GUID");
543                 wkguid_val = ldb_dn_get_extended_component(dn, "WKGUID");
544
545                 /*
546                   prioritise the GUID - we have had instances of
547                   duplicate SIDs in the database in the
548                   ForeignSecurityPrinciples due to provision errors
549                  */
550                 if (guid_val) {
551                         all_partitions = true;
552                         base_dn = ldb_get_default_basedn(ldb_module_get_ctx(module));
553                         base_dn_filter = talloc_asprintf(req, "(objectGUID=%s)",
554                                                          ldb_binary_encode(req, *guid_val));
555                         if (!base_dn_filter) {
556                                 return ldb_oom(ldb_module_get_ctx(module));
557                         }
558                         base_dn_scope = LDB_SCOPE_SUBTREE;
559                         base_dn_attrs = no_attr;
560
561                 } else if (sid_val) {
562                         all_partitions = true;
563                         base_dn = ldb_get_default_basedn(ldb_module_get_ctx(module));
564                         base_dn_filter = talloc_asprintf(req, "(objectSid=%s)",
565                                                          ldb_binary_encode(req, *sid_val));
566                         if (!base_dn_filter) {
567                                 return ldb_oom(ldb_module_get_ctx(module));
568                         }
569                         base_dn_scope = LDB_SCOPE_SUBTREE;
570                         base_dn_attrs = no_attr;
571
572                 } else if (wkguid_val) {
573                         char *wkguid_dup;
574                         char *tail_str;
575                         char *p;
576
577                         wkguid_dup = talloc_strndup(req, (char *)wkguid_val->data, wkguid_val->length);
578
579                         p = strchr(wkguid_dup, ',');
580                         if (!p) {
581                                 return ldb_error(ldb_module_get_ctx(module), LDB_ERR_INVALID_DN_SYNTAX,
582                                                  "Invalid WKGUID format");
583                         }
584
585                         p[0] = '\0';
586                         p++;
587
588                         wellknown_object = talloc_asprintf(req, "B:32:%s:", wkguid_dup);
589                         if (!wellknown_object) {
590                                 return ldb_oom(ldb_module_get_ctx(module));
591                         }
592
593                         tail_str = p;
594
595                         base_dn = ldb_dn_new(req, ldb_module_get_ctx(module), tail_str);
596                         talloc_free(wkguid_dup);
597                         if (!base_dn) {
598                                 return ldb_oom(ldb_module_get_ctx(module));
599                         }
600                         base_dn_filter = talloc_strdup(req, "(objectClass=*)");
601                         if (!base_dn_filter) {
602                                 return ldb_oom(ldb_module_get_ctx(module));
603                         }
604                         base_dn_scope = LDB_SCOPE_BASE;
605                         base_dn_attrs = wkattr;
606                 } else {
607                         return ldb_error(ldb_module_get_ctx(module), LDB_ERR_INVALID_DN_SYNTAX,
608                                          "Invalid extended DN component");
609                 }
610
611                 ac = talloc_zero(req, struct extended_search_context);
612                 if (ac == NULL) {
613                         return ldb_oom(ldb_module_get_ctx(module));
614                 }
615                 
616                 ac->module = module;
617                 ac->req = req;
618                 ac->dn = dn;
619                 ac->basedn = NULL;  /* Filled in if the search finds the DN by SID/GUID etc */
620                 ac->wellknown_object = wellknown_object;
621                 
622                 /* If the base DN was an extended DN (perhaps a well known
623                  * GUID) then search for that, so we can proceed with the original operation */
624
625                 ret = ldb_build_search_req(&down_req,
626                                            ldb_module_get_ctx(module), ac,
627                                            base_dn,
628                                            base_dn_scope,
629                                            base_dn_filter,
630                                            base_dn_attrs,
631                                            req->controls,
632                                            ac, extended_base_callback,
633                                            req);
634                 LDB_REQ_SET_LOCATION(down_req);
635                 if (ret != LDB_SUCCESS) {
636                         return ldb_operr(ldb_module_get_ctx(module));
637                 }
638
639                 if (all_partitions) {
640                         struct ldb_search_options_control *control;
641                         control = talloc(down_req, struct ldb_search_options_control);
642                         control->search_options = 2;
643                         ret = ldb_request_replace_control(down_req,
644                                                       LDB_CONTROL_SEARCH_OPTIONS_OID,
645                                                       true, control);
646                         if (ret != LDB_SUCCESS) {
647                                 ldb_oom(ldb_module_get_ctx(module));
648                                 return ret;
649                         }
650                 }
651
652                 /* perform the search */
653                 return ldb_next_request(module, down_req);
654         }
655 }
656
657 static int extended_dn_in_search(struct ldb_module *module, struct ldb_request *req)
658 {
659         return extended_dn_in_fix(module, req, req->op.search.base);
660 }
661
662 static int extended_dn_in_modify(struct ldb_module *module, struct ldb_request *req)
663 {
664         return extended_dn_in_fix(module, req, req->op.mod.message->dn);
665 }
666
667 static int extended_dn_in_del(struct ldb_module *module, struct ldb_request *req)
668 {
669         return extended_dn_in_fix(module, req, req->op.del.dn);
670 }
671
672 static int extended_dn_in_rename(struct ldb_module *module, struct ldb_request *req)
673 {
674         return extended_dn_in_fix(module, req, req->op.rename.olddn);
675 }
676
677 static const struct ldb_module_ops ldb_extended_dn_in_module_ops = {
678         .name              = "extended_dn_in",
679         .search            = extended_dn_in_search,
680         .modify            = extended_dn_in_modify,
681         .del               = extended_dn_in_del,
682         .rename            = extended_dn_in_rename,
683 };
684
685 int ldb_extended_dn_in_module_init(const char *version)
686 {
687         LDB_MODULE_CHECK_VERSION(version);
688         return ldb_register_module(&ldb_extended_dn_in_module_ops);
689 }