2 Unix SMB/Netbios implementation.
4 MSDFS services for Samba
5 Copyright (C) Shirish Kalele 2000
6 Copyright (C) Jeremy Allison 2007
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #define DBGC_CLASS DBGC_MSDFS
25 #include "system/filesys.h"
26 #include "smbd/smbd.h"
27 #include "smbd/globals.h"
30 #include "lib/param/loadparm.h"
31 #include "libcli/security/security.h"
32 #include "librpc/gen_ndr/ndr_dfsblobs.h"
34 /**********************************************************************
35 Parse a DFS pathname of the form \hostname\service\reqpath
36 into the dfs_path structure.
37 If POSIX pathnames is true, the pathname may also be of the
38 form /hostname/service/reqpath.
39 We cope with either here.
41 Unfortunately, due to broken clients who might set the
42 SVAL(inbuf,smb_flg2) & FLAGS2_DFS_PATHNAMES bit and then
43 send a local path, we have to cope with that too....
45 If conn != NULL then ensure the provided service is
46 the one pointed to by the connection.
48 This version does everything using pointers within one copy of the
49 pathname string, talloced on the struct dfs_path pointer (which
50 must be talloced). This may be too clever to live....
52 **********************************************************************/
54 static NTSTATUS
parse_dfs_path(connection_struct
*conn
,
57 bool allow_broken_path
,
58 struct dfs_path
*pdp
, /* MUST BE TALLOCED */
59 bool *ppath_contains_wcard
)
65 NTSTATUS status
= NT_STATUS_OK
;
71 * This is the only talloc we should need to do
72 * on the struct dfs_path. All the pointers inside
73 * it should point to offsets within this string.
76 pathname_local
= talloc_strdup(pdp
, pathname
);
77 if (!pathname_local
) {
78 return NT_STATUS_NO_MEMORY
;
80 /* Get a pointer to the terminating '\0' */
81 eos_ptr
= &pathname_local
[strlen(pathname_local
)];
82 p
= temp
= pathname_local
;
84 pdp
->posix_path
= (lp_posix_pathnames() && *pathname
== '/');
86 sepchar
= pdp
->posix_path
? '/' : '\\';
88 if (allow_broken_path
&& (*pathname
!= sepchar
)) {
89 DEBUG(10,("parse_dfs_path: path %s doesn't start with %c\n",
92 * Possibly client sent a local path by mistake.
93 * Try and convert to a local path.
96 pdp
->hostname
= eos_ptr
; /* "" */
97 pdp
->servicename
= eos_ptr
; /* "" */
99 /* We've got no info about separators. */
100 pdp
->posix_path
= lp_posix_pathnames();
102 DEBUG(10,("parse_dfs_path: trying to convert %s to a "
109 * Safe to use on talloc'ed string as it only shrinks.
110 * It also doesn't affect the eos_ptr.
112 trim_char(temp
,sepchar
,sepchar
);
114 DEBUG(10,("parse_dfs_path: temp = |%s| after trimming %c's\n",
118 /* Parse out hostname. */
119 p
= strchr_m(temp
,sepchar
);
121 DEBUG(10,("parse_dfs_path: can't parse hostname from path %s\n",
124 * Possibly client sent a local path by mistake.
125 * Try and convert to a local path.
128 pdp
->hostname
= eos_ptr
; /* "" */
129 pdp
->servicename
= eos_ptr
; /* "" */
132 DEBUG(10,("parse_dfs_path: trying to convert %s "
138 pdp
->hostname
= temp
;
140 DEBUG(10,("parse_dfs_path: hostname: %s\n",pdp
->hostname
));
142 /* Parse out servicename. */
144 p
= strchr_m(servicename
,sepchar
);
149 /* Is this really our servicename ? */
150 if (conn
&& !( strequal(servicename
, lp_servicename(talloc_tos(), SNUM(conn
)))
151 || (strequal(servicename
, HOMES_NAME
)
152 && strequal(lp_servicename(talloc_tos(), SNUM(conn
)),
153 get_current_username()) )) ) {
154 DEBUG(10,("parse_dfs_path: %s is not our servicename\n",
158 * Possibly client sent a local path by mistake.
159 * Try and convert to a local path.
162 pdp
->hostname
= eos_ptr
; /* "" */
163 pdp
->servicename
= eos_ptr
; /* "" */
165 /* Repair the path - replace the sepchar's
168 *servicename
= sepchar
;
174 DEBUG(10,("parse_dfs_path: trying to convert %s "
180 pdp
->servicename
= servicename
;
182 DEBUG(10,("parse_dfs_path: servicename: %s\n",pdp
->servicename
));
185 /* Client sent self referral \server\share. */
186 pdp
->reqpath
= eos_ptr
; /* "" */
194 *ppath_contains_wcard
= False
;
198 /* Rest is reqpath. */
199 if (pdp
->posix_path
) {
200 status
= check_path_syntax_posix(pdp
->reqpath
);
203 status
= check_path_syntax_wcard(pdp
->reqpath
,
204 ppath_contains_wcard
);
206 status
= check_path_syntax(pdp
->reqpath
);
210 if (!NT_STATUS_IS_OK(status
)) {
211 DEBUG(10,("parse_dfs_path: '%s' failed with %s\n",
212 p
, nt_errstr(status
) ));
216 DEBUG(10,("parse_dfs_path: rest of the path: %s\n",pdp
->reqpath
));
220 /********************************************************
221 Fake up a connection struct for the VFS layer, for use in
222 applications (such as the python bindings), that do not want the
223 global working directory changed under them.
225 SMB_VFS_CONNECT requires root privileges.
226 *********************************************************/
228 static NTSTATUS
create_conn_struct_as_root(TALLOC_CTX
*ctx
,
229 struct tevent_context
*ev
,
230 struct messaging_context
*msg
,
231 connection_struct
**pconn
,
234 const struct auth_session_info
*session_info
)
236 connection_struct
*conn
;
238 const char *vfs_user
;
239 struct smbd_server_connection
*sconn
;
240 const char *servicename
= lp_const_servicename(snum
);
242 sconn
= talloc_zero(ctx
, struct smbd_server_connection
);
244 return NT_STATUS_NO_MEMORY
;
248 sconn
->msg_ctx
= msg
;
250 smbd_echo_init(sconn
);
252 conn
= conn_new(sconn
);
255 return NT_STATUS_NO_MEMORY
;
258 /* Now we have conn, we need to make sconn a child of conn,
259 * for a proper talloc tree */
260 talloc_steal(conn
, sconn
);
262 if (snum
== -1 && servicename
== NULL
) {
263 servicename
= "Unknown Service (snum == -1)";
266 connpath
= talloc_strdup(conn
, path
);
269 return NT_STATUS_NO_MEMORY
;
271 connpath
= talloc_string_sub(conn
,
277 return NT_STATUS_NO_MEMORY
;
280 /* needed for smbd_vfs_init() */
282 conn
->params
->service
= snum
;
283 conn
->cnum
= TID_FIELD_INVALID
;
285 if (session_info
!= NULL
) {
286 conn
->session_info
= copy_session_info(conn
, session_info
);
287 if (conn
->session_info
== NULL
) {
288 DEBUG(0, ("copy_serverinfo failed\n"));
290 return NT_STATUS_NO_MEMORY
;
292 vfs_user
= conn
->session_info
->unix_info
->unix_name
;
294 /* use current authenticated user in absence of session_info */
295 vfs_user
= get_current_username();
298 set_conn_connectpath(conn
, connpath
);
301 * New code to check if there's a share security descripter
302 * added from NT server manager. This is done after the
303 * smb.conf checks are done as we need a uid and token. JRA.
306 if (conn
->session_info
) {
307 share_access_check(conn
->session_info
->security_token
,
309 MAXIMUM_ALLOWED_ACCESS
,
310 &conn
->share_access
);
312 if ((conn
->share_access
& FILE_WRITE_DATA
) == 0) {
313 if ((conn
->share_access
& FILE_READ_DATA
) == 0) {
314 /* No access, read or write. */
315 DEBUG(0,("create_conn_struct: connection to %s "
316 "denied due to security "
320 return NT_STATUS_ACCESS_DENIED
;
322 conn
->read_only
= true;
326 conn
->share_access
= 0;
327 conn
->read_only
= true;
330 if (!smbd_vfs_init(conn
)) {
331 NTSTATUS status
= map_nt_error_from_unix(errno
);
332 DEBUG(0,("create_conn_struct: smbd_vfs_init failed.\n"));
337 /* this must be the first filesystem operation that we do */
338 if (SMB_VFS_CONNECT(conn
, servicename
, vfs_user
) < 0) {
339 DEBUG(0,("VFS connect failed!\n"));
341 return NT_STATUS_UNSUCCESSFUL
;
344 conn
->fs_capabilities
= SMB_VFS_FS_CAPABILITIES(conn
, &conn
->ts_res
);
350 /********************************************************
351 Fake up a connection struct for the VFS layer, for use in
352 applications (such as the python bindings), that do not want the
353 global working directory changed under them.
355 SMB_VFS_CONNECT requires root privileges.
356 *********************************************************/
358 NTSTATUS
create_conn_struct(TALLOC_CTX
*ctx
,
359 struct tevent_context
*ev
,
360 struct messaging_context
*msg
,
361 connection_struct
**pconn
,
364 const struct auth_session_info
*session_info
)
368 status
= create_conn_struct_as_root(ctx
, ev
,
377 /********************************************************
378 Fake up a connection struct for the VFS layer.
379 Note: this performs a vfs connect and CHANGES CWD !!!! JRA.
381 The old working directory is returned on *poldcwd, allocated on ctx.
382 *********************************************************/
384 NTSTATUS
create_conn_struct_cwd(TALLOC_CTX
*ctx
,
385 struct tevent_context
*ev
,
386 struct messaging_context
*msg
,
387 connection_struct
**pconn
,
390 const struct auth_session_info
*session_info
,
393 connection_struct
*conn
;
396 NTSTATUS status
= create_conn_struct(ctx
, ev
,
400 if (!NT_STATUS_IS_OK(status
)) {
405 * Windows seems to insist on doing trans2getdfsreferral() calls on
406 * the IPC$ share as the anonymous user. If we try to chdir as that
407 * user we will fail.... WTF ? JRA.
410 oldcwd
= vfs_GetWd(ctx
, conn
);
411 if (oldcwd
== NULL
) {
412 status
= map_nt_error_from_unix(errno
);
413 DEBUG(3, ("vfs_GetWd failed: %s\n", strerror(errno
)));
418 if (vfs_ChDir(conn
,conn
->connectpath
) != 0) {
419 status
= map_nt_error_from_unix(errno
);
420 DEBUG(3,("create_conn_struct: Can't ChDir to new conn path %s. "
422 conn
->connectpath
, strerror(errno
) ));
433 /**********************************************************************
434 Parse the contents of a symlink to verify if it is an msdfs referral
435 A valid referral is of the form:
437 msdfs:server1\share1,server2\share2
438 msdfs:server1\share1\pathname,server2\share2\pathname
439 msdfs:server1/share1,server2/share2
440 msdfs:server1/share1/pathname,server2/share2/pathname.
442 Note that the alternate paths returned here must be of the canonicalized
446 \server\share\path\to\file,
448 even in posix path mode. This is because we have no knowledge if the
449 server we're referring to understands posix paths.
450 **********************************************************************/
452 static bool parse_msdfs_symlink(TALLOC_CTX
*ctx
,
454 struct referral
**preflist
,
459 char **alt_path
= NULL
;
461 struct referral
*reflist
;
464 temp
= talloc_strdup(ctx
, target
);
468 prot
= strtok_r(temp
, ":", &saveptr
);
470 DEBUG(0,("parse_msdfs_symlink: invalid path !\n"));
474 alt_path
= talloc_array(ctx
, char *, MAX_REFERRAL_COUNT
);
479 /* parse out the alternate paths */
480 while((count
<MAX_REFERRAL_COUNT
) &&
481 ((alt_path
[count
] = strtok_r(NULL
, ",", &saveptr
)) != NULL
)) {
485 DEBUG(10,("parse_msdfs_symlink: count=%d\n", count
));
488 reflist
= *preflist
= talloc_zero_array(ctx
,
489 struct referral
, count
);
490 if(reflist
== NULL
) {
491 TALLOC_FREE(alt_path
);
495 reflist
= *preflist
= NULL
;
498 for(i
=0;i
<count
;i
++) {
501 /* Canonicalize link target.
502 * Replace all /'s in the path by a \ */
503 string_replace(alt_path
[i
], '/', '\\');
505 /* Remove leading '\\'s */
507 while (*p
&& (*p
== '\\')) {
511 reflist
[i
].alternate_path
= talloc_asprintf(ctx
,
514 if (!reflist
[i
].alternate_path
) {
518 reflist
[i
].proximity
= 0;
519 reflist
[i
].ttl
= REFERRAL_TTL
;
520 DEBUG(10, ("parse_msdfs_symlink: Created alt path: %s\n",
521 reflist
[i
].alternate_path
));
526 TALLOC_FREE(alt_path
);
530 /**********************************************************************
531 Returns true if the unix path is a valid msdfs symlink and also
532 returns the target string from inside the link.
533 **********************************************************************/
535 static bool is_msdfs_link_internal(TALLOC_CTX
*ctx
,
536 connection_struct
*conn
,
538 char **pp_link_target
,
539 SMB_STRUCT_STAT
*sbufp
)
541 int referral_len
= 0;
542 #if defined(HAVE_BROKEN_READLINK)
543 char link_target_buf
[PATH_MAX
];
545 char link_target_buf
[7];
548 char *link_target
= NULL
;
549 struct smb_filename smb_fname
;
551 if (pp_link_target
) {
553 link_target
= talloc_array(ctx
, char, bufsize
);
557 *pp_link_target
= link_target
;
559 bufsize
= sizeof(link_target_buf
);
560 link_target
= link_target_buf
;
563 ZERO_STRUCT(smb_fname
);
564 smb_fname
.base_name
= discard_const_p(char, path
);
566 if (SMB_VFS_LSTAT(conn
, &smb_fname
) != 0) {
567 DEBUG(5,("is_msdfs_link_read_target: %s does not exist.\n",
571 if (!S_ISLNK(smb_fname
.st
.st_ex_mode
)) {
572 DEBUG(5,("is_msdfs_link_read_target: %s is not a link.\n",
577 *sbufp
= smb_fname
.st
;
580 referral_len
= SMB_VFS_READLINK(conn
, path
, link_target
, bufsize
- 1);
581 if (referral_len
== -1) {
582 DEBUG(0,("is_msdfs_link_read_target: Error reading "
583 "msdfs link %s: %s\n",
584 path
, strerror(errno
)));
587 link_target
[referral_len
] = '\0';
589 DEBUG(5,("is_msdfs_link_internal: %s -> %s\n",path
,
592 if (!strnequal(link_target
, "msdfs:", 6)) {
599 if (link_target
!= link_target_buf
) {
600 TALLOC_FREE(link_target
);
605 /**********************************************************************
606 Returns true if the unix path is a valid msdfs symlink.
607 **********************************************************************/
609 bool is_msdfs_link(connection_struct
*conn
,
611 SMB_STRUCT_STAT
*sbufp
)
613 return is_msdfs_link_internal(talloc_tos(),
620 /*****************************************************************
621 Used by other functions to decide if a dfs path is remote,
622 and to get the list of referred locations for that remote path.
624 search_flag: For findfirsts, dfs links themselves are not
625 redirected, but paths beyond the links are. For normal smb calls,
626 even dfs links need to be redirected.
628 consumedcntp: how much of the dfs path is being redirected. the client
629 should try the remaining path on the redirected server.
631 If this returns NT_STATUS_PATH_NOT_COVERED the contents of the msdfs
632 link redirect are in targetpath.
633 *****************************************************************/
635 static NTSTATUS
dfs_path_lookup(TALLOC_CTX
*ctx
,
636 connection_struct
*conn
,
637 const char *dfspath
, /* Incoming complete dfs path */
638 const struct dfs_path
*pdp
, /* Parsed out
639 server+share+extrapath. */
640 bool search_flag
, /* Called from a findfirst ? */
642 char **pp_targetpath
)
647 struct smb_filename
*smb_fname
= NULL
;
648 char *canon_dfspath
= NULL
; /* Canonicalized dfs path. (only '/'
651 DEBUG(10,("dfs_path_lookup: Conn path = %s reqpath = %s\n",
652 conn
->connectpath
, pdp
->reqpath
));
655 * Note the unix path conversion here we're doing we
656 * throw away. We're looking for a symlink for a dfs
657 * resolution, if we don't find it we'll do another
658 * unix_convert later in the codepath.
661 status
= unix_convert(ctx
, conn
, pdp
->reqpath
, &smb_fname
,
662 search_flag
? UCF_ALWAYS_ALLOW_WCARD_LCOMP
: 0);
664 if (!NT_STATUS_IS_OK(status
)) {
665 if (!NT_STATUS_EQUAL(status
,
666 NT_STATUS_OBJECT_PATH_NOT_FOUND
)) {
669 if (smb_fname
== NULL
|| smb_fname
->base_name
== NULL
) {
674 /* Optimization - check if we can redirect the whole path. */
676 if (is_msdfs_link_internal(ctx
, conn
, smb_fname
->base_name
,
677 pp_targetpath
, NULL
)) {
679 DEBUG(6,("dfs_path_lookup (FindFirst) No redirection "
680 "for dfs link %s.\n", dfspath
));
681 status
= NT_STATUS_OK
;
685 DEBUG(6,("dfs_path_lookup: %s resolves to a "
686 "valid dfs link %s.\n", dfspath
,
687 pp_targetpath
? *pp_targetpath
: ""));
690 *consumedcntp
= strlen(dfspath
);
692 status
= NT_STATUS_PATH_NOT_COVERED
;
696 /* Prepare to test only for '/' components in the given path,
697 * so if a Windows path replace all '\\' characters with '/'.
698 * For a POSIX DFS path we know all separators are already '/'. */
700 canon_dfspath
= talloc_strdup(ctx
, dfspath
);
701 if (!canon_dfspath
) {
702 status
= NT_STATUS_NO_MEMORY
;
705 if (!pdp
->posix_path
) {
706 string_replace(canon_dfspath
, '\\', '/');
710 * localpath comes out of unix_convert, so it has
711 * no trailing backslash. Make sure that canon_dfspath hasn't either.
712 * Fix for bug #4860 from Jan Martin <Jan.Martin@rwedea.com>.
715 trim_char(canon_dfspath
,0,'/');
718 * Redirect if any component in the path is a link.
719 * We do this by walking backwards through the
720 * local path, chopping off the last component
721 * in both the local path and the canonicalized
722 * DFS path. If we hit a DFS link then we're done.
725 p
= strrchr_m(smb_fname
->base_name
, '/');
727 q
= strrchr_m(canon_dfspath
, '/');
736 if (is_msdfs_link_internal(ctx
, conn
,
737 smb_fname
->base_name
, pp_targetpath
,
739 DEBUG(4, ("dfs_path_lookup: Redirecting %s because "
740 "parent %s is dfs link\n", dfspath
,
741 smb_fname_str_dbg(smb_fname
)));
744 *consumedcntp
= strlen(canon_dfspath
);
745 DEBUG(10, ("dfs_path_lookup: Path consumed: %s "
751 status
= NT_STATUS_PATH_NOT_COVERED
;
755 /* Step back on the filesystem. */
756 p
= strrchr_m(smb_fname
->base_name
, '/');
759 /* And in the canonicalized dfs path. */
760 q
= strrchr_m(canon_dfspath
, '/');
764 status
= NT_STATUS_OK
;
766 TALLOC_FREE(smb_fname
);
770 /*****************************************************************
771 Decides if a dfs pathname should be redirected or not.
772 If not, the pathname is converted to a tcon-relative local unix path
774 search_wcard_flag: this flag performs 2 functions both related
775 to searches. See resolve_dfs_path() and parse_dfs_path_XX()
778 This function can return NT_STATUS_OK, meaning use the returned path as-is
779 (mapped into a local path).
780 or NT_STATUS_NOT_COVERED meaning return a DFS redirect, or
781 any other NT_STATUS error which is a genuine error to be
782 returned to the client.
783 *****************************************************************/
785 static NTSTATUS
dfs_redirect(TALLOC_CTX
*ctx
,
786 connection_struct
*conn
,
788 bool search_wcard_flag
,
789 bool allow_broken_path
,
791 bool *ppath_contains_wcard
)
794 struct dfs_path
*pdp
= talloc(ctx
, struct dfs_path
);
797 return NT_STATUS_NO_MEMORY
;
800 status
= parse_dfs_path(conn
, path_in
, search_wcard_flag
,
801 allow_broken_path
, pdp
,
802 ppath_contains_wcard
);
803 if (!NT_STATUS_IS_OK(status
)) {
808 if (pdp
->reqpath
[0] == '\0') {
810 *pp_path_out
= talloc_strdup(ctx
, "");
812 return NT_STATUS_NO_MEMORY
;
814 DEBUG(5,("dfs_redirect: self-referral.\n"));
818 /* If dfs pathname for a non-dfs share, convert to tcon-relative
819 path and return OK */
821 if (!lp_msdfs_root(SNUM(conn
))) {
822 *pp_path_out
= talloc_strdup(ctx
, pdp
->reqpath
);
825 return NT_STATUS_NO_MEMORY
;
830 /* If it looked like a local path (zero hostname/servicename)
831 * just treat as a tcon-relative path. */
833 if (pdp
->hostname
[0] == '\0' && pdp
->servicename
[0] == '\0') {
834 *pp_path_out
= talloc_strdup(ctx
, pdp
->reqpath
);
837 return NT_STATUS_NO_MEMORY
;
842 if (!( strequal(pdp
->servicename
, lp_servicename(talloc_tos(), SNUM(conn
)))
843 || (strequal(pdp
->servicename
, HOMES_NAME
)
844 && strequal(lp_servicename(talloc_tos(), SNUM(conn
)),
845 conn
->session_info
->unix_info
->sanitized_username
) )) ) {
847 /* The given sharename doesn't match this connection. */
850 return NT_STATUS_OBJECT_PATH_NOT_FOUND
;
853 status
= dfs_path_lookup(ctx
, conn
, path_in
, pdp
,
854 search_wcard_flag
, NULL
, NULL
);
855 if (!NT_STATUS_IS_OK(status
)) {
856 if (NT_STATUS_EQUAL(status
, NT_STATUS_PATH_NOT_COVERED
)) {
857 DEBUG(3,("dfs_redirect: Redirecting %s\n", path_in
));
859 DEBUG(10,("dfs_redirect: dfs_path_lookup "
860 "failed for %s with %s\n",
861 path_in
, nt_errstr(status
) ));
866 DEBUG(3,("dfs_redirect: Not redirecting %s.\n", path_in
));
868 /* Form non-dfs tcon-relative path */
869 *pp_path_out
= talloc_strdup(ctx
, pdp
->reqpath
);
872 return NT_STATUS_NO_MEMORY
;
875 DEBUG(3,("dfs_redirect: Path %s converted to non-dfs path %s\n",
882 /**********************************************************************
883 Return a self referral.
884 **********************************************************************/
886 static NTSTATUS
self_ref(TALLOC_CTX
*ctx
,
887 const char *dfs_path
,
888 struct junction_map
*jucn
,
890 bool *self_referralp
)
892 struct referral
*ref
;
894 *self_referralp
= True
;
896 jucn
->referral_count
= 1;
897 if((ref
= talloc_zero(ctx
, struct referral
)) == NULL
) {
898 return NT_STATUS_NO_MEMORY
;
901 ref
->alternate_path
= talloc_strdup(ctx
, dfs_path
);
902 if (!ref
->alternate_path
) {
904 return NT_STATUS_NO_MEMORY
;
907 ref
->ttl
= REFERRAL_TTL
;
908 jucn
->referral_list
= ref
;
909 *consumedcntp
= strlen(dfs_path
);
913 /**********************************************************************
914 Gets valid referrals for a dfs path and fills up the
915 junction_map structure.
916 **********************************************************************/
918 NTSTATUS
get_referred_path(TALLOC_CTX
*ctx
,
919 const char *dfs_path
,
920 bool allow_broken_path
,
921 struct junction_map
*jucn
,
923 bool *self_referralp
)
925 struct connection_struct
*conn
;
926 char *targetpath
= NULL
;
928 NTSTATUS status
= NT_STATUS_NOT_FOUND
;
930 struct dfs_path
*pdp
= talloc(ctx
, struct dfs_path
);
934 return NT_STATUS_NO_MEMORY
;
937 *self_referralp
= False
;
939 status
= parse_dfs_path(NULL
, dfs_path
, False
, allow_broken_path
,
941 if (!NT_STATUS_IS_OK(status
)) {
945 jucn
->service_name
= talloc_strdup(ctx
, pdp
->servicename
);
946 jucn
->volume_name
= talloc_strdup(ctx
, pdp
->reqpath
);
947 if (!jucn
->service_name
|| !jucn
->volume_name
) {
949 return NT_STATUS_NO_MEMORY
;
952 /* Verify the share is a dfs root */
953 snum
= lp_servicenumber(jucn
->service_name
);
955 char *service_name
= NULL
;
956 if ((snum
= find_service(ctx
, jucn
->service_name
, &service_name
)) < 0) {
957 return NT_STATUS_NOT_FOUND
;
960 return NT_STATUS_NO_MEMORY
;
962 TALLOC_FREE(jucn
->service_name
);
963 jucn
->service_name
= talloc_strdup(ctx
, service_name
);
964 if (!jucn
->service_name
) {
966 return NT_STATUS_NO_MEMORY
;
970 if (!lp_msdfs_root(snum
) && (*lp_msdfs_proxy(talloc_tos(), snum
) == '\0')) {
971 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not "
973 pdp
->servicename
, dfs_path
));
975 return NT_STATUS_NOT_FOUND
;
979 * Self referrals are tested with a anonymous IPC connection and
980 * a GET_DFS_REFERRAL call to \\server\share. (which means
981 * dp.reqpath[0] points to an empty string). create_conn_struct cd's
982 * into the directory and will fail if it cannot (as the anonymous
983 * user). Cope with this.
986 if (pdp
->reqpath
[0] == '\0') {
988 struct referral
*ref
;
991 if (*lp_msdfs_proxy(talloc_tos(), snum
) == '\0') {
1001 * It's an msdfs proxy share. Redirect to
1002 * the configured target share.
1005 tmp
= talloc_asprintf(talloc_tos(), "msdfs:%s",
1006 lp_msdfs_proxy(talloc_tos(), snum
));
1009 return NT_STATUS_NO_MEMORY
;
1012 if (!parse_msdfs_symlink(ctx
, tmp
, &ref
, &refcount
)) {
1015 return NT_STATUS_INVALID_PARAMETER
;
1018 jucn
->referral_count
= refcount
;
1019 jucn
->referral_list
= ref
;
1020 *consumedcntp
= strlen(dfs_path
);
1022 return NT_STATUS_OK
;
1025 status
= create_conn_struct_cwd(ctx
,
1026 server_event_context(),
1027 server_messaging_context(),
1029 lp_path(talloc_tos(), snum
), NULL
, &oldpath
);
1030 if (!NT_STATUS_IS_OK(status
)) {
1035 /* If this is a DFS path dfs_lookup should return
1036 * NT_STATUS_PATH_NOT_COVERED. */
1038 status
= dfs_path_lookup(ctx
, conn
, dfs_path
, pdp
,
1039 False
, consumedcntp
, &targetpath
);
1041 if (!NT_STATUS_EQUAL(status
, NT_STATUS_PATH_NOT_COVERED
)) {
1042 DEBUG(3,("get_referred_path: No valid referrals for path %s\n",
1044 if (NT_STATUS_IS_OK(status
)) {
1046 * We are in an error path here (we
1047 * know it's not a DFS path), but
1048 * dfs_path_lookup() can return
1049 * NT_STATUS_OK. Ensure we always
1050 * return a valid error code.
1052 * #9588 - ACLs are not inherited to directories
1055 status
= NT_STATUS_NOT_FOUND
;
1060 /* We know this is a valid dfs link. Parse the targetpath. */
1061 if (!parse_msdfs_symlink(ctx
, targetpath
,
1062 &jucn
->referral_list
,
1063 &jucn
->referral_count
)) {
1064 DEBUG(3,("get_referred_path: failed to parse symlink "
1065 "target %s\n", targetpath
));
1066 status
= NT_STATUS_NOT_FOUND
;
1070 status
= NT_STATUS_OK
;
1072 vfs_ChDir(conn
, oldpath
);
1073 SMB_VFS_DISCONNECT(conn
);
1079 /******************************************************************
1080 Set up the DFS referral for the dfs pathname. This call returns
1081 the amount of the path covered by this server, and where the
1082 client should be redirected to. This is the meat of the
1083 TRANS2_GET_DFS_REFERRAL call.
1084 ******************************************************************/
1086 int setup_dfs_referral(connection_struct
*orig_conn
,
1087 const char *dfs_path
,
1088 int max_referral_level
,
1089 char **ppdata
, NTSTATUS
*pstatus
)
1091 char *pdata
= *ppdata
;
1093 struct dfs_GetDFSReferral
*r
;
1094 DATA_BLOB blob
= data_blob_null
;
1096 enum ndr_err_code ndr_err
;
1098 r
= talloc_zero(talloc_tos(), struct dfs_GetDFSReferral
);
1100 *pstatus
= NT_STATUS_NO_MEMORY
;
1104 r
->in
.req
.max_referral_level
= max_referral_level
;
1105 r
->in
.req
.servername
= talloc_strdup(r
, dfs_path
);
1106 if (r
->in
.req
.servername
== NULL
) {
1108 *pstatus
= NT_STATUS_NO_MEMORY
;
1112 status
= SMB_VFS_GET_DFS_REFERRALS(orig_conn
, r
);
1113 if (!NT_STATUS_IS_OK(status
)) {
1119 ndr_err
= ndr_push_struct_blob(&blob
, r
,
1121 (ndr_push_flags_fn_t
)ndr_push_dfs_referral_resp
);
1122 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
1124 *pstatus
= NT_STATUS_INVALID_PARAMETER
;
1128 pdata
= (char *)SMB_REALLOC(pdata
, blob
.length
);
1131 DEBUG(0,("referral setup:"
1132 "malloc failed for Realloc!\n"));
1136 reply_size
= blob
.length
;
1137 memcpy(pdata
, blob
.data
, blob
.length
);
1140 *pstatus
= NT_STATUS_OK
;
1144 /**********************************************************************
1145 The following functions are called by the NETDFS RPC pipe functions
1146 **********************************************************************/
1148 /*********************************************************************
1149 Creates a junction structure from a DFS pathname
1150 **********************************************************************/
1152 bool create_junction(TALLOC_CTX
*ctx
,
1153 const char *dfs_path
,
1154 bool allow_broken_path
,
1155 struct junction_map
*jucn
)
1159 struct dfs_path
*pdp
= talloc(ctx
,struct dfs_path
);
1165 status
= parse_dfs_path(NULL
, dfs_path
, False
, allow_broken_path
,
1167 if (!NT_STATUS_IS_OK(status
)) {
1171 /* check if path is dfs : validate first token */
1172 if (!is_myname_or_ipaddr(pdp
->hostname
)) {
1173 DEBUG(4,("create_junction: Invalid hostname %s "
1175 pdp
->hostname
, dfs_path
));
1180 /* Check for a non-DFS share */
1181 snum
= lp_servicenumber(pdp
->servicename
);
1183 if(snum
< 0 || !lp_msdfs_root(snum
)) {
1184 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1190 jucn
->service_name
= talloc_strdup(ctx
, pdp
->servicename
);
1191 jucn
->volume_name
= talloc_strdup(ctx
, pdp
->reqpath
);
1192 jucn
->comment
= lp_comment(ctx
, snum
);
1195 if (!jucn
->service_name
|| !jucn
->volume_name
|| ! jucn
->comment
) {
1201 /**********************************************************************
1202 Forms a valid Unix pathname from the junction
1203 **********************************************************************/
1205 static bool junction_to_local_path(const struct junction_map
*jucn
,
1207 connection_struct
**conn_out
,
1213 snum
= lp_servicenumber(jucn
->service_name
);
1217 status
= create_conn_struct_cwd(talloc_tos(),
1218 server_event_context(),
1219 server_messaging_context(),
1221 snum
, lp_path(talloc_tos(), snum
), NULL
, oldpath
);
1222 if (!NT_STATUS_IS_OK(status
)) {
1226 *pp_path_out
= talloc_asprintf(*conn_out
,
1228 lp_path(talloc_tos(), snum
),
1230 if (!*pp_path_out
) {
1231 vfs_ChDir(*conn_out
, *oldpath
);
1232 SMB_VFS_DISCONNECT(*conn_out
);
1233 conn_free(*conn_out
);
1239 bool create_msdfs_link(const struct junction_map
*jucn
)
1243 char *msdfs_link
= NULL
;
1244 connection_struct
*conn
;
1246 bool insert_comma
= False
;
1249 if(!junction_to_local_path(jucn
, &path
, &conn
, &cwd
)) {
1253 /* Form the msdfs_link contents */
1254 msdfs_link
= talloc_strdup(conn
, "msdfs:");
1258 for(i
=0; i
<jucn
->referral_count
; i
++) {
1259 char *refpath
= jucn
->referral_list
[i
].alternate_path
;
1261 /* Alternate paths always use Windows separators. */
1262 trim_char(refpath
, '\\', '\\');
1263 if(*refpath
== '\0') {
1265 insert_comma
= False
;
1269 if (i
> 0 && insert_comma
) {
1270 msdfs_link
= talloc_asprintf_append_buffer(msdfs_link
,
1274 msdfs_link
= talloc_asprintf_append_buffer(msdfs_link
,
1282 if (!insert_comma
) {
1283 insert_comma
= True
;
1287 DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
1290 if(SMB_VFS_SYMLINK(conn
, msdfs_link
, path
) < 0) {
1291 if (errno
== EEXIST
) {
1292 struct smb_filename
*smb_fname
;
1294 smb_fname
= synthetic_smb_fname(talloc_tos(), path
,
1296 if (smb_fname
== NULL
) {
1301 if(SMB_VFS_UNLINK(conn
, smb_fname
)!=0) {
1302 TALLOC_FREE(smb_fname
);
1305 TALLOC_FREE(smb_fname
);
1307 if (SMB_VFS_SYMLINK(conn
, msdfs_link
, path
) < 0) {
1308 DEBUG(1,("create_msdfs_link: symlink failed "
1309 "%s -> %s\nError: %s\n",
1310 path
, msdfs_link
, strerror(errno
)));
1318 vfs_ChDir(conn
, cwd
);
1319 SMB_VFS_DISCONNECT(conn
);
1324 bool remove_msdfs_link(const struct junction_map
*jucn
)
1328 connection_struct
*conn
;
1330 struct smb_filename
*smb_fname
;
1332 if (!junction_to_local_path(jucn
, &path
, &conn
, &cwd
)) {
1336 smb_fname
= synthetic_smb_fname(talloc_tos(), path
, NULL
, NULL
);
1337 if (smb_fname
== NULL
) {
1342 if( SMB_VFS_UNLINK(conn
, smb_fname
) == 0 ) {
1346 TALLOC_FREE(smb_fname
);
1347 vfs_ChDir(conn
, cwd
);
1348 SMB_VFS_DISCONNECT(conn
);
1353 /*********************************************************************
1354 Return the number of DFS links at the root of this share.
1355 *********************************************************************/
1357 static int count_dfs_links(TALLOC_CTX
*ctx
, int snum
)
1361 const char *dname
= NULL
;
1362 char *talloced
= NULL
;
1363 const char *connect_path
= lp_path(talloc_tos(), snum
);
1364 const char *msdfs_proxy
= lp_msdfs_proxy(talloc_tos(), snum
);
1365 connection_struct
*conn
;
1369 if(*connect_path
== '\0') {
1374 * Fake up a connection struct for the VFS layer.
1377 status
= create_conn_struct_cwd(talloc_tos(),
1378 server_event_context(),
1379 server_messaging_context(),
1381 snum
, connect_path
, NULL
, &cwd
);
1382 if (!NT_STATUS_IS_OK(status
)) {
1383 DEBUG(3, ("create_conn_struct failed: %s\n",
1384 nt_errstr(status
)));
1388 /* Count a link for the msdfs root - convention */
1391 /* No more links if this is an msdfs proxy. */
1392 if (*msdfs_proxy
!= '\0') {
1396 /* Now enumerate all dfs links */
1397 dirp
= SMB_VFS_OPENDIR(conn
, ".", NULL
, 0);
1402 while ((dname
= vfs_readdirname(conn
, dirp
, NULL
, &talloced
))
1404 if (is_msdfs_link(conn
,
1409 TALLOC_FREE(talloced
);
1412 SMB_VFS_CLOSEDIR(conn
,dirp
);
1415 vfs_ChDir(conn
, cwd
);
1416 SMB_VFS_DISCONNECT(conn
);
1421 /*********************************************************************
1422 *********************************************************************/
1424 static int form_junctions(TALLOC_CTX
*ctx
,
1426 struct junction_map
*jucn
,
1431 const char *dname
= NULL
;
1432 char *talloced
= NULL
;
1433 const char *connect_path
= lp_path(talloc_tos(), snum
);
1434 char *service_name
= lp_servicename(talloc_tos(), snum
);
1435 const char *msdfs_proxy
= lp_msdfs_proxy(talloc_tos(), snum
);
1436 connection_struct
*conn
;
1437 struct referral
*ref
= NULL
;
1441 if (jn_remain
== 0) {
1445 if(*connect_path
== '\0') {
1450 * Fake up a connection struct for the VFS layer.
1453 status
= create_conn_struct_cwd(ctx
,
1454 server_event_context(),
1455 server_messaging_context(),
1456 &conn
, snum
, connect_path
, NULL
,
1458 if (!NT_STATUS_IS_OK(status
)) {
1459 DEBUG(3, ("create_conn_struct failed: %s\n",
1460 nt_errstr(status
)));
1464 /* form a junction for the msdfs root - convention
1465 DO NOT REMOVE THIS: NT clients will not work with us
1466 if this is not present
1468 jucn
[cnt
].service_name
= talloc_strdup(ctx
,service_name
);
1469 jucn
[cnt
].volume_name
= talloc_strdup(ctx
, "");
1470 if (!jucn
[cnt
].service_name
|| !jucn
[cnt
].volume_name
) {
1473 jucn
[cnt
].comment
= "";
1474 jucn
[cnt
].referral_count
= 1;
1476 ref
= jucn
[cnt
].referral_list
= talloc_zero(ctx
, struct referral
);
1477 if (jucn
[cnt
].referral_list
== NULL
) {
1482 ref
->ttl
= REFERRAL_TTL
;
1483 if (*msdfs_proxy
!= '\0') {
1484 ref
->alternate_path
= talloc_strdup(ctx
,
1487 ref
->alternate_path
= talloc_asprintf(ctx
,
1489 get_local_machine_name(),
1493 if (!ref
->alternate_path
) {
1498 /* Don't enumerate if we're an msdfs proxy. */
1499 if (*msdfs_proxy
!= '\0') {
1503 /* Now enumerate all dfs links */
1504 dirp
= SMB_VFS_OPENDIR(conn
, ".", NULL
, 0);
1509 while ((dname
= vfs_readdirname(conn
, dirp
, NULL
, &talloced
))
1511 char *link_target
= NULL
;
1512 if (cnt
>= jn_remain
) {
1513 DEBUG(2, ("form_junctions: ran out of MSDFS "
1515 TALLOC_FREE(talloced
);
1518 if (is_msdfs_link_internal(ctx
,
1520 dname
, &link_target
,
1522 if (parse_msdfs_symlink(ctx
,
1524 &jucn
[cnt
].referral_list
,
1525 &jucn
[cnt
].referral_count
)) {
1527 jucn
[cnt
].service_name
= talloc_strdup(ctx
,
1529 jucn
[cnt
].volume_name
= talloc_strdup(ctx
,
1531 if (!jucn
[cnt
].service_name
||
1532 !jucn
[cnt
].volume_name
) {
1533 TALLOC_FREE(talloced
);
1536 jucn
[cnt
].comment
= "";
1539 TALLOC_FREE(link_target
);
1541 TALLOC_FREE(talloced
);
1547 SMB_VFS_CLOSEDIR(conn
,dirp
);
1550 vfs_ChDir(conn
, cwd
);
1555 struct junction_map
*enum_msdfs_links(TALLOC_CTX
*ctx
, size_t *p_num_jn
)
1557 struct junction_map
*jn
= NULL
;
1559 size_t jn_count
= 0;
1563 if(!lp_host_msdfs()) {
1567 /* Ensure all the usershares are loaded. */
1569 load_registry_shares();
1570 sharecount
= load_usershare_shares(NULL
, connections_snum_used
);
1573 for(i
=0;i
< sharecount
;i
++) {
1574 if(lp_msdfs_root(i
)) {
1575 jn_count
+= count_dfs_links(ctx
, i
);
1578 if (jn_count
== 0) {
1581 jn
= talloc_array(ctx
, struct junction_map
, jn_count
);
1585 for(i
=0; i
< sharecount
; i
++) {
1586 if (*p_num_jn
>= jn_count
) {
1589 if(lp_msdfs_root(i
)) {
1590 *p_num_jn
+= form_junctions(ctx
, i
,
1592 jn_count
- *p_num_jn
);
1598 /******************************************************************************
1599 Core function to resolve a dfs pathname possibly containing a wildcard. If
1600 ppath_contains_wcard != NULL, it will be set to true if a wildcard is
1601 detected during dfs resolution.
1602 ******************************************************************************/
1604 NTSTATUS
resolve_dfspath_wcard(TALLOC_CTX
*ctx
,
1605 connection_struct
*conn
,
1607 const char *name_in
,
1609 bool allow_broken_path
,
1611 bool *ppath_contains_wcard
)
1613 bool path_contains_wcard
;
1614 NTSTATUS status
= NT_STATUS_OK
;
1616 if (dfs_pathnames
) {
1617 status
= dfs_redirect(ctx
,
1623 &path_contains_wcard
);
1625 if (NT_STATUS_IS_OK(status
) && ppath_contains_wcard
!= NULL
) {
1626 *ppath_contains_wcard
= path_contains_wcard
;
1630 * Cheat and just return a copy of the in ptr.
1631 * Once srvstr_get_path() uses talloc it'll
1632 * be a talloced ptr anyway.
1634 *pp_name_out
= discard_const_p(char, name_in
);