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
,
403 struct smb_filename
**poldcwd_fname
)
405 connection_struct
*conn
;
406 struct smb_filename
*oldcwd_fname
= NULL
;
407 struct smb_filename smb_fname_connectpath
= {0};
409 NTSTATUS status
= create_conn_struct(ctx
, ev
,
413 if (!NT_STATUS_IS_OK(status
)) {
418 * Windows seems to insist on doing trans2getdfsreferral() calls on
419 * the IPC$ share as the anonymous user. If we try to chdir as that
420 * user we will fail.... WTF ? JRA.
423 oldcwd_fname
= vfs_GetWd(ctx
, conn
);
424 if (oldcwd_fname
== NULL
) {
425 status
= map_nt_error_from_unix(errno
);
426 DEBUG(3, ("vfs_GetWd failed: %s\n", strerror(errno
)));
431 smb_fname_connectpath
= (struct smb_filename
) {
432 .base_name
= conn
->connectpath
435 if (vfs_ChDir(conn
, &smb_fname_connectpath
) != 0) {
436 status
= map_nt_error_from_unix(errno
);
437 DEBUG(3,("create_conn_struct: Can't ChDir to new conn path %s. "
439 conn
->connectpath
, strerror(errno
) ));
440 TALLOC_FREE(oldcwd_fname
);
446 *poldcwd_fname
= oldcwd_fname
;
451 static void shuffle_strlist(char **list
, int count
)
457 for (i
= count
; i
> 1; i
--) {
458 r
= generate_random() % i
;
466 /**********************************************************************
467 Parse the contents of a symlink to verify if it is an msdfs referral
468 A valid referral is of the form:
470 msdfs:server1\share1,server2\share2
471 msdfs:server1\share1\pathname,server2\share2\pathname
472 msdfs:server1/share1,server2/share2
473 msdfs:server1/share1/pathname,server2/share2/pathname.
475 Note that the alternate paths returned here must be of the canonicalized
479 \server\share\path\to\file,
481 even in posix path mode. This is because we have no knowledge if the
482 server we're referring to understands posix paths.
483 **********************************************************************/
485 static bool parse_msdfs_symlink(TALLOC_CTX
*ctx
,
488 struct referral
**preflist
,
493 char **alt_path
= NULL
;
495 struct referral
*reflist
;
498 temp
= talloc_strdup(ctx
, target
);
502 prot
= strtok_r(temp
, ":", &saveptr
);
504 DEBUG(0,("parse_msdfs_symlink: invalid path !\n"));
508 alt_path
= talloc_array(ctx
, char *, MAX_REFERRAL_COUNT
);
513 /* parse out the alternate paths */
514 while((count
<MAX_REFERRAL_COUNT
) &&
515 ((alt_path
[count
] = strtok_r(NULL
, ",", &saveptr
)) != NULL
)) {
519 /* shuffle alternate paths */
520 if (lp_msdfs_shuffle_referrals(snum
)) {
521 shuffle_strlist(alt_path
, count
);
524 DEBUG(10,("parse_msdfs_symlink: count=%d\n", count
));
527 reflist
= *preflist
= talloc_zero_array(ctx
,
528 struct referral
, count
);
529 if(reflist
== NULL
) {
530 TALLOC_FREE(alt_path
);
534 reflist
= *preflist
= NULL
;
537 for(i
=0;i
<count
;i
++) {
540 /* Canonicalize link target.
541 * Replace all /'s in the path by a \ */
542 string_replace(alt_path
[i
], '/', '\\');
544 /* Remove leading '\\'s */
546 while (*p
&& (*p
== '\\')) {
550 reflist
[i
].alternate_path
= talloc_asprintf(ctx
,
553 if (!reflist
[i
].alternate_path
) {
557 reflist
[i
].proximity
= 0;
558 reflist
[i
].ttl
= REFERRAL_TTL
;
559 DEBUG(10, ("parse_msdfs_symlink: Created alt path: %s\n",
560 reflist
[i
].alternate_path
));
565 TALLOC_FREE(alt_path
);
569 /**********************************************************************
570 Returns true if the unix path is a valid msdfs symlink and also
571 returns the target string from inside the link.
572 **********************************************************************/
574 static bool is_msdfs_link_internal(TALLOC_CTX
*ctx
,
575 connection_struct
*conn
,
576 struct smb_filename
*smb_fname
,
577 char **pp_link_target
)
579 int referral_len
= 0;
580 #if defined(HAVE_BROKEN_READLINK)
581 char link_target_buf
[PATH_MAX
];
583 char link_target_buf
[7];
586 char *link_target
= NULL
;
588 if (pp_link_target
) {
590 link_target
= talloc_array(ctx
, char, bufsize
);
594 *pp_link_target
= link_target
;
596 bufsize
= sizeof(link_target_buf
);
597 link_target
= link_target_buf
;
600 if (SMB_VFS_LSTAT(conn
, smb_fname
) != 0) {
601 DEBUG(5,("is_msdfs_link_read_target: %s does not exist.\n",
602 smb_fname
->base_name
));
605 if (!S_ISLNK(smb_fname
->st
.st_ex_mode
)) {
606 DEBUG(5,("is_msdfs_link_read_target: %s is not a link.\n",
607 smb_fname
->base_name
));
611 referral_len
= SMB_VFS_READLINK(conn
, smb_fname
,
612 link_target
, bufsize
- 1);
613 if (referral_len
== -1) {
614 DEBUG(0,("is_msdfs_link_read_target: Error reading "
615 "msdfs link %s: %s\n",
616 smb_fname
->base_name
, strerror(errno
)));
619 link_target
[referral_len
] = '\0';
621 DEBUG(5,("is_msdfs_link_internal: %s -> %s\n", smb_fname
->base_name
,
624 if (!strnequal(link_target
, "msdfs:", 6)) {
631 if (link_target
!= link_target_buf
) {
632 TALLOC_FREE(link_target
);
637 /**********************************************************************
638 Returns true if the unix path is a valid msdfs symlink.
639 **********************************************************************/
641 bool is_msdfs_link(connection_struct
*conn
,
642 struct smb_filename
*smb_fname
)
644 return is_msdfs_link_internal(talloc_tos(),
650 /*****************************************************************
651 Used by other functions to decide if a dfs path is remote,
652 and to get the list of referred locations for that remote path.
654 search_flag: For findfirsts, dfs links themselves are not
655 redirected, but paths beyond the links are. For normal smb calls,
656 even dfs links need to be redirected.
658 consumedcntp: how much of the dfs path is being redirected. the client
659 should try the remaining path on the redirected server.
661 If this returns NT_STATUS_PATH_NOT_COVERED the contents of the msdfs
662 link redirect are in targetpath.
663 *****************************************************************/
665 static NTSTATUS
dfs_path_lookup(TALLOC_CTX
*ctx
,
666 connection_struct
*conn
,
667 const char *dfspath
, /* Incoming complete dfs path */
668 const struct dfs_path
*pdp
, /* Parsed out
669 server+share+extrapath. */
672 char **pp_targetpath
)
677 struct smb_filename
*smb_fname
= NULL
;
678 char *canon_dfspath
= NULL
; /* Canonicalized dfs path. (only '/'
681 DEBUG(10,("dfs_path_lookup: Conn path = %s reqpath = %s\n",
682 conn
->connectpath
, pdp
->reqpath
));
685 * Note the unix path conversion here we're doing we
686 * throw away. We're looking for a symlink for a dfs
687 * resolution, if we don't find it we'll do another
688 * unix_convert later in the codepath.
691 status
= unix_convert(ctx
, conn
, pdp
->reqpath
, &smb_fname
,
694 if (!NT_STATUS_IS_OK(status
)) {
695 if (!NT_STATUS_EQUAL(status
,
696 NT_STATUS_OBJECT_PATH_NOT_FOUND
)) {
699 if (smb_fname
== NULL
|| smb_fname
->base_name
== NULL
) {
704 /* Optimization - check if we can redirect the whole path. */
706 if (is_msdfs_link_internal(ctx
, conn
, smb_fname
, pp_targetpath
)) {
707 /* XX_ALLOW_WCARD_XXX is called from search functions. */
709 (UCF_COND_ALLOW_WCARD_LCOMP
|
710 UCF_ALWAYS_ALLOW_WCARD_LCOMP
)) {
711 DEBUG(6,("dfs_path_lookup (FindFirst) No redirection "
712 "for dfs link %s.\n", dfspath
));
713 status
= NT_STATUS_OK
;
717 DEBUG(6,("dfs_path_lookup: %s resolves to a "
718 "valid dfs link %s.\n", dfspath
,
719 pp_targetpath
? *pp_targetpath
: ""));
722 *consumedcntp
= strlen(dfspath
);
724 status
= NT_STATUS_PATH_NOT_COVERED
;
728 /* Prepare to test only for '/' components in the given path,
729 * so if a Windows path replace all '\\' characters with '/'.
730 * For a POSIX DFS path we know all separators are already '/'. */
732 canon_dfspath
= talloc_strdup(ctx
, dfspath
);
733 if (!canon_dfspath
) {
734 status
= NT_STATUS_NO_MEMORY
;
737 if (!pdp
->posix_path
) {
738 string_replace(canon_dfspath
, '\\', '/');
742 * localpath comes out of unix_convert, so it has
743 * no trailing backslash. Make sure that canon_dfspath hasn't either.
744 * Fix for bug #4860 from Jan Martin <Jan.Martin@rwedea.com>.
747 trim_char(canon_dfspath
,0,'/');
750 * Redirect if any component in the path is a link.
751 * We do this by walking backwards through the
752 * local path, chopping off the last component
753 * in both the local path and the canonicalized
754 * DFS path. If we hit a DFS link then we're done.
757 p
= strrchr_m(smb_fname
->base_name
, '/');
759 q
= strrchr_m(canon_dfspath
, '/');
768 if (is_msdfs_link_internal(ctx
, conn
,
769 smb_fname
, pp_targetpath
)) {
770 DEBUG(4, ("dfs_path_lookup: Redirecting %s because "
771 "parent %s is dfs link\n", dfspath
,
772 smb_fname_str_dbg(smb_fname
)));
775 *consumedcntp
= strlen(canon_dfspath
);
776 DEBUG(10, ("dfs_path_lookup: Path consumed: %s "
782 status
= NT_STATUS_PATH_NOT_COVERED
;
786 /* Step back on the filesystem. */
787 p
= strrchr_m(smb_fname
->base_name
, '/');
790 /* And in the canonicalized dfs path. */
791 q
= strrchr_m(canon_dfspath
, '/');
795 status
= NT_STATUS_OK
;
797 TALLOC_FREE(smb_fname
);
801 /*****************************************************************
802 Decides if a dfs pathname should be redirected or not.
803 If not, the pathname is converted to a tcon-relative local unix path
805 search_wcard_flag: this flag performs 2 functions both related
806 to searches. See resolve_dfs_path() and parse_dfs_path_XX()
809 This function can return NT_STATUS_OK, meaning use the returned path as-is
810 (mapped into a local path).
811 or NT_STATUS_NOT_COVERED meaning return a DFS redirect, or
812 any other NT_STATUS error which is a genuine error to be
813 returned to the client.
814 *****************************************************************/
816 static NTSTATUS
dfs_redirect(TALLOC_CTX
*ctx
,
817 connection_struct
*conn
,
820 bool allow_broken_path
,
822 bool *ppath_contains_wcard
)
825 bool search_wcard_flag
= (ucf_flags
&
826 (UCF_COND_ALLOW_WCARD_LCOMP
|UCF_ALWAYS_ALLOW_WCARD_LCOMP
));
827 struct dfs_path
*pdp
= talloc(ctx
, struct dfs_path
);
830 return NT_STATUS_NO_MEMORY
;
833 status
= parse_dfs_path(conn
, path_in
, search_wcard_flag
,
834 allow_broken_path
, pdp
,
835 ppath_contains_wcard
);
836 if (!NT_STATUS_IS_OK(status
)) {
841 if (pdp
->reqpath
[0] == '\0') {
843 *pp_path_out
= talloc_strdup(ctx
, "");
845 return NT_STATUS_NO_MEMORY
;
847 DEBUG(5,("dfs_redirect: self-referral.\n"));
851 /* If dfs pathname for a non-dfs share, convert to tcon-relative
852 path and return OK */
854 if (!lp_msdfs_root(SNUM(conn
))) {
855 *pp_path_out
= talloc_strdup(ctx
, pdp
->reqpath
);
858 return NT_STATUS_NO_MEMORY
;
863 /* If it looked like a local path (zero hostname/servicename)
864 * just treat as a tcon-relative path. */
866 if (pdp
->hostname
[0] == '\0' && pdp
->servicename
[0] == '\0') {
867 *pp_path_out
= talloc_strdup(ctx
, pdp
->reqpath
);
870 return NT_STATUS_NO_MEMORY
;
875 if (!( strequal(pdp
->servicename
, lp_servicename(talloc_tos(), SNUM(conn
)))
876 || (strequal(pdp
->servicename
, HOMES_NAME
)
877 && strequal(lp_servicename(talloc_tos(), SNUM(conn
)),
878 conn
->session_info
->unix_info
->sanitized_username
) )) ) {
880 /* The given sharename doesn't match this connection. */
883 return NT_STATUS_OBJECT_PATH_NOT_FOUND
;
886 status
= dfs_path_lookup(ctx
, conn
, path_in
, pdp
,
887 ucf_flags
, NULL
, NULL
);
888 if (!NT_STATUS_IS_OK(status
)) {
889 if (NT_STATUS_EQUAL(status
, NT_STATUS_PATH_NOT_COVERED
)) {
890 DEBUG(3,("dfs_redirect: Redirecting %s\n", path_in
));
892 DEBUG(10,("dfs_redirect: dfs_path_lookup "
893 "failed for %s with %s\n",
894 path_in
, nt_errstr(status
) ));
899 DEBUG(3,("dfs_redirect: Not redirecting %s.\n", path_in
));
901 /* Form non-dfs tcon-relative path */
902 *pp_path_out
= talloc_strdup(ctx
, pdp
->reqpath
);
905 return NT_STATUS_NO_MEMORY
;
908 DEBUG(3,("dfs_redirect: Path %s converted to non-dfs path %s\n",
915 /**********************************************************************
916 Return a self referral.
917 **********************************************************************/
919 static NTSTATUS
self_ref(TALLOC_CTX
*ctx
,
920 const char *dfs_path
,
921 struct junction_map
*jucn
,
923 bool *self_referralp
)
925 struct referral
*ref
;
927 *self_referralp
= True
;
929 jucn
->referral_count
= 1;
930 if((ref
= talloc_zero(ctx
, struct referral
)) == NULL
) {
931 return NT_STATUS_NO_MEMORY
;
934 ref
->alternate_path
= talloc_strdup(ctx
, dfs_path
);
935 if (!ref
->alternate_path
) {
937 return NT_STATUS_NO_MEMORY
;
940 ref
->ttl
= REFERRAL_TTL
;
941 jucn
->referral_list
= ref
;
942 *consumedcntp
= strlen(dfs_path
);
946 /**********************************************************************
947 Gets valid referrals for a dfs path and fills up the
948 junction_map structure.
949 **********************************************************************/
951 NTSTATUS
get_referred_path(TALLOC_CTX
*ctx
,
952 const char *dfs_path
,
953 const struct tsocket_address
*remote_address
,
954 const struct tsocket_address
*local_address
,
955 bool allow_broken_path
,
956 struct junction_map
*jucn
,
958 bool *self_referralp
)
960 struct connection_struct
*conn
;
961 char *targetpath
= NULL
;
963 NTSTATUS status
= NT_STATUS_NOT_FOUND
;
965 struct dfs_path
*pdp
= talloc(ctx
, struct dfs_path
);
966 struct smb_filename
*oldcwd_fname
= NULL
;
969 return NT_STATUS_NO_MEMORY
;
972 *self_referralp
= False
;
974 status
= parse_dfs_path(NULL
, dfs_path
, False
, allow_broken_path
,
976 if (!NT_STATUS_IS_OK(status
)) {
980 jucn
->service_name
= talloc_strdup(ctx
, pdp
->servicename
);
981 jucn
->volume_name
= talloc_strdup(ctx
, pdp
->reqpath
);
982 if (!jucn
->service_name
|| !jucn
->volume_name
) {
984 return NT_STATUS_NO_MEMORY
;
987 /* Verify the share is a dfs root */
988 snum
= lp_servicenumber(jucn
->service_name
);
990 char *service_name
= NULL
;
991 if ((snum
= find_service(ctx
, jucn
->service_name
, &service_name
)) < 0) {
992 return NT_STATUS_NOT_FOUND
;
995 return NT_STATUS_NO_MEMORY
;
997 TALLOC_FREE(jucn
->service_name
);
998 jucn
->service_name
= talloc_strdup(ctx
, service_name
);
999 if (!jucn
->service_name
) {
1001 return NT_STATUS_NO_MEMORY
;
1005 if (!lp_msdfs_root(snum
) && (*lp_msdfs_proxy(talloc_tos(), snum
) == '\0')) {
1006 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not "
1008 pdp
->servicename
, dfs_path
));
1010 return NT_STATUS_NOT_FOUND
;
1014 * Self referrals are tested with a anonymous IPC connection and
1015 * a GET_DFS_REFERRAL call to \\server\share. (which means
1016 * dp.reqpath[0] points to an empty string). create_conn_struct cd's
1017 * into the directory and will fail if it cannot (as the anonymous
1018 * user). Cope with this.
1021 if (pdp
->reqpath
[0] == '\0') {
1023 struct referral
*ref
;
1026 if (*lp_msdfs_proxy(talloc_tos(), snum
) == '\0') {
1028 return self_ref(ctx
,
1036 * It's an msdfs proxy share. Redirect to
1037 * the configured target share.
1040 tmp
= talloc_asprintf(talloc_tos(), "msdfs:%s",
1041 lp_msdfs_proxy(talloc_tos(), snum
));
1044 return NT_STATUS_NO_MEMORY
;
1047 if (!parse_msdfs_symlink(ctx
, snum
, tmp
, &ref
, &refcount
)) {
1050 return NT_STATUS_INVALID_PARAMETER
;
1053 jucn
->referral_count
= refcount
;
1054 jucn
->referral_list
= ref
;
1055 *consumedcntp
= strlen(dfs_path
);
1057 return NT_STATUS_OK
;
1060 status
= create_conn_struct_cwd(ctx
,
1061 server_event_context(),
1062 server_messaging_context(),
1065 lp_path(talloc_tos(), snum
),
1068 if (!NT_STATUS_IS_OK(status
)) {
1076 * The remote and local address should be passed down to
1077 * create_conn_struct_cwd.
1079 if (conn
->sconn
->remote_address
== NULL
) {
1080 conn
->sconn
->remote_address
=
1081 tsocket_address_copy(remote_address
, conn
->sconn
);
1082 if (conn
->sconn
->remote_address
== NULL
) {
1084 return NT_STATUS_NO_MEMORY
;
1087 if (conn
->sconn
->local_address
== NULL
) {
1088 conn
->sconn
->local_address
=
1089 tsocket_address_copy(local_address
, conn
->sconn
);
1090 if (conn
->sconn
->local_address
== NULL
) {
1092 return NT_STATUS_NO_MEMORY
;
1096 /* If this is a DFS path dfs_lookup should return
1097 * NT_STATUS_PATH_NOT_COVERED. */
1099 status
= dfs_path_lookup(ctx
, conn
, dfs_path
, pdp
,
1100 0, consumedcntp
, &targetpath
);
1102 if (!NT_STATUS_EQUAL(status
, NT_STATUS_PATH_NOT_COVERED
)) {
1103 DEBUG(3,("get_referred_path: No valid referrals for path %s\n",
1105 if (NT_STATUS_IS_OK(status
)) {
1107 * We are in an error path here (we
1108 * know it's not a DFS path), but
1109 * dfs_path_lookup() can return
1110 * NT_STATUS_OK. Ensure we always
1111 * return a valid error code.
1113 * #9588 - ACLs are not inherited to directories
1116 status
= NT_STATUS_NOT_FOUND
;
1121 /* We know this is a valid dfs link. Parse the targetpath. */
1122 if (!parse_msdfs_symlink(ctx
, snum
, targetpath
,
1123 &jucn
->referral_list
,
1124 &jucn
->referral_count
)) {
1125 DEBUG(3,("get_referred_path: failed to parse symlink "
1126 "target %s\n", targetpath
));
1127 status
= NT_STATUS_NOT_FOUND
;
1131 status
= NT_STATUS_OK
;
1133 vfs_ChDir(conn
, oldcwd_fname
);
1134 TALLOC_FREE(oldcwd_fname
);
1135 SMB_VFS_DISCONNECT(conn
);
1141 /******************************************************************
1142 Set up the DFS referral for the dfs pathname. This call returns
1143 the amount of the path covered by this server, and where the
1144 client should be redirected to. This is the meat of the
1145 TRANS2_GET_DFS_REFERRAL call.
1146 ******************************************************************/
1148 int setup_dfs_referral(connection_struct
*orig_conn
,
1149 const char *dfs_path
,
1150 int max_referral_level
,
1151 char **ppdata
, NTSTATUS
*pstatus
)
1153 char *pdata
= *ppdata
;
1155 struct dfs_GetDFSReferral
*r
;
1156 DATA_BLOB blob
= data_blob_null
;
1158 enum ndr_err_code ndr_err
;
1160 r
= talloc_zero(talloc_tos(), struct dfs_GetDFSReferral
);
1162 *pstatus
= NT_STATUS_NO_MEMORY
;
1166 r
->in
.req
.max_referral_level
= max_referral_level
;
1167 r
->in
.req
.servername
= talloc_strdup(r
, dfs_path
);
1168 if (r
->in
.req
.servername
== NULL
) {
1170 *pstatus
= NT_STATUS_NO_MEMORY
;
1174 status
= SMB_VFS_GET_DFS_REFERRALS(orig_conn
, r
);
1175 if (!NT_STATUS_IS_OK(status
)) {
1181 ndr_err
= ndr_push_struct_blob(&blob
, r
,
1183 (ndr_push_flags_fn_t
)ndr_push_dfs_referral_resp
);
1184 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
1186 *pstatus
= NT_STATUS_INVALID_PARAMETER
;
1190 pdata
= (char *)SMB_REALLOC(pdata
, blob
.length
);
1193 DEBUG(0,("referral setup:"
1194 "malloc failed for Realloc!\n"));
1198 reply_size
= blob
.length
;
1199 memcpy(pdata
, blob
.data
, blob
.length
);
1202 *pstatus
= NT_STATUS_OK
;
1206 /**********************************************************************
1207 The following functions are called by the NETDFS RPC pipe functions
1208 **********************************************************************/
1210 /*********************************************************************
1211 Creates a junction structure from a DFS pathname
1212 **********************************************************************/
1214 bool create_junction(TALLOC_CTX
*ctx
,
1215 const char *dfs_path
,
1216 bool allow_broken_path
,
1217 struct junction_map
*jucn
)
1221 struct dfs_path
*pdp
= talloc(ctx
,struct dfs_path
);
1227 status
= parse_dfs_path(NULL
, dfs_path
, False
, allow_broken_path
,
1229 if (!NT_STATUS_IS_OK(status
)) {
1233 /* check if path is dfs : validate first token */
1234 if (!is_myname_or_ipaddr(pdp
->hostname
)) {
1235 DEBUG(4,("create_junction: Invalid hostname %s "
1237 pdp
->hostname
, dfs_path
));
1242 /* Check for a non-DFS share */
1243 snum
= lp_servicenumber(pdp
->servicename
);
1245 if(snum
< 0 || !lp_msdfs_root(snum
)) {
1246 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1252 jucn
->service_name
= talloc_strdup(ctx
, pdp
->servicename
);
1253 jucn
->volume_name
= talloc_strdup(ctx
, pdp
->reqpath
);
1254 jucn
->comment
= lp_comment(ctx
, snum
);
1257 if (!jucn
->service_name
|| !jucn
->volume_name
|| ! jucn
->comment
) {
1263 /**********************************************************************
1264 Forms a valid Unix pathname from the junction
1265 **********************************************************************/
1267 static bool junction_to_local_path(const struct junction_map
*jucn
,
1269 connection_struct
**conn_out
,
1270 struct smb_filename
**oldpath_fname
)
1275 snum
= lp_servicenumber(jucn
->service_name
);
1279 status
= create_conn_struct_cwd(talloc_tos(),
1280 server_event_context(),
1281 server_messaging_context(),
1284 lp_path(talloc_tos(), snum
),
1287 if (!NT_STATUS_IS_OK(status
)) {
1291 *pp_path_out
= talloc_asprintf(*conn_out
,
1293 lp_path(talloc_tos(), snum
),
1295 if (!*pp_path_out
) {
1296 vfs_ChDir(*conn_out
, *oldpath_fname
);
1297 SMB_VFS_DISCONNECT(*conn_out
);
1298 conn_free(*conn_out
);
1304 bool create_msdfs_link(const struct junction_map
*jucn
)
1307 struct smb_filename
*cwd_fname
= NULL
;
1308 char *msdfs_link
= NULL
;
1309 connection_struct
*conn
;
1311 bool insert_comma
= False
;
1313 struct smb_filename
*smb_fname
= NULL
;
1315 if(!junction_to_local_path(jucn
, &path
, &conn
, &cwd_fname
)) {
1319 /* Form the msdfs_link contents */
1320 msdfs_link
= talloc_strdup(conn
, "msdfs:");
1324 for(i
=0; i
<jucn
->referral_count
; i
++) {
1325 char *refpath
= jucn
->referral_list
[i
].alternate_path
;
1327 /* Alternate paths always use Windows separators. */
1328 trim_char(refpath
, '\\', '\\');
1329 if(*refpath
== '\0') {
1331 insert_comma
= False
;
1335 if (i
> 0 && insert_comma
) {
1336 msdfs_link
= talloc_asprintf_append_buffer(msdfs_link
,
1340 msdfs_link
= talloc_asprintf_append_buffer(msdfs_link
,
1348 if (!insert_comma
) {
1349 insert_comma
= True
;
1353 DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
1356 smb_fname
= synthetic_smb_fname(talloc_tos(),
1361 if (smb_fname
== NULL
) {
1366 if(SMB_VFS_SYMLINK(conn
, msdfs_link
, smb_fname
) < 0) {
1367 if (errno
== EEXIST
) {
1368 if(SMB_VFS_UNLINK(conn
, smb_fname
)!=0) {
1369 TALLOC_FREE(smb_fname
);
1373 if (SMB_VFS_SYMLINK(conn
, msdfs_link
, smb_fname
) < 0) {
1374 DEBUG(1,("create_msdfs_link: symlink failed "
1375 "%s -> %s\nError: %s\n",
1376 path
, msdfs_link
, strerror(errno
)));
1384 TALLOC_FREE(smb_fname
);
1385 vfs_ChDir(conn
, cwd_fname
);
1386 TALLOC_FREE(cwd_fname
);
1387 SMB_VFS_DISCONNECT(conn
);
1392 bool remove_msdfs_link(const struct junction_map
*jucn
)
1395 struct smb_filename
*cwd_fname
= NULL
;
1396 connection_struct
*conn
;
1398 struct smb_filename
*smb_fname
;
1400 if (!junction_to_local_path(jucn
, &path
, &conn
, &cwd_fname
)) {
1404 smb_fname
= synthetic_smb_fname(talloc_tos(),
1409 if (smb_fname
== NULL
) {
1414 if( SMB_VFS_UNLINK(conn
, smb_fname
) == 0 ) {
1418 TALLOC_FREE(smb_fname
);
1419 vfs_ChDir(conn
, cwd_fname
);
1420 TALLOC_FREE(cwd_fname
);
1421 SMB_VFS_DISCONNECT(conn
);
1426 /*********************************************************************
1427 Return the number of DFS links at the root of this share.
1428 *********************************************************************/
1430 static int count_dfs_links(TALLOC_CTX
*ctx
, int snum
)
1434 const char *dname
= NULL
;
1435 char *talloced
= NULL
;
1436 const char *connect_path
= lp_path(talloc_tos(), snum
);
1437 const char *msdfs_proxy
= lp_msdfs_proxy(talloc_tos(), snum
);
1438 connection_struct
*conn
;
1440 struct smb_filename
*cwd_fname
= NULL
;
1441 struct smb_filename
*smb_fname
= NULL
;
1443 if(*connect_path
== '\0') {
1448 * Fake up a connection struct for the VFS layer.
1451 status
= create_conn_struct_cwd(talloc_tos(),
1452 server_event_context(),
1453 server_messaging_context(),
1459 if (!NT_STATUS_IS_OK(status
)) {
1460 DEBUG(3, ("create_conn_struct failed: %s\n",
1461 nt_errstr(status
)));
1465 /* Count a link for the msdfs root - convention */
1468 /* No more links if this is an msdfs proxy. */
1469 if (*msdfs_proxy
!= '\0') {
1473 smb_fname
= synthetic_smb_fname(talloc_tos(),
1478 if (smb_fname
== NULL
) {
1482 /* Now enumerate all dfs links */
1483 dirp
= SMB_VFS_OPENDIR(conn
, smb_fname
, NULL
, 0);
1488 while ((dname
= vfs_readdirname(conn
, dirp
, NULL
, &talloced
))
1490 struct smb_filename
*smb_dname
=
1491 synthetic_smb_fname(talloc_tos(),
1496 if (smb_dname
== NULL
) {
1499 if (is_msdfs_link(conn
, smb_dname
)) {
1502 TALLOC_FREE(talloced
);
1503 TALLOC_FREE(smb_dname
);
1506 SMB_VFS_CLOSEDIR(conn
,dirp
);
1509 TALLOC_FREE(smb_fname
);
1510 vfs_ChDir(conn
, cwd_fname
);
1511 TALLOC_FREE(cwd_fname
);
1512 SMB_VFS_DISCONNECT(conn
);
1517 /*********************************************************************
1518 *********************************************************************/
1520 static int form_junctions(TALLOC_CTX
*ctx
,
1522 struct junction_map
*jucn
,
1527 const char *dname
= NULL
;
1528 char *talloced
= NULL
;
1529 const char *connect_path
= lp_path(talloc_tos(), snum
);
1530 char *service_name
= lp_servicename(talloc_tos(), snum
);
1531 const char *msdfs_proxy
= lp_msdfs_proxy(talloc_tos(), snum
);
1532 connection_struct
*conn
;
1533 struct referral
*ref
= NULL
;
1534 struct smb_filename
*cwd_fname
= NULL
;
1535 struct smb_filename
*smb_fname
= NULL
;
1538 if (jn_remain
== 0) {
1542 if(*connect_path
== '\0') {
1547 * Fake up a connection struct for the VFS layer.
1550 status
= create_conn_struct_cwd(ctx
,
1551 server_event_context(),
1552 server_messaging_context(),
1558 if (!NT_STATUS_IS_OK(status
)) {
1559 DEBUG(3, ("create_conn_struct failed: %s\n",
1560 nt_errstr(status
)));
1564 /* form a junction for the msdfs root - convention
1565 DO NOT REMOVE THIS: NT clients will not work with us
1566 if this is not present
1568 jucn
[cnt
].service_name
= talloc_strdup(ctx
,service_name
);
1569 jucn
[cnt
].volume_name
= talloc_strdup(ctx
, "");
1570 if (!jucn
[cnt
].service_name
|| !jucn
[cnt
].volume_name
) {
1573 jucn
[cnt
].comment
= "";
1574 jucn
[cnt
].referral_count
= 1;
1576 ref
= jucn
[cnt
].referral_list
= talloc_zero(ctx
, struct referral
);
1577 if (jucn
[cnt
].referral_list
== NULL
) {
1582 ref
->ttl
= REFERRAL_TTL
;
1583 if (*msdfs_proxy
!= '\0') {
1584 ref
->alternate_path
= talloc_strdup(ctx
,
1587 ref
->alternate_path
= talloc_asprintf(ctx
,
1589 get_local_machine_name(),
1593 if (!ref
->alternate_path
) {
1598 /* Don't enumerate if we're an msdfs proxy. */
1599 if (*msdfs_proxy
!= '\0') {
1603 smb_fname
= synthetic_smb_fname(talloc_tos(),
1608 if (smb_fname
== NULL
) {
1612 /* Now enumerate all dfs links */
1613 dirp
= SMB_VFS_OPENDIR(conn
, smb_fname
, NULL
, 0);
1618 while ((dname
= vfs_readdirname(conn
, dirp
, NULL
, &talloced
))
1620 char *link_target
= NULL
;
1621 struct smb_filename
*smb_dname
= NULL
;
1623 if (cnt
>= jn_remain
) {
1624 DEBUG(2, ("form_junctions: ran out of MSDFS "
1626 TALLOC_FREE(talloced
);
1629 smb_dname
= synthetic_smb_fname(talloc_tos(),
1634 if (smb_dname
== NULL
) {
1635 TALLOC_FREE(talloced
);
1638 if (is_msdfs_link_internal(ctx
,
1640 smb_dname
, &link_target
)) {
1641 if (parse_msdfs_symlink(ctx
, snum
,
1643 &jucn
[cnt
].referral_list
,
1644 &jucn
[cnt
].referral_count
)) {
1646 jucn
[cnt
].service_name
= talloc_strdup(ctx
,
1648 jucn
[cnt
].volume_name
= talloc_strdup(ctx
,
1650 if (!jucn
[cnt
].service_name
||
1651 !jucn
[cnt
].volume_name
) {
1652 TALLOC_FREE(talloced
);
1655 jucn
[cnt
].comment
= "";
1658 TALLOC_FREE(link_target
);
1660 TALLOC_FREE(talloced
);
1661 TALLOC_FREE(smb_dname
);
1667 SMB_VFS_CLOSEDIR(conn
,dirp
);
1670 TALLOC_FREE(smb_fname
);
1671 vfs_ChDir(conn
, cwd_fname
);
1672 TALLOC_FREE(cwd_fname
);
1677 struct junction_map
*enum_msdfs_links(TALLOC_CTX
*ctx
, size_t *p_num_jn
)
1679 struct junction_map
*jn
= NULL
;
1681 size_t jn_count
= 0;
1685 if(!lp_host_msdfs()) {
1689 /* Ensure all the usershares are loaded. */
1691 load_registry_shares();
1692 sharecount
= load_usershare_shares(NULL
, connections_snum_used
);
1695 for(i
=0;i
< sharecount
;i
++) {
1696 if(lp_msdfs_root(i
)) {
1697 jn_count
+= count_dfs_links(ctx
, i
);
1700 if (jn_count
== 0) {
1703 jn
= talloc_array(ctx
, struct junction_map
, jn_count
);
1707 for(i
=0; i
< sharecount
; i
++) {
1708 if (*p_num_jn
>= jn_count
) {
1711 if(lp_msdfs_root(i
)) {
1712 *p_num_jn
+= form_junctions(ctx
, i
,
1714 jn_count
- *p_num_jn
);
1720 /******************************************************************************
1721 Core function to resolve a dfs pathname possibly containing a wildcard. If
1722 ppath_contains_wcard != NULL, it will be set to true if a wildcard is
1723 detected during dfs resolution.
1724 ******************************************************************************/
1726 NTSTATUS
resolve_dfspath_wcard(TALLOC_CTX
*ctx
,
1727 connection_struct
*conn
,
1728 const char *name_in
,
1730 bool allow_broken_path
,
1732 bool *ppath_contains_wcard
)
1734 bool path_contains_wcard
= false;
1735 NTSTATUS status
= NT_STATUS_OK
;
1737 status
= dfs_redirect(ctx
,
1743 &path_contains_wcard
);
1745 if (NT_STATUS_IS_OK(status
) &&
1746 ppath_contains_wcard
!= NULL
&&
1747 path_contains_wcard
) {
1748 *ppath_contains_wcard
= path_contains_wcard
;