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