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