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_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
)
518 SMB_STRUCT_STAT sbuf
;
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
, search_flag
, &localpath
,
541 if (!NT_STATUS_IS_OK(status
) && !NT_STATUS_EQUAL(status
,
542 NT_STATUS_OBJECT_PATH_NOT_FOUND
)) {
546 /* Optimization - check if we can redirect the whole path. */
548 if (is_msdfs_link_internal(ctx
, conn
, localpath
, pp_targetpath
, NULL
)) {
550 DEBUG(6,("dfs_path_lookup (FindFirst) No redirection "
551 "for dfs link %s.\n", dfspath
));
555 DEBUG(6,("dfs_path_lookup: %s resolves to a "
556 "valid dfs link %s.\n", dfspath
,
557 pp_targetpath
? *pp_targetpath
: ""));
560 *consumedcntp
= strlen(dfspath
);
562 return NT_STATUS_PATH_NOT_COVERED
;
565 /* Prepare to test only for '/' components in the given path,
566 * so if a Windows path replace all '\\' characters with '/'.
567 * For a POSIX DFS path we know all separators are already '/'. */
569 canon_dfspath
= talloc_strdup(ctx
, dfspath
);
570 if (!canon_dfspath
) {
571 return NT_STATUS_NO_MEMORY
;
573 if (!pdp
->posix_path
) {
574 string_replace(canon_dfspath
, '\\', '/');
578 * localpath comes out of unix_convert, so it has
579 * no trailing backslash. Make sure that canon_dfspath hasn't either.
580 * Fix for bug #4860 from Jan Martin <Jan.Martin@rwedea.com>.
583 trim_char(canon_dfspath
,0,'/');
586 * Redirect if any component in the path is a link.
587 * We do this by walking backwards through the
588 * local path, chopping off the last component
589 * in both the local path and the canonicalized
590 * DFS path. If we hit a DFS link then we're done.
593 p
= strrchr_m(localpath
, '/');
595 q
= strrchr_m(canon_dfspath
, '/');
604 if (is_msdfs_link_internal(ctx
, conn
,
605 localpath
, pp_targetpath
, NULL
)) {
606 DEBUG(4, ("dfs_path_lookup: Redirecting %s because "
607 "parent %s is dfs link\n", dfspath
, localpath
));
610 *consumedcntp
= strlen(canon_dfspath
);
611 DEBUG(10, ("dfs_path_lookup: Path consumed: %s "
617 return NT_STATUS_PATH_NOT_COVERED
;
620 /* Step back on the filesystem. */
621 p
= strrchr_m(localpath
, '/');
624 /* And in the canonicalized dfs path. */
625 q
= strrchr_m(canon_dfspath
, '/');
632 /*****************************************************************
633 Decides if a dfs pathname should be redirected or not.
634 If not, the pathname is converted to a tcon-relative local unix path
636 search_wcard_flag: this flag performs 2 functions both related
637 to searches. See resolve_dfs_path() and parse_dfs_path_XX()
640 This function can return NT_STATUS_OK, meaning use the returned path as-is
641 (mapped into a local path).
642 or NT_STATUS_NOT_COVERED meaning return a DFS redirect, or
643 any other NT_STATUS error which is a genuine error to be
644 returned to the client.
645 *****************************************************************/
647 static NTSTATUS
dfs_redirect(TALLOC_CTX
*ctx
,
648 connection_struct
*conn
,
650 bool search_wcard_flag
,
652 bool *ppath_contains_wcard
)
655 struct dfs_path
*pdp
= TALLOC_P(ctx
, struct dfs_path
);
658 return NT_STATUS_NO_MEMORY
;
661 status
= parse_dfs_path(conn
, path_in
, search_wcard_flag
, pdp
,
662 ppath_contains_wcard
);
663 if (!NT_STATUS_IS_OK(status
)) {
668 if (pdp
->reqpath
[0] == '\0') {
670 *pp_path_out
= talloc_strdup(ctx
, "");
672 return NT_STATUS_NO_MEMORY
;
674 DEBUG(5,("dfs_redirect: self-referral.\n"));
678 /* If dfs pathname for a non-dfs share, convert to tcon-relative
679 path and return OK */
681 if (!lp_msdfs_root(SNUM(conn
))) {
682 *pp_path_out
= talloc_strdup(ctx
, pdp
->reqpath
);
685 return NT_STATUS_NO_MEMORY
;
690 /* If it looked like a local path (zero hostname/servicename)
691 * just treat as a tcon-relative path. */
693 if (pdp
->hostname
[0] == '\0' && pdp
->servicename
[0] == '\0') {
694 *pp_path_out
= talloc_strdup(ctx
, pdp
->reqpath
);
697 return NT_STATUS_NO_MEMORY
;
702 if (!( strequal(pdp
->servicename
, lp_servicename(SNUM(conn
)))
703 || (strequal(pdp
->servicename
, HOMES_NAME
)
704 && strequal(lp_servicename(SNUM(conn
)),
705 conn
->server_info
->sanitized_username
) )) ) {
707 /* The given sharename doesn't match this connection. */
710 return NT_STATUS_OBJECT_PATH_NOT_FOUND
;
713 status
= dfs_path_lookup(ctx
, conn
, path_in
, pdp
,
714 search_wcard_flag
, NULL
, NULL
);
715 if (!NT_STATUS_IS_OK(status
)) {
716 if (NT_STATUS_EQUAL(status
, NT_STATUS_PATH_NOT_COVERED
)) {
717 DEBUG(3,("dfs_redirect: Redirecting %s\n", path_in
));
719 DEBUG(10,("dfs_redirect: dfs_path_lookup "
720 "failed for %s with %s\n",
721 path_in
, nt_errstr(status
) ));
726 DEBUG(3,("dfs_redirect: Not redirecting %s.\n", path_in
));
728 /* Form non-dfs tcon-relative path */
729 *pp_path_out
= talloc_strdup(ctx
, pdp
->reqpath
);
732 return NT_STATUS_NO_MEMORY
;
735 DEBUG(3,("dfs_redirect: Path %s converted to non-dfs path %s\n",
742 /**********************************************************************
743 Return a self referral.
744 **********************************************************************/
746 static NTSTATUS
self_ref(TALLOC_CTX
*ctx
,
747 const char *dfs_path
,
748 struct junction_map
*jucn
,
750 bool *self_referralp
)
752 struct referral
*ref
;
754 *self_referralp
= True
;
756 jucn
->referral_count
= 1;
757 if((ref
= TALLOC_ZERO_P(ctx
, struct referral
)) == NULL
) {
758 return NT_STATUS_NO_MEMORY
;
761 ref
->alternate_path
= talloc_strdup(ctx
, dfs_path
);
762 if (!ref
->alternate_path
) {
763 return NT_STATUS_NO_MEMORY
;
766 ref
->ttl
= REFERRAL_TTL
;
767 jucn
->referral_list
= ref
;
768 *consumedcntp
= strlen(dfs_path
);
772 /**********************************************************************
773 Gets valid referrals for a dfs path and fills up the
774 junction_map structure.
775 **********************************************************************/
777 NTSTATUS
get_referred_path(TALLOC_CTX
*ctx
,
778 const char *dfs_path
,
779 struct junction_map
*jucn
,
781 bool *self_referralp
)
783 struct connection_struct
*conn
;
784 char *targetpath
= NULL
;
786 NTSTATUS status
= NT_STATUS_NOT_FOUND
;
788 struct dfs_path
*pdp
= TALLOC_P(ctx
, struct dfs_path
);
792 return NT_STATUS_NO_MEMORY
;
795 *self_referralp
= False
;
797 status
= parse_dfs_path(NULL
, dfs_path
, False
, pdp
, &dummy
);
798 if (!NT_STATUS_IS_OK(status
)) {
802 jucn
->service_name
= talloc_strdup(ctx
, pdp
->servicename
);
803 jucn
->volume_name
= talloc_strdup(ctx
, pdp
->reqpath
);
804 if (!jucn
->service_name
|| !jucn
->volume_name
) {
806 return NT_STATUS_NO_MEMORY
;
809 /* Verify the share is a dfs root */
810 snum
= lp_servicenumber(jucn
->service_name
);
812 fstring service_name
;
813 fstrcpy(service_name
, jucn
->service_name
);
814 if ((snum
= find_service(service_name
)) < 0) {
815 return NT_STATUS_NOT_FOUND
;
817 TALLOC_FREE(jucn
->service_name
);
818 jucn
->service_name
= talloc_strdup(ctx
, service_name
);
819 if (!jucn
->service_name
) {
821 return NT_STATUS_NO_MEMORY
;
825 if (!lp_msdfs_root(snum
) && (*lp_msdfs_proxy(snum
) == '\0')) {
826 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not "
828 pdp
->servicename
, dfs_path
));
830 return NT_STATUS_NOT_FOUND
;
834 * Self referrals are tested with a anonymous IPC connection and
835 * a GET_DFS_REFERRAL call to \\server\share. (which means
836 * dp.reqpath[0] points to an empty string). create_conn_struct cd's
837 * into the directory and will fail if it cannot (as the anonymous
838 * user). Cope with this.
841 if (pdp
->reqpath
[0] == '\0') {
843 struct referral
*ref
;
845 if (*lp_msdfs_proxy(snum
) == '\0') {
855 * It's an msdfs proxy share. Redirect to
856 * the configured target share.
859 jucn
->referral_count
= 1;
860 if ((ref
= TALLOC_ZERO_P(ctx
, struct referral
)) == NULL
) {
862 return NT_STATUS_NO_MEMORY
;
865 if (!(tmp
= talloc_strdup(ctx
, lp_msdfs_proxy(snum
)))) {
867 return NT_STATUS_NO_MEMORY
;
870 trim_string(tmp
, "\\", 0);
872 ref
->alternate_path
= talloc_asprintf(ctx
, "\\%s", tmp
);
875 if (!ref
->alternate_path
) {
877 return NT_STATUS_NO_MEMORY
;
880 if (pdp
->reqpath
[0] != '\0') {
881 ref
->alternate_path
= talloc_asprintf_append(
885 if (!ref
->alternate_path
) {
887 return NT_STATUS_NO_MEMORY
;
891 ref
->ttl
= REFERRAL_TTL
;
892 jucn
->referral_list
= ref
;
893 *consumedcntp
= strlen(dfs_path
);
898 status
= create_conn_struct(ctx
, &conn
, snum
, lp_pathname(snum
),
900 if (!NT_STATUS_IS_OK(status
)) {
905 /* If this is a DFS path dfs_lookup should return
906 * NT_STATUS_PATH_NOT_COVERED. */
908 status
= dfs_path_lookup(ctx
, conn
, dfs_path
, pdp
,
909 False
, consumedcntp
, &targetpath
);
911 if (!NT_STATUS_EQUAL(status
, NT_STATUS_PATH_NOT_COVERED
)) {
912 DEBUG(3,("get_referred_path: No valid referrals for path %s\n",
914 vfs_ChDir(conn
, oldpath
);
915 conn_free_internal(conn
);
920 /* We know this is a valid dfs link. Parse the targetpath. */
921 if (!parse_msdfs_symlink(ctx
, targetpath
,
922 &jucn
->referral_list
,
923 &jucn
->referral_count
)) {
924 DEBUG(3,("get_referred_path: failed to parse symlink "
925 "target %s\n", targetpath
));
926 vfs_ChDir(conn
, oldpath
);
927 conn_free_internal(conn
);
929 return NT_STATUS_NOT_FOUND
;
932 vfs_ChDir(conn
, oldpath
);
933 conn_free_internal(conn
);
938 static int setup_ver2_dfs_referral(const char *pathname
,
940 struct junction_map
*junction
,
943 char* pdata
= *ppdata
;
945 smb_ucs2_t
*uni_requestedpath
= NULL
;
946 int uni_reqpathoffset1
,uni_reqpathoffset2
;
948 int requestedpathlen
=0;
953 DEBUG(10,("Setting up version2 referral\nRequested path:\n"));
955 requestedpathlen
= rpcstr_push_talloc(talloc_tos(),
956 &uni_requestedpath
, pathname
);
957 if (uni_requestedpath
== NULL
|| requestedpathlen
== 0) {
962 dump_data(0, (unsigned char *)uni_requestedpath
,
966 DEBUG(10,("ref count = %u\n",junction
->referral_count
));
968 uni_reqpathoffset1
= REFERRAL_HEADER_SIZE
+
969 VERSION2_REFERRAL_SIZE
* junction
->referral_count
;
971 uni_reqpathoffset2
= uni_reqpathoffset1
+ requestedpathlen
;
973 uni_curroffset
= uni_reqpathoffset2
+ requestedpathlen
;
975 reply_size
= REFERRAL_HEADER_SIZE
+
976 VERSION2_REFERRAL_SIZE
*junction
->referral_count
+
977 2 * requestedpathlen
;
978 DEBUG(10,("reply_size: %u\n",reply_size
));
980 /* add up the unicode lengths of all the referral paths */
981 for(i
=0;i
<junction
->referral_count
;i
++) {
982 DEBUG(10,("referral %u : %s\n",
984 junction
->referral_list
[i
].alternate_path
));
986 (strlen(junction
->referral_list
[i
].alternate_path
)+1)*2;
989 DEBUG(10,("reply_size = %u\n",reply_size
));
990 /* add the unexplained 0x16 bytes */
993 pdata
= (char *)SMB_REALLOC(pdata
,reply_size
);
995 DEBUG(0,("Realloc failed!\n"));
1000 /* copy in the dfs requested paths.. required for offset calculations */
1001 memcpy(pdata
+uni_reqpathoffset1
,uni_requestedpath
,requestedpathlen
);
1002 memcpy(pdata
+uni_reqpathoffset2
,uni_requestedpath
,requestedpathlen
);
1004 /* create the header */
1005 SSVAL(pdata
,0,requestedpathlen
- 2); /* UCS2 of path consumed minus
1007 /* number of referral in this pkt */
1008 SSVAL(pdata
,2,junction
->referral_count
);
1010 SIVAL(pdata
,4,DFSREF_REFERRAL_SERVER
| DFSREF_STORAGE_SERVER
);
1012 SIVAL(pdata
,4,DFSREF_STORAGE_SERVER
);
1016 /* add the referral elements */
1017 for(i
=0;i
<junction
->referral_count
;i
++) {
1018 struct referral
* ref
= &junction
->referral_list
[i
];
1021 SSVAL(pdata
,offset
,2); /* version 2 */
1022 SSVAL(pdata
,offset
+2,VERSION2_REFERRAL_SIZE
);
1024 SSVAL(pdata
,offset
+4,1);
1026 SSVAL(pdata
,offset
+4,0);
1029 /* ref_flags :use path_consumed bytes? */
1030 SSVAL(pdata
,offset
+6,0);
1031 SIVAL(pdata
,offset
+8,ref
->proximity
);
1032 SIVAL(pdata
,offset
+12,ref
->ttl
);
1034 SSVAL(pdata
,offset
+16,uni_reqpathoffset1
-offset
);
1035 SSVAL(pdata
,offset
+18,uni_reqpathoffset2
-offset
);
1036 /* copy referred path into current offset */
1037 unilen
= rpcstr_push(pdata
+uni_curroffset
,
1038 ref
->alternate_path
,
1039 reply_size
- uni_curroffset
,
1042 SSVAL(pdata
,offset
+20,uni_curroffset
-offset
);
1044 uni_curroffset
+= unilen
;
1045 offset
+= VERSION2_REFERRAL_SIZE
;
1047 /* add in the unexplained 22 (0x16) bytes at the end */
1048 memset(pdata
+uni_curroffset
,'\0',0x16);
1052 static int setup_ver3_dfs_referral(const char *pathname
,
1054 struct junction_map
*junction
,
1057 char *pdata
= *ppdata
;
1059 smb_ucs2_t
*uni_reqpath
= NULL
;
1060 int uni_reqpathoffset1
, uni_reqpathoffset2
;
1067 DEBUG(10,("setting up version3 referral\n"));
1069 reqpathlen
= rpcstr_push_talloc(talloc_tos(), &uni_reqpath
, pathname
);
1070 if (uni_reqpath
== NULL
|| reqpathlen
== 0) {
1075 dump_data(0, (unsigned char *)uni_reqpath
,
1079 uni_reqpathoffset1
= REFERRAL_HEADER_SIZE
+
1080 VERSION3_REFERRAL_SIZE
* junction
->referral_count
;
1081 uni_reqpathoffset2
= uni_reqpathoffset1
+ reqpathlen
;
1082 reply_size
= uni_curroffset
= uni_reqpathoffset2
+ reqpathlen
;
1084 for(i
=0;i
<junction
->referral_count
;i
++) {
1085 DEBUG(10,("referral %u : %s\n",
1087 junction
->referral_list
[i
].alternate_path
));
1089 (strlen(junction
->referral_list
[i
].alternate_path
)+1)*2;
1092 pdata
= (char *)SMB_REALLOC(pdata
,reply_size
);
1094 DEBUG(0,("version3 referral setup:"
1095 "malloc failed for Realloc!\n"));
1100 /* create the header */
1101 SSVAL(pdata
,0,reqpathlen
- 2); /* UCS2 of path consumed minus
1103 SSVAL(pdata
,2,junction
->referral_count
); /* number of referral */
1105 SIVAL(pdata
,4,DFSREF_REFERRAL_SERVER
| DFSREF_STORAGE_SERVER
);
1107 SIVAL(pdata
,4,DFSREF_STORAGE_SERVER
);
1110 /* copy in the reqpaths */
1111 memcpy(pdata
+uni_reqpathoffset1
,uni_reqpath
,reqpathlen
);
1112 memcpy(pdata
+uni_reqpathoffset2
,uni_reqpath
,reqpathlen
);
1115 for(i
=0;i
<junction
->referral_count
;i
++) {
1116 struct referral
* ref
= &(junction
->referral_list
[i
]);
1119 SSVAL(pdata
,offset
,3); /* version 3 */
1120 SSVAL(pdata
,offset
+2,VERSION3_REFERRAL_SIZE
);
1122 SSVAL(pdata
,offset
+4,1);
1124 SSVAL(pdata
,offset
+4,0);
1127 /* ref_flags :use path_consumed bytes? */
1128 SSVAL(pdata
,offset
+6,0);
1129 SIVAL(pdata
,offset
+8,ref
->ttl
);
1131 SSVAL(pdata
,offset
+12,uni_reqpathoffset1
-offset
);
1132 SSVAL(pdata
,offset
+14,uni_reqpathoffset2
-offset
);
1133 /* copy referred path into current offset */
1134 unilen
= rpcstr_push(pdata
+uni_curroffset
,ref
->alternate_path
,
1135 reply_size
- uni_curroffset
,
1136 STR_UNICODE
| STR_TERMINATE
);
1137 SSVAL(pdata
,offset
+16,uni_curroffset
-offset
);
1138 /* copy 0x10 bytes of 00's in the ServiceSite GUID */
1139 memset(pdata
+offset
+18,'\0',16);
1141 uni_curroffset
+= unilen
;
1142 offset
+= VERSION3_REFERRAL_SIZE
;
1147 /******************************************************************
1148 Set up the DFS referral for the dfs pathname. This call returns
1149 the amount of the path covered by this server, and where the
1150 client should be redirected to. This is the meat of the
1151 TRANS2_GET_DFS_REFERRAL call.
1152 ******************************************************************/
1154 int setup_dfs_referral(connection_struct
*orig_conn
,
1155 const char *dfs_path
,
1156 int max_referral_level
,
1157 char **ppdata
, NTSTATUS
*pstatus
)
1159 struct junction_map
*junction
= NULL
;
1160 int consumedcnt
= 0;
1161 bool self_referral
= False
;
1163 char *pathnamep
= NULL
;
1164 char *local_dfs_path
= NULL
;
1167 if (!(ctx
=talloc_init("setup_dfs_referral"))) {
1168 *pstatus
= NT_STATUS_NO_MEMORY
;
1172 /* get the junction entry */
1174 talloc_destroy(ctx
);
1175 *pstatus
= NT_STATUS_NOT_FOUND
;
1180 * Trim pathname sent by client so it begins with only one backslash.
1181 * Two backslashes confuse some dfs clients
1184 local_dfs_path
= talloc_strdup(ctx
,dfs_path
);
1185 if (!local_dfs_path
) {
1186 *pstatus
= NT_STATUS_NO_MEMORY
;
1187 talloc_destroy(ctx
);
1190 pathnamep
= local_dfs_path
;
1191 while (IS_DIRECTORY_SEP(pathnamep
[0]) &&
1192 IS_DIRECTORY_SEP(pathnamep
[1])) {
1196 junction
= TALLOC_ZERO_P(ctx
, struct junction_map
);
1198 *pstatus
= NT_STATUS_NO_MEMORY
;
1199 talloc_destroy(ctx
);
1203 /* The following call can change cwd. */
1204 *pstatus
= get_referred_path(ctx
, pathnamep
, junction
,
1205 &consumedcnt
, &self_referral
);
1206 if (!NT_STATUS_IS_OK(*pstatus
)) {
1207 vfs_ChDir(orig_conn
,orig_conn
->connectpath
);
1208 talloc_destroy(ctx
);
1211 vfs_ChDir(orig_conn
,orig_conn
->connectpath
);
1213 if (!self_referral
) {
1214 pathnamep
[consumedcnt
] = '\0';
1216 if( DEBUGLVL( 3 ) ) {
1218 dbgtext("setup_dfs_referral: Path %s to "
1219 "alternate path(s):",
1221 for(i
=0;i
<junction
->referral_count
;i
++)
1223 junction
->referral_list
[i
].alternate_path
);
1228 /* create the referral depeding on version */
1229 DEBUG(10,("max_referral_level :%d\n",max_referral_level
));
1231 if (max_referral_level
< 2) {
1232 max_referral_level
= 2;
1234 if (max_referral_level
> 3) {
1235 max_referral_level
= 3;
1238 switch(max_referral_level
) {
1240 reply_size
= setup_ver2_dfs_referral(pathnamep
,
1245 reply_size
= setup_ver3_dfs_referral(pathnamep
, ppdata
,
1246 junction
, self_referral
);
1249 DEBUG(0,("setup_dfs_referral: Invalid dfs referral "
1251 max_referral_level
));
1252 talloc_destroy(ctx
);
1253 *pstatus
= NT_STATUS_INVALID_LEVEL
;
1258 DEBUGADD(0,("DFS Referral pdata:\n"));
1259 dump_data(0,(uint8
*)*ppdata
,reply_size
);
1262 talloc_destroy(ctx
);
1263 *pstatus
= NT_STATUS_OK
;
1267 /**********************************************************************
1268 The following functions are called by the NETDFS RPC pipe functions
1269 **********************************************************************/
1271 /*********************************************************************
1272 Creates a junction structure from a DFS pathname
1273 **********************************************************************/
1275 bool create_junction(TALLOC_CTX
*ctx
,
1276 const char *dfs_path
,
1277 struct junction_map
*jucn
)
1281 struct dfs_path
*pdp
= TALLOC_P(ctx
,struct dfs_path
);
1287 status
= parse_dfs_path(NULL
, dfs_path
, False
, pdp
, &dummy
);
1288 if (!NT_STATUS_IS_OK(status
)) {
1292 /* check if path is dfs : validate first token */
1293 if (!is_myname_or_ipaddr(pdp
->hostname
)) {
1294 DEBUG(4,("create_junction: Invalid hostname %s "
1296 pdp
->hostname
, dfs_path
));
1301 /* Check for a non-DFS share */
1302 snum
= lp_servicenumber(pdp
->servicename
);
1304 if(snum
< 0 || !lp_msdfs_root(snum
)) {
1305 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1311 jucn
->service_name
= talloc_strdup(ctx
, pdp
->servicename
);
1312 jucn
->volume_name
= talloc_strdup(ctx
, pdp
->reqpath
);
1313 jucn
->comment
= talloc_strdup(ctx
, lp_comment(snum
));
1316 if (!jucn
->service_name
|| !jucn
->volume_name
|| ! jucn
->comment
) {
1322 /**********************************************************************
1323 Forms a valid Unix pathname from the junction
1324 **********************************************************************/
1326 static bool junction_to_local_path(const struct junction_map
*jucn
,
1328 connection_struct
**conn_out
,
1334 snum
= lp_servicenumber(jucn
->service_name
);
1338 status
= create_conn_struct(talloc_tos(), conn_out
, snum
,
1339 lp_pathname(snum
), NULL
, oldpath
);
1340 if (!NT_STATUS_IS_OK(status
)) {
1344 *pp_path_out
= talloc_asprintf(*conn_out
,
1348 if (!*pp_path_out
) {
1349 vfs_ChDir(*conn_out
, *oldpath
);
1350 conn_free_internal(*conn_out
);
1356 bool create_msdfs_link(const struct junction_map
*jucn
)
1360 char *msdfs_link
= NULL
;
1361 connection_struct
*conn
;
1363 bool insert_comma
= False
;
1366 if(!junction_to_local_path(jucn
, &path
, &conn
, &cwd
)) {
1370 /* Form the msdfs_link contents */
1371 msdfs_link
= talloc_strdup(conn
, "msdfs:");
1375 for(i
=0; i
<jucn
->referral_count
; i
++) {
1376 char *refpath
= jucn
->referral_list
[i
].alternate_path
;
1378 /* Alternate paths always use Windows separators. */
1379 trim_char(refpath
, '\\', '\\');
1380 if(*refpath
== '\0') {
1382 insert_comma
= False
;
1386 if (i
> 0 && insert_comma
) {
1387 msdfs_link
= talloc_asprintf_append_buffer(msdfs_link
,
1391 msdfs_link
= talloc_asprintf_append_buffer(msdfs_link
,
1399 if (!insert_comma
) {
1400 insert_comma
= True
;
1404 DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
1407 if(SMB_VFS_SYMLINK(conn
, msdfs_link
, path
) < 0) {
1408 if (errno
== EEXIST
) {
1409 if(SMB_VFS_UNLINK(conn
,path
)!=0) {
1413 if (SMB_VFS_SYMLINK(conn
, msdfs_link
, path
) < 0) {
1414 DEBUG(1,("create_msdfs_link: symlink failed "
1415 "%s -> %s\nError: %s\n",
1416 path
, msdfs_link
, strerror(errno
)));
1424 vfs_ChDir(conn
, cwd
);
1425 conn_free_internal(conn
);
1429 bool remove_msdfs_link(const struct junction_map
*jucn
)
1433 connection_struct
*conn
;
1436 if (!junction_to_local_path(jucn
, &path
, &conn
, &cwd
)) {
1440 if( SMB_VFS_UNLINK(conn
, path
) == 0 ) {
1444 vfs_ChDir(conn
, cwd
);
1445 conn_free_internal(conn
);
1449 /*********************************************************************
1450 Return the number of DFS links at the root of this share.
1451 *********************************************************************/
1453 static int count_dfs_links(TALLOC_CTX
*ctx
, int snum
)
1456 SMB_STRUCT_DIR
*dirp
= NULL
;
1458 const char *connect_path
= lp_pathname(snum
);
1459 const char *msdfs_proxy
= lp_msdfs_proxy(snum
);
1460 connection_struct
*conn
;
1464 if(*connect_path
== '\0') {
1469 * Fake up a connection struct for the VFS layer.
1472 status
= create_conn_struct(talloc_tos(), &conn
, snum
, connect_path
,
1474 if (!NT_STATUS_IS_OK(status
)) {
1475 DEBUG(3, ("create_conn_struct failed: %s\n",
1476 nt_errstr(status
)));
1480 /* Count a link for the msdfs root - convention */
1483 /* No more links if this is an msdfs proxy. */
1484 if (*msdfs_proxy
!= '\0') {
1488 /* Now enumerate all dfs links */
1489 dirp
= SMB_VFS_OPENDIR(conn
, ".", NULL
, 0);
1494 while ((dname
= vfs_readdirname(conn
, dirp
, NULL
)) != NULL
) {
1495 if (is_msdfs_link(conn
,
1502 SMB_VFS_CLOSEDIR(conn
,dirp
);
1505 vfs_ChDir(conn
, cwd
);
1506 conn_free_internal(conn
);
1510 /*********************************************************************
1511 *********************************************************************/
1513 static int form_junctions(TALLOC_CTX
*ctx
,
1515 struct junction_map
*jucn
,
1519 SMB_STRUCT_DIR
*dirp
= NULL
;
1521 const char *connect_path
= lp_pathname(snum
);
1522 char *service_name
= lp_servicename(snum
);
1523 const char *msdfs_proxy
= lp_msdfs_proxy(snum
);
1524 connection_struct
*conn
;
1525 struct referral
*ref
= NULL
;
1529 if (jn_remain
== 0) {
1533 if(*connect_path
== '\0') {
1538 * Fake up a connection struct for the VFS layer.
1541 status
= create_conn_struct(ctx
, &conn
, snum
, connect_path
, NULL
,
1543 if (!NT_STATUS_IS_OK(status
)) {
1544 DEBUG(3, ("create_conn_struct failed: %s\n",
1545 nt_errstr(status
)));
1549 /* form a junction for the msdfs root - convention
1550 DO NOT REMOVE THIS: NT clients will not work with us
1551 if this is not present
1553 jucn
[cnt
].service_name
= talloc_strdup(ctx
,service_name
);
1554 jucn
[cnt
].volume_name
= talloc_strdup(ctx
, "");
1555 if (!jucn
[cnt
].service_name
|| !jucn
[cnt
].volume_name
) {
1558 jucn
[cnt
].comment
= "";
1559 jucn
[cnt
].referral_count
= 1;
1561 ref
= jucn
[cnt
].referral_list
= TALLOC_ZERO_P(ctx
, struct referral
);
1562 if (jucn
[cnt
].referral_list
== NULL
) {
1567 ref
->ttl
= REFERRAL_TTL
;
1568 if (*msdfs_proxy
!= '\0') {
1569 ref
->alternate_path
= talloc_strdup(ctx
,
1572 ref
->alternate_path
= talloc_asprintf(ctx
,
1574 get_local_machine_name(),
1578 if (!ref
->alternate_path
) {
1583 /* Don't enumerate if we're an msdfs proxy. */
1584 if (*msdfs_proxy
!= '\0') {
1588 /* Now enumerate all dfs links */
1589 dirp
= SMB_VFS_OPENDIR(conn
, ".", NULL
, 0);
1594 while ((dname
= vfs_readdirname(conn
, dirp
, NULL
)) != NULL
) {
1595 char *link_target
= NULL
;
1596 if (cnt
>= jn_remain
) {
1597 DEBUG(2, ("form_junctions: ran out of MSDFS "
1601 if (is_msdfs_link_internal(ctx
,
1603 dname
, &link_target
,
1605 if (parse_msdfs_symlink(ctx
,
1607 &jucn
[cnt
].referral_list
,
1608 &jucn
[cnt
].referral_count
)) {
1610 jucn
[cnt
].service_name
= talloc_strdup(ctx
,
1612 jucn
[cnt
].volume_name
= talloc_strdup(ctx
,
1614 if (!jucn
[cnt
].service_name
||
1615 !jucn
[cnt
].volume_name
) {
1618 jucn
[cnt
].comment
= "";
1621 TALLOC_FREE(link_target
);
1628 SMB_VFS_CLOSEDIR(conn
,dirp
);
1631 vfs_ChDir(conn
, cwd
);
1632 conn_free_internal(conn
);
1636 struct junction_map
*enum_msdfs_links(TALLOC_CTX
*ctx
, size_t *p_num_jn
)
1638 struct junction_map
*jn
= NULL
;
1640 size_t jn_count
= 0;
1644 if(!lp_host_msdfs()) {
1648 /* Ensure all the usershares are loaded. */
1650 load_registry_shares();
1651 sharecount
= load_usershare_shares();
1654 for(i
=0;i
< sharecount
;i
++) {
1655 if(lp_msdfs_root(i
)) {
1656 jn_count
+= count_dfs_links(ctx
, i
);
1659 if (jn_count
== 0) {
1662 jn
= TALLOC_ARRAY(ctx
, struct junction_map
, jn_count
);
1666 for(i
=0; i
< sharecount
; i
++) {
1667 if (*p_num_jn
>= jn_count
) {
1670 if(lp_msdfs_root(i
)) {
1671 *p_num_jn
+= form_junctions(ctx
, i
,
1673 jn_count
- *p_num_jn
);
1679 /******************************************************************************
1680 Core function to resolve a dfs pathname.
1681 ******************************************************************************/
1683 NTSTATUS
resolve_dfspath(TALLOC_CTX
*ctx
,
1684 connection_struct
*conn
,
1686 const char *name_in
,
1689 NTSTATUS status
= NT_STATUS_OK
;
1691 if (dfs_pathnames
) {
1692 status
= dfs_redirect(ctx
,
1700 * Cheat and just return a copy of the in ptr.
1701 * Once srvstr_get_path() uses talloc it'll
1702 * be a talloced ptr anyway.
1704 *pp_name_out
= CONST_DISCARD(char *,name_in
);
1709 /******************************************************************************
1710 Core function to resolve a dfs pathname possibly containing a wildcard.
1711 This function is identical to the above except for the bool param to
1712 dfs_redirect but I need this to be separate so it's really clear when
1713 we're allowing wildcards and when we're not. JRA.
1714 ******************************************************************************/
1716 NTSTATUS
resolve_dfspath_wcard(TALLOC_CTX
*ctx
,
1717 connection_struct
*conn
,
1719 const char *name_in
,
1721 bool *ppath_contains_wcard
)
1723 NTSTATUS status
= NT_STATUS_OK
;
1724 if (dfs_pathnames
) {
1725 status
= dfs_redirect(ctx
,
1730 ppath_contains_wcard
);
1733 * Cheat and just return a copy of the in ptr.
1734 * Once srvstr_get_path() uses talloc it'll
1735 * be a talloced ptr anyway.
1737 *pp_name_out
= CONST_DISCARD(char *,name_in
);