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