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"
38 #include "source3/smbd/dir.h"
40 /**********************************************************************
41 Parse a DFS pathname of the form(s)
43 \hostname\service - self referral
44 \hostname\service\remainingpath - Windows referral path
46 FIXME! Should we also parse:
47 \hostname\service/remainingpath - POSIX referral path
48 as currently nothing uses this ?
50 into the dfs_path components. Strict form.
52 Checks DFS path starts with separator.
53 Checks hostname is ours.
54 Ensures servicename (share) is sent, and
55 if so, terminates the name or is followed by
58 If returned, remainingpath is untouched. Caller must call
59 check_path_syntax() on it.
61 Called by all non-fileserver processing (DFS RPC, FSCTL_DFS_GET_REFERRALS)
62 etc. Errors out on any inconsistency in the path.
63 **********************************************************************/
65 static NTSTATUS
parse_dfs_path_strict(TALLOC_CTX
*ctx
,
69 char **_remaining_path
)
71 char *pathname_local
= NULL
;
73 const char *hostname
= NULL
;
74 const char *servicename
= NULL
;
75 const char *reqpath
= NULL
;
76 bool my_hostname
= false;
79 DBG_DEBUG("path = |%s|\n", pathname
);
81 pathname_local
= talloc_strdup(talloc_tos(), pathname
);
82 if (pathname_local
== NULL
) {
83 return NT_STATUS_NO_MEMORY
;
86 * parse_dfs_path_strict() is called from
87 * get_referred_path() and create_junction()
88 * which use Windows DFS paths of \server\share.
92 * Strict DFS paths *must* start with the
93 * path separator '\\'.
96 if (pathname_local
[0] != '\\') {
97 DBG_ERR("path %s doesn't start with \\\n",
99 status
= NT_STATUS_NOT_FOUND
;
104 /* Parse out hostname. */
105 p
= strchr(pathname_local
+ 1, '\\');
107 DBG_ERR("can't parse hostname from path %s\n",
109 status
= NT_STATUS_NOT_FOUND
;
113 hostname
= &pathname_local
[1];
115 DBG_DEBUG("hostname: %s\n", hostname
);
117 /* Is this really our hostname ? */
118 my_hostname
= is_myname_or_ipaddr(hostname
);
120 DBG_ERR("Hostname %s is not ours.\n",
122 status
= NT_STATUS_NOT_FOUND
;
129 * Find the end of servicename by looking for
130 * a directory separator character. The character
131 * should be '\\' for a Windows path.
132 * If there is no separator, then this is a self-referral
133 * of "\server\share".
136 p
= strchr(servicename
, '\\');
141 DBG_DEBUG("servicename: %s\n", servicename
);
144 /* Client sent self referral "\server\share". */
147 /* Step past the '\0' we just replaced '\\' with. */
151 DBG_DEBUG("rest of the path: %s\n", reqpath
);
153 if (_hostname
!= NULL
) {
154 *_hostname
= talloc_strdup(ctx
, hostname
);
155 if (*_hostname
== NULL
) {
156 status
= NT_STATUS_NO_MEMORY
;
160 if (_servicename
!= NULL
) {
161 *_servicename
= talloc_strdup(ctx
, servicename
);
162 if (*_servicename
== NULL
) {
163 status
= NT_STATUS_NO_MEMORY
;
167 if (_remaining_path
!= NULL
) {
168 *_remaining_path
= talloc_strdup(ctx
, reqpath
);
169 if (*_remaining_path
== NULL
) {
170 status
= NT_STATUS_NO_MEMORY
;
175 status
= NT_STATUS_OK
;
177 TALLOC_FREE(pathname_local
);
181 /********************************************************
182 Fake up a connection struct for the VFS layer, for use in
183 applications (such as the python bindings), that do not want the
184 global working directory changed under them.
186 SMB_VFS_CONNECT requires root privileges.
187 *********************************************************/
189 static NTSTATUS
create_conn_struct_as_root(TALLOC_CTX
*ctx
,
190 struct tevent_context
*ev
,
191 struct messaging_context
*msg
,
192 connection_struct
**pconn
,
195 const struct auth_session_info
*session_info
)
197 connection_struct
*conn
;
199 const char *vfs_user
;
200 struct smbd_server_connection
*sconn
;
201 const char *servicename
= lp_const_servicename(snum
);
204 sconn
= talloc_zero(ctx
, struct smbd_server_connection
);
206 return NT_STATUS_NO_MEMORY
;
210 sconn
->msg_ctx
= msg
;
212 conn
= conn_new(sconn
);
215 return NT_STATUS_NO_MEMORY
;
218 /* Now we have conn, we need to make sconn a child of conn,
219 * for a proper talloc tree */
220 talloc_steal(conn
, sconn
);
222 if (snum
== -1 && servicename
== NULL
) {
223 servicename
= "Unknown Service (snum == -1)";
226 connpath
= talloc_strdup(conn
, path
);
229 return NT_STATUS_NO_MEMORY
;
231 connpath
= talloc_string_sub(conn
,
237 return NT_STATUS_NO_MEMORY
;
240 /* needed for smbd_vfs_init() */
242 conn
->params
->service
= snum
;
243 conn
->cnum
= TID_FIELD_INVALID
;
245 SMB_ASSERT(session_info
!= NULL
);
247 conn
->session_info
= copy_session_info(conn
, session_info
);
248 if (conn
->session_info
== NULL
) {
249 DBG_ERR("copy_serverinfo failed\n");
251 return NT_STATUS_NO_MEMORY
;
254 /* unix_info could be NULL in session_info */
255 if (conn
->session_info
->unix_info
!= NULL
) {
256 vfs_user
= conn
->session_info
->unix_info
->unix_name
;
258 vfs_user
= get_current_username();
261 conn_setup_case_options(conn
);
263 set_conn_connectpath(conn
, connpath
);
266 * New code to check if there's a share security descriptor
267 * added from NT server manager. This is done after the
268 * smb.conf checks are done as we need a uid and token. JRA.
271 share_access_check(conn
->session_info
->security_token
,
273 MAXIMUM_ALLOWED_ACCESS
,
274 &conn
->share_access
);
276 if ((conn
->share_access
& FILE_WRITE_DATA
) == 0) {
277 if ((conn
->share_access
& FILE_READ_DATA
) == 0) {
278 /* No access, read or write. */
279 DBG_WARNING("connection to %s "
280 "denied due to security "
284 return NT_STATUS_ACCESS_DENIED
;
286 conn
->read_only
= true;
289 if (!smbd_vfs_init(conn
)) {
290 NTSTATUS status
= map_nt_error_from_unix(errno
);
291 DEBUG(0,("create_conn_struct: smbd_vfs_init failed.\n"));
296 /* this must be the first filesystem operation that we do */
297 if (SMB_VFS_CONNECT(conn
, servicename
, vfs_user
) < 0) {
298 DEBUG(0,("VFS connect failed!\n"));
300 return NT_STATUS_UNSUCCESSFUL
;
303 ok
= canonicalize_connect_path(conn
);
305 DBG_ERR("Failed to canonicalize sharepath\n");
307 return NT_STATUS_ACCESS_DENIED
;
310 conn
->fs_capabilities
= SMB_VFS_FS_CAPABILITIES(conn
, &conn
->ts_res
);
311 conn
->tcon_done
= true;
312 *pconn
= talloc_move(ctx
, &conn
);
317 static int conn_struct_tos_destructor(struct conn_struct_tos
*c
)
319 if (c
->oldcwd_fname
!= NULL
) {
320 vfs_ChDir(c
->conn
, c
->oldcwd_fname
);
321 TALLOC_FREE(c
->oldcwd_fname
);
323 SMB_VFS_DISCONNECT(c
->conn
);
328 /********************************************************
329 Fake up a connection struct for the VFS layer, for use in
330 applications (such as the python bindings), that do not want the
331 global working directory changed under them.
333 SMB_VFS_CONNECT requires root privileges.
334 This temporary uses become_root() and unbecome_root().
336 But further impersonation has to be done by the caller.
337 *********************************************************/
338 NTSTATUS
create_conn_struct_tos(struct messaging_context
*msg
,
341 const struct auth_session_info
*session_info
,
342 struct conn_struct_tos
**_c
)
344 struct conn_struct_tos
*c
= NULL
;
345 struct tevent_context
*ev
= NULL
;
350 c
= talloc_zero(talloc_tos(), struct conn_struct_tos
);
352 return NT_STATUS_NO_MEMORY
;
355 ev
= samba_tevent_context_init(c
);
358 return NT_STATUS_NO_MEMORY
;
362 status
= create_conn_struct_as_root(c
,
370 if (!NT_STATUS_IS_OK(status
)) {
375 talloc_set_destructor(c
, conn_struct_tos_destructor
);
381 /********************************************************
382 Fake up a connection struct for the VFS layer.
383 Note: this performs a vfs connect and CHANGES CWD !!!! JRA.
385 See also the comment for create_conn_struct_tos() above!
387 The CWD change is reverted by the destructor of
388 conn_struct_tos when the current talloc_tos() is destroyed.
389 *********************************************************/
390 NTSTATUS
create_conn_struct_tos_cwd(struct messaging_context
*msg
,
393 const struct auth_session_info
*session_info
,
394 struct conn_struct_tos
**_c
)
396 struct conn_struct_tos
*c
= NULL
;
397 struct smb_filename smb_fname_connectpath
= {0};
402 status
= create_conn_struct_tos(msg
,
407 if (!NT_STATUS_IS_OK(status
)) {
412 * Windows seems to insist on doing trans2getdfsreferral() calls on
413 * the IPC$ share as the anonymous user. If we try to chdir as that
414 * user we will fail.... WTF ? JRA.
417 c
->oldcwd_fname
= vfs_GetWd(c
, c
->conn
);
418 if (c
->oldcwd_fname
== NULL
) {
419 status
= map_nt_error_from_unix(errno
);
420 DEBUG(3, ("vfs_GetWd failed: %s\n", strerror(errno
)));
425 smb_fname_connectpath
= (struct smb_filename
) {
426 .base_name
= c
->conn
->connectpath
429 if (vfs_ChDir(c
->conn
, &smb_fname_connectpath
) != 0) {
430 status
= map_nt_error_from_unix(errno
);
431 DBG_NOTICE("Can't ChDir to new conn path %s. "
433 c
->conn
->connectpath
, strerror(errno
));
434 TALLOC_FREE(c
->oldcwd_fname
);
443 /********************************************************
444 Fake up a connection struct for the VFS layer.
445 This takes an TALLOC_CTX and tevent_context from the
446 caller and the resulting connection_struct is stable
447 across the lifetime of mem_ctx and ev.
449 Note: this performs a vfs connect and changes cwd.
451 See also the comment for create_conn_struct_tos() above!
452 *********************************************************/
454 NTSTATUS
create_conn_struct_cwd(TALLOC_CTX
*mem_ctx
,
455 struct tevent_context
*ev
,
456 struct messaging_context
*msg
,
457 const struct auth_session_info
*session_info
,
460 struct connection_struct
**c
)
465 status
= create_conn_struct_as_root(mem_ctx
,
476 static void shuffle_strlist(char **list
, int count
)
482 for (i
= count
; i
> 1; i
--) {
483 r
= generate_random() % i
;
491 /**********************************************************************
492 Parse the contents of a symlink to verify if it is an msdfs referral
493 A valid referral is of the form:
495 msdfs:server1\share1,server2\share2
496 msdfs:server1\share1\pathname,server2\share2\pathname
497 msdfs:server1/share1,server2/share2
498 msdfs:server1/share1/pathname,server2/share2/pathname.
500 Note that the alternate paths returned here must be of the canonicalized
504 \server\share\path\to\file,
506 even in posix path mode. This is because we have no knowledge if the
507 server we're referring to understands posix paths.
508 **********************************************************************/
510 bool parse_msdfs_symlink(TALLOC_CTX
*ctx
,
511 bool shuffle_referrals
,
513 struct referral
**ppreflist
,
518 char **alt_path
= NULL
;
520 struct referral
*reflist
= NULL
;
523 temp
= talloc_strdup(ctx
, target
);
527 prot
= strtok_r(temp
, ":", &saveptr
);
529 DEBUG(0,("parse_msdfs_symlink: invalid path !\n"));
534 alt_path
= talloc_array(ctx
, char *, MAX_REFERRAL_COUNT
);
540 /* parse out the alternate paths */
541 while((count
<MAX_REFERRAL_COUNT
) &&
542 ((alt_path
[count
] = strtok_r(NULL
, ",", &saveptr
)) != NULL
)) {
546 /* shuffle alternate paths */
547 if (shuffle_referrals
) {
548 shuffle_strlist(alt_path
, count
);
551 DBG_DEBUG("count=%zu\n", count
);
554 reflist
= talloc_zero_array(ctx
,
555 struct referral
, count
);
556 if(reflist
== NULL
) {
558 TALLOC_FREE(alt_path
);
565 for(i
=0;i
<count
;i
++) {
568 /* Canonicalize link target.
569 * Replace all /'s in the path by a \ */
570 string_replace(alt_path
[i
], '/', '\\');
572 /* Remove leading '\\'s */
574 while (*p
&& (*p
== '\\')) {
578 reflist
[i
].alternate_path
= talloc_asprintf(reflist
,
581 if (!reflist
[i
].alternate_path
) {
583 TALLOC_FREE(alt_path
);
584 TALLOC_FREE(reflist
);
588 reflist
[i
].proximity
= 0;
589 reflist
[i
].ttl
= REFERRAL_TTL
;
590 DBG_DEBUG("Created alt path: %s\n",
591 reflist
[i
].alternate_path
);
594 if (ppreflist
!= NULL
) {
595 *ppreflist
= reflist
;
597 TALLOC_FREE(reflist
);
599 if (prefcount
!= NULL
) {
603 TALLOC_FREE(alt_path
);
607 /**********************************************************************
608 Returns true if the unix path is a valid msdfs symlink.
609 **********************************************************************/
611 bool is_msdfs_link(struct files_struct
*dirfsp
,
612 struct smb_filename
*atname
)
614 NTSTATUS status
= SMB_VFS_READ_DFS_PATHAT(dirfsp
->conn
,
620 return (NT_STATUS_IS_OK(status
));
623 /*****************************************************************
624 Used by other functions to decide if a dfs path is remote,
625 and to get the list of referred locations for that remote path.
627 consumedcntp: how much of the dfs path is being redirected. the client
628 should try the remaining path on the redirected server.
629 *****************************************************************/
631 static NTSTATUS
dfs_path_lookup(TALLOC_CTX
*ctx
,
632 connection_struct
*conn
,
633 const char *dfspath
, /* Incoming complete dfs path */
634 const char *reqpath
, /* Parsed out remaining path. */
636 size_t *consumedcntp
,
637 struct referral
**ppreflist
,
638 size_t *preferral_count
)
641 struct smb_filename
*parent_smb_fname
= NULL
;
642 struct smb_filename
*smb_fname_rel
= NULL
;
644 char *local_pathname
= NULL
;
645 char *last_component
= NULL
;
647 size_t removed_components
= 0;
648 bool posix
= (ucf_flags
& UCF_POSIX_PATHNAMES
);
650 char *canon_dfspath
= NULL
;
652 DBG_DEBUG("Conn path = %s reqpath = %s\n", conn
->connectpath
, reqpath
);
654 local_pathname
= talloc_strdup(ctx
, reqpath
);
655 if (local_pathname
== NULL
) {
656 status
= NT_STATUS_NO_MEMORY
;
660 /* We know reqpath isn't a DFS path. */
661 ucf_flags
&= ~UCF_DFS_PATHNAME
;
663 if (ucf_flags
& UCF_GMT_PATHNAME
) {
664 extract_snapshot_token(local_pathname
, &twrp
);
665 ucf_flags
&= ~UCF_GMT_PATHNAME
;
669 * We should have been given a DFS path to resolve.
670 * This should return NT_STATUS_PATH_NOT_COVERED.
672 * Do a pathname walk, stripping off components
673 * until we get NT_STATUS_OK instead of
674 * NT_STATUS_PATH_NOT_COVERED.
676 * Fail on any other error.
680 TALLOC_CTX
*frame
= NULL
;
681 struct files_struct
*dirfsp
= NULL
;
682 struct smb_filename
*smb_fname_walk
= NULL
;
684 TALLOC_FREE(parent_smb_fname
);
687 * Use a local stackframe as filename_convert_dirfsp()
688 * opens handles on the last two components in the path.
689 * Allow these to be freed as we step back through
690 * the local_pathname.
692 frame
= talloc_stackframe();
693 status
= filename_convert_dirfsp(frame
,
700 /* If we got a name, save it. */
701 if (smb_fname_walk
!= NULL
) {
702 parent_smb_fname
= talloc_move(ctx
, &smb_fname_walk
);
706 if (!NT_STATUS_EQUAL(status
, NT_STATUS_PATH_NOT_COVERED
)) {
708 * For any other status than NT_STATUS_PATH_NOT_COVERED
709 * (including NT_STATUS_OK) we exit the walk.
710 * If it's an error we catch it outside the loop.
715 /* Step back one component and save it off as last_component. */
716 TALLOC_FREE(last_component
);
717 p
= strrchr(local_pathname
, '/');
720 * We removed all components.
721 * Go around once more to make
722 * sure we can open the root '\0'.
724 last_component
= talloc_strdup(ctx
, local_pathname
);
725 *local_pathname
= '\0';
727 last_component
= talloc_strdup(ctx
, p
+1);
730 if (last_component
== NULL
) {
731 status
= NT_STATUS_NO_MEMORY
;
734 /* Integer wrap check. */
735 if (removed_components
+ 1 < removed_components
) {
736 status
= NT_STATUS_INVALID_PARAMETER
;
739 removed_components
++;
742 if (!NT_STATUS_IS_OK(status
)) {
743 DBG_DEBUG("dfspath = %s. reqpath = %s. Error %s.\n",
750 if (parent_smb_fname
->fsp
== NULL
) {
751 /* Unable to open parent. */
752 DBG_DEBUG("dfspath = %s. reqpath = %s. "
753 "Unable to open parent directory (%s).\n",
756 smb_fname_str_dbg(parent_smb_fname
));
757 status
= NT_STATUS_OBJECT_PATH_NOT_FOUND
;
761 if (removed_components
== 0) {
763 * We never got NT_STATUS_PATH_NOT_COVERED.
764 * There was no DFS redirect.
766 DBG_DEBUG("dfspath = %s. reqpath = %s. "
767 "No removed components.\n",
770 status
= NT_STATUS_OBJECT_PATH_NOT_FOUND
;
775 * One of the removed_components was the MSDFS link
776 * at the end. We need to count this in the resolved
777 * path below, so remove one from removed_components.
779 removed_components
--;
782 * Now parent_smb_fname->fsp is the parent directory dirfsp,
783 * last_component is the untranslated MS-DFS link name.
784 * Search for it in the parent directory to get the real
787 status
= get_real_filename_at(parent_smb_fname
->fsp
,
792 if (!NT_STATUS_IS_OK(status
)) {
793 DBG_DEBUG("dfspath = %s. reqpath = %s "
794 "get_real_filename_at(%s, %s) error (%s)\n",
797 smb_fname_str_dbg(parent_smb_fname
),
803 smb_fname_rel
= synthetic_smb_fname(ctx
,
808 posix
? SMB_FILENAME_POSIX_PATH
: 0);
809 if (smb_fname_rel
== NULL
) {
810 status
= NT_STATUS_NO_MEMORY
;
814 /* Get the referral to return. */
815 status
= SMB_VFS_READ_DFS_PATHAT(conn
,
817 parent_smb_fname
->fsp
,
821 if (!NT_STATUS_IS_OK(status
)) {
822 DBG_DEBUG("dfspath = %s. reqpath = %s. "
823 "SMB_VFS_READ_DFS_PATHAT(%s, %s) error (%s)\n",
826 smb_fname_str_dbg(parent_smb_fname
),
827 smb_fname_str_dbg(smb_fname_rel
),
833 * Now we must work out how much of the
834 * given pathname we consumed.
836 canon_dfspath
= talloc_strdup(ctx
, dfspath
);
837 if (!canon_dfspath
) {
838 status
= NT_STATUS_NO_MEMORY
;
841 /* Canonicalize the raw dfspath. */
842 string_replace(canon_dfspath
, '\\', '/');
845 * reqpath comes out of parse_dfs_path(), so it has
846 * no trailing backslash. Make sure that canon_dfspath hasn't either.
848 trim_char(canon_dfspath
, 0, '/');
850 DBG_DEBUG("Unconsumed path: %s\n", canon_dfspath
);
852 while (removed_components
> 0) {
853 p
= strrchr(canon_dfspath
, '/');
857 removed_components
--;
858 if (p
== NULL
&& removed_components
!= 0) {
859 DBG_ERR("Component mismatch. path = %s, "
860 "%zu components left\n",
863 status
= NT_STATUS_OBJECT_PATH_NOT_FOUND
;
867 *consumedcntp
= strlen(canon_dfspath
);
868 DBG_DEBUG("Path consumed: %s (%zu)\n", canon_dfspath
, *consumedcntp
);
869 status
= NT_STATUS_OK
;
873 TALLOC_FREE(parent_smb_fname
);
874 TALLOC_FREE(local_pathname
);
875 TALLOC_FREE(last_component
);
877 TALLOC_FREE(smb_fname_rel
);
878 TALLOC_FREE(canon_dfspath
);
882 /**********************************************************************
883 Return a self referral.
884 **********************************************************************/
886 static NTSTATUS
self_ref(TALLOC_CTX
*ctx
,
887 const char *dfs_path
,
888 struct junction_map
*jucn
,
889 size_t *consumedcntp
,
890 bool *self_referralp
)
892 struct referral
*ref
;
894 *self_referralp
= True
;
896 jucn
->referral_count
= 1;
897 if((ref
= talloc_zero(ctx
, struct referral
)) == NULL
) {
898 return NT_STATUS_NO_MEMORY
;
901 ref
->alternate_path
= talloc_strdup(ctx
, dfs_path
);
902 if (!ref
->alternate_path
) {
904 return NT_STATUS_NO_MEMORY
;
907 ref
->ttl
= REFERRAL_TTL
;
908 jucn
->referral_list
= ref
;
909 *consumedcntp
= strlen(dfs_path
);
913 /**********************************************************************
914 Gets valid referrals for a dfs path and fills up the
915 junction_map structure.
916 **********************************************************************/
918 NTSTATUS
get_referred_path(TALLOC_CTX
*ctx
,
919 struct auth_session_info
*session_info
,
920 const char *dfs_path
,
921 const struct tsocket_address
*remote_address
,
922 const struct tsocket_address
*local_address
,
923 struct junction_map
*jucn
,
924 size_t *consumedcntp
,
925 bool *self_referralp
)
927 TALLOC_CTX
*frame
= talloc_stackframe();
928 const struct loadparm_substitution
*lp_sub
=
929 loadparm_s3_global_substitution();
930 struct conn_struct_tos
*c
= NULL
;
931 struct connection_struct
*conn
= NULL
;
932 char *servicename
= NULL
;
933 char *reqpath
= NULL
;
935 NTSTATUS status
= NT_STATUS_NOT_FOUND
;
937 *self_referralp
= False
;
939 status
= parse_dfs_path_strict(
945 if (!NT_STATUS_IS_OK(status
)) {
950 /* Path referrals are always non-POSIX. */
951 status
= check_path_syntax(reqpath
, false);
952 if (!NT_STATUS_IS_OK(status
)) {
957 jucn
->service_name
= talloc_strdup(ctx
, servicename
);
958 jucn
->volume_name
= talloc_strdup(ctx
, reqpath
);
959 if (!jucn
->service_name
|| !jucn
->volume_name
) {
961 return NT_STATUS_NO_MEMORY
;
964 /* Verify the share is a dfs root */
965 snum
= lp_servicenumber(jucn
->service_name
);
967 char *service_name
= NULL
;
968 if ((snum
= find_service(ctx
, jucn
->service_name
, &service_name
)) < 0) {
970 return NT_STATUS_NOT_FOUND
;
974 return NT_STATUS_NO_MEMORY
;
976 TALLOC_FREE(jucn
->service_name
);
977 jucn
->service_name
= talloc_strdup(ctx
, service_name
);
978 if (!jucn
->service_name
) {
980 return NT_STATUS_NO_MEMORY
;
984 if (!lp_msdfs_root(snum
) && (*lp_msdfs_proxy(talloc_tos(), lp_sub
, snum
) == '\0')) {
985 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not "
987 servicename
, dfs_path
));
989 return NT_STATUS_NOT_FOUND
;
993 * Self referrals are tested with a anonymous IPC connection and
994 * a GET_DFS_REFERRAL call to \\server\share. (which means
995 * dp.reqpath[0] points to an empty string). create_conn_struct cd's
996 * into the directory and will fail if it cannot (as the anonymous
997 * user). Cope with this.
1000 if (reqpath
[0] == '\0') {
1002 struct referral
*ref
;
1005 if (*lp_msdfs_proxy(talloc_tos(), lp_sub
, snum
) == '\0') {
1007 return self_ref(ctx
,
1015 * It's an msdfs proxy share. Redirect to
1016 * the configured target share.
1019 tmp
= talloc_asprintf(frame
, "msdfs:%s",
1020 lp_msdfs_proxy(frame
, lp_sub
, snum
));
1023 return NT_STATUS_NO_MEMORY
;
1026 if (!parse_msdfs_symlink(ctx
,
1027 lp_msdfs_shuffle_referrals(snum
),
1032 return NT_STATUS_INVALID_PARAMETER
;
1034 jucn
->referral_count
= refcount
;
1035 jucn
->referral_list
= ref
;
1036 *consumedcntp
= strlen(dfs_path
);
1038 return NT_STATUS_OK
;
1041 status
= create_conn_struct_tos_cwd(global_messaging_context(),
1043 lp_path(frame
, lp_sub
, snum
),
1046 if (!NT_STATUS_IS_OK(status
)) {
1055 * The remote and local address should be passed down to
1056 * create_conn_struct_cwd.
1058 if (conn
->sconn
->remote_address
== NULL
) {
1059 conn
->sconn
->remote_address
=
1060 tsocket_address_copy(remote_address
, conn
->sconn
);
1061 if (conn
->sconn
->remote_address
== NULL
) {
1063 return NT_STATUS_NO_MEMORY
;
1066 if (conn
->sconn
->local_address
== NULL
) {
1067 conn
->sconn
->local_address
=
1068 tsocket_address_copy(local_address
, conn
->sconn
);
1069 if (conn
->sconn
->local_address
== NULL
) {
1071 return NT_STATUS_NO_MEMORY
;
1075 status
= dfs_path_lookup(ctx
,
1081 &jucn
->referral_list
,
1082 &jucn
->referral_count
);
1084 if (!NT_STATUS_IS_OK(status
)) {
1085 DBG_NOTICE("No valid referrals for path %s (%s)\n",
1094 /******************************************************************
1095 Set up the DFS referral for the dfs pathname. This call returns
1096 the amount of the path covered by this server, and where the
1097 client should be redirected to. This is the meat of the
1098 TRANS2_GET_DFS_REFERRAL call.
1099 ******************************************************************/
1101 int setup_dfs_referral(connection_struct
*orig_conn
,
1102 const char *dfs_path
,
1103 int max_referral_level
,
1104 char **ppdata
, NTSTATUS
*pstatus
)
1106 char *pdata
= *ppdata
;
1108 struct dfs_GetDFSReferral
*r
;
1109 DATA_BLOB blob
= data_blob_null
;
1111 enum ndr_err_code ndr_err
;
1113 r
= talloc_zero(talloc_tos(), struct dfs_GetDFSReferral
);
1115 *pstatus
= NT_STATUS_NO_MEMORY
;
1119 r
->in
.req
.max_referral_level
= max_referral_level
;
1120 r
->in
.req
.servername
= talloc_strdup(r
, dfs_path
);
1121 if (r
->in
.req
.servername
== NULL
) {
1123 *pstatus
= NT_STATUS_NO_MEMORY
;
1127 status
= SMB_VFS_GET_DFS_REFERRALS(orig_conn
, r
);
1128 if (!NT_STATUS_IS_OK(status
)) {
1134 ndr_err
= ndr_push_struct_blob(&blob
, r
,
1136 (ndr_push_flags_fn_t
)ndr_push_dfs_referral_resp
);
1137 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
1139 *pstatus
= NT_STATUS_INVALID_PARAMETER
;
1143 pdata
= (char *)SMB_REALLOC(pdata
, blob
.length
);
1146 DEBUG(0,("referral setup:"
1147 "malloc failed for Realloc!\n"));
1151 reply_size
= blob
.length
;
1152 memcpy(pdata
, blob
.data
, blob
.length
);
1155 *pstatus
= NT_STATUS_OK
;
1159 /**********************************************************************
1160 The following functions are called by the NETDFS RPC pipe functions
1161 **********************************************************************/
1163 /*********************************************************************
1164 Creates a junction structure from a DFS pathname
1165 **********************************************************************/
1167 bool create_junction(TALLOC_CTX
*ctx
,
1168 const char *dfs_path
,
1169 struct junction_map
*jucn
)
1171 const struct loadparm_substitution
*lp_sub
=
1172 loadparm_s3_global_substitution();
1174 char *servicename
= NULL
;
1175 char *reqpath
= NULL
;
1178 status
= parse_dfs_path_strict(
1184 if (!NT_STATUS_IS_OK(status
)) {
1188 /* Check for a non-DFS share */
1189 snum
= lp_servicenumber(servicename
);
1191 if(snum
< 0 || !lp_msdfs_root(snum
)) {
1192 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1197 /* Junction create paths are always non-POSIX. */
1198 status
= check_path_syntax(reqpath
, false);
1199 if (!NT_STATUS_IS_OK(status
)) {
1203 jucn
->service_name
= talloc_strdup(ctx
, servicename
);
1204 jucn
->volume_name
= talloc_strdup(ctx
, reqpath
);
1205 jucn
->comment
= lp_comment(ctx
, lp_sub
, snum
);
1207 if (!jucn
->service_name
|| !jucn
->volume_name
|| ! jucn
->comment
) {
1213 /**********************************************************************
1214 Forms a valid Unix pathname from the junction
1215 **********************************************************************/
1217 static bool junction_to_local_path_tos(const struct junction_map
*jucn
,
1218 struct auth_session_info
*session_info
,
1220 connection_struct
**conn_out
)
1222 const struct loadparm_substitution
*lp_sub
=
1223 loadparm_s3_global_substitution();
1224 struct conn_struct_tos
*c
= NULL
;
1226 char *path_out
= NULL
;
1229 snum
= lp_servicenumber(jucn
->service_name
);
1233 status
= create_conn_struct_tos_cwd(global_messaging_context(),
1235 lp_path(talloc_tos(), lp_sub
, snum
),
1238 if (!NT_STATUS_IS_OK(status
)) {
1242 path_out
= talloc_asprintf(c
,
1244 lp_path(talloc_tos(), lp_sub
, snum
),
1246 if (path_out
== NULL
) {
1250 *pp_path_out
= path_out
;
1251 *conn_out
= c
->conn
;
1256 * Create a msdfs string in Samba format we can store
1257 * in a filesystem object (currently a symlink).
1260 char *msdfs_link_string(TALLOC_CTX
*ctx
,
1261 const struct referral
*reflist
,
1262 size_t referral_count
)
1264 char *refpath
= NULL
;
1265 bool insert_comma
= false;
1266 char *msdfs_link
= NULL
;
1269 /* Form the msdfs_link contents */
1270 msdfs_link
= talloc_strdup(ctx
, "msdfs:");
1271 if (msdfs_link
== NULL
) {
1275 for( i
= 0; i
< referral_count
; i
++) {
1276 refpath
= talloc_strdup(ctx
, reflist
[i
].alternate_path
);
1278 if (refpath
== NULL
) {
1282 /* Alternate paths always use Windows separators. */
1283 trim_char(refpath
, '\\', '\\');
1284 if (*refpath
== '\0') {
1286 insert_comma
= false;
1290 if (i
> 0 && insert_comma
) {
1291 msdfs_link
= talloc_asprintf_append_buffer(msdfs_link
,
1295 msdfs_link
= talloc_asprintf_append_buffer(msdfs_link
,
1300 if (msdfs_link
== NULL
) {
1304 if (!insert_comma
) {
1305 insert_comma
= true;
1308 TALLOC_FREE(refpath
);
1315 TALLOC_FREE(refpath
);
1316 TALLOC_FREE(msdfs_link
);
1320 bool create_msdfs_link(const struct junction_map
*jucn
,
1321 struct auth_session_info
*session_info
)
1323 TALLOC_CTX
*frame
= talloc_stackframe();
1325 connection_struct
*conn
;
1326 struct smb_filename
*smb_fname
= NULL
;
1327 struct smb_filename
*parent_fname
= NULL
;
1328 struct smb_filename
*at_fname
= NULL
;
1333 ok
= junction_to_local_path_tos(jucn
, session_info
, &path
, &conn
);
1338 if (!CAN_WRITE(conn
)) {
1339 const struct loadparm_substitution
*lp_sub
=
1340 loadparm_s3_global_substitution();
1341 int snum
= lp_servicenumber(jucn
->service_name
);
1343 DBG_WARNING("Can't create DFS entry on read-only share %s\n",
1344 lp_servicename(frame
, lp_sub
, snum
));
1348 smb_fname
= synthetic_smb_fname(frame
,
1354 if (smb_fname
== NULL
) {
1358 status
= parent_pathref(frame
,
1363 if (!NT_STATUS_IS_OK(status
)) {
1367 status
= SMB_VFS_CREATE_DFS_PATHAT(conn
,
1370 jucn
->referral_list
,
1371 jucn
->referral_count
);
1372 if (!NT_STATUS_IS_OK(status
)) {
1373 if (NT_STATUS_EQUAL(status
, NT_STATUS_OBJECT_NAME_COLLISION
)) {
1374 int retval
= SMB_VFS_UNLINKAT(conn
,
1382 status
= SMB_VFS_CREATE_DFS_PATHAT(conn
,
1385 jucn
->referral_list
,
1386 jucn
->referral_count
);
1387 if (!NT_STATUS_IS_OK(status
)) {
1388 DBG_WARNING("SMB_VFS_CREATE_DFS_PATHAT failed "
1403 bool remove_msdfs_link(const struct junction_map
*jucn
,
1404 struct auth_session_info
*session_info
)
1406 TALLOC_CTX
*frame
= talloc_stackframe();
1408 connection_struct
*conn
;
1410 struct smb_filename
*smb_fname
;
1411 struct smb_filename
*parent_fname
= NULL
;
1412 struct smb_filename
*at_fname
= NULL
;
1417 ok
= junction_to_local_path_tos(jucn
, session_info
, &path
, &conn
);
1423 if (!CAN_WRITE(conn
)) {
1424 const struct loadparm_substitution
*lp_sub
=
1425 loadparm_s3_global_substitution();
1426 int snum
= lp_servicenumber(jucn
->service_name
);
1428 DBG_WARNING("Can't remove DFS entry on read-only share %s\n",
1429 lp_servicename(frame
, lp_sub
, snum
));
1434 smb_fname
= synthetic_smb_fname(frame
,
1440 if (smb_fname
== NULL
) {
1446 status
= parent_pathref(frame
,
1451 if (!NT_STATUS_IS_OK(status
)) {
1456 retval
= SMB_VFS_UNLINKAT(conn
,
1468 /*********************************************************************
1469 Return the number of DFS links at the root of this share.
1470 *********************************************************************/
1472 static size_t count_dfs_links(TALLOC_CTX
*ctx
,
1473 struct auth_session_info
*session_info
,
1476 TALLOC_CTX
*frame
= talloc_stackframe();
1477 const struct loadparm_substitution
*lp_sub
=
1478 loadparm_s3_global_substitution();
1480 const char *dname
= NULL
;
1481 char *talloced
= NULL
;
1482 const char *connect_path
= lp_path(frame
, lp_sub
, snum
);
1483 const char *msdfs_proxy
= lp_msdfs_proxy(frame
, lp_sub
, snum
);
1484 struct conn_struct_tos
*c
= NULL
;
1485 connection_struct
*conn
= NULL
;
1487 struct smb_filename
*smb_fname
= NULL
;
1488 struct smb_Dir
*dir_hnd
= NULL
;
1490 if(*connect_path
== '\0') {
1496 * Fake up a connection struct for the VFS layer.
1499 status
= create_conn_struct_tos_cwd(global_messaging_context(),
1504 if (!NT_STATUS_IS_OK(status
)) {
1505 DEBUG(3, ("create_conn_struct failed: %s\n",
1506 nt_errstr(status
)));
1512 /* Count a link for the msdfs root - convention */
1515 /* No more links if this is an msdfs proxy. */
1516 if (*msdfs_proxy
!= '\0') {
1520 smb_fname
= synthetic_smb_fname(frame
,
1526 if (smb_fname
== NULL
) {
1530 /* Now enumerate all dfs links */
1531 status
= OpenDir(frame
,
1537 if (!NT_STATUS_IS_OK(status
)) {
1538 errno
= map_errno_from_nt_status(status
);
1542 while ((dname
= ReadDirName(dir_hnd
, &talloced
)) != NULL
) {
1543 struct smb_filename
*smb_dname
=
1544 synthetic_smb_fname(frame
,
1550 if (smb_dname
== NULL
) {
1553 if (is_msdfs_link(dir_hnd_fetch_fsp(dir_hnd
), smb_dname
)) {
1554 if (cnt
+ 1 < cnt
) {
1560 TALLOC_FREE(talloced
);
1561 TALLOC_FREE(smb_dname
);
1569 /*********************************************************************
1570 *********************************************************************/
1572 static int form_junctions(TALLOC_CTX
*ctx
,
1573 struct auth_session_info
*session_info
,
1575 struct junction_map
*jucn
,
1578 TALLOC_CTX
*frame
= talloc_stackframe();
1579 const struct loadparm_substitution
*lp_sub
=
1580 loadparm_s3_global_substitution();
1582 const char *dname
= NULL
;
1583 char *talloced
= NULL
;
1584 const char *connect_path
= lp_path(frame
, lp_sub
, snum
);
1585 char *service_name
= lp_servicename(frame
, lp_sub
, snum
);
1586 const char *msdfs_proxy
= lp_msdfs_proxy(frame
, lp_sub
, snum
);
1587 struct conn_struct_tos
*c
= NULL
;
1588 connection_struct
*conn
= NULL
;
1589 struct referral
*ref
= NULL
;
1590 struct smb_filename
*smb_fname
= NULL
;
1591 struct smb_Dir
*dir_hnd
= NULL
;
1594 if (jn_remain
== 0) {
1599 if(*connect_path
== '\0') {
1605 * Fake up a connection struct for the VFS layer.
1608 status
= create_conn_struct_tos_cwd(global_messaging_context(),
1613 if (!NT_STATUS_IS_OK(status
)) {
1614 DEBUG(3, ("create_conn_struct failed: %s\n",
1615 nt_errstr(status
)));
1621 /* form a junction for the msdfs root - convention
1622 DO NOT REMOVE THIS: NT clients will not work with us
1623 if this is not present
1625 jucn
[cnt
].service_name
= talloc_strdup(ctx
,service_name
);
1626 jucn
[cnt
].volume_name
= talloc_strdup(ctx
, "");
1627 if (!jucn
[cnt
].service_name
|| !jucn
[cnt
].volume_name
) {
1630 jucn
[cnt
].comment
= "";
1631 jucn
[cnt
].referral_count
= 1;
1633 ref
= jucn
[cnt
].referral_list
= talloc_zero(ctx
, struct referral
);
1634 if (jucn
[cnt
].referral_list
== NULL
) {
1639 ref
->ttl
= REFERRAL_TTL
;
1640 if (*msdfs_proxy
!= '\0') {
1641 ref
->alternate_path
= talloc_strdup(ctx
,
1644 ref
->alternate_path
= talloc_asprintf(ctx
,
1646 get_local_machine_name(),
1650 if (!ref
->alternate_path
) {
1655 /* Don't enumerate if we're an msdfs proxy. */
1656 if (*msdfs_proxy
!= '\0') {
1660 smb_fname
= synthetic_smb_fname(frame
,
1666 if (smb_fname
== NULL
) {
1670 /* Now enumerate all dfs links */
1671 status
= OpenDir(frame
,
1677 if (!NT_STATUS_IS_OK(status
)) {
1678 errno
= map_errno_from_nt_status(status
);
1682 while ((dname
= ReadDirName(dir_hnd
, &talloced
)) != NULL
) {
1683 struct smb_filename
*smb_dname
= NULL
;
1685 if (cnt
>= jn_remain
) {
1686 DEBUG(2, ("form_junctions: ran out of MSDFS "
1687 "junction slots\n"));
1688 TALLOC_FREE(talloced
);
1691 smb_dname
= synthetic_smb_fname(talloc_tos(),
1697 if (smb_dname
== NULL
) {
1698 TALLOC_FREE(talloced
);
1702 status
= SMB_VFS_READ_DFS_PATHAT(conn
,
1706 &jucn
[cnt
].referral_list
,
1707 &jucn
[cnt
].referral_count
);
1709 if (NT_STATUS_IS_OK(status
)) {
1710 jucn
[cnt
].service_name
= talloc_strdup(ctx
,
1712 jucn
[cnt
].volume_name
= talloc_strdup(ctx
, dname
);
1713 if (!jucn
[cnt
].service_name
|| !jucn
[cnt
].volume_name
) {
1714 TALLOC_FREE(talloced
);
1717 jucn
[cnt
].comment
= "";
1720 TALLOC_FREE(talloced
);
1721 TALLOC_FREE(smb_dname
);
1729 struct junction_map
*enum_msdfs_links(TALLOC_CTX
*ctx
,
1730 struct auth_session_info
*session_info
,
1733 struct junction_map
*jn
= NULL
;
1735 size_t jn_count
= 0;
1739 if(!lp_host_msdfs()) {
1743 /* Ensure all the usershares are loaded. */
1745 load_registry_shares();
1746 sharecount
= load_usershare_shares(NULL
, connections_snum_used
);
1749 for(i
=0;i
< sharecount
;i
++) {
1750 if(lp_msdfs_root(i
)) {
1751 jn_count
+= count_dfs_links(ctx
, session_info
, i
);
1754 if (jn_count
== 0) {
1757 jn
= talloc_array(ctx
, struct junction_map
, jn_count
);
1761 for(i
=0; i
< sharecount
; i
++) {
1762 if (*p_num_jn
>= jn_count
) {
1765 if(lp_msdfs_root(i
)) {
1766 *p_num_jn
+= form_junctions(ctx
,
1770 jn_count
- *p_num_jn
);