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