f738bc42fba0aee09898b8b440091b9a9ba92fb3
[obnox/samba/samba-obnox.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
60 static const struct ldb_module_ops ldb_extended_dn_in_openldap_module_ops;
61
62 /* An extra layer of indirection because LDB does not allow the original request to be altered */
63
64 static int extended_final_callback(struct ldb_request *req, struct ldb_reply *ares)
65 {
66         int ret = LDB_ERR_OPERATIONS_ERROR;
67         struct extended_search_context *ac;
68         ac = talloc_get_type(req->context, struct extended_search_context);
69
70         if (ares->error != LDB_SUCCESS) {
71                 ret = ldb_module_done(ac->req, ares->controls,
72                                       ares->response, ares->error);
73         } else {
74                 switch (ares->type) {
75                 case LDB_REPLY_ENTRY:
76                         
77                         ret = ldb_module_send_entry(ac->req, ares->message, ares->controls);
78                         break;
79                 case LDB_REPLY_REFERRAL:
80                         
81                         ret = ldb_module_send_referral(ac->req, ares->referral);
82                         break;
83                 case LDB_REPLY_DONE:
84                         
85                         ret = ldb_module_done(ac->req, ares->controls,
86                                               ares->response, ares->error);
87                         break;
88                 }
89         }
90         return ret;
91 }
92
93 static int extended_base_callback(struct ldb_request *req, struct ldb_reply *ares)
94 {
95         struct extended_search_context *ac;
96         struct ldb_request *down_req;
97         struct ldb_message_element *el;
98         int ret;
99         unsigned int i, j;
100         size_t wkn_len = 0;
101         char *valstr = NULL;
102         const char *found = NULL;
103
104         ac = talloc_get_type(req->context, struct extended_search_context);
105
106         if (!ares) {
107                 return ldb_module_done(ac->req, NULL, NULL,
108                                         LDB_ERR_OPERATIONS_ERROR);
109         }
110         if (ares->error != LDB_SUCCESS) {
111                 return ldb_module_done(ac->req, ares->controls,
112                                         ares->response, ares->error);
113         }
114
115         switch (ares->type) {
116         case LDB_REPLY_ENTRY:
117                 if (ac->basedn) {
118                         /* we have more than one match! This can
119                            happen as S-1-5-17 appears twice in a
120                            normal provision. We need to return
121                            NO_SUCH_OBJECT */
122                         const char *str = talloc_asprintf(req, "Duplicate base-DN matches found for '%s'",
123                                                           ldb_dn_get_extended_linearized(req, ac->dn, 1));
124                         ldb_set_errstring(ldb_module_get_ctx(ac->module), str);
125                         return ldb_module_done(ac->req, NULL, NULL,
126                                                LDB_ERR_NO_SUCH_OBJECT);
127                 }
128
129                 if (!ac->wellknown_object) {
130                         ac->basedn = talloc_steal(ac, ares->message->dn);
131                         break;
132                 }
133
134                 wkn_len = strlen(ac->wellknown_object);
135
136                 for (j=0; wkattr[j]; j++) {
137
138                         el = ldb_msg_find_element(ares->message, wkattr[j]);
139                         if (!el) {
140                                 ac->basedn = NULL;
141                                 continue;
142                         }
143
144                         for (i=0; i < el->num_values; i++) {
145                                 valstr = talloc_strndup(ac,
146                                                         (const char *)el->values[i].data,
147                                                         el->values[i].length);
148                                 if (!valstr) {
149                                         ldb_oom(ldb_module_get_ctx(ac->module));
150                                         return ldb_module_done(ac->req, NULL, NULL,
151                                                         LDB_ERR_OPERATIONS_ERROR);
152                                 }
153
154                                 if (strncasecmp(valstr, ac->wellknown_object, wkn_len) != 0) {
155                                         talloc_free(valstr);
156                                         continue;
157                                 }
158
159                                 found = &valstr[wkn_len];
160                                 break;
161                         }
162                         if (found) {
163                                 break;
164                         }
165                 }
166
167                 if (!found) {
168                         break;
169                 }
170
171                 ac->basedn = ldb_dn_new(ac, ldb_module_get_ctx(ac->module), found);
172                 talloc_free(valstr);
173                 if (!ac->basedn) {
174                         ldb_oom(ldb_module_get_ctx(ac->module));
175                         return ldb_module_done(ac->req, NULL, NULL,
176                                                LDB_ERR_OPERATIONS_ERROR);
177                 }
178
179                 break;
180
181         case LDB_REPLY_REFERRAL:
182                 break;
183
184         case LDB_REPLY_DONE:
185
186                 if (!ac->basedn) {
187                         const char *str = talloc_asprintf(req, "Base-DN '%s' not found",
188                                                           ldb_dn_get_extended_linearized(req, ac->dn, 1));
189                         ldb_set_errstring(ldb_module_get_ctx(ac->module), str);
190                         return ldb_module_done(ac->req, NULL, NULL,
191                                                LDB_ERR_NO_SUCH_OBJECT);
192                 }
193
194                 switch (ac->req->operation) {
195                 case LDB_SEARCH:
196                         ret = ldb_build_search_req_ex(&down_req,
197                                                       ldb_module_get_ctx(ac->module), ac->req,
198                                                       ac->basedn,
199                                                       ac->req->op.search.scope,
200                                                       ac->req->op.search.tree,
201                                                       ac->req->op.search.attrs,
202                                                       ac->req->controls,
203                                                       ac, extended_final_callback, 
204                                                       ac->req);
205                         LDB_REQ_SET_LOCATION(down_req);
206                         break;
207                 case LDB_ADD:
208                 {
209                         struct ldb_message *add_msg = ldb_msg_copy_shallow(ac, ac->req->op.add.message);
210                         if (!add_msg) {
211                                 ldb_oom(ldb_module_get_ctx(ac->module));
212                                 return ldb_module_done(ac->req, NULL, NULL,
213                                                        LDB_ERR_OPERATIONS_ERROR);
214                         }
215                         
216                         add_msg->dn = ac->basedn;
217
218                         ret = ldb_build_add_req(&down_req,
219                                                 ldb_module_get_ctx(ac->module), ac->req,
220                                                 add_msg, 
221                                                 ac->req->controls,
222                                                 ac, extended_final_callback, 
223                                                 ac->req);
224                         LDB_REQ_SET_LOCATION(down_req);
225                         break;
226                 }
227                 case LDB_MODIFY:
228                 {
229                         struct ldb_message *mod_msg = ldb_msg_copy_shallow(ac, ac->req->op.mod.message);
230                         if (!mod_msg) {
231                                 ldb_oom(ldb_module_get_ctx(ac->module));
232                                 return ldb_module_done(ac->req, NULL, NULL,
233                                                        LDB_ERR_OPERATIONS_ERROR);
234                         }
235                         
236                         mod_msg->dn = ac->basedn;
237
238                         ret = ldb_build_mod_req(&down_req,
239                                                 ldb_module_get_ctx(ac->module), ac->req,
240                                                 mod_msg, 
241                                                 ac->req->controls,
242                                                 ac, extended_final_callback, 
243                                                 ac->req);
244                         LDB_REQ_SET_LOCATION(down_req);
245                         break;
246                 }
247                 case LDB_DELETE:
248                         ret = ldb_build_del_req(&down_req,
249                                                 ldb_module_get_ctx(ac->module), ac->req,
250                                                 ac->basedn, 
251                                                 ac->req->controls,
252                                                 ac, extended_final_callback, 
253                                                 ac->req);
254                         LDB_REQ_SET_LOCATION(down_req);
255                         break;
256                 case LDB_RENAME:
257                         ret = ldb_build_rename_req(&down_req,
258                                                    ldb_module_get_ctx(ac->module), ac->req,
259                                                    ac->basedn, 
260                                                    ac->req->op.rename.newdn,
261                                                    ac->req->controls,
262                                                    ac, extended_final_callback, 
263                                                    ac->req);
264                         LDB_REQ_SET_LOCATION(down_req);
265                         break;
266                 default:
267                         return ldb_module_done(ac->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
268                 }
269                 
270                 if (ret != LDB_SUCCESS) {
271                         return ldb_module_done(ac->req, NULL, NULL, ret);
272                 }
273
274                 return ldb_next_request(ac->module, down_req);
275         }
276         talloc_free(ares);
277         return LDB_SUCCESS;
278 }
279
280
281 /*
282   windows ldap searchs don't allow a baseDN with more
283   than one extended component, or an extended
284   component and a string DN
285
286   We only enforce this over ldap, not for internal
287   use, as there are just too many places where we
288   internally want to use a DN that has come from a
289   search with extended DN enabled, or comes from a DRS
290   naming context.
291
292   Enforcing this would also make debugging samba much
293   harder, as we'd need to use ldb_dn_minimise() in a
294   lot of places, and that would lose the DN string
295   which is so useful for working out what a request is
296   for
297 */
298 static bool ldb_dn_match_allowed(struct ldb_dn *dn, struct ldb_request *req)
299 {
300         int num_components = ldb_dn_get_comp_num(dn);
301         int num_ex_components = ldb_dn_get_extended_comp_num(dn);
302
303         if (num_ex_components == 0) {
304                 return true;
305         }
306
307         if ((num_components != 0 || num_ex_components != 1) &&
308             ldb_req_is_untrusted(req)) {
309                 return false;
310         }
311         return true;
312 }
313
314
315 struct extended_dn_filter_ctx {
316         bool test_only;
317         bool matched;
318         struct ldb_module *module;
319         struct ldb_request *req;
320         struct dsdb_schema *schema;
321         uint32_t dsdb_flags;
322 };
323
324 /*
325   create a always non-matching node from a equality node
326  */
327 static void set_parse_tree_false(struct ldb_parse_tree *tree)
328 {
329         const char *attr = tree->u.equality.attr;
330         struct ldb_val value = tree->u.equality.value;
331         tree->operation = LDB_OP_EXTENDED;
332         tree->u.extended.attr = attr;
333         tree->u.extended.value = value;
334         tree->u.extended.rule_id = SAMBA_LDAP_MATCH_ALWAYS_FALSE;
335         tree->u.extended.dnAttributes = 0;
336 }
337
338 /*
339   called on all nodes in the parse tree
340  */
341 static int extended_dn_filter_callback(struct ldb_parse_tree *tree, void *private_context)
342 {
343         struct extended_dn_filter_ctx *filter_ctx;
344         int ret;
345         struct ldb_dn *dn;
346         const struct ldb_val *sid_val, *guid_val;
347         const char *no_attrs[] = { NULL };
348         struct ldb_result *res;
349         const struct dsdb_attribute *attribute;
350         bool has_extended_component;
351         enum ldb_scope scope;
352         struct ldb_dn *base_dn;
353         const char *expression;
354         uint32_t dsdb_flags;
355
356         if (tree->operation != LDB_OP_EQUALITY) {
357                 return LDB_SUCCESS;
358         }
359
360         filter_ctx = talloc_get_type_abort(private_context, struct extended_dn_filter_ctx);
361
362         if (filter_ctx->test_only && filter_ctx->matched) {
363                 /* the tree already matched */
364                 return LDB_SUCCESS;
365         }
366
367         if (!filter_ctx->schema) {
368                 /* Schema not setup yet */
369                 return LDB_SUCCESS;
370         }
371         attribute = dsdb_attribute_by_lDAPDisplayName(filter_ctx->schema, tree->u.equality.attr);
372         if (attribute == NULL) {
373                 return LDB_SUCCESS;
374         }
375
376         if (attribute->dn_format != DSDB_NORMAL_DN) {
377                 return LDB_SUCCESS;
378         }
379
380         has_extended_component = (memchr(tree->u.equality.value.data, '<',
381                                          tree->u.equality.value.length) != NULL);
382
383         /*
384          * Don't turn it into an extended DN if we're talking to OpenLDAP.
385          * We just check the module_ops pointer instead of adding a private
386          * pointer and a boolean to tell us the exact same thing.
387          */
388         if (!has_extended_component) {
389                 if (!attribute->one_way_link ||
390                     ldb_module_get_ops(filter_ctx->module) == &ldb_extended_dn_in_openldap_module_ops)
391                 return LDB_SUCCESS;
392         }
393
394         dn = ldb_dn_from_ldb_val(filter_ctx, ldb_module_get_ctx(filter_ctx->module), &tree->u.equality.value);
395         if (dn == NULL) {
396                 /* testing against windows shows that we don't raise
397                    an error here */
398                 return LDB_SUCCESS;
399         }
400
401         guid_val = ldb_dn_get_extended_component(dn, "GUID");
402         sid_val  = ldb_dn_get_extended_component(dn, "SID");
403
404         if (!guid_val && !sid_val && (attribute->searchFlags & SEARCH_FLAG_ATTINDEX)) {
405                 /* if it is indexed, then fixing the string DN will do
406                    no good here, as we will not find the attribute in
407                    the index. So for now fall through to a standard DN
408                    component comparison */
409                 return LDB_SUCCESS;
410         }
411
412         if (filter_ctx->test_only) {
413                 /* we need to copy the tree */
414                 filter_ctx->matched = true;
415                 return LDB_SUCCESS;
416         }
417
418         if (!ldb_dn_match_allowed(dn, filter_ctx->req)) {
419                 /* we need to make this element of the filter always
420                    be false */
421                 set_parse_tree_false(tree);
422                 return LDB_SUCCESS;
423         }
424
425         dsdb_flags = filter_ctx->dsdb_flags | DSDB_FLAG_NEXT_MODULE;
426
427         if (guid_val) {
428                 expression = talloc_asprintf(filter_ctx, "objectGUID=%s", ldb_binary_encode(filter_ctx, *guid_val));
429                 scope = LDB_SCOPE_SUBTREE;
430                 base_dn = NULL;
431                 dsdb_flags |= DSDB_SEARCH_SEARCH_ALL_PARTITIONS;
432         } else if (sid_val) {
433                 expression = talloc_asprintf(filter_ctx, "objectSID=%s", ldb_binary_encode(filter_ctx, *sid_val));
434                 scope = LDB_SCOPE_SUBTREE;
435                 base_dn = NULL;
436                 dsdb_flags |= DSDB_SEARCH_SEARCH_ALL_PARTITIONS;
437         } else {
438                 /* fallback to searching using the string DN as the base DN */
439                 expression = "objectClass=*";
440                 base_dn = dn;
441                 scope = LDB_SCOPE_BASE;
442         }
443
444         ret = dsdb_module_search(filter_ctx->module,
445                                  filter_ctx,
446                                  &res,
447                                  base_dn,
448                                  scope,
449                                  no_attrs,
450                                  dsdb_flags,
451                                  filter_ctx->req,
452                                  "%s", expression);
453         if (scope == LDB_SCOPE_BASE && ret == LDB_ERR_NO_SUCH_OBJECT) {
454                 /* note that this will need to change for multi-domain
455                    support */
456                 set_parse_tree_false(tree);
457                 return LDB_SUCCESS;
458         }
459
460         if (ret != LDB_SUCCESS) {
461                 return LDB_SUCCESS;
462         }
463
464
465         if (res->count != 1) {
466                 return LDB_SUCCESS;
467         }
468
469         /* replace the search expression element with the matching DN */
470         tree->u.equality.value.data = (uint8_t *)talloc_strdup(tree,
471                                                                ldb_dn_get_extended_linearized(tree, res->msgs[0]->dn, 1));
472         if (tree->u.equality.value.data == NULL) {
473                 return ldb_oom(ldb_module_get_ctx(filter_ctx->module));
474         }
475         tree->u.equality.value.length = strlen((const char *)tree->u.equality.value.data);
476         talloc_free(res);
477
478         filter_ctx->matched = true;
479         return LDB_SUCCESS;
480 }
481
482 /*
483   fix the parse tree to change any extended DN components to their
484   caconical form
485  */
486 static int extended_dn_fix_filter(struct ldb_module *module,
487                                   struct ldb_request *req,
488                                   uint32_t default_dsdb_flags)
489 {
490         struct extended_dn_filter_ctx *filter_ctx;
491         int ret;
492
493         filter_ctx = talloc_zero(req, struct extended_dn_filter_ctx);
494         if (filter_ctx == NULL) {
495                 return ldb_module_oom(module);
496         }
497
498         /* first pass through the existing tree to see if anything
499            needs to be modified. Filtering DNs on the input side is rare,
500            so this avoids copying the parse tree in most cases */
501         filter_ctx->test_only = true;
502         filter_ctx->matched   = false;
503         filter_ctx->module    = module;
504         filter_ctx->req       = req;
505         filter_ctx->schema    = dsdb_get_schema(ldb_module_get_ctx(module), filter_ctx);
506         filter_ctx->dsdb_flags= default_dsdb_flags;
507
508         ret = ldb_parse_tree_walk(req->op.search.tree, extended_dn_filter_callback, filter_ctx);
509         if (ret != LDB_SUCCESS) {
510                 talloc_free(filter_ctx);
511                 return ret;
512         }
513
514         if (!filter_ctx->matched) {
515                 /* nothing matched, no need for a new parse tree */
516                 talloc_free(filter_ctx);
517                 return LDB_SUCCESS;
518         }
519
520         filter_ctx->test_only = false;
521         filter_ctx->matched   = false;
522
523         req->op.search.tree = ldb_parse_tree_copy_shallow(req, req->op.search.tree);
524         if (req->op.search.tree == NULL) {
525                 return ldb_oom(ldb_module_get_ctx(module));
526         }
527
528         ret = ldb_parse_tree_walk(req->op.search.tree, extended_dn_filter_callback, filter_ctx);
529         if (ret != LDB_SUCCESS) {
530                 talloc_free(filter_ctx);
531                 return ret;
532         }
533
534         talloc_free(filter_ctx);
535         return LDB_SUCCESS;
536 }
537
538 /*
539   fix DNs and filter expressions to cope with the semantics of
540   extended DNs
541  */
542 static int extended_dn_in_fix(struct ldb_module *module, struct ldb_request *req, struct ldb_dn *dn)
543 {
544         struct extended_search_context *ac;
545         struct ldb_request *down_req;
546         int ret;
547         struct ldb_dn *base_dn = NULL;
548         enum ldb_scope base_dn_scope = LDB_SCOPE_BASE;
549         const char *base_dn_filter = NULL;
550         const char * const *base_dn_attrs = NULL;
551         char *wellknown_object = NULL;
552         static const char *no_attr[] = {
553                 NULL
554         };
555         uint32_t dsdb_flags = DSDB_FLAG_AS_SYSTEM | DSDB_SEARCH_SHOW_EXTENDED_DN;
556
557         if (ldb_request_get_control(req, LDB_CONTROL_SHOW_DELETED_OID)) {
558                 dsdb_flags |= DSDB_SEARCH_SHOW_DELETED;
559         }
560         if (ldb_request_get_control(req, LDB_CONTROL_SHOW_RECYCLED_OID)) {
561                 dsdb_flags |= DSDB_SEARCH_SHOW_RECYCLED;
562         }
563         if (ldb_request_get_control(req, DSDB_CONTROL_DBCHECK)) {
564                 dsdb_flags |= DSDB_SEARCH_SHOW_RECYCLED;
565         }
566
567         if (req->operation == LDB_SEARCH) {
568                 ret = extended_dn_fix_filter(module, req, dsdb_flags);
569                 if (ret != LDB_SUCCESS) {
570                         return ret;
571                 }
572         }
573
574         if (!ldb_dn_has_extended(dn)) {
575                 /* Move along there isn't anything to see here */
576                 return ldb_next_request(module, req);
577         } else {
578                 /* It looks like we need to map the DN */
579                 const struct ldb_val *sid_val, *guid_val, *wkguid_val;
580
581                 if (!ldb_dn_match_allowed(dn, req)) {
582                         return ldb_error(ldb_module_get_ctx(module),
583                                          LDB_ERR_INVALID_DN_SYNTAX, "invalid number of DN components");
584                 }
585
586                 sid_val = ldb_dn_get_extended_component(dn, "SID");
587                 guid_val = ldb_dn_get_extended_component(dn, "GUID");
588                 wkguid_val = ldb_dn_get_extended_component(dn, "WKGUID");
589
590                 /*
591                   prioritise the GUID - we have had instances of
592                   duplicate SIDs in the database in the
593                   ForeignSecurityPrinciples due to provision errors
594                  */
595                 if (guid_val) {
596                         dsdb_flags |= DSDB_SEARCH_SEARCH_ALL_PARTITIONS;
597                         base_dn = NULL;
598                         base_dn_filter = talloc_asprintf(req, "(objectGUID=%s)",
599                                                          ldb_binary_encode(req, *guid_val));
600                         if (!base_dn_filter) {
601                                 return ldb_oom(ldb_module_get_ctx(module));
602                         }
603                         base_dn_scope = LDB_SCOPE_SUBTREE;
604                         base_dn_attrs = no_attr;
605
606                 } else if (sid_val) {
607                         dsdb_flags |= DSDB_SEARCH_SEARCH_ALL_PARTITIONS;
608                         base_dn = NULL;
609                         base_dn_filter = talloc_asprintf(req, "(objectSid=%s)",
610                                                          ldb_binary_encode(req, *sid_val));
611                         if (!base_dn_filter) {
612                                 return ldb_oom(ldb_module_get_ctx(module));
613                         }
614                         base_dn_scope = LDB_SCOPE_SUBTREE;
615                         base_dn_attrs = no_attr;
616
617                 } else if (wkguid_val) {
618                         char *wkguid_dup;
619                         char *tail_str;
620                         char *p;
621
622                         wkguid_dup = talloc_strndup(req, (char *)wkguid_val->data, wkguid_val->length);
623
624                         p = strchr(wkguid_dup, ',');
625                         if (!p) {
626                                 return ldb_error(ldb_module_get_ctx(module), LDB_ERR_INVALID_DN_SYNTAX,
627                                                  "Invalid WKGUID format");
628                         }
629
630                         p[0] = '\0';
631                         p++;
632
633                         wellknown_object = talloc_asprintf(req, "B:32:%s:", wkguid_dup);
634                         if (!wellknown_object) {
635                                 return ldb_oom(ldb_module_get_ctx(module));
636                         }
637
638                         tail_str = p;
639
640                         base_dn = ldb_dn_new(req, ldb_module_get_ctx(module), tail_str);
641                         talloc_free(wkguid_dup);
642                         if (!base_dn) {
643                                 return ldb_oom(ldb_module_get_ctx(module));
644                         }
645                         base_dn_filter = talloc_strdup(req, "(objectClass=*)");
646                         if (!base_dn_filter) {
647                                 return ldb_oom(ldb_module_get_ctx(module));
648                         }
649                         base_dn_scope = LDB_SCOPE_BASE;
650                         base_dn_attrs = wkattr;
651                 } else {
652                         return ldb_error(ldb_module_get_ctx(module), LDB_ERR_INVALID_DN_SYNTAX,
653                                          "Invalid extended DN component");
654                 }
655
656                 ac = talloc_zero(req, struct extended_search_context);
657                 if (ac == NULL) {
658                         return ldb_oom(ldb_module_get_ctx(module));
659                 }
660                 
661                 ac->module = module;
662                 ac->req = req;
663                 ac->dn = dn;
664                 ac->basedn = NULL;  /* Filled in if the search finds the DN by SID/GUID etc */
665                 ac->wellknown_object = wellknown_object;
666                 
667                 /* If the base DN was an extended DN (perhaps a well known
668                  * GUID) then search for that, so we can proceed with the original operation */
669
670                 ret = ldb_build_search_req(&down_req,
671                                            ldb_module_get_ctx(module), ac,
672                                            base_dn,
673                                            base_dn_scope,
674                                            base_dn_filter,
675                                            base_dn_attrs,
676                                            NULL,
677                                            ac, extended_base_callback,
678                                            req);
679                 LDB_REQ_SET_LOCATION(down_req);
680                 if (ret != LDB_SUCCESS) {
681                         return ldb_operr(ldb_module_get_ctx(module));
682                 }
683
684                 ret = dsdb_request_add_controls(down_req, dsdb_flags);
685                 if (ret != LDB_SUCCESS) {
686                         return ret;
687                 }
688
689                 /* perform the search */
690                 return ldb_next_request(module, down_req);
691         }
692 }
693
694 static int extended_dn_in_search(struct ldb_module *module, struct ldb_request *req)
695 {
696         return extended_dn_in_fix(module, req, req->op.search.base);
697 }
698
699 static int extended_dn_in_modify(struct ldb_module *module, struct ldb_request *req)
700 {
701         return extended_dn_in_fix(module, req, req->op.mod.message->dn);
702 }
703
704 static int extended_dn_in_del(struct ldb_module *module, struct ldb_request *req)
705 {
706         return extended_dn_in_fix(module, req, req->op.del.dn);
707 }
708
709 static int extended_dn_in_rename(struct ldb_module *module, struct ldb_request *req)
710 {
711         return extended_dn_in_fix(module, req, req->op.rename.olddn);
712 }
713
714 static const struct ldb_module_ops ldb_extended_dn_in_module_ops = {
715         .name              = "extended_dn_in",
716         .search            = extended_dn_in_search,
717         .modify            = extended_dn_in_modify,
718         .del               = extended_dn_in_del,
719         .rename            = extended_dn_in_rename,
720 };
721
722 static const struct ldb_module_ops ldb_extended_dn_in_openldap_module_ops = {
723         .name              = "extended_dn_in_openldap",
724         .search            = extended_dn_in_search,
725         .modify            = extended_dn_in_modify,
726         .del               = extended_dn_in_del,
727         .rename            = extended_dn_in_rename,
728 };
729
730 int ldb_extended_dn_in_module_init(const char *version)
731 {
732         int ret;
733         LDB_MODULE_CHECK_VERSION(version);
734         ret = ldb_register_module(&ldb_extended_dn_in_openldap_module_ops);
735         if (ret != LDB_SUCCESS) {
736                 return ret;
737         }
738         return ldb_register_module(&ldb_extended_dn_in_module_ops);
739 }