r19963: Add 'registry shares = yes' and registry key security descriptors.
[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(const 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, const char *dfspath, 
321                                                          struct dfs_path *dp, 
322                       connection_struct *conn, BOOL search_flag, 
323                       struct referral **reflistpp, int *refcntp,
324                       BOOL *self_referralp, int *consumedcntp)
325 {
326         pstring localpath;
327         int consumed_level = 1;
328         char *p;
329         BOOL bad_path = False;
330         SMB_STRUCT_STAT sbuf;
331         pstring reqpath;
332
333         if (!dp || !conn) {
334                 DEBUG(1,("resolve_dfs_path: NULL dfs_path* or NULL connection_struct*!\n"));
335                 return False;
336         }
337
338         if (!ctx && (reflistpp || refcntp)) {
339                 DEBUG(0,("resolve_dfs_path: logic error. TALLOC_CTX must not be NULL.\n"));
340         }
341
342         if (dp->reqpath[0] == '\0') {
343                 if (self_referralp) {
344                         DEBUG(6,("resolve_dfs_path: self-referral. returning False\n"));
345                         *self_referralp = True;
346                 }
347                 return False;
348         }
349
350         DEBUG(10,("resolve_dfs_path: Conn path = %s req_path = %s\n", conn->connectpath, dp->reqpath));
351
352         unix_convert(dp->reqpath,conn,0,&bad_path,&sbuf);
353         /* JRA... should we strlower the last component here.... ? */
354         pstrcpy(localpath, dp->reqpath);
355
356         /* check if need to redirect */
357         if (is_msdfs_link(ctx, conn, localpath, reflistpp, refcntp, NULL)) {
358                 if ( search_flag ) {
359                         DEBUG(6,("resolve_dfs_path (FindFirst) No redirection "
360                                  "for dfs link %s.\n", dfspath));
361                         return False;
362                 }
363
364                 DEBUG(6,("resolve_dfs_path: %s resolves to a valid Dfs link.\n", dfspath));
365                 if (consumedcntp) {
366                         *consumedcntp = strlen(dfspath);
367                 }
368                 return True;
369         }
370
371         /* redirect if any component in the path is a link */
372         pstrcpy(reqpath, dp->reqpath);
373         p = strrchr_m(reqpath, '/');
374         while (p) {
375                 *p = '\0';
376                 pstrcpy(localpath, reqpath);
377                 if (is_msdfs_link(ctx, conn, localpath, reflistpp, refcntp, NULL)) {
378                         DEBUG(4, ("resolve_dfs_path: Redirecting %s because parent %s is dfs link\n", dfspath, localpath));
379
380                         /* To find the path consumed, we truncate the original
381                            DFS pathname passed to use to remove the last
382                            component. The length of the resulting string is
383                            the path consumed 
384                         */
385                         
386                         if (consumedcntp) {
387                                 char *q;
388                                 pstring buf;
389                                 pstrcpy(buf, dfspath);
390                                 trim_char(buf, '\0', '\\');
391                                 for (; consumed_level; consumed_level--) {
392                                         q = strrchr_m(buf, '\\');
393                                         if (q) {
394                                                 *q = 0;
395                                         }
396                                 }
397                                 *consumedcntp = strlen(buf);
398                                 DEBUG(10, ("resolve_dfs_path: Path consumed: %s (%d)\n", buf, *consumedcntp));
399                         }
400                         
401                         return True;
402                 }
403                 p = strrchr_m(reqpath, '/');
404                 consumed_level++;
405         }
406
407         return False;
408 }
409
410 /*****************************************************************
411   Decides if a dfs pathname should be redirected or not.
412   If not, the pathname is converted to a tcon-relative local unix path
413
414   search_wcard_flag: this flag performs 2 functions bother related
415   to searches.  See resolve_dfs_path() and parse_processed_dfs_path()
416   for details.
417 *****************************************************************/
418
419 BOOL dfs_redirect( pstring pathname, connection_struct *conn, BOOL search_wcard_flag )
420 {
421         struct dfs_path dp;
422         
423         if (!conn || !pathname) {
424                 return False;
425         }
426
427         parse_processed_dfs_path(pathname, &dp, search_wcard_flag);
428
429         /* if dfs pathname for a non-dfs share, convert to tcon-relative
430            path and return false */
431         if (!lp_msdfs_root(SNUM(conn))) {
432                 pstrcpy(pathname, dp.reqpath);
433                 return False;
434         }
435         
436         if ( !( strequal(dp.servicename, lp_servicename(SNUM(conn))) 
437                 || ( strequal(dp.servicename, HOMES_NAME) 
438                      && strequal(lp_servicename(SNUM(conn)), get_current_username()) )) ) 
439         {
440                 return False;
441         }
442
443         if (resolve_dfs_path(NULL, pathname, &dp, conn, search_wcard_flag,
444                              NULL, NULL, NULL, NULL)) {
445                 DEBUG(3,("dfs_redirect: Redirecting %s\n", pathname));
446                 return True;
447         } else {
448                 DEBUG(3,("dfs_redirect: Not redirecting %s.\n", pathname));
449                 
450                 /* Form non-dfs tcon-relative path */
451                 pstrcpy(pathname, dp.reqpath);
452                 DEBUG(3,("dfs_redirect: Path converted to non-dfs path %s\n", pathname));
453                 return False;
454         }
455
456         /* never reached */
457 }
458
459 /**********************************************************************
460  Return a self referral.
461 **********************************************************************/
462
463 static BOOL self_ref(TALLOC_CTX *ctx, const char *pathname, struct junction_map *jucn,
464                         int *consumedcntp, BOOL *self_referralp)
465 {
466         struct referral *ref;
467
468         if (self_referralp != NULL) {
469                 *self_referralp = True;
470         }
471
472         jucn->referral_count = 1;
473         if((ref = TALLOC_P(ctx, struct referral)) == NULL) {
474                 DEBUG(0,("self_ref: malloc failed for referral\n"));
475                 return False;
476         }
477
478         pstrcpy(ref->alternate_path,pathname);
479         ref->proximity = 0;
480         ref->ttl = REFERRAL_TTL;
481         jucn->referral_list = ref;
482         if (consumedcntp) {
483                 *consumedcntp = strlen(pathname);
484         }
485
486         return True;
487 }
488
489 /**********************************************************************
490  Gets valid referrals for a dfs path and fills up the
491  junction_map structure.
492 **********************************************************************/
493
494 BOOL get_referred_path(TALLOC_CTX *ctx, const char *pathname, struct junction_map *jucn,
495                        int *consumedcntp, BOOL *self_referralp)
496 {
497         struct dfs_path dp;
498
499         struct connection_struct conns;
500         struct connection_struct *conn = &conns;
501         pstring conn_path;
502         int snum;
503         BOOL ret = False;
504         BOOL self_referral = False;
505
506         if (!pathname || !jucn) {
507                 return False;
508         }
509
510         ZERO_STRUCT(conns);
511
512         if (self_referralp) {
513                 *self_referralp = False;
514         } else {
515                 self_referralp = &self_referral;
516         }
517
518         parse_dfs_path(pathname, &dp);
519
520         /* Verify hostname in path */
521         if (!is_myname_or_ipaddr(dp.hostname)) {
522                 DEBUG(3, ("get_referred_path: Invalid hostname %s in path %s\n",
523                         dp.hostname, pathname));
524                 return False;
525         }
526
527         pstrcpy(jucn->service_name, dp.servicename);
528         pstrcpy(jucn->volume_name, dp.reqpath);
529
530         /* Verify the share is a dfs root */
531         snum = lp_servicenumber(jucn->service_name);
532         if(snum < 0) {
533                 if ((snum = find_service(jucn->service_name)) < 0) {
534                         return False;
535                 }
536         }
537
538         if (!lp_msdfs_root(snum)) {
539                 DEBUG(3,("get_referred_path: .%s. in dfs path %s is not a dfs root.\n",
540                          dp.servicename, pathname));
541                 goto out;
542         }
543
544         /*
545          * Self referrals are tested with a anonymous IPC connection and
546          * a GET_DFS_REFERRAL call to \\server\share. (which means dp.reqpath[0] points
547          * to an empty string). create_conn_struct cd's into the directory and will
548          * fail if it cannot (as the anonymous user). Cope with this.
549          */
550
551         if (dp.reqpath[0] == '\0') {
552
553                 struct referral* ref;
554
555                 if (*lp_msdfs_proxy(snum) == '\0') {
556                         return self_ref(ctx, pathname, jucn, consumedcntp, self_referralp);
557                 }
558
559                 jucn->referral_count = 1;
560                 if ((ref = TALLOC_P(ctx, struct referral)) == NULL) {
561                         DEBUG(0, ("malloc failed for referral\n"));
562                         goto out;
563                 }
564
565                 pstrcpy(ref->alternate_path, lp_msdfs_proxy(snum));
566                 if (dp.reqpath[0] != '\0') {
567                         pstrcat(ref->alternate_path, dp.reqpath);
568                 }
569                 ref->proximity = 0;
570                 ref->ttl = REFERRAL_TTL;
571                 jucn->referral_list = ref;
572                 if (consumedcntp) {
573                         *consumedcntp = strlen(pathname);
574                 }
575                 ret = True;
576                 goto out;
577         }
578
579         pstrcpy(conn_path, lp_pathname(snum));
580         if (!create_conn_struct(conn, snum, conn_path)) {
581                 return False;
582         }
583
584         /* If not remote & not a self referral, return False */
585         if (!resolve_dfs_path(ctx, pathname, &dp, conn, False, 
586                               &jucn->referral_list, &jucn->referral_count,
587                               self_referralp, consumedcntp)) {
588                 if (!*self_referralp) {
589                         DEBUG(3,("get_referred_path: No valid referrals for path %s\n", pathname));
590                         goto out;
591                 }
592         }
593         
594         /* if self_referral, fill up the junction map */
595         if (*self_referralp) {
596                 if (self_ref(ctx, pathname, jucn, consumedcntp, self_referralp) == False) {
597                         goto out;
598                 }
599         }
600         
601         ret = True;
602
603 out:
604
605         conn_free_internal(conn);
606         return ret;
607 }
608
609 static int setup_ver2_dfs_referral(char *pathname, char **ppdata, 
610                                    struct junction_map *junction,
611                                    int consumedcnt,
612                                    BOOL self_referral)
613 {
614         char* pdata = *ppdata;
615
616         unsigned char uni_requestedpath[1024];
617         int uni_reqpathoffset1,uni_reqpathoffset2;
618         int uni_curroffset;
619         int requestedpathlen=0;
620         int offset;
621         int reply_size = 0;
622         int i=0;
623
624         DEBUG(10,("setting up version2 referral\nRequested path:\n"));
625
626         requestedpathlen = rpcstr_push(uni_requestedpath, pathname, sizeof(pstring),
627                                        STR_TERMINATE);
628
629         if (DEBUGLVL(10)) {
630             dump_data(0, (const char *) uni_requestedpath,requestedpathlen);
631         }
632
633         DEBUG(10,("ref count = %u\n",junction->referral_count));
634
635         uni_reqpathoffset1 = REFERRAL_HEADER_SIZE + 
636                         VERSION2_REFERRAL_SIZE * junction->referral_count;
637
638         uni_reqpathoffset2 = uni_reqpathoffset1 + requestedpathlen;
639
640         uni_curroffset = uni_reqpathoffset2 + requestedpathlen;
641
642         reply_size = REFERRAL_HEADER_SIZE + VERSION2_REFERRAL_SIZE*junction->referral_count +
643                                         2 * requestedpathlen;
644         DEBUG(10,("reply_size: %u\n",reply_size));
645
646         /* add up the unicode lengths of all the referral paths */
647         for(i=0;i<junction->referral_count;i++) {
648                 DEBUG(10,("referral %u : %s\n",i,junction->referral_list[i].alternate_path));
649                 reply_size += (strlen(junction->referral_list[i].alternate_path)+1)*2;
650         }
651
652         DEBUG(10,("reply_size = %u\n",reply_size));
653         /* add the unexplained 0x16 bytes */
654         reply_size += 0x16;
655
656         pdata = (char *)SMB_REALLOC(pdata,reply_size);
657         if(pdata == NULL) {
658                 DEBUG(0,("malloc failed for Realloc!\n"));
659                 return -1;
660         }
661         *ppdata = pdata;
662
663         /* copy in the dfs requested paths.. required for offset calculations */
664         memcpy(pdata+uni_reqpathoffset1,uni_requestedpath,requestedpathlen);
665         memcpy(pdata+uni_reqpathoffset2,uni_requestedpath,requestedpathlen);
666
667         /* create the header */
668         SSVAL(pdata,0,consumedcnt * 2); /* path consumed */
669         SSVAL(pdata,2,junction->referral_count); /* number of referral in this pkt */
670         if(self_referral) {
671                 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER); 
672         } else {
673                 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
674         }
675
676         offset = 8;
677         /* add the referral elements */
678         for(i=0;i<junction->referral_count;i++) {
679                 struct referral* ref = &junction->referral_list[i];
680                 int unilen;
681
682                 SSVAL(pdata,offset,2); /* version 2 */
683                 SSVAL(pdata,offset+2,VERSION2_REFERRAL_SIZE);
684                 if(self_referral) {
685                         SSVAL(pdata,offset+4,1);
686                 } else {
687                         SSVAL(pdata,offset+4,0);
688                 }
689                 SSVAL(pdata,offset+6,0); /* ref_flags :use path_consumed bytes? */
690                 SIVAL(pdata,offset+8,ref->proximity);
691                 SIVAL(pdata,offset+12,ref->ttl);
692
693                 SSVAL(pdata,offset+16,uni_reqpathoffset1-offset);
694                 SSVAL(pdata,offset+18,uni_reqpathoffset2-offset);
695                 /* copy referred path into current offset */
696                 unilen = rpcstr_push(pdata+uni_curroffset, ref->alternate_path,
697                                      sizeof(pstring), STR_UNICODE);
698
699                 SSVAL(pdata,offset+20,uni_curroffset-offset);
700
701                 uni_curroffset += unilen;
702                 offset += VERSION2_REFERRAL_SIZE;
703         }
704         /* add in the unexplained 22 (0x16) bytes at the end */
705         memset(pdata+uni_curroffset,'\0',0x16);
706         return reply_size;
707 }
708
709 static int setup_ver3_dfs_referral(char *pathname, char **ppdata, 
710                                    struct junction_map *junction,
711                                    int consumedcnt,
712                                    BOOL self_referral)
713 {
714         char* pdata = *ppdata;
715
716         unsigned char uni_reqpath[1024];
717         int uni_reqpathoffset1, uni_reqpathoffset2;
718         int uni_curroffset;
719         int reply_size = 0;
720
721         int reqpathlen = 0;
722         int offset,i=0;
723         
724         DEBUG(10,("setting up version3 referral\n"));
725
726         reqpathlen = rpcstr_push(uni_reqpath, pathname, sizeof(pstring), STR_TERMINATE);
727         
728         if (DEBUGLVL(10)) {
729             dump_data(0, (char *) uni_reqpath,reqpathlen);
730         }
731
732         uni_reqpathoffset1 = REFERRAL_HEADER_SIZE + VERSION3_REFERRAL_SIZE * junction->referral_count;
733         uni_reqpathoffset2 = uni_reqpathoffset1 + reqpathlen;
734         reply_size = uni_curroffset = uni_reqpathoffset2 + reqpathlen;
735
736         for(i=0;i<junction->referral_count;i++) {
737                 DEBUG(10,("referral %u : %s\n",i,junction->referral_list[i].alternate_path));
738                 reply_size += (strlen(junction->referral_list[i].alternate_path)+1)*2;
739         }
740
741         pdata = (char *)SMB_REALLOC(pdata,reply_size);
742         if(pdata == NULL) {
743                 DEBUG(0,("version3 referral setup: malloc failed for Realloc!\n"));
744                 return -1;
745         }
746         *ppdata = pdata;
747
748         /* create the header */
749         SSVAL(pdata,0,consumedcnt * 2); /* path consumed */
750         SSVAL(pdata,2,junction->referral_count); /* number of referral */
751         if(self_referral) {
752                 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER); 
753         } else {
754                 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
755         }
756         
757         /* copy in the reqpaths */
758         memcpy(pdata+uni_reqpathoffset1,uni_reqpath,reqpathlen);
759         memcpy(pdata+uni_reqpathoffset2,uni_reqpath,reqpathlen);
760         
761         offset = 8;
762         for(i=0;i<junction->referral_count;i++) {
763                 struct referral* ref = &(junction->referral_list[i]);
764                 int unilen;
765
766                 SSVAL(pdata,offset,3); /* version 3 */
767                 SSVAL(pdata,offset+2,VERSION3_REFERRAL_SIZE);
768                 if(self_referral) {
769                         SSVAL(pdata,offset+4,1);
770                 } else {
771                         SSVAL(pdata,offset+4,0);
772                 }
773
774                 SSVAL(pdata,offset+6,0); /* ref_flags :use path_consumed bytes? */
775                 SIVAL(pdata,offset+8,ref->ttl);
776             
777                 SSVAL(pdata,offset+12,uni_reqpathoffset1-offset);
778                 SSVAL(pdata,offset+14,uni_reqpathoffset2-offset);
779                 /* copy referred path into current offset */
780                 unilen = rpcstr_push(pdata+uni_curroffset,ref->alternate_path,
781                                      sizeof(pstring), STR_UNICODE | STR_TERMINATE);
782                 SSVAL(pdata,offset+16,uni_curroffset-offset);
783                 /* copy 0x10 bytes of 00's in the ServiceSite GUID */
784                 memset(pdata+offset+18,'\0',16);
785
786                 uni_curroffset += unilen;
787                 offset += VERSION3_REFERRAL_SIZE;
788         }
789         return reply_size;
790 }
791
792 /******************************************************************
793  Set up the Dfs referral for the dfs pathname
794 ******************************************************************/
795
796 int setup_dfs_referral(connection_struct *orig_conn, char *pathname, int max_referral_level, char **ppdata)
797 {
798         struct junction_map junction;
799         int consumedcnt;
800         BOOL self_referral = False;
801         pstring buf;
802         int reply_size = 0;
803         char *pathnamep = pathname;
804         TALLOC_CTX *ctx;
805
806         if (!(ctx=talloc_init("setup_dfs_referral"))) {
807                 return -1;
808         }
809
810         ZERO_STRUCT(junction);
811
812         /* get the junction entry */
813         if (!pathnamep) {
814                 talloc_destroy(ctx);
815                 return -1;
816         }
817
818         /* Trim pathname sent by client so it begins with only one backslash.
819            Two backslashes confuse some dfs clients
820          */
821         while (pathnamep[0] == '\\' && pathnamep[1] == '\\') {
822                 pathnamep++;
823         }
824
825         pstrcpy(buf, pathnamep);
826         /* The following call can change cwd. */
827         if (!get_referred_path(ctx, buf, &junction, &consumedcnt, &self_referral)) {
828                 vfs_ChDir(orig_conn,orig_conn->connectpath);
829                 talloc_destroy(ctx);
830                 return -1;
831         }
832         vfs_ChDir(orig_conn,orig_conn->connectpath);
833         
834         if (!self_referral) {
835                 pathnamep[consumedcnt] = '\0';
836
837                 if( DEBUGLVL( 3 ) ) {
838                         int i=0;
839                         dbgtext("setup_dfs_referral: Path %s to alternate path(s):",pathnamep);
840                         for(i=0;i<junction.referral_count;i++)
841                                 dbgtext(" %s",junction.referral_list[i].alternate_path);
842                         dbgtext(".\n");
843                 }
844         }
845
846         /* create the referral depeding on version */
847         DEBUG(10,("max_referral_level :%d\n",max_referral_level));
848         if(max_referral_level<2 || max_referral_level>3) {
849                 max_referral_level = 2;
850         }
851
852         switch(max_referral_level) {
853         case 2:
854                 reply_size = setup_ver2_dfs_referral(pathnamep, ppdata, &junction, 
855                                                      consumedcnt, self_referral);
856                 break;
857         case 3:
858                 reply_size = setup_ver3_dfs_referral(pathnamep, ppdata, &junction, 
859                                                      consumedcnt, self_referral);
860                 break;
861         default:
862                 DEBUG(0,("setup_dfs_referral: Invalid dfs referral version: %d\n", max_referral_level));
863                 talloc_destroy(ctx);
864                 return -1;
865         }
866       
867         if (DEBUGLVL(10)) {
868                 DEBUGADD(0,("DFS Referral pdata:\n"));
869                 dump_data(0,*ppdata,reply_size);
870         }
871
872         talloc_destroy(ctx);
873         return reply_size;
874 }
875
876 /**********************************************************************
877  The following functions are called by the NETDFS RPC pipe functions
878  **********************************************************************/
879
880 /*********************************************************************
881  Creates a junction structure from a Dfs pathname
882 **********************************************************************/
883
884 BOOL create_junction(const char *pathname, struct junction_map *jucn)
885 {
886         struct dfs_path dp;
887  
888         parse_dfs_path(pathname,&dp);
889
890         /* check if path is dfs : validate first token */
891         if (!is_myname_or_ipaddr(dp.hostname)) {
892                 DEBUG(4,("create_junction: Invalid hostname %s in dfs path %s\n",
893                         dp.hostname, pathname));
894                 return False;
895         }
896
897         /* Check for a non-DFS share */
898         if(!lp_msdfs_root(lp_servicenumber(dp.servicename))) {
899                 DEBUG(4,("create_junction: %s is not an msdfs root.\n", dp.servicename));
900                 return False;
901         }
902
903         pstrcpy(jucn->service_name,dp.servicename);
904         pstrcpy(jucn->volume_name,dp.reqpath);
905         pstrcpy(jucn->comment, lp_comment(lp_servicenumber(dp.servicename)));
906         return True;
907 }
908
909 /**********************************************************************
910  Forms a valid Unix pathname from the junction 
911  **********************************************************************/
912
913 static BOOL junction_to_local_path(struct junction_map *jucn, char *path,
914                                    int max_pathlen, connection_struct *conn)
915 {
916         int snum;
917         pstring conn_path;
918
919         if(!path || !jucn) {
920                 return False;
921         }
922
923         snum = lp_servicenumber(jucn->service_name);
924         if(snum < 0) {
925                 return False;
926         }
927
928         safe_strcpy(path, lp_pathname(snum), max_pathlen-1);
929         safe_strcat(path, "/", max_pathlen-1);
930         safe_strcat(path, jucn->volume_name, max_pathlen-1);
931
932         pstrcpy(conn_path, lp_pathname(snum));
933         if (!create_conn_struct(conn, snum, conn_path)) {
934                 return False;
935         }
936
937         return True;
938 }
939
940 BOOL create_msdfs_link(struct junction_map *jucn, BOOL exists)
941 {
942         pstring path;
943         pstring msdfs_link;
944         connection_struct conns;
945         connection_struct *conn = &conns;
946         int i=0;
947         BOOL insert_comma = False;
948         BOOL ret = False;
949
950         ZERO_STRUCT(conns);
951
952         if(!junction_to_local_path(jucn, path, sizeof(path), conn)) {
953                 return False;
954         }
955   
956         /* form the msdfs_link contents */
957         pstrcpy(msdfs_link, "msdfs:");
958         for(i=0; i<jucn->referral_count; i++) {
959                 char* refpath = jucn->referral_list[i].alternate_path;
960       
961                 trim_char(refpath, '\\', '\\');
962                 if(*refpath == '\0') {
963                         if (i == 0) {
964                                 insert_comma = False;
965                         }
966                         continue;
967                 }
968                 if (i > 0 && insert_comma) {
969                         pstrcat(msdfs_link, ",");
970                 }
971
972                 pstrcat(msdfs_link, refpath);
973                 if (!insert_comma) {
974                         insert_comma = True;
975                 }
976         }
977
978         DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n", path, msdfs_link));
979
980         if(exists) {
981                 if(SMB_VFS_UNLINK(conn,path)!=0) {
982                         goto out;
983                 }
984         }
985
986         if(SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
987                 DEBUG(1,("create_msdfs_link: symlink failed %s -> %s\nError: %s\n", 
988                                 path, msdfs_link, strerror(errno)));
989                 goto out;
990         }
991         
992         
993         ret = True;
994         
995 out:
996
997         conn_free_internal(conn);
998         return ret;
999 }
1000
1001 BOOL remove_msdfs_link(struct junction_map *jucn)
1002 {
1003         pstring path;
1004         connection_struct conns;
1005         connection_struct *conn = &conns;
1006         BOOL ret = False;
1007
1008         ZERO_STRUCT(conns);
1009
1010         if( junction_to_local_path(jucn, path, sizeof(path), conn) ) {
1011                 if( SMB_VFS_UNLINK(conn, path) == 0 ) {
1012                         ret = True;
1013                 }
1014                 talloc_destroy( conn->mem_ctx );
1015         }
1016
1017         conn_free_internal(conn);
1018         return ret;
1019 }
1020
1021 static int form_junctions(TALLOC_CTX *ctx, int snum, struct junction_map *jucn, int jn_remain)
1022 {
1023         int cnt = 0;
1024         SMB_STRUCT_DIR *dirp;
1025         char* dname;
1026         pstring connect_path;
1027         char* service_name = lp_servicename(snum);
1028         connection_struct conn;
1029         struct referral *ref = NULL;
1030  
1031         ZERO_STRUCT(conn);
1032
1033         if (jn_remain <= 0) {
1034                 return 0;
1035         }
1036
1037         pstrcpy(connect_path,lp_pathname(snum));
1038
1039         if(*connect_path == '\0') {
1040                 return 0;
1041         }
1042
1043         /*
1044          * Fake up a connection struct for the VFS layer.
1045          */
1046
1047         if (!create_conn_struct(&conn, snum, connect_path)) {
1048                 return 0;
1049         }
1050
1051         /* form a junction for the msdfs root - convention 
1052            DO NOT REMOVE THIS: NT clients will not work with us
1053            if this is not present
1054         */ 
1055         pstrcpy(jucn[cnt].service_name, service_name);
1056         jucn[cnt].volume_name[0] = '\0';
1057         jucn[cnt].referral_count = 1;
1058
1059         ref = jucn[cnt].referral_list = TALLOC_P(ctx, struct referral);
1060         if (jucn[cnt].referral_list == NULL) {
1061                 DEBUG(0, ("Malloc failed!\n"));
1062                 goto out;
1063         }
1064
1065         ref->proximity = 0;
1066         ref->ttl = REFERRAL_TTL;
1067         if (*lp_msdfs_proxy(snum) != '\0') {
1068                 pstrcpy(ref->alternate_path, lp_msdfs_proxy(snum));
1069                 cnt++;
1070                 goto out;
1071         }
1072                 
1073         slprintf(ref->alternate_path, sizeof(pstring)-1,
1074                  "\\\\%s\\%s", get_local_machine_name(), service_name);
1075         cnt++;
1076
1077         /* Now enumerate all dfs links */
1078         dirp = SMB_VFS_OPENDIR(&conn, ".", NULL, 0);
1079         if(!dirp) {
1080                 goto out;
1081         }
1082
1083         while ((dname = vfs_readdirname(&conn, dirp)) != NULL) {
1084                 if (cnt >= jn_remain) {
1085                         SMB_VFS_CLOSEDIR(&conn,dirp);
1086                         DEBUG(2, ("ran out of MSDFS junction slots"));
1087                         goto out;
1088                 }
1089                 if (is_msdfs_link(ctx, &conn, dname, &jucn[cnt].referral_list,
1090                                   &jucn[cnt].referral_count, NULL)) {
1091                         pstrcpy(jucn[cnt].service_name, service_name);
1092                         pstrcpy(jucn[cnt].volume_name, dname);
1093                         cnt++;
1094                 }
1095         }
1096         
1097         SMB_VFS_CLOSEDIR(&conn,dirp);
1098
1099 out:
1100
1101         conn_free_internal(&conn);
1102         return cnt;
1103 }
1104
1105 int enum_msdfs_links(TALLOC_CTX *ctx, struct junction_map *jucn, int jn_max)
1106 {
1107         int i=0;
1108         int sharecount = 0;
1109         int jn_count = 0;
1110
1111         if(!lp_host_msdfs()) {
1112                 return 0;
1113         }
1114
1115         /* Ensure all the usershares are loaded. */
1116         become_root();
1117         load_registry_shares();
1118         sharecount = load_usershare_shares();
1119         unbecome_root();
1120
1121         for(i=0;i < sharecount && (jn_max - jn_count) > 0;i++) {
1122                 if(lp_msdfs_root(i)) {
1123                         jn_count += form_junctions(ctx, i,jucn,jn_max - jn_count);
1124                 }
1125         }
1126         return jn_count;
1127 }