2 Unix SMB/Netbios implementation.
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
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....
45 **********************************************************************/
47 static NTSTATUS
parse_dfs_path(connection_struct
*conn
,
50 struct dfs_path
*pdp
, /* MUST BE TALLOCED */
51 bool *ppath_contains_wcard
)
57 NTSTATUS status
= NT_STATUS_OK
;
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",
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();
94 DEBUG(10,("parse_dfs_path: trying to convert %s to a "
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",
110 /* Parse out hostname. */
111 p
= strchr_m(temp
,sepchar
);
113 DEBUG(10,("parse_dfs_path: can't parse hostname from path %s\n",
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
; /* "" */
124 DEBUG(10,("parse_dfs_path: trying to convert %s "
130 pdp
->hostname
= temp
;
132 DEBUG(10,("parse_dfs_path: hostname: %s\n",pdp
->hostname
));
134 /* Parse out servicename. */
136 p
= strchr_m(servicename
,sepchar
);
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",
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
160 *servicename
= sepchar
;
166 DEBUG(10,("parse_dfs_path: trying to convert %s "
172 pdp
->servicename
= servicename
;
174 DEBUG(10,("parse_dfs_path: servicename: %s\n",pdp
->servicename
));
177 /* Client sent self referral \server\share. */
178 pdp
->reqpath
= eos_ptr
; /* "" */
186 *ppath_contains_wcard
= False
;
190 /* Rest is reqpath. */
191 if (pdp
->posix_path
) {
192 status
= check_path_syntax_posix(pdp
->reqpath
);
195 status
= check_path_syntax_wcard(pdp
->reqpath
,
196 ppath_contains_wcard
);
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
) ));
208 DEBUG(10,("parse_dfs_path: rest of the path: %s\n",pdp
->reqpath
));
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
,
221 struct auth_serversupplied_info
*server_info
,
224 connection_struct
*conn
;
228 conn
= TALLOC_ZERO_P(ctx
, connection_struct
);
230 return NT_STATUS_NO_MEMORY
;
233 connpath
= talloc_strdup(conn
, path
);
236 return NT_STATUS_NO_MEMORY
;
238 connpath
= talloc_string_sub(conn
,
241 lp_servicename(snum
));
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"));
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"));
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"));
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
)));
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. "
295 conn
->connectpath
, strerror(errno
) ));
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
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
,
327 struct referral
**preflist
,
332 char **alt_path
= NULL
;
334 struct referral
*reflist
;
337 temp
= talloc_strdup(ctx
, target
);
341 prot
= strtok_r(temp
, ":", &saveptr
);
343 DEBUG(0,("parse_msdfs_symlink: invalid path !\n"));
347 alt_path
= TALLOC_ARRAY(ctx
, char *, MAX_REFERRAL_COUNT
);
352 /* parse out the alternate paths */
353 while((count
<MAX_REFERRAL_COUNT
) &&
354 ((alt_path
[count
] = strtok_r(NULL
, ",", &saveptr
)) != NULL
)) {
358 DEBUG(10,("parse_msdfs_symlink: count=%d\n", count
));
361 reflist
= *preflist
= TALLOC_ZERO_ARRAY(ctx
,
362 struct referral
, count
);
363 if(reflist
== NULL
) {
364 TALLOC_FREE(alt_path
);
368 reflist
= *preflist
= NULL
;
371 for(i
=0;i
<count
;i
++) {
374 /* Canonicalize link target.
375 * Replace all /'s in the path by a \ */
376 string_replace(alt_path
[i
], '/', '\\');
378 /* Remove leading '\\'s */
380 while (*p
&& (*p
== '\\')) {
384 reflist
[i
].alternate_path
= talloc_asprintf(ctx
,
387 if (!reflist
[i
].alternate_path
) {
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
));
399 TALLOC_FREE(alt_path
);
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
,
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
];
418 char link_target_buf
[7];
421 char *link_target
= NULL
;
422 struct smb_filename smb_fname
;
424 if (pp_link_target
) {
426 link_target
= TALLOC_ARRAY(ctx
, char, bufsize
);
430 *pp_link_target
= link_target
;
432 bufsize
= sizeof(link_target_buf
);
433 link_target
= link_target_buf
;
436 ZERO_STRUCT(smb_fname
);
437 smb_fname
.base_name
= discard_const_p(char, path
);
439 if (SMB_VFS_LSTAT(conn
, &smb_fname
) != 0) {
440 DEBUG(5,("is_msdfs_link_read_target: %s does not exist.\n",
444 if (!S_ISLNK(smb_fname
.st
.st_ex_mode
)) {
445 DEBUG(5,("is_msdfs_link_read_target: %s is not a link.\n",
450 *sbufp
= smb_fname
.st
;
453 referral_len
= SMB_VFS_READLINK(conn
, path
, link_target
, bufsize
- 1);
454 if (referral_len
== -1) {
455 DEBUG(0,("is_msdfs_link_read_target: Error reading "
456 "msdfs link %s: %s\n",
457 path
, strerror(errno
)));
460 link_target
[referral_len
] = '\0';
462 DEBUG(5,("is_msdfs_link_internal: %s -> %s\n",path
,
465 if (!strnequal(link_target
, "msdfs:", 6)) {
472 if (link_target
!= link_target_buf
) {
473 TALLOC_FREE(link_target
);
478 /**********************************************************************
479 Returns true if the unix path is a valid msdfs symlink.
480 **********************************************************************/
482 bool is_msdfs_link(connection_struct
*conn
,
484 SMB_STRUCT_STAT
*sbufp
)
486 return is_msdfs_link_internal(talloc_tos(),
493 /*****************************************************************
494 Used by other functions to decide if a dfs path is remote,
495 and to get the list of referred locations for that remote path.
497 search_flag: For findfirsts, dfs links themselves are not
498 redirected, but paths beyond the links are. For normal smb calls,
499 even dfs links need to be redirected.
501 consumedcntp: how much of the dfs path is being redirected. the client
502 should try the remaining path on the redirected server.
504 If this returns NT_STATUS_PATH_NOT_COVERED the contents of the msdfs
505 link redirect are in targetpath.
506 *****************************************************************/
508 static NTSTATUS
dfs_path_lookup(TALLOC_CTX
*ctx
,
509 connection_struct
*conn
,
510 const char *dfspath
, /* Incoming complete dfs path */
511 const struct dfs_path
*pdp
, /* Parsed out
512 server+share+extrapath. */
513 bool search_flag
, /* Called from a findfirst ? */
515 char **pp_targetpath
)
520 struct smb_filename
*smb_fname
= NULL
;
521 char *canon_dfspath
= NULL
; /* Canonicalized dfs path. (only '/'
524 DEBUG(10,("dfs_path_lookup: Conn path = %s reqpath = %s\n",
525 conn
->connectpath
, pdp
->reqpath
));
528 * Note the unix path conversion here we're doing we
529 * throw away. We're looking for a symlink for a dfs
530 * resolution, if we don't find it we'll do another
531 * unix_convert later in the codepath.
534 status
= unix_convert(ctx
, conn
, pdp
->reqpath
, &smb_fname
,
535 search_flag
? UCF_ALWAYS_ALLOW_WCARD_LCOMP
: 0);
537 if (!NT_STATUS_IS_OK(status
)) {
538 if (!NT_STATUS_EQUAL(status
,
539 NT_STATUS_OBJECT_PATH_NOT_FOUND
)) {
542 if (smb_fname
== NULL
|| smb_fname
->base_name
== NULL
) {
547 /* Optimization - check if we can redirect the whole path. */
549 if (is_msdfs_link_internal(ctx
, conn
, smb_fname
->base_name
,
550 pp_targetpath
, NULL
)) {
552 DEBUG(6,("dfs_path_lookup (FindFirst) No redirection "
553 "for dfs link %s.\n", dfspath
));
554 status
= NT_STATUS_OK
;
558 DEBUG(6,("dfs_path_lookup: %s resolves to a "
559 "valid dfs link %s.\n", dfspath
,
560 pp_targetpath
? *pp_targetpath
: ""));
563 *consumedcntp
= strlen(dfspath
);
565 status
= NT_STATUS_PATH_NOT_COVERED
;
569 /* Prepare to test only for '/' components in the given path,
570 * so if a Windows path replace all '\\' characters with '/'.
571 * For a POSIX DFS path we know all separators are already '/'. */
573 canon_dfspath
= talloc_strdup(ctx
, dfspath
);
574 if (!canon_dfspath
) {
575 status
= NT_STATUS_NO_MEMORY
;
578 if (!pdp
->posix_path
) {
579 string_replace(canon_dfspath
, '\\', '/');
583 * localpath comes out of unix_convert, so it has
584 * no trailing backslash. Make sure that canon_dfspath hasn't either.
585 * Fix for bug #4860 from Jan Martin <Jan.Martin@rwedea.com>.
588 trim_char(canon_dfspath
,0,'/');
591 * Redirect if any component in the path is a link.
592 * We do this by walking backwards through the
593 * local path, chopping off the last component
594 * in both the local path and the canonicalized
595 * DFS path. If we hit a DFS link then we're done.
598 p
= strrchr_m(smb_fname
->base_name
, '/');
600 q
= strrchr_m(canon_dfspath
, '/');
609 if (is_msdfs_link_internal(ctx
, conn
,
610 smb_fname
->base_name
, pp_targetpath
,
612 DEBUG(4, ("dfs_path_lookup: Redirecting %s because "
613 "parent %s is dfs link\n", dfspath
,
614 smb_fname_str_dbg(smb_fname
)));
617 *consumedcntp
= strlen(canon_dfspath
);
618 DEBUG(10, ("dfs_path_lookup: Path consumed: %s "
624 status
= NT_STATUS_PATH_NOT_COVERED
;
628 /* Step back on the filesystem. */
629 p
= strrchr_m(smb_fname
->base_name
, '/');
632 /* And in the canonicalized dfs path. */
633 q
= strrchr_m(canon_dfspath
, '/');
637 status
= NT_STATUS_OK
;
639 TALLOC_FREE(smb_fname
);
643 /*****************************************************************
644 Decides if a dfs pathname should be redirected or not.
645 If not, the pathname is converted to a tcon-relative local unix path
647 search_wcard_flag: this flag performs 2 functions both related
648 to searches. See resolve_dfs_path() and parse_dfs_path_XX()
651 This function can return NT_STATUS_OK, meaning use the returned path as-is
652 (mapped into a local path).
653 or NT_STATUS_NOT_COVERED meaning return a DFS redirect, or
654 any other NT_STATUS error which is a genuine error to be
655 returned to the client.
656 *****************************************************************/
658 static NTSTATUS
dfs_redirect(TALLOC_CTX
*ctx
,
659 connection_struct
*conn
,
661 bool search_wcard_flag
,
663 bool *ppath_contains_wcard
)
666 struct dfs_path
*pdp
= TALLOC_P(ctx
, struct dfs_path
);
669 return NT_STATUS_NO_MEMORY
;
672 status
= parse_dfs_path(conn
, path_in
, search_wcard_flag
, pdp
,
673 ppath_contains_wcard
);
674 if (!NT_STATUS_IS_OK(status
)) {
679 if (pdp
->reqpath
[0] == '\0') {
681 *pp_path_out
= talloc_strdup(ctx
, "");
683 return NT_STATUS_NO_MEMORY
;
685 DEBUG(5,("dfs_redirect: self-referral.\n"));
689 /* If dfs pathname for a non-dfs share, convert to tcon-relative
690 path and return OK */
692 if (!lp_msdfs_root(SNUM(conn
))) {
693 *pp_path_out
= talloc_strdup(ctx
, pdp
->reqpath
);
696 return NT_STATUS_NO_MEMORY
;
701 /* If it looked like a local path (zero hostname/servicename)
702 * just treat as a tcon-relative path. */
704 if (pdp
->hostname
[0] == '\0' && pdp
->servicename
[0] == '\0') {
705 *pp_path_out
= talloc_strdup(ctx
, pdp
->reqpath
);
708 return NT_STATUS_NO_MEMORY
;
713 if (!( strequal(pdp
->servicename
, lp_servicename(SNUM(conn
)))
714 || (strequal(pdp
->servicename
, HOMES_NAME
)
715 && strequal(lp_servicename(SNUM(conn
)),
716 conn
->server_info
->sanitized_username
) )) ) {
718 /* The given sharename doesn't match this connection. */
721 return NT_STATUS_OBJECT_PATH_NOT_FOUND
;
724 status
= dfs_path_lookup(ctx
, conn
, path_in
, pdp
,
725 search_wcard_flag
, NULL
, NULL
);
726 if (!NT_STATUS_IS_OK(status
)) {
727 if (NT_STATUS_EQUAL(status
, NT_STATUS_PATH_NOT_COVERED
)) {
728 DEBUG(3,("dfs_redirect: Redirecting %s\n", path_in
));
730 DEBUG(10,("dfs_redirect: dfs_path_lookup "
731 "failed for %s with %s\n",
732 path_in
, nt_errstr(status
) ));
737 DEBUG(3,("dfs_redirect: Not redirecting %s.\n", path_in
));
739 /* Form non-dfs tcon-relative path */
740 *pp_path_out
= talloc_strdup(ctx
, pdp
->reqpath
);
743 return NT_STATUS_NO_MEMORY
;
746 DEBUG(3,("dfs_redirect: Path %s converted to non-dfs path %s\n",
753 /**********************************************************************
754 Return a self referral.
755 **********************************************************************/
757 static NTSTATUS
self_ref(TALLOC_CTX
*ctx
,
758 const char *dfs_path
,
759 struct junction_map
*jucn
,
761 bool *self_referralp
)
763 struct referral
*ref
;
765 *self_referralp
= True
;
767 jucn
->referral_count
= 1;
768 if((ref
= TALLOC_ZERO_P(ctx
, struct referral
)) == NULL
) {
769 return NT_STATUS_NO_MEMORY
;
772 ref
->alternate_path
= talloc_strdup(ctx
, dfs_path
);
773 if (!ref
->alternate_path
) {
774 return NT_STATUS_NO_MEMORY
;
777 ref
->ttl
= REFERRAL_TTL
;
778 jucn
->referral_list
= ref
;
779 *consumedcntp
= strlen(dfs_path
);
783 /**********************************************************************
784 Gets valid referrals for a dfs path and fills up the
785 junction_map structure.
786 **********************************************************************/
788 NTSTATUS
get_referred_path(TALLOC_CTX
*ctx
,
789 struct auth_serversupplied_info
*server_info
,
790 const char *dfs_path
,
791 struct junction_map
*jucn
,
793 bool *self_referralp
)
795 struct connection_struct
*conn
;
796 char *targetpath
= NULL
;
798 NTSTATUS status
= NT_STATUS_NOT_FOUND
;
800 struct dfs_path
*pdp
= TALLOC_P(ctx
, struct dfs_path
);
804 return NT_STATUS_NO_MEMORY
;
807 *self_referralp
= False
;
809 status
= parse_dfs_path(NULL
, dfs_path
, False
, pdp
, &dummy
);
810 if (!NT_STATUS_IS_OK(status
)) {
814 jucn
->service_name
= talloc_strdup(ctx
, pdp
->servicename
);
815 jucn
->volume_name
= talloc_strdup(ctx
, pdp
->reqpath
);
816 if (!jucn
->service_name
|| !jucn
->volume_name
) {
818 return NT_STATUS_NO_MEMORY
;
821 /* Verify the share is a dfs root */
822 snum
= lp_servicenumber(jucn
->service_name
);
824 fstring service_name
;
825 fstrcpy(service_name
, jucn
->service_name
);
826 if ((snum
= find_service(service_name
)) < 0) {
827 return NT_STATUS_NOT_FOUND
;
829 TALLOC_FREE(jucn
->service_name
);
830 jucn
->service_name
= talloc_strdup(ctx
, service_name
);
831 if (!jucn
->service_name
) {
833 return NT_STATUS_NO_MEMORY
;
837 if (!lp_msdfs_root(snum
) && (*lp_msdfs_proxy(snum
) == '\0')) {
838 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not "
840 pdp
->servicename
, dfs_path
));
842 return NT_STATUS_NOT_FOUND
;
846 * Self referrals are tested with a anonymous IPC connection and
847 * a GET_DFS_REFERRAL call to \\server\share. (which means
848 * dp.reqpath[0] points to an empty string). create_conn_struct cd's
849 * into the directory and will fail if it cannot (as the anonymous
850 * user). Cope with this.
853 if (pdp
->reqpath
[0] == '\0') {
855 struct referral
*ref
;
857 if (*lp_msdfs_proxy(snum
) == '\0') {
867 * It's an msdfs proxy share. Redirect to
868 * the configured target share.
871 jucn
->referral_count
= 1;
872 if ((ref
= TALLOC_ZERO_P(ctx
, struct referral
)) == NULL
) {
874 return NT_STATUS_NO_MEMORY
;
877 if (!(tmp
= talloc_strdup(ctx
, lp_msdfs_proxy(snum
)))) {
879 return NT_STATUS_NO_MEMORY
;
882 trim_string(tmp
, "\\", 0);
884 ref
->alternate_path
= talloc_asprintf(ctx
, "\\%s", tmp
);
887 if (!ref
->alternate_path
) {
889 return NT_STATUS_NO_MEMORY
;
892 if (pdp
->reqpath
[0] != '\0') {
893 ref
->alternate_path
= talloc_asprintf_append(
897 if (!ref
->alternate_path
) {
899 return NT_STATUS_NO_MEMORY
;
903 ref
->ttl
= REFERRAL_TTL
;
904 jucn
->referral_list
= ref
;
905 *consumedcntp
= strlen(dfs_path
);
910 status
= create_conn_struct(ctx
, &conn
, snum
, lp_pathname(snum
),
911 server_info
, &oldpath
);
912 if (!NT_STATUS_IS_OK(status
)) {
917 /* If this is a DFS path dfs_lookup should return
918 * NT_STATUS_PATH_NOT_COVERED. */
920 status
= dfs_path_lookup(ctx
, conn
, dfs_path
, pdp
,
921 False
, consumedcntp
, &targetpath
);
923 if (!NT_STATUS_EQUAL(status
, NT_STATUS_PATH_NOT_COVERED
)) {
924 DEBUG(3,("get_referred_path: No valid referrals for path %s\n",
926 vfs_ChDir(conn
, oldpath
);
932 /* We know this is a valid dfs link. Parse the targetpath. */
933 if (!parse_msdfs_symlink(ctx
, targetpath
,
934 &jucn
->referral_list
,
935 &jucn
->referral_count
)) {
936 DEBUG(3,("get_referred_path: failed to parse symlink "
937 "target %s\n", targetpath
));
938 vfs_ChDir(conn
, oldpath
);
941 return NT_STATUS_NOT_FOUND
;
944 vfs_ChDir(conn
, oldpath
);
950 static int setup_ver2_dfs_referral(const char *pathname
,
952 struct junction_map
*junction
,
955 char* pdata
= *ppdata
;
957 smb_ucs2_t
*uni_requestedpath
= NULL
;
958 int uni_reqpathoffset1
,uni_reqpathoffset2
;
960 int requestedpathlen
=0;
965 DEBUG(10,("Setting up version2 referral\nRequested path:\n"));
967 requestedpathlen
= rpcstr_push_talloc(talloc_tos(),
968 &uni_requestedpath
, pathname
);
969 if (uni_requestedpath
== NULL
|| requestedpathlen
== 0) {
974 dump_data(0, (unsigned char *)uni_requestedpath
,
978 DEBUG(10,("ref count = %u\n",junction
->referral_count
));
980 uni_reqpathoffset1
= REFERRAL_HEADER_SIZE
+
981 VERSION2_REFERRAL_SIZE
* junction
->referral_count
;
983 uni_reqpathoffset2
= uni_reqpathoffset1
+ requestedpathlen
;
985 uni_curroffset
= uni_reqpathoffset2
+ requestedpathlen
;
987 reply_size
= REFERRAL_HEADER_SIZE
+
988 VERSION2_REFERRAL_SIZE
*junction
->referral_count
+
989 2 * requestedpathlen
;
990 DEBUG(10,("reply_size: %u\n",reply_size
));
992 /* add up the unicode lengths of all the referral paths */
993 for(i
=0;i
<junction
->referral_count
;i
++) {
994 DEBUG(10,("referral %u : %s\n",
996 junction
->referral_list
[i
].alternate_path
));
998 (strlen(junction
->referral_list
[i
].alternate_path
)+1)*2;
1001 DEBUG(10,("reply_size = %u\n",reply_size
));
1002 /* add the unexplained 0x16 bytes */
1005 pdata
= (char *)SMB_REALLOC(pdata
,reply_size
);
1007 DEBUG(0,("Realloc failed!\n"));
1012 /* copy in the dfs requested paths.. required for offset calculations */
1013 memcpy(pdata
+uni_reqpathoffset1
,uni_requestedpath
,requestedpathlen
);
1014 memcpy(pdata
+uni_reqpathoffset2
,uni_requestedpath
,requestedpathlen
);
1016 /* create the header */
1017 SSVAL(pdata
,0,requestedpathlen
- 2); /* UCS2 of path consumed minus
1019 /* number of referral in this pkt */
1020 SSVAL(pdata
,2,junction
->referral_count
);
1022 SIVAL(pdata
,4,DFSREF_REFERRAL_SERVER
| DFSREF_STORAGE_SERVER
);
1024 SIVAL(pdata
,4,DFSREF_STORAGE_SERVER
);
1028 /* add the referral elements */
1029 for(i
=0;i
<junction
->referral_count
;i
++) {
1030 struct referral
* ref
= &junction
->referral_list
[i
];
1033 SSVAL(pdata
,offset
,2); /* version 2 */
1034 SSVAL(pdata
,offset
+2,VERSION2_REFERRAL_SIZE
);
1036 SSVAL(pdata
,offset
+4,1);
1038 SSVAL(pdata
,offset
+4,0);
1041 /* ref_flags :use path_consumed bytes? */
1042 SSVAL(pdata
,offset
+6,0);
1043 SIVAL(pdata
,offset
+8,ref
->proximity
);
1044 SIVAL(pdata
,offset
+12,ref
->ttl
);
1046 SSVAL(pdata
,offset
+16,uni_reqpathoffset1
-offset
);
1047 SSVAL(pdata
,offset
+18,uni_reqpathoffset2
-offset
);
1048 /* copy referred path into current offset */
1049 unilen
= rpcstr_push(pdata
+uni_curroffset
,
1050 ref
->alternate_path
,
1051 reply_size
- uni_curroffset
,
1054 SSVAL(pdata
,offset
+20,uni_curroffset
-offset
);
1056 uni_curroffset
+= unilen
;
1057 offset
+= VERSION2_REFERRAL_SIZE
;
1059 /* add in the unexplained 22 (0x16) bytes at the end */
1060 memset(pdata
+uni_curroffset
,'\0',0x16);
1064 static int setup_ver3_dfs_referral(const char *pathname
,
1066 struct junction_map
*junction
,
1069 char *pdata
= *ppdata
;
1071 smb_ucs2_t
*uni_reqpath
= NULL
;
1072 int uni_reqpathoffset1
, uni_reqpathoffset2
;
1079 DEBUG(10,("setting up version3 referral\n"));
1081 reqpathlen
= rpcstr_push_talloc(talloc_tos(), &uni_reqpath
, pathname
);
1082 if (uni_reqpath
== NULL
|| reqpathlen
== 0) {
1087 dump_data(0, (unsigned char *)uni_reqpath
,
1091 uni_reqpathoffset1
= REFERRAL_HEADER_SIZE
+
1092 VERSION3_REFERRAL_SIZE
* junction
->referral_count
;
1093 uni_reqpathoffset2
= uni_reqpathoffset1
+ reqpathlen
;
1094 reply_size
= uni_curroffset
= uni_reqpathoffset2
+ reqpathlen
;
1096 for(i
=0;i
<junction
->referral_count
;i
++) {
1097 DEBUG(10,("referral %u : %s\n",
1099 junction
->referral_list
[i
].alternate_path
));
1101 (strlen(junction
->referral_list
[i
].alternate_path
)+1)*2;
1104 pdata
= (char *)SMB_REALLOC(pdata
,reply_size
);
1106 DEBUG(0,("version3 referral setup:"
1107 "malloc failed for Realloc!\n"));
1112 /* create the header */
1113 SSVAL(pdata
,0,reqpathlen
- 2); /* UCS2 of path consumed minus
1115 SSVAL(pdata
,2,junction
->referral_count
); /* number of referral */
1117 SIVAL(pdata
,4,DFSREF_REFERRAL_SERVER
| DFSREF_STORAGE_SERVER
);
1119 SIVAL(pdata
,4,DFSREF_STORAGE_SERVER
);
1122 /* copy in the reqpaths */
1123 memcpy(pdata
+uni_reqpathoffset1
,uni_reqpath
,reqpathlen
);
1124 memcpy(pdata
+uni_reqpathoffset2
,uni_reqpath
,reqpathlen
);
1127 for(i
=0;i
<junction
->referral_count
;i
++) {
1128 struct referral
* ref
= &(junction
->referral_list
[i
]);
1131 SSVAL(pdata
,offset
,3); /* version 3 */
1132 SSVAL(pdata
,offset
+2,VERSION3_REFERRAL_SIZE
);
1134 SSVAL(pdata
,offset
+4,1);
1136 SSVAL(pdata
,offset
+4,0);
1139 /* ref_flags :use path_consumed bytes? */
1140 SSVAL(pdata
,offset
+6,0);
1141 SIVAL(pdata
,offset
+8,ref
->ttl
);
1143 SSVAL(pdata
,offset
+12,uni_reqpathoffset1
-offset
);
1144 SSVAL(pdata
,offset
+14,uni_reqpathoffset2
-offset
);
1145 /* copy referred path into current offset */
1146 unilen
= rpcstr_push(pdata
+uni_curroffset
,ref
->alternate_path
,
1147 reply_size
- uni_curroffset
,
1148 STR_UNICODE
| STR_TERMINATE
);
1149 SSVAL(pdata
,offset
+16,uni_curroffset
-offset
);
1150 /* copy 0x10 bytes of 00's in the ServiceSite GUID */
1151 memset(pdata
+offset
+18,'\0',16);
1153 uni_curroffset
+= unilen
;
1154 offset
+= VERSION3_REFERRAL_SIZE
;
1159 /******************************************************************
1160 Set up the DFS referral for the dfs pathname. This call returns
1161 the amount of the path covered by this server, and where the
1162 client should be redirected to. This is the meat of the
1163 TRANS2_GET_DFS_REFERRAL call.
1164 ******************************************************************/
1166 int setup_dfs_referral(connection_struct
*orig_conn
,
1167 const char *dfs_path
,
1168 int max_referral_level
,
1169 char **ppdata
, NTSTATUS
*pstatus
)
1171 struct junction_map
*junction
= NULL
;
1172 int consumedcnt
= 0;
1173 bool self_referral
= False
;
1175 char *pathnamep
= NULL
;
1176 char *local_dfs_path
= NULL
;
1179 if (!(ctx
=talloc_init("setup_dfs_referral"))) {
1180 *pstatus
= NT_STATUS_NO_MEMORY
;
1184 /* get the junction entry */
1186 talloc_destroy(ctx
);
1187 *pstatus
= NT_STATUS_NOT_FOUND
;
1192 * Trim pathname sent by client so it begins with only one backslash.
1193 * Two backslashes confuse some dfs clients
1196 local_dfs_path
= talloc_strdup(ctx
,dfs_path
);
1197 if (!local_dfs_path
) {
1198 *pstatus
= NT_STATUS_NO_MEMORY
;
1199 talloc_destroy(ctx
);
1202 pathnamep
= local_dfs_path
;
1203 while (IS_DIRECTORY_SEP(pathnamep
[0]) &&
1204 IS_DIRECTORY_SEP(pathnamep
[1])) {
1208 junction
= TALLOC_ZERO_P(ctx
, struct junction_map
);
1210 *pstatus
= NT_STATUS_NO_MEMORY
;
1211 talloc_destroy(ctx
);
1215 /* The following call can change cwd. */
1216 *pstatus
= get_referred_path(ctx
, orig_conn
->server_info
,
1217 pathnamep
, junction
,
1218 &consumedcnt
, &self_referral
);
1219 if (!NT_STATUS_IS_OK(*pstatus
)) {
1220 vfs_ChDir(orig_conn
,orig_conn
->connectpath
);
1221 talloc_destroy(ctx
);
1224 vfs_ChDir(orig_conn
,orig_conn
->connectpath
);
1226 if (!self_referral
) {
1227 pathnamep
[consumedcnt
] = '\0';
1229 if( DEBUGLVL( 3 ) ) {
1231 dbgtext("setup_dfs_referral: Path %s to "
1232 "alternate path(s):",
1234 for(i
=0;i
<junction
->referral_count
;i
++)
1236 junction
->referral_list
[i
].alternate_path
);
1241 /* create the referral depeding on version */
1242 DEBUG(10,("max_referral_level :%d\n",max_referral_level
));
1244 if (max_referral_level
< 2) {
1245 max_referral_level
= 2;
1247 if (max_referral_level
> 3) {
1248 max_referral_level
= 3;
1251 switch(max_referral_level
) {
1253 reply_size
= setup_ver2_dfs_referral(pathnamep
,
1258 reply_size
= setup_ver3_dfs_referral(pathnamep
, ppdata
,
1259 junction
, self_referral
);
1262 DEBUG(0,("setup_dfs_referral: Invalid dfs referral "
1264 max_referral_level
));
1265 talloc_destroy(ctx
);
1266 *pstatus
= NT_STATUS_INVALID_LEVEL
;
1271 DEBUGADD(0,("DFS Referral pdata:\n"));
1272 dump_data(0,(uint8
*)*ppdata
,reply_size
);
1275 talloc_destroy(ctx
);
1276 *pstatus
= NT_STATUS_OK
;
1280 /**********************************************************************
1281 The following functions are called by the NETDFS RPC pipe functions
1282 **********************************************************************/
1284 /*********************************************************************
1285 Creates a junction structure from a DFS pathname
1286 **********************************************************************/
1288 bool create_junction(TALLOC_CTX
*ctx
,
1289 const char *dfs_path
,
1290 struct junction_map
*jucn
)
1294 struct dfs_path
*pdp
= TALLOC_P(ctx
,struct dfs_path
);
1300 status
= parse_dfs_path(NULL
, dfs_path
, False
, pdp
, &dummy
);
1301 if (!NT_STATUS_IS_OK(status
)) {
1305 /* check if path is dfs : validate first token */
1306 if (!is_myname_or_ipaddr(pdp
->hostname
)) {
1307 DEBUG(4,("create_junction: Invalid hostname %s "
1309 pdp
->hostname
, dfs_path
));
1314 /* Check for a non-DFS share */
1315 snum
= lp_servicenumber(pdp
->servicename
);
1317 if(snum
< 0 || !lp_msdfs_root(snum
)) {
1318 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1324 jucn
->service_name
= talloc_strdup(ctx
, pdp
->servicename
);
1325 jucn
->volume_name
= talloc_strdup(ctx
, pdp
->reqpath
);
1326 jucn
->comment
= talloc_strdup(ctx
, lp_comment(snum
));
1329 if (!jucn
->service_name
|| !jucn
->volume_name
|| ! jucn
->comment
) {
1335 /**********************************************************************
1336 Forms a valid Unix pathname from the junction
1337 **********************************************************************/
1339 static bool junction_to_local_path(const struct junction_map
*jucn
,
1341 connection_struct
**conn_out
,
1347 snum
= lp_servicenumber(jucn
->service_name
);
1351 status
= create_conn_struct(talloc_tos(), conn_out
, snum
,
1352 lp_pathname(snum
), NULL
, oldpath
);
1353 if (!NT_STATUS_IS_OK(status
)) {
1357 *pp_path_out
= talloc_asprintf(*conn_out
,
1361 if (!*pp_path_out
) {
1362 vfs_ChDir(*conn_out
, *oldpath
);
1363 conn_free(*conn_out
);
1369 bool create_msdfs_link(const struct junction_map
*jucn
)
1373 char *msdfs_link
= NULL
;
1374 connection_struct
*conn
;
1376 bool insert_comma
= False
;
1379 if(!junction_to_local_path(jucn
, &path
, &conn
, &cwd
)) {
1383 /* Form the msdfs_link contents */
1384 msdfs_link
= talloc_strdup(conn
, "msdfs:");
1388 for(i
=0; i
<jucn
->referral_count
; i
++) {
1389 char *refpath
= jucn
->referral_list
[i
].alternate_path
;
1391 /* Alternate paths always use Windows separators. */
1392 trim_char(refpath
, '\\', '\\');
1393 if(*refpath
== '\0') {
1395 insert_comma
= False
;
1399 if (i
> 0 && insert_comma
) {
1400 msdfs_link
= talloc_asprintf_append_buffer(msdfs_link
,
1404 msdfs_link
= talloc_asprintf_append_buffer(msdfs_link
,
1412 if (!insert_comma
) {
1413 insert_comma
= True
;
1417 DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
1420 if(SMB_VFS_SYMLINK(conn
, msdfs_link
, path
) < 0) {
1421 if (errno
== EEXIST
) {
1422 struct smb_filename
*smb_fname
= NULL
;
1425 status
= create_synthetic_smb_fname(talloc_tos(), path
,
1428 if (!NT_STATUS_IS_OK(status
)) {
1429 errno
= map_errno_from_nt_status(status
);
1433 if(SMB_VFS_UNLINK(conn
, smb_fname
)!=0) {
1434 TALLOC_FREE(smb_fname
);
1437 TALLOC_FREE(smb_fname
);
1439 if (SMB_VFS_SYMLINK(conn
, msdfs_link
, path
) < 0) {
1440 DEBUG(1,("create_msdfs_link: symlink failed "
1441 "%s -> %s\nError: %s\n",
1442 path
, msdfs_link
, strerror(errno
)));
1450 vfs_ChDir(conn
, cwd
);
1455 bool remove_msdfs_link(const struct junction_map
*jucn
)
1459 connection_struct
*conn
;
1461 struct smb_filename
*smb_fname
= NULL
;
1464 if (!junction_to_local_path(jucn
, &path
, &conn
, &cwd
)) {
1468 status
= create_synthetic_smb_fname(talloc_tos(), path
,
1471 if (!NT_STATUS_IS_OK(status
)) {
1472 errno
= map_errno_from_nt_status(status
);
1476 if( SMB_VFS_UNLINK(conn
, smb_fname
) == 0 ) {
1480 TALLOC_FREE(smb_fname
);
1481 vfs_ChDir(conn
, cwd
);
1486 /*********************************************************************
1487 Return the number of DFS links at the root of this share.
1488 *********************************************************************/
1490 static int count_dfs_links(TALLOC_CTX
*ctx
, int snum
)
1493 SMB_STRUCT_DIR
*dirp
= NULL
;
1494 const char *dname
= NULL
;
1495 char *talloced
= NULL
;
1496 const char *connect_path
= lp_pathname(snum
);
1497 const char *msdfs_proxy
= lp_msdfs_proxy(snum
);
1498 connection_struct
*conn
;
1502 if(*connect_path
== '\0') {
1507 * Fake up a connection struct for the VFS layer.
1510 status
= create_conn_struct(talloc_tos(), &conn
, snum
, connect_path
,
1512 if (!NT_STATUS_IS_OK(status
)) {
1513 DEBUG(3, ("create_conn_struct failed: %s\n",
1514 nt_errstr(status
)));
1518 /* Count a link for the msdfs root - convention */
1521 /* No more links if this is an msdfs proxy. */
1522 if (*msdfs_proxy
!= '\0') {
1526 /* Now enumerate all dfs links */
1527 dirp
= SMB_VFS_OPENDIR(conn
, ".", NULL
, 0);
1532 while ((dname
= vfs_readdirname(conn
, dirp
, NULL
, &talloced
))
1534 if (is_msdfs_link(conn
,
1539 TALLOC_FREE(talloced
);
1542 SMB_VFS_CLOSEDIR(conn
,dirp
);
1545 vfs_ChDir(conn
, cwd
);
1550 /*********************************************************************
1551 *********************************************************************/
1553 static int form_junctions(TALLOC_CTX
*ctx
,
1555 struct junction_map
*jucn
,
1559 SMB_STRUCT_DIR
*dirp
= NULL
;
1560 const char *dname
= NULL
;
1561 char *talloced
= NULL
;
1562 const char *connect_path
= lp_pathname(snum
);
1563 char *service_name
= lp_servicename(snum
);
1564 const char *msdfs_proxy
= lp_msdfs_proxy(snum
);
1565 connection_struct
*conn
;
1566 struct referral
*ref
= NULL
;
1570 if (jn_remain
== 0) {
1574 if(*connect_path
== '\0') {
1579 * Fake up a connection struct for the VFS layer.
1582 status
= create_conn_struct(ctx
, &conn
, snum
, connect_path
, NULL
,
1584 if (!NT_STATUS_IS_OK(status
)) {
1585 DEBUG(3, ("create_conn_struct failed: %s\n",
1586 nt_errstr(status
)));
1590 /* form a junction for the msdfs root - convention
1591 DO NOT REMOVE THIS: NT clients will not work with us
1592 if this is not present
1594 jucn
[cnt
].service_name
= talloc_strdup(ctx
,service_name
);
1595 jucn
[cnt
].volume_name
= talloc_strdup(ctx
, "");
1596 if (!jucn
[cnt
].service_name
|| !jucn
[cnt
].volume_name
) {
1599 jucn
[cnt
].comment
= "";
1600 jucn
[cnt
].referral_count
= 1;
1602 ref
= jucn
[cnt
].referral_list
= TALLOC_ZERO_P(ctx
, struct referral
);
1603 if (jucn
[cnt
].referral_list
== NULL
) {
1608 ref
->ttl
= REFERRAL_TTL
;
1609 if (*msdfs_proxy
!= '\0') {
1610 ref
->alternate_path
= talloc_strdup(ctx
,
1613 ref
->alternate_path
= talloc_asprintf(ctx
,
1615 get_local_machine_name(),
1619 if (!ref
->alternate_path
) {
1624 /* Don't enumerate if we're an msdfs proxy. */
1625 if (*msdfs_proxy
!= '\0') {
1629 /* Now enumerate all dfs links */
1630 dirp
= SMB_VFS_OPENDIR(conn
, ".", NULL
, 0);
1635 while ((dname
= vfs_readdirname(conn
, dirp
, NULL
, &talloced
))
1637 char *link_target
= NULL
;
1638 if (cnt
>= jn_remain
) {
1639 DEBUG(2, ("form_junctions: ran out of MSDFS "
1641 TALLOC_FREE(talloced
);
1644 if (is_msdfs_link_internal(ctx
,
1646 dname
, &link_target
,
1648 if (parse_msdfs_symlink(ctx
,
1650 &jucn
[cnt
].referral_list
,
1651 &jucn
[cnt
].referral_count
)) {
1653 jucn
[cnt
].service_name
= talloc_strdup(ctx
,
1655 jucn
[cnt
].volume_name
= talloc_strdup(ctx
,
1657 if (!jucn
[cnt
].service_name
||
1658 !jucn
[cnt
].volume_name
) {
1659 TALLOC_FREE(talloced
);
1662 jucn
[cnt
].comment
= "";
1665 TALLOC_FREE(link_target
);
1667 TALLOC_FREE(talloced
);
1673 SMB_VFS_CLOSEDIR(conn
,dirp
);
1676 vfs_ChDir(conn
, cwd
);
1681 struct junction_map
*enum_msdfs_links(TALLOC_CTX
*ctx
, size_t *p_num_jn
)
1683 struct junction_map
*jn
= NULL
;
1685 size_t jn_count
= 0;
1689 if(!lp_host_msdfs()) {
1693 /* Ensure all the usershares are loaded. */
1695 load_registry_shares();
1696 sharecount
= load_usershare_shares();
1699 for(i
=0;i
< sharecount
;i
++) {
1700 if(lp_msdfs_root(i
)) {
1701 jn_count
+= count_dfs_links(ctx
, i
);
1704 if (jn_count
== 0) {
1707 jn
= TALLOC_ARRAY(ctx
, struct junction_map
, jn_count
);
1711 for(i
=0; i
< sharecount
; i
++) {
1712 if (*p_num_jn
>= jn_count
) {
1715 if(lp_msdfs_root(i
)) {
1716 *p_num_jn
+= form_junctions(ctx
, i
,
1718 jn_count
- *p_num_jn
);
1724 /******************************************************************************
1725 Core function to resolve a dfs pathname possibly containing a wildcard. If
1726 ppath_contains_wcard != NULL, it will be set to true if a wildcard is
1727 detected during dfs resolution.
1728 ******************************************************************************/
1730 NTSTATUS
resolve_dfspath_wcard(TALLOC_CTX
*ctx
,
1731 connection_struct
*conn
,
1733 const char *name_in
,
1736 bool *ppath_contains_wcard
)
1738 bool path_contains_wcard
;
1739 NTSTATUS status
= NT_STATUS_OK
;
1741 if (dfs_pathnames
) {
1742 status
= dfs_redirect(ctx
,
1747 &path_contains_wcard
);
1749 if (NT_STATUS_IS_OK(status
) && ppath_contains_wcard
!= NULL
) {
1750 *ppath_contains_wcard
= path_contains_wcard
;
1754 * Cheat and just return a copy of the in ptr.
1755 * Once srvstr_get_path() uses talloc it'll
1756 * be a talloced ptr anyway.
1758 *pp_name_out
= CONST_DISCARD(char *,name_in
);