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