2 Unix SMB/Netbios implementation.
4 MSDFS services for Samba
5 Copyright (C) Shirish Kalele 2000
6 Copyright (C) Jeremy Allison 2007
7 Copyright (C) Robin McCorkell 2015
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 3 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>.
24 #define DBGC_CLASS DBGC_MSDFS
26 #include "system/filesys.h"
27 #include "smbd/smbd.h"
28 #include "smbd/globals.h"
31 #include "../auth/auth_util.h"
32 #include "lib/param/loadparm.h"
33 #include "libcli/security/security.h"
34 #include "librpc/gen_ndr/ndr_dfsblobs.h"
35 #include "lib/tsocket/tsocket.h"
36 #include "lib/global_contexts.h"
37 #include "source3/lib/substitute.h"
39 /**********************************************************************
40 Parse a DFS pathname of the form \hostname\service\reqpath
41 into the dfs_path structure.
42 If POSIX pathnames is true, the pathname may also be of the
43 form /hostname/service/reqpath.
44 We cope with either here.
46 Unfortunately, due to broken clients who might set the
47 SVAL(inbuf,smb_flg2) & FLAGS2_DFS_PATHNAMES bit and then
48 send a local path, we have to cope with that too....
50 If conn != NULL then ensure the provided service is
51 the one pointed to by the connection.
53 This version does everything using pointers within one copy of the
54 pathname string, talloced on the struct dfs_path pointer (which
55 must be talloced). This may be too clever to live....
57 **********************************************************************/
59 static NTSTATUS
parse_dfs_path(connection_struct
*conn
,
62 bool allow_broken_path
,
63 struct dfs_path
*pdp
) /* MUST BE TALLOCED */
65 const struct loadparm_substitution
*lp_sub
=
66 loadparm_s3_global_substitution();
71 NTSTATUS status
= NT_STATUS_OK
;
77 * This is the only talloc we should need to do
78 * on the struct dfs_path. All the pointers inside
79 * it should point to offsets within this string.
82 pathname_local
= talloc_strdup(pdp
, pathname
);
83 if (!pathname_local
) {
84 return NT_STATUS_NO_MEMORY
;
86 /* Get a pointer to the terminating '\0' */
87 eos_ptr
= &pathname_local
[strlen(pathname_local
)];
88 p
= temp
= pathname_local
;
91 * Non-broken DFS paths *must* start with the
92 * path separator. For Windows this is always '\\',
93 * for posix paths this is always '/'.
96 if (*pathname
== '/') {
97 pdp
->posix_path
= true;
100 pdp
->posix_path
= false;
104 if (allow_broken_path
&& (*pathname
!= sepchar
)) {
105 DEBUG(10,("parse_dfs_path: path %s doesn't start with %c\n",
106 pathname
, sepchar
));
108 * Possibly client sent a local path by mistake.
109 * Try and convert to a local path.
110 * Note that this is an SMB1-only fallback
111 * to cope with known broken SMB1 clients.
114 pdp
->hostname
= eos_ptr
; /* "" */
115 pdp
->servicename
= eos_ptr
; /* "" */
117 /* We've got no info about separators. */
118 pdp
->posix_path
= lp_posix_pathnames();
120 DEBUG(10,("parse_dfs_path: trying to convert %s to a "
127 * Safe to use on talloc'ed string as it only shrinks.
128 * It also doesn't affect the eos_ptr.
130 trim_char(temp
,sepchar
,sepchar
);
132 DEBUG(10,("parse_dfs_path: temp = |%s| after trimming %c's\n",
136 /* Parse out hostname. */
137 p
= strchr_m(temp
,sepchar
);
139 DEBUG(10,("parse_dfs_path: can't parse hostname from path %s\n",
142 * Possibly client sent a local path by mistake.
143 * Try and convert to a local path.
146 pdp
->hostname
= eos_ptr
; /* "" */
147 pdp
->servicename
= eos_ptr
; /* "" */
150 DEBUG(10,("parse_dfs_path: trying to convert %s "
156 pdp
->hostname
= temp
;
158 DEBUG(10,("parse_dfs_path: hostname: %s\n",pdp
->hostname
));
160 /* Parse out servicename. */
162 p
= strchr_m(servicename
,sepchar
);
167 /* Is this really our servicename ? */
168 if (conn
&& !( strequal(servicename
, lp_servicename(talloc_tos(), lp_sub
, SNUM(conn
)))
169 || (strequal(servicename
, HOMES_NAME
)
170 && strequal(lp_servicename(talloc_tos(), lp_sub
, SNUM(conn
)),
171 get_current_username()) )) ) {
172 DEBUG(10,("parse_dfs_path: %s is not our servicename\n",
176 * Possibly client sent a local path by mistake.
177 * Try and convert to a local path.
180 pdp
->hostname
= eos_ptr
; /* "" */
181 pdp
->servicename
= eos_ptr
; /* "" */
183 /* Repair the path - replace the sepchar's
186 *servicename
= sepchar
;
192 DEBUG(10,("parse_dfs_path: trying to convert %s "
198 pdp
->servicename
= servicename
;
200 DEBUG(10,("parse_dfs_path: servicename: %s\n",pdp
->servicename
));
203 /* Client sent self referral \server\share. */
204 pdp
->reqpath
= eos_ptr
; /* "" */
214 /* Rest is reqpath. */
215 if (pdp
->posix_path
) {
216 status
= check_path_syntax_posix(pdp
->reqpath
);
218 status
= check_path_syntax(pdp
->reqpath
);
221 if (!NT_STATUS_IS_OK(status
)) {
222 DEBUG(10,("parse_dfs_path: '%s' failed with %s\n",
223 p
, nt_errstr(status
) ));
227 DEBUG(10,("parse_dfs_path: rest of the path: %s\n",pdp
->reqpath
));
231 /********************************************************
232 Fake up a connection struct for the VFS layer, for use in
233 applications (such as the python bindings), that do not want the
234 global working directory changed under them.
236 SMB_VFS_CONNECT requires root privileges.
237 *********************************************************/
239 static NTSTATUS
create_conn_struct_as_root(TALLOC_CTX
*ctx
,
240 struct tevent_context
*ev
,
241 struct messaging_context
*msg
,
242 connection_struct
**pconn
,
245 const struct auth_session_info
*session_info
)
247 connection_struct
*conn
;
249 const char *vfs_user
;
250 struct smbd_server_connection
*sconn
;
251 const char *servicename
= lp_const_servicename(snum
);
254 sconn
= talloc_zero(ctx
, struct smbd_server_connection
);
256 return NT_STATUS_NO_MEMORY
;
260 sconn
->msg_ctx
= msg
;
262 conn
= conn_new(sconn
);
265 return NT_STATUS_NO_MEMORY
;
268 /* Now we have conn, we need to make sconn a child of conn,
269 * for a proper talloc tree */
270 talloc_steal(conn
, sconn
);
272 if (snum
== -1 && servicename
== NULL
) {
273 servicename
= "Unknown Service (snum == -1)";
276 connpath
= talloc_strdup(conn
, path
);
279 return NT_STATUS_NO_MEMORY
;
281 connpath
= talloc_string_sub(conn
,
287 return NT_STATUS_NO_MEMORY
;
290 /* needed for smbd_vfs_init() */
292 conn
->params
->service
= snum
;
293 conn
->cnum
= TID_FIELD_INVALID
;
295 SMB_ASSERT(session_info
!= NULL
);
297 conn
->session_info
= copy_session_info(conn
, session_info
);
298 if (conn
->session_info
== NULL
) {
299 DBG_ERR("copy_serverinfo failed\n");
301 return NT_STATUS_NO_MEMORY
;
304 /* unix_info could be NULL in session_info */
305 if (conn
->session_info
->unix_info
!= NULL
) {
306 vfs_user
= conn
->session_info
->unix_info
->unix_name
;
308 vfs_user
= get_current_username();
311 conn_setup_case_options(conn
);
313 set_conn_connectpath(conn
, connpath
);
316 * New code to check if there's a share security descriptor
317 * added from NT server manager. This is done after the
318 * smb.conf checks are done as we need a uid and token. JRA.
321 share_access_check(conn
->session_info
->security_token
,
323 MAXIMUM_ALLOWED_ACCESS
,
324 &conn
->share_access
);
326 if ((conn
->share_access
& FILE_WRITE_DATA
) == 0) {
327 if ((conn
->share_access
& FILE_READ_DATA
) == 0) {
328 /* No access, read or write. */
329 DBG_WARNING("connection to %s "
330 "denied due to security "
334 return NT_STATUS_ACCESS_DENIED
;
336 conn
->read_only
= true;
339 if (!smbd_vfs_init(conn
)) {
340 NTSTATUS status
= map_nt_error_from_unix(errno
);
341 DEBUG(0,("create_conn_struct: smbd_vfs_init failed.\n"));
346 /* this must be the first filesystem operation that we do */
347 if (SMB_VFS_CONNECT(conn
, servicename
, vfs_user
) < 0) {
348 DEBUG(0,("VFS connect failed!\n"));
350 return NT_STATUS_UNSUCCESSFUL
;
353 ok
= canonicalize_connect_path(conn
);
355 DBG_ERR("Failed to canonicalize sharepath\n");
357 return NT_STATUS_ACCESS_DENIED
;
360 conn
->fs_capabilities
= SMB_VFS_FS_CAPABILITIES(conn
, &conn
->ts_res
);
361 conn
->tcon_done
= true;
362 *pconn
= talloc_move(ctx
, &conn
);
367 static int conn_struct_tos_destructor(struct conn_struct_tos
*c
)
369 if (c
->oldcwd_fname
!= NULL
) {
370 vfs_ChDir(c
->conn
, c
->oldcwd_fname
);
371 TALLOC_FREE(c
->oldcwd_fname
);
373 SMB_VFS_DISCONNECT(c
->conn
);
378 /********************************************************
379 Fake up a connection struct for the VFS layer, for use in
380 applications (such as the python bindings), that do not want the
381 global working directory changed under them.
383 SMB_VFS_CONNECT requires root privileges.
384 This temporary uses become_root() and unbecome_root().
386 But further impersonation has to be cone by the caller.
387 *********************************************************/
388 NTSTATUS
create_conn_struct_tos(struct messaging_context
*msg
,
391 const struct auth_session_info
*session_info
,
392 struct conn_struct_tos
**_c
)
394 struct conn_struct_tos
*c
= NULL
;
395 struct tevent_context
*ev
= NULL
;
400 c
= talloc_zero(talloc_tos(), struct conn_struct_tos
);
402 return NT_STATUS_NO_MEMORY
;
405 ev
= samba_tevent_context_init(c
);
408 return NT_STATUS_NO_MEMORY
;
412 status
= create_conn_struct_as_root(c
,
420 if (!NT_STATUS_IS_OK(status
)) {
425 talloc_set_destructor(c
, conn_struct_tos_destructor
);
431 /********************************************************
432 Fake up a connection struct for the VFS layer.
433 Note: this performs a vfs connect and CHANGES CWD !!!! JRA.
435 See also the comment for create_conn_struct_tos() above!
437 The CWD change is reverted by the destructor of
438 conn_struct_tos when the current talloc_tos() is destroyed.
439 *********************************************************/
440 NTSTATUS
create_conn_struct_tos_cwd(struct messaging_context
*msg
,
443 const struct auth_session_info
*session_info
,
444 struct conn_struct_tos
**_c
)
446 struct conn_struct_tos
*c
= NULL
;
447 struct smb_filename smb_fname_connectpath
= {0};
452 status
= create_conn_struct_tos(msg
,
457 if (!NT_STATUS_IS_OK(status
)) {
462 * Windows seems to insist on doing trans2getdfsreferral() calls on
463 * the IPC$ share as the anonymous user. If we try to chdir as that
464 * user we will fail.... WTF ? JRA.
467 c
->oldcwd_fname
= vfs_GetWd(c
, c
->conn
);
468 if (c
->oldcwd_fname
== NULL
) {
469 status
= map_nt_error_from_unix(errno
);
470 DEBUG(3, ("vfs_GetWd failed: %s\n", strerror(errno
)));
475 smb_fname_connectpath
= (struct smb_filename
) {
476 .base_name
= c
->conn
->connectpath
479 if (vfs_ChDir(c
->conn
, &smb_fname_connectpath
) != 0) {
480 status
= map_nt_error_from_unix(errno
);
481 DBG_NOTICE("Can't ChDir to new conn path %s. "
483 c
->conn
->connectpath
, strerror(errno
));
484 TALLOC_FREE(c
->oldcwd_fname
);
493 /********************************************************
494 Fake up a connection struct for the VFS layer.
495 This takes an TALLOC_CTX and tevent_context from the
496 caller and the resulting connection_struct is stable
497 across the lifetime of mem_ctx and ev.
499 Note: this performs a vfs connect and changes cwd.
501 See also the comment for create_conn_struct_tos() above!
502 *********************************************************/
504 NTSTATUS
create_conn_struct_cwd(TALLOC_CTX
*mem_ctx
,
505 struct tevent_context
*ev
,
506 struct messaging_context
*msg
,
507 const struct auth_session_info
*session_info
,
510 struct connection_struct
**c
)
515 status
= create_conn_struct_as_root(mem_ctx
,
526 static void shuffle_strlist(char **list
, int count
)
532 for (i
= count
; i
> 1; i
--) {
533 r
= generate_random() % i
;
541 /**********************************************************************
542 Parse the contents of a symlink to verify if it is an msdfs referral
543 A valid referral is of the form:
545 msdfs:server1\share1,server2\share2
546 msdfs:server1\share1\pathname,server2\share2\pathname
547 msdfs:server1/share1,server2/share2
548 msdfs:server1/share1/pathname,server2/share2/pathname.
550 Note that the alternate paths returned here must be of the canonicalized
554 \server\share\path\to\file,
556 even in posix path mode. This is because we have no knowledge if the
557 server we're referring to understands posix paths.
558 **********************************************************************/
560 bool parse_msdfs_symlink(TALLOC_CTX
*ctx
,
561 bool shuffle_referrals
,
563 struct referral
**ppreflist
,
568 char **alt_path
= NULL
;
570 struct referral
*reflist
= NULL
;
573 temp
= talloc_strdup(ctx
, target
);
577 prot
= strtok_r(temp
, ":", &saveptr
);
579 DEBUG(0,("parse_msdfs_symlink: invalid path !\n"));
584 alt_path
= talloc_array(ctx
, char *, MAX_REFERRAL_COUNT
);
590 /* parse out the alternate paths */
591 while((count
<MAX_REFERRAL_COUNT
) &&
592 ((alt_path
[count
] = strtok_r(NULL
, ",", &saveptr
)) != NULL
)) {
596 /* shuffle alternate paths */
597 if (shuffle_referrals
) {
598 shuffle_strlist(alt_path
, count
);
601 DBG_DEBUG("count=%zu\n", count
);
604 reflist
= talloc_zero_array(ctx
,
605 struct referral
, count
);
606 if(reflist
== NULL
) {
608 TALLOC_FREE(alt_path
);
615 for(i
=0;i
<count
;i
++) {
618 /* Canonicalize link target.
619 * Replace all /'s in the path by a \ */
620 string_replace(alt_path
[i
], '/', '\\');
622 /* Remove leading '\\'s */
624 while (*p
&& (*p
== '\\')) {
628 reflist
[i
].alternate_path
= talloc_asprintf(reflist
,
631 if (!reflist
[i
].alternate_path
) {
633 TALLOC_FREE(alt_path
);
634 TALLOC_FREE(reflist
);
638 reflist
[i
].proximity
= 0;
639 reflist
[i
].ttl
= REFERRAL_TTL
;
640 DBG_DEBUG("Created alt path: %s\n",
641 reflist
[i
].alternate_path
);
644 if (ppreflist
!= NULL
) {
645 *ppreflist
= reflist
;
647 TALLOC_FREE(reflist
);
649 if (prefcount
!= NULL
) {
653 TALLOC_FREE(alt_path
);
657 /**********************************************************************
658 Returns true if the unix path is a valid msdfs symlink.
659 **********************************************************************/
661 bool is_msdfs_link(struct files_struct
*dirfsp
,
662 struct smb_filename
*atname
)
664 NTSTATUS status
= SMB_VFS_READ_DFS_PATHAT(dirfsp
->conn
,
670 return (NT_STATUS_IS_OK(status
));
673 /*****************************************************************
674 Used by other functions to decide if a dfs path is remote,
675 and to get the list of referred locations for that remote path.
677 consumedcntp: how much of the dfs path is being redirected. the client
678 should try the remaining path on the redirected server.
680 If this returns NT_STATUS_PATH_NOT_COVERED the contents of the msdfs
681 link redirect are in targetpath.
682 *****************************************************************/
684 static NTSTATUS
dfs_path_lookup(TALLOC_CTX
*ctx
,
685 connection_struct
*conn
,
686 const char *dfspath
, /* Incoming complete dfs path */
687 const struct dfs_path
*pdp
, /* Parsed out
688 server+share+extrapath. */
692 struct referral
**ppreflist
,
693 size_t *preferral_count
)
698 struct smb_filename
*smb_fname
= NULL
;
699 struct smb_filename
*parent_fname
= NULL
;
700 struct smb_filename
*atname
= NULL
;
701 char *canon_dfspath
= NULL
; /* Canonicalized dfs path. (only '/'
704 DEBUG(10,("dfs_path_lookup: Conn path = %s reqpath = %s\n",
705 conn
->connectpath
, pdp
->reqpath
));
708 * Note the unix path conversion here we're doing we
709 * throw away. We're looking for a symlink for a dfs
710 * resolution, if we don't find it we'll do another
711 * unix_convert later in the codepath.
714 status
= unix_convert(ctx
, conn
, pdp
->reqpath
, 0, &smb_fname
,
717 if (!NT_STATUS_IS_OK(status
)) {
718 if (!NT_STATUS_EQUAL(status
,
719 NT_STATUS_OBJECT_PATH_NOT_FOUND
)) {
722 if (smb_fname
== NULL
|| smb_fname
->base_name
== NULL
) {
727 /* Optimization - check if we can redirect the whole path. */
728 status
= parent_pathref(ctx
,
733 if (NT_STATUS_IS_OK(status
)) {
735 * We must have a parent_fname->fsp before
736 * we can call SMB_VFS_READ_DFS_PATHAT().
738 status
= SMB_VFS_READ_DFS_PATHAT(conn
,
744 /* We're now done with parent_fname and atname. */
745 TALLOC_FREE(parent_fname
);
747 if (NT_STATUS_IS_OK(status
)) {
748 DBG_INFO("%s resolves to a valid dfs link\n",
752 *consumedcntp
= strlen(dfspath
);
754 status
= NT_STATUS_PATH_NOT_COVERED
;
759 /* Prepare to test only for '/' components in the given path,
760 * so if a Windows path replace all '\\' characters with '/'.
761 * For a POSIX DFS path we know all separators are already '/'. */
763 canon_dfspath
= talloc_strdup(ctx
, dfspath
);
764 if (!canon_dfspath
) {
765 status
= NT_STATUS_NO_MEMORY
;
768 if (!pdp
->posix_path
) {
769 string_replace(canon_dfspath
, '\\', '/');
773 * localpath comes out of unix_convert, so it has
774 * no trailing backslash. Make sure that canon_dfspath hasn't either.
775 * Fix for bug #4860 from Jan Martin <Jan.Martin@rwedea.com>.
778 trim_char(canon_dfspath
,0,'/');
781 * Redirect if any component in the path is a link.
782 * We do this by walking backwards through the
783 * local path, chopping off the last component
784 * in both the local path and the canonicalized
785 * DFS path. If we hit a DFS link then we're done.
788 p
= strrchr_m(smb_fname
->base_name
, '/');
790 q
= strrchr_m(canon_dfspath
, '/');
800 * Ensure parent_pathref() calls vfs_stat() on
801 * the newly truncated path.
803 SET_STAT_INVALID(smb_fname
->st
);
804 status
= parent_pathref(ctx
,
809 if (NT_STATUS_IS_OK(status
)) {
811 * We must have a parent_fname->fsp before
812 * we can call SMB_VFS_READ_DFS_PATHAT().
814 status
= SMB_VFS_READ_DFS_PATHAT(conn
,
821 /* We're now done with parent_fname and atname. */
822 TALLOC_FREE(parent_fname
);
824 if (NT_STATUS_IS_OK(status
)) {
825 DBG_INFO("Redirecting %s because "
826 "parent %s is a dfs link\n",
828 smb_fname_str_dbg(smb_fname
));
831 *consumedcntp
= strlen(canon_dfspath
);
832 DBG_DEBUG("Path consumed: %s "
838 status
= NT_STATUS_PATH_NOT_COVERED
;
843 /* Step back on the filesystem. */
844 p
= strrchr_m(smb_fname
->base_name
, '/');
847 /* And in the canonicalized dfs path. */
848 q
= strrchr_m(canon_dfspath
, '/');
852 if ((ucf_flags
& UCF_GMT_PATHNAME
) && _twrp
!= NULL
) {
853 *_twrp
= smb_fname
->twrp
;
856 status
= NT_STATUS_OK
;
859 /* This should already be free, but make sure. */
860 TALLOC_FREE(parent_fname
);
861 TALLOC_FREE(smb_fname
);
865 /*****************************************************************
866 Decides if a dfs pathname should be redirected or not.
867 If not, the pathname is converted to a tcon-relative local unix path
869 search_wcard_flag: this flag performs 2 functions both related
870 to searches. See resolve_dfs_path() and parse_dfs_path_XX()
873 This function can return NT_STATUS_OK, meaning use the returned path as-is
874 (mapped into a local path).
875 or NT_STATUS_NOT_COVERED meaning return a DFS redirect, or
876 any other NT_STATUS error which is a genuine error to be
877 returned to the client.
878 *****************************************************************/
880 NTSTATUS
dfs_redirect(TALLOC_CTX
*ctx
,
881 connection_struct
*conn
,
884 bool allow_broken_path
,
888 const struct loadparm_substitution
*lp_sub
=
889 loadparm_s3_global_substitution();
891 struct dfs_path
*pdp
= talloc(ctx
, struct dfs_path
);
894 return NT_STATUS_NO_MEMORY
;
897 status
= parse_dfs_path(conn
, path_in
, false,
898 allow_broken_path
, pdp
);
899 if (!NT_STATUS_IS_OK(status
)) {
904 if (pdp
->reqpath
[0] == '\0') {
906 *pp_path_out
= talloc_strdup(ctx
, "");
908 return NT_STATUS_NO_MEMORY
;
910 DEBUG(5,("dfs_redirect: self-referral.\n"));
914 /* If dfs pathname for a non-dfs share, convert to tcon-relative
915 path and return OK */
917 if (!lp_msdfs_root(SNUM(conn
))) {
918 *pp_path_out
= talloc_strdup(ctx
, pdp
->reqpath
);
921 return NT_STATUS_NO_MEMORY
;
926 /* If it looked like a local path (zero hostname/servicename)
927 * just treat as a tcon-relative path. */
929 if (pdp
->hostname
[0] == '\0' && pdp
->servicename
[0] == '\0') {
930 *pp_path_out
= talloc_strdup(ctx
, pdp
->reqpath
);
933 return NT_STATUS_NO_MEMORY
;
938 if (!( strequal(pdp
->servicename
, lp_servicename(talloc_tos(), lp_sub
, SNUM(conn
)))
939 || (strequal(pdp
->servicename
, HOMES_NAME
)
940 && strequal(lp_servicename(talloc_tos(), lp_sub
, SNUM(conn
)),
941 conn
->session_info
->unix_info
->sanitized_username
) )) ) {
943 /* The given sharename doesn't match this connection. */
946 return NT_STATUS_OBJECT_PATH_NOT_FOUND
;
949 status
= dfs_path_lookup(ctx
,
955 NULL
, /* int *consumedcntp */
956 NULL
, /* struct referral **ppreflist */
957 NULL
); /* size_t *preferral_count */
958 if (!NT_STATUS_IS_OK(status
)) {
959 if (NT_STATUS_EQUAL(status
, NT_STATUS_PATH_NOT_COVERED
)) {
960 DEBUG(3,("dfs_redirect: Redirecting %s\n", path_in
));
962 DEBUG(10,("dfs_redirect: dfs_path_lookup "
963 "failed for %s with %s\n",
964 path_in
, nt_errstr(status
) ));
969 DEBUG(3,("dfs_redirect: Not redirecting %s.\n", path_in
));
971 /* Form non-dfs tcon-relative path */
972 *pp_path_out
= talloc_strdup(ctx
, pdp
->reqpath
);
975 return NT_STATUS_NO_MEMORY
;
978 DEBUG(3,("dfs_redirect: Path %s converted to non-dfs path %s\n",
985 /**********************************************************************
986 Return a self referral.
987 **********************************************************************/
989 static NTSTATUS
self_ref(TALLOC_CTX
*ctx
,
990 const char *dfs_path
,
991 struct junction_map
*jucn
,
993 bool *self_referralp
)
995 struct referral
*ref
;
997 *self_referralp
= True
;
999 jucn
->referral_count
= 1;
1000 if((ref
= talloc_zero(ctx
, struct referral
)) == NULL
) {
1001 return NT_STATUS_NO_MEMORY
;
1004 ref
->alternate_path
= talloc_strdup(ctx
, dfs_path
);
1005 if (!ref
->alternate_path
) {
1007 return NT_STATUS_NO_MEMORY
;
1010 ref
->ttl
= REFERRAL_TTL
;
1011 jucn
->referral_list
= ref
;
1012 *consumedcntp
= strlen(dfs_path
);
1013 return NT_STATUS_OK
;
1016 /**********************************************************************
1017 Gets valid referrals for a dfs path and fills up the
1018 junction_map structure.
1019 **********************************************************************/
1021 NTSTATUS
get_referred_path(TALLOC_CTX
*ctx
,
1022 struct auth_session_info
*session_info
,
1023 const char *dfs_path
,
1024 const struct tsocket_address
*remote_address
,
1025 const struct tsocket_address
*local_address
,
1026 bool allow_broken_path
,
1027 struct junction_map
*jucn
,
1029 bool *self_referralp
)
1031 TALLOC_CTX
*frame
= talloc_stackframe();
1032 const struct loadparm_substitution
*lp_sub
=
1033 loadparm_s3_global_substitution();
1034 struct conn_struct_tos
*c
= NULL
;
1035 struct connection_struct
*conn
= NULL
;
1037 NTSTATUS status
= NT_STATUS_NOT_FOUND
;
1038 struct dfs_path
*pdp
= talloc_zero(frame
, struct dfs_path
);
1042 return NT_STATUS_NO_MEMORY
;
1045 *self_referralp
= False
;
1047 status
= parse_dfs_path(NULL
, dfs_path
, False
, allow_broken_path
, pdp
);
1048 if (!NT_STATUS_IS_OK(status
)) {
1053 jucn
->service_name
= talloc_strdup(ctx
, pdp
->servicename
);
1054 jucn
->volume_name
= talloc_strdup(ctx
, pdp
->reqpath
);
1055 if (!jucn
->service_name
|| !jucn
->volume_name
) {
1057 return NT_STATUS_NO_MEMORY
;
1060 /* Verify the share is a dfs root */
1061 snum
= lp_servicenumber(jucn
->service_name
);
1063 char *service_name
= NULL
;
1064 if ((snum
= find_service(ctx
, jucn
->service_name
, &service_name
)) < 0) {
1066 return NT_STATUS_NOT_FOUND
;
1068 if (!service_name
) {
1070 return NT_STATUS_NO_MEMORY
;
1072 TALLOC_FREE(jucn
->service_name
);
1073 jucn
->service_name
= talloc_strdup(ctx
, service_name
);
1074 if (!jucn
->service_name
) {
1076 return NT_STATUS_NO_MEMORY
;
1080 if (!lp_msdfs_root(snum
) && (*lp_msdfs_proxy(talloc_tos(), lp_sub
, snum
) == '\0')) {
1081 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not "
1083 pdp
->servicename
, dfs_path
));
1085 return NT_STATUS_NOT_FOUND
;
1089 * Self referrals are tested with a anonymous IPC connection and
1090 * a GET_DFS_REFERRAL call to \\server\share. (which means
1091 * dp.reqpath[0] points to an empty string). create_conn_struct cd's
1092 * into the directory and will fail if it cannot (as the anonymous
1093 * user). Cope with this.
1096 if (pdp
->reqpath
[0] == '\0') {
1098 struct referral
*ref
;
1101 if (*lp_msdfs_proxy(talloc_tos(), lp_sub
, snum
) == '\0') {
1103 return self_ref(ctx
,
1111 * It's an msdfs proxy share. Redirect to
1112 * the configured target share.
1115 tmp
= talloc_asprintf(frame
, "msdfs:%s",
1116 lp_msdfs_proxy(frame
, lp_sub
, snum
));
1119 return NT_STATUS_NO_MEMORY
;
1122 if (!parse_msdfs_symlink(ctx
,
1123 lp_msdfs_shuffle_referrals(snum
),
1128 return NT_STATUS_INVALID_PARAMETER
;
1130 jucn
->referral_count
= refcount
;
1131 jucn
->referral_list
= ref
;
1132 *consumedcntp
= strlen(dfs_path
);
1134 return NT_STATUS_OK
;
1137 status
= create_conn_struct_tos_cwd(global_messaging_context(),
1139 lp_path(frame
, lp_sub
, snum
),
1142 if (!NT_STATUS_IS_OK(status
)) {
1151 * The remote and local address should be passed down to
1152 * create_conn_struct_cwd.
1154 if (conn
->sconn
->remote_address
== NULL
) {
1155 conn
->sconn
->remote_address
=
1156 tsocket_address_copy(remote_address
, conn
->sconn
);
1157 if (conn
->sconn
->remote_address
== NULL
) {
1159 return NT_STATUS_NO_MEMORY
;
1162 if (conn
->sconn
->local_address
== NULL
) {
1163 conn
->sconn
->local_address
=
1164 tsocket_address_copy(local_address
, conn
->sconn
);
1165 if (conn
->sconn
->local_address
== NULL
) {
1167 return NT_STATUS_NO_MEMORY
;
1171 /* If this is a DFS path dfs_lookup should return
1172 * NT_STATUS_PATH_NOT_COVERED. */
1174 status
= dfs_path_lookup(ctx
,
1181 &jucn
->referral_list
,
1182 &jucn
->referral_count
);
1184 if (!NT_STATUS_EQUAL(status
, NT_STATUS_PATH_NOT_COVERED
)) {
1185 DEBUG(3,("get_referred_path: No valid referrals for path %s\n",
1187 if (NT_STATUS_IS_OK(status
)) {
1189 * We are in an error path here (we
1190 * know it's not a DFS path), but
1191 * dfs_path_lookup() can return
1192 * NT_STATUS_OK. Ensure we always
1193 * return a valid error code.
1195 * #9588 - ACLs are not inherited to directories
1198 status
= NT_STATUS_NOT_FOUND
;
1203 status
= NT_STATUS_OK
;
1209 /******************************************************************
1210 Set up the DFS referral for the dfs pathname. This call returns
1211 the amount of the path covered by this server, and where the
1212 client should be redirected to. This is the meat of the
1213 TRANS2_GET_DFS_REFERRAL call.
1214 ******************************************************************/
1216 int setup_dfs_referral(connection_struct
*orig_conn
,
1217 const char *dfs_path
,
1218 int max_referral_level
,
1219 char **ppdata
, NTSTATUS
*pstatus
)
1221 char *pdata
= *ppdata
;
1223 struct dfs_GetDFSReferral
*r
;
1224 DATA_BLOB blob
= data_blob_null
;
1226 enum ndr_err_code ndr_err
;
1228 r
= talloc_zero(talloc_tos(), struct dfs_GetDFSReferral
);
1230 *pstatus
= NT_STATUS_NO_MEMORY
;
1234 r
->in
.req
.max_referral_level
= max_referral_level
;
1235 r
->in
.req
.servername
= talloc_strdup(r
, dfs_path
);
1236 if (r
->in
.req
.servername
== NULL
) {
1238 *pstatus
= NT_STATUS_NO_MEMORY
;
1242 status
= SMB_VFS_GET_DFS_REFERRALS(orig_conn
, r
);
1243 if (!NT_STATUS_IS_OK(status
)) {
1249 ndr_err
= ndr_push_struct_blob(&blob
, r
,
1251 (ndr_push_flags_fn_t
)ndr_push_dfs_referral_resp
);
1252 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
1254 *pstatus
= NT_STATUS_INVALID_PARAMETER
;
1258 pdata
= (char *)SMB_REALLOC(pdata
, blob
.length
);
1261 DEBUG(0,("referral setup:"
1262 "malloc failed for Realloc!\n"));
1266 reply_size
= blob
.length
;
1267 memcpy(pdata
, blob
.data
, blob
.length
);
1270 *pstatus
= NT_STATUS_OK
;
1274 /**********************************************************************
1275 The following functions are called by the NETDFS RPC pipe functions
1276 **********************************************************************/
1278 /*********************************************************************
1279 Creates a junction structure from a DFS pathname
1280 **********************************************************************/
1282 bool create_junction(TALLOC_CTX
*ctx
,
1283 const char *dfs_path
,
1284 bool allow_broken_path
,
1285 struct junction_map
*jucn
)
1287 const struct loadparm_substitution
*lp_sub
=
1288 loadparm_s3_global_substitution();
1290 struct dfs_path
*pdp
= talloc(ctx
,struct dfs_path
);
1296 status
= parse_dfs_path(NULL
, dfs_path
, False
, allow_broken_path
, pdp
);
1297 if (!NT_STATUS_IS_OK(status
)) {
1301 /* check if path is dfs : validate first token */
1302 if (!is_myname_or_ipaddr(pdp
->hostname
)) {
1303 DEBUG(4,("create_junction: Invalid hostname %s "
1305 pdp
->hostname
, dfs_path
));
1310 /* Check for a non-DFS share */
1311 snum
= lp_servicenumber(pdp
->servicename
);
1313 if(snum
< 0 || !lp_msdfs_root(snum
)) {
1314 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1320 jucn
->service_name
= talloc_strdup(ctx
, pdp
->servicename
);
1321 jucn
->volume_name
= talloc_strdup(ctx
, pdp
->reqpath
);
1322 jucn
->comment
= lp_comment(ctx
, lp_sub
, snum
);
1325 if (!jucn
->service_name
|| !jucn
->volume_name
|| ! jucn
->comment
) {
1331 /**********************************************************************
1332 Forms a valid Unix pathname from the junction
1333 **********************************************************************/
1335 static bool junction_to_local_path_tos(const struct junction_map
*jucn
,
1336 struct auth_session_info
*session_info
,
1338 connection_struct
**conn_out
)
1340 const struct loadparm_substitution
*lp_sub
=
1341 loadparm_s3_global_substitution();
1342 struct conn_struct_tos
*c
= NULL
;
1344 char *path_out
= NULL
;
1347 snum
= lp_servicenumber(jucn
->service_name
);
1351 status
= create_conn_struct_tos_cwd(global_messaging_context(),
1353 lp_path(talloc_tos(), lp_sub
, snum
),
1356 if (!NT_STATUS_IS_OK(status
)) {
1360 path_out
= talloc_asprintf(c
,
1362 lp_path(talloc_tos(), lp_sub
, snum
),
1364 if (path_out
== NULL
) {
1368 *pp_path_out
= path_out
;
1369 *conn_out
= c
->conn
;
1374 * Create a msdfs string in Samba format we can store
1375 * in a filesystem object (currently a symlink).
1378 char *msdfs_link_string(TALLOC_CTX
*ctx
,
1379 const struct referral
*reflist
,
1380 size_t referral_count
)
1382 char *refpath
= NULL
;
1383 bool insert_comma
= false;
1384 char *msdfs_link
= NULL
;
1387 /* Form the msdfs_link contents */
1388 msdfs_link
= talloc_strdup(ctx
, "msdfs:");
1389 if (msdfs_link
== NULL
) {
1393 for( i
= 0; i
< referral_count
; i
++) {
1394 refpath
= talloc_strdup(ctx
, reflist
[i
].alternate_path
);
1396 if (refpath
== NULL
) {
1400 /* Alternate paths always use Windows separators. */
1401 trim_char(refpath
, '\\', '\\');
1402 if (*refpath
== '\0') {
1404 insert_comma
= false;
1408 if (i
> 0 && insert_comma
) {
1409 msdfs_link
= talloc_asprintf_append_buffer(msdfs_link
,
1413 msdfs_link
= talloc_asprintf_append_buffer(msdfs_link
,
1418 if (msdfs_link
== NULL
) {
1422 if (!insert_comma
) {
1423 insert_comma
= true;
1426 TALLOC_FREE(refpath
);
1433 TALLOC_FREE(refpath
);
1434 TALLOC_FREE(msdfs_link
);
1438 bool create_msdfs_link(const struct junction_map
*jucn
,
1439 struct auth_session_info
*session_info
)
1441 TALLOC_CTX
*frame
= talloc_stackframe();
1443 connection_struct
*conn
;
1444 struct smb_filename
*smb_fname
= NULL
;
1445 struct smb_filename
*parent_fname
= NULL
;
1446 struct smb_filename
*at_fname
= NULL
;
1451 ok
= junction_to_local_path_tos(jucn
, session_info
, &path
, &conn
);
1456 if (!CAN_WRITE(conn
)) {
1457 const struct loadparm_substitution
*lp_sub
=
1458 loadparm_s3_global_substitution();
1459 int snum
= lp_servicenumber(jucn
->service_name
);
1461 DBG_WARNING("Can't create DFS entry on read-only share %s\n",
1462 lp_servicename(frame
, lp_sub
, snum
));
1466 smb_fname
= synthetic_smb_fname(frame
,
1472 if (smb_fname
== NULL
) {
1476 status
= parent_pathref(frame
,
1481 if (!NT_STATUS_IS_OK(status
)) {
1485 status
= SMB_VFS_CREATE_DFS_PATHAT(conn
,
1488 jucn
->referral_list
,
1489 jucn
->referral_count
);
1490 if (!NT_STATUS_IS_OK(status
)) {
1491 if (NT_STATUS_EQUAL(status
, NT_STATUS_OBJECT_NAME_COLLISION
)) {
1492 int retval
= SMB_VFS_UNLINKAT(conn
,
1500 status
= SMB_VFS_CREATE_DFS_PATHAT(conn
,
1503 jucn
->referral_list
,
1504 jucn
->referral_count
);
1505 if (!NT_STATUS_IS_OK(status
)) {
1506 DBG_WARNING("SMB_VFS_CREATE_DFS_PATHAT failed "
1521 bool remove_msdfs_link(const struct junction_map
*jucn
,
1522 struct auth_session_info
*session_info
)
1524 TALLOC_CTX
*frame
= talloc_stackframe();
1526 connection_struct
*conn
;
1528 struct smb_filename
*smb_fname
;
1529 struct smb_filename
*parent_fname
= NULL
;
1530 struct smb_filename
*at_fname
= NULL
;
1535 ok
= junction_to_local_path_tos(jucn
, session_info
, &path
, &conn
);
1541 if (!CAN_WRITE(conn
)) {
1542 const struct loadparm_substitution
*lp_sub
=
1543 loadparm_s3_global_substitution();
1544 int snum
= lp_servicenumber(jucn
->service_name
);
1546 DBG_WARNING("Can't remove DFS entry on read-only share %s\n",
1547 lp_servicename(frame
, lp_sub
, snum
));
1552 smb_fname
= synthetic_smb_fname(frame
,
1558 if (smb_fname
== NULL
) {
1564 status
= parent_pathref(frame
,
1569 if (!NT_STATUS_IS_OK(status
)) {
1574 retval
= SMB_VFS_UNLINKAT(conn
,
1586 /*********************************************************************
1587 Return the number of DFS links at the root of this share.
1588 *********************************************************************/
1590 static size_t count_dfs_links(TALLOC_CTX
*ctx
,
1591 struct auth_session_info
*session_info
,
1594 TALLOC_CTX
*frame
= talloc_stackframe();
1595 const struct loadparm_substitution
*lp_sub
=
1596 loadparm_s3_global_substitution();
1598 const char *dname
= NULL
;
1599 char *talloced
= NULL
;
1600 const char *connect_path
= lp_path(frame
, lp_sub
, snum
);
1601 const char *msdfs_proxy
= lp_msdfs_proxy(frame
, lp_sub
, snum
);
1602 struct conn_struct_tos
*c
= NULL
;
1603 connection_struct
*conn
= NULL
;
1605 struct smb_filename
*smb_fname
= NULL
;
1606 struct smb_Dir
*dir_hnd
= NULL
;
1609 if(*connect_path
== '\0') {
1615 * Fake up a connection struct for the VFS layer.
1618 status
= create_conn_struct_tos_cwd(global_messaging_context(),
1623 if (!NT_STATUS_IS_OK(status
)) {
1624 DEBUG(3, ("create_conn_struct failed: %s\n",
1625 nt_errstr(status
)));
1631 /* Count a link for the msdfs root - convention */
1634 /* No more links if this is an msdfs proxy. */
1635 if (*msdfs_proxy
!= '\0') {
1639 smb_fname
= synthetic_smb_fname(frame
,
1645 if (smb_fname
== NULL
) {
1649 /* Now enumerate all dfs links */
1650 status
= OpenDir(frame
,
1656 if (!NT_STATUS_IS_OK(status
)) {
1657 errno
= map_errno_from_nt_status(status
);
1661 while ((dname
= ReadDirName(dir_hnd
, &offset
, NULL
, &talloced
))
1664 struct smb_filename
*smb_dname
=
1665 synthetic_smb_fname(frame
,
1671 if (smb_dname
== NULL
) {
1674 if (is_msdfs_link(dir_hnd_fetch_fsp(dir_hnd
), smb_dname
)) {
1675 if (cnt
+ 1 < cnt
) {
1681 TALLOC_FREE(talloced
);
1682 TALLOC_FREE(smb_dname
);
1690 /*********************************************************************
1691 *********************************************************************/
1693 static int form_junctions(TALLOC_CTX
*ctx
,
1694 struct auth_session_info
*session_info
,
1696 struct junction_map
*jucn
,
1699 TALLOC_CTX
*frame
= talloc_stackframe();
1700 const struct loadparm_substitution
*lp_sub
=
1701 loadparm_s3_global_substitution();
1703 const char *dname
= NULL
;
1704 char *talloced
= NULL
;
1705 const char *connect_path
= lp_path(frame
, lp_sub
, snum
);
1706 char *service_name
= lp_servicename(frame
, lp_sub
, snum
);
1707 const char *msdfs_proxy
= lp_msdfs_proxy(frame
, lp_sub
, snum
);
1708 struct conn_struct_tos
*c
= NULL
;
1709 connection_struct
*conn
= NULL
;
1710 struct referral
*ref
= NULL
;
1711 struct smb_filename
*smb_fname
= NULL
;
1712 struct smb_Dir
*dir_hnd
= NULL
;
1716 if (jn_remain
== 0) {
1721 if(*connect_path
== '\0') {
1727 * Fake up a connection struct for the VFS layer.
1730 status
= create_conn_struct_tos_cwd(global_messaging_context(),
1735 if (!NT_STATUS_IS_OK(status
)) {
1736 DEBUG(3, ("create_conn_struct failed: %s\n",
1737 nt_errstr(status
)));
1743 /* form a junction for the msdfs root - convention
1744 DO NOT REMOVE THIS: NT clients will not work with us
1745 if this is not present
1747 jucn
[cnt
].service_name
= talloc_strdup(ctx
,service_name
);
1748 jucn
[cnt
].volume_name
= talloc_strdup(ctx
, "");
1749 if (!jucn
[cnt
].service_name
|| !jucn
[cnt
].volume_name
) {
1752 jucn
[cnt
].comment
= "";
1753 jucn
[cnt
].referral_count
= 1;
1755 ref
= jucn
[cnt
].referral_list
= talloc_zero(ctx
, struct referral
);
1756 if (jucn
[cnt
].referral_list
== NULL
) {
1761 ref
->ttl
= REFERRAL_TTL
;
1762 if (*msdfs_proxy
!= '\0') {
1763 ref
->alternate_path
= talloc_strdup(ctx
,
1766 ref
->alternate_path
= talloc_asprintf(ctx
,
1768 get_local_machine_name(),
1772 if (!ref
->alternate_path
) {
1777 /* Don't enumerate if we're an msdfs proxy. */
1778 if (*msdfs_proxy
!= '\0') {
1782 smb_fname
= synthetic_smb_fname(frame
,
1788 if (smb_fname
== NULL
) {
1792 /* Now enumerate all dfs links */
1793 status
= OpenDir(frame
,
1799 if (!NT_STATUS_IS_OK(status
)) {
1800 errno
= map_errno_from_nt_status(status
);
1804 while ((dname
= ReadDirName(dir_hnd
, &offset
, NULL
, &talloced
))
1807 struct smb_filename
*smb_dname
= NULL
;
1809 if (cnt
>= jn_remain
) {
1810 DEBUG(2, ("form_junctions: ran out of MSDFS "
1812 TALLOC_FREE(talloced
);
1815 smb_dname
= synthetic_smb_fname(talloc_tos(),
1821 if (smb_dname
== NULL
) {
1822 TALLOC_FREE(talloced
);
1826 status
= SMB_VFS_READ_DFS_PATHAT(conn
,
1830 &jucn
[cnt
].referral_list
,
1831 &jucn
[cnt
].referral_count
);
1833 if (NT_STATUS_IS_OK(status
)) {
1834 jucn
[cnt
].service_name
= talloc_strdup(ctx
,
1836 jucn
[cnt
].volume_name
= talloc_strdup(ctx
, dname
);
1837 if (!jucn
[cnt
].service_name
|| !jucn
[cnt
].volume_name
) {
1838 TALLOC_FREE(talloced
);
1841 jucn
[cnt
].comment
= "";
1844 TALLOC_FREE(talloced
);
1845 TALLOC_FREE(smb_dname
);
1853 struct junction_map
*enum_msdfs_links(TALLOC_CTX
*ctx
,
1854 struct auth_session_info
*session_info
,
1857 struct junction_map
*jn
= NULL
;
1859 size_t jn_count
= 0;
1863 if(!lp_host_msdfs()) {
1867 /* Ensure all the usershares are loaded. */
1869 load_registry_shares();
1870 sharecount
= load_usershare_shares(NULL
, connections_snum_used
);
1873 for(i
=0;i
< sharecount
;i
++) {
1874 if(lp_msdfs_root(i
)) {
1875 jn_count
+= count_dfs_links(ctx
, session_info
, i
);
1878 if (jn_count
== 0) {
1881 jn
= talloc_array(ctx
, struct junction_map
, jn_count
);
1885 for(i
=0; i
< sharecount
; i
++) {
1886 if (*p_num_jn
>= jn_count
) {
1889 if(lp_msdfs_root(i
)) {
1890 *p_num_jn
+= form_junctions(ctx
,
1894 jn_count
- *p_num_jn
);