r25327: Normalize the path we return for 'msdfs proxy'
[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 3 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, see <http://www.gnu.org/licenses/>.
20
21 */
22
23 #define DBGC_CLASS DBGC_MSDFS
24 #include "includes.h"
25
26 extern uint32 global_client_caps;
27
28 /**********************************************************************
29  Parse a DFS pathname of the form \hostname\service\reqpath
30  into the dfs_path structure.
31  If POSIX pathnames is true, the pathname may also be of the
32  form /hostname/service/reqpath.
33  We cope with either here.
34
35  Unfortunately, due to broken clients who might set the
36  SVAL(inbuf,smb_flg2) & FLAGS2_DFS_PATHNAMES bit and then
37  send a local path, we have to cope with that too....
38
39  This version does everything using pointers within one copy of the
40  pathname string, talloced on the struct dfs_path pointer (which
41  must be talloced). This may be too clever to live....
42  JRA.
43 **********************************************************************/
44
45 static NTSTATUS parse_dfs_path(const char *pathname,
46                                 BOOL allow_wcards,
47                                 struct dfs_path *pdp, /* MUST BE TALLOCED */
48                                 BOOL *ppath_contains_wcard)
49 {
50         char *pathname_local;
51         char *p,*temp;
52         char *eos_ptr;
53         NTSTATUS status = NT_STATUS_OK;
54         char sepchar;
55
56         ZERO_STRUCTP(pdp);
57
58         /*
59          * This is the only talloc we should need to do
60          * on the struct dfs_path. All the pointers inside
61          * it should point to offsets within this string.
62          */
63
64         pathname_local = talloc_strdup(pdp, pathname);
65         if (!pathname_local) {
66                 return NT_STATUS_NO_MEMORY;
67         }
68         /* Get a pointer to the terminating '\0' */
69         eos_ptr = &pathname_local[strlen(pathname_local)];
70         p = temp = pathname_local;
71
72         pdp->posix_path = (lp_posix_pathnames() && *pathname == '/');
73
74         sepchar = pdp->posix_path ? '/' : '\\';
75
76         if (*pathname != sepchar) {
77                 DEBUG(10,("parse_dfs_path: path %s doesn't start with %c\n",
78                         pathname, sepchar ));
79                 /*
80                  * Possibly client sent a local path by mistake.
81                  * Try and convert to a local path.
82                  */
83
84                 pdp->hostname = eos_ptr; /* "" */
85                 pdp->servicename = eos_ptr; /* "" */
86
87                 /* We've got no info about separators. */
88                 pdp->posix_path = lp_posix_pathnames();
89                 p = temp;
90                 DEBUG(10,("parse_dfs_path: trying to convert %s to a "
91                         "local path\n",
92                         temp));
93                 goto local_path;
94         }
95
96         /*
97          * Safe to use on talloc'ed string as it only shrinks.
98          * It also doesn't affect the eos_ptr.
99          */
100         trim_char(temp,sepchar,sepchar);
101
102         DEBUG(10,("parse_dfs_path: temp = |%s| after trimming %c's\n",
103                 temp, sepchar));
104
105         /* Now tokenize. */
106         /* Parse out hostname. */
107         p = strchr_m(temp,sepchar);
108         if(p == NULL) {
109                 DEBUG(10,("parse_dfs_path: can't parse hostname from path %s\n",
110                         temp));
111                 /*
112                  * Possibly client sent a local path by mistake.
113                  * Try and convert to a local path.
114                  */
115
116                 pdp->hostname = eos_ptr; /* "" */
117                 pdp->servicename = eos_ptr; /* "" */
118
119                 p = temp;
120                 DEBUG(10,("parse_dfs_path: trying to convert %s "
121                         "to a local path\n",
122                         temp));
123                 goto local_path;
124         }
125         *p = '\0';
126         pdp->hostname = temp;
127
128         DEBUG(10,("parse_dfs_path: hostname: %s\n",pdp->hostname));
129
130         /* If we got a hostname, is it ours (or an IP address) ? */
131         if (!is_myname_or_ipaddr(pdp->hostname)) {
132                 /* Repair path. */
133                 *p = sepchar;
134                 DEBUG(10,("parse_dfs_path: hostname %s isn't ours. "
135                         "Try local path from path %s\n",
136                         pdp->hostname, temp));
137                 /*
138                  * Possibly client sent a local path by mistake.
139                  * Try and convert to a local path.
140                  */
141
142                 pdp->hostname = eos_ptr; /* "" */
143                 pdp->servicename = eos_ptr; /* "" */
144
145                 p = temp;
146                 DEBUG(10,("parse_dfs_path: trying to convert %s "
147                         "to a local path\n",
148                         temp));
149                 goto local_path;
150         }
151
152         /* Parse out servicename. */
153         temp = p+1;
154         p = strchr_m(temp,sepchar);
155         if(p == NULL) {
156                 pdp->servicename = temp;
157                 pdp->reqpath = eos_ptr; /* "" */
158                 return NT_STATUS_OK;
159         }
160         *p = '\0';
161         pdp->servicename = temp;
162         DEBUG(10,("parse_dfs_path: servicename: %s\n",pdp->servicename));
163
164         p++;
165
166   local_path:
167
168         *ppath_contains_wcard = False;
169
170         pdp->reqpath = p;
171
172         /* Rest is reqpath. */
173         if (pdp->posix_path) {
174                 status = check_path_syntax_posix(pdp->reqpath);
175         } else {
176                 if (allow_wcards) {
177                         status = check_path_syntax_wcard(pdp->reqpath,
178                                         ppath_contains_wcard);
179                 } else {
180                         status = check_path_syntax(pdp->reqpath);
181                 }
182         }
183
184         if (!NT_STATUS_IS_OK(status)) {
185                 DEBUG(10,("parse_dfs_path: '%s' failed with %s\n",
186                         p, nt_errstr(status) ));
187                 return status;
188         }
189
190         DEBUG(10,("parse_dfs_path: rest of the path: %s\n",pdp->reqpath));
191         return NT_STATUS_OK;
192 }
193
194 /********************************************************
195  Fake up a connection struct for the VFS layer.
196  Note this CHANGES CWD !!!! JRA.
197 *********************************************************/
198
199 static NTSTATUS create_conn_struct(connection_struct *conn,
200                                 int snum,
201                                 const char *path)
202 {
203         pstring connpath;
204
205         ZERO_STRUCTP(conn);
206
207         pstrcpy(connpath, path);
208         pstring_sub(connpath , "%S", lp_servicename(snum));
209
210         /* needed for smbd_vfs_init() */
211
212         if ((conn->mem_ctx=talloc_init("connection_struct")) == NULL) {
213                 DEBUG(0,("talloc_init(connection_struct) failed!\n"));
214                 return NT_STATUS_NO_MEMORY;
215         }
216
217         if (!(conn->params = TALLOC_ZERO_P(conn->mem_ctx,
218                                         struct share_params))) {
219                 DEBUG(0, ("TALLOC failed\n"));
220                 return NT_STATUS_NO_MEMORY;
221         }
222
223         conn->params->service = snum;
224
225         set_conn_connectpath(conn, connpath);
226
227         if (!smbd_vfs_init(conn)) {
228                 NTSTATUS status = map_nt_error_from_unix(errno);
229                 DEBUG(0,("create_conn_struct: smbd_vfs_init failed.\n"));
230                 conn_free_internal(conn);
231                 return status;
232         }
233
234         /*
235          * Windows seems to insist on doing trans2getdfsreferral() calls on
236          * the IPC$ share as the anonymous user. If we try to chdir as that
237          * user we will fail.... WTF ? JRA.
238          */
239
240         if (vfs_ChDir(conn,conn->connectpath) != 0) {
241                 NTSTATUS status = map_nt_error_from_unix(errno);
242                 DEBUG(3,("create_conn_struct: Can't ChDir to new conn path %s. "
243                         "Error was %s\n",
244                         conn->connectpath, strerror(errno) ));
245                 conn_free_internal(conn);
246                 return status;
247         }
248
249         return NT_STATUS_OK;
250 }
251
252 /**********************************************************************
253  Parse the contents of a symlink to verify if it is an msdfs referral
254  A valid referral is of the form:
255
256  msdfs:server1\share1,server2\share2
257  msdfs:server1\share1\pathname,server2\share2\pathname
258  msdfs:server1/share1,server2/share2
259  msdfs:server1/share1/pathname,server2/share2/pathname.
260
261  Note that the alternate paths returned here must be of the canonicalized
262  form:
263
264  \server\share or
265  \server\share\path\to\file,
266
267  even in posix path mode. This is because we have no knowledge if the
268  server we're referring to understands posix paths.
269  **********************************************************************/
270
271 static BOOL parse_msdfs_symlink(TALLOC_CTX *ctx,
272                                 const char *target,
273                                 struct referral **preflist,
274                                 int *refcount)
275 {
276         char *temp = NULL;
277         char *prot;
278         char **alt_path = NULL;
279         int count = 0, i;
280         struct referral *reflist;
281
282         temp = talloc_strdup(ctx, target);
283         if (!temp) {
284                 return False;
285         }
286         prot = strtok(temp,":");
287         if (!prot) {
288                 DEBUG(0,("parse_msdfs_symlink: invalid path !\n"));
289                 return False;
290         }
291
292         alt_path = TALLOC_ARRAY(ctx, char *, MAX_REFERRAL_COUNT);
293         if (!alt_path) {
294                 return False;
295         }
296
297         /* parse out the alternate paths */
298         while((count<MAX_REFERRAL_COUNT) &&
299               ((alt_path[count] = strtok(NULL,",")) != NULL)) {
300                 count++;
301         }
302
303         DEBUG(10,("parse_msdfs_symlink: count=%d\n", count));
304
305         if (count) {
306                 reflist = *preflist = TALLOC_ZERO_ARRAY(ctx,
307                                 struct referral, count);
308                 if(reflist == NULL) {
309                         TALLOC_FREE(alt_path);
310                         return False;
311                 }
312         } else {
313                 reflist = *preflist = NULL;
314         }
315
316         for(i=0;i<count;i++) {
317                 char *p;
318
319                 /* Canonicalize link target.
320                  * Replace all /'s in the path by a \ */
321                 string_replace(alt_path[i], '/', '\\');
322
323                 /* Remove leading '\\'s */
324                 p = alt_path[i];
325                 while (*p && (*p == '\\')) {
326                         p++;
327                 }
328
329                 reflist[i].alternate_path = talloc_asprintf(ctx,
330                                 "\\%s",
331                                 p);
332                 if (!reflist[i].alternate_path) {
333                         return False;
334                 }
335
336                 reflist[i].proximity = 0;
337                 reflist[i].ttl = REFERRAL_TTL;
338                 DEBUG(10, ("parse_msdfs_symlink: Created alt path: %s\n",
339                                         reflist[i].alternate_path));
340                 *refcount += 1;
341         }
342
343         TALLOC_FREE(alt_path);
344         return True;
345 }
346
347 /**********************************************************************
348  Returns true if the unix path is a valid msdfs symlink and also
349  returns the target string from inside the link.
350 **********************************************************************/
351
352 static BOOL is_msdfs_link_internal(TALLOC_CTX *ctx,
353                         connection_struct *conn,
354                         const char *path,
355                         char **pp_link_target,
356                         SMB_STRUCT_STAT *sbufp)
357 {
358         SMB_STRUCT_STAT st;
359         int referral_len = 0;
360         char link_target_buf[7];
361         size_t bufsize = 0;
362         char *link_target = NULL;
363
364         if (pp_link_target) {
365                 bufsize = 1024;
366                 link_target = TALLOC_ARRAY(ctx, char, bufsize);
367                 if (!link_target) {
368                         return False;
369                 }
370                 *pp_link_target = link_target;
371         } else {
372                 bufsize = sizeof(link_target_buf);
373                 link_target = link_target_buf;
374         }
375
376         if (sbufp == NULL) {
377                 sbufp = &st;
378         }
379
380         if (SMB_VFS_LSTAT(conn, path, sbufp) != 0) {
381                 DEBUG(5,("is_msdfs_link_read_target: %s does not exist.\n",
382                         path));
383                 goto err;
384         }
385
386         if (!S_ISLNK(sbufp->st_mode)) {
387                 DEBUG(5,("is_msdfs_link_read_target: %s is not a link.\n",
388                                         path));
389                 goto err;
390         }
391
392         referral_len = SMB_VFS_READLINK(conn, path, link_target, bufsize - 1);
393         if (referral_len == -1) {
394                 DEBUG(0,("is_msdfs_link_read_target: Error reading "
395                         "msdfs link %s: %s\n",
396                         path, strerror(errno)));
397                 goto err;
398         }
399         link_target[referral_len] = '\0';
400
401         DEBUG(5,("is_msdfs_link_internal: %s -> %s\n",path,
402                                 link_target));
403
404         if (!strnequal(link_target, "msdfs:", 6)) {
405                 goto err;
406         }
407         return True;
408
409   err:
410
411         if (link_target != link_target_buf) {
412                 TALLOC_FREE(link_target);
413         }
414         return False;
415 }
416
417 /**********************************************************************
418  Returns true if the unix path is a valid msdfs symlink.
419 **********************************************************************/
420
421 BOOL is_msdfs_link(connection_struct *conn,
422                 const char *path,
423                 SMB_STRUCT_STAT *sbufp)
424 {
425         return is_msdfs_link_internal(talloc_tos(),
426                                         conn,
427                                         path,
428                                         NULL,
429                                         sbufp);
430 }
431
432 /*****************************************************************
433  Used by other functions to decide if a dfs path is remote,
434  and to get the list of referred locations for that remote path.
435
436  search_flag: For findfirsts, dfs links themselves are not
437  redirected, but paths beyond the links are. For normal smb calls,
438  even dfs links need to be redirected.
439
440  consumedcntp: how much of the dfs path is being redirected. the client
441  should try the remaining path on the redirected server.
442
443  If this returns NT_STATUS_PATH_NOT_COVERED the contents of the msdfs
444  link redirect are in targetpath.
445 *****************************************************************/
446
447 static NTSTATUS dfs_path_lookup(TALLOC_CTX *ctx,
448                 connection_struct *conn,
449                 const char *dfspath, /* Incoming complete dfs path */
450                 const struct dfs_path *pdp, /* Parsed out
451                                                server+share+extrapath. */
452                 BOOL search_flag, /* Called from a findfirst ? */
453                 int *consumedcntp,
454                 char **pp_targetpath)
455 {
456         char *p = NULL;
457         char *q = NULL;
458         SMB_STRUCT_STAT sbuf;
459         NTSTATUS status;
460         char *localpath = NULL;
461         char *canon_dfspath = NULL; /* Canonicalized dfs path. (only '/'
462                                   components). */
463
464         DEBUG(10,("dfs_path_lookup: Conn path = %s reqpath = %s\n",
465                 conn->connectpath, pdp->reqpath));
466
467         /*
468          * Note the unix path conversion here we're doing we can
469          * throw away. We're looking for a symlink for a dfs
470          * resolution, if we don't find it we'll do another
471          * unix_convert later in the codepath.
472          * If we needed to remember what we'd resolved in
473          * dp->reqpath (as the original code did) we'd
474          * pstrcpy(localhost, dp->reqpath) on any code
475          * path below that returns True - but I don't
476          * think this is needed. JRA.
477          */
478
479         status = unix_convert(ctx, conn, pdp->reqpath, search_flag, &localpath,
480                         NULL, &sbuf);
481         if (!NT_STATUS_IS_OK(status) && !NT_STATUS_EQUAL(status,
482                                         NT_STATUS_OBJECT_PATH_NOT_FOUND)) {
483                 return status;
484         }
485
486         /* Optimization - check if we can redirect the whole path. */
487
488         if (is_msdfs_link_internal(ctx, conn, localpath, pp_targetpath, NULL)) {
489                 if (search_flag) {
490                         DEBUG(6,("dfs_path_lookup (FindFirst) No redirection "
491                                  "for dfs link %s.\n", dfspath));
492                         return NT_STATUS_OK;
493                 }
494
495                 DEBUG(6,("dfs_path_lookup: %s resolves to a "
496                         "valid dfs link %s.\n", dfspath,
497                         pp_targetpath ? *pp_targetpath : ""));
498
499                 if (consumedcntp) {
500                         *consumedcntp = strlen(dfspath);
501                 }
502                 return NT_STATUS_PATH_NOT_COVERED;
503         }
504
505         /* Prepare to test only for '/' components in the given path,
506          * so if a Windows path replace all '\\' characters with '/'.
507          * For a POSIX DFS path we know all separators are already '/'. */
508
509         canon_dfspath = talloc_strdup(ctx, dfspath);
510         if (!canon_dfspath) {
511                 return NT_STATUS_NO_MEMORY;
512         }
513         if (!pdp->posix_path) {
514                 string_replace(canon_dfspath, '\\', '/');
515         }
516
517         /*
518          * localpath comes out of unix_convert, so it has
519          * no trailing backslash. Make sure that canon_dfspath hasn't either.
520          * Fix for bug #4860 from Jan Martin <Jan.Martin@rwedea.com>.
521          */
522
523         trim_char(canon_dfspath,0,'/');
524
525         /*
526          * Redirect if any component in the path is a link.
527          * We do this by walking backwards through the
528          * local path, chopping off the last component
529          * in both the local path and the canonicalized
530          * DFS path. If we hit a DFS link then we're done.
531          */
532
533         p = strrchr_m(localpath, '/');
534         if (consumedcntp) {
535                 q = strrchr_m(canon_dfspath, '/');
536         }
537
538         while (p) {
539                 *p = '\0';
540                 if (q) {
541                         *q = '\0';
542                 }
543
544                 if (is_msdfs_link_internal(ctx, conn,
545                                         localpath, pp_targetpath, NULL)) {
546                         DEBUG(4, ("dfs_path_lookup: Redirecting %s because "
547                                 "parent %s is dfs link\n", dfspath, localpath));
548
549                         if (consumedcntp) {
550                                 *consumedcntp = strlen(canon_dfspath);
551                                 DEBUG(10, ("dfs_path_lookup: Path consumed: %s "
552                                         "(%d)\n",
553                                         canon_dfspath,
554                                         *consumedcntp));
555                         }
556
557                         return NT_STATUS_PATH_NOT_COVERED;
558                 }
559
560                 /* Step back on the filesystem. */
561                 p = strrchr_m(localpath, '/');
562
563                 if (consumedcntp) {
564                         /* And in the canonicalized dfs path. */
565                         q = strrchr_m(canon_dfspath, '/');
566                 }
567         }
568
569         return NT_STATUS_OK;
570 }
571
572 /*****************************************************************
573  Decides if a dfs pathname should be redirected or not.
574  If not, the pathname is converted to a tcon-relative local unix path
575
576  search_wcard_flag: this flag performs 2 functions both related
577  to searches.  See resolve_dfs_path() and parse_dfs_path_XX()
578  for details.
579
580  This function can return NT_STATUS_OK, meaning use the returned path as-is
581  (mapped into a local path).
582  or NT_STATUS_NOT_COVERED meaning return a DFS redirect, or
583  any other NT_STATUS error which is a genuine error to be
584  returned to the client.
585 *****************************************************************/
586
587 static NTSTATUS dfs_redirect(TALLOC_CTX *ctx,
588                         connection_struct *conn,
589                         const char *path_in,
590                         BOOL search_wcard_flag,
591                         char **pp_path_out,
592                         BOOL *ppath_contains_wcard)
593 {
594         NTSTATUS status;
595         struct dfs_path *pdp = TALLOC_P(ctx, struct dfs_path);
596
597         if (!pdp) {
598                 return NT_STATUS_NO_MEMORY;
599         }
600
601         status = parse_dfs_path(path_in, search_wcard_flag, pdp,
602                         ppath_contains_wcard);
603         if (!NT_STATUS_IS_OK(status)) {
604                 TALLOC_FREE(pdp);
605                 return status;
606         }
607
608         if (pdp->reqpath[0] == '\0') {
609                 TALLOC_FREE(pdp);
610                 *pp_path_out = talloc_strdup(ctx, "");
611                 if (!*pp_path_out) {
612                         return NT_STATUS_NO_MEMORY;
613                 }
614                 DEBUG(5,("dfs_redirect: self-referral.\n"));
615                 return NT_STATUS_OK;
616         }
617
618         /* If dfs pathname for a non-dfs share, convert to tcon-relative
619            path and return OK */
620
621         if (!lp_msdfs_root(SNUM(conn))) {
622                 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
623                 TALLOC_FREE(pdp);
624                 if (!*pp_path_out) {
625                         return NT_STATUS_NO_MEMORY;
626                 }
627                 return NT_STATUS_OK;
628         }
629
630         /* If it looked like a local path (zero hostname/servicename)
631          * just treat as a tcon-relative path. */
632
633         if (pdp->hostname[0] == '\0' && pdp->servicename[0] == '\0') {
634                 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
635                 TALLOC_FREE(pdp);
636                 if (!*pp_path_out) {
637                         return NT_STATUS_NO_MEMORY;
638                 }
639                 return NT_STATUS_OK;
640         }
641
642         if (!( strequal(pdp->servicename, lp_servicename(SNUM(conn)))
643                         || (strequal(pdp->servicename, HOMES_NAME)
644                         && strequal(lp_servicename(SNUM(conn)),
645                                 get_current_username()) )) ) {
646
647                 /* The given sharename doesn't match this connection. */
648                 TALLOC_FREE(pdp);
649
650                 return NT_STATUS_OBJECT_PATH_NOT_FOUND;
651         }
652
653         status = dfs_path_lookup(ctx, conn, path_in, pdp,
654                         search_wcard_flag, NULL, NULL);
655         if (!NT_STATUS_IS_OK(status)) {
656                 if (NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
657                         DEBUG(3,("dfs_redirect: Redirecting %s\n", path_in));
658                 } else {
659                         DEBUG(10,("dfs_redirect: dfs_path_lookup "
660                                 "failed for %s with %s\n",
661                                 path_in, nt_errstr(status) ));
662                 }
663                 return status;
664         }
665
666         DEBUG(3,("dfs_redirect: Not redirecting %s.\n", path_in));
667
668         /* Form non-dfs tcon-relative path */
669         *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
670         TALLOC_FREE(pdp);
671         if (!*pp_path_out) {
672                 return NT_STATUS_NO_MEMORY;
673         }
674
675         DEBUG(3,("dfs_redirect: Path %s converted to non-dfs path %s\n",
676                                 path_in,
677                                 *pp_path_out));
678
679         return NT_STATUS_OK;
680 }
681
682 /**********************************************************************
683  Return a self referral.
684 **********************************************************************/
685
686 static NTSTATUS self_ref(TALLOC_CTX *ctx,
687                         const char *dfs_path,
688                         struct junction_map *jucn,
689                         int *consumedcntp,
690                         BOOL *self_referralp)
691 {
692         struct referral *ref;
693
694         *self_referralp = True;
695
696         jucn->referral_count = 1;
697         if((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) {
698                 return NT_STATUS_NO_MEMORY;
699         }
700
701         ref->alternate_path = talloc_strdup(ctx, dfs_path);
702         if (!ref->alternate_path) {
703                 return NT_STATUS_NO_MEMORY;
704         }
705         ref->proximity = 0;
706         ref->ttl = REFERRAL_TTL;
707         jucn->referral_list = ref;
708         *consumedcntp = strlen(dfs_path);
709         return NT_STATUS_OK;
710 }
711
712 /**********************************************************************
713  Gets valid referrals for a dfs path and fills up the
714  junction_map structure.
715 **********************************************************************/
716
717 NTSTATUS get_referred_path(TALLOC_CTX *ctx,
718                         const char *dfs_path,
719                         struct junction_map *jucn,
720                         int *consumedcntp,
721                         BOOL *self_referralp)
722 {
723         struct connection_struct conns;
724         struct connection_struct *conn = &conns;
725         char *targetpath = NULL;
726         int snum;
727         NTSTATUS status = NT_STATUS_NOT_FOUND;
728         BOOL dummy;
729         struct dfs_path *pdp = TALLOC_P(ctx, struct dfs_path);
730
731         if (!pdp) {
732                 return NT_STATUS_NO_MEMORY;
733         }
734
735         ZERO_STRUCT(conns);
736         *self_referralp = False;
737
738         status = parse_dfs_path(dfs_path, False, pdp, &dummy);
739         if (!NT_STATUS_IS_OK(status)) {
740                 return status;
741         }
742
743         /* Verify hostname in path */
744         if (!is_myname_or_ipaddr(pdp->hostname)) {
745                 DEBUG(3, ("get_referred_path: Invalid hostname %s in path %s\n",
746                         pdp->hostname, dfs_path));
747                 TALLOC_FREE(pdp);
748                 return NT_STATUS_NOT_FOUND;
749         }
750
751         jucn->service_name = talloc_strdup(ctx, pdp->servicename);
752         jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
753         if (!jucn->service_name || !jucn->volume_name) {
754                 TALLOC_FREE(pdp);
755                 return NT_STATUS_NO_MEMORY;
756         }
757
758         /* Verify the share is a dfs root */
759         snum = lp_servicenumber(jucn->service_name);
760         if(snum < 0) {
761                 fstring service_name;
762                 fstrcpy(service_name, jucn->service_name);
763                 if ((snum = find_service(service_name)) < 0) {
764                         return NT_STATUS_NOT_FOUND;
765                 }
766                 TALLOC_FREE(jucn->service_name);
767                 jucn->service_name = talloc_strdup(ctx, service_name);
768                 if (!jucn->service_name) {
769                         TALLOC_FREE(pdp);
770                         return NT_STATUS_NO_MEMORY;
771                 }
772         }
773
774         if (!lp_msdfs_root(snum) && (*lp_msdfs_proxy(snum) == '\0')) {
775                 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not "
776                         "a dfs root.\n",
777                         pdp->servicename, dfs_path));
778                 TALLOC_FREE(pdp);
779                 return NT_STATUS_NOT_FOUND;
780         }
781
782         /*
783          * Self referrals are tested with a anonymous IPC connection and
784          * a GET_DFS_REFERRAL call to \\server\share. (which means
785          * dp.reqpath[0] points to an empty string). create_conn_struct cd's
786          * into the directory and will fail if it cannot (as the anonymous
787          * user). Cope with this.
788          */
789
790         if (pdp->reqpath[0] == '\0') {
791                 char *tmp;
792                 struct referral *ref;
793
794                 if (*lp_msdfs_proxy(snum) == '\0') {
795                         TALLOC_FREE(pdp);
796                         return self_ref(ctx,
797                                         dfs_path,
798                                         jucn,
799                                         consumedcntp,
800                                         self_referralp);
801                 }
802
803                 /*
804                  * It's an msdfs proxy share. Redirect to
805                  * the configured target share.
806                  */
807
808                 jucn->referral_count = 1;
809                 if ((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) {
810                         TALLOC_FREE(pdp);
811                         return NT_STATUS_NO_MEMORY;
812                 }
813
814                 if (!(tmp = talloc_strdup(ctx, lp_msdfs_proxy(snum)))) {
815                         TALLOC_FREE(pdp);
816                         return NT_STATUS_NO_MEMORY;
817                 }
818
819                 trim_string(tmp, "\\", 0);
820
821                 ref->alternate_path = talloc_asprintf(ctx, "\\%s", tmp);
822                 TALLOC_FREE(tmp);
823
824                 if (!ref->alternate_path) {
825                         TALLOC_FREE(pdp);
826                         return NT_STATUS_NO_MEMORY;
827                 }
828
829                 if (pdp->reqpath[0] != '\0') {
830                         ref->alternate_path = talloc_asprintf_append(
831                                         ref->alternate_path,
832                                         "%s",
833                                         pdp->reqpath);
834                         if (!ref->alternate_path) {
835                                 TALLOC_FREE(pdp);
836                                 return NT_STATUS_NO_MEMORY;
837                         }
838                 }
839                 ref->proximity = 0;
840                 ref->ttl = REFERRAL_TTL;
841                 jucn->referral_list = ref;
842                 *consumedcntp = strlen(dfs_path);
843                 TALLOC_FREE(pdp);
844                 return NT_STATUS_OK;
845         }
846
847         status = create_conn_struct(conn, snum, lp_pathname(snum));
848         if (!NT_STATUS_IS_OK(status)) {
849                 TALLOC_FREE(pdp);
850                 return status;
851         }
852
853         /* If this is a DFS path dfs_lookup should return
854          * NT_STATUS_PATH_NOT_COVERED. */
855
856         status = dfs_path_lookup(ctx, conn, dfs_path, pdp,
857                         False, consumedcntp, &targetpath);
858
859         if (!NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
860                 DEBUG(3,("get_referred_path: No valid referrals for path %s\n",
861                         dfs_path));
862                 conn_free_internal(conn);
863                 TALLOC_FREE(pdp);
864                 return status;
865         }
866
867         /* We know this is a valid dfs link. Parse the targetpath. */
868         if (!parse_msdfs_symlink(ctx, targetpath,
869                                 &jucn->referral_list,
870                                 &jucn->referral_count)) {
871                 DEBUG(3,("get_referred_path: failed to parse symlink "
872                         "target %s\n", targetpath ));
873                 conn_free_internal(conn);
874                 TALLOC_FREE(pdp);
875                 return NT_STATUS_NOT_FOUND;
876         }
877
878         conn_free_internal(conn);
879         TALLOC_FREE(pdp);
880         return NT_STATUS_OK;
881 }
882
883 static int setup_ver2_dfs_referral(const char *pathname,
884                                 char **ppdata,
885                                 struct junction_map *junction,
886                                 int consumedcnt,
887                                 BOOL self_referral)
888 {
889         char* pdata = *ppdata;
890
891         unsigned char uni_requestedpath[sizeof(pstring)];
892         int uni_reqpathoffset1,uni_reqpathoffset2;
893         int uni_curroffset;
894         int requestedpathlen=0;
895         int offset;
896         int reply_size = 0;
897         int i=0;
898
899         DEBUG(10,("Setting up version2 referral\nRequested path:\n"));
900
901         requestedpathlen = rpcstr_push(uni_requestedpath,
902                                         pathname, sizeof(pstring),
903                                         STR_TERMINATE);
904
905         if (DEBUGLVL(10)) {
906                 dump_data(0, uni_requestedpath,requestedpathlen);
907         }
908
909         DEBUG(10,("ref count = %u\n",junction->referral_count));
910
911         uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
912                         VERSION2_REFERRAL_SIZE * junction->referral_count;
913
914         uni_reqpathoffset2 = uni_reqpathoffset1 + requestedpathlen;
915
916         uni_curroffset = uni_reqpathoffset2 + requestedpathlen;
917
918         reply_size = REFERRAL_HEADER_SIZE +
919                         VERSION2_REFERRAL_SIZE*junction->referral_count +
920                         2 * requestedpathlen;
921         DEBUG(10,("reply_size: %u\n",reply_size));
922
923         /* add up the unicode lengths of all the referral paths */
924         for(i=0;i<junction->referral_count;i++) {
925                 DEBUG(10,("referral %u : %s\n",
926                         i,
927                         junction->referral_list[i].alternate_path));
928                 reply_size +=
929                         (strlen(junction->referral_list[i].alternate_path)+1)*2;
930         }
931
932         DEBUG(10,("reply_size = %u\n",reply_size));
933         /* add the unexplained 0x16 bytes */
934         reply_size += 0x16;
935
936         pdata = (char *)SMB_REALLOC(pdata,reply_size);
937         if(pdata == NULL) {
938                 DEBUG(0,("Realloc failed!\n"));
939                 return -1;
940         }
941         *ppdata = pdata;
942
943         /* copy in the dfs requested paths.. required for offset calculations */
944         memcpy(pdata+uni_reqpathoffset1,uni_requestedpath,requestedpathlen);
945         memcpy(pdata+uni_reqpathoffset2,uni_requestedpath,requestedpathlen);
946
947         /* create the header */
948         SSVAL(pdata,0,consumedcnt * 2); /* path consumed */
949         /* number of referral in this pkt */
950         SSVAL(pdata,2,junction->referral_count);
951         if(self_referral) {
952                 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
953         } else {
954                 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
955         }
956
957         offset = 8;
958         /* add the referral elements */
959         for(i=0;i<junction->referral_count;i++) {
960                 struct referral* ref = &junction->referral_list[i];
961                 int unilen;
962
963                 SSVAL(pdata,offset,2); /* version 2 */
964                 SSVAL(pdata,offset+2,VERSION2_REFERRAL_SIZE);
965                 if(self_referral) {
966                         SSVAL(pdata,offset+4,1);
967                 } else {
968                         SSVAL(pdata,offset+4,0);
969                 }
970
971                 /* ref_flags :use path_consumed bytes? */
972                 SSVAL(pdata,offset+6,0);
973                 SIVAL(pdata,offset+8,ref->proximity);
974                 SIVAL(pdata,offset+12,ref->ttl);
975
976                 SSVAL(pdata,offset+16,uni_reqpathoffset1-offset);
977                 SSVAL(pdata,offset+18,uni_reqpathoffset2-offset);
978                 /* copy referred path into current offset */
979                 unilen = rpcstr_push(pdata+uni_curroffset, ref->alternate_path,
980                                      sizeof(pstring), STR_UNICODE);
981
982                 SSVAL(pdata,offset+20,uni_curroffset-offset);
983
984                 uni_curroffset += unilen;
985                 offset += VERSION2_REFERRAL_SIZE;
986         }
987         /* add in the unexplained 22 (0x16) bytes at the end */
988         memset(pdata+uni_curroffset,'\0',0x16);
989         return reply_size;
990 }
991
992 static int setup_ver3_dfs_referral(const char *pathname,
993                                 char **ppdata,
994                                 struct junction_map *junction,
995                                 int consumedcnt,
996                                 BOOL self_referral)
997 {
998         char *pdata = *ppdata;
999
1000         unsigned char uni_reqpath[sizeof(pstring)];
1001         int uni_reqpathoffset1, uni_reqpathoffset2;
1002         int uni_curroffset;
1003         int reply_size = 0;
1004
1005         int reqpathlen = 0;
1006         int offset,i=0;
1007
1008         DEBUG(10,("setting up version3 referral\n"));
1009
1010         reqpathlen = rpcstr_push(uni_reqpath, pathname,
1011                         sizeof(pstring), STR_TERMINATE);
1012
1013         if (DEBUGLVL(10)) {
1014                 dump_data(0, uni_reqpath,reqpathlen);
1015         }
1016
1017         uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
1018                         VERSION3_REFERRAL_SIZE * junction->referral_count;
1019         uni_reqpathoffset2 = uni_reqpathoffset1 + reqpathlen;
1020         reply_size = uni_curroffset = uni_reqpathoffset2 + reqpathlen;
1021
1022         for(i=0;i<junction->referral_count;i++) {
1023                 DEBUG(10,("referral %u : %s\n",
1024                         i,
1025                         junction->referral_list[i].alternate_path));
1026                 reply_size +=
1027                         (strlen(junction->referral_list[i].alternate_path)+1)*2;
1028         }
1029
1030         pdata = (char *)SMB_REALLOC(pdata,reply_size);
1031         if(pdata == NULL) {
1032                 DEBUG(0,("version3 referral setup:"
1033                         "malloc failed for Realloc!\n"));
1034                 return -1;
1035         }
1036         *ppdata = pdata;
1037
1038         /* create the header */
1039         SSVAL(pdata,0,consumedcnt * 2); /* path consumed */
1040         SSVAL(pdata,2,junction->referral_count); /* number of referral */
1041         if(self_referral) {
1042                 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
1043         } else {
1044                 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
1045         }
1046
1047         /* copy in the reqpaths */
1048         memcpy(pdata+uni_reqpathoffset1,uni_reqpath,reqpathlen);
1049         memcpy(pdata+uni_reqpathoffset2,uni_reqpath,reqpathlen);
1050
1051         offset = 8;
1052         for(i=0;i<junction->referral_count;i++) {
1053                 struct referral* ref = &(junction->referral_list[i]);
1054                 int unilen;
1055
1056                 SSVAL(pdata,offset,3); /* version 3 */
1057                 SSVAL(pdata,offset+2,VERSION3_REFERRAL_SIZE);
1058                 if(self_referral) {
1059                         SSVAL(pdata,offset+4,1);
1060                 } else {
1061                         SSVAL(pdata,offset+4,0);
1062                 }
1063
1064                 /* ref_flags :use path_consumed bytes? */
1065                 SSVAL(pdata,offset+6,0);
1066                 SIVAL(pdata,offset+8,ref->ttl);
1067
1068                 SSVAL(pdata,offset+12,uni_reqpathoffset1-offset);
1069                 SSVAL(pdata,offset+14,uni_reqpathoffset2-offset);
1070                 /* copy referred path into current offset */
1071                 unilen = rpcstr_push(pdata+uni_curroffset,ref->alternate_path,
1072                                      sizeof(pstring),
1073                                      STR_UNICODE | STR_TERMINATE);
1074                 SSVAL(pdata,offset+16,uni_curroffset-offset);
1075                 /* copy 0x10 bytes of 00's in the ServiceSite GUID */
1076                 memset(pdata+offset+18,'\0',16);
1077
1078                 uni_curroffset += unilen;
1079                 offset += VERSION3_REFERRAL_SIZE;
1080         }
1081         return reply_size;
1082 }
1083
1084 /******************************************************************
1085  Set up the DFS referral for the dfs pathname. This call returns
1086  the amount of the path covered by this server, and where the
1087  client should be redirected to. This is the meat of the
1088  TRANS2_GET_DFS_REFERRAL call.
1089 ******************************************************************/
1090
1091 int setup_dfs_referral(connection_struct *orig_conn,
1092                         const char *dfs_path,
1093                         int max_referral_level,
1094                         char **ppdata, NTSTATUS *pstatus)
1095 {
1096         struct junction_map *junction = NULL;
1097         int consumedcnt = 0;
1098         BOOL self_referral = False;
1099         int reply_size = 0;
1100         char *pathnamep = NULL;
1101         char *local_dfs_path = NULL;
1102         TALLOC_CTX *ctx;
1103
1104         if (!(ctx=talloc_init("setup_dfs_referral"))) {
1105                 *pstatus = NT_STATUS_NO_MEMORY;
1106                 return -1;
1107         }
1108
1109         /* get the junction entry */
1110         if (!dfs_path) {
1111                 talloc_destroy(ctx);
1112                 *pstatus = NT_STATUS_NOT_FOUND;
1113                 return -1;
1114         }
1115
1116         /*
1117          * Trim pathname sent by client so it begins with only one backslash.
1118          * Two backslashes confuse some dfs clients
1119          */
1120
1121         local_dfs_path = talloc_strdup(ctx,dfs_path);
1122         if (!local_dfs_path) {
1123                 *pstatus = NT_STATUS_NO_MEMORY;
1124                 talloc_destroy(ctx);
1125                 return -1;
1126         }
1127         pathnamep = local_dfs_path;
1128         while (IS_DIRECTORY_SEP(pathnamep[0]) &&
1129                         IS_DIRECTORY_SEP(pathnamep[1])) {
1130                 pathnamep++;
1131         }
1132
1133         junction = TALLOC_ZERO_P(ctx, struct junction_map);
1134         if (!junction) {
1135                 *pstatus = NT_STATUS_NO_MEMORY;
1136                 talloc_destroy(ctx);
1137                 return -1;
1138         }
1139
1140         /* The following call can change cwd. */
1141         *pstatus = get_referred_path(ctx, pathnamep, junction,
1142                         &consumedcnt, &self_referral);
1143         if (!NT_STATUS_IS_OK(*pstatus)) {
1144                 vfs_ChDir(orig_conn,orig_conn->connectpath);
1145                 talloc_destroy(ctx);
1146                 return -1;
1147         }
1148         vfs_ChDir(orig_conn,orig_conn->connectpath);
1149
1150         if (!self_referral) {
1151                 pathnamep[consumedcnt] = '\0';
1152
1153                 if( DEBUGLVL( 3 ) ) {
1154                         int i=0;
1155                         dbgtext("setup_dfs_referral: Path %s to "
1156                                 "alternate path(s):",
1157                                 pathnamep);
1158                         for(i=0;i<junction->referral_count;i++)
1159                                 dbgtext(" %s",
1160                                 junction->referral_list[i].alternate_path);
1161                         dbgtext(".\n");
1162                 }
1163         }
1164
1165         /* create the referral depeding on version */
1166         DEBUG(10,("max_referral_level :%d\n",max_referral_level));
1167
1168         if (max_referral_level < 2) {
1169                 max_referral_level = 2;
1170         }
1171         if (max_referral_level > 3) {
1172                 max_referral_level = 3;
1173         }
1174
1175         switch(max_referral_level) {
1176         case 2:
1177                 reply_size = setup_ver2_dfs_referral(pathnamep,
1178                                         ppdata, junction,
1179                                         consumedcnt, self_referral);
1180                 break;
1181         case 3:
1182                 reply_size = setup_ver3_dfs_referral(pathnamep, ppdata,
1183                                         junction, consumedcnt, self_referral);
1184                 break;
1185         default:
1186                 DEBUG(0,("setup_dfs_referral: Invalid dfs referral "
1187                         "version: %d\n",
1188                         max_referral_level));
1189                 talloc_destroy(ctx);
1190                 *pstatus = NT_STATUS_INVALID_LEVEL;
1191                 return -1;
1192         }
1193
1194         if (DEBUGLVL(10)) {
1195                 DEBUGADD(0,("DFS Referral pdata:\n"));
1196                 dump_data(0,(uint8 *)*ppdata,reply_size);
1197         }
1198
1199         talloc_destroy(ctx);
1200         *pstatus = NT_STATUS_OK;
1201         return reply_size;
1202 }
1203
1204 /**********************************************************************
1205  The following functions are called by the NETDFS RPC pipe functions
1206  **********************************************************************/
1207
1208 /*********************************************************************
1209  Creates a junction structure from a DFS pathname
1210 **********************************************************************/
1211
1212 BOOL create_junction(TALLOC_CTX *ctx,
1213                 const char *dfs_path,
1214                 struct junction_map *jucn)
1215 {
1216         int snum;
1217         BOOL dummy;
1218         struct dfs_path *pdp = TALLOC_P(ctx,struct dfs_path);
1219         NTSTATUS status;
1220
1221         if (!pdp) {
1222                 return False;
1223         }
1224         status = parse_dfs_path(dfs_path, False, pdp, &dummy);
1225         if (!NT_STATUS_IS_OK(status)) {
1226                 return False;
1227         }
1228
1229         /* check if path is dfs : validate first token */
1230         if (!is_myname_or_ipaddr(pdp->hostname)) {
1231                 DEBUG(4,("create_junction: Invalid hostname %s "
1232                         "in dfs path %s\n",
1233                         pdp->hostname, dfs_path));
1234                 TALLOC_FREE(pdp);
1235                 return False;
1236         }
1237
1238         /* Check for a non-DFS share */
1239         snum = lp_servicenumber(pdp->servicename);
1240
1241         if(snum < 0 || !lp_msdfs_root(snum)) {
1242                 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1243                         pdp->servicename));
1244                 TALLOC_FREE(pdp);
1245                 return False;
1246         }
1247
1248         jucn->service_name = talloc_strdup(ctx, pdp->servicename);
1249         jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
1250         jucn->comment = talloc_strdup(ctx, lp_comment(snum));
1251
1252         TALLOC_FREE(pdp);
1253         if (!jucn->service_name || !jucn->volume_name || ! jucn->comment) {
1254                 return False;
1255         }
1256         return True;
1257 }
1258
1259 /**********************************************************************
1260  Forms a valid Unix pathname from the junction
1261  **********************************************************************/
1262
1263 static BOOL junction_to_local_path(const struct junction_map *jucn,
1264                                 char **pp_path_out,
1265                                 connection_struct *conn_out)
1266 {
1267         int snum;
1268
1269         snum = lp_servicenumber(jucn->service_name);
1270         if(snum < 0) {
1271                 return False;
1272         }
1273         if (!NT_STATUS_IS_OK(create_conn_struct(conn_out, snum,
1274                                         lp_pathname(snum)))) {
1275                 return False;
1276         }
1277
1278         *pp_path_out = talloc_asprintf(conn_out->mem_ctx,
1279                         "%s/%s",
1280                         lp_pathname(snum),
1281                         jucn->volume_name);
1282         if (!*pp_path_out) {
1283                 return False;
1284         }
1285         return True;
1286 }
1287
1288 BOOL create_msdfs_link(const struct junction_map *jucn,
1289                 BOOL exists)
1290 {
1291         char *path = NULL;
1292         char *msdfs_link = NULL;
1293         connection_struct conns;
1294         connection_struct *conn = &conns;
1295         int i=0;
1296         BOOL insert_comma = False;
1297         BOOL ret = False;
1298
1299         ZERO_STRUCT(conns);
1300
1301         if(!junction_to_local_path(jucn, &path, conn)) {
1302                 return False;
1303         }
1304
1305         /* Form the msdfs_link contents */
1306         msdfs_link = talloc_strdup(conn->mem_ctx, "msdfs:");
1307         if (!msdfs_link) {
1308                 goto out;
1309         }
1310         for(i=0; i<jucn->referral_count; i++) {
1311                 char *refpath = jucn->referral_list[i].alternate_path;
1312
1313                 /* Alternate paths always use Windows separators. */
1314                 trim_char(refpath, '\\', '\\');
1315                 if(*refpath == '\0') {
1316                         if (i == 0) {
1317                                 insert_comma = False;
1318                         }
1319                         continue;
1320                 }
1321                 if (i > 0 && insert_comma) {
1322                         msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1323                                         ",%s",
1324                                         refpath);
1325                 } else {
1326                         msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1327                                         "%s",
1328                                         refpath);
1329                 }
1330
1331                 if (!msdfs_link) {
1332                         goto out;
1333                 }
1334                 if (!insert_comma) {
1335                         insert_comma = True;
1336                 }
1337         }
1338
1339         DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
1340                 path, msdfs_link));
1341
1342         if(exists) {
1343                 if(SMB_VFS_UNLINK(conn,path)!=0) {
1344                         goto out;
1345                 }
1346         }
1347
1348         if(SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
1349                 DEBUG(1,("create_msdfs_link: symlink failed "
1350                         "%s -> %s\nError: %s\n", 
1351                         path, msdfs_link, strerror(errno)));
1352                 goto out;
1353         }
1354
1355         ret = True;
1356
1357 out:
1358
1359         conn_free_internal(conn);
1360         return ret;
1361 }
1362
1363 BOOL remove_msdfs_link(const struct junction_map *jucn)
1364 {
1365         char *path = NULL;
1366         connection_struct conns;
1367         connection_struct *conn = &conns;
1368         BOOL ret = False;
1369
1370         ZERO_STRUCT(conns);
1371
1372         if( junction_to_local_path(jucn, &path, conn) ) {
1373                 if( SMB_VFS_UNLINK(conn, path) == 0 ) {
1374                         ret = True;
1375                 }
1376         }
1377
1378         conn_free_internal(conn);
1379         return ret;
1380 }
1381
1382 /*********************************************************************
1383  Return the number of DFS links at the root of this share.
1384 *********************************************************************/
1385
1386 static int count_dfs_links(TALLOC_CTX *ctx, int snum)
1387 {
1388         size_t cnt = 0;
1389         SMB_STRUCT_DIR *dirp = NULL;
1390         char *dname = NULL;
1391         const char *connect_path = lp_pathname(snum);
1392         const char *msdfs_proxy = lp_msdfs_proxy(snum);
1393         connection_struct conn;
1394
1395         ZERO_STRUCT(conn);
1396
1397         if(*connect_path == '\0') {
1398                 return 0;
1399         }
1400
1401         /*
1402          * Fake up a connection struct for the VFS layer.
1403          */
1404
1405         if (!NT_STATUS_IS_OK(create_conn_struct(&conn, snum, connect_path))) {
1406                 return 0;
1407         }
1408
1409         /* Count a link for the msdfs root - convention */
1410         cnt = 1;
1411
1412         /* No more links if this is an msdfs proxy. */
1413         if (*msdfs_proxy != '\0') {
1414                 goto out;
1415         }
1416
1417         /* Now enumerate all dfs links */
1418         dirp = SMB_VFS_OPENDIR(&conn, ".", NULL, 0);
1419         if(!dirp) {
1420                 goto out;
1421         }
1422
1423         while ((dname = vfs_readdirname(&conn, dirp)) != NULL) {
1424                 if (is_msdfs_link(&conn,
1425                                 dname,
1426                                 NULL)) {
1427                         cnt++;
1428                 }
1429         }
1430
1431         SMB_VFS_CLOSEDIR(&conn,dirp);
1432
1433 out:
1434
1435         conn_free_internal(&conn);
1436         return cnt;
1437 }
1438
1439 /*********************************************************************
1440 *********************************************************************/
1441
1442 static int form_junctions(TALLOC_CTX *ctx,
1443                                 int snum,
1444                                 struct junction_map *jucn,
1445                                 size_t jn_remain)
1446 {
1447         size_t cnt = 0;
1448         SMB_STRUCT_DIR *dirp = NULL;
1449         char *dname = NULL;
1450         const char *connect_path = lp_pathname(snum);
1451         char *service_name = lp_servicename(snum);
1452         const char *msdfs_proxy = lp_msdfs_proxy(snum);
1453         connection_struct conn;
1454         struct referral *ref = NULL;
1455
1456         ZERO_STRUCT(conn);
1457
1458         if (jn_remain == 0) {
1459                 return 0;
1460         }
1461
1462         if(*connect_path == '\0') {
1463                 return 0;
1464         }
1465
1466         /*
1467          * Fake up a connection struct for the VFS layer.
1468          */
1469
1470         if (!NT_STATUS_IS_OK(create_conn_struct(&conn, snum, connect_path))) {
1471                 return 0;
1472         }
1473
1474         /* form a junction for the msdfs root - convention
1475            DO NOT REMOVE THIS: NT clients will not work with us
1476            if this is not present
1477         */
1478         jucn[cnt].service_name = talloc_strdup(ctx,service_name);
1479         jucn[cnt].volume_name = talloc_strdup(ctx, "");
1480         if (!jucn[cnt].service_name || jucn[cnt].volume_name) {
1481                 goto out;
1482         }
1483         jucn[cnt].referral_count = 1;
1484
1485         ref = jucn[cnt].referral_list = TALLOC_ZERO_P(ctx, struct referral);
1486         if (jucn[cnt].referral_list == NULL) {
1487                 goto out;
1488         }
1489
1490         ref->proximity = 0;
1491         ref->ttl = REFERRAL_TTL;
1492         if (*msdfs_proxy != '\0') {
1493                 ref->alternate_path = talloc_strdup(ctx,
1494                                                 msdfs_proxy);
1495         } else {
1496                 ref->alternate_path = talloc_asprintf(ctx,
1497                         "\\\\%s\\%s",
1498                         get_local_machine_name(),
1499                         service_name);
1500         }
1501
1502         if (!ref->alternate_path) {
1503                 goto out;
1504         }
1505         cnt++;
1506
1507         /* Don't enumerate if we're an msdfs proxy. */
1508         if (*msdfs_proxy != '\0') {
1509                 goto out;
1510         }
1511
1512         /* Now enumerate all dfs links */
1513         dirp = SMB_VFS_OPENDIR(&conn, ".", NULL, 0);
1514         if(!dirp) {
1515                 goto out;
1516         }
1517
1518         while ((dname = vfs_readdirname(&conn, dirp)) != NULL) {
1519                 char *link_target = NULL;
1520                 if (cnt >= jn_remain) {
1521                         SMB_VFS_CLOSEDIR(&conn,dirp);
1522                         DEBUG(2, ("form_junctions: ran out of MSDFS "
1523                                 "junction slots"));
1524                         goto out;
1525                 }
1526                 if (is_msdfs_link_internal(ctx,
1527                                         &conn,
1528                                         dname, &link_target,
1529                                         NULL)) {
1530                         if (parse_msdfs_symlink(ctx,
1531                                         link_target,
1532                                         &jucn[cnt].referral_list,
1533                                         &jucn[cnt].referral_count)) {
1534
1535                                 jucn[cnt].service_name = talloc_strdup(ctx,
1536                                                                 service_name);
1537                                 jucn[cnt].volume_name = talloc_strdup(ctx,
1538                                                                 dname);
1539                                 if (!jucn[cnt].service_name ||
1540                                                 !jucn[cnt].volume_name) {
1541                                         goto out;
1542                                 }
1543                                 cnt++;
1544                         }
1545                 }
1546         }
1547
1548 out:
1549
1550         if (dirp) {
1551                 SMB_VFS_CLOSEDIR(&conn,dirp);
1552         }
1553
1554         conn_free_internal(&conn);
1555         return cnt;
1556 }
1557
1558 struct junction_map *enum_msdfs_links(TALLOC_CTX *ctx, size_t *p_num_jn)
1559 {
1560         struct junction_map *jn = NULL;
1561         int i=0;
1562         size_t jn_count = 0;
1563         int sharecount = 0;
1564
1565         *p_num_jn = 0;
1566         if(!lp_host_msdfs()) {
1567                 return NULL;
1568         }
1569
1570         /* Ensure all the usershares are loaded. */
1571         become_root();
1572         load_registry_shares();
1573         sharecount = load_usershare_shares();
1574         unbecome_root();
1575
1576         for(i=0;i < sharecount;i++) {
1577                 if(lp_msdfs_root(i)) {
1578                         jn_count += count_dfs_links(ctx, i);
1579                 }
1580         }
1581         if (jn_count == 0) {
1582                 return NULL;
1583         }
1584         jn = TALLOC_ARRAY(ctx,  struct junction_map, jn_count);
1585         if (!jn) {
1586                 return NULL;
1587         }
1588         for(i=0; i < sharecount; i++) {
1589                 if (*p_num_jn >= jn_count) {
1590                         break;
1591                 }
1592                 if(lp_msdfs_root(i)) {
1593                         *p_num_jn += form_junctions(ctx, i,
1594                                         &jn[*p_num_jn],
1595                                         jn_count - *p_num_jn);
1596                 }
1597         }
1598         return jn;
1599 }
1600
1601 /******************************************************************************
1602  Core function to resolve a dfs pathname.
1603 ******************************************************************************/
1604
1605 NTSTATUS resolve_dfspath(TALLOC_CTX *ctx,
1606                         connection_struct *conn,
1607                         BOOL dfs_pathnames,
1608                         const char *name_in,
1609                         char **pp_name_out)
1610 {
1611         NTSTATUS status = NT_STATUS_OK;
1612         BOOL dummy;
1613         if (dfs_pathnames) {
1614                 status = dfs_redirect(ctx,
1615                                         conn,
1616                                         name_in,
1617                                         False,
1618                                         pp_name_out,
1619                                         &dummy);
1620         } else {
1621                 /*
1622                  * Cheat and just return a copy of the in ptr.
1623                  * Once srvstr_get_path() uses talloc it'll
1624                  * be a talloced ptr anyway.
1625                  */
1626                 *pp_name_out = CONST_DISCARD(char *,name_in);
1627         }
1628         return status;
1629 }
1630
1631 /******************************************************************************
1632  Core function to resolve a dfs pathname possibly containing a wildcard.
1633  This function is identical to the above except for the BOOL param to
1634  dfs_redirect but I need this to be separate so it's really clear when
1635  we're allowing wildcards and when we're not. JRA.
1636 ******************************************************************************/
1637
1638 NTSTATUS resolve_dfspath_wcard(TALLOC_CTX *ctx,
1639                                 connection_struct *conn,
1640                                 BOOL dfs_pathnames,
1641                                 const char *name_in,
1642                                 char **pp_name_out,
1643                                 BOOL *ppath_contains_wcard)
1644 {
1645         NTSTATUS status = NT_STATUS_OK;
1646         if (dfs_pathnames) {
1647                 status = dfs_redirect(ctx,
1648                                         conn,
1649                                         name_in,
1650                                         True,
1651                                         pp_name_out,
1652                                         ppath_contains_wcard);
1653         } else {
1654                 /*
1655                  * Cheat and just return a copy of the in ptr.
1656                  * Once srvstr_get_path() uses talloc it'll
1657                  * be a talloced ptr anyway.
1658                  */
1659                 *pp_name_out = CONST_DISCARD(char *,name_in);
1660         }
1661         return status;
1662 }