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
)
53 struct smbd_server_connection
*sconn
= smbd_server_conn
;
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 (!sconn
->allow_smb2
&& (*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
,
222 struct auth_serversupplied_info
*server_info
,
225 connection_struct
*conn
;
229 conn
= TALLOC_ZERO_P(ctx
, connection_struct
);
231 return NT_STATUS_NO_MEMORY
;
234 connpath
= talloc_strdup(conn
, path
);
237 return NT_STATUS_NO_MEMORY
;
239 connpath
= talloc_string_sub(conn
,
242 lp_servicename(snum
));
245 return NT_STATUS_NO_MEMORY
;
248 /* needed for smbd_vfs_init() */
250 if (!(conn
->params
= TALLOC_ZERO_P(conn
, struct share_params
))) {
251 DEBUG(0, ("TALLOC failed\n"));
253 return NT_STATUS_NO_MEMORY
;
256 conn
->params
->service
= snum
;
258 if (server_info
!= NULL
) {
259 conn
->server_info
= copy_serverinfo(conn
, server_info
);
260 if (conn
->server_info
== NULL
) {
261 DEBUG(0, ("copy_serverinfo failed\n"));
263 return NT_STATUS_NO_MEMORY
;
267 set_conn_connectpath(conn
, connpath
);
269 if (!smbd_vfs_init(conn
)) {
270 NTSTATUS status
= map_nt_error_from_unix(errno
);
271 DEBUG(0,("create_conn_struct: smbd_vfs_init failed.\n"));
276 conn
->fs_capabilities
= SMB_VFS_FS_CAPABILITIES(conn
, &conn
->ts_res
);
279 * Windows seems to insist on doing trans2getdfsreferral() calls on
280 * the IPC$ share as the anonymous user. If we try to chdir as that
281 * user we will fail.... WTF ? JRA.
284 oldcwd
= vfs_GetWd(ctx
, conn
);
285 if (oldcwd
== NULL
) {
286 NTSTATUS status
= map_nt_error_from_unix(errno
);
287 DEBUG(3, ("vfs_GetWd failed: %s\n", strerror(errno
)));
292 if (vfs_ChDir(conn
,conn
->connectpath
) != 0) {
293 NTSTATUS status
= map_nt_error_from_unix(errno
);
294 DEBUG(3,("create_conn_struct: Can't ChDir to new conn path %s. "
296 conn
->connectpath
, strerror(errno
) ));
307 /**********************************************************************
308 Parse the contents of a symlink to verify if it is an msdfs referral
309 A valid referral is of the form:
311 msdfs:server1\share1,server2\share2
312 msdfs:server1\share1\pathname,server2\share2\pathname
313 msdfs:server1/share1,server2/share2
314 msdfs:server1/share1/pathname,server2/share2/pathname.
316 Note that the alternate paths returned here must be of the canonicalized
320 \server\share\path\to\file,
322 even in posix path mode. This is because we have no knowledge if the
323 server we're referring to understands posix paths.
324 **********************************************************************/
326 static bool parse_msdfs_symlink(TALLOC_CTX
*ctx
,
328 struct referral
**preflist
,
333 char **alt_path
= NULL
;
335 struct referral
*reflist
;
338 temp
= talloc_strdup(ctx
, target
);
342 prot
= strtok_r(temp
, ":", &saveptr
);
344 DEBUG(0,("parse_msdfs_symlink: invalid path !\n"));
348 alt_path
= TALLOC_ARRAY(ctx
, char *, MAX_REFERRAL_COUNT
);
353 /* parse out the alternate paths */
354 while((count
<MAX_REFERRAL_COUNT
) &&
355 ((alt_path
[count
] = strtok_r(NULL
, ",", &saveptr
)) != NULL
)) {
359 DEBUG(10,("parse_msdfs_symlink: count=%d\n", count
));
362 reflist
= *preflist
= TALLOC_ZERO_ARRAY(ctx
,
363 struct referral
, count
);
364 if(reflist
== NULL
) {
365 TALLOC_FREE(alt_path
);
369 reflist
= *preflist
= NULL
;
372 for(i
=0;i
<count
;i
++) {
375 /* Canonicalize link target.
376 * Replace all /'s in the path by a \ */
377 string_replace(alt_path
[i
], '/', '\\');
379 /* Remove leading '\\'s */
381 while (*p
&& (*p
== '\\')) {
385 reflist
[i
].alternate_path
= talloc_asprintf(ctx
,
388 if (!reflist
[i
].alternate_path
) {
392 reflist
[i
].proximity
= 0;
393 reflist
[i
].ttl
= REFERRAL_TTL
;
394 DEBUG(10, ("parse_msdfs_symlink: Created alt path: %s\n",
395 reflist
[i
].alternate_path
));
400 TALLOC_FREE(alt_path
);
404 /**********************************************************************
405 Returns true if the unix path is a valid msdfs symlink and also
406 returns the target string from inside the link.
407 **********************************************************************/
409 static bool is_msdfs_link_internal(TALLOC_CTX
*ctx
,
410 connection_struct
*conn
,
412 char **pp_link_target
,
413 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
;
423 struct smb_filename smb_fname
;
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 ZERO_STRUCT(smb_fname
);
438 smb_fname
.base_name
= discard_const_p(char, path
);
440 if (SMB_VFS_LSTAT(conn
, &smb_fname
) != 0) {
441 DEBUG(5,("is_msdfs_link_read_target: %s does not exist.\n",
445 if (!S_ISLNK(smb_fname
.st
.st_ex_mode
)) {
446 DEBUG(5,("is_msdfs_link_read_target: %s is not a link.\n",
451 *sbufp
= smb_fname
.st
;
454 referral_len
= SMB_VFS_READLINK(conn
, path
, link_target
, bufsize
- 1);
455 if (referral_len
== -1) {
456 DEBUG(0,("is_msdfs_link_read_target: Error reading "
457 "msdfs link %s: %s\n",
458 path
, strerror(errno
)));
461 link_target
[referral_len
] = '\0';
463 DEBUG(5,("is_msdfs_link_internal: %s -> %s\n",path
,
466 if (!strnequal(link_target
, "msdfs:", 6)) {
473 if (link_target
!= link_target_buf
) {
474 TALLOC_FREE(link_target
);
479 /**********************************************************************
480 Returns true if the unix path is a valid msdfs symlink.
481 **********************************************************************/
483 bool is_msdfs_link(connection_struct
*conn
,
485 SMB_STRUCT_STAT
*sbufp
)
487 return is_msdfs_link_internal(talloc_tos(),
494 /*****************************************************************
495 Used by other functions to decide if a dfs path is remote,
496 and to get the list of referred locations for that remote path.
498 search_flag: For findfirsts, dfs links themselves are not
499 redirected, but paths beyond the links are. For normal smb calls,
500 even dfs links need to be redirected.
502 consumedcntp: how much of the dfs path is being redirected. the client
503 should try the remaining path on the redirected server.
505 If this returns NT_STATUS_PATH_NOT_COVERED the contents of the msdfs
506 link redirect are in targetpath.
507 *****************************************************************/
509 static NTSTATUS
dfs_path_lookup(TALLOC_CTX
*ctx
,
510 connection_struct
*conn
,
511 const char *dfspath
, /* Incoming complete dfs path */
512 const struct dfs_path
*pdp
, /* Parsed out
513 server+share+extrapath. */
514 bool search_flag
, /* Called from a findfirst ? */
516 char **pp_targetpath
)
521 struct smb_filename
*smb_fname
= NULL
;
522 char *canon_dfspath
= NULL
; /* Canonicalized dfs path. (only '/'
525 DEBUG(10,("dfs_path_lookup: Conn path = %s reqpath = %s\n",
526 conn
->connectpath
, pdp
->reqpath
));
529 * Note the unix path conversion here we're doing we can
530 * throw away. We're looking for a symlink for a dfs
531 * resolution, if we don't find it we'll do another
532 * unix_convert later in the codepath.
533 * If we needed to remember what we'd resolved in
534 * dp->reqpath (as the original code did) we'd
535 * copy (localhost, dp->reqpath) on any code
536 * path below that returns True - but I don't
537 * think this is needed. JRA.
540 status
= unix_convert(ctx
, conn
, pdp
->reqpath
, &smb_fname
,
541 search_flag
? UCF_ALWAYS_ALLOW_WCARD_LCOMP
: 0);
543 if (!NT_STATUS_IS_OK(status
)) {
544 if (!NT_STATUS_EQUAL(status
,
545 NT_STATUS_OBJECT_PATH_NOT_FOUND
)) {
549 /* Create an smb_fname to use below. */
550 status
= create_synthetic_smb_fname(ctx
, pdp
->reqpath
, NULL
,
552 if (!NT_STATUS_IS_OK(status
)) {
557 /* Optimization - check if we can redirect the whole path. */
559 if (is_msdfs_link_internal(ctx
, conn
, smb_fname
->base_name
,
560 pp_targetpath
, NULL
)) {
562 DEBUG(6,("dfs_path_lookup (FindFirst) No redirection "
563 "for dfs link %s.\n", dfspath
));
564 status
= NT_STATUS_OK
;
568 DEBUG(6,("dfs_path_lookup: %s resolves to a "
569 "valid dfs link %s.\n", dfspath
,
570 pp_targetpath
? *pp_targetpath
: ""));
573 *consumedcntp
= strlen(dfspath
);
575 status
= NT_STATUS_PATH_NOT_COVERED
;
579 /* Prepare to test only for '/' components in the given path,
580 * so if a Windows path replace all '\\' characters with '/'.
581 * For a POSIX DFS path we know all separators are already '/'. */
583 canon_dfspath
= talloc_strdup(ctx
, dfspath
);
584 if (!canon_dfspath
) {
585 status
= NT_STATUS_NO_MEMORY
;
588 if (!pdp
->posix_path
) {
589 string_replace(canon_dfspath
, '\\', '/');
593 * localpath comes out of unix_convert, so it has
594 * no trailing backslash. Make sure that canon_dfspath hasn't either.
595 * Fix for bug #4860 from Jan Martin <Jan.Martin@rwedea.com>.
598 trim_char(canon_dfspath
,0,'/');
601 * Redirect if any component in the path is a link.
602 * We do this by walking backwards through the
603 * local path, chopping off the last component
604 * in both the local path and the canonicalized
605 * DFS path. If we hit a DFS link then we're done.
608 p
= strrchr_m(smb_fname
->base_name
, '/');
610 q
= strrchr_m(canon_dfspath
, '/');
619 if (is_msdfs_link_internal(ctx
, conn
,
620 smb_fname
->base_name
, pp_targetpath
,
622 DEBUG(4, ("dfs_path_lookup: Redirecting %s because "
623 "parent %s is dfs link\n", dfspath
,
624 smb_fname_str_dbg(smb_fname
)));
627 *consumedcntp
= strlen(canon_dfspath
);
628 DEBUG(10, ("dfs_path_lookup: Path consumed: %s "
634 status
= NT_STATUS_PATH_NOT_COVERED
;
638 /* Step back on the filesystem. */
639 p
= strrchr_m(smb_fname
->base_name
, '/');
642 /* And in the canonicalized dfs path. */
643 q
= strrchr_m(canon_dfspath
, '/');
647 status
= NT_STATUS_OK
;
649 TALLOC_FREE(smb_fname
);
653 /*****************************************************************
654 Decides if a dfs pathname should be redirected or not.
655 If not, the pathname is converted to a tcon-relative local unix path
657 search_wcard_flag: this flag performs 2 functions both related
658 to searches. See resolve_dfs_path() and parse_dfs_path_XX()
661 This function can return NT_STATUS_OK, meaning use the returned path as-is
662 (mapped into a local path).
663 or NT_STATUS_NOT_COVERED meaning return a DFS redirect, or
664 any other NT_STATUS error which is a genuine error to be
665 returned to the client.
666 *****************************************************************/
668 static NTSTATUS
dfs_redirect(TALLOC_CTX
*ctx
,
669 connection_struct
*conn
,
671 bool search_wcard_flag
,
673 bool *ppath_contains_wcard
)
676 struct dfs_path
*pdp
= TALLOC_P(ctx
, struct dfs_path
);
679 return NT_STATUS_NO_MEMORY
;
682 status
= parse_dfs_path(conn
, path_in
, search_wcard_flag
, pdp
,
683 ppath_contains_wcard
);
684 if (!NT_STATUS_IS_OK(status
)) {
689 if (pdp
->reqpath
[0] == '\0') {
691 *pp_path_out
= talloc_strdup(ctx
, "");
693 return NT_STATUS_NO_MEMORY
;
695 DEBUG(5,("dfs_redirect: self-referral.\n"));
699 /* If dfs pathname for a non-dfs share, convert to tcon-relative
700 path and return OK */
702 if (!lp_msdfs_root(SNUM(conn
))) {
703 *pp_path_out
= talloc_strdup(ctx
, pdp
->reqpath
);
706 return NT_STATUS_NO_MEMORY
;
711 /* If it looked like a local path (zero hostname/servicename)
712 * just treat as a tcon-relative path. */
714 if (pdp
->hostname
[0] == '\0' && pdp
->servicename
[0] == '\0') {
715 *pp_path_out
= talloc_strdup(ctx
, pdp
->reqpath
);
718 return NT_STATUS_NO_MEMORY
;
723 if (!( strequal(pdp
->servicename
, lp_servicename(SNUM(conn
)))
724 || (strequal(pdp
->servicename
, HOMES_NAME
)
725 && strequal(lp_servicename(SNUM(conn
)),
726 conn
->server_info
->sanitized_username
) )) ) {
728 /* The given sharename doesn't match this connection. */
731 return NT_STATUS_OBJECT_PATH_NOT_FOUND
;
734 status
= dfs_path_lookup(ctx
, conn
, path_in
, pdp
,
735 search_wcard_flag
, NULL
, NULL
);
736 if (!NT_STATUS_IS_OK(status
)) {
737 if (NT_STATUS_EQUAL(status
, NT_STATUS_PATH_NOT_COVERED
)) {
738 DEBUG(3,("dfs_redirect: Redirecting %s\n", path_in
));
740 DEBUG(10,("dfs_redirect: dfs_path_lookup "
741 "failed for %s with %s\n",
742 path_in
, nt_errstr(status
) ));
747 DEBUG(3,("dfs_redirect: Not redirecting %s.\n", path_in
));
749 /* Form non-dfs tcon-relative path */
750 *pp_path_out
= talloc_strdup(ctx
, pdp
->reqpath
);
753 return NT_STATUS_NO_MEMORY
;
756 DEBUG(3,("dfs_redirect: Path %s converted to non-dfs path %s\n",
763 /**********************************************************************
764 Return a self referral.
765 **********************************************************************/
767 static NTSTATUS
self_ref(TALLOC_CTX
*ctx
,
768 const char *dfs_path
,
769 struct junction_map
*jucn
,
771 bool *self_referralp
)
773 struct referral
*ref
;
775 *self_referralp
= True
;
777 jucn
->referral_count
= 1;
778 if((ref
= TALLOC_ZERO_P(ctx
, struct referral
)) == NULL
) {
779 return NT_STATUS_NO_MEMORY
;
782 ref
->alternate_path
= talloc_strdup(ctx
, dfs_path
);
783 if (!ref
->alternate_path
) {
784 return NT_STATUS_NO_MEMORY
;
787 ref
->ttl
= REFERRAL_TTL
;
788 jucn
->referral_list
= ref
;
789 *consumedcntp
= strlen(dfs_path
);
793 /**********************************************************************
794 Gets valid referrals for a dfs path and fills up the
795 junction_map structure.
796 **********************************************************************/
798 NTSTATUS
get_referred_path(TALLOC_CTX
*ctx
,
799 const char *dfs_path
,
800 struct junction_map
*jucn
,
802 bool *self_referralp
)
804 struct connection_struct
*conn
;
805 char *targetpath
= NULL
;
807 NTSTATUS status
= NT_STATUS_NOT_FOUND
;
809 struct dfs_path
*pdp
= TALLOC_P(ctx
, struct dfs_path
);
813 return NT_STATUS_NO_MEMORY
;
816 *self_referralp
= False
;
818 status
= parse_dfs_path(NULL
, dfs_path
, False
, pdp
, &dummy
);
819 if (!NT_STATUS_IS_OK(status
)) {
823 jucn
->service_name
= talloc_strdup(ctx
, pdp
->servicename
);
824 jucn
->volume_name
= talloc_strdup(ctx
, pdp
->reqpath
);
825 if (!jucn
->service_name
|| !jucn
->volume_name
) {
827 return NT_STATUS_NO_MEMORY
;
830 /* Verify the share is a dfs root */
831 snum
= lp_servicenumber(jucn
->service_name
);
833 fstring service_name
;
834 fstrcpy(service_name
, jucn
->service_name
);
835 if ((snum
= find_service(service_name
)) < 0) {
836 return NT_STATUS_NOT_FOUND
;
838 TALLOC_FREE(jucn
->service_name
);
839 jucn
->service_name
= talloc_strdup(ctx
, service_name
);
840 if (!jucn
->service_name
) {
842 return NT_STATUS_NO_MEMORY
;
846 if (!lp_msdfs_root(snum
) && (*lp_msdfs_proxy(snum
) == '\0')) {
847 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not "
849 pdp
->servicename
, dfs_path
));
851 return NT_STATUS_NOT_FOUND
;
855 * Self referrals are tested with a anonymous IPC connection and
856 * a GET_DFS_REFERRAL call to \\server\share. (which means
857 * dp.reqpath[0] points to an empty string). create_conn_struct cd's
858 * into the directory and will fail if it cannot (as the anonymous
859 * user). Cope with this.
862 if (pdp
->reqpath
[0] == '\0') {
864 struct referral
*ref
;
866 if (*lp_msdfs_proxy(snum
) == '\0') {
876 * It's an msdfs proxy share. Redirect to
877 * the configured target share.
880 jucn
->referral_count
= 1;
881 if ((ref
= TALLOC_ZERO_P(ctx
, struct referral
)) == NULL
) {
883 return NT_STATUS_NO_MEMORY
;
886 if (!(tmp
= talloc_strdup(ctx
, lp_msdfs_proxy(snum
)))) {
888 return NT_STATUS_NO_MEMORY
;
891 trim_string(tmp
, "\\", 0);
893 ref
->alternate_path
= talloc_asprintf(ctx
, "\\%s", tmp
);
896 if (!ref
->alternate_path
) {
898 return NT_STATUS_NO_MEMORY
;
901 if (pdp
->reqpath
[0] != '\0') {
902 ref
->alternate_path
= talloc_asprintf_append(
906 if (!ref
->alternate_path
) {
908 return NT_STATUS_NO_MEMORY
;
912 ref
->ttl
= REFERRAL_TTL
;
913 jucn
->referral_list
= ref
;
914 *consumedcntp
= strlen(dfs_path
);
919 status
= create_conn_struct(ctx
, &conn
, snum
, lp_pathname(snum
),
921 if (!NT_STATUS_IS_OK(status
)) {
926 /* If this is a DFS path dfs_lookup should return
927 * NT_STATUS_PATH_NOT_COVERED. */
929 status
= dfs_path_lookup(ctx
, conn
, dfs_path
, pdp
,
930 False
, consumedcntp
, &targetpath
);
932 if (!NT_STATUS_EQUAL(status
, NT_STATUS_PATH_NOT_COVERED
)) {
933 DEBUG(3,("get_referred_path: No valid referrals for path %s\n",
935 vfs_ChDir(conn
, oldpath
);
941 /* We know this is a valid dfs link. Parse the targetpath. */
942 if (!parse_msdfs_symlink(ctx
, targetpath
,
943 &jucn
->referral_list
,
944 &jucn
->referral_count
)) {
945 DEBUG(3,("get_referred_path: failed to parse symlink "
946 "target %s\n", targetpath
));
947 vfs_ChDir(conn
, oldpath
);
950 return NT_STATUS_NOT_FOUND
;
953 vfs_ChDir(conn
, oldpath
);
959 static int setup_ver2_dfs_referral(const char *pathname
,
961 struct junction_map
*junction
,
964 char* pdata
= *ppdata
;
966 smb_ucs2_t
*uni_requestedpath
= NULL
;
967 int uni_reqpathoffset1
,uni_reqpathoffset2
;
969 int requestedpathlen
=0;
974 DEBUG(10,("Setting up version2 referral\nRequested path:\n"));
976 requestedpathlen
= rpcstr_push_talloc(talloc_tos(),
977 &uni_requestedpath
, pathname
);
978 if (uni_requestedpath
== NULL
|| requestedpathlen
== 0) {
983 dump_data(0, (unsigned char *)uni_requestedpath
,
987 DEBUG(10,("ref count = %u\n",junction
->referral_count
));
989 uni_reqpathoffset1
= REFERRAL_HEADER_SIZE
+
990 VERSION2_REFERRAL_SIZE
* junction
->referral_count
;
992 uni_reqpathoffset2
= uni_reqpathoffset1
+ requestedpathlen
;
994 uni_curroffset
= uni_reqpathoffset2
+ requestedpathlen
;
996 reply_size
= REFERRAL_HEADER_SIZE
+
997 VERSION2_REFERRAL_SIZE
*junction
->referral_count
+
998 2 * requestedpathlen
;
999 DEBUG(10,("reply_size: %u\n",reply_size
));
1001 /* add up the unicode lengths of all the referral paths */
1002 for(i
=0;i
<junction
->referral_count
;i
++) {
1003 DEBUG(10,("referral %u : %s\n",
1005 junction
->referral_list
[i
].alternate_path
));
1007 (strlen(junction
->referral_list
[i
].alternate_path
)+1)*2;
1010 DEBUG(10,("reply_size = %u\n",reply_size
));
1011 /* add the unexplained 0x16 bytes */
1014 pdata
= (char *)SMB_REALLOC(pdata
,reply_size
);
1016 DEBUG(0,("Realloc failed!\n"));
1021 /* copy in the dfs requested paths.. required for offset calculations */
1022 memcpy(pdata
+uni_reqpathoffset1
,uni_requestedpath
,requestedpathlen
);
1023 memcpy(pdata
+uni_reqpathoffset2
,uni_requestedpath
,requestedpathlen
);
1025 /* create the header */
1026 SSVAL(pdata
,0,requestedpathlen
- 2); /* UCS2 of path consumed minus
1028 /* number of referral in this pkt */
1029 SSVAL(pdata
,2,junction
->referral_count
);
1031 SIVAL(pdata
,4,DFSREF_REFERRAL_SERVER
| DFSREF_STORAGE_SERVER
);
1033 SIVAL(pdata
,4,DFSREF_STORAGE_SERVER
);
1037 /* add the referral elements */
1038 for(i
=0;i
<junction
->referral_count
;i
++) {
1039 struct referral
* ref
= &junction
->referral_list
[i
];
1042 SSVAL(pdata
,offset
,2); /* version 2 */
1043 SSVAL(pdata
,offset
+2,VERSION2_REFERRAL_SIZE
);
1045 SSVAL(pdata
,offset
+4,1);
1047 SSVAL(pdata
,offset
+4,0);
1050 /* ref_flags :use path_consumed bytes? */
1051 SSVAL(pdata
,offset
+6,0);
1052 SIVAL(pdata
,offset
+8,ref
->proximity
);
1053 SIVAL(pdata
,offset
+12,ref
->ttl
);
1055 SSVAL(pdata
,offset
+16,uni_reqpathoffset1
-offset
);
1056 SSVAL(pdata
,offset
+18,uni_reqpathoffset2
-offset
);
1057 /* copy referred path into current offset */
1058 unilen
= rpcstr_push(pdata
+uni_curroffset
,
1059 ref
->alternate_path
,
1060 reply_size
- uni_curroffset
,
1063 SSVAL(pdata
,offset
+20,uni_curroffset
-offset
);
1065 uni_curroffset
+= unilen
;
1066 offset
+= VERSION2_REFERRAL_SIZE
;
1068 /* add in the unexplained 22 (0x16) bytes at the end */
1069 memset(pdata
+uni_curroffset
,'\0',0x16);
1073 static int setup_ver3_dfs_referral(const char *pathname
,
1075 struct junction_map
*junction
,
1078 char *pdata
= *ppdata
;
1080 smb_ucs2_t
*uni_reqpath
= NULL
;
1081 int uni_reqpathoffset1
, uni_reqpathoffset2
;
1088 DEBUG(10,("setting up version3 referral\n"));
1090 reqpathlen
= rpcstr_push_talloc(talloc_tos(), &uni_reqpath
, pathname
);
1091 if (uni_reqpath
== NULL
|| reqpathlen
== 0) {
1096 dump_data(0, (unsigned char *)uni_reqpath
,
1100 uni_reqpathoffset1
= REFERRAL_HEADER_SIZE
+
1101 VERSION3_REFERRAL_SIZE
* junction
->referral_count
;
1102 uni_reqpathoffset2
= uni_reqpathoffset1
+ reqpathlen
;
1103 reply_size
= uni_curroffset
= uni_reqpathoffset2
+ reqpathlen
;
1105 for(i
=0;i
<junction
->referral_count
;i
++) {
1106 DEBUG(10,("referral %u : %s\n",
1108 junction
->referral_list
[i
].alternate_path
));
1110 (strlen(junction
->referral_list
[i
].alternate_path
)+1)*2;
1113 pdata
= (char *)SMB_REALLOC(pdata
,reply_size
);
1115 DEBUG(0,("version3 referral setup:"
1116 "malloc failed for Realloc!\n"));
1121 /* create the header */
1122 SSVAL(pdata
,0,reqpathlen
- 2); /* UCS2 of path consumed minus
1124 SSVAL(pdata
,2,junction
->referral_count
); /* number of referral */
1126 SIVAL(pdata
,4,DFSREF_REFERRAL_SERVER
| DFSREF_STORAGE_SERVER
);
1128 SIVAL(pdata
,4,DFSREF_STORAGE_SERVER
);
1131 /* copy in the reqpaths */
1132 memcpy(pdata
+uni_reqpathoffset1
,uni_reqpath
,reqpathlen
);
1133 memcpy(pdata
+uni_reqpathoffset2
,uni_reqpath
,reqpathlen
);
1136 for(i
=0;i
<junction
->referral_count
;i
++) {
1137 struct referral
* ref
= &(junction
->referral_list
[i
]);
1140 SSVAL(pdata
,offset
,3); /* version 3 */
1141 SSVAL(pdata
,offset
+2,VERSION3_REFERRAL_SIZE
);
1143 SSVAL(pdata
,offset
+4,1);
1145 SSVAL(pdata
,offset
+4,0);
1148 /* ref_flags :use path_consumed bytes? */
1149 SSVAL(pdata
,offset
+6,0);
1150 SIVAL(pdata
,offset
+8,ref
->ttl
);
1152 SSVAL(pdata
,offset
+12,uni_reqpathoffset1
-offset
);
1153 SSVAL(pdata
,offset
+14,uni_reqpathoffset2
-offset
);
1154 /* copy referred path into current offset */
1155 unilen
= rpcstr_push(pdata
+uni_curroffset
,ref
->alternate_path
,
1156 reply_size
- uni_curroffset
,
1157 STR_UNICODE
| STR_TERMINATE
);
1158 SSVAL(pdata
,offset
+16,uni_curroffset
-offset
);
1159 /* copy 0x10 bytes of 00's in the ServiceSite GUID */
1160 memset(pdata
+offset
+18,'\0',16);
1162 uni_curroffset
+= unilen
;
1163 offset
+= VERSION3_REFERRAL_SIZE
;
1168 /******************************************************************
1169 Set up the DFS referral for the dfs pathname. This call returns
1170 the amount of the path covered by this server, and where the
1171 client should be redirected to. This is the meat of the
1172 TRANS2_GET_DFS_REFERRAL call.
1173 ******************************************************************/
1175 int setup_dfs_referral(connection_struct
*orig_conn
,
1176 const char *dfs_path
,
1177 int max_referral_level
,
1178 char **ppdata
, NTSTATUS
*pstatus
)
1180 struct junction_map
*junction
= NULL
;
1181 int consumedcnt
= 0;
1182 bool self_referral
= False
;
1184 char *pathnamep
= NULL
;
1185 char *local_dfs_path
= NULL
;
1188 if (!(ctx
=talloc_init("setup_dfs_referral"))) {
1189 *pstatus
= NT_STATUS_NO_MEMORY
;
1193 /* get the junction entry */
1195 talloc_destroy(ctx
);
1196 *pstatus
= NT_STATUS_NOT_FOUND
;
1201 * Trim pathname sent by client so it begins with only one backslash.
1202 * Two backslashes confuse some dfs clients
1205 local_dfs_path
= talloc_strdup(ctx
,dfs_path
);
1206 if (!local_dfs_path
) {
1207 *pstatus
= NT_STATUS_NO_MEMORY
;
1208 talloc_destroy(ctx
);
1211 pathnamep
= local_dfs_path
;
1212 while (IS_DIRECTORY_SEP(pathnamep
[0]) &&
1213 IS_DIRECTORY_SEP(pathnamep
[1])) {
1217 junction
= TALLOC_ZERO_P(ctx
, struct junction_map
);
1219 *pstatus
= NT_STATUS_NO_MEMORY
;
1220 talloc_destroy(ctx
);
1224 /* The following call can change cwd. */
1225 *pstatus
= get_referred_path(ctx
, pathnamep
, junction
,
1226 &consumedcnt
, &self_referral
);
1227 if (!NT_STATUS_IS_OK(*pstatus
)) {
1228 vfs_ChDir(orig_conn
,orig_conn
->connectpath
);
1229 talloc_destroy(ctx
);
1232 vfs_ChDir(orig_conn
,orig_conn
->connectpath
);
1234 if (!self_referral
) {
1235 pathnamep
[consumedcnt
] = '\0';
1237 if( DEBUGLVL( 3 ) ) {
1239 dbgtext("setup_dfs_referral: Path %s to "
1240 "alternate path(s):",
1242 for(i
=0;i
<junction
->referral_count
;i
++)
1244 junction
->referral_list
[i
].alternate_path
);
1249 /* create the referral depeding on version */
1250 DEBUG(10,("max_referral_level :%d\n",max_referral_level
));
1252 if (max_referral_level
< 2) {
1253 max_referral_level
= 2;
1255 if (max_referral_level
> 3) {
1256 max_referral_level
= 3;
1259 switch(max_referral_level
) {
1261 reply_size
= setup_ver2_dfs_referral(pathnamep
,
1266 reply_size
= setup_ver3_dfs_referral(pathnamep
, ppdata
,
1267 junction
, self_referral
);
1270 DEBUG(0,("setup_dfs_referral: Invalid dfs referral "
1272 max_referral_level
));
1273 talloc_destroy(ctx
);
1274 *pstatus
= NT_STATUS_INVALID_LEVEL
;
1279 DEBUGADD(0,("DFS Referral pdata:\n"));
1280 dump_data(0,(uint8
*)*ppdata
,reply_size
);
1283 talloc_destroy(ctx
);
1284 *pstatus
= NT_STATUS_OK
;
1288 /**********************************************************************
1289 The following functions are called by the NETDFS RPC pipe functions
1290 **********************************************************************/
1292 /*********************************************************************
1293 Creates a junction structure from a DFS pathname
1294 **********************************************************************/
1296 bool create_junction(TALLOC_CTX
*ctx
,
1297 const char *dfs_path
,
1298 struct junction_map
*jucn
)
1302 struct dfs_path
*pdp
= TALLOC_P(ctx
,struct dfs_path
);
1308 status
= parse_dfs_path(NULL
, dfs_path
, False
, pdp
, &dummy
);
1309 if (!NT_STATUS_IS_OK(status
)) {
1313 /* check if path is dfs : validate first token */
1314 if (!is_myname_or_ipaddr(pdp
->hostname
)) {
1315 DEBUG(4,("create_junction: Invalid hostname %s "
1317 pdp
->hostname
, dfs_path
));
1322 /* Check for a non-DFS share */
1323 snum
= lp_servicenumber(pdp
->servicename
);
1325 if(snum
< 0 || !lp_msdfs_root(snum
)) {
1326 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1332 jucn
->service_name
= talloc_strdup(ctx
, pdp
->servicename
);
1333 jucn
->volume_name
= talloc_strdup(ctx
, pdp
->reqpath
);
1334 jucn
->comment
= talloc_strdup(ctx
, lp_comment(snum
));
1337 if (!jucn
->service_name
|| !jucn
->volume_name
|| ! jucn
->comment
) {
1343 /**********************************************************************
1344 Forms a valid Unix pathname from the junction
1345 **********************************************************************/
1347 static bool junction_to_local_path(const struct junction_map
*jucn
,
1349 connection_struct
**conn_out
,
1355 snum
= lp_servicenumber(jucn
->service_name
);
1359 status
= create_conn_struct(talloc_tos(), conn_out
, snum
,
1360 lp_pathname(snum
), NULL
, oldpath
);
1361 if (!NT_STATUS_IS_OK(status
)) {
1365 *pp_path_out
= talloc_asprintf(*conn_out
,
1369 if (!*pp_path_out
) {
1370 vfs_ChDir(*conn_out
, *oldpath
);
1371 conn_free(*conn_out
);
1377 bool create_msdfs_link(const struct junction_map
*jucn
)
1381 char *msdfs_link
= NULL
;
1382 connection_struct
*conn
;
1384 bool insert_comma
= False
;
1387 if(!junction_to_local_path(jucn
, &path
, &conn
, &cwd
)) {
1391 /* Form the msdfs_link contents */
1392 msdfs_link
= talloc_strdup(conn
, "msdfs:");
1396 for(i
=0; i
<jucn
->referral_count
; i
++) {
1397 char *refpath
= jucn
->referral_list
[i
].alternate_path
;
1399 /* Alternate paths always use Windows separators. */
1400 trim_char(refpath
, '\\', '\\');
1401 if(*refpath
== '\0') {
1403 insert_comma
= False
;
1407 if (i
> 0 && insert_comma
) {
1408 msdfs_link
= talloc_asprintf_append_buffer(msdfs_link
,
1412 msdfs_link
= talloc_asprintf_append_buffer(msdfs_link
,
1420 if (!insert_comma
) {
1421 insert_comma
= True
;
1425 DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
1428 if(SMB_VFS_SYMLINK(conn
, msdfs_link
, path
) < 0) {
1429 if (errno
== EEXIST
) {
1430 struct smb_filename
*smb_fname
= NULL
;
1433 status
= create_synthetic_smb_fname(talloc_tos(), path
,
1436 if (!NT_STATUS_IS_OK(status
)) {
1437 errno
= map_errno_from_nt_status(status
);
1441 if(SMB_VFS_UNLINK(conn
, smb_fname
)!=0) {
1442 TALLOC_FREE(smb_fname
);
1445 TALLOC_FREE(smb_fname
);
1447 if (SMB_VFS_SYMLINK(conn
, msdfs_link
, path
) < 0) {
1448 DEBUG(1,("create_msdfs_link: symlink failed "
1449 "%s -> %s\nError: %s\n",
1450 path
, msdfs_link
, strerror(errno
)));
1458 vfs_ChDir(conn
, cwd
);
1463 bool remove_msdfs_link(const struct junction_map
*jucn
)
1467 connection_struct
*conn
;
1469 struct smb_filename
*smb_fname
= NULL
;
1472 if (!junction_to_local_path(jucn
, &path
, &conn
, &cwd
)) {
1476 status
= create_synthetic_smb_fname(talloc_tos(), path
,
1479 if (!NT_STATUS_IS_OK(status
)) {
1480 errno
= map_errno_from_nt_status(status
);
1484 if( SMB_VFS_UNLINK(conn
, smb_fname
) == 0 ) {
1488 TALLOC_FREE(smb_fname
);
1489 vfs_ChDir(conn
, cwd
);
1494 /*********************************************************************
1495 Return the number of DFS links at the root of this share.
1496 *********************************************************************/
1498 static int count_dfs_links(TALLOC_CTX
*ctx
, int snum
)
1501 SMB_STRUCT_DIR
*dirp
= NULL
;
1502 const char *dname
= NULL
;
1503 char *talloced
= NULL
;
1504 const char *connect_path
= lp_pathname(snum
);
1505 const char *msdfs_proxy
= lp_msdfs_proxy(snum
);
1506 connection_struct
*conn
;
1510 if(*connect_path
== '\0') {
1515 * Fake up a connection struct for the VFS layer.
1518 status
= create_conn_struct(talloc_tos(), &conn
, snum
, connect_path
,
1520 if (!NT_STATUS_IS_OK(status
)) {
1521 DEBUG(3, ("create_conn_struct failed: %s\n",
1522 nt_errstr(status
)));
1526 /* Count a link for the msdfs root - convention */
1529 /* No more links if this is an msdfs proxy. */
1530 if (*msdfs_proxy
!= '\0') {
1534 /* Now enumerate all dfs links */
1535 dirp
= SMB_VFS_OPENDIR(conn
, ".", NULL
, 0);
1540 while ((dname
= vfs_readdirname(conn
, dirp
, NULL
, &talloced
))
1542 if (is_msdfs_link(conn
,
1547 TALLOC_FREE(talloced
);
1550 SMB_VFS_CLOSEDIR(conn
,dirp
);
1553 vfs_ChDir(conn
, cwd
);
1558 /*********************************************************************
1559 *********************************************************************/
1561 static int form_junctions(TALLOC_CTX
*ctx
,
1563 struct junction_map
*jucn
,
1567 SMB_STRUCT_DIR
*dirp
= NULL
;
1568 const char *dname
= NULL
;
1569 char *talloced
= NULL
;
1570 const char *connect_path
= lp_pathname(snum
);
1571 char *service_name
= lp_servicename(snum
);
1572 const char *msdfs_proxy
= lp_msdfs_proxy(snum
);
1573 connection_struct
*conn
;
1574 struct referral
*ref
= NULL
;
1578 if (jn_remain
== 0) {
1582 if(*connect_path
== '\0') {
1587 * Fake up a connection struct for the VFS layer.
1590 status
= create_conn_struct(ctx
, &conn
, snum
, connect_path
, NULL
,
1592 if (!NT_STATUS_IS_OK(status
)) {
1593 DEBUG(3, ("create_conn_struct failed: %s\n",
1594 nt_errstr(status
)));
1598 /* form a junction for the msdfs root - convention
1599 DO NOT REMOVE THIS: NT clients will not work with us
1600 if this is not present
1602 jucn
[cnt
].service_name
= talloc_strdup(ctx
,service_name
);
1603 jucn
[cnt
].volume_name
= talloc_strdup(ctx
, "");
1604 if (!jucn
[cnt
].service_name
|| !jucn
[cnt
].volume_name
) {
1607 jucn
[cnt
].comment
= "";
1608 jucn
[cnt
].referral_count
= 1;
1610 ref
= jucn
[cnt
].referral_list
= TALLOC_ZERO_P(ctx
, struct referral
);
1611 if (jucn
[cnt
].referral_list
== NULL
) {
1616 ref
->ttl
= REFERRAL_TTL
;
1617 if (*msdfs_proxy
!= '\0') {
1618 ref
->alternate_path
= talloc_strdup(ctx
,
1621 ref
->alternate_path
= talloc_asprintf(ctx
,
1623 get_local_machine_name(),
1627 if (!ref
->alternate_path
) {
1632 /* Don't enumerate if we're an msdfs proxy. */
1633 if (*msdfs_proxy
!= '\0') {
1637 /* Now enumerate all dfs links */
1638 dirp
= SMB_VFS_OPENDIR(conn
, ".", NULL
, 0);
1643 while ((dname
= vfs_readdirname(conn
, dirp
, NULL
, &talloced
))
1645 char *link_target
= NULL
;
1646 if (cnt
>= jn_remain
) {
1647 DEBUG(2, ("form_junctions: ran out of MSDFS "
1649 TALLOC_FREE(talloced
);
1652 if (is_msdfs_link_internal(ctx
,
1654 dname
, &link_target
,
1656 if (parse_msdfs_symlink(ctx
,
1658 &jucn
[cnt
].referral_list
,
1659 &jucn
[cnt
].referral_count
)) {
1661 jucn
[cnt
].service_name
= talloc_strdup(ctx
,
1663 jucn
[cnt
].volume_name
= talloc_strdup(ctx
,
1665 if (!jucn
[cnt
].service_name
||
1666 !jucn
[cnt
].volume_name
) {
1667 TALLOC_FREE(talloced
);
1670 jucn
[cnt
].comment
= "";
1673 TALLOC_FREE(link_target
);
1675 TALLOC_FREE(talloced
);
1681 SMB_VFS_CLOSEDIR(conn
,dirp
);
1684 vfs_ChDir(conn
, cwd
);
1689 struct junction_map
*enum_msdfs_links(TALLOC_CTX
*ctx
, size_t *p_num_jn
)
1691 struct junction_map
*jn
= NULL
;
1693 size_t jn_count
= 0;
1697 if(!lp_host_msdfs()) {
1701 /* Ensure all the usershares are loaded. */
1703 load_registry_shares();
1704 sharecount
= load_usershare_shares();
1707 for(i
=0;i
< sharecount
;i
++) {
1708 if(lp_msdfs_root(i
)) {
1709 jn_count
+= count_dfs_links(ctx
, i
);
1712 if (jn_count
== 0) {
1715 jn
= TALLOC_ARRAY(ctx
, struct junction_map
, jn_count
);
1719 for(i
=0; i
< sharecount
; i
++) {
1720 if (*p_num_jn
>= jn_count
) {
1723 if(lp_msdfs_root(i
)) {
1724 *p_num_jn
+= form_junctions(ctx
, i
,
1726 jn_count
- *p_num_jn
);
1732 /******************************************************************************
1733 Core function to resolve a dfs pathname possibly containing a wildcard. If
1734 ppath_contains_wcard != NULL, it will be set to true if a wildcard is
1735 detected during dfs resolution.
1736 ******************************************************************************/
1738 NTSTATUS
resolve_dfspath_wcard(TALLOC_CTX
*ctx
,
1739 connection_struct
*conn
,
1741 const char *name_in
,
1744 bool *ppath_contains_wcard
)
1746 bool path_contains_wcard
;
1747 NTSTATUS status
= NT_STATUS_OK
;
1749 if (dfs_pathnames
) {
1750 status
= dfs_redirect(ctx
,
1755 &path_contains_wcard
);
1757 if (NT_STATUS_IS_OK(status
) && ppath_contains_wcard
!= NULL
) {
1758 *ppath_contains_wcard
= path_contains_wcard
;
1762 * Cheat and just return a copy of the in ptr.
1763 * Once srvstr_get_path() uses talloc it'll
1764 * be a talloced ptr anyway.
1766 *pp_name_out
= CONST_DISCARD(char *,name_in
);