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 sconn
->smb1
.echo_handler
.trusted_fd
= -1;
251 sconn
->smb1
.echo_handler
.socket_lock_fd
= -1;
253 conn
= conn_new(sconn
);
256 return NT_STATUS_NO_MEMORY
;
259 /* Now we have conn, we need to make sconn a child of conn,
260 * for a proper talloc tree */
261 talloc_steal(conn
, sconn
);
263 if (snum
== -1 && servicename
== NULL
) {
264 servicename
= "Unknown Service (snum == -1)";
267 connpath
= talloc_strdup(conn
, path
);
270 return NT_STATUS_NO_MEMORY
;
272 connpath
= talloc_string_sub(conn
,
278 return NT_STATUS_NO_MEMORY
;
281 /* needed for smbd_vfs_init() */
283 conn
->params
->service
= snum
;
284 conn
->cnum
= TID_FIELD_INVALID
;
286 if (session_info
!= NULL
) {
287 conn
->session_info
= copy_session_info(conn
, session_info
);
288 if (conn
->session_info
== NULL
) {
289 DEBUG(0, ("copy_serverinfo failed\n"));
291 return NT_STATUS_NO_MEMORY
;
293 vfs_user
= conn
->session_info
->unix_info
->unix_name
;
295 /* use current authenticated user in absence of session_info */
296 vfs_user
= get_current_username();
299 set_conn_connectpath(conn
, connpath
);
302 * New code to check if there's a share security descripter
303 * added from NT server manager. This is done after the
304 * smb.conf checks are done as we need a uid and token. JRA.
307 if (conn
->session_info
) {
308 share_access_check(conn
->session_info
->security_token
,
310 MAXIMUM_ALLOWED_ACCESS
,
311 &conn
->share_access
);
313 if ((conn
->share_access
& FILE_WRITE_DATA
) == 0) {
314 if ((conn
->share_access
& FILE_READ_DATA
) == 0) {
315 /* No access, read or write. */
316 DEBUG(0,("create_conn_struct: connection to %s "
317 "denied due to security "
321 return NT_STATUS_ACCESS_DENIED
;
323 conn
->read_only
= true;
327 conn
->share_access
= 0;
328 conn
->read_only
= true;
331 if (!smbd_vfs_init(conn
)) {
332 NTSTATUS status
= map_nt_error_from_unix(errno
);
333 DEBUG(0,("create_conn_struct: smbd_vfs_init failed.\n"));
338 /* this must be the first filesystem operation that we do */
339 if (SMB_VFS_CONNECT(conn
, servicename
, vfs_user
) < 0) {
340 DEBUG(0,("VFS connect failed!\n"));
342 return NT_STATUS_UNSUCCESSFUL
;
345 conn
->fs_capabilities
= SMB_VFS_FS_CAPABILITIES(conn
, &conn
->ts_res
);
351 /********************************************************
352 Fake up a connection struct for the VFS layer, for use in
353 applications (such as the python bindings), that do not want the
354 global working directory changed under them.
356 SMB_VFS_CONNECT requires root privileges.
357 *********************************************************/
359 NTSTATUS
create_conn_struct(TALLOC_CTX
*ctx
,
360 struct tevent_context
*ev
,
361 struct messaging_context
*msg
,
362 connection_struct
**pconn
,
365 const struct auth_session_info
*session_info
)
369 status
= create_conn_struct_as_root(ctx
, ev
,
378 /********************************************************
379 Fake up a connection struct for the VFS layer.
380 Note: this performs a vfs connect and CHANGES CWD !!!! JRA.
382 The old working directory is returned on *poldcwd, allocated on ctx.
383 *********************************************************/
385 NTSTATUS
create_conn_struct_cwd(TALLOC_CTX
*ctx
,
386 struct tevent_context
*ev
,
387 struct messaging_context
*msg
,
388 connection_struct
**pconn
,
391 const struct auth_session_info
*session_info
,
394 connection_struct
*conn
;
397 NTSTATUS status
= create_conn_struct(ctx
, ev
,
401 if (!NT_STATUS_IS_OK(status
)) {
406 * Windows seems to insist on doing trans2getdfsreferral() calls on
407 * the IPC$ share as the anonymous user. If we try to chdir as that
408 * user we will fail.... WTF ? JRA.
411 oldcwd
= vfs_GetWd(ctx
, conn
);
412 if (oldcwd
== NULL
) {
413 status
= map_nt_error_from_unix(errno
);
414 DEBUG(3, ("vfs_GetWd failed: %s\n", strerror(errno
)));
419 if (vfs_ChDir(conn
,conn
->connectpath
) != 0) {
420 status
= map_nt_error_from_unix(errno
);
421 DEBUG(3,("create_conn_struct: Can't ChDir to new conn path %s. "
423 conn
->connectpath
, strerror(errno
) ));
434 /**********************************************************************
435 Parse the contents of a symlink to verify if it is an msdfs referral
436 A valid referral is of the form:
438 msdfs:server1\share1,server2\share2
439 msdfs:server1\share1\pathname,server2\share2\pathname
440 msdfs:server1/share1,server2/share2
441 msdfs:server1/share1/pathname,server2/share2/pathname.
443 Note that the alternate paths returned here must be of the canonicalized
447 \server\share\path\to\file,
449 even in posix path mode. This is because we have no knowledge if the
450 server we're referring to understands posix paths.
451 **********************************************************************/
453 static bool parse_msdfs_symlink(TALLOC_CTX
*ctx
,
455 struct referral
**preflist
,
460 char **alt_path
= NULL
;
462 struct referral
*reflist
;
465 temp
= talloc_strdup(ctx
, target
);
469 prot
= strtok_r(temp
, ":", &saveptr
);
471 DEBUG(0,("parse_msdfs_symlink: invalid path !\n"));
475 alt_path
= talloc_array(ctx
, char *, MAX_REFERRAL_COUNT
);
480 /* parse out the alternate paths */
481 while((count
<MAX_REFERRAL_COUNT
) &&
482 ((alt_path
[count
] = strtok_r(NULL
, ",", &saveptr
)) != NULL
)) {
486 DEBUG(10,("parse_msdfs_symlink: count=%d\n", count
));
489 reflist
= *preflist
= talloc_zero_array(ctx
,
490 struct referral
, count
);
491 if(reflist
== NULL
) {
492 TALLOC_FREE(alt_path
);
496 reflist
= *preflist
= NULL
;
499 for(i
=0;i
<count
;i
++) {
502 /* Canonicalize link target.
503 * Replace all /'s in the path by a \ */
504 string_replace(alt_path
[i
], '/', '\\');
506 /* Remove leading '\\'s */
508 while (*p
&& (*p
== '\\')) {
512 reflist
[i
].alternate_path
= talloc_asprintf(ctx
,
515 if (!reflist
[i
].alternate_path
) {
519 reflist
[i
].proximity
= 0;
520 reflist
[i
].ttl
= REFERRAL_TTL
;
521 DEBUG(10, ("parse_msdfs_symlink: Created alt path: %s\n",
522 reflist
[i
].alternate_path
));
527 TALLOC_FREE(alt_path
);
531 /**********************************************************************
532 Returns true if the unix path is a valid msdfs symlink and also
533 returns the target string from inside the link.
534 **********************************************************************/
536 static bool is_msdfs_link_internal(TALLOC_CTX
*ctx
,
537 connection_struct
*conn
,
539 char **pp_link_target
,
540 SMB_STRUCT_STAT
*sbufp
)
542 int referral_len
= 0;
543 #if defined(HAVE_BROKEN_READLINK)
544 char link_target_buf
[PATH_MAX
];
546 char link_target_buf
[7];
549 char *link_target
= NULL
;
550 struct smb_filename smb_fname
;
552 if (pp_link_target
) {
554 link_target
= talloc_array(ctx
, char, bufsize
);
558 *pp_link_target
= link_target
;
560 bufsize
= sizeof(link_target_buf
);
561 link_target
= link_target_buf
;
564 ZERO_STRUCT(smb_fname
);
565 smb_fname
.base_name
= discard_const_p(char, path
);
567 if (SMB_VFS_LSTAT(conn
, &smb_fname
) != 0) {
568 DEBUG(5,("is_msdfs_link_read_target: %s does not exist.\n",
572 if (!S_ISLNK(smb_fname
.st
.st_ex_mode
)) {
573 DEBUG(5,("is_msdfs_link_read_target: %s is not a link.\n",
578 *sbufp
= smb_fname
.st
;
581 referral_len
= SMB_VFS_READLINK(conn
, path
, link_target
, bufsize
- 1);
582 if (referral_len
== -1) {
583 DEBUG(0,("is_msdfs_link_read_target: Error reading "
584 "msdfs link %s: %s\n",
585 path
, strerror(errno
)));
588 link_target
[referral_len
] = '\0';
590 DEBUG(5,("is_msdfs_link_internal: %s -> %s\n",path
,
593 if (!strnequal(link_target
, "msdfs:", 6)) {
600 if (link_target
!= link_target_buf
) {
601 TALLOC_FREE(link_target
);
606 /**********************************************************************
607 Returns true if the unix path is a valid msdfs symlink.
608 **********************************************************************/
610 bool is_msdfs_link(connection_struct
*conn
,
612 SMB_STRUCT_STAT
*sbufp
)
614 return is_msdfs_link_internal(talloc_tos(),
621 /*****************************************************************
622 Used by other functions to decide if a dfs path is remote,
623 and to get the list of referred locations for that remote path.
625 search_flag: For findfirsts, dfs links themselves are not
626 redirected, but paths beyond the links are. For normal smb calls,
627 even dfs links need to be redirected.
629 consumedcntp: how much of the dfs path is being redirected. the client
630 should try the remaining path on the redirected server.
632 If this returns NT_STATUS_PATH_NOT_COVERED the contents of the msdfs
633 link redirect are in targetpath.
634 *****************************************************************/
636 static NTSTATUS
dfs_path_lookup(TALLOC_CTX
*ctx
,
637 connection_struct
*conn
,
638 const char *dfspath
, /* Incoming complete dfs path */
639 const struct dfs_path
*pdp
, /* Parsed out
640 server+share+extrapath. */
641 bool search_flag
, /* Called from a findfirst ? */
643 char **pp_targetpath
)
648 struct smb_filename
*smb_fname
= NULL
;
649 char *canon_dfspath
= NULL
; /* Canonicalized dfs path. (only '/'
652 DEBUG(10,("dfs_path_lookup: Conn path = %s reqpath = %s\n",
653 conn
->connectpath
, pdp
->reqpath
));
656 * Note the unix path conversion here we're doing we
657 * throw away. We're looking for a symlink for a dfs
658 * resolution, if we don't find it we'll do another
659 * unix_convert later in the codepath.
662 status
= unix_convert(ctx
, conn
, pdp
->reqpath
, &smb_fname
,
663 search_flag
? UCF_ALWAYS_ALLOW_WCARD_LCOMP
: 0);
665 if (!NT_STATUS_IS_OK(status
)) {
666 if (!NT_STATUS_EQUAL(status
,
667 NT_STATUS_OBJECT_PATH_NOT_FOUND
)) {
670 if (smb_fname
== NULL
|| smb_fname
->base_name
== NULL
) {
675 /* Optimization - check if we can redirect the whole path. */
677 if (is_msdfs_link_internal(ctx
, conn
, smb_fname
->base_name
,
678 pp_targetpath
, NULL
)) {
680 DEBUG(6,("dfs_path_lookup (FindFirst) No redirection "
681 "for dfs link %s.\n", dfspath
));
682 status
= NT_STATUS_OK
;
686 DEBUG(6,("dfs_path_lookup: %s resolves to a "
687 "valid dfs link %s.\n", dfspath
,
688 pp_targetpath
? *pp_targetpath
: ""));
691 *consumedcntp
= strlen(dfspath
);
693 status
= NT_STATUS_PATH_NOT_COVERED
;
697 /* Prepare to test only for '/' components in the given path,
698 * so if a Windows path replace all '\\' characters with '/'.
699 * For a POSIX DFS path we know all separators are already '/'. */
701 canon_dfspath
= talloc_strdup(ctx
, dfspath
);
702 if (!canon_dfspath
) {
703 status
= NT_STATUS_NO_MEMORY
;
706 if (!pdp
->posix_path
) {
707 string_replace(canon_dfspath
, '\\', '/');
711 * localpath comes out of unix_convert, so it has
712 * no trailing backslash. Make sure that canon_dfspath hasn't either.
713 * Fix for bug #4860 from Jan Martin <Jan.Martin@rwedea.com>.
716 trim_char(canon_dfspath
,0,'/');
719 * Redirect if any component in the path is a link.
720 * We do this by walking backwards through the
721 * local path, chopping off the last component
722 * in both the local path and the canonicalized
723 * DFS path. If we hit a DFS link then we're done.
726 p
= strrchr_m(smb_fname
->base_name
, '/');
728 q
= strrchr_m(canon_dfspath
, '/');
737 if (is_msdfs_link_internal(ctx
, conn
,
738 smb_fname
->base_name
, pp_targetpath
,
740 DEBUG(4, ("dfs_path_lookup: Redirecting %s because "
741 "parent %s is dfs link\n", dfspath
,
742 smb_fname_str_dbg(smb_fname
)));
745 *consumedcntp
= strlen(canon_dfspath
);
746 DEBUG(10, ("dfs_path_lookup: Path consumed: %s "
752 status
= NT_STATUS_PATH_NOT_COVERED
;
756 /* Step back on the filesystem. */
757 p
= strrchr_m(smb_fname
->base_name
, '/');
760 /* And in the canonicalized dfs path. */
761 q
= strrchr_m(canon_dfspath
, '/');
765 status
= NT_STATUS_OK
;
767 TALLOC_FREE(smb_fname
);
771 /*****************************************************************
772 Decides if a dfs pathname should be redirected or not.
773 If not, the pathname is converted to a tcon-relative local unix path
775 search_wcard_flag: this flag performs 2 functions both related
776 to searches. See resolve_dfs_path() and parse_dfs_path_XX()
779 This function can return NT_STATUS_OK, meaning use the returned path as-is
780 (mapped into a local path).
781 or NT_STATUS_NOT_COVERED meaning return a DFS redirect, or
782 any other NT_STATUS error which is a genuine error to be
783 returned to the client.
784 *****************************************************************/
786 static NTSTATUS
dfs_redirect(TALLOC_CTX
*ctx
,
787 connection_struct
*conn
,
789 bool search_wcard_flag
,
790 bool allow_broken_path
,
792 bool *ppath_contains_wcard
)
795 struct dfs_path
*pdp
= talloc(ctx
, struct dfs_path
);
798 return NT_STATUS_NO_MEMORY
;
801 status
= parse_dfs_path(conn
, path_in
, search_wcard_flag
,
802 allow_broken_path
, pdp
,
803 ppath_contains_wcard
);
804 if (!NT_STATUS_IS_OK(status
)) {
809 if (pdp
->reqpath
[0] == '\0') {
811 *pp_path_out
= talloc_strdup(ctx
, "");
813 return NT_STATUS_NO_MEMORY
;
815 DEBUG(5,("dfs_redirect: self-referral.\n"));
819 /* If dfs pathname for a non-dfs share, convert to tcon-relative
820 path and return OK */
822 if (!lp_msdfs_root(SNUM(conn
))) {
823 *pp_path_out
= talloc_strdup(ctx
, pdp
->reqpath
);
826 return NT_STATUS_NO_MEMORY
;
831 /* If it looked like a local path (zero hostname/servicename)
832 * just treat as a tcon-relative path. */
834 if (pdp
->hostname
[0] == '\0' && pdp
->servicename
[0] == '\0') {
835 *pp_path_out
= talloc_strdup(ctx
, pdp
->reqpath
);
838 return NT_STATUS_NO_MEMORY
;
843 if (!( strequal(pdp
->servicename
, lp_servicename(talloc_tos(), SNUM(conn
)))
844 || (strequal(pdp
->servicename
, HOMES_NAME
)
845 && strequal(lp_servicename(talloc_tos(), SNUM(conn
)),
846 conn
->session_info
->unix_info
->sanitized_username
) )) ) {
848 /* The given sharename doesn't match this connection. */
851 return NT_STATUS_OBJECT_PATH_NOT_FOUND
;
854 status
= dfs_path_lookup(ctx
, conn
, path_in
, pdp
,
855 search_wcard_flag
, NULL
, NULL
);
856 if (!NT_STATUS_IS_OK(status
)) {
857 if (NT_STATUS_EQUAL(status
, NT_STATUS_PATH_NOT_COVERED
)) {
858 DEBUG(3,("dfs_redirect: Redirecting %s\n", path_in
));
860 DEBUG(10,("dfs_redirect: dfs_path_lookup "
861 "failed for %s with %s\n",
862 path_in
, nt_errstr(status
) ));
867 DEBUG(3,("dfs_redirect: Not redirecting %s.\n", path_in
));
869 /* Form non-dfs tcon-relative path */
870 *pp_path_out
= talloc_strdup(ctx
, pdp
->reqpath
);
873 return NT_STATUS_NO_MEMORY
;
876 DEBUG(3,("dfs_redirect: Path %s converted to non-dfs path %s\n",
883 /**********************************************************************
884 Return a self referral.
885 **********************************************************************/
887 static NTSTATUS
self_ref(TALLOC_CTX
*ctx
,
888 const char *dfs_path
,
889 struct junction_map
*jucn
,
891 bool *self_referralp
)
893 struct referral
*ref
;
895 *self_referralp
= True
;
897 jucn
->referral_count
= 1;
898 if((ref
= talloc_zero(ctx
, struct referral
)) == NULL
) {
899 return NT_STATUS_NO_MEMORY
;
902 ref
->alternate_path
= talloc_strdup(ctx
, dfs_path
);
903 if (!ref
->alternate_path
) {
905 return NT_STATUS_NO_MEMORY
;
908 ref
->ttl
= REFERRAL_TTL
;
909 jucn
->referral_list
= ref
;
910 *consumedcntp
= strlen(dfs_path
);
914 /**********************************************************************
915 Gets valid referrals for a dfs path and fills up the
916 junction_map structure.
917 **********************************************************************/
919 NTSTATUS
get_referred_path(TALLOC_CTX
*ctx
,
920 const char *dfs_path
,
921 bool allow_broken_path
,
922 struct junction_map
*jucn
,
924 bool *self_referralp
)
926 struct connection_struct
*conn
;
927 char *targetpath
= NULL
;
929 NTSTATUS status
= NT_STATUS_NOT_FOUND
;
931 struct dfs_path
*pdp
= talloc(ctx
, struct dfs_path
);
935 return NT_STATUS_NO_MEMORY
;
938 *self_referralp
= False
;
940 status
= parse_dfs_path(NULL
, dfs_path
, False
, allow_broken_path
,
942 if (!NT_STATUS_IS_OK(status
)) {
946 jucn
->service_name
= talloc_strdup(ctx
, pdp
->servicename
);
947 jucn
->volume_name
= talloc_strdup(ctx
, pdp
->reqpath
);
948 if (!jucn
->service_name
|| !jucn
->volume_name
) {
950 return NT_STATUS_NO_MEMORY
;
953 /* Verify the share is a dfs root */
954 snum
= lp_servicenumber(jucn
->service_name
);
956 char *service_name
= NULL
;
957 if ((snum
= find_service(ctx
, jucn
->service_name
, &service_name
)) < 0) {
958 return NT_STATUS_NOT_FOUND
;
961 return NT_STATUS_NO_MEMORY
;
963 TALLOC_FREE(jucn
->service_name
);
964 jucn
->service_name
= talloc_strdup(ctx
, service_name
);
965 if (!jucn
->service_name
) {
967 return NT_STATUS_NO_MEMORY
;
971 if (!lp_msdfs_root(snum
) && (*lp_msdfs_proxy(talloc_tos(), snum
) == '\0')) {
972 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not "
974 pdp
->servicename
, dfs_path
));
976 return NT_STATUS_NOT_FOUND
;
980 * Self referrals are tested with a anonymous IPC connection and
981 * a GET_DFS_REFERRAL call to \\server\share. (which means
982 * dp.reqpath[0] points to an empty string). create_conn_struct cd's
983 * into the directory and will fail if it cannot (as the anonymous
984 * user). Cope with this.
987 if (pdp
->reqpath
[0] == '\0') {
989 struct referral
*ref
;
992 if (*lp_msdfs_proxy(talloc_tos(), snum
) == '\0') {
1002 * It's an msdfs proxy share. Redirect to
1003 * the configured target share.
1006 tmp
= talloc_asprintf(talloc_tos(), "msdfs:%s",
1007 lp_msdfs_proxy(talloc_tos(), snum
));
1010 return NT_STATUS_NO_MEMORY
;
1013 if (!parse_msdfs_symlink(ctx
, tmp
, &ref
, &refcount
)) {
1016 return NT_STATUS_INVALID_PARAMETER
;
1019 jucn
->referral_count
= refcount
;
1020 jucn
->referral_list
= ref
;
1021 *consumedcntp
= strlen(dfs_path
);
1023 return NT_STATUS_OK
;
1026 status
= create_conn_struct_cwd(ctx
,
1027 server_event_context(),
1028 server_messaging_context(),
1030 lp_path(talloc_tos(), snum
), NULL
, &oldpath
);
1031 if (!NT_STATUS_IS_OK(status
)) {
1036 /* If this is a DFS path dfs_lookup should return
1037 * NT_STATUS_PATH_NOT_COVERED. */
1039 status
= dfs_path_lookup(ctx
, conn
, dfs_path
, pdp
,
1040 False
, consumedcntp
, &targetpath
);
1042 if (!NT_STATUS_EQUAL(status
, NT_STATUS_PATH_NOT_COVERED
)) {
1043 DEBUG(3,("get_referred_path: No valid referrals for path %s\n",
1045 if (NT_STATUS_IS_OK(status
)) {
1047 * We are in an error path here (we
1048 * know it's not a DFS path), but
1049 * dfs_path_lookup() can return
1050 * NT_STATUS_OK. Ensure we always
1051 * return a valid error code.
1053 * #9588 - ACLs are not inherited to directories
1056 status
= NT_STATUS_NOT_FOUND
;
1061 /* We know this is a valid dfs link. Parse the targetpath. */
1062 if (!parse_msdfs_symlink(ctx
, targetpath
,
1063 &jucn
->referral_list
,
1064 &jucn
->referral_count
)) {
1065 DEBUG(3,("get_referred_path: failed to parse symlink "
1066 "target %s\n", targetpath
));
1067 status
= NT_STATUS_NOT_FOUND
;
1071 status
= NT_STATUS_OK
;
1073 vfs_ChDir(conn
, oldpath
);
1074 SMB_VFS_DISCONNECT(conn
);
1080 /******************************************************************
1081 Set up the DFS referral for the dfs pathname. This call returns
1082 the amount of the path covered by this server, and where the
1083 client should be redirected to. This is the meat of the
1084 TRANS2_GET_DFS_REFERRAL call.
1085 ******************************************************************/
1087 int setup_dfs_referral(connection_struct
*orig_conn
,
1088 const char *dfs_path
,
1089 int max_referral_level
,
1090 char **ppdata
, NTSTATUS
*pstatus
)
1092 char *pdata
= *ppdata
;
1094 struct dfs_GetDFSReferral
*r
;
1095 DATA_BLOB blob
= data_blob_null
;
1097 enum ndr_err_code ndr_err
;
1099 r
= talloc_zero(talloc_tos(), struct dfs_GetDFSReferral
);
1101 *pstatus
= NT_STATUS_NO_MEMORY
;
1105 r
->in
.req
.max_referral_level
= max_referral_level
;
1106 r
->in
.req
.servername
= talloc_strdup(r
, dfs_path
);
1107 if (r
->in
.req
.servername
== NULL
) {
1109 *pstatus
= NT_STATUS_NO_MEMORY
;
1113 status
= SMB_VFS_GET_DFS_REFERRALS(orig_conn
, r
);
1114 if (!NT_STATUS_IS_OK(status
)) {
1120 ndr_err
= ndr_push_struct_blob(&blob
, r
,
1122 (ndr_push_flags_fn_t
)ndr_push_dfs_referral_resp
);
1123 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
1125 *pstatus
= NT_STATUS_INVALID_PARAMETER
;
1129 pdata
= (char *)SMB_REALLOC(pdata
, blob
.length
);
1132 DEBUG(0,("referral setup:"
1133 "malloc failed for Realloc!\n"));
1137 reply_size
= blob
.length
;
1138 memcpy(pdata
, blob
.data
, blob
.length
);
1141 *pstatus
= NT_STATUS_OK
;
1145 /**********************************************************************
1146 The following functions are called by the NETDFS RPC pipe functions
1147 **********************************************************************/
1149 /*********************************************************************
1150 Creates a junction structure from a DFS pathname
1151 **********************************************************************/
1153 bool create_junction(TALLOC_CTX
*ctx
,
1154 const char *dfs_path
,
1155 bool allow_broken_path
,
1156 struct junction_map
*jucn
)
1160 struct dfs_path
*pdp
= talloc(ctx
,struct dfs_path
);
1166 status
= parse_dfs_path(NULL
, dfs_path
, False
, allow_broken_path
,
1168 if (!NT_STATUS_IS_OK(status
)) {
1172 /* check if path is dfs : validate first token */
1173 if (!is_myname_or_ipaddr(pdp
->hostname
)) {
1174 DEBUG(4,("create_junction: Invalid hostname %s "
1176 pdp
->hostname
, dfs_path
));
1181 /* Check for a non-DFS share */
1182 snum
= lp_servicenumber(pdp
->servicename
);
1184 if(snum
< 0 || !lp_msdfs_root(snum
)) {
1185 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1191 jucn
->service_name
= talloc_strdup(ctx
, pdp
->servicename
);
1192 jucn
->volume_name
= talloc_strdup(ctx
, pdp
->reqpath
);
1193 jucn
->comment
= lp_comment(ctx
, snum
);
1196 if (!jucn
->service_name
|| !jucn
->volume_name
|| ! jucn
->comment
) {
1202 /**********************************************************************
1203 Forms a valid Unix pathname from the junction
1204 **********************************************************************/
1206 static bool junction_to_local_path(const struct junction_map
*jucn
,
1208 connection_struct
**conn_out
,
1214 snum
= lp_servicenumber(jucn
->service_name
);
1218 status
= create_conn_struct_cwd(talloc_tos(),
1219 server_event_context(),
1220 server_messaging_context(),
1222 snum
, lp_path(talloc_tos(), snum
), NULL
, oldpath
);
1223 if (!NT_STATUS_IS_OK(status
)) {
1227 *pp_path_out
= talloc_asprintf(*conn_out
,
1229 lp_path(talloc_tos(), snum
),
1231 if (!*pp_path_out
) {
1232 vfs_ChDir(*conn_out
, *oldpath
);
1233 SMB_VFS_DISCONNECT(*conn_out
);
1234 conn_free(*conn_out
);
1240 bool create_msdfs_link(const struct junction_map
*jucn
)
1244 char *msdfs_link
= NULL
;
1245 connection_struct
*conn
;
1247 bool insert_comma
= False
;
1250 if(!junction_to_local_path(jucn
, &path
, &conn
, &cwd
)) {
1254 /* Form the msdfs_link contents */
1255 msdfs_link
= talloc_strdup(conn
, "msdfs:");
1259 for(i
=0; i
<jucn
->referral_count
; i
++) {
1260 char *refpath
= jucn
->referral_list
[i
].alternate_path
;
1262 /* Alternate paths always use Windows separators. */
1263 trim_char(refpath
, '\\', '\\');
1264 if(*refpath
== '\0') {
1266 insert_comma
= False
;
1270 if (i
> 0 && insert_comma
) {
1271 msdfs_link
= talloc_asprintf_append_buffer(msdfs_link
,
1275 msdfs_link
= talloc_asprintf_append_buffer(msdfs_link
,
1283 if (!insert_comma
) {
1284 insert_comma
= True
;
1288 DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
1291 if(SMB_VFS_SYMLINK(conn
, msdfs_link
, path
) < 0) {
1292 if (errno
== EEXIST
) {
1293 struct smb_filename
*smb_fname
;
1295 smb_fname
= synthetic_smb_fname(talloc_tos(), path
,
1297 if (smb_fname
== NULL
) {
1302 if(SMB_VFS_UNLINK(conn
, smb_fname
)!=0) {
1303 TALLOC_FREE(smb_fname
);
1306 TALLOC_FREE(smb_fname
);
1308 if (SMB_VFS_SYMLINK(conn
, msdfs_link
, path
) < 0) {
1309 DEBUG(1,("create_msdfs_link: symlink failed "
1310 "%s -> %s\nError: %s\n",
1311 path
, msdfs_link
, strerror(errno
)));
1319 vfs_ChDir(conn
, cwd
);
1320 SMB_VFS_DISCONNECT(conn
);
1325 bool remove_msdfs_link(const struct junction_map
*jucn
)
1329 connection_struct
*conn
;
1331 struct smb_filename
*smb_fname
;
1333 if (!junction_to_local_path(jucn
, &path
, &conn
, &cwd
)) {
1337 smb_fname
= synthetic_smb_fname(talloc_tos(), path
, NULL
, NULL
);
1338 if (smb_fname
== NULL
) {
1343 if( SMB_VFS_UNLINK(conn
, smb_fname
) == 0 ) {
1347 TALLOC_FREE(smb_fname
);
1348 vfs_ChDir(conn
, cwd
);
1349 SMB_VFS_DISCONNECT(conn
);
1354 /*********************************************************************
1355 Return the number of DFS links at the root of this share.
1356 *********************************************************************/
1358 static int count_dfs_links(TALLOC_CTX
*ctx
, int snum
)
1362 const char *dname
= NULL
;
1363 char *talloced
= NULL
;
1364 const char *connect_path
= lp_path(talloc_tos(), snum
);
1365 const char *msdfs_proxy
= lp_msdfs_proxy(talloc_tos(), snum
);
1366 connection_struct
*conn
;
1370 if(*connect_path
== '\0') {
1375 * Fake up a connection struct for the VFS layer.
1378 status
= create_conn_struct_cwd(talloc_tos(),
1379 server_event_context(),
1380 server_messaging_context(),
1382 snum
, connect_path
, NULL
, &cwd
);
1383 if (!NT_STATUS_IS_OK(status
)) {
1384 DEBUG(3, ("create_conn_struct failed: %s\n",
1385 nt_errstr(status
)));
1389 /* Count a link for the msdfs root - convention */
1392 /* No more links if this is an msdfs proxy. */
1393 if (*msdfs_proxy
!= '\0') {
1397 /* Now enumerate all dfs links */
1398 dirp
= SMB_VFS_OPENDIR(conn
, ".", NULL
, 0);
1403 while ((dname
= vfs_readdirname(conn
, dirp
, NULL
, &talloced
))
1405 if (is_msdfs_link(conn
,
1410 TALLOC_FREE(talloced
);
1413 SMB_VFS_CLOSEDIR(conn
,dirp
);
1416 vfs_ChDir(conn
, cwd
);
1417 SMB_VFS_DISCONNECT(conn
);
1422 /*********************************************************************
1423 *********************************************************************/
1425 static int form_junctions(TALLOC_CTX
*ctx
,
1427 struct junction_map
*jucn
,
1432 const char *dname
= NULL
;
1433 char *talloced
= NULL
;
1434 const char *connect_path
= lp_path(talloc_tos(), snum
);
1435 char *service_name
= lp_servicename(talloc_tos(), snum
);
1436 const char *msdfs_proxy
= lp_msdfs_proxy(talloc_tos(), snum
);
1437 connection_struct
*conn
;
1438 struct referral
*ref
= NULL
;
1442 if (jn_remain
== 0) {
1446 if(*connect_path
== '\0') {
1451 * Fake up a connection struct for the VFS layer.
1454 status
= create_conn_struct_cwd(ctx
,
1455 server_event_context(),
1456 server_messaging_context(),
1457 &conn
, snum
, connect_path
, NULL
,
1459 if (!NT_STATUS_IS_OK(status
)) {
1460 DEBUG(3, ("create_conn_struct failed: %s\n",
1461 nt_errstr(status
)));
1465 /* form a junction for the msdfs root - convention
1466 DO NOT REMOVE THIS: NT clients will not work with us
1467 if this is not present
1469 jucn
[cnt
].service_name
= talloc_strdup(ctx
,service_name
);
1470 jucn
[cnt
].volume_name
= talloc_strdup(ctx
, "");
1471 if (!jucn
[cnt
].service_name
|| !jucn
[cnt
].volume_name
) {
1474 jucn
[cnt
].comment
= "";
1475 jucn
[cnt
].referral_count
= 1;
1477 ref
= jucn
[cnt
].referral_list
= talloc_zero(ctx
, struct referral
);
1478 if (jucn
[cnt
].referral_list
== NULL
) {
1483 ref
->ttl
= REFERRAL_TTL
;
1484 if (*msdfs_proxy
!= '\0') {
1485 ref
->alternate_path
= talloc_strdup(ctx
,
1488 ref
->alternate_path
= talloc_asprintf(ctx
,
1490 get_local_machine_name(),
1494 if (!ref
->alternate_path
) {
1499 /* Don't enumerate if we're an msdfs proxy. */
1500 if (*msdfs_proxy
!= '\0') {
1504 /* Now enumerate all dfs links */
1505 dirp
= SMB_VFS_OPENDIR(conn
, ".", NULL
, 0);
1510 while ((dname
= vfs_readdirname(conn
, dirp
, NULL
, &talloced
))
1512 char *link_target
= NULL
;
1513 if (cnt
>= jn_remain
) {
1514 DEBUG(2, ("form_junctions: ran out of MSDFS "
1516 TALLOC_FREE(talloced
);
1519 if (is_msdfs_link_internal(ctx
,
1521 dname
, &link_target
,
1523 if (parse_msdfs_symlink(ctx
,
1525 &jucn
[cnt
].referral_list
,
1526 &jucn
[cnt
].referral_count
)) {
1528 jucn
[cnt
].service_name
= talloc_strdup(ctx
,
1530 jucn
[cnt
].volume_name
= talloc_strdup(ctx
,
1532 if (!jucn
[cnt
].service_name
||
1533 !jucn
[cnt
].volume_name
) {
1534 TALLOC_FREE(talloced
);
1537 jucn
[cnt
].comment
= "";
1540 TALLOC_FREE(link_target
);
1542 TALLOC_FREE(talloced
);
1548 SMB_VFS_CLOSEDIR(conn
,dirp
);
1551 vfs_ChDir(conn
, cwd
);
1556 struct junction_map
*enum_msdfs_links(TALLOC_CTX
*ctx
, size_t *p_num_jn
)
1558 struct junction_map
*jn
= NULL
;
1560 size_t jn_count
= 0;
1564 if(!lp_host_msdfs()) {
1568 /* Ensure all the usershares are loaded. */
1570 load_registry_shares();
1571 sharecount
= load_usershare_shares(NULL
, connections_snum_used
);
1574 for(i
=0;i
< sharecount
;i
++) {
1575 if(lp_msdfs_root(i
)) {
1576 jn_count
+= count_dfs_links(ctx
, i
);
1579 if (jn_count
== 0) {
1582 jn
= talloc_array(ctx
, struct junction_map
, jn_count
);
1586 for(i
=0; i
< sharecount
; i
++) {
1587 if (*p_num_jn
>= jn_count
) {
1590 if(lp_msdfs_root(i
)) {
1591 *p_num_jn
+= form_junctions(ctx
, i
,
1593 jn_count
- *p_num_jn
);
1599 /******************************************************************************
1600 Core function to resolve a dfs pathname possibly containing a wildcard. If
1601 ppath_contains_wcard != NULL, it will be set to true if a wildcard is
1602 detected during dfs resolution.
1603 ******************************************************************************/
1605 NTSTATUS
resolve_dfspath_wcard(TALLOC_CTX
*ctx
,
1606 connection_struct
*conn
,
1608 const char *name_in
,
1610 bool allow_broken_path
,
1612 bool *ppath_contains_wcard
)
1614 bool path_contains_wcard
;
1615 NTSTATUS status
= NT_STATUS_OK
;
1617 if (dfs_pathnames
) {
1618 status
= dfs_redirect(ctx
,
1624 &path_contains_wcard
);
1626 if (NT_STATUS_IS_OK(status
) && ppath_contains_wcard
!= NULL
) {
1627 *ppath_contains_wcard
= path_contains_wcard
;
1631 * Cheat and just return a copy of the in ptr.
1632 * Once srvstr_get_path() uses talloc it'll
1633 * be a talloced ptr anyway.
1635 *pp_name_out
= discard_const_p(char, name_in
);