2 Unix SMB/Netbios implementation.
4 MSDFS services for Samba
5 Copyright (C) Shirish Kalele 2000
6 Copyright (C) Jeremy Allison 2007
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #define DBGC_CLASS DBGC_MSDFS
25 #include "smbd/globals.h"
27 /**********************************************************************
28 Parse a DFS pathname of the form \hostname\service\reqpath
29 into the dfs_path structure.
30 If POSIX pathnames is true, the pathname may also be of the
31 form /hostname/service/reqpath.
32 We cope with either here.
34 Unfortunately, due to broken clients who might set the
35 SVAL(inbuf,smb_flg2) & FLAGS2_DFS_PATHNAMES bit and then
36 send a local path, we have to cope with that too....
38 If conn != NULL then ensure the provided service is
39 the one pointed to by the connection.
41 This version does everything using pointers within one copy of the
42 pathname string, talloced on the struct dfs_path pointer (which
43 must be talloced). This may be too clever to live....
45 **********************************************************************/
47 static NTSTATUS
parse_dfs_path(connection_struct
*conn
,
50 struct dfs_path
*pdp
, /* MUST BE TALLOCED */
51 bool *ppath_contains_wcard
)
57 NTSTATUS status
= NT_STATUS_OK
;
63 * This is the only talloc we should need to do
64 * on the struct dfs_path. All the pointers inside
65 * it should point to offsets within this string.
68 pathname_local
= talloc_strdup(pdp
, pathname
);
69 if (!pathname_local
) {
70 return NT_STATUS_NO_MEMORY
;
72 /* Get a pointer to the terminating '\0' */
73 eos_ptr
= &pathname_local
[strlen(pathname_local
)];
74 p
= temp
= pathname_local
;
76 pdp
->posix_path
= (lp_posix_pathnames() && *pathname
== '/');
78 sepchar
= pdp
->posix_path
? '/' : '\\';
80 if (*pathname
!= sepchar
) {
81 DEBUG(10,("parse_dfs_path: path %s doesn't start with %c\n",
84 * Possibly client sent a local path by mistake.
85 * Try and convert to a local path.
88 pdp
->hostname
= eos_ptr
; /* "" */
89 pdp
->servicename
= eos_ptr
; /* "" */
91 /* We've got no info about separators. */
92 pdp
->posix_path
= lp_posix_pathnames();
94 DEBUG(10,("parse_dfs_path: trying to convert %s to a "
101 * Safe to use on talloc'ed string as it only shrinks.
102 * It also doesn't affect the eos_ptr.
104 trim_char(temp
,sepchar
,sepchar
);
106 DEBUG(10,("parse_dfs_path: temp = |%s| after trimming %c's\n",
110 /* Parse out hostname. */
111 p
= strchr_m(temp
,sepchar
);
113 DEBUG(10,("parse_dfs_path: can't parse hostname from path %s\n",
116 * Possibly client sent a local path by mistake.
117 * Try and convert to a local path.
120 pdp
->hostname
= eos_ptr
; /* "" */
121 pdp
->servicename
= eos_ptr
; /* "" */
124 DEBUG(10,("parse_dfs_path: trying to convert %s "
130 pdp
->hostname
= temp
;
132 DEBUG(10,("parse_dfs_path: hostname: %s\n",pdp
->hostname
));
134 /* Parse out servicename. */
136 p
= strchr_m(servicename
,sepchar
);
141 /* Is this really our servicename ? */
142 if (conn
&& !( strequal(servicename
, lp_servicename(SNUM(conn
)))
143 || (strequal(servicename
, HOMES_NAME
)
144 && strequal(lp_servicename(SNUM(conn
)),
145 get_current_username()) )) ) {
146 DEBUG(10,("parse_dfs_path: %s is not our servicename\n",
150 * Possibly client sent a local path by mistake.
151 * Try and convert to a local path.
154 pdp
->hostname
= eos_ptr
; /* "" */
155 pdp
->servicename
= eos_ptr
; /* "" */
157 /* Repair the path - replace the sepchar's
160 *servicename
= sepchar
;
166 DEBUG(10,("parse_dfs_path: trying to convert %s "
172 pdp
->servicename
= servicename
;
174 DEBUG(10,("parse_dfs_path: servicename: %s\n",pdp
->servicename
));
177 /* Client sent self referral \server\share. */
178 pdp
->reqpath
= eos_ptr
; /* "" */
186 *ppath_contains_wcard
= False
;
190 /* Rest is reqpath. */
191 if (pdp
->posix_path
) {
192 status
= check_path_syntax_posix(pdp
->reqpath
);
195 status
= check_path_syntax_wcard(pdp
->reqpath
,
196 ppath_contains_wcard
);
198 status
= check_path_syntax(pdp
->reqpath
);
202 if (!NT_STATUS_IS_OK(status
)) {
203 DEBUG(10,("parse_dfs_path: '%s' failed with %s\n",
204 p
, nt_errstr(status
) ));
208 DEBUG(10,("parse_dfs_path: rest of the path: %s\n",pdp
->reqpath
));
212 /********************************************************
213 Fake up a connection struct for the VFS layer.
214 Note this CHANGES CWD !!!! JRA.
215 *********************************************************/
217 NTSTATUS
create_conn_struct(TALLOC_CTX
*ctx
,
218 connection_struct
**pconn
,
221 struct auth_serversupplied_info
*server_info
,
224 connection_struct
*conn
;
228 conn
= TALLOC_ZERO_P(ctx
, connection_struct
);
230 return NT_STATUS_NO_MEMORY
;
233 connpath
= talloc_strdup(conn
, path
);
236 return NT_STATUS_NO_MEMORY
;
238 connpath
= talloc_string_sub(conn
,
241 lp_servicename(snum
));
244 return NT_STATUS_NO_MEMORY
;
247 /* needed for smbd_vfs_init() */
249 if (!(conn
->params
= TALLOC_ZERO_P(conn
, struct share_params
))) {
250 DEBUG(0, ("TALLOC failed\n"));
252 return NT_STATUS_NO_MEMORY
;
255 conn
->params
->service
= snum
;
257 if (server_info
!= NULL
) {
258 conn
->server_info
= copy_serverinfo(conn
, server_info
);
259 if (conn
->server_info
== NULL
) {
260 DEBUG(0, ("copy_serverinfo failed\n"));
262 return NT_STATUS_NO_MEMORY
;
266 set_conn_connectpath(conn
, connpath
);
268 if (!smbd_vfs_init(conn
)) {
269 NTSTATUS status
= map_nt_error_from_unix(errno
);
270 DEBUG(0,("create_conn_struct: smbd_vfs_init failed.\n"));
275 conn
->fs_capabilities
= SMB_VFS_FS_CAPABILITIES(conn
, &conn
->ts_res
);
278 * Windows seems to insist on doing trans2getdfsreferral() calls on
279 * the IPC$ share as the anonymous user. If we try to chdir as that
280 * user we will fail.... WTF ? JRA.
283 oldcwd
= vfs_GetWd(ctx
, conn
);
284 if (oldcwd
== NULL
) {
285 NTSTATUS status
= map_nt_error_from_unix(errno
);
286 DEBUG(3, ("vfs_GetWd failed: %s\n", strerror(errno
)));
291 if (vfs_ChDir(conn
,conn
->connectpath
) != 0) {
292 NTSTATUS status
= map_nt_error_from_unix(errno
);
293 DEBUG(3,("create_conn_struct: Can't ChDir to new conn path %s. "
295 conn
->connectpath
, strerror(errno
) ));
306 /**********************************************************************
307 Parse the contents of a symlink to verify if it is an msdfs referral
308 A valid referral is of the form:
310 msdfs:server1\share1,server2\share2
311 msdfs:server1\share1\pathname,server2\share2\pathname
312 msdfs:server1/share1,server2/share2
313 msdfs:server1/share1/pathname,server2/share2/pathname.
315 Note that the alternate paths returned here must be of the canonicalized
319 \server\share\path\to\file,
321 even in posix path mode. This is because we have no knowledge if the
322 server we're referring to understands posix paths.
323 **********************************************************************/
325 static bool parse_msdfs_symlink(TALLOC_CTX
*ctx
,
327 struct referral
**preflist
,
332 char **alt_path
= NULL
;
334 struct referral
*reflist
;
337 temp
= talloc_strdup(ctx
, target
);
341 prot
= strtok_r(temp
, ":", &saveptr
);
343 DEBUG(0,("parse_msdfs_symlink: invalid path !\n"));
347 alt_path
= TALLOC_ARRAY(ctx
, char *, MAX_REFERRAL_COUNT
);
352 /* parse out the alternate paths */
353 while((count
<MAX_REFERRAL_COUNT
) &&
354 ((alt_path
[count
] = strtok_r(NULL
, ",", &saveptr
)) != NULL
)) {
358 DEBUG(10,("parse_msdfs_symlink: count=%d\n", count
));
361 reflist
= *preflist
= TALLOC_ZERO_ARRAY(ctx
,
362 struct referral
, count
);
363 if(reflist
== NULL
) {
364 TALLOC_FREE(alt_path
);
368 reflist
= *preflist
= NULL
;
371 for(i
=0;i
<count
;i
++) {
374 /* Canonicalize link target.
375 * Replace all /'s in the path by a \ */
376 string_replace(alt_path
[i
], '/', '\\');
378 /* Remove leading '\\'s */
380 while (*p
&& (*p
== '\\')) {
384 reflist
[i
].alternate_path
= talloc_asprintf(ctx
,
387 if (!reflist
[i
].alternate_path
) {
391 reflist
[i
].proximity
= 0;
392 reflist
[i
].ttl
= REFERRAL_TTL
;
393 DEBUG(10, ("parse_msdfs_symlink: Created alt path: %s\n",
394 reflist
[i
].alternate_path
));
399 TALLOC_FREE(alt_path
);
403 /**********************************************************************
404 Returns true if the unix path is a valid msdfs symlink and also
405 returns the target string from inside the link.
406 **********************************************************************/
408 static bool is_msdfs_link_internal(TALLOC_CTX
*ctx
,
409 connection_struct
*conn
,
411 char **pp_link_target
,
412 SMB_STRUCT_STAT
*sbufp
)
414 int referral_len
= 0;
415 #if defined(HAVE_BROKEN_READLINK)
416 char link_target_buf
[PATH_MAX
];
418 char link_target_buf
[7];
421 char *link_target
= NULL
;
422 struct smb_filename
*smb_fname
= NULL
;
425 if (pp_link_target
) {
427 link_target
= TALLOC_ARRAY(ctx
, char, bufsize
);
431 *pp_link_target
= link_target
;
433 bufsize
= sizeof(link_target_buf
);
434 link_target
= link_target_buf
;
437 status
= create_synthetic_smb_fname(talloc_tos(), path
, NULL
, NULL
,
439 if (!NT_STATUS_IS_OK(status
)) {
443 if (SMB_VFS_LSTAT(conn
, smb_fname
) != 0) {
444 DEBUG(5,("is_msdfs_link_read_target: %s does not exist.\n",
446 TALLOC_FREE(smb_fname
);
449 if (!S_ISLNK(smb_fname
->st
.st_ex_mode
)) {
450 DEBUG(5,("is_msdfs_link_read_target: %s is not a link.\n",
452 TALLOC_FREE(smb_fname
);
456 *sbufp
= smb_fname
->st
;
458 TALLOC_FREE(smb_fname
);
460 referral_len
= SMB_VFS_READLINK(conn
, path
, link_target
, bufsize
- 1);
461 if (referral_len
== -1) {
462 DEBUG(0,("is_msdfs_link_read_target: Error reading "
463 "msdfs link %s: %s\n",
464 path
, strerror(errno
)));
467 link_target
[referral_len
] = '\0';
469 DEBUG(5,("is_msdfs_link_internal: %s -> %s\n",path
,
472 if (!strnequal(link_target
, "msdfs:", 6)) {
479 if (link_target
!= link_target_buf
) {
480 TALLOC_FREE(link_target
);
485 /**********************************************************************
486 Returns true if the unix path is a valid msdfs symlink.
487 **********************************************************************/
489 bool is_msdfs_link(connection_struct
*conn
,
491 SMB_STRUCT_STAT
*sbufp
)
493 return is_msdfs_link_internal(talloc_tos(),
500 /*****************************************************************
501 Used by other functions to decide if a dfs path is remote,
502 and to get the list of referred locations for that remote path.
504 search_flag: For findfirsts, dfs links themselves are not
505 redirected, but paths beyond the links are. For normal smb calls,
506 even dfs links need to be redirected.
508 consumedcntp: how much of the dfs path is being redirected. the client
509 should try the remaining path on the redirected server.
511 If this returns NT_STATUS_PATH_NOT_COVERED the contents of the msdfs
512 link redirect are in targetpath.
513 *****************************************************************/
515 static NTSTATUS
dfs_path_lookup(TALLOC_CTX
*ctx
,
516 connection_struct
*conn
,
517 const char *dfspath
, /* Incoming complete dfs path */
518 const struct dfs_path
*pdp
, /* Parsed out
519 server+share+extrapath. */
520 bool search_flag
, /* Called from a findfirst ? */
522 char **pp_targetpath
)
527 struct smb_filename
*smb_fname
= NULL
;
528 char *canon_dfspath
= NULL
; /* Canonicalized dfs path. (only '/'
531 DEBUG(10,("dfs_path_lookup: Conn path = %s reqpath = %s\n",
532 conn
->connectpath
, pdp
->reqpath
));
535 * Note the unix path conversion here we're doing we can
536 * throw away. We're looking for a symlink for a dfs
537 * resolution, if we don't find it we'll do another
538 * unix_convert later in the codepath.
539 * If we needed to remember what we'd resolved in
540 * dp->reqpath (as the original code did) we'd
541 * copy (localhost, dp->reqpath) on any code
542 * path below that returns True - but I don't
543 * think this is needed. JRA.
546 status
= unix_convert(ctx
, conn
, pdp
->reqpath
, &smb_fname
,
547 search_flag
? UCF_ALWAYS_ALLOW_WCARD_LCOMP
: 0);
549 if (!NT_STATUS_IS_OK(status
)) {
550 if (!NT_STATUS_EQUAL(status
,
551 NT_STATUS_OBJECT_PATH_NOT_FOUND
)) {
555 /* Create an smb_fname to use below. */
556 status
= create_synthetic_smb_fname(ctx
, pdp
->reqpath
, NULL
,
558 if (!NT_STATUS_IS_OK(status
)) {
563 /* Optimization - check if we can redirect the whole path. */
565 if (is_msdfs_link_internal(ctx
, conn
, smb_fname
->base_name
,
566 pp_targetpath
, NULL
)) {
568 DEBUG(6,("dfs_path_lookup (FindFirst) No redirection "
569 "for dfs link %s.\n", dfspath
));
570 status
= NT_STATUS_OK
;
574 DEBUG(6,("dfs_path_lookup: %s resolves to a "
575 "valid dfs link %s.\n", dfspath
,
576 pp_targetpath
? *pp_targetpath
: ""));
579 *consumedcntp
= strlen(dfspath
);
581 status
= NT_STATUS_PATH_NOT_COVERED
;
585 /* Prepare to test only for '/' components in the given path,
586 * so if a Windows path replace all '\\' characters with '/'.
587 * For a POSIX DFS path we know all separators are already '/'. */
589 canon_dfspath
= talloc_strdup(ctx
, dfspath
);
590 if (!canon_dfspath
) {
591 status
= NT_STATUS_NO_MEMORY
;
594 if (!pdp
->posix_path
) {
595 string_replace(canon_dfspath
, '\\', '/');
599 * localpath comes out of unix_convert, so it has
600 * no trailing backslash. Make sure that canon_dfspath hasn't either.
601 * Fix for bug #4860 from Jan Martin <Jan.Martin@rwedea.com>.
604 trim_char(canon_dfspath
,0,'/');
607 * Redirect if any component in the path is a link.
608 * We do this by walking backwards through the
609 * local path, chopping off the last component
610 * in both the local path and the canonicalized
611 * DFS path. If we hit a DFS link then we're done.
614 p
= strrchr_m(smb_fname
->base_name
, '/');
616 q
= strrchr_m(canon_dfspath
, '/');
625 if (is_msdfs_link_internal(ctx
, conn
,
626 smb_fname
->base_name
, pp_targetpath
,
628 DEBUG(4, ("dfs_path_lookup: Redirecting %s because "
629 "parent %s is dfs link\n", dfspath
,
630 smb_fname_str_dbg(smb_fname
)));
633 *consumedcntp
= strlen(canon_dfspath
);
634 DEBUG(10, ("dfs_path_lookup: Path consumed: %s "
640 status
= NT_STATUS_PATH_NOT_COVERED
;
644 /* Step back on the filesystem. */
645 p
= strrchr_m(smb_fname
->base_name
, '/');
648 /* And in the canonicalized dfs path. */
649 q
= strrchr_m(canon_dfspath
, '/');
653 status
= NT_STATUS_OK
;
655 TALLOC_FREE(smb_fname
);
659 /*****************************************************************
660 Decides if a dfs pathname should be redirected or not.
661 If not, the pathname is converted to a tcon-relative local unix path
663 search_wcard_flag: this flag performs 2 functions both related
664 to searches. See resolve_dfs_path() and parse_dfs_path_XX()
667 This function can return NT_STATUS_OK, meaning use the returned path as-is
668 (mapped into a local path).
669 or NT_STATUS_NOT_COVERED meaning return a DFS redirect, or
670 any other NT_STATUS error which is a genuine error to be
671 returned to the client.
672 *****************************************************************/
674 static NTSTATUS
dfs_redirect(TALLOC_CTX
*ctx
,
675 connection_struct
*conn
,
677 bool search_wcard_flag
,
679 bool *ppath_contains_wcard
)
682 struct dfs_path
*pdp
= TALLOC_P(ctx
, struct dfs_path
);
685 return NT_STATUS_NO_MEMORY
;
688 status
= parse_dfs_path(conn
, path_in
, search_wcard_flag
, pdp
,
689 ppath_contains_wcard
);
690 if (!NT_STATUS_IS_OK(status
)) {
695 if (pdp
->reqpath
[0] == '\0') {
697 *pp_path_out
= talloc_strdup(ctx
, "");
699 return NT_STATUS_NO_MEMORY
;
701 DEBUG(5,("dfs_redirect: self-referral.\n"));
705 /* If dfs pathname for a non-dfs share, convert to tcon-relative
706 path and return OK */
708 if (!lp_msdfs_root(SNUM(conn
))) {
709 *pp_path_out
= talloc_strdup(ctx
, pdp
->reqpath
);
712 return NT_STATUS_NO_MEMORY
;
717 /* If it looked like a local path (zero hostname/servicename)
718 * just treat as a tcon-relative path. */
720 if (pdp
->hostname
[0] == '\0' && pdp
->servicename
[0] == '\0') {
721 *pp_path_out
= talloc_strdup(ctx
, pdp
->reqpath
);
724 return NT_STATUS_NO_MEMORY
;
729 if (!( strequal(pdp
->servicename
, lp_servicename(SNUM(conn
)))
730 || (strequal(pdp
->servicename
, HOMES_NAME
)
731 && strequal(lp_servicename(SNUM(conn
)),
732 conn
->server_info
->sanitized_username
) )) ) {
734 /* The given sharename doesn't match this connection. */
737 return NT_STATUS_OBJECT_PATH_NOT_FOUND
;
740 status
= dfs_path_lookup(ctx
, conn
, path_in
, pdp
,
741 search_wcard_flag
, NULL
, NULL
);
742 if (!NT_STATUS_IS_OK(status
)) {
743 if (NT_STATUS_EQUAL(status
, NT_STATUS_PATH_NOT_COVERED
)) {
744 DEBUG(3,("dfs_redirect: Redirecting %s\n", path_in
));
746 DEBUG(10,("dfs_redirect: dfs_path_lookup "
747 "failed for %s with %s\n",
748 path_in
, nt_errstr(status
) ));
753 DEBUG(3,("dfs_redirect: Not redirecting %s.\n", path_in
));
755 /* Form non-dfs tcon-relative path */
756 *pp_path_out
= talloc_strdup(ctx
, pdp
->reqpath
);
759 return NT_STATUS_NO_MEMORY
;
762 DEBUG(3,("dfs_redirect: Path %s converted to non-dfs path %s\n",
769 /**********************************************************************
770 Return a self referral.
771 **********************************************************************/
773 static NTSTATUS
self_ref(TALLOC_CTX
*ctx
,
774 const char *dfs_path
,
775 struct junction_map
*jucn
,
777 bool *self_referralp
)
779 struct referral
*ref
;
781 *self_referralp
= True
;
783 jucn
->referral_count
= 1;
784 if((ref
= TALLOC_ZERO_P(ctx
, struct referral
)) == NULL
) {
785 return NT_STATUS_NO_MEMORY
;
788 ref
->alternate_path
= talloc_strdup(ctx
, dfs_path
);
789 if (!ref
->alternate_path
) {
790 return NT_STATUS_NO_MEMORY
;
793 ref
->ttl
= REFERRAL_TTL
;
794 jucn
->referral_list
= ref
;
795 *consumedcntp
= strlen(dfs_path
);
799 /**********************************************************************
800 Gets valid referrals for a dfs path and fills up the
801 junction_map structure.
802 **********************************************************************/
804 NTSTATUS
get_referred_path(TALLOC_CTX
*ctx
,
805 const char *dfs_path
,
806 struct junction_map
*jucn
,
808 bool *self_referralp
)
810 struct connection_struct
*conn
;
811 char *targetpath
= NULL
;
813 NTSTATUS status
= NT_STATUS_NOT_FOUND
;
815 struct dfs_path
*pdp
= TALLOC_P(ctx
, struct dfs_path
);
819 return NT_STATUS_NO_MEMORY
;
822 *self_referralp
= False
;
824 status
= parse_dfs_path(NULL
, dfs_path
, False
, pdp
, &dummy
);
825 if (!NT_STATUS_IS_OK(status
)) {
829 jucn
->service_name
= talloc_strdup(ctx
, pdp
->servicename
);
830 jucn
->volume_name
= talloc_strdup(ctx
, pdp
->reqpath
);
831 if (!jucn
->service_name
|| !jucn
->volume_name
) {
833 return NT_STATUS_NO_MEMORY
;
836 /* Verify the share is a dfs root */
837 snum
= lp_servicenumber(jucn
->service_name
);
839 fstring service_name
;
840 fstrcpy(service_name
, jucn
->service_name
);
841 if ((snum
= find_service(service_name
)) < 0) {
842 return NT_STATUS_NOT_FOUND
;
844 TALLOC_FREE(jucn
->service_name
);
845 jucn
->service_name
= talloc_strdup(ctx
, service_name
);
846 if (!jucn
->service_name
) {
848 return NT_STATUS_NO_MEMORY
;
852 if (!lp_msdfs_root(snum
) && (*lp_msdfs_proxy(snum
) == '\0')) {
853 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not "
855 pdp
->servicename
, dfs_path
));
857 return NT_STATUS_NOT_FOUND
;
861 * Self referrals are tested with a anonymous IPC connection and
862 * a GET_DFS_REFERRAL call to \\server\share. (which means
863 * dp.reqpath[0] points to an empty string). create_conn_struct cd's
864 * into the directory and will fail if it cannot (as the anonymous
865 * user). Cope with this.
868 if (pdp
->reqpath
[0] == '\0') {
870 struct referral
*ref
;
872 if (*lp_msdfs_proxy(snum
) == '\0') {
882 * It's an msdfs proxy share. Redirect to
883 * the configured target share.
886 jucn
->referral_count
= 1;
887 if ((ref
= TALLOC_ZERO_P(ctx
, struct referral
)) == NULL
) {
889 return NT_STATUS_NO_MEMORY
;
892 if (!(tmp
= talloc_strdup(ctx
, lp_msdfs_proxy(snum
)))) {
894 return NT_STATUS_NO_MEMORY
;
897 trim_string(tmp
, "\\", 0);
899 ref
->alternate_path
= talloc_asprintf(ctx
, "\\%s", tmp
);
902 if (!ref
->alternate_path
) {
904 return NT_STATUS_NO_MEMORY
;
907 if (pdp
->reqpath
[0] != '\0') {
908 ref
->alternate_path
= talloc_asprintf_append(
912 if (!ref
->alternate_path
) {
914 return NT_STATUS_NO_MEMORY
;
918 ref
->ttl
= REFERRAL_TTL
;
919 jucn
->referral_list
= ref
;
920 *consumedcntp
= strlen(dfs_path
);
925 status
= create_conn_struct(ctx
, &conn
, snum
, lp_pathname(snum
),
927 if (!NT_STATUS_IS_OK(status
)) {
932 /* If this is a DFS path dfs_lookup should return
933 * NT_STATUS_PATH_NOT_COVERED. */
935 status
= dfs_path_lookup(ctx
, conn
, dfs_path
, pdp
,
936 False
, consumedcntp
, &targetpath
);
938 if (!NT_STATUS_EQUAL(status
, NT_STATUS_PATH_NOT_COVERED
)) {
939 DEBUG(3,("get_referred_path: No valid referrals for path %s\n",
941 vfs_ChDir(conn
, oldpath
);
947 /* We know this is a valid dfs link. Parse the targetpath. */
948 if (!parse_msdfs_symlink(ctx
, targetpath
,
949 &jucn
->referral_list
,
950 &jucn
->referral_count
)) {
951 DEBUG(3,("get_referred_path: failed to parse symlink "
952 "target %s\n", targetpath
));
953 vfs_ChDir(conn
, oldpath
);
956 return NT_STATUS_NOT_FOUND
;
959 vfs_ChDir(conn
, oldpath
);
965 static int setup_ver2_dfs_referral(const char *pathname
,
967 struct junction_map
*junction
,
970 char* pdata
= *ppdata
;
972 smb_ucs2_t
*uni_requestedpath
= NULL
;
973 int uni_reqpathoffset1
,uni_reqpathoffset2
;
975 int requestedpathlen
=0;
980 DEBUG(10,("Setting up version2 referral\nRequested path:\n"));
982 requestedpathlen
= rpcstr_push_talloc(talloc_tos(),
983 &uni_requestedpath
, pathname
);
984 if (uni_requestedpath
== NULL
|| requestedpathlen
== 0) {
989 dump_data(0, (unsigned char *)uni_requestedpath
,
993 DEBUG(10,("ref count = %u\n",junction
->referral_count
));
995 uni_reqpathoffset1
= REFERRAL_HEADER_SIZE
+
996 VERSION2_REFERRAL_SIZE
* junction
->referral_count
;
998 uni_reqpathoffset2
= uni_reqpathoffset1
+ requestedpathlen
;
1000 uni_curroffset
= uni_reqpathoffset2
+ requestedpathlen
;
1002 reply_size
= REFERRAL_HEADER_SIZE
+
1003 VERSION2_REFERRAL_SIZE
*junction
->referral_count
+
1004 2 * requestedpathlen
;
1005 DEBUG(10,("reply_size: %u\n",reply_size
));
1007 /* add up the unicode lengths of all the referral paths */
1008 for(i
=0;i
<junction
->referral_count
;i
++) {
1009 DEBUG(10,("referral %u : %s\n",
1011 junction
->referral_list
[i
].alternate_path
));
1013 (strlen(junction
->referral_list
[i
].alternate_path
)+1)*2;
1016 DEBUG(10,("reply_size = %u\n",reply_size
));
1017 /* add the unexplained 0x16 bytes */
1020 pdata
= (char *)SMB_REALLOC(pdata
,reply_size
);
1022 DEBUG(0,("Realloc failed!\n"));
1027 /* copy in the dfs requested paths.. required for offset calculations */
1028 memcpy(pdata
+uni_reqpathoffset1
,uni_requestedpath
,requestedpathlen
);
1029 memcpy(pdata
+uni_reqpathoffset2
,uni_requestedpath
,requestedpathlen
);
1031 /* create the header */
1032 SSVAL(pdata
,0,requestedpathlen
- 2); /* UCS2 of path consumed minus
1034 /* number of referral in this pkt */
1035 SSVAL(pdata
,2,junction
->referral_count
);
1037 SIVAL(pdata
,4,DFSREF_REFERRAL_SERVER
| DFSREF_STORAGE_SERVER
);
1039 SIVAL(pdata
,4,DFSREF_STORAGE_SERVER
);
1043 /* add the referral elements */
1044 for(i
=0;i
<junction
->referral_count
;i
++) {
1045 struct referral
* ref
= &junction
->referral_list
[i
];
1048 SSVAL(pdata
,offset
,2); /* version 2 */
1049 SSVAL(pdata
,offset
+2,VERSION2_REFERRAL_SIZE
);
1051 SSVAL(pdata
,offset
+4,1);
1053 SSVAL(pdata
,offset
+4,0);
1056 /* ref_flags :use path_consumed bytes? */
1057 SSVAL(pdata
,offset
+6,0);
1058 SIVAL(pdata
,offset
+8,ref
->proximity
);
1059 SIVAL(pdata
,offset
+12,ref
->ttl
);
1061 SSVAL(pdata
,offset
+16,uni_reqpathoffset1
-offset
);
1062 SSVAL(pdata
,offset
+18,uni_reqpathoffset2
-offset
);
1063 /* copy referred path into current offset */
1064 unilen
= rpcstr_push(pdata
+uni_curroffset
,
1065 ref
->alternate_path
,
1066 reply_size
- uni_curroffset
,
1069 SSVAL(pdata
,offset
+20,uni_curroffset
-offset
);
1071 uni_curroffset
+= unilen
;
1072 offset
+= VERSION2_REFERRAL_SIZE
;
1074 /* add in the unexplained 22 (0x16) bytes at the end */
1075 memset(pdata
+uni_curroffset
,'\0',0x16);
1079 static int setup_ver3_dfs_referral(const char *pathname
,
1081 struct junction_map
*junction
,
1084 char *pdata
= *ppdata
;
1086 smb_ucs2_t
*uni_reqpath
= NULL
;
1087 int uni_reqpathoffset1
, uni_reqpathoffset2
;
1094 DEBUG(10,("setting up version3 referral\n"));
1096 reqpathlen
= rpcstr_push_talloc(talloc_tos(), &uni_reqpath
, pathname
);
1097 if (uni_reqpath
== NULL
|| reqpathlen
== 0) {
1102 dump_data(0, (unsigned char *)uni_reqpath
,
1106 uni_reqpathoffset1
= REFERRAL_HEADER_SIZE
+
1107 VERSION3_REFERRAL_SIZE
* junction
->referral_count
;
1108 uni_reqpathoffset2
= uni_reqpathoffset1
+ reqpathlen
;
1109 reply_size
= uni_curroffset
= uni_reqpathoffset2
+ reqpathlen
;
1111 for(i
=0;i
<junction
->referral_count
;i
++) {
1112 DEBUG(10,("referral %u : %s\n",
1114 junction
->referral_list
[i
].alternate_path
));
1116 (strlen(junction
->referral_list
[i
].alternate_path
)+1)*2;
1119 pdata
= (char *)SMB_REALLOC(pdata
,reply_size
);
1121 DEBUG(0,("version3 referral setup:"
1122 "malloc failed for Realloc!\n"));
1127 /* create the header */
1128 SSVAL(pdata
,0,reqpathlen
- 2); /* UCS2 of path consumed minus
1130 SSVAL(pdata
,2,junction
->referral_count
); /* number of referral */
1132 SIVAL(pdata
,4,DFSREF_REFERRAL_SERVER
| DFSREF_STORAGE_SERVER
);
1134 SIVAL(pdata
,4,DFSREF_STORAGE_SERVER
);
1137 /* copy in the reqpaths */
1138 memcpy(pdata
+uni_reqpathoffset1
,uni_reqpath
,reqpathlen
);
1139 memcpy(pdata
+uni_reqpathoffset2
,uni_reqpath
,reqpathlen
);
1142 for(i
=0;i
<junction
->referral_count
;i
++) {
1143 struct referral
* ref
= &(junction
->referral_list
[i
]);
1146 SSVAL(pdata
,offset
,3); /* version 3 */
1147 SSVAL(pdata
,offset
+2,VERSION3_REFERRAL_SIZE
);
1149 SSVAL(pdata
,offset
+4,1);
1151 SSVAL(pdata
,offset
+4,0);
1154 /* ref_flags :use path_consumed bytes? */
1155 SSVAL(pdata
,offset
+6,0);
1156 SIVAL(pdata
,offset
+8,ref
->ttl
);
1158 SSVAL(pdata
,offset
+12,uni_reqpathoffset1
-offset
);
1159 SSVAL(pdata
,offset
+14,uni_reqpathoffset2
-offset
);
1160 /* copy referred path into current offset */
1161 unilen
= rpcstr_push(pdata
+uni_curroffset
,ref
->alternate_path
,
1162 reply_size
- uni_curroffset
,
1163 STR_UNICODE
| STR_TERMINATE
);
1164 SSVAL(pdata
,offset
+16,uni_curroffset
-offset
);
1165 /* copy 0x10 bytes of 00's in the ServiceSite GUID */
1166 memset(pdata
+offset
+18,'\0',16);
1168 uni_curroffset
+= unilen
;
1169 offset
+= VERSION3_REFERRAL_SIZE
;
1174 /******************************************************************
1175 Set up the DFS referral for the dfs pathname. This call returns
1176 the amount of the path covered by this server, and where the
1177 client should be redirected to. This is the meat of the
1178 TRANS2_GET_DFS_REFERRAL call.
1179 ******************************************************************/
1181 int setup_dfs_referral(connection_struct
*orig_conn
,
1182 const char *dfs_path
,
1183 int max_referral_level
,
1184 char **ppdata
, NTSTATUS
*pstatus
)
1186 struct junction_map
*junction
= NULL
;
1187 int consumedcnt
= 0;
1188 bool self_referral
= False
;
1190 char *pathnamep
= NULL
;
1191 char *local_dfs_path
= NULL
;
1194 if (!(ctx
=talloc_init("setup_dfs_referral"))) {
1195 *pstatus
= NT_STATUS_NO_MEMORY
;
1199 /* get the junction entry */
1201 talloc_destroy(ctx
);
1202 *pstatus
= NT_STATUS_NOT_FOUND
;
1207 * Trim pathname sent by client so it begins with only one backslash.
1208 * Two backslashes confuse some dfs clients
1211 local_dfs_path
= talloc_strdup(ctx
,dfs_path
);
1212 if (!local_dfs_path
) {
1213 *pstatus
= NT_STATUS_NO_MEMORY
;
1214 talloc_destroy(ctx
);
1217 pathnamep
= local_dfs_path
;
1218 while (IS_DIRECTORY_SEP(pathnamep
[0]) &&
1219 IS_DIRECTORY_SEP(pathnamep
[1])) {
1223 junction
= TALLOC_ZERO_P(ctx
, struct junction_map
);
1225 *pstatus
= NT_STATUS_NO_MEMORY
;
1226 talloc_destroy(ctx
);
1230 /* The following call can change cwd. */
1231 *pstatus
= get_referred_path(ctx
, pathnamep
, junction
,
1232 &consumedcnt
, &self_referral
);
1233 if (!NT_STATUS_IS_OK(*pstatus
)) {
1234 vfs_ChDir(orig_conn
,orig_conn
->connectpath
);
1235 talloc_destroy(ctx
);
1238 vfs_ChDir(orig_conn
,orig_conn
->connectpath
);
1240 if (!self_referral
) {
1241 pathnamep
[consumedcnt
] = '\0';
1243 if( DEBUGLVL( 3 ) ) {
1245 dbgtext("setup_dfs_referral: Path %s to "
1246 "alternate path(s):",
1248 for(i
=0;i
<junction
->referral_count
;i
++)
1250 junction
->referral_list
[i
].alternate_path
);
1255 /* create the referral depeding on version */
1256 DEBUG(10,("max_referral_level :%d\n",max_referral_level
));
1258 if (max_referral_level
< 2) {
1259 max_referral_level
= 2;
1261 if (max_referral_level
> 3) {
1262 max_referral_level
= 3;
1265 switch(max_referral_level
) {
1267 reply_size
= setup_ver2_dfs_referral(pathnamep
,
1272 reply_size
= setup_ver3_dfs_referral(pathnamep
, ppdata
,
1273 junction
, self_referral
);
1276 DEBUG(0,("setup_dfs_referral: Invalid dfs referral "
1278 max_referral_level
));
1279 talloc_destroy(ctx
);
1280 *pstatus
= NT_STATUS_INVALID_LEVEL
;
1285 DEBUGADD(0,("DFS Referral pdata:\n"));
1286 dump_data(0,(uint8
*)*ppdata
,reply_size
);
1289 talloc_destroy(ctx
);
1290 *pstatus
= NT_STATUS_OK
;
1294 /**********************************************************************
1295 The following functions are called by the NETDFS RPC pipe functions
1296 **********************************************************************/
1298 /*********************************************************************
1299 Creates a junction structure from a DFS pathname
1300 **********************************************************************/
1302 bool create_junction(TALLOC_CTX
*ctx
,
1303 const char *dfs_path
,
1304 struct junction_map
*jucn
)
1308 struct dfs_path
*pdp
= TALLOC_P(ctx
,struct dfs_path
);
1314 status
= parse_dfs_path(NULL
, dfs_path
, False
, pdp
, &dummy
);
1315 if (!NT_STATUS_IS_OK(status
)) {
1319 /* check if path is dfs : validate first token */
1320 if (!is_myname_or_ipaddr(pdp
->hostname
)) {
1321 DEBUG(4,("create_junction: Invalid hostname %s "
1323 pdp
->hostname
, dfs_path
));
1328 /* Check for a non-DFS share */
1329 snum
= lp_servicenumber(pdp
->servicename
);
1331 if(snum
< 0 || !lp_msdfs_root(snum
)) {
1332 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1338 jucn
->service_name
= talloc_strdup(ctx
, pdp
->servicename
);
1339 jucn
->volume_name
= talloc_strdup(ctx
, pdp
->reqpath
);
1340 jucn
->comment
= talloc_strdup(ctx
, lp_comment(snum
));
1343 if (!jucn
->service_name
|| !jucn
->volume_name
|| ! jucn
->comment
) {
1349 /**********************************************************************
1350 Forms a valid Unix pathname from the junction
1351 **********************************************************************/
1353 static bool junction_to_local_path(const struct junction_map
*jucn
,
1355 connection_struct
**conn_out
,
1361 snum
= lp_servicenumber(jucn
->service_name
);
1365 status
= create_conn_struct(talloc_tos(), conn_out
, snum
,
1366 lp_pathname(snum
), NULL
, oldpath
);
1367 if (!NT_STATUS_IS_OK(status
)) {
1371 *pp_path_out
= talloc_asprintf(*conn_out
,
1375 if (!*pp_path_out
) {
1376 vfs_ChDir(*conn_out
, *oldpath
);
1377 conn_free(*conn_out
);
1383 bool create_msdfs_link(const struct junction_map
*jucn
)
1387 char *msdfs_link
= NULL
;
1388 connection_struct
*conn
;
1390 bool insert_comma
= False
;
1393 if(!junction_to_local_path(jucn
, &path
, &conn
, &cwd
)) {
1397 /* Form the msdfs_link contents */
1398 msdfs_link
= talloc_strdup(conn
, "msdfs:");
1402 for(i
=0; i
<jucn
->referral_count
; i
++) {
1403 char *refpath
= jucn
->referral_list
[i
].alternate_path
;
1405 /* Alternate paths always use Windows separators. */
1406 trim_char(refpath
, '\\', '\\');
1407 if(*refpath
== '\0') {
1409 insert_comma
= False
;
1413 if (i
> 0 && insert_comma
) {
1414 msdfs_link
= talloc_asprintf_append_buffer(msdfs_link
,
1418 msdfs_link
= talloc_asprintf_append_buffer(msdfs_link
,
1426 if (!insert_comma
) {
1427 insert_comma
= True
;
1431 DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
1434 if(SMB_VFS_SYMLINK(conn
, msdfs_link
, path
) < 0) {
1435 if (errno
== EEXIST
) {
1436 struct smb_filename
*smb_fname
= NULL
;
1439 status
= create_synthetic_smb_fname(talloc_tos(), path
,
1442 if (!NT_STATUS_IS_OK(status
)) {
1443 errno
= map_errno_from_nt_status(status
);
1447 if(SMB_VFS_UNLINK(conn
, smb_fname
)!=0) {
1448 TALLOC_FREE(smb_fname
);
1451 TALLOC_FREE(smb_fname
);
1453 if (SMB_VFS_SYMLINK(conn
, msdfs_link
, path
) < 0) {
1454 DEBUG(1,("create_msdfs_link: symlink failed "
1455 "%s -> %s\nError: %s\n",
1456 path
, msdfs_link
, strerror(errno
)));
1464 vfs_ChDir(conn
, cwd
);
1469 bool remove_msdfs_link(const struct junction_map
*jucn
)
1473 connection_struct
*conn
;
1475 struct smb_filename
*smb_fname
= NULL
;
1478 if (!junction_to_local_path(jucn
, &path
, &conn
, &cwd
)) {
1482 status
= create_synthetic_smb_fname(talloc_tos(), path
,
1485 if (!NT_STATUS_IS_OK(status
)) {
1486 errno
= map_errno_from_nt_status(status
);
1490 if( SMB_VFS_UNLINK(conn
, smb_fname
) == 0 ) {
1494 TALLOC_FREE(smb_fname
);
1495 vfs_ChDir(conn
, cwd
);
1500 /*********************************************************************
1501 Return the number of DFS links at the root of this share.
1502 *********************************************************************/
1504 static int count_dfs_links(TALLOC_CTX
*ctx
, int snum
)
1507 SMB_STRUCT_DIR
*dirp
= NULL
;
1509 const char *connect_path
= lp_pathname(snum
);
1510 const char *msdfs_proxy
= lp_msdfs_proxy(snum
);
1511 connection_struct
*conn
;
1515 if(*connect_path
== '\0') {
1520 * Fake up a connection struct for the VFS layer.
1523 status
= create_conn_struct(talloc_tos(), &conn
, snum
, connect_path
,
1525 if (!NT_STATUS_IS_OK(status
)) {
1526 DEBUG(3, ("create_conn_struct failed: %s\n",
1527 nt_errstr(status
)));
1531 /* Count a link for the msdfs root - convention */
1534 /* No more links if this is an msdfs proxy. */
1535 if (*msdfs_proxy
!= '\0') {
1539 /* Now enumerate all dfs links */
1540 dirp
= SMB_VFS_OPENDIR(conn
, ".", NULL
, 0);
1545 while ((dname
= vfs_readdirname(conn
, dirp
, NULL
)) != NULL
) {
1546 if (is_msdfs_link(conn
,
1554 SMB_VFS_CLOSEDIR(conn
,dirp
);
1557 vfs_ChDir(conn
, cwd
);
1562 /*********************************************************************
1563 *********************************************************************/
1565 static int form_junctions(TALLOC_CTX
*ctx
,
1567 struct junction_map
*jucn
,
1571 SMB_STRUCT_DIR
*dirp
= NULL
;
1573 const char *connect_path
= lp_pathname(snum
);
1574 char *service_name
= lp_servicename(snum
);
1575 const char *msdfs_proxy
= lp_msdfs_proxy(snum
);
1576 connection_struct
*conn
;
1577 struct referral
*ref
= NULL
;
1581 if (jn_remain
== 0) {
1585 if(*connect_path
== '\0') {
1590 * Fake up a connection struct for the VFS layer.
1593 status
= create_conn_struct(ctx
, &conn
, snum
, connect_path
, NULL
,
1595 if (!NT_STATUS_IS_OK(status
)) {
1596 DEBUG(3, ("create_conn_struct failed: %s\n",
1597 nt_errstr(status
)));
1601 /* form a junction for the msdfs root - convention
1602 DO NOT REMOVE THIS: NT clients will not work with us
1603 if this is not present
1605 jucn
[cnt
].service_name
= talloc_strdup(ctx
,service_name
);
1606 jucn
[cnt
].volume_name
= talloc_strdup(ctx
, "");
1607 if (!jucn
[cnt
].service_name
|| !jucn
[cnt
].volume_name
) {
1610 jucn
[cnt
].comment
= "";
1611 jucn
[cnt
].referral_count
= 1;
1613 ref
= jucn
[cnt
].referral_list
= TALLOC_ZERO_P(ctx
, struct referral
);
1614 if (jucn
[cnt
].referral_list
== NULL
) {
1619 ref
->ttl
= REFERRAL_TTL
;
1620 if (*msdfs_proxy
!= '\0') {
1621 ref
->alternate_path
= talloc_strdup(ctx
,
1624 ref
->alternate_path
= talloc_asprintf(ctx
,
1626 get_local_machine_name(),
1630 if (!ref
->alternate_path
) {
1635 /* Don't enumerate if we're an msdfs proxy. */
1636 if (*msdfs_proxy
!= '\0') {
1640 /* Now enumerate all dfs links */
1641 dirp
= SMB_VFS_OPENDIR(conn
, ".", NULL
, 0);
1646 while ((dname
= vfs_readdirname(conn
, dirp
, NULL
)) != NULL
) {
1647 char *link_target
= NULL
;
1648 if (cnt
>= jn_remain
) {
1649 DEBUG(2, ("form_junctions: ran out of MSDFS "
1654 if (is_msdfs_link_internal(ctx
,
1656 dname
, &link_target
,
1658 if (parse_msdfs_symlink(ctx
,
1660 &jucn
[cnt
].referral_list
,
1661 &jucn
[cnt
].referral_count
)) {
1663 jucn
[cnt
].service_name
= talloc_strdup(ctx
,
1665 jucn
[cnt
].volume_name
= talloc_strdup(ctx
,
1667 if (!jucn
[cnt
].service_name
||
1668 !jucn
[cnt
].volume_name
) {
1672 jucn
[cnt
].comment
= "";
1675 TALLOC_FREE(link_target
);
1683 SMB_VFS_CLOSEDIR(conn
,dirp
);
1686 vfs_ChDir(conn
, cwd
);
1691 struct junction_map
*enum_msdfs_links(TALLOC_CTX
*ctx
, size_t *p_num_jn
)
1693 struct junction_map
*jn
= NULL
;
1695 size_t jn_count
= 0;
1699 if(!lp_host_msdfs()) {
1703 /* Ensure all the usershares are loaded. */
1705 load_registry_shares();
1706 sharecount
= load_usershare_shares();
1709 for(i
=0;i
< sharecount
;i
++) {
1710 if(lp_msdfs_root(i
)) {
1711 jn_count
+= count_dfs_links(ctx
, i
);
1714 if (jn_count
== 0) {
1717 jn
= TALLOC_ARRAY(ctx
, struct junction_map
, jn_count
);
1721 for(i
=0; i
< sharecount
; i
++) {
1722 if (*p_num_jn
>= jn_count
) {
1725 if(lp_msdfs_root(i
)) {
1726 *p_num_jn
+= form_junctions(ctx
, i
,
1728 jn_count
- *p_num_jn
);
1734 /******************************************************************************
1735 Core function to resolve a dfs pathname possibly containing a wildcard. If
1736 ppath_contains_wcard != NULL, it will be set to true if a wildcard is
1737 detected during dfs resolution.
1738 ******************************************************************************/
1740 NTSTATUS
resolve_dfspath_wcard(TALLOC_CTX
*ctx
,
1741 connection_struct
*conn
,
1743 const char *name_in
,
1745 bool *ppath_contains_wcard
)
1747 bool path_contains_wcard
;
1748 NTSTATUS status
= NT_STATUS_OK
;
1750 if (dfs_pathnames
) {
1751 status
= dfs_redirect(ctx
,
1756 &path_contains_wcard
);
1758 if (NT_STATUS_IS_OK(status
) && ppath_contains_wcard
!= NULL
) {
1759 *ppath_contains_wcard
= path_contains_wcard
;
1763 * Cheat and just return a copy of the in ptr.
1764 * Once srvstr_get_path() uses talloc it'll
1765 * be a talloced ptr anyway.
1767 *pp_name_out
= CONST_DISCARD(char *,name_in
);