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