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.
224 *********************************************************/
226 NTSTATUS
create_conn_struct(TALLOC_CTX
*ctx
,
227 struct tevent_context
*ev
,
228 struct messaging_context
*msg
,
229 connection_struct
**pconn
,
232 const struct auth_session_info
*session_info
)
234 connection_struct
*conn
;
236 const char *vfs_user
;
237 struct smbd_server_connection
*sconn
;
238 const char *servicename
= lp_const_servicename(snum
);
240 sconn
= talloc_zero(ctx
, struct smbd_server_connection
);
242 return NT_STATUS_NO_MEMORY
;
246 sconn
->msg_ctx
= msg
;
248 sconn
->smb1
.echo_handler
.trusted_fd
= -1;
249 sconn
->smb1
.echo_handler
.socket_lock_fd
= -1;
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 descripter
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(0,("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.
351 Note: this performs a vfs connect and CHANGES CWD !!!! JRA.
353 The old working directory is returned on *poldcwd, allocated on ctx.
354 *********************************************************/
356 NTSTATUS
create_conn_struct_cwd(TALLOC_CTX
*ctx
,
357 struct tevent_context
*ev
,
358 struct messaging_context
*msg
,
359 connection_struct
**pconn
,
362 const struct auth_session_info
*session_info
,
365 connection_struct
*conn
;
368 NTSTATUS status
= create_conn_struct(ctx
, ev
,
372 if (!NT_STATUS_IS_OK(status
)) {
377 * Windows seems to insist on doing trans2getdfsreferral() calls on
378 * the IPC$ share as the anonymous user. If we try to chdir as that
379 * user we will fail.... WTF ? JRA.
382 oldcwd
= vfs_GetWd(ctx
, conn
);
383 if (oldcwd
== NULL
) {
384 status
= map_nt_error_from_unix(errno
);
385 DEBUG(3, ("vfs_GetWd failed: %s\n", strerror(errno
)));
390 if (vfs_ChDir(conn
,conn
->connectpath
) != 0) {
391 status
= map_nt_error_from_unix(errno
);
392 DEBUG(3,("create_conn_struct: Can't ChDir to new conn path %s. "
394 conn
->connectpath
, strerror(errno
) ));
405 /**********************************************************************
406 Parse the contents of a symlink to verify if it is an msdfs referral
407 A valid referral is of the form:
409 msdfs:server1\share1,server2\share2
410 msdfs:server1\share1\pathname,server2\share2\pathname
411 msdfs:server1/share1,server2/share2
412 msdfs:server1/share1/pathname,server2/share2/pathname.
414 Note that the alternate paths returned here must be of the canonicalized
418 \server\share\path\to\file,
420 even in posix path mode. This is because we have no knowledge if the
421 server we're referring to understands posix paths.
422 **********************************************************************/
424 static bool parse_msdfs_symlink(TALLOC_CTX
*ctx
,
426 struct referral
**preflist
,
431 char **alt_path
= NULL
;
433 struct referral
*reflist
;
436 temp
= talloc_strdup(ctx
, target
);
440 prot
= strtok_r(temp
, ":", &saveptr
);
442 DEBUG(0,("parse_msdfs_symlink: invalid path !\n"));
446 alt_path
= talloc_array(ctx
, char *, MAX_REFERRAL_COUNT
);
451 /* parse out the alternate paths */
452 while((count
<MAX_REFERRAL_COUNT
) &&
453 ((alt_path
[count
] = strtok_r(NULL
, ",", &saveptr
)) != NULL
)) {
457 DEBUG(10,("parse_msdfs_symlink: count=%d\n", count
));
460 reflist
= *preflist
= talloc_zero_array(ctx
,
461 struct referral
, count
);
462 if(reflist
== NULL
) {
463 TALLOC_FREE(alt_path
);
467 reflist
= *preflist
= NULL
;
470 for(i
=0;i
<count
;i
++) {
473 /* Canonicalize link target.
474 * Replace all /'s in the path by a \ */
475 string_replace(alt_path
[i
], '/', '\\');
477 /* Remove leading '\\'s */
479 while (*p
&& (*p
== '\\')) {
483 reflist
[i
].alternate_path
= talloc_asprintf(ctx
,
486 if (!reflist
[i
].alternate_path
) {
490 reflist
[i
].proximity
= 0;
491 reflist
[i
].ttl
= REFERRAL_TTL
;
492 DEBUG(10, ("parse_msdfs_symlink: Created alt path: %s\n",
493 reflist
[i
].alternate_path
));
498 TALLOC_FREE(alt_path
);
502 /**********************************************************************
503 Returns true if the unix path is a valid msdfs symlink and also
504 returns the target string from inside the link.
505 **********************************************************************/
507 static bool is_msdfs_link_internal(TALLOC_CTX
*ctx
,
508 connection_struct
*conn
,
510 char **pp_link_target
,
511 SMB_STRUCT_STAT
*sbufp
)
513 int referral_len
= 0;
514 #if defined(HAVE_BROKEN_READLINK)
515 char link_target_buf
[PATH_MAX
];
517 char link_target_buf
[7];
520 char *link_target
= NULL
;
521 struct smb_filename smb_fname
;
523 if (pp_link_target
) {
525 link_target
= talloc_array(ctx
, char, bufsize
);
529 *pp_link_target
= link_target
;
531 bufsize
= sizeof(link_target_buf
);
532 link_target
= link_target_buf
;
535 ZERO_STRUCT(smb_fname
);
536 smb_fname
.base_name
= discard_const_p(char, path
);
538 if (SMB_VFS_LSTAT(conn
, &smb_fname
) != 0) {
539 DEBUG(5,("is_msdfs_link_read_target: %s does not exist.\n",
543 if (!S_ISLNK(smb_fname
.st
.st_ex_mode
)) {
544 DEBUG(5,("is_msdfs_link_read_target: %s is not a link.\n",
549 *sbufp
= smb_fname
.st
;
552 referral_len
= SMB_VFS_READLINK(conn
, path
, link_target
, bufsize
- 1);
553 if (referral_len
== -1) {
554 DEBUG(0,("is_msdfs_link_read_target: Error reading "
555 "msdfs link %s: %s\n",
556 path
, strerror(errno
)));
559 link_target
[referral_len
] = '\0';
561 DEBUG(5,("is_msdfs_link_internal: %s -> %s\n",path
,
564 if (!strnequal(link_target
, "msdfs:", 6)) {
571 if (link_target
!= link_target_buf
) {
572 TALLOC_FREE(link_target
);
577 /**********************************************************************
578 Returns true if the unix path is a valid msdfs symlink.
579 **********************************************************************/
581 bool is_msdfs_link(connection_struct
*conn
,
583 SMB_STRUCT_STAT
*sbufp
)
585 return is_msdfs_link_internal(talloc_tos(),
592 /*****************************************************************
593 Used by other functions to decide if a dfs path is remote,
594 and to get the list of referred locations for that remote path.
596 search_flag: For findfirsts, dfs links themselves are not
597 redirected, but paths beyond the links are. For normal smb calls,
598 even dfs links need to be redirected.
600 consumedcntp: how much of the dfs path is being redirected. the client
601 should try the remaining path on the redirected server.
603 If this returns NT_STATUS_PATH_NOT_COVERED the contents of the msdfs
604 link redirect are in targetpath.
605 *****************************************************************/
607 static NTSTATUS
dfs_path_lookup(TALLOC_CTX
*ctx
,
608 connection_struct
*conn
,
609 const char *dfspath
, /* Incoming complete dfs path */
610 const struct dfs_path
*pdp
, /* Parsed out
611 server+share+extrapath. */
612 bool search_flag
, /* Called from a findfirst ? */
614 char **pp_targetpath
)
619 struct smb_filename
*smb_fname
= NULL
;
620 char *canon_dfspath
= NULL
; /* Canonicalized dfs path. (only '/'
623 DEBUG(10,("dfs_path_lookup: Conn path = %s reqpath = %s\n",
624 conn
->connectpath
, pdp
->reqpath
));
627 * Note the unix path conversion here we're doing we
628 * throw away. We're looking for a symlink for a dfs
629 * resolution, if we don't find it we'll do another
630 * unix_convert later in the codepath.
633 status
= unix_convert(ctx
, conn
, pdp
->reqpath
, &smb_fname
,
634 search_flag
? UCF_ALWAYS_ALLOW_WCARD_LCOMP
: 0);
636 if (!NT_STATUS_IS_OK(status
)) {
637 if (!NT_STATUS_EQUAL(status
,
638 NT_STATUS_OBJECT_PATH_NOT_FOUND
)) {
641 if (smb_fname
== NULL
|| smb_fname
->base_name
== NULL
) {
646 /* Optimization - check if we can redirect the whole path. */
648 if (is_msdfs_link_internal(ctx
, conn
, smb_fname
->base_name
,
649 pp_targetpath
, NULL
)) {
651 DEBUG(6,("dfs_path_lookup (FindFirst) No redirection "
652 "for dfs link %s.\n", dfspath
));
653 status
= NT_STATUS_OK
;
657 DEBUG(6,("dfs_path_lookup: %s resolves to a "
658 "valid dfs link %s.\n", dfspath
,
659 pp_targetpath
? *pp_targetpath
: ""));
662 *consumedcntp
= strlen(dfspath
);
664 status
= NT_STATUS_PATH_NOT_COVERED
;
668 /* Prepare to test only for '/' components in the given path,
669 * so if a Windows path replace all '\\' characters with '/'.
670 * For a POSIX DFS path we know all separators are already '/'. */
672 canon_dfspath
= talloc_strdup(ctx
, dfspath
);
673 if (!canon_dfspath
) {
674 status
= NT_STATUS_NO_MEMORY
;
677 if (!pdp
->posix_path
) {
678 string_replace(canon_dfspath
, '\\', '/');
682 * localpath comes out of unix_convert, so it has
683 * no trailing backslash. Make sure that canon_dfspath hasn't either.
684 * Fix for bug #4860 from Jan Martin <Jan.Martin@rwedea.com>.
687 trim_char(canon_dfspath
,0,'/');
690 * Redirect if any component in the path is a link.
691 * We do this by walking backwards through the
692 * local path, chopping off the last component
693 * in both the local path and the canonicalized
694 * DFS path. If we hit a DFS link then we're done.
697 p
= strrchr_m(smb_fname
->base_name
, '/');
699 q
= strrchr_m(canon_dfspath
, '/');
708 if (is_msdfs_link_internal(ctx
, conn
,
709 smb_fname
->base_name
, pp_targetpath
,
711 DEBUG(4, ("dfs_path_lookup: Redirecting %s because "
712 "parent %s is dfs link\n", dfspath
,
713 smb_fname_str_dbg(smb_fname
)));
716 *consumedcntp
= strlen(canon_dfspath
);
717 DEBUG(10, ("dfs_path_lookup: Path consumed: %s "
723 status
= NT_STATUS_PATH_NOT_COVERED
;
727 /* Step back on the filesystem. */
728 p
= strrchr_m(smb_fname
->base_name
, '/');
731 /* And in the canonicalized dfs path. */
732 q
= strrchr_m(canon_dfspath
, '/');
736 status
= NT_STATUS_OK
;
738 TALLOC_FREE(smb_fname
);
742 /*****************************************************************
743 Decides if a dfs pathname should be redirected or not.
744 If not, the pathname is converted to a tcon-relative local unix path
746 search_wcard_flag: this flag performs 2 functions both related
747 to searches. See resolve_dfs_path() and parse_dfs_path_XX()
750 This function can return NT_STATUS_OK, meaning use the returned path as-is
751 (mapped into a local path).
752 or NT_STATUS_NOT_COVERED meaning return a DFS redirect, or
753 any other NT_STATUS error which is a genuine error to be
754 returned to the client.
755 *****************************************************************/
757 static NTSTATUS
dfs_redirect(TALLOC_CTX
*ctx
,
758 connection_struct
*conn
,
760 bool search_wcard_flag
,
761 bool allow_broken_path
,
763 bool *ppath_contains_wcard
)
766 struct dfs_path
*pdp
= talloc(ctx
, struct dfs_path
);
769 return NT_STATUS_NO_MEMORY
;
772 status
= parse_dfs_path(conn
, path_in
, search_wcard_flag
,
773 allow_broken_path
, pdp
,
774 ppath_contains_wcard
);
775 if (!NT_STATUS_IS_OK(status
)) {
780 if (pdp
->reqpath
[0] == '\0') {
782 *pp_path_out
= talloc_strdup(ctx
, "");
784 return NT_STATUS_NO_MEMORY
;
786 DEBUG(5,("dfs_redirect: self-referral.\n"));
790 /* If dfs pathname for a non-dfs share, convert to tcon-relative
791 path and return OK */
793 if (!lp_msdfs_root(SNUM(conn
))) {
794 *pp_path_out
= talloc_strdup(ctx
, pdp
->reqpath
);
797 return NT_STATUS_NO_MEMORY
;
802 /* If it looked like a local path (zero hostname/servicename)
803 * just treat as a tcon-relative path. */
805 if (pdp
->hostname
[0] == '\0' && pdp
->servicename
[0] == '\0') {
806 *pp_path_out
= talloc_strdup(ctx
, pdp
->reqpath
);
809 return NT_STATUS_NO_MEMORY
;
814 if (!( strequal(pdp
->servicename
, lp_servicename(talloc_tos(), SNUM(conn
)))
815 || (strequal(pdp
->servicename
, HOMES_NAME
)
816 && strequal(lp_servicename(talloc_tos(), SNUM(conn
)),
817 conn
->session_info
->unix_info
->sanitized_username
) )) ) {
819 /* The given sharename doesn't match this connection. */
822 return NT_STATUS_OBJECT_PATH_NOT_FOUND
;
825 status
= dfs_path_lookup(ctx
, conn
, path_in
, pdp
,
826 search_wcard_flag
, NULL
, NULL
);
827 if (!NT_STATUS_IS_OK(status
)) {
828 if (NT_STATUS_EQUAL(status
, NT_STATUS_PATH_NOT_COVERED
)) {
829 DEBUG(3,("dfs_redirect: Redirecting %s\n", path_in
));
831 DEBUG(10,("dfs_redirect: dfs_path_lookup "
832 "failed for %s with %s\n",
833 path_in
, nt_errstr(status
) ));
838 DEBUG(3,("dfs_redirect: Not redirecting %s.\n", path_in
));
840 /* Form non-dfs tcon-relative path */
841 *pp_path_out
= talloc_strdup(ctx
, pdp
->reqpath
);
844 return NT_STATUS_NO_MEMORY
;
847 DEBUG(3,("dfs_redirect: Path %s converted to non-dfs path %s\n",
854 /**********************************************************************
855 Return a self referral.
856 **********************************************************************/
858 static NTSTATUS
self_ref(TALLOC_CTX
*ctx
,
859 const char *dfs_path
,
860 struct junction_map
*jucn
,
862 bool *self_referralp
)
864 struct referral
*ref
;
866 *self_referralp
= True
;
868 jucn
->referral_count
= 1;
869 if((ref
= talloc_zero(ctx
, struct referral
)) == NULL
) {
870 return NT_STATUS_NO_MEMORY
;
873 ref
->alternate_path
= talloc_strdup(ctx
, dfs_path
);
874 if (!ref
->alternate_path
) {
876 return NT_STATUS_NO_MEMORY
;
879 ref
->ttl
= REFERRAL_TTL
;
880 jucn
->referral_list
= ref
;
881 *consumedcntp
= strlen(dfs_path
);
885 /**********************************************************************
886 Gets valid referrals for a dfs path and fills up the
887 junction_map structure.
888 **********************************************************************/
890 NTSTATUS
get_referred_path(TALLOC_CTX
*ctx
,
891 const char *dfs_path
,
892 bool allow_broken_path
,
893 struct junction_map
*jucn
,
895 bool *self_referralp
)
897 struct connection_struct
*conn
;
898 char *targetpath
= NULL
;
900 NTSTATUS status
= NT_STATUS_NOT_FOUND
;
902 struct dfs_path
*pdp
= talloc(ctx
, struct dfs_path
);
906 return NT_STATUS_NO_MEMORY
;
909 *self_referralp
= False
;
911 status
= parse_dfs_path(NULL
, dfs_path
, False
, allow_broken_path
,
913 if (!NT_STATUS_IS_OK(status
)) {
917 jucn
->service_name
= talloc_strdup(ctx
, pdp
->servicename
);
918 jucn
->volume_name
= talloc_strdup(ctx
, pdp
->reqpath
);
919 if (!jucn
->service_name
|| !jucn
->volume_name
) {
921 return NT_STATUS_NO_MEMORY
;
924 /* Verify the share is a dfs root */
925 snum
= lp_servicenumber(jucn
->service_name
);
927 char *service_name
= NULL
;
928 if ((snum
= find_service(ctx
, jucn
->service_name
, &service_name
)) < 0) {
929 return NT_STATUS_NOT_FOUND
;
932 return NT_STATUS_NO_MEMORY
;
934 TALLOC_FREE(jucn
->service_name
);
935 jucn
->service_name
= talloc_strdup(ctx
, service_name
);
936 if (!jucn
->service_name
) {
938 return NT_STATUS_NO_MEMORY
;
942 if (!lp_msdfs_root(snum
) && (*lp_msdfs_proxy(talloc_tos(), snum
) == '\0')) {
943 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not "
945 pdp
->servicename
, dfs_path
));
947 return NT_STATUS_NOT_FOUND
;
951 * Self referrals are tested with a anonymous IPC connection and
952 * a GET_DFS_REFERRAL call to \\server\share. (which means
953 * dp.reqpath[0] points to an empty string). create_conn_struct cd's
954 * into the directory and will fail if it cannot (as the anonymous
955 * user). Cope with this.
958 if (pdp
->reqpath
[0] == '\0') {
960 struct referral
*ref
;
962 if (*lp_msdfs_proxy(talloc_tos(), snum
) == '\0') {
972 * It's an msdfs proxy share. Redirect to
973 * the configured target share.
976 jucn
->referral_count
= 1;
977 if ((ref
= talloc_zero(ctx
, struct referral
)) == NULL
) {
979 return NT_STATUS_NO_MEMORY
;
982 if (!(tmp
= talloc_strdup(ctx
, lp_msdfs_proxy(talloc_tos(), snum
)))) {
984 return NT_STATUS_NO_MEMORY
;
987 trim_string(tmp
, "\\", 0);
989 ref
->alternate_path
= talloc_asprintf(ctx
, "\\%s", tmp
);
992 if (!ref
->alternate_path
) {
994 return NT_STATUS_NO_MEMORY
;
997 if (pdp
->reqpath
[0] != '\0') {
998 ref
->alternate_path
= talloc_asprintf_append(
1002 if (!ref
->alternate_path
) {
1004 return NT_STATUS_NO_MEMORY
;
1008 ref
->ttl
= REFERRAL_TTL
;
1009 jucn
->referral_list
= ref
;
1010 *consumedcntp
= strlen(dfs_path
);
1012 return NT_STATUS_OK
;
1015 status
= create_conn_struct_cwd(ctx
,
1016 server_event_context(),
1017 server_messaging_context(),
1019 lp_pathname(talloc_tos(), snum
), NULL
, &oldpath
);
1020 if (!NT_STATUS_IS_OK(status
)) {
1025 /* If this is a DFS path dfs_lookup should return
1026 * NT_STATUS_PATH_NOT_COVERED. */
1028 status
= dfs_path_lookup(ctx
, conn
, dfs_path
, pdp
,
1029 False
, consumedcntp
, &targetpath
);
1031 if (!NT_STATUS_EQUAL(status
, NT_STATUS_PATH_NOT_COVERED
)) {
1032 DEBUG(3,("get_referred_path: No valid referrals for path %s\n",
1037 /* We know this is a valid dfs link. Parse the targetpath. */
1038 if (!parse_msdfs_symlink(ctx
, targetpath
,
1039 &jucn
->referral_list
,
1040 &jucn
->referral_count
)) {
1041 DEBUG(3,("get_referred_path: failed to parse symlink "
1042 "target %s\n", targetpath
));
1043 status
= NT_STATUS_NOT_FOUND
;
1047 status
= NT_STATUS_OK
;
1049 vfs_ChDir(conn
, oldpath
);
1050 SMB_VFS_DISCONNECT(conn
);
1056 /******************************************************************
1057 Set up the DFS referral for the dfs pathname. This call returns
1058 the amount of the path covered by this server, and where the
1059 client should be redirected to. This is the meat of the
1060 TRANS2_GET_DFS_REFERRAL call.
1061 ******************************************************************/
1063 int setup_dfs_referral(connection_struct
*orig_conn
,
1064 const char *dfs_path
,
1065 int max_referral_level
,
1066 char **ppdata
, NTSTATUS
*pstatus
)
1068 char *pdata
= *ppdata
;
1070 struct dfs_GetDFSReferral
*r
;
1071 DATA_BLOB blob
= data_blob_null
;
1073 enum ndr_err_code ndr_err
;
1075 r
= talloc_zero(talloc_tos(), struct dfs_GetDFSReferral
);
1077 *pstatus
= NT_STATUS_NO_MEMORY
;
1081 r
->in
.req
.max_referral_level
= max_referral_level
;
1082 r
->in
.req
.servername
= talloc_strdup(r
, dfs_path
);
1083 if (r
->in
.req
.servername
== NULL
) {
1085 *pstatus
= NT_STATUS_NO_MEMORY
;
1089 status
= SMB_VFS_GET_DFS_REFERRALS(orig_conn
, r
);
1090 if (!NT_STATUS_IS_OK(status
)) {
1096 ndr_err
= ndr_push_struct_blob(&blob
, r
,
1098 (ndr_push_flags_fn_t
)ndr_push_dfs_referral_resp
);
1099 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
1101 *pstatus
= NT_STATUS_INVALID_PARAMETER
;
1105 pdata
= (char *)SMB_REALLOC(pdata
, blob
.length
);
1108 DEBUG(0,("referral setup:"
1109 "malloc failed for Realloc!\n"));
1113 reply_size
= blob
.length
;
1114 memcpy(pdata
, blob
.data
, blob
.length
);
1117 *pstatus
= NT_STATUS_OK
;
1121 /**********************************************************************
1122 The following functions are called by the NETDFS RPC pipe functions
1123 **********************************************************************/
1125 /*********************************************************************
1126 Creates a junction structure from a DFS pathname
1127 **********************************************************************/
1129 bool create_junction(TALLOC_CTX
*ctx
,
1130 const char *dfs_path
,
1131 bool allow_broken_path
,
1132 struct junction_map
*jucn
)
1136 struct dfs_path
*pdp
= talloc(ctx
,struct dfs_path
);
1142 status
= parse_dfs_path(NULL
, dfs_path
, False
, allow_broken_path
,
1144 if (!NT_STATUS_IS_OK(status
)) {
1148 /* check if path is dfs : validate first token */
1149 if (!is_myname_or_ipaddr(pdp
->hostname
)) {
1150 DEBUG(4,("create_junction: Invalid hostname %s "
1152 pdp
->hostname
, dfs_path
));
1157 /* Check for a non-DFS share */
1158 snum
= lp_servicenumber(pdp
->servicename
);
1160 if(snum
< 0 || !lp_msdfs_root(snum
)) {
1161 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1167 jucn
->service_name
= talloc_strdup(ctx
, pdp
->servicename
);
1168 jucn
->volume_name
= talloc_strdup(ctx
, pdp
->reqpath
);
1169 jucn
->comment
= lp_comment(ctx
, snum
);
1172 if (!jucn
->service_name
|| !jucn
->volume_name
|| ! jucn
->comment
) {
1178 /**********************************************************************
1179 Forms a valid Unix pathname from the junction
1180 **********************************************************************/
1182 static bool junction_to_local_path(const struct junction_map
*jucn
,
1184 connection_struct
**conn_out
,
1190 snum
= lp_servicenumber(jucn
->service_name
);
1194 status
= create_conn_struct_cwd(talloc_tos(),
1195 server_event_context(),
1196 server_messaging_context(),
1198 snum
, lp_pathname(talloc_tos(), snum
), NULL
, oldpath
);
1199 if (!NT_STATUS_IS_OK(status
)) {
1203 *pp_path_out
= talloc_asprintf(*conn_out
,
1205 lp_pathname(talloc_tos(), snum
),
1207 if (!*pp_path_out
) {
1208 vfs_ChDir(*conn_out
, *oldpath
);
1209 SMB_VFS_DISCONNECT(*conn_out
);
1210 conn_free(*conn_out
);
1216 bool create_msdfs_link(const struct junction_map
*jucn
)
1220 char *msdfs_link
= NULL
;
1221 connection_struct
*conn
;
1223 bool insert_comma
= False
;
1226 if(!junction_to_local_path(jucn
, &path
, &conn
, &cwd
)) {
1230 /* Form the msdfs_link contents */
1231 msdfs_link
= talloc_strdup(conn
, "msdfs:");
1235 for(i
=0; i
<jucn
->referral_count
; i
++) {
1236 char *refpath
= jucn
->referral_list
[i
].alternate_path
;
1238 /* Alternate paths always use Windows separators. */
1239 trim_char(refpath
, '\\', '\\');
1240 if(*refpath
== '\0') {
1242 insert_comma
= False
;
1246 if (i
> 0 && insert_comma
) {
1247 msdfs_link
= talloc_asprintf_append_buffer(msdfs_link
,
1251 msdfs_link
= talloc_asprintf_append_buffer(msdfs_link
,
1259 if (!insert_comma
) {
1260 insert_comma
= True
;
1264 DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
1267 if(SMB_VFS_SYMLINK(conn
, msdfs_link
, path
) < 0) {
1268 if (errno
== EEXIST
) {
1269 struct smb_filename
*smb_fname
= NULL
;
1272 status
= create_synthetic_smb_fname(talloc_tos(), path
,
1275 if (!NT_STATUS_IS_OK(status
)) {
1276 errno
= map_errno_from_nt_status(status
);
1280 if(SMB_VFS_UNLINK(conn
, smb_fname
)!=0) {
1281 TALLOC_FREE(smb_fname
);
1284 TALLOC_FREE(smb_fname
);
1286 if (SMB_VFS_SYMLINK(conn
, msdfs_link
, path
) < 0) {
1287 DEBUG(1,("create_msdfs_link: symlink failed "
1288 "%s -> %s\nError: %s\n",
1289 path
, msdfs_link
, strerror(errno
)));
1297 vfs_ChDir(conn
, cwd
);
1298 SMB_VFS_DISCONNECT(conn
);
1303 bool remove_msdfs_link(const struct junction_map
*jucn
)
1307 connection_struct
*conn
;
1309 struct smb_filename
*smb_fname
= NULL
;
1312 if (!junction_to_local_path(jucn
, &path
, &conn
, &cwd
)) {
1316 status
= create_synthetic_smb_fname(talloc_tos(), path
,
1319 if (!NT_STATUS_IS_OK(status
)) {
1320 errno
= map_errno_from_nt_status(status
);
1324 if( SMB_VFS_UNLINK(conn
, smb_fname
) == 0 ) {
1328 TALLOC_FREE(smb_fname
);
1329 vfs_ChDir(conn
, cwd
);
1330 SMB_VFS_DISCONNECT(conn
);
1335 /*********************************************************************
1336 Return the number of DFS links at the root of this share.
1337 *********************************************************************/
1339 static int count_dfs_links(TALLOC_CTX
*ctx
, int snum
)
1343 const char *dname
= NULL
;
1344 char *talloced
= NULL
;
1345 const char *connect_path
= lp_pathname(talloc_tos(), snum
);
1346 const char *msdfs_proxy
= lp_msdfs_proxy(talloc_tos(), snum
);
1347 connection_struct
*conn
;
1351 if(*connect_path
== '\0') {
1356 * Fake up a connection struct for the VFS layer.
1359 status
= create_conn_struct_cwd(talloc_tos(),
1360 server_event_context(),
1361 server_messaging_context(),
1363 snum
, connect_path
, NULL
, &cwd
);
1364 if (!NT_STATUS_IS_OK(status
)) {
1365 DEBUG(3, ("create_conn_struct failed: %s\n",
1366 nt_errstr(status
)));
1370 /* Count a link for the msdfs root - convention */
1373 /* No more links if this is an msdfs proxy. */
1374 if (*msdfs_proxy
!= '\0') {
1378 /* Now enumerate all dfs links */
1379 dirp
= SMB_VFS_OPENDIR(conn
, ".", NULL
, 0);
1384 while ((dname
= vfs_readdirname(conn
, dirp
, NULL
, &talloced
))
1386 if (is_msdfs_link(conn
,
1391 TALLOC_FREE(talloced
);
1394 SMB_VFS_CLOSEDIR(conn
,dirp
);
1397 vfs_ChDir(conn
, cwd
);
1398 SMB_VFS_DISCONNECT(conn
);
1403 /*********************************************************************
1404 *********************************************************************/
1406 static int form_junctions(TALLOC_CTX
*ctx
,
1408 struct junction_map
*jucn
,
1413 const char *dname
= NULL
;
1414 char *talloced
= NULL
;
1415 const char *connect_path
= lp_pathname(talloc_tos(), snum
);
1416 char *service_name
= lp_servicename(talloc_tos(), snum
);
1417 const char *msdfs_proxy
= lp_msdfs_proxy(talloc_tos(), snum
);
1418 connection_struct
*conn
;
1419 struct referral
*ref
= NULL
;
1423 if (jn_remain
== 0) {
1427 if(*connect_path
== '\0') {
1432 * Fake up a connection struct for the VFS layer.
1435 status
= create_conn_struct_cwd(ctx
,
1436 server_event_context(),
1437 server_messaging_context(),
1438 &conn
, snum
, connect_path
, NULL
,
1440 if (!NT_STATUS_IS_OK(status
)) {
1441 DEBUG(3, ("create_conn_struct failed: %s\n",
1442 nt_errstr(status
)));
1446 /* form a junction for the msdfs root - convention
1447 DO NOT REMOVE THIS: NT clients will not work with us
1448 if this is not present
1450 jucn
[cnt
].service_name
= talloc_strdup(ctx
,service_name
);
1451 jucn
[cnt
].volume_name
= talloc_strdup(ctx
, "");
1452 if (!jucn
[cnt
].service_name
|| !jucn
[cnt
].volume_name
) {
1455 jucn
[cnt
].comment
= "";
1456 jucn
[cnt
].referral_count
= 1;
1458 ref
= jucn
[cnt
].referral_list
= talloc_zero(ctx
, struct referral
);
1459 if (jucn
[cnt
].referral_list
== NULL
) {
1464 ref
->ttl
= REFERRAL_TTL
;
1465 if (*msdfs_proxy
!= '\0') {
1466 ref
->alternate_path
= talloc_strdup(ctx
,
1469 ref
->alternate_path
= talloc_asprintf(ctx
,
1471 get_local_machine_name(),
1475 if (!ref
->alternate_path
) {
1480 /* Don't enumerate if we're an msdfs proxy. */
1481 if (*msdfs_proxy
!= '\0') {
1485 /* Now enumerate all dfs links */
1486 dirp
= SMB_VFS_OPENDIR(conn
, ".", NULL
, 0);
1491 while ((dname
= vfs_readdirname(conn
, dirp
, NULL
, &talloced
))
1493 char *link_target
= NULL
;
1494 if (cnt
>= jn_remain
) {
1495 DEBUG(2, ("form_junctions: ran out of MSDFS "
1497 TALLOC_FREE(talloced
);
1500 if (is_msdfs_link_internal(ctx
,
1502 dname
, &link_target
,
1504 if (parse_msdfs_symlink(ctx
,
1506 &jucn
[cnt
].referral_list
,
1507 &jucn
[cnt
].referral_count
)) {
1509 jucn
[cnt
].service_name
= talloc_strdup(ctx
,
1511 jucn
[cnt
].volume_name
= talloc_strdup(ctx
,
1513 if (!jucn
[cnt
].service_name
||
1514 !jucn
[cnt
].volume_name
) {
1515 TALLOC_FREE(talloced
);
1518 jucn
[cnt
].comment
= "";
1521 TALLOC_FREE(link_target
);
1523 TALLOC_FREE(talloced
);
1529 SMB_VFS_CLOSEDIR(conn
,dirp
);
1532 vfs_ChDir(conn
, cwd
);
1537 struct junction_map
*enum_msdfs_links(TALLOC_CTX
*ctx
, size_t *p_num_jn
)
1539 struct junction_map
*jn
= NULL
;
1541 size_t jn_count
= 0;
1545 if(!lp_host_msdfs()) {
1549 /* Ensure all the usershares are loaded. */
1551 load_registry_shares();
1552 sharecount
= load_usershare_shares(NULL
, connections_snum_used
);
1555 for(i
=0;i
< sharecount
;i
++) {
1556 if(lp_msdfs_root(i
)) {
1557 jn_count
+= count_dfs_links(ctx
, i
);
1560 if (jn_count
== 0) {
1563 jn
= talloc_array(ctx
, struct junction_map
, jn_count
);
1567 for(i
=0; i
< sharecount
; i
++) {
1568 if (*p_num_jn
>= jn_count
) {
1571 if(lp_msdfs_root(i
)) {
1572 *p_num_jn
+= form_junctions(ctx
, i
,
1574 jn_count
- *p_num_jn
);
1580 /******************************************************************************
1581 Core function to resolve a dfs pathname possibly containing a wildcard. If
1582 ppath_contains_wcard != NULL, it will be set to true if a wildcard is
1583 detected during dfs resolution.
1584 ******************************************************************************/
1586 NTSTATUS
resolve_dfspath_wcard(TALLOC_CTX
*ctx
,
1587 connection_struct
*conn
,
1589 const char *name_in
,
1591 bool allow_broken_path
,
1593 bool *ppath_contains_wcard
)
1595 bool path_contains_wcard
;
1596 NTSTATUS status
= NT_STATUS_OK
;
1598 if (dfs_pathnames
) {
1599 status
= dfs_redirect(ctx
,
1605 &path_contains_wcard
);
1607 if (NT_STATUS_IS_OK(status
) && ppath_contains_wcard
!= NULL
) {
1608 *ppath_contains_wcard
= path_contains_wcard
;
1612 * Cheat and just return a copy of the in ptr.
1613 * Once srvstr_get_path() uses talloc it'll
1614 * be a talloced ptr anyway.
1616 *pp_name_out
= discard_const_p(char, name_in
);