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