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
;
238 conn
= talloc_zero(ctx
, connection_struct
);
240 return NT_STATUS_NO_MEMORY
;
243 connpath
= talloc_strdup(conn
, path
);
246 return NT_STATUS_NO_MEMORY
;
248 connpath
= talloc_string_sub(conn
,
251 lp_servicename(talloc_tos(), snum
));
254 return NT_STATUS_NO_MEMORY
;
257 conn
->sconn
= talloc_zero(conn
, struct smbd_server_connection
);
258 if (conn
->sconn
== NULL
) {
260 return NT_STATUS_NO_MEMORY
;
263 conn
->sconn
->ev_ctx
= ev
;
264 conn
->sconn
->msg_ctx
= msg
;
265 conn
->sconn
->sock
= -1;
266 conn
->sconn
->smb1
.echo_handler
.trusted_fd
= -1;
267 conn
->sconn
->smb1
.echo_handler
.socket_lock_fd
= -1;
269 /* needed for smbd_vfs_init() */
271 if (!(conn
->params
= talloc_zero(conn
, struct share_params
))) {
272 DEBUG(0, ("TALLOC failed\n"));
274 return NT_STATUS_NO_MEMORY
;
277 conn
->params
->service
= snum
;
278 conn
->cnum
= TID_FIELD_INVALID
;
280 DLIST_ADD(conn
->sconn
->connections
, conn
);
281 conn
->sconn
->num_connections
++;
283 if (session_info
!= NULL
) {
284 conn
->session_info
= copy_session_info(conn
, session_info
);
285 if (conn
->session_info
== NULL
) {
286 DEBUG(0, ("copy_serverinfo failed\n"));
288 return NT_STATUS_NO_MEMORY
;
290 vfs_user
= conn
->session_info
->unix_info
->unix_name
;
292 /* use current authenticated user in absence of session_info */
293 vfs_user
= get_current_username();
296 set_conn_connectpath(conn
, connpath
);
299 * New code to check if there's a share security descripter
300 * added from NT server manager. This is done after the
301 * smb.conf checks are done as we need a uid and token. JRA.
304 if (conn
->session_info
) {
305 share_access_check(conn
->session_info
->security_token
,
306 lp_servicename(talloc_tos(), snum
),
307 MAXIMUM_ALLOWED_ACCESS
,
308 &conn
->share_access
);
310 if ((conn
->share_access
& FILE_WRITE_DATA
) == 0) {
311 if ((conn
->share_access
& FILE_READ_DATA
) == 0) {
312 /* No access, read or write. */
313 DEBUG(0,("create_conn_struct: connection to %s "
314 "denied due to security "
316 lp_servicename(talloc_tos(), snum
)));
318 return NT_STATUS_ACCESS_DENIED
;
320 conn
->read_only
= true;
324 conn
->share_access
= 0;
325 conn
->read_only
= true;
328 if (!smbd_vfs_init(conn
)) {
329 NTSTATUS status
= map_nt_error_from_unix(errno
);
330 DEBUG(0,("create_conn_struct: smbd_vfs_init failed.\n"));
335 /* this must be the first filesystem operation that we do */
336 if (SMB_VFS_CONNECT(conn
, lp_servicename(talloc_tos(), snum
), vfs_user
) < 0) {
337 DEBUG(0,("VFS connect failed!\n"));
339 return NT_STATUS_UNSUCCESSFUL
;
342 conn
->fs_capabilities
= SMB_VFS_FS_CAPABILITIES(conn
, &conn
->ts_res
);
348 /********************************************************
349 Fake up a connection struct for the VFS layer.
350 Note: this performs a vfs connect and CHANGES CWD !!!! JRA.
352 The old working directory is returned on *poldcwd, allocated on ctx.
353 *********************************************************/
355 NTSTATUS
create_conn_struct_cwd(TALLOC_CTX
*ctx
,
356 struct tevent_context
*ev
,
357 struct messaging_context
*msg
,
358 connection_struct
**pconn
,
361 const struct auth_session_info
*session_info
,
364 connection_struct
*conn
;
367 NTSTATUS status
= create_conn_struct(ctx
, ev
,
371 if (!NT_STATUS_IS_OK(status
)) {
376 * Windows seems to insist on doing trans2getdfsreferral() calls on
377 * the IPC$ share as the anonymous user. If we try to chdir as that
378 * user we will fail.... WTF ? JRA.
381 oldcwd
= vfs_GetWd(ctx
, conn
);
382 if (oldcwd
== NULL
) {
383 status
= map_nt_error_from_unix(errno
);
384 DEBUG(3, ("vfs_GetWd failed: %s\n", strerror(errno
)));
389 if (vfs_ChDir(conn
,conn
->connectpath
) != 0) {
390 status
= map_nt_error_from_unix(errno
);
391 DEBUG(3,("create_conn_struct: Can't ChDir to new conn path %s. "
393 conn
->connectpath
, strerror(errno
) ));
404 /**********************************************************************
405 Parse the contents of a symlink to verify if it is an msdfs referral
406 A valid referral is of the form:
408 msdfs:server1\share1,server2\share2
409 msdfs:server1\share1\pathname,server2\share2\pathname
410 msdfs:server1/share1,server2/share2
411 msdfs:server1/share1/pathname,server2/share2/pathname.
413 Note that the alternate paths returned here must be of the canonicalized
417 \server\share\path\to\file,
419 even in posix path mode. This is because we have no knowledge if the
420 server we're referring to understands posix paths.
421 **********************************************************************/
423 static bool parse_msdfs_symlink(TALLOC_CTX
*ctx
,
425 struct referral
**preflist
,
430 char **alt_path
= NULL
;
432 struct referral
*reflist
;
435 temp
= talloc_strdup(ctx
, target
);
439 prot
= strtok_r(temp
, ":", &saveptr
);
441 DEBUG(0,("parse_msdfs_symlink: invalid path !\n"));
445 alt_path
= talloc_array(ctx
, char *, MAX_REFERRAL_COUNT
);
450 /* parse out the alternate paths */
451 while((count
<MAX_REFERRAL_COUNT
) &&
452 ((alt_path
[count
] = strtok_r(NULL
, ",", &saveptr
)) != NULL
)) {
456 DEBUG(10,("parse_msdfs_symlink: count=%d\n", count
));
459 reflist
= *preflist
= talloc_zero_array(ctx
,
460 struct referral
, count
);
461 if(reflist
== NULL
) {
462 TALLOC_FREE(alt_path
);
466 reflist
= *preflist
= NULL
;
469 for(i
=0;i
<count
;i
++) {
472 /* Canonicalize link target.
473 * Replace all /'s in the path by a \ */
474 string_replace(alt_path
[i
], '/', '\\');
476 /* Remove leading '\\'s */
478 while (*p
&& (*p
== '\\')) {
482 reflist
[i
].alternate_path
= talloc_asprintf(ctx
,
485 if (!reflist
[i
].alternate_path
) {
489 reflist
[i
].proximity
= 0;
490 reflist
[i
].ttl
= REFERRAL_TTL
;
491 DEBUG(10, ("parse_msdfs_symlink: Created alt path: %s\n",
492 reflist
[i
].alternate_path
));
497 TALLOC_FREE(alt_path
);
501 /**********************************************************************
502 Returns true if the unix path is a valid msdfs symlink and also
503 returns the target string from inside the link.
504 **********************************************************************/
506 static bool is_msdfs_link_internal(TALLOC_CTX
*ctx
,
507 connection_struct
*conn
,
509 char **pp_link_target
,
510 SMB_STRUCT_STAT
*sbufp
)
512 int referral_len
= 0;
513 #if defined(HAVE_BROKEN_READLINK)
514 char link_target_buf
[PATH_MAX
];
516 char link_target_buf
[7];
519 char *link_target
= NULL
;
520 struct smb_filename smb_fname
;
522 if (pp_link_target
) {
524 link_target
= talloc_array(ctx
, char, bufsize
);
528 *pp_link_target
= link_target
;
530 bufsize
= sizeof(link_target_buf
);
531 link_target
= link_target_buf
;
534 ZERO_STRUCT(smb_fname
);
535 smb_fname
.base_name
= discard_const_p(char, path
);
537 if (SMB_VFS_LSTAT(conn
, &smb_fname
) != 0) {
538 DEBUG(5,("is_msdfs_link_read_target: %s does not exist.\n",
542 if (!S_ISLNK(smb_fname
.st
.st_ex_mode
)) {
543 DEBUG(5,("is_msdfs_link_read_target: %s is not a link.\n",
548 *sbufp
= smb_fname
.st
;
551 referral_len
= SMB_VFS_READLINK(conn
, path
, link_target
, bufsize
- 1);
552 if (referral_len
== -1) {
553 DEBUG(0,("is_msdfs_link_read_target: Error reading "
554 "msdfs link %s: %s\n",
555 path
, strerror(errno
)));
558 link_target
[referral_len
] = '\0';
560 DEBUG(5,("is_msdfs_link_internal: %s -> %s\n",path
,
563 if (!strnequal(link_target
, "msdfs:", 6)) {
570 if (link_target
!= link_target_buf
) {
571 TALLOC_FREE(link_target
);
576 /**********************************************************************
577 Returns true if the unix path is a valid msdfs symlink.
578 **********************************************************************/
580 bool is_msdfs_link(connection_struct
*conn
,
582 SMB_STRUCT_STAT
*sbufp
)
584 return is_msdfs_link_internal(talloc_tos(),
591 /*****************************************************************
592 Used by other functions to decide if a dfs path is remote,
593 and to get the list of referred locations for that remote path.
595 search_flag: For findfirsts, dfs links themselves are not
596 redirected, but paths beyond the links are. For normal smb calls,
597 even dfs links need to be redirected.
599 consumedcntp: how much of the dfs path is being redirected. the client
600 should try the remaining path on the redirected server.
602 If this returns NT_STATUS_PATH_NOT_COVERED the contents of the msdfs
603 link redirect are in targetpath.
604 *****************************************************************/
606 static NTSTATUS
dfs_path_lookup(TALLOC_CTX
*ctx
,
607 connection_struct
*conn
,
608 const char *dfspath
, /* Incoming complete dfs path */
609 const struct dfs_path
*pdp
, /* Parsed out
610 server+share+extrapath. */
611 bool search_flag
, /* Called from a findfirst ? */
613 char **pp_targetpath
)
618 struct smb_filename
*smb_fname
= NULL
;
619 char *canon_dfspath
= NULL
; /* Canonicalized dfs path. (only '/'
622 DEBUG(10,("dfs_path_lookup: Conn path = %s reqpath = %s\n",
623 conn
->connectpath
, pdp
->reqpath
));
626 * Note the unix path conversion here we're doing we
627 * throw away. We're looking for a symlink for a dfs
628 * resolution, if we don't find it we'll do another
629 * unix_convert later in the codepath.
632 status
= unix_convert(ctx
, conn
, pdp
->reqpath
, &smb_fname
,
633 search_flag
? UCF_ALWAYS_ALLOW_WCARD_LCOMP
: 0);
635 if (!NT_STATUS_IS_OK(status
)) {
636 if (!NT_STATUS_EQUAL(status
,
637 NT_STATUS_OBJECT_PATH_NOT_FOUND
)) {
640 if (smb_fname
== NULL
|| smb_fname
->base_name
== NULL
) {
645 /* Optimization - check if we can redirect the whole path. */
647 if (is_msdfs_link_internal(ctx
, conn
, smb_fname
->base_name
,
648 pp_targetpath
, NULL
)) {
650 DEBUG(6,("dfs_path_lookup (FindFirst) No redirection "
651 "for dfs link %s.\n", dfspath
));
652 status
= NT_STATUS_OK
;
656 DEBUG(6,("dfs_path_lookup: %s resolves to a "
657 "valid dfs link %s.\n", dfspath
,
658 pp_targetpath
? *pp_targetpath
: ""));
661 *consumedcntp
= strlen(dfspath
);
663 status
= NT_STATUS_PATH_NOT_COVERED
;
667 /* Prepare to test only for '/' components in the given path,
668 * so if a Windows path replace all '\\' characters with '/'.
669 * For a POSIX DFS path we know all separators are already '/'. */
671 canon_dfspath
= talloc_strdup(ctx
, dfspath
);
672 if (!canon_dfspath
) {
673 status
= NT_STATUS_NO_MEMORY
;
676 if (!pdp
->posix_path
) {
677 string_replace(canon_dfspath
, '\\', '/');
681 * localpath comes out of unix_convert, so it has
682 * no trailing backslash. Make sure that canon_dfspath hasn't either.
683 * Fix for bug #4860 from Jan Martin <Jan.Martin@rwedea.com>.
686 trim_char(canon_dfspath
,0,'/');
689 * Redirect if any component in the path is a link.
690 * We do this by walking backwards through the
691 * local path, chopping off the last component
692 * in both the local path and the canonicalized
693 * DFS path. If we hit a DFS link then we're done.
696 p
= strrchr_m(smb_fname
->base_name
, '/');
698 q
= strrchr_m(canon_dfspath
, '/');
707 if (is_msdfs_link_internal(ctx
, conn
,
708 smb_fname
->base_name
, pp_targetpath
,
710 DEBUG(4, ("dfs_path_lookup: Redirecting %s because "
711 "parent %s is dfs link\n", dfspath
,
712 smb_fname_str_dbg(smb_fname
)));
715 *consumedcntp
= strlen(canon_dfspath
);
716 DEBUG(10, ("dfs_path_lookup: Path consumed: %s "
722 status
= NT_STATUS_PATH_NOT_COVERED
;
726 /* Step back on the filesystem. */
727 p
= strrchr_m(smb_fname
->base_name
, '/');
730 /* And in the canonicalized dfs path. */
731 q
= strrchr_m(canon_dfspath
, '/');
735 status
= NT_STATUS_OK
;
737 TALLOC_FREE(smb_fname
);
741 /*****************************************************************
742 Decides if a dfs pathname should be redirected or not.
743 If not, the pathname is converted to a tcon-relative local unix path
745 search_wcard_flag: this flag performs 2 functions both related
746 to searches. See resolve_dfs_path() and parse_dfs_path_XX()
749 This function can return NT_STATUS_OK, meaning use the returned path as-is
750 (mapped into a local path).
751 or NT_STATUS_NOT_COVERED meaning return a DFS redirect, or
752 any other NT_STATUS error which is a genuine error to be
753 returned to the client.
754 *****************************************************************/
756 static NTSTATUS
dfs_redirect(TALLOC_CTX
*ctx
,
757 connection_struct
*conn
,
759 bool search_wcard_flag
,
760 bool allow_broken_path
,
762 bool *ppath_contains_wcard
)
765 struct dfs_path
*pdp
= talloc(ctx
, struct dfs_path
);
768 return NT_STATUS_NO_MEMORY
;
771 status
= parse_dfs_path(conn
, path_in
, search_wcard_flag
,
772 allow_broken_path
, pdp
,
773 ppath_contains_wcard
);
774 if (!NT_STATUS_IS_OK(status
)) {
779 if (pdp
->reqpath
[0] == '\0') {
781 *pp_path_out
= talloc_strdup(ctx
, "");
783 return NT_STATUS_NO_MEMORY
;
785 DEBUG(5,("dfs_redirect: self-referral.\n"));
789 /* If dfs pathname for a non-dfs share, convert to tcon-relative
790 path and return OK */
792 if (!lp_msdfs_root(SNUM(conn
))) {
793 *pp_path_out
= talloc_strdup(ctx
, pdp
->reqpath
);
796 return NT_STATUS_NO_MEMORY
;
801 /* If it looked like a local path (zero hostname/servicename)
802 * just treat as a tcon-relative path. */
804 if (pdp
->hostname
[0] == '\0' && pdp
->servicename
[0] == '\0') {
805 *pp_path_out
= talloc_strdup(ctx
, pdp
->reqpath
);
808 return NT_STATUS_NO_MEMORY
;
813 if (!( strequal(pdp
->servicename
, lp_servicename(talloc_tos(), SNUM(conn
)))
814 || (strequal(pdp
->servicename
, HOMES_NAME
)
815 && strequal(lp_servicename(talloc_tos(), SNUM(conn
)),
816 conn
->session_info
->unix_info
->sanitized_username
) )) ) {
818 /* The given sharename doesn't match this connection. */
821 return NT_STATUS_OBJECT_PATH_NOT_FOUND
;
824 status
= dfs_path_lookup(ctx
, conn
, path_in
, pdp
,
825 search_wcard_flag
, NULL
, NULL
);
826 if (!NT_STATUS_IS_OK(status
)) {
827 if (NT_STATUS_EQUAL(status
, NT_STATUS_PATH_NOT_COVERED
)) {
828 DEBUG(3,("dfs_redirect: Redirecting %s\n", path_in
));
830 DEBUG(10,("dfs_redirect: dfs_path_lookup "
831 "failed for %s with %s\n",
832 path_in
, nt_errstr(status
) ));
837 DEBUG(3,("dfs_redirect: Not redirecting %s.\n", path_in
));
839 /* Form non-dfs tcon-relative path */
840 *pp_path_out
= talloc_strdup(ctx
, pdp
->reqpath
);
843 return NT_STATUS_NO_MEMORY
;
846 DEBUG(3,("dfs_redirect: Path %s converted to non-dfs path %s\n",
853 /**********************************************************************
854 Return a self referral.
855 **********************************************************************/
857 static NTSTATUS
self_ref(TALLOC_CTX
*ctx
,
858 const char *dfs_path
,
859 struct junction_map
*jucn
,
861 bool *self_referralp
)
863 struct referral
*ref
;
865 *self_referralp
= True
;
867 jucn
->referral_count
= 1;
868 if((ref
= talloc_zero(ctx
, struct referral
)) == NULL
) {
869 return NT_STATUS_NO_MEMORY
;
872 ref
->alternate_path
= talloc_strdup(ctx
, dfs_path
);
873 if (!ref
->alternate_path
) {
875 return NT_STATUS_NO_MEMORY
;
878 ref
->ttl
= REFERRAL_TTL
;
879 jucn
->referral_list
= ref
;
880 *consumedcntp
= strlen(dfs_path
);
884 /**********************************************************************
885 Gets valid referrals for a dfs path and fills up the
886 junction_map structure.
887 **********************************************************************/
889 NTSTATUS
get_referred_path(TALLOC_CTX
*ctx
,
890 const char *dfs_path
,
891 bool allow_broken_path
,
892 struct junction_map
*jucn
,
894 bool *self_referralp
)
896 struct connection_struct
*conn
;
897 char *targetpath
= NULL
;
899 NTSTATUS status
= NT_STATUS_NOT_FOUND
;
901 struct dfs_path
*pdp
= talloc(ctx
, struct dfs_path
);
905 return NT_STATUS_NO_MEMORY
;
908 *self_referralp
= False
;
910 status
= parse_dfs_path(NULL
, dfs_path
, False
, allow_broken_path
,
912 if (!NT_STATUS_IS_OK(status
)) {
916 jucn
->service_name
= talloc_strdup(ctx
, pdp
->servicename
);
917 jucn
->volume_name
= talloc_strdup(ctx
, pdp
->reqpath
);
918 if (!jucn
->service_name
|| !jucn
->volume_name
) {
920 return NT_STATUS_NO_MEMORY
;
923 /* Verify the share is a dfs root */
924 snum
= lp_servicenumber(jucn
->service_name
);
926 char *service_name
= NULL
;
927 if ((snum
= find_service(ctx
, jucn
->service_name
, &service_name
)) < 0) {
928 return NT_STATUS_NOT_FOUND
;
931 return NT_STATUS_NO_MEMORY
;
933 TALLOC_FREE(jucn
->service_name
);
934 jucn
->service_name
= talloc_strdup(ctx
, service_name
);
935 if (!jucn
->service_name
) {
937 return NT_STATUS_NO_MEMORY
;
941 if (!lp_msdfs_root(snum
) && (*lp_msdfs_proxy(talloc_tos(), snum
) == '\0')) {
942 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not "
944 pdp
->servicename
, dfs_path
));
946 return NT_STATUS_NOT_FOUND
;
950 * Self referrals are tested with a anonymous IPC connection and
951 * a GET_DFS_REFERRAL call to \\server\share. (which means
952 * dp.reqpath[0] points to an empty string). create_conn_struct cd's
953 * into the directory and will fail if it cannot (as the anonymous
954 * user). Cope with this.
957 if (pdp
->reqpath
[0] == '\0') {
959 struct referral
*ref
;
961 if (*lp_msdfs_proxy(talloc_tos(), snum
) == '\0') {
971 * It's an msdfs proxy share. Redirect to
972 * the configured target share.
975 jucn
->referral_count
= 1;
976 if ((ref
= talloc_zero(ctx
, struct referral
)) == NULL
) {
978 return NT_STATUS_NO_MEMORY
;
981 if (!(tmp
= talloc_strdup(ctx
, lp_msdfs_proxy(talloc_tos(), snum
)))) {
983 return NT_STATUS_NO_MEMORY
;
986 trim_string(tmp
, "\\", 0);
988 ref
->alternate_path
= talloc_asprintf(ctx
, "\\%s", tmp
);
991 if (!ref
->alternate_path
) {
993 return NT_STATUS_NO_MEMORY
;
996 if (pdp
->reqpath
[0] != '\0') {
997 ref
->alternate_path
= talloc_asprintf_append(
1001 if (!ref
->alternate_path
) {
1003 return NT_STATUS_NO_MEMORY
;
1007 ref
->ttl
= REFERRAL_TTL
;
1008 jucn
->referral_list
= ref
;
1009 *consumedcntp
= strlen(dfs_path
);
1011 return NT_STATUS_OK
;
1014 status
= create_conn_struct_cwd(ctx
,
1015 server_event_context(),
1016 server_messaging_context(),
1018 lp_pathname(talloc_tos(), snum
), NULL
, &oldpath
);
1019 if (!NT_STATUS_IS_OK(status
)) {
1024 /* If this is a DFS path dfs_lookup should return
1025 * NT_STATUS_PATH_NOT_COVERED. */
1027 status
= dfs_path_lookup(ctx
, conn
, dfs_path
, pdp
,
1028 False
, consumedcntp
, &targetpath
);
1030 if (!NT_STATUS_EQUAL(status
, NT_STATUS_PATH_NOT_COVERED
)) {
1031 DEBUG(3,("get_referred_path: No valid referrals for path %s\n",
1036 /* We know this is a valid dfs link. Parse the targetpath. */
1037 if (!parse_msdfs_symlink(ctx
, targetpath
,
1038 &jucn
->referral_list
,
1039 &jucn
->referral_count
)) {
1040 DEBUG(3,("get_referred_path: failed to parse symlink "
1041 "target %s\n", targetpath
));
1042 status
= NT_STATUS_NOT_FOUND
;
1046 status
= NT_STATUS_OK
;
1048 vfs_ChDir(conn
, oldpath
);
1049 SMB_VFS_DISCONNECT(conn
);
1055 /******************************************************************
1056 Set up the DFS referral for the dfs pathname. This call returns
1057 the amount of the path covered by this server, and where the
1058 client should be redirected to. This is the meat of the
1059 TRANS2_GET_DFS_REFERRAL call.
1060 ******************************************************************/
1062 int setup_dfs_referral(connection_struct
*orig_conn
,
1063 const char *dfs_path
,
1064 int max_referral_level
,
1065 char **ppdata
, NTSTATUS
*pstatus
)
1067 char *pdata
= *ppdata
;
1069 struct dfs_GetDFSReferral
*r
;
1070 DATA_BLOB blob
= data_blob_null
;
1072 enum ndr_err_code ndr_err
;
1074 r
= talloc_zero(talloc_tos(), struct dfs_GetDFSReferral
);
1076 *pstatus
= NT_STATUS_NO_MEMORY
;
1080 r
->in
.req
.max_referral_level
= max_referral_level
;
1081 r
->in
.req
.servername
= talloc_strdup(r
, dfs_path
);
1082 if (r
->in
.req
.servername
== NULL
) {
1084 *pstatus
= NT_STATUS_NO_MEMORY
;
1088 status
= SMB_VFS_GET_DFS_REFERRALS(orig_conn
, r
);
1089 if (!NT_STATUS_IS_OK(status
)) {
1095 ndr_err
= ndr_push_struct_blob(&blob
, r
,
1097 (ndr_push_flags_fn_t
)ndr_push_dfs_referral_resp
);
1098 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
1100 *pstatus
= NT_STATUS_INVALID_PARAMETER
;
1104 pdata
= (char *)SMB_REALLOC(pdata
, blob
.length
);
1107 DEBUG(0,("referral setup:"
1108 "malloc failed for Realloc!\n"));
1112 reply_size
= blob
.length
;
1113 memcpy(pdata
, blob
.data
, blob
.length
);
1116 *pstatus
= NT_STATUS_OK
;
1120 /**********************************************************************
1121 The following functions are called by the NETDFS RPC pipe functions
1122 **********************************************************************/
1124 /*********************************************************************
1125 Creates a junction structure from a DFS pathname
1126 **********************************************************************/
1128 bool create_junction(TALLOC_CTX
*ctx
,
1129 const char *dfs_path
,
1130 bool allow_broken_path
,
1131 struct junction_map
*jucn
)
1135 struct dfs_path
*pdp
= talloc(ctx
,struct dfs_path
);
1141 status
= parse_dfs_path(NULL
, dfs_path
, False
, allow_broken_path
,
1143 if (!NT_STATUS_IS_OK(status
)) {
1147 /* check if path is dfs : validate first token */
1148 if (!is_myname_or_ipaddr(pdp
->hostname
)) {
1149 DEBUG(4,("create_junction: Invalid hostname %s "
1151 pdp
->hostname
, dfs_path
));
1156 /* Check for a non-DFS share */
1157 snum
= lp_servicenumber(pdp
->servicename
);
1159 if(snum
< 0 || !lp_msdfs_root(snum
)) {
1160 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1166 jucn
->service_name
= talloc_strdup(ctx
, pdp
->servicename
);
1167 jucn
->volume_name
= talloc_strdup(ctx
, pdp
->reqpath
);
1168 jucn
->comment
= lp_comment(ctx
, snum
);
1171 if (!jucn
->service_name
|| !jucn
->volume_name
|| ! jucn
->comment
) {
1177 /**********************************************************************
1178 Forms a valid Unix pathname from the junction
1179 **********************************************************************/
1181 static bool junction_to_local_path(const struct junction_map
*jucn
,
1183 connection_struct
**conn_out
,
1189 snum
= lp_servicenumber(jucn
->service_name
);
1193 status
= create_conn_struct_cwd(talloc_tos(),
1194 server_event_context(),
1195 server_messaging_context(),
1197 snum
, lp_pathname(talloc_tos(), snum
), NULL
, oldpath
);
1198 if (!NT_STATUS_IS_OK(status
)) {
1202 *pp_path_out
= talloc_asprintf(*conn_out
,
1204 lp_pathname(talloc_tos(), snum
),
1206 if (!*pp_path_out
) {
1207 vfs_ChDir(*conn_out
, *oldpath
);
1208 SMB_VFS_DISCONNECT(*conn_out
);
1209 conn_free(*conn_out
);
1215 bool create_msdfs_link(const struct junction_map
*jucn
)
1219 char *msdfs_link
= NULL
;
1220 connection_struct
*conn
;
1222 bool insert_comma
= False
;
1225 if(!junction_to_local_path(jucn
, &path
, &conn
, &cwd
)) {
1229 /* Form the msdfs_link contents */
1230 msdfs_link
= talloc_strdup(conn
, "msdfs:");
1234 for(i
=0; i
<jucn
->referral_count
; i
++) {
1235 char *refpath
= jucn
->referral_list
[i
].alternate_path
;
1237 /* Alternate paths always use Windows separators. */
1238 trim_char(refpath
, '\\', '\\');
1239 if(*refpath
== '\0') {
1241 insert_comma
= False
;
1245 if (i
> 0 && insert_comma
) {
1246 msdfs_link
= talloc_asprintf_append_buffer(msdfs_link
,
1250 msdfs_link
= talloc_asprintf_append_buffer(msdfs_link
,
1258 if (!insert_comma
) {
1259 insert_comma
= True
;
1263 DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
1266 if(SMB_VFS_SYMLINK(conn
, msdfs_link
, path
) < 0) {
1267 if (errno
== EEXIST
) {
1268 struct smb_filename
*smb_fname
= NULL
;
1271 status
= create_synthetic_smb_fname(talloc_tos(), path
,
1274 if (!NT_STATUS_IS_OK(status
)) {
1275 errno
= map_errno_from_nt_status(status
);
1279 if(SMB_VFS_UNLINK(conn
, smb_fname
)!=0) {
1280 TALLOC_FREE(smb_fname
);
1283 TALLOC_FREE(smb_fname
);
1285 if (SMB_VFS_SYMLINK(conn
, msdfs_link
, path
) < 0) {
1286 DEBUG(1,("create_msdfs_link: symlink failed "
1287 "%s -> %s\nError: %s\n",
1288 path
, msdfs_link
, strerror(errno
)));
1296 vfs_ChDir(conn
, cwd
);
1297 SMB_VFS_DISCONNECT(conn
);
1302 bool remove_msdfs_link(const struct junction_map
*jucn
)
1306 connection_struct
*conn
;
1308 struct smb_filename
*smb_fname
= NULL
;
1311 if (!junction_to_local_path(jucn
, &path
, &conn
, &cwd
)) {
1315 status
= create_synthetic_smb_fname(talloc_tos(), path
,
1318 if (!NT_STATUS_IS_OK(status
)) {
1319 errno
= map_errno_from_nt_status(status
);
1323 if( SMB_VFS_UNLINK(conn
, smb_fname
) == 0 ) {
1327 TALLOC_FREE(smb_fname
);
1328 vfs_ChDir(conn
, cwd
);
1329 SMB_VFS_DISCONNECT(conn
);
1334 /*********************************************************************
1335 Return the number of DFS links at the root of this share.
1336 *********************************************************************/
1338 static int count_dfs_links(TALLOC_CTX
*ctx
, int snum
)
1342 const char *dname
= NULL
;
1343 char *talloced
= NULL
;
1344 const char *connect_path
= lp_pathname(talloc_tos(), snum
);
1345 const char *msdfs_proxy
= lp_msdfs_proxy(talloc_tos(), snum
);
1346 connection_struct
*conn
;
1350 if(*connect_path
== '\0') {
1355 * Fake up a connection struct for the VFS layer.
1358 status
= create_conn_struct_cwd(talloc_tos(),
1359 server_event_context(),
1360 server_messaging_context(),
1362 snum
, connect_path
, NULL
, &cwd
);
1363 if (!NT_STATUS_IS_OK(status
)) {
1364 DEBUG(3, ("create_conn_struct failed: %s\n",
1365 nt_errstr(status
)));
1369 /* Count a link for the msdfs root - convention */
1372 /* No more links if this is an msdfs proxy. */
1373 if (*msdfs_proxy
!= '\0') {
1377 /* Now enumerate all dfs links */
1378 dirp
= SMB_VFS_OPENDIR(conn
, ".", NULL
, 0);
1383 while ((dname
= vfs_readdirname(conn
, dirp
, NULL
, &talloced
))
1385 if (is_msdfs_link(conn
,
1390 TALLOC_FREE(talloced
);
1393 SMB_VFS_CLOSEDIR(conn
,dirp
);
1396 vfs_ChDir(conn
, cwd
);
1397 SMB_VFS_DISCONNECT(conn
);
1402 /*********************************************************************
1403 *********************************************************************/
1405 static int form_junctions(TALLOC_CTX
*ctx
,
1407 struct junction_map
*jucn
,
1412 const char *dname
= NULL
;
1413 char *talloced
= NULL
;
1414 const char *connect_path
= lp_pathname(talloc_tos(), snum
);
1415 char *service_name
= lp_servicename(talloc_tos(), snum
);
1416 const char *msdfs_proxy
= lp_msdfs_proxy(talloc_tos(), snum
);
1417 connection_struct
*conn
;
1418 struct referral
*ref
= NULL
;
1422 if (jn_remain
== 0) {
1426 if(*connect_path
== '\0') {
1431 * Fake up a connection struct for the VFS layer.
1434 status
= create_conn_struct_cwd(ctx
,
1435 server_event_context(),
1436 server_messaging_context(),
1437 &conn
, snum
, connect_path
, NULL
,
1439 if (!NT_STATUS_IS_OK(status
)) {
1440 DEBUG(3, ("create_conn_struct failed: %s\n",
1441 nt_errstr(status
)));
1445 /* form a junction for the msdfs root - convention
1446 DO NOT REMOVE THIS: NT clients will not work with us
1447 if this is not present
1449 jucn
[cnt
].service_name
= talloc_strdup(ctx
,service_name
);
1450 jucn
[cnt
].volume_name
= talloc_strdup(ctx
, "");
1451 if (!jucn
[cnt
].service_name
|| !jucn
[cnt
].volume_name
) {
1454 jucn
[cnt
].comment
= "";
1455 jucn
[cnt
].referral_count
= 1;
1457 ref
= jucn
[cnt
].referral_list
= talloc_zero(ctx
, struct referral
);
1458 if (jucn
[cnt
].referral_list
== NULL
) {
1463 ref
->ttl
= REFERRAL_TTL
;
1464 if (*msdfs_proxy
!= '\0') {
1465 ref
->alternate_path
= talloc_strdup(ctx
,
1468 ref
->alternate_path
= talloc_asprintf(ctx
,
1470 get_local_machine_name(),
1474 if (!ref
->alternate_path
) {
1479 /* Don't enumerate if we're an msdfs proxy. */
1480 if (*msdfs_proxy
!= '\0') {
1484 /* Now enumerate all dfs links */
1485 dirp
= SMB_VFS_OPENDIR(conn
, ".", NULL
, 0);
1490 while ((dname
= vfs_readdirname(conn
, dirp
, NULL
, &talloced
))
1492 char *link_target
= NULL
;
1493 if (cnt
>= jn_remain
) {
1494 DEBUG(2, ("form_junctions: ran out of MSDFS "
1496 TALLOC_FREE(talloced
);
1499 if (is_msdfs_link_internal(ctx
,
1501 dname
, &link_target
,
1503 if (parse_msdfs_symlink(ctx
,
1505 &jucn
[cnt
].referral_list
,
1506 &jucn
[cnt
].referral_count
)) {
1508 jucn
[cnt
].service_name
= talloc_strdup(ctx
,
1510 jucn
[cnt
].volume_name
= talloc_strdup(ctx
,
1512 if (!jucn
[cnt
].service_name
||
1513 !jucn
[cnt
].volume_name
) {
1514 TALLOC_FREE(talloced
);
1517 jucn
[cnt
].comment
= "";
1520 TALLOC_FREE(link_target
);
1522 TALLOC_FREE(talloced
);
1528 SMB_VFS_CLOSEDIR(conn
,dirp
);
1531 vfs_ChDir(conn
, cwd
);
1536 struct junction_map
*enum_msdfs_links(TALLOC_CTX
*ctx
, size_t *p_num_jn
)
1538 struct junction_map
*jn
= NULL
;
1540 size_t jn_count
= 0;
1544 if(!lp_host_msdfs()) {
1548 /* Ensure all the usershares are loaded. */
1550 load_registry_shares();
1551 sharecount
= load_usershare_shares(NULL
, connections_snum_used
);
1554 for(i
=0;i
< sharecount
;i
++) {
1555 if(lp_msdfs_root(i
)) {
1556 jn_count
+= count_dfs_links(ctx
, i
);
1559 if (jn_count
== 0) {
1562 jn
= talloc_array(ctx
, struct junction_map
, jn_count
);
1566 for(i
=0; i
< sharecount
; i
++) {
1567 if (*p_num_jn
>= jn_count
) {
1570 if(lp_msdfs_root(i
)) {
1571 *p_num_jn
+= form_junctions(ctx
, i
,
1573 jn_count
- *p_num_jn
);
1579 /******************************************************************************
1580 Core function to resolve a dfs pathname possibly containing a wildcard. If
1581 ppath_contains_wcard != NULL, it will be set to true if a wildcard is
1582 detected during dfs resolution.
1583 ******************************************************************************/
1585 NTSTATUS
resolve_dfspath_wcard(TALLOC_CTX
*ctx
,
1586 connection_struct
*conn
,
1588 const char *name_in
,
1590 bool allow_broken_path
,
1592 bool *ppath_contains_wcard
)
1594 bool path_contains_wcard
;
1595 NTSTATUS status
= NT_STATUS_OK
;
1597 if (dfs_pathnames
) {
1598 status
= dfs_redirect(ctx
,
1604 &path_contains_wcard
);
1606 if (NT_STATUS_IS_OK(status
) && ppath_contains_wcard
!= NULL
) {
1607 *ppath_contains_wcard
= path_contains_wcard
;
1611 * Cheat and just return a copy of the in ptr.
1612 * Once srvstr_get_path() uses talloc it'll
1613 * be a talloced ptr anyway.
1615 *pp_name_out
= discard_const_p(char, name_in
);