r24253: From Jan Martin <Jan.Martin@rwedea.com>.
[nivanova/samba-autobuild/.git] / source3 / smbd / msdfs.c
1 /* 
2    Unix SMB/Netbios implementation.
3    Version 3.0
4    MSDFS services for Samba
5    Copyright (C) Shirish Kalele 2000
6    Copyright (C) Jeremy Allison 2007
7
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 3 of the License, or
11    (at your option) any later version.
12    
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17    
18    You should have received a copy of the GNU General Public License
19    along with this program.  If not, see <http://www.gnu.org/licenses/>.
20
21 */
22
23 #define DBGC_CLASS DBGC_MSDFS
24 #include "includes.h"
25
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          * localpath comes out of unix_convert, so it has
426          * no trailing backslash. Make sure that canon_dfspath hasn't either.
427          * Fix for bug #4860 from Jan Martin <Jan.Martin@rwedea.com>.
428          */
429
430         trim_char(canon_dfspath,0,'/');
431
432         /*
433          * Redirect if any component in the path is a link.
434          * We do this by walking backwards through the 
435          * local path, chopping off the last component
436          * in both the local path and the canonicalized
437          * DFS path. If we hit a DFS link then we're done.
438          */
439
440         p = strrchr_m(localpath, '/');
441         if (consumedcntp) {
442                 q = strrchr_m(canon_dfspath, '/');
443         }
444
445         while (p) {
446                 *p = '\0';
447                 if (q) {
448                         *q = '\0';
449                 }
450
451                 if (is_msdfs_link(conn, localpath, targetpath, NULL)) {
452                         DEBUG(4, ("dfs_path_lookup: Redirecting %s because "
453                                 "parent %s is dfs link\n", dfspath, localpath));
454
455                         if (consumedcntp) {
456                                 *consumedcntp = strlen(canon_dfspath);
457                                 DEBUG(10, ("dfs_path_lookup: Path consumed: %s "
458                                         "(%d)\n", canon_dfspath, *consumedcntp));
459                         }
460
461                         return NT_STATUS_PATH_NOT_COVERED;
462                 }
463
464                 /* Step back on the filesystem. */
465                 p = strrchr_m(localpath, '/');
466
467                 if (consumedcntp) {
468                         /* And in the canonicalized dfs path. */
469                         q = strrchr_m(canon_dfspath, '/');
470                 }
471         }
472
473         return NT_STATUS_OK;
474 }
475
476 /*****************************************************************
477  Decides if a dfs pathname should be redirected or not.
478  If not, the pathname is converted to a tcon-relative local unix path
479
480  search_wcard_flag: this flag performs 2 functions bother related
481  to searches.  See resolve_dfs_path() and parse_dfs_path_XX()
482  for details.
483
484  This function can return NT_STATUS_OK, meaning use the returned path as-is
485  (mapped into a local path).
486  or NT_STATUS_NOT_COVERED meaning return a DFS redirect, or
487  any other NT_STATUS error which is a genuine error to be
488  returned to the client. 
489 *****************************************************************/
490
491 static NTSTATUS dfs_redirect( connection_struct *conn,
492                         pstring dfs_path,
493                         BOOL search_wcard_flag,
494                         BOOL *ppath_contains_wcard)
495 {
496         NTSTATUS status;
497         struct dfs_path dp;
498         pstring targetpath;
499         
500         status = parse_dfs_path(dfs_path, search_wcard_flag, &dp, ppath_contains_wcard);
501         if (!NT_STATUS_IS_OK(status)) {
502                 return status;
503         }
504
505         if (dp.reqpath[0] == '\0') {
506                 pstrcpy(dfs_path, dp.reqpath);
507                 DEBUG(5,("dfs_redirect: self-referral.\n"));
508                 return NT_STATUS_OK;
509         }
510
511         /* If dfs pathname for a non-dfs share, convert to tcon-relative
512            path and return OK */
513
514         if (!lp_msdfs_root(SNUM(conn))) {
515                 pstrcpy(dfs_path, dp.reqpath);
516                 return NT_STATUS_OK;
517         }
518
519         /* If it looked like a local path (zero hostname/servicename)
520          * just treat as a tcon-relative path. */ 
521
522         if (dp.hostname[0] == '\0' && dp.servicename[0] == '\0') { 
523                 pstrcpy(dfs_path, dp.reqpath);
524                 return NT_STATUS_OK;
525         }
526
527         if (!( strequal(dp.servicename, lp_servicename(SNUM(conn))) 
528                         || (strequal(dp.servicename, HOMES_NAME) 
529                         && strequal(lp_servicename(SNUM(conn)), get_current_username()) )) ) {
530
531                 /* The given sharename doesn't match this connection. */
532
533                 return NT_STATUS_OBJECT_PATH_NOT_FOUND;
534         }
535
536         status = dfs_path_lookup(conn, dfs_path, &dp,
537                         search_wcard_flag, NULL, targetpath);
538         if (!NT_STATUS_IS_OK(status)) {
539                 if (NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
540                         DEBUG(3,("dfs_redirect: Redirecting %s\n", dfs_path));
541                 } else {
542                         DEBUG(10,("dfs_redirect: dfs_path_lookup failed for %s with %s\n",
543                                 dfs_path, nt_errstr(status) ));
544                 }
545                 return status;
546         }
547
548         DEBUG(3,("dfs_redirect: Not redirecting %s.\n", dfs_path));
549
550         /* Form non-dfs tcon-relative path */
551         pstrcpy(dfs_path, dp.reqpath);
552
553         DEBUG(3,("dfs_redirect: Path converted to non-dfs path %s\n", dfs_path));
554         return NT_STATUS_OK;
555 }
556
557 /**********************************************************************
558  Return a self referral.
559 **********************************************************************/
560
561 static NTSTATUS self_ref(TALLOC_CTX *ctx,
562                         const char *dfs_path,
563                         struct junction_map *jucn,
564                         int *consumedcntp,
565                         BOOL *self_referralp)
566 {
567         struct referral *ref;
568
569         *self_referralp = True;
570
571         jucn->referral_count = 1;
572         if((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) {
573                 DEBUG(0,("self_ref: talloc failed for referral\n"));
574                 return NT_STATUS_NO_MEMORY;
575         }
576
577         pstrcpy(ref->alternate_path,dfs_path);
578         ref->proximity = 0;
579         ref->ttl = REFERRAL_TTL;
580         jucn->referral_list = ref;
581         *consumedcntp = strlen(dfs_path);
582         return NT_STATUS_OK;
583 }
584
585 /**********************************************************************
586  Gets valid referrals for a dfs path and fills up the
587  junction_map structure.
588 **********************************************************************/
589
590 NTSTATUS get_referred_path(TALLOC_CTX *ctx,
591                         const char *dfs_path,
592                         struct junction_map *jucn,
593                         int *consumedcntp,
594                         BOOL *self_referralp)
595 {
596         struct connection_struct conns;
597         struct connection_struct *conn = &conns;
598         struct dfs_path dp;
599         pstring conn_path;
600         pstring targetpath;
601         int snum;
602         NTSTATUS status = NT_STATUS_NOT_FOUND;
603         BOOL dummy;
604
605         ZERO_STRUCT(conns);
606
607         *self_referralp = False;
608
609         status = parse_dfs_path(dfs_path, False, &dp, &dummy);
610         if (!NT_STATUS_IS_OK(status)) {
611                 return status;
612         }
613
614         /* Verify hostname in path */
615         if (!is_myname_or_ipaddr(dp.hostname)) {
616                 DEBUG(3, ("get_referred_path: Invalid hostname %s in path %s\n",
617                         dp.hostname, dfs_path));
618                 return NT_STATUS_NOT_FOUND;
619         }
620
621         fstrcpy(jucn->service_name, dp.servicename);
622         pstrcpy(jucn->volume_name, dp.reqpath);
623
624         /* Verify the share is a dfs root */
625         snum = lp_servicenumber(jucn->service_name);
626         if(snum < 0) {
627                 if ((snum = find_service(jucn->service_name)) < 0) {
628                         return NT_STATUS_NOT_FOUND;
629                 }
630         }
631
632         if (!lp_msdfs_root(snum)) {
633                 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not a dfs root.\n",
634                          dp.servicename, dfs_path));
635                 return NT_STATUS_NOT_FOUND;
636         }
637
638         /*
639          * Self referrals are tested with a anonymous IPC connection and
640          * a GET_DFS_REFERRAL call to \\server\share. (which means dp.reqpath[0] points
641          * to an empty string). create_conn_struct cd's into the directory and will
642          * fail if it cannot (as the anonymous user). Cope with this.
643          */
644
645         if (dp.reqpath[0] == '\0') {
646                 struct referral *ref;
647
648                 if (*lp_msdfs_proxy(snum) == '\0') {
649                         return self_ref(ctx,
650                                         dfs_path,
651                                         jucn,
652                                         consumedcntp,
653                                         self_referralp);
654                 }
655
656                 /* 
657                  * It's an msdfs proxy share. Redirect to
658                  * the configured target share.
659                  */
660
661                 jucn->referral_count = 1;
662                 if ((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) {
663                         DEBUG(0, ("malloc failed for referral\n"));
664                         return NT_STATUS_NO_MEMORY;
665                 }
666
667                 pstrcpy(ref->alternate_path, lp_msdfs_proxy(snum));
668                 if (dp.reqpath[0] != '\0') {
669                         pstrcat(ref->alternate_path, dp.reqpath);
670                 }
671                 ref->proximity = 0;
672                 ref->ttl = REFERRAL_TTL;
673                 jucn->referral_list = ref;
674                 *consumedcntp = strlen(dfs_path);
675                 return NT_STATUS_OK;
676         }
677
678         pstrcpy(conn_path, lp_pathname(snum));
679         status = create_conn_struct(conn, snum, conn_path);
680         if (!NT_STATUS_IS_OK(status)) {
681                 return status;
682         }
683
684         /* If this is a DFS path dfs_lookup should return
685          * NT_STATUS_PATH_NOT_COVERED. */
686
687         status = dfs_path_lookup(conn, dfs_path, &dp,
688                         False, consumedcntp, targetpath);
689
690         if (!NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
691                 DEBUG(3,("get_referred_path: No valid referrals for path %s\n",
692                         dfs_path));
693                 conn_free_internal(conn);
694                 return status;
695         }
696
697         /* We know this is a valid dfs link. Parse the targetpath. */
698         if (!parse_msdfs_symlink(ctx, targetpath,
699                                 &jucn->referral_list,
700                                 &jucn->referral_count)) {
701                 DEBUG(3,("get_referred_path: failed to parse symlink "
702                         "target %s\n", targetpath ));
703                 conn_free_internal(conn);
704                 return NT_STATUS_NOT_FOUND;
705         }
706
707         conn_free_internal(conn);
708         return NT_STATUS_OK;
709 }
710
711 static int setup_ver2_dfs_referral(const char *pathname,
712                                 char **ppdata, 
713                                 struct junction_map *junction,
714                                 int consumedcnt,
715                                 BOOL self_referral)
716 {
717         char* pdata = *ppdata;
718
719         unsigned char uni_requestedpath[1024];
720         int uni_reqpathoffset1,uni_reqpathoffset2;
721         int uni_curroffset;
722         int requestedpathlen=0;
723         int offset;
724         int reply_size = 0;
725         int i=0;
726
727         DEBUG(10,("Setting up version2 referral\nRequested path:\n"));
728
729         requestedpathlen = rpcstr_push(uni_requestedpath, pathname, sizeof(pstring),
730                                        STR_TERMINATE);
731
732         if (DEBUGLVL(10)) {
733             dump_data(0, uni_requestedpath,requestedpathlen);
734         }
735
736         DEBUG(10,("ref count = %u\n",junction->referral_count));
737
738         uni_reqpathoffset1 = REFERRAL_HEADER_SIZE + 
739                         VERSION2_REFERRAL_SIZE * junction->referral_count;
740
741         uni_reqpathoffset2 = uni_reqpathoffset1 + requestedpathlen;
742
743         uni_curroffset = uni_reqpathoffset2 + requestedpathlen;
744
745         reply_size = REFERRAL_HEADER_SIZE + VERSION2_REFERRAL_SIZE*junction->referral_count +
746                                         2 * requestedpathlen;
747         DEBUG(10,("reply_size: %u\n",reply_size));
748
749         /* add up the unicode lengths of all the referral paths */
750         for(i=0;i<junction->referral_count;i++) {
751                 DEBUG(10,("referral %u : %s\n",i,junction->referral_list[i].alternate_path));
752                 reply_size += (strlen(junction->referral_list[i].alternate_path)+1)*2;
753         }
754
755         DEBUG(10,("reply_size = %u\n",reply_size));
756         /* add the unexplained 0x16 bytes */
757         reply_size += 0x16;
758
759         pdata = (char *)SMB_REALLOC(pdata,reply_size);
760         if(pdata == NULL) {
761                 DEBUG(0,("Realloc failed!\n"));
762                 return -1;
763         }
764         *ppdata = pdata;
765
766         /* copy in the dfs requested paths.. required for offset calculations */
767         memcpy(pdata+uni_reqpathoffset1,uni_requestedpath,requestedpathlen);
768         memcpy(pdata+uni_reqpathoffset2,uni_requestedpath,requestedpathlen);
769
770         /* create the header */
771         SSVAL(pdata,0,consumedcnt * 2); /* path consumed */
772         SSVAL(pdata,2,junction->referral_count); /* number of referral in this pkt */
773         if(self_referral) {
774                 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER); 
775         } else {
776                 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
777         }
778
779         offset = 8;
780         /* add the referral elements */
781         for(i=0;i<junction->referral_count;i++) {
782                 struct referral* ref = &junction->referral_list[i];
783                 int unilen;
784
785                 SSVAL(pdata,offset,2); /* version 2 */
786                 SSVAL(pdata,offset+2,VERSION2_REFERRAL_SIZE);
787                 if(self_referral) {
788                         SSVAL(pdata,offset+4,1);
789                 } else {
790                         SSVAL(pdata,offset+4,0);
791                 }
792                 SSVAL(pdata,offset+6,0); /* ref_flags :use path_consumed bytes? */
793                 SIVAL(pdata,offset+8,ref->proximity);
794                 SIVAL(pdata,offset+12,ref->ttl);
795
796                 SSVAL(pdata,offset+16,uni_reqpathoffset1-offset);
797                 SSVAL(pdata,offset+18,uni_reqpathoffset2-offset);
798                 /* copy referred path into current offset */
799                 unilen = rpcstr_push(pdata+uni_curroffset, ref->alternate_path,
800                                      sizeof(pstring), STR_UNICODE);
801
802                 SSVAL(pdata,offset+20,uni_curroffset-offset);
803
804                 uni_curroffset += unilen;
805                 offset += VERSION2_REFERRAL_SIZE;
806         }
807         /* add in the unexplained 22 (0x16) bytes at the end */
808         memset(pdata+uni_curroffset,'\0',0x16);
809         return reply_size;
810 }
811
812 static int setup_ver3_dfs_referral(const char *pathname,
813                                 char **ppdata, 
814                                 struct junction_map *junction,
815                                 int consumedcnt,
816                                 BOOL self_referral)
817 {
818         char* pdata = *ppdata;
819
820         unsigned char uni_reqpath[1024];
821         int uni_reqpathoffset1, uni_reqpathoffset2;
822         int uni_curroffset;
823         int reply_size = 0;
824
825         int reqpathlen = 0;
826         int offset,i=0;
827         
828         DEBUG(10,("setting up version3 referral\n"));
829
830         reqpathlen = rpcstr_push(uni_reqpath, pathname, sizeof(pstring), STR_TERMINATE);
831         
832         if (DEBUGLVL(10)) {
833             dump_data(0, uni_reqpath,reqpathlen);
834         }
835
836         uni_reqpathoffset1 = REFERRAL_HEADER_SIZE + VERSION3_REFERRAL_SIZE * junction->referral_count;
837         uni_reqpathoffset2 = uni_reqpathoffset1 + reqpathlen;
838         reply_size = uni_curroffset = uni_reqpathoffset2 + reqpathlen;
839
840         for(i=0;i<junction->referral_count;i++) {
841                 DEBUG(10,("referral %u : %s\n",i,junction->referral_list[i].alternate_path));
842                 reply_size += (strlen(junction->referral_list[i].alternate_path)+1)*2;
843         }
844
845         pdata = (char *)SMB_REALLOC(pdata,reply_size);
846         if(pdata == NULL) {
847                 DEBUG(0,("version3 referral setup: malloc failed for Realloc!\n"));
848                 return -1;
849         }
850         *ppdata = pdata;
851
852         /* create the header */
853         SSVAL(pdata,0,consumedcnt * 2); /* path consumed */
854         SSVAL(pdata,2,junction->referral_count); /* number of referral */
855         if(self_referral) {
856                 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER); 
857         } else {
858                 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
859         }
860         
861         /* copy in the reqpaths */
862         memcpy(pdata+uni_reqpathoffset1,uni_reqpath,reqpathlen);
863         memcpy(pdata+uni_reqpathoffset2,uni_reqpath,reqpathlen);
864         
865         offset = 8;
866         for(i=0;i<junction->referral_count;i++) {
867                 struct referral* ref = &(junction->referral_list[i]);
868                 int unilen;
869
870                 SSVAL(pdata,offset,3); /* version 3 */
871                 SSVAL(pdata,offset+2,VERSION3_REFERRAL_SIZE);
872                 if(self_referral) {
873                         SSVAL(pdata,offset+4,1);
874                 } else {
875                         SSVAL(pdata,offset+4,0);
876                 }
877
878                 SSVAL(pdata,offset+6,0); /* ref_flags :use path_consumed bytes? */
879                 SIVAL(pdata,offset+8,ref->ttl);
880             
881                 SSVAL(pdata,offset+12,uni_reqpathoffset1-offset);
882                 SSVAL(pdata,offset+14,uni_reqpathoffset2-offset);
883                 /* copy referred path into current offset */
884                 unilen = rpcstr_push(pdata+uni_curroffset,ref->alternate_path,
885                                      sizeof(pstring), STR_UNICODE | STR_TERMINATE);
886                 SSVAL(pdata,offset+16,uni_curroffset-offset);
887                 /* copy 0x10 bytes of 00's in the ServiceSite GUID */
888                 memset(pdata+offset+18,'\0',16);
889
890                 uni_curroffset += unilen;
891                 offset += VERSION3_REFERRAL_SIZE;
892         }
893         return reply_size;
894 }
895
896 /******************************************************************
897  Set up the DFS referral for the dfs pathname. This call returns
898  the amount of the path covered by this server, and where the
899  client should be redirected to. This is the meat of the
900  TRANS2_GET_DFS_REFERRAL call.
901 ******************************************************************/
902
903 int setup_dfs_referral(connection_struct *orig_conn,
904                         const char *dfs_path,
905                         int max_referral_level,
906                         char **ppdata, NTSTATUS *pstatus)
907 {
908         struct junction_map junction;
909         int consumedcnt = 0;
910         BOOL self_referral = False;
911         int reply_size = 0;
912         char *pathnamep = NULL;
913         pstring local_dfs_path;
914         TALLOC_CTX *ctx;
915
916         if (!(ctx=talloc_init("setup_dfs_referral"))) {
917                 *pstatus = NT_STATUS_NO_MEMORY;
918                 return -1;
919         }
920
921         ZERO_STRUCT(junction);
922
923         /* get the junction entry */
924         if (!dfs_path) {
925                 talloc_destroy(ctx);
926                 *pstatus = NT_STATUS_NOT_FOUND;
927                 return -1;
928         }
929
930         /* 
931          * Trim pathname sent by client so it begins with only one backslash.
932          * Two backslashes confuse some dfs clients
933          */
934
935         pstrcpy(local_dfs_path, dfs_path);
936         pathnamep = local_dfs_path;
937         while (IS_DIRECTORY_SEP(pathnamep[0]) && IS_DIRECTORY_SEP(pathnamep[1])) {
938                 pathnamep++;
939         }
940
941         /* The following call can change cwd. */
942         *pstatus = get_referred_path(ctx, pathnamep, &junction, &consumedcnt, &self_referral);
943         if (!NT_STATUS_IS_OK(*pstatus)) {
944                 vfs_ChDir(orig_conn,orig_conn->connectpath);
945                 talloc_destroy(ctx);
946                 return -1;
947         }
948         vfs_ChDir(orig_conn,orig_conn->connectpath);
949         
950         if (!self_referral) {
951                 pathnamep[consumedcnt] = '\0';
952
953                 if( DEBUGLVL( 3 ) ) {
954                         int i=0;
955                         dbgtext("setup_dfs_referral: Path %s to alternate path(s):",pathnamep);
956                         for(i=0;i<junction.referral_count;i++)
957                                 dbgtext(" %s",junction.referral_list[i].alternate_path);
958                         dbgtext(".\n");
959                 }
960         }
961
962         /* create the referral depeding on version */
963         DEBUG(10,("max_referral_level :%d\n",max_referral_level));
964
965         if (max_referral_level < 2) {
966                 max_referral_level = 2;
967         }
968         if (max_referral_level > 3) {
969                 max_referral_level = 3;
970         }
971
972         switch(max_referral_level) {
973         case 2:
974                 reply_size = setup_ver2_dfs_referral(pathnamep, ppdata, &junction, 
975                                                      consumedcnt, self_referral);
976                 break;
977         case 3:
978                 reply_size = setup_ver3_dfs_referral(pathnamep, ppdata, &junction, 
979                                                      consumedcnt, self_referral);
980                 break;
981         default:
982                 DEBUG(0,("setup_dfs_referral: Invalid dfs referral version: %d\n", max_referral_level));
983                 talloc_destroy(ctx);
984                 *pstatus = NT_STATUS_INVALID_LEVEL;
985                 return -1;
986         }
987       
988         if (DEBUGLVL(10)) {
989                 DEBUGADD(0,("DFS Referral pdata:\n"));
990                 dump_data(0,(uint8 *)*ppdata,reply_size);
991         }
992
993         talloc_destroy(ctx);
994         *pstatus = NT_STATUS_OK;
995         return reply_size;
996 }
997
998 /**********************************************************************
999  The following functions are called by the NETDFS RPC pipe functions
1000  **********************************************************************/
1001
1002 /*********************************************************************
1003  Creates a junction structure from a DFS pathname
1004 **********************************************************************/
1005
1006 BOOL create_junction(const char *dfs_path, struct junction_map *jucn)
1007 {
1008         int snum;
1009         BOOL dummy;
1010         struct dfs_path dp;
1011  
1012         NTSTATUS status = parse_dfs_path(dfs_path, False, &dp, &dummy);
1013
1014         if (!NT_STATUS_IS_OK(status)) {
1015                 return False;
1016         }
1017
1018         /* check if path is dfs : validate first token */
1019         if (!is_myname_or_ipaddr(dp.hostname)) {
1020                 DEBUG(4,("create_junction: Invalid hostname %s in dfs path %s\n",
1021                         dp.hostname, dfs_path));
1022                 return False;
1023         }
1024
1025         /* Check for a non-DFS share */
1026         snum = lp_servicenumber(dp.servicename);
1027
1028         if(snum < 0 || !lp_msdfs_root(snum)) {
1029                 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1030                         dp.servicename));
1031                 return False;
1032         }
1033
1034         fstrcpy(jucn->service_name,dp.servicename);
1035         pstrcpy(jucn->volume_name,dp.reqpath);
1036         pstrcpy(jucn->comment, lp_comment(snum));
1037         return True;
1038 }
1039
1040 /**********************************************************************
1041  Forms a valid Unix pathname from the junction 
1042  **********************************************************************/
1043
1044 static BOOL junction_to_local_path(struct junction_map *jucn,
1045                                 char *path,
1046                                 int max_pathlen,
1047                                 connection_struct *conn_out)
1048 {
1049         int snum;
1050         pstring conn_path;
1051
1052         snum = lp_servicenumber(jucn->service_name);
1053         if(snum < 0) {
1054                 return False;
1055         }
1056
1057         safe_strcpy(path, lp_pathname(snum), max_pathlen-1);
1058         safe_strcat(path, "/", max_pathlen-1);
1059         safe_strcat(path, jucn->volume_name, max_pathlen-1);
1060
1061         pstrcpy(conn_path, lp_pathname(snum));
1062         if (!NT_STATUS_IS_OK(create_conn_struct(conn_out, snum, conn_path))) {
1063                 return False;
1064         }
1065
1066         return True;
1067 }
1068
1069 BOOL create_msdfs_link(struct junction_map *jucn, BOOL exists)
1070 {
1071         pstring path;
1072         pstring msdfs_link;
1073         connection_struct conns;
1074         connection_struct *conn = &conns;
1075         int i=0;
1076         BOOL insert_comma = False;
1077         BOOL ret = False;
1078
1079         ZERO_STRUCT(conns);
1080
1081         if(!junction_to_local_path(jucn, path, sizeof(path), conn)) {
1082                 return False;
1083         }
1084   
1085         /* Form the msdfs_link contents */
1086         pstrcpy(msdfs_link, "msdfs:");
1087         for(i=0; i<jucn->referral_count; i++) {
1088                 char* refpath = jucn->referral_list[i].alternate_path;
1089       
1090                 /* Alternate paths always use Windows separators. */
1091                 trim_char(refpath, '\\', '\\');
1092                 if(*refpath == '\0') {
1093                         if (i == 0) {
1094                                 insert_comma = False;
1095                         }
1096                         continue;
1097                 }
1098                 if (i > 0 && insert_comma) {
1099                         pstrcat(msdfs_link, ",");
1100                 }
1101
1102                 pstrcat(msdfs_link, refpath);
1103                 if (!insert_comma) {
1104                         insert_comma = True;
1105                 }
1106         }
1107
1108         DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
1109                 path, msdfs_link));
1110
1111         if(exists) {
1112                 if(SMB_VFS_UNLINK(conn,path)!=0) {
1113                         goto out;
1114                 }
1115         }
1116
1117         if(SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
1118                 DEBUG(1,("create_msdfs_link: symlink failed %s -> %s\nError: %s\n", 
1119                                 path, msdfs_link, strerror(errno)));
1120                 goto out;
1121         }
1122         
1123         
1124         ret = True;
1125
1126 out:
1127
1128         conn_free_internal(conn);
1129         return ret;
1130 }
1131
1132 BOOL remove_msdfs_link(struct junction_map *jucn)
1133 {
1134         pstring path;
1135         connection_struct conns;
1136         connection_struct *conn = &conns;
1137         BOOL ret = False;
1138
1139         ZERO_STRUCT(conns);
1140
1141         if( junction_to_local_path(jucn, path, sizeof(path), conn) ) {
1142                 if( SMB_VFS_UNLINK(conn, path) == 0 ) {
1143                         ret = True;
1144                 }
1145                 talloc_destroy( conn->mem_ctx );
1146         }
1147
1148         conn_free_internal(conn);
1149         return ret;
1150 }
1151
1152 static int form_junctions(TALLOC_CTX *ctx,
1153                                 int snum,
1154                                 struct junction_map *jucn,
1155                                 int jn_remain)
1156 {
1157         int cnt = 0;
1158         SMB_STRUCT_DIR *dirp;
1159         char *dname;
1160         pstring connect_path;
1161         char *service_name = lp_servicename(snum);
1162         connection_struct conn;
1163         struct referral *ref = NULL;
1164  
1165         ZERO_STRUCT(conn);
1166
1167         if (jn_remain <= 0) {
1168                 return 0;
1169         }
1170
1171         pstrcpy(connect_path,lp_pathname(snum));
1172
1173         if(*connect_path == '\0') {
1174                 return 0;
1175         }
1176
1177         /*
1178          * Fake up a connection struct for the VFS layer.
1179          */
1180
1181         if (!NT_STATUS_IS_OK(create_conn_struct(&conn, snum, connect_path))) {
1182                 return 0;
1183         }
1184
1185         /* form a junction for the msdfs root - convention 
1186            DO NOT REMOVE THIS: NT clients will not work with us
1187            if this is not present
1188         */ 
1189         fstrcpy(jucn[cnt].service_name, service_name);
1190         jucn[cnt].volume_name[0] = '\0';
1191         jucn[cnt].referral_count = 1;
1192
1193         ref = jucn[cnt].referral_list = TALLOC_ZERO_P(ctx, struct referral);
1194         if (jucn[cnt].referral_list == NULL) {
1195                 DEBUG(0, ("talloc failed!\n"));
1196                 goto out;
1197         }
1198
1199         ref->proximity = 0;
1200         ref->ttl = REFERRAL_TTL;
1201         if (*lp_msdfs_proxy(snum) != '\0') {
1202                 pstrcpy(ref->alternate_path, lp_msdfs_proxy(snum));
1203                 cnt++;
1204                 goto out;
1205         }
1206
1207         pstr_sprintf(ref->alternate_path, "\\\\%s\\%s",
1208                         get_local_machine_name(),
1209                         service_name);
1210         cnt++;
1211
1212         /* Now enumerate all dfs links */
1213         dirp = SMB_VFS_OPENDIR(&conn, ".", NULL, 0);
1214         if(!dirp) {
1215                 goto out;
1216         }
1217
1218         while ((dname = vfs_readdirname(&conn, dirp)) != NULL) {
1219                 pstring link_target;
1220                 if (cnt >= jn_remain) {
1221                         SMB_VFS_CLOSEDIR(&conn,dirp);
1222                         DEBUG(2, ("ran out of MSDFS junction slots"));
1223                         goto out;
1224                 }
1225                 if (is_msdfs_link(&conn, dname, link_target, NULL)) {
1226                         if (parse_msdfs_symlink(ctx,
1227                                         link_target,
1228                                         &jucn[cnt].referral_list,
1229                                         &jucn[cnt].referral_count)) {
1230
1231                                 fstrcpy(jucn[cnt].service_name, service_name);
1232                                 pstrcpy(jucn[cnt].volume_name, dname);
1233                                 cnt++;
1234                         }
1235                 }
1236         }
1237         
1238         SMB_VFS_CLOSEDIR(&conn,dirp);
1239
1240 out:
1241
1242         conn_free_internal(&conn);
1243         return cnt;
1244 }
1245
1246 int enum_msdfs_links(TALLOC_CTX *ctx, struct junction_map *jucn, int jn_max)
1247 {
1248         int i=0;
1249         int sharecount = 0;
1250         int jn_count = 0;
1251
1252         if(!lp_host_msdfs()) {
1253                 return 0;
1254         }
1255
1256         /* Ensure all the usershares are loaded. */
1257         become_root();
1258         load_registry_shares();
1259         sharecount = load_usershare_shares();
1260         unbecome_root();
1261
1262         for(i=0;i < sharecount && (jn_max - jn_count) > 0;i++) {
1263                 if(lp_msdfs_root(i)) {
1264                         jn_count += form_junctions(ctx, i,jucn,jn_max - jn_count);
1265                 }
1266         }
1267         return jn_count;
1268 }
1269
1270 /******************************************************************************
1271  Core function to resolve a dfs pathname.
1272 ******************************************************************************/
1273
1274 NTSTATUS resolve_dfspath(connection_struct *conn, BOOL dfs_pathnames, pstring name)
1275 {
1276         NTSTATUS status = NT_STATUS_OK;
1277         BOOL dummy;
1278         if (dfs_pathnames) {
1279                 status = dfs_redirect(conn, name, False, &dummy);
1280         }
1281         return status;
1282 }
1283
1284 /******************************************************************************
1285  Core function to resolve a dfs pathname possibly containing a wildcard.
1286  This function is identical to the above except for the BOOL param to
1287  dfs_redirect but I need this to be separate so it's really clear when
1288  we're allowing wildcards and when we're not. JRA.
1289 ******************************************************************************/
1290
1291 NTSTATUS resolve_dfspath_wcard(connection_struct *conn, BOOL dfs_pathnames, pstring name, BOOL *ppath_contains_wcard)
1292 {
1293         NTSTATUS status = NT_STATUS_OK;
1294         if (dfs_pathnames) {
1295                 status = dfs_redirect(conn, name, True, ppath_contains_wcard);
1296         }
1297         return status;
1298 }