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(s)
42 \hostname\service - self referral
43 \hostname\service\remainingpath - Windows referral path
45 FIXME! Should we also parse:
46 \hostname\service/remainingpath - POSIX referral path
47 as currently nothing uses this ?
49 into the dfs_path components. Strict form.
51 Checks DFS path starts with separator.
52 Checks hostname is ours.
53 Ensures servicename (share) is sent, and
54 if so, terminates the name or is followed by
57 If returned, remainingpath is untouched. Caller must call
58 check_path_syntax() on it.
60 Called by all non-fileserver processing (DFS RPC, FSCTL_DFS_GET_REFERRALS)
61 etc. Errors out on any inconsistency in the path.
62 **********************************************************************/
64 static NTSTATUS
parse_dfs_path_strict(TALLOC_CTX
*ctx
,
68 char **_remaining_path
)
70 char *pathname_local
= NULL
;
72 const char *hostname
= NULL
;
73 const char *servicename
= NULL
;
74 const char *reqpath
= NULL
;
75 bool my_hostname
= false;
78 DBG_DEBUG("path = |%s|\n", pathname
);
80 pathname_local
= talloc_strdup(talloc_tos(), pathname
);
81 if (pathname_local
== NULL
) {
82 return NT_STATUS_NO_MEMORY
;
85 * parse_dfs_path_strict() is called from
86 * get_referred_path() and create_junction()
87 * which use Windows DFS paths of \server\share.
91 * Strict DFS paths *must* start with the
92 * path separator '\\'.
95 if (pathname_local
[0] != '\\') {
96 DBG_ERR("path %s doesn't start with \\\n",
98 status
= NT_STATUS_NOT_FOUND
;
103 /* Parse out hostname. */
104 p
= strchr(pathname_local
+ 1, '\\');
106 DBG_ERR("can't parse hostname from path %s\n",
108 status
= NT_STATUS_NOT_FOUND
;
112 hostname
= &pathname_local
[1];
114 DBG_DEBUG("hostname: %s\n", hostname
);
116 /* Is this really our hostname ? */
117 my_hostname
= is_myname_or_ipaddr(hostname
);
119 DBG_ERR("Hostname %s is not ours.\n",
121 status
= NT_STATUS_NOT_FOUND
;
128 * Find the end of servicename by looking for
129 * a directory separator character. The character
130 * should be '\\' for a Windows path.
131 * If there is no separator, then this is a self-referral
132 * of "\server\share".
135 p
= strchr(servicename
, '\\');
140 DBG_DEBUG("servicename: %s\n", servicename
);
143 /* Client sent self referral "\server\share". */
146 /* Step past the '\0' we just replaced '\\' with. */
150 DBG_DEBUG("rest of the path: %s\n", reqpath
);
152 if (_hostname
!= NULL
) {
153 *_hostname
= talloc_strdup(ctx
, hostname
);
154 if (*_hostname
== NULL
) {
155 status
= NT_STATUS_NO_MEMORY
;
159 if (_servicename
!= NULL
) {
160 *_servicename
= talloc_strdup(ctx
, servicename
);
161 if (*_servicename
== NULL
) {
162 status
= NT_STATUS_NO_MEMORY
;
166 if (_remaining_path
!= NULL
) {
167 *_remaining_path
= talloc_strdup(ctx
, reqpath
);
168 if (*_remaining_path
== NULL
) {
169 status
= NT_STATUS_NO_MEMORY
;
174 status
= NT_STATUS_OK
;
176 TALLOC_FREE(pathname_local
);
180 /********************************************************
181 Fake up a connection struct for the VFS layer, for use in
182 applications (such as the python bindings), that do not want the
183 global working directory changed under them.
185 SMB_VFS_CONNECT requires root privileges.
186 *********************************************************/
188 static NTSTATUS
create_conn_struct_as_root(TALLOC_CTX
*ctx
,
189 struct tevent_context
*ev
,
190 struct messaging_context
*msg
,
191 connection_struct
**pconn
,
194 const struct auth_session_info
*session_info
)
196 connection_struct
*conn
;
198 const char *vfs_user
;
199 struct smbd_server_connection
*sconn
;
200 const char *servicename
= lp_const_servicename(snum
);
203 sconn
= talloc_zero(ctx
, struct smbd_server_connection
);
205 return NT_STATUS_NO_MEMORY
;
209 sconn
->msg_ctx
= msg
;
211 conn
= conn_new(sconn
);
214 return NT_STATUS_NO_MEMORY
;
217 /* Now we have conn, we need to make sconn a child of conn,
218 * for a proper talloc tree */
219 talloc_steal(conn
, sconn
);
221 if (snum
== -1 && servicename
== NULL
) {
222 servicename
= "Unknown Service (snum == -1)";
225 connpath
= talloc_strdup(conn
, path
);
228 return NT_STATUS_NO_MEMORY
;
230 connpath
= talloc_string_sub(conn
,
236 return NT_STATUS_NO_MEMORY
;
239 /* needed for smbd_vfs_init() */
241 conn
->params
->service
= snum
;
242 conn
->cnum
= TID_FIELD_INVALID
;
244 SMB_ASSERT(session_info
!= NULL
);
246 conn
->session_info
= copy_session_info(conn
, session_info
);
247 if (conn
->session_info
== NULL
) {
248 DBG_ERR("copy_serverinfo failed\n");
250 return NT_STATUS_NO_MEMORY
;
253 /* unix_info could be NULL in session_info */
254 if (conn
->session_info
->unix_info
!= NULL
) {
255 vfs_user
= conn
->session_info
->unix_info
->unix_name
;
257 vfs_user
= get_current_username();
260 conn_setup_case_options(conn
);
262 set_conn_connectpath(conn
, connpath
);
265 * New code to check if there's a share security descriptor
266 * added from NT server manager. This is done after the
267 * smb.conf checks are done as we need a uid and token. JRA.
270 share_access_check(conn
->session_info
->security_token
,
272 MAXIMUM_ALLOWED_ACCESS
,
273 &conn
->share_access
);
275 if ((conn
->share_access
& FILE_WRITE_DATA
) == 0) {
276 if ((conn
->share_access
& FILE_READ_DATA
) == 0) {
277 /* No access, read or write. */
278 DBG_WARNING("connection to %s "
279 "denied due to security "
283 return NT_STATUS_ACCESS_DENIED
;
285 conn
->read_only
= true;
288 if (!smbd_vfs_init(conn
)) {
289 NTSTATUS status
= map_nt_error_from_unix(errno
);
290 DEBUG(0,("create_conn_struct: smbd_vfs_init failed.\n"));
295 /* this must be the first filesystem operation that we do */
296 if (SMB_VFS_CONNECT(conn
, servicename
, vfs_user
) < 0) {
297 DEBUG(0,("VFS connect failed!\n"));
299 return NT_STATUS_UNSUCCESSFUL
;
302 ok
= canonicalize_connect_path(conn
);
304 DBG_ERR("Failed to canonicalize sharepath\n");
306 return NT_STATUS_ACCESS_DENIED
;
309 conn
->fs_capabilities
= SMB_VFS_FS_CAPABILITIES(conn
, &conn
->ts_res
);
310 conn
->tcon_done
= true;
311 *pconn
= talloc_move(ctx
, &conn
);
316 static int conn_struct_tos_destructor(struct conn_struct_tos
*c
)
318 if (c
->oldcwd_fname
!= NULL
) {
319 vfs_ChDir(c
->conn
, c
->oldcwd_fname
);
320 TALLOC_FREE(c
->oldcwd_fname
);
322 SMB_VFS_DISCONNECT(c
->conn
);
327 /********************************************************
328 Fake up a connection struct for the VFS layer, for use in
329 applications (such as the python bindings), that do not want the
330 global working directory changed under them.
332 SMB_VFS_CONNECT requires root privileges.
333 This temporary uses become_root() and unbecome_root().
335 But further impersonation has to be cone by the caller.
336 *********************************************************/
337 NTSTATUS
create_conn_struct_tos(struct messaging_context
*msg
,
340 const struct auth_session_info
*session_info
,
341 struct conn_struct_tos
**_c
)
343 struct conn_struct_tos
*c
= NULL
;
344 struct tevent_context
*ev
= NULL
;
349 c
= talloc_zero(talloc_tos(), struct conn_struct_tos
);
351 return NT_STATUS_NO_MEMORY
;
354 ev
= samba_tevent_context_init(c
);
357 return NT_STATUS_NO_MEMORY
;
361 status
= create_conn_struct_as_root(c
,
369 if (!NT_STATUS_IS_OK(status
)) {
374 talloc_set_destructor(c
, conn_struct_tos_destructor
);
380 /********************************************************
381 Fake up a connection struct for the VFS layer.
382 Note: this performs a vfs connect and CHANGES CWD !!!! JRA.
384 See also the comment for create_conn_struct_tos() above!
386 The CWD change is reverted by the destructor of
387 conn_struct_tos when the current talloc_tos() is destroyed.
388 *********************************************************/
389 NTSTATUS
create_conn_struct_tos_cwd(struct messaging_context
*msg
,
392 const struct auth_session_info
*session_info
,
393 struct conn_struct_tos
**_c
)
395 struct conn_struct_tos
*c
= NULL
;
396 struct smb_filename smb_fname_connectpath
= {0};
401 status
= create_conn_struct_tos(msg
,
406 if (!NT_STATUS_IS_OK(status
)) {
411 * Windows seems to insist on doing trans2getdfsreferral() calls on
412 * the IPC$ share as the anonymous user. If we try to chdir as that
413 * user we will fail.... WTF ? JRA.
416 c
->oldcwd_fname
= vfs_GetWd(c
, c
->conn
);
417 if (c
->oldcwd_fname
== NULL
) {
418 status
= map_nt_error_from_unix(errno
);
419 DEBUG(3, ("vfs_GetWd failed: %s\n", strerror(errno
)));
424 smb_fname_connectpath
= (struct smb_filename
) {
425 .base_name
= c
->conn
->connectpath
428 if (vfs_ChDir(c
->conn
, &smb_fname_connectpath
) != 0) {
429 status
= map_nt_error_from_unix(errno
);
430 DBG_NOTICE("Can't ChDir to new conn path %s. "
432 c
->conn
->connectpath
, strerror(errno
));
433 TALLOC_FREE(c
->oldcwd_fname
);
442 /********************************************************
443 Fake up a connection struct for the VFS layer.
444 This takes an TALLOC_CTX and tevent_context from the
445 caller and the resulting connection_struct is stable
446 across the lifetime of mem_ctx and ev.
448 Note: this performs a vfs connect and changes cwd.
450 See also the comment for create_conn_struct_tos() above!
451 *********************************************************/
453 NTSTATUS
create_conn_struct_cwd(TALLOC_CTX
*mem_ctx
,
454 struct tevent_context
*ev
,
455 struct messaging_context
*msg
,
456 const struct auth_session_info
*session_info
,
459 struct connection_struct
**c
)
464 status
= create_conn_struct_as_root(mem_ctx
,
475 static void shuffle_strlist(char **list
, int count
)
481 for (i
= count
; i
> 1; i
--) {
482 r
= generate_random() % i
;
490 /**********************************************************************
491 Parse the contents of a symlink to verify if it is an msdfs referral
492 A valid referral is of the form:
494 msdfs:server1\share1,server2\share2
495 msdfs:server1\share1\pathname,server2\share2\pathname
496 msdfs:server1/share1,server2/share2
497 msdfs:server1/share1/pathname,server2/share2/pathname.
499 Note that the alternate paths returned here must be of the canonicalized
503 \server\share\path\to\file,
505 even in posix path mode. This is because we have no knowledge if the
506 server we're referring to understands posix paths.
507 **********************************************************************/
509 bool parse_msdfs_symlink(TALLOC_CTX
*ctx
,
510 bool shuffle_referrals
,
512 struct referral
**ppreflist
,
517 char **alt_path
= NULL
;
519 struct referral
*reflist
= NULL
;
522 temp
= talloc_strdup(ctx
, target
);
526 prot
= strtok_r(temp
, ":", &saveptr
);
528 DEBUG(0,("parse_msdfs_symlink: invalid path !\n"));
533 alt_path
= talloc_array(ctx
, char *, MAX_REFERRAL_COUNT
);
539 /* parse out the alternate paths */
540 while((count
<MAX_REFERRAL_COUNT
) &&
541 ((alt_path
[count
] = strtok_r(NULL
, ",", &saveptr
)) != NULL
)) {
545 /* shuffle alternate paths */
546 if (shuffle_referrals
) {
547 shuffle_strlist(alt_path
, count
);
550 DBG_DEBUG("count=%zu\n", count
);
553 reflist
= talloc_zero_array(ctx
,
554 struct referral
, count
);
555 if(reflist
== NULL
) {
557 TALLOC_FREE(alt_path
);
564 for(i
=0;i
<count
;i
++) {
567 /* Canonicalize link target.
568 * Replace all /'s in the path by a \ */
569 string_replace(alt_path
[i
], '/', '\\');
571 /* Remove leading '\\'s */
573 while (*p
&& (*p
== '\\')) {
577 reflist
[i
].alternate_path
= talloc_asprintf(reflist
,
580 if (!reflist
[i
].alternate_path
) {
582 TALLOC_FREE(alt_path
);
583 TALLOC_FREE(reflist
);
587 reflist
[i
].proximity
= 0;
588 reflist
[i
].ttl
= REFERRAL_TTL
;
589 DBG_DEBUG("Created alt path: %s\n",
590 reflist
[i
].alternate_path
);
593 if (ppreflist
!= NULL
) {
594 *ppreflist
= reflist
;
596 TALLOC_FREE(reflist
);
598 if (prefcount
!= NULL
) {
602 TALLOC_FREE(alt_path
);
606 /**********************************************************************
607 Returns true if the unix path is a valid msdfs symlink.
608 **********************************************************************/
610 bool is_msdfs_link(struct files_struct
*dirfsp
,
611 struct smb_filename
*atname
)
613 NTSTATUS status
= SMB_VFS_READ_DFS_PATHAT(dirfsp
->conn
,
619 return (NT_STATUS_IS_OK(status
));
622 /*****************************************************************
623 Used by other functions to decide if a dfs path is remote,
624 and to get the list of referred locations for that remote path.
626 consumedcntp: how much of the dfs path is being redirected. the client
627 should try the remaining path on the redirected server.
628 *****************************************************************/
630 static NTSTATUS
dfs_path_lookup(TALLOC_CTX
*ctx
,
631 connection_struct
*conn
,
632 const char *dfspath
, /* Incoming complete dfs path */
633 const char *reqpath
, /* Parsed out remaining path. */
635 size_t *consumedcntp
,
636 struct referral
**ppreflist
,
637 size_t *preferral_count
)
640 struct smb_filename
*parent_smb_fname
= NULL
;
641 struct smb_filename
*smb_fname_rel
= NULL
;
643 char *local_pathname
= NULL
;
644 char *last_component
= NULL
;
646 size_t removed_components
= 0;
647 bool posix
= (ucf_flags
& UCF_POSIX_PATHNAMES
);
649 char *canon_dfspath
= NULL
;
651 DBG_DEBUG("Conn path = %s reqpath = %s\n", conn
->connectpath
, reqpath
);
653 local_pathname
= talloc_strdup(ctx
, reqpath
);
654 if (local_pathname
== NULL
) {
655 status
= NT_STATUS_NO_MEMORY
;
659 /* We know reqpath isn't a DFS path. */
660 ucf_flags
&= ~UCF_DFS_PATHNAME
;
662 if (ucf_flags
& UCF_GMT_PATHNAME
) {
663 extract_snapshot_token(local_pathname
, &twrp
);
664 ucf_flags
&= ~UCF_GMT_PATHNAME
;
668 * We should have been given a DFS path to resolve.
669 * This should return NT_STATUS_PATH_NOT_COVERED.
671 * Do a pathname walk, stripping off components
672 * until we get NT_STATUS_OK instead of
673 * NT_STATUS_PATH_NOT_COVERED.
675 * Fail on any other error.
679 TALLOC_CTX
*frame
= NULL
;
680 struct files_struct
*dirfsp
= NULL
;
681 struct smb_filename
*smb_fname_walk
= NULL
;
683 TALLOC_FREE(parent_smb_fname
);
686 * Use a local stackframe as filename_convert_dirfsp()
687 * opens handles on the last two components in the path.
688 * Allow these to be freed as we step back through
689 * the local_pathname.
691 frame
= talloc_stackframe();
692 status
= filename_convert_dirfsp(frame
,
699 /* If we got a name, save it. */
700 if (smb_fname_walk
!= NULL
) {
701 parent_smb_fname
= talloc_move(ctx
, &smb_fname_walk
);
705 if (!NT_STATUS_EQUAL(status
, NT_STATUS_PATH_NOT_COVERED
)) {
707 * For any other status than NT_STATUS_PATH_NOT_COVERED
708 * (including NT_STATUS_OK) we exit the walk.
709 * If it's an error we catch it outside the loop.
714 /* Step back one component and save it off as last_component. */
715 TALLOC_FREE(last_component
);
716 p
= strrchr(local_pathname
, '/');
719 * We removed all components.
720 * Go around once more to make
721 * sure we can open the root '\0'.
723 last_component
= talloc_strdup(ctx
, local_pathname
);
724 *local_pathname
= '\0';
726 last_component
= talloc_strdup(ctx
, p
+1);
729 if (last_component
== NULL
) {
730 status
= NT_STATUS_NO_MEMORY
;
733 /* Integer wrap check. */
734 if (removed_components
+ 1 < removed_components
) {
735 status
= NT_STATUS_INVALID_PARAMETER
;
738 removed_components
++;
741 if (!NT_STATUS_IS_OK(status
)) {
742 DBG_DEBUG("dfspath = %s. reqpath = %s. Error %s.\n",
749 if (parent_smb_fname
->fsp
== NULL
) {
750 /* Unable to open parent. */
751 DBG_DEBUG("dfspath = %s. reqpath = %s. "
752 "Unable to open parent directory (%s).\n",
755 smb_fname_str_dbg(parent_smb_fname
));
756 status
= NT_STATUS_OBJECT_PATH_NOT_FOUND
;
760 if (removed_components
== 0) {
762 * We never got NT_STATUS_PATH_NOT_COVERED.
763 * There was no DFS redirect.
765 DBG_DEBUG("dfspath = %s. reqpath = %s. "
766 "No removed components.\n",
769 status
= NT_STATUS_OBJECT_PATH_NOT_FOUND
;
774 * One of the removed_components was the MSDFS link
775 * at the end. We need to count this in the resolved
776 * path below, so remove one from removed_components.
778 removed_components
--;
781 * Now parent_smb_fname->fsp is the parent directory dirfsp,
782 * last_component is the untranslated MS-DFS link name.
783 * Search for it in the parent directory to get the real
786 status
= get_real_filename_at(parent_smb_fname
->fsp
,
791 if (!NT_STATUS_IS_OK(status
)) {
792 DBG_DEBUG("dfspath = %s. reqpath = %s "
793 "get_real_filename_at(%s, %s) error (%s)\n",
796 smb_fname_str_dbg(parent_smb_fname
),
802 smb_fname_rel
= synthetic_smb_fname(ctx
,
807 posix
? SMB_FILENAME_POSIX_PATH
: 0);
808 if (smb_fname_rel
== NULL
) {
809 status
= NT_STATUS_NO_MEMORY
;
813 /* Get the referral to return. */
814 status
= SMB_VFS_READ_DFS_PATHAT(conn
,
816 parent_smb_fname
->fsp
,
820 if (!NT_STATUS_IS_OK(status
)) {
821 DBG_DEBUG("dfspath = %s. reqpath = %s. "
822 "SMB_VFS_READ_DFS_PATHAT(%s, %s) error (%s)\n",
825 smb_fname_str_dbg(parent_smb_fname
),
826 smb_fname_str_dbg(smb_fname_rel
),
832 * Now we must work out how much of the
833 * given pathname we consumed.
835 canon_dfspath
= talloc_strdup(ctx
, dfspath
);
836 if (!canon_dfspath
) {
837 status
= NT_STATUS_NO_MEMORY
;
840 /* Canonicalize the raw dfspath. */
841 string_replace(canon_dfspath
, '\\', '/');
844 * reqpath comes out of parse_dfs_path(), so it has
845 * no trailing backslash. Make sure that canon_dfspath hasn't either.
847 trim_char(canon_dfspath
, 0, '/');
849 DBG_DEBUG("Unconsumed path: %s\n", canon_dfspath
);
851 while (removed_components
> 0) {
852 p
= strrchr(canon_dfspath
, '/');
856 removed_components
--;
857 if (p
== NULL
&& removed_components
!= 0) {
858 DBG_ERR("Component mismatch. path = %s, "
859 "%zu components left\n",
862 status
= NT_STATUS_OBJECT_PATH_NOT_FOUND
;
866 *consumedcntp
= strlen(canon_dfspath
);
867 DBG_DEBUG("Path consumed: %s (%zu)\n", canon_dfspath
, *consumedcntp
);
868 status
= NT_STATUS_OK
;
872 TALLOC_FREE(parent_smb_fname
);
873 TALLOC_FREE(local_pathname
);
874 TALLOC_FREE(last_component
);
876 TALLOC_FREE(smb_fname_rel
);
877 TALLOC_FREE(canon_dfspath
);
881 /**********************************************************************
882 Return a self referral.
883 **********************************************************************/
885 static NTSTATUS
self_ref(TALLOC_CTX
*ctx
,
886 const char *dfs_path
,
887 struct junction_map
*jucn
,
888 size_t *consumedcntp
,
889 bool *self_referralp
)
891 struct referral
*ref
;
893 *self_referralp
= True
;
895 jucn
->referral_count
= 1;
896 if((ref
= talloc_zero(ctx
, struct referral
)) == NULL
) {
897 return NT_STATUS_NO_MEMORY
;
900 ref
->alternate_path
= talloc_strdup(ctx
, dfs_path
);
901 if (!ref
->alternate_path
) {
903 return NT_STATUS_NO_MEMORY
;
906 ref
->ttl
= REFERRAL_TTL
;
907 jucn
->referral_list
= ref
;
908 *consumedcntp
= strlen(dfs_path
);
912 /**********************************************************************
913 Gets valid referrals for a dfs path and fills up the
914 junction_map structure.
915 **********************************************************************/
917 NTSTATUS
get_referred_path(TALLOC_CTX
*ctx
,
918 struct auth_session_info
*session_info
,
919 const char *dfs_path
,
920 const struct tsocket_address
*remote_address
,
921 const struct tsocket_address
*local_address
,
922 struct junction_map
*jucn
,
923 size_t *consumedcntp
,
924 bool *self_referralp
)
926 TALLOC_CTX
*frame
= talloc_stackframe();
927 const struct loadparm_substitution
*lp_sub
=
928 loadparm_s3_global_substitution();
929 struct conn_struct_tos
*c
= NULL
;
930 struct connection_struct
*conn
= NULL
;
931 char *servicename
= NULL
;
932 char *reqpath
= NULL
;
934 NTSTATUS status
= NT_STATUS_NOT_FOUND
;
936 *self_referralp
= False
;
938 status
= parse_dfs_path_strict(
944 if (!NT_STATUS_IS_OK(status
)) {
949 /* Path referrals are always non-POSIX. */
950 status
= check_path_syntax(reqpath
, false);
951 if (!NT_STATUS_IS_OK(status
)) {
956 jucn
->service_name
= talloc_strdup(ctx
, servicename
);
957 jucn
->volume_name
= talloc_strdup(ctx
, reqpath
);
958 if (!jucn
->service_name
|| !jucn
->volume_name
) {
960 return NT_STATUS_NO_MEMORY
;
963 /* Verify the share is a dfs root */
964 snum
= lp_servicenumber(jucn
->service_name
);
966 char *service_name
= NULL
;
967 if ((snum
= find_service(ctx
, jucn
->service_name
, &service_name
)) < 0) {
969 return NT_STATUS_NOT_FOUND
;
973 return NT_STATUS_NO_MEMORY
;
975 TALLOC_FREE(jucn
->service_name
);
976 jucn
->service_name
= talloc_strdup(ctx
, service_name
);
977 if (!jucn
->service_name
) {
979 return NT_STATUS_NO_MEMORY
;
983 if (!lp_msdfs_root(snum
) && (*lp_msdfs_proxy(talloc_tos(), lp_sub
, snum
) == '\0')) {
984 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not "
986 servicename
, dfs_path
));
988 return NT_STATUS_NOT_FOUND
;
992 * Self referrals are tested with a anonymous IPC connection and
993 * a GET_DFS_REFERRAL call to \\server\share. (which means
994 * dp.reqpath[0] points to an empty string). create_conn_struct cd's
995 * into the directory and will fail if it cannot (as the anonymous
996 * user). Cope with this.
999 if (reqpath
[0] == '\0') {
1001 struct referral
*ref
;
1004 if (*lp_msdfs_proxy(talloc_tos(), lp_sub
, snum
) == '\0') {
1006 return self_ref(ctx
,
1014 * It's an msdfs proxy share. Redirect to
1015 * the configured target share.
1018 tmp
= talloc_asprintf(frame
, "msdfs:%s",
1019 lp_msdfs_proxy(frame
, lp_sub
, snum
));
1022 return NT_STATUS_NO_MEMORY
;
1025 if (!parse_msdfs_symlink(ctx
,
1026 lp_msdfs_shuffle_referrals(snum
),
1031 return NT_STATUS_INVALID_PARAMETER
;
1033 jucn
->referral_count
= refcount
;
1034 jucn
->referral_list
= ref
;
1035 *consumedcntp
= strlen(dfs_path
);
1037 return NT_STATUS_OK
;
1040 status
= create_conn_struct_tos_cwd(global_messaging_context(),
1042 lp_path(frame
, lp_sub
, snum
),
1045 if (!NT_STATUS_IS_OK(status
)) {
1054 * The remote and local address should be passed down to
1055 * create_conn_struct_cwd.
1057 if (conn
->sconn
->remote_address
== NULL
) {
1058 conn
->sconn
->remote_address
=
1059 tsocket_address_copy(remote_address
, conn
->sconn
);
1060 if (conn
->sconn
->remote_address
== NULL
) {
1062 return NT_STATUS_NO_MEMORY
;
1065 if (conn
->sconn
->local_address
== NULL
) {
1066 conn
->sconn
->local_address
=
1067 tsocket_address_copy(local_address
, conn
->sconn
);
1068 if (conn
->sconn
->local_address
== NULL
) {
1070 return NT_STATUS_NO_MEMORY
;
1074 status
= dfs_path_lookup(ctx
,
1080 &jucn
->referral_list
,
1081 &jucn
->referral_count
);
1083 if (!NT_STATUS_IS_OK(status
)) {
1084 DBG_NOTICE("No valid referrals for path %s (%s)\n",
1093 /******************************************************************
1094 Set up the DFS referral for the dfs pathname. This call returns
1095 the amount of the path covered by this server, and where the
1096 client should be redirected to. This is the meat of the
1097 TRANS2_GET_DFS_REFERRAL call.
1098 ******************************************************************/
1100 int setup_dfs_referral(connection_struct
*orig_conn
,
1101 const char *dfs_path
,
1102 int max_referral_level
,
1103 char **ppdata
, NTSTATUS
*pstatus
)
1105 char *pdata
= *ppdata
;
1107 struct dfs_GetDFSReferral
*r
;
1108 DATA_BLOB blob
= data_blob_null
;
1110 enum ndr_err_code ndr_err
;
1112 r
= talloc_zero(talloc_tos(), struct dfs_GetDFSReferral
);
1114 *pstatus
= NT_STATUS_NO_MEMORY
;
1118 r
->in
.req
.max_referral_level
= max_referral_level
;
1119 r
->in
.req
.servername
= talloc_strdup(r
, dfs_path
);
1120 if (r
->in
.req
.servername
== NULL
) {
1122 *pstatus
= NT_STATUS_NO_MEMORY
;
1126 status
= SMB_VFS_GET_DFS_REFERRALS(orig_conn
, r
);
1127 if (!NT_STATUS_IS_OK(status
)) {
1133 ndr_err
= ndr_push_struct_blob(&blob
, r
,
1135 (ndr_push_flags_fn_t
)ndr_push_dfs_referral_resp
);
1136 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
1138 *pstatus
= NT_STATUS_INVALID_PARAMETER
;
1142 pdata
= (char *)SMB_REALLOC(pdata
, blob
.length
);
1145 DEBUG(0,("referral setup:"
1146 "malloc failed for Realloc!\n"));
1150 reply_size
= blob
.length
;
1151 memcpy(pdata
, blob
.data
, blob
.length
);
1154 *pstatus
= NT_STATUS_OK
;
1158 /**********************************************************************
1159 The following functions are called by the NETDFS RPC pipe functions
1160 **********************************************************************/
1162 /*********************************************************************
1163 Creates a junction structure from a DFS pathname
1164 **********************************************************************/
1166 bool create_junction(TALLOC_CTX
*ctx
,
1167 const char *dfs_path
,
1168 struct junction_map
*jucn
)
1170 const struct loadparm_substitution
*lp_sub
=
1171 loadparm_s3_global_substitution();
1173 char *servicename
= NULL
;
1174 char *reqpath
= NULL
;
1177 status
= parse_dfs_path_strict(
1183 if (!NT_STATUS_IS_OK(status
)) {
1187 /* Check for a non-DFS share */
1188 snum
= lp_servicenumber(servicename
);
1190 if(snum
< 0 || !lp_msdfs_root(snum
)) {
1191 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1196 /* Junction create paths are always non-POSIX. */
1197 status
= check_path_syntax(reqpath
, false);
1198 if (!NT_STATUS_IS_OK(status
)) {
1202 jucn
->service_name
= talloc_strdup(ctx
, servicename
);
1203 jucn
->volume_name
= talloc_strdup(ctx
, reqpath
);
1204 jucn
->comment
= lp_comment(ctx
, lp_sub
, snum
);
1206 if (!jucn
->service_name
|| !jucn
->volume_name
|| ! jucn
->comment
) {
1212 /**********************************************************************
1213 Forms a valid Unix pathname from the junction
1214 **********************************************************************/
1216 static bool junction_to_local_path_tos(const struct junction_map
*jucn
,
1217 struct auth_session_info
*session_info
,
1219 connection_struct
**conn_out
)
1221 const struct loadparm_substitution
*lp_sub
=
1222 loadparm_s3_global_substitution();
1223 struct conn_struct_tos
*c
= NULL
;
1225 char *path_out
= NULL
;
1228 snum
= lp_servicenumber(jucn
->service_name
);
1232 status
= create_conn_struct_tos_cwd(global_messaging_context(),
1234 lp_path(talloc_tos(), lp_sub
, snum
),
1237 if (!NT_STATUS_IS_OK(status
)) {
1241 path_out
= talloc_asprintf(c
,
1243 lp_path(talloc_tos(), lp_sub
, snum
),
1245 if (path_out
== NULL
) {
1249 *pp_path_out
= path_out
;
1250 *conn_out
= c
->conn
;
1255 * Create a msdfs string in Samba format we can store
1256 * in a filesystem object (currently a symlink).
1259 char *msdfs_link_string(TALLOC_CTX
*ctx
,
1260 const struct referral
*reflist
,
1261 size_t referral_count
)
1263 char *refpath
= NULL
;
1264 bool insert_comma
= false;
1265 char *msdfs_link
= NULL
;
1268 /* Form the msdfs_link contents */
1269 msdfs_link
= talloc_strdup(ctx
, "msdfs:");
1270 if (msdfs_link
== NULL
) {
1274 for( i
= 0; i
< referral_count
; i
++) {
1275 refpath
= talloc_strdup(ctx
, reflist
[i
].alternate_path
);
1277 if (refpath
== NULL
) {
1281 /* Alternate paths always use Windows separators. */
1282 trim_char(refpath
, '\\', '\\');
1283 if (*refpath
== '\0') {
1285 insert_comma
= false;
1289 if (i
> 0 && insert_comma
) {
1290 msdfs_link
= talloc_asprintf_append_buffer(msdfs_link
,
1294 msdfs_link
= talloc_asprintf_append_buffer(msdfs_link
,
1299 if (msdfs_link
== NULL
) {
1303 if (!insert_comma
) {
1304 insert_comma
= true;
1307 TALLOC_FREE(refpath
);
1314 TALLOC_FREE(refpath
);
1315 TALLOC_FREE(msdfs_link
);
1319 bool create_msdfs_link(const struct junction_map
*jucn
,
1320 struct auth_session_info
*session_info
)
1322 TALLOC_CTX
*frame
= talloc_stackframe();
1324 connection_struct
*conn
;
1325 struct smb_filename
*smb_fname
= NULL
;
1326 struct smb_filename
*parent_fname
= NULL
;
1327 struct smb_filename
*at_fname
= NULL
;
1332 ok
= junction_to_local_path_tos(jucn
, session_info
, &path
, &conn
);
1337 if (!CAN_WRITE(conn
)) {
1338 const struct loadparm_substitution
*lp_sub
=
1339 loadparm_s3_global_substitution();
1340 int snum
= lp_servicenumber(jucn
->service_name
);
1342 DBG_WARNING("Can't create DFS entry on read-only share %s\n",
1343 lp_servicename(frame
, lp_sub
, snum
));
1347 smb_fname
= synthetic_smb_fname(frame
,
1353 if (smb_fname
== NULL
) {
1357 status
= parent_pathref(frame
,
1362 if (!NT_STATUS_IS_OK(status
)) {
1366 status
= SMB_VFS_CREATE_DFS_PATHAT(conn
,
1369 jucn
->referral_list
,
1370 jucn
->referral_count
);
1371 if (!NT_STATUS_IS_OK(status
)) {
1372 if (NT_STATUS_EQUAL(status
, NT_STATUS_OBJECT_NAME_COLLISION
)) {
1373 int retval
= SMB_VFS_UNLINKAT(conn
,
1381 status
= SMB_VFS_CREATE_DFS_PATHAT(conn
,
1384 jucn
->referral_list
,
1385 jucn
->referral_count
);
1386 if (!NT_STATUS_IS_OK(status
)) {
1387 DBG_WARNING("SMB_VFS_CREATE_DFS_PATHAT failed "
1402 bool remove_msdfs_link(const struct junction_map
*jucn
,
1403 struct auth_session_info
*session_info
)
1405 TALLOC_CTX
*frame
= talloc_stackframe();
1407 connection_struct
*conn
;
1409 struct smb_filename
*smb_fname
;
1410 struct smb_filename
*parent_fname
= NULL
;
1411 struct smb_filename
*at_fname
= NULL
;
1416 ok
= junction_to_local_path_tos(jucn
, session_info
, &path
, &conn
);
1422 if (!CAN_WRITE(conn
)) {
1423 const struct loadparm_substitution
*lp_sub
=
1424 loadparm_s3_global_substitution();
1425 int snum
= lp_servicenumber(jucn
->service_name
);
1427 DBG_WARNING("Can't remove DFS entry on read-only share %s\n",
1428 lp_servicename(frame
, lp_sub
, snum
));
1433 smb_fname
= synthetic_smb_fname(frame
,
1439 if (smb_fname
== NULL
) {
1445 status
= parent_pathref(frame
,
1450 if (!NT_STATUS_IS_OK(status
)) {
1455 retval
= SMB_VFS_UNLINKAT(conn
,
1467 /*********************************************************************
1468 Return the number of DFS links at the root of this share.
1469 *********************************************************************/
1471 static size_t count_dfs_links(TALLOC_CTX
*ctx
,
1472 struct auth_session_info
*session_info
,
1475 TALLOC_CTX
*frame
= talloc_stackframe();
1476 const struct loadparm_substitution
*lp_sub
=
1477 loadparm_s3_global_substitution();
1479 const char *dname
= NULL
;
1480 char *talloced
= NULL
;
1481 const char *connect_path
= lp_path(frame
, lp_sub
, snum
);
1482 const char *msdfs_proxy
= lp_msdfs_proxy(frame
, lp_sub
, snum
);
1483 struct conn_struct_tos
*c
= NULL
;
1484 connection_struct
*conn
= NULL
;
1486 struct smb_filename
*smb_fname
= NULL
;
1487 struct smb_Dir
*dir_hnd
= NULL
;
1489 if(*connect_path
== '\0') {
1495 * Fake up a connection struct for the VFS layer.
1498 status
= create_conn_struct_tos_cwd(global_messaging_context(),
1503 if (!NT_STATUS_IS_OK(status
)) {
1504 DEBUG(3, ("create_conn_struct failed: %s\n",
1505 nt_errstr(status
)));
1511 /* Count a link for the msdfs root - convention */
1514 /* No more links if this is an msdfs proxy. */
1515 if (*msdfs_proxy
!= '\0') {
1519 smb_fname
= synthetic_smb_fname(frame
,
1525 if (smb_fname
== NULL
) {
1529 /* Now enumerate all dfs links */
1530 status
= OpenDir(frame
,
1536 if (!NT_STATUS_IS_OK(status
)) {
1537 errno
= map_errno_from_nt_status(status
);
1541 while ((dname
= ReadDirName(dir_hnd
, &talloced
)) != NULL
) {
1542 struct smb_filename
*smb_dname
=
1543 synthetic_smb_fname(frame
,
1549 if (smb_dname
== NULL
) {
1552 if (is_msdfs_link(dir_hnd_fetch_fsp(dir_hnd
), smb_dname
)) {
1553 if (cnt
+ 1 < cnt
) {
1559 TALLOC_FREE(talloced
);
1560 TALLOC_FREE(smb_dname
);
1568 /*********************************************************************
1569 *********************************************************************/
1571 static int form_junctions(TALLOC_CTX
*ctx
,
1572 struct auth_session_info
*session_info
,
1574 struct junction_map
*jucn
,
1577 TALLOC_CTX
*frame
= talloc_stackframe();
1578 const struct loadparm_substitution
*lp_sub
=
1579 loadparm_s3_global_substitution();
1581 const char *dname
= NULL
;
1582 char *talloced
= NULL
;
1583 const char *connect_path
= lp_path(frame
, lp_sub
, snum
);
1584 char *service_name
= lp_servicename(frame
, lp_sub
, snum
);
1585 const char *msdfs_proxy
= lp_msdfs_proxy(frame
, lp_sub
, snum
);
1586 struct conn_struct_tos
*c
= NULL
;
1587 connection_struct
*conn
= NULL
;
1588 struct referral
*ref
= NULL
;
1589 struct smb_filename
*smb_fname
= NULL
;
1590 struct smb_Dir
*dir_hnd
= NULL
;
1593 if (jn_remain
== 0) {
1598 if(*connect_path
== '\0') {
1604 * Fake up a connection struct for the VFS layer.
1607 status
= create_conn_struct_tos_cwd(global_messaging_context(),
1612 if (!NT_STATUS_IS_OK(status
)) {
1613 DEBUG(3, ("create_conn_struct failed: %s\n",
1614 nt_errstr(status
)));
1620 /* form a junction for the msdfs root - convention
1621 DO NOT REMOVE THIS: NT clients will not work with us
1622 if this is not present
1624 jucn
[cnt
].service_name
= talloc_strdup(ctx
,service_name
);
1625 jucn
[cnt
].volume_name
= talloc_strdup(ctx
, "");
1626 if (!jucn
[cnt
].service_name
|| !jucn
[cnt
].volume_name
) {
1629 jucn
[cnt
].comment
= "";
1630 jucn
[cnt
].referral_count
= 1;
1632 ref
= jucn
[cnt
].referral_list
= talloc_zero(ctx
, struct referral
);
1633 if (jucn
[cnt
].referral_list
== NULL
) {
1638 ref
->ttl
= REFERRAL_TTL
;
1639 if (*msdfs_proxy
!= '\0') {
1640 ref
->alternate_path
= talloc_strdup(ctx
,
1643 ref
->alternate_path
= talloc_asprintf(ctx
,
1645 get_local_machine_name(),
1649 if (!ref
->alternate_path
) {
1654 /* Don't enumerate if we're an msdfs proxy. */
1655 if (*msdfs_proxy
!= '\0') {
1659 smb_fname
= synthetic_smb_fname(frame
,
1665 if (smb_fname
== NULL
) {
1669 /* Now enumerate all dfs links */
1670 status
= OpenDir(frame
,
1676 if (!NT_STATUS_IS_OK(status
)) {
1677 errno
= map_errno_from_nt_status(status
);
1681 while ((dname
= ReadDirName(dir_hnd
, &talloced
)) != NULL
) {
1682 struct smb_filename
*smb_dname
= NULL
;
1684 if (cnt
>= jn_remain
) {
1685 DEBUG(2, ("form_junctions: ran out of MSDFS "
1687 TALLOC_FREE(talloced
);
1690 smb_dname
= synthetic_smb_fname(talloc_tos(),
1696 if (smb_dname
== NULL
) {
1697 TALLOC_FREE(talloced
);
1701 status
= SMB_VFS_READ_DFS_PATHAT(conn
,
1705 &jucn
[cnt
].referral_list
,
1706 &jucn
[cnt
].referral_count
);
1708 if (NT_STATUS_IS_OK(status
)) {
1709 jucn
[cnt
].service_name
= talloc_strdup(ctx
,
1711 jucn
[cnt
].volume_name
= talloc_strdup(ctx
, dname
);
1712 if (!jucn
[cnt
].service_name
|| !jucn
[cnt
].volume_name
) {
1713 TALLOC_FREE(talloced
);
1716 jucn
[cnt
].comment
= "";
1719 TALLOC_FREE(talloced
);
1720 TALLOC_FREE(smb_dname
);
1728 struct junction_map
*enum_msdfs_links(TALLOC_CTX
*ctx
,
1729 struct auth_session_info
*session_info
,
1732 struct junction_map
*jn
= NULL
;
1734 size_t jn_count
= 0;
1738 if(!lp_host_msdfs()) {
1742 /* Ensure all the usershares are loaded. */
1744 load_registry_shares();
1745 sharecount
= load_usershare_shares(NULL
, connections_snum_used
);
1748 for(i
=0;i
< sharecount
;i
++) {
1749 if(lp_msdfs_root(i
)) {
1750 jn_count
+= count_dfs_links(ctx
, session_info
, i
);
1753 if (jn_count
== 0) {
1756 jn
= talloc_array(ctx
, struct junction_map
, jn_count
);
1760 for(i
=0; i
< sharecount
; i
++) {
1761 if (*p_num_jn
>= jn_count
) {
1764 if(lp_msdfs_root(i
)) {
1765 *p_num_jn
+= form_junctions(ctx
,
1769 jn_count
- *p_num_jn
);