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