Merge branch 'master' of /home/tridge/samba/git/combined
[Samba/aatanasov.git] / source3 / smbd / msdfs.c
blob4a338fa5904707c1eb7ae2fbbe5ff29610081c26
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
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.
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.
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/>.
23 #define DBGC_CLASS DBGC_MSDFS
24 #include "includes.h"
25 #include "smbd/globals.h"
27 /**********************************************************************
28 Parse a DFS pathname of the form \hostname\service\reqpath
29 into the dfs_path structure.
30 If POSIX pathnames is true, the pathname may also be of the
31 form /hostname/service/reqpath.
32 We cope with either here.
34 Unfortunately, due to broken clients who might set the
35 SVAL(inbuf,smb_flg2) & FLAGS2_DFS_PATHNAMES bit and then
36 send a local path, we have to cope with that too....
38 If conn != NULL then ensure the provided service is
39 the one pointed to by the connection.
41 This version does everything using pointers within one copy of the
42 pathname string, talloced on the struct dfs_path pointer (which
43 must be talloced). This may be too clever to live....
44 JRA.
45 **********************************************************************/
47 static NTSTATUS parse_dfs_path(connection_struct *conn,
48 const char *pathname,
49 bool allow_wcards,
50 struct dfs_path *pdp, /* MUST BE TALLOCED */
51 bool *ppath_contains_wcard)
53 char *pathname_local;
54 char *p,*temp;
55 char *servicename;
56 char *eos_ptr;
57 NTSTATUS status = NT_STATUS_OK;
58 char sepchar;
60 ZERO_STRUCTP(pdp);
63 * This is the only talloc we should need to do
64 * on the struct dfs_path. All the pointers inside
65 * it should point to offsets within this string.
68 pathname_local = talloc_strdup(pdp, pathname);
69 if (!pathname_local) {
70 return NT_STATUS_NO_MEMORY;
72 /* Get a pointer to the terminating '\0' */
73 eos_ptr = &pathname_local[strlen(pathname_local)];
74 p = temp = pathname_local;
76 pdp->posix_path = (lp_posix_pathnames() && *pathname == '/');
78 sepchar = pdp->posix_path ? '/' : '\\';
80 if (*pathname != sepchar) {
81 DEBUG(10,("parse_dfs_path: path %s doesn't start with %c\n",
82 pathname, sepchar ));
84 * Possibly client sent a local path by mistake.
85 * Try and convert to a local path.
88 pdp->hostname = eos_ptr; /* "" */
89 pdp->servicename = eos_ptr; /* "" */
91 /* We've got no info about separators. */
92 pdp->posix_path = lp_posix_pathnames();
93 p = temp;
94 DEBUG(10,("parse_dfs_path: trying to convert %s to a "
95 "local path\n",
96 temp));
97 goto local_path;
101 * Safe to use on talloc'ed string as it only shrinks.
102 * It also doesn't affect the eos_ptr.
104 trim_char(temp,sepchar,sepchar);
106 DEBUG(10,("parse_dfs_path: temp = |%s| after trimming %c's\n",
107 temp, sepchar));
109 /* Now tokenize. */
110 /* Parse out hostname. */
111 p = strchr_m(temp,sepchar);
112 if(p == NULL) {
113 DEBUG(10,("parse_dfs_path: can't parse hostname from path %s\n",
114 temp));
116 * Possibly client sent a local path by mistake.
117 * Try and convert to a local path.
120 pdp->hostname = eos_ptr; /* "" */
121 pdp->servicename = eos_ptr; /* "" */
123 p = temp;
124 DEBUG(10,("parse_dfs_path: trying to convert %s "
125 "to a local path\n",
126 temp));
127 goto local_path;
129 *p = '\0';
130 pdp->hostname = temp;
132 DEBUG(10,("parse_dfs_path: hostname: %s\n",pdp->hostname));
134 /* Parse out servicename. */
135 servicename = p+1;
136 p = strchr_m(servicename,sepchar);
137 if (p) {
138 *p = '\0';
141 /* Is this really our servicename ? */
142 if (conn && !( strequal(servicename, lp_servicename(SNUM(conn)))
143 || (strequal(servicename, HOMES_NAME)
144 && strequal(lp_servicename(SNUM(conn)),
145 get_current_username()) )) ) {
146 DEBUG(10,("parse_dfs_path: %s is not our servicename\n",
147 servicename));
150 * Possibly client sent a local path by mistake.
151 * Try and convert to a local path.
154 pdp->hostname = eos_ptr; /* "" */
155 pdp->servicename = eos_ptr; /* "" */
157 /* Repair the path - replace the sepchar's
158 we nulled out */
159 servicename--;
160 *servicename = sepchar;
161 if (p) {
162 *p = sepchar;
165 p = temp;
166 DEBUG(10,("parse_dfs_path: trying to convert %s "
167 "to a local path\n",
168 temp));
169 goto local_path;
172 pdp->servicename = servicename;
174 DEBUG(10,("parse_dfs_path: servicename: %s\n",pdp->servicename));
176 if(p == NULL) {
177 /* Client sent self referral \server\share. */
178 pdp->reqpath = eos_ptr; /* "" */
179 return NT_STATUS_OK;
182 p++;
184 local_path:
186 *ppath_contains_wcard = False;
188 pdp->reqpath = p;
190 /* Rest is reqpath. */
191 if (pdp->posix_path) {
192 status = check_path_syntax_posix(pdp->reqpath);
193 } else {
194 if (allow_wcards) {
195 status = check_path_syntax_wcard(pdp->reqpath,
196 ppath_contains_wcard);
197 } else {
198 status = check_path_syntax(pdp->reqpath);
202 if (!NT_STATUS_IS_OK(status)) {
203 DEBUG(10,("parse_dfs_path: '%s' failed with %s\n",
204 p, nt_errstr(status) ));
205 return status;
208 DEBUG(10,("parse_dfs_path: rest of the path: %s\n",pdp->reqpath));
209 return NT_STATUS_OK;
212 /********************************************************
213 Fake up a connection struct for the VFS layer.
214 Note this CHANGES CWD !!!! JRA.
215 *********************************************************/
217 NTSTATUS create_conn_struct(TALLOC_CTX *ctx,
218 connection_struct **pconn,
219 int snum,
220 const char *path,
221 struct auth_serversupplied_info *server_info,
222 char **poldcwd)
224 connection_struct *conn;
225 char *connpath;
226 char *oldcwd;
228 conn = TALLOC_ZERO_P(ctx, connection_struct);
229 if (conn == NULL) {
230 return NT_STATUS_NO_MEMORY;
233 connpath = talloc_strdup(conn, path);
234 if (!connpath) {
235 TALLOC_FREE(conn);
236 return NT_STATUS_NO_MEMORY;
238 connpath = talloc_string_sub(conn,
239 connpath,
240 "%S",
241 lp_servicename(snum));
242 if (!connpath) {
243 TALLOC_FREE(conn);
244 return NT_STATUS_NO_MEMORY;
247 /* needed for smbd_vfs_init() */
249 if (!(conn->params = TALLOC_ZERO_P(conn, struct share_params))) {
250 DEBUG(0, ("TALLOC failed\n"));
251 TALLOC_FREE(conn);
252 return NT_STATUS_NO_MEMORY;
255 conn->params->service = snum;
257 if (server_info != NULL) {
258 conn->server_info = copy_serverinfo(conn, server_info);
259 if (conn->server_info == NULL) {
260 DEBUG(0, ("copy_serverinfo failed\n"));
261 TALLOC_FREE(conn);
262 return NT_STATUS_NO_MEMORY;
266 set_conn_connectpath(conn, connpath);
268 if (!smbd_vfs_init(conn)) {
269 NTSTATUS status = map_nt_error_from_unix(errno);
270 DEBUG(0,("create_conn_struct: smbd_vfs_init failed.\n"));
271 conn_free(conn);
272 return status;
275 conn->fs_capabilities = SMB_VFS_FS_CAPABILITIES(conn, &conn->ts_res);
278 * Windows seems to insist on doing trans2getdfsreferral() calls on
279 * the IPC$ share as the anonymous user. If we try to chdir as that
280 * user we will fail.... WTF ? JRA.
283 oldcwd = vfs_GetWd(ctx, conn);
284 if (oldcwd == NULL) {
285 NTSTATUS status = map_nt_error_from_unix(errno);
286 DEBUG(3, ("vfs_GetWd failed: %s\n", strerror(errno)));
287 conn_free(conn);
288 return status;
291 if (vfs_ChDir(conn,conn->connectpath) != 0) {
292 NTSTATUS status = map_nt_error_from_unix(errno);
293 DEBUG(3,("create_conn_struct: Can't ChDir to new conn path %s. "
294 "Error was %s\n",
295 conn->connectpath, strerror(errno) ));
296 conn_free(conn);
297 return status;
300 *pconn = conn;
301 *poldcwd = oldcwd;
303 return NT_STATUS_OK;
306 /**********************************************************************
307 Parse the contents of a symlink to verify if it is an msdfs referral
308 A valid referral is of the form:
310 msdfs:server1\share1,server2\share2
311 msdfs:server1\share1\pathname,server2\share2\pathname
312 msdfs:server1/share1,server2/share2
313 msdfs:server1/share1/pathname,server2/share2/pathname.
315 Note that the alternate paths returned here must be of the canonicalized
316 form:
318 \server\share or
319 \server\share\path\to\file,
321 even in posix path mode. This is because we have no knowledge if the
322 server we're referring to understands posix paths.
323 **********************************************************************/
325 static bool parse_msdfs_symlink(TALLOC_CTX *ctx,
326 const char *target,
327 struct referral **preflist,
328 int *refcount)
330 char *temp = NULL;
331 char *prot;
332 char **alt_path = NULL;
333 int count = 0, i;
334 struct referral *reflist;
335 char *saveptr;
337 temp = talloc_strdup(ctx, target);
338 if (!temp) {
339 return False;
341 prot = strtok_r(temp, ":", &saveptr);
342 if (!prot) {
343 DEBUG(0,("parse_msdfs_symlink: invalid path !\n"));
344 return False;
347 alt_path = TALLOC_ARRAY(ctx, char *, MAX_REFERRAL_COUNT);
348 if (!alt_path) {
349 return False;
352 /* parse out the alternate paths */
353 while((count<MAX_REFERRAL_COUNT) &&
354 ((alt_path[count] = strtok_r(NULL, ",", &saveptr)) != NULL)) {
355 count++;
358 DEBUG(10,("parse_msdfs_symlink: count=%d\n", count));
360 if (count) {
361 reflist = *preflist = TALLOC_ZERO_ARRAY(ctx,
362 struct referral, count);
363 if(reflist == NULL) {
364 TALLOC_FREE(alt_path);
365 return False;
367 } else {
368 reflist = *preflist = NULL;
371 for(i=0;i<count;i++) {
372 char *p;
374 /* Canonicalize link target.
375 * Replace all /'s in the path by a \ */
376 string_replace(alt_path[i], '/', '\\');
378 /* Remove leading '\\'s */
379 p = alt_path[i];
380 while (*p && (*p == '\\')) {
381 p++;
384 reflist[i].alternate_path = talloc_asprintf(ctx,
385 "\\%s",
387 if (!reflist[i].alternate_path) {
388 return False;
391 reflist[i].proximity = 0;
392 reflist[i].ttl = REFERRAL_TTL;
393 DEBUG(10, ("parse_msdfs_symlink: Created alt path: %s\n",
394 reflist[i].alternate_path));
397 *refcount = count;
399 TALLOC_FREE(alt_path);
400 return True;
403 /**********************************************************************
404 Returns true if the unix path is a valid msdfs symlink and also
405 returns the target string from inside the link.
406 **********************************************************************/
408 static bool is_msdfs_link_internal(TALLOC_CTX *ctx,
409 connection_struct *conn,
410 const char *path,
411 char **pp_link_target,
412 SMB_STRUCT_STAT *sbufp)
414 int referral_len = 0;
415 #if defined(HAVE_BROKEN_READLINK)
416 char link_target_buf[PATH_MAX];
417 #else
418 char link_target_buf[7];
419 #endif
420 size_t bufsize = 0;
421 char *link_target = NULL;
422 struct smb_filename *smb_fname = NULL;
423 NTSTATUS status;
425 if (pp_link_target) {
426 bufsize = 1024;
427 link_target = TALLOC_ARRAY(ctx, char, bufsize);
428 if (!link_target) {
429 return False;
431 *pp_link_target = link_target;
432 } else {
433 bufsize = sizeof(link_target_buf);
434 link_target = link_target_buf;
437 status = create_synthetic_smb_fname(talloc_tos(), path, NULL, NULL,
438 &smb_fname);
439 if (!NT_STATUS_IS_OK(status)) {
440 goto err;
443 if (SMB_VFS_LSTAT(conn, smb_fname) != 0) {
444 DEBUG(5,("is_msdfs_link_read_target: %s does not exist.\n",
445 path));
446 TALLOC_FREE(smb_fname);
447 goto err;
449 if (!S_ISLNK(smb_fname->st.st_ex_mode)) {
450 DEBUG(5,("is_msdfs_link_read_target: %s is not a link.\n",
451 path));
452 TALLOC_FREE(smb_fname);
453 goto err;
455 if (sbufp != NULL) {
456 *sbufp = smb_fname->st;
458 TALLOC_FREE(smb_fname);
460 referral_len = SMB_VFS_READLINK(conn, path, link_target, bufsize - 1);
461 if (referral_len == -1) {
462 DEBUG(0,("is_msdfs_link_read_target: Error reading "
463 "msdfs link %s: %s\n",
464 path, strerror(errno)));
465 goto err;
467 link_target[referral_len] = '\0';
469 DEBUG(5,("is_msdfs_link_internal: %s -> %s\n",path,
470 link_target));
472 if (!strnequal(link_target, "msdfs:", 6)) {
473 goto err;
475 return True;
477 err:
479 if (link_target != link_target_buf) {
480 TALLOC_FREE(link_target);
482 return False;
485 /**********************************************************************
486 Returns true if the unix path is a valid msdfs symlink.
487 **********************************************************************/
489 bool is_msdfs_link(connection_struct *conn,
490 const char *path,
491 SMB_STRUCT_STAT *sbufp)
493 return is_msdfs_link_internal(talloc_tos(),
494 conn,
495 path,
496 NULL,
497 sbufp);
500 /*****************************************************************
501 Used by other functions to decide if a dfs path is remote,
502 and to get the list of referred locations for that remote path.
504 search_flag: For findfirsts, dfs links themselves are not
505 redirected, but paths beyond the links are. For normal smb calls,
506 even dfs links need to be redirected.
508 consumedcntp: how much of the dfs path is being redirected. the client
509 should try the remaining path on the redirected server.
511 If this returns NT_STATUS_PATH_NOT_COVERED the contents of the msdfs
512 link redirect are in targetpath.
513 *****************************************************************/
515 static NTSTATUS dfs_path_lookup(TALLOC_CTX *ctx,
516 connection_struct *conn,
517 const char *dfspath, /* Incoming complete dfs path */
518 const struct dfs_path *pdp, /* Parsed out
519 server+share+extrapath. */
520 bool search_flag, /* Called from a findfirst ? */
521 int *consumedcntp,
522 char **pp_targetpath)
524 char *p = NULL;
525 char *q = NULL;
526 NTSTATUS status;
527 struct smb_filename *smb_fname = NULL;
528 char *canon_dfspath = NULL; /* Canonicalized dfs path. (only '/'
529 components). */
531 DEBUG(10,("dfs_path_lookup: Conn path = %s reqpath = %s\n",
532 conn->connectpath, pdp->reqpath));
535 * Note the unix path conversion here we're doing we can
536 * throw away. We're looking for a symlink for a dfs
537 * resolution, if we don't find it we'll do another
538 * unix_convert later in the codepath.
539 * If we needed to remember what we'd resolved in
540 * dp->reqpath (as the original code did) we'd
541 * copy (localhost, dp->reqpath) on any code
542 * path below that returns True - but I don't
543 * think this is needed. JRA.
546 status = unix_convert(ctx, conn, pdp->reqpath, &smb_fname,
547 search_flag ? UCF_ALWAYS_ALLOW_WCARD_LCOMP : 0);
549 if (!NT_STATUS_IS_OK(status)) {
550 if (!NT_STATUS_EQUAL(status,
551 NT_STATUS_OBJECT_PATH_NOT_FOUND)) {
552 return status;
555 /* Create an smb_fname to use below. */
556 status = create_synthetic_smb_fname(ctx, pdp->reqpath, NULL,
557 NULL, &smb_fname);
558 if (!NT_STATUS_IS_OK(status)) {
559 return status;
563 /* Optimization - check if we can redirect the whole path. */
565 if (is_msdfs_link_internal(ctx, conn, smb_fname->base_name,
566 pp_targetpath, NULL)) {
567 if (search_flag) {
568 DEBUG(6,("dfs_path_lookup (FindFirst) No redirection "
569 "for dfs link %s.\n", dfspath));
570 status = NT_STATUS_OK;
571 goto out;
574 DEBUG(6,("dfs_path_lookup: %s resolves to a "
575 "valid dfs link %s.\n", dfspath,
576 pp_targetpath ? *pp_targetpath : ""));
578 if (consumedcntp) {
579 *consumedcntp = strlen(dfspath);
581 status = NT_STATUS_PATH_NOT_COVERED;
582 goto out;
585 /* Prepare to test only for '/' components in the given path,
586 * so if a Windows path replace all '\\' characters with '/'.
587 * For a POSIX DFS path we know all separators are already '/'. */
589 canon_dfspath = talloc_strdup(ctx, dfspath);
590 if (!canon_dfspath) {
591 status = NT_STATUS_NO_MEMORY;
592 goto out;
594 if (!pdp->posix_path) {
595 string_replace(canon_dfspath, '\\', '/');
599 * localpath comes out of unix_convert, so it has
600 * no trailing backslash. Make sure that canon_dfspath hasn't either.
601 * Fix for bug #4860 from Jan Martin <Jan.Martin@rwedea.com>.
604 trim_char(canon_dfspath,0,'/');
607 * Redirect if any component in the path is a link.
608 * We do this by walking backwards through the
609 * local path, chopping off the last component
610 * in both the local path and the canonicalized
611 * DFS path. If we hit a DFS link then we're done.
614 p = strrchr_m(smb_fname->base_name, '/');
615 if (consumedcntp) {
616 q = strrchr_m(canon_dfspath, '/');
619 while (p) {
620 *p = '\0';
621 if (q) {
622 *q = '\0';
625 if (is_msdfs_link_internal(ctx, conn,
626 smb_fname->base_name, pp_targetpath,
627 NULL)) {
628 DEBUG(4, ("dfs_path_lookup: Redirecting %s because "
629 "parent %s is dfs link\n", dfspath,
630 smb_fname_str_dbg(smb_fname)));
632 if (consumedcntp) {
633 *consumedcntp = strlen(canon_dfspath);
634 DEBUG(10, ("dfs_path_lookup: Path consumed: %s "
635 "(%d)\n",
636 canon_dfspath,
637 *consumedcntp));
640 status = NT_STATUS_PATH_NOT_COVERED;
641 goto out;
644 /* Step back on the filesystem. */
645 p = strrchr_m(smb_fname->base_name, '/');
647 if (consumedcntp) {
648 /* And in the canonicalized dfs path. */
649 q = strrchr_m(canon_dfspath, '/');
653 status = NT_STATUS_OK;
654 out:
655 TALLOC_FREE(smb_fname);
656 return status;
659 /*****************************************************************
660 Decides if a dfs pathname should be redirected or not.
661 If not, the pathname is converted to a tcon-relative local unix path
663 search_wcard_flag: this flag performs 2 functions both related
664 to searches. See resolve_dfs_path() and parse_dfs_path_XX()
665 for details.
667 This function can return NT_STATUS_OK, meaning use the returned path as-is
668 (mapped into a local path).
669 or NT_STATUS_NOT_COVERED meaning return a DFS redirect, or
670 any other NT_STATUS error which is a genuine error to be
671 returned to the client.
672 *****************************************************************/
674 static NTSTATUS dfs_redirect(TALLOC_CTX *ctx,
675 connection_struct *conn,
676 const char *path_in,
677 bool search_wcard_flag,
678 char **pp_path_out,
679 bool *ppath_contains_wcard)
681 NTSTATUS status;
682 struct dfs_path *pdp = TALLOC_P(ctx, struct dfs_path);
684 if (!pdp) {
685 return NT_STATUS_NO_MEMORY;
688 status = parse_dfs_path(conn, path_in, search_wcard_flag, pdp,
689 ppath_contains_wcard);
690 if (!NT_STATUS_IS_OK(status)) {
691 TALLOC_FREE(pdp);
692 return status;
695 if (pdp->reqpath[0] == '\0') {
696 TALLOC_FREE(pdp);
697 *pp_path_out = talloc_strdup(ctx, "");
698 if (!*pp_path_out) {
699 return NT_STATUS_NO_MEMORY;
701 DEBUG(5,("dfs_redirect: self-referral.\n"));
702 return NT_STATUS_OK;
705 /* If dfs pathname for a non-dfs share, convert to tcon-relative
706 path and return OK */
708 if (!lp_msdfs_root(SNUM(conn))) {
709 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
710 TALLOC_FREE(pdp);
711 if (!*pp_path_out) {
712 return NT_STATUS_NO_MEMORY;
714 return NT_STATUS_OK;
717 /* If it looked like a local path (zero hostname/servicename)
718 * just treat as a tcon-relative path. */
720 if (pdp->hostname[0] == '\0' && pdp->servicename[0] == '\0') {
721 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
722 TALLOC_FREE(pdp);
723 if (!*pp_path_out) {
724 return NT_STATUS_NO_MEMORY;
726 return NT_STATUS_OK;
729 if (!( strequal(pdp->servicename, lp_servicename(SNUM(conn)))
730 || (strequal(pdp->servicename, HOMES_NAME)
731 && strequal(lp_servicename(SNUM(conn)),
732 conn->server_info->sanitized_username) )) ) {
734 /* The given sharename doesn't match this connection. */
735 TALLOC_FREE(pdp);
737 return NT_STATUS_OBJECT_PATH_NOT_FOUND;
740 status = dfs_path_lookup(ctx, conn, path_in, pdp,
741 search_wcard_flag, NULL, NULL);
742 if (!NT_STATUS_IS_OK(status)) {
743 if (NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
744 DEBUG(3,("dfs_redirect: Redirecting %s\n", path_in));
745 } else {
746 DEBUG(10,("dfs_redirect: dfs_path_lookup "
747 "failed for %s with %s\n",
748 path_in, nt_errstr(status) ));
750 return status;
753 DEBUG(3,("dfs_redirect: Not redirecting %s.\n", path_in));
755 /* Form non-dfs tcon-relative path */
756 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
757 TALLOC_FREE(pdp);
758 if (!*pp_path_out) {
759 return NT_STATUS_NO_MEMORY;
762 DEBUG(3,("dfs_redirect: Path %s converted to non-dfs path %s\n",
763 path_in,
764 *pp_path_out));
766 return NT_STATUS_OK;
769 /**********************************************************************
770 Return a self referral.
771 **********************************************************************/
773 static NTSTATUS self_ref(TALLOC_CTX *ctx,
774 const char *dfs_path,
775 struct junction_map *jucn,
776 int *consumedcntp,
777 bool *self_referralp)
779 struct referral *ref;
781 *self_referralp = True;
783 jucn->referral_count = 1;
784 if((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) {
785 return NT_STATUS_NO_MEMORY;
788 ref->alternate_path = talloc_strdup(ctx, dfs_path);
789 if (!ref->alternate_path) {
790 return NT_STATUS_NO_MEMORY;
792 ref->proximity = 0;
793 ref->ttl = REFERRAL_TTL;
794 jucn->referral_list = ref;
795 *consumedcntp = strlen(dfs_path);
796 return NT_STATUS_OK;
799 /**********************************************************************
800 Gets valid referrals for a dfs path and fills up the
801 junction_map structure.
802 **********************************************************************/
804 NTSTATUS get_referred_path(TALLOC_CTX *ctx,
805 const char *dfs_path,
806 struct junction_map *jucn,
807 int *consumedcntp,
808 bool *self_referralp)
810 struct connection_struct *conn;
811 char *targetpath = NULL;
812 int snum;
813 NTSTATUS status = NT_STATUS_NOT_FOUND;
814 bool dummy;
815 struct dfs_path *pdp = TALLOC_P(ctx, struct dfs_path);
816 char *oldpath;
818 if (!pdp) {
819 return NT_STATUS_NO_MEMORY;
822 *self_referralp = False;
824 status = parse_dfs_path(NULL, dfs_path, False, pdp, &dummy);
825 if (!NT_STATUS_IS_OK(status)) {
826 return status;
829 jucn->service_name = talloc_strdup(ctx, pdp->servicename);
830 jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
831 if (!jucn->service_name || !jucn->volume_name) {
832 TALLOC_FREE(pdp);
833 return NT_STATUS_NO_MEMORY;
836 /* Verify the share is a dfs root */
837 snum = lp_servicenumber(jucn->service_name);
838 if(snum < 0) {
839 fstring service_name;
840 fstrcpy(service_name, jucn->service_name);
841 if ((snum = find_service(service_name)) < 0) {
842 return NT_STATUS_NOT_FOUND;
844 TALLOC_FREE(jucn->service_name);
845 jucn->service_name = talloc_strdup(ctx, service_name);
846 if (!jucn->service_name) {
847 TALLOC_FREE(pdp);
848 return NT_STATUS_NO_MEMORY;
852 if (!lp_msdfs_root(snum) && (*lp_msdfs_proxy(snum) == '\0')) {
853 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not "
854 "a dfs root.\n",
855 pdp->servicename, dfs_path));
856 TALLOC_FREE(pdp);
857 return NT_STATUS_NOT_FOUND;
861 * Self referrals are tested with a anonymous IPC connection and
862 * a GET_DFS_REFERRAL call to \\server\share. (which means
863 * dp.reqpath[0] points to an empty string). create_conn_struct cd's
864 * into the directory and will fail if it cannot (as the anonymous
865 * user). Cope with this.
868 if (pdp->reqpath[0] == '\0') {
869 char *tmp;
870 struct referral *ref;
872 if (*lp_msdfs_proxy(snum) == '\0') {
873 TALLOC_FREE(pdp);
874 return self_ref(ctx,
875 dfs_path,
876 jucn,
877 consumedcntp,
878 self_referralp);
882 * It's an msdfs proxy share. Redirect to
883 * the configured target share.
886 jucn->referral_count = 1;
887 if ((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) {
888 TALLOC_FREE(pdp);
889 return NT_STATUS_NO_MEMORY;
892 if (!(tmp = talloc_strdup(ctx, lp_msdfs_proxy(snum)))) {
893 TALLOC_FREE(pdp);
894 return NT_STATUS_NO_MEMORY;
897 trim_string(tmp, "\\", 0);
899 ref->alternate_path = talloc_asprintf(ctx, "\\%s", tmp);
900 TALLOC_FREE(tmp);
902 if (!ref->alternate_path) {
903 TALLOC_FREE(pdp);
904 return NT_STATUS_NO_MEMORY;
907 if (pdp->reqpath[0] != '\0') {
908 ref->alternate_path = talloc_asprintf_append(
909 ref->alternate_path,
910 "%s",
911 pdp->reqpath);
912 if (!ref->alternate_path) {
913 TALLOC_FREE(pdp);
914 return NT_STATUS_NO_MEMORY;
917 ref->proximity = 0;
918 ref->ttl = REFERRAL_TTL;
919 jucn->referral_list = ref;
920 *consumedcntp = strlen(dfs_path);
921 TALLOC_FREE(pdp);
922 return NT_STATUS_OK;
925 status = create_conn_struct(ctx, &conn, snum, lp_pathname(snum),
926 NULL, &oldpath);
927 if (!NT_STATUS_IS_OK(status)) {
928 TALLOC_FREE(pdp);
929 return status;
932 /* If this is a DFS path dfs_lookup should return
933 * NT_STATUS_PATH_NOT_COVERED. */
935 status = dfs_path_lookup(ctx, conn, dfs_path, pdp,
936 False, consumedcntp, &targetpath);
938 if (!NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
939 DEBUG(3,("get_referred_path: No valid referrals for path %s\n",
940 dfs_path));
941 vfs_ChDir(conn, oldpath);
942 conn_free(conn);
943 TALLOC_FREE(pdp);
944 return status;
947 /* We know this is a valid dfs link. Parse the targetpath. */
948 if (!parse_msdfs_symlink(ctx, targetpath,
949 &jucn->referral_list,
950 &jucn->referral_count)) {
951 DEBUG(3,("get_referred_path: failed to parse symlink "
952 "target %s\n", targetpath ));
953 vfs_ChDir(conn, oldpath);
954 conn_free(conn);
955 TALLOC_FREE(pdp);
956 return NT_STATUS_NOT_FOUND;
959 vfs_ChDir(conn, oldpath);
960 conn_free(conn);
961 TALLOC_FREE(pdp);
962 return NT_STATUS_OK;
965 static int setup_ver2_dfs_referral(const char *pathname,
966 char **ppdata,
967 struct junction_map *junction,
968 bool self_referral)
970 char* pdata = *ppdata;
972 smb_ucs2_t *uni_requestedpath = NULL;
973 int uni_reqpathoffset1,uni_reqpathoffset2;
974 int uni_curroffset;
975 int requestedpathlen=0;
976 int offset;
977 int reply_size = 0;
978 int i=0;
980 DEBUG(10,("Setting up version2 referral\nRequested path:\n"));
982 requestedpathlen = rpcstr_push_talloc(talloc_tos(),
983 &uni_requestedpath, pathname);
984 if (uni_requestedpath == NULL || requestedpathlen == 0) {
985 return -1;
988 if (DEBUGLVL(10)) {
989 dump_data(0, (unsigned char *)uni_requestedpath,
990 requestedpathlen);
993 DEBUG(10,("ref count = %u\n",junction->referral_count));
995 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
996 VERSION2_REFERRAL_SIZE * junction->referral_count;
998 uni_reqpathoffset2 = uni_reqpathoffset1 + requestedpathlen;
1000 uni_curroffset = uni_reqpathoffset2 + requestedpathlen;
1002 reply_size = REFERRAL_HEADER_SIZE +
1003 VERSION2_REFERRAL_SIZE*junction->referral_count +
1004 2 * requestedpathlen;
1005 DEBUG(10,("reply_size: %u\n",reply_size));
1007 /* add up the unicode lengths of all the referral paths */
1008 for(i=0;i<junction->referral_count;i++) {
1009 DEBUG(10,("referral %u : %s\n",
1011 junction->referral_list[i].alternate_path));
1012 reply_size +=
1013 (strlen(junction->referral_list[i].alternate_path)+1)*2;
1016 DEBUG(10,("reply_size = %u\n",reply_size));
1017 /* add the unexplained 0x16 bytes */
1018 reply_size += 0x16;
1020 pdata = (char *)SMB_REALLOC(pdata,reply_size);
1021 if(pdata == NULL) {
1022 DEBUG(0,("Realloc failed!\n"));
1023 return -1;
1025 *ppdata = pdata;
1027 /* copy in the dfs requested paths.. required for offset calculations */
1028 memcpy(pdata+uni_reqpathoffset1,uni_requestedpath,requestedpathlen);
1029 memcpy(pdata+uni_reqpathoffset2,uni_requestedpath,requestedpathlen);
1031 /* create the header */
1032 SSVAL(pdata,0,requestedpathlen - 2); /* UCS2 of path consumed minus
1033 2 byte null */
1034 /* number of referral in this pkt */
1035 SSVAL(pdata,2,junction->referral_count);
1036 if(self_referral) {
1037 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
1038 } else {
1039 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
1042 offset = 8;
1043 /* add the referral elements */
1044 for(i=0;i<junction->referral_count;i++) {
1045 struct referral* ref = &junction->referral_list[i];
1046 int unilen;
1048 SSVAL(pdata,offset,2); /* version 2 */
1049 SSVAL(pdata,offset+2,VERSION2_REFERRAL_SIZE);
1050 if(self_referral) {
1051 SSVAL(pdata,offset+4,1);
1052 } else {
1053 SSVAL(pdata,offset+4,0);
1056 /* ref_flags :use path_consumed bytes? */
1057 SSVAL(pdata,offset+6,0);
1058 SIVAL(pdata,offset+8,ref->proximity);
1059 SIVAL(pdata,offset+12,ref->ttl);
1061 SSVAL(pdata,offset+16,uni_reqpathoffset1-offset);
1062 SSVAL(pdata,offset+18,uni_reqpathoffset2-offset);
1063 /* copy referred path into current offset */
1064 unilen = rpcstr_push(pdata+uni_curroffset,
1065 ref->alternate_path,
1066 reply_size - uni_curroffset,
1067 STR_UNICODE);
1069 SSVAL(pdata,offset+20,uni_curroffset-offset);
1071 uni_curroffset += unilen;
1072 offset += VERSION2_REFERRAL_SIZE;
1074 /* add in the unexplained 22 (0x16) bytes at the end */
1075 memset(pdata+uni_curroffset,'\0',0x16);
1076 return reply_size;
1079 static int setup_ver3_dfs_referral(const char *pathname,
1080 char **ppdata,
1081 struct junction_map *junction,
1082 bool self_referral)
1084 char *pdata = *ppdata;
1086 smb_ucs2_t *uni_reqpath = NULL;
1087 int uni_reqpathoffset1, uni_reqpathoffset2;
1088 int uni_curroffset;
1089 int reply_size = 0;
1091 int reqpathlen = 0;
1092 int offset,i=0;
1094 DEBUG(10,("setting up version3 referral\n"));
1096 reqpathlen = rpcstr_push_talloc(talloc_tos(), &uni_reqpath, pathname);
1097 if (uni_reqpath == NULL || reqpathlen == 0) {
1098 return -1;
1101 if (DEBUGLVL(10)) {
1102 dump_data(0, (unsigned char *)uni_reqpath,
1103 reqpathlen);
1106 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
1107 VERSION3_REFERRAL_SIZE * junction->referral_count;
1108 uni_reqpathoffset2 = uni_reqpathoffset1 + reqpathlen;
1109 reply_size = uni_curroffset = uni_reqpathoffset2 + reqpathlen;
1111 for(i=0;i<junction->referral_count;i++) {
1112 DEBUG(10,("referral %u : %s\n",
1114 junction->referral_list[i].alternate_path));
1115 reply_size +=
1116 (strlen(junction->referral_list[i].alternate_path)+1)*2;
1119 pdata = (char *)SMB_REALLOC(pdata,reply_size);
1120 if(pdata == NULL) {
1121 DEBUG(0,("version3 referral setup:"
1122 "malloc failed for Realloc!\n"));
1123 return -1;
1125 *ppdata = pdata;
1127 /* create the header */
1128 SSVAL(pdata,0,reqpathlen - 2); /* UCS2 of path consumed minus
1129 2 byte null */
1130 SSVAL(pdata,2,junction->referral_count); /* number of referral */
1131 if(self_referral) {
1132 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
1133 } else {
1134 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
1137 /* copy in the reqpaths */
1138 memcpy(pdata+uni_reqpathoffset1,uni_reqpath,reqpathlen);
1139 memcpy(pdata+uni_reqpathoffset2,uni_reqpath,reqpathlen);
1141 offset = 8;
1142 for(i=0;i<junction->referral_count;i++) {
1143 struct referral* ref = &(junction->referral_list[i]);
1144 int unilen;
1146 SSVAL(pdata,offset,3); /* version 3 */
1147 SSVAL(pdata,offset+2,VERSION3_REFERRAL_SIZE);
1148 if(self_referral) {
1149 SSVAL(pdata,offset+4,1);
1150 } else {
1151 SSVAL(pdata,offset+4,0);
1154 /* ref_flags :use path_consumed bytes? */
1155 SSVAL(pdata,offset+6,0);
1156 SIVAL(pdata,offset+8,ref->ttl);
1158 SSVAL(pdata,offset+12,uni_reqpathoffset1-offset);
1159 SSVAL(pdata,offset+14,uni_reqpathoffset2-offset);
1160 /* copy referred path into current offset */
1161 unilen = rpcstr_push(pdata+uni_curroffset,ref->alternate_path,
1162 reply_size - uni_curroffset,
1163 STR_UNICODE | STR_TERMINATE);
1164 SSVAL(pdata,offset+16,uni_curroffset-offset);
1165 /* copy 0x10 bytes of 00's in the ServiceSite GUID */
1166 memset(pdata+offset+18,'\0',16);
1168 uni_curroffset += unilen;
1169 offset += VERSION3_REFERRAL_SIZE;
1171 return reply_size;
1174 /******************************************************************
1175 Set up the DFS referral for the dfs pathname. This call returns
1176 the amount of the path covered by this server, and where the
1177 client should be redirected to. This is the meat of the
1178 TRANS2_GET_DFS_REFERRAL call.
1179 ******************************************************************/
1181 int setup_dfs_referral(connection_struct *orig_conn,
1182 const char *dfs_path,
1183 int max_referral_level,
1184 char **ppdata, NTSTATUS *pstatus)
1186 struct junction_map *junction = NULL;
1187 int consumedcnt = 0;
1188 bool self_referral = False;
1189 int reply_size = 0;
1190 char *pathnamep = NULL;
1191 char *local_dfs_path = NULL;
1192 TALLOC_CTX *ctx;
1194 if (!(ctx=talloc_init("setup_dfs_referral"))) {
1195 *pstatus = NT_STATUS_NO_MEMORY;
1196 return -1;
1199 /* get the junction entry */
1200 if (!dfs_path) {
1201 talloc_destroy(ctx);
1202 *pstatus = NT_STATUS_NOT_FOUND;
1203 return -1;
1207 * Trim pathname sent by client so it begins with only one backslash.
1208 * Two backslashes confuse some dfs clients
1211 local_dfs_path = talloc_strdup(ctx,dfs_path);
1212 if (!local_dfs_path) {
1213 *pstatus = NT_STATUS_NO_MEMORY;
1214 talloc_destroy(ctx);
1215 return -1;
1217 pathnamep = local_dfs_path;
1218 while (IS_DIRECTORY_SEP(pathnamep[0]) &&
1219 IS_DIRECTORY_SEP(pathnamep[1])) {
1220 pathnamep++;
1223 junction = TALLOC_ZERO_P(ctx, struct junction_map);
1224 if (!junction) {
1225 *pstatus = NT_STATUS_NO_MEMORY;
1226 talloc_destroy(ctx);
1227 return -1;
1230 /* The following call can change cwd. */
1231 *pstatus = get_referred_path(ctx, pathnamep, junction,
1232 &consumedcnt, &self_referral);
1233 if (!NT_STATUS_IS_OK(*pstatus)) {
1234 vfs_ChDir(orig_conn,orig_conn->connectpath);
1235 talloc_destroy(ctx);
1236 return -1;
1238 vfs_ChDir(orig_conn,orig_conn->connectpath);
1240 if (!self_referral) {
1241 pathnamep[consumedcnt] = '\0';
1243 if( DEBUGLVL( 3 ) ) {
1244 int i=0;
1245 dbgtext("setup_dfs_referral: Path %s to "
1246 "alternate path(s):",
1247 pathnamep);
1248 for(i=0;i<junction->referral_count;i++)
1249 dbgtext(" %s",
1250 junction->referral_list[i].alternate_path);
1251 dbgtext(".\n");
1255 /* create the referral depeding on version */
1256 DEBUG(10,("max_referral_level :%d\n",max_referral_level));
1258 if (max_referral_level < 2) {
1259 max_referral_level = 2;
1261 if (max_referral_level > 3) {
1262 max_referral_level = 3;
1265 switch(max_referral_level) {
1266 case 2:
1267 reply_size = setup_ver2_dfs_referral(pathnamep,
1268 ppdata, junction,
1269 self_referral);
1270 break;
1271 case 3:
1272 reply_size = setup_ver3_dfs_referral(pathnamep, ppdata,
1273 junction, self_referral);
1274 break;
1275 default:
1276 DEBUG(0,("setup_dfs_referral: Invalid dfs referral "
1277 "version: %d\n",
1278 max_referral_level));
1279 talloc_destroy(ctx);
1280 *pstatus = NT_STATUS_INVALID_LEVEL;
1281 return -1;
1284 if (DEBUGLVL(10)) {
1285 DEBUGADD(0,("DFS Referral pdata:\n"));
1286 dump_data(0,(uint8 *)*ppdata,reply_size);
1289 talloc_destroy(ctx);
1290 *pstatus = NT_STATUS_OK;
1291 return reply_size;
1294 /**********************************************************************
1295 The following functions are called by the NETDFS RPC pipe functions
1296 **********************************************************************/
1298 /*********************************************************************
1299 Creates a junction structure from a DFS pathname
1300 **********************************************************************/
1302 bool create_junction(TALLOC_CTX *ctx,
1303 const char *dfs_path,
1304 struct junction_map *jucn)
1306 int snum;
1307 bool dummy;
1308 struct dfs_path *pdp = TALLOC_P(ctx,struct dfs_path);
1309 NTSTATUS status;
1311 if (!pdp) {
1312 return False;
1314 status = parse_dfs_path(NULL, dfs_path, False, pdp, &dummy);
1315 if (!NT_STATUS_IS_OK(status)) {
1316 return False;
1319 /* check if path is dfs : validate first token */
1320 if (!is_myname_or_ipaddr(pdp->hostname)) {
1321 DEBUG(4,("create_junction: Invalid hostname %s "
1322 "in dfs path %s\n",
1323 pdp->hostname, dfs_path));
1324 TALLOC_FREE(pdp);
1325 return False;
1328 /* Check for a non-DFS share */
1329 snum = lp_servicenumber(pdp->servicename);
1331 if(snum < 0 || !lp_msdfs_root(snum)) {
1332 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1333 pdp->servicename));
1334 TALLOC_FREE(pdp);
1335 return False;
1338 jucn->service_name = talloc_strdup(ctx, pdp->servicename);
1339 jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
1340 jucn->comment = talloc_strdup(ctx, lp_comment(snum));
1342 TALLOC_FREE(pdp);
1343 if (!jucn->service_name || !jucn->volume_name || ! jucn->comment) {
1344 return False;
1346 return True;
1349 /**********************************************************************
1350 Forms a valid Unix pathname from the junction
1351 **********************************************************************/
1353 static bool junction_to_local_path(const struct junction_map *jucn,
1354 char **pp_path_out,
1355 connection_struct **conn_out,
1356 char **oldpath)
1358 int snum;
1359 NTSTATUS status;
1361 snum = lp_servicenumber(jucn->service_name);
1362 if(snum < 0) {
1363 return False;
1365 status = create_conn_struct(talloc_tos(), conn_out, snum,
1366 lp_pathname(snum), NULL, oldpath);
1367 if (!NT_STATUS_IS_OK(status)) {
1368 return False;
1371 *pp_path_out = talloc_asprintf(*conn_out,
1372 "%s/%s",
1373 lp_pathname(snum),
1374 jucn->volume_name);
1375 if (!*pp_path_out) {
1376 vfs_ChDir(*conn_out, *oldpath);
1377 conn_free(*conn_out);
1378 return False;
1380 return True;
1383 bool create_msdfs_link(const struct junction_map *jucn)
1385 char *path = NULL;
1386 char *cwd;
1387 char *msdfs_link = NULL;
1388 connection_struct *conn;
1389 int i=0;
1390 bool insert_comma = False;
1391 bool ret = False;
1393 if(!junction_to_local_path(jucn, &path, &conn, &cwd)) {
1394 return False;
1397 /* Form the msdfs_link contents */
1398 msdfs_link = talloc_strdup(conn, "msdfs:");
1399 if (!msdfs_link) {
1400 goto out;
1402 for(i=0; i<jucn->referral_count; i++) {
1403 char *refpath = jucn->referral_list[i].alternate_path;
1405 /* Alternate paths always use Windows separators. */
1406 trim_char(refpath, '\\', '\\');
1407 if(*refpath == '\0') {
1408 if (i == 0) {
1409 insert_comma = False;
1411 continue;
1413 if (i > 0 && insert_comma) {
1414 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1415 ",%s",
1416 refpath);
1417 } else {
1418 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1419 "%s",
1420 refpath);
1423 if (!msdfs_link) {
1424 goto out;
1426 if (!insert_comma) {
1427 insert_comma = True;
1431 DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
1432 path, msdfs_link));
1434 if(SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
1435 if (errno == EEXIST) {
1436 struct smb_filename *smb_fname = NULL;
1437 NTSTATUS status;
1439 status = create_synthetic_smb_fname(talloc_tos(), path,
1440 NULL, NULL,
1441 &smb_fname);
1442 if (!NT_STATUS_IS_OK(status)) {
1443 errno = map_errno_from_nt_status(status);
1444 goto out;
1447 if(SMB_VFS_UNLINK(conn, smb_fname)!=0) {
1448 TALLOC_FREE(smb_fname);
1449 goto out;
1451 TALLOC_FREE(smb_fname);
1453 if (SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
1454 DEBUG(1,("create_msdfs_link: symlink failed "
1455 "%s -> %s\nError: %s\n",
1456 path, msdfs_link, strerror(errno)));
1457 goto out;
1461 ret = True;
1463 out:
1464 vfs_ChDir(conn, cwd);
1465 conn_free(conn);
1466 return ret;
1469 bool remove_msdfs_link(const struct junction_map *jucn)
1471 char *path = NULL;
1472 char *cwd;
1473 connection_struct *conn;
1474 bool ret = False;
1475 struct smb_filename *smb_fname = NULL;
1476 NTSTATUS status;
1478 if (!junction_to_local_path(jucn, &path, &conn, &cwd)) {
1479 return false;
1482 status = create_synthetic_smb_fname(talloc_tos(), path,
1483 NULL, NULL,
1484 &smb_fname);
1485 if (!NT_STATUS_IS_OK(status)) {
1486 errno = map_errno_from_nt_status(status);
1487 return false;
1490 if( SMB_VFS_UNLINK(conn, smb_fname) == 0 ) {
1491 ret = True;
1494 TALLOC_FREE(smb_fname);
1495 vfs_ChDir(conn, cwd);
1496 conn_free(conn);
1497 return ret;
1500 /*********************************************************************
1501 Return the number of DFS links at the root of this share.
1502 *********************************************************************/
1504 static int count_dfs_links(TALLOC_CTX *ctx, int snum)
1506 size_t cnt = 0;
1507 SMB_STRUCT_DIR *dirp = NULL;
1508 char *dname = NULL;
1509 const char *connect_path = lp_pathname(snum);
1510 const char *msdfs_proxy = lp_msdfs_proxy(snum);
1511 connection_struct *conn;
1512 NTSTATUS status;
1513 char *cwd;
1515 if(*connect_path == '\0') {
1516 return 0;
1520 * Fake up a connection struct for the VFS layer.
1523 status = create_conn_struct(talloc_tos(), &conn, snum, connect_path,
1524 NULL, &cwd);
1525 if (!NT_STATUS_IS_OK(status)) {
1526 DEBUG(3, ("create_conn_struct failed: %s\n",
1527 nt_errstr(status)));
1528 return 0;
1531 /* Count a link for the msdfs root - convention */
1532 cnt = 1;
1534 /* No more links if this is an msdfs proxy. */
1535 if (*msdfs_proxy != '\0') {
1536 goto out;
1539 /* Now enumerate all dfs links */
1540 dirp = SMB_VFS_OPENDIR(conn, ".", NULL, 0);
1541 if(!dirp) {
1542 goto out;
1545 while ((dname = vfs_readdirname(conn, dirp, NULL)) != NULL) {
1546 if (is_msdfs_link(conn,
1547 dname,
1548 NULL)) {
1549 cnt++;
1551 TALLOC_FREE(dname);
1554 SMB_VFS_CLOSEDIR(conn,dirp);
1556 out:
1557 vfs_ChDir(conn, cwd);
1558 conn_free(conn);
1559 return cnt;
1562 /*********************************************************************
1563 *********************************************************************/
1565 static int form_junctions(TALLOC_CTX *ctx,
1566 int snum,
1567 struct junction_map *jucn,
1568 size_t jn_remain)
1570 size_t cnt = 0;
1571 SMB_STRUCT_DIR *dirp = NULL;
1572 char *dname = NULL;
1573 const char *connect_path = lp_pathname(snum);
1574 char *service_name = lp_servicename(snum);
1575 const char *msdfs_proxy = lp_msdfs_proxy(snum);
1576 connection_struct *conn;
1577 struct referral *ref = NULL;
1578 char *cwd;
1579 NTSTATUS status;
1581 if (jn_remain == 0) {
1582 return 0;
1585 if(*connect_path == '\0') {
1586 return 0;
1590 * Fake up a connection struct for the VFS layer.
1593 status = create_conn_struct(ctx, &conn, snum, connect_path, NULL,
1594 &cwd);
1595 if (!NT_STATUS_IS_OK(status)) {
1596 DEBUG(3, ("create_conn_struct failed: %s\n",
1597 nt_errstr(status)));
1598 return 0;
1601 /* form a junction for the msdfs root - convention
1602 DO NOT REMOVE THIS: NT clients will not work with us
1603 if this is not present
1605 jucn[cnt].service_name = talloc_strdup(ctx,service_name);
1606 jucn[cnt].volume_name = talloc_strdup(ctx, "");
1607 if (!jucn[cnt].service_name || !jucn[cnt].volume_name) {
1608 goto out;
1610 jucn[cnt].comment = "";
1611 jucn[cnt].referral_count = 1;
1613 ref = jucn[cnt].referral_list = TALLOC_ZERO_P(ctx, struct referral);
1614 if (jucn[cnt].referral_list == NULL) {
1615 goto out;
1618 ref->proximity = 0;
1619 ref->ttl = REFERRAL_TTL;
1620 if (*msdfs_proxy != '\0') {
1621 ref->alternate_path = talloc_strdup(ctx,
1622 msdfs_proxy);
1623 } else {
1624 ref->alternate_path = talloc_asprintf(ctx,
1625 "\\\\%s\\%s",
1626 get_local_machine_name(),
1627 service_name);
1630 if (!ref->alternate_path) {
1631 goto out;
1633 cnt++;
1635 /* Don't enumerate if we're an msdfs proxy. */
1636 if (*msdfs_proxy != '\0') {
1637 goto out;
1640 /* Now enumerate all dfs links */
1641 dirp = SMB_VFS_OPENDIR(conn, ".", NULL, 0);
1642 if(!dirp) {
1643 goto out;
1646 while ((dname = vfs_readdirname(conn, dirp, NULL)) != NULL) {
1647 char *link_target = NULL;
1648 if (cnt >= jn_remain) {
1649 DEBUG(2, ("form_junctions: ran out of MSDFS "
1650 "junction slots"));
1651 TALLOC_FREE(dname);
1652 goto out;
1654 if (is_msdfs_link_internal(ctx,
1655 conn,
1656 dname, &link_target,
1657 NULL)) {
1658 if (parse_msdfs_symlink(ctx,
1659 link_target,
1660 &jucn[cnt].referral_list,
1661 &jucn[cnt].referral_count)) {
1663 jucn[cnt].service_name = talloc_strdup(ctx,
1664 service_name);
1665 jucn[cnt].volume_name = talloc_strdup(ctx,
1666 dname);
1667 if (!jucn[cnt].service_name ||
1668 !jucn[cnt].volume_name) {
1669 TALLOC_FREE(dname);
1670 goto out;
1672 jucn[cnt].comment = "";
1673 cnt++;
1675 TALLOC_FREE(link_target);
1677 TALLOC_FREE(dname);
1680 out:
1682 if (dirp) {
1683 SMB_VFS_CLOSEDIR(conn,dirp);
1686 vfs_ChDir(conn, cwd);
1687 conn_free(conn);
1688 return cnt;
1691 struct junction_map *enum_msdfs_links(TALLOC_CTX *ctx, size_t *p_num_jn)
1693 struct junction_map *jn = NULL;
1694 int i=0;
1695 size_t jn_count = 0;
1696 int sharecount = 0;
1698 *p_num_jn = 0;
1699 if(!lp_host_msdfs()) {
1700 return NULL;
1703 /* Ensure all the usershares are loaded. */
1704 become_root();
1705 load_registry_shares();
1706 sharecount = load_usershare_shares();
1707 unbecome_root();
1709 for(i=0;i < sharecount;i++) {
1710 if(lp_msdfs_root(i)) {
1711 jn_count += count_dfs_links(ctx, i);
1714 if (jn_count == 0) {
1715 return NULL;
1717 jn = TALLOC_ARRAY(ctx, struct junction_map, jn_count);
1718 if (!jn) {
1719 return NULL;
1721 for(i=0; i < sharecount; i++) {
1722 if (*p_num_jn >= jn_count) {
1723 break;
1725 if(lp_msdfs_root(i)) {
1726 *p_num_jn += form_junctions(ctx, i,
1727 &jn[*p_num_jn],
1728 jn_count - *p_num_jn);
1731 return jn;
1734 /******************************************************************************
1735 Core function to resolve a dfs pathname possibly containing a wildcard. If
1736 ppath_contains_wcard != NULL, it will be set to true if a wildcard is
1737 detected during dfs resolution.
1738 ******************************************************************************/
1740 NTSTATUS resolve_dfspath_wcard(TALLOC_CTX *ctx,
1741 connection_struct *conn,
1742 bool dfs_pathnames,
1743 const char *name_in,
1744 char **pp_name_out,
1745 bool *ppath_contains_wcard)
1747 bool path_contains_wcard;
1748 NTSTATUS status = NT_STATUS_OK;
1750 if (dfs_pathnames) {
1751 status = dfs_redirect(ctx,
1752 conn,
1753 name_in,
1754 True,
1755 pp_name_out,
1756 &path_contains_wcard);
1758 if (NT_STATUS_IS_OK(status) && ppath_contains_wcard != NULL) {
1759 *ppath_contains_wcard = path_contains_wcard;
1761 } else {
1763 * Cheat and just return a copy of the in ptr.
1764 * Once srvstr_get_path() uses talloc it'll
1765 * be a talloced ptr anyway.
1767 *pp_name_out = CONST_DISCARD(char *,name_in);
1769 return status;