Fix bug 6382: Case insensitive access to DFS links broken
[samba.git] / source / smbd / msdfs.c
1 /*
2    Unix SMB/Netbios implementation.
3    Version 3.0
4    MSDFS services for Samba
5    Copyright (C) Shirish Kalele 2000
6    Copyright (C) Jeremy Allison 2007
7
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 3 of the License, or
11    (at your option) any later version.
12
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program.  If not, see <http://www.gnu.org/licenses/>.
20
21 */
22
23 #define DBGC_CLASS DBGC_MSDFS
24 #include "includes.h"
25
26 extern uint32 global_client_caps;
27
28 /**********************************************************************
29  Parse a DFS pathname of the form \hostname\service\reqpath
30  into the dfs_path structure.
31  If POSIX pathnames is true, the pathname may also be of the
32  form /hostname/service/reqpath.
33  We cope with either here.
34
35  Unfortunately, due to broken clients who might set the
36  SVAL(inbuf,smb_flg2) & FLAGS2_DFS_PATHNAMES bit and then
37  send a local path, we have to cope with that too....
38
39  If conn != NULL then ensure the provided service is
40  the one pointed to by the connection.
41
42  This version does everything using pointers within one copy of the
43  pathname string, talloced on the struct dfs_path pointer (which
44  must be talloced). This may be too clever to live....
45  JRA.
46 **********************************************************************/
47
48 static NTSTATUS parse_dfs_path(connection_struct *conn,
49                                 const char *pathname,
50                                 bool allow_wcards,
51                                 struct dfs_path *pdp, /* MUST BE TALLOCED */
52                                 bool *ppath_contains_wcard)
53 {
54         char *pathname_local;
55         char *p,*temp;
56         char *servicename;
57         char *eos_ptr;
58         NTSTATUS status = NT_STATUS_OK;
59         char sepchar;
60
61         ZERO_STRUCTP(pdp);
62
63         /*
64          * This is the only talloc we should need to do
65          * on the struct dfs_path. All the pointers inside
66          * it should point to offsets within this string.
67          */
68
69         pathname_local = talloc_strdup(pdp, pathname);
70         if (!pathname_local) {
71                 return NT_STATUS_NO_MEMORY;
72         }
73         /* Get a pointer to the terminating '\0' */
74         eos_ptr = &pathname_local[strlen(pathname_local)];
75         p = temp = pathname_local;
76
77         pdp->posix_path = (lp_posix_pathnames() && *pathname == '/');
78
79         sepchar = pdp->posix_path ? '/' : '\\';
80
81         if (*pathname != sepchar) {
82                 DEBUG(10,("parse_dfs_path: path %s doesn't start with %c\n",
83                         pathname, sepchar ));
84                 /*
85                  * Possibly client sent a local path by mistake.
86                  * Try and convert to a local path.
87                  */
88
89                 pdp->hostname = eos_ptr; /* "" */
90                 pdp->servicename = eos_ptr; /* "" */
91
92                 /* We've got no info about separators. */
93                 pdp->posix_path = lp_posix_pathnames();
94                 p = temp;
95                 DEBUG(10,("parse_dfs_path: trying to convert %s to a "
96                         "local path\n",
97                         temp));
98                 goto local_path;
99         }
100
101         /*
102          * Safe to use on talloc'ed string as it only shrinks.
103          * It also doesn't affect the eos_ptr.
104          */
105         trim_char(temp,sepchar,sepchar);
106
107         DEBUG(10,("parse_dfs_path: temp = |%s| after trimming %c's\n",
108                 temp, sepchar));
109
110         /* Now tokenize. */
111         /* Parse out hostname. */
112         p = strchr_m(temp,sepchar);
113         if(p == NULL) {
114                 DEBUG(10,("parse_dfs_path: can't parse hostname from path %s\n",
115                         temp));
116                 /*
117                  * Possibly client sent a local path by mistake.
118                  * Try and convert to a local path.
119                  */
120
121                 pdp->hostname = eos_ptr; /* "" */
122                 pdp->servicename = eos_ptr; /* "" */
123
124                 p = temp;
125                 DEBUG(10,("parse_dfs_path: trying to convert %s "
126                         "to a local path\n",
127                         temp));
128                 goto local_path;
129         }
130         *p = '\0';
131         pdp->hostname = temp;
132
133         DEBUG(10,("parse_dfs_path: hostname: %s\n",pdp->hostname));
134
135         /* Parse out servicename. */
136         servicename = p+1;
137         p = strchr_m(servicename,sepchar);
138         if (p) {
139                 *p = '\0';
140         }
141
142         /* Is this really our servicename ? */
143         if (conn && !( strequal(servicename, lp_servicename(SNUM(conn)))
144                         || (strequal(servicename, HOMES_NAME)
145                         && strequal(lp_servicename(SNUM(conn)),
146                                 get_current_username()) )) ) {
147                 DEBUG(10,("parse_dfs_path: %s is not our servicename\n",
148                         servicename));
149
150                 /*
151                  * Possibly client sent a local path by mistake.
152                  * Try and convert to a local path.
153                  */
154
155                 pdp->hostname = eos_ptr; /* "" */
156                 pdp->servicename = eos_ptr; /* "" */
157
158                 /* Repair the path - replace the sepchar's
159                    we nulled out */
160                 servicename--;
161                 *servicename = sepchar;
162                 if (p) {
163                         *p = sepchar;
164                 }
165
166                 p = temp;
167                 DEBUG(10,("parse_dfs_path: trying to convert %s "
168                         "to a local path\n",
169                         temp));
170                 goto local_path;
171         }
172
173         pdp->servicename = servicename;
174
175         DEBUG(10,("parse_dfs_path: servicename: %s\n",pdp->servicename));
176
177         if(p == NULL) {
178                 /* Client sent self referral \server\share. */
179                 pdp->reqpath = eos_ptr; /* "" */
180                 return NT_STATUS_OK;
181         }
182
183         p++;
184
185   local_path:
186
187         *ppath_contains_wcard = False;
188
189         pdp->reqpath = p;
190
191         /* Rest is reqpath. */
192         if (pdp->posix_path) {
193                 status = check_path_syntax_posix(pdp->reqpath);
194         } else {
195                 if (allow_wcards) {
196                         status = check_path_syntax_wcard(pdp->reqpath,
197                                         ppath_contains_wcard);
198                 } else {
199                         status = check_path_syntax(pdp->reqpath);
200                 }
201         }
202
203         if (!NT_STATUS_IS_OK(status)) {
204                 DEBUG(10,("parse_dfs_path: '%s' failed with %s\n",
205                         p, nt_errstr(status) ));
206                 return status;
207         }
208
209         DEBUG(10,("parse_dfs_path: rest of the path: %s\n",pdp->reqpath));
210         return NT_STATUS_OK;
211 }
212
213 /********************************************************
214  Fake up a connection struct for the VFS layer.
215  Note this CHANGES CWD !!!! JRA.
216 *********************************************************/
217
218 static NTSTATUS create_conn_struct(TALLOC_CTX *ctx,
219                                 connection_struct *conn,
220                                 int snum,
221                                 const char *path)
222 {
223         char *connpath;
224
225         ZERO_STRUCTP(conn);
226
227         connpath = talloc_strdup(ctx, path);
228         if (!connpath) {
229                 return NT_STATUS_NO_MEMORY;
230         }
231         connpath = talloc_string_sub(ctx,
232                                 connpath,
233                                 "%S",
234                                 lp_servicename(snum));
235         if (!connpath) {
236                 return NT_STATUS_NO_MEMORY;
237         }
238
239         /* needed for smbd_vfs_init() */
240
241         if ((conn->mem_ctx=talloc_init("connection_struct")) == NULL) {
242                 DEBUG(0,("talloc_init(connection_struct) failed!\n"));
243                 return NT_STATUS_NO_MEMORY;
244         }
245
246         if (!(conn->params = TALLOC_ZERO_P(conn->mem_ctx,
247                                         struct share_params))) {
248                 DEBUG(0, ("TALLOC failed\n"));
249                 return NT_STATUS_NO_MEMORY;
250         }
251
252         conn->params->service = snum;
253
254         set_conn_connectpath(conn, connpath);
255
256         if (!smbd_vfs_init(conn)) {
257                 NTSTATUS status = map_nt_error_from_unix(errno);
258                 DEBUG(0,("create_conn_struct: smbd_vfs_init failed.\n"));
259                 conn_free_internal(conn);
260                 return status;
261         }
262
263         conn->fs_capabilities = SMB_VFS_FS_CAPABILITIES(conn);
264
265         /*
266          * Windows seems to insist on doing trans2getdfsreferral() calls on
267          * the IPC$ share as the anonymous user. If we try to chdir as that
268          * user we will fail.... WTF ? JRA.
269          */
270
271         if (vfs_ChDir(conn,conn->connectpath) != 0) {
272                 NTSTATUS status = map_nt_error_from_unix(errno);
273                 DEBUG(3,("create_conn_struct: Can't ChDir to new conn path %s. "
274                         "Error was %s\n",
275                         conn->connectpath, strerror(errno) ));
276                 conn_free_internal(conn);
277                 return status;
278         }
279
280         return NT_STATUS_OK;
281 }
282
283 /**********************************************************************
284  Parse the contents of a symlink to verify if it is an msdfs referral
285  A valid referral is of the form:
286
287  msdfs:server1\share1,server2\share2
288  msdfs:server1\share1\pathname,server2\share2\pathname
289  msdfs:server1/share1,server2/share2
290  msdfs:server1/share1/pathname,server2/share2/pathname.
291
292  Note that the alternate paths returned here must be of the canonicalized
293  form:
294
295  \server\share or
296  \server\share\path\to\file,
297
298  even in posix path mode. This is because we have no knowledge if the
299  server we're referring to understands posix paths.
300  **********************************************************************/
301
302 static bool parse_msdfs_symlink(TALLOC_CTX *ctx,
303                                 const char *target,
304                                 struct referral **preflist,
305                                 int *refcount)
306 {
307         char *temp = NULL;
308         char *prot;
309         char **alt_path = NULL;
310         int count = 0, i;
311         struct referral *reflist;
312         char *saveptr;
313
314         temp = talloc_strdup(ctx, target);
315         if (!temp) {
316                 return False;
317         }
318         prot = strtok_r(temp, ":", &saveptr);
319         if (!prot) {
320                 DEBUG(0,("parse_msdfs_symlink: invalid path !\n"));
321                 return False;
322         }
323
324         alt_path = TALLOC_ARRAY(ctx, char *, MAX_REFERRAL_COUNT);
325         if (!alt_path) {
326                 return False;
327         }
328
329         /* parse out the alternate paths */
330         while((count<MAX_REFERRAL_COUNT) &&
331               ((alt_path[count] = strtok_r(NULL, ",", &saveptr)) != NULL)) {
332                 count++;
333         }
334
335         DEBUG(10,("parse_msdfs_symlink: count=%d\n", count));
336
337         if (count) {
338                 reflist = *preflist = TALLOC_ZERO_ARRAY(ctx,
339                                 struct referral, count);
340                 if(reflist == NULL) {
341                         TALLOC_FREE(alt_path);
342                         return False;
343                 }
344         } else {
345                 reflist = *preflist = NULL;
346         }
347
348         for(i=0;i<count;i++) {
349                 char *p;
350
351                 /* Canonicalize link target.
352                  * Replace all /'s in the path by a \ */
353                 string_replace(alt_path[i], '/', '\\');
354
355                 /* Remove leading '\\'s */
356                 p = alt_path[i];
357                 while (*p && (*p == '\\')) {
358                         p++;
359                 }
360
361                 reflist[i].alternate_path = talloc_asprintf(ctx,
362                                 "\\%s",
363                                 p);
364                 if (!reflist[i].alternate_path) {
365                         return False;
366                 }
367
368                 reflist[i].proximity = 0;
369                 reflist[i].ttl = REFERRAL_TTL;
370                 DEBUG(10, ("parse_msdfs_symlink: Created alt path: %s\n",
371                                         reflist[i].alternate_path));
372         }
373
374         *refcount = count;
375
376         TALLOC_FREE(alt_path);
377         return True;
378 }
379
380 /**********************************************************************
381  Returns true if the unix path is a valid msdfs symlink and also
382  returns the target string from inside the link.
383 **********************************************************************/
384
385 static bool is_msdfs_link_internal(TALLOC_CTX *ctx,
386                         connection_struct *conn,
387                         const char *path,
388                         char **pp_link_target,
389                         SMB_STRUCT_STAT *sbufp)
390 {
391         SMB_STRUCT_STAT st;
392         int referral_len = 0;
393         char link_target_buf[7];
394         size_t bufsize = 0;
395         char *link_target = NULL;
396
397         if (pp_link_target) {
398                 bufsize = 1024;
399                 link_target = TALLOC_ARRAY(ctx, char, bufsize);
400                 if (!link_target) {
401                         return False;
402                 }
403                 *pp_link_target = link_target;
404         } else {
405                 bufsize = sizeof(link_target_buf);
406                 link_target = link_target_buf;
407         }
408
409         if (sbufp == NULL) {
410                 sbufp = &st;
411         }
412
413         if (SMB_VFS_LSTAT(conn, path, sbufp) != 0) {
414                 DEBUG(5,("is_msdfs_link_read_target: %s does not exist.\n",
415                         path));
416                 goto err;
417         }
418
419         if (!S_ISLNK(sbufp->st_mode)) {
420                 DEBUG(5,("is_msdfs_link_read_target: %s is not a link.\n",
421                                         path));
422                 goto err;
423         }
424
425         referral_len = SMB_VFS_READLINK(conn, path, link_target, bufsize - 1);
426         if (referral_len == -1) {
427                 DEBUG(0,("is_msdfs_link_read_target: Error reading "
428                         "msdfs link %s: %s\n",
429                         path, strerror(errno)));
430                 goto err;
431         }
432         link_target[referral_len] = '\0';
433
434         DEBUG(5,("is_msdfs_link_internal: %s -> %s\n",path,
435                                 link_target));
436
437         if (!strnequal(link_target, "msdfs:", 6)) {
438                 goto err;
439         }
440         return True;
441
442   err:
443
444         if (link_target != link_target_buf) {
445                 TALLOC_FREE(link_target);
446         }
447         return False;
448 }
449
450 /**********************************************************************
451  Returns true if the unix path is a valid msdfs symlink.
452 **********************************************************************/
453
454 bool is_msdfs_link(connection_struct *conn,
455                 const char *path,
456                 SMB_STRUCT_STAT *sbufp)
457 {
458         return is_msdfs_link_internal(talloc_tos(),
459                                         conn,
460                                         path,
461                                         NULL,
462                                         sbufp);
463 }
464
465 /*****************************************************************
466  Used by other functions to decide if a dfs path is remote,
467  and to get the list of referred locations for that remote path.
468
469  search_flag: For findfirsts, dfs links themselves are not
470  redirected, but paths beyond the links are. For normal smb calls,
471  even dfs links need to be redirected.
472
473  consumedcntp: how much of the dfs path is being redirected. the client
474  should try the remaining path on the redirected server.
475
476  If this returns NT_STATUS_PATH_NOT_COVERED the contents of the msdfs
477  link redirect are in targetpath.
478 *****************************************************************/
479
480 static NTSTATUS dfs_path_lookup(TALLOC_CTX *ctx,
481                 connection_struct *conn,
482                 const char *dfspath, /* Incoming complete dfs path */
483                 const struct dfs_path *pdp, /* Parsed out
484                                                server+share+extrapath. */
485                 bool search_flag, /* Called from a findfirst ? */
486                 int *consumedcntp,
487                 char **pp_targetpath)
488 {
489         char *p = NULL;
490         char *q = NULL;
491         SMB_STRUCT_STAT sbuf;
492         NTSTATUS status;
493         char *localpath = NULL;
494         char *canon_dfspath = NULL; /* Canonicalized dfs path. (only '/'
495                                   components). */
496
497         DEBUG(10,("dfs_path_lookup: Conn path = %s reqpath = %s\n",
498                 conn->connectpath, pdp->reqpath));
499
500         /*
501          * Note the unix path conversion here we're doing we can
502          * throw away. We're looking for a symlink for a dfs
503          * resolution, if we don't find it we'll do another
504          * unix_convert later in the codepath.
505          * If we needed to remember what we'd resolved in
506          * dp->reqpath (as the original code did) we'd
507          * copy (localhost, dp->reqpath) on any code
508          * path below that returns True - but I don't
509          * think this is needed. JRA.
510          */
511
512         status = unix_convert(ctx, conn, pdp->reqpath, search_flag, &localpath,
513                         NULL, &sbuf);
514         if (!NT_STATUS_IS_OK(status) && !NT_STATUS_EQUAL(status,
515                                         NT_STATUS_OBJECT_PATH_NOT_FOUND)) {
516                 return status;
517         }
518
519         /* Optimization - check if we can redirect the whole path. */
520
521         if (is_msdfs_link_internal(ctx, conn, localpath, pp_targetpath, NULL)) {
522                 if (search_flag) {
523                         DEBUG(6,("dfs_path_lookup (FindFirst) No redirection "
524                                  "for dfs link %s.\n", dfspath));
525                         return NT_STATUS_OK;
526                 }
527
528                 DEBUG(6,("dfs_path_lookup: %s resolves to a "
529                         "valid dfs link %s.\n", dfspath,
530                         pp_targetpath ? *pp_targetpath : ""));
531
532                 if (consumedcntp) {
533                         *consumedcntp = strlen(dfspath);
534                 }
535                 return NT_STATUS_PATH_NOT_COVERED;
536         }
537
538         /* Prepare to test only for '/' components in the given path,
539          * so if a Windows path replace all '\\' characters with '/'.
540          * For a POSIX DFS path we know all separators are already '/'. */
541
542         canon_dfspath = talloc_strdup(ctx, dfspath);
543         if (!canon_dfspath) {
544                 return NT_STATUS_NO_MEMORY;
545         }
546         if (!pdp->posix_path) {
547                 string_replace(canon_dfspath, '\\', '/');
548         }
549
550         /*
551          * localpath comes out of unix_convert, so it has
552          * no trailing backslash. Make sure that canon_dfspath hasn't either.
553          * Fix for bug #4860 from Jan Martin <Jan.Martin@rwedea.com>.
554          */
555
556         trim_char(canon_dfspath,0,'/');
557
558         /*
559          * Redirect if any component in the path is a link.
560          * We do this by walking backwards through the
561          * local path, chopping off the last component
562          * in both the local path and the canonicalized
563          * DFS path. If we hit a DFS link then we're done.
564          */
565
566         p = strrchr_m(localpath, '/');
567         if (consumedcntp) {
568                 q = strrchr_m(canon_dfspath, '/');
569         }
570
571         while (p) {
572                 *p = '\0';
573                 if (q) {
574                         *q = '\0';
575                 }
576
577                 if (is_msdfs_link_internal(ctx, conn,
578                                         localpath, pp_targetpath, NULL)) {
579                         DEBUG(4, ("dfs_path_lookup: Redirecting %s because "
580                                 "parent %s is dfs link\n", dfspath, localpath));
581
582                         if (consumedcntp) {
583                                 *consumedcntp = strlen(canon_dfspath);
584                                 DEBUG(10, ("dfs_path_lookup: Path consumed: %s "
585                                         "(%d)\n",
586                                         canon_dfspath,
587                                         *consumedcntp));
588                         }
589
590                         return NT_STATUS_PATH_NOT_COVERED;
591                 }
592
593                 /* Step back on the filesystem. */
594                 p = strrchr_m(localpath, '/');
595
596                 if (consumedcntp) {
597                         /* And in the canonicalized dfs path. */
598                         q = strrchr_m(canon_dfspath, '/');
599                 }
600         }
601
602         return NT_STATUS_OK;
603 }
604
605 /*****************************************************************
606  Decides if a dfs pathname should be redirected or not.
607  If not, the pathname is converted to a tcon-relative local unix path
608
609  search_wcard_flag: this flag performs 2 functions both related
610  to searches.  See resolve_dfs_path() and parse_dfs_path_XX()
611  for details.
612
613  This function can return NT_STATUS_OK, meaning use the returned path as-is
614  (mapped into a local path).
615  or NT_STATUS_NOT_COVERED meaning return a DFS redirect, or
616  any other NT_STATUS error which is a genuine error to be
617  returned to the client.
618 *****************************************************************/
619
620 static NTSTATUS dfs_redirect(TALLOC_CTX *ctx,
621                         connection_struct *conn,
622                         const char *path_in,
623                         bool search_wcard_flag,
624                         char **pp_path_out,
625                         bool *ppath_contains_wcard)
626 {
627         NTSTATUS status;
628         struct dfs_path *pdp = TALLOC_P(ctx, struct dfs_path);
629
630         if (!pdp) {
631                 return NT_STATUS_NO_MEMORY;
632         }
633
634         status = parse_dfs_path(conn, path_in, search_wcard_flag, pdp,
635                         ppath_contains_wcard);
636         if (!NT_STATUS_IS_OK(status)) {
637                 TALLOC_FREE(pdp);
638                 return status;
639         }
640
641         if (pdp->reqpath[0] == '\0') {
642                 TALLOC_FREE(pdp);
643                 *pp_path_out = talloc_strdup(ctx, "");
644                 if (!*pp_path_out) {
645                         return NT_STATUS_NO_MEMORY;
646                 }
647                 DEBUG(5,("dfs_redirect: self-referral.\n"));
648                 return NT_STATUS_OK;
649         }
650
651         /* If dfs pathname for a non-dfs share, convert to tcon-relative
652            path and return OK */
653
654         if (!lp_msdfs_root(SNUM(conn))) {
655                 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
656                 TALLOC_FREE(pdp);
657                 if (!*pp_path_out) {
658                         return NT_STATUS_NO_MEMORY;
659                 }
660                 return NT_STATUS_OK;
661         }
662
663         /* If it looked like a local path (zero hostname/servicename)
664          * just treat as a tcon-relative path. */
665
666         if (pdp->hostname[0] == '\0' && pdp->servicename[0] == '\0') {
667                 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
668                 TALLOC_FREE(pdp);
669                 if (!*pp_path_out) {
670                         return NT_STATUS_NO_MEMORY;
671                 }
672                 return NT_STATUS_OK;
673         }
674
675         status = dfs_path_lookup(ctx, conn, path_in, pdp,
676                         search_wcard_flag, NULL, NULL);
677         if (!NT_STATUS_IS_OK(status)) {
678                 if (NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
679                         DEBUG(3,("dfs_redirect: Redirecting %s\n", path_in));
680                 } else {
681                         DEBUG(10,("dfs_redirect: dfs_path_lookup "
682                                 "failed for %s with %s\n",
683                                 path_in, nt_errstr(status) ));
684                 }
685                 return status;
686         }
687
688         DEBUG(3,("dfs_redirect: Not redirecting %s.\n", path_in));
689
690         /* Form non-dfs tcon-relative path */
691         *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
692         TALLOC_FREE(pdp);
693         if (!*pp_path_out) {
694                 return NT_STATUS_NO_MEMORY;
695         }
696
697         DEBUG(3,("dfs_redirect: Path %s converted to non-dfs path %s\n",
698                                 path_in,
699                                 *pp_path_out));
700
701         return NT_STATUS_OK;
702 }
703
704 /**********************************************************************
705  Return a self referral.
706 **********************************************************************/
707
708 static NTSTATUS self_ref(TALLOC_CTX *ctx,
709                         const char *dfs_path,
710                         struct junction_map *jucn,
711                         int *consumedcntp,
712                         bool *self_referralp)
713 {
714         struct referral *ref;
715
716         *self_referralp = True;
717
718         jucn->referral_count = 1;
719         if((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) {
720                 return NT_STATUS_NO_MEMORY;
721         }
722
723         ref->alternate_path = talloc_strdup(ctx, dfs_path);
724         if (!ref->alternate_path) {
725                 return NT_STATUS_NO_MEMORY;
726         }
727         ref->proximity = 0;
728         ref->ttl = REFERRAL_TTL;
729         jucn->referral_list = ref;
730         *consumedcntp = strlen(dfs_path);
731         return NT_STATUS_OK;
732 }
733
734 /**********************************************************************
735  Gets valid referrals for a dfs path and fills up the
736  junction_map structure.
737 **********************************************************************/
738
739 NTSTATUS get_referred_path(TALLOC_CTX *ctx,
740                         const char *dfs_path,
741                         struct junction_map *jucn,
742                         int *consumedcntp,
743                         bool *self_referralp)
744 {
745         struct connection_struct conns;
746         struct connection_struct *conn = &conns;
747         char *targetpath = NULL;
748         int snum;
749         NTSTATUS status = NT_STATUS_NOT_FOUND;
750         bool dummy;
751         struct dfs_path *pdp = TALLOC_P(ctx, struct dfs_path);
752
753         if (!pdp) {
754                 return NT_STATUS_NO_MEMORY;
755         }
756
757         ZERO_STRUCT(conns);
758         *self_referralp = False;
759
760         status = parse_dfs_path(NULL, dfs_path, False, pdp, &dummy);
761         if (!NT_STATUS_IS_OK(status)) {
762                 return status;
763         }
764
765         jucn->service_name = talloc_strdup(ctx, pdp->servicename);
766         jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
767         if (!jucn->service_name || !jucn->volume_name) {
768                 TALLOC_FREE(pdp);
769                 return NT_STATUS_NO_MEMORY;
770         }
771
772         /* Verify the share is a dfs root */
773         snum = lp_servicenumber(jucn->service_name);
774         if(snum < 0) {
775                 fstring service_name;
776                 fstrcpy(service_name, jucn->service_name);
777                 if ((snum = find_service(service_name)) < 0) {
778                         return NT_STATUS_NOT_FOUND;
779                 }
780                 TALLOC_FREE(jucn->service_name);
781                 jucn->service_name = talloc_strdup(ctx, service_name);
782                 if (!jucn->service_name) {
783                         TALLOC_FREE(pdp);
784                         return NT_STATUS_NO_MEMORY;
785                 }
786         }
787
788         if (!lp_msdfs_root(snum) && (*lp_msdfs_proxy(snum) == '\0')) {
789                 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not "
790                         "a dfs root.\n",
791                         pdp->servicename, dfs_path));
792                 TALLOC_FREE(pdp);
793                 return NT_STATUS_NOT_FOUND;
794         }
795
796         /*
797          * Self referrals are tested with a anonymous IPC connection and
798          * a GET_DFS_REFERRAL call to \\server\share. (which means
799          * dp.reqpath[0] points to an empty string). create_conn_struct cd's
800          * into the directory and will fail if it cannot (as the anonymous
801          * user). Cope with this.
802          */
803
804         if (pdp->reqpath[0] == '\0') {
805                 char *tmp;
806                 struct referral *ref;
807
808                 if (*lp_msdfs_proxy(snum) == '\0') {
809                         TALLOC_FREE(pdp);
810                         return self_ref(ctx,
811                                         dfs_path,
812                                         jucn,
813                                         consumedcntp,
814                                         self_referralp);
815                 }
816
817                 /*
818                  * It's an msdfs proxy share. Redirect to
819                  * the configured target share.
820                  */
821
822                 jucn->referral_count = 1;
823                 if ((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) {
824                         TALLOC_FREE(pdp);
825                         return NT_STATUS_NO_MEMORY;
826                 }
827
828                 if (!(tmp = talloc_strdup(ctx, lp_msdfs_proxy(snum)))) {
829                         TALLOC_FREE(pdp);
830                         return NT_STATUS_NO_MEMORY;
831                 }
832
833                 trim_string(tmp, "\\", 0);
834
835                 ref->alternate_path = talloc_asprintf(ctx, "\\%s", tmp);
836                 TALLOC_FREE(tmp);
837
838                 if (!ref->alternate_path) {
839                         TALLOC_FREE(pdp);
840                         return NT_STATUS_NO_MEMORY;
841                 }
842
843                 if (pdp->reqpath[0] != '\0') {
844                         ref->alternate_path = talloc_asprintf_append(
845                                         ref->alternate_path,
846                                         "%s",
847                                         pdp->reqpath);
848                         if (!ref->alternate_path) {
849                                 TALLOC_FREE(pdp);
850                                 return NT_STATUS_NO_MEMORY;
851                         }
852                 }
853                 ref->proximity = 0;
854                 ref->ttl = REFERRAL_TTL;
855                 jucn->referral_list = ref;
856                 *consumedcntp = strlen(dfs_path);
857                 TALLOC_FREE(pdp);
858                 return NT_STATUS_OK;
859         }
860
861         status = create_conn_struct(ctx, conn, snum, lp_pathname(snum));
862         if (!NT_STATUS_IS_OK(status)) {
863                 TALLOC_FREE(pdp);
864                 return status;
865         }
866
867         /* If this is a DFS path dfs_lookup should return
868          * NT_STATUS_PATH_NOT_COVERED. */
869
870         status = dfs_path_lookup(ctx, conn, dfs_path, pdp,
871                         False, consumedcntp, &targetpath);
872
873         if (!NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
874                 DEBUG(3,("get_referred_path: No valid referrals for path %s\n",
875                         dfs_path));
876                 conn_free_internal(conn);
877                 TALLOC_FREE(pdp);
878                 return status;
879         }
880
881         /* We know this is a valid dfs link. Parse the targetpath. */
882         if (!parse_msdfs_symlink(ctx, targetpath,
883                                 &jucn->referral_list,
884                                 &jucn->referral_count)) {
885                 DEBUG(3,("get_referred_path: failed to parse symlink "
886                         "target %s\n", targetpath ));
887                 conn_free_internal(conn);
888                 TALLOC_FREE(pdp);
889                 return NT_STATUS_NOT_FOUND;
890         }
891
892         conn_free_internal(conn);
893         TALLOC_FREE(pdp);
894         return NT_STATUS_OK;
895 }
896
897 static int setup_ver2_dfs_referral(const char *pathname,
898                                 char **ppdata,
899                                 struct junction_map *junction,
900                                 bool self_referral)
901 {
902         char* pdata = *ppdata;
903
904         smb_ucs2_t *uni_requestedpath = NULL;
905         int uni_reqpathoffset1,uni_reqpathoffset2;
906         int uni_curroffset;
907         int requestedpathlen=0;
908         int offset;
909         int reply_size = 0;
910         int i=0;
911
912         DEBUG(10,("Setting up version2 referral\nRequested path:\n"));
913
914         requestedpathlen = rpcstr_push_talloc(talloc_tos(),
915                                         &uni_requestedpath, pathname);
916         if (uni_requestedpath == NULL || requestedpathlen == 0) {
917                 return -1;
918         }
919
920         if (DEBUGLVL(10)) {
921                 dump_data(0, (unsigned char *)uni_requestedpath,
922                         requestedpathlen);
923         }
924
925         DEBUG(10,("ref count = %u\n",junction->referral_count));
926
927         uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
928                         VERSION2_REFERRAL_SIZE * junction->referral_count;
929
930         uni_reqpathoffset2 = uni_reqpathoffset1 + requestedpathlen;
931
932         uni_curroffset = uni_reqpathoffset2 + requestedpathlen;
933
934         reply_size = REFERRAL_HEADER_SIZE +
935                         VERSION2_REFERRAL_SIZE*junction->referral_count +
936                         2 * requestedpathlen;
937         DEBUG(10,("reply_size: %u\n",reply_size));
938
939         /* add up the unicode lengths of all the referral paths */
940         for(i=0;i<junction->referral_count;i++) {
941                 DEBUG(10,("referral %u : %s\n",
942                         i,
943                         junction->referral_list[i].alternate_path));
944                 reply_size +=
945                         (strlen(junction->referral_list[i].alternate_path)+1)*2;
946         }
947
948         DEBUG(10,("reply_size = %u\n",reply_size));
949         /* add the unexplained 0x16 bytes */
950         reply_size += 0x16;
951
952         pdata = (char *)SMB_REALLOC(pdata,reply_size);
953         if(pdata == NULL) {
954                 DEBUG(0,("Realloc failed!\n"));
955                 return -1;
956         }
957         *ppdata = pdata;
958
959         /* copy in the dfs requested paths.. required for offset calculations */
960         memcpy(pdata+uni_reqpathoffset1,uni_requestedpath,requestedpathlen);
961         memcpy(pdata+uni_reqpathoffset2,uni_requestedpath,requestedpathlen);
962
963         /* create the header */
964         SSVAL(pdata,0,requestedpathlen - 2); /* UCS2 of path consumed minus
965                                                 2 byte null */
966         /* number of referral in this pkt */
967         SSVAL(pdata,2,junction->referral_count);
968         if(self_referral) {
969                 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
970         } else {
971                 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
972         }
973
974         offset = 8;
975         /* add the referral elements */
976         for(i=0;i<junction->referral_count;i++) {
977                 struct referral* ref = &junction->referral_list[i];
978                 int unilen;
979
980                 SSVAL(pdata,offset,2); /* version 2 */
981                 SSVAL(pdata,offset+2,VERSION2_REFERRAL_SIZE);
982                 if(self_referral) {
983                         SSVAL(pdata,offset+4,1);
984                 } else {
985                         SSVAL(pdata,offset+4,0);
986                 }
987
988                 /* ref_flags :use path_consumed bytes? */
989                 SSVAL(pdata,offset+6,0);
990                 SIVAL(pdata,offset+8,ref->proximity);
991                 SIVAL(pdata,offset+12,ref->ttl);
992
993                 SSVAL(pdata,offset+16,uni_reqpathoffset1-offset);
994                 SSVAL(pdata,offset+18,uni_reqpathoffset2-offset);
995                 /* copy referred path into current offset */
996                 unilen = rpcstr_push(pdata+uni_curroffset,
997                                         ref->alternate_path,
998                                         reply_size - uni_curroffset,
999                                         STR_UNICODE);
1000
1001                 SSVAL(pdata,offset+20,uni_curroffset-offset);
1002
1003                 uni_curroffset += unilen;
1004                 offset += VERSION2_REFERRAL_SIZE;
1005         }
1006         /* add in the unexplained 22 (0x16) bytes at the end */
1007         memset(pdata+uni_curroffset,'\0',0x16);
1008         return reply_size;
1009 }
1010
1011 static int setup_ver3_dfs_referral(const char *pathname,
1012                                 char **ppdata,
1013                                 struct junction_map *junction,
1014                                 bool self_referral)
1015 {
1016         char *pdata = *ppdata;
1017
1018         smb_ucs2_t *uni_reqpath = NULL;
1019         int uni_reqpathoffset1, uni_reqpathoffset2;
1020         int uni_curroffset;
1021         int reply_size = 0;
1022
1023         int reqpathlen = 0;
1024         int offset,i=0;
1025
1026         DEBUG(10,("setting up version3 referral\n"));
1027
1028         reqpathlen = rpcstr_push_talloc(talloc_tos(), &uni_reqpath, pathname);
1029         if (uni_reqpath == NULL || reqpathlen == 0) {
1030                 return -1;
1031         }
1032
1033         if (DEBUGLVL(10)) {
1034                 dump_data(0, (unsigned char *)uni_reqpath,
1035                         reqpathlen);
1036         }
1037
1038         uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
1039                         VERSION3_REFERRAL_SIZE * junction->referral_count;
1040         uni_reqpathoffset2 = uni_reqpathoffset1 + reqpathlen;
1041         reply_size = uni_curroffset = uni_reqpathoffset2 + reqpathlen;
1042
1043         for(i=0;i<junction->referral_count;i++) {
1044                 DEBUG(10,("referral %u : %s\n",
1045                         i,
1046                         junction->referral_list[i].alternate_path));
1047                 reply_size +=
1048                         (strlen(junction->referral_list[i].alternate_path)+1)*2;
1049         }
1050
1051         pdata = (char *)SMB_REALLOC(pdata,reply_size);
1052         if(pdata == NULL) {
1053                 DEBUG(0,("version3 referral setup:"
1054                         "malloc failed for Realloc!\n"));
1055                 return -1;
1056         }
1057         *ppdata = pdata;
1058
1059         /* create the header */
1060         SSVAL(pdata,0,reqpathlen - 2); /* UCS2 of path consumed minus
1061                                           2 byte null */
1062         SSVAL(pdata,2,junction->referral_count); /* number of referral */
1063         if(self_referral) {
1064                 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
1065         } else {
1066                 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
1067         }
1068
1069         /* copy in the reqpaths */
1070         memcpy(pdata+uni_reqpathoffset1,uni_reqpath,reqpathlen);
1071         memcpy(pdata+uni_reqpathoffset2,uni_reqpath,reqpathlen);
1072
1073         offset = 8;
1074         for(i=0;i<junction->referral_count;i++) {
1075                 struct referral* ref = &(junction->referral_list[i]);
1076                 int unilen;
1077
1078                 SSVAL(pdata,offset,3); /* version 3 */
1079                 SSVAL(pdata,offset+2,VERSION3_REFERRAL_SIZE);
1080                 if(self_referral) {
1081                         SSVAL(pdata,offset+4,1);
1082                 } else {
1083                         SSVAL(pdata,offset+4,0);
1084                 }
1085
1086                 /* ref_flags :use path_consumed bytes? */
1087                 SSVAL(pdata,offset+6,0);
1088                 SIVAL(pdata,offset+8,ref->ttl);
1089
1090                 SSVAL(pdata,offset+12,uni_reqpathoffset1-offset);
1091                 SSVAL(pdata,offset+14,uni_reqpathoffset2-offset);
1092                 /* copy referred path into current offset */
1093                 unilen = rpcstr_push(pdata+uni_curroffset,ref->alternate_path,
1094                                         reply_size - uni_curroffset,
1095                                         STR_UNICODE | STR_TERMINATE);
1096                 SSVAL(pdata,offset+16,uni_curroffset-offset);
1097                 /* copy 0x10 bytes of 00's in the ServiceSite GUID */
1098                 memset(pdata+offset+18,'\0',16);
1099
1100                 uni_curroffset += unilen;
1101                 offset += VERSION3_REFERRAL_SIZE;
1102         }
1103         return reply_size;
1104 }
1105
1106 /******************************************************************
1107  Set up the DFS referral for the dfs pathname. This call returns
1108  the amount of the path covered by this server, and where the
1109  client should be redirected to. This is the meat of the
1110  TRANS2_GET_DFS_REFERRAL call.
1111 ******************************************************************/
1112
1113 int setup_dfs_referral(connection_struct *orig_conn,
1114                         const char *dfs_path,
1115                         int max_referral_level,
1116                         char **ppdata, NTSTATUS *pstatus)
1117 {
1118         struct junction_map *junction = NULL;
1119         int consumedcnt = 0;
1120         bool self_referral = False;
1121         int reply_size = 0;
1122         char *pathnamep = NULL;
1123         char *local_dfs_path = NULL;
1124         TALLOC_CTX *ctx;
1125
1126         if (!(ctx=talloc_init("setup_dfs_referral"))) {
1127                 *pstatus = NT_STATUS_NO_MEMORY;
1128                 return -1;
1129         }
1130
1131         /* get the junction entry */
1132         if (!dfs_path) {
1133                 talloc_destroy(ctx);
1134                 *pstatus = NT_STATUS_NOT_FOUND;
1135                 return -1;
1136         }
1137
1138         /*
1139          * Trim pathname sent by client so it begins with only one backslash.
1140          * Two backslashes confuse some dfs clients
1141          */
1142
1143         local_dfs_path = talloc_strdup(ctx,dfs_path);
1144         if (!local_dfs_path) {
1145                 *pstatus = NT_STATUS_NO_MEMORY;
1146                 talloc_destroy(ctx);
1147                 return -1;
1148         }
1149         pathnamep = local_dfs_path;
1150         while (IS_DIRECTORY_SEP(pathnamep[0]) &&
1151                         IS_DIRECTORY_SEP(pathnamep[1])) {
1152                 pathnamep++;
1153         }
1154
1155         junction = TALLOC_ZERO_P(ctx, struct junction_map);
1156         if (!junction) {
1157                 *pstatus = NT_STATUS_NO_MEMORY;
1158                 talloc_destroy(ctx);
1159                 return -1;
1160         }
1161
1162         /* The following call can change cwd. */
1163         *pstatus = get_referred_path(ctx, pathnamep, junction,
1164                         &consumedcnt, &self_referral);
1165         if (!NT_STATUS_IS_OK(*pstatus)) {
1166                 vfs_ChDir(orig_conn,orig_conn->connectpath);
1167                 talloc_destroy(ctx);
1168                 return -1;
1169         }
1170         vfs_ChDir(orig_conn,orig_conn->connectpath);
1171
1172         if (!self_referral) {
1173                 pathnamep[consumedcnt] = '\0';
1174
1175                 if( DEBUGLVL( 3 ) ) {
1176                         int i=0;
1177                         dbgtext("setup_dfs_referral: Path %s to "
1178                                 "alternate path(s):",
1179                                 pathnamep);
1180                         for(i=0;i<junction->referral_count;i++)
1181                                 dbgtext(" %s",
1182                                 junction->referral_list[i].alternate_path);
1183                         dbgtext(".\n");
1184                 }
1185         }
1186
1187         /* create the referral depeding on version */
1188         DEBUG(10,("max_referral_level :%d\n",max_referral_level));
1189
1190         if (max_referral_level < 2) {
1191                 max_referral_level = 2;
1192         }
1193         if (max_referral_level > 3) {
1194                 max_referral_level = 3;
1195         }
1196
1197         switch(max_referral_level) {
1198         case 2:
1199                 reply_size = setup_ver2_dfs_referral(pathnamep,
1200                                         ppdata, junction,
1201                                         self_referral);
1202                 break;
1203         case 3:
1204                 reply_size = setup_ver3_dfs_referral(pathnamep, ppdata,
1205                                         junction, self_referral);
1206                 break;
1207         default:
1208                 DEBUG(0,("setup_dfs_referral: Invalid dfs referral "
1209                         "version: %d\n",
1210                         max_referral_level));
1211                 talloc_destroy(ctx);
1212                 *pstatus = NT_STATUS_INVALID_LEVEL;
1213                 return -1;
1214         }
1215
1216         if (DEBUGLVL(10)) {
1217                 DEBUGADD(0,("DFS Referral pdata:\n"));
1218                 dump_data(0,(uint8 *)*ppdata,reply_size);
1219         }
1220
1221         talloc_destroy(ctx);
1222         *pstatus = NT_STATUS_OK;
1223         return reply_size;
1224 }
1225
1226 /**********************************************************************
1227  The following functions are called by the NETDFS RPC pipe functions
1228  **********************************************************************/
1229
1230 /*********************************************************************
1231  Creates a junction structure from a DFS pathname
1232 **********************************************************************/
1233
1234 bool create_junction(TALLOC_CTX *ctx,
1235                 const char *dfs_path,
1236                 struct junction_map *jucn)
1237 {
1238         int snum;
1239         bool dummy;
1240         struct dfs_path *pdp = TALLOC_P(ctx,struct dfs_path);
1241         NTSTATUS status;
1242
1243         if (!pdp) {
1244                 return False;
1245         }
1246         status = parse_dfs_path(NULL, dfs_path, False, pdp, &dummy);
1247         if (!NT_STATUS_IS_OK(status)) {
1248                 return False;
1249         }
1250
1251         /* check if path is dfs : validate first token */
1252         if (!is_myname_or_ipaddr(pdp->hostname)) {
1253                 DEBUG(4,("create_junction: Invalid hostname %s "
1254                         "in dfs path %s\n",
1255                         pdp->hostname, dfs_path));
1256                 TALLOC_FREE(pdp);
1257                 return False;
1258         }
1259
1260         /* Check for a non-DFS share */
1261         snum = lp_servicenumber(pdp->servicename);
1262
1263         if(snum < 0 || !lp_msdfs_root(snum)) {
1264                 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1265                         pdp->servicename));
1266                 TALLOC_FREE(pdp);
1267                 return False;
1268         }
1269
1270         jucn->service_name = talloc_strdup(ctx, pdp->servicename);
1271         jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
1272         jucn->comment = talloc_strdup(ctx, lp_comment(snum));
1273
1274         TALLOC_FREE(pdp);
1275         if (!jucn->service_name || !jucn->volume_name || ! jucn->comment) {
1276                 return False;
1277         }
1278         return True;
1279 }
1280
1281 /**********************************************************************
1282  Forms a valid Unix pathname from the junction
1283  **********************************************************************/
1284
1285 static bool junction_to_local_path(const struct junction_map *jucn,
1286                                 char **pp_path_out,
1287                                 connection_struct *conn_out)
1288 {
1289         int snum;
1290
1291         snum = lp_servicenumber(jucn->service_name);
1292         if(snum < 0) {
1293                 return False;
1294         }
1295         if (!NT_STATUS_IS_OK(create_conn_struct(talloc_tos(),
1296                                         conn_out, snum,
1297                                         lp_pathname(snum)))) {
1298                 return False;
1299         }
1300
1301         *pp_path_out = talloc_asprintf(conn_out->mem_ctx,
1302                         "%s/%s",
1303                         lp_pathname(snum),
1304                         jucn->volume_name);
1305         if (!*pp_path_out) {
1306                 return False;
1307         }
1308         return True;
1309 }
1310
1311 bool create_msdfs_link(const struct junction_map *jucn)
1312 {
1313         char *path = NULL;
1314         char *msdfs_link = NULL;
1315         connection_struct conns;
1316         connection_struct *conn = &conns;
1317         int i=0;
1318         bool insert_comma = False;
1319         bool ret = False;
1320
1321         ZERO_STRUCT(conns);
1322
1323         if(!junction_to_local_path(jucn, &path, conn)) {
1324                 return False;
1325         }
1326
1327         /* Form the msdfs_link contents */
1328         msdfs_link = talloc_strdup(conn->mem_ctx, "msdfs:");
1329         if (!msdfs_link) {
1330                 goto out;
1331         }
1332         for(i=0; i<jucn->referral_count; i++) {
1333                 char *refpath = jucn->referral_list[i].alternate_path;
1334
1335                 /* Alternate paths always use Windows separators. */
1336                 trim_char(refpath, '\\', '\\');
1337                 if(*refpath == '\0') {
1338                         if (i == 0) {
1339                                 insert_comma = False;
1340                         }
1341                         continue;
1342                 }
1343                 if (i > 0 && insert_comma) {
1344                         msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1345                                         ",%s",
1346                                         refpath);
1347                 } else {
1348                         msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1349                                         "%s",
1350                                         refpath);
1351                 }
1352
1353                 if (!msdfs_link) {
1354                         goto out;
1355                 }
1356                 if (!insert_comma) {
1357                         insert_comma = True;
1358                 }
1359         }
1360
1361         DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
1362                 path, msdfs_link));
1363
1364         if(SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
1365                 if (errno == EEXIST) {
1366                         if(SMB_VFS_UNLINK(conn,path)!=0) {
1367                                 goto out;
1368                         }
1369                 }
1370                 if (SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
1371                         DEBUG(1,("create_msdfs_link: symlink failed "
1372                                  "%s -> %s\nError: %s\n",
1373                                  path, msdfs_link, strerror(errno)));
1374                         goto out;
1375                 }
1376         }
1377
1378         ret = True;
1379
1380 out:
1381
1382         conn_free_internal(conn);
1383         return ret;
1384 }
1385
1386 bool remove_msdfs_link(const struct junction_map *jucn)
1387 {
1388         char *path = NULL;
1389         connection_struct conns;
1390         connection_struct *conn = &conns;
1391         bool ret = False;
1392
1393         ZERO_STRUCT(conns);
1394
1395         if( junction_to_local_path(jucn, &path, conn) ) {
1396                 if( SMB_VFS_UNLINK(conn, path) == 0 ) {
1397                         ret = True;
1398                 }
1399         }
1400
1401         conn_free_internal(conn);
1402         return ret;
1403 }
1404
1405 /*********************************************************************
1406  Return the number of DFS links at the root of this share.
1407 *********************************************************************/
1408
1409 static int count_dfs_links(TALLOC_CTX *ctx, int snum)
1410 {
1411         size_t cnt = 0;
1412         SMB_STRUCT_DIR *dirp = NULL;
1413         char *dname = NULL;
1414         const char *connect_path = lp_pathname(snum);
1415         const char *msdfs_proxy = lp_msdfs_proxy(snum);
1416         connection_struct conn;
1417
1418         ZERO_STRUCT(conn);
1419
1420         if(*connect_path == '\0') {
1421                 return 0;
1422         }
1423
1424         /*
1425          * Fake up a connection struct for the VFS layer.
1426          */
1427
1428         if (!NT_STATUS_IS_OK(create_conn_struct(talloc_tos(),
1429                                         &conn, snum, connect_path))) {
1430                 return 0;
1431         }
1432
1433         /* Count a link for the msdfs root - convention */
1434         cnt = 1;
1435
1436         /* No more links if this is an msdfs proxy. */
1437         if (*msdfs_proxy != '\0') {
1438                 goto out;
1439         }
1440
1441         /* Now enumerate all dfs links */
1442         dirp = SMB_VFS_OPENDIR(&conn, ".", NULL, 0);
1443         if(!dirp) {
1444                 goto out;
1445         }
1446
1447         while ((dname = vfs_readdirname(&conn, dirp)) != NULL) {
1448                 if (is_msdfs_link(&conn,
1449                                 dname,
1450                                 NULL)) {
1451                         cnt++;
1452                 }
1453         }
1454
1455         SMB_VFS_CLOSEDIR(&conn,dirp);
1456
1457 out:
1458
1459         conn_free_internal(&conn);
1460         return cnt;
1461 }
1462
1463 /*********************************************************************
1464 *********************************************************************/
1465
1466 static int form_junctions(TALLOC_CTX *ctx,
1467                                 int snum,
1468                                 struct junction_map *jucn,
1469                                 size_t jn_remain)
1470 {
1471         size_t cnt = 0;
1472         SMB_STRUCT_DIR *dirp = NULL;
1473         char *dname = NULL;
1474         const char *connect_path = lp_pathname(snum);
1475         char *service_name = lp_servicename(snum);
1476         const char *msdfs_proxy = lp_msdfs_proxy(snum);
1477         connection_struct conn;
1478         struct referral *ref = NULL;
1479
1480         ZERO_STRUCT(conn);
1481
1482         if (jn_remain == 0) {
1483                 return 0;
1484         }
1485
1486         if(*connect_path == '\0') {
1487                 return 0;
1488         }
1489
1490         /*
1491          * Fake up a connection struct for the VFS layer.
1492          */
1493
1494         if (!NT_STATUS_IS_OK(create_conn_struct(ctx, &conn, snum, connect_path))) {
1495                 return 0;
1496         }
1497
1498         /* form a junction for the msdfs root - convention
1499            DO NOT REMOVE THIS: NT clients will not work with us
1500            if this is not present
1501         */
1502         jucn[cnt].service_name = talloc_strdup(ctx,service_name);
1503         jucn[cnt].volume_name = talloc_strdup(ctx, "");
1504         if (!jucn[cnt].service_name || !jucn[cnt].volume_name) {
1505                 goto out;
1506         }
1507         jucn[cnt].comment = "";
1508         jucn[cnt].referral_count = 1;
1509
1510         ref = jucn[cnt].referral_list = TALLOC_ZERO_P(ctx, struct referral);
1511         if (jucn[cnt].referral_list == NULL) {
1512                 goto out;
1513         }
1514
1515         ref->proximity = 0;
1516         ref->ttl = REFERRAL_TTL;
1517         if (*msdfs_proxy != '\0') {
1518                 ref->alternate_path = talloc_strdup(ctx,
1519                                                 msdfs_proxy);
1520         } else {
1521                 ref->alternate_path = talloc_asprintf(ctx,
1522                         "\\\\%s\\%s",
1523                         get_local_machine_name(),
1524                         service_name);
1525         }
1526
1527         if (!ref->alternate_path) {
1528                 goto out;
1529         }
1530         cnt++;
1531
1532         /* Don't enumerate if we're an msdfs proxy. */
1533         if (*msdfs_proxy != '\0') {
1534                 goto out;
1535         }
1536
1537         /* Now enumerate all dfs links */
1538         dirp = SMB_VFS_OPENDIR(&conn, ".", NULL, 0);
1539         if(!dirp) {
1540                 goto out;
1541         }
1542
1543         while ((dname = vfs_readdirname(&conn, dirp)) != NULL) {
1544                 char *link_target = NULL;
1545                 if (cnt >= jn_remain) {
1546                         DEBUG(2, ("form_junctions: ran out of MSDFS "
1547                                 "junction slots"));
1548                         goto out;
1549                 }
1550                 if (is_msdfs_link_internal(ctx,
1551                                         &conn,
1552                                         dname, &link_target,
1553                                         NULL)) {
1554                         if (parse_msdfs_symlink(ctx,
1555                                         link_target,
1556                                         &jucn[cnt].referral_list,
1557                                         &jucn[cnt].referral_count)) {
1558
1559                                 jucn[cnt].service_name = talloc_strdup(ctx,
1560                                                                 service_name);
1561                                 jucn[cnt].volume_name = talloc_strdup(ctx,
1562                                                                 dname);
1563                                 if (!jucn[cnt].service_name ||
1564                                                 !jucn[cnt].volume_name) {
1565                                         goto out;
1566                                 }
1567                                 jucn[cnt].comment = "";
1568                                 cnt++;
1569                         }
1570                         TALLOC_FREE(link_target);
1571                 }
1572         }
1573
1574 out:
1575
1576         if (dirp) {
1577                 SMB_VFS_CLOSEDIR(&conn,dirp);
1578         }
1579
1580         conn_free_internal(&conn);
1581         return cnt;
1582 }
1583
1584 struct junction_map *enum_msdfs_links(TALLOC_CTX *ctx, size_t *p_num_jn)
1585 {
1586         struct junction_map *jn = NULL;
1587         int i=0;
1588         size_t jn_count = 0;
1589         int sharecount = 0;
1590
1591         *p_num_jn = 0;
1592         if(!lp_host_msdfs()) {
1593                 return NULL;
1594         }
1595
1596         /* Ensure all the usershares are loaded. */
1597         become_root();
1598         load_registry_shares();
1599         sharecount = load_usershare_shares();
1600         unbecome_root();
1601
1602         for(i=0;i < sharecount;i++) {
1603                 if(lp_msdfs_root(i)) {
1604                         jn_count += count_dfs_links(ctx, i);
1605                 }
1606         }
1607         if (jn_count == 0) {
1608                 return NULL;
1609         }
1610         jn = TALLOC_ARRAY(ctx,  struct junction_map, jn_count);
1611         if (!jn) {
1612                 return NULL;
1613         }
1614         for(i=0; i < sharecount; i++) {
1615                 if (*p_num_jn >= jn_count) {
1616                         break;
1617                 }
1618                 if(lp_msdfs_root(i)) {
1619                         *p_num_jn += form_junctions(ctx, i,
1620                                         &jn[*p_num_jn],
1621                                         jn_count - *p_num_jn);
1622                 }
1623         }
1624         return jn;
1625 }
1626
1627 /******************************************************************************
1628  Core function to resolve a dfs pathname.
1629 ******************************************************************************/
1630
1631 NTSTATUS resolve_dfspath(TALLOC_CTX *ctx,
1632                         connection_struct *conn,
1633                         bool dfs_pathnames,
1634                         const char *name_in,
1635                         char **pp_name_out)
1636 {
1637         NTSTATUS status = NT_STATUS_OK;
1638         bool dummy;
1639         if (dfs_pathnames) {
1640                 status = dfs_redirect(ctx,
1641                                         conn,
1642                                         name_in,
1643                                         False,
1644                                         pp_name_out,
1645                                         &dummy);
1646         } else {
1647                 /*
1648                  * Cheat and just return a copy of the in ptr.
1649                  * Once srvstr_get_path() uses talloc it'll
1650                  * be a talloced ptr anyway.
1651                  */
1652                 *pp_name_out = CONST_DISCARD(char *,name_in);
1653         }
1654         return status;
1655 }
1656
1657 /******************************************************************************
1658  Core function to resolve a dfs pathname possibly containing a wildcard.
1659  This function is identical to the above except for the bool param to
1660  dfs_redirect but I need this to be separate so it's really clear when
1661  we're allowing wildcards and when we're not. JRA.
1662 ******************************************************************************/
1663
1664 NTSTATUS resolve_dfspath_wcard(TALLOC_CTX *ctx,
1665                                 connection_struct *conn,
1666                                 bool dfs_pathnames,
1667                                 const char *name_in,
1668                                 char **pp_name_out,
1669                                 bool *ppath_contains_wcard)
1670 {
1671         NTSTATUS status = NT_STATUS_OK;
1672         if (dfs_pathnames) {
1673                 status = dfs_redirect(ctx,
1674                                         conn,
1675                                         name_in,
1676                                         True,
1677                                         pp_name_out,
1678                                         ppath_contains_wcard);
1679         } else {
1680                 /*
1681                  * Cheat and just return a copy of the in ptr.
1682                  * Once srvstr_get_path() uses talloc it'll
1683                  * be a talloced ptr anyway.
1684                  */
1685                 *pp_name_out = CONST_DISCARD(char *,name_in);
1686         }
1687         return status;
1688 }