2 Unix SMB/Netbios implementation.
4 MSDFS services for Samba
5 Copyright (C) Shirish Kalele 2000
6 Copyright (C) Jeremy Allison 2007
7 Copyright (C) Robin McCorkell 2015
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 3 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>.
24 #define DBGC_CLASS DBGC_MSDFS
26 #include "system/filesys.h"
27 #include "smbd/smbd.h"
28 #include "smbd/globals.h"
31 #include "lib/param/loadparm.h"
32 #include "libcli/security/security.h"
33 #include "librpc/gen_ndr/ndr_dfsblobs.h"
35 /**********************************************************************
36 Parse a DFS pathname of the form \hostname\service\reqpath
37 into the dfs_path structure.
38 If POSIX pathnames is true, the pathname may also be of the
39 form /hostname/service/reqpath.
40 We cope with either here.
42 Unfortunately, due to broken clients who might set the
43 SVAL(inbuf,smb_flg2) & FLAGS2_DFS_PATHNAMES bit and then
44 send a local path, we have to cope with that too....
46 If conn != NULL then ensure the provided service is
47 the one pointed to by the connection.
49 This version does everything using pointers within one copy of the
50 pathname string, talloced on the struct dfs_path pointer (which
51 must be talloced). This may be too clever to live....
53 **********************************************************************/
55 static NTSTATUS
parse_dfs_path(connection_struct
*conn
,
58 bool allow_broken_path
,
59 struct dfs_path
*pdp
, /* MUST BE TALLOCED */
60 bool *ppath_contains_wcard
)
66 NTSTATUS status
= NT_STATUS_OK
;
72 * This is the only talloc we should need to do
73 * on the struct dfs_path. All the pointers inside
74 * it should point to offsets within this string.
77 pathname_local
= talloc_strdup(pdp
, pathname
);
78 if (!pathname_local
) {
79 return NT_STATUS_NO_MEMORY
;
81 /* Get a pointer to the terminating '\0' */
82 eos_ptr
= &pathname_local
[strlen(pathname_local
)];
83 p
= temp
= pathname_local
;
85 pdp
->posix_path
= (lp_posix_pathnames() && *pathname
== '/');
87 sepchar
= pdp
->posix_path
? '/' : '\\';
89 if (allow_broken_path
&& (*pathname
!= sepchar
)) {
90 DEBUG(10,("parse_dfs_path: path %s doesn't start with %c\n",
93 * Possibly client sent a local path by mistake.
94 * Try and convert to a local path.
97 pdp
->hostname
= eos_ptr
; /* "" */
98 pdp
->servicename
= eos_ptr
; /* "" */
100 /* We've got no info about separators. */
101 pdp
->posix_path
= lp_posix_pathnames();
103 DEBUG(10,("parse_dfs_path: trying to convert %s to a "
110 * Safe to use on talloc'ed string as it only shrinks.
111 * It also doesn't affect the eos_ptr.
113 trim_char(temp
,sepchar
,sepchar
);
115 DEBUG(10,("parse_dfs_path: temp = |%s| after trimming %c's\n",
119 /* Parse out hostname. */
120 p
= strchr_m(temp
,sepchar
);
122 DEBUG(10,("parse_dfs_path: can't parse hostname from path %s\n",
125 * Possibly client sent a local path by mistake.
126 * Try and convert to a local path.
129 pdp
->hostname
= eos_ptr
; /* "" */
130 pdp
->servicename
= eos_ptr
; /* "" */
133 DEBUG(10,("parse_dfs_path: trying to convert %s "
139 pdp
->hostname
= temp
;
141 DEBUG(10,("parse_dfs_path: hostname: %s\n",pdp
->hostname
));
143 /* Parse out servicename. */
145 p
= strchr_m(servicename
,sepchar
);
150 /* Is this really our servicename ? */
151 if (conn
&& !( strequal(servicename
, lp_servicename(talloc_tos(), SNUM(conn
)))
152 || (strequal(servicename
, HOMES_NAME
)
153 && strequal(lp_servicename(talloc_tos(), SNUM(conn
)),
154 get_current_username()) )) ) {
155 DEBUG(10,("parse_dfs_path: %s is not our servicename\n",
159 * Possibly client sent a local path by mistake.
160 * Try and convert to a local path.
163 pdp
->hostname
= eos_ptr
; /* "" */
164 pdp
->servicename
= eos_ptr
; /* "" */
166 /* Repair the path - replace the sepchar's
169 *servicename
= sepchar
;
175 DEBUG(10,("parse_dfs_path: trying to convert %s "
181 pdp
->servicename
= servicename
;
183 DEBUG(10,("parse_dfs_path: servicename: %s\n",pdp
->servicename
));
186 /* Client sent self referral \server\share. */
187 pdp
->reqpath
= eos_ptr
; /* "" */
195 *ppath_contains_wcard
= False
;
199 /* Rest is reqpath. */
200 if (pdp
->posix_path
) {
201 status
= check_path_syntax_posix(pdp
->reqpath
);
204 status
= check_path_syntax_wcard(pdp
->reqpath
,
205 ppath_contains_wcard
);
207 status
= check_path_syntax(pdp
->reqpath
);
211 if (!NT_STATUS_IS_OK(status
)) {
212 DEBUG(10,("parse_dfs_path: '%s' failed with %s\n",
213 p
, nt_errstr(status
) ));
217 DEBUG(10,("parse_dfs_path: rest of the path: %s\n",pdp
->reqpath
));
221 /********************************************************
222 Fake up a connection struct for the VFS layer, for use in
223 applications (such as the python bindings), that do not want the
224 global working directory changed under them.
226 SMB_VFS_CONNECT requires root privileges.
227 *********************************************************/
229 static NTSTATUS
create_conn_struct_as_root(TALLOC_CTX
*ctx
,
230 struct tevent_context
*ev
,
231 struct messaging_context
*msg
,
232 connection_struct
**pconn
,
235 const struct auth_session_info
*session_info
)
237 connection_struct
*conn
;
239 const char *vfs_user
;
240 struct smbd_server_connection
*sconn
;
241 const char *servicename
= lp_const_servicename(snum
);
243 sconn
= talloc_zero(ctx
, struct smbd_server_connection
);
245 return NT_STATUS_NO_MEMORY
;
249 sconn
->msg_ctx
= msg
;
251 conn
= conn_new(sconn
);
254 return NT_STATUS_NO_MEMORY
;
257 /* Now we have conn, we need to make sconn a child of conn,
258 * for a proper talloc tree */
259 talloc_steal(conn
, sconn
);
261 if (snum
== -1 && servicename
== NULL
) {
262 servicename
= "Unknown Service (snum == -1)";
265 connpath
= talloc_strdup(conn
, path
);
268 return NT_STATUS_NO_MEMORY
;
270 connpath
= talloc_string_sub(conn
,
276 return NT_STATUS_NO_MEMORY
;
279 /* needed for smbd_vfs_init() */
281 conn
->params
->service
= snum
;
282 conn
->cnum
= TID_FIELD_INVALID
;
284 if (session_info
!= NULL
) {
285 conn
->session_info
= copy_session_info(conn
, session_info
);
286 if (conn
->session_info
== NULL
) {
287 DEBUG(0, ("copy_serverinfo failed\n"));
289 return NT_STATUS_NO_MEMORY
;
291 vfs_user
= conn
->session_info
->unix_info
->unix_name
;
293 /* use current authenticated user in absence of session_info */
294 vfs_user
= get_current_username();
297 set_conn_connectpath(conn
, connpath
);
300 * New code to check if there's a share security descriptor
301 * added from NT server manager. This is done after the
302 * smb.conf checks are done as we need a uid and token. JRA.
305 if (conn
->session_info
) {
306 share_access_check(conn
->session_info
->security_token
,
308 MAXIMUM_ALLOWED_ACCESS
,
309 &conn
->share_access
);
311 if ((conn
->share_access
& FILE_WRITE_DATA
) == 0) {
312 if ((conn
->share_access
& FILE_READ_DATA
) == 0) {
313 /* No access, read or write. */
314 DEBUG(3,("create_conn_struct: connection to %s "
315 "denied due to security "
319 return NT_STATUS_ACCESS_DENIED
;
321 conn
->read_only
= true;
325 conn
->share_access
= 0;
326 conn
->read_only
= true;
329 if (!smbd_vfs_init(conn
)) {
330 NTSTATUS status
= map_nt_error_from_unix(errno
);
331 DEBUG(0,("create_conn_struct: smbd_vfs_init failed.\n"));
336 /* this must be the first filesystem operation that we do */
337 if (SMB_VFS_CONNECT(conn
, servicename
, vfs_user
) < 0) {
338 DEBUG(0,("VFS connect failed!\n"));
340 return NT_STATUS_UNSUCCESSFUL
;
343 conn
->fs_capabilities
= SMB_VFS_FS_CAPABILITIES(conn
, &conn
->ts_res
);
349 /********************************************************
350 Fake up a connection struct for the VFS layer, for use in
351 applications (such as the python bindings), that do not want the
352 global working directory changed under them.
354 SMB_VFS_CONNECT requires root privileges.
355 *********************************************************/
357 NTSTATUS
create_conn_struct(TALLOC_CTX
*ctx
,
358 struct tevent_context
*ev
,
359 struct messaging_context
*msg
,
360 connection_struct
**pconn
,
363 const struct auth_session_info
*session_info
)
367 status
= create_conn_struct_as_root(ctx
, ev
,
376 /********************************************************
377 Fake up a connection struct for the VFS layer.
378 Note: this performs a vfs connect and CHANGES CWD !!!! JRA.
380 The old working directory is returned on *poldcwd, allocated on ctx.
381 *********************************************************/
383 NTSTATUS
create_conn_struct_cwd(TALLOC_CTX
*ctx
,
384 struct tevent_context
*ev
,
385 struct messaging_context
*msg
,
386 connection_struct
**pconn
,
389 const struct auth_session_info
*session_info
,
392 connection_struct
*conn
;
395 NTSTATUS status
= create_conn_struct(ctx
, ev
,
399 if (!NT_STATUS_IS_OK(status
)) {
404 * Windows seems to insist on doing trans2getdfsreferral() calls on
405 * the IPC$ share as the anonymous user. If we try to chdir as that
406 * user we will fail.... WTF ? JRA.
409 oldcwd
= vfs_GetWd(ctx
, conn
);
410 if (oldcwd
== NULL
) {
411 status
= map_nt_error_from_unix(errno
);
412 DEBUG(3, ("vfs_GetWd failed: %s\n", strerror(errno
)));
417 if (vfs_ChDir(conn
,conn
->connectpath
) != 0) {
418 status
= map_nt_error_from_unix(errno
);
419 DEBUG(3,("create_conn_struct: Can't ChDir to new conn path %s. "
421 conn
->connectpath
, strerror(errno
) ));
432 static void shuffle_strlist(char **list
, int count
)
438 for (i
= count
; i
> 1; i
--) {
439 r
= generate_random() % i
;
447 /**********************************************************************
448 Parse the contents of a symlink to verify if it is an msdfs referral
449 A valid referral is of the form:
451 msdfs:server1\share1,server2\share2
452 msdfs:server1\share1\pathname,server2\share2\pathname
453 msdfs:server1/share1,server2/share2
454 msdfs:server1/share1/pathname,server2/share2/pathname.
456 Note that the alternate paths returned here must be of the canonicalized
460 \server\share\path\to\file,
462 even in posix path mode. This is because we have no knowledge if the
463 server we're referring to understands posix paths.
464 **********************************************************************/
466 static bool parse_msdfs_symlink(TALLOC_CTX
*ctx
,
469 struct referral
**preflist
,
474 char **alt_path
= NULL
;
476 struct referral
*reflist
;
479 temp
= talloc_strdup(ctx
, target
);
483 prot
= strtok_r(temp
, ":", &saveptr
);
485 DEBUG(0,("parse_msdfs_symlink: invalid path !\n"));
489 alt_path
= talloc_array(ctx
, char *, MAX_REFERRAL_COUNT
);
494 /* parse out the alternate paths */
495 while((count
<MAX_REFERRAL_COUNT
) &&
496 ((alt_path
[count
] = strtok_r(NULL
, ",", &saveptr
)) != NULL
)) {
500 /* shuffle alternate paths */
501 if (lp_msdfs_shuffle_referrals(snum
)) {
502 shuffle_strlist(alt_path
, count
);
505 DEBUG(10,("parse_msdfs_symlink: count=%d\n", count
));
508 reflist
= *preflist
= talloc_zero_array(ctx
,
509 struct referral
, count
);
510 if(reflist
== NULL
) {
511 TALLOC_FREE(alt_path
);
515 reflist
= *preflist
= NULL
;
518 for(i
=0;i
<count
;i
++) {
521 /* Canonicalize link target.
522 * Replace all /'s in the path by a \ */
523 string_replace(alt_path
[i
], '/', '\\');
525 /* Remove leading '\\'s */
527 while (*p
&& (*p
== '\\')) {
531 reflist
[i
].alternate_path
= talloc_asprintf(ctx
,
534 if (!reflist
[i
].alternate_path
) {
538 reflist
[i
].proximity
= 0;
539 reflist
[i
].ttl
= REFERRAL_TTL
;
540 DEBUG(10, ("parse_msdfs_symlink: Created alt path: %s\n",
541 reflist
[i
].alternate_path
));
546 TALLOC_FREE(alt_path
);
550 /**********************************************************************
551 Returns true if the unix path is a valid msdfs symlink and also
552 returns the target string from inside the link.
553 **********************************************************************/
555 static bool is_msdfs_link_internal(TALLOC_CTX
*ctx
,
556 connection_struct
*conn
,
558 char **pp_link_target
,
559 SMB_STRUCT_STAT
*sbufp
)
561 int referral_len
= 0;
562 #if defined(HAVE_BROKEN_READLINK)
563 char link_target_buf
[PATH_MAX
];
565 char link_target_buf
[7];
568 char *link_target
= NULL
;
569 struct smb_filename smb_fname
;
571 if (pp_link_target
) {
573 link_target
= talloc_array(ctx
, char, bufsize
);
577 *pp_link_target
= link_target
;
579 bufsize
= sizeof(link_target_buf
);
580 link_target
= link_target_buf
;
583 ZERO_STRUCT(smb_fname
);
584 smb_fname
.base_name
= discard_const_p(char, path
);
586 if (SMB_VFS_LSTAT(conn
, &smb_fname
) != 0) {
587 DEBUG(5,("is_msdfs_link_read_target: %s does not exist.\n",
591 if (!S_ISLNK(smb_fname
.st
.st_ex_mode
)) {
592 DEBUG(5,("is_msdfs_link_read_target: %s is not a link.\n",
597 *sbufp
= smb_fname
.st
;
600 referral_len
= SMB_VFS_READLINK(conn
, path
, link_target
, bufsize
- 1);
601 if (referral_len
== -1) {
602 DEBUG(0,("is_msdfs_link_read_target: Error reading "
603 "msdfs link %s: %s\n",
604 path
, strerror(errno
)));
607 link_target
[referral_len
] = '\0';
609 DEBUG(5,("is_msdfs_link_internal: %s -> %s\n",path
,
612 if (!strnequal(link_target
, "msdfs:", 6)) {
619 if (link_target
!= link_target_buf
) {
620 TALLOC_FREE(link_target
);
625 /**********************************************************************
626 Returns true if the unix path is a valid msdfs symlink.
627 **********************************************************************/
629 bool is_msdfs_link(connection_struct
*conn
,
631 SMB_STRUCT_STAT
*sbufp
)
633 return is_msdfs_link_internal(talloc_tos(),
640 /*****************************************************************
641 Used by other functions to decide if a dfs path is remote,
642 and to get the list of referred locations for that remote path.
644 search_flag: For findfirsts, dfs links themselves are not
645 redirected, but paths beyond the links are. For normal smb calls,
646 even dfs links need to be redirected.
648 consumedcntp: how much of the dfs path is being redirected. the client
649 should try the remaining path on the redirected server.
651 If this returns NT_STATUS_PATH_NOT_COVERED the contents of the msdfs
652 link redirect are in targetpath.
653 *****************************************************************/
655 static NTSTATUS
dfs_path_lookup(TALLOC_CTX
*ctx
,
656 connection_struct
*conn
,
657 const char *dfspath
, /* Incoming complete dfs path */
658 const struct dfs_path
*pdp
, /* Parsed out
659 server+share+extrapath. */
660 bool search_flag
, /* Called from a findfirst ? */
662 char **pp_targetpath
)
667 struct smb_filename
*smb_fname
= NULL
;
668 char *canon_dfspath
= NULL
; /* Canonicalized dfs path. (only '/'
671 DEBUG(10,("dfs_path_lookup: Conn path = %s reqpath = %s\n",
672 conn
->connectpath
, pdp
->reqpath
));
675 * Note the unix path conversion here we're doing we
676 * throw away. We're looking for a symlink for a dfs
677 * resolution, if we don't find it we'll do another
678 * unix_convert later in the codepath.
681 status
= unix_convert(ctx
, conn
, pdp
->reqpath
, &smb_fname
,
682 search_flag
? UCF_ALWAYS_ALLOW_WCARD_LCOMP
: 0);
684 if (!NT_STATUS_IS_OK(status
)) {
685 if (!NT_STATUS_EQUAL(status
,
686 NT_STATUS_OBJECT_PATH_NOT_FOUND
)) {
689 if (smb_fname
== NULL
|| smb_fname
->base_name
== NULL
) {
694 /* Optimization - check if we can redirect the whole path. */
696 if (is_msdfs_link_internal(ctx
, conn
, smb_fname
->base_name
,
697 pp_targetpath
, NULL
)) {
699 DEBUG(6,("dfs_path_lookup (FindFirst) No redirection "
700 "for dfs link %s.\n", dfspath
));
701 status
= NT_STATUS_OK
;
705 DEBUG(6,("dfs_path_lookup: %s resolves to a "
706 "valid dfs link %s.\n", dfspath
,
707 pp_targetpath
? *pp_targetpath
: ""));
710 *consumedcntp
= strlen(dfspath
);
712 status
= NT_STATUS_PATH_NOT_COVERED
;
716 /* Prepare to test only for '/' components in the given path,
717 * so if a Windows path replace all '\\' characters with '/'.
718 * For a POSIX DFS path we know all separators are already '/'. */
720 canon_dfspath
= talloc_strdup(ctx
, dfspath
);
721 if (!canon_dfspath
) {
722 status
= NT_STATUS_NO_MEMORY
;
725 if (!pdp
->posix_path
) {
726 string_replace(canon_dfspath
, '\\', '/');
730 * localpath comes out of unix_convert, so it has
731 * no trailing backslash. Make sure that canon_dfspath hasn't either.
732 * Fix for bug #4860 from Jan Martin <Jan.Martin@rwedea.com>.
735 trim_char(canon_dfspath
,0,'/');
738 * Redirect if any component in the path is a link.
739 * We do this by walking backwards through the
740 * local path, chopping off the last component
741 * in both the local path and the canonicalized
742 * DFS path. If we hit a DFS link then we're done.
745 p
= strrchr_m(smb_fname
->base_name
, '/');
747 q
= strrchr_m(canon_dfspath
, '/');
756 if (is_msdfs_link_internal(ctx
, conn
,
757 smb_fname
->base_name
, pp_targetpath
,
759 DEBUG(4, ("dfs_path_lookup: Redirecting %s because "
760 "parent %s is dfs link\n", dfspath
,
761 smb_fname_str_dbg(smb_fname
)));
764 *consumedcntp
= strlen(canon_dfspath
);
765 DEBUG(10, ("dfs_path_lookup: Path consumed: %s "
771 status
= NT_STATUS_PATH_NOT_COVERED
;
775 /* Step back on the filesystem. */
776 p
= strrchr_m(smb_fname
->base_name
, '/');
779 /* And in the canonicalized dfs path. */
780 q
= strrchr_m(canon_dfspath
, '/');
784 status
= NT_STATUS_OK
;
786 TALLOC_FREE(smb_fname
);
790 /*****************************************************************
791 Decides if a dfs pathname should be redirected or not.
792 If not, the pathname is converted to a tcon-relative local unix path
794 search_wcard_flag: this flag performs 2 functions both related
795 to searches. See resolve_dfs_path() and parse_dfs_path_XX()
798 This function can return NT_STATUS_OK, meaning use the returned path as-is
799 (mapped into a local path).
800 or NT_STATUS_NOT_COVERED meaning return a DFS redirect, or
801 any other NT_STATUS error which is a genuine error to be
802 returned to the client.
803 *****************************************************************/
805 static NTSTATUS
dfs_redirect(TALLOC_CTX
*ctx
,
806 connection_struct
*conn
,
808 bool search_wcard_flag
,
809 bool allow_broken_path
,
811 bool *ppath_contains_wcard
)
814 struct dfs_path
*pdp
= talloc(ctx
, struct dfs_path
);
817 return NT_STATUS_NO_MEMORY
;
820 status
= parse_dfs_path(conn
, path_in
, search_wcard_flag
,
821 allow_broken_path
, pdp
,
822 ppath_contains_wcard
);
823 if (!NT_STATUS_IS_OK(status
)) {
828 if (pdp
->reqpath
[0] == '\0') {
830 *pp_path_out
= talloc_strdup(ctx
, "");
832 return NT_STATUS_NO_MEMORY
;
834 DEBUG(5,("dfs_redirect: self-referral.\n"));
838 /* If dfs pathname for a non-dfs share, convert to tcon-relative
839 path and return OK */
841 if (!lp_msdfs_root(SNUM(conn
))) {
842 *pp_path_out
= talloc_strdup(ctx
, pdp
->reqpath
);
845 return NT_STATUS_NO_MEMORY
;
850 /* If it looked like a local path (zero hostname/servicename)
851 * just treat as a tcon-relative path. */
853 if (pdp
->hostname
[0] == '\0' && pdp
->servicename
[0] == '\0') {
854 *pp_path_out
= talloc_strdup(ctx
, pdp
->reqpath
);
857 return NT_STATUS_NO_MEMORY
;
862 if (!( strequal(pdp
->servicename
, lp_servicename(talloc_tos(), SNUM(conn
)))
863 || (strequal(pdp
->servicename
, HOMES_NAME
)
864 && strequal(lp_servicename(talloc_tos(), SNUM(conn
)),
865 conn
->session_info
->unix_info
->sanitized_username
) )) ) {
867 /* The given sharename doesn't match this connection. */
870 return NT_STATUS_OBJECT_PATH_NOT_FOUND
;
873 status
= dfs_path_lookup(ctx
, conn
, path_in
, pdp
,
874 search_wcard_flag
, NULL
, NULL
);
875 if (!NT_STATUS_IS_OK(status
)) {
876 if (NT_STATUS_EQUAL(status
, NT_STATUS_PATH_NOT_COVERED
)) {
877 DEBUG(3,("dfs_redirect: Redirecting %s\n", path_in
));
879 DEBUG(10,("dfs_redirect: dfs_path_lookup "
880 "failed for %s with %s\n",
881 path_in
, nt_errstr(status
) ));
886 DEBUG(3,("dfs_redirect: Not redirecting %s.\n", path_in
));
888 /* Form non-dfs tcon-relative path */
889 *pp_path_out
= talloc_strdup(ctx
, pdp
->reqpath
);
892 return NT_STATUS_NO_MEMORY
;
895 DEBUG(3,("dfs_redirect: Path %s converted to non-dfs path %s\n",
902 /**********************************************************************
903 Return a self referral.
904 **********************************************************************/
906 static NTSTATUS
self_ref(TALLOC_CTX
*ctx
,
907 const char *dfs_path
,
908 struct junction_map
*jucn
,
910 bool *self_referralp
)
912 struct referral
*ref
;
914 *self_referralp
= True
;
916 jucn
->referral_count
= 1;
917 if((ref
= talloc_zero(ctx
, struct referral
)) == NULL
) {
918 return NT_STATUS_NO_MEMORY
;
921 ref
->alternate_path
= talloc_strdup(ctx
, dfs_path
);
922 if (!ref
->alternate_path
) {
924 return NT_STATUS_NO_MEMORY
;
927 ref
->ttl
= REFERRAL_TTL
;
928 jucn
->referral_list
= ref
;
929 *consumedcntp
= strlen(dfs_path
);
933 /**********************************************************************
934 Gets valid referrals for a dfs path and fills up the
935 junction_map structure.
936 **********************************************************************/
938 NTSTATUS
get_referred_path(TALLOC_CTX
*ctx
,
939 const char *dfs_path
,
940 bool allow_broken_path
,
941 struct junction_map
*jucn
,
943 bool *self_referralp
)
945 struct connection_struct
*conn
;
946 char *targetpath
= NULL
;
948 NTSTATUS status
= NT_STATUS_NOT_FOUND
;
950 struct dfs_path
*pdp
= talloc(ctx
, struct dfs_path
);
954 return NT_STATUS_NO_MEMORY
;
957 *self_referralp
= False
;
959 status
= parse_dfs_path(NULL
, dfs_path
, False
, allow_broken_path
,
961 if (!NT_STATUS_IS_OK(status
)) {
965 jucn
->service_name
= talloc_strdup(ctx
, pdp
->servicename
);
966 jucn
->volume_name
= talloc_strdup(ctx
, pdp
->reqpath
);
967 if (!jucn
->service_name
|| !jucn
->volume_name
) {
969 return NT_STATUS_NO_MEMORY
;
972 /* Verify the share is a dfs root */
973 snum
= lp_servicenumber(jucn
->service_name
);
975 char *service_name
= NULL
;
976 if ((snum
= find_service(ctx
, jucn
->service_name
, &service_name
)) < 0) {
977 return NT_STATUS_NOT_FOUND
;
980 return NT_STATUS_NO_MEMORY
;
982 TALLOC_FREE(jucn
->service_name
);
983 jucn
->service_name
= talloc_strdup(ctx
, service_name
);
984 if (!jucn
->service_name
) {
986 return NT_STATUS_NO_MEMORY
;
990 if (!lp_msdfs_root(snum
) && (*lp_msdfs_proxy(talloc_tos(), snum
) == '\0')) {
991 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not "
993 pdp
->servicename
, dfs_path
));
995 return NT_STATUS_NOT_FOUND
;
999 * Self referrals are tested with a anonymous IPC connection and
1000 * a GET_DFS_REFERRAL call to \\server\share. (which means
1001 * dp.reqpath[0] points to an empty string). create_conn_struct cd's
1002 * into the directory and will fail if it cannot (as the anonymous
1003 * user). Cope with this.
1006 if (pdp
->reqpath
[0] == '\0') {
1008 struct referral
*ref
;
1011 if (*lp_msdfs_proxy(talloc_tos(), snum
) == '\0') {
1013 return self_ref(ctx
,
1021 * It's an msdfs proxy share. Redirect to
1022 * the configured target share.
1025 tmp
= talloc_asprintf(talloc_tos(), "msdfs:%s",
1026 lp_msdfs_proxy(talloc_tos(), snum
));
1029 return NT_STATUS_NO_MEMORY
;
1032 if (!parse_msdfs_symlink(ctx
, snum
, tmp
, &ref
, &refcount
)) {
1035 return NT_STATUS_INVALID_PARAMETER
;
1038 jucn
->referral_count
= refcount
;
1039 jucn
->referral_list
= ref
;
1040 *consumedcntp
= strlen(dfs_path
);
1042 return NT_STATUS_OK
;
1045 status
= create_conn_struct_cwd(ctx
,
1046 server_event_context(),
1047 server_messaging_context(),
1049 lp_path(talloc_tos(), snum
), NULL
, &oldpath
);
1050 if (!NT_STATUS_IS_OK(status
)) {
1055 /* If this is a DFS path dfs_lookup should return
1056 * NT_STATUS_PATH_NOT_COVERED. */
1058 status
= dfs_path_lookup(ctx
, conn
, dfs_path
, pdp
,
1059 False
, consumedcntp
, &targetpath
);
1061 if (!NT_STATUS_EQUAL(status
, NT_STATUS_PATH_NOT_COVERED
)) {
1062 DEBUG(3,("get_referred_path: No valid referrals for path %s\n",
1064 if (NT_STATUS_IS_OK(status
)) {
1066 * We are in an error path here (we
1067 * know it's not a DFS path), but
1068 * dfs_path_lookup() can return
1069 * NT_STATUS_OK. Ensure we always
1070 * return a valid error code.
1072 * #9588 - ACLs are not inherited to directories
1075 status
= NT_STATUS_NOT_FOUND
;
1080 /* We know this is a valid dfs link. Parse the targetpath. */
1081 if (!parse_msdfs_symlink(ctx
, snum
, targetpath
,
1082 &jucn
->referral_list
,
1083 &jucn
->referral_count
)) {
1084 DEBUG(3,("get_referred_path: failed to parse symlink "
1085 "target %s\n", targetpath
));
1086 status
= NT_STATUS_NOT_FOUND
;
1090 status
= NT_STATUS_OK
;
1092 vfs_ChDir(conn
, oldpath
);
1093 SMB_VFS_DISCONNECT(conn
);
1099 /******************************************************************
1100 Set up the DFS referral for the dfs pathname. This call returns
1101 the amount of the path covered by this server, and where the
1102 client should be redirected to. This is the meat of the
1103 TRANS2_GET_DFS_REFERRAL call.
1104 ******************************************************************/
1106 int setup_dfs_referral(connection_struct
*orig_conn
,
1107 const char *dfs_path
,
1108 int max_referral_level
,
1109 char **ppdata
, NTSTATUS
*pstatus
)
1111 char *pdata
= *ppdata
;
1113 struct dfs_GetDFSReferral
*r
;
1114 DATA_BLOB blob
= data_blob_null
;
1116 enum ndr_err_code ndr_err
;
1118 r
= talloc_zero(talloc_tos(), struct dfs_GetDFSReferral
);
1120 *pstatus
= NT_STATUS_NO_MEMORY
;
1124 r
->in
.req
.max_referral_level
= max_referral_level
;
1125 r
->in
.req
.servername
= talloc_strdup(r
, dfs_path
);
1126 if (r
->in
.req
.servername
== NULL
) {
1128 *pstatus
= NT_STATUS_NO_MEMORY
;
1132 status
= SMB_VFS_GET_DFS_REFERRALS(orig_conn
, r
);
1133 if (!NT_STATUS_IS_OK(status
)) {
1139 ndr_err
= ndr_push_struct_blob(&blob
, r
,
1141 (ndr_push_flags_fn_t
)ndr_push_dfs_referral_resp
);
1142 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
1144 *pstatus
= NT_STATUS_INVALID_PARAMETER
;
1148 pdata
= (char *)SMB_REALLOC(pdata
, blob
.length
);
1151 DEBUG(0,("referral setup:"
1152 "malloc failed for Realloc!\n"));
1156 reply_size
= blob
.length
;
1157 memcpy(pdata
, blob
.data
, blob
.length
);
1160 *pstatus
= NT_STATUS_OK
;
1164 /**********************************************************************
1165 The following functions are called by the NETDFS RPC pipe functions
1166 **********************************************************************/
1168 /*********************************************************************
1169 Creates a junction structure from a DFS pathname
1170 **********************************************************************/
1172 bool create_junction(TALLOC_CTX
*ctx
,
1173 const char *dfs_path
,
1174 bool allow_broken_path
,
1175 struct junction_map
*jucn
)
1179 struct dfs_path
*pdp
= talloc(ctx
,struct dfs_path
);
1185 status
= parse_dfs_path(NULL
, dfs_path
, False
, allow_broken_path
,
1187 if (!NT_STATUS_IS_OK(status
)) {
1191 /* check if path is dfs : validate first token */
1192 if (!is_myname_or_ipaddr(pdp
->hostname
)) {
1193 DEBUG(4,("create_junction: Invalid hostname %s "
1195 pdp
->hostname
, dfs_path
));
1200 /* Check for a non-DFS share */
1201 snum
= lp_servicenumber(pdp
->servicename
);
1203 if(snum
< 0 || !lp_msdfs_root(snum
)) {
1204 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1210 jucn
->service_name
= talloc_strdup(ctx
, pdp
->servicename
);
1211 jucn
->volume_name
= talloc_strdup(ctx
, pdp
->reqpath
);
1212 jucn
->comment
= lp_comment(ctx
, snum
);
1215 if (!jucn
->service_name
|| !jucn
->volume_name
|| ! jucn
->comment
) {
1221 /**********************************************************************
1222 Forms a valid Unix pathname from the junction
1223 **********************************************************************/
1225 static bool junction_to_local_path(const struct junction_map
*jucn
,
1227 connection_struct
**conn_out
,
1233 snum
= lp_servicenumber(jucn
->service_name
);
1237 status
= create_conn_struct_cwd(talloc_tos(),
1238 server_event_context(),
1239 server_messaging_context(),
1241 snum
, lp_path(talloc_tos(), snum
), NULL
, oldpath
);
1242 if (!NT_STATUS_IS_OK(status
)) {
1246 *pp_path_out
= talloc_asprintf(*conn_out
,
1248 lp_path(talloc_tos(), snum
),
1250 if (!*pp_path_out
) {
1251 vfs_ChDir(*conn_out
, *oldpath
);
1252 SMB_VFS_DISCONNECT(*conn_out
);
1253 conn_free(*conn_out
);
1259 bool create_msdfs_link(const struct junction_map
*jucn
)
1263 char *msdfs_link
= NULL
;
1264 connection_struct
*conn
;
1266 bool insert_comma
= False
;
1269 if(!junction_to_local_path(jucn
, &path
, &conn
, &cwd
)) {
1273 /* Form the msdfs_link contents */
1274 msdfs_link
= talloc_strdup(conn
, "msdfs:");
1278 for(i
=0; i
<jucn
->referral_count
; i
++) {
1279 char *refpath
= jucn
->referral_list
[i
].alternate_path
;
1281 /* Alternate paths always use Windows separators. */
1282 trim_char(refpath
, '\\', '\\');
1283 if(*refpath
== '\0') {
1285 insert_comma
= False
;
1289 if (i
> 0 && insert_comma
) {
1290 msdfs_link
= talloc_asprintf_append_buffer(msdfs_link
,
1294 msdfs_link
= talloc_asprintf_append_buffer(msdfs_link
,
1302 if (!insert_comma
) {
1303 insert_comma
= True
;
1307 DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
1310 if(SMB_VFS_SYMLINK(conn
, msdfs_link
, path
) < 0) {
1311 if (errno
== EEXIST
) {
1312 struct smb_filename
*smb_fname
;
1314 smb_fname
= synthetic_smb_fname(talloc_tos(), path
,
1316 if (smb_fname
== NULL
) {
1321 if(SMB_VFS_UNLINK(conn
, smb_fname
)!=0) {
1322 TALLOC_FREE(smb_fname
);
1325 TALLOC_FREE(smb_fname
);
1327 if (SMB_VFS_SYMLINK(conn
, msdfs_link
, path
) < 0) {
1328 DEBUG(1,("create_msdfs_link: symlink failed "
1329 "%s -> %s\nError: %s\n",
1330 path
, msdfs_link
, strerror(errno
)));
1338 vfs_ChDir(conn
, cwd
);
1339 SMB_VFS_DISCONNECT(conn
);
1344 bool remove_msdfs_link(const struct junction_map
*jucn
)
1348 connection_struct
*conn
;
1350 struct smb_filename
*smb_fname
;
1352 if (!junction_to_local_path(jucn
, &path
, &conn
, &cwd
)) {
1356 smb_fname
= synthetic_smb_fname(talloc_tos(), path
, NULL
, NULL
);
1357 if (smb_fname
== NULL
) {
1362 if( SMB_VFS_UNLINK(conn
, smb_fname
) == 0 ) {
1366 TALLOC_FREE(smb_fname
);
1367 vfs_ChDir(conn
, cwd
);
1368 SMB_VFS_DISCONNECT(conn
);
1373 /*********************************************************************
1374 Return the number of DFS links at the root of this share.
1375 *********************************************************************/
1377 static int count_dfs_links(TALLOC_CTX
*ctx
, int snum
)
1381 const char *dname
= NULL
;
1382 char *talloced
= NULL
;
1383 const char *connect_path
= lp_path(talloc_tos(), snum
);
1384 const char *msdfs_proxy
= lp_msdfs_proxy(talloc_tos(), snum
);
1385 connection_struct
*conn
;
1389 if(*connect_path
== '\0') {
1394 * Fake up a connection struct for the VFS layer.
1397 status
= create_conn_struct_cwd(talloc_tos(),
1398 server_event_context(),
1399 server_messaging_context(),
1401 snum
, connect_path
, NULL
, &cwd
);
1402 if (!NT_STATUS_IS_OK(status
)) {
1403 DEBUG(3, ("create_conn_struct failed: %s\n",
1404 nt_errstr(status
)));
1408 /* Count a link for the msdfs root - convention */
1411 /* No more links if this is an msdfs proxy. */
1412 if (*msdfs_proxy
!= '\0') {
1416 /* Now enumerate all dfs links */
1417 dirp
= SMB_VFS_OPENDIR(conn
, ".", NULL
, 0);
1422 while ((dname
= vfs_readdirname(conn
, dirp
, NULL
, &talloced
))
1424 if (is_msdfs_link(conn
,
1429 TALLOC_FREE(talloced
);
1432 SMB_VFS_CLOSEDIR(conn
,dirp
);
1435 vfs_ChDir(conn
, cwd
);
1436 SMB_VFS_DISCONNECT(conn
);
1441 /*********************************************************************
1442 *********************************************************************/
1444 static int form_junctions(TALLOC_CTX
*ctx
,
1446 struct junction_map
*jucn
,
1451 const char *dname
= NULL
;
1452 char *talloced
= NULL
;
1453 const char *connect_path
= lp_path(talloc_tos(), snum
);
1454 char *service_name
= lp_servicename(talloc_tos(), snum
);
1455 const char *msdfs_proxy
= lp_msdfs_proxy(talloc_tos(), snum
);
1456 connection_struct
*conn
;
1457 struct referral
*ref
= NULL
;
1461 if (jn_remain
== 0) {
1465 if(*connect_path
== '\0') {
1470 * Fake up a connection struct for the VFS layer.
1473 status
= create_conn_struct_cwd(ctx
,
1474 server_event_context(),
1475 server_messaging_context(),
1476 &conn
, snum
, connect_path
, NULL
,
1478 if (!NT_STATUS_IS_OK(status
)) {
1479 DEBUG(3, ("create_conn_struct failed: %s\n",
1480 nt_errstr(status
)));
1484 /* form a junction for the msdfs root - convention
1485 DO NOT REMOVE THIS: NT clients will not work with us
1486 if this is not present
1488 jucn
[cnt
].service_name
= talloc_strdup(ctx
,service_name
);
1489 jucn
[cnt
].volume_name
= talloc_strdup(ctx
, "");
1490 if (!jucn
[cnt
].service_name
|| !jucn
[cnt
].volume_name
) {
1493 jucn
[cnt
].comment
= "";
1494 jucn
[cnt
].referral_count
= 1;
1496 ref
= jucn
[cnt
].referral_list
= talloc_zero(ctx
, struct referral
);
1497 if (jucn
[cnt
].referral_list
== NULL
) {
1502 ref
->ttl
= REFERRAL_TTL
;
1503 if (*msdfs_proxy
!= '\0') {
1504 ref
->alternate_path
= talloc_strdup(ctx
,
1507 ref
->alternate_path
= talloc_asprintf(ctx
,
1509 get_local_machine_name(),
1513 if (!ref
->alternate_path
) {
1518 /* Don't enumerate if we're an msdfs proxy. */
1519 if (*msdfs_proxy
!= '\0') {
1523 /* Now enumerate all dfs links */
1524 dirp
= SMB_VFS_OPENDIR(conn
, ".", NULL
, 0);
1529 while ((dname
= vfs_readdirname(conn
, dirp
, NULL
, &talloced
))
1531 char *link_target
= NULL
;
1532 if (cnt
>= jn_remain
) {
1533 DEBUG(2, ("form_junctions: ran out of MSDFS "
1535 TALLOC_FREE(talloced
);
1538 if (is_msdfs_link_internal(ctx
,
1540 dname
, &link_target
,
1542 if (parse_msdfs_symlink(ctx
, snum
,
1544 &jucn
[cnt
].referral_list
,
1545 &jucn
[cnt
].referral_count
)) {
1547 jucn
[cnt
].service_name
= talloc_strdup(ctx
,
1549 jucn
[cnt
].volume_name
= talloc_strdup(ctx
,
1551 if (!jucn
[cnt
].service_name
||
1552 !jucn
[cnt
].volume_name
) {
1553 TALLOC_FREE(talloced
);
1556 jucn
[cnt
].comment
= "";
1559 TALLOC_FREE(link_target
);
1561 TALLOC_FREE(talloced
);
1567 SMB_VFS_CLOSEDIR(conn
,dirp
);
1570 vfs_ChDir(conn
, cwd
);
1575 struct junction_map
*enum_msdfs_links(TALLOC_CTX
*ctx
, size_t *p_num_jn
)
1577 struct junction_map
*jn
= NULL
;
1579 size_t jn_count
= 0;
1583 if(!lp_host_msdfs()) {
1587 /* Ensure all the usershares are loaded. */
1589 load_registry_shares();
1590 sharecount
= load_usershare_shares(NULL
, connections_snum_used
);
1593 for(i
=0;i
< sharecount
;i
++) {
1594 if(lp_msdfs_root(i
)) {
1595 jn_count
+= count_dfs_links(ctx
, i
);
1598 if (jn_count
== 0) {
1601 jn
= talloc_array(ctx
, struct junction_map
, jn_count
);
1605 for(i
=0; i
< sharecount
; i
++) {
1606 if (*p_num_jn
>= jn_count
) {
1609 if(lp_msdfs_root(i
)) {
1610 *p_num_jn
+= form_junctions(ctx
, i
,
1612 jn_count
- *p_num_jn
);
1618 /******************************************************************************
1619 Core function to resolve a dfs pathname possibly containing a wildcard. If
1620 ppath_contains_wcard != NULL, it will be set to true if a wildcard is
1621 detected during dfs resolution.
1622 ******************************************************************************/
1624 NTSTATUS
resolve_dfspath_wcard(TALLOC_CTX
*ctx
,
1625 connection_struct
*conn
,
1627 const char *name_in
,
1629 bool allow_broken_path
,
1631 bool *ppath_contains_wcard
)
1633 bool path_contains_wcard
;
1634 NTSTATUS status
= NT_STATUS_OK
;
1636 if (dfs_pathnames
) {
1637 status
= dfs_redirect(ctx
,
1643 &path_contains_wcard
);
1645 if (NT_STATUS_IS_OK(status
) && ppath_contains_wcard
!= NULL
) {
1646 *ppath_contains_wcard
= path_contains_wcard
;
1650 * Cheat and just return a copy of the in ptr.
1651 * Once srvstr_get_path() uses talloc it'll
1652 * be a talloced ptr anyway.
1654 *pp_name_out
= discard_const_p(char, name_in
);