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