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