Fix a double-closedir() in form_junctions()
[ira/wip.git] / source3 / smbd / msdfs.c
1 /*
2    Unix SMB/Netbios implementation.
3    Version 3.0
4    MSDFS services for Samba
5    Copyright (C) Shirish Kalele 2000
6    Copyright (C) Jeremy Allison 2007
7
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 3 of the License, or
11    (at your option) any later version.
12
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program.  If not, see <http://www.gnu.org/licenses/>.
20
21 */
22
23 #define DBGC_CLASS DBGC_MSDFS
24 #include "includes.h"
25
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         if (!( strequal(pdp->servicename, lp_servicename(SNUM(conn)))
676                         || (strequal(pdp->servicename, HOMES_NAME)
677                         && strequal(lp_servicename(SNUM(conn)),
678                                 conn->server_info->sanitized_username) )) ) {
679
680                 /* The given sharename doesn't match this connection. */
681                 TALLOC_FREE(pdp);
682
683                 return NT_STATUS_OBJECT_PATH_NOT_FOUND;
684         }
685
686         status = dfs_path_lookup(ctx, conn, path_in, pdp,
687                         search_wcard_flag, NULL, NULL);
688         if (!NT_STATUS_IS_OK(status)) {
689                 if (NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
690                         DEBUG(3,("dfs_redirect: Redirecting %s\n", path_in));
691                 } else {
692                         DEBUG(10,("dfs_redirect: dfs_path_lookup "
693                                 "failed for %s with %s\n",
694                                 path_in, nt_errstr(status) ));
695                 }
696                 return status;
697         }
698
699         DEBUG(3,("dfs_redirect: Not redirecting %s.\n", path_in));
700
701         /* Form non-dfs tcon-relative path */
702         *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
703         TALLOC_FREE(pdp);
704         if (!*pp_path_out) {
705                 return NT_STATUS_NO_MEMORY;
706         }
707
708         DEBUG(3,("dfs_redirect: Path %s converted to non-dfs path %s\n",
709                                 path_in,
710                                 *pp_path_out));
711
712         return NT_STATUS_OK;
713 }
714
715 /**********************************************************************
716  Return a self referral.
717 **********************************************************************/
718
719 static NTSTATUS self_ref(TALLOC_CTX *ctx,
720                         const char *dfs_path,
721                         struct junction_map *jucn,
722                         int *consumedcntp,
723                         bool *self_referralp)
724 {
725         struct referral *ref;
726
727         *self_referralp = True;
728
729         jucn->referral_count = 1;
730         if((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) {
731                 return NT_STATUS_NO_MEMORY;
732         }
733
734         ref->alternate_path = talloc_strdup(ctx, dfs_path);
735         if (!ref->alternate_path) {
736                 return NT_STATUS_NO_MEMORY;
737         }
738         ref->proximity = 0;
739         ref->ttl = REFERRAL_TTL;
740         jucn->referral_list = ref;
741         *consumedcntp = strlen(dfs_path);
742         return NT_STATUS_OK;
743 }
744
745 /**********************************************************************
746  Gets valid referrals for a dfs path and fills up the
747  junction_map structure.
748 **********************************************************************/
749
750 NTSTATUS get_referred_path(TALLOC_CTX *ctx,
751                         const char *dfs_path,
752                         struct junction_map *jucn,
753                         int *consumedcntp,
754                         bool *self_referralp)
755 {
756         struct connection_struct *conn;
757         char *targetpath = NULL;
758         int snum;
759         NTSTATUS status = NT_STATUS_NOT_FOUND;
760         bool dummy;
761         struct dfs_path *pdp = TALLOC_P(ctx, struct dfs_path);
762
763         if (!pdp) {
764                 return NT_STATUS_NO_MEMORY;
765         }
766
767         *self_referralp = False;
768
769         status = parse_dfs_path(NULL, dfs_path, False, pdp, &dummy);
770         if (!NT_STATUS_IS_OK(status)) {
771                 return status;
772         }
773
774         jucn->service_name = talloc_strdup(ctx, pdp->servicename);
775         jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
776         if (!jucn->service_name || !jucn->volume_name) {
777                 TALLOC_FREE(pdp);
778                 return NT_STATUS_NO_MEMORY;
779         }
780
781         /* Verify the share is a dfs root */
782         snum = lp_servicenumber(jucn->service_name);
783         if(snum < 0) {
784                 fstring service_name;
785                 fstrcpy(service_name, jucn->service_name);
786                 if ((snum = find_service(service_name)) < 0) {
787                         return NT_STATUS_NOT_FOUND;
788                 }
789                 TALLOC_FREE(jucn->service_name);
790                 jucn->service_name = talloc_strdup(ctx, service_name);
791                 if (!jucn->service_name) {
792                         TALLOC_FREE(pdp);
793                         return NT_STATUS_NO_MEMORY;
794                 }
795         }
796
797         if (!lp_msdfs_root(snum) && (*lp_msdfs_proxy(snum) == '\0')) {
798                 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not "
799                         "a dfs root.\n",
800                         pdp->servicename, dfs_path));
801                 TALLOC_FREE(pdp);
802                 return NT_STATUS_NOT_FOUND;
803         }
804
805         /*
806          * Self referrals are tested with a anonymous IPC connection and
807          * a GET_DFS_REFERRAL call to \\server\share. (which means
808          * dp.reqpath[0] points to an empty string). create_conn_struct cd's
809          * into the directory and will fail if it cannot (as the anonymous
810          * user). Cope with this.
811          */
812
813         if (pdp->reqpath[0] == '\0') {
814                 char *tmp;
815                 struct referral *ref;
816
817                 if (*lp_msdfs_proxy(snum) == '\0') {
818                         TALLOC_FREE(pdp);
819                         return self_ref(ctx,
820                                         dfs_path,
821                                         jucn,
822                                         consumedcntp,
823                                         self_referralp);
824                 }
825
826                 /*
827                  * It's an msdfs proxy share. Redirect to
828                  * the configured target share.
829                  */
830
831                 jucn->referral_count = 1;
832                 if ((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) {
833                         TALLOC_FREE(pdp);
834                         return NT_STATUS_NO_MEMORY;
835                 }
836
837                 if (!(tmp = talloc_strdup(ctx, lp_msdfs_proxy(snum)))) {
838                         TALLOC_FREE(pdp);
839                         return NT_STATUS_NO_MEMORY;
840                 }
841
842                 trim_string(tmp, "\\", 0);
843
844                 ref->alternate_path = talloc_asprintf(ctx, "\\%s", tmp);
845                 TALLOC_FREE(tmp);
846
847                 if (!ref->alternate_path) {
848                         TALLOC_FREE(pdp);
849                         return NT_STATUS_NO_MEMORY;
850                 }
851
852                 if (pdp->reqpath[0] != '\0') {
853                         ref->alternate_path = talloc_asprintf_append(
854                                         ref->alternate_path,
855                                         "%s",
856                                         pdp->reqpath);
857                         if (!ref->alternate_path) {
858                                 TALLOC_FREE(pdp);
859                                 return NT_STATUS_NO_MEMORY;
860                         }
861                 }
862                 ref->proximity = 0;
863                 ref->ttl = REFERRAL_TTL;
864                 jucn->referral_list = ref;
865                 *consumedcntp = strlen(dfs_path);
866                 TALLOC_FREE(pdp);
867                 return NT_STATUS_OK;
868         }
869
870         status = create_conn_struct(ctx, &conn, snum, lp_pathname(snum));
871         if (!NT_STATUS_IS_OK(status)) {
872                 TALLOC_FREE(pdp);
873                 return status;
874         }
875
876         /* If this is a DFS path dfs_lookup should return
877          * NT_STATUS_PATH_NOT_COVERED. */
878
879         status = dfs_path_lookup(ctx, conn, dfs_path, pdp,
880                         False, consumedcntp, &targetpath);
881
882         if (!NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
883                 DEBUG(3,("get_referred_path: No valid referrals for path %s\n",
884                         dfs_path));
885                 conn_free_internal(conn);
886                 TALLOC_FREE(pdp);
887                 return status;
888         }
889
890         /* We know this is a valid dfs link. Parse the targetpath. */
891         if (!parse_msdfs_symlink(ctx, targetpath,
892                                 &jucn->referral_list,
893                                 &jucn->referral_count)) {
894                 DEBUG(3,("get_referred_path: failed to parse symlink "
895                         "target %s\n", targetpath ));
896                 conn_free_internal(conn);
897                 TALLOC_FREE(pdp);
898                 return NT_STATUS_NOT_FOUND;
899         }
900
901         conn_free_internal(conn);
902         TALLOC_FREE(pdp);
903         return NT_STATUS_OK;
904 }
905
906 static int setup_ver2_dfs_referral(const char *pathname,
907                                 char **ppdata,
908                                 struct junction_map *junction,
909                                 int consumedcnt,
910                                 bool self_referral)
911 {
912         char* pdata = *ppdata;
913
914         smb_ucs2_t *uni_requestedpath = NULL;
915         int uni_reqpathoffset1,uni_reqpathoffset2;
916         int uni_curroffset;
917         int requestedpathlen=0;
918         int offset;
919         int reply_size = 0;
920         int i=0;
921
922         DEBUG(10,("Setting up version2 referral\nRequested path:\n"));
923
924         requestedpathlen = rpcstr_push_talloc(talloc_tos(),
925                                         &uni_requestedpath, pathname);
926         if (uni_requestedpath == NULL || requestedpathlen == 0) {
927                 return -1;
928         }
929
930         if (DEBUGLVL(10)) {
931                 dump_data(0, (unsigned char *)uni_requestedpath,
932                         requestedpathlen);
933         }
934
935         DEBUG(10,("ref count = %u\n",junction->referral_count));
936
937         uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
938                         VERSION2_REFERRAL_SIZE * junction->referral_count;
939
940         uni_reqpathoffset2 = uni_reqpathoffset1 + requestedpathlen;
941
942         uni_curroffset = uni_reqpathoffset2 + requestedpathlen;
943
944         reply_size = REFERRAL_HEADER_SIZE +
945                         VERSION2_REFERRAL_SIZE*junction->referral_count +
946                         2 * requestedpathlen;
947         DEBUG(10,("reply_size: %u\n",reply_size));
948
949         /* add up the unicode lengths of all the referral paths */
950         for(i=0;i<junction->referral_count;i++) {
951                 DEBUG(10,("referral %u : %s\n",
952                         i,
953                         junction->referral_list[i].alternate_path));
954                 reply_size +=
955                         (strlen(junction->referral_list[i].alternate_path)+1)*2;
956         }
957
958         DEBUG(10,("reply_size = %u\n",reply_size));
959         /* add the unexplained 0x16 bytes */
960         reply_size += 0x16;
961
962         pdata = (char *)SMB_REALLOC(pdata,reply_size);
963         if(pdata == NULL) {
964                 DEBUG(0,("Realloc failed!\n"));
965                 return -1;
966         }
967         *ppdata = pdata;
968
969         /* copy in the dfs requested paths.. required for offset calculations */
970         memcpy(pdata+uni_reqpathoffset1,uni_requestedpath,requestedpathlen);
971         memcpy(pdata+uni_reqpathoffset2,uni_requestedpath,requestedpathlen);
972
973         /* create the header */
974         SSVAL(pdata,0,consumedcnt * 2); /* path consumed */
975         /* number of referral in this pkt */
976         SSVAL(pdata,2,junction->referral_count);
977         if(self_referral) {
978                 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
979         } else {
980                 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
981         }
982
983         offset = 8;
984         /* add the referral elements */
985         for(i=0;i<junction->referral_count;i++) {
986                 struct referral* ref = &junction->referral_list[i];
987                 int unilen;
988
989                 SSVAL(pdata,offset,2); /* version 2 */
990                 SSVAL(pdata,offset+2,VERSION2_REFERRAL_SIZE);
991                 if(self_referral) {
992                         SSVAL(pdata,offset+4,1);
993                 } else {
994                         SSVAL(pdata,offset+4,0);
995                 }
996
997                 /* ref_flags :use path_consumed bytes? */
998                 SSVAL(pdata,offset+6,0);
999                 SIVAL(pdata,offset+8,ref->proximity);
1000                 SIVAL(pdata,offset+12,ref->ttl);
1001
1002                 SSVAL(pdata,offset+16,uni_reqpathoffset1-offset);
1003                 SSVAL(pdata,offset+18,uni_reqpathoffset2-offset);
1004                 /* copy referred path into current offset */
1005                 unilen = rpcstr_push(pdata+uni_curroffset,
1006                                         ref->alternate_path,
1007                                         reply_size - uni_curroffset,
1008                                         STR_UNICODE);
1009
1010                 SSVAL(pdata,offset+20,uni_curroffset-offset);
1011
1012                 uni_curroffset += unilen;
1013                 offset += VERSION2_REFERRAL_SIZE;
1014         }
1015         /* add in the unexplained 22 (0x16) bytes at the end */
1016         memset(pdata+uni_curroffset,'\0',0x16);
1017         return reply_size;
1018 }
1019
1020 static int setup_ver3_dfs_referral(const char *pathname,
1021                                 char **ppdata,
1022                                 struct junction_map *junction,
1023                                 int consumedcnt,
1024                                 bool self_referral)
1025 {
1026         char *pdata = *ppdata;
1027
1028         smb_ucs2_t *uni_reqpath = NULL;
1029         int uni_reqpathoffset1, uni_reqpathoffset2;
1030         int uni_curroffset;
1031         int reply_size = 0;
1032
1033         int reqpathlen = 0;
1034         int offset,i=0;
1035
1036         DEBUG(10,("setting up version3 referral\n"));
1037
1038         reqpathlen = rpcstr_push_talloc(talloc_tos(), &uni_reqpath, pathname);
1039         if (uni_reqpath == NULL || reqpathlen == 0) {
1040                 return -1;
1041         }
1042
1043         if (DEBUGLVL(10)) {
1044                 dump_data(0, (unsigned char *)uni_reqpath,
1045                         reqpathlen);
1046         }
1047
1048         uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
1049                         VERSION3_REFERRAL_SIZE * junction->referral_count;
1050         uni_reqpathoffset2 = uni_reqpathoffset1 + reqpathlen;
1051         reply_size = uni_curroffset = uni_reqpathoffset2 + reqpathlen;
1052
1053         for(i=0;i<junction->referral_count;i++) {
1054                 DEBUG(10,("referral %u : %s\n",
1055                         i,
1056                         junction->referral_list[i].alternate_path));
1057                 reply_size +=
1058                         (strlen(junction->referral_list[i].alternate_path)+1)*2;
1059         }
1060
1061         pdata = (char *)SMB_REALLOC(pdata,reply_size);
1062         if(pdata == NULL) {
1063                 DEBUG(0,("version3 referral setup:"
1064                         "malloc failed for Realloc!\n"));
1065                 return -1;
1066         }
1067         *ppdata = pdata;
1068
1069         /* create the header */
1070         SSVAL(pdata,0,consumedcnt * 2); /* path consumed */
1071         SSVAL(pdata,2,junction->referral_count); /* number of referral */
1072         if(self_referral) {
1073                 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
1074         } else {
1075                 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
1076         }
1077
1078         /* copy in the reqpaths */
1079         memcpy(pdata+uni_reqpathoffset1,uni_reqpath,reqpathlen);
1080         memcpy(pdata+uni_reqpathoffset2,uni_reqpath,reqpathlen);
1081
1082         offset = 8;
1083         for(i=0;i<junction->referral_count;i++) {
1084                 struct referral* ref = &(junction->referral_list[i]);
1085                 int unilen;
1086
1087                 SSVAL(pdata,offset,3); /* version 3 */
1088                 SSVAL(pdata,offset+2,VERSION3_REFERRAL_SIZE);
1089                 if(self_referral) {
1090                         SSVAL(pdata,offset+4,1);
1091                 } else {
1092                         SSVAL(pdata,offset+4,0);
1093                 }
1094
1095                 /* ref_flags :use path_consumed bytes? */
1096                 SSVAL(pdata,offset+6,0);
1097                 SIVAL(pdata,offset+8,ref->ttl);
1098
1099                 SSVAL(pdata,offset+12,uni_reqpathoffset1-offset);
1100                 SSVAL(pdata,offset+14,uni_reqpathoffset2-offset);
1101                 /* copy referred path into current offset */
1102                 unilen = rpcstr_push(pdata+uni_curroffset,ref->alternate_path,
1103                                         reply_size - uni_curroffset,
1104                                         STR_UNICODE | STR_TERMINATE);
1105                 SSVAL(pdata,offset+16,uni_curroffset-offset);
1106                 /* copy 0x10 bytes of 00's in the ServiceSite GUID */
1107                 memset(pdata+offset+18,'\0',16);
1108
1109                 uni_curroffset += unilen;
1110                 offset += VERSION3_REFERRAL_SIZE;
1111         }
1112         return reply_size;
1113 }
1114
1115 /******************************************************************
1116  Set up the DFS referral for the dfs pathname. This call returns
1117  the amount of the path covered by this server, and where the
1118  client should be redirected to. This is the meat of the
1119  TRANS2_GET_DFS_REFERRAL call.
1120 ******************************************************************/
1121
1122 int setup_dfs_referral(connection_struct *orig_conn,
1123                         const char *dfs_path,
1124                         int max_referral_level,
1125                         char **ppdata, NTSTATUS *pstatus)
1126 {
1127         struct junction_map *junction = NULL;
1128         int consumedcnt = 0;
1129         bool self_referral = False;
1130         int reply_size = 0;
1131         char *pathnamep = NULL;
1132         char *local_dfs_path = NULL;
1133         TALLOC_CTX *ctx;
1134
1135         if (!(ctx=talloc_init("setup_dfs_referral"))) {
1136                 *pstatus = NT_STATUS_NO_MEMORY;
1137                 return -1;
1138         }
1139
1140         /* get the junction entry */
1141         if (!dfs_path) {
1142                 talloc_destroy(ctx);
1143                 *pstatus = NT_STATUS_NOT_FOUND;
1144                 return -1;
1145         }
1146
1147         /*
1148          * Trim pathname sent by client so it begins with only one backslash.
1149          * Two backslashes confuse some dfs clients
1150          */
1151
1152         local_dfs_path = talloc_strdup(ctx,dfs_path);
1153         if (!local_dfs_path) {
1154                 *pstatus = NT_STATUS_NO_MEMORY;
1155                 talloc_destroy(ctx);
1156                 return -1;
1157         }
1158         pathnamep = local_dfs_path;
1159         while (IS_DIRECTORY_SEP(pathnamep[0]) &&
1160                         IS_DIRECTORY_SEP(pathnamep[1])) {
1161                 pathnamep++;
1162         }
1163
1164         junction = TALLOC_ZERO_P(ctx, struct junction_map);
1165         if (!junction) {
1166                 *pstatus = NT_STATUS_NO_MEMORY;
1167                 talloc_destroy(ctx);
1168                 return -1;
1169         }
1170
1171         /* The following call can change cwd. */
1172         *pstatus = get_referred_path(ctx, pathnamep, junction,
1173                         &consumedcnt, &self_referral);
1174         if (!NT_STATUS_IS_OK(*pstatus)) {
1175                 vfs_ChDir(orig_conn,orig_conn->connectpath);
1176                 talloc_destroy(ctx);
1177                 return -1;
1178         }
1179         vfs_ChDir(orig_conn,orig_conn->connectpath);
1180
1181         if (!self_referral) {
1182                 pathnamep[consumedcnt] = '\0';
1183
1184                 if( DEBUGLVL( 3 ) ) {
1185                         int i=0;
1186                         dbgtext("setup_dfs_referral: Path %s to "
1187                                 "alternate path(s):",
1188                                 pathnamep);
1189                         for(i=0;i<junction->referral_count;i++)
1190                                 dbgtext(" %s",
1191                                 junction->referral_list[i].alternate_path);
1192                         dbgtext(".\n");
1193                 }
1194         }
1195
1196         /* create the referral depeding on version */
1197         DEBUG(10,("max_referral_level :%d\n",max_referral_level));
1198
1199         if (max_referral_level < 2) {
1200                 max_referral_level = 2;
1201         }
1202         if (max_referral_level > 3) {
1203                 max_referral_level = 3;
1204         }
1205
1206         switch(max_referral_level) {
1207         case 2:
1208                 reply_size = setup_ver2_dfs_referral(pathnamep,
1209                                         ppdata, junction,
1210                                         consumedcnt, self_referral);
1211                 break;
1212         case 3:
1213                 reply_size = setup_ver3_dfs_referral(pathnamep, ppdata,
1214                                         junction, consumedcnt, self_referral);
1215                 break;
1216         default:
1217                 DEBUG(0,("setup_dfs_referral: Invalid dfs referral "
1218                         "version: %d\n",
1219                         max_referral_level));
1220                 talloc_destroy(ctx);
1221                 *pstatus = NT_STATUS_INVALID_LEVEL;
1222                 return -1;
1223         }
1224
1225         if (DEBUGLVL(10)) {
1226                 DEBUGADD(0,("DFS Referral pdata:\n"));
1227                 dump_data(0,(uint8 *)*ppdata,reply_size);
1228         }
1229
1230         talloc_destroy(ctx);
1231         *pstatus = NT_STATUS_OK;
1232         return reply_size;
1233 }
1234
1235 /**********************************************************************
1236  The following functions are called by the NETDFS RPC pipe functions
1237  **********************************************************************/
1238
1239 /*********************************************************************
1240  Creates a junction structure from a DFS pathname
1241 **********************************************************************/
1242
1243 bool create_junction(TALLOC_CTX *ctx,
1244                 const char *dfs_path,
1245                 struct junction_map *jucn)
1246 {
1247         int snum;
1248         bool dummy;
1249         struct dfs_path *pdp = TALLOC_P(ctx,struct dfs_path);
1250         NTSTATUS status;
1251
1252         if (!pdp) {
1253                 return False;
1254         }
1255         status = parse_dfs_path(NULL, dfs_path, False, pdp, &dummy);
1256         if (!NT_STATUS_IS_OK(status)) {
1257                 return False;
1258         }
1259
1260         /* check if path is dfs : validate first token */
1261         if (!is_myname_or_ipaddr(pdp->hostname)) {
1262                 DEBUG(4,("create_junction: Invalid hostname %s "
1263                         "in dfs path %s\n",
1264                         pdp->hostname, dfs_path));
1265                 TALLOC_FREE(pdp);
1266                 return False;
1267         }
1268
1269         /* Check for a non-DFS share */
1270         snum = lp_servicenumber(pdp->servicename);
1271
1272         if(snum < 0 || !lp_msdfs_root(snum)) {
1273                 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1274                         pdp->servicename));
1275                 TALLOC_FREE(pdp);
1276                 return False;
1277         }
1278
1279         jucn->service_name = talloc_strdup(ctx, pdp->servicename);
1280         jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
1281         jucn->comment = talloc_strdup(ctx, lp_comment(snum));
1282
1283         TALLOC_FREE(pdp);
1284         if (!jucn->service_name || !jucn->volume_name || ! jucn->comment) {
1285                 return False;
1286         }
1287         return True;
1288 }
1289
1290 /**********************************************************************
1291  Forms a valid Unix pathname from the junction
1292  **********************************************************************/
1293
1294 static bool junction_to_local_path(const struct junction_map *jucn,
1295                                 char **pp_path_out,
1296                                 connection_struct **conn_out)
1297 {
1298         int snum;
1299
1300         snum = lp_servicenumber(jucn->service_name);
1301         if(snum < 0) {
1302                 return False;
1303         }
1304         if (!NT_STATUS_IS_OK(create_conn_struct(talloc_tos(),
1305                                         conn_out, snum,
1306                                         lp_pathname(snum)))) {
1307                 return False;
1308         }
1309
1310         *pp_path_out = talloc_asprintf(conn_out,
1311                         "%s/%s",
1312                         lp_pathname(snum),
1313                         jucn->volume_name);
1314         if (!*pp_path_out) {
1315                 return False;
1316         }
1317         return True;
1318 }
1319
1320 bool create_msdfs_link(const struct junction_map *jucn,
1321                 bool exists)
1322 {
1323         char *path = NULL;
1324         char *msdfs_link = NULL;
1325         connection_struct *conn;
1326         int i=0;
1327         bool insert_comma = False;
1328         bool ret = False;
1329
1330         if(!junction_to_local_path(jucn, &path, &conn)) {
1331                 return False;
1332         }
1333
1334         /* Form the msdfs_link contents */
1335         msdfs_link = talloc_strdup(conn, "msdfs:");
1336         if (!msdfs_link) {
1337                 goto out;
1338         }
1339         for(i=0; i<jucn->referral_count; i++) {
1340                 char *refpath = jucn->referral_list[i].alternate_path;
1341
1342                 /* Alternate paths always use Windows separators. */
1343                 trim_char(refpath, '\\', '\\');
1344                 if(*refpath == '\0') {
1345                         if (i == 0) {
1346                                 insert_comma = False;
1347                         }
1348                         continue;
1349                 }
1350                 if (i > 0 && insert_comma) {
1351                         msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1352                                         ",%s",
1353                                         refpath);
1354                 } else {
1355                         msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1356                                         "%s",
1357                                         refpath);
1358                 }
1359
1360                 if (!msdfs_link) {
1361                         goto out;
1362                 }
1363                 if (!insert_comma) {
1364                         insert_comma = True;
1365                 }
1366         }
1367
1368         DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
1369                 path, msdfs_link));
1370
1371         if(exists) {
1372                 if(SMB_VFS_UNLINK(conn,path)!=0) {
1373                         goto out;
1374                 }
1375         }
1376
1377         if(SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
1378                 DEBUG(1,("create_msdfs_link: symlink failed "
1379                         "%s -> %s\nError: %s\n", 
1380                         path, msdfs_link, strerror(errno)));
1381                 goto out;
1382         }
1383
1384         ret = True;
1385
1386 out:
1387
1388         conn_free_internal(conn);
1389         return ret;
1390 }
1391
1392 bool remove_msdfs_link(const struct junction_map *jucn)
1393 {
1394         char *path = NULL;
1395         connection_struct *conn;
1396         bool ret = False;
1397
1398         if( junction_to_local_path(jucn, &path, &conn) ) {
1399                 if( SMB_VFS_UNLINK(conn, path) == 0 ) {
1400                         ret = True;
1401                 }
1402         }
1403
1404         conn_free_internal(conn);
1405         return ret;
1406 }
1407
1408 /*********************************************************************
1409  Return the number of DFS links at the root of this share.
1410 *********************************************************************/
1411
1412 static int count_dfs_links(TALLOC_CTX *ctx, int snum)
1413 {
1414         size_t cnt = 0;
1415         SMB_STRUCT_DIR *dirp = NULL;
1416         char *dname = NULL;
1417         const char *connect_path = lp_pathname(snum);
1418         const char *msdfs_proxy = lp_msdfs_proxy(snum);
1419         connection_struct *conn;
1420
1421         if(*connect_path == '\0') {
1422                 return 0;
1423         }
1424
1425         /*
1426          * Fake up a connection struct for the VFS layer.
1427          */
1428
1429         if (!NT_STATUS_IS_OK(create_conn_struct(talloc_tos(),
1430                                         &conn, snum, connect_path))) {
1431                 return 0;
1432         }
1433
1434         /* Count a link for the msdfs root - convention */
1435         cnt = 1;
1436
1437         /* No more links if this is an msdfs proxy. */
1438         if (*msdfs_proxy != '\0') {
1439                 goto out;
1440         }
1441
1442         /* Now enumerate all dfs links */
1443         dirp = SMB_VFS_OPENDIR(conn, ".", NULL, 0);
1444         if(!dirp) {
1445                 goto out;
1446         }
1447
1448         while ((dname = vfs_readdirname(conn, dirp)) != NULL) {
1449                 if (is_msdfs_link(conn,
1450                                 dname,
1451                                 NULL)) {
1452                         cnt++;
1453                 }
1454         }
1455
1456         SMB_VFS_CLOSEDIR(conn,dirp);
1457
1458 out:
1459
1460         conn_free_internal(conn);
1461         return cnt;
1462 }
1463
1464 /*********************************************************************
1465 *********************************************************************/
1466
1467 static int form_junctions(TALLOC_CTX *ctx,
1468                                 int snum,
1469                                 struct junction_map *jucn,
1470                                 size_t jn_remain)
1471 {
1472         size_t cnt = 0;
1473         SMB_STRUCT_DIR *dirp = NULL;
1474         char *dname = NULL;
1475         const char *connect_path = lp_pathname(snum);
1476         char *service_name = lp_servicename(snum);
1477         const char *msdfs_proxy = lp_msdfs_proxy(snum);
1478         connection_struct *conn;
1479         struct referral *ref = NULL;
1480
1481         if (jn_remain == 0) {
1482                 return 0;
1483         }
1484
1485         if(*connect_path == '\0') {
1486                 return 0;
1487         }
1488
1489         /*
1490          * Fake up a connection struct for the VFS layer.
1491          */
1492
1493         if (!NT_STATUS_IS_OK(create_conn_struct(ctx, &conn, snum, connect_path))) {
1494                 return 0;
1495         }
1496
1497         /* form a junction for the msdfs root - convention
1498            DO NOT REMOVE THIS: NT clients will not work with us
1499            if this is not present
1500         */
1501         jucn[cnt].service_name = talloc_strdup(ctx,service_name);
1502         jucn[cnt].volume_name = talloc_strdup(ctx, "");
1503         if (!jucn[cnt].service_name || !jucn[cnt].volume_name) {
1504                 goto out;
1505         }
1506         jucn[cnt].referral_count = 1;
1507
1508         ref = jucn[cnt].referral_list = TALLOC_ZERO_P(ctx, struct referral);
1509         if (jucn[cnt].referral_list == NULL) {
1510                 goto out;
1511         }
1512
1513         ref->proximity = 0;
1514         ref->ttl = REFERRAL_TTL;
1515         if (*msdfs_proxy != '\0') {
1516                 ref->alternate_path = talloc_strdup(ctx,
1517                                                 msdfs_proxy);
1518         } else {
1519                 ref->alternate_path = talloc_asprintf(ctx,
1520                         "\\\\%s\\%s",
1521                         get_local_machine_name(),
1522                         service_name);
1523         }
1524
1525         if (!ref->alternate_path) {
1526                 goto out;
1527         }
1528         cnt++;
1529
1530         /* Don't enumerate if we're an msdfs proxy. */
1531         if (*msdfs_proxy != '\0') {
1532                 goto out;
1533         }
1534
1535         /* Now enumerate all dfs links */
1536         dirp = SMB_VFS_OPENDIR(conn, ".", NULL, 0);
1537         if(!dirp) {
1538                 goto out;
1539         }
1540
1541         while ((dname = vfs_readdirname(conn, dirp)) != NULL) {
1542                 char *link_target = NULL;
1543                 if (cnt >= jn_remain) {
1544                         DEBUG(2, ("form_junctions: ran out of MSDFS "
1545                                 "junction slots"));
1546                         goto out;
1547                 }
1548                 if (is_msdfs_link_internal(ctx,
1549                                         conn,
1550                                         dname, &link_target,
1551                                         NULL)) {
1552                         if (parse_msdfs_symlink(ctx,
1553                                         link_target,
1554                                         &jucn[cnt].referral_list,
1555                                         &jucn[cnt].referral_count)) {
1556
1557                                 jucn[cnt].service_name = talloc_strdup(ctx,
1558                                                                 service_name);
1559                                 jucn[cnt].volume_name = talloc_strdup(ctx,
1560                                                                 dname);
1561                                 if (!jucn[cnt].service_name ||
1562                                                 !jucn[cnt].volume_name) {
1563                                         goto out;
1564                                 }
1565                                 cnt++;
1566                         }
1567                 }
1568         }
1569
1570 out:
1571
1572         if (dirp) {
1573                 SMB_VFS_CLOSEDIR(conn,dirp);
1574         }
1575
1576         conn_free_internal(conn);
1577         return cnt;
1578 }
1579
1580 struct junction_map *enum_msdfs_links(TALLOC_CTX *ctx, size_t *p_num_jn)
1581 {
1582         struct junction_map *jn = NULL;
1583         int i=0;
1584         size_t jn_count = 0;
1585         int sharecount = 0;
1586
1587         *p_num_jn = 0;
1588         if(!lp_host_msdfs()) {
1589                 return NULL;
1590         }
1591
1592         /* Ensure all the usershares are loaded. */
1593         become_root();
1594         load_registry_shares();
1595         sharecount = load_usershare_shares();
1596         unbecome_root();
1597
1598         for(i=0;i < sharecount;i++) {
1599                 if(lp_msdfs_root(i)) {
1600                         jn_count += count_dfs_links(ctx, i);
1601                 }
1602         }
1603         if (jn_count == 0) {
1604                 return NULL;
1605         }
1606         jn = TALLOC_ARRAY(ctx,  struct junction_map, jn_count);
1607         if (!jn) {
1608                 return NULL;
1609         }
1610         for(i=0; i < sharecount; i++) {
1611                 if (*p_num_jn >= jn_count) {
1612                         break;
1613                 }
1614                 if(lp_msdfs_root(i)) {
1615                         *p_num_jn += form_junctions(ctx, i,
1616                                         &jn[*p_num_jn],
1617                                         jn_count - *p_num_jn);
1618                 }
1619         }
1620         return jn;
1621 }
1622
1623 /******************************************************************************
1624  Core function to resolve a dfs pathname.
1625 ******************************************************************************/
1626
1627 NTSTATUS resolve_dfspath(TALLOC_CTX *ctx,
1628                         connection_struct *conn,
1629                         bool dfs_pathnames,
1630                         const char *name_in,
1631                         char **pp_name_out)
1632 {
1633         NTSTATUS status = NT_STATUS_OK;
1634         bool dummy;
1635         if (dfs_pathnames) {
1636                 status = dfs_redirect(ctx,
1637                                         conn,
1638                                         name_in,
1639                                         False,
1640                                         pp_name_out,
1641                                         &dummy);
1642         } else {
1643                 /*
1644                  * Cheat and just return a copy of the in ptr.
1645                  * Once srvstr_get_path() uses talloc it'll
1646                  * be a talloced ptr anyway.
1647                  */
1648                 *pp_name_out = CONST_DISCARD(char *,name_in);
1649         }
1650         return status;
1651 }
1652
1653 /******************************************************************************
1654  Core function to resolve a dfs pathname possibly containing a wildcard.
1655  This function is identical to the above except for the bool param to
1656  dfs_redirect but I need this to be separate so it's really clear when
1657  we're allowing wildcards and when we're not. JRA.
1658 ******************************************************************************/
1659
1660 NTSTATUS resolve_dfspath_wcard(TALLOC_CTX *ctx,
1661                                 connection_struct *conn,
1662                                 bool dfs_pathnames,
1663                                 const char *name_in,
1664                                 char **pp_name_out,
1665                                 bool *ppath_contains_wcard)
1666 {
1667         NTSTATUS status = NT_STATUS_OK;
1668         if (dfs_pathnames) {
1669                 status = dfs_redirect(ctx,
1670                                         conn,
1671                                         name_in,
1672                                         True,
1673                                         pp_name_out,
1674                                         ppath_contains_wcard);
1675         } else {
1676                 /*
1677                  * Cheat and just return a copy of the in ptr.
1678                  * Once srvstr_get_path() uses talloc it'll
1679                  * be a talloced ptr anyway.
1680                  */
1681                 *pp_name_out = CONST_DISCARD(char *,name_in);
1682         }
1683         return status;
1684 }