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