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