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