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