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 can
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.
532 * If we needed to remember what we'd resolved in
533 * dp->reqpath (as the original code did) we'd
534 * copy (localhost, dp->reqpath) on any code
535 * path below that returns True - but I don't
536 * think this is needed. JRA.
539 status
= unix_convert(ctx
, conn
, pdp
->reqpath
, &smb_fname
,
540 search_flag
? UCF_ALWAYS_ALLOW_WCARD_LCOMP
: 0);
542 if (!NT_STATUS_IS_OK(status
)) {
543 if (!NT_STATUS_EQUAL(status
,
544 NT_STATUS_OBJECT_PATH_NOT_FOUND
)) {
548 /* Create an smb_fname to use below. */
549 status
= create_synthetic_smb_fname(ctx
, pdp
->reqpath
, NULL
,
551 if (!NT_STATUS_IS_OK(status
)) {
556 /* Optimization - check if we can redirect the whole path. */
558 if (is_msdfs_link_internal(ctx
, conn
, smb_fname
->base_name
,
559 pp_targetpath
, NULL
)) {
561 DEBUG(6,("dfs_path_lookup (FindFirst) No redirection "
562 "for dfs link %s.\n", dfspath
));
563 status
= NT_STATUS_OK
;
567 DEBUG(6,("dfs_path_lookup: %s resolves to a "
568 "valid dfs link %s.\n", dfspath
,
569 pp_targetpath
? *pp_targetpath
: ""));
572 *consumedcntp
= strlen(dfspath
);
574 status
= NT_STATUS_PATH_NOT_COVERED
;
578 /* Prepare to test only for '/' components in the given path,
579 * so if a Windows path replace all '\\' characters with '/'.
580 * For a POSIX DFS path we know all separators are already '/'. */
582 canon_dfspath
= talloc_strdup(ctx
, dfspath
);
583 if (!canon_dfspath
) {
584 status
= NT_STATUS_NO_MEMORY
;
587 if (!pdp
->posix_path
) {
588 string_replace(canon_dfspath
, '\\', '/');
592 * localpath comes out of unix_convert, so it has
593 * no trailing backslash. Make sure that canon_dfspath hasn't either.
594 * Fix for bug #4860 from Jan Martin <Jan.Martin@rwedea.com>.
597 trim_char(canon_dfspath
,0,'/');
600 * Redirect if any component in the path is a link.
601 * We do this by walking backwards through the
602 * local path, chopping off the last component
603 * in both the local path and the canonicalized
604 * DFS path. If we hit a DFS link then we're done.
607 p
= strrchr_m(smb_fname
->base_name
, '/');
609 q
= strrchr_m(canon_dfspath
, '/');
618 if (is_msdfs_link_internal(ctx
, conn
,
619 smb_fname
->base_name
, pp_targetpath
,
621 DEBUG(4, ("dfs_path_lookup: Redirecting %s because "
622 "parent %s is dfs link\n", dfspath
,
623 smb_fname_str_dbg(smb_fname
)));
626 *consumedcntp
= strlen(canon_dfspath
);
627 DEBUG(10, ("dfs_path_lookup: Path consumed: %s "
633 status
= NT_STATUS_PATH_NOT_COVERED
;
637 /* Step back on the filesystem. */
638 p
= strrchr_m(smb_fname
->base_name
, '/');
641 /* And in the canonicalized dfs path. */
642 q
= strrchr_m(canon_dfspath
, '/');
646 status
= NT_STATUS_OK
;
648 TALLOC_FREE(smb_fname
);
652 /*****************************************************************
653 Decides if a dfs pathname should be redirected or not.
654 If not, the pathname is converted to a tcon-relative local unix path
656 search_wcard_flag: this flag performs 2 functions both related
657 to searches. See resolve_dfs_path() and parse_dfs_path_XX()
660 This function can return NT_STATUS_OK, meaning use the returned path as-is
661 (mapped into a local path).
662 or NT_STATUS_NOT_COVERED meaning return a DFS redirect, or
663 any other NT_STATUS error which is a genuine error to be
664 returned to the client.
665 *****************************************************************/
667 static NTSTATUS
dfs_redirect(TALLOC_CTX
*ctx
,
668 connection_struct
*conn
,
670 bool search_wcard_flag
,
672 bool *ppath_contains_wcard
)
675 struct dfs_path
*pdp
= TALLOC_P(ctx
, struct dfs_path
);
678 return NT_STATUS_NO_MEMORY
;
681 status
= parse_dfs_path(conn
, path_in
, search_wcard_flag
, pdp
,
682 ppath_contains_wcard
);
683 if (!NT_STATUS_IS_OK(status
)) {
688 if (pdp
->reqpath
[0] == '\0') {
690 *pp_path_out
= talloc_strdup(ctx
, "");
692 return NT_STATUS_NO_MEMORY
;
694 DEBUG(5,("dfs_redirect: self-referral.\n"));
698 /* If dfs pathname for a non-dfs share, convert to tcon-relative
699 path and return OK */
701 if (!lp_msdfs_root(SNUM(conn
))) {
702 *pp_path_out
= talloc_strdup(ctx
, pdp
->reqpath
);
705 return NT_STATUS_NO_MEMORY
;
710 /* If it looked like a local path (zero hostname/servicename)
711 * just treat as a tcon-relative path. */
713 if (pdp
->hostname
[0] == '\0' && pdp
->servicename
[0] == '\0') {
714 *pp_path_out
= talloc_strdup(ctx
, pdp
->reqpath
);
717 return NT_STATUS_NO_MEMORY
;
722 if (!( strequal(pdp
->servicename
, lp_servicename(SNUM(conn
)))
723 || (strequal(pdp
->servicename
, HOMES_NAME
)
724 && strequal(lp_servicename(SNUM(conn
)),
725 conn
->server_info
->sanitized_username
) )) ) {
727 /* The given sharename doesn't match this connection. */
730 return NT_STATUS_OBJECT_PATH_NOT_FOUND
;
733 status
= dfs_path_lookup(ctx
, conn
, path_in
, pdp
,
734 search_wcard_flag
, NULL
, NULL
);
735 if (!NT_STATUS_IS_OK(status
)) {
736 if (NT_STATUS_EQUAL(status
, NT_STATUS_PATH_NOT_COVERED
)) {
737 DEBUG(3,("dfs_redirect: Redirecting %s\n", path_in
));
739 DEBUG(10,("dfs_redirect: dfs_path_lookup "
740 "failed for %s with %s\n",
741 path_in
, nt_errstr(status
) ));
746 DEBUG(3,("dfs_redirect: Not redirecting %s.\n", path_in
));
748 /* Form non-dfs tcon-relative path */
749 *pp_path_out
= talloc_strdup(ctx
, pdp
->reqpath
);
752 return NT_STATUS_NO_MEMORY
;
755 DEBUG(3,("dfs_redirect: Path %s converted to non-dfs path %s\n",
762 /**********************************************************************
763 Return a self referral.
764 **********************************************************************/
766 static NTSTATUS
self_ref(TALLOC_CTX
*ctx
,
767 const char *dfs_path
,
768 struct junction_map
*jucn
,
770 bool *self_referralp
)
772 struct referral
*ref
;
774 *self_referralp
= True
;
776 jucn
->referral_count
= 1;
777 if((ref
= TALLOC_ZERO_P(ctx
, struct referral
)) == NULL
) {
778 return NT_STATUS_NO_MEMORY
;
781 ref
->alternate_path
= talloc_strdup(ctx
, dfs_path
);
782 if (!ref
->alternate_path
) {
783 return NT_STATUS_NO_MEMORY
;
786 ref
->ttl
= REFERRAL_TTL
;
787 jucn
->referral_list
= ref
;
788 *consumedcntp
= strlen(dfs_path
);
792 /**********************************************************************
793 Gets valid referrals for a dfs path and fills up the
794 junction_map structure.
795 **********************************************************************/
797 NTSTATUS
get_referred_path(TALLOC_CTX
*ctx
,
798 const char *dfs_path
,
799 struct junction_map
*jucn
,
801 bool *self_referralp
)
803 struct connection_struct
*conn
;
804 char *targetpath
= NULL
;
806 NTSTATUS status
= NT_STATUS_NOT_FOUND
;
808 struct dfs_path
*pdp
= TALLOC_P(ctx
, struct dfs_path
);
812 return NT_STATUS_NO_MEMORY
;
815 *self_referralp
= False
;
817 status
= parse_dfs_path(NULL
, dfs_path
, False
, pdp
, &dummy
);
818 if (!NT_STATUS_IS_OK(status
)) {
822 jucn
->service_name
= talloc_strdup(ctx
, pdp
->servicename
);
823 jucn
->volume_name
= talloc_strdup(ctx
, pdp
->reqpath
);
824 if (!jucn
->service_name
|| !jucn
->volume_name
) {
826 return NT_STATUS_NO_MEMORY
;
829 /* Verify the share is a dfs root */
830 snum
= lp_servicenumber(jucn
->service_name
);
832 fstring service_name
;
833 fstrcpy(service_name
, jucn
->service_name
);
834 if ((snum
= find_service(service_name
)) < 0) {
835 return NT_STATUS_NOT_FOUND
;
837 TALLOC_FREE(jucn
->service_name
);
838 jucn
->service_name
= talloc_strdup(ctx
, service_name
);
839 if (!jucn
->service_name
) {
841 return NT_STATUS_NO_MEMORY
;
845 if (!lp_msdfs_root(snum
) && (*lp_msdfs_proxy(snum
) == '\0')) {
846 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not "
848 pdp
->servicename
, dfs_path
));
850 return NT_STATUS_NOT_FOUND
;
854 * Self referrals are tested with a anonymous IPC connection and
855 * a GET_DFS_REFERRAL call to \\server\share. (which means
856 * dp.reqpath[0] points to an empty string). create_conn_struct cd's
857 * into the directory and will fail if it cannot (as the anonymous
858 * user). Cope with this.
861 if (pdp
->reqpath
[0] == '\0') {
863 struct referral
*ref
;
865 if (*lp_msdfs_proxy(snum
) == '\0') {
875 * It's an msdfs proxy share. Redirect to
876 * the configured target share.
879 jucn
->referral_count
= 1;
880 if ((ref
= TALLOC_ZERO_P(ctx
, struct referral
)) == NULL
) {
882 return NT_STATUS_NO_MEMORY
;
885 if (!(tmp
= talloc_strdup(ctx
, lp_msdfs_proxy(snum
)))) {
887 return NT_STATUS_NO_MEMORY
;
890 trim_string(tmp
, "\\", 0);
892 ref
->alternate_path
= talloc_asprintf(ctx
, "\\%s", tmp
);
895 if (!ref
->alternate_path
) {
897 return NT_STATUS_NO_MEMORY
;
900 if (pdp
->reqpath
[0] != '\0') {
901 ref
->alternate_path
= talloc_asprintf_append(
905 if (!ref
->alternate_path
) {
907 return NT_STATUS_NO_MEMORY
;
911 ref
->ttl
= REFERRAL_TTL
;
912 jucn
->referral_list
= ref
;
913 *consumedcntp
= strlen(dfs_path
);
918 status
= create_conn_struct(ctx
, &conn
, snum
, lp_pathname(snum
),
920 if (!NT_STATUS_IS_OK(status
)) {
925 /* If this is a DFS path dfs_lookup should return
926 * NT_STATUS_PATH_NOT_COVERED. */
928 status
= dfs_path_lookup(ctx
, conn
, dfs_path
, pdp
,
929 False
, consumedcntp
, &targetpath
);
931 if (!NT_STATUS_EQUAL(status
, NT_STATUS_PATH_NOT_COVERED
)) {
932 DEBUG(3,("get_referred_path: No valid referrals for path %s\n",
934 vfs_ChDir(conn
, oldpath
);
940 /* We know this is a valid dfs link. Parse the targetpath. */
941 if (!parse_msdfs_symlink(ctx
, targetpath
,
942 &jucn
->referral_list
,
943 &jucn
->referral_count
)) {
944 DEBUG(3,("get_referred_path: failed to parse symlink "
945 "target %s\n", targetpath
));
946 vfs_ChDir(conn
, oldpath
);
949 return NT_STATUS_NOT_FOUND
;
952 vfs_ChDir(conn
, oldpath
);
958 static int setup_ver2_dfs_referral(const char *pathname
,
960 struct junction_map
*junction
,
963 char* pdata
= *ppdata
;
965 smb_ucs2_t
*uni_requestedpath
= NULL
;
966 int uni_reqpathoffset1
,uni_reqpathoffset2
;
968 int requestedpathlen
=0;
973 DEBUG(10,("Setting up version2 referral\nRequested path:\n"));
975 requestedpathlen
= rpcstr_push_talloc(talloc_tos(),
976 &uni_requestedpath
, pathname
);
977 if (uni_requestedpath
== NULL
|| requestedpathlen
== 0) {
982 dump_data(0, (unsigned char *)uni_requestedpath
,
986 DEBUG(10,("ref count = %u\n",junction
->referral_count
));
988 uni_reqpathoffset1
= REFERRAL_HEADER_SIZE
+
989 VERSION2_REFERRAL_SIZE
* junction
->referral_count
;
991 uni_reqpathoffset2
= uni_reqpathoffset1
+ requestedpathlen
;
993 uni_curroffset
= uni_reqpathoffset2
+ requestedpathlen
;
995 reply_size
= REFERRAL_HEADER_SIZE
+
996 VERSION2_REFERRAL_SIZE
*junction
->referral_count
+
997 2 * requestedpathlen
;
998 DEBUG(10,("reply_size: %u\n",reply_size
));
1000 /* add up the unicode lengths of all the referral paths */
1001 for(i
=0;i
<junction
->referral_count
;i
++) {
1002 DEBUG(10,("referral %u : %s\n",
1004 junction
->referral_list
[i
].alternate_path
));
1006 (strlen(junction
->referral_list
[i
].alternate_path
)+1)*2;
1009 DEBUG(10,("reply_size = %u\n",reply_size
));
1010 /* add the unexplained 0x16 bytes */
1013 pdata
= (char *)SMB_REALLOC(pdata
,reply_size
);
1015 DEBUG(0,("Realloc failed!\n"));
1020 /* copy in the dfs requested paths.. required for offset calculations */
1021 memcpy(pdata
+uni_reqpathoffset1
,uni_requestedpath
,requestedpathlen
);
1022 memcpy(pdata
+uni_reqpathoffset2
,uni_requestedpath
,requestedpathlen
);
1024 /* create the header */
1025 SSVAL(pdata
,0,requestedpathlen
- 2); /* UCS2 of path consumed minus
1027 /* number of referral in this pkt */
1028 SSVAL(pdata
,2,junction
->referral_count
);
1030 SIVAL(pdata
,4,DFSREF_REFERRAL_SERVER
| DFSREF_STORAGE_SERVER
);
1032 SIVAL(pdata
,4,DFSREF_STORAGE_SERVER
);
1036 /* add the referral elements */
1037 for(i
=0;i
<junction
->referral_count
;i
++) {
1038 struct referral
* ref
= &junction
->referral_list
[i
];
1041 SSVAL(pdata
,offset
,2); /* version 2 */
1042 SSVAL(pdata
,offset
+2,VERSION2_REFERRAL_SIZE
);
1044 SSVAL(pdata
,offset
+4,1);
1046 SSVAL(pdata
,offset
+4,0);
1049 /* ref_flags :use path_consumed bytes? */
1050 SSVAL(pdata
,offset
+6,0);
1051 SIVAL(pdata
,offset
+8,ref
->proximity
);
1052 SIVAL(pdata
,offset
+12,ref
->ttl
);
1054 SSVAL(pdata
,offset
+16,uni_reqpathoffset1
-offset
);
1055 SSVAL(pdata
,offset
+18,uni_reqpathoffset2
-offset
);
1056 /* copy referred path into current offset */
1057 unilen
= rpcstr_push(pdata
+uni_curroffset
,
1058 ref
->alternate_path
,
1059 reply_size
- uni_curroffset
,
1062 SSVAL(pdata
,offset
+20,uni_curroffset
-offset
);
1064 uni_curroffset
+= unilen
;
1065 offset
+= VERSION2_REFERRAL_SIZE
;
1067 /* add in the unexplained 22 (0x16) bytes at the end */
1068 memset(pdata
+uni_curroffset
,'\0',0x16);
1072 static int setup_ver3_dfs_referral(const char *pathname
,
1074 struct junction_map
*junction
,
1077 char *pdata
= *ppdata
;
1079 smb_ucs2_t
*uni_reqpath
= NULL
;
1080 int uni_reqpathoffset1
, uni_reqpathoffset2
;
1087 DEBUG(10,("setting up version3 referral\n"));
1089 reqpathlen
= rpcstr_push_talloc(talloc_tos(), &uni_reqpath
, pathname
);
1090 if (uni_reqpath
== NULL
|| reqpathlen
== 0) {
1095 dump_data(0, (unsigned char *)uni_reqpath
,
1099 uni_reqpathoffset1
= REFERRAL_HEADER_SIZE
+
1100 VERSION3_REFERRAL_SIZE
* junction
->referral_count
;
1101 uni_reqpathoffset2
= uni_reqpathoffset1
+ reqpathlen
;
1102 reply_size
= uni_curroffset
= uni_reqpathoffset2
+ reqpathlen
;
1104 for(i
=0;i
<junction
->referral_count
;i
++) {
1105 DEBUG(10,("referral %u : %s\n",
1107 junction
->referral_list
[i
].alternate_path
));
1109 (strlen(junction
->referral_list
[i
].alternate_path
)+1)*2;
1112 pdata
= (char *)SMB_REALLOC(pdata
,reply_size
);
1114 DEBUG(0,("version3 referral setup:"
1115 "malloc failed for Realloc!\n"));
1120 /* create the header */
1121 SSVAL(pdata
,0,reqpathlen
- 2); /* UCS2 of path consumed minus
1123 SSVAL(pdata
,2,junction
->referral_count
); /* number of referral */
1125 SIVAL(pdata
,4,DFSREF_REFERRAL_SERVER
| DFSREF_STORAGE_SERVER
);
1127 SIVAL(pdata
,4,DFSREF_STORAGE_SERVER
);
1130 /* copy in the reqpaths */
1131 memcpy(pdata
+uni_reqpathoffset1
,uni_reqpath
,reqpathlen
);
1132 memcpy(pdata
+uni_reqpathoffset2
,uni_reqpath
,reqpathlen
);
1135 for(i
=0;i
<junction
->referral_count
;i
++) {
1136 struct referral
* ref
= &(junction
->referral_list
[i
]);
1139 SSVAL(pdata
,offset
,3); /* version 3 */
1140 SSVAL(pdata
,offset
+2,VERSION3_REFERRAL_SIZE
);
1142 SSVAL(pdata
,offset
+4,1);
1144 SSVAL(pdata
,offset
+4,0);
1147 /* ref_flags :use path_consumed bytes? */
1148 SSVAL(pdata
,offset
+6,0);
1149 SIVAL(pdata
,offset
+8,ref
->ttl
);
1151 SSVAL(pdata
,offset
+12,uni_reqpathoffset1
-offset
);
1152 SSVAL(pdata
,offset
+14,uni_reqpathoffset2
-offset
);
1153 /* copy referred path into current offset */
1154 unilen
= rpcstr_push(pdata
+uni_curroffset
,ref
->alternate_path
,
1155 reply_size
- uni_curroffset
,
1156 STR_UNICODE
| STR_TERMINATE
);
1157 SSVAL(pdata
,offset
+16,uni_curroffset
-offset
);
1158 /* copy 0x10 bytes of 00's in the ServiceSite GUID */
1159 memset(pdata
+offset
+18,'\0',16);
1161 uni_curroffset
+= unilen
;
1162 offset
+= VERSION3_REFERRAL_SIZE
;
1167 /******************************************************************
1168 Set up the DFS referral for the dfs pathname. This call returns
1169 the amount of the path covered by this server, and where the
1170 client should be redirected to. This is the meat of the
1171 TRANS2_GET_DFS_REFERRAL call.
1172 ******************************************************************/
1174 int setup_dfs_referral(connection_struct
*orig_conn
,
1175 const char *dfs_path
,
1176 int max_referral_level
,
1177 char **ppdata
, NTSTATUS
*pstatus
)
1179 struct junction_map
*junction
= NULL
;
1180 int consumedcnt
= 0;
1181 bool self_referral
= False
;
1183 char *pathnamep
= NULL
;
1184 char *local_dfs_path
= NULL
;
1187 if (!(ctx
=talloc_init("setup_dfs_referral"))) {
1188 *pstatus
= NT_STATUS_NO_MEMORY
;
1192 /* get the junction entry */
1194 talloc_destroy(ctx
);
1195 *pstatus
= NT_STATUS_NOT_FOUND
;
1200 * Trim pathname sent by client so it begins with only one backslash.
1201 * Two backslashes confuse some dfs clients
1204 local_dfs_path
= talloc_strdup(ctx
,dfs_path
);
1205 if (!local_dfs_path
) {
1206 *pstatus
= NT_STATUS_NO_MEMORY
;
1207 talloc_destroy(ctx
);
1210 pathnamep
= local_dfs_path
;
1211 while (IS_DIRECTORY_SEP(pathnamep
[0]) &&
1212 IS_DIRECTORY_SEP(pathnamep
[1])) {
1216 junction
= TALLOC_ZERO_P(ctx
, struct junction_map
);
1218 *pstatus
= NT_STATUS_NO_MEMORY
;
1219 talloc_destroy(ctx
);
1223 /* The following call can change cwd. */
1224 *pstatus
= get_referred_path(ctx
, pathnamep
, junction
,
1225 &consumedcnt
, &self_referral
);
1226 if (!NT_STATUS_IS_OK(*pstatus
)) {
1227 vfs_ChDir(orig_conn
,orig_conn
->connectpath
);
1228 talloc_destroy(ctx
);
1231 vfs_ChDir(orig_conn
,orig_conn
->connectpath
);
1233 if (!self_referral
) {
1234 pathnamep
[consumedcnt
] = '\0';
1236 if( DEBUGLVL( 3 ) ) {
1238 dbgtext("setup_dfs_referral: Path %s to "
1239 "alternate path(s):",
1241 for(i
=0;i
<junction
->referral_count
;i
++)
1243 junction
->referral_list
[i
].alternate_path
);
1248 /* create the referral depeding on version */
1249 DEBUG(10,("max_referral_level :%d\n",max_referral_level
));
1251 if (max_referral_level
< 2) {
1252 max_referral_level
= 2;
1254 if (max_referral_level
> 3) {
1255 max_referral_level
= 3;
1258 switch(max_referral_level
) {
1260 reply_size
= setup_ver2_dfs_referral(pathnamep
,
1265 reply_size
= setup_ver3_dfs_referral(pathnamep
, ppdata
,
1266 junction
, self_referral
);
1269 DEBUG(0,("setup_dfs_referral: Invalid dfs referral "
1271 max_referral_level
));
1272 talloc_destroy(ctx
);
1273 *pstatus
= NT_STATUS_INVALID_LEVEL
;
1278 DEBUGADD(0,("DFS Referral pdata:\n"));
1279 dump_data(0,(uint8
*)*ppdata
,reply_size
);
1282 talloc_destroy(ctx
);
1283 *pstatus
= NT_STATUS_OK
;
1287 /**********************************************************************
1288 The following functions are called by the NETDFS RPC pipe functions
1289 **********************************************************************/
1291 /*********************************************************************
1292 Creates a junction structure from a DFS pathname
1293 **********************************************************************/
1295 bool create_junction(TALLOC_CTX
*ctx
,
1296 const char *dfs_path
,
1297 struct junction_map
*jucn
)
1301 struct dfs_path
*pdp
= TALLOC_P(ctx
,struct dfs_path
);
1307 status
= parse_dfs_path(NULL
, dfs_path
, False
, pdp
, &dummy
);
1308 if (!NT_STATUS_IS_OK(status
)) {
1312 /* check if path is dfs : validate first token */
1313 if (!is_myname_or_ipaddr(pdp
->hostname
)) {
1314 DEBUG(4,("create_junction: Invalid hostname %s "
1316 pdp
->hostname
, dfs_path
));
1321 /* Check for a non-DFS share */
1322 snum
= lp_servicenumber(pdp
->servicename
);
1324 if(snum
< 0 || !lp_msdfs_root(snum
)) {
1325 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1331 jucn
->service_name
= talloc_strdup(ctx
, pdp
->servicename
);
1332 jucn
->volume_name
= talloc_strdup(ctx
, pdp
->reqpath
);
1333 jucn
->comment
= talloc_strdup(ctx
, lp_comment(snum
));
1336 if (!jucn
->service_name
|| !jucn
->volume_name
|| ! jucn
->comment
) {
1342 /**********************************************************************
1343 Forms a valid Unix pathname from the junction
1344 **********************************************************************/
1346 static bool junction_to_local_path(const struct junction_map
*jucn
,
1348 connection_struct
**conn_out
,
1354 snum
= lp_servicenumber(jucn
->service_name
);
1358 status
= create_conn_struct(talloc_tos(), conn_out
, snum
,
1359 lp_pathname(snum
), NULL
, oldpath
);
1360 if (!NT_STATUS_IS_OK(status
)) {
1364 *pp_path_out
= talloc_asprintf(*conn_out
,
1368 if (!*pp_path_out
) {
1369 vfs_ChDir(*conn_out
, *oldpath
);
1370 conn_free(*conn_out
);
1376 bool create_msdfs_link(const struct junction_map
*jucn
)
1380 char *msdfs_link
= NULL
;
1381 connection_struct
*conn
;
1383 bool insert_comma
= False
;
1386 if(!junction_to_local_path(jucn
, &path
, &conn
, &cwd
)) {
1390 /* Form the msdfs_link contents */
1391 msdfs_link
= talloc_strdup(conn
, "msdfs:");
1395 for(i
=0; i
<jucn
->referral_count
; i
++) {
1396 char *refpath
= jucn
->referral_list
[i
].alternate_path
;
1398 /* Alternate paths always use Windows separators. */
1399 trim_char(refpath
, '\\', '\\');
1400 if(*refpath
== '\0') {
1402 insert_comma
= False
;
1406 if (i
> 0 && insert_comma
) {
1407 msdfs_link
= talloc_asprintf_append_buffer(msdfs_link
,
1411 msdfs_link
= talloc_asprintf_append_buffer(msdfs_link
,
1419 if (!insert_comma
) {
1420 insert_comma
= True
;
1424 DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
1427 if(SMB_VFS_SYMLINK(conn
, msdfs_link
, path
) < 0) {
1428 if (errno
== EEXIST
) {
1429 struct smb_filename
*smb_fname
= NULL
;
1432 status
= create_synthetic_smb_fname(talloc_tos(), path
,
1435 if (!NT_STATUS_IS_OK(status
)) {
1436 errno
= map_errno_from_nt_status(status
);
1440 if(SMB_VFS_UNLINK(conn
, smb_fname
)!=0) {
1441 TALLOC_FREE(smb_fname
);
1444 TALLOC_FREE(smb_fname
);
1446 if (SMB_VFS_SYMLINK(conn
, msdfs_link
, path
) < 0) {
1447 DEBUG(1,("create_msdfs_link: symlink failed "
1448 "%s -> %s\nError: %s\n",
1449 path
, msdfs_link
, strerror(errno
)));
1457 vfs_ChDir(conn
, cwd
);
1462 bool remove_msdfs_link(const struct junction_map
*jucn
)
1466 connection_struct
*conn
;
1468 struct smb_filename
*smb_fname
= NULL
;
1471 if (!junction_to_local_path(jucn
, &path
, &conn
, &cwd
)) {
1475 status
= create_synthetic_smb_fname(talloc_tos(), path
,
1478 if (!NT_STATUS_IS_OK(status
)) {
1479 errno
= map_errno_from_nt_status(status
);
1483 if( SMB_VFS_UNLINK(conn
, smb_fname
) == 0 ) {
1487 TALLOC_FREE(smb_fname
);
1488 vfs_ChDir(conn
, cwd
);
1493 /*********************************************************************
1494 Return the number of DFS links at the root of this share.
1495 *********************************************************************/
1497 static int count_dfs_links(TALLOC_CTX
*ctx
, int snum
)
1500 SMB_STRUCT_DIR
*dirp
= NULL
;
1501 const char *dname
= NULL
;
1502 char *talloced
= NULL
;
1503 const char *connect_path
= lp_pathname(snum
);
1504 const char *msdfs_proxy
= lp_msdfs_proxy(snum
);
1505 connection_struct
*conn
;
1509 if(*connect_path
== '\0') {
1514 * Fake up a connection struct for the VFS layer.
1517 status
= create_conn_struct(talloc_tos(), &conn
, snum
, connect_path
,
1519 if (!NT_STATUS_IS_OK(status
)) {
1520 DEBUG(3, ("create_conn_struct failed: %s\n",
1521 nt_errstr(status
)));
1525 /* Count a link for the msdfs root - convention */
1528 /* No more links if this is an msdfs proxy. */
1529 if (*msdfs_proxy
!= '\0') {
1533 /* Now enumerate all dfs links */
1534 dirp
= SMB_VFS_OPENDIR(conn
, ".", NULL
, 0);
1539 while ((dname
= vfs_readdirname(conn
, dirp
, NULL
, &talloced
))
1541 if (is_msdfs_link(conn
,
1546 TALLOC_FREE(talloced
);
1549 SMB_VFS_CLOSEDIR(conn
,dirp
);
1552 vfs_ChDir(conn
, cwd
);
1557 /*********************************************************************
1558 *********************************************************************/
1560 static int form_junctions(TALLOC_CTX
*ctx
,
1562 struct junction_map
*jucn
,
1566 SMB_STRUCT_DIR
*dirp
= NULL
;
1567 const char *dname
= NULL
;
1568 char *talloced
= NULL
;
1569 const char *connect_path
= lp_pathname(snum
);
1570 char *service_name
= lp_servicename(snum
);
1571 const char *msdfs_proxy
= lp_msdfs_proxy(snum
);
1572 connection_struct
*conn
;
1573 struct referral
*ref
= NULL
;
1577 if (jn_remain
== 0) {
1581 if(*connect_path
== '\0') {
1586 * Fake up a connection struct for the VFS layer.
1589 status
= create_conn_struct(ctx
, &conn
, snum
, connect_path
, NULL
,
1591 if (!NT_STATUS_IS_OK(status
)) {
1592 DEBUG(3, ("create_conn_struct failed: %s\n",
1593 nt_errstr(status
)));
1597 /* form a junction for the msdfs root - convention
1598 DO NOT REMOVE THIS: NT clients will not work with us
1599 if this is not present
1601 jucn
[cnt
].service_name
= talloc_strdup(ctx
,service_name
);
1602 jucn
[cnt
].volume_name
= talloc_strdup(ctx
, "");
1603 if (!jucn
[cnt
].service_name
|| !jucn
[cnt
].volume_name
) {
1606 jucn
[cnt
].comment
= "";
1607 jucn
[cnt
].referral_count
= 1;
1609 ref
= jucn
[cnt
].referral_list
= TALLOC_ZERO_P(ctx
, struct referral
);
1610 if (jucn
[cnt
].referral_list
== NULL
) {
1615 ref
->ttl
= REFERRAL_TTL
;
1616 if (*msdfs_proxy
!= '\0') {
1617 ref
->alternate_path
= talloc_strdup(ctx
,
1620 ref
->alternate_path
= talloc_asprintf(ctx
,
1622 get_local_machine_name(),
1626 if (!ref
->alternate_path
) {
1631 /* Don't enumerate if we're an msdfs proxy. */
1632 if (*msdfs_proxy
!= '\0') {
1636 /* Now enumerate all dfs links */
1637 dirp
= SMB_VFS_OPENDIR(conn
, ".", NULL
, 0);
1642 while ((dname
= vfs_readdirname(conn
, dirp
, NULL
, &talloced
))
1644 char *link_target
= NULL
;
1645 if (cnt
>= jn_remain
) {
1646 DEBUG(2, ("form_junctions: ran out of MSDFS "
1648 TALLOC_FREE(talloced
);
1651 if (is_msdfs_link_internal(ctx
,
1653 dname
, &link_target
,
1655 if (parse_msdfs_symlink(ctx
,
1657 &jucn
[cnt
].referral_list
,
1658 &jucn
[cnt
].referral_count
)) {
1660 jucn
[cnt
].service_name
= talloc_strdup(ctx
,
1662 jucn
[cnt
].volume_name
= talloc_strdup(ctx
,
1664 if (!jucn
[cnt
].service_name
||
1665 !jucn
[cnt
].volume_name
) {
1666 TALLOC_FREE(talloced
);
1669 jucn
[cnt
].comment
= "";
1672 TALLOC_FREE(link_target
);
1674 TALLOC_FREE(talloced
);
1680 SMB_VFS_CLOSEDIR(conn
,dirp
);
1683 vfs_ChDir(conn
, cwd
);
1688 struct junction_map
*enum_msdfs_links(TALLOC_CTX
*ctx
, size_t *p_num_jn
)
1690 struct junction_map
*jn
= NULL
;
1692 size_t jn_count
= 0;
1696 if(!lp_host_msdfs()) {
1700 /* Ensure all the usershares are loaded. */
1702 load_registry_shares();
1703 sharecount
= load_usershare_shares();
1706 for(i
=0;i
< sharecount
;i
++) {
1707 if(lp_msdfs_root(i
)) {
1708 jn_count
+= count_dfs_links(ctx
, i
);
1711 if (jn_count
== 0) {
1714 jn
= TALLOC_ARRAY(ctx
, struct junction_map
, jn_count
);
1718 for(i
=0; i
< sharecount
; i
++) {
1719 if (*p_num_jn
>= jn_count
) {
1722 if(lp_msdfs_root(i
)) {
1723 *p_num_jn
+= form_junctions(ctx
, i
,
1725 jn_count
- *p_num_jn
);
1731 /******************************************************************************
1732 Core function to resolve a dfs pathname possibly containing a wildcard. If
1733 ppath_contains_wcard != NULL, it will be set to true if a wildcard is
1734 detected during dfs resolution.
1735 ******************************************************************************/
1737 NTSTATUS
resolve_dfspath_wcard(TALLOC_CTX
*ctx
,
1738 connection_struct
*conn
,
1740 const char *name_in
,
1743 bool *ppath_contains_wcard
)
1745 bool path_contains_wcard
;
1746 NTSTATUS status
= NT_STATUS_OK
;
1748 if (dfs_pathnames
) {
1749 status
= dfs_redirect(ctx
,
1754 &path_contains_wcard
);
1756 if (NT_STATUS_IS_OK(status
) && ppath_contains_wcard
!= NULL
) {
1757 *ppath_contains_wcard
= path_contains_wcard
;
1761 * Cheat and just return a copy of the in ptr.
1762 * Once srvstr_get_path() uses talloc it'll
1763 * be a talloced ptr anyway.
1765 *pp_name_out
= CONST_DISCARD(char *,name_in
);