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