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
;
86 * Non-broken DFS paths *must* start with the
87 * path separator. For Windows this is always '\\',
88 * for posix paths this is always '/'.
91 if (*pathname
== '/') {
92 pdp
->posix_path
= true;
95 pdp
->posix_path
= false;
99 if (allow_broken_path
&& (*pathname
!= sepchar
)) {
100 DEBUG(10,("parse_dfs_path: path %s doesn't start with %c\n",
101 pathname
, sepchar
));
103 * Possibly client sent a local path by mistake.
104 * Try and convert to a local path.
105 * Note that this is an SMB1-only fallback
106 * to cope with known broken SMB1 clients.
109 pdp
->hostname
= eos_ptr
; /* "" */
110 pdp
->servicename
= eos_ptr
; /* "" */
112 /* We've got no info about separators. */
113 pdp
->posix_path
= lp_posix_pathnames();
115 DEBUG(10,("parse_dfs_path: trying to convert %s to a "
122 * Safe to use on talloc'ed string as it only shrinks.
123 * It also doesn't affect the eos_ptr.
125 trim_char(temp
,sepchar
,sepchar
);
127 DEBUG(10,("parse_dfs_path: temp = |%s| after trimming %c's\n",
131 /* Parse out hostname. */
132 p
= strchr_m(temp
,sepchar
);
134 DEBUG(10,("parse_dfs_path: can't parse hostname from path %s\n",
137 * Possibly client sent a local path by mistake.
138 * Try and convert to a local path.
141 pdp
->hostname
= eos_ptr
; /* "" */
142 pdp
->servicename
= eos_ptr
; /* "" */
145 DEBUG(10,("parse_dfs_path: trying to convert %s "
151 pdp
->hostname
= temp
;
153 DEBUG(10,("parse_dfs_path: hostname: %s\n",pdp
->hostname
));
155 /* Parse out servicename. */
157 p
= strchr_m(servicename
,sepchar
);
162 /* Is this really our servicename ? */
163 if (conn
&& !( strequal(servicename
, lp_servicename(talloc_tos(), SNUM(conn
)))
164 || (strequal(servicename
, HOMES_NAME
)
165 && strequal(lp_servicename(talloc_tos(), SNUM(conn
)),
166 get_current_username()) )) ) {
167 DEBUG(10,("parse_dfs_path: %s is not our servicename\n",
171 * Possibly client sent a local path by mistake.
172 * Try and convert to a local path.
175 pdp
->hostname
= eos_ptr
; /* "" */
176 pdp
->servicename
= eos_ptr
; /* "" */
178 /* Repair the path - replace the sepchar's
181 *servicename
= sepchar
;
187 DEBUG(10,("parse_dfs_path: trying to convert %s "
193 pdp
->servicename
= servicename
;
195 DEBUG(10,("parse_dfs_path: servicename: %s\n",pdp
->servicename
));
198 /* Client sent self referral \server\share. */
199 pdp
->reqpath
= eos_ptr
; /* "" */
207 *ppath_contains_wcard
= False
;
211 /* Rest is reqpath. */
212 if (pdp
->posix_path
) {
213 status
= check_path_syntax_posix(pdp
->reqpath
);
216 status
= check_path_syntax_wcard(pdp
->reqpath
,
217 ppath_contains_wcard
);
219 status
= check_path_syntax(pdp
->reqpath
);
223 if (!NT_STATUS_IS_OK(status
)) {
224 DEBUG(10,("parse_dfs_path: '%s' failed with %s\n",
225 p
, nt_errstr(status
) ));
229 DEBUG(10,("parse_dfs_path: rest of the path: %s\n",pdp
->reqpath
));
233 /********************************************************
234 Fake up a connection struct for the VFS layer, for use in
235 applications (such as the python bindings), that do not want the
236 global working directory changed under them.
238 SMB_VFS_CONNECT requires root privileges.
239 *********************************************************/
241 static NTSTATUS
create_conn_struct_as_root(TALLOC_CTX
*ctx
,
242 struct tevent_context
*ev
,
243 struct messaging_context
*msg
,
244 connection_struct
**pconn
,
247 const struct auth_session_info
*session_info
)
249 connection_struct
*conn
;
251 const char *vfs_user
;
252 struct smbd_server_connection
*sconn
;
253 const char *servicename
= lp_const_servicename(snum
);
255 sconn
= talloc_zero(ctx
, struct smbd_server_connection
);
257 return NT_STATUS_NO_MEMORY
;
261 sconn
->msg_ctx
= msg
;
263 conn
= conn_new(sconn
);
266 return NT_STATUS_NO_MEMORY
;
269 /* Now we have conn, we need to make sconn a child of conn,
270 * for a proper talloc tree */
271 talloc_steal(conn
, sconn
);
273 if (snum
== -1 && servicename
== NULL
) {
274 servicename
= "Unknown Service (snum == -1)";
277 connpath
= talloc_strdup(conn
, path
);
280 return NT_STATUS_NO_MEMORY
;
282 connpath
= talloc_string_sub(conn
,
288 return NT_STATUS_NO_MEMORY
;
291 /* needed for smbd_vfs_init() */
293 conn
->params
->service
= snum
;
294 conn
->cnum
= TID_FIELD_INVALID
;
296 if (session_info
!= NULL
) {
297 conn
->session_info
= copy_session_info(conn
, session_info
);
298 if (conn
->session_info
== NULL
) {
299 DEBUG(0, ("copy_serverinfo failed\n"));
301 return NT_STATUS_NO_MEMORY
;
303 vfs_user
= conn
->session_info
->unix_info
->unix_name
;
305 /* use current authenticated user in absence of session_info */
306 vfs_user
= get_current_username();
309 set_conn_connectpath(conn
, connpath
);
312 * New code to check if there's a share security descriptor
313 * added from NT server manager. This is done after the
314 * smb.conf checks are done as we need a uid and token. JRA.
317 if (conn
->session_info
) {
318 share_access_check(conn
->session_info
->security_token
,
320 MAXIMUM_ALLOWED_ACCESS
,
321 &conn
->share_access
);
323 if ((conn
->share_access
& FILE_WRITE_DATA
) == 0) {
324 if ((conn
->share_access
& FILE_READ_DATA
) == 0) {
325 /* No access, read or write. */
326 DEBUG(3,("create_conn_struct: connection to %s "
327 "denied due to security "
331 return NT_STATUS_ACCESS_DENIED
;
333 conn
->read_only
= true;
337 conn
->share_access
= 0;
338 conn
->read_only
= true;
341 if (!smbd_vfs_init(conn
)) {
342 NTSTATUS status
= map_nt_error_from_unix(errno
);
343 DEBUG(0,("create_conn_struct: smbd_vfs_init failed.\n"));
348 /* this must be the first filesystem operation that we do */
349 if (SMB_VFS_CONNECT(conn
, servicename
, vfs_user
) < 0) {
350 DEBUG(0,("VFS connect failed!\n"));
352 return NT_STATUS_UNSUCCESSFUL
;
355 conn
->fs_capabilities
= SMB_VFS_FS_CAPABILITIES(conn
, &conn
->ts_res
);
361 /********************************************************
362 Fake up a connection struct for the VFS layer, for use in
363 applications (such as the python bindings), that do not want the
364 global working directory changed under them.
366 SMB_VFS_CONNECT requires root privileges.
367 *********************************************************/
369 NTSTATUS
create_conn_struct(TALLOC_CTX
*ctx
,
370 struct tevent_context
*ev
,
371 struct messaging_context
*msg
,
372 connection_struct
**pconn
,
375 const struct auth_session_info
*session_info
)
379 status
= create_conn_struct_as_root(ctx
, ev
,
388 /********************************************************
389 Fake up a connection struct for the VFS layer.
390 Note: this performs a vfs connect and CHANGES CWD !!!! JRA.
392 The old working directory is returned on *poldcwd, allocated on ctx.
393 *********************************************************/
395 NTSTATUS
create_conn_struct_cwd(TALLOC_CTX
*ctx
,
396 struct tevent_context
*ev
,
397 struct messaging_context
*msg
,
398 connection_struct
**pconn
,
401 const struct auth_session_info
*session_info
,
404 connection_struct
*conn
;
407 NTSTATUS status
= create_conn_struct(ctx
, ev
,
411 if (!NT_STATUS_IS_OK(status
)) {
416 * Windows seems to insist on doing trans2getdfsreferral() calls on
417 * the IPC$ share as the anonymous user. If we try to chdir as that
418 * user we will fail.... WTF ? JRA.
421 oldcwd
= vfs_GetWd(ctx
, conn
);
422 if (oldcwd
== NULL
) {
423 status
= map_nt_error_from_unix(errno
);
424 DEBUG(3, ("vfs_GetWd failed: %s\n", strerror(errno
)));
429 if (vfs_ChDir(conn
,conn
->connectpath
) != 0) {
430 status
= map_nt_error_from_unix(errno
);
431 DEBUG(3,("create_conn_struct: Can't ChDir to new conn path %s. "
433 conn
->connectpath
, strerror(errno
) ));
444 static void shuffle_strlist(char **list
, int count
)
450 for (i
= count
; i
> 1; i
--) {
451 r
= generate_random() % i
;
459 /**********************************************************************
460 Parse the contents of a symlink to verify if it is an msdfs referral
461 A valid referral is of the form:
463 msdfs:server1\share1,server2\share2
464 msdfs:server1\share1\pathname,server2\share2\pathname
465 msdfs:server1/share1,server2/share2
466 msdfs:server1/share1/pathname,server2/share2/pathname.
468 Note that the alternate paths returned here must be of the canonicalized
472 \server\share\path\to\file,
474 even in posix path mode. This is because we have no knowledge if the
475 server we're referring to understands posix paths.
476 **********************************************************************/
478 static bool parse_msdfs_symlink(TALLOC_CTX
*ctx
,
481 struct referral
**preflist
,
486 char **alt_path
= NULL
;
488 struct referral
*reflist
;
491 temp
= talloc_strdup(ctx
, target
);
495 prot
= strtok_r(temp
, ":", &saveptr
);
497 DEBUG(0,("parse_msdfs_symlink: invalid path !\n"));
501 alt_path
= talloc_array(ctx
, char *, MAX_REFERRAL_COUNT
);
506 /* parse out the alternate paths */
507 while((count
<MAX_REFERRAL_COUNT
) &&
508 ((alt_path
[count
] = strtok_r(NULL
, ",", &saveptr
)) != NULL
)) {
512 /* shuffle alternate paths */
513 if (lp_msdfs_shuffle_referrals(snum
)) {
514 shuffle_strlist(alt_path
, count
);
517 DEBUG(10,("parse_msdfs_symlink: count=%d\n", count
));
520 reflist
= *preflist
= talloc_zero_array(ctx
,
521 struct referral
, count
);
522 if(reflist
== NULL
) {
523 TALLOC_FREE(alt_path
);
527 reflist
= *preflist
= NULL
;
530 for(i
=0;i
<count
;i
++) {
533 /* Canonicalize link target.
534 * Replace all /'s in the path by a \ */
535 string_replace(alt_path
[i
], '/', '\\');
537 /* Remove leading '\\'s */
539 while (*p
&& (*p
== '\\')) {
543 reflist
[i
].alternate_path
= talloc_asprintf(ctx
,
546 if (!reflist
[i
].alternate_path
) {
550 reflist
[i
].proximity
= 0;
551 reflist
[i
].ttl
= REFERRAL_TTL
;
552 DEBUG(10, ("parse_msdfs_symlink: Created alt path: %s\n",
553 reflist
[i
].alternate_path
));
558 TALLOC_FREE(alt_path
);
562 /**********************************************************************
563 Returns true if the unix path is a valid msdfs symlink and also
564 returns the target string from inside the link.
565 **********************************************************************/
567 static bool is_msdfs_link_internal(TALLOC_CTX
*ctx
,
568 connection_struct
*conn
,
570 char **pp_link_target
,
571 SMB_STRUCT_STAT
*sbufp
)
573 int referral_len
= 0;
574 #if defined(HAVE_BROKEN_READLINK)
575 char link_target_buf
[PATH_MAX
];
577 char link_target_buf
[7];
580 char *link_target
= NULL
;
581 struct smb_filename smb_fname
;
583 if (pp_link_target
) {
585 link_target
= talloc_array(ctx
, char, bufsize
);
589 *pp_link_target
= link_target
;
591 bufsize
= sizeof(link_target_buf
);
592 link_target
= link_target_buf
;
595 ZERO_STRUCT(smb_fname
);
596 smb_fname
.base_name
= discard_const_p(char, path
);
598 if (SMB_VFS_LSTAT(conn
, &smb_fname
) != 0) {
599 DEBUG(5,("is_msdfs_link_read_target: %s does not exist.\n",
603 if (!S_ISLNK(smb_fname
.st
.st_ex_mode
)) {
604 DEBUG(5,("is_msdfs_link_read_target: %s is not a link.\n",
609 *sbufp
= smb_fname
.st
;
612 referral_len
= SMB_VFS_READLINK(conn
, path
, link_target
, bufsize
- 1);
613 if (referral_len
== -1) {
614 DEBUG(0,("is_msdfs_link_read_target: Error reading "
615 "msdfs link %s: %s\n",
616 path
, strerror(errno
)));
619 link_target
[referral_len
] = '\0';
621 DEBUG(5,("is_msdfs_link_internal: %s -> %s\n",path
,
624 if (!strnequal(link_target
, "msdfs:", 6)) {
631 if (link_target
!= link_target_buf
) {
632 TALLOC_FREE(link_target
);
637 /**********************************************************************
638 Returns true if the unix path is a valid msdfs symlink.
639 **********************************************************************/
641 bool is_msdfs_link(connection_struct
*conn
,
643 SMB_STRUCT_STAT
*sbufp
)
645 return is_msdfs_link_internal(talloc_tos(),
652 /*****************************************************************
653 Used by other functions to decide if a dfs path is remote,
654 and to get the list of referred locations for that remote path.
656 search_flag: For findfirsts, dfs links themselves are not
657 redirected, but paths beyond the links are. For normal smb calls,
658 even dfs links need to be redirected.
660 consumedcntp: how much of the dfs path is being redirected. the client
661 should try the remaining path on the redirected server.
663 If this returns NT_STATUS_PATH_NOT_COVERED the contents of the msdfs
664 link redirect are in targetpath.
665 *****************************************************************/
667 static NTSTATUS
dfs_path_lookup(TALLOC_CTX
*ctx
,
668 connection_struct
*conn
,
669 const char *dfspath
, /* Incoming complete dfs path */
670 const struct dfs_path
*pdp
, /* Parsed out
671 server+share+extrapath. */
674 char **pp_targetpath
)
679 struct smb_filename
*smb_fname
= NULL
;
680 char *canon_dfspath
= NULL
; /* Canonicalized dfs path. (only '/'
683 DEBUG(10,("dfs_path_lookup: Conn path = %s reqpath = %s\n",
684 conn
->connectpath
, pdp
->reqpath
));
687 * Note the unix path conversion here we're doing we
688 * throw away. We're looking for a symlink for a dfs
689 * resolution, if we don't find it we'll do another
690 * unix_convert later in the codepath.
693 status
= unix_convert(ctx
, conn
, pdp
->reqpath
, &smb_fname
,
696 if (!NT_STATUS_IS_OK(status
)) {
697 if (!NT_STATUS_EQUAL(status
,
698 NT_STATUS_OBJECT_PATH_NOT_FOUND
)) {
701 if (smb_fname
== NULL
|| smb_fname
->base_name
== NULL
) {
706 /* Optimization - check if we can redirect the whole path. */
708 if (is_msdfs_link_internal(ctx
, conn
, smb_fname
->base_name
,
709 pp_targetpath
, NULL
)) {
710 /* XX_ALLOW_WCARD_XXX is called from search functions. */
712 (UCF_COND_ALLOW_WCARD_LCOMP
|
713 UCF_ALWAYS_ALLOW_WCARD_LCOMP
)) {
714 DEBUG(6,("dfs_path_lookup (FindFirst) No redirection "
715 "for dfs link %s.\n", dfspath
));
716 status
= NT_STATUS_OK
;
720 DEBUG(6,("dfs_path_lookup: %s resolves to a "
721 "valid dfs link %s.\n", dfspath
,
722 pp_targetpath
? *pp_targetpath
: ""));
725 *consumedcntp
= strlen(dfspath
);
727 status
= NT_STATUS_PATH_NOT_COVERED
;
731 /* Prepare to test only for '/' components in the given path,
732 * so if a Windows path replace all '\\' characters with '/'.
733 * For a POSIX DFS path we know all separators are already '/'. */
735 canon_dfspath
= talloc_strdup(ctx
, dfspath
);
736 if (!canon_dfspath
) {
737 status
= NT_STATUS_NO_MEMORY
;
740 if (!pdp
->posix_path
) {
741 string_replace(canon_dfspath
, '\\', '/');
745 * localpath comes out of unix_convert, so it has
746 * no trailing backslash. Make sure that canon_dfspath hasn't either.
747 * Fix for bug #4860 from Jan Martin <Jan.Martin@rwedea.com>.
750 trim_char(canon_dfspath
,0,'/');
753 * Redirect if any component in the path is a link.
754 * We do this by walking backwards through the
755 * local path, chopping off the last component
756 * in both the local path and the canonicalized
757 * DFS path. If we hit a DFS link then we're done.
760 p
= strrchr_m(smb_fname
->base_name
, '/');
762 q
= strrchr_m(canon_dfspath
, '/');
771 if (is_msdfs_link_internal(ctx
, conn
,
772 smb_fname
->base_name
, pp_targetpath
,
774 DEBUG(4, ("dfs_path_lookup: Redirecting %s because "
775 "parent %s is dfs link\n", dfspath
,
776 smb_fname_str_dbg(smb_fname
)));
779 *consumedcntp
= strlen(canon_dfspath
);
780 DEBUG(10, ("dfs_path_lookup: Path consumed: %s "
786 status
= NT_STATUS_PATH_NOT_COVERED
;
790 /* Step back on the filesystem. */
791 p
= strrchr_m(smb_fname
->base_name
, '/');
794 /* And in the canonicalized dfs path. */
795 q
= strrchr_m(canon_dfspath
, '/');
799 status
= NT_STATUS_OK
;
801 TALLOC_FREE(smb_fname
);
805 /*****************************************************************
806 Decides if a dfs pathname should be redirected or not.
807 If not, the pathname is converted to a tcon-relative local unix path
809 search_wcard_flag: this flag performs 2 functions both related
810 to searches. See resolve_dfs_path() and parse_dfs_path_XX()
813 This function can return NT_STATUS_OK, meaning use the returned path as-is
814 (mapped into a local path).
815 or NT_STATUS_NOT_COVERED meaning return a DFS redirect, or
816 any other NT_STATUS error which is a genuine error to be
817 returned to the client.
818 *****************************************************************/
820 static NTSTATUS
dfs_redirect(TALLOC_CTX
*ctx
,
821 connection_struct
*conn
,
824 bool allow_broken_path
,
826 bool *ppath_contains_wcard
)
829 bool search_wcard_flag
= (ucf_flags
&
830 (UCF_COND_ALLOW_WCARD_LCOMP
|UCF_ALWAYS_ALLOW_WCARD_LCOMP
));
831 struct dfs_path
*pdp
= talloc(ctx
, struct dfs_path
);
834 return NT_STATUS_NO_MEMORY
;
837 status
= parse_dfs_path(conn
, path_in
, search_wcard_flag
,
838 allow_broken_path
, pdp
,
839 ppath_contains_wcard
);
840 if (!NT_STATUS_IS_OK(status
)) {
845 if (pdp
->reqpath
[0] == '\0') {
847 *pp_path_out
= talloc_strdup(ctx
, "");
849 return NT_STATUS_NO_MEMORY
;
851 DEBUG(5,("dfs_redirect: self-referral.\n"));
855 /* If dfs pathname for a non-dfs share, convert to tcon-relative
856 path and return OK */
858 if (!lp_msdfs_root(SNUM(conn
))) {
859 *pp_path_out
= talloc_strdup(ctx
, pdp
->reqpath
);
862 return NT_STATUS_NO_MEMORY
;
867 /* If it looked like a local path (zero hostname/servicename)
868 * just treat as a tcon-relative path. */
870 if (pdp
->hostname
[0] == '\0' && pdp
->servicename
[0] == '\0') {
871 *pp_path_out
= talloc_strdup(ctx
, pdp
->reqpath
);
874 return NT_STATUS_NO_MEMORY
;
879 if (!( strequal(pdp
->servicename
, lp_servicename(talloc_tos(), SNUM(conn
)))
880 || (strequal(pdp
->servicename
, HOMES_NAME
)
881 && strequal(lp_servicename(talloc_tos(), SNUM(conn
)),
882 conn
->session_info
->unix_info
->sanitized_username
) )) ) {
884 /* The given sharename doesn't match this connection. */
887 return NT_STATUS_OBJECT_PATH_NOT_FOUND
;
890 status
= dfs_path_lookup(ctx
, conn
, path_in
, pdp
,
891 search_wcard_flag
, NULL
, NULL
);
892 if (!NT_STATUS_IS_OK(status
)) {
893 if (NT_STATUS_EQUAL(status
, NT_STATUS_PATH_NOT_COVERED
)) {
894 DEBUG(3,("dfs_redirect: Redirecting %s\n", path_in
));
896 DEBUG(10,("dfs_redirect: dfs_path_lookup "
897 "failed for %s with %s\n",
898 path_in
, nt_errstr(status
) ));
903 DEBUG(3,("dfs_redirect: Not redirecting %s.\n", path_in
));
905 /* Form non-dfs tcon-relative path */
906 *pp_path_out
= talloc_strdup(ctx
, pdp
->reqpath
);
909 return NT_STATUS_NO_MEMORY
;
912 DEBUG(3,("dfs_redirect: Path %s converted to non-dfs path %s\n",
919 /**********************************************************************
920 Return a self referral.
921 **********************************************************************/
923 static NTSTATUS
self_ref(TALLOC_CTX
*ctx
,
924 const char *dfs_path
,
925 struct junction_map
*jucn
,
927 bool *self_referralp
)
929 struct referral
*ref
;
931 *self_referralp
= True
;
933 jucn
->referral_count
= 1;
934 if((ref
= talloc_zero(ctx
, struct referral
)) == NULL
) {
935 return NT_STATUS_NO_MEMORY
;
938 ref
->alternate_path
= talloc_strdup(ctx
, dfs_path
);
939 if (!ref
->alternate_path
) {
941 return NT_STATUS_NO_MEMORY
;
944 ref
->ttl
= REFERRAL_TTL
;
945 jucn
->referral_list
= ref
;
946 *consumedcntp
= strlen(dfs_path
);
950 /**********************************************************************
951 Gets valid referrals for a dfs path and fills up the
952 junction_map structure.
953 **********************************************************************/
955 NTSTATUS
get_referred_path(TALLOC_CTX
*ctx
,
956 const char *dfs_path
,
957 bool allow_broken_path
,
958 struct junction_map
*jucn
,
960 bool *self_referralp
)
962 struct connection_struct
*conn
;
963 char *targetpath
= NULL
;
965 NTSTATUS status
= NT_STATUS_NOT_FOUND
;
967 struct dfs_path
*pdp
= talloc(ctx
, struct dfs_path
);
971 return NT_STATUS_NO_MEMORY
;
974 *self_referralp
= False
;
976 status
= parse_dfs_path(NULL
, dfs_path
, False
, allow_broken_path
,
978 if (!NT_STATUS_IS_OK(status
)) {
982 jucn
->service_name
= talloc_strdup(ctx
, pdp
->servicename
);
983 jucn
->volume_name
= talloc_strdup(ctx
, pdp
->reqpath
);
984 if (!jucn
->service_name
|| !jucn
->volume_name
) {
986 return NT_STATUS_NO_MEMORY
;
989 /* Verify the share is a dfs root */
990 snum
= lp_servicenumber(jucn
->service_name
);
992 char *service_name
= NULL
;
993 if ((snum
= find_service(ctx
, jucn
->service_name
, &service_name
)) < 0) {
994 return NT_STATUS_NOT_FOUND
;
997 return NT_STATUS_NO_MEMORY
;
999 TALLOC_FREE(jucn
->service_name
);
1000 jucn
->service_name
= talloc_strdup(ctx
, service_name
);
1001 if (!jucn
->service_name
) {
1003 return NT_STATUS_NO_MEMORY
;
1007 if (!lp_msdfs_root(snum
) && (*lp_msdfs_proxy(talloc_tos(), snum
) == '\0')) {
1008 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not "
1010 pdp
->servicename
, dfs_path
));
1012 return NT_STATUS_NOT_FOUND
;
1016 * Self referrals are tested with a anonymous IPC connection and
1017 * a GET_DFS_REFERRAL call to \\server\share. (which means
1018 * dp.reqpath[0] points to an empty string). create_conn_struct cd's
1019 * into the directory and will fail if it cannot (as the anonymous
1020 * user). Cope with this.
1023 if (pdp
->reqpath
[0] == '\0') {
1025 struct referral
*ref
;
1028 if (*lp_msdfs_proxy(talloc_tos(), snum
) == '\0') {
1030 return self_ref(ctx
,
1038 * It's an msdfs proxy share. Redirect to
1039 * the configured target share.
1042 tmp
= talloc_asprintf(talloc_tos(), "msdfs:%s",
1043 lp_msdfs_proxy(talloc_tos(), snum
));
1046 return NT_STATUS_NO_MEMORY
;
1049 if (!parse_msdfs_symlink(ctx
, snum
, tmp
, &ref
, &refcount
)) {
1052 return NT_STATUS_INVALID_PARAMETER
;
1055 jucn
->referral_count
= refcount
;
1056 jucn
->referral_list
= ref
;
1057 *consumedcntp
= strlen(dfs_path
);
1059 return NT_STATUS_OK
;
1062 status
= create_conn_struct_cwd(ctx
,
1063 server_event_context(),
1064 server_messaging_context(),
1066 lp_path(talloc_tos(), snum
), NULL
, &oldpath
);
1067 if (!NT_STATUS_IS_OK(status
)) {
1072 /* If this is a DFS path dfs_lookup should return
1073 * NT_STATUS_PATH_NOT_COVERED. */
1075 status
= dfs_path_lookup(ctx
, conn
, dfs_path
, pdp
,
1076 False
, consumedcntp
, &targetpath
);
1078 if (!NT_STATUS_EQUAL(status
, NT_STATUS_PATH_NOT_COVERED
)) {
1079 DEBUG(3,("get_referred_path: No valid referrals for path %s\n",
1081 if (NT_STATUS_IS_OK(status
)) {
1083 * We are in an error path here (we
1084 * know it's not a DFS path), but
1085 * dfs_path_lookup() can return
1086 * NT_STATUS_OK. Ensure we always
1087 * return a valid error code.
1089 * #9588 - ACLs are not inherited to directories
1092 status
= NT_STATUS_NOT_FOUND
;
1097 /* We know this is a valid dfs link. Parse the targetpath. */
1098 if (!parse_msdfs_symlink(ctx
, snum
, targetpath
,
1099 &jucn
->referral_list
,
1100 &jucn
->referral_count
)) {
1101 DEBUG(3,("get_referred_path: failed to parse symlink "
1102 "target %s\n", targetpath
));
1103 status
= NT_STATUS_NOT_FOUND
;
1107 status
= NT_STATUS_OK
;
1109 vfs_ChDir(conn
, oldpath
);
1110 SMB_VFS_DISCONNECT(conn
);
1116 /******************************************************************
1117 Set up the DFS referral for the dfs pathname. This call returns
1118 the amount of the path covered by this server, and where the
1119 client should be redirected to. This is the meat of the
1120 TRANS2_GET_DFS_REFERRAL call.
1121 ******************************************************************/
1123 int setup_dfs_referral(connection_struct
*orig_conn
,
1124 const char *dfs_path
,
1125 int max_referral_level
,
1126 char **ppdata
, NTSTATUS
*pstatus
)
1128 char *pdata
= *ppdata
;
1130 struct dfs_GetDFSReferral
*r
;
1131 DATA_BLOB blob
= data_blob_null
;
1133 enum ndr_err_code ndr_err
;
1135 r
= talloc_zero(talloc_tos(), struct dfs_GetDFSReferral
);
1137 *pstatus
= NT_STATUS_NO_MEMORY
;
1141 r
->in
.req
.max_referral_level
= max_referral_level
;
1142 r
->in
.req
.servername
= talloc_strdup(r
, dfs_path
);
1143 if (r
->in
.req
.servername
== NULL
) {
1145 *pstatus
= NT_STATUS_NO_MEMORY
;
1149 status
= SMB_VFS_GET_DFS_REFERRALS(orig_conn
, r
);
1150 if (!NT_STATUS_IS_OK(status
)) {
1156 ndr_err
= ndr_push_struct_blob(&blob
, r
,
1158 (ndr_push_flags_fn_t
)ndr_push_dfs_referral_resp
);
1159 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
1161 *pstatus
= NT_STATUS_INVALID_PARAMETER
;
1165 pdata
= (char *)SMB_REALLOC(pdata
, blob
.length
);
1168 DEBUG(0,("referral setup:"
1169 "malloc failed for Realloc!\n"));
1173 reply_size
= blob
.length
;
1174 memcpy(pdata
, blob
.data
, blob
.length
);
1177 *pstatus
= NT_STATUS_OK
;
1181 /**********************************************************************
1182 The following functions are called by the NETDFS RPC pipe functions
1183 **********************************************************************/
1185 /*********************************************************************
1186 Creates a junction structure from a DFS pathname
1187 **********************************************************************/
1189 bool create_junction(TALLOC_CTX
*ctx
,
1190 const char *dfs_path
,
1191 bool allow_broken_path
,
1192 struct junction_map
*jucn
)
1196 struct dfs_path
*pdp
= talloc(ctx
,struct dfs_path
);
1202 status
= parse_dfs_path(NULL
, dfs_path
, False
, allow_broken_path
,
1204 if (!NT_STATUS_IS_OK(status
)) {
1208 /* check if path is dfs : validate first token */
1209 if (!is_myname_or_ipaddr(pdp
->hostname
)) {
1210 DEBUG(4,("create_junction: Invalid hostname %s "
1212 pdp
->hostname
, dfs_path
));
1217 /* Check for a non-DFS share */
1218 snum
= lp_servicenumber(pdp
->servicename
);
1220 if(snum
< 0 || !lp_msdfs_root(snum
)) {
1221 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1227 jucn
->service_name
= talloc_strdup(ctx
, pdp
->servicename
);
1228 jucn
->volume_name
= talloc_strdup(ctx
, pdp
->reqpath
);
1229 jucn
->comment
= lp_comment(ctx
, snum
);
1232 if (!jucn
->service_name
|| !jucn
->volume_name
|| ! jucn
->comment
) {
1238 /**********************************************************************
1239 Forms a valid Unix pathname from the junction
1240 **********************************************************************/
1242 static bool junction_to_local_path(const struct junction_map
*jucn
,
1244 connection_struct
**conn_out
,
1250 snum
= lp_servicenumber(jucn
->service_name
);
1254 status
= create_conn_struct_cwd(talloc_tos(),
1255 server_event_context(),
1256 server_messaging_context(),
1258 snum
, lp_path(talloc_tos(), snum
), NULL
, oldpath
);
1259 if (!NT_STATUS_IS_OK(status
)) {
1263 *pp_path_out
= talloc_asprintf(*conn_out
,
1265 lp_path(talloc_tos(), snum
),
1267 if (!*pp_path_out
) {
1268 vfs_ChDir(*conn_out
, *oldpath
);
1269 SMB_VFS_DISCONNECT(*conn_out
);
1270 conn_free(*conn_out
);
1276 bool create_msdfs_link(const struct junction_map
*jucn
)
1280 char *msdfs_link
= NULL
;
1281 connection_struct
*conn
;
1283 bool insert_comma
= False
;
1286 if(!junction_to_local_path(jucn
, &path
, &conn
, &cwd
)) {
1290 /* Form the msdfs_link contents */
1291 msdfs_link
= talloc_strdup(conn
, "msdfs:");
1295 for(i
=0; i
<jucn
->referral_count
; i
++) {
1296 char *refpath
= jucn
->referral_list
[i
].alternate_path
;
1298 /* Alternate paths always use Windows separators. */
1299 trim_char(refpath
, '\\', '\\');
1300 if(*refpath
== '\0') {
1302 insert_comma
= False
;
1306 if (i
> 0 && insert_comma
) {
1307 msdfs_link
= talloc_asprintf_append_buffer(msdfs_link
,
1311 msdfs_link
= talloc_asprintf_append_buffer(msdfs_link
,
1319 if (!insert_comma
) {
1320 insert_comma
= True
;
1324 DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
1327 if(SMB_VFS_SYMLINK(conn
, msdfs_link
, path
) < 0) {
1328 if (errno
== EEXIST
) {
1329 struct smb_filename
*smb_fname
;
1331 smb_fname
= synthetic_smb_fname(talloc_tos(),
1336 if (smb_fname
== NULL
) {
1341 if(SMB_VFS_UNLINK(conn
, smb_fname
)!=0) {
1342 TALLOC_FREE(smb_fname
);
1345 TALLOC_FREE(smb_fname
);
1347 if (SMB_VFS_SYMLINK(conn
, msdfs_link
, path
) < 0) {
1348 DEBUG(1,("create_msdfs_link: symlink failed "
1349 "%s -> %s\nError: %s\n",
1350 path
, msdfs_link
, strerror(errno
)));
1358 vfs_ChDir(conn
, cwd
);
1359 SMB_VFS_DISCONNECT(conn
);
1364 bool remove_msdfs_link(const struct junction_map
*jucn
)
1368 connection_struct
*conn
;
1370 struct smb_filename
*smb_fname
;
1372 if (!junction_to_local_path(jucn
, &path
, &conn
, &cwd
)) {
1376 smb_fname
= synthetic_smb_fname(talloc_tos(),
1381 if (smb_fname
== NULL
) {
1386 if( SMB_VFS_UNLINK(conn
, smb_fname
) == 0 ) {
1390 TALLOC_FREE(smb_fname
);
1391 vfs_ChDir(conn
, cwd
);
1392 SMB_VFS_DISCONNECT(conn
);
1397 /*********************************************************************
1398 Return the number of DFS links at the root of this share.
1399 *********************************************************************/
1401 static int count_dfs_links(TALLOC_CTX
*ctx
, int snum
)
1405 const char *dname
= NULL
;
1406 char *talloced
= NULL
;
1407 const char *connect_path
= lp_path(talloc_tos(), snum
);
1408 const char *msdfs_proxy
= lp_msdfs_proxy(talloc_tos(), snum
);
1409 connection_struct
*conn
;
1412 struct smb_filename
*smb_fname
= NULL
;
1414 if(*connect_path
== '\0') {
1419 * Fake up a connection struct for the VFS layer.
1422 status
= create_conn_struct_cwd(talloc_tos(),
1423 server_event_context(),
1424 server_messaging_context(),
1426 snum
, connect_path
, NULL
, &cwd
);
1427 if (!NT_STATUS_IS_OK(status
)) {
1428 DEBUG(3, ("create_conn_struct failed: %s\n",
1429 nt_errstr(status
)));
1433 /* Count a link for the msdfs root - convention */
1436 /* No more links if this is an msdfs proxy. */
1437 if (*msdfs_proxy
!= '\0') {
1441 smb_fname
= synthetic_smb_fname(talloc_tos(),
1446 if (smb_fname
== NULL
) {
1450 /* Now enumerate all dfs links */
1451 dirp
= SMB_VFS_OPENDIR(conn
, smb_fname
, NULL
, 0);
1456 while ((dname
= vfs_readdirname(conn
, dirp
, NULL
, &talloced
))
1458 if (is_msdfs_link(conn
,
1463 TALLOC_FREE(talloced
);
1466 SMB_VFS_CLOSEDIR(conn
,dirp
);
1469 TALLOC_FREE(smb_fname
);
1470 vfs_ChDir(conn
, cwd
);
1471 SMB_VFS_DISCONNECT(conn
);
1476 /*********************************************************************
1477 *********************************************************************/
1479 static int form_junctions(TALLOC_CTX
*ctx
,
1481 struct junction_map
*jucn
,
1486 const char *dname
= NULL
;
1487 char *talloced
= NULL
;
1488 const char *connect_path
= lp_path(talloc_tos(), snum
);
1489 char *service_name
= lp_servicename(talloc_tos(), snum
);
1490 const char *msdfs_proxy
= lp_msdfs_proxy(talloc_tos(), snum
);
1491 connection_struct
*conn
;
1492 struct referral
*ref
= NULL
;
1494 struct smb_filename
*smb_fname
= NULL
;
1497 if (jn_remain
== 0) {
1501 if(*connect_path
== '\0') {
1506 * Fake up a connection struct for the VFS layer.
1509 status
= create_conn_struct_cwd(ctx
,
1510 server_event_context(),
1511 server_messaging_context(),
1512 &conn
, snum
, connect_path
, NULL
,
1514 if (!NT_STATUS_IS_OK(status
)) {
1515 DEBUG(3, ("create_conn_struct failed: %s\n",
1516 nt_errstr(status
)));
1520 /* form a junction for the msdfs root - convention
1521 DO NOT REMOVE THIS: NT clients will not work with us
1522 if this is not present
1524 jucn
[cnt
].service_name
= talloc_strdup(ctx
,service_name
);
1525 jucn
[cnt
].volume_name
= talloc_strdup(ctx
, "");
1526 if (!jucn
[cnt
].service_name
|| !jucn
[cnt
].volume_name
) {
1529 jucn
[cnt
].comment
= "";
1530 jucn
[cnt
].referral_count
= 1;
1532 ref
= jucn
[cnt
].referral_list
= talloc_zero(ctx
, struct referral
);
1533 if (jucn
[cnt
].referral_list
== NULL
) {
1538 ref
->ttl
= REFERRAL_TTL
;
1539 if (*msdfs_proxy
!= '\0') {
1540 ref
->alternate_path
= talloc_strdup(ctx
,
1543 ref
->alternate_path
= talloc_asprintf(ctx
,
1545 get_local_machine_name(),
1549 if (!ref
->alternate_path
) {
1554 /* Don't enumerate if we're an msdfs proxy. */
1555 if (*msdfs_proxy
!= '\0') {
1559 smb_fname
= synthetic_smb_fname(talloc_tos(),
1564 if (smb_fname
== NULL
) {
1568 /* Now enumerate all dfs links */
1569 dirp
= SMB_VFS_OPENDIR(conn
, smb_fname
, NULL
, 0);
1574 while ((dname
= vfs_readdirname(conn
, dirp
, NULL
, &talloced
))
1576 char *link_target
= NULL
;
1577 if (cnt
>= jn_remain
) {
1578 DEBUG(2, ("form_junctions: ran out of MSDFS "
1580 TALLOC_FREE(talloced
);
1583 if (is_msdfs_link_internal(ctx
,
1585 dname
, &link_target
,
1587 if (parse_msdfs_symlink(ctx
, snum
,
1589 &jucn
[cnt
].referral_list
,
1590 &jucn
[cnt
].referral_count
)) {
1592 jucn
[cnt
].service_name
= talloc_strdup(ctx
,
1594 jucn
[cnt
].volume_name
= talloc_strdup(ctx
,
1596 if (!jucn
[cnt
].service_name
||
1597 !jucn
[cnt
].volume_name
) {
1598 TALLOC_FREE(talloced
);
1601 jucn
[cnt
].comment
= "";
1604 TALLOC_FREE(link_target
);
1606 TALLOC_FREE(talloced
);
1612 SMB_VFS_CLOSEDIR(conn
,dirp
);
1615 TALLOC_FREE(smb_fname
);
1616 vfs_ChDir(conn
, cwd
);
1621 struct junction_map
*enum_msdfs_links(TALLOC_CTX
*ctx
, size_t *p_num_jn
)
1623 struct junction_map
*jn
= NULL
;
1625 size_t jn_count
= 0;
1629 if(!lp_host_msdfs()) {
1633 /* Ensure all the usershares are loaded. */
1635 load_registry_shares();
1636 sharecount
= load_usershare_shares(NULL
, connections_snum_used
);
1639 for(i
=0;i
< sharecount
;i
++) {
1640 if(lp_msdfs_root(i
)) {
1641 jn_count
+= count_dfs_links(ctx
, i
);
1644 if (jn_count
== 0) {
1647 jn
= talloc_array(ctx
, struct junction_map
, jn_count
);
1651 for(i
=0; i
< sharecount
; i
++) {
1652 if (*p_num_jn
>= jn_count
) {
1655 if(lp_msdfs_root(i
)) {
1656 *p_num_jn
+= form_junctions(ctx
, i
,
1658 jn_count
- *p_num_jn
);
1664 /******************************************************************************
1665 Core function to resolve a dfs pathname possibly containing a wildcard. If
1666 ppath_contains_wcard != NULL, it will be set to true if a wildcard is
1667 detected during dfs resolution.
1668 ******************************************************************************/
1670 NTSTATUS
resolve_dfspath_wcard(TALLOC_CTX
*ctx
,
1671 connection_struct
*conn
,
1673 const char *name_in
,
1675 bool allow_broken_path
,
1677 bool *ppath_contains_wcard
)
1679 bool path_contains_wcard
;
1680 NTSTATUS status
= NT_STATUS_OK
;
1682 if (dfs_pathnames
) {
1683 status
= dfs_redirect(ctx
,
1689 &path_contains_wcard
);
1691 if (NT_STATUS_IS_OK(status
) && ppath_contains_wcard
!= NULL
) {
1692 *ppath_contains_wcard
= path_contains_wcard
;
1696 * Cheat and just return a copy of the in ptr.
1697 * Once srvstr_get_path() uses talloc it'll
1698 * be a talloced ptr anyway.
1700 *pp_name_out
= discard_const_p(char, name_in
);