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