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