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