r23843: Fix bug #4777, reported by Bill Marshall <bmarsh@us.ibm.com>.
[samba.git] / source3 / smbd / msdfs.c
1 /* 
2    Unix SMB/Netbios implementation.
3    Version 3.0
4    MSDFS services for Samba
5    Copyright (C) Shirish Kalele 2000
6    Copyright (C) Jeremy Allison 2007
7
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 3 of the License, or
11    (at your option) any later version.
12    
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17    
18    You should have received a copy of the GNU General Public License
19    along with this program.  If not, see <http://www.gnu.org/licenses/>.
20
21 */
22
23 #define DBGC_CLASS DBGC_MSDFS
24 #include "includes.h"
25
26 extern uint32 global_client_caps;
27
28 /**********************************************************************
29  Parse a DFS pathname of the form \hostname\service\reqpath
30  into the dfs_path structure.
31  If POSIX pathnames is true, the pathname may also be of the
32  form /hostname/service/reqpath.
33  We cope with either here.
34
35  Unfortunately, due to broken clients who might set the
36  SVAL(inbuf,smb_flg2) & FLAGS2_DFS_PATHNAMES bit and then
37  send a local path, we have to cope with that too....
38
39  JRA.
40 **********************************************************************/
41
42 static NTSTATUS parse_dfs_path(const char *pathname,
43                                 BOOL allow_wcards,
44                                 struct dfs_path *pdp,
45                                 BOOL *ppath_contains_wcard)
46 {
47         pstring pathname_local;
48         char *p,*temp;
49         NTSTATUS status = NT_STATUS_OK;
50         char sepchar;
51
52         ZERO_STRUCTP(pdp);
53
54         pstrcpy(pathname_local,pathname);
55         p = temp = pathname_local;
56
57         pdp->posix_path = (lp_posix_pathnames() && *pathname == '/');
58
59         sepchar = pdp->posix_path ? '/' : '\\';
60
61         if (*pathname != sepchar) {
62                 DEBUG(10,("parse_dfs_path: path %s doesn't start with %c\n",
63                         pathname, sepchar ));
64                 /*
65                  * Possibly client sent a local path by mistake.
66                  * Try and convert to a local path.
67                  */
68
69                 pdp->hostname[0] = '\0';
70                 pdp->servicename[0] = '\0';
71
72                 /* We've got no info about separators. */
73                 pdp->posix_path = lp_posix_pathnames();
74                 p = temp;
75                 DEBUG(10,("parse_dfs_path: trying to convert %s to a local path\n",
76                         temp));
77                 goto local_path;
78         }
79
80         trim_char(temp,sepchar,sepchar);
81
82         DEBUG(10,("parse_dfs_path: temp = |%s| after trimming %c's\n",
83                 temp, sepchar));
84
85         /* Now tokenize. */
86         /* Parse out hostname. */
87         p = strchr_m(temp,sepchar);
88         if(p == NULL) {
89                 DEBUG(10,("parse_dfs_path: can't parse hostname from path %s\n",
90                         temp));
91                 /*
92                  * Possibly client sent a local path by mistake.
93                  * Try and convert to a local path.
94                  */
95
96                 pdp->hostname[0] = '\0';
97                 pdp->servicename[0] = '\0';
98
99                 p = temp;
100                 DEBUG(10,("parse_dfs_path: trying to convert %s to a local path\n",
101                         temp));
102                 goto local_path;
103         }
104         *p = '\0';
105         fstrcpy(pdp->hostname,temp);
106         DEBUG(10,("parse_dfs_path: hostname: %s\n",pdp->hostname));
107
108         /* If we got a hostname, is it ours (or an IP address) ? */
109         if (!is_myname_or_ipaddr(pdp->hostname)) {
110                 /* Repair path. */
111                 *p = sepchar;
112                 DEBUG(10,("parse_dfs_path: hostname %s isn't ours. Try local path from path %s\n",
113                         pdp->hostname, temp));
114                 /*
115                  * Possibly client sent a local path by mistake.
116                  * Try and convert to a local path.
117                  */
118
119                 pdp->hostname[0] = '\0';
120                 pdp->servicename[0] = '\0';
121
122                 p = temp;
123                 DEBUG(10,("parse_dfs_path: trying to convert %s to a local path\n",
124                         temp));
125                 goto local_path;
126         }
127
128         /* Parse out servicename. */
129         temp = p+1;
130         p = strchr_m(temp,sepchar);
131         if(p == NULL) {
132                 fstrcpy(pdp->servicename,temp);
133                 pdp->reqpath[0] = '\0';
134                 return NT_STATUS_OK;
135         }
136         *p = '\0';
137         fstrcpy(pdp->servicename,temp);
138         DEBUG(10,("parse_dfs_path: servicename: %s\n",pdp->servicename));
139
140         p++;
141
142   local_path:
143
144         *ppath_contains_wcard = False;
145
146         pstrcpy(pdp->reqpath, p);
147
148         /* Rest is reqpath. */
149         if (pdp->posix_path) {
150                 status = check_path_syntax_posix(pdp->reqpath);
151         } else {
152                 if (allow_wcards) {
153                         status = check_path_syntax_wcard(pdp->reqpath, ppath_contains_wcard);
154                 } else {
155                         status = check_path_syntax(pdp->reqpath);
156                 }
157         }
158
159         if (!NT_STATUS_IS_OK(status)) {
160                 DEBUG(10,("parse_dfs_path: '%s' failed with %s\n",
161                         p, nt_errstr(status) ));
162                 return status;
163         }
164
165         DEBUG(10,("parse_dfs_path: rest of the path: %s\n",pdp->reqpath));
166         return NT_STATUS_OK;
167 }
168
169 /********************************************************
170  Fake up a connection struct for the VFS layer.
171  Note this CHANGES CWD !!!! JRA.
172 *********************************************************/
173
174 static NTSTATUS create_conn_struct(connection_struct *conn, int snum, const char *path)
175 {
176         pstring connpath;
177
178         ZERO_STRUCTP(conn);
179
180         pstrcpy(connpath, path);
181         pstring_sub(connpath , "%S", lp_servicename(snum));
182
183         /* needed for smbd_vfs_init() */
184
185         if ((conn->mem_ctx=talloc_init("connection_struct")) == NULL) {
186                 DEBUG(0,("talloc_init(connection_struct) failed!\n"));
187                 return NT_STATUS_NO_MEMORY;
188         }
189
190         if (!(conn->params = TALLOC_ZERO_P(conn->mem_ctx, struct share_params))) {
191                 DEBUG(0, ("TALLOC failed\n"));
192                 return NT_STATUS_NO_MEMORY;
193         }
194
195         conn->params->service = snum;
196
197         set_conn_connectpath(conn, connpath);
198
199         if (!smbd_vfs_init(conn)) {
200                 NTSTATUS status = map_nt_error_from_unix(errno);
201                 DEBUG(0,("create_conn_struct: smbd_vfs_init failed.\n"));
202                 conn_free_internal(conn);
203                 return status;
204         }
205
206         /*
207          * Windows seems to insist on doing trans2getdfsreferral() calls on the IPC$
208          * share as the anonymous user. If we try to chdir as that user we will
209          * fail.... WTF ? JRA.
210          */
211
212         if (vfs_ChDir(conn,conn->connectpath) != 0) {
213                 NTSTATUS status = map_nt_error_from_unix(errno);
214                 DEBUG(3,("create_conn_struct: Can't ChDir to new conn path %s. Error was %s\n",
215                                         conn->connectpath, strerror(errno) ));
216                 conn_free_internal(conn);
217                 return status;
218         }
219
220         return NT_STATUS_OK;
221 }
222
223 /**********************************************************************
224  Parse the contents of a symlink to verify if it is an msdfs referral
225  A valid referral is of the form:
226
227  msdfs:server1\share1,server2\share2
228  msdfs:server1\share1\pathname,server2\share2\pathname
229  msdfs:server1/share1,server2/share2
230  msdfs:server1/share1/pathname,server2/share2/pathname.
231
232  Note that the alternate paths returned here must be of the canonicalized
233  form:
234
235  \server\share or
236  \server\share\path\to\file,
237
238  even in posix path mode. This is because we have no knowledge if the
239  server we're referring to understands posix paths.
240  **********************************************************************/
241
242 static BOOL parse_msdfs_symlink(TALLOC_CTX *ctx,
243                                 char *target,
244                                 struct referral **preflist,
245                                 int *refcount)
246 {
247         pstring temp;
248         char *prot;
249         char *alt_path[MAX_REFERRAL_COUNT];
250         int count = 0, i;
251         struct referral *reflist;
252
253         pstrcpy(temp,target);
254         prot = strtok(temp,":");
255         if (!prot) {
256                 DEBUG(0,("parse_msdfs_symlink: invalid path !\n"));
257                 return False;
258         }
259
260         /* parse out the alternate paths */
261         while((count<MAX_REFERRAL_COUNT) &&
262               ((alt_path[count] = strtok(NULL,",")) != NULL)) {
263                 count++;
264         }
265
266         DEBUG(10,("parse_msdfs_symlink: count=%d\n", count));
267
268         if (count) {
269                 reflist = *preflist = TALLOC_ZERO_ARRAY(ctx, struct referral, count);
270                 if(reflist == NULL) {
271                         DEBUG(0,("parse_msdfs_symlink: talloc failed!\n"));
272                         return False;
273                 }
274         } else {
275                 reflist = *preflist = NULL;
276         }
277         
278         for(i=0;i<count;i++) {
279                 char *p;
280
281                 /* Canonicalize link target. Replace all /'s in the path by a \ */
282                 string_replace(alt_path[i], '/', '\\');
283
284                 /* Remove leading '\\'s */
285                 p = alt_path[i];
286                 while (*p && (*p == '\\')) {
287                         p++;
288                 }
289
290                 pstrcpy(reflist[i].alternate_path, "\\");
291                 pstrcat(reflist[i].alternate_path, p);
292
293                 reflist[i].proximity = 0;
294                 reflist[i].ttl = REFERRAL_TTL;
295                 DEBUG(10, ("parse_msdfs_symlink: Created alt path: %s\n", reflist[i].alternate_path));
296                 *refcount += 1;
297         }
298
299         return True;
300 }
301  
302 /**********************************************************************
303  Returns true if the unix path is a valid msdfs symlink and also
304  returns the target string from inside the link.
305 **********************************************************************/
306
307 BOOL is_msdfs_link(connection_struct *conn,
308                         const char *path,
309                         pstring link_target,
310                         SMB_STRUCT_STAT *sbufp)
311 {
312         SMB_STRUCT_STAT st;
313         int referral_len = 0;
314
315         if (sbufp == NULL) {
316                 sbufp = &st;
317         }
318
319         if (SMB_VFS_LSTAT(conn, path, sbufp) != 0) {
320                 DEBUG(5,("is_msdfs_link: %s does not exist.\n",path));
321                 return False;
322         }
323   
324         if (!S_ISLNK(sbufp->st_mode)) {
325                 DEBUG(5,("is_msdfs_link: %s is not a link.\n",path));
326                 return False;
327         }
328
329         /* open the link and read it */
330         referral_len = SMB_VFS_READLINK(conn, path, link_target, sizeof(pstring)-1);
331         if (referral_len == -1) {
332                 DEBUG(0,("is_msdfs_link: Error reading msdfs link %s: %s\n",
333                         path, strerror(errno)));
334                 return False;
335         }
336         link_target[referral_len] = '\0';
337
338         DEBUG(5,("is_msdfs_link: %s -> %s\n",path, link_target));
339
340         if (!strnequal(link_target, "msdfs:", 6)) {
341                 return False;
342         }
343         return True;
344 }
345
346 /*****************************************************************
347  Used by other functions to decide if a dfs path is remote,
348  and to get the list of referred locations for that remote path.
349  
350  search_flag: For findfirsts, dfs links themselves are not
351  redirected, but paths beyond the links are. For normal smb calls,
352  even dfs links need to be redirected.
353
354  consumedcntp: how much of the dfs path is being redirected. the client
355  should try the remaining path on the redirected server.
356
357  If this returns NT_STATUS_PATH_NOT_COVERED the contents of the msdfs
358  link redirect are in targetpath.
359 *****************************************************************/
360
361 static NTSTATUS dfs_path_lookup(connection_struct *conn,
362                         const char *dfspath, /* Incoming complete dfs path */
363                         const struct dfs_path *pdp, /* Parsed out server+share+extrapath. */
364                         BOOL search_flag, /* Called from a findfirst ? */
365                         int *consumedcntp,
366                         pstring targetpath)
367 {
368         char *p = NULL;
369         char *q = NULL;
370         SMB_STRUCT_STAT sbuf;
371         NTSTATUS status;
372         pstring localpath;
373         pstring canon_dfspath; /* Canonicalized dfs path. (only '/' components). */
374
375         DEBUG(10,("dfs_path_lookup: Conn path = %s reqpath = %s\n",
376                 conn->connectpath, pdp->reqpath));
377
378         /* 
379          * Note the unix path conversion here we're doing we can
380          * throw away. We're looking for a symlink for a dfs
381          * resolution, if we don't find it we'll do another
382          * unix_convert later in the codepath.
383          * If we needed to remember what we'd resolved in
384          * dp->reqpath (as the original code did) we'd
385          * pstrcpy(localhost, dp->reqpath) on any code
386          * path below that returns True - but I don't
387          * think this is needed. JRA.
388          */
389
390         pstrcpy(localpath, pdp->reqpath);
391         status = unix_convert(conn, localpath, search_flag, NULL, &sbuf);
392         if (!NT_STATUS_IS_OK(status) && !NT_STATUS_EQUAL(status,
393                                         NT_STATUS_OBJECT_PATH_NOT_FOUND)) {
394                 return status;
395         }
396
397         /* Optimization - check if we can redirect the whole path. */
398
399         if (is_msdfs_link(conn, localpath, targetpath, NULL)) {
400                 if (search_flag) {
401                         DEBUG(6,("dfs_path_lookup (FindFirst) No redirection "
402                                  "for dfs link %s.\n", dfspath));
403                         return NT_STATUS_OK;
404                 }
405
406                 DEBUG(6,("dfs_path_lookup: %s resolves to a "
407                         "valid dfs link %s.\n", dfspath, targetpath));
408
409                 if (consumedcntp) {
410                         *consumedcntp = strlen(dfspath);
411                 }
412                 return NT_STATUS_PATH_NOT_COVERED;
413         }
414
415         /* Prepare to test only for '/' components in the given path,
416          * so if a Windows path replace all '\\' characters with '/'.
417          * For a POSIX DFS path we know all separators are already '/'. */
418
419         pstrcpy(canon_dfspath, dfspath);
420         if (!pdp->posix_path) {
421                 string_replace(canon_dfspath, '\\', '/');
422         }
423
424         /*
425          * Redirect if any component in the path is a link.
426          * We do this by walking backwards through the 
427          * local path, chopping off the last component
428          * in both the local path and the canonicalized
429          * DFS path. If we hit a DFS link then we're done.
430          */
431
432         p = strrchr_m(localpath, '/');
433         if (consumedcntp) {
434                 q = strrchr_m(canon_dfspath, '/');
435         }
436
437         while (p) {
438                 *p = '\0';
439                 if (q) {
440                         *q = '\0';
441                 }
442
443                 if (is_msdfs_link(conn, localpath, targetpath, NULL)) {
444                         DEBUG(4, ("dfs_path_lookup: Redirecting %s because "
445                                 "parent %s is dfs link\n", dfspath, localpath));
446
447                         if (consumedcntp) {
448                                 *consumedcntp = strlen(canon_dfspath);
449                                 DEBUG(10, ("dfs_path_lookup: Path consumed: %s "
450                                         "(%d)\n", canon_dfspath, *consumedcntp));
451                         }
452
453                         return NT_STATUS_PATH_NOT_COVERED;
454                 }
455
456                 /* Step back on the filesystem. */
457                 p = strrchr_m(localpath, '/');
458
459                 if (consumedcntp) {
460                         /* And in the canonicalized dfs path. */
461                         q = strrchr_m(canon_dfspath, '/');
462                 }
463         }
464
465         return NT_STATUS_OK;
466 }
467
468 /*****************************************************************
469  Decides if a dfs pathname should be redirected or not.
470  If not, the pathname is converted to a tcon-relative local unix path
471
472  search_wcard_flag: this flag performs 2 functions bother related
473  to searches.  See resolve_dfs_path() and parse_dfs_path_XX()
474  for details.
475
476  This function can return NT_STATUS_OK, meaning use the returned path as-is
477  (mapped into a local path).
478  or NT_STATUS_NOT_COVERED meaning return a DFS redirect, or
479  any other NT_STATUS error which is a genuine error to be
480  returned to the client. 
481 *****************************************************************/
482
483 static NTSTATUS dfs_redirect( connection_struct *conn,
484                         pstring dfs_path,
485                         BOOL search_wcard_flag,
486                         BOOL *ppath_contains_wcard)
487 {
488         NTSTATUS status;
489         struct dfs_path dp;
490         pstring targetpath;
491         
492         status = parse_dfs_path(dfs_path, search_wcard_flag, &dp, ppath_contains_wcard);
493         if (!NT_STATUS_IS_OK(status)) {
494                 return status;
495         }
496
497         if (dp.reqpath[0] == '\0') {
498                 pstrcpy(dfs_path, dp.reqpath);
499                 DEBUG(5,("dfs_redirect: self-referral.\n"));
500                 return NT_STATUS_OK;
501         }
502
503         /* If dfs pathname for a non-dfs share, convert to tcon-relative
504            path and return OK */
505
506         if (!lp_msdfs_root(SNUM(conn))) {
507                 pstrcpy(dfs_path, dp.reqpath);
508                 return NT_STATUS_OK;
509         }
510
511         /* If it looked like a local path (zero hostname/servicename)
512          * just treat as a tcon-relative path. */ 
513
514         if (dp.hostname[0] == '\0' && dp.servicename[0] == '\0') { 
515                 pstrcpy(dfs_path, dp.reqpath);
516                 return NT_STATUS_OK;
517         }
518
519         if (!( strequal(dp.servicename, lp_servicename(SNUM(conn))) 
520                         || (strequal(dp.servicename, HOMES_NAME) 
521                         && strequal(lp_servicename(SNUM(conn)), get_current_username()) )) ) {
522
523                 /* The given sharename doesn't match this connection. */
524
525                 return NT_STATUS_OBJECT_PATH_NOT_FOUND;
526         }
527
528         status = dfs_path_lookup(conn, dfs_path, &dp,
529                         search_wcard_flag, NULL, targetpath);
530         if (!NT_STATUS_IS_OK(status)) {
531                 if (NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
532                         DEBUG(3,("dfs_redirect: Redirecting %s\n", dfs_path));
533                 } else {
534                         DEBUG(10,("dfs_redirect: dfs_path_lookup failed for %s with %s\n",
535                                 dfs_path, nt_errstr(status) ));
536                 }
537                 return status;
538         }
539
540         DEBUG(3,("dfs_redirect: Not redirecting %s.\n", dfs_path));
541
542         /* Form non-dfs tcon-relative path */
543         pstrcpy(dfs_path, dp.reqpath);
544
545         DEBUG(3,("dfs_redirect: Path converted to non-dfs path %s\n", dfs_path));
546         return NT_STATUS_OK;
547 }
548
549 /**********************************************************************
550  Return a self referral.
551 **********************************************************************/
552
553 static NTSTATUS self_ref(TALLOC_CTX *ctx,
554                         const char *dfs_path,
555                         struct junction_map *jucn,
556                         int *consumedcntp,
557                         BOOL *self_referralp)
558 {
559         struct referral *ref;
560
561         *self_referralp = True;
562
563         jucn->referral_count = 1;
564         if((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) {
565                 DEBUG(0,("self_ref: talloc failed for referral\n"));
566                 return NT_STATUS_NO_MEMORY;
567         }
568
569         pstrcpy(ref->alternate_path,dfs_path);
570         ref->proximity = 0;
571         ref->ttl = REFERRAL_TTL;
572         jucn->referral_list = ref;
573         *consumedcntp = strlen(dfs_path);
574         return NT_STATUS_OK;
575 }
576
577 /**********************************************************************
578  Gets valid referrals for a dfs path and fills up the
579  junction_map structure.
580 **********************************************************************/
581
582 NTSTATUS get_referred_path(TALLOC_CTX *ctx,
583                         const char *dfs_path,
584                         struct junction_map *jucn,
585                         int *consumedcntp,
586                         BOOL *self_referralp)
587 {
588         struct connection_struct conns;
589         struct connection_struct *conn = &conns;
590         struct dfs_path dp;
591         pstring conn_path;
592         pstring targetpath;
593         int snum;
594         NTSTATUS status = NT_STATUS_NOT_FOUND;
595         BOOL dummy;
596
597         ZERO_STRUCT(conns);
598
599         *self_referralp = False;
600
601         status = parse_dfs_path(dfs_path, False, &dp, &dummy);
602         if (!NT_STATUS_IS_OK(status)) {
603                 return status;
604         }
605
606         /* Verify hostname in path */
607         if (!is_myname_or_ipaddr(dp.hostname)) {
608                 DEBUG(3, ("get_referred_path: Invalid hostname %s in path %s\n",
609                         dp.hostname, dfs_path));
610                 return NT_STATUS_NOT_FOUND;
611         }
612
613         fstrcpy(jucn->service_name, dp.servicename);
614         pstrcpy(jucn->volume_name, dp.reqpath);
615
616         /* Verify the share is a dfs root */
617         snum = lp_servicenumber(jucn->service_name);
618         if(snum < 0) {
619                 if ((snum = find_service(jucn->service_name)) < 0) {
620                         return NT_STATUS_NOT_FOUND;
621                 }
622         }
623
624         if (!lp_msdfs_root(snum)) {
625                 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not a dfs root.\n",
626                          dp.servicename, dfs_path));
627                 return NT_STATUS_NOT_FOUND;
628         }
629
630         /*
631          * Self referrals are tested with a anonymous IPC connection and
632          * a GET_DFS_REFERRAL call to \\server\share. (which means dp.reqpath[0] points
633          * to an empty string). create_conn_struct cd's into the directory and will
634          * fail if it cannot (as the anonymous user). Cope with this.
635          */
636
637         if (dp.reqpath[0] == '\0') {
638                 struct referral *ref;
639
640                 if (*lp_msdfs_proxy(snum) == '\0') {
641                         return self_ref(ctx,
642                                         dfs_path,
643                                         jucn,
644                                         consumedcntp,
645                                         self_referralp);
646                 }
647
648                 /* 
649                  * It's an msdfs proxy share. Redirect to
650                  * the configured target share.
651                  */
652
653                 jucn->referral_count = 1;
654                 if ((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) {
655                         DEBUG(0, ("malloc failed for referral\n"));
656                         return NT_STATUS_NO_MEMORY;
657                 }
658
659                 pstrcpy(ref->alternate_path, lp_msdfs_proxy(snum));
660                 if (dp.reqpath[0] != '\0') {
661                         pstrcat(ref->alternate_path, dp.reqpath);
662                 }
663                 ref->proximity = 0;
664                 ref->ttl = REFERRAL_TTL;
665                 jucn->referral_list = ref;
666                 *consumedcntp = strlen(dfs_path);
667                 return NT_STATUS_OK;
668         }
669
670         pstrcpy(conn_path, lp_pathname(snum));
671         status = create_conn_struct(conn, snum, conn_path);
672         if (!NT_STATUS_IS_OK(status)) {
673                 return status;
674         }
675
676         /* If this is a DFS path dfs_lookup should return
677          * NT_STATUS_PATH_NOT_COVERED. */
678
679         status = dfs_path_lookup(conn, dfs_path, &dp,
680                         False, consumedcntp, targetpath);
681
682         if (!NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
683                 DEBUG(3,("get_referred_path: No valid referrals for path %s\n",
684                         dfs_path));
685                 conn_free_internal(conn);
686                 return status;
687         }
688
689         /* We know this is a valid dfs link. Parse the targetpath. */
690         if (!parse_msdfs_symlink(ctx, targetpath,
691                                 &jucn->referral_list,
692                                 &jucn->referral_count)) {
693                 DEBUG(3,("get_referred_path: failed to parse symlink "
694                         "target %s\n", targetpath ));
695                 conn_free_internal(conn);
696                 return NT_STATUS_NOT_FOUND;
697         }
698
699         conn_free_internal(conn);
700         return NT_STATUS_OK;
701 }
702
703 static int setup_ver2_dfs_referral(const char *pathname,
704                                 char **ppdata, 
705                                 struct junction_map *junction,
706                                 int consumedcnt,
707                                 BOOL self_referral)
708 {
709         char* pdata = *ppdata;
710
711         unsigned char uni_requestedpath[1024];
712         int uni_reqpathoffset1,uni_reqpathoffset2;
713         int uni_curroffset;
714         int requestedpathlen=0;
715         int offset;
716         int reply_size = 0;
717         int i=0;
718
719         DEBUG(10,("Setting up version2 referral\nRequested path:\n"));
720
721         requestedpathlen = rpcstr_push(uni_requestedpath, pathname, sizeof(pstring),
722                                        STR_TERMINATE);
723
724         if (DEBUGLVL(10)) {
725             dump_data(0, uni_requestedpath,requestedpathlen);
726         }
727
728         DEBUG(10,("ref count = %u\n",junction->referral_count));
729
730         uni_reqpathoffset1 = REFERRAL_HEADER_SIZE + 
731                         VERSION2_REFERRAL_SIZE * junction->referral_count;
732
733         uni_reqpathoffset2 = uni_reqpathoffset1 + requestedpathlen;
734
735         uni_curroffset = uni_reqpathoffset2 + requestedpathlen;
736
737         reply_size = REFERRAL_HEADER_SIZE + VERSION2_REFERRAL_SIZE*junction->referral_count +
738                                         2 * requestedpathlen;
739         DEBUG(10,("reply_size: %u\n",reply_size));
740
741         /* add up the unicode lengths of all the referral paths */
742         for(i=0;i<junction->referral_count;i++) {
743                 DEBUG(10,("referral %u : %s\n",i,junction->referral_list[i].alternate_path));
744                 reply_size += (strlen(junction->referral_list[i].alternate_path)+1)*2;
745         }
746
747         DEBUG(10,("reply_size = %u\n",reply_size));
748         /* add the unexplained 0x16 bytes */
749         reply_size += 0x16;
750
751         pdata = (char *)SMB_REALLOC(pdata,reply_size);
752         if(pdata == NULL) {
753                 DEBUG(0,("Realloc failed!\n"));
754                 return -1;
755         }
756         *ppdata = pdata;
757
758         /* copy in the dfs requested paths.. required for offset calculations */
759         memcpy(pdata+uni_reqpathoffset1,uni_requestedpath,requestedpathlen);
760         memcpy(pdata+uni_reqpathoffset2,uni_requestedpath,requestedpathlen);
761
762         /* create the header */
763         SSVAL(pdata,0,consumedcnt * 2); /* path consumed */
764         SSVAL(pdata,2,junction->referral_count); /* number of referral in this pkt */
765         if(self_referral) {
766                 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER); 
767         } else {
768                 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
769         }
770
771         offset = 8;
772         /* add the referral elements */
773         for(i=0;i<junction->referral_count;i++) {
774                 struct referral* ref = &junction->referral_list[i];
775                 int unilen;
776
777                 SSVAL(pdata,offset,2); /* version 2 */
778                 SSVAL(pdata,offset+2,VERSION2_REFERRAL_SIZE);
779                 if(self_referral) {
780                         SSVAL(pdata,offset+4,1);
781                 } else {
782                         SSVAL(pdata,offset+4,0);
783                 }
784                 SSVAL(pdata,offset+6,0); /* ref_flags :use path_consumed bytes? */
785                 SIVAL(pdata,offset+8,ref->proximity);
786                 SIVAL(pdata,offset+12,ref->ttl);
787
788                 SSVAL(pdata,offset+16,uni_reqpathoffset1-offset);
789                 SSVAL(pdata,offset+18,uni_reqpathoffset2-offset);
790                 /* copy referred path into current offset */
791                 unilen = rpcstr_push(pdata+uni_curroffset, ref->alternate_path,
792                                      sizeof(pstring), STR_UNICODE);
793
794                 SSVAL(pdata,offset+20,uni_curroffset-offset);
795
796                 uni_curroffset += unilen;
797                 offset += VERSION2_REFERRAL_SIZE;
798         }
799         /* add in the unexplained 22 (0x16) bytes at the end */
800         memset(pdata+uni_curroffset,'\0',0x16);
801         return reply_size;
802 }
803
804 static int setup_ver3_dfs_referral(const char *pathname,
805                                 char **ppdata, 
806                                 struct junction_map *junction,
807                                 int consumedcnt,
808                                 BOOL self_referral)
809 {
810         char* pdata = *ppdata;
811
812         unsigned char uni_reqpath[1024];
813         int uni_reqpathoffset1, uni_reqpathoffset2;
814         int uni_curroffset;
815         int reply_size = 0;
816
817         int reqpathlen = 0;
818         int offset,i=0;
819         
820         DEBUG(10,("setting up version3 referral\n"));
821
822         reqpathlen = rpcstr_push(uni_reqpath, pathname, sizeof(pstring), STR_TERMINATE);
823         
824         if (DEBUGLVL(10)) {
825             dump_data(0, uni_reqpath,reqpathlen);
826         }
827
828         uni_reqpathoffset1 = REFERRAL_HEADER_SIZE + VERSION3_REFERRAL_SIZE * junction->referral_count;
829         uni_reqpathoffset2 = uni_reqpathoffset1 + reqpathlen;
830         reply_size = uni_curroffset = uni_reqpathoffset2 + reqpathlen;
831
832         for(i=0;i<junction->referral_count;i++) {
833                 DEBUG(10,("referral %u : %s\n",i,junction->referral_list[i].alternate_path));
834                 reply_size += (strlen(junction->referral_list[i].alternate_path)+1)*2;
835         }
836
837         pdata = (char *)SMB_REALLOC(pdata,reply_size);
838         if(pdata == NULL) {
839                 DEBUG(0,("version3 referral setup: malloc failed for Realloc!\n"));
840                 return -1;
841         }
842         *ppdata = pdata;
843
844         /* create the header */
845         SSVAL(pdata,0,consumedcnt * 2); /* path consumed */
846         SSVAL(pdata,2,junction->referral_count); /* number of referral */
847         if(self_referral) {
848                 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER); 
849         } else {
850                 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
851         }
852         
853         /* copy in the reqpaths */
854         memcpy(pdata+uni_reqpathoffset1,uni_reqpath,reqpathlen);
855         memcpy(pdata+uni_reqpathoffset2,uni_reqpath,reqpathlen);
856         
857         offset = 8;
858         for(i=0;i<junction->referral_count;i++) {
859                 struct referral* ref = &(junction->referral_list[i]);
860                 int unilen;
861
862                 SSVAL(pdata,offset,3); /* version 3 */
863                 SSVAL(pdata,offset+2,VERSION3_REFERRAL_SIZE);
864                 if(self_referral) {
865                         SSVAL(pdata,offset+4,1);
866                 } else {
867                         SSVAL(pdata,offset+4,0);
868                 }
869
870                 SSVAL(pdata,offset+6,0); /* ref_flags :use path_consumed bytes? */
871                 SIVAL(pdata,offset+8,ref->ttl);
872             
873                 SSVAL(pdata,offset+12,uni_reqpathoffset1-offset);
874                 SSVAL(pdata,offset+14,uni_reqpathoffset2-offset);
875                 /* copy referred path into current offset */
876                 unilen = rpcstr_push(pdata+uni_curroffset,ref->alternate_path,
877                                      sizeof(pstring), STR_UNICODE | STR_TERMINATE);
878                 SSVAL(pdata,offset+16,uni_curroffset-offset);
879                 /* copy 0x10 bytes of 00's in the ServiceSite GUID */
880                 memset(pdata+offset+18,'\0',16);
881
882                 uni_curroffset += unilen;
883                 offset += VERSION3_REFERRAL_SIZE;
884         }
885         return reply_size;
886 }
887
888 /******************************************************************
889  Set up the DFS referral for the dfs pathname. This call returns
890  the amount of the path covered by this server, and where the
891  client should be redirected to. This is the meat of the
892  TRANS2_GET_DFS_REFERRAL call.
893 ******************************************************************/
894
895 int setup_dfs_referral(connection_struct *orig_conn,
896                         const char *dfs_path,
897                         int max_referral_level,
898                         char **ppdata, NTSTATUS *pstatus)
899 {
900         struct junction_map junction;
901         int consumedcnt = 0;
902         BOOL self_referral = False;
903         int reply_size = 0;
904         char *pathnamep = NULL;
905         pstring local_dfs_path;
906         TALLOC_CTX *ctx;
907
908         if (!(ctx=talloc_init("setup_dfs_referral"))) {
909                 *pstatus = NT_STATUS_NO_MEMORY;
910                 return -1;
911         }
912
913         ZERO_STRUCT(junction);
914
915         /* get the junction entry */
916         if (!dfs_path) {
917                 talloc_destroy(ctx);
918                 *pstatus = NT_STATUS_NOT_FOUND;
919                 return -1;
920         }
921
922         /* 
923          * Trim pathname sent by client so it begins with only one backslash.
924          * Two backslashes confuse some dfs clients
925          */
926
927         pstrcpy(local_dfs_path, dfs_path);
928         pathnamep = local_dfs_path;
929         while (IS_DIRECTORY_SEP(pathnamep[0]) && IS_DIRECTORY_SEP(pathnamep[1])) {
930                 pathnamep++;
931         }
932
933         /* The following call can change cwd. */
934         *pstatus = get_referred_path(ctx, pathnamep, &junction, &consumedcnt, &self_referral);
935         if (!NT_STATUS_IS_OK(*pstatus)) {
936                 vfs_ChDir(orig_conn,orig_conn->connectpath);
937                 talloc_destroy(ctx);
938                 return -1;
939         }
940         vfs_ChDir(orig_conn,orig_conn->connectpath);
941         
942         if (!self_referral) {
943                 pathnamep[consumedcnt] = '\0';
944
945                 if( DEBUGLVL( 3 ) ) {
946                         int i=0;
947                         dbgtext("setup_dfs_referral: Path %s to alternate path(s):",pathnamep);
948                         for(i=0;i<junction.referral_count;i++)
949                                 dbgtext(" %s",junction.referral_list[i].alternate_path);
950                         dbgtext(".\n");
951                 }
952         }
953
954         /* create the referral depeding on version */
955         DEBUG(10,("max_referral_level :%d\n",max_referral_level));
956
957         if (max_referral_level < 2) {
958                 max_referral_level = 2;
959         }
960         if (max_referral_level > 3) {
961                 max_referral_level = 3;
962         }
963
964         switch(max_referral_level) {
965         case 2:
966                 reply_size = setup_ver2_dfs_referral(pathnamep, ppdata, &junction, 
967                                                      consumedcnt, self_referral);
968                 break;
969         case 3:
970                 reply_size = setup_ver3_dfs_referral(pathnamep, ppdata, &junction, 
971                                                      consumedcnt, self_referral);
972                 break;
973         default:
974                 DEBUG(0,("setup_dfs_referral: Invalid dfs referral version: %d\n", max_referral_level));
975                 talloc_destroy(ctx);
976                 *pstatus = NT_STATUS_INVALID_LEVEL;
977                 return -1;
978         }
979       
980         if (DEBUGLVL(10)) {
981                 DEBUGADD(0,("DFS Referral pdata:\n"));
982                 dump_data(0,(uint8 *)*ppdata,reply_size);
983         }
984
985         talloc_destroy(ctx);
986         *pstatus = NT_STATUS_OK;
987         return reply_size;
988 }
989
990 /**********************************************************************
991  The following functions are called by the NETDFS RPC pipe functions
992  **********************************************************************/
993
994 /*********************************************************************
995  Creates a junction structure from a DFS pathname
996 **********************************************************************/
997
998 BOOL create_junction(const char *dfs_path, struct junction_map *jucn)
999 {
1000         int snum;
1001         BOOL dummy;
1002         struct dfs_path dp;
1003  
1004         NTSTATUS status = parse_dfs_path(dfs_path, False, &dp, &dummy);
1005
1006         if (!NT_STATUS_IS_OK(status)) {
1007                 return False;
1008         }
1009
1010         /* check if path is dfs : validate first token */
1011         if (!is_myname_or_ipaddr(dp.hostname)) {
1012                 DEBUG(4,("create_junction: Invalid hostname %s in dfs path %s\n",
1013                         dp.hostname, dfs_path));
1014                 return False;
1015         }
1016
1017         /* Check for a non-DFS share */
1018         snum = lp_servicenumber(dp.servicename);
1019
1020         if(snum < 0 || !lp_msdfs_root(snum)) {
1021                 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1022                         dp.servicename));
1023                 return False;
1024         }
1025
1026         fstrcpy(jucn->service_name,dp.servicename);
1027         pstrcpy(jucn->volume_name,dp.reqpath);
1028         pstrcpy(jucn->comment, lp_comment(snum));
1029         return True;
1030 }
1031
1032 /**********************************************************************
1033  Forms a valid Unix pathname from the junction 
1034  **********************************************************************/
1035
1036 static BOOL junction_to_local_path(struct junction_map *jucn,
1037                                 char *path,
1038                                 int max_pathlen,
1039                                 connection_struct *conn_out)
1040 {
1041         int snum;
1042         pstring conn_path;
1043
1044         snum = lp_servicenumber(jucn->service_name);
1045         if(snum < 0) {
1046                 return False;
1047         }
1048
1049         safe_strcpy(path, lp_pathname(snum), max_pathlen-1);
1050         safe_strcat(path, "/", max_pathlen-1);
1051         safe_strcat(path, jucn->volume_name, max_pathlen-1);
1052
1053         pstrcpy(conn_path, lp_pathname(snum));
1054         if (!NT_STATUS_IS_OK(create_conn_struct(conn_out, snum, conn_path))) {
1055                 return False;
1056         }
1057
1058         return True;
1059 }
1060
1061 BOOL create_msdfs_link(struct junction_map *jucn, BOOL exists)
1062 {
1063         pstring path;
1064         pstring msdfs_link;
1065         connection_struct conns;
1066         connection_struct *conn = &conns;
1067         int i=0;
1068         BOOL insert_comma = False;
1069         BOOL ret = False;
1070
1071         ZERO_STRUCT(conns);
1072
1073         if(!junction_to_local_path(jucn, path, sizeof(path), conn)) {
1074                 return False;
1075         }
1076   
1077         /* Form the msdfs_link contents */
1078         pstrcpy(msdfs_link, "msdfs:");
1079         for(i=0; i<jucn->referral_count; i++) {
1080                 char* refpath = jucn->referral_list[i].alternate_path;
1081       
1082                 /* Alternate paths always use Windows separators. */
1083                 trim_char(refpath, '\\', '\\');
1084                 if(*refpath == '\0') {
1085                         if (i == 0) {
1086                                 insert_comma = False;
1087                         }
1088                         continue;
1089                 }
1090                 if (i > 0 && insert_comma) {
1091                         pstrcat(msdfs_link, ",");
1092                 }
1093
1094                 pstrcat(msdfs_link, refpath);
1095                 if (!insert_comma) {
1096                         insert_comma = True;
1097                 }
1098         }
1099
1100         DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
1101                 path, msdfs_link));
1102
1103         if(exists) {
1104                 if(SMB_VFS_UNLINK(conn,path)!=0) {
1105                         goto out;
1106                 }
1107         }
1108
1109         if(SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
1110                 DEBUG(1,("create_msdfs_link: symlink failed %s -> %s\nError: %s\n", 
1111                                 path, msdfs_link, strerror(errno)));
1112                 goto out;
1113         }
1114         
1115         
1116         ret = True;
1117
1118 out:
1119
1120         conn_free_internal(conn);
1121         return ret;
1122 }
1123
1124 BOOL remove_msdfs_link(struct junction_map *jucn)
1125 {
1126         pstring path;
1127         connection_struct conns;
1128         connection_struct *conn = &conns;
1129         BOOL ret = False;
1130
1131         ZERO_STRUCT(conns);
1132
1133         if( junction_to_local_path(jucn, path, sizeof(path), conn) ) {
1134                 if( SMB_VFS_UNLINK(conn, path) == 0 ) {
1135                         ret = True;
1136                 }
1137                 talloc_destroy( conn->mem_ctx );
1138         }
1139
1140         conn_free_internal(conn);
1141         return ret;
1142 }
1143
1144 static int form_junctions(TALLOC_CTX *ctx,
1145                                 int snum,
1146                                 struct junction_map *jucn,
1147                                 int jn_remain)
1148 {
1149         int cnt = 0;
1150         SMB_STRUCT_DIR *dirp;
1151         char *dname;
1152         pstring connect_path;
1153         char *service_name = lp_servicename(snum);
1154         connection_struct conn;
1155         struct referral *ref = NULL;
1156  
1157         ZERO_STRUCT(conn);
1158
1159         if (jn_remain <= 0) {
1160                 return 0;
1161         }
1162
1163         pstrcpy(connect_path,lp_pathname(snum));
1164
1165         if(*connect_path == '\0') {
1166                 return 0;
1167         }
1168
1169         /*
1170          * Fake up a connection struct for the VFS layer.
1171          */
1172
1173         if (!NT_STATUS_IS_OK(create_conn_struct(&conn, snum, connect_path))) {
1174                 return 0;
1175         }
1176
1177         /* form a junction for the msdfs root - convention 
1178            DO NOT REMOVE THIS: NT clients will not work with us
1179            if this is not present
1180         */ 
1181         fstrcpy(jucn[cnt].service_name, service_name);
1182         jucn[cnt].volume_name[0] = '\0';
1183         jucn[cnt].referral_count = 1;
1184
1185         ref = jucn[cnt].referral_list = TALLOC_ZERO_P(ctx, struct referral);
1186         if (jucn[cnt].referral_list == NULL) {
1187                 DEBUG(0, ("talloc failed!\n"));
1188                 goto out;
1189         }
1190
1191         ref->proximity = 0;
1192         ref->ttl = REFERRAL_TTL;
1193         if (*lp_msdfs_proxy(snum) != '\0') {
1194                 pstrcpy(ref->alternate_path, lp_msdfs_proxy(snum));
1195                 cnt++;
1196                 goto out;
1197         }
1198
1199         pstr_sprintf(ref->alternate_path, "\\\\%s\\%s",
1200                         get_local_machine_name(),
1201                         service_name);
1202         cnt++;
1203
1204         /* Now enumerate all dfs links */
1205         dirp = SMB_VFS_OPENDIR(&conn, ".", NULL, 0);
1206         if(!dirp) {
1207                 goto out;
1208         }
1209
1210         while ((dname = vfs_readdirname(&conn, dirp)) != NULL) {
1211                 pstring link_target;
1212                 if (cnt >= jn_remain) {
1213                         SMB_VFS_CLOSEDIR(&conn,dirp);
1214                         DEBUG(2, ("ran out of MSDFS junction slots"));
1215                         goto out;
1216                 }
1217                 if (is_msdfs_link(&conn, dname, link_target, NULL)) {
1218                         if (parse_msdfs_symlink(ctx,
1219                                         link_target,
1220                                         &jucn[cnt].referral_list,
1221                                         &jucn[cnt].referral_count)) {
1222
1223                                 fstrcpy(jucn[cnt].service_name, service_name);
1224                                 pstrcpy(jucn[cnt].volume_name, dname);
1225                                 cnt++;
1226                         }
1227                 }
1228         }
1229         
1230         SMB_VFS_CLOSEDIR(&conn,dirp);
1231
1232 out:
1233
1234         conn_free_internal(&conn);
1235         return cnt;
1236 }
1237
1238 int enum_msdfs_links(TALLOC_CTX *ctx, struct junction_map *jucn, int jn_max)
1239 {
1240         int i=0;
1241         int sharecount = 0;
1242         int jn_count = 0;
1243
1244         if(!lp_host_msdfs()) {
1245                 return 0;
1246         }
1247
1248         /* Ensure all the usershares are loaded. */
1249         become_root();
1250         load_registry_shares();
1251         sharecount = load_usershare_shares();
1252         unbecome_root();
1253
1254         for(i=0;i < sharecount && (jn_max - jn_count) > 0;i++) {
1255                 if(lp_msdfs_root(i)) {
1256                         jn_count += form_junctions(ctx, i,jucn,jn_max - jn_count);
1257                 }
1258         }
1259         return jn_count;
1260 }
1261
1262 /******************************************************************************
1263  Core function to resolve a dfs pathname.
1264 ******************************************************************************/
1265
1266 NTSTATUS resolve_dfspath(connection_struct *conn, BOOL dfs_pathnames, pstring name)
1267 {
1268         NTSTATUS status = NT_STATUS_OK;
1269         BOOL dummy;
1270         if (dfs_pathnames) {
1271                 status = dfs_redirect(conn, name, False, &dummy);
1272         }
1273         return status;
1274 }
1275
1276 /******************************************************************************
1277  Core function to resolve a dfs pathname possibly containing a wildcard.
1278  This function is identical to the above except for the BOOL param to
1279  dfs_redirect but I need this to be separate so it's really clear when
1280  we're allowing wildcards and when we're not. JRA.
1281 ******************************************************************************/
1282
1283 NTSTATUS resolve_dfspath_wcard(connection_struct *conn, BOOL dfs_pathnames, pstring name, BOOL *ppath_contains_wcard)
1284 {
1285         NTSTATUS status = NT_STATUS_OK;
1286         if (dfs_pathnames) {
1287                 status = dfs_redirect(conn, name, True, ppath_contains_wcard);
1288         }
1289         return status;
1290 }