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