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 "lib/param/loadparm.h"
32 #include "libcli/security/security.h"
33 #include "librpc/gen_ndr/ndr_dfsblobs.h"
34 #include "lib/tsocket/tsocket.h"
36 /**********************************************************************
37 Parse a DFS pathname of the form \hostname\service\reqpath
38 into the dfs_path structure.
39 If POSIX pathnames is true, the pathname may also be of the
40 form /hostname/service/reqpath.
41 We cope with either here.
43 Unfortunately, due to broken clients who might set the
44 SVAL(inbuf,smb_flg2) & FLAGS2_DFS_PATHNAMES bit and then
45 send a local path, we have to cope with that too....
47 If conn != NULL then ensure the provided service is
48 the one pointed to by the connection.
50 This version does everything using pointers within one copy of the
51 pathname string, talloced on the struct dfs_path pointer (which
52 must be talloced). This may be too clever to live....
54 **********************************************************************/
56 static NTSTATUS
parse_dfs_path(connection_struct
*conn
,
59 bool allow_broken_path
,
60 struct dfs_path
*pdp
, /* MUST BE TALLOCED */
61 bool *ppath_contains_wcard
)
67 NTSTATUS status
= NT_STATUS_OK
;
73 * This is the only talloc we should need to do
74 * on the struct dfs_path. All the pointers inside
75 * it should point to offsets within this string.
78 pathname_local
= talloc_strdup(pdp
, pathname
);
79 if (!pathname_local
) {
80 return NT_STATUS_NO_MEMORY
;
82 /* Get a pointer to the terminating '\0' */
83 eos_ptr
= &pathname_local
[strlen(pathname_local
)];
84 p
= temp
= pathname_local
;
87 * Non-broken DFS paths *must* start with the
88 * path separator. For Windows this is always '\\',
89 * for posix paths this is always '/'.
92 if (*pathname
== '/') {
93 pdp
->posix_path
= true;
96 pdp
->posix_path
= false;
100 if (allow_broken_path
&& (*pathname
!= sepchar
)) {
101 DEBUG(10,("parse_dfs_path: path %s doesn't start with %c\n",
102 pathname
, sepchar
));
104 * Possibly client sent a local path by mistake.
105 * Try and convert to a local path.
106 * Note that this is an SMB1-only fallback
107 * to cope with known broken SMB1 clients.
110 pdp
->hostname
= eos_ptr
; /* "" */
111 pdp
->servicename
= eos_ptr
; /* "" */
113 /* We've got no info about separators. */
114 pdp
->posix_path
= lp_posix_pathnames();
116 DEBUG(10,("parse_dfs_path: trying to convert %s to a "
123 * Safe to use on talloc'ed string as it only shrinks.
124 * It also doesn't affect the eos_ptr.
126 trim_char(temp
,sepchar
,sepchar
);
128 DEBUG(10,("parse_dfs_path: temp = |%s| after trimming %c's\n",
132 /* Parse out hostname. */
133 p
= strchr_m(temp
,sepchar
);
135 DEBUG(10,("parse_dfs_path: can't parse hostname from path %s\n",
138 * Possibly client sent a local path by mistake.
139 * Try and convert to a local path.
142 pdp
->hostname
= eos_ptr
; /* "" */
143 pdp
->servicename
= eos_ptr
; /* "" */
146 DEBUG(10,("parse_dfs_path: trying to convert %s "
152 pdp
->hostname
= temp
;
154 DEBUG(10,("parse_dfs_path: hostname: %s\n",pdp
->hostname
));
156 /* Parse out servicename. */
158 p
= strchr_m(servicename
,sepchar
);
163 /* Is this really our servicename ? */
164 if (conn
&& !( strequal(servicename
, lp_servicename(talloc_tos(), SNUM(conn
)))
165 || (strequal(servicename
, HOMES_NAME
)
166 && strequal(lp_servicename(talloc_tos(), SNUM(conn
)),
167 get_current_username()) )) ) {
168 DEBUG(10,("parse_dfs_path: %s is not our servicename\n",
172 * Possibly client sent a local path by mistake.
173 * Try and convert to a local path.
176 pdp
->hostname
= eos_ptr
; /* "" */
177 pdp
->servicename
= eos_ptr
; /* "" */
179 /* Repair the path - replace the sepchar's
182 *servicename
= sepchar
;
188 DEBUG(10,("parse_dfs_path: trying to convert %s "
194 pdp
->servicename
= servicename
;
196 DEBUG(10,("parse_dfs_path: servicename: %s\n",pdp
->servicename
));
199 /* Client sent self referral \server\share. */
200 pdp
->reqpath
= eos_ptr
; /* "" */
208 *ppath_contains_wcard
= False
;
212 /* Rest is reqpath. */
213 if (pdp
->posix_path
) {
214 status
= check_path_syntax_posix(pdp
->reqpath
);
217 status
= check_path_syntax_wcard(pdp
->reqpath
,
218 ppath_contains_wcard
);
220 status
= check_path_syntax(pdp
->reqpath
);
224 if (!NT_STATUS_IS_OK(status
)) {
225 DEBUG(10,("parse_dfs_path: '%s' failed with %s\n",
226 p
, nt_errstr(status
) ));
230 DEBUG(10,("parse_dfs_path: rest of the path: %s\n",pdp
->reqpath
));
234 /********************************************************
235 Fake up a connection struct for the VFS layer, for use in
236 applications (such as the python bindings), that do not want the
237 global working directory changed under them.
239 SMB_VFS_CONNECT requires root privileges.
240 *********************************************************/
242 static NTSTATUS
create_conn_struct_as_root(TALLOC_CTX
*ctx
,
243 struct tevent_context
*ev
,
244 struct messaging_context
*msg
,
245 connection_struct
**pconn
,
248 const struct auth_session_info
*session_info
)
250 connection_struct
*conn
;
252 const char *vfs_user
;
253 struct smbd_server_connection
*sconn
;
254 const char *servicename
= lp_const_servicename(snum
);
256 sconn
= talloc_zero(ctx
, struct smbd_server_connection
);
258 return NT_STATUS_NO_MEMORY
;
262 sconn
->msg_ctx
= msg
;
264 conn
= conn_new(sconn
);
267 return NT_STATUS_NO_MEMORY
;
270 /* Now we have conn, we need to make sconn a child of conn,
271 * for a proper talloc tree */
272 talloc_steal(conn
, sconn
);
274 if (snum
== -1 && servicename
== NULL
) {
275 servicename
= "Unknown Service (snum == -1)";
278 connpath
= talloc_strdup(conn
, path
);
281 return NT_STATUS_NO_MEMORY
;
283 connpath
= talloc_string_sub(conn
,
289 return NT_STATUS_NO_MEMORY
;
292 /* needed for smbd_vfs_init() */
294 conn
->params
->service
= snum
;
295 conn
->cnum
= TID_FIELD_INVALID
;
297 if (session_info
!= NULL
) {
298 conn
->session_info
= copy_session_info(conn
, session_info
);
299 if (conn
->session_info
== NULL
) {
300 DEBUG(0, ("copy_serverinfo failed\n"));
302 return NT_STATUS_NO_MEMORY
;
304 vfs_user
= conn
->session_info
->unix_info
->unix_name
;
306 /* use current authenticated user in absence of session_info */
307 vfs_user
= get_current_username();
310 set_conn_connectpath(conn
, connpath
);
313 * New code to check if there's a share security descriptor
314 * added from NT server manager. This is done after the
315 * smb.conf checks are done as we need a uid and token. JRA.
318 if (conn
->session_info
) {
319 share_access_check(conn
->session_info
->security_token
,
321 MAXIMUM_ALLOWED_ACCESS
,
322 &conn
->share_access
);
324 if ((conn
->share_access
& FILE_WRITE_DATA
) == 0) {
325 if ((conn
->share_access
& FILE_READ_DATA
) == 0) {
326 /* No access, read or write. */
327 DEBUG(3,("create_conn_struct: connection to %s "
328 "denied due to security "
332 return NT_STATUS_ACCESS_DENIED
;
334 conn
->read_only
= true;
338 conn
->share_access
= 0;
339 conn
->read_only
= true;
342 if (!smbd_vfs_init(conn
)) {
343 NTSTATUS status
= map_nt_error_from_unix(errno
);
344 DEBUG(0,("create_conn_struct: smbd_vfs_init failed.\n"));
349 /* this must be the first filesystem operation that we do */
350 if (SMB_VFS_CONNECT(conn
, servicename
, vfs_user
) < 0) {
351 DEBUG(0,("VFS connect failed!\n"));
353 return NT_STATUS_UNSUCCESSFUL
;
356 conn
->fs_capabilities
= SMB_VFS_FS_CAPABILITIES(conn
, &conn
->ts_res
);
362 /********************************************************
363 Fake up a connection struct for the VFS layer, for use in
364 applications (such as the python bindings), that do not want the
365 global working directory changed under them.
367 SMB_VFS_CONNECT requires root privileges.
368 *********************************************************/
370 NTSTATUS
create_conn_struct(TALLOC_CTX
*ctx
,
371 struct tevent_context
*ev
,
372 struct messaging_context
*msg
,
373 connection_struct
**pconn
,
376 const struct auth_session_info
*session_info
)
380 status
= create_conn_struct_as_root(ctx
, ev
,
389 /********************************************************
390 Fake up a connection struct for the VFS layer.
391 Note: this performs a vfs connect and CHANGES CWD !!!! JRA.
393 The old working directory is returned on *poldcwd, allocated on ctx.
394 *********************************************************/
396 NTSTATUS
create_conn_struct_cwd(TALLOC_CTX
*ctx
,
397 struct tevent_context
*ev
,
398 struct messaging_context
*msg
,
399 connection_struct
**pconn
,
402 const struct auth_session_info
*session_info
,
405 connection_struct
*conn
;
408 NTSTATUS status
= create_conn_struct(ctx
, ev
,
412 if (!NT_STATUS_IS_OK(status
)) {
417 * Windows seems to insist on doing trans2getdfsreferral() calls on
418 * the IPC$ share as the anonymous user. If we try to chdir as that
419 * user we will fail.... WTF ? JRA.
422 oldcwd
= vfs_GetWd(ctx
, conn
);
423 if (oldcwd
== NULL
) {
424 status
= map_nt_error_from_unix(errno
);
425 DEBUG(3, ("vfs_GetWd failed: %s\n", strerror(errno
)));
430 if (vfs_ChDir(conn
,conn
->connectpath
) != 0) {
431 status
= map_nt_error_from_unix(errno
);
432 DEBUG(3,("create_conn_struct: Can't ChDir to new conn path %s. "
434 conn
->connectpath
, strerror(errno
) ));
445 static void shuffle_strlist(char **list
, int count
)
451 for (i
= count
; i
> 1; i
--) {
452 r
= generate_random() % i
;
460 /**********************************************************************
461 Parse the contents of a symlink to verify if it is an msdfs referral
462 A valid referral is of the form:
464 msdfs:server1\share1,server2\share2
465 msdfs:server1\share1\pathname,server2\share2\pathname
466 msdfs:server1/share1,server2/share2
467 msdfs:server1/share1/pathname,server2/share2/pathname.
469 Note that the alternate paths returned here must be of the canonicalized
473 \server\share\path\to\file,
475 even in posix path mode. This is because we have no knowledge if the
476 server we're referring to understands posix paths.
477 **********************************************************************/
479 static bool parse_msdfs_symlink(TALLOC_CTX
*ctx
,
482 struct referral
**preflist
,
487 char **alt_path
= NULL
;
489 struct referral
*reflist
;
492 temp
= talloc_strdup(ctx
, target
);
496 prot
= strtok_r(temp
, ":", &saveptr
);
498 DEBUG(0,("parse_msdfs_symlink: invalid path !\n"));
502 alt_path
= talloc_array(ctx
, char *, MAX_REFERRAL_COUNT
);
507 /* parse out the alternate paths */
508 while((count
<MAX_REFERRAL_COUNT
) &&
509 ((alt_path
[count
] = strtok_r(NULL
, ",", &saveptr
)) != NULL
)) {
513 /* shuffle alternate paths */
514 if (lp_msdfs_shuffle_referrals(snum
)) {
515 shuffle_strlist(alt_path
, count
);
518 DEBUG(10,("parse_msdfs_symlink: count=%d\n", count
));
521 reflist
= *preflist
= talloc_zero_array(ctx
,
522 struct referral
, count
);
523 if(reflist
== NULL
) {
524 TALLOC_FREE(alt_path
);
528 reflist
= *preflist
= NULL
;
531 for(i
=0;i
<count
;i
++) {
534 /* Canonicalize link target.
535 * Replace all /'s in the path by a \ */
536 string_replace(alt_path
[i
], '/', '\\');
538 /* Remove leading '\\'s */
540 while (*p
&& (*p
== '\\')) {
544 reflist
[i
].alternate_path
= talloc_asprintf(ctx
,
547 if (!reflist
[i
].alternate_path
) {
551 reflist
[i
].proximity
= 0;
552 reflist
[i
].ttl
= REFERRAL_TTL
;
553 DEBUG(10, ("parse_msdfs_symlink: Created alt path: %s\n",
554 reflist
[i
].alternate_path
));
559 TALLOC_FREE(alt_path
);
563 /**********************************************************************
564 Returns true if the unix path is a valid msdfs symlink and also
565 returns the target string from inside the link.
566 **********************************************************************/
568 static bool is_msdfs_link_internal(TALLOC_CTX
*ctx
,
569 connection_struct
*conn
,
571 char **pp_link_target
,
572 SMB_STRUCT_STAT
*sbufp
)
574 int referral_len
= 0;
575 #if defined(HAVE_BROKEN_READLINK)
576 char link_target_buf
[PATH_MAX
];
578 char link_target_buf
[7];
581 char *link_target
= NULL
;
582 struct smb_filename smb_fname
;
584 if (pp_link_target
) {
586 link_target
= talloc_array(ctx
, char, bufsize
);
590 *pp_link_target
= link_target
;
592 bufsize
= sizeof(link_target_buf
);
593 link_target
= link_target_buf
;
596 ZERO_STRUCT(smb_fname
);
597 smb_fname
.base_name
= discard_const_p(char, path
);
599 if (SMB_VFS_LSTAT(conn
, &smb_fname
) != 0) {
600 DEBUG(5,("is_msdfs_link_read_target: %s does not exist.\n",
604 if (!S_ISLNK(smb_fname
.st
.st_ex_mode
)) {
605 DEBUG(5,("is_msdfs_link_read_target: %s is not a link.\n",
610 *sbufp
= smb_fname
.st
;
613 referral_len
= SMB_VFS_READLINK(conn
, path
, link_target
, bufsize
- 1);
614 if (referral_len
== -1) {
615 DEBUG(0,("is_msdfs_link_read_target: Error reading "
616 "msdfs link %s: %s\n",
617 path
, strerror(errno
)));
620 link_target
[referral_len
] = '\0';
622 DEBUG(5,("is_msdfs_link_internal: %s -> %s\n",path
,
625 if (!strnequal(link_target
, "msdfs:", 6)) {
632 if (link_target
!= link_target_buf
) {
633 TALLOC_FREE(link_target
);
638 /**********************************************************************
639 Returns true if the unix path is a valid msdfs symlink.
640 **********************************************************************/
642 bool is_msdfs_link(connection_struct
*conn
,
644 SMB_STRUCT_STAT
*sbufp
)
646 return is_msdfs_link_internal(talloc_tos(),
653 /*****************************************************************
654 Used by other functions to decide if a dfs path is remote,
655 and to get the list of referred locations for that remote path.
657 search_flag: For findfirsts, dfs links themselves are not
658 redirected, but paths beyond the links are. For normal smb calls,
659 even dfs links need to be redirected.
661 consumedcntp: how much of the dfs path is being redirected. the client
662 should try the remaining path on the redirected server.
664 If this returns NT_STATUS_PATH_NOT_COVERED the contents of the msdfs
665 link redirect are in targetpath.
666 *****************************************************************/
668 static NTSTATUS
dfs_path_lookup(TALLOC_CTX
*ctx
,
669 connection_struct
*conn
,
670 const char *dfspath
, /* Incoming complete dfs path */
671 const struct dfs_path
*pdp
, /* Parsed out
672 server+share+extrapath. */
675 char **pp_targetpath
)
680 struct smb_filename
*smb_fname
= NULL
;
681 char *canon_dfspath
= NULL
; /* Canonicalized dfs path. (only '/'
684 DEBUG(10,("dfs_path_lookup: Conn path = %s reqpath = %s\n",
685 conn
->connectpath
, pdp
->reqpath
));
688 * Note the unix path conversion here we're doing we
689 * throw away. We're looking for a symlink for a dfs
690 * resolution, if we don't find it we'll do another
691 * unix_convert later in the codepath.
694 status
= unix_convert(ctx
, conn
, pdp
->reqpath
, &smb_fname
,
697 if (!NT_STATUS_IS_OK(status
)) {
698 if (!NT_STATUS_EQUAL(status
,
699 NT_STATUS_OBJECT_PATH_NOT_FOUND
)) {
702 if (smb_fname
== NULL
|| smb_fname
->base_name
== NULL
) {
707 /* Optimization - check if we can redirect the whole path. */
709 if (is_msdfs_link_internal(ctx
, conn
, smb_fname
->base_name
,
710 pp_targetpath
, NULL
)) {
711 /* XX_ALLOW_WCARD_XXX is called from search functions. */
713 (UCF_COND_ALLOW_WCARD_LCOMP
|
714 UCF_ALWAYS_ALLOW_WCARD_LCOMP
)) {
715 DEBUG(6,("dfs_path_lookup (FindFirst) No redirection "
716 "for dfs link %s.\n", dfspath
));
717 status
= NT_STATUS_OK
;
721 DEBUG(6,("dfs_path_lookup: %s resolves to a "
722 "valid dfs link %s.\n", dfspath
,
723 pp_targetpath
? *pp_targetpath
: ""));
726 *consumedcntp
= strlen(dfspath
);
728 status
= NT_STATUS_PATH_NOT_COVERED
;
732 /* Prepare to test only for '/' components in the given path,
733 * so if a Windows path replace all '\\' characters with '/'.
734 * For a POSIX DFS path we know all separators are already '/'. */
736 canon_dfspath
= talloc_strdup(ctx
, dfspath
);
737 if (!canon_dfspath
) {
738 status
= NT_STATUS_NO_MEMORY
;
741 if (!pdp
->posix_path
) {
742 string_replace(canon_dfspath
, '\\', '/');
746 * localpath comes out of unix_convert, so it has
747 * no trailing backslash. Make sure that canon_dfspath hasn't either.
748 * Fix for bug #4860 from Jan Martin <Jan.Martin@rwedea.com>.
751 trim_char(canon_dfspath
,0,'/');
754 * Redirect if any component in the path is a link.
755 * We do this by walking backwards through the
756 * local path, chopping off the last component
757 * in both the local path and the canonicalized
758 * DFS path. If we hit a DFS link then we're done.
761 p
= strrchr_m(smb_fname
->base_name
, '/');
763 q
= strrchr_m(canon_dfspath
, '/');
772 if (is_msdfs_link_internal(ctx
, conn
,
773 smb_fname
->base_name
, pp_targetpath
,
775 DEBUG(4, ("dfs_path_lookup: Redirecting %s because "
776 "parent %s is dfs link\n", dfspath
,
777 smb_fname_str_dbg(smb_fname
)));
780 *consumedcntp
= strlen(canon_dfspath
);
781 DEBUG(10, ("dfs_path_lookup: Path consumed: %s "
787 status
= NT_STATUS_PATH_NOT_COVERED
;
791 /* Step back on the filesystem. */
792 p
= strrchr_m(smb_fname
->base_name
, '/');
795 /* And in the canonicalized dfs path. */
796 q
= strrchr_m(canon_dfspath
, '/');
800 status
= NT_STATUS_OK
;
802 TALLOC_FREE(smb_fname
);
806 /*****************************************************************
807 Decides if a dfs pathname should be redirected or not.
808 If not, the pathname is converted to a tcon-relative local unix path
810 search_wcard_flag: this flag performs 2 functions both related
811 to searches. See resolve_dfs_path() and parse_dfs_path_XX()
814 This function can return NT_STATUS_OK, meaning use the returned path as-is
815 (mapped into a local path).
816 or NT_STATUS_NOT_COVERED meaning return a DFS redirect, or
817 any other NT_STATUS error which is a genuine error to be
818 returned to the client.
819 *****************************************************************/
821 static NTSTATUS
dfs_redirect(TALLOC_CTX
*ctx
,
822 connection_struct
*conn
,
825 bool allow_broken_path
,
827 bool *ppath_contains_wcard
)
830 bool search_wcard_flag
= (ucf_flags
&
831 (UCF_COND_ALLOW_WCARD_LCOMP
|UCF_ALWAYS_ALLOW_WCARD_LCOMP
));
832 struct dfs_path
*pdp
= talloc(ctx
, struct dfs_path
);
835 return NT_STATUS_NO_MEMORY
;
838 status
= parse_dfs_path(conn
, path_in
, search_wcard_flag
,
839 allow_broken_path
, pdp
,
840 ppath_contains_wcard
);
841 if (!NT_STATUS_IS_OK(status
)) {
846 if (pdp
->reqpath
[0] == '\0') {
848 *pp_path_out
= talloc_strdup(ctx
, "");
850 return NT_STATUS_NO_MEMORY
;
852 DEBUG(5,("dfs_redirect: self-referral.\n"));
856 /* If dfs pathname for a non-dfs share, convert to tcon-relative
857 path and return OK */
859 if (!lp_msdfs_root(SNUM(conn
))) {
860 *pp_path_out
= talloc_strdup(ctx
, pdp
->reqpath
);
863 return NT_STATUS_NO_MEMORY
;
868 /* If it looked like a local path (zero hostname/servicename)
869 * just treat as a tcon-relative path. */
871 if (pdp
->hostname
[0] == '\0' && pdp
->servicename
[0] == '\0') {
872 *pp_path_out
= talloc_strdup(ctx
, pdp
->reqpath
);
875 return NT_STATUS_NO_MEMORY
;
880 if (!( strequal(pdp
->servicename
, lp_servicename(talloc_tos(), SNUM(conn
)))
881 || (strequal(pdp
->servicename
, HOMES_NAME
)
882 && strequal(lp_servicename(talloc_tos(), SNUM(conn
)),
883 conn
->session_info
->unix_info
->sanitized_username
) )) ) {
885 /* The given sharename doesn't match this connection. */
888 return NT_STATUS_OBJECT_PATH_NOT_FOUND
;
891 status
= dfs_path_lookup(ctx
, conn
, path_in
, pdp
,
892 ucf_flags
, NULL
, NULL
);
893 if (!NT_STATUS_IS_OK(status
)) {
894 if (NT_STATUS_EQUAL(status
, NT_STATUS_PATH_NOT_COVERED
)) {
895 DEBUG(3,("dfs_redirect: Redirecting %s\n", path_in
));
897 DEBUG(10,("dfs_redirect: dfs_path_lookup "
898 "failed for %s with %s\n",
899 path_in
, nt_errstr(status
) ));
904 DEBUG(3,("dfs_redirect: Not redirecting %s.\n", path_in
));
906 /* Form non-dfs tcon-relative path */
907 *pp_path_out
= talloc_strdup(ctx
, pdp
->reqpath
);
910 return NT_STATUS_NO_MEMORY
;
913 DEBUG(3,("dfs_redirect: Path %s converted to non-dfs path %s\n",
920 /**********************************************************************
921 Return a self referral.
922 **********************************************************************/
924 static NTSTATUS
self_ref(TALLOC_CTX
*ctx
,
925 const char *dfs_path
,
926 struct junction_map
*jucn
,
928 bool *self_referralp
)
930 struct referral
*ref
;
932 *self_referralp
= True
;
934 jucn
->referral_count
= 1;
935 if((ref
= talloc_zero(ctx
, struct referral
)) == NULL
) {
936 return NT_STATUS_NO_MEMORY
;
939 ref
->alternate_path
= talloc_strdup(ctx
, dfs_path
);
940 if (!ref
->alternate_path
) {
942 return NT_STATUS_NO_MEMORY
;
945 ref
->ttl
= REFERRAL_TTL
;
946 jucn
->referral_list
= ref
;
947 *consumedcntp
= strlen(dfs_path
);
951 /**********************************************************************
952 Gets valid referrals for a dfs path and fills up the
953 junction_map structure.
954 **********************************************************************/
956 NTSTATUS
get_referred_path(TALLOC_CTX
*ctx
,
957 const char *dfs_path
,
958 const struct tsocket_address
*remote_address
,
959 const struct tsocket_address
*local_address
,
960 bool allow_broken_path
,
961 struct junction_map
*jucn
,
963 bool *self_referralp
)
965 struct connection_struct
*conn
;
966 char *targetpath
= NULL
;
968 NTSTATUS status
= NT_STATUS_NOT_FOUND
;
970 struct dfs_path
*pdp
= talloc(ctx
, struct dfs_path
);
974 return NT_STATUS_NO_MEMORY
;
977 *self_referralp
= False
;
979 status
= parse_dfs_path(NULL
, dfs_path
, False
, allow_broken_path
,
981 if (!NT_STATUS_IS_OK(status
)) {
985 jucn
->service_name
= talloc_strdup(ctx
, pdp
->servicename
);
986 jucn
->volume_name
= talloc_strdup(ctx
, pdp
->reqpath
);
987 if (!jucn
->service_name
|| !jucn
->volume_name
) {
989 return NT_STATUS_NO_MEMORY
;
992 /* Verify the share is a dfs root */
993 snum
= lp_servicenumber(jucn
->service_name
);
995 char *service_name
= NULL
;
996 if ((snum
= find_service(ctx
, jucn
->service_name
, &service_name
)) < 0) {
997 return NT_STATUS_NOT_FOUND
;
1000 return NT_STATUS_NO_MEMORY
;
1002 TALLOC_FREE(jucn
->service_name
);
1003 jucn
->service_name
= talloc_strdup(ctx
, service_name
);
1004 if (!jucn
->service_name
) {
1006 return NT_STATUS_NO_MEMORY
;
1010 if (!lp_msdfs_root(snum
) && (*lp_msdfs_proxy(talloc_tos(), snum
) == '\0')) {
1011 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not "
1013 pdp
->servicename
, dfs_path
));
1015 return NT_STATUS_NOT_FOUND
;
1019 * Self referrals are tested with a anonymous IPC connection and
1020 * a GET_DFS_REFERRAL call to \\server\share. (which means
1021 * dp.reqpath[0] points to an empty string). create_conn_struct cd's
1022 * into the directory and will fail if it cannot (as the anonymous
1023 * user). Cope with this.
1026 if (pdp
->reqpath
[0] == '\0') {
1028 struct referral
*ref
;
1031 if (*lp_msdfs_proxy(talloc_tos(), snum
) == '\0') {
1033 return self_ref(ctx
,
1041 * It's an msdfs proxy share. Redirect to
1042 * the configured target share.
1045 tmp
= talloc_asprintf(talloc_tos(), "msdfs:%s",
1046 lp_msdfs_proxy(talloc_tos(), snum
));
1049 return NT_STATUS_NO_MEMORY
;
1052 if (!parse_msdfs_symlink(ctx
, snum
, tmp
, &ref
, &refcount
)) {
1055 return NT_STATUS_INVALID_PARAMETER
;
1058 jucn
->referral_count
= refcount
;
1059 jucn
->referral_list
= ref
;
1060 *consumedcntp
= strlen(dfs_path
);
1062 return NT_STATUS_OK
;
1065 status
= create_conn_struct_cwd(ctx
,
1066 server_event_context(),
1067 server_messaging_context(),
1069 lp_path(talloc_tos(), snum
), NULL
, &oldpath
);
1070 if (!NT_STATUS_IS_OK(status
)) {
1078 * The remote and local address should be passed down to
1079 * create_conn_struct_cwd.
1081 if (conn
->sconn
->remote_address
== NULL
) {
1082 conn
->sconn
->remote_address
=
1083 tsocket_address_copy(remote_address
, conn
->sconn
);
1084 if (conn
->sconn
->remote_address
== NULL
) {
1086 return NT_STATUS_NO_MEMORY
;
1089 if (conn
->sconn
->local_address
== NULL
) {
1090 conn
->sconn
->local_address
=
1091 tsocket_address_copy(local_address
, conn
->sconn
);
1092 if (conn
->sconn
->local_address
== NULL
) {
1094 return NT_STATUS_NO_MEMORY
;
1098 /* If this is a DFS path dfs_lookup should return
1099 * NT_STATUS_PATH_NOT_COVERED. */
1101 status
= dfs_path_lookup(ctx
, conn
, dfs_path
, pdp
,
1102 0, consumedcntp
, &targetpath
);
1104 if (!NT_STATUS_EQUAL(status
, NT_STATUS_PATH_NOT_COVERED
)) {
1105 DEBUG(3,("get_referred_path: No valid referrals for path %s\n",
1107 if (NT_STATUS_IS_OK(status
)) {
1109 * We are in an error path here (we
1110 * know it's not a DFS path), but
1111 * dfs_path_lookup() can return
1112 * NT_STATUS_OK. Ensure we always
1113 * return a valid error code.
1115 * #9588 - ACLs are not inherited to directories
1118 status
= NT_STATUS_NOT_FOUND
;
1123 /* We know this is a valid dfs link. Parse the targetpath. */
1124 if (!parse_msdfs_symlink(ctx
, snum
, targetpath
,
1125 &jucn
->referral_list
,
1126 &jucn
->referral_count
)) {
1127 DEBUG(3,("get_referred_path: failed to parse symlink "
1128 "target %s\n", targetpath
));
1129 status
= NT_STATUS_NOT_FOUND
;
1133 status
= NT_STATUS_OK
;
1135 vfs_ChDir(conn
, oldpath
);
1136 SMB_VFS_DISCONNECT(conn
);
1142 /******************************************************************
1143 Set up the DFS referral for the dfs pathname. This call returns
1144 the amount of the path covered by this server, and where the
1145 client should be redirected to. This is the meat of the
1146 TRANS2_GET_DFS_REFERRAL call.
1147 ******************************************************************/
1149 int setup_dfs_referral(connection_struct
*orig_conn
,
1150 const char *dfs_path
,
1151 int max_referral_level
,
1152 char **ppdata
, NTSTATUS
*pstatus
)
1154 char *pdata
= *ppdata
;
1156 struct dfs_GetDFSReferral
*r
;
1157 DATA_BLOB blob
= data_blob_null
;
1159 enum ndr_err_code ndr_err
;
1161 r
= talloc_zero(talloc_tos(), struct dfs_GetDFSReferral
);
1163 *pstatus
= NT_STATUS_NO_MEMORY
;
1167 r
->in
.req
.max_referral_level
= max_referral_level
;
1168 r
->in
.req
.servername
= talloc_strdup(r
, dfs_path
);
1169 if (r
->in
.req
.servername
== NULL
) {
1171 *pstatus
= NT_STATUS_NO_MEMORY
;
1175 status
= SMB_VFS_GET_DFS_REFERRALS(orig_conn
, r
);
1176 if (!NT_STATUS_IS_OK(status
)) {
1182 ndr_err
= ndr_push_struct_blob(&blob
, r
,
1184 (ndr_push_flags_fn_t
)ndr_push_dfs_referral_resp
);
1185 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
1187 *pstatus
= NT_STATUS_INVALID_PARAMETER
;
1191 pdata
= (char *)SMB_REALLOC(pdata
, blob
.length
);
1194 DEBUG(0,("referral setup:"
1195 "malloc failed for Realloc!\n"));
1199 reply_size
= blob
.length
;
1200 memcpy(pdata
, blob
.data
, blob
.length
);
1203 *pstatus
= NT_STATUS_OK
;
1207 /**********************************************************************
1208 The following functions are called by the NETDFS RPC pipe functions
1209 **********************************************************************/
1211 /*********************************************************************
1212 Creates a junction structure from a DFS pathname
1213 **********************************************************************/
1215 bool create_junction(TALLOC_CTX
*ctx
,
1216 const char *dfs_path
,
1217 bool allow_broken_path
,
1218 struct junction_map
*jucn
)
1222 struct dfs_path
*pdp
= talloc(ctx
,struct dfs_path
);
1228 status
= parse_dfs_path(NULL
, dfs_path
, False
, allow_broken_path
,
1230 if (!NT_STATUS_IS_OK(status
)) {
1234 /* check if path is dfs : validate first token */
1235 if (!is_myname_or_ipaddr(pdp
->hostname
)) {
1236 DEBUG(4,("create_junction: Invalid hostname %s "
1238 pdp
->hostname
, dfs_path
));
1243 /* Check for a non-DFS share */
1244 snum
= lp_servicenumber(pdp
->servicename
);
1246 if(snum
< 0 || !lp_msdfs_root(snum
)) {
1247 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1253 jucn
->service_name
= talloc_strdup(ctx
, pdp
->servicename
);
1254 jucn
->volume_name
= talloc_strdup(ctx
, pdp
->reqpath
);
1255 jucn
->comment
= lp_comment(ctx
, snum
);
1258 if (!jucn
->service_name
|| !jucn
->volume_name
|| ! jucn
->comment
) {
1264 /**********************************************************************
1265 Forms a valid Unix pathname from the junction
1266 **********************************************************************/
1268 static bool junction_to_local_path(const struct junction_map
*jucn
,
1270 connection_struct
**conn_out
,
1276 snum
= lp_servicenumber(jucn
->service_name
);
1280 status
= create_conn_struct_cwd(talloc_tos(),
1281 server_event_context(),
1282 server_messaging_context(),
1284 snum
, lp_path(talloc_tos(), snum
), NULL
, oldpath
);
1285 if (!NT_STATUS_IS_OK(status
)) {
1289 *pp_path_out
= talloc_asprintf(*conn_out
,
1291 lp_path(talloc_tos(), snum
),
1293 if (!*pp_path_out
) {
1294 vfs_ChDir(*conn_out
, *oldpath
);
1295 SMB_VFS_DISCONNECT(*conn_out
);
1296 conn_free(*conn_out
);
1302 bool create_msdfs_link(const struct junction_map
*jucn
)
1306 char *msdfs_link
= NULL
;
1307 connection_struct
*conn
;
1309 bool insert_comma
= False
;
1312 if(!junction_to_local_path(jucn
, &path
, &conn
, &cwd
)) {
1316 /* Form the msdfs_link contents */
1317 msdfs_link
= talloc_strdup(conn
, "msdfs:");
1321 for(i
=0; i
<jucn
->referral_count
; i
++) {
1322 char *refpath
= jucn
->referral_list
[i
].alternate_path
;
1324 /* Alternate paths always use Windows separators. */
1325 trim_char(refpath
, '\\', '\\');
1326 if(*refpath
== '\0') {
1328 insert_comma
= False
;
1332 if (i
> 0 && insert_comma
) {
1333 msdfs_link
= talloc_asprintf_append_buffer(msdfs_link
,
1337 msdfs_link
= talloc_asprintf_append_buffer(msdfs_link
,
1345 if (!insert_comma
) {
1346 insert_comma
= True
;
1350 DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
1353 if(SMB_VFS_SYMLINK(conn
, msdfs_link
, path
) < 0) {
1354 if (errno
== EEXIST
) {
1355 struct smb_filename
*smb_fname
;
1357 smb_fname
= synthetic_smb_fname(talloc_tos(),
1362 if (smb_fname
== NULL
) {
1367 if(SMB_VFS_UNLINK(conn
, smb_fname
)!=0) {
1368 TALLOC_FREE(smb_fname
);
1371 TALLOC_FREE(smb_fname
);
1373 if (SMB_VFS_SYMLINK(conn
, msdfs_link
, path
) < 0) {
1374 DEBUG(1,("create_msdfs_link: symlink failed "
1375 "%s -> %s\nError: %s\n",
1376 path
, msdfs_link
, strerror(errno
)));
1384 vfs_ChDir(conn
, cwd
);
1385 SMB_VFS_DISCONNECT(conn
);
1390 bool remove_msdfs_link(const struct junction_map
*jucn
)
1394 connection_struct
*conn
;
1396 struct smb_filename
*smb_fname
;
1398 if (!junction_to_local_path(jucn
, &path
, &conn
, &cwd
)) {
1402 smb_fname
= synthetic_smb_fname(talloc_tos(),
1407 if (smb_fname
== NULL
) {
1412 if( SMB_VFS_UNLINK(conn
, smb_fname
) == 0 ) {
1416 TALLOC_FREE(smb_fname
);
1417 vfs_ChDir(conn
, cwd
);
1418 SMB_VFS_DISCONNECT(conn
);
1423 /*********************************************************************
1424 Return the number of DFS links at the root of this share.
1425 *********************************************************************/
1427 static int count_dfs_links(TALLOC_CTX
*ctx
, int snum
)
1431 const char *dname
= NULL
;
1432 char *talloced
= NULL
;
1433 const char *connect_path
= lp_path(talloc_tos(), snum
);
1434 const char *msdfs_proxy
= lp_msdfs_proxy(talloc_tos(), snum
);
1435 connection_struct
*conn
;
1438 struct smb_filename
*smb_fname
= NULL
;
1440 if(*connect_path
== '\0') {
1445 * Fake up a connection struct for the VFS layer.
1448 status
= create_conn_struct_cwd(talloc_tos(),
1449 server_event_context(),
1450 server_messaging_context(),
1452 snum
, connect_path
, NULL
, &cwd
);
1453 if (!NT_STATUS_IS_OK(status
)) {
1454 DEBUG(3, ("create_conn_struct failed: %s\n",
1455 nt_errstr(status
)));
1459 /* Count a link for the msdfs root - convention */
1462 /* No more links if this is an msdfs proxy. */
1463 if (*msdfs_proxy
!= '\0') {
1467 smb_fname
= synthetic_smb_fname(talloc_tos(),
1472 if (smb_fname
== NULL
) {
1476 /* Now enumerate all dfs links */
1477 dirp
= SMB_VFS_OPENDIR(conn
, smb_fname
, NULL
, 0);
1482 while ((dname
= vfs_readdirname(conn
, dirp
, NULL
, &talloced
))
1484 if (is_msdfs_link(conn
,
1489 TALLOC_FREE(talloced
);
1492 SMB_VFS_CLOSEDIR(conn
,dirp
);
1495 TALLOC_FREE(smb_fname
);
1496 vfs_ChDir(conn
, cwd
);
1497 SMB_VFS_DISCONNECT(conn
);
1502 /*********************************************************************
1503 *********************************************************************/
1505 static int form_junctions(TALLOC_CTX
*ctx
,
1507 struct junction_map
*jucn
,
1512 const char *dname
= NULL
;
1513 char *talloced
= NULL
;
1514 const char *connect_path
= lp_path(talloc_tos(), snum
);
1515 char *service_name
= lp_servicename(talloc_tos(), snum
);
1516 const char *msdfs_proxy
= lp_msdfs_proxy(talloc_tos(), snum
);
1517 connection_struct
*conn
;
1518 struct referral
*ref
= NULL
;
1520 struct smb_filename
*smb_fname
= NULL
;
1523 if (jn_remain
== 0) {
1527 if(*connect_path
== '\0') {
1532 * Fake up a connection struct for the VFS layer.
1535 status
= create_conn_struct_cwd(ctx
,
1536 server_event_context(),
1537 server_messaging_context(),
1538 &conn
, snum
, connect_path
, NULL
,
1540 if (!NT_STATUS_IS_OK(status
)) {
1541 DEBUG(3, ("create_conn_struct failed: %s\n",
1542 nt_errstr(status
)));
1546 /* form a junction for the msdfs root - convention
1547 DO NOT REMOVE THIS: NT clients will not work with us
1548 if this is not present
1550 jucn
[cnt
].service_name
= talloc_strdup(ctx
,service_name
);
1551 jucn
[cnt
].volume_name
= talloc_strdup(ctx
, "");
1552 if (!jucn
[cnt
].service_name
|| !jucn
[cnt
].volume_name
) {
1555 jucn
[cnt
].comment
= "";
1556 jucn
[cnt
].referral_count
= 1;
1558 ref
= jucn
[cnt
].referral_list
= talloc_zero(ctx
, struct referral
);
1559 if (jucn
[cnt
].referral_list
== NULL
) {
1564 ref
->ttl
= REFERRAL_TTL
;
1565 if (*msdfs_proxy
!= '\0') {
1566 ref
->alternate_path
= talloc_strdup(ctx
,
1569 ref
->alternate_path
= talloc_asprintf(ctx
,
1571 get_local_machine_name(),
1575 if (!ref
->alternate_path
) {
1580 /* Don't enumerate if we're an msdfs proxy. */
1581 if (*msdfs_proxy
!= '\0') {
1585 smb_fname
= synthetic_smb_fname(talloc_tos(),
1590 if (smb_fname
== NULL
) {
1594 /* Now enumerate all dfs links */
1595 dirp
= SMB_VFS_OPENDIR(conn
, smb_fname
, NULL
, 0);
1600 while ((dname
= vfs_readdirname(conn
, dirp
, NULL
, &talloced
))
1602 char *link_target
= NULL
;
1603 if (cnt
>= jn_remain
) {
1604 DEBUG(2, ("form_junctions: ran out of MSDFS "
1606 TALLOC_FREE(talloced
);
1609 if (is_msdfs_link_internal(ctx
,
1611 dname
, &link_target
,
1613 if (parse_msdfs_symlink(ctx
, snum
,
1615 &jucn
[cnt
].referral_list
,
1616 &jucn
[cnt
].referral_count
)) {
1618 jucn
[cnt
].service_name
= talloc_strdup(ctx
,
1620 jucn
[cnt
].volume_name
= talloc_strdup(ctx
,
1622 if (!jucn
[cnt
].service_name
||
1623 !jucn
[cnt
].volume_name
) {
1624 TALLOC_FREE(talloced
);
1627 jucn
[cnt
].comment
= "";
1630 TALLOC_FREE(link_target
);
1632 TALLOC_FREE(talloced
);
1638 SMB_VFS_CLOSEDIR(conn
,dirp
);
1641 TALLOC_FREE(smb_fname
);
1642 vfs_ChDir(conn
, cwd
);
1647 struct junction_map
*enum_msdfs_links(TALLOC_CTX
*ctx
, size_t *p_num_jn
)
1649 struct junction_map
*jn
= NULL
;
1651 size_t jn_count
= 0;
1655 if(!lp_host_msdfs()) {
1659 /* Ensure all the usershares are loaded. */
1661 load_registry_shares();
1662 sharecount
= load_usershare_shares(NULL
, connections_snum_used
);
1665 for(i
=0;i
< sharecount
;i
++) {
1666 if(lp_msdfs_root(i
)) {
1667 jn_count
+= count_dfs_links(ctx
, i
);
1670 if (jn_count
== 0) {
1673 jn
= talloc_array(ctx
, struct junction_map
, jn_count
);
1677 for(i
=0; i
< sharecount
; i
++) {
1678 if (*p_num_jn
>= jn_count
) {
1681 if(lp_msdfs_root(i
)) {
1682 *p_num_jn
+= form_junctions(ctx
, i
,
1684 jn_count
- *p_num_jn
);
1690 /******************************************************************************
1691 Core function to resolve a dfs pathname possibly containing a wildcard. If
1692 ppath_contains_wcard != NULL, it will be set to true if a wildcard is
1693 detected during dfs resolution.
1694 ******************************************************************************/
1696 NTSTATUS
resolve_dfspath_wcard(TALLOC_CTX
*ctx
,
1697 connection_struct
*conn
,
1699 const char *name_in
,
1701 bool allow_broken_path
,
1703 bool *ppath_contains_wcard
)
1705 bool path_contains_wcard
;
1706 NTSTATUS status
= NT_STATUS_OK
;
1708 if (dfs_pathnames
) {
1709 status
= dfs_redirect(ctx
,
1715 &path_contains_wcard
);
1717 if (NT_STATUS_IS_OK(status
) && ppath_contains_wcard
!= NULL
) {
1718 *ppath_contains_wcard
= path_contains_wcard
;
1722 * Cheat and just return a copy of the in ptr.
1723 * Once srvstr_get_path() uses talloc it'll
1724 * be a talloced ptr anyway.
1726 *pp_name_out
= discard_const_p(char, name_in
);