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(SNUM(conn
)))
151 || (strequal(servicename
, HOMES_NAME
)
152 && strequal(lp_servicename(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.
222 Note: this performs a vfs connect and CHANGES CWD !!!! JRA.
223 *********************************************************/
225 NTSTATUS
create_conn_struct(TALLOC_CTX
*ctx
,
226 struct smbd_server_connection
*sconn
,
227 connection_struct
**pconn
,
230 const struct auth_session_info
*session_info
,
233 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(snum
));
254 return NT_STATUS_NO_MEMORY
;
257 /* needed for smbd_vfs_init() */
259 if (!(conn
->params
= talloc_zero(conn
, struct share_params
))) {
260 DEBUG(0, ("TALLOC failed\n"));
262 return NT_STATUS_NO_MEMORY
;
265 conn
->params
->service
= snum
;
268 conn
->sconn
->num_tcons_open
++;
270 if (session_info
!= NULL
) {
271 conn
->session_info
= copy_session_info(conn
, session_info
);
272 if (conn
->session_info
== NULL
) {
273 DEBUG(0, ("copy_serverinfo failed\n"));
275 return NT_STATUS_NO_MEMORY
;
277 vfs_user
= conn
->session_info
->unix_info
->unix_name
;
279 /* use current authenticated user in absence of session_info */
280 vfs_user
= get_current_username();
283 set_conn_connectpath(conn
, connpath
);
286 * New code to check if there's a share security descripter
287 * added from NT server manager. This is done after the
288 * smb.conf checks are done as we need a uid and token. JRA.
291 if (conn
->session_info
) {
292 share_access_check(conn
->session_info
->security_token
,
293 lp_servicename(snum
), MAXIMUM_ALLOWED_ACCESS
,
294 &conn
->share_access
);
296 if ((conn
->share_access
& FILE_WRITE_DATA
) == 0) {
297 if ((conn
->share_access
& FILE_READ_DATA
) == 0) {
298 /* No access, read or write. */
299 DEBUG(0,("create_conn_struct: connection to %s "
300 "denied due to security "
302 lp_servicename(snum
)));
304 return NT_STATUS_ACCESS_DENIED
;
306 conn
->read_only
= true;
310 conn
->share_access
= 0;
311 conn
->read_only
= true;
314 if (!smbd_vfs_init(conn
)) {
315 NTSTATUS status
= map_nt_error_from_unix(errno
);
316 DEBUG(0,("create_conn_struct: smbd_vfs_init failed.\n"));
321 /* this must be the first filesystem operation that we do */
322 if (SMB_VFS_CONNECT(conn
, lp_servicename(snum
), vfs_user
) < 0) {
323 DEBUG(0,("VFS connect failed!\n"));
325 return NT_STATUS_UNSUCCESSFUL
;
328 conn
->fs_capabilities
= SMB_VFS_FS_CAPABILITIES(conn
, &conn
->ts_res
);
331 * Windows seems to insist on doing trans2getdfsreferral() calls on
332 * the IPC$ share as the anonymous user. If we try to chdir as that
333 * user we will fail.... WTF ? JRA.
336 oldcwd
= vfs_GetWd(ctx
, conn
);
337 if (oldcwd
== NULL
) {
338 NTSTATUS status
= map_nt_error_from_unix(errno
);
339 DEBUG(3, ("vfs_GetWd failed: %s\n", strerror(errno
)));
344 if (vfs_ChDir(conn
,conn
->connectpath
) != 0) {
345 NTSTATUS status
= map_nt_error_from_unix(errno
);
346 DEBUG(3,("create_conn_struct: Can't ChDir to new conn path %s. "
348 conn
->connectpath
, strerror(errno
) ));
359 /**********************************************************************
360 Parse the contents of a symlink to verify if it is an msdfs referral
361 A valid referral is of the form:
363 msdfs:server1\share1,server2\share2
364 msdfs:server1\share1\pathname,server2\share2\pathname
365 msdfs:server1/share1,server2/share2
366 msdfs:server1/share1/pathname,server2/share2/pathname.
368 Note that the alternate paths returned here must be of the canonicalized
372 \server\share\path\to\file,
374 even in posix path mode. This is because we have no knowledge if the
375 server we're referring to understands posix paths.
376 **********************************************************************/
378 static bool parse_msdfs_symlink(TALLOC_CTX
*ctx
,
380 struct referral
**preflist
,
385 char **alt_path
= NULL
;
387 struct referral
*reflist
;
390 temp
= talloc_strdup(ctx
, target
);
394 prot
= strtok_r(temp
, ":", &saveptr
);
396 DEBUG(0,("parse_msdfs_symlink: invalid path !\n"));
400 alt_path
= talloc_array(ctx
, char *, MAX_REFERRAL_COUNT
);
405 /* parse out the alternate paths */
406 while((count
<MAX_REFERRAL_COUNT
) &&
407 ((alt_path
[count
] = strtok_r(NULL
, ",", &saveptr
)) != NULL
)) {
411 DEBUG(10,("parse_msdfs_symlink: count=%d\n", count
));
414 reflist
= *preflist
= talloc_zero_array(ctx
,
415 struct referral
, count
);
416 if(reflist
== NULL
) {
417 TALLOC_FREE(alt_path
);
421 reflist
= *preflist
= NULL
;
424 for(i
=0;i
<count
;i
++) {
427 /* Canonicalize link target.
428 * Replace all /'s in the path by a \ */
429 string_replace(alt_path
[i
], '/', '\\');
431 /* Remove leading '\\'s */
433 while (*p
&& (*p
== '\\')) {
437 reflist
[i
].alternate_path
= talloc_asprintf(ctx
,
440 if (!reflist
[i
].alternate_path
) {
444 reflist
[i
].proximity
= 0;
445 reflist
[i
].ttl
= REFERRAL_TTL
;
446 DEBUG(10, ("parse_msdfs_symlink: Created alt path: %s\n",
447 reflist
[i
].alternate_path
));
452 TALLOC_FREE(alt_path
);
456 /**********************************************************************
457 Returns true if the unix path is a valid msdfs symlink and also
458 returns the target string from inside the link.
459 **********************************************************************/
461 static bool is_msdfs_link_internal(TALLOC_CTX
*ctx
,
462 connection_struct
*conn
,
464 char **pp_link_target
,
465 SMB_STRUCT_STAT
*sbufp
)
467 int referral_len
= 0;
468 #if defined(HAVE_BROKEN_READLINK)
469 char link_target_buf
[PATH_MAX
];
471 char link_target_buf
[7];
474 char *link_target
= NULL
;
475 struct smb_filename smb_fname
;
477 if (pp_link_target
) {
479 link_target
= talloc_array(ctx
, char, bufsize
);
483 *pp_link_target
= link_target
;
485 bufsize
= sizeof(link_target_buf
);
486 link_target
= link_target_buf
;
489 ZERO_STRUCT(smb_fname
);
490 smb_fname
.base_name
= discard_const_p(char, path
);
492 if (SMB_VFS_LSTAT(conn
, &smb_fname
) != 0) {
493 DEBUG(5,("is_msdfs_link_read_target: %s does not exist.\n",
497 if (!S_ISLNK(smb_fname
.st
.st_ex_mode
)) {
498 DEBUG(5,("is_msdfs_link_read_target: %s is not a link.\n",
503 *sbufp
= smb_fname
.st
;
506 referral_len
= SMB_VFS_READLINK(conn
, path
, link_target
, bufsize
- 1);
507 if (referral_len
== -1) {
508 DEBUG(0,("is_msdfs_link_read_target: Error reading "
509 "msdfs link %s: %s\n",
510 path
, strerror(errno
)));
513 link_target
[referral_len
] = '\0';
515 DEBUG(5,("is_msdfs_link_internal: %s -> %s\n",path
,
518 if (!strnequal(link_target
, "msdfs:", 6)) {
525 if (link_target
!= link_target_buf
) {
526 TALLOC_FREE(link_target
);
531 /**********************************************************************
532 Returns true if the unix path is a valid msdfs symlink.
533 **********************************************************************/
535 bool is_msdfs_link(connection_struct
*conn
,
537 SMB_STRUCT_STAT
*sbufp
)
539 return is_msdfs_link_internal(talloc_tos(),
546 /*****************************************************************
547 Used by other functions to decide if a dfs path is remote,
548 and to get the list of referred locations for that remote path.
550 search_flag: For findfirsts, dfs links themselves are not
551 redirected, but paths beyond the links are. For normal smb calls,
552 even dfs links need to be redirected.
554 consumedcntp: how much of the dfs path is being redirected. the client
555 should try the remaining path on the redirected server.
557 If this returns NT_STATUS_PATH_NOT_COVERED the contents of the msdfs
558 link redirect are in targetpath.
559 *****************************************************************/
561 static NTSTATUS
dfs_path_lookup(TALLOC_CTX
*ctx
,
562 connection_struct
*conn
,
563 const char *dfspath
, /* Incoming complete dfs path */
564 const struct dfs_path
*pdp
, /* Parsed out
565 server+share+extrapath. */
566 bool search_flag
, /* Called from a findfirst ? */
568 char **pp_targetpath
)
573 struct smb_filename
*smb_fname
= NULL
;
574 char *canon_dfspath
= NULL
; /* Canonicalized dfs path. (only '/'
577 DEBUG(10,("dfs_path_lookup: Conn path = %s reqpath = %s\n",
578 conn
->connectpath
, pdp
->reqpath
));
581 * Note the unix path conversion here we're doing we
582 * throw away. We're looking for a symlink for a dfs
583 * resolution, if we don't find it we'll do another
584 * unix_convert later in the codepath.
587 status
= unix_convert(ctx
, conn
, pdp
->reqpath
, &smb_fname
,
588 search_flag
? UCF_ALWAYS_ALLOW_WCARD_LCOMP
: 0);
590 if (!NT_STATUS_IS_OK(status
)) {
591 if (!NT_STATUS_EQUAL(status
,
592 NT_STATUS_OBJECT_PATH_NOT_FOUND
)) {
595 if (smb_fname
== NULL
|| smb_fname
->base_name
== NULL
) {
600 /* Optimization - check if we can redirect the whole path. */
602 if (is_msdfs_link_internal(ctx
, conn
, smb_fname
->base_name
,
603 pp_targetpath
, NULL
)) {
605 DEBUG(6,("dfs_path_lookup (FindFirst) No redirection "
606 "for dfs link %s.\n", dfspath
));
607 status
= NT_STATUS_OK
;
611 DEBUG(6,("dfs_path_lookup: %s resolves to a "
612 "valid dfs link %s.\n", dfspath
,
613 pp_targetpath
? *pp_targetpath
: ""));
616 *consumedcntp
= strlen(dfspath
);
618 status
= NT_STATUS_PATH_NOT_COVERED
;
622 /* Prepare to test only for '/' components in the given path,
623 * so if a Windows path replace all '\\' characters with '/'.
624 * For a POSIX DFS path we know all separators are already '/'. */
626 canon_dfspath
= talloc_strdup(ctx
, dfspath
);
627 if (!canon_dfspath
) {
628 status
= NT_STATUS_NO_MEMORY
;
631 if (!pdp
->posix_path
) {
632 string_replace(canon_dfspath
, '\\', '/');
636 * localpath comes out of unix_convert, so it has
637 * no trailing backslash. Make sure that canon_dfspath hasn't either.
638 * Fix for bug #4860 from Jan Martin <Jan.Martin@rwedea.com>.
641 trim_char(canon_dfspath
,0,'/');
644 * Redirect if any component in the path is a link.
645 * We do this by walking backwards through the
646 * local path, chopping off the last component
647 * in both the local path and the canonicalized
648 * DFS path. If we hit a DFS link then we're done.
651 p
= strrchr_m(smb_fname
->base_name
, '/');
653 q
= strrchr_m(canon_dfspath
, '/');
662 if (is_msdfs_link_internal(ctx
, conn
,
663 smb_fname
->base_name
, pp_targetpath
,
665 DEBUG(4, ("dfs_path_lookup: Redirecting %s because "
666 "parent %s is dfs link\n", dfspath
,
667 smb_fname_str_dbg(smb_fname
)));
670 *consumedcntp
= strlen(canon_dfspath
);
671 DEBUG(10, ("dfs_path_lookup: Path consumed: %s "
677 status
= NT_STATUS_PATH_NOT_COVERED
;
681 /* Step back on the filesystem. */
682 p
= strrchr_m(smb_fname
->base_name
, '/');
685 /* And in the canonicalized dfs path. */
686 q
= strrchr_m(canon_dfspath
, '/');
690 status
= NT_STATUS_OK
;
692 TALLOC_FREE(smb_fname
);
696 /*****************************************************************
697 Decides if a dfs pathname should be redirected or not.
698 If not, the pathname is converted to a tcon-relative local unix path
700 search_wcard_flag: this flag performs 2 functions both related
701 to searches. See resolve_dfs_path() and parse_dfs_path_XX()
704 This function can return NT_STATUS_OK, meaning use the returned path as-is
705 (mapped into a local path).
706 or NT_STATUS_NOT_COVERED meaning return a DFS redirect, or
707 any other NT_STATUS error which is a genuine error to be
708 returned to the client.
709 *****************************************************************/
711 static NTSTATUS
dfs_redirect(TALLOC_CTX
*ctx
,
712 connection_struct
*conn
,
714 bool search_wcard_flag
,
715 bool allow_broken_path
,
717 bool *ppath_contains_wcard
)
720 struct dfs_path
*pdp
= talloc(ctx
, struct dfs_path
);
723 return NT_STATUS_NO_MEMORY
;
726 status
= parse_dfs_path(conn
, path_in
, search_wcard_flag
,
727 allow_broken_path
, pdp
,
728 ppath_contains_wcard
);
729 if (!NT_STATUS_IS_OK(status
)) {
734 if (pdp
->reqpath
[0] == '\0') {
736 *pp_path_out
= talloc_strdup(ctx
, "");
738 return NT_STATUS_NO_MEMORY
;
740 DEBUG(5,("dfs_redirect: self-referral.\n"));
744 /* If dfs pathname for a non-dfs share, convert to tcon-relative
745 path and return OK */
747 if (!lp_msdfs_root(SNUM(conn
))) {
748 *pp_path_out
= talloc_strdup(ctx
, pdp
->reqpath
);
751 return NT_STATUS_NO_MEMORY
;
756 /* If it looked like a local path (zero hostname/servicename)
757 * just treat as a tcon-relative path. */
759 if (pdp
->hostname
[0] == '\0' && pdp
->servicename
[0] == '\0') {
760 *pp_path_out
= talloc_strdup(ctx
, pdp
->reqpath
);
763 return NT_STATUS_NO_MEMORY
;
768 if (!( strequal(pdp
->servicename
, lp_servicename(SNUM(conn
)))
769 || (strequal(pdp
->servicename
, HOMES_NAME
)
770 && strequal(lp_servicename(SNUM(conn
)),
771 conn
->session_info
->unix_info
->sanitized_username
) )) ) {
773 /* The given sharename doesn't match this connection. */
776 return NT_STATUS_OBJECT_PATH_NOT_FOUND
;
779 status
= dfs_path_lookup(ctx
, conn
, path_in
, pdp
,
780 search_wcard_flag
, NULL
, NULL
);
781 if (!NT_STATUS_IS_OK(status
)) {
782 if (NT_STATUS_EQUAL(status
, NT_STATUS_PATH_NOT_COVERED
)) {
783 DEBUG(3,("dfs_redirect: Redirecting %s\n", path_in
));
785 DEBUG(10,("dfs_redirect: dfs_path_lookup "
786 "failed for %s with %s\n",
787 path_in
, nt_errstr(status
) ));
792 DEBUG(3,("dfs_redirect: Not redirecting %s.\n", path_in
));
794 /* Form non-dfs tcon-relative path */
795 *pp_path_out
= talloc_strdup(ctx
, pdp
->reqpath
);
798 return NT_STATUS_NO_MEMORY
;
801 DEBUG(3,("dfs_redirect: Path %s converted to non-dfs path %s\n",
808 /**********************************************************************
809 Return a self referral.
810 **********************************************************************/
812 static NTSTATUS
self_ref(TALLOC_CTX
*ctx
,
813 const char *dfs_path
,
814 struct junction_map
*jucn
,
816 bool *self_referralp
)
818 struct referral
*ref
;
820 *self_referralp
= True
;
822 jucn
->referral_count
= 1;
823 if((ref
= talloc_zero(ctx
, struct referral
)) == NULL
) {
824 return NT_STATUS_NO_MEMORY
;
827 ref
->alternate_path
= talloc_strdup(ctx
, dfs_path
);
828 if (!ref
->alternate_path
) {
829 return NT_STATUS_NO_MEMORY
;
832 ref
->ttl
= REFERRAL_TTL
;
833 jucn
->referral_list
= ref
;
834 *consumedcntp
= strlen(dfs_path
);
838 /**********************************************************************
839 Gets valid referrals for a dfs path and fills up the
840 junction_map structure.
841 **********************************************************************/
843 NTSTATUS
get_referred_path(TALLOC_CTX
*ctx
,
844 const char *dfs_path
,
845 struct smbd_server_connection
*sconn
,
846 struct junction_map
*jucn
,
848 bool *self_referralp
)
850 struct connection_struct
*conn
;
851 char *targetpath
= NULL
;
853 NTSTATUS status
= NT_STATUS_NOT_FOUND
;
855 struct dfs_path
*pdp
= talloc(ctx
, struct dfs_path
);
859 return NT_STATUS_NO_MEMORY
;
862 *self_referralp
= False
;
864 status
= parse_dfs_path(NULL
, dfs_path
, False
, !sconn
->using_smb2
,
866 if (!NT_STATUS_IS_OK(status
)) {
870 jucn
->service_name
= talloc_strdup(ctx
, pdp
->servicename
);
871 jucn
->volume_name
= talloc_strdup(ctx
, pdp
->reqpath
);
872 if (!jucn
->service_name
|| !jucn
->volume_name
) {
874 return NT_STATUS_NO_MEMORY
;
877 /* Verify the share is a dfs root */
878 snum
= lp_servicenumber(jucn
->service_name
);
880 char *service_name
= NULL
;
881 if ((snum
= find_service(ctx
, jucn
->service_name
, &service_name
)) < 0) {
882 return NT_STATUS_NOT_FOUND
;
885 return NT_STATUS_NO_MEMORY
;
887 TALLOC_FREE(jucn
->service_name
);
888 jucn
->service_name
= talloc_strdup(ctx
, service_name
);
889 if (!jucn
->service_name
) {
891 return NT_STATUS_NO_MEMORY
;
895 if (!lp_msdfs_root(snum
) && (*lp_msdfs_proxy(snum
) == '\0')) {
896 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not "
898 pdp
->servicename
, dfs_path
));
900 return NT_STATUS_NOT_FOUND
;
904 * Self referrals are tested with a anonymous IPC connection and
905 * a GET_DFS_REFERRAL call to \\server\share. (which means
906 * dp.reqpath[0] points to an empty string). create_conn_struct cd's
907 * into the directory and will fail if it cannot (as the anonymous
908 * user). Cope with this.
911 if (pdp
->reqpath
[0] == '\0') {
913 struct referral
*ref
;
915 if (*lp_msdfs_proxy(snum
) == '\0') {
925 * It's an msdfs proxy share. Redirect to
926 * the configured target share.
929 jucn
->referral_count
= 1;
930 if ((ref
= talloc_zero(ctx
, struct referral
)) == NULL
) {
932 return NT_STATUS_NO_MEMORY
;
935 if (!(tmp
= talloc_strdup(ctx
, lp_msdfs_proxy(snum
)))) {
937 return NT_STATUS_NO_MEMORY
;
940 trim_string(tmp
, "\\", 0);
942 ref
->alternate_path
= talloc_asprintf(ctx
, "\\%s", tmp
);
945 if (!ref
->alternate_path
) {
947 return NT_STATUS_NO_MEMORY
;
950 if (pdp
->reqpath
[0] != '\0') {
951 ref
->alternate_path
= talloc_asprintf_append(
955 if (!ref
->alternate_path
) {
957 return NT_STATUS_NO_MEMORY
;
961 ref
->ttl
= REFERRAL_TTL
;
962 jucn
->referral_list
= ref
;
963 *consumedcntp
= strlen(dfs_path
);
968 status
= create_conn_struct(ctx
, sconn
, &conn
, snum
,
969 lp_pathname(snum
), NULL
, &oldpath
);
970 if (!NT_STATUS_IS_OK(status
)) {
975 /* If this is a DFS path dfs_lookup should return
976 * NT_STATUS_PATH_NOT_COVERED. */
978 status
= dfs_path_lookup(ctx
, conn
, dfs_path
, pdp
,
979 False
, consumedcntp
, &targetpath
);
981 if (!NT_STATUS_EQUAL(status
, NT_STATUS_PATH_NOT_COVERED
)) {
982 DEBUG(3,("get_referred_path: No valid referrals for path %s\n",
987 /* We know this is a valid dfs link. Parse the targetpath. */
988 if (!parse_msdfs_symlink(ctx
, targetpath
,
989 &jucn
->referral_list
,
990 &jucn
->referral_count
)) {
991 DEBUG(3,("get_referred_path: failed to parse symlink "
992 "target %s\n", targetpath
));
993 status
= NT_STATUS_NOT_FOUND
;
997 status
= NT_STATUS_OK
;
999 vfs_ChDir(conn
, oldpath
);
1000 SMB_VFS_DISCONNECT(conn
);
1006 /******************************************************************
1007 Set up the DFS referral for the dfs pathname. This call returns
1008 the amount of the path covered by this server, and where the
1009 client should be redirected to. This is the meat of the
1010 TRANS2_GET_DFS_REFERRAL call.
1011 ******************************************************************/
1013 int setup_dfs_referral(connection_struct
*orig_conn
,
1014 const char *dfs_path
,
1015 int max_referral_level
,
1016 char **ppdata
, NTSTATUS
*pstatus
)
1018 char *pdata
= *ppdata
;
1020 struct dfs_GetDFSReferral
*r
;
1021 DATA_BLOB blob
= data_blob_null
;
1023 enum ndr_err_code ndr_err
;
1025 r
= talloc_zero(talloc_tos(), struct dfs_GetDFSReferral
);
1027 *pstatus
= NT_STATUS_NO_MEMORY
;
1031 r
->in
.req
.max_referral_level
= max_referral_level
;
1032 r
->in
.req
.servername
= talloc_strdup(r
, dfs_path
);
1033 if (r
->in
.req
.servername
== NULL
) {
1035 *pstatus
= NT_STATUS_NO_MEMORY
;
1039 status
= SMB_VFS_GET_DFS_REFERRALS(orig_conn
, r
);
1040 if (!NT_STATUS_IS_OK(status
)) {
1046 ndr_err
= ndr_push_struct_blob(&blob
, r
,
1048 (ndr_push_flags_fn_t
)ndr_push_dfs_referral_resp
);
1049 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
1051 *pstatus
= NT_STATUS_INVALID_PARAMETER
;
1055 pdata
= (char *)SMB_REALLOC(pdata
, blob
.length
);
1058 DEBUG(0,("referral setup:"
1059 "malloc failed for Realloc!\n"));
1063 reply_size
= blob
.length
;
1064 memcpy(pdata
, blob
.data
, blob
.length
);
1067 *pstatus
= NT_STATUS_OK
;
1071 /**********************************************************************
1072 The following functions are called by the NETDFS RPC pipe functions
1073 **********************************************************************/
1075 /*********************************************************************
1076 Creates a junction structure from a DFS pathname
1077 **********************************************************************/
1079 bool create_junction(TALLOC_CTX
*ctx
,
1080 const char *dfs_path
,
1081 bool allow_broken_path
,
1082 struct junction_map
*jucn
)
1086 struct dfs_path
*pdp
= talloc(ctx
,struct dfs_path
);
1092 status
= parse_dfs_path(NULL
, dfs_path
, False
, allow_broken_path
,
1094 if (!NT_STATUS_IS_OK(status
)) {
1098 /* check if path is dfs : validate first token */
1099 if (!is_myname_or_ipaddr(pdp
->hostname
)) {
1100 DEBUG(4,("create_junction: Invalid hostname %s "
1102 pdp
->hostname
, dfs_path
));
1107 /* Check for a non-DFS share */
1108 snum
= lp_servicenumber(pdp
->servicename
);
1110 if(snum
< 0 || !lp_msdfs_root(snum
)) {
1111 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1117 jucn
->service_name
= talloc_strdup(ctx
, pdp
->servicename
);
1118 jucn
->volume_name
= talloc_strdup(ctx
, pdp
->reqpath
);
1119 jucn
->comment
= talloc_strdup(ctx
, lp_comment(snum
));
1122 if (!jucn
->service_name
|| !jucn
->volume_name
|| ! jucn
->comment
) {
1128 /**********************************************************************
1129 Forms a valid Unix pathname from the junction
1130 **********************************************************************/
1132 static bool junction_to_local_path(const struct junction_map
*jucn
,
1134 connection_struct
**conn_out
,
1140 snum
= lp_servicenumber(jucn
->service_name
);
1144 status
= create_conn_struct(talloc_tos(), smbd_server_conn
, conn_out
,
1145 snum
, lp_pathname(snum
), NULL
, oldpath
);
1146 if (!NT_STATUS_IS_OK(status
)) {
1150 *pp_path_out
= talloc_asprintf(*conn_out
,
1154 if (!*pp_path_out
) {
1155 vfs_ChDir(*conn_out
, *oldpath
);
1156 SMB_VFS_DISCONNECT(*conn_out
);
1157 conn_free(*conn_out
);
1163 bool create_msdfs_link(const struct junction_map
*jucn
)
1167 char *msdfs_link
= NULL
;
1168 connection_struct
*conn
;
1170 bool insert_comma
= False
;
1173 if(!junction_to_local_path(jucn
, &path
, &conn
, &cwd
)) {
1177 /* Form the msdfs_link contents */
1178 msdfs_link
= talloc_strdup(conn
, "msdfs:");
1182 for(i
=0; i
<jucn
->referral_count
; i
++) {
1183 char *refpath
= jucn
->referral_list
[i
].alternate_path
;
1185 /* Alternate paths always use Windows separators. */
1186 trim_char(refpath
, '\\', '\\');
1187 if(*refpath
== '\0') {
1189 insert_comma
= False
;
1193 if (i
> 0 && insert_comma
) {
1194 msdfs_link
= talloc_asprintf_append_buffer(msdfs_link
,
1198 msdfs_link
= talloc_asprintf_append_buffer(msdfs_link
,
1206 if (!insert_comma
) {
1207 insert_comma
= True
;
1211 DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
1214 if(SMB_VFS_SYMLINK(conn
, msdfs_link
, path
) < 0) {
1215 if (errno
== EEXIST
) {
1216 struct smb_filename
*smb_fname
= NULL
;
1219 status
= create_synthetic_smb_fname(talloc_tos(), path
,
1222 if (!NT_STATUS_IS_OK(status
)) {
1223 errno
= map_errno_from_nt_status(status
);
1227 if(SMB_VFS_UNLINK(conn
, smb_fname
)!=0) {
1228 TALLOC_FREE(smb_fname
);
1231 TALLOC_FREE(smb_fname
);
1233 if (SMB_VFS_SYMLINK(conn
, msdfs_link
, path
) < 0) {
1234 DEBUG(1,("create_msdfs_link: symlink failed "
1235 "%s -> %s\nError: %s\n",
1236 path
, msdfs_link
, strerror(errno
)));
1244 vfs_ChDir(conn
, cwd
);
1245 SMB_VFS_DISCONNECT(conn
);
1250 bool remove_msdfs_link(const struct junction_map
*jucn
)
1254 connection_struct
*conn
;
1256 struct smb_filename
*smb_fname
= NULL
;
1259 if (!junction_to_local_path(jucn
, &path
, &conn
, &cwd
)) {
1263 status
= create_synthetic_smb_fname(talloc_tos(), path
,
1266 if (!NT_STATUS_IS_OK(status
)) {
1267 errno
= map_errno_from_nt_status(status
);
1271 if( SMB_VFS_UNLINK(conn
, smb_fname
) == 0 ) {
1275 TALLOC_FREE(smb_fname
);
1276 vfs_ChDir(conn
, cwd
);
1277 SMB_VFS_DISCONNECT(conn
);
1282 /*********************************************************************
1283 Return the number of DFS links at the root of this share.
1284 *********************************************************************/
1286 static int count_dfs_links(TALLOC_CTX
*ctx
, int snum
)
1289 SMB_STRUCT_DIR
*dirp
= NULL
;
1290 const char *dname
= NULL
;
1291 char *talloced
= NULL
;
1292 const char *connect_path
= lp_pathname(snum
);
1293 const char *msdfs_proxy
= lp_msdfs_proxy(snum
);
1294 connection_struct
*conn
;
1298 if(*connect_path
== '\0') {
1303 * Fake up a connection struct for the VFS layer.
1306 status
= create_conn_struct(talloc_tos(), smbd_server_conn
, &conn
,
1307 snum
, connect_path
, NULL
, &cwd
);
1308 if (!NT_STATUS_IS_OK(status
)) {
1309 DEBUG(3, ("create_conn_struct failed: %s\n",
1310 nt_errstr(status
)));
1314 /* Count a link for the msdfs root - convention */
1317 /* No more links if this is an msdfs proxy. */
1318 if (*msdfs_proxy
!= '\0') {
1322 /* Now enumerate all dfs links */
1323 dirp
= SMB_VFS_OPENDIR(conn
, ".", NULL
, 0);
1328 while ((dname
= vfs_readdirname(conn
, dirp
, NULL
, &talloced
))
1330 if (is_msdfs_link(conn
,
1335 TALLOC_FREE(talloced
);
1338 SMB_VFS_CLOSEDIR(conn
,dirp
);
1341 vfs_ChDir(conn
, cwd
);
1342 SMB_VFS_DISCONNECT(conn
);
1347 /*********************************************************************
1348 *********************************************************************/
1350 static int form_junctions(TALLOC_CTX
*ctx
,
1352 struct junction_map
*jucn
,
1356 SMB_STRUCT_DIR
*dirp
= NULL
;
1357 const char *dname
= NULL
;
1358 char *talloced
= NULL
;
1359 const char *connect_path
= lp_pathname(snum
);
1360 char *service_name
= lp_servicename(snum
);
1361 const char *msdfs_proxy
= lp_msdfs_proxy(snum
);
1362 connection_struct
*conn
;
1363 struct referral
*ref
= NULL
;
1367 if (jn_remain
== 0) {
1371 if(*connect_path
== '\0') {
1376 * Fake up a connection struct for the VFS layer.
1379 status
= create_conn_struct(ctx
, smbd_server_conn
, &conn
, snum
, connect_path
, NULL
,
1381 if (!NT_STATUS_IS_OK(status
)) {
1382 DEBUG(3, ("create_conn_struct failed: %s\n",
1383 nt_errstr(status
)));
1387 /* form a junction for the msdfs root - convention
1388 DO NOT REMOVE THIS: NT clients will not work with us
1389 if this is not present
1391 jucn
[cnt
].service_name
= talloc_strdup(ctx
,service_name
);
1392 jucn
[cnt
].volume_name
= talloc_strdup(ctx
, "");
1393 if (!jucn
[cnt
].service_name
|| !jucn
[cnt
].volume_name
) {
1396 jucn
[cnt
].comment
= "";
1397 jucn
[cnt
].referral_count
= 1;
1399 ref
= jucn
[cnt
].referral_list
= talloc_zero(ctx
, struct referral
);
1400 if (jucn
[cnt
].referral_list
== NULL
) {
1405 ref
->ttl
= REFERRAL_TTL
;
1406 if (*msdfs_proxy
!= '\0') {
1407 ref
->alternate_path
= talloc_strdup(ctx
,
1410 ref
->alternate_path
= talloc_asprintf(ctx
,
1412 get_local_machine_name(),
1416 if (!ref
->alternate_path
) {
1421 /* Don't enumerate if we're an msdfs proxy. */
1422 if (*msdfs_proxy
!= '\0') {
1426 /* Now enumerate all dfs links */
1427 dirp
= SMB_VFS_OPENDIR(conn
, ".", NULL
, 0);
1432 while ((dname
= vfs_readdirname(conn
, dirp
, NULL
, &talloced
))
1434 char *link_target
= NULL
;
1435 if (cnt
>= jn_remain
) {
1436 DEBUG(2, ("form_junctions: ran out of MSDFS "
1438 TALLOC_FREE(talloced
);
1441 if (is_msdfs_link_internal(ctx
,
1443 dname
, &link_target
,
1445 if (parse_msdfs_symlink(ctx
,
1447 &jucn
[cnt
].referral_list
,
1448 &jucn
[cnt
].referral_count
)) {
1450 jucn
[cnt
].service_name
= talloc_strdup(ctx
,
1452 jucn
[cnt
].volume_name
= talloc_strdup(ctx
,
1454 if (!jucn
[cnt
].service_name
||
1455 !jucn
[cnt
].volume_name
) {
1456 TALLOC_FREE(talloced
);
1459 jucn
[cnt
].comment
= "";
1462 TALLOC_FREE(link_target
);
1464 TALLOC_FREE(talloced
);
1470 SMB_VFS_CLOSEDIR(conn
,dirp
);
1473 vfs_ChDir(conn
, cwd
);
1478 struct junction_map
*enum_msdfs_links(struct smbd_server_connection
*sconn
,
1479 TALLOC_CTX
*ctx
, size_t *p_num_jn
)
1481 struct junction_map
*jn
= NULL
;
1483 size_t jn_count
= 0;
1487 if(!lp_host_msdfs()) {
1491 /* Ensure all the usershares are loaded. */
1493 load_registry_shares();
1494 sharecount
= load_usershare_shares(sconn
);
1497 for(i
=0;i
< sharecount
;i
++) {
1498 if(lp_msdfs_root(i
)) {
1499 jn_count
+= count_dfs_links(ctx
, i
);
1502 if (jn_count
== 0) {
1505 jn
= talloc_array(ctx
, struct junction_map
, jn_count
);
1509 for(i
=0; i
< sharecount
; i
++) {
1510 if (*p_num_jn
>= jn_count
) {
1513 if(lp_msdfs_root(i
)) {
1514 *p_num_jn
+= form_junctions(ctx
, i
,
1516 jn_count
- *p_num_jn
);
1522 /******************************************************************************
1523 Core function to resolve a dfs pathname possibly containing a wildcard. If
1524 ppath_contains_wcard != NULL, it will be set to true if a wildcard is
1525 detected during dfs resolution.
1526 ******************************************************************************/
1528 NTSTATUS
resolve_dfspath_wcard(TALLOC_CTX
*ctx
,
1529 connection_struct
*conn
,
1531 const char *name_in
,
1534 bool *ppath_contains_wcard
)
1536 bool path_contains_wcard
;
1537 NTSTATUS status
= NT_STATUS_OK
;
1539 if (dfs_pathnames
) {
1540 status
= dfs_redirect(ctx
,
1544 !smbd_server_conn
->using_smb2
,
1546 &path_contains_wcard
);
1548 if (NT_STATUS_IS_OK(status
) && ppath_contains_wcard
!= NULL
) {
1549 *ppath_contains_wcard
= path_contains_wcard
;
1553 * Cheat and just return a copy of the in ptr.
1554 * Once srvstr_get_path() uses talloc it'll
1555 * be a talloced ptr anyway.
1557 *pp_name_out
= discard_const_p(char, name_in
);