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"));
271 conn_free_internal(conn
);
275 conn
->fs_capabilities
= SMB_VFS_FS_CAPABILITIES(conn
);
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_internal(conn
);
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
) ));
296 conn_free_internal(conn
);
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
)
415 int referral_len
= 0;
416 #if defined(HAVE_BROKEN_READLINK)
417 char link_target_buf
[PATH_MAX
];
419 char link_target_buf
[7];
422 char *link_target
= NULL
;
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
;
440 if (SMB_VFS_LSTAT(conn
, path
, sbufp
) != 0) {
441 DEBUG(5,("is_msdfs_link_read_target: %s does not exist.\n",
446 if (!S_ISLNK(sbufp
->st_ex_mode
)) {
447 DEBUG(5,("is_msdfs_link_read_target: %s is not a link.\n",
452 referral_len
= SMB_VFS_READLINK(conn
, path
, link_target
, bufsize
- 1);
453 if (referral_len
== -1) {
454 DEBUG(0,("is_msdfs_link_read_target: Error reading "
455 "msdfs link %s: %s\n",
456 path
, strerror(errno
)));
459 link_target
[referral_len
] = '\0';
461 DEBUG(5,("is_msdfs_link_internal: %s -> %s\n",path
,
464 if (!strnequal(link_target
, "msdfs:", 6)) {
471 if (link_target
!= link_target_buf
) {
472 TALLOC_FREE(link_target
);
477 /**********************************************************************
478 Returns true if the unix path is a valid msdfs symlink.
479 **********************************************************************/
481 bool is_msdfs_link(connection_struct
*conn
,
483 SMB_STRUCT_STAT
*sbufp
)
485 return is_msdfs_link_internal(talloc_tos(),
492 /*****************************************************************
493 Used by other functions to decide if a dfs path is remote,
494 and to get the list of referred locations for that remote path.
496 search_flag: For findfirsts, dfs links themselves are not
497 redirected, but paths beyond the links are. For normal smb calls,
498 even dfs links need to be redirected.
500 consumedcntp: how much of the dfs path is being redirected. the client
501 should try the remaining path on the redirected server.
503 If this returns NT_STATUS_PATH_NOT_COVERED the contents of the msdfs
504 link redirect are in targetpath.
505 *****************************************************************/
507 static NTSTATUS
dfs_path_lookup(TALLOC_CTX
*ctx
,
508 connection_struct
*conn
,
509 const char *dfspath
, /* Incoming complete dfs path */
510 const struct dfs_path
*pdp
, /* Parsed out
511 server+share+extrapath. */
512 bool search_flag
, /* Called from a findfirst ? */
514 char **pp_targetpath
)
519 struct smb_filename
*smb_fname
= NULL
;
520 char *localpath
= 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_ALLOW_WCARD_LCOMP
: 0);
542 if (!NT_STATUS_IS_OK(status
) && !NT_STATUS_EQUAL(status
,
543 NT_STATUS_OBJECT_PATH_NOT_FOUND
)) {
547 status
= get_full_smb_filename(ctx
, smb_fname
, &localpath
);
548 if (!NT_STATUS_IS_OK(status
)) {
549 TALLOC_FREE(smb_fname
);
553 TALLOC_FREE(smb_fname
);
555 /* Optimization - check if we can redirect the whole path. */
557 if (is_msdfs_link_internal(ctx
, conn
, localpath
, pp_targetpath
, NULL
)) {
559 DEBUG(6,("dfs_path_lookup (FindFirst) No redirection "
560 "for dfs link %s.\n", dfspath
));
564 DEBUG(6,("dfs_path_lookup: %s resolves to a "
565 "valid dfs link %s.\n", dfspath
,
566 pp_targetpath
? *pp_targetpath
: ""));
569 *consumedcntp
= strlen(dfspath
);
571 return NT_STATUS_PATH_NOT_COVERED
;
574 /* Prepare to test only for '/' components in the given path,
575 * so if a Windows path replace all '\\' characters with '/'.
576 * For a POSIX DFS path we know all separators are already '/'. */
578 canon_dfspath
= talloc_strdup(ctx
, dfspath
);
579 if (!canon_dfspath
) {
580 return NT_STATUS_NO_MEMORY
;
582 if (!pdp
->posix_path
) {
583 string_replace(canon_dfspath
, '\\', '/');
587 * localpath comes out of unix_convert, so it has
588 * no trailing backslash. Make sure that canon_dfspath hasn't either.
589 * Fix for bug #4860 from Jan Martin <Jan.Martin@rwedea.com>.
592 trim_char(canon_dfspath
,0,'/');
595 * Redirect if any component in the path is a link.
596 * We do this by walking backwards through the
597 * local path, chopping off the last component
598 * in both the local path and the canonicalized
599 * DFS path. If we hit a DFS link then we're done.
602 p
= strrchr_m(localpath
, '/');
604 q
= strrchr_m(canon_dfspath
, '/');
613 if (is_msdfs_link_internal(ctx
, conn
,
614 localpath
, pp_targetpath
, NULL
)) {
615 DEBUG(4, ("dfs_path_lookup: Redirecting %s because "
616 "parent %s is dfs link\n", dfspath
, localpath
));
619 *consumedcntp
= strlen(canon_dfspath
);
620 DEBUG(10, ("dfs_path_lookup: Path consumed: %s "
626 return NT_STATUS_PATH_NOT_COVERED
;
629 /* Step back on the filesystem. */
630 p
= strrchr_m(localpath
, '/');
633 /* And in the canonicalized dfs path. */
634 q
= strrchr_m(canon_dfspath
, '/');
641 /*****************************************************************
642 Decides if a dfs pathname should be redirected or not.
643 If not, the pathname is converted to a tcon-relative local unix path
645 search_wcard_flag: this flag performs 2 functions both related
646 to searches. See resolve_dfs_path() and parse_dfs_path_XX()
649 This function can return NT_STATUS_OK, meaning use the returned path as-is
650 (mapped into a local path).
651 or NT_STATUS_NOT_COVERED meaning return a DFS redirect, or
652 any other NT_STATUS error which is a genuine error to be
653 returned to the client.
654 *****************************************************************/
656 static NTSTATUS
dfs_redirect(TALLOC_CTX
*ctx
,
657 connection_struct
*conn
,
659 bool search_wcard_flag
,
661 bool *ppath_contains_wcard
)
664 struct dfs_path
*pdp
= TALLOC_P(ctx
, struct dfs_path
);
667 return NT_STATUS_NO_MEMORY
;
670 status
= parse_dfs_path(conn
, path_in
, search_wcard_flag
, pdp
,
671 ppath_contains_wcard
);
672 if (!NT_STATUS_IS_OK(status
)) {
677 if (pdp
->reqpath
[0] == '\0') {
679 *pp_path_out
= talloc_strdup(ctx
, "");
681 return NT_STATUS_NO_MEMORY
;
683 DEBUG(5,("dfs_redirect: self-referral.\n"));
687 /* If dfs pathname for a non-dfs share, convert to tcon-relative
688 path and return OK */
690 if (!lp_msdfs_root(SNUM(conn
))) {
691 *pp_path_out
= talloc_strdup(ctx
, pdp
->reqpath
);
694 return NT_STATUS_NO_MEMORY
;
699 /* If it looked like a local path (zero hostname/servicename)
700 * just treat as a tcon-relative path. */
702 if (pdp
->hostname
[0] == '\0' && pdp
->servicename
[0] == '\0') {
703 *pp_path_out
= talloc_strdup(ctx
, pdp
->reqpath
);
706 return NT_STATUS_NO_MEMORY
;
711 if (!( strequal(pdp
->servicename
, lp_servicename(SNUM(conn
)))
712 || (strequal(pdp
->servicename
, HOMES_NAME
)
713 && strequal(lp_servicename(SNUM(conn
)),
714 conn
->server_info
->sanitized_username
) )) ) {
716 /* The given sharename doesn't match this connection. */
719 return NT_STATUS_OBJECT_PATH_NOT_FOUND
;
722 status
= dfs_path_lookup(ctx
, conn
, path_in
, pdp
,
723 search_wcard_flag
, NULL
, NULL
);
724 if (!NT_STATUS_IS_OK(status
)) {
725 if (NT_STATUS_EQUAL(status
, NT_STATUS_PATH_NOT_COVERED
)) {
726 DEBUG(3,("dfs_redirect: Redirecting %s\n", path_in
));
728 DEBUG(10,("dfs_redirect: dfs_path_lookup "
729 "failed for %s with %s\n",
730 path_in
, nt_errstr(status
) ));
735 DEBUG(3,("dfs_redirect: Not redirecting %s.\n", path_in
));
737 /* Form non-dfs tcon-relative path */
738 *pp_path_out
= talloc_strdup(ctx
, pdp
->reqpath
);
741 return NT_STATUS_NO_MEMORY
;
744 DEBUG(3,("dfs_redirect: Path %s converted to non-dfs path %s\n",
751 /**********************************************************************
752 Return a self referral.
753 **********************************************************************/
755 static NTSTATUS
self_ref(TALLOC_CTX
*ctx
,
756 const char *dfs_path
,
757 struct junction_map
*jucn
,
759 bool *self_referralp
)
761 struct referral
*ref
;
763 *self_referralp
= True
;
765 jucn
->referral_count
= 1;
766 if((ref
= TALLOC_ZERO_P(ctx
, struct referral
)) == NULL
) {
767 return NT_STATUS_NO_MEMORY
;
770 ref
->alternate_path
= talloc_strdup(ctx
, dfs_path
);
771 if (!ref
->alternate_path
) {
772 return NT_STATUS_NO_MEMORY
;
775 ref
->ttl
= REFERRAL_TTL
;
776 jucn
->referral_list
= ref
;
777 *consumedcntp
= strlen(dfs_path
);
781 /**********************************************************************
782 Gets valid referrals for a dfs path and fills up the
783 junction_map structure.
784 **********************************************************************/
786 NTSTATUS
get_referred_path(TALLOC_CTX
*ctx
,
787 const char *dfs_path
,
788 struct junction_map
*jucn
,
790 bool *self_referralp
)
792 struct connection_struct
*conn
;
793 char *targetpath
= NULL
;
795 NTSTATUS status
= NT_STATUS_NOT_FOUND
;
797 struct dfs_path
*pdp
= TALLOC_P(ctx
, struct dfs_path
);
801 return NT_STATUS_NO_MEMORY
;
804 *self_referralp
= False
;
806 status
= parse_dfs_path(NULL
, dfs_path
, False
, pdp
, &dummy
);
807 if (!NT_STATUS_IS_OK(status
)) {
811 jucn
->service_name
= talloc_strdup(ctx
, pdp
->servicename
);
812 jucn
->volume_name
= talloc_strdup(ctx
, pdp
->reqpath
);
813 if (!jucn
->service_name
|| !jucn
->volume_name
) {
815 return NT_STATUS_NO_MEMORY
;
818 /* Verify the share is a dfs root */
819 snum
= lp_servicenumber(jucn
->service_name
);
821 fstring service_name
;
822 fstrcpy(service_name
, jucn
->service_name
);
823 if ((snum
= find_service(service_name
)) < 0) {
824 return NT_STATUS_NOT_FOUND
;
826 TALLOC_FREE(jucn
->service_name
);
827 jucn
->service_name
= talloc_strdup(ctx
, service_name
);
828 if (!jucn
->service_name
) {
830 return NT_STATUS_NO_MEMORY
;
834 if (!lp_msdfs_root(snum
) && (*lp_msdfs_proxy(snum
) == '\0')) {
835 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not "
837 pdp
->servicename
, dfs_path
));
839 return NT_STATUS_NOT_FOUND
;
843 * Self referrals are tested with a anonymous IPC connection and
844 * a GET_DFS_REFERRAL call to \\server\share. (which means
845 * dp.reqpath[0] points to an empty string). create_conn_struct cd's
846 * into the directory and will fail if it cannot (as the anonymous
847 * user). Cope with this.
850 if (pdp
->reqpath
[0] == '\0') {
852 struct referral
*ref
;
854 if (*lp_msdfs_proxy(snum
) == '\0') {
864 * It's an msdfs proxy share. Redirect to
865 * the configured target share.
868 jucn
->referral_count
= 1;
869 if ((ref
= TALLOC_ZERO_P(ctx
, struct referral
)) == NULL
) {
871 return NT_STATUS_NO_MEMORY
;
874 if (!(tmp
= talloc_strdup(ctx
, lp_msdfs_proxy(snum
)))) {
876 return NT_STATUS_NO_MEMORY
;
879 trim_string(tmp
, "\\", 0);
881 ref
->alternate_path
= talloc_asprintf(ctx
, "\\%s", tmp
);
884 if (!ref
->alternate_path
) {
886 return NT_STATUS_NO_MEMORY
;
889 if (pdp
->reqpath
[0] != '\0') {
890 ref
->alternate_path
= talloc_asprintf_append(
894 if (!ref
->alternate_path
) {
896 return NT_STATUS_NO_MEMORY
;
900 ref
->ttl
= REFERRAL_TTL
;
901 jucn
->referral_list
= ref
;
902 *consumedcntp
= strlen(dfs_path
);
907 status
= create_conn_struct(ctx
, &conn
, snum
, lp_pathname(snum
),
909 if (!NT_STATUS_IS_OK(status
)) {
914 /* If this is a DFS path dfs_lookup should return
915 * NT_STATUS_PATH_NOT_COVERED. */
917 status
= dfs_path_lookup(ctx
, conn
, dfs_path
, pdp
,
918 False
, consumedcntp
, &targetpath
);
920 if (!NT_STATUS_EQUAL(status
, NT_STATUS_PATH_NOT_COVERED
)) {
921 DEBUG(3,("get_referred_path: No valid referrals for path %s\n",
923 vfs_ChDir(conn
, oldpath
);
924 conn_free_internal(conn
);
929 /* We know this is a valid dfs link. Parse the targetpath. */
930 if (!parse_msdfs_symlink(ctx
, targetpath
,
931 &jucn
->referral_list
,
932 &jucn
->referral_count
)) {
933 DEBUG(3,("get_referred_path: failed to parse symlink "
934 "target %s\n", targetpath
));
935 vfs_ChDir(conn
, oldpath
);
936 conn_free_internal(conn
);
938 return NT_STATUS_NOT_FOUND
;
941 vfs_ChDir(conn
, oldpath
);
942 conn_free_internal(conn
);
947 static int setup_ver2_dfs_referral(const char *pathname
,
949 struct junction_map
*junction
,
952 char* pdata
= *ppdata
;
954 smb_ucs2_t
*uni_requestedpath
= NULL
;
955 int uni_reqpathoffset1
,uni_reqpathoffset2
;
957 int requestedpathlen
=0;
962 DEBUG(10,("Setting up version2 referral\nRequested path:\n"));
964 requestedpathlen
= rpcstr_push_talloc(talloc_tos(),
965 &uni_requestedpath
, pathname
);
966 if (uni_requestedpath
== NULL
|| requestedpathlen
== 0) {
971 dump_data(0, (unsigned char *)uni_requestedpath
,
975 DEBUG(10,("ref count = %u\n",junction
->referral_count
));
977 uni_reqpathoffset1
= REFERRAL_HEADER_SIZE
+
978 VERSION2_REFERRAL_SIZE
* junction
->referral_count
;
980 uni_reqpathoffset2
= uni_reqpathoffset1
+ requestedpathlen
;
982 uni_curroffset
= uni_reqpathoffset2
+ requestedpathlen
;
984 reply_size
= REFERRAL_HEADER_SIZE
+
985 VERSION2_REFERRAL_SIZE
*junction
->referral_count
+
986 2 * requestedpathlen
;
987 DEBUG(10,("reply_size: %u\n",reply_size
));
989 /* add up the unicode lengths of all the referral paths */
990 for(i
=0;i
<junction
->referral_count
;i
++) {
991 DEBUG(10,("referral %u : %s\n",
993 junction
->referral_list
[i
].alternate_path
));
995 (strlen(junction
->referral_list
[i
].alternate_path
)+1)*2;
998 DEBUG(10,("reply_size = %u\n",reply_size
));
999 /* add the unexplained 0x16 bytes */
1002 pdata
= (char *)SMB_REALLOC(pdata
,reply_size
);
1004 DEBUG(0,("Realloc failed!\n"));
1009 /* copy in the dfs requested paths.. required for offset calculations */
1010 memcpy(pdata
+uni_reqpathoffset1
,uni_requestedpath
,requestedpathlen
);
1011 memcpy(pdata
+uni_reqpathoffset2
,uni_requestedpath
,requestedpathlen
);
1013 /* create the header */
1014 SSVAL(pdata
,0,requestedpathlen
- 2); /* UCS2 of path consumed minus
1016 /* number of referral in this pkt */
1017 SSVAL(pdata
,2,junction
->referral_count
);
1019 SIVAL(pdata
,4,DFSREF_REFERRAL_SERVER
| DFSREF_STORAGE_SERVER
);
1021 SIVAL(pdata
,4,DFSREF_STORAGE_SERVER
);
1025 /* add the referral elements */
1026 for(i
=0;i
<junction
->referral_count
;i
++) {
1027 struct referral
* ref
= &junction
->referral_list
[i
];
1030 SSVAL(pdata
,offset
,2); /* version 2 */
1031 SSVAL(pdata
,offset
+2,VERSION2_REFERRAL_SIZE
);
1033 SSVAL(pdata
,offset
+4,1);
1035 SSVAL(pdata
,offset
+4,0);
1038 /* ref_flags :use path_consumed bytes? */
1039 SSVAL(pdata
,offset
+6,0);
1040 SIVAL(pdata
,offset
+8,ref
->proximity
);
1041 SIVAL(pdata
,offset
+12,ref
->ttl
);
1043 SSVAL(pdata
,offset
+16,uni_reqpathoffset1
-offset
);
1044 SSVAL(pdata
,offset
+18,uni_reqpathoffset2
-offset
);
1045 /* copy referred path into current offset */
1046 unilen
= rpcstr_push(pdata
+uni_curroffset
,
1047 ref
->alternate_path
,
1048 reply_size
- uni_curroffset
,
1051 SSVAL(pdata
,offset
+20,uni_curroffset
-offset
);
1053 uni_curroffset
+= unilen
;
1054 offset
+= VERSION2_REFERRAL_SIZE
;
1056 /* add in the unexplained 22 (0x16) bytes at the end */
1057 memset(pdata
+uni_curroffset
,'\0',0x16);
1061 static int setup_ver3_dfs_referral(const char *pathname
,
1063 struct junction_map
*junction
,
1066 char *pdata
= *ppdata
;
1068 smb_ucs2_t
*uni_reqpath
= NULL
;
1069 int uni_reqpathoffset1
, uni_reqpathoffset2
;
1076 DEBUG(10,("setting up version3 referral\n"));
1078 reqpathlen
= rpcstr_push_talloc(talloc_tos(), &uni_reqpath
, pathname
);
1079 if (uni_reqpath
== NULL
|| reqpathlen
== 0) {
1084 dump_data(0, (unsigned char *)uni_reqpath
,
1088 uni_reqpathoffset1
= REFERRAL_HEADER_SIZE
+
1089 VERSION3_REFERRAL_SIZE
* junction
->referral_count
;
1090 uni_reqpathoffset2
= uni_reqpathoffset1
+ reqpathlen
;
1091 reply_size
= uni_curroffset
= uni_reqpathoffset2
+ reqpathlen
;
1093 for(i
=0;i
<junction
->referral_count
;i
++) {
1094 DEBUG(10,("referral %u : %s\n",
1096 junction
->referral_list
[i
].alternate_path
));
1098 (strlen(junction
->referral_list
[i
].alternate_path
)+1)*2;
1101 pdata
= (char *)SMB_REALLOC(pdata
,reply_size
);
1103 DEBUG(0,("version3 referral setup:"
1104 "malloc failed for Realloc!\n"));
1109 /* create the header */
1110 SSVAL(pdata
,0,reqpathlen
- 2); /* UCS2 of path consumed minus
1112 SSVAL(pdata
,2,junction
->referral_count
); /* number of referral */
1114 SIVAL(pdata
,4,DFSREF_REFERRAL_SERVER
| DFSREF_STORAGE_SERVER
);
1116 SIVAL(pdata
,4,DFSREF_STORAGE_SERVER
);
1119 /* copy in the reqpaths */
1120 memcpy(pdata
+uni_reqpathoffset1
,uni_reqpath
,reqpathlen
);
1121 memcpy(pdata
+uni_reqpathoffset2
,uni_reqpath
,reqpathlen
);
1124 for(i
=0;i
<junction
->referral_count
;i
++) {
1125 struct referral
* ref
= &(junction
->referral_list
[i
]);
1128 SSVAL(pdata
,offset
,3); /* version 3 */
1129 SSVAL(pdata
,offset
+2,VERSION3_REFERRAL_SIZE
);
1131 SSVAL(pdata
,offset
+4,1);
1133 SSVAL(pdata
,offset
+4,0);
1136 /* ref_flags :use path_consumed bytes? */
1137 SSVAL(pdata
,offset
+6,0);
1138 SIVAL(pdata
,offset
+8,ref
->ttl
);
1140 SSVAL(pdata
,offset
+12,uni_reqpathoffset1
-offset
);
1141 SSVAL(pdata
,offset
+14,uni_reqpathoffset2
-offset
);
1142 /* copy referred path into current offset */
1143 unilen
= rpcstr_push(pdata
+uni_curroffset
,ref
->alternate_path
,
1144 reply_size
- uni_curroffset
,
1145 STR_UNICODE
| STR_TERMINATE
);
1146 SSVAL(pdata
,offset
+16,uni_curroffset
-offset
);
1147 /* copy 0x10 bytes of 00's in the ServiceSite GUID */
1148 memset(pdata
+offset
+18,'\0',16);
1150 uni_curroffset
+= unilen
;
1151 offset
+= VERSION3_REFERRAL_SIZE
;
1156 /******************************************************************
1157 Set up the DFS referral for the dfs pathname. This call returns
1158 the amount of the path covered by this server, and where the
1159 client should be redirected to. This is the meat of the
1160 TRANS2_GET_DFS_REFERRAL call.
1161 ******************************************************************/
1163 int setup_dfs_referral(connection_struct
*orig_conn
,
1164 const char *dfs_path
,
1165 int max_referral_level
,
1166 char **ppdata
, NTSTATUS
*pstatus
)
1168 struct junction_map
*junction
= NULL
;
1169 int consumedcnt
= 0;
1170 bool self_referral
= False
;
1172 char *pathnamep
= NULL
;
1173 char *local_dfs_path
= NULL
;
1176 if (!(ctx
=talloc_init("setup_dfs_referral"))) {
1177 *pstatus
= NT_STATUS_NO_MEMORY
;
1181 /* get the junction entry */
1183 talloc_destroy(ctx
);
1184 *pstatus
= NT_STATUS_NOT_FOUND
;
1189 * Trim pathname sent by client so it begins with only one backslash.
1190 * Two backslashes confuse some dfs clients
1193 local_dfs_path
= talloc_strdup(ctx
,dfs_path
);
1194 if (!local_dfs_path
) {
1195 *pstatus
= NT_STATUS_NO_MEMORY
;
1196 talloc_destroy(ctx
);
1199 pathnamep
= local_dfs_path
;
1200 while (IS_DIRECTORY_SEP(pathnamep
[0]) &&
1201 IS_DIRECTORY_SEP(pathnamep
[1])) {
1205 junction
= TALLOC_ZERO_P(ctx
, struct junction_map
);
1207 *pstatus
= NT_STATUS_NO_MEMORY
;
1208 talloc_destroy(ctx
);
1212 /* The following call can change cwd. */
1213 *pstatus
= get_referred_path(ctx
, pathnamep
, junction
,
1214 &consumedcnt
, &self_referral
);
1215 if (!NT_STATUS_IS_OK(*pstatus
)) {
1216 vfs_ChDir(orig_conn
,orig_conn
->connectpath
);
1217 talloc_destroy(ctx
);
1220 vfs_ChDir(orig_conn
,orig_conn
->connectpath
);
1222 if (!self_referral
) {
1223 pathnamep
[consumedcnt
] = '\0';
1225 if( DEBUGLVL( 3 ) ) {
1227 dbgtext("setup_dfs_referral: Path %s to "
1228 "alternate path(s):",
1230 for(i
=0;i
<junction
->referral_count
;i
++)
1232 junction
->referral_list
[i
].alternate_path
);
1237 /* create the referral depeding on version */
1238 DEBUG(10,("max_referral_level :%d\n",max_referral_level
));
1240 if (max_referral_level
< 2) {
1241 max_referral_level
= 2;
1243 if (max_referral_level
> 3) {
1244 max_referral_level
= 3;
1247 switch(max_referral_level
) {
1249 reply_size
= setup_ver2_dfs_referral(pathnamep
,
1254 reply_size
= setup_ver3_dfs_referral(pathnamep
, ppdata
,
1255 junction
, self_referral
);
1258 DEBUG(0,("setup_dfs_referral: Invalid dfs referral "
1260 max_referral_level
));
1261 talloc_destroy(ctx
);
1262 *pstatus
= NT_STATUS_INVALID_LEVEL
;
1267 DEBUGADD(0,("DFS Referral pdata:\n"));
1268 dump_data(0,(uint8
*)*ppdata
,reply_size
);
1271 talloc_destroy(ctx
);
1272 *pstatus
= NT_STATUS_OK
;
1276 /**********************************************************************
1277 The following functions are called by the NETDFS RPC pipe functions
1278 **********************************************************************/
1280 /*********************************************************************
1281 Creates a junction structure from a DFS pathname
1282 **********************************************************************/
1284 bool create_junction(TALLOC_CTX
*ctx
,
1285 const char *dfs_path
,
1286 struct junction_map
*jucn
)
1290 struct dfs_path
*pdp
= TALLOC_P(ctx
,struct dfs_path
);
1296 status
= parse_dfs_path(NULL
, dfs_path
, False
, pdp
, &dummy
);
1297 if (!NT_STATUS_IS_OK(status
)) {
1301 /* check if path is dfs : validate first token */
1302 if (!is_myname_or_ipaddr(pdp
->hostname
)) {
1303 DEBUG(4,("create_junction: Invalid hostname %s "
1305 pdp
->hostname
, dfs_path
));
1310 /* Check for a non-DFS share */
1311 snum
= lp_servicenumber(pdp
->servicename
);
1313 if(snum
< 0 || !lp_msdfs_root(snum
)) {
1314 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1320 jucn
->service_name
= talloc_strdup(ctx
, pdp
->servicename
);
1321 jucn
->volume_name
= talloc_strdup(ctx
, pdp
->reqpath
);
1322 jucn
->comment
= talloc_strdup(ctx
, lp_comment(snum
));
1325 if (!jucn
->service_name
|| !jucn
->volume_name
|| ! jucn
->comment
) {
1331 /**********************************************************************
1332 Forms a valid Unix pathname from the junction
1333 **********************************************************************/
1335 static bool junction_to_local_path(const struct junction_map
*jucn
,
1337 connection_struct
**conn_out
,
1343 snum
= lp_servicenumber(jucn
->service_name
);
1347 status
= create_conn_struct(talloc_tos(), conn_out
, snum
,
1348 lp_pathname(snum
), NULL
, oldpath
);
1349 if (!NT_STATUS_IS_OK(status
)) {
1353 *pp_path_out
= talloc_asprintf(*conn_out
,
1357 if (!*pp_path_out
) {
1358 vfs_ChDir(*conn_out
, *oldpath
);
1359 conn_free_internal(*conn_out
);
1365 bool create_msdfs_link(const struct junction_map
*jucn
)
1369 char *msdfs_link
= NULL
;
1370 connection_struct
*conn
;
1372 bool insert_comma
= False
;
1375 if(!junction_to_local_path(jucn
, &path
, &conn
, &cwd
)) {
1379 /* Form the msdfs_link contents */
1380 msdfs_link
= talloc_strdup(conn
, "msdfs:");
1384 for(i
=0; i
<jucn
->referral_count
; i
++) {
1385 char *refpath
= jucn
->referral_list
[i
].alternate_path
;
1387 /* Alternate paths always use Windows separators. */
1388 trim_char(refpath
, '\\', '\\');
1389 if(*refpath
== '\0') {
1391 insert_comma
= False
;
1395 if (i
> 0 && insert_comma
) {
1396 msdfs_link
= talloc_asprintf_append_buffer(msdfs_link
,
1400 msdfs_link
= talloc_asprintf_append_buffer(msdfs_link
,
1408 if (!insert_comma
) {
1409 insert_comma
= True
;
1413 DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
1416 if(SMB_VFS_SYMLINK(conn
, msdfs_link
, path
) < 0) {
1417 if (errno
== EEXIST
) {
1418 if(SMB_VFS_UNLINK(conn
,path
)!=0) {
1422 if (SMB_VFS_SYMLINK(conn
, msdfs_link
, path
) < 0) {
1423 DEBUG(1,("create_msdfs_link: symlink failed "
1424 "%s -> %s\nError: %s\n",
1425 path
, msdfs_link
, strerror(errno
)));
1433 vfs_ChDir(conn
, cwd
);
1434 conn_free_internal(conn
);
1438 bool remove_msdfs_link(const struct junction_map
*jucn
)
1442 connection_struct
*conn
;
1445 if (!junction_to_local_path(jucn
, &path
, &conn
, &cwd
)) {
1449 if( SMB_VFS_UNLINK(conn
, path
) == 0 ) {
1453 vfs_ChDir(conn
, cwd
);
1454 conn_free_internal(conn
);
1458 /*********************************************************************
1459 Return the number of DFS links at the root of this share.
1460 *********************************************************************/
1462 static int count_dfs_links(TALLOC_CTX
*ctx
, int snum
)
1465 SMB_STRUCT_DIR
*dirp
= NULL
;
1467 const char *connect_path
= lp_pathname(snum
);
1468 const char *msdfs_proxy
= lp_msdfs_proxy(snum
);
1469 connection_struct
*conn
;
1473 if(*connect_path
== '\0') {
1478 * Fake up a connection struct for the VFS layer.
1481 status
= create_conn_struct(talloc_tos(), &conn
, snum
, connect_path
,
1483 if (!NT_STATUS_IS_OK(status
)) {
1484 DEBUG(3, ("create_conn_struct failed: %s\n",
1485 nt_errstr(status
)));
1489 /* Count a link for the msdfs root - convention */
1492 /* No more links if this is an msdfs proxy. */
1493 if (*msdfs_proxy
!= '\0') {
1497 /* Now enumerate all dfs links */
1498 dirp
= SMB_VFS_OPENDIR(conn
, ".", NULL
, 0);
1503 while ((dname
= vfs_readdirname(conn
, dirp
, NULL
)) != NULL
) {
1504 if (is_msdfs_link(conn
,
1511 SMB_VFS_CLOSEDIR(conn
,dirp
);
1514 vfs_ChDir(conn
, cwd
);
1515 conn_free_internal(conn
);
1519 /*********************************************************************
1520 *********************************************************************/
1522 static int form_junctions(TALLOC_CTX
*ctx
,
1524 struct junction_map
*jucn
,
1528 SMB_STRUCT_DIR
*dirp
= NULL
;
1530 const char *connect_path
= lp_pathname(snum
);
1531 char *service_name
= lp_servicename(snum
);
1532 const char *msdfs_proxy
= lp_msdfs_proxy(snum
);
1533 connection_struct
*conn
;
1534 struct referral
*ref
= NULL
;
1538 if (jn_remain
== 0) {
1542 if(*connect_path
== '\0') {
1547 * Fake up a connection struct for the VFS layer.
1550 status
= create_conn_struct(ctx
, &conn
, snum
, connect_path
, NULL
,
1552 if (!NT_STATUS_IS_OK(status
)) {
1553 DEBUG(3, ("create_conn_struct failed: %s\n",
1554 nt_errstr(status
)));
1558 /* form a junction for the msdfs root - convention
1559 DO NOT REMOVE THIS: NT clients will not work with us
1560 if this is not present
1562 jucn
[cnt
].service_name
= talloc_strdup(ctx
,service_name
);
1563 jucn
[cnt
].volume_name
= talloc_strdup(ctx
, "");
1564 if (!jucn
[cnt
].service_name
|| !jucn
[cnt
].volume_name
) {
1567 jucn
[cnt
].comment
= "";
1568 jucn
[cnt
].referral_count
= 1;
1570 ref
= jucn
[cnt
].referral_list
= TALLOC_ZERO_P(ctx
, struct referral
);
1571 if (jucn
[cnt
].referral_list
== NULL
) {
1576 ref
->ttl
= REFERRAL_TTL
;
1577 if (*msdfs_proxy
!= '\0') {
1578 ref
->alternate_path
= talloc_strdup(ctx
,
1581 ref
->alternate_path
= talloc_asprintf(ctx
,
1583 get_local_machine_name(),
1587 if (!ref
->alternate_path
) {
1592 /* Don't enumerate if we're an msdfs proxy. */
1593 if (*msdfs_proxy
!= '\0') {
1597 /* Now enumerate all dfs links */
1598 dirp
= SMB_VFS_OPENDIR(conn
, ".", NULL
, 0);
1603 while ((dname
= vfs_readdirname(conn
, dirp
, NULL
)) != NULL
) {
1604 char *link_target
= NULL
;
1605 if (cnt
>= jn_remain
) {
1606 DEBUG(2, ("form_junctions: ran out of MSDFS "
1610 if (is_msdfs_link_internal(ctx
,
1612 dname
, &link_target
,
1614 if (parse_msdfs_symlink(ctx
,
1616 &jucn
[cnt
].referral_list
,
1617 &jucn
[cnt
].referral_count
)) {
1619 jucn
[cnt
].service_name
= talloc_strdup(ctx
,
1621 jucn
[cnt
].volume_name
= talloc_strdup(ctx
,
1623 if (!jucn
[cnt
].service_name
||
1624 !jucn
[cnt
].volume_name
) {
1627 jucn
[cnt
].comment
= "";
1630 TALLOC_FREE(link_target
);
1637 SMB_VFS_CLOSEDIR(conn
,dirp
);
1640 vfs_ChDir(conn
, cwd
);
1641 conn_free_internal(conn
);
1645 struct junction_map
*enum_msdfs_links(TALLOC_CTX
*ctx
, size_t *p_num_jn
)
1647 struct junction_map
*jn
= NULL
;
1649 size_t jn_count
= 0;
1653 if(!lp_host_msdfs()) {
1657 /* Ensure all the usershares are loaded. */
1659 load_registry_shares();
1660 sharecount
= load_usershare_shares();
1663 for(i
=0;i
< sharecount
;i
++) {
1664 if(lp_msdfs_root(i
)) {
1665 jn_count
+= count_dfs_links(ctx
, i
);
1668 if (jn_count
== 0) {
1671 jn
= TALLOC_ARRAY(ctx
, struct junction_map
, jn_count
);
1675 for(i
=0; i
< sharecount
; i
++) {
1676 if (*p_num_jn
>= jn_count
) {
1679 if(lp_msdfs_root(i
)) {
1680 *p_num_jn
+= form_junctions(ctx
, i
,
1682 jn_count
- *p_num_jn
);
1688 /******************************************************************************
1689 Core function to resolve a dfs pathname.
1690 ******************************************************************************/
1692 NTSTATUS
resolve_dfspath(TALLOC_CTX
*ctx
,
1693 connection_struct
*conn
,
1695 const char *name_in
,
1698 NTSTATUS status
= NT_STATUS_OK
;
1700 if (dfs_pathnames
) {
1701 status
= dfs_redirect(ctx
,
1709 * Cheat and just return a copy of the in ptr.
1710 * Once srvstr_get_path() uses talloc it'll
1711 * be a talloced ptr anyway.
1713 *pp_name_out
= CONST_DISCARD(char *,name_in
);
1718 /******************************************************************************
1719 Core function to resolve a dfs pathname possibly containing a wildcard.
1720 This function is identical to the above except for the bool param to
1721 dfs_redirect but I need this to be separate so it's really clear when
1722 we're allowing wildcards and when we're not. JRA.
1723 ******************************************************************************/
1725 NTSTATUS
resolve_dfspath_wcard(TALLOC_CTX
*ctx
,
1726 connection_struct
*conn
,
1728 const char *name_in
,
1730 bool *ppath_contains_wcard
)
1732 NTSTATUS status
= NT_STATUS_OK
;
1733 if (dfs_pathnames
) {
1734 status
= dfs_redirect(ctx
,
1739 ppath_contains_wcard
);
1742 * Cheat and just return a copy of the in ptr.
1743 * Once srvstr_get_path() uses talloc it'll
1744 * be a talloced ptr anyway.
1746 *pp_name_out
= CONST_DISCARD(char *,name_in
);