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/pthreadpool/pthreadpool_tevent.h"
38 /**********************************************************************
39 Parse a DFS pathname of the form \hostname\service\reqpath
40 into the dfs_path structure.
41 If POSIX pathnames is true, the pathname may also be of the
42 form /hostname/service/reqpath.
43 We cope with either here.
45 Unfortunately, due to broken clients who might set the
46 SVAL(inbuf,smb_flg2) & FLAGS2_DFS_PATHNAMES bit and then
47 send a local path, we have to cope with that too....
49 If conn != NULL then ensure the provided service is
50 the one pointed to by the connection.
52 This version does everything using pointers within one copy of the
53 pathname string, talloced on the struct dfs_path pointer (which
54 must be talloced). This may be too clever to live....
56 **********************************************************************/
58 static NTSTATUS
parse_dfs_path(connection_struct
*conn
,
61 bool allow_broken_path
,
62 struct dfs_path
*pdp
, /* MUST BE TALLOCED */
63 bool *ppath_contains_wcard
)
69 NTSTATUS status
= NT_STATUS_OK
;
75 * This is the only talloc we should need to do
76 * on the struct dfs_path. All the pointers inside
77 * it should point to offsets within this string.
80 pathname_local
= talloc_strdup(pdp
, pathname
);
81 if (!pathname_local
) {
82 return NT_STATUS_NO_MEMORY
;
84 /* Get a pointer to the terminating '\0' */
85 eos_ptr
= &pathname_local
[strlen(pathname_local
)];
86 p
= temp
= pathname_local
;
89 * Non-broken DFS paths *must* start with the
90 * path separator. For Windows this is always '\\',
91 * for posix paths this is always '/'.
94 if (*pathname
== '/') {
95 pdp
->posix_path
= true;
98 pdp
->posix_path
= false;
102 if (allow_broken_path
&& (*pathname
!= sepchar
)) {
103 DEBUG(10,("parse_dfs_path: path %s doesn't start with %c\n",
104 pathname
, sepchar
));
106 * Possibly client sent a local path by mistake.
107 * Try and convert to a local path.
108 * Note that this is an SMB1-only fallback
109 * to cope with known broken SMB1 clients.
112 pdp
->hostname
= eos_ptr
; /* "" */
113 pdp
->servicename
= eos_ptr
; /* "" */
115 /* We've got no info about separators. */
116 pdp
->posix_path
= lp_posix_pathnames();
118 DEBUG(10,("parse_dfs_path: trying to convert %s to a "
125 * Safe to use on talloc'ed string as it only shrinks.
126 * It also doesn't affect the eos_ptr.
128 trim_char(temp
,sepchar
,sepchar
);
130 DEBUG(10,("parse_dfs_path: temp = |%s| after trimming %c's\n",
134 /* Parse out hostname. */
135 p
= strchr_m(temp
,sepchar
);
137 DEBUG(10,("parse_dfs_path: can't parse hostname from path %s\n",
140 * Possibly client sent a local path by mistake.
141 * Try and convert to a local path.
144 pdp
->hostname
= eos_ptr
; /* "" */
145 pdp
->servicename
= eos_ptr
; /* "" */
148 DEBUG(10,("parse_dfs_path: trying to convert %s "
154 pdp
->hostname
= temp
;
156 DEBUG(10,("parse_dfs_path: hostname: %s\n",pdp
->hostname
));
158 /* Parse out servicename. */
160 p
= strchr_m(servicename
,sepchar
);
165 /* Is this really our servicename ? */
166 if (conn
&& !( strequal(servicename
, lp_servicename(talloc_tos(), SNUM(conn
)))
167 || (strequal(servicename
, HOMES_NAME
)
168 && strequal(lp_servicename(talloc_tos(), SNUM(conn
)),
169 get_current_username()) )) ) {
170 DEBUG(10,("parse_dfs_path: %s is not our servicename\n",
174 * Possibly client sent a local path by mistake.
175 * Try and convert to a local path.
178 pdp
->hostname
= eos_ptr
; /* "" */
179 pdp
->servicename
= eos_ptr
; /* "" */
181 /* Repair the path - replace the sepchar's
184 *servicename
= sepchar
;
190 DEBUG(10,("parse_dfs_path: trying to convert %s "
196 pdp
->servicename
= servicename
;
198 DEBUG(10,("parse_dfs_path: servicename: %s\n",pdp
->servicename
));
201 /* Client sent self referral \server\share. */
202 pdp
->reqpath
= eos_ptr
; /* "" */
210 *ppath_contains_wcard
= False
;
214 /* Rest is reqpath. */
215 if (pdp
->posix_path
) {
216 status
= check_path_syntax_posix(pdp
->reqpath
);
219 status
= check_path_syntax_wcard(pdp
->reqpath
,
220 ppath_contains_wcard
);
222 status
= check_path_syntax(pdp
->reqpath
);
226 if (!NT_STATUS_IS_OK(status
)) {
227 DEBUG(10,("parse_dfs_path: '%s' failed with %s\n",
228 p
, nt_errstr(status
) ));
232 DEBUG(10,("parse_dfs_path: rest of the path: %s\n",pdp
->reqpath
));
236 /********************************************************
237 Fake up a connection struct for the VFS layer, for use in
238 applications (such as the python bindings), that do not want the
239 global working directory changed under them.
241 SMB_VFS_CONNECT requires root privileges.
242 *********************************************************/
244 static NTSTATUS
create_conn_struct_as_root(TALLOC_CTX
*ctx
,
245 struct messaging_context
*msg
,
246 connection_struct
**pconn
,
249 const struct auth_session_info
*session_info
)
251 connection_struct
*conn
;
253 const char *vfs_user
;
254 struct smbd_server_connection
*sconn
;
255 const char *servicename
= lp_const_servicename(snum
);
258 sconn
= talloc_zero(ctx
, struct smbd_server_connection
);
260 return NT_STATUS_NO_MEMORY
;
263 sconn
->raw_ev_ctx
= samba_tevent_context_init(sconn
);
264 if (sconn
->raw_ev_ctx
== NULL
) {
266 return NT_STATUS_NO_MEMORY
;
269 sconn
->root_ev_ctx
= smbd_impersonate_root_create(sconn
->raw_ev_ctx
);
270 if (sconn
->root_ev_ctx
== NULL
) {
272 return NT_STATUS_NO_MEMORY
;
274 sconn
->guest_ev_ctx
= smbd_impersonate_guest_create(sconn
->raw_ev_ctx
);
275 if (sconn
->guest_ev_ctx
== NULL
) {
277 return NT_STATUS_NO_MEMORY
;
281 * We only provide sync threadpools.
283 ret
= pthreadpool_tevent_init(sconn
, 0, &sconn
->sync_thread_pool
);
286 return NT_STATUS_NO_MEMORY
;
288 sconn
->raw_thread_pool
= sconn
->sync_thread_pool
;
290 sconn
->msg_ctx
= msg
;
292 conn
= conn_new(sconn
);
295 return NT_STATUS_NO_MEMORY
;
298 /* Now we have conn, we need to make sconn a child of conn,
299 * for a proper talloc tree */
300 talloc_steal(conn
, sconn
);
302 if (snum
== -1 && servicename
== NULL
) {
303 servicename
= "Unknown Service (snum == -1)";
306 connpath
= talloc_strdup(conn
, path
);
309 return NT_STATUS_NO_MEMORY
;
311 connpath
= talloc_string_sub(conn
,
317 return NT_STATUS_NO_MEMORY
;
320 /* needed for smbd_vfs_init() */
322 conn
->params
->service
= snum
;
323 conn
->cnum
= TID_FIELD_INVALID
;
325 if (session_info
!= NULL
) {
326 conn
->session_info
= copy_session_info(conn
, session_info
);
327 if (conn
->session_info
== NULL
) {
328 DEBUG(0, ("copy_serverinfo failed\n"));
330 return NT_STATUS_NO_MEMORY
;
332 /* unix_info could be NULL in session_info */
333 if (conn
->session_info
->unix_info
!= NULL
) {
334 vfs_user
= conn
->session_info
->unix_info
->unix_name
;
336 vfs_user
= get_current_username();
339 /* use current authenticated user in absence of session_info */
340 vfs_user
= get_current_username();
344 * The impersonation has to be done by the caller
345 * of create_conn_struct_tos[_cwd]().
347 * Note: the context can't be changed anyway
348 * as we're using our own tevent_context
349 * and not a global one were other requests
350 * could change the current unix token.
352 * We just use a wrapper tevent_context in order
353 * to avoid crashes because TALLOC_FREE(conn->user_ev_ctx)
354 * would also remove sconn->raw_ev_ctx.
356 conn
->user_ev_ctx
= smbd_impersonate_debug_create(sconn
->raw_ev_ctx
,
357 "FAKE impersonation",
359 if (conn
->user_ev_ctx
== NULL
) {
361 return NT_STATUS_NO_MEMORY
;
364 set_conn_connectpath(conn
, connpath
);
367 * New code to check if there's a share security descriptor
368 * added from NT server manager. This is done after the
369 * smb.conf checks are done as we need a uid and token. JRA.
372 if (conn
->session_info
) {
373 share_access_check(conn
->session_info
->security_token
,
375 MAXIMUM_ALLOWED_ACCESS
,
376 &conn
->share_access
);
378 if ((conn
->share_access
& FILE_WRITE_DATA
) == 0) {
379 if ((conn
->share_access
& FILE_READ_DATA
) == 0) {
380 /* No access, read or write. */
381 DEBUG(3,("create_conn_struct: connection to %s "
382 "denied due to security "
386 return NT_STATUS_ACCESS_DENIED
;
388 conn
->read_only
= true;
392 conn
->share_access
= 0;
393 conn
->read_only
= true;
396 if (!smbd_vfs_init(conn
)) {
397 NTSTATUS status
= map_nt_error_from_unix(errno
);
398 DEBUG(0,("create_conn_struct: smbd_vfs_init failed.\n"));
403 /* this must be the first filesystem operation that we do */
404 if (SMB_VFS_CONNECT(conn
, servicename
, vfs_user
) < 0) {
405 DEBUG(0,("VFS connect failed!\n"));
407 return NT_STATUS_UNSUCCESSFUL
;
410 talloc_free(conn
->origpath
);
411 conn
->origpath
= talloc_strdup(conn
, conn
->connectpath
);
412 if (conn
->origpath
== NULL
) {
414 return NT_STATUS_NO_MEMORY
;
417 conn
->fs_capabilities
= SMB_VFS_FS_CAPABILITIES(conn
, &conn
->ts_res
);
418 conn
->tcon_done
= true;
419 *pconn
= talloc_move(ctx
, &conn
);
424 static int conn_struct_tos_destructor(struct conn_struct_tos
*c
)
426 if (c
->oldcwd_fname
!= NULL
) {
427 vfs_ChDir(c
->conn
, c
->oldcwd_fname
);
428 TALLOC_FREE(c
->oldcwd_fname
);
430 SMB_VFS_DISCONNECT(c
->conn
);
435 /********************************************************
436 Fake up a connection struct for the VFS layer, for use in
437 applications (such as the python bindings), that do not want the
438 global working directory changed under them.
440 SMB_VFS_CONNECT requires root privileges.
441 This temporary uses become_root() and unbecome_root().
443 But further impersonation has to be cone by the caller.
444 *********************************************************/
445 NTSTATUS
create_conn_struct_tos(struct messaging_context
*msg
,
448 const struct auth_session_info
*session_info
,
449 struct conn_struct_tos
**_c
)
451 struct conn_struct_tos
*c
= NULL
;
456 c
= talloc_zero(talloc_tos(), struct conn_struct_tos
);
458 return NT_STATUS_NO_MEMORY
;
462 status
= create_conn_struct_as_root(c
,
469 if (!NT_STATUS_IS_OK(status
)) {
474 talloc_set_destructor(c
, conn_struct_tos_destructor
);
480 /********************************************************
481 Fake up a connection struct for the VFS layer.
482 Note: this performs a vfs connect and CHANGES CWD !!!! JRA.
484 See also the comment for create_conn_struct_tos() above!
486 The CWD change is reverted by the destructor of
487 conn_struct_tos when the current talloc_tos() is destroyed.
488 *********************************************************/
489 NTSTATUS
create_conn_struct_tos_cwd(struct messaging_context
*msg
,
492 const struct auth_session_info
*session_info
,
493 struct conn_struct_tos
**_c
)
495 struct conn_struct_tos
*c
= NULL
;
496 struct smb_filename smb_fname_connectpath
= {0};
501 status
= create_conn_struct_tos(msg
,
506 if (!NT_STATUS_IS_OK(status
)) {
511 * Windows seems to insist on doing trans2getdfsreferral() calls on
512 * the IPC$ share as the anonymous user. If we try to chdir as that
513 * user we will fail.... WTF ? JRA.
516 c
->oldcwd_fname
= vfs_GetWd(c
, c
->conn
);
517 if (c
->oldcwd_fname
== NULL
) {
518 status
= map_nt_error_from_unix(errno
);
519 DEBUG(3, ("vfs_GetWd failed: %s\n", strerror(errno
)));
524 smb_fname_connectpath
= (struct smb_filename
) {
525 .base_name
= c
->conn
->connectpath
528 if (vfs_ChDir(c
->conn
, &smb_fname_connectpath
) != 0) {
529 status
= map_nt_error_from_unix(errno
);
530 DBG_NOTICE("Can't ChDir to new conn path %s. "
532 c
->conn
->connectpath
, strerror(errno
));
533 TALLOC_FREE(c
->oldcwd_fname
);
542 static void shuffle_strlist(char **list
, int count
)
548 for (i
= count
; i
> 1; i
--) {
549 r
= generate_random() % i
;
557 /**********************************************************************
558 Parse the contents of a symlink to verify if it is an msdfs referral
559 A valid referral is of the form:
561 msdfs:server1\share1,server2\share2
562 msdfs:server1\share1\pathname,server2\share2\pathname
563 msdfs:server1/share1,server2/share2
564 msdfs:server1/share1/pathname,server2/share2/pathname.
566 Note that the alternate paths returned here must be of the canonicalized
570 \server\share\path\to\file,
572 even in posix path mode. This is because we have no knowledge if the
573 server we're referring to understands posix paths.
574 **********************************************************************/
576 static bool parse_msdfs_symlink(TALLOC_CTX
*ctx
,
579 struct referral
**preflist
,
584 char **alt_path
= NULL
;
586 struct referral
*reflist
;
589 temp
= talloc_strdup(ctx
, target
);
593 prot
= strtok_r(temp
, ":", &saveptr
);
595 DEBUG(0,("parse_msdfs_symlink: invalid path !\n"));
599 alt_path
= talloc_array(ctx
, char *, MAX_REFERRAL_COUNT
);
604 /* parse out the alternate paths */
605 while((count
<MAX_REFERRAL_COUNT
) &&
606 ((alt_path
[count
] = strtok_r(NULL
, ",", &saveptr
)) != NULL
)) {
610 /* shuffle alternate paths */
611 if (lp_msdfs_shuffle_referrals(snum
)) {
612 shuffle_strlist(alt_path
, count
);
615 DEBUG(10,("parse_msdfs_symlink: count=%d\n", count
));
618 reflist
= *preflist
= talloc_zero_array(ctx
,
619 struct referral
, count
);
620 if(reflist
== NULL
) {
621 TALLOC_FREE(alt_path
);
625 reflist
= *preflist
= NULL
;
628 for(i
=0;i
<count
;i
++) {
631 /* Canonicalize link target.
632 * Replace all /'s in the path by a \ */
633 string_replace(alt_path
[i
], '/', '\\');
635 /* Remove leading '\\'s */
637 while (*p
&& (*p
== '\\')) {
641 reflist
[i
].alternate_path
= talloc_asprintf(ctx
,
644 if (!reflist
[i
].alternate_path
) {
648 reflist
[i
].proximity
= 0;
649 reflist
[i
].ttl
= REFERRAL_TTL
;
650 DEBUG(10, ("parse_msdfs_symlink: Created alt path: %s\n",
651 reflist
[i
].alternate_path
));
656 TALLOC_FREE(alt_path
);
660 /**********************************************************************
661 Returns true if the unix path is a valid msdfs symlink and also
662 returns the target string from inside the link.
663 **********************************************************************/
665 static bool is_msdfs_link_internal(TALLOC_CTX
*ctx
,
666 connection_struct
*conn
,
667 struct smb_filename
*smb_fname
,
668 char **pp_link_target
)
670 int referral_len
= 0;
671 #if defined(HAVE_BROKEN_READLINK)
672 char link_target_buf
[PATH_MAX
];
674 char link_target_buf
[7];
677 char *link_target
= NULL
;
679 if (pp_link_target
) {
681 link_target
= talloc_array(ctx
, char, bufsize
);
685 *pp_link_target
= link_target
;
687 bufsize
= sizeof(link_target_buf
);
688 link_target
= link_target_buf
;
691 if (SMB_VFS_LSTAT(conn
, smb_fname
) != 0) {
692 DEBUG(5,("is_msdfs_link_read_target: %s does not exist.\n",
693 smb_fname
->base_name
));
696 if (!S_ISLNK(smb_fname
->st
.st_ex_mode
)) {
697 DEBUG(5,("is_msdfs_link_read_target: %s is not a link.\n",
698 smb_fname
->base_name
));
702 referral_len
= SMB_VFS_READLINK(conn
, smb_fname
,
703 link_target
, bufsize
- 1);
704 if (referral_len
== -1) {
705 DEBUG(0,("is_msdfs_link_read_target: Error reading "
706 "msdfs link %s: %s\n",
707 smb_fname
->base_name
, strerror(errno
)));
710 link_target
[referral_len
] = '\0';
712 DEBUG(5,("is_msdfs_link_internal: %s -> %s\n", smb_fname
->base_name
,
715 if (!strnequal(link_target
, "msdfs:", 6)) {
722 if (link_target
!= link_target_buf
) {
723 TALLOC_FREE(link_target
);
728 /**********************************************************************
729 Returns true if the unix path is a valid msdfs symlink.
730 **********************************************************************/
732 bool is_msdfs_link(connection_struct
*conn
,
733 struct smb_filename
*smb_fname
)
735 return is_msdfs_link_internal(talloc_tos(),
741 /*****************************************************************
742 Used by other functions to decide if a dfs path is remote,
743 and to get the list of referred locations for that remote path.
745 search_flag: For findfirsts, dfs links themselves are not
746 redirected, but paths beyond the links are. For normal smb calls,
747 even dfs links need to be redirected.
749 consumedcntp: how much of the dfs path is being redirected. the client
750 should try the remaining path on the redirected server.
752 If this returns NT_STATUS_PATH_NOT_COVERED the contents of the msdfs
753 link redirect are in targetpath.
754 *****************************************************************/
756 static NTSTATUS
dfs_path_lookup(TALLOC_CTX
*ctx
,
757 connection_struct
*conn
,
758 const char *dfspath
, /* Incoming complete dfs path */
759 const struct dfs_path
*pdp
, /* Parsed out
760 server+share+extrapath. */
763 char **pp_targetpath
)
768 struct smb_filename
*smb_fname
= NULL
;
769 char *canon_dfspath
= NULL
; /* Canonicalized dfs path. (only '/'
772 DEBUG(10,("dfs_path_lookup: Conn path = %s reqpath = %s\n",
773 conn
->connectpath
, pdp
->reqpath
));
776 * Note the unix path conversion here we're doing we
777 * throw away. We're looking for a symlink for a dfs
778 * resolution, if we don't find it we'll do another
779 * unix_convert later in the codepath.
782 status
= unix_convert(ctx
, conn
, pdp
->reqpath
, &smb_fname
,
785 if (!NT_STATUS_IS_OK(status
)) {
786 if (!NT_STATUS_EQUAL(status
,
787 NT_STATUS_OBJECT_PATH_NOT_FOUND
)) {
790 if (smb_fname
== NULL
|| smb_fname
->base_name
== NULL
) {
795 /* Optimization - check if we can redirect the whole path. */
797 if (is_msdfs_link_internal(ctx
, conn
, smb_fname
, pp_targetpath
)) {
798 /* XX_ALLOW_WCARD_XXX is called from search functions. */
800 (UCF_COND_ALLOW_WCARD_LCOMP
|
801 UCF_ALWAYS_ALLOW_WCARD_LCOMP
)) {
802 DEBUG(6,("dfs_path_lookup (FindFirst) No redirection "
803 "for dfs link %s.\n", dfspath
));
804 status
= NT_STATUS_OK
;
808 DEBUG(6,("dfs_path_lookup: %s resolves to a "
809 "valid dfs link %s.\n", dfspath
,
810 pp_targetpath
? *pp_targetpath
: ""));
813 *consumedcntp
= strlen(dfspath
);
815 status
= NT_STATUS_PATH_NOT_COVERED
;
819 /* Prepare to test only for '/' components in the given path,
820 * so if a Windows path replace all '\\' characters with '/'.
821 * For a POSIX DFS path we know all separators are already '/'. */
823 canon_dfspath
= talloc_strdup(ctx
, dfspath
);
824 if (!canon_dfspath
) {
825 status
= NT_STATUS_NO_MEMORY
;
828 if (!pdp
->posix_path
) {
829 string_replace(canon_dfspath
, '\\', '/');
833 * localpath comes out of unix_convert, so it has
834 * no trailing backslash. Make sure that canon_dfspath hasn't either.
835 * Fix for bug #4860 from Jan Martin <Jan.Martin@rwedea.com>.
838 trim_char(canon_dfspath
,0,'/');
841 * Redirect if any component in the path is a link.
842 * We do this by walking backwards through the
843 * local path, chopping off the last component
844 * in both the local path and the canonicalized
845 * DFS path. If we hit a DFS link then we're done.
848 p
= strrchr_m(smb_fname
->base_name
, '/');
850 q
= strrchr_m(canon_dfspath
, '/');
859 if (is_msdfs_link_internal(ctx
, conn
,
860 smb_fname
, pp_targetpath
)) {
861 DEBUG(4, ("dfs_path_lookup: Redirecting %s because "
862 "parent %s is dfs link\n", dfspath
,
863 smb_fname_str_dbg(smb_fname
)));
866 *consumedcntp
= strlen(canon_dfspath
);
867 DEBUG(10, ("dfs_path_lookup: Path consumed: %s "
873 status
= NT_STATUS_PATH_NOT_COVERED
;
877 /* Step back on the filesystem. */
878 p
= strrchr_m(smb_fname
->base_name
, '/');
881 /* And in the canonicalized dfs path. */
882 q
= strrchr_m(canon_dfspath
, '/');
886 status
= NT_STATUS_OK
;
888 TALLOC_FREE(smb_fname
);
892 /*****************************************************************
893 Decides if a dfs pathname should be redirected or not.
894 If not, the pathname is converted to a tcon-relative local unix path
896 search_wcard_flag: this flag performs 2 functions both related
897 to searches. See resolve_dfs_path() and parse_dfs_path_XX()
900 This function can return NT_STATUS_OK, meaning use the returned path as-is
901 (mapped into a local path).
902 or NT_STATUS_NOT_COVERED meaning return a DFS redirect, or
903 any other NT_STATUS error which is a genuine error to be
904 returned to the client.
905 *****************************************************************/
907 static NTSTATUS
dfs_redirect(TALLOC_CTX
*ctx
,
908 connection_struct
*conn
,
911 bool allow_broken_path
,
913 bool *ppath_contains_wcard
)
916 bool search_wcard_flag
= (ucf_flags
&
917 (UCF_COND_ALLOW_WCARD_LCOMP
|UCF_ALWAYS_ALLOW_WCARD_LCOMP
));
918 struct dfs_path
*pdp
= talloc(ctx
, struct dfs_path
);
921 return NT_STATUS_NO_MEMORY
;
924 status
= parse_dfs_path(conn
, path_in
, search_wcard_flag
,
925 allow_broken_path
, pdp
,
926 ppath_contains_wcard
);
927 if (!NT_STATUS_IS_OK(status
)) {
932 if (pdp
->reqpath
[0] == '\0') {
934 *pp_path_out
= talloc_strdup(ctx
, "");
936 return NT_STATUS_NO_MEMORY
;
938 DEBUG(5,("dfs_redirect: self-referral.\n"));
942 /* If dfs pathname for a non-dfs share, convert to tcon-relative
943 path and return OK */
945 if (!lp_msdfs_root(SNUM(conn
))) {
946 *pp_path_out
= talloc_strdup(ctx
, pdp
->reqpath
);
949 return NT_STATUS_NO_MEMORY
;
954 /* If it looked like a local path (zero hostname/servicename)
955 * just treat as a tcon-relative path. */
957 if (pdp
->hostname
[0] == '\0' && pdp
->servicename
[0] == '\0') {
958 *pp_path_out
= talloc_strdup(ctx
, pdp
->reqpath
);
961 return NT_STATUS_NO_MEMORY
;
966 if (!( strequal(pdp
->servicename
, lp_servicename(talloc_tos(), SNUM(conn
)))
967 || (strequal(pdp
->servicename
, HOMES_NAME
)
968 && strequal(lp_servicename(talloc_tos(), SNUM(conn
)),
969 conn
->session_info
->unix_info
->sanitized_username
) )) ) {
971 /* The given sharename doesn't match this connection. */
974 return NT_STATUS_OBJECT_PATH_NOT_FOUND
;
977 status
= dfs_path_lookup(ctx
, conn
, path_in
, pdp
,
978 ucf_flags
, NULL
, NULL
);
979 if (!NT_STATUS_IS_OK(status
)) {
980 if (NT_STATUS_EQUAL(status
, NT_STATUS_PATH_NOT_COVERED
)) {
981 DEBUG(3,("dfs_redirect: Redirecting %s\n", path_in
));
983 DEBUG(10,("dfs_redirect: dfs_path_lookup "
984 "failed for %s with %s\n",
985 path_in
, nt_errstr(status
) ));
990 DEBUG(3,("dfs_redirect: Not redirecting %s.\n", path_in
));
992 /* Form non-dfs tcon-relative path */
993 *pp_path_out
= talloc_strdup(ctx
, pdp
->reqpath
);
996 return NT_STATUS_NO_MEMORY
;
999 DEBUG(3,("dfs_redirect: Path %s converted to non-dfs path %s\n",
1003 return NT_STATUS_OK
;
1006 /**********************************************************************
1007 Return a self referral.
1008 **********************************************************************/
1010 static NTSTATUS
self_ref(TALLOC_CTX
*ctx
,
1011 const char *dfs_path
,
1012 struct junction_map
*jucn
,
1014 bool *self_referralp
)
1016 struct referral
*ref
;
1018 *self_referralp
= True
;
1020 jucn
->referral_count
= 1;
1021 if((ref
= talloc_zero(ctx
, struct referral
)) == NULL
) {
1022 return NT_STATUS_NO_MEMORY
;
1025 ref
->alternate_path
= talloc_strdup(ctx
, dfs_path
);
1026 if (!ref
->alternate_path
) {
1028 return NT_STATUS_NO_MEMORY
;
1031 ref
->ttl
= REFERRAL_TTL
;
1032 jucn
->referral_list
= ref
;
1033 *consumedcntp
= strlen(dfs_path
);
1034 return NT_STATUS_OK
;
1037 /**********************************************************************
1038 Gets valid referrals for a dfs path and fills up the
1039 junction_map structure.
1040 **********************************************************************/
1042 NTSTATUS
get_referred_path(TALLOC_CTX
*ctx
,
1043 const char *dfs_path
,
1044 const struct tsocket_address
*remote_address
,
1045 const struct tsocket_address
*local_address
,
1046 bool allow_broken_path
,
1047 struct junction_map
*jucn
,
1049 bool *self_referralp
)
1051 TALLOC_CTX
*frame
= talloc_stackframe();
1052 struct conn_struct_tos
*c
= NULL
;
1053 struct connection_struct
*conn
= NULL
;
1054 char *targetpath
= NULL
;
1056 NTSTATUS status
= NT_STATUS_NOT_FOUND
;
1058 struct dfs_path
*pdp
= talloc_zero(frame
, struct dfs_path
);
1062 return NT_STATUS_NO_MEMORY
;
1065 *self_referralp
= False
;
1067 status
= parse_dfs_path(NULL
, dfs_path
, False
, allow_broken_path
,
1069 if (!NT_STATUS_IS_OK(status
)) {
1074 jucn
->service_name
= talloc_strdup(ctx
, pdp
->servicename
);
1075 jucn
->volume_name
= talloc_strdup(ctx
, pdp
->reqpath
);
1076 if (!jucn
->service_name
|| !jucn
->volume_name
) {
1078 return NT_STATUS_NO_MEMORY
;
1081 /* Verify the share is a dfs root */
1082 snum
= lp_servicenumber(jucn
->service_name
);
1084 char *service_name
= NULL
;
1085 if ((snum
= find_service(ctx
, jucn
->service_name
, &service_name
)) < 0) {
1087 return NT_STATUS_NOT_FOUND
;
1089 if (!service_name
) {
1091 return NT_STATUS_NO_MEMORY
;
1093 TALLOC_FREE(jucn
->service_name
);
1094 jucn
->service_name
= talloc_strdup(ctx
, service_name
);
1095 if (!jucn
->service_name
) {
1097 return NT_STATUS_NO_MEMORY
;
1101 if (!lp_msdfs_root(snum
) && (*lp_msdfs_proxy(talloc_tos(), snum
) == '\0')) {
1102 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not "
1104 pdp
->servicename
, dfs_path
));
1106 return NT_STATUS_NOT_FOUND
;
1110 * Self referrals are tested with a anonymous IPC connection and
1111 * a GET_DFS_REFERRAL call to \\server\share. (which means
1112 * dp.reqpath[0] points to an empty string). create_conn_struct cd's
1113 * into the directory and will fail if it cannot (as the anonymous
1114 * user). Cope with this.
1117 if (pdp
->reqpath
[0] == '\0') {
1119 struct referral
*ref
;
1122 if (*lp_msdfs_proxy(talloc_tos(), snum
) == '\0') {
1124 return self_ref(ctx
,
1132 * It's an msdfs proxy share. Redirect to
1133 * the configured target share.
1136 tmp
= talloc_asprintf(frame
, "msdfs:%s",
1137 lp_msdfs_proxy(frame
, snum
));
1140 return NT_STATUS_NO_MEMORY
;
1143 if (!parse_msdfs_symlink(ctx
, snum
, tmp
, &ref
, &refcount
)) {
1145 return NT_STATUS_INVALID_PARAMETER
;
1147 jucn
->referral_count
= refcount
;
1148 jucn
->referral_list
= ref
;
1149 *consumedcntp
= strlen(dfs_path
);
1151 return NT_STATUS_OK
;
1154 status
= create_conn_struct_tos_cwd(global_messaging_context(),
1156 lp_path(frame
, snum
),
1159 if (!NT_STATUS_IS_OK(status
)) {
1168 * The remote and local address should be passed down to
1169 * create_conn_struct_cwd.
1171 if (conn
->sconn
->remote_address
== NULL
) {
1172 conn
->sconn
->remote_address
=
1173 tsocket_address_copy(remote_address
, conn
->sconn
);
1174 if (conn
->sconn
->remote_address
== NULL
) {
1176 return NT_STATUS_NO_MEMORY
;
1179 if (conn
->sconn
->local_address
== NULL
) {
1180 conn
->sconn
->local_address
=
1181 tsocket_address_copy(local_address
, conn
->sconn
);
1182 if (conn
->sconn
->local_address
== NULL
) {
1184 return NT_STATUS_NO_MEMORY
;
1188 /* If this is a DFS path dfs_lookup should return
1189 * NT_STATUS_PATH_NOT_COVERED. */
1191 status
= dfs_path_lookup(ctx
, conn
, dfs_path
, pdp
,
1192 0, consumedcntp
, &targetpath
);
1194 if (!NT_STATUS_EQUAL(status
, NT_STATUS_PATH_NOT_COVERED
)) {
1195 DEBUG(3,("get_referred_path: No valid referrals for path %s\n",
1197 if (NT_STATUS_IS_OK(status
)) {
1199 * We are in an error path here (we
1200 * know it's not a DFS path), but
1201 * dfs_path_lookup() can return
1202 * NT_STATUS_OK. Ensure we always
1203 * return a valid error code.
1205 * #9588 - ACLs are not inherited to directories
1208 status
= NT_STATUS_NOT_FOUND
;
1213 /* We know this is a valid dfs link. Parse the targetpath. */
1214 if (!parse_msdfs_symlink(ctx
, snum
, targetpath
,
1215 &jucn
->referral_list
,
1216 &jucn
->referral_count
)) {
1217 DEBUG(3,("get_referred_path: failed to parse symlink "
1218 "target %s\n", targetpath
));
1219 status
= NT_STATUS_NOT_FOUND
;
1223 status
= NT_STATUS_OK
;
1229 /******************************************************************
1230 Set up the DFS referral for the dfs pathname. This call returns
1231 the amount of the path covered by this server, and where the
1232 client should be redirected to. This is the meat of the
1233 TRANS2_GET_DFS_REFERRAL call.
1234 ******************************************************************/
1236 int setup_dfs_referral(connection_struct
*orig_conn
,
1237 const char *dfs_path
,
1238 int max_referral_level
,
1239 char **ppdata
, NTSTATUS
*pstatus
)
1241 char *pdata
= *ppdata
;
1243 struct dfs_GetDFSReferral
*r
;
1244 DATA_BLOB blob
= data_blob_null
;
1246 enum ndr_err_code ndr_err
;
1248 r
= talloc_zero(talloc_tos(), struct dfs_GetDFSReferral
);
1250 *pstatus
= NT_STATUS_NO_MEMORY
;
1254 r
->in
.req
.max_referral_level
= max_referral_level
;
1255 r
->in
.req
.servername
= talloc_strdup(r
, dfs_path
);
1256 if (r
->in
.req
.servername
== NULL
) {
1258 *pstatus
= NT_STATUS_NO_MEMORY
;
1262 status
= SMB_VFS_GET_DFS_REFERRALS(orig_conn
, r
);
1263 if (!NT_STATUS_IS_OK(status
)) {
1269 ndr_err
= ndr_push_struct_blob(&blob
, r
,
1271 (ndr_push_flags_fn_t
)ndr_push_dfs_referral_resp
);
1272 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
1274 *pstatus
= NT_STATUS_INVALID_PARAMETER
;
1278 pdata
= (char *)SMB_REALLOC(pdata
, blob
.length
);
1281 DEBUG(0,("referral setup:"
1282 "malloc failed for Realloc!\n"));
1286 reply_size
= blob
.length
;
1287 memcpy(pdata
, blob
.data
, blob
.length
);
1290 *pstatus
= NT_STATUS_OK
;
1294 /**********************************************************************
1295 The following functions are called by the NETDFS RPC pipe functions
1296 **********************************************************************/
1298 /*********************************************************************
1299 Creates a junction structure from a DFS pathname
1300 **********************************************************************/
1302 bool create_junction(TALLOC_CTX
*ctx
,
1303 const char *dfs_path
,
1304 bool allow_broken_path
,
1305 struct junction_map
*jucn
)
1309 struct dfs_path
*pdp
= talloc(ctx
,struct dfs_path
);
1315 status
= parse_dfs_path(NULL
, dfs_path
, False
, allow_broken_path
,
1317 if (!NT_STATUS_IS_OK(status
)) {
1321 /* check if path is dfs : validate first token */
1322 if (!is_myname_or_ipaddr(pdp
->hostname
)) {
1323 DEBUG(4,("create_junction: Invalid hostname %s "
1325 pdp
->hostname
, dfs_path
));
1330 /* Check for a non-DFS share */
1331 snum
= lp_servicenumber(pdp
->servicename
);
1333 if(snum
< 0 || !lp_msdfs_root(snum
)) {
1334 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1340 jucn
->service_name
= talloc_strdup(ctx
, pdp
->servicename
);
1341 jucn
->volume_name
= talloc_strdup(ctx
, pdp
->reqpath
);
1342 jucn
->comment
= lp_comment(ctx
, snum
);
1345 if (!jucn
->service_name
|| !jucn
->volume_name
|| ! jucn
->comment
) {
1351 /**********************************************************************
1352 Forms a valid Unix pathname from the junction
1353 **********************************************************************/
1355 static bool junction_to_local_path_tos(const struct junction_map
*jucn
,
1357 connection_struct
**conn_out
)
1359 struct conn_struct_tos
*c
= NULL
;
1361 char *path_out
= NULL
;
1364 snum
= lp_servicenumber(jucn
->service_name
);
1368 status
= create_conn_struct_tos_cwd(global_messaging_context(),
1370 lp_path(talloc_tos(), snum
),
1373 if (!NT_STATUS_IS_OK(status
)) {
1377 path_out
= talloc_asprintf(c
,
1379 lp_path(talloc_tos(), snum
),
1381 if (path_out
== NULL
) {
1385 *pp_path_out
= path_out
;
1386 *conn_out
= c
->conn
;
1390 bool create_msdfs_link(const struct junction_map
*jucn
)
1392 TALLOC_CTX
*frame
= talloc_stackframe();
1394 char *msdfs_link
= NULL
;
1395 connection_struct
*conn
;
1397 bool insert_comma
= False
;
1399 struct smb_filename
*smb_fname
= NULL
;
1402 ok
= junction_to_local_path_tos(jucn
, &path
, &conn
);
1408 /* Form the msdfs_link contents */
1409 msdfs_link
= talloc_strdup(conn
, "msdfs:");
1413 for(i
=0; i
<jucn
->referral_count
; i
++) {
1414 char *refpath
= jucn
->referral_list
[i
].alternate_path
;
1416 /* Alternate paths always use Windows separators. */
1417 trim_char(refpath
, '\\', '\\');
1418 if(*refpath
== '\0') {
1420 insert_comma
= False
;
1424 if (i
> 0 && insert_comma
) {
1425 msdfs_link
= talloc_asprintf_append_buffer(msdfs_link
,
1429 msdfs_link
= talloc_asprintf_append_buffer(msdfs_link
,
1437 if (!insert_comma
) {
1438 insert_comma
= True
;
1442 DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
1445 smb_fname
= synthetic_smb_fname(frame
,
1450 if (smb_fname
== NULL
) {
1455 if(SMB_VFS_SYMLINK(conn
, msdfs_link
, smb_fname
) < 0) {
1456 if (errno
== EEXIST
) {
1457 if(SMB_VFS_UNLINK(conn
, smb_fname
)!=0) {
1458 TALLOC_FREE(smb_fname
);
1462 if (SMB_VFS_SYMLINK(conn
, msdfs_link
, smb_fname
) < 0) {
1463 DEBUG(1,("create_msdfs_link: symlink failed "
1464 "%s -> %s\nError: %s\n",
1465 path
, msdfs_link
, strerror(errno
)));
1477 bool remove_msdfs_link(const struct junction_map
*jucn
)
1479 TALLOC_CTX
*frame
= talloc_stackframe();
1481 connection_struct
*conn
;
1483 struct smb_filename
*smb_fname
;
1486 ok
= junction_to_local_path_tos(jucn
, &path
, &conn
);
1492 smb_fname
= synthetic_smb_fname(frame
,
1497 if (smb_fname
== NULL
) {
1503 if( SMB_VFS_UNLINK(conn
, smb_fname
) == 0 ) {
1511 /*********************************************************************
1512 Return the number of DFS links at the root of this share.
1513 *********************************************************************/
1515 static int count_dfs_links(TALLOC_CTX
*ctx
, int snum
)
1517 TALLOC_CTX
*frame
= talloc_stackframe();
1520 const char *dname
= NULL
;
1521 char *talloced
= NULL
;
1522 const char *connect_path
= lp_path(frame
, snum
);
1523 const char *msdfs_proxy
= lp_msdfs_proxy(frame
, snum
);
1524 struct conn_struct_tos
*c
= NULL
;
1525 connection_struct
*conn
= NULL
;
1527 struct smb_filename
*smb_fname
= NULL
;
1529 if(*connect_path
== '\0') {
1535 * Fake up a connection struct for the VFS layer.
1538 status
= create_conn_struct_tos_cwd(global_messaging_context(),
1543 if (!NT_STATUS_IS_OK(status
)) {
1544 DEBUG(3, ("create_conn_struct failed: %s\n",
1545 nt_errstr(status
)));
1551 /* Count a link for the msdfs root - convention */
1554 /* No more links if this is an msdfs proxy. */
1555 if (*msdfs_proxy
!= '\0') {
1559 smb_fname
= synthetic_smb_fname(frame
,
1564 if (smb_fname
== NULL
) {
1568 /* Now enumerate all dfs links */
1569 dirp
= SMB_VFS_OPENDIR(conn
, smb_fname
, NULL
, 0);
1574 while ((dname
= vfs_readdirname(conn
, dirp
, NULL
, &talloced
))
1576 struct smb_filename
*smb_dname
=
1577 synthetic_smb_fname(frame
,
1582 if (smb_dname
== NULL
) {
1585 if (is_msdfs_link(conn
, smb_dname
)) {
1588 TALLOC_FREE(talloced
);
1589 TALLOC_FREE(smb_dname
);
1592 SMB_VFS_CLOSEDIR(conn
,dirp
);
1599 /*********************************************************************
1600 *********************************************************************/
1602 static int form_junctions(TALLOC_CTX
*ctx
,
1604 struct junction_map
*jucn
,
1607 TALLOC_CTX
*frame
= talloc_stackframe();
1610 const char *dname
= NULL
;
1611 char *talloced
= NULL
;
1612 const char *connect_path
= lp_path(frame
, snum
);
1613 char *service_name
= lp_servicename(frame
, snum
);
1614 const char *msdfs_proxy
= lp_msdfs_proxy(frame
, snum
);
1615 struct conn_struct_tos
*c
= NULL
;
1616 connection_struct
*conn
= NULL
;
1617 struct referral
*ref
= NULL
;
1618 struct smb_filename
*smb_fname
= NULL
;
1621 if (jn_remain
== 0) {
1626 if(*connect_path
== '\0') {
1632 * Fake up a connection struct for the VFS layer.
1635 status
= create_conn_struct_tos_cwd(global_messaging_context(),
1640 if (!NT_STATUS_IS_OK(status
)) {
1641 DEBUG(3, ("create_conn_struct failed: %s\n",
1642 nt_errstr(status
)));
1648 /* form a junction for the msdfs root - convention
1649 DO NOT REMOVE THIS: NT clients will not work with us
1650 if this is not present
1652 jucn
[cnt
].service_name
= talloc_strdup(ctx
,service_name
);
1653 jucn
[cnt
].volume_name
= talloc_strdup(ctx
, "");
1654 if (!jucn
[cnt
].service_name
|| !jucn
[cnt
].volume_name
) {
1657 jucn
[cnt
].comment
= "";
1658 jucn
[cnt
].referral_count
= 1;
1660 ref
= jucn
[cnt
].referral_list
= talloc_zero(ctx
, struct referral
);
1661 if (jucn
[cnt
].referral_list
== NULL
) {
1666 ref
->ttl
= REFERRAL_TTL
;
1667 if (*msdfs_proxy
!= '\0') {
1668 ref
->alternate_path
= talloc_strdup(ctx
,
1671 ref
->alternate_path
= talloc_asprintf(ctx
,
1673 get_local_machine_name(),
1677 if (!ref
->alternate_path
) {
1682 /* Don't enumerate if we're an msdfs proxy. */
1683 if (*msdfs_proxy
!= '\0') {
1687 smb_fname
= synthetic_smb_fname(frame
,
1692 if (smb_fname
== NULL
) {
1696 /* Now enumerate all dfs links */
1697 dirp
= SMB_VFS_OPENDIR(conn
, smb_fname
, NULL
, 0);
1702 while ((dname
= vfs_readdirname(conn
, dirp
, NULL
, &talloced
))
1704 char *link_target
= NULL
;
1705 struct smb_filename
*smb_dname
= NULL
;
1707 if (cnt
>= jn_remain
) {
1708 DEBUG(2, ("form_junctions: ran out of MSDFS "
1710 TALLOC_FREE(talloced
);
1713 smb_dname
= synthetic_smb_fname(talloc_tos(),
1718 if (smb_dname
== NULL
) {
1719 TALLOC_FREE(talloced
);
1722 if (is_msdfs_link_internal(ctx
,
1724 smb_dname
, &link_target
)) {
1725 if (parse_msdfs_symlink(ctx
, snum
,
1727 &jucn
[cnt
].referral_list
,
1728 &jucn
[cnt
].referral_count
)) {
1730 jucn
[cnt
].service_name
= talloc_strdup(ctx
,
1732 jucn
[cnt
].volume_name
= talloc_strdup(ctx
,
1734 if (!jucn
[cnt
].service_name
||
1735 !jucn
[cnt
].volume_name
) {
1736 TALLOC_FREE(talloced
);
1739 jucn
[cnt
].comment
= "";
1742 TALLOC_FREE(link_target
);
1744 TALLOC_FREE(talloced
);
1745 TALLOC_FREE(smb_dname
);
1751 SMB_VFS_CLOSEDIR(conn
,dirp
);
1758 struct junction_map
*enum_msdfs_links(TALLOC_CTX
*ctx
, size_t *p_num_jn
)
1760 struct junction_map
*jn
= NULL
;
1762 size_t jn_count
= 0;
1766 if(!lp_host_msdfs()) {
1770 /* Ensure all the usershares are loaded. */
1772 load_registry_shares();
1773 sharecount
= load_usershare_shares(NULL
, connections_snum_used
);
1776 for(i
=0;i
< sharecount
;i
++) {
1777 if(lp_msdfs_root(i
)) {
1778 jn_count
+= count_dfs_links(ctx
, i
);
1781 if (jn_count
== 0) {
1784 jn
= talloc_array(ctx
, struct junction_map
, jn_count
);
1788 for(i
=0; i
< sharecount
; i
++) {
1789 if (*p_num_jn
>= jn_count
) {
1792 if(lp_msdfs_root(i
)) {
1793 *p_num_jn
+= form_junctions(ctx
, i
,
1795 jn_count
- *p_num_jn
);
1801 /******************************************************************************
1802 Core function to resolve a dfs pathname possibly containing a wildcard. If
1803 ppath_contains_wcard != NULL, it will be set to true if a wildcard is
1804 detected during dfs resolution.
1805 ******************************************************************************/
1807 NTSTATUS
resolve_dfspath_wcard(TALLOC_CTX
*ctx
,
1808 connection_struct
*conn
,
1809 const char *name_in
,
1811 bool allow_broken_path
,
1813 bool *ppath_contains_wcard
)
1815 bool path_contains_wcard
= false;
1816 NTSTATUS status
= NT_STATUS_OK
;
1818 status
= dfs_redirect(ctx
,
1824 &path_contains_wcard
);
1826 if (NT_STATUS_IS_OK(status
) &&
1827 ppath_contains_wcard
!= NULL
&&
1828 path_contains_wcard
) {
1829 *ppath_contains_wcard
= path_contains_wcard
;