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