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