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