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 "system/filesys.h"
26 #include "smbd/smbd.h"
27 #include "smbd/globals.h"
30 #include "lib/param/loadparm.h"
31 #include "libcli/security/security.h"
32 #include "librpc/gen_ndr/ndr_dfsblobs.h"
34 /**********************************************************************
35 Parse a DFS pathname of the form \hostname\service\reqpath
36 into the dfs_path structure.
37 If POSIX pathnames is true, the pathname may also be of the
38 form /hostname/service/reqpath.
39 We cope with either here.
41 Unfortunately, due to broken clients who might set the
42 SVAL(inbuf,smb_flg2) & FLAGS2_DFS_PATHNAMES bit and then
43 send a local path, we have to cope with that too....
45 If conn != NULL then ensure the provided service is
46 the one pointed to by the connection.
48 This version does everything using pointers within one copy of the
49 pathname string, talloced on the struct dfs_path pointer (which
50 must be talloced). This may be too clever to live....
52 **********************************************************************/
54 static NTSTATUS
parse_dfs_path(connection_struct
*conn
,
57 bool allow_broken_path
,
58 struct dfs_path
*pdp
, /* MUST BE TALLOCED */
59 bool *ppath_contains_wcard
)
65 NTSTATUS status
= NT_STATUS_OK
;
71 * This is the only talloc we should need to do
72 * on the struct dfs_path. All the pointers inside
73 * it should point to offsets within this string.
76 pathname_local
= talloc_strdup(pdp
, pathname
);
77 if (!pathname_local
) {
78 return NT_STATUS_NO_MEMORY
;
80 /* Get a pointer to the terminating '\0' */
81 eos_ptr
= &pathname_local
[strlen(pathname_local
)];
82 p
= temp
= pathname_local
;
84 pdp
->posix_path
= (lp_posix_pathnames() && *pathname
== '/');
86 sepchar
= pdp
->posix_path
? '/' : '\\';
88 if (allow_broken_path
&& (*pathname
!= sepchar
)) {
89 DEBUG(10,("parse_dfs_path: path %s doesn't start with %c\n",
92 * Possibly client sent a local path by mistake.
93 * Try and convert to a local path.
96 pdp
->hostname
= eos_ptr
; /* "" */
97 pdp
->servicename
= eos_ptr
; /* "" */
99 /* We've got no info about separators. */
100 pdp
->posix_path
= lp_posix_pathnames();
102 DEBUG(10,("parse_dfs_path: trying to convert %s to a "
109 * Safe to use on talloc'ed string as it only shrinks.
110 * It also doesn't affect the eos_ptr.
112 trim_char(temp
,sepchar
,sepchar
);
114 DEBUG(10,("parse_dfs_path: temp = |%s| after trimming %c's\n",
118 /* Parse out hostname. */
119 p
= strchr_m(temp
,sepchar
);
121 DEBUG(10,("parse_dfs_path: can't parse hostname from path %s\n",
124 * Possibly client sent a local path by mistake.
125 * Try and convert to a local path.
128 pdp
->hostname
= eos_ptr
; /* "" */
129 pdp
->servicename
= eos_ptr
; /* "" */
132 DEBUG(10,("parse_dfs_path: trying to convert %s "
138 pdp
->hostname
= temp
;
140 DEBUG(10,("parse_dfs_path: hostname: %s\n",pdp
->hostname
));
142 /* Parse out servicename. */
144 p
= strchr_m(servicename
,sepchar
);
149 /* Is this really our servicename ? */
150 if (conn
&& !( strequal(servicename
, lp_servicename(talloc_tos(), SNUM(conn
)))
151 || (strequal(servicename
, HOMES_NAME
)
152 && strequal(lp_servicename(talloc_tos(), SNUM(conn
)),
153 get_current_username()) )) ) {
154 DEBUG(10,("parse_dfs_path: %s is not our servicename\n",
158 * Possibly client sent a local path by mistake.
159 * Try and convert to a local path.
162 pdp
->hostname
= eos_ptr
; /* "" */
163 pdp
->servicename
= eos_ptr
; /* "" */
165 /* Repair the path - replace the sepchar's
168 *servicename
= sepchar
;
174 DEBUG(10,("parse_dfs_path: trying to convert %s "
180 pdp
->servicename
= servicename
;
182 DEBUG(10,("parse_dfs_path: servicename: %s\n",pdp
->servicename
));
185 /* Client sent self referral \server\share. */
186 pdp
->reqpath
= eos_ptr
; /* "" */
194 *ppath_contains_wcard
= False
;
198 /* Rest is reqpath. */
199 if (pdp
->posix_path
) {
200 status
= check_path_syntax_posix(pdp
->reqpath
);
203 status
= check_path_syntax_wcard(pdp
->reqpath
,
204 ppath_contains_wcard
);
206 status
= check_path_syntax(pdp
->reqpath
);
210 if (!NT_STATUS_IS_OK(status
)) {
211 DEBUG(10,("parse_dfs_path: '%s' failed with %s\n",
212 p
, nt_errstr(status
) ));
216 DEBUG(10,("parse_dfs_path: rest of the path: %s\n",pdp
->reqpath
));
220 /********************************************************
221 Fake up a connection struct for the VFS layer, for use in
222 applications (such as the python bindings), that do not want the
223 global working directory changed under them.
225 SMB_VFS_CONNECT requires root privileges.
226 *********************************************************/
228 static NTSTATUS
create_conn_struct_as_root(TALLOC_CTX
*ctx
,
229 struct tevent_context
*ev
,
230 struct messaging_context
*msg
,
231 connection_struct
**pconn
,
234 const struct auth_session_info
*session_info
)
236 connection_struct
*conn
;
238 const char *vfs_user
;
239 struct smbd_server_connection
*sconn
;
240 const char *servicename
= lp_const_servicename(snum
);
242 sconn
= talloc_zero(ctx
, struct smbd_server_connection
);
244 return NT_STATUS_NO_MEMORY
;
248 sconn
->msg_ctx
= msg
;
250 sconn
->smb1
.echo_handler
.trusted_fd
= -1;
251 sconn
->smb1
.echo_handler
.socket_lock_fd
= -1;
253 conn
= conn_new(sconn
);
256 return NT_STATUS_NO_MEMORY
;
259 /* Now we have conn, we need to make sconn a child of conn,
260 * for a proper talloc tree */
261 talloc_steal(conn
, sconn
);
263 if (snum
== -1 && servicename
== NULL
) {
264 servicename
= "Unknown Service (snum == -1)";
267 connpath
= talloc_strdup(conn
, path
);
270 return NT_STATUS_NO_MEMORY
;
272 connpath
= talloc_string_sub(conn
,
278 return NT_STATUS_NO_MEMORY
;
281 /* needed for smbd_vfs_init() */
283 conn
->params
->service
= snum
;
284 conn
->cnum
= TID_FIELD_INVALID
;
286 if (session_info
!= NULL
) {
287 conn
->session_info
= copy_session_info(conn
, session_info
);
288 if (conn
->session_info
== NULL
) {
289 DEBUG(0, ("copy_serverinfo failed\n"));
291 return NT_STATUS_NO_MEMORY
;
293 vfs_user
= conn
->session_info
->unix_info
->unix_name
;
295 /* use current authenticated user in absence of session_info */
296 vfs_user
= get_current_username();
299 set_conn_connectpath(conn
, connpath
);
302 * New code to check if there's a share security descripter
303 * added from NT server manager. This is done after the
304 * smb.conf checks are done as we need a uid and token. JRA.
307 if (conn
->session_info
) {
308 share_access_check(conn
->session_info
->security_token
,
310 MAXIMUM_ALLOWED_ACCESS
,
311 &conn
->share_access
);
313 if ((conn
->share_access
& FILE_WRITE_DATA
) == 0) {
314 if ((conn
->share_access
& FILE_READ_DATA
) == 0) {
315 /* No access, read or write. */
316 DEBUG(0,("create_conn_struct: connection to %s "
317 "denied due to security "
321 return NT_STATUS_ACCESS_DENIED
;
323 conn
->read_only
= true;
327 conn
->share_access
= 0;
328 conn
->read_only
= true;
331 if (!smbd_vfs_init(conn
)) {
332 NTSTATUS status
= map_nt_error_from_unix(errno
);
333 DEBUG(0,("create_conn_struct: smbd_vfs_init failed.\n"));
338 /* this must be the first filesystem operation that we do */
339 if (SMB_VFS_CONNECT(conn
, servicename
, vfs_user
) < 0) {
340 DEBUG(0,("VFS connect failed!\n"));
342 return NT_STATUS_UNSUCCESSFUL
;
345 conn
->fs_capabilities
= SMB_VFS_FS_CAPABILITIES(conn
, &conn
->ts_res
);
351 /********************************************************
352 Fake up a connection struct for the VFS layer, for use in
353 applications (such as the python bindings), that do not want the
354 global working directory changed under them.
356 SMB_VFS_CONNECT requires root privileges.
357 *********************************************************/
359 NTSTATUS
create_conn_struct(TALLOC_CTX
*ctx
,
360 struct tevent_context
*ev
,
361 struct messaging_context
*msg
,
362 connection_struct
**pconn
,
365 const struct auth_session_info
*session_info
)
369 status
= create_conn_struct_as_root(ctx
, ev
,
378 /********************************************************
379 Fake up a connection struct for the VFS layer.
380 Note: this performs a vfs connect and CHANGES CWD !!!! JRA.
382 The old working directory is returned on *poldcwd, allocated on ctx.
383 *********************************************************/
385 NTSTATUS
create_conn_struct_cwd(TALLOC_CTX
*ctx
,
386 struct tevent_context
*ev
,
387 struct messaging_context
*msg
,
388 connection_struct
**pconn
,
391 const struct auth_session_info
*session_info
,
394 connection_struct
*conn
;
397 NTSTATUS status
= create_conn_struct(ctx
, ev
,
401 if (!NT_STATUS_IS_OK(status
)) {
406 * Windows seems to insist on doing trans2getdfsreferral() calls on
407 * the IPC$ share as the anonymous user. If we try to chdir as that
408 * user we will fail.... WTF ? JRA.
411 oldcwd
= vfs_GetWd(ctx
, conn
);
412 if (oldcwd
== NULL
) {
413 status
= map_nt_error_from_unix(errno
);
414 DEBUG(3, ("vfs_GetWd failed: %s\n", strerror(errno
)));
419 if (vfs_ChDir(conn
,conn
->connectpath
) != 0) {
420 status
= map_nt_error_from_unix(errno
);
421 DEBUG(3,("create_conn_struct: Can't ChDir to new conn path %s. "
423 conn
->connectpath
, strerror(errno
) ));
434 /**********************************************************************
435 Parse the contents of a symlink to verify if it is an msdfs referral
436 A valid referral is of the form:
438 msdfs:server1\share1,server2\share2
439 msdfs:server1\share1\pathname,server2\share2\pathname
440 msdfs:server1/share1,server2/share2
441 msdfs:server1/share1/pathname,server2/share2/pathname.
443 Note that the alternate paths returned here must be of the canonicalized
447 \server\share\path\to\file,
449 even in posix path mode. This is because we have no knowledge if the
450 server we're referring to understands posix paths.
451 **********************************************************************/
453 static bool parse_msdfs_symlink(TALLOC_CTX
*ctx
,
455 struct referral
**preflist
,
460 char **alt_path
= NULL
;
462 struct referral
*reflist
;
465 temp
= talloc_strdup(ctx
, target
);
469 prot
= strtok_r(temp
, ":", &saveptr
);
471 DEBUG(0,("parse_msdfs_symlink: invalid path !\n"));
475 alt_path
= talloc_array(ctx
, char *, MAX_REFERRAL_COUNT
);
480 /* parse out the alternate paths */
481 while((count
<MAX_REFERRAL_COUNT
) &&
482 ((alt_path
[count
] = strtok_r(NULL
, ",", &saveptr
)) != NULL
)) {
486 DEBUG(10,("parse_msdfs_symlink: count=%d\n", count
));
489 reflist
= *preflist
= talloc_zero_array(ctx
,
490 struct referral
, count
);
491 if(reflist
== NULL
) {
492 TALLOC_FREE(alt_path
);
496 reflist
= *preflist
= NULL
;
499 for(i
=0;i
<count
;i
++) {
502 /* Canonicalize link target.
503 * Replace all /'s in the path by a \ */
504 string_replace(alt_path
[i
], '/', '\\');
506 /* Remove leading '\\'s */
508 while (*p
&& (*p
== '\\')) {
512 reflist
[i
].alternate_path
= talloc_asprintf(ctx
,
515 if (!reflist
[i
].alternate_path
) {
519 reflist
[i
].proximity
= 0;
520 reflist
[i
].ttl
= REFERRAL_TTL
;
521 DEBUG(10, ("parse_msdfs_symlink: Created alt path: %s\n",
522 reflist
[i
].alternate_path
));
527 TALLOC_FREE(alt_path
);
531 /**********************************************************************
532 Returns true if the unix path is a valid msdfs symlink and also
533 returns the target string from inside the link.
534 **********************************************************************/
536 static bool is_msdfs_link_internal(TALLOC_CTX
*ctx
,
537 connection_struct
*conn
,
539 char **pp_link_target
,
540 SMB_STRUCT_STAT
*sbufp
)
542 int referral_len
= 0;
543 #if defined(HAVE_BROKEN_READLINK)
544 char link_target_buf
[PATH_MAX
];
546 char link_target_buf
[7];
549 char *link_target
= NULL
;
550 struct smb_filename smb_fname
;
552 if (pp_link_target
) {
554 link_target
= talloc_array(ctx
, char, bufsize
);
558 *pp_link_target
= link_target
;
560 bufsize
= sizeof(link_target_buf
);
561 link_target
= link_target_buf
;
564 ZERO_STRUCT(smb_fname
);
565 smb_fname
.base_name
= discard_const_p(char, path
);
567 if (SMB_VFS_LSTAT(conn
, &smb_fname
) != 0) {
568 DEBUG(5,("is_msdfs_link_read_target: %s does not exist.\n",
572 if (!S_ISLNK(smb_fname
.st
.st_ex_mode
)) {
573 DEBUG(5,("is_msdfs_link_read_target: %s is not a link.\n",
578 *sbufp
= smb_fname
.st
;
581 referral_len
= SMB_VFS_READLINK(conn
, path
, link_target
, bufsize
- 1);
582 if (referral_len
== -1) {
583 DEBUG(0,("is_msdfs_link_read_target: Error reading "
584 "msdfs link %s: %s\n",
585 path
, strerror(errno
)));
588 link_target
[referral_len
] = '\0';
590 DEBUG(5,("is_msdfs_link_internal: %s -> %s\n",path
,
593 if (!strnequal(link_target
, "msdfs:", 6)) {
600 if (link_target
!= link_target_buf
) {
601 TALLOC_FREE(link_target
);
606 /**********************************************************************
607 Returns true if the unix path is a valid msdfs symlink.
608 **********************************************************************/
610 bool is_msdfs_link(connection_struct
*conn
,
612 SMB_STRUCT_STAT
*sbufp
)
614 return is_msdfs_link_internal(talloc_tos(),
621 /*****************************************************************
622 Used by other functions to decide if a dfs path is remote,
623 and to get the list of referred locations for that remote path.
625 search_flag: For findfirsts, dfs links themselves are not
626 redirected, but paths beyond the links are. For normal smb calls,
627 even dfs links need to be redirected.
629 consumedcntp: how much of the dfs path is being redirected. the client
630 should try the remaining path on the redirected server.
632 If this returns NT_STATUS_PATH_NOT_COVERED the contents of the msdfs
633 link redirect are in targetpath.
634 *****************************************************************/
636 static NTSTATUS
dfs_path_lookup(TALLOC_CTX
*ctx
,
637 connection_struct
*conn
,
638 const char *dfspath
, /* Incoming complete dfs path */
639 const struct dfs_path
*pdp
, /* Parsed out
640 server+share+extrapath. */
641 bool search_flag
, /* Called from a findfirst ? */
643 char **pp_targetpath
)
648 struct smb_filename
*smb_fname
= NULL
;
649 char *canon_dfspath
= NULL
; /* Canonicalized dfs path. (only '/'
652 DEBUG(10,("dfs_path_lookup: Conn path = %s reqpath = %s\n",
653 conn
->connectpath
, pdp
->reqpath
));
656 * Note the unix path conversion here we're doing we
657 * throw away. We're looking for a symlink for a dfs
658 * resolution, if we don't find it we'll do another
659 * unix_convert later in the codepath.
662 status
= unix_convert(ctx
, conn
, pdp
->reqpath
, &smb_fname
,
663 search_flag
? UCF_ALWAYS_ALLOW_WCARD_LCOMP
: 0);
665 if (!NT_STATUS_IS_OK(status
)) {
666 if (!NT_STATUS_EQUAL(status
,
667 NT_STATUS_OBJECT_PATH_NOT_FOUND
)) {
670 if (smb_fname
== NULL
|| smb_fname
->base_name
== NULL
) {
675 /* Optimization - check if we can redirect the whole path. */
677 if (is_msdfs_link_internal(ctx
, conn
, smb_fname
->base_name
,
678 pp_targetpath
, NULL
)) {
680 DEBUG(6,("dfs_path_lookup (FindFirst) No redirection "
681 "for dfs link %s.\n", dfspath
));
682 status
= NT_STATUS_OK
;
686 DEBUG(6,("dfs_path_lookup: %s resolves to a "
687 "valid dfs link %s.\n", dfspath
,
688 pp_targetpath
? *pp_targetpath
: ""));
691 *consumedcntp
= strlen(dfspath
);
693 status
= NT_STATUS_PATH_NOT_COVERED
;
697 /* Prepare to test only for '/' components in the given path,
698 * so if a Windows path replace all '\\' characters with '/'.
699 * For a POSIX DFS path we know all separators are already '/'. */
701 canon_dfspath
= talloc_strdup(ctx
, dfspath
);
702 if (!canon_dfspath
) {
703 status
= NT_STATUS_NO_MEMORY
;
706 if (!pdp
->posix_path
) {
707 string_replace(canon_dfspath
, '\\', '/');
711 * localpath comes out of unix_convert, so it has
712 * no trailing backslash. Make sure that canon_dfspath hasn't either.
713 * Fix for bug #4860 from Jan Martin <Jan.Martin@rwedea.com>.
716 trim_char(canon_dfspath
,0,'/');
719 * Redirect if any component in the path is a link.
720 * We do this by walking backwards through the
721 * local path, chopping off the last component
722 * in both the local path and the canonicalized
723 * DFS path. If we hit a DFS link then we're done.
726 p
= strrchr_m(smb_fname
->base_name
, '/');
728 q
= strrchr_m(canon_dfspath
, '/');
737 if (is_msdfs_link_internal(ctx
, conn
,
738 smb_fname
->base_name
, pp_targetpath
,
740 DEBUG(4, ("dfs_path_lookup: Redirecting %s because "
741 "parent %s is dfs link\n", dfspath
,
742 smb_fname_str_dbg(smb_fname
)));
745 *consumedcntp
= strlen(canon_dfspath
);
746 DEBUG(10, ("dfs_path_lookup: Path consumed: %s "
752 status
= NT_STATUS_PATH_NOT_COVERED
;
756 /* Step back on the filesystem. */
757 p
= strrchr_m(smb_fname
->base_name
, '/');
760 /* And in the canonicalized dfs path. */
761 q
= strrchr_m(canon_dfspath
, '/');
765 status
= NT_STATUS_OK
;
767 TALLOC_FREE(smb_fname
);
771 /*****************************************************************
772 Decides if a dfs pathname should be redirected or not.
773 If not, the pathname is converted to a tcon-relative local unix path
775 search_wcard_flag: this flag performs 2 functions both related
776 to searches. See resolve_dfs_path() and parse_dfs_path_XX()
779 This function can return NT_STATUS_OK, meaning use the returned path as-is
780 (mapped into a local path).
781 or NT_STATUS_NOT_COVERED meaning return a DFS redirect, or
782 any other NT_STATUS error which is a genuine error to be
783 returned to the client.
784 *****************************************************************/
786 static NTSTATUS
dfs_redirect(TALLOC_CTX
*ctx
,
787 connection_struct
*conn
,
789 bool search_wcard_flag
,
790 bool allow_broken_path
,
792 bool *ppath_contains_wcard
)
795 struct dfs_path
*pdp
= talloc(ctx
, struct dfs_path
);
798 return NT_STATUS_NO_MEMORY
;
801 status
= parse_dfs_path(conn
, path_in
, search_wcard_flag
,
802 allow_broken_path
, pdp
,
803 ppath_contains_wcard
);
804 if (!NT_STATUS_IS_OK(status
)) {
809 if (pdp
->reqpath
[0] == '\0') {
811 *pp_path_out
= talloc_strdup(ctx
, "");
813 return NT_STATUS_NO_MEMORY
;
815 DEBUG(5,("dfs_redirect: self-referral.\n"));
819 /* If dfs pathname for a non-dfs share, convert to tcon-relative
820 path and return OK */
822 if (!lp_msdfs_root(SNUM(conn
))) {
823 *pp_path_out
= talloc_strdup(ctx
, pdp
->reqpath
);
826 return NT_STATUS_NO_MEMORY
;
831 /* If it looked like a local path (zero hostname/servicename)
832 * just treat as a tcon-relative path. */
834 if (pdp
->hostname
[0] == '\0' && pdp
->servicename
[0] == '\0') {
835 *pp_path_out
= talloc_strdup(ctx
, pdp
->reqpath
);
838 return NT_STATUS_NO_MEMORY
;
843 if (!( strequal(pdp
->servicename
, lp_servicename(talloc_tos(), SNUM(conn
)))
844 || (strequal(pdp
->servicename
, HOMES_NAME
)
845 && strequal(lp_servicename(talloc_tos(), SNUM(conn
)),
846 conn
->session_info
->unix_info
->sanitized_username
) )) ) {
848 /* The given sharename doesn't match this connection. */
851 return NT_STATUS_OBJECT_PATH_NOT_FOUND
;
854 status
= dfs_path_lookup(ctx
, conn
, path_in
, pdp
,
855 search_wcard_flag
, NULL
, NULL
);
856 if (!NT_STATUS_IS_OK(status
)) {
857 if (NT_STATUS_EQUAL(status
, NT_STATUS_PATH_NOT_COVERED
)) {
858 DEBUG(3,("dfs_redirect: Redirecting %s\n", path_in
));
860 DEBUG(10,("dfs_redirect: dfs_path_lookup "
861 "failed for %s with %s\n",
862 path_in
, nt_errstr(status
) ));
867 DEBUG(3,("dfs_redirect: Not redirecting %s.\n", path_in
));
869 /* Form non-dfs tcon-relative path */
870 *pp_path_out
= talloc_strdup(ctx
, pdp
->reqpath
);
873 return NT_STATUS_NO_MEMORY
;
876 DEBUG(3,("dfs_redirect: Path %s converted to non-dfs path %s\n",
883 /**********************************************************************
884 Return a self referral.
885 **********************************************************************/
887 static NTSTATUS
self_ref(TALLOC_CTX
*ctx
,
888 const char *dfs_path
,
889 struct junction_map
*jucn
,
891 bool *self_referralp
)
893 struct referral
*ref
;
895 *self_referralp
= True
;
897 jucn
->referral_count
= 1;
898 if((ref
= talloc_zero(ctx
, struct referral
)) == NULL
) {
899 return NT_STATUS_NO_MEMORY
;
902 ref
->alternate_path
= talloc_strdup(ctx
, dfs_path
);
903 if (!ref
->alternate_path
) {
905 return NT_STATUS_NO_MEMORY
;
908 ref
->ttl
= REFERRAL_TTL
;
909 jucn
->referral_list
= ref
;
910 *consumedcntp
= strlen(dfs_path
);
914 /**********************************************************************
915 Gets valid referrals for a dfs path and fills up the
916 junction_map structure.
917 **********************************************************************/
919 NTSTATUS
get_referred_path(TALLOC_CTX
*ctx
,
920 const char *dfs_path
,
921 bool allow_broken_path
,
922 struct junction_map
*jucn
,
924 bool *self_referralp
)
926 struct connection_struct
*conn
;
927 char *targetpath
= NULL
;
929 NTSTATUS status
= NT_STATUS_NOT_FOUND
;
931 struct dfs_path
*pdp
= talloc(ctx
, struct dfs_path
);
935 return NT_STATUS_NO_MEMORY
;
938 *self_referralp
= False
;
940 status
= parse_dfs_path(NULL
, dfs_path
, False
, allow_broken_path
,
942 if (!NT_STATUS_IS_OK(status
)) {
946 jucn
->service_name
= talloc_strdup(ctx
, pdp
->servicename
);
947 jucn
->volume_name
= talloc_strdup(ctx
, pdp
->reqpath
);
948 if (!jucn
->service_name
|| !jucn
->volume_name
) {
950 return NT_STATUS_NO_MEMORY
;
953 /* Verify the share is a dfs root */
954 snum
= lp_servicenumber(jucn
->service_name
);
956 char *service_name
= NULL
;
957 if ((snum
= find_service(ctx
, jucn
->service_name
, &service_name
)) < 0) {
958 return NT_STATUS_NOT_FOUND
;
961 return NT_STATUS_NO_MEMORY
;
963 TALLOC_FREE(jucn
->service_name
);
964 jucn
->service_name
= talloc_strdup(ctx
, service_name
);
965 if (!jucn
->service_name
) {
967 return NT_STATUS_NO_MEMORY
;
971 if (!lp_msdfs_root(snum
) && (*lp_msdfs_proxy(talloc_tos(), snum
) == '\0')) {
972 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not "
974 pdp
->servicename
, dfs_path
));
976 return NT_STATUS_NOT_FOUND
;
980 * Self referrals are tested with a anonymous IPC connection and
981 * a GET_DFS_REFERRAL call to \\server\share. (which means
982 * dp.reqpath[0] points to an empty string). create_conn_struct cd's
983 * into the directory and will fail if it cannot (as the anonymous
984 * user). Cope with this.
987 if (pdp
->reqpath
[0] == '\0') {
989 struct referral
*ref
;
991 if (*lp_msdfs_proxy(talloc_tos(), snum
) == '\0') {
1001 * It's an msdfs proxy share. Redirect to
1002 * the configured target share.
1005 jucn
->referral_count
= 1;
1006 if ((ref
= talloc_zero(ctx
, struct referral
)) == NULL
) {
1008 return NT_STATUS_NO_MEMORY
;
1011 if (!(tmp
= talloc_strdup(ctx
, lp_msdfs_proxy(talloc_tos(), snum
)))) {
1013 return NT_STATUS_NO_MEMORY
;
1016 trim_string(tmp
, "\\", 0);
1018 ref
->alternate_path
= talloc_asprintf(ctx
, "\\%s", tmp
);
1021 if (!ref
->alternate_path
) {
1023 return NT_STATUS_NO_MEMORY
;
1026 if (pdp
->reqpath
[0] != '\0') {
1027 ref
->alternate_path
= talloc_asprintf_append(
1028 ref
->alternate_path
,
1031 if (!ref
->alternate_path
) {
1033 return NT_STATUS_NO_MEMORY
;
1037 ref
->ttl
= REFERRAL_TTL
;
1038 jucn
->referral_list
= ref
;
1039 *consumedcntp
= strlen(dfs_path
);
1041 return NT_STATUS_OK
;
1044 status
= create_conn_struct_cwd(ctx
,
1045 server_event_context(),
1046 server_messaging_context(),
1048 lp_pathname(talloc_tos(), snum
), NULL
, &oldpath
);
1049 if (!NT_STATUS_IS_OK(status
)) {
1054 /* If this is a DFS path dfs_lookup should return
1055 * NT_STATUS_PATH_NOT_COVERED. */
1057 status
= dfs_path_lookup(ctx
, conn
, dfs_path
, pdp
,
1058 False
, consumedcntp
, &targetpath
);
1060 if (!NT_STATUS_EQUAL(status
, NT_STATUS_PATH_NOT_COVERED
)) {
1061 DEBUG(3,("get_referred_path: No valid referrals for path %s\n",
1063 if (NT_STATUS_IS_OK(status
)) {
1065 * We are in an error path here (we
1066 * know it's not a DFS path), but
1067 * dfs_path_lookup() can return
1068 * NT_STATUS_OK. Ensure we always
1069 * return a valid error code.
1071 * #9588 - ACLs are not inherited to directories
1074 status
= NT_STATUS_NOT_FOUND
;
1079 /* We know this is a valid dfs link. Parse the targetpath. */
1080 if (!parse_msdfs_symlink(ctx
, targetpath
,
1081 &jucn
->referral_list
,
1082 &jucn
->referral_count
)) {
1083 DEBUG(3,("get_referred_path: failed to parse symlink "
1084 "target %s\n", targetpath
));
1085 status
= NT_STATUS_NOT_FOUND
;
1089 status
= NT_STATUS_OK
;
1091 vfs_ChDir(conn
, oldpath
);
1092 SMB_VFS_DISCONNECT(conn
);
1098 /******************************************************************
1099 Set up the DFS referral for the dfs pathname. This call returns
1100 the amount of the path covered by this server, and where the
1101 client should be redirected to. This is the meat of the
1102 TRANS2_GET_DFS_REFERRAL call.
1103 ******************************************************************/
1105 int setup_dfs_referral(connection_struct
*orig_conn
,
1106 const char *dfs_path
,
1107 int max_referral_level
,
1108 char **ppdata
, NTSTATUS
*pstatus
)
1110 char *pdata
= *ppdata
;
1112 struct dfs_GetDFSReferral
*r
;
1113 DATA_BLOB blob
= data_blob_null
;
1115 enum ndr_err_code ndr_err
;
1117 r
= talloc_zero(talloc_tos(), struct dfs_GetDFSReferral
);
1119 *pstatus
= NT_STATUS_NO_MEMORY
;
1123 r
->in
.req
.max_referral_level
= max_referral_level
;
1124 r
->in
.req
.servername
= talloc_strdup(r
, dfs_path
);
1125 if (r
->in
.req
.servername
== NULL
) {
1127 *pstatus
= NT_STATUS_NO_MEMORY
;
1131 status
= SMB_VFS_GET_DFS_REFERRALS(orig_conn
, r
);
1132 if (!NT_STATUS_IS_OK(status
)) {
1138 ndr_err
= ndr_push_struct_blob(&blob
, r
,
1140 (ndr_push_flags_fn_t
)ndr_push_dfs_referral_resp
);
1141 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
1143 *pstatus
= NT_STATUS_INVALID_PARAMETER
;
1147 pdata
= (char *)SMB_REALLOC(pdata
, blob
.length
);
1150 DEBUG(0,("referral setup:"
1151 "malloc failed for Realloc!\n"));
1155 reply_size
= blob
.length
;
1156 memcpy(pdata
, blob
.data
, blob
.length
);
1159 *pstatus
= NT_STATUS_OK
;
1163 /**********************************************************************
1164 The following functions are called by the NETDFS RPC pipe functions
1165 **********************************************************************/
1167 /*********************************************************************
1168 Creates a junction structure from a DFS pathname
1169 **********************************************************************/
1171 bool create_junction(TALLOC_CTX
*ctx
,
1172 const char *dfs_path
,
1173 bool allow_broken_path
,
1174 struct junction_map
*jucn
)
1178 struct dfs_path
*pdp
= talloc(ctx
,struct dfs_path
);
1184 status
= parse_dfs_path(NULL
, dfs_path
, False
, allow_broken_path
,
1186 if (!NT_STATUS_IS_OK(status
)) {
1190 /* check if path is dfs : validate first token */
1191 if (!is_myname_or_ipaddr(pdp
->hostname
)) {
1192 DEBUG(4,("create_junction: Invalid hostname %s "
1194 pdp
->hostname
, dfs_path
));
1199 /* Check for a non-DFS share */
1200 snum
= lp_servicenumber(pdp
->servicename
);
1202 if(snum
< 0 || !lp_msdfs_root(snum
)) {
1203 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1209 jucn
->service_name
= talloc_strdup(ctx
, pdp
->servicename
);
1210 jucn
->volume_name
= talloc_strdup(ctx
, pdp
->reqpath
);
1211 jucn
->comment
= lp_comment(ctx
, snum
);
1214 if (!jucn
->service_name
|| !jucn
->volume_name
|| ! jucn
->comment
) {
1220 /**********************************************************************
1221 Forms a valid Unix pathname from the junction
1222 **********************************************************************/
1224 static bool junction_to_local_path(const struct junction_map
*jucn
,
1226 connection_struct
**conn_out
,
1232 snum
= lp_servicenumber(jucn
->service_name
);
1236 status
= create_conn_struct_cwd(talloc_tos(),
1237 server_event_context(),
1238 server_messaging_context(),
1240 snum
, lp_pathname(talloc_tos(), snum
), NULL
, oldpath
);
1241 if (!NT_STATUS_IS_OK(status
)) {
1245 *pp_path_out
= talloc_asprintf(*conn_out
,
1247 lp_pathname(talloc_tos(), snum
),
1249 if (!*pp_path_out
) {
1250 vfs_ChDir(*conn_out
, *oldpath
);
1251 SMB_VFS_DISCONNECT(*conn_out
);
1252 conn_free(*conn_out
);
1258 bool create_msdfs_link(const struct junction_map
*jucn
)
1262 char *msdfs_link
= NULL
;
1263 connection_struct
*conn
;
1265 bool insert_comma
= False
;
1268 if(!junction_to_local_path(jucn
, &path
, &conn
, &cwd
)) {
1272 /* Form the msdfs_link contents */
1273 msdfs_link
= talloc_strdup(conn
, "msdfs:");
1277 for(i
=0; i
<jucn
->referral_count
; i
++) {
1278 char *refpath
= jucn
->referral_list
[i
].alternate_path
;
1280 /* Alternate paths always use Windows separators. */
1281 trim_char(refpath
, '\\', '\\');
1282 if(*refpath
== '\0') {
1284 insert_comma
= False
;
1288 if (i
> 0 && insert_comma
) {
1289 msdfs_link
= talloc_asprintf_append_buffer(msdfs_link
,
1293 msdfs_link
= talloc_asprintf_append_buffer(msdfs_link
,
1301 if (!insert_comma
) {
1302 insert_comma
= True
;
1306 DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
1309 if(SMB_VFS_SYMLINK(conn
, msdfs_link
, path
) < 0) {
1310 if (errno
== EEXIST
) {
1311 struct smb_filename
*smb_fname
;
1313 smb_fname
= synthetic_smb_fname(talloc_tos(), path
,
1315 if (smb_fname
== NULL
) {
1320 if(SMB_VFS_UNLINK(conn
, smb_fname
)!=0) {
1321 TALLOC_FREE(smb_fname
);
1324 TALLOC_FREE(smb_fname
);
1326 if (SMB_VFS_SYMLINK(conn
, msdfs_link
, path
) < 0) {
1327 DEBUG(1,("create_msdfs_link: symlink failed "
1328 "%s -> %s\nError: %s\n",
1329 path
, msdfs_link
, strerror(errno
)));
1337 vfs_ChDir(conn
, cwd
);
1338 SMB_VFS_DISCONNECT(conn
);
1343 bool remove_msdfs_link(const struct junction_map
*jucn
)
1347 connection_struct
*conn
;
1349 struct smb_filename
*smb_fname
;
1351 if (!junction_to_local_path(jucn
, &path
, &conn
, &cwd
)) {
1355 smb_fname
= synthetic_smb_fname(talloc_tos(), path
, NULL
, NULL
);
1356 if (smb_fname
== NULL
) {
1361 if( SMB_VFS_UNLINK(conn
, smb_fname
) == 0 ) {
1365 TALLOC_FREE(smb_fname
);
1366 vfs_ChDir(conn
, cwd
);
1367 SMB_VFS_DISCONNECT(conn
);
1372 /*********************************************************************
1373 Return the number of DFS links at the root of this share.
1374 *********************************************************************/
1376 static int count_dfs_links(TALLOC_CTX
*ctx
, int snum
)
1380 const char *dname
= NULL
;
1381 char *talloced
= NULL
;
1382 const char *connect_path
= lp_pathname(talloc_tos(), snum
);
1383 const char *msdfs_proxy
= lp_msdfs_proxy(talloc_tos(), snum
);
1384 connection_struct
*conn
;
1388 if(*connect_path
== '\0') {
1393 * Fake up a connection struct for the VFS layer.
1396 status
= create_conn_struct_cwd(talloc_tos(),
1397 server_event_context(),
1398 server_messaging_context(),
1400 snum
, connect_path
, NULL
, &cwd
);
1401 if (!NT_STATUS_IS_OK(status
)) {
1402 DEBUG(3, ("create_conn_struct failed: %s\n",
1403 nt_errstr(status
)));
1407 /* Count a link for the msdfs root - convention */
1410 /* No more links if this is an msdfs proxy. */
1411 if (*msdfs_proxy
!= '\0') {
1415 /* Now enumerate all dfs links */
1416 dirp
= SMB_VFS_OPENDIR(conn
, ".", NULL
, 0);
1421 while ((dname
= vfs_readdirname(conn
, dirp
, NULL
, &talloced
))
1423 if (is_msdfs_link(conn
,
1428 TALLOC_FREE(talloced
);
1431 SMB_VFS_CLOSEDIR(conn
,dirp
);
1434 vfs_ChDir(conn
, cwd
);
1435 SMB_VFS_DISCONNECT(conn
);
1440 /*********************************************************************
1441 *********************************************************************/
1443 static int form_junctions(TALLOC_CTX
*ctx
,
1445 struct junction_map
*jucn
,
1450 const char *dname
= NULL
;
1451 char *talloced
= NULL
;
1452 const char *connect_path
= lp_pathname(talloc_tos(), snum
);
1453 char *service_name
= lp_servicename(talloc_tos(), snum
);
1454 const char *msdfs_proxy
= lp_msdfs_proxy(talloc_tos(), snum
);
1455 connection_struct
*conn
;
1456 struct referral
*ref
= NULL
;
1460 if (jn_remain
== 0) {
1464 if(*connect_path
== '\0') {
1469 * Fake up a connection struct for the VFS layer.
1472 status
= create_conn_struct_cwd(ctx
,
1473 server_event_context(),
1474 server_messaging_context(),
1475 &conn
, snum
, connect_path
, NULL
,
1477 if (!NT_STATUS_IS_OK(status
)) {
1478 DEBUG(3, ("create_conn_struct failed: %s\n",
1479 nt_errstr(status
)));
1483 /* form a junction for the msdfs root - convention
1484 DO NOT REMOVE THIS: NT clients will not work with us
1485 if this is not present
1487 jucn
[cnt
].service_name
= talloc_strdup(ctx
,service_name
);
1488 jucn
[cnt
].volume_name
= talloc_strdup(ctx
, "");
1489 if (!jucn
[cnt
].service_name
|| !jucn
[cnt
].volume_name
) {
1492 jucn
[cnt
].comment
= "";
1493 jucn
[cnt
].referral_count
= 1;
1495 ref
= jucn
[cnt
].referral_list
= talloc_zero(ctx
, struct referral
);
1496 if (jucn
[cnt
].referral_list
== NULL
) {
1501 ref
->ttl
= REFERRAL_TTL
;
1502 if (*msdfs_proxy
!= '\0') {
1503 ref
->alternate_path
= talloc_strdup(ctx
,
1506 ref
->alternate_path
= talloc_asprintf(ctx
,
1508 get_local_machine_name(),
1512 if (!ref
->alternate_path
) {
1517 /* Don't enumerate if we're an msdfs proxy. */
1518 if (*msdfs_proxy
!= '\0') {
1522 /* Now enumerate all dfs links */
1523 dirp
= SMB_VFS_OPENDIR(conn
, ".", NULL
, 0);
1528 while ((dname
= vfs_readdirname(conn
, dirp
, NULL
, &talloced
))
1530 char *link_target
= NULL
;
1531 if (cnt
>= jn_remain
) {
1532 DEBUG(2, ("form_junctions: ran out of MSDFS "
1534 TALLOC_FREE(talloced
);
1537 if (is_msdfs_link_internal(ctx
,
1539 dname
, &link_target
,
1541 if (parse_msdfs_symlink(ctx
,
1543 &jucn
[cnt
].referral_list
,
1544 &jucn
[cnt
].referral_count
)) {
1546 jucn
[cnt
].service_name
= talloc_strdup(ctx
,
1548 jucn
[cnt
].volume_name
= talloc_strdup(ctx
,
1550 if (!jucn
[cnt
].service_name
||
1551 !jucn
[cnt
].volume_name
) {
1552 TALLOC_FREE(talloced
);
1555 jucn
[cnt
].comment
= "";
1558 TALLOC_FREE(link_target
);
1560 TALLOC_FREE(talloced
);
1566 SMB_VFS_CLOSEDIR(conn
,dirp
);
1569 vfs_ChDir(conn
, cwd
);
1574 struct junction_map
*enum_msdfs_links(TALLOC_CTX
*ctx
, size_t *p_num_jn
)
1576 struct junction_map
*jn
= NULL
;
1578 size_t jn_count
= 0;
1582 if(!lp_host_msdfs()) {
1586 /* Ensure all the usershares are loaded. */
1588 load_registry_shares();
1589 sharecount
= load_usershare_shares(NULL
, connections_snum_used
);
1592 for(i
=0;i
< sharecount
;i
++) {
1593 if(lp_msdfs_root(i
)) {
1594 jn_count
+= count_dfs_links(ctx
, i
);
1597 if (jn_count
== 0) {
1600 jn
= talloc_array(ctx
, struct junction_map
, jn_count
);
1604 for(i
=0; i
< sharecount
; i
++) {
1605 if (*p_num_jn
>= jn_count
) {
1608 if(lp_msdfs_root(i
)) {
1609 *p_num_jn
+= form_junctions(ctx
, i
,
1611 jn_count
- *p_num_jn
);
1617 /******************************************************************************
1618 Core function to resolve a dfs pathname possibly containing a wildcard. If
1619 ppath_contains_wcard != NULL, it will be set to true if a wildcard is
1620 detected during dfs resolution.
1621 ******************************************************************************/
1623 NTSTATUS
resolve_dfspath_wcard(TALLOC_CTX
*ctx
,
1624 connection_struct
*conn
,
1626 const char *name_in
,
1628 bool allow_broken_path
,
1630 bool *ppath_contains_wcard
)
1632 bool path_contains_wcard
;
1633 NTSTATUS status
= NT_STATUS_OK
;
1635 if (dfs_pathnames
) {
1636 status
= dfs_redirect(ctx
,
1642 &path_contains_wcard
);
1644 if (NT_STATUS_IS_OK(status
) && ppath_contains_wcard
!= NULL
) {
1645 *ppath_contains_wcard
= path_contains_wcard
;
1649 * Cheat and just return a copy of the in ptr.
1650 * Once srvstr_get_path() uses talloc it'll
1651 * be a talloced ptr anyway.
1653 *pp_name_out
= discard_const_p(char, name_in
);