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