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
26 extern uint32 global_client_caps
;
28 /**********************************************************************
29 Parse a DFS pathname of the form \hostname\service\reqpath
30 into the dfs_path structure.
31 If POSIX pathnames is true, the pathname may also be of the
32 form /hostname/service/reqpath.
33 We cope with either here.
35 Unfortunately, due to broken clients who might set the
36 SVAL(inbuf,smb_flg2) & FLAGS2_DFS_PATHNAMES bit and then
37 send a local path, we have to cope with that too....
39 If conn != NULL then ensure the provided service is
40 the one pointed to by the connection.
42 This version does everything using pointers within one copy of the
43 pathname string, talloced on the struct dfs_path pointer (which
44 must be talloced). This may be too clever to live....
46 **********************************************************************/
48 static NTSTATUS
parse_dfs_path(connection_struct
*conn
,
51 struct dfs_path
*pdp
, /* MUST BE TALLOCED */
52 bool *ppath_contains_wcard
)
58 NTSTATUS status
= NT_STATUS_OK
;
64 * This is the only talloc we should need to do
65 * on the struct dfs_path. All the pointers inside
66 * it should point to offsets within this string.
69 pathname_local
= talloc_strdup(pdp
, pathname
);
70 if (!pathname_local
) {
71 return NT_STATUS_NO_MEMORY
;
73 /* Get a pointer to the terminating '\0' */
74 eos_ptr
= &pathname_local
[strlen(pathname_local
)];
75 p
= temp
= pathname_local
;
77 pdp
->posix_path
= (lp_posix_pathnames() && *pathname
== '/');
79 sepchar
= pdp
->posix_path
? '/' : '\\';
81 if (*pathname
!= sepchar
) {
82 DEBUG(10,("parse_dfs_path: path %s doesn't start with %c\n",
85 * Possibly client sent a local path by mistake.
86 * Try and convert to a local path.
89 pdp
->hostname
= eos_ptr
; /* "" */
90 pdp
->servicename
= eos_ptr
; /* "" */
92 /* We've got no info about separators. */
93 pdp
->posix_path
= lp_posix_pathnames();
95 DEBUG(10,("parse_dfs_path: trying to convert %s to a "
102 * Safe to use on talloc'ed string as it only shrinks.
103 * It also doesn't affect the eos_ptr.
105 trim_char(temp
,sepchar
,sepchar
);
107 DEBUG(10,("parse_dfs_path: temp = |%s| after trimming %c's\n",
111 /* Parse out hostname. */
112 p
= strchr_m(temp
,sepchar
);
114 DEBUG(10,("parse_dfs_path: can't parse hostname from path %s\n",
117 * Possibly client sent a local path by mistake.
118 * Try and convert to a local path.
121 pdp
->hostname
= eos_ptr
; /* "" */
122 pdp
->servicename
= eos_ptr
; /* "" */
125 DEBUG(10,("parse_dfs_path: trying to convert %s "
131 pdp
->hostname
= temp
;
133 DEBUG(10,("parse_dfs_path: hostname: %s\n",pdp
->hostname
));
135 /* Parse out servicename. */
137 p
= strchr_m(servicename
,sepchar
);
142 /* Is this really our servicename ? */
143 if (conn
&& !( strequal(servicename
, lp_servicename(SNUM(conn
)))
144 || (strequal(servicename
, HOMES_NAME
)
145 && strequal(lp_servicename(SNUM(conn
)),
146 get_current_username()) )) ) {
147 DEBUG(10,("parse_dfs_path: %s is not our servicename\n",
151 * Possibly client sent a local path by mistake.
152 * Try and convert to a local path.
155 pdp
->hostname
= eos_ptr
; /* "" */
156 pdp
->servicename
= eos_ptr
; /* "" */
158 /* Repair the path - replace the sepchar's
161 *servicename
= sepchar
;
167 DEBUG(10,("parse_dfs_path: trying to convert %s "
173 pdp
->servicename
= servicename
;
175 DEBUG(10,("parse_dfs_path: servicename: %s\n",pdp
->servicename
));
178 /* Client sent self referral \server\share. */
179 pdp
->reqpath
= eos_ptr
; /* "" */
187 *ppath_contains_wcard
= False
;
191 /* Rest is reqpath. */
192 if (pdp
->posix_path
) {
193 status
= check_path_syntax_posix(pdp
->reqpath
);
196 status
= check_path_syntax_wcard(pdp
->reqpath
,
197 ppath_contains_wcard
);
199 status
= check_path_syntax(pdp
->reqpath
);
203 if (!NT_STATUS_IS_OK(status
)) {
204 DEBUG(10,("parse_dfs_path: '%s' failed with %s\n",
205 p
, nt_errstr(status
) ));
209 DEBUG(10,("parse_dfs_path: rest of the path: %s\n",pdp
->reqpath
));
213 /********************************************************
214 Fake up a connection struct for the VFS layer.
215 Note this CHANGES CWD !!!! JRA.
216 *********************************************************/
218 NTSTATUS
create_conn_struct(TALLOC_CTX
*ctx
,
219 connection_struct
**pconn
,
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 set_conn_connectpath(conn
, connpath
);
259 if (!smbd_vfs_init(conn
)) {
260 NTSTATUS status
= map_nt_error_from_unix(errno
);
261 DEBUG(0,("create_conn_struct: smbd_vfs_init failed.\n"));
262 conn_free_internal(conn
);
266 conn
->fs_capabilities
= SMB_VFS_FS_CAPABILITIES(conn
);
269 * Windows seems to insist on doing trans2getdfsreferral() calls on
270 * the IPC$ share as the anonymous user. If we try to chdir as that
271 * user we will fail.... WTF ? JRA.
274 oldcwd
= vfs_GetWd(ctx
, conn
);
275 if (oldcwd
== NULL
) {
276 NTSTATUS status
= map_nt_error_from_unix(errno
);
277 DEBUG(3, ("vfs_GetWd failed: %s\n", strerror(errno
)));
278 conn_free_internal(conn
);
282 if (vfs_ChDir(conn
,conn
->connectpath
) != 0) {
283 NTSTATUS status
= map_nt_error_from_unix(errno
);
284 DEBUG(3,("create_conn_struct: Can't ChDir to new conn path %s. "
286 conn
->connectpath
, strerror(errno
) ));
287 conn_free_internal(conn
);
297 /**********************************************************************
298 Parse the contents of a symlink to verify if it is an msdfs referral
299 A valid referral is of the form:
301 msdfs:server1\share1,server2\share2
302 msdfs:server1\share1\pathname,server2\share2\pathname
303 msdfs:server1/share1,server2/share2
304 msdfs:server1/share1/pathname,server2/share2/pathname.
306 Note that the alternate paths returned here must be of the canonicalized
310 \server\share\path\to\file,
312 even in posix path mode. This is because we have no knowledge if the
313 server we're referring to understands posix paths.
314 **********************************************************************/
316 static bool parse_msdfs_symlink(TALLOC_CTX
*ctx
,
318 struct referral
**preflist
,
323 char **alt_path
= NULL
;
325 struct referral
*reflist
;
328 temp
= talloc_strdup(ctx
, target
);
332 prot
= strtok_r(temp
, ":", &saveptr
);
334 DEBUG(0,("parse_msdfs_symlink: invalid path !\n"));
338 alt_path
= TALLOC_ARRAY(ctx
, char *, MAX_REFERRAL_COUNT
);
343 /* parse out the alternate paths */
344 while((count
<MAX_REFERRAL_COUNT
) &&
345 ((alt_path
[count
] = strtok_r(NULL
, ",", &saveptr
)) != NULL
)) {
349 DEBUG(10,("parse_msdfs_symlink: count=%d\n", count
));
352 reflist
= *preflist
= TALLOC_ZERO_ARRAY(ctx
,
353 struct referral
, count
);
354 if(reflist
== NULL
) {
355 TALLOC_FREE(alt_path
);
359 reflist
= *preflist
= NULL
;
362 for(i
=0;i
<count
;i
++) {
365 /* Canonicalize link target.
366 * Replace all /'s in the path by a \ */
367 string_replace(alt_path
[i
], '/', '\\');
369 /* Remove leading '\\'s */
371 while (*p
&& (*p
== '\\')) {
375 reflist
[i
].alternate_path
= talloc_asprintf(ctx
,
378 if (!reflist
[i
].alternate_path
) {
382 reflist
[i
].proximity
= 0;
383 reflist
[i
].ttl
= REFERRAL_TTL
;
384 DEBUG(10, ("parse_msdfs_symlink: Created alt path: %s\n",
385 reflist
[i
].alternate_path
));
390 TALLOC_FREE(alt_path
);
394 /**********************************************************************
395 Returns true if the unix path is a valid msdfs symlink and also
396 returns the target string from inside the link.
397 **********************************************************************/
399 static bool is_msdfs_link_internal(TALLOC_CTX
*ctx
,
400 connection_struct
*conn
,
402 char **pp_link_target
,
403 SMB_STRUCT_STAT
*sbufp
)
406 int referral_len
= 0;
407 #if defined(HAVE_BROKEN_READLINK)
408 char link_target_buf
[PATH_MAX
];
410 char link_target_buf
[7];
413 char *link_target
= NULL
;
415 if (pp_link_target
) {
417 link_target
= TALLOC_ARRAY(ctx
, char, bufsize
);
421 *pp_link_target
= link_target
;
423 bufsize
= sizeof(link_target_buf
);
424 link_target
= link_target_buf
;
431 if (SMB_VFS_LSTAT(conn
, path
, sbufp
) != 0) {
432 DEBUG(5,("is_msdfs_link_read_target: %s does not exist.\n",
437 if (!S_ISLNK(sbufp
->st_mode
)) {
438 DEBUG(5,("is_msdfs_link_read_target: %s is not a link.\n",
443 referral_len
= SMB_VFS_READLINK(conn
, path
, link_target
, bufsize
- 1);
444 if (referral_len
== -1) {
445 DEBUG(0,("is_msdfs_link_read_target: Error reading "
446 "msdfs link %s: %s\n",
447 path
, strerror(errno
)));
450 link_target
[referral_len
] = '\0';
452 DEBUG(5,("is_msdfs_link_internal: %s -> %s\n",path
,
455 if (!strnequal(link_target
, "msdfs:", 6)) {
462 if (link_target
!= link_target_buf
) {
463 TALLOC_FREE(link_target
);
468 /**********************************************************************
469 Returns true if the unix path is a valid msdfs symlink.
470 **********************************************************************/
472 bool is_msdfs_link(connection_struct
*conn
,
474 SMB_STRUCT_STAT
*sbufp
)
476 return is_msdfs_link_internal(talloc_tos(),
483 /*****************************************************************
484 Used by other functions to decide if a dfs path is remote,
485 and to get the list of referred locations for that remote path.
487 search_flag: For findfirsts, dfs links themselves are not
488 redirected, but paths beyond the links are. For normal smb calls,
489 even dfs links need to be redirected.
491 consumedcntp: how much of the dfs path is being redirected. the client
492 should try the remaining path on the redirected server.
494 If this returns NT_STATUS_PATH_NOT_COVERED the contents of the msdfs
495 link redirect are in targetpath.
496 *****************************************************************/
498 static NTSTATUS
dfs_path_lookup(TALLOC_CTX
*ctx
,
499 connection_struct
*conn
,
500 const char *dfspath
, /* Incoming complete dfs path */
501 const struct dfs_path
*pdp
, /* Parsed out
502 server+share+extrapath. */
503 bool search_flag
, /* Called from a findfirst ? */
505 char **pp_targetpath
)
509 SMB_STRUCT_STAT sbuf
;
511 char *localpath
= NULL
;
512 char *canon_dfspath
= NULL
; /* Canonicalized dfs path. (only '/'
515 DEBUG(10,("dfs_path_lookup: Conn path = %s reqpath = %s\n",
516 conn
->connectpath
, pdp
->reqpath
));
519 * Note the unix path conversion here we're doing we can
520 * throw away. We're looking for a symlink for a dfs
521 * resolution, if we don't find it we'll do another
522 * unix_convert later in the codepath.
523 * If we needed to remember what we'd resolved in
524 * dp->reqpath (as the original code did) we'd
525 * copy (localhost, dp->reqpath) on any code
526 * path below that returns True - but I don't
527 * think this is needed. JRA.
530 status
= unix_convert(ctx
, conn
, pdp
->reqpath
, search_flag
, &localpath
,
532 if (!NT_STATUS_IS_OK(status
) && !NT_STATUS_EQUAL(status
,
533 NT_STATUS_OBJECT_PATH_NOT_FOUND
)) {
537 /* Optimization - check if we can redirect the whole path. */
539 if (is_msdfs_link_internal(ctx
, conn
, localpath
, pp_targetpath
, NULL
)) {
541 DEBUG(6,("dfs_path_lookup (FindFirst) No redirection "
542 "for dfs link %s.\n", dfspath
));
546 DEBUG(6,("dfs_path_lookup: %s resolves to a "
547 "valid dfs link %s.\n", dfspath
,
548 pp_targetpath
? *pp_targetpath
: ""));
551 *consumedcntp
= strlen(dfspath
);
553 return NT_STATUS_PATH_NOT_COVERED
;
556 /* Prepare to test only for '/' components in the given path,
557 * so if a Windows path replace all '\\' characters with '/'.
558 * For a POSIX DFS path we know all separators are already '/'. */
560 canon_dfspath
= talloc_strdup(ctx
, dfspath
);
561 if (!canon_dfspath
) {
562 return NT_STATUS_NO_MEMORY
;
564 if (!pdp
->posix_path
) {
565 string_replace(canon_dfspath
, '\\', '/');
569 * localpath comes out of unix_convert, so it has
570 * no trailing backslash. Make sure that canon_dfspath hasn't either.
571 * Fix for bug #4860 from Jan Martin <Jan.Martin@rwedea.com>.
574 trim_char(canon_dfspath
,0,'/');
577 * Redirect if any component in the path is a link.
578 * We do this by walking backwards through the
579 * local path, chopping off the last component
580 * in both the local path and the canonicalized
581 * DFS path. If we hit a DFS link then we're done.
584 p
= strrchr_m(localpath
, '/');
586 q
= strrchr_m(canon_dfspath
, '/');
595 if (is_msdfs_link_internal(ctx
, conn
,
596 localpath
, pp_targetpath
, NULL
)) {
597 DEBUG(4, ("dfs_path_lookup: Redirecting %s because "
598 "parent %s is dfs link\n", dfspath
, localpath
));
601 *consumedcntp
= strlen(canon_dfspath
);
602 DEBUG(10, ("dfs_path_lookup: Path consumed: %s "
608 return NT_STATUS_PATH_NOT_COVERED
;
611 /* Step back on the filesystem. */
612 p
= strrchr_m(localpath
, '/');
615 /* And in the canonicalized dfs path. */
616 q
= strrchr_m(canon_dfspath
, '/');
623 /*****************************************************************
624 Decides if a dfs pathname should be redirected or not.
625 If not, the pathname is converted to a tcon-relative local unix path
627 search_wcard_flag: this flag performs 2 functions both related
628 to searches. See resolve_dfs_path() and parse_dfs_path_XX()
631 This function can return NT_STATUS_OK, meaning use the returned path as-is
632 (mapped into a local path).
633 or NT_STATUS_NOT_COVERED meaning return a DFS redirect, or
634 any other NT_STATUS error which is a genuine error to be
635 returned to the client.
636 *****************************************************************/
638 static NTSTATUS
dfs_redirect(TALLOC_CTX
*ctx
,
639 connection_struct
*conn
,
641 bool search_wcard_flag
,
643 bool *ppath_contains_wcard
)
646 struct dfs_path
*pdp
= TALLOC_P(ctx
, struct dfs_path
);
649 return NT_STATUS_NO_MEMORY
;
652 status
= parse_dfs_path(conn
, path_in
, search_wcard_flag
, pdp
,
653 ppath_contains_wcard
);
654 if (!NT_STATUS_IS_OK(status
)) {
659 if (pdp
->reqpath
[0] == '\0') {
661 *pp_path_out
= talloc_strdup(ctx
, "");
663 return NT_STATUS_NO_MEMORY
;
665 DEBUG(5,("dfs_redirect: self-referral.\n"));
669 /* If dfs pathname for a non-dfs share, convert to tcon-relative
670 path and return OK */
672 if (!lp_msdfs_root(SNUM(conn
))) {
673 *pp_path_out
= talloc_strdup(ctx
, pdp
->reqpath
);
676 return NT_STATUS_NO_MEMORY
;
681 /* If it looked like a local path (zero hostname/servicename)
682 * just treat as a tcon-relative path. */
684 if (pdp
->hostname
[0] == '\0' && pdp
->servicename
[0] == '\0') {
685 *pp_path_out
= talloc_strdup(ctx
, pdp
->reqpath
);
688 return NT_STATUS_NO_MEMORY
;
693 if (!( strequal(pdp
->servicename
, lp_servicename(SNUM(conn
)))
694 || (strequal(pdp
->servicename
, HOMES_NAME
)
695 && strequal(lp_servicename(SNUM(conn
)),
696 conn
->server_info
->sanitized_username
) )) ) {
698 /* The given sharename doesn't match this connection. */
701 return NT_STATUS_OBJECT_PATH_NOT_FOUND
;
704 status
= dfs_path_lookup(ctx
, conn
, path_in
, pdp
,
705 search_wcard_flag
, NULL
, NULL
);
706 if (!NT_STATUS_IS_OK(status
)) {
707 if (NT_STATUS_EQUAL(status
, NT_STATUS_PATH_NOT_COVERED
)) {
708 DEBUG(3,("dfs_redirect: Redirecting %s\n", path_in
));
710 DEBUG(10,("dfs_redirect: dfs_path_lookup "
711 "failed for %s with %s\n",
712 path_in
, nt_errstr(status
) ));
717 DEBUG(3,("dfs_redirect: Not redirecting %s.\n", path_in
));
719 /* Form non-dfs tcon-relative path */
720 *pp_path_out
= talloc_strdup(ctx
, pdp
->reqpath
);
723 return NT_STATUS_NO_MEMORY
;
726 DEBUG(3,("dfs_redirect: Path %s converted to non-dfs path %s\n",
733 /**********************************************************************
734 Return a self referral.
735 **********************************************************************/
737 static NTSTATUS
self_ref(TALLOC_CTX
*ctx
,
738 const char *dfs_path
,
739 struct junction_map
*jucn
,
741 bool *self_referralp
)
743 struct referral
*ref
;
745 *self_referralp
= True
;
747 jucn
->referral_count
= 1;
748 if((ref
= TALLOC_ZERO_P(ctx
, struct referral
)) == NULL
) {
749 return NT_STATUS_NO_MEMORY
;
752 ref
->alternate_path
= talloc_strdup(ctx
, dfs_path
);
753 if (!ref
->alternate_path
) {
754 return NT_STATUS_NO_MEMORY
;
757 ref
->ttl
= REFERRAL_TTL
;
758 jucn
->referral_list
= ref
;
759 *consumedcntp
= strlen(dfs_path
);
763 /**********************************************************************
764 Gets valid referrals for a dfs path and fills up the
765 junction_map structure.
766 **********************************************************************/
768 NTSTATUS
get_referred_path(TALLOC_CTX
*ctx
,
769 const char *dfs_path
,
770 struct junction_map
*jucn
,
772 bool *self_referralp
)
774 struct connection_struct
*conn
;
775 char *targetpath
= NULL
;
777 NTSTATUS status
= NT_STATUS_NOT_FOUND
;
779 struct dfs_path
*pdp
= TALLOC_P(ctx
, struct dfs_path
);
783 return NT_STATUS_NO_MEMORY
;
786 *self_referralp
= False
;
788 status
= parse_dfs_path(NULL
, dfs_path
, False
, pdp
, &dummy
);
789 if (!NT_STATUS_IS_OK(status
)) {
793 jucn
->service_name
= talloc_strdup(ctx
, pdp
->servicename
);
794 jucn
->volume_name
= talloc_strdup(ctx
, pdp
->reqpath
);
795 if (!jucn
->service_name
|| !jucn
->volume_name
) {
797 return NT_STATUS_NO_MEMORY
;
800 /* Verify the share is a dfs root */
801 snum
= lp_servicenumber(jucn
->service_name
);
803 fstring service_name
;
804 fstrcpy(service_name
, jucn
->service_name
);
805 if ((snum
= find_service(service_name
)) < 0) {
806 return NT_STATUS_NOT_FOUND
;
808 TALLOC_FREE(jucn
->service_name
);
809 jucn
->service_name
= talloc_strdup(ctx
, service_name
);
810 if (!jucn
->service_name
) {
812 return NT_STATUS_NO_MEMORY
;
816 if (!lp_msdfs_root(snum
) && (*lp_msdfs_proxy(snum
) == '\0')) {
817 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not "
819 pdp
->servicename
, dfs_path
));
821 return NT_STATUS_NOT_FOUND
;
825 * Self referrals are tested with a anonymous IPC connection and
826 * a GET_DFS_REFERRAL call to \\server\share. (which means
827 * dp.reqpath[0] points to an empty string). create_conn_struct cd's
828 * into the directory and will fail if it cannot (as the anonymous
829 * user). Cope with this.
832 if (pdp
->reqpath
[0] == '\0') {
834 struct referral
*ref
;
836 if (*lp_msdfs_proxy(snum
) == '\0') {
846 * It's an msdfs proxy share. Redirect to
847 * the configured target share.
850 jucn
->referral_count
= 1;
851 if ((ref
= TALLOC_ZERO_P(ctx
, struct referral
)) == NULL
) {
853 return NT_STATUS_NO_MEMORY
;
856 if (!(tmp
= talloc_strdup(ctx
, lp_msdfs_proxy(snum
)))) {
858 return NT_STATUS_NO_MEMORY
;
861 trim_string(tmp
, "\\", 0);
863 ref
->alternate_path
= talloc_asprintf(ctx
, "\\%s", tmp
);
866 if (!ref
->alternate_path
) {
868 return NT_STATUS_NO_MEMORY
;
871 if (pdp
->reqpath
[0] != '\0') {
872 ref
->alternate_path
= talloc_asprintf_append(
876 if (!ref
->alternate_path
) {
878 return NT_STATUS_NO_MEMORY
;
882 ref
->ttl
= REFERRAL_TTL
;
883 jucn
->referral_list
= ref
;
884 *consumedcntp
= strlen(dfs_path
);
889 status
= create_conn_struct(ctx
, &conn
, snum
, lp_pathname(snum
),
891 if (!NT_STATUS_IS_OK(status
)) {
896 /* If this is a DFS path dfs_lookup should return
897 * NT_STATUS_PATH_NOT_COVERED. */
899 status
= dfs_path_lookup(ctx
, conn
, dfs_path
, pdp
,
900 False
, consumedcntp
, &targetpath
);
902 if (!NT_STATUS_EQUAL(status
, NT_STATUS_PATH_NOT_COVERED
)) {
903 DEBUG(3,("get_referred_path: No valid referrals for path %s\n",
905 vfs_ChDir(conn
, oldpath
);
906 conn_free_internal(conn
);
911 /* We know this is a valid dfs link. Parse the targetpath. */
912 if (!parse_msdfs_symlink(ctx
, targetpath
,
913 &jucn
->referral_list
,
914 &jucn
->referral_count
)) {
915 DEBUG(3,("get_referred_path: failed to parse symlink "
916 "target %s\n", targetpath
));
917 vfs_ChDir(conn
, oldpath
);
918 conn_free_internal(conn
);
920 return NT_STATUS_NOT_FOUND
;
923 vfs_ChDir(conn
, oldpath
);
924 conn_free_internal(conn
);
929 static int setup_ver2_dfs_referral(const char *pathname
,
931 struct junction_map
*junction
,
934 char* pdata
= *ppdata
;
936 smb_ucs2_t
*uni_requestedpath
= NULL
;
937 int uni_reqpathoffset1
,uni_reqpathoffset2
;
939 int requestedpathlen
=0;
944 DEBUG(10,("Setting up version2 referral\nRequested path:\n"));
946 requestedpathlen
= rpcstr_push_talloc(talloc_tos(),
947 &uni_requestedpath
, pathname
);
948 if (uni_requestedpath
== NULL
|| requestedpathlen
== 0) {
953 dump_data(0, (unsigned char *)uni_requestedpath
,
957 DEBUG(10,("ref count = %u\n",junction
->referral_count
));
959 uni_reqpathoffset1
= REFERRAL_HEADER_SIZE
+
960 VERSION2_REFERRAL_SIZE
* junction
->referral_count
;
962 uni_reqpathoffset2
= uni_reqpathoffset1
+ requestedpathlen
;
964 uni_curroffset
= uni_reqpathoffset2
+ requestedpathlen
;
966 reply_size
= REFERRAL_HEADER_SIZE
+
967 VERSION2_REFERRAL_SIZE
*junction
->referral_count
+
968 2 * requestedpathlen
;
969 DEBUG(10,("reply_size: %u\n",reply_size
));
971 /* add up the unicode lengths of all the referral paths */
972 for(i
=0;i
<junction
->referral_count
;i
++) {
973 DEBUG(10,("referral %u : %s\n",
975 junction
->referral_list
[i
].alternate_path
));
977 (strlen(junction
->referral_list
[i
].alternate_path
)+1)*2;
980 DEBUG(10,("reply_size = %u\n",reply_size
));
981 /* add the unexplained 0x16 bytes */
984 pdata
= (char *)SMB_REALLOC(pdata
,reply_size
);
986 DEBUG(0,("Realloc failed!\n"));
991 /* copy in the dfs requested paths.. required for offset calculations */
992 memcpy(pdata
+uni_reqpathoffset1
,uni_requestedpath
,requestedpathlen
);
993 memcpy(pdata
+uni_reqpathoffset2
,uni_requestedpath
,requestedpathlen
);
995 /* create the header */
996 SSVAL(pdata
,0,requestedpathlen
- 2); /* UCS2 of path consumed minus
998 /* number of referral in this pkt */
999 SSVAL(pdata
,2,junction
->referral_count
);
1001 SIVAL(pdata
,4,DFSREF_REFERRAL_SERVER
| DFSREF_STORAGE_SERVER
);
1003 SIVAL(pdata
,4,DFSREF_STORAGE_SERVER
);
1007 /* add the referral elements */
1008 for(i
=0;i
<junction
->referral_count
;i
++) {
1009 struct referral
* ref
= &junction
->referral_list
[i
];
1012 SSVAL(pdata
,offset
,2); /* version 2 */
1013 SSVAL(pdata
,offset
+2,VERSION2_REFERRAL_SIZE
);
1015 SSVAL(pdata
,offset
+4,1);
1017 SSVAL(pdata
,offset
+4,0);
1020 /* ref_flags :use path_consumed bytes? */
1021 SSVAL(pdata
,offset
+6,0);
1022 SIVAL(pdata
,offset
+8,ref
->proximity
);
1023 SIVAL(pdata
,offset
+12,ref
->ttl
);
1025 SSVAL(pdata
,offset
+16,uni_reqpathoffset1
-offset
);
1026 SSVAL(pdata
,offset
+18,uni_reqpathoffset2
-offset
);
1027 /* copy referred path into current offset */
1028 unilen
= rpcstr_push(pdata
+uni_curroffset
,
1029 ref
->alternate_path
,
1030 reply_size
- uni_curroffset
,
1033 SSVAL(pdata
,offset
+20,uni_curroffset
-offset
);
1035 uni_curroffset
+= unilen
;
1036 offset
+= VERSION2_REFERRAL_SIZE
;
1038 /* add in the unexplained 22 (0x16) bytes at the end */
1039 memset(pdata
+uni_curroffset
,'\0',0x16);
1043 static int setup_ver3_dfs_referral(const char *pathname
,
1045 struct junction_map
*junction
,
1048 char *pdata
= *ppdata
;
1050 smb_ucs2_t
*uni_reqpath
= NULL
;
1051 int uni_reqpathoffset1
, uni_reqpathoffset2
;
1058 DEBUG(10,("setting up version3 referral\n"));
1060 reqpathlen
= rpcstr_push_talloc(talloc_tos(), &uni_reqpath
, pathname
);
1061 if (uni_reqpath
== NULL
|| reqpathlen
== 0) {
1066 dump_data(0, (unsigned char *)uni_reqpath
,
1070 uni_reqpathoffset1
= REFERRAL_HEADER_SIZE
+
1071 VERSION3_REFERRAL_SIZE
* junction
->referral_count
;
1072 uni_reqpathoffset2
= uni_reqpathoffset1
+ reqpathlen
;
1073 reply_size
= uni_curroffset
= uni_reqpathoffset2
+ reqpathlen
;
1075 for(i
=0;i
<junction
->referral_count
;i
++) {
1076 DEBUG(10,("referral %u : %s\n",
1078 junction
->referral_list
[i
].alternate_path
));
1080 (strlen(junction
->referral_list
[i
].alternate_path
)+1)*2;
1083 pdata
= (char *)SMB_REALLOC(pdata
,reply_size
);
1085 DEBUG(0,("version3 referral setup:"
1086 "malloc failed for Realloc!\n"));
1091 /* create the header */
1092 SSVAL(pdata
,0,reqpathlen
- 2); /* UCS2 of path consumed minus
1094 SSVAL(pdata
,2,junction
->referral_count
); /* number of referral */
1096 SIVAL(pdata
,4,DFSREF_REFERRAL_SERVER
| DFSREF_STORAGE_SERVER
);
1098 SIVAL(pdata
,4,DFSREF_STORAGE_SERVER
);
1101 /* copy in the reqpaths */
1102 memcpy(pdata
+uni_reqpathoffset1
,uni_reqpath
,reqpathlen
);
1103 memcpy(pdata
+uni_reqpathoffset2
,uni_reqpath
,reqpathlen
);
1106 for(i
=0;i
<junction
->referral_count
;i
++) {
1107 struct referral
* ref
= &(junction
->referral_list
[i
]);
1110 SSVAL(pdata
,offset
,3); /* version 3 */
1111 SSVAL(pdata
,offset
+2,VERSION3_REFERRAL_SIZE
);
1113 SSVAL(pdata
,offset
+4,1);
1115 SSVAL(pdata
,offset
+4,0);
1118 /* ref_flags :use path_consumed bytes? */
1119 SSVAL(pdata
,offset
+6,0);
1120 SIVAL(pdata
,offset
+8,ref
->ttl
);
1122 SSVAL(pdata
,offset
+12,uni_reqpathoffset1
-offset
);
1123 SSVAL(pdata
,offset
+14,uni_reqpathoffset2
-offset
);
1124 /* copy referred path into current offset */
1125 unilen
= rpcstr_push(pdata
+uni_curroffset
,ref
->alternate_path
,
1126 reply_size
- uni_curroffset
,
1127 STR_UNICODE
| STR_TERMINATE
);
1128 SSVAL(pdata
,offset
+16,uni_curroffset
-offset
);
1129 /* copy 0x10 bytes of 00's in the ServiceSite GUID */
1130 memset(pdata
+offset
+18,'\0',16);
1132 uni_curroffset
+= unilen
;
1133 offset
+= VERSION3_REFERRAL_SIZE
;
1138 /******************************************************************
1139 Set up the DFS referral for the dfs pathname. This call returns
1140 the amount of the path covered by this server, and where the
1141 client should be redirected to. This is the meat of the
1142 TRANS2_GET_DFS_REFERRAL call.
1143 ******************************************************************/
1145 int setup_dfs_referral(connection_struct
*orig_conn
,
1146 const char *dfs_path
,
1147 int max_referral_level
,
1148 char **ppdata
, NTSTATUS
*pstatus
)
1150 struct junction_map
*junction
= NULL
;
1151 int consumedcnt
= 0;
1152 bool self_referral
= False
;
1154 char *pathnamep
= NULL
;
1155 char *local_dfs_path
= NULL
;
1158 if (!(ctx
=talloc_init("setup_dfs_referral"))) {
1159 *pstatus
= NT_STATUS_NO_MEMORY
;
1163 /* get the junction entry */
1165 talloc_destroy(ctx
);
1166 *pstatus
= NT_STATUS_NOT_FOUND
;
1171 * Trim pathname sent by client so it begins with only one backslash.
1172 * Two backslashes confuse some dfs clients
1175 local_dfs_path
= talloc_strdup(ctx
,dfs_path
);
1176 if (!local_dfs_path
) {
1177 *pstatus
= NT_STATUS_NO_MEMORY
;
1178 talloc_destroy(ctx
);
1181 pathnamep
= local_dfs_path
;
1182 while (IS_DIRECTORY_SEP(pathnamep
[0]) &&
1183 IS_DIRECTORY_SEP(pathnamep
[1])) {
1187 junction
= TALLOC_ZERO_P(ctx
, struct junction_map
);
1189 *pstatus
= NT_STATUS_NO_MEMORY
;
1190 talloc_destroy(ctx
);
1194 /* The following call can change cwd. */
1195 *pstatus
= get_referred_path(ctx
, pathnamep
, junction
,
1196 &consumedcnt
, &self_referral
);
1197 if (!NT_STATUS_IS_OK(*pstatus
)) {
1198 vfs_ChDir(orig_conn
,orig_conn
->connectpath
);
1199 talloc_destroy(ctx
);
1202 vfs_ChDir(orig_conn
,orig_conn
->connectpath
);
1204 if (!self_referral
) {
1205 pathnamep
[consumedcnt
] = '\0';
1207 if( DEBUGLVL( 3 ) ) {
1209 dbgtext("setup_dfs_referral: Path %s to "
1210 "alternate path(s):",
1212 for(i
=0;i
<junction
->referral_count
;i
++)
1214 junction
->referral_list
[i
].alternate_path
);
1219 /* create the referral depeding on version */
1220 DEBUG(10,("max_referral_level :%d\n",max_referral_level
));
1222 if (max_referral_level
< 2) {
1223 max_referral_level
= 2;
1225 if (max_referral_level
> 3) {
1226 max_referral_level
= 3;
1229 switch(max_referral_level
) {
1231 reply_size
= setup_ver2_dfs_referral(pathnamep
,
1236 reply_size
= setup_ver3_dfs_referral(pathnamep
, ppdata
,
1237 junction
, self_referral
);
1240 DEBUG(0,("setup_dfs_referral: Invalid dfs referral "
1242 max_referral_level
));
1243 talloc_destroy(ctx
);
1244 *pstatus
= NT_STATUS_INVALID_LEVEL
;
1249 DEBUGADD(0,("DFS Referral pdata:\n"));
1250 dump_data(0,(uint8
*)*ppdata
,reply_size
);
1253 talloc_destroy(ctx
);
1254 *pstatus
= NT_STATUS_OK
;
1258 /**********************************************************************
1259 The following functions are called by the NETDFS RPC pipe functions
1260 **********************************************************************/
1262 /*********************************************************************
1263 Creates a junction structure from a DFS pathname
1264 **********************************************************************/
1266 bool create_junction(TALLOC_CTX
*ctx
,
1267 const char *dfs_path
,
1268 struct junction_map
*jucn
)
1272 struct dfs_path
*pdp
= TALLOC_P(ctx
,struct dfs_path
);
1278 status
= parse_dfs_path(NULL
, dfs_path
, False
, pdp
, &dummy
);
1279 if (!NT_STATUS_IS_OK(status
)) {
1283 /* check if path is dfs : validate first token */
1284 if (!is_myname_or_ipaddr(pdp
->hostname
)) {
1285 DEBUG(4,("create_junction: Invalid hostname %s "
1287 pdp
->hostname
, dfs_path
));
1292 /* Check for a non-DFS share */
1293 snum
= lp_servicenumber(pdp
->servicename
);
1295 if(snum
< 0 || !lp_msdfs_root(snum
)) {
1296 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1302 jucn
->service_name
= talloc_strdup(ctx
, pdp
->servicename
);
1303 jucn
->volume_name
= talloc_strdup(ctx
, pdp
->reqpath
);
1304 jucn
->comment
= talloc_strdup(ctx
, lp_comment(snum
));
1307 if (!jucn
->service_name
|| !jucn
->volume_name
|| ! jucn
->comment
) {
1313 /**********************************************************************
1314 Forms a valid Unix pathname from the junction
1315 **********************************************************************/
1317 static bool junction_to_local_path(const struct junction_map
*jucn
,
1319 connection_struct
**conn_out
,
1325 snum
= lp_servicenumber(jucn
->service_name
);
1329 status
= create_conn_struct(talloc_tos(), conn_out
, snum
,
1330 lp_pathname(snum
), oldpath
);
1331 if (!NT_STATUS_IS_OK(status
)) {
1335 *pp_path_out
= talloc_asprintf(*conn_out
,
1339 if (!*pp_path_out
) {
1340 vfs_ChDir(*conn_out
, *oldpath
);
1341 conn_free_internal(*conn_out
);
1347 bool create_msdfs_link(const struct junction_map
*jucn
)
1351 char *msdfs_link
= NULL
;
1352 connection_struct
*conn
;
1354 bool insert_comma
= False
;
1357 if(!junction_to_local_path(jucn
, &path
, &conn
, &cwd
)) {
1361 /* Form the msdfs_link contents */
1362 msdfs_link
= talloc_strdup(conn
, "msdfs:");
1366 for(i
=0; i
<jucn
->referral_count
; i
++) {
1367 char *refpath
= jucn
->referral_list
[i
].alternate_path
;
1369 /* Alternate paths always use Windows separators. */
1370 trim_char(refpath
, '\\', '\\');
1371 if(*refpath
== '\0') {
1373 insert_comma
= False
;
1377 if (i
> 0 && insert_comma
) {
1378 msdfs_link
= talloc_asprintf_append_buffer(msdfs_link
,
1382 msdfs_link
= talloc_asprintf_append_buffer(msdfs_link
,
1390 if (!insert_comma
) {
1391 insert_comma
= True
;
1395 DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
1398 if(SMB_VFS_SYMLINK(conn
, msdfs_link
, path
) < 0) {
1399 if (errno
== EEXIST
) {
1400 if(SMB_VFS_UNLINK(conn
,path
)!=0) {
1404 if (SMB_VFS_SYMLINK(conn
, msdfs_link
, path
) < 0) {
1405 DEBUG(1,("create_msdfs_link: symlink failed "
1406 "%s -> %s\nError: %s\n",
1407 path
, msdfs_link
, strerror(errno
)));
1415 vfs_ChDir(conn
, cwd
);
1416 conn_free_internal(conn
);
1420 bool remove_msdfs_link(const struct junction_map
*jucn
)
1424 connection_struct
*conn
;
1427 if (!junction_to_local_path(jucn
, &path
, &conn
, &cwd
)) {
1431 if( SMB_VFS_UNLINK(conn
, path
) == 0 ) {
1435 vfs_ChDir(conn
, cwd
);
1436 conn_free_internal(conn
);
1440 /*********************************************************************
1441 Return the number of DFS links at the root of this share.
1442 *********************************************************************/
1444 static int count_dfs_links(TALLOC_CTX
*ctx
, int snum
)
1447 SMB_STRUCT_DIR
*dirp
= NULL
;
1449 const char *connect_path
= lp_pathname(snum
);
1450 const char *msdfs_proxy
= lp_msdfs_proxy(snum
);
1451 connection_struct
*conn
;
1455 if(*connect_path
== '\0') {
1460 * Fake up a connection struct for the VFS layer.
1463 status
= create_conn_struct(talloc_tos(), &conn
, snum
, connect_path
,
1465 if (!NT_STATUS_IS_OK(status
)) {
1466 DEBUG(3, ("create_conn_struct failed: %s\n",
1467 nt_errstr(status
)));
1471 /* Count a link for the msdfs root - convention */
1474 /* No more links if this is an msdfs proxy. */
1475 if (*msdfs_proxy
!= '\0') {
1479 /* Now enumerate all dfs links */
1480 dirp
= SMB_VFS_OPENDIR(conn
, ".", NULL
, 0);
1485 while ((dname
= vfs_readdirname(conn
, dirp
)) != NULL
) {
1486 if (is_msdfs_link(conn
,
1493 SMB_VFS_CLOSEDIR(conn
,dirp
);
1496 vfs_ChDir(conn
, cwd
);
1497 conn_free_internal(conn
);
1501 /*********************************************************************
1502 *********************************************************************/
1504 static int form_junctions(TALLOC_CTX
*ctx
,
1506 struct junction_map
*jucn
,
1510 SMB_STRUCT_DIR
*dirp
= NULL
;
1512 const char *connect_path
= lp_pathname(snum
);
1513 char *service_name
= lp_servicename(snum
);
1514 const char *msdfs_proxy
= lp_msdfs_proxy(snum
);
1515 connection_struct
*conn
;
1516 struct referral
*ref
= NULL
;
1520 if (jn_remain
== 0) {
1524 if(*connect_path
== '\0') {
1529 * Fake up a connection struct for the VFS layer.
1532 status
= create_conn_struct(ctx
, &conn
, snum
, connect_path
, &cwd
);
1533 if (!NT_STATUS_IS_OK(status
)) {
1534 DEBUG(3, ("create_conn_struct failed: %s\n",
1535 nt_errstr(status
)));
1539 /* form a junction for the msdfs root - convention
1540 DO NOT REMOVE THIS: NT clients will not work with us
1541 if this is not present
1543 jucn
[cnt
].service_name
= talloc_strdup(ctx
,service_name
);
1544 jucn
[cnt
].volume_name
= talloc_strdup(ctx
, "");
1545 if (!jucn
[cnt
].service_name
|| !jucn
[cnt
].volume_name
) {
1548 jucn
[cnt
].comment
= "";
1549 jucn
[cnt
].referral_count
= 1;
1551 ref
= jucn
[cnt
].referral_list
= TALLOC_ZERO_P(ctx
, struct referral
);
1552 if (jucn
[cnt
].referral_list
== NULL
) {
1557 ref
->ttl
= REFERRAL_TTL
;
1558 if (*msdfs_proxy
!= '\0') {
1559 ref
->alternate_path
= talloc_strdup(ctx
,
1562 ref
->alternate_path
= talloc_asprintf(ctx
,
1564 get_local_machine_name(),
1568 if (!ref
->alternate_path
) {
1573 /* Don't enumerate if we're an msdfs proxy. */
1574 if (*msdfs_proxy
!= '\0') {
1578 /* Now enumerate all dfs links */
1579 dirp
= SMB_VFS_OPENDIR(conn
, ".", NULL
, 0);
1584 while ((dname
= vfs_readdirname(conn
, dirp
)) != NULL
) {
1585 char *link_target
= NULL
;
1586 if (cnt
>= jn_remain
) {
1587 DEBUG(2, ("form_junctions: ran out of MSDFS "
1591 if (is_msdfs_link_internal(ctx
,
1593 dname
, &link_target
,
1595 if (parse_msdfs_symlink(ctx
,
1597 &jucn
[cnt
].referral_list
,
1598 &jucn
[cnt
].referral_count
)) {
1600 jucn
[cnt
].service_name
= talloc_strdup(ctx
,
1602 jucn
[cnt
].volume_name
= talloc_strdup(ctx
,
1604 if (!jucn
[cnt
].service_name
||
1605 !jucn
[cnt
].volume_name
) {
1608 jucn
[cnt
].comment
= "";
1611 TALLOC_FREE(link_target
);
1618 SMB_VFS_CLOSEDIR(conn
,dirp
);
1621 vfs_ChDir(conn
, cwd
);
1622 conn_free_internal(conn
);
1626 struct junction_map
*enum_msdfs_links(TALLOC_CTX
*ctx
, size_t *p_num_jn
)
1628 struct junction_map
*jn
= NULL
;
1630 size_t jn_count
= 0;
1634 if(!lp_host_msdfs()) {
1638 /* Ensure all the usershares are loaded. */
1640 load_registry_shares();
1641 sharecount
= load_usershare_shares();
1644 for(i
=0;i
< sharecount
;i
++) {
1645 if(lp_msdfs_root(i
)) {
1646 jn_count
+= count_dfs_links(ctx
, i
);
1649 if (jn_count
== 0) {
1652 jn
= TALLOC_ARRAY(ctx
, struct junction_map
, jn_count
);
1656 for(i
=0; i
< sharecount
; i
++) {
1657 if (*p_num_jn
>= jn_count
) {
1660 if(lp_msdfs_root(i
)) {
1661 *p_num_jn
+= form_junctions(ctx
, i
,
1663 jn_count
- *p_num_jn
);
1669 /******************************************************************************
1670 Core function to resolve a dfs pathname.
1671 ******************************************************************************/
1673 NTSTATUS
resolve_dfspath(TALLOC_CTX
*ctx
,
1674 connection_struct
*conn
,
1676 const char *name_in
,
1679 NTSTATUS status
= NT_STATUS_OK
;
1681 if (dfs_pathnames
) {
1682 status
= dfs_redirect(ctx
,
1690 * Cheat and just return a copy of the in ptr.
1691 * Once srvstr_get_path() uses talloc it'll
1692 * be a talloced ptr anyway.
1694 *pp_name_out
= CONST_DISCARD(char *,name_in
);
1699 /******************************************************************************
1700 Core function to resolve a dfs pathname possibly containing a wildcard.
1701 This function is identical to the above except for the bool param to
1702 dfs_redirect but I need this to be separate so it's really clear when
1703 we're allowing wildcards and when we're not. JRA.
1704 ******************************************************************************/
1706 NTSTATUS
resolve_dfspath_wcard(TALLOC_CTX
*ctx
,
1707 connection_struct
*conn
,
1709 const char *name_in
,
1711 bool *ppath_contains_wcard
)
1713 NTSTATUS status
= NT_STATUS_OK
;
1714 if (dfs_pathnames
) {
1715 status
= dfs_redirect(ctx
,
1720 ppath_contains_wcard
);
1723 * Cheat and just return a copy of the in ptr.
1724 * Once srvstr_get_path() uses talloc it'll
1725 * be a talloced ptr anyway.
1727 *pp_name_out
= CONST_DISCARD(char *,name_in
);