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