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
->using_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 const 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 conn
->sconn
= smbd_server_conn
;
259 conn
->sconn
->num_tcons_open
++;
261 if (server_info
!= NULL
) {
262 conn
->server_info
= copy_serverinfo(conn
, server_info
);
263 if (conn
->server_info
== NULL
) {
264 DEBUG(0, ("copy_serverinfo failed\n"));
266 return NT_STATUS_NO_MEMORY
;
270 set_conn_connectpath(conn
, connpath
);
272 if (!smbd_vfs_init(conn
)) {
273 NTSTATUS status
= map_nt_error_from_unix(errno
);
274 DEBUG(0,("create_conn_struct: smbd_vfs_init failed.\n"));
279 conn
->fs_capabilities
= SMB_VFS_FS_CAPABILITIES(conn
, &conn
->ts_res
);
282 * Windows seems to insist on doing trans2getdfsreferral() calls on
283 * the IPC$ share as the anonymous user. If we try to chdir as that
284 * user we will fail.... WTF ? JRA.
287 oldcwd
= vfs_GetWd(ctx
, conn
);
288 if (oldcwd
== NULL
) {
289 NTSTATUS status
= map_nt_error_from_unix(errno
);
290 DEBUG(3, ("vfs_GetWd failed: %s\n", strerror(errno
)));
295 if (vfs_ChDir(conn
,conn
->connectpath
) != 0) {
296 NTSTATUS status
= map_nt_error_from_unix(errno
);
297 DEBUG(3,("create_conn_struct: Can't ChDir to new conn path %s. "
299 conn
->connectpath
, strerror(errno
) ));
310 /**********************************************************************
311 Parse the contents of a symlink to verify if it is an msdfs referral
312 A valid referral is of the form:
314 msdfs:server1\share1,server2\share2
315 msdfs:server1\share1\pathname,server2\share2\pathname
316 msdfs:server1/share1,server2/share2
317 msdfs:server1/share1/pathname,server2/share2/pathname.
319 Note that the alternate paths returned here must be of the canonicalized
323 \server\share\path\to\file,
325 even in posix path mode. This is because we have no knowledge if the
326 server we're referring to understands posix paths.
327 **********************************************************************/
329 static bool parse_msdfs_symlink(TALLOC_CTX
*ctx
,
331 struct referral
**preflist
,
336 char **alt_path
= NULL
;
338 struct referral
*reflist
;
341 temp
= talloc_strdup(ctx
, target
);
345 prot
= strtok_r(temp
, ":", &saveptr
);
347 DEBUG(0,("parse_msdfs_symlink: invalid path !\n"));
351 alt_path
= TALLOC_ARRAY(ctx
, char *, MAX_REFERRAL_COUNT
);
356 /* parse out the alternate paths */
357 while((count
<MAX_REFERRAL_COUNT
) &&
358 ((alt_path
[count
] = strtok_r(NULL
, ",", &saveptr
)) != NULL
)) {
362 DEBUG(10,("parse_msdfs_symlink: count=%d\n", count
));
365 reflist
= *preflist
= TALLOC_ZERO_ARRAY(ctx
,
366 struct referral
, count
);
367 if(reflist
== NULL
) {
368 TALLOC_FREE(alt_path
);
372 reflist
= *preflist
= NULL
;
375 for(i
=0;i
<count
;i
++) {
378 /* Canonicalize link target.
379 * Replace all /'s in the path by a \ */
380 string_replace(alt_path
[i
], '/', '\\');
382 /* Remove leading '\\'s */
384 while (*p
&& (*p
== '\\')) {
388 reflist
[i
].alternate_path
= talloc_asprintf(ctx
,
391 if (!reflist
[i
].alternate_path
) {
395 reflist
[i
].proximity
= 0;
396 reflist
[i
].ttl
= REFERRAL_TTL
;
397 DEBUG(10, ("parse_msdfs_symlink: Created alt path: %s\n",
398 reflist
[i
].alternate_path
));
403 TALLOC_FREE(alt_path
);
407 /**********************************************************************
408 Returns true if the unix path is a valid msdfs symlink and also
409 returns the target string from inside the link.
410 **********************************************************************/
412 static bool is_msdfs_link_internal(TALLOC_CTX
*ctx
,
413 connection_struct
*conn
,
415 char **pp_link_target
,
416 SMB_STRUCT_STAT
*sbufp
)
418 int referral_len
= 0;
419 #if defined(HAVE_BROKEN_READLINK)
420 char link_target_buf
[PATH_MAX
];
422 char link_target_buf
[7];
425 char *link_target
= NULL
;
426 struct smb_filename smb_fname
;
428 if (pp_link_target
) {
430 link_target
= TALLOC_ARRAY(ctx
, char, bufsize
);
434 *pp_link_target
= link_target
;
436 bufsize
= sizeof(link_target_buf
);
437 link_target
= link_target_buf
;
440 ZERO_STRUCT(smb_fname
);
441 smb_fname
.base_name
= discard_const_p(char, path
);
443 if (SMB_VFS_LSTAT(conn
, &smb_fname
) != 0) {
444 DEBUG(5,("is_msdfs_link_read_target: %s does not exist.\n",
448 if (!S_ISLNK(smb_fname
.st
.st_ex_mode
)) {
449 DEBUG(5,("is_msdfs_link_read_target: %s is not a link.\n",
454 *sbufp
= smb_fname
.st
;
457 referral_len
= SMB_VFS_READLINK(conn
, path
, link_target
, bufsize
- 1);
458 if (referral_len
== -1) {
459 DEBUG(0,("is_msdfs_link_read_target: Error reading "
460 "msdfs link %s: %s\n",
461 path
, strerror(errno
)));
464 link_target
[referral_len
] = '\0';
466 DEBUG(5,("is_msdfs_link_internal: %s -> %s\n",path
,
469 if (!strnequal(link_target
, "msdfs:", 6)) {
476 if (link_target
!= link_target_buf
) {
477 TALLOC_FREE(link_target
);
482 /**********************************************************************
483 Returns true if the unix path is a valid msdfs symlink.
484 **********************************************************************/
486 bool is_msdfs_link(connection_struct
*conn
,
488 SMB_STRUCT_STAT
*sbufp
)
490 return is_msdfs_link_internal(talloc_tos(),
497 /*****************************************************************
498 Used by other functions to decide if a dfs path is remote,
499 and to get the list of referred locations for that remote path.
501 search_flag: For findfirsts, dfs links themselves are not
502 redirected, but paths beyond the links are. For normal smb calls,
503 even dfs links need to be redirected.
505 consumedcntp: how much of the dfs path is being redirected. the client
506 should try the remaining path on the redirected server.
508 If this returns NT_STATUS_PATH_NOT_COVERED the contents of the msdfs
509 link redirect are in targetpath.
510 *****************************************************************/
512 static NTSTATUS
dfs_path_lookup(TALLOC_CTX
*ctx
,
513 connection_struct
*conn
,
514 const char *dfspath
, /* Incoming complete dfs path */
515 const struct dfs_path
*pdp
, /* Parsed out
516 server+share+extrapath. */
517 bool search_flag
, /* Called from a findfirst ? */
519 char **pp_targetpath
)
524 struct smb_filename
*smb_fname
= NULL
;
525 char *canon_dfspath
= NULL
; /* Canonicalized dfs path. (only '/'
528 DEBUG(10,("dfs_path_lookup: Conn path = %s reqpath = %s\n",
529 conn
->connectpath
, pdp
->reqpath
));
532 * Note the unix path conversion here we're doing we can
533 * throw away. We're looking for a symlink for a dfs
534 * resolution, if we don't find it we'll do another
535 * unix_convert later in the codepath.
536 * If we needed to remember what we'd resolved in
537 * dp->reqpath (as the original code did) we'd
538 * copy (localhost, dp->reqpath) on any code
539 * path below that returns True - but I don't
540 * think this is needed. JRA.
543 status
= unix_convert(ctx
, conn
, pdp
->reqpath
, &smb_fname
,
544 search_flag
? UCF_ALWAYS_ALLOW_WCARD_LCOMP
: 0);
546 if (!NT_STATUS_IS_OK(status
)) {
547 if (!NT_STATUS_EQUAL(status
,
548 NT_STATUS_OBJECT_PATH_NOT_FOUND
)) {
552 /* Create an smb_fname to use below. */
553 status
= create_synthetic_smb_fname(ctx
, pdp
->reqpath
, NULL
,
555 if (!NT_STATUS_IS_OK(status
)) {
560 /* Optimization - check if we can redirect the whole path. */
562 if (is_msdfs_link_internal(ctx
, conn
, smb_fname
->base_name
,
563 pp_targetpath
, NULL
)) {
565 DEBUG(6,("dfs_path_lookup (FindFirst) No redirection "
566 "for dfs link %s.\n", dfspath
));
567 status
= NT_STATUS_OK
;
571 DEBUG(6,("dfs_path_lookup: %s resolves to a "
572 "valid dfs link %s.\n", dfspath
,
573 pp_targetpath
? *pp_targetpath
: ""));
576 *consumedcntp
= strlen(dfspath
);
578 status
= NT_STATUS_PATH_NOT_COVERED
;
582 /* Prepare to test only for '/' components in the given path,
583 * so if a Windows path replace all '\\' characters with '/'.
584 * For a POSIX DFS path we know all separators are already '/'. */
586 canon_dfspath
= talloc_strdup(ctx
, dfspath
);
587 if (!canon_dfspath
) {
588 status
= NT_STATUS_NO_MEMORY
;
591 if (!pdp
->posix_path
) {
592 string_replace(canon_dfspath
, '\\', '/');
596 * localpath comes out of unix_convert, so it has
597 * no trailing backslash. Make sure that canon_dfspath hasn't either.
598 * Fix for bug #4860 from Jan Martin <Jan.Martin@rwedea.com>.
601 trim_char(canon_dfspath
,0,'/');
604 * Redirect if any component in the path is a link.
605 * We do this by walking backwards through the
606 * local path, chopping off the last component
607 * in both the local path and the canonicalized
608 * DFS path. If we hit a DFS link then we're done.
611 p
= strrchr_m(smb_fname
->base_name
, '/');
613 q
= strrchr_m(canon_dfspath
, '/');
622 if (is_msdfs_link_internal(ctx
, conn
,
623 smb_fname
->base_name
, pp_targetpath
,
625 DEBUG(4, ("dfs_path_lookup: Redirecting %s because "
626 "parent %s is dfs link\n", dfspath
,
627 smb_fname_str_dbg(smb_fname
)));
630 *consumedcntp
= strlen(canon_dfspath
);
631 DEBUG(10, ("dfs_path_lookup: Path consumed: %s "
637 status
= NT_STATUS_PATH_NOT_COVERED
;
641 /* Step back on the filesystem. */
642 p
= strrchr_m(smb_fname
->base_name
, '/');
645 /* And in the canonicalized dfs path. */
646 q
= strrchr_m(canon_dfspath
, '/');
650 status
= NT_STATUS_OK
;
652 TALLOC_FREE(smb_fname
);
656 /*****************************************************************
657 Decides if a dfs pathname should be redirected or not.
658 If not, the pathname is converted to a tcon-relative local unix path
660 search_wcard_flag: this flag performs 2 functions both related
661 to searches. See resolve_dfs_path() and parse_dfs_path_XX()
664 This function can return NT_STATUS_OK, meaning use the returned path as-is
665 (mapped into a local path).
666 or NT_STATUS_NOT_COVERED meaning return a DFS redirect, or
667 any other NT_STATUS error which is a genuine error to be
668 returned to the client.
669 *****************************************************************/
671 static NTSTATUS
dfs_redirect(TALLOC_CTX
*ctx
,
672 connection_struct
*conn
,
674 bool search_wcard_flag
,
676 bool *ppath_contains_wcard
)
679 struct dfs_path
*pdp
= TALLOC_P(ctx
, struct dfs_path
);
682 return NT_STATUS_NO_MEMORY
;
685 status
= parse_dfs_path(conn
, path_in
, search_wcard_flag
, pdp
,
686 ppath_contains_wcard
);
687 if (!NT_STATUS_IS_OK(status
)) {
692 if (pdp
->reqpath
[0] == '\0') {
694 *pp_path_out
= talloc_strdup(ctx
, "");
696 return NT_STATUS_NO_MEMORY
;
698 DEBUG(5,("dfs_redirect: self-referral.\n"));
702 /* If dfs pathname for a non-dfs share, convert to tcon-relative
703 path and return OK */
705 if (!lp_msdfs_root(SNUM(conn
))) {
706 *pp_path_out
= talloc_strdup(ctx
, pdp
->reqpath
);
709 return NT_STATUS_NO_MEMORY
;
714 /* If it looked like a local path (zero hostname/servicename)
715 * just treat as a tcon-relative path. */
717 if (pdp
->hostname
[0] == '\0' && pdp
->servicename
[0] == '\0') {
718 *pp_path_out
= talloc_strdup(ctx
, pdp
->reqpath
);
721 return NT_STATUS_NO_MEMORY
;
726 if (!( strequal(pdp
->servicename
, lp_servicename(SNUM(conn
)))
727 || (strequal(pdp
->servicename
, HOMES_NAME
)
728 && strequal(lp_servicename(SNUM(conn
)),
729 conn
->server_info
->sanitized_username
) )) ) {
731 /* The given sharename doesn't match this connection. */
734 return NT_STATUS_OBJECT_PATH_NOT_FOUND
;
737 status
= dfs_path_lookup(ctx
, conn
, path_in
, pdp
,
738 search_wcard_flag
, NULL
, NULL
);
739 if (!NT_STATUS_IS_OK(status
)) {
740 if (NT_STATUS_EQUAL(status
, NT_STATUS_PATH_NOT_COVERED
)) {
741 DEBUG(3,("dfs_redirect: Redirecting %s\n", path_in
));
743 DEBUG(10,("dfs_redirect: dfs_path_lookup "
744 "failed for %s with %s\n",
745 path_in
, nt_errstr(status
) ));
750 DEBUG(3,("dfs_redirect: Not redirecting %s.\n", path_in
));
752 /* Form non-dfs tcon-relative path */
753 *pp_path_out
= talloc_strdup(ctx
, pdp
->reqpath
);
756 return NT_STATUS_NO_MEMORY
;
759 DEBUG(3,("dfs_redirect: Path %s converted to non-dfs path %s\n",
766 /**********************************************************************
767 Return a self referral.
768 **********************************************************************/
770 static NTSTATUS
self_ref(TALLOC_CTX
*ctx
,
771 const char *dfs_path
,
772 struct junction_map
*jucn
,
774 bool *self_referralp
)
776 struct referral
*ref
;
778 *self_referralp
= True
;
780 jucn
->referral_count
= 1;
781 if((ref
= TALLOC_ZERO_P(ctx
, struct referral
)) == NULL
) {
782 return NT_STATUS_NO_MEMORY
;
785 ref
->alternate_path
= talloc_strdup(ctx
, dfs_path
);
786 if (!ref
->alternate_path
) {
787 return NT_STATUS_NO_MEMORY
;
790 ref
->ttl
= REFERRAL_TTL
;
791 jucn
->referral_list
= ref
;
792 *consumedcntp
= strlen(dfs_path
);
796 /**********************************************************************
797 Gets valid referrals for a dfs path and fills up the
798 junction_map structure.
799 **********************************************************************/
801 NTSTATUS
get_referred_path(TALLOC_CTX
*ctx
,
802 const char *dfs_path
,
803 struct junction_map
*jucn
,
805 bool *self_referralp
)
807 struct connection_struct
*conn
;
808 char *targetpath
= NULL
;
810 NTSTATUS status
= NT_STATUS_NOT_FOUND
;
812 struct dfs_path
*pdp
= TALLOC_P(ctx
, struct dfs_path
);
816 return NT_STATUS_NO_MEMORY
;
819 *self_referralp
= False
;
821 status
= parse_dfs_path(NULL
, dfs_path
, False
, pdp
, &dummy
);
822 if (!NT_STATUS_IS_OK(status
)) {
826 jucn
->service_name
= talloc_strdup(ctx
, pdp
->servicename
);
827 jucn
->volume_name
= talloc_strdup(ctx
, pdp
->reqpath
);
828 if (!jucn
->service_name
|| !jucn
->volume_name
) {
830 return NT_STATUS_NO_MEMORY
;
833 /* Verify the share is a dfs root */
834 snum
= lp_servicenumber(jucn
->service_name
);
836 char *service_name
= NULL
;
837 if ((snum
= find_service(ctx
, jucn
->service_name
, &service_name
)) < 0) {
838 return NT_STATUS_NOT_FOUND
;
841 return NT_STATUS_NO_MEMORY
;
843 TALLOC_FREE(jucn
->service_name
);
844 jucn
->service_name
= talloc_strdup(ctx
, service_name
);
845 if (!jucn
->service_name
) {
847 return NT_STATUS_NO_MEMORY
;
851 if (!lp_msdfs_root(snum
) && (*lp_msdfs_proxy(snum
) == '\0')) {
852 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not "
854 pdp
->servicename
, dfs_path
));
856 return NT_STATUS_NOT_FOUND
;
860 * Self referrals are tested with a anonymous IPC connection and
861 * a GET_DFS_REFERRAL call to \\server\share. (which means
862 * dp.reqpath[0] points to an empty string). create_conn_struct cd's
863 * into the directory and will fail if it cannot (as the anonymous
864 * user). Cope with this.
867 if (pdp
->reqpath
[0] == '\0') {
869 struct referral
*ref
;
871 if (*lp_msdfs_proxy(snum
) == '\0') {
881 * It's an msdfs proxy share. Redirect to
882 * the configured target share.
885 jucn
->referral_count
= 1;
886 if ((ref
= TALLOC_ZERO_P(ctx
, struct referral
)) == NULL
) {
888 return NT_STATUS_NO_MEMORY
;
891 if (!(tmp
= talloc_strdup(ctx
, lp_msdfs_proxy(snum
)))) {
893 return NT_STATUS_NO_MEMORY
;
896 trim_string(tmp
, "\\", 0);
898 ref
->alternate_path
= talloc_asprintf(ctx
, "\\%s", tmp
);
901 if (!ref
->alternate_path
) {
903 return NT_STATUS_NO_MEMORY
;
906 if (pdp
->reqpath
[0] != '\0') {
907 ref
->alternate_path
= talloc_asprintf_append(
911 if (!ref
->alternate_path
) {
913 return NT_STATUS_NO_MEMORY
;
917 ref
->ttl
= REFERRAL_TTL
;
918 jucn
->referral_list
= ref
;
919 *consumedcntp
= strlen(dfs_path
);
924 status
= create_conn_struct(ctx
, &conn
, snum
, lp_pathname(snum
),
926 if (!NT_STATUS_IS_OK(status
)) {
931 /* If this is a DFS path dfs_lookup should return
932 * NT_STATUS_PATH_NOT_COVERED. */
934 status
= dfs_path_lookup(ctx
, conn
, dfs_path
, pdp
,
935 False
, consumedcntp
, &targetpath
);
937 if (!NT_STATUS_EQUAL(status
, NT_STATUS_PATH_NOT_COVERED
)) {
938 DEBUG(3,("get_referred_path: No valid referrals for path %s\n",
940 vfs_ChDir(conn
, oldpath
);
946 /* We know this is a valid dfs link. Parse the targetpath. */
947 if (!parse_msdfs_symlink(ctx
, targetpath
,
948 &jucn
->referral_list
,
949 &jucn
->referral_count
)) {
950 DEBUG(3,("get_referred_path: failed to parse symlink "
951 "target %s\n", targetpath
));
952 vfs_ChDir(conn
, oldpath
);
955 return NT_STATUS_NOT_FOUND
;
958 vfs_ChDir(conn
, oldpath
);
964 static int setup_ver2_dfs_referral(const char *pathname
,
966 struct junction_map
*junction
,
969 char* pdata
= *ppdata
;
971 smb_ucs2_t
*uni_requestedpath
= NULL
;
972 int uni_reqpathoffset1
,uni_reqpathoffset2
;
974 int requestedpathlen
=0;
979 DEBUG(10,("Setting up version2 referral\nRequested path:\n"));
981 requestedpathlen
= rpcstr_push_talloc(talloc_tos(),
982 &uni_requestedpath
, pathname
);
983 if (uni_requestedpath
== NULL
|| requestedpathlen
== 0) {
988 dump_data(0, (unsigned char *)uni_requestedpath
,
992 DEBUG(10,("ref count = %u\n",junction
->referral_count
));
994 uni_reqpathoffset1
= REFERRAL_HEADER_SIZE
+
995 VERSION2_REFERRAL_SIZE
* junction
->referral_count
;
997 uni_reqpathoffset2
= uni_reqpathoffset1
+ requestedpathlen
;
999 uni_curroffset
= uni_reqpathoffset2
+ requestedpathlen
;
1001 reply_size
= REFERRAL_HEADER_SIZE
+
1002 VERSION2_REFERRAL_SIZE
*junction
->referral_count
+
1003 2 * requestedpathlen
;
1004 DEBUG(10,("reply_size: %u\n",reply_size
));
1006 /* add up the unicode lengths of all the referral paths */
1007 for(i
=0;i
<junction
->referral_count
;i
++) {
1008 DEBUG(10,("referral %u : %s\n",
1010 junction
->referral_list
[i
].alternate_path
));
1012 (strlen(junction
->referral_list
[i
].alternate_path
)+1)*2;
1015 DEBUG(10,("reply_size = %u\n",reply_size
));
1016 /* add the unexplained 0x16 bytes */
1019 pdata
= (char *)SMB_REALLOC(pdata
,reply_size
);
1021 DEBUG(0,("Realloc failed!\n"));
1026 /* copy in the dfs requested paths.. required for offset calculations */
1027 memcpy(pdata
+uni_reqpathoffset1
,uni_requestedpath
,requestedpathlen
);
1028 memcpy(pdata
+uni_reqpathoffset2
,uni_requestedpath
,requestedpathlen
);
1030 /* create the header */
1031 SSVAL(pdata
,0,requestedpathlen
- 2); /* UCS2 of path consumed minus
1033 /* number of referral in this pkt */
1034 SSVAL(pdata
,2,junction
->referral_count
);
1036 SIVAL(pdata
,4,DFSREF_REFERRAL_SERVER
| DFSREF_STORAGE_SERVER
);
1038 SIVAL(pdata
,4,DFSREF_STORAGE_SERVER
);
1042 /* add the referral elements */
1043 for(i
=0;i
<junction
->referral_count
;i
++) {
1044 struct referral
* ref
= &junction
->referral_list
[i
];
1047 SSVAL(pdata
,offset
,2); /* version 2 */
1048 SSVAL(pdata
,offset
+2,VERSION2_REFERRAL_SIZE
);
1050 SSVAL(pdata
,offset
+4,1);
1052 SSVAL(pdata
,offset
+4,0);
1055 /* ref_flags :use path_consumed bytes? */
1056 SSVAL(pdata
,offset
+6,0);
1057 SIVAL(pdata
,offset
+8,ref
->proximity
);
1058 SIVAL(pdata
,offset
+12,ref
->ttl
);
1060 SSVAL(pdata
,offset
+16,uni_reqpathoffset1
-offset
);
1061 SSVAL(pdata
,offset
+18,uni_reqpathoffset2
-offset
);
1062 /* copy referred path into current offset */
1063 unilen
= rpcstr_push(pdata
+uni_curroffset
,
1064 ref
->alternate_path
,
1065 reply_size
- uni_curroffset
,
1068 SSVAL(pdata
,offset
+20,uni_curroffset
-offset
);
1070 uni_curroffset
+= unilen
;
1071 offset
+= VERSION2_REFERRAL_SIZE
;
1073 /* add in the unexplained 22 (0x16) bytes at the end */
1074 memset(pdata
+uni_curroffset
,'\0',0x16);
1078 static int setup_ver3_dfs_referral(const char *pathname
,
1080 struct junction_map
*junction
,
1083 char *pdata
= *ppdata
;
1085 smb_ucs2_t
*uni_reqpath
= NULL
;
1086 int uni_reqpathoffset1
, uni_reqpathoffset2
;
1093 DEBUG(10,("setting up version3 referral\n"));
1095 reqpathlen
= rpcstr_push_talloc(talloc_tos(), &uni_reqpath
, pathname
);
1096 if (uni_reqpath
== NULL
|| reqpathlen
== 0) {
1101 dump_data(0, (unsigned char *)uni_reqpath
,
1105 uni_reqpathoffset1
= REFERRAL_HEADER_SIZE
+
1106 VERSION3_REFERRAL_SIZE
* junction
->referral_count
;
1107 uni_reqpathoffset2
= uni_reqpathoffset1
+ reqpathlen
;
1108 reply_size
= uni_curroffset
= uni_reqpathoffset2
+ reqpathlen
;
1110 for(i
=0;i
<junction
->referral_count
;i
++) {
1111 DEBUG(10,("referral %u : %s\n",
1113 junction
->referral_list
[i
].alternate_path
));
1115 (strlen(junction
->referral_list
[i
].alternate_path
)+1)*2;
1118 pdata
= (char *)SMB_REALLOC(pdata
,reply_size
);
1120 DEBUG(0,("version3 referral setup:"
1121 "malloc failed for Realloc!\n"));
1126 /* create the header */
1127 SSVAL(pdata
,0,reqpathlen
- 2); /* UCS2 of path consumed minus
1129 SSVAL(pdata
,2,junction
->referral_count
); /* number of referral */
1131 SIVAL(pdata
,4,DFSREF_REFERRAL_SERVER
| DFSREF_STORAGE_SERVER
);
1133 SIVAL(pdata
,4,DFSREF_STORAGE_SERVER
);
1136 /* copy in the reqpaths */
1137 memcpy(pdata
+uni_reqpathoffset1
,uni_reqpath
,reqpathlen
);
1138 memcpy(pdata
+uni_reqpathoffset2
,uni_reqpath
,reqpathlen
);
1141 for(i
=0;i
<junction
->referral_count
;i
++) {
1142 struct referral
* ref
= &(junction
->referral_list
[i
]);
1145 SSVAL(pdata
,offset
,3); /* version 3 */
1146 SSVAL(pdata
,offset
+2,VERSION3_REFERRAL_SIZE
);
1148 SSVAL(pdata
,offset
+4,1);
1150 SSVAL(pdata
,offset
+4,0);
1153 /* ref_flags :use path_consumed bytes? */
1154 SSVAL(pdata
,offset
+6,0);
1155 SIVAL(pdata
,offset
+8,ref
->ttl
);
1157 SSVAL(pdata
,offset
+12,uni_reqpathoffset1
-offset
);
1158 SSVAL(pdata
,offset
+14,uni_reqpathoffset2
-offset
);
1159 /* copy referred path into current offset */
1160 unilen
= rpcstr_push(pdata
+uni_curroffset
,ref
->alternate_path
,
1161 reply_size
- uni_curroffset
,
1162 STR_UNICODE
| STR_TERMINATE
);
1163 SSVAL(pdata
,offset
+16,uni_curroffset
-offset
);
1164 /* copy 0x10 bytes of 00's in the ServiceSite GUID */
1165 memset(pdata
+offset
+18,'\0',16);
1167 uni_curroffset
+= unilen
;
1168 offset
+= VERSION3_REFERRAL_SIZE
;
1173 /******************************************************************
1174 Set up the DFS referral for the dfs pathname. This call returns
1175 the amount of the path covered by this server, and where the
1176 client should be redirected to. This is the meat of the
1177 TRANS2_GET_DFS_REFERRAL call.
1178 ******************************************************************/
1180 int setup_dfs_referral(connection_struct
*orig_conn
,
1181 const char *dfs_path
,
1182 int max_referral_level
,
1183 char **ppdata
, NTSTATUS
*pstatus
)
1185 struct junction_map
*junction
= NULL
;
1186 int consumedcnt
= 0;
1187 bool self_referral
= False
;
1189 char *pathnamep
= NULL
;
1190 char *local_dfs_path
= NULL
;
1193 if (!(ctx
=talloc_init("setup_dfs_referral"))) {
1194 *pstatus
= NT_STATUS_NO_MEMORY
;
1198 /* get the junction entry */
1200 talloc_destroy(ctx
);
1201 *pstatus
= NT_STATUS_NOT_FOUND
;
1206 * Trim pathname sent by client so it begins with only one backslash.
1207 * Two backslashes confuse some dfs clients
1210 local_dfs_path
= talloc_strdup(ctx
,dfs_path
);
1211 if (!local_dfs_path
) {
1212 *pstatus
= NT_STATUS_NO_MEMORY
;
1213 talloc_destroy(ctx
);
1216 pathnamep
= local_dfs_path
;
1217 while (IS_DIRECTORY_SEP(pathnamep
[0]) &&
1218 IS_DIRECTORY_SEP(pathnamep
[1])) {
1222 junction
= TALLOC_ZERO_P(ctx
, struct junction_map
);
1224 *pstatus
= NT_STATUS_NO_MEMORY
;
1225 talloc_destroy(ctx
);
1229 /* The following call can change cwd. */
1230 *pstatus
= get_referred_path(ctx
, pathnamep
, junction
,
1231 &consumedcnt
, &self_referral
);
1232 if (!NT_STATUS_IS_OK(*pstatus
)) {
1233 vfs_ChDir(orig_conn
,orig_conn
->connectpath
);
1234 talloc_destroy(ctx
);
1237 vfs_ChDir(orig_conn
,orig_conn
->connectpath
);
1239 if (!self_referral
) {
1240 pathnamep
[consumedcnt
] = '\0';
1242 if( DEBUGLVL( 3 ) ) {
1244 dbgtext("setup_dfs_referral: Path %s to "
1245 "alternate path(s):",
1247 for(i
=0;i
<junction
->referral_count
;i
++)
1249 junction
->referral_list
[i
].alternate_path
);
1254 /* create the referral depeding on version */
1255 DEBUG(10,("max_referral_level :%d\n",max_referral_level
));
1257 if (max_referral_level
< 2) {
1258 max_referral_level
= 2;
1260 if (max_referral_level
> 3) {
1261 max_referral_level
= 3;
1264 switch(max_referral_level
) {
1266 reply_size
= setup_ver2_dfs_referral(pathnamep
,
1271 reply_size
= setup_ver3_dfs_referral(pathnamep
, ppdata
,
1272 junction
, self_referral
);
1275 DEBUG(0,("setup_dfs_referral: Invalid dfs referral "
1277 max_referral_level
));
1278 talloc_destroy(ctx
);
1279 *pstatus
= NT_STATUS_INVALID_LEVEL
;
1284 DEBUGADD(0,("DFS Referral pdata:\n"));
1285 dump_data(0,(uint8
*)*ppdata
,reply_size
);
1288 talloc_destroy(ctx
);
1289 *pstatus
= NT_STATUS_OK
;
1293 /**********************************************************************
1294 The following functions are called by the NETDFS RPC pipe functions
1295 **********************************************************************/
1297 /*********************************************************************
1298 Creates a junction structure from a DFS pathname
1299 **********************************************************************/
1301 bool create_junction(TALLOC_CTX
*ctx
,
1302 const char *dfs_path
,
1303 struct junction_map
*jucn
)
1307 struct dfs_path
*pdp
= TALLOC_P(ctx
,struct dfs_path
);
1313 status
= parse_dfs_path(NULL
, dfs_path
, False
, pdp
, &dummy
);
1314 if (!NT_STATUS_IS_OK(status
)) {
1318 /* check if path is dfs : validate first token */
1319 if (!is_myname_or_ipaddr(pdp
->hostname
)) {
1320 DEBUG(4,("create_junction: Invalid hostname %s "
1322 pdp
->hostname
, dfs_path
));
1327 /* Check for a non-DFS share */
1328 snum
= lp_servicenumber(pdp
->servicename
);
1330 if(snum
< 0 || !lp_msdfs_root(snum
)) {
1331 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1337 jucn
->service_name
= talloc_strdup(ctx
, pdp
->servicename
);
1338 jucn
->volume_name
= talloc_strdup(ctx
, pdp
->reqpath
);
1339 jucn
->comment
= talloc_strdup(ctx
, lp_comment(snum
));
1342 if (!jucn
->service_name
|| !jucn
->volume_name
|| ! jucn
->comment
) {
1348 /**********************************************************************
1349 Forms a valid Unix pathname from the junction
1350 **********************************************************************/
1352 static bool junction_to_local_path(const struct junction_map
*jucn
,
1354 connection_struct
**conn_out
,
1360 snum
= lp_servicenumber(jucn
->service_name
);
1364 status
= create_conn_struct(talloc_tos(), conn_out
, snum
,
1365 lp_pathname(snum
), NULL
, oldpath
);
1366 if (!NT_STATUS_IS_OK(status
)) {
1370 *pp_path_out
= talloc_asprintf(*conn_out
,
1374 if (!*pp_path_out
) {
1375 vfs_ChDir(*conn_out
, *oldpath
);
1376 conn_free(*conn_out
);
1382 bool create_msdfs_link(const struct junction_map
*jucn
)
1386 char *msdfs_link
= NULL
;
1387 connection_struct
*conn
;
1389 bool insert_comma
= False
;
1392 if(!junction_to_local_path(jucn
, &path
, &conn
, &cwd
)) {
1396 /* Form the msdfs_link contents */
1397 msdfs_link
= talloc_strdup(conn
, "msdfs:");
1401 for(i
=0; i
<jucn
->referral_count
; i
++) {
1402 char *refpath
= jucn
->referral_list
[i
].alternate_path
;
1404 /* Alternate paths always use Windows separators. */
1405 trim_char(refpath
, '\\', '\\');
1406 if(*refpath
== '\0') {
1408 insert_comma
= False
;
1412 if (i
> 0 && insert_comma
) {
1413 msdfs_link
= talloc_asprintf_append_buffer(msdfs_link
,
1417 msdfs_link
= talloc_asprintf_append_buffer(msdfs_link
,
1425 if (!insert_comma
) {
1426 insert_comma
= True
;
1430 DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
1433 if(SMB_VFS_SYMLINK(conn
, msdfs_link
, path
) < 0) {
1434 if (errno
== EEXIST
) {
1435 struct smb_filename
*smb_fname
= NULL
;
1438 status
= create_synthetic_smb_fname(talloc_tos(), path
,
1441 if (!NT_STATUS_IS_OK(status
)) {
1442 errno
= map_errno_from_nt_status(status
);
1446 if(SMB_VFS_UNLINK(conn
, smb_fname
)!=0) {
1447 TALLOC_FREE(smb_fname
);
1450 TALLOC_FREE(smb_fname
);
1452 if (SMB_VFS_SYMLINK(conn
, msdfs_link
, path
) < 0) {
1453 DEBUG(1,("create_msdfs_link: symlink failed "
1454 "%s -> %s\nError: %s\n",
1455 path
, msdfs_link
, strerror(errno
)));
1463 vfs_ChDir(conn
, cwd
);
1468 bool remove_msdfs_link(const struct junction_map
*jucn
)
1472 connection_struct
*conn
;
1474 struct smb_filename
*smb_fname
= NULL
;
1477 if (!junction_to_local_path(jucn
, &path
, &conn
, &cwd
)) {
1481 status
= create_synthetic_smb_fname(talloc_tos(), path
,
1484 if (!NT_STATUS_IS_OK(status
)) {
1485 errno
= map_errno_from_nt_status(status
);
1489 if( SMB_VFS_UNLINK(conn
, smb_fname
) == 0 ) {
1493 TALLOC_FREE(smb_fname
);
1494 vfs_ChDir(conn
, cwd
);
1499 /*********************************************************************
1500 Return the number of DFS links at the root of this share.
1501 *********************************************************************/
1503 static int count_dfs_links(TALLOC_CTX
*ctx
, int snum
)
1506 SMB_STRUCT_DIR
*dirp
= NULL
;
1507 const char *dname
= NULL
;
1508 char *talloced
= 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
, &talloced
))
1547 if (is_msdfs_link(conn
,
1552 TALLOC_FREE(talloced
);
1555 SMB_VFS_CLOSEDIR(conn
,dirp
);
1558 vfs_ChDir(conn
, cwd
);
1563 /*********************************************************************
1564 *********************************************************************/
1566 static int form_junctions(TALLOC_CTX
*ctx
,
1568 struct junction_map
*jucn
,
1572 SMB_STRUCT_DIR
*dirp
= NULL
;
1573 const char *dname
= NULL
;
1574 char *talloced
= NULL
;
1575 const char *connect_path
= lp_pathname(snum
);
1576 char *service_name
= lp_servicename(snum
);
1577 const char *msdfs_proxy
= lp_msdfs_proxy(snum
);
1578 connection_struct
*conn
;
1579 struct referral
*ref
= NULL
;
1583 if (jn_remain
== 0) {
1587 if(*connect_path
== '\0') {
1592 * Fake up a connection struct for the VFS layer.
1595 status
= create_conn_struct(ctx
, &conn
, snum
, connect_path
, NULL
,
1597 if (!NT_STATUS_IS_OK(status
)) {
1598 DEBUG(3, ("create_conn_struct failed: %s\n",
1599 nt_errstr(status
)));
1603 /* form a junction for the msdfs root - convention
1604 DO NOT REMOVE THIS: NT clients will not work with us
1605 if this is not present
1607 jucn
[cnt
].service_name
= talloc_strdup(ctx
,service_name
);
1608 jucn
[cnt
].volume_name
= talloc_strdup(ctx
, "");
1609 if (!jucn
[cnt
].service_name
|| !jucn
[cnt
].volume_name
) {
1612 jucn
[cnt
].comment
= "";
1613 jucn
[cnt
].referral_count
= 1;
1615 ref
= jucn
[cnt
].referral_list
= TALLOC_ZERO_P(ctx
, struct referral
);
1616 if (jucn
[cnt
].referral_list
== NULL
) {
1621 ref
->ttl
= REFERRAL_TTL
;
1622 if (*msdfs_proxy
!= '\0') {
1623 ref
->alternate_path
= talloc_strdup(ctx
,
1626 ref
->alternate_path
= talloc_asprintf(ctx
,
1628 get_local_machine_name(),
1632 if (!ref
->alternate_path
) {
1637 /* Don't enumerate if we're an msdfs proxy. */
1638 if (*msdfs_proxy
!= '\0') {
1642 /* Now enumerate all dfs links */
1643 dirp
= SMB_VFS_OPENDIR(conn
, ".", NULL
, 0);
1648 while ((dname
= vfs_readdirname(conn
, dirp
, NULL
, &talloced
))
1650 char *link_target
= NULL
;
1651 if (cnt
>= jn_remain
) {
1652 DEBUG(2, ("form_junctions: ran out of MSDFS "
1654 TALLOC_FREE(talloced
);
1657 if (is_msdfs_link_internal(ctx
,
1659 dname
, &link_target
,
1661 if (parse_msdfs_symlink(ctx
,
1663 &jucn
[cnt
].referral_list
,
1664 &jucn
[cnt
].referral_count
)) {
1666 jucn
[cnt
].service_name
= talloc_strdup(ctx
,
1668 jucn
[cnt
].volume_name
= talloc_strdup(ctx
,
1670 if (!jucn
[cnt
].service_name
||
1671 !jucn
[cnt
].volume_name
) {
1672 TALLOC_FREE(talloced
);
1675 jucn
[cnt
].comment
= "";
1678 TALLOC_FREE(link_target
);
1680 TALLOC_FREE(talloced
);
1686 SMB_VFS_CLOSEDIR(conn
,dirp
);
1689 vfs_ChDir(conn
, cwd
);
1694 struct junction_map
*enum_msdfs_links(TALLOC_CTX
*ctx
, size_t *p_num_jn
)
1696 struct junction_map
*jn
= NULL
;
1698 size_t jn_count
= 0;
1702 if(!lp_host_msdfs()) {
1706 /* Ensure all the usershares are loaded. */
1708 load_registry_shares();
1709 sharecount
= load_usershare_shares();
1712 for(i
=0;i
< sharecount
;i
++) {
1713 if(lp_msdfs_root(i
)) {
1714 jn_count
+= count_dfs_links(ctx
, i
);
1717 if (jn_count
== 0) {
1720 jn
= TALLOC_ARRAY(ctx
, struct junction_map
, jn_count
);
1724 for(i
=0; i
< sharecount
; i
++) {
1725 if (*p_num_jn
>= jn_count
) {
1728 if(lp_msdfs_root(i
)) {
1729 *p_num_jn
+= form_junctions(ctx
, i
,
1731 jn_count
- *p_num_jn
);
1737 /******************************************************************************
1738 Core function to resolve a dfs pathname possibly containing a wildcard. If
1739 ppath_contains_wcard != NULL, it will be set to true if a wildcard is
1740 detected during dfs resolution.
1741 ******************************************************************************/
1743 NTSTATUS
resolve_dfspath_wcard(TALLOC_CTX
*ctx
,
1744 connection_struct
*conn
,
1746 const char *name_in
,
1749 bool *ppath_contains_wcard
)
1751 bool path_contains_wcard
;
1752 NTSTATUS status
= NT_STATUS_OK
;
1754 if (dfs_pathnames
) {
1755 status
= dfs_redirect(ctx
,
1760 &path_contains_wcard
);
1762 if (NT_STATUS_IS_OK(status
) && ppath_contains_wcard
!= NULL
) {
1763 *ppath_contains_wcard
= path_contains_wcard
;
1767 * Cheat and just return a copy of the in ptr.
1768 * Once srvstr_get_path() uses talloc it'll
1769 * be a talloced ptr anyway.
1771 *pp_name_out
= CONST_DISCARD(char *,name_in
);