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