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