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