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
;
266 conn
->cnum
= (unsigned)-1;
269 DLIST_ADD(sconn
->connections
, conn
);
270 conn
->sconn
->num_connections
++;
272 if (session_info
!= NULL
) {
273 conn
->session_info
= copy_session_info(conn
, session_info
);
274 if (conn
->session_info
== NULL
) {
275 DEBUG(0, ("copy_serverinfo failed\n"));
277 return NT_STATUS_NO_MEMORY
;
279 vfs_user
= conn
->session_info
->unix_info
->unix_name
;
281 /* use current authenticated user in absence of session_info */
282 vfs_user
= get_current_username();
285 set_conn_connectpath(conn
, connpath
);
288 * New code to check if there's a share security descripter
289 * added from NT server manager. This is done after the
290 * smb.conf checks are done as we need a uid and token. JRA.
293 if (conn
->session_info
) {
294 share_access_check(conn
->session_info
->security_token
,
295 lp_servicename(snum
), MAXIMUM_ALLOWED_ACCESS
,
296 &conn
->share_access
);
298 if ((conn
->share_access
& FILE_WRITE_DATA
) == 0) {
299 if ((conn
->share_access
& FILE_READ_DATA
) == 0) {
300 /* No access, read or write. */
301 DEBUG(0,("create_conn_struct: connection to %s "
302 "denied due to security "
304 lp_servicename(snum
)));
306 return NT_STATUS_ACCESS_DENIED
;
308 conn
->read_only
= true;
312 conn
->share_access
= 0;
313 conn
->read_only
= true;
316 if (!smbd_vfs_init(conn
)) {
317 NTSTATUS status
= map_nt_error_from_unix(errno
);
318 DEBUG(0,("create_conn_struct: smbd_vfs_init failed.\n"));
323 /* this must be the first filesystem operation that we do */
324 if (SMB_VFS_CONNECT(conn
, lp_servicename(snum
), vfs_user
) < 0) {
325 DEBUG(0,("VFS connect failed!\n"));
327 return NT_STATUS_UNSUCCESSFUL
;
330 conn
->fs_capabilities
= SMB_VFS_FS_CAPABILITIES(conn
, &conn
->ts_res
);
333 * Windows seems to insist on doing trans2getdfsreferral() calls on
334 * the IPC$ share as the anonymous user. If we try to chdir as that
335 * user we will fail.... WTF ? JRA.
338 oldcwd
= vfs_GetWd(ctx
, conn
);
339 if (oldcwd
== NULL
) {
340 NTSTATUS status
= map_nt_error_from_unix(errno
);
341 DEBUG(3, ("vfs_GetWd failed: %s\n", strerror(errno
)));
346 if (vfs_ChDir(conn
,conn
->connectpath
) != 0) {
347 NTSTATUS status
= map_nt_error_from_unix(errno
);
348 DEBUG(3,("create_conn_struct: Can't ChDir to new conn path %s. "
350 conn
->connectpath
, strerror(errno
) ));
361 /**********************************************************************
362 Parse the contents of a symlink to verify if it is an msdfs referral
363 A valid referral is of the form:
365 msdfs:server1\share1,server2\share2
366 msdfs:server1\share1\pathname,server2\share2\pathname
367 msdfs:server1/share1,server2/share2
368 msdfs:server1/share1/pathname,server2/share2/pathname.
370 Note that the alternate paths returned here must be of the canonicalized
374 \server\share\path\to\file,
376 even in posix path mode. This is because we have no knowledge if the
377 server we're referring to understands posix paths.
378 **********************************************************************/
380 static bool parse_msdfs_symlink(TALLOC_CTX
*ctx
,
382 struct referral
**preflist
,
387 char **alt_path
= NULL
;
389 struct referral
*reflist
;
392 temp
= talloc_strdup(ctx
, target
);
396 prot
= strtok_r(temp
, ":", &saveptr
);
398 DEBUG(0,("parse_msdfs_symlink: invalid path !\n"));
402 alt_path
= talloc_array(ctx
, char *, MAX_REFERRAL_COUNT
);
407 /* parse out the alternate paths */
408 while((count
<MAX_REFERRAL_COUNT
) &&
409 ((alt_path
[count
] = strtok_r(NULL
, ",", &saveptr
)) != NULL
)) {
413 DEBUG(10,("parse_msdfs_symlink: count=%d\n", count
));
416 reflist
= *preflist
= talloc_zero_array(ctx
,
417 struct referral
, count
);
418 if(reflist
== NULL
) {
419 TALLOC_FREE(alt_path
);
423 reflist
= *preflist
= NULL
;
426 for(i
=0;i
<count
;i
++) {
429 /* Canonicalize link target.
430 * Replace all /'s in the path by a \ */
431 string_replace(alt_path
[i
], '/', '\\');
433 /* Remove leading '\\'s */
435 while (*p
&& (*p
== '\\')) {
439 reflist
[i
].alternate_path
= talloc_asprintf(ctx
,
442 if (!reflist
[i
].alternate_path
) {
446 reflist
[i
].proximity
= 0;
447 reflist
[i
].ttl
= REFERRAL_TTL
;
448 DEBUG(10, ("parse_msdfs_symlink: Created alt path: %s\n",
449 reflist
[i
].alternate_path
));
454 TALLOC_FREE(alt_path
);
458 /**********************************************************************
459 Returns true if the unix path is a valid msdfs symlink and also
460 returns the target string from inside the link.
461 **********************************************************************/
463 static bool is_msdfs_link_internal(TALLOC_CTX
*ctx
,
464 connection_struct
*conn
,
466 char **pp_link_target
,
467 SMB_STRUCT_STAT
*sbufp
)
469 int referral_len
= 0;
470 #if defined(HAVE_BROKEN_READLINK)
471 char link_target_buf
[PATH_MAX
];
473 char link_target_buf
[7];
476 char *link_target
= NULL
;
477 struct smb_filename smb_fname
;
479 if (pp_link_target
) {
481 link_target
= talloc_array(ctx
, char, bufsize
);
485 *pp_link_target
= link_target
;
487 bufsize
= sizeof(link_target_buf
);
488 link_target
= link_target_buf
;
491 ZERO_STRUCT(smb_fname
);
492 smb_fname
.base_name
= discard_const_p(char, path
);
494 if (SMB_VFS_LSTAT(conn
, &smb_fname
) != 0) {
495 DEBUG(5,("is_msdfs_link_read_target: %s does not exist.\n",
499 if (!S_ISLNK(smb_fname
.st
.st_ex_mode
)) {
500 DEBUG(5,("is_msdfs_link_read_target: %s is not a link.\n",
505 *sbufp
= smb_fname
.st
;
508 referral_len
= SMB_VFS_READLINK(conn
, path
, link_target
, bufsize
- 1);
509 if (referral_len
== -1) {
510 DEBUG(0,("is_msdfs_link_read_target: Error reading "
511 "msdfs link %s: %s\n",
512 path
, strerror(errno
)));
515 link_target
[referral_len
] = '\0';
517 DEBUG(5,("is_msdfs_link_internal: %s -> %s\n",path
,
520 if (!strnequal(link_target
, "msdfs:", 6)) {
527 if (link_target
!= link_target_buf
) {
528 TALLOC_FREE(link_target
);
533 /**********************************************************************
534 Returns true if the unix path is a valid msdfs symlink.
535 **********************************************************************/
537 bool is_msdfs_link(connection_struct
*conn
,
539 SMB_STRUCT_STAT
*sbufp
)
541 return is_msdfs_link_internal(talloc_tos(),
548 /*****************************************************************
549 Used by other functions to decide if a dfs path is remote,
550 and to get the list of referred locations for that remote path.
552 search_flag: For findfirsts, dfs links themselves are not
553 redirected, but paths beyond the links are. For normal smb calls,
554 even dfs links need to be redirected.
556 consumedcntp: how much of the dfs path is being redirected. the client
557 should try the remaining path on the redirected server.
559 If this returns NT_STATUS_PATH_NOT_COVERED the contents of the msdfs
560 link redirect are in targetpath.
561 *****************************************************************/
563 static NTSTATUS
dfs_path_lookup(TALLOC_CTX
*ctx
,
564 connection_struct
*conn
,
565 const char *dfspath
, /* Incoming complete dfs path */
566 const struct dfs_path
*pdp
, /* Parsed out
567 server+share+extrapath. */
568 bool search_flag
, /* Called from a findfirst ? */
570 char **pp_targetpath
)
575 struct smb_filename
*smb_fname
= NULL
;
576 char *canon_dfspath
= NULL
; /* Canonicalized dfs path. (only '/'
579 DEBUG(10,("dfs_path_lookup: Conn path = %s reqpath = %s\n",
580 conn
->connectpath
, pdp
->reqpath
));
583 * Note the unix path conversion here we're doing we
584 * throw away. We're looking for a symlink for a dfs
585 * resolution, if we don't find it we'll do another
586 * unix_convert later in the codepath.
589 status
= unix_convert(ctx
, conn
, pdp
->reqpath
, &smb_fname
,
590 search_flag
? UCF_ALWAYS_ALLOW_WCARD_LCOMP
: 0);
592 if (!NT_STATUS_IS_OK(status
)) {
593 if (!NT_STATUS_EQUAL(status
,
594 NT_STATUS_OBJECT_PATH_NOT_FOUND
)) {
597 if (smb_fname
== NULL
|| smb_fname
->base_name
== NULL
) {
602 /* Optimization - check if we can redirect the whole path. */
604 if (is_msdfs_link_internal(ctx
, conn
, smb_fname
->base_name
,
605 pp_targetpath
, NULL
)) {
607 DEBUG(6,("dfs_path_lookup (FindFirst) No redirection "
608 "for dfs link %s.\n", dfspath
));
609 status
= NT_STATUS_OK
;
613 DEBUG(6,("dfs_path_lookup: %s resolves to a "
614 "valid dfs link %s.\n", dfspath
,
615 pp_targetpath
? *pp_targetpath
: ""));
618 *consumedcntp
= strlen(dfspath
);
620 status
= NT_STATUS_PATH_NOT_COVERED
;
624 /* Prepare to test only for '/' components in the given path,
625 * so if a Windows path replace all '\\' characters with '/'.
626 * For a POSIX DFS path we know all separators are already '/'. */
628 canon_dfspath
= talloc_strdup(ctx
, dfspath
);
629 if (!canon_dfspath
) {
630 status
= NT_STATUS_NO_MEMORY
;
633 if (!pdp
->posix_path
) {
634 string_replace(canon_dfspath
, '\\', '/');
638 * localpath comes out of unix_convert, so it has
639 * no trailing backslash. Make sure that canon_dfspath hasn't either.
640 * Fix for bug #4860 from Jan Martin <Jan.Martin@rwedea.com>.
643 trim_char(canon_dfspath
,0,'/');
646 * Redirect if any component in the path is a link.
647 * We do this by walking backwards through the
648 * local path, chopping off the last component
649 * in both the local path and the canonicalized
650 * DFS path. If we hit a DFS link then we're done.
653 p
= strrchr_m(smb_fname
->base_name
, '/');
655 q
= strrchr_m(canon_dfspath
, '/');
664 if (is_msdfs_link_internal(ctx
, conn
,
665 smb_fname
->base_name
, pp_targetpath
,
667 DEBUG(4, ("dfs_path_lookup: Redirecting %s because "
668 "parent %s is dfs link\n", dfspath
,
669 smb_fname_str_dbg(smb_fname
)));
672 *consumedcntp
= strlen(canon_dfspath
);
673 DEBUG(10, ("dfs_path_lookup: Path consumed: %s "
679 status
= NT_STATUS_PATH_NOT_COVERED
;
683 /* Step back on the filesystem. */
684 p
= strrchr_m(smb_fname
->base_name
, '/');
687 /* And in the canonicalized dfs path. */
688 q
= strrchr_m(canon_dfspath
, '/');
692 status
= NT_STATUS_OK
;
694 TALLOC_FREE(smb_fname
);
698 /*****************************************************************
699 Decides if a dfs pathname should be redirected or not.
700 If not, the pathname is converted to a tcon-relative local unix path
702 search_wcard_flag: this flag performs 2 functions both related
703 to searches. See resolve_dfs_path() and parse_dfs_path_XX()
706 This function can return NT_STATUS_OK, meaning use the returned path as-is
707 (mapped into a local path).
708 or NT_STATUS_NOT_COVERED meaning return a DFS redirect, or
709 any other NT_STATUS error which is a genuine error to be
710 returned to the client.
711 *****************************************************************/
713 static NTSTATUS
dfs_redirect(TALLOC_CTX
*ctx
,
714 connection_struct
*conn
,
716 bool search_wcard_flag
,
717 bool allow_broken_path
,
719 bool *ppath_contains_wcard
)
722 struct dfs_path
*pdp
= talloc(ctx
, struct dfs_path
);
725 return NT_STATUS_NO_MEMORY
;
728 status
= parse_dfs_path(conn
, path_in
, search_wcard_flag
,
729 allow_broken_path
, pdp
,
730 ppath_contains_wcard
);
731 if (!NT_STATUS_IS_OK(status
)) {
736 if (pdp
->reqpath
[0] == '\0') {
738 *pp_path_out
= talloc_strdup(ctx
, "");
740 return NT_STATUS_NO_MEMORY
;
742 DEBUG(5,("dfs_redirect: self-referral.\n"));
746 /* If dfs pathname for a non-dfs share, convert to tcon-relative
747 path and return OK */
749 if (!lp_msdfs_root(SNUM(conn
))) {
750 *pp_path_out
= talloc_strdup(ctx
, pdp
->reqpath
);
753 return NT_STATUS_NO_MEMORY
;
758 /* If it looked like a local path (zero hostname/servicename)
759 * just treat as a tcon-relative path. */
761 if (pdp
->hostname
[0] == '\0' && pdp
->servicename
[0] == '\0') {
762 *pp_path_out
= talloc_strdup(ctx
, pdp
->reqpath
);
765 return NT_STATUS_NO_MEMORY
;
770 if (!( strequal(pdp
->servicename
, lp_servicename(SNUM(conn
)))
771 || (strequal(pdp
->servicename
, HOMES_NAME
)
772 && strequal(lp_servicename(SNUM(conn
)),
773 conn
->session_info
->unix_info
->sanitized_username
) )) ) {
775 /* The given sharename doesn't match this connection. */
778 return NT_STATUS_OBJECT_PATH_NOT_FOUND
;
781 status
= dfs_path_lookup(ctx
, conn
, path_in
, pdp
,
782 search_wcard_flag
, NULL
, NULL
);
783 if (!NT_STATUS_IS_OK(status
)) {
784 if (NT_STATUS_EQUAL(status
, NT_STATUS_PATH_NOT_COVERED
)) {
785 DEBUG(3,("dfs_redirect: Redirecting %s\n", path_in
));
787 DEBUG(10,("dfs_redirect: dfs_path_lookup "
788 "failed for %s with %s\n",
789 path_in
, nt_errstr(status
) ));
794 DEBUG(3,("dfs_redirect: Not redirecting %s.\n", path_in
));
796 /* Form non-dfs tcon-relative path */
797 *pp_path_out
= talloc_strdup(ctx
, pdp
->reqpath
);
800 return NT_STATUS_NO_MEMORY
;
803 DEBUG(3,("dfs_redirect: Path %s converted to non-dfs path %s\n",
810 /**********************************************************************
811 Return a self referral.
812 **********************************************************************/
814 static NTSTATUS
self_ref(TALLOC_CTX
*ctx
,
815 const char *dfs_path
,
816 struct junction_map
*jucn
,
818 bool *self_referralp
)
820 struct referral
*ref
;
822 *self_referralp
= True
;
824 jucn
->referral_count
= 1;
825 if((ref
= talloc_zero(ctx
, struct referral
)) == NULL
) {
826 return NT_STATUS_NO_MEMORY
;
829 ref
->alternate_path
= talloc_strdup(ctx
, dfs_path
);
830 if (!ref
->alternate_path
) {
831 return NT_STATUS_NO_MEMORY
;
834 ref
->ttl
= REFERRAL_TTL
;
835 jucn
->referral_list
= ref
;
836 *consumedcntp
= strlen(dfs_path
);
840 /**********************************************************************
841 Gets valid referrals for a dfs path and fills up the
842 junction_map structure.
843 **********************************************************************/
845 NTSTATUS
get_referred_path(TALLOC_CTX
*ctx
,
846 const char *dfs_path
,
847 struct smbd_server_connection
*sconn
,
848 struct junction_map
*jucn
,
850 bool *self_referralp
)
852 struct connection_struct
*conn
;
853 char *targetpath
= NULL
;
855 NTSTATUS status
= NT_STATUS_NOT_FOUND
;
857 struct dfs_path
*pdp
= talloc(ctx
, struct dfs_path
);
861 return NT_STATUS_NO_MEMORY
;
864 *self_referralp
= False
;
866 status
= parse_dfs_path(NULL
, dfs_path
, False
, !sconn
->using_smb2
,
868 if (!NT_STATUS_IS_OK(status
)) {
872 jucn
->service_name
= talloc_strdup(ctx
, pdp
->servicename
);
873 jucn
->volume_name
= talloc_strdup(ctx
, pdp
->reqpath
);
874 if (!jucn
->service_name
|| !jucn
->volume_name
) {
876 return NT_STATUS_NO_MEMORY
;
879 /* Verify the share is a dfs root */
880 snum
= lp_servicenumber(jucn
->service_name
);
882 char *service_name
= NULL
;
883 if ((snum
= find_service(ctx
, jucn
->service_name
, &service_name
)) < 0) {
884 return NT_STATUS_NOT_FOUND
;
887 return NT_STATUS_NO_MEMORY
;
889 TALLOC_FREE(jucn
->service_name
);
890 jucn
->service_name
= talloc_strdup(ctx
, service_name
);
891 if (!jucn
->service_name
) {
893 return NT_STATUS_NO_MEMORY
;
897 if (!lp_msdfs_root(snum
) && (*lp_msdfs_proxy(snum
) == '\0')) {
898 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not "
900 pdp
->servicename
, dfs_path
));
902 return NT_STATUS_NOT_FOUND
;
906 * Self referrals are tested with a anonymous IPC connection and
907 * a GET_DFS_REFERRAL call to \\server\share. (which means
908 * dp.reqpath[0] points to an empty string). create_conn_struct cd's
909 * into the directory and will fail if it cannot (as the anonymous
910 * user). Cope with this.
913 if (pdp
->reqpath
[0] == '\0') {
915 struct referral
*ref
;
917 if (*lp_msdfs_proxy(snum
) == '\0') {
927 * It's an msdfs proxy share. Redirect to
928 * the configured target share.
931 jucn
->referral_count
= 1;
932 if ((ref
= talloc_zero(ctx
, struct referral
)) == NULL
) {
934 return NT_STATUS_NO_MEMORY
;
937 if (!(tmp
= talloc_strdup(ctx
, lp_msdfs_proxy(snum
)))) {
939 return NT_STATUS_NO_MEMORY
;
942 trim_string(tmp
, "\\", 0);
944 ref
->alternate_path
= talloc_asprintf(ctx
, "\\%s", tmp
);
947 if (!ref
->alternate_path
) {
949 return NT_STATUS_NO_MEMORY
;
952 if (pdp
->reqpath
[0] != '\0') {
953 ref
->alternate_path
= talloc_asprintf_append(
957 if (!ref
->alternate_path
) {
959 return NT_STATUS_NO_MEMORY
;
963 ref
->ttl
= REFERRAL_TTL
;
964 jucn
->referral_list
= ref
;
965 *consumedcntp
= strlen(dfs_path
);
970 status
= create_conn_struct(ctx
, sconn
, &conn
, snum
,
971 lp_pathname(snum
), NULL
, &oldpath
);
972 if (!NT_STATUS_IS_OK(status
)) {
977 /* If this is a DFS path dfs_lookup should return
978 * NT_STATUS_PATH_NOT_COVERED. */
980 status
= dfs_path_lookup(ctx
, conn
, dfs_path
, pdp
,
981 False
, consumedcntp
, &targetpath
);
983 if (!NT_STATUS_EQUAL(status
, NT_STATUS_PATH_NOT_COVERED
)) {
984 DEBUG(3,("get_referred_path: No valid referrals for path %s\n",
989 /* We know this is a valid dfs link. Parse the targetpath. */
990 if (!parse_msdfs_symlink(ctx
, targetpath
,
991 &jucn
->referral_list
,
992 &jucn
->referral_count
)) {
993 DEBUG(3,("get_referred_path: failed to parse symlink "
994 "target %s\n", targetpath
));
995 status
= NT_STATUS_NOT_FOUND
;
999 status
= NT_STATUS_OK
;
1001 vfs_ChDir(conn
, oldpath
);
1002 SMB_VFS_DISCONNECT(conn
);
1008 /******************************************************************
1009 Set up the DFS referral for the dfs pathname. This call returns
1010 the amount of the path covered by this server, and where the
1011 client should be redirected to. This is the meat of the
1012 TRANS2_GET_DFS_REFERRAL call.
1013 ******************************************************************/
1015 int setup_dfs_referral(connection_struct
*orig_conn
,
1016 const char *dfs_path
,
1017 int max_referral_level
,
1018 char **ppdata
, NTSTATUS
*pstatus
)
1020 char *pdata
= *ppdata
;
1022 struct dfs_GetDFSReferral
*r
;
1023 DATA_BLOB blob
= data_blob_null
;
1025 enum ndr_err_code ndr_err
;
1027 r
= talloc_zero(talloc_tos(), struct dfs_GetDFSReferral
);
1029 *pstatus
= NT_STATUS_NO_MEMORY
;
1033 r
->in
.req
.max_referral_level
= max_referral_level
;
1034 r
->in
.req
.servername
= talloc_strdup(r
, dfs_path
);
1035 if (r
->in
.req
.servername
== NULL
) {
1037 *pstatus
= NT_STATUS_NO_MEMORY
;
1041 status
= SMB_VFS_GET_DFS_REFERRALS(orig_conn
, r
);
1042 if (!NT_STATUS_IS_OK(status
)) {
1048 ndr_err
= ndr_push_struct_blob(&blob
, r
,
1050 (ndr_push_flags_fn_t
)ndr_push_dfs_referral_resp
);
1051 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
1053 *pstatus
= NT_STATUS_INVALID_PARAMETER
;
1057 pdata
= (char *)SMB_REALLOC(pdata
, blob
.length
);
1060 DEBUG(0,("referral setup:"
1061 "malloc failed for Realloc!\n"));
1065 reply_size
= blob
.length
;
1066 memcpy(pdata
, blob
.data
, blob
.length
);
1069 *pstatus
= NT_STATUS_OK
;
1073 /**********************************************************************
1074 The following functions are called by the NETDFS RPC pipe functions
1075 **********************************************************************/
1077 /*********************************************************************
1078 Creates a junction structure from a DFS pathname
1079 **********************************************************************/
1081 bool create_junction(TALLOC_CTX
*ctx
,
1082 const char *dfs_path
,
1083 bool allow_broken_path
,
1084 struct junction_map
*jucn
)
1088 struct dfs_path
*pdp
= talloc(ctx
,struct dfs_path
);
1094 status
= parse_dfs_path(NULL
, dfs_path
, False
, allow_broken_path
,
1096 if (!NT_STATUS_IS_OK(status
)) {
1100 /* check if path is dfs : validate first token */
1101 if (!is_myname_or_ipaddr(pdp
->hostname
)) {
1102 DEBUG(4,("create_junction: Invalid hostname %s "
1104 pdp
->hostname
, dfs_path
));
1109 /* Check for a non-DFS share */
1110 snum
= lp_servicenumber(pdp
->servicename
);
1112 if(snum
< 0 || !lp_msdfs_root(snum
)) {
1113 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1119 jucn
->service_name
= talloc_strdup(ctx
, pdp
->servicename
);
1120 jucn
->volume_name
= talloc_strdup(ctx
, pdp
->reqpath
);
1121 jucn
->comment
= talloc_strdup(ctx
, lp_comment(snum
));
1124 if (!jucn
->service_name
|| !jucn
->volume_name
|| ! jucn
->comment
) {
1130 /**********************************************************************
1131 Forms a valid Unix pathname from the junction
1132 **********************************************************************/
1134 static bool junction_to_local_path(const struct junction_map
*jucn
,
1136 connection_struct
**conn_out
,
1142 snum
= lp_servicenumber(jucn
->service_name
);
1146 status
= create_conn_struct(talloc_tos(), smbd_server_conn
, conn_out
,
1147 snum
, lp_pathname(snum
), NULL
, oldpath
);
1148 if (!NT_STATUS_IS_OK(status
)) {
1152 *pp_path_out
= talloc_asprintf(*conn_out
,
1156 if (!*pp_path_out
) {
1157 vfs_ChDir(*conn_out
, *oldpath
);
1158 SMB_VFS_DISCONNECT(*conn_out
);
1159 conn_free(*conn_out
);
1165 bool create_msdfs_link(const struct junction_map
*jucn
)
1169 char *msdfs_link
= NULL
;
1170 connection_struct
*conn
;
1172 bool insert_comma
= False
;
1175 if(!junction_to_local_path(jucn
, &path
, &conn
, &cwd
)) {
1179 /* Form the msdfs_link contents */
1180 msdfs_link
= talloc_strdup(conn
, "msdfs:");
1184 for(i
=0; i
<jucn
->referral_count
; i
++) {
1185 char *refpath
= jucn
->referral_list
[i
].alternate_path
;
1187 /* Alternate paths always use Windows separators. */
1188 trim_char(refpath
, '\\', '\\');
1189 if(*refpath
== '\0') {
1191 insert_comma
= False
;
1195 if (i
> 0 && insert_comma
) {
1196 msdfs_link
= talloc_asprintf_append_buffer(msdfs_link
,
1200 msdfs_link
= talloc_asprintf_append_buffer(msdfs_link
,
1208 if (!insert_comma
) {
1209 insert_comma
= True
;
1213 DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
1216 if(SMB_VFS_SYMLINK(conn
, msdfs_link
, path
) < 0) {
1217 if (errno
== EEXIST
) {
1218 struct smb_filename
*smb_fname
= NULL
;
1221 status
= create_synthetic_smb_fname(talloc_tos(), path
,
1224 if (!NT_STATUS_IS_OK(status
)) {
1225 errno
= map_errno_from_nt_status(status
);
1229 if(SMB_VFS_UNLINK(conn
, smb_fname
)!=0) {
1230 TALLOC_FREE(smb_fname
);
1233 TALLOC_FREE(smb_fname
);
1235 if (SMB_VFS_SYMLINK(conn
, msdfs_link
, path
) < 0) {
1236 DEBUG(1,("create_msdfs_link: symlink failed "
1237 "%s -> %s\nError: %s\n",
1238 path
, msdfs_link
, strerror(errno
)));
1246 vfs_ChDir(conn
, cwd
);
1247 SMB_VFS_DISCONNECT(conn
);
1252 bool remove_msdfs_link(const struct junction_map
*jucn
)
1256 connection_struct
*conn
;
1258 struct smb_filename
*smb_fname
= NULL
;
1261 if (!junction_to_local_path(jucn
, &path
, &conn
, &cwd
)) {
1265 status
= create_synthetic_smb_fname(talloc_tos(), path
,
1268 if (!NT_STATUS_IS_OK(status
)) {
1269 errno
= map_errno_from_nt_status(status
);
1273 if( SMB_VFS_UNLINK(conn
, smb_fname
) == 0 ) {
1277 TALLOC_FREE(smb_fname
);
1278 vfs_ChDir(conn
, cwd
);
1279 SMB_VFS_DISCONNECT(conn
);
1284 /*********************************************************************
1285 Return the number of DFS links at the root of this share.
1286 *********************************************************************/
1288 static int count_dfs_links(TALLOC_CTX
*ctx
, int snum
)
1292 const char *dname
= NULL
;
1293 char *talloced
= NULL
;
1294 const char *connect_path
= lp_pathname(snum
);
1295 const char *msdfs_proxy
= lp_msdfs_proxy(snum
);
1296 connection_struct
*conn
;
1300 if(*connect_path
== '\0') {
1305 * Fake up a connection struct for the VFS layer.
1308 status
= create_conn_struct(talloc_tos(), smbd_server_conn
, &conn
,
1309 snum
, connect_path
, NULL
, &cwd
);
1310 if (!NT_STATUS_IS_OK(status
)) {
1311 DEBUG(3, ("create_conn_struct failed: %s\n",
1312 nt_errstr(status
)));
1316 /* Count a link for the msdfs root - convention */
1319 /* No more links if this is an msdfs proxy. */
1320 if (*msdfs_proxy
!= '\0') {
1324 /* Now enumerate all dfs links */
1325 dirp
= SMB_VFS_OPENDIR(conn
, ".", NULL
, 0);
1330 while ((dname
= vfs_readdirname(conn
, dirp
, NULL
, &talloced
))
1332 if (is_msdfs_link(conn
,
1337 TALLOC_FREE(talloced
);
1340 SMB_VFS_CLOSEDIR(conn
,dirp
);
1343 vfs_ChDir(conn
, cwd
);
1344 SMB_VFS_DISCONNECT(conn
);
1349 /*********************************************************************
1350 *********************************************************************/
1352 static int form_junctions(TALLOC_CTX
*ctx
,
1354 struct junction_map
*jucn
,
1359 const char *dname
= NULL
;
1360 char *talloced
= NULL
;
1361 const char *connect_path
= lp_pathname(snum
);
1362 char *service_name
= lp_servicename(snum
);
1363 const char *msdfs_proxy
= lp_msdfs_proxy(snum
);
1364 connection_struct
*conn
;
1365 struct referral
*ref
= NULL
;
1369 if (jn_remain
== 0) {
1373 if(*connect_path
== '\0') {
1378 * Fake up a connection struct for the VFS layer.
1381 status
= create_conn_struct(ctx
, smbd_server_conn
, &conn
, snum
, connect_path
, NULL
,
1383 if (!NT_STATUS_IS_OK(status
)) {
1384 DEBUG(3, ("create_conn_struct failed: %s\n",
1385 nt_errstr(status
)));
1389 /* form a junction for the msdfs root - convention
1390 DO NOT REMOVE THIS: NT clients will not work with us
1391 if this is not present
1393 jucn
[cnt
].service_name
= talloc_strdup(ctx
,service_name
);
1394 jucn
[cnt
].volume_name
= talloc_strdup(ctx
, "");
1395 if (!jucn
[cnt
].service_name
|| !jucn
[cnt
].volume_name
) {
1398 jucn
[cnt
].comment
= "";
1399 jucn
[cnt
].referral_count
= 1;
1401 ref
= jucn
[cnt
].referral_list
= talloc_zero(ctx
, struct referral
);
1402 if (jucn
[cnt
].referral_list
== NULL
) {
1407 ref
->ttl
= REFERRAL_TTL
;
1408 if (*msdfs_proxy
!= '\0') {
1409 ref
->alternate_path
= talloc_strdup(ctx
,
1412 ref
->alternate_path
= talloc_asprintf(ctx
,
1414 get_local_machine_name(),
1418 if (!ref
->alternate_path
) {
1423 /* Don't enumerate if we're an msdfs proxy. */
1424 if (*msdfs_proxy
!= '\0') {
1428 /* Now enumerate all dfs links */
1429 dirp
= SMB_VFS_OPENDIR(conn
, ".", NULL
, 0);
1434 while ((dname
= vfs_readdirname(conn
, dirp
, NULL
, &talloced
))
1436 char *link_target
= NULL
;
1437 if (cnt
>= jn_remain
) {
1438 DEBUG(2, ("form_junctions: ran out of MSDFS "
1440 TALLOC_FREE(talloced
);
1443 if (is_msdfs_link_internal(ctx
,
1445 dname
, &link_target
,
1447 if (parse_msdfs_symlink(ctx
,
1449 &jucn
[cnt
].referral_list
,
1450 &jucn
[cnt
].referral_count
)) {
1452 jucn
[cnt
].service_name
= talloc_strdup(ctx
,
1454 jucn
[cnt
].volume_name
= talloc_strdup(ctx
,
1456 if (!jucn
[cnt
].service_name
||
1457 !jucn
[cnt
].volume_name
) {
1458 TALLOC_FREE(talloced
);
1461 jucn
[cnt
].comment
= "";
1464 TALLOC_FREE(link_target
);
1466 TALLOC_FREE(talloced
);
1472 SMB_VFS_CLOSEDIR(conn
,dirp
);
1475 vfs_ChDir(conn
, cwd
);
1480 struct junction_map
*enum_msdfs_links(TALLOC_CTX
*ctx
, size_t *p_num_jn
)
1482 struct junction_map
*jn
= NULL
;
1484 size_t jn_count
= 0;
1488 if(!lp_host_msdfs()) {
1492 /* Ensure all the usershares are loaded. */
1494 load_registry_shares();
1495 sharecount
= load_usershare_shares(NULL
, connections_snum_used
);
1498 for(i
=0;i
< sharecount
;i
++) {
1499 if(lp_msdfs_root(i
)) {
1500 jn_count
+= count_dfs_links(ctx
, i
);
1503 if (jn_count
== 0) {
1506 jn
= talloc_array(ctx
, struct junction_map
, jn_count
);
1510 for(i
=0; i
< sharecount
; i
++) {
1511 if (*p_num_jn
>= jn_count
) {
1514 if(lp_msdfs_root(i
)) {
1515 *p_num_jn
+= form_junctions(ctx
, i
,
1517 jn_count
- *p_num_jn
);
1523 /******************************************************************************
1524 Core function to resolve a dfs pathname possibly containing a wildcard. If
1525 ppath_contains_wcard != NULL, it will be set to true if a wildcard is
1526 detected during dfs resolution.
1527 ******************************************************************************/
1529 NTSTATUS
resolve_dfspath_wcard(TALLOC_CTX
*ctx
,
1530 connection_struct
*conn
,
1532 const char *name_in
,
1535 bool *ppath_contains_wcard
)
1537 bool path_contains_wcard
;
1538 NTSTATUS status
= NT_STATUS_OK
;
1540 if (dfs_pathnames
) {
1541 status
= dfs_redirect(ctx
,
1545 !smbd_server_conn
->using_smb2
,
1547 &path_contains_wcard
);
1549 if (NT_STATUS_IS_OK(status
) && ppath_contains_wcard
!= NULL
) {
1550 *ppath_contains_wcard
= path_contains_wcard
;
1554 * Cheat and just return a copy of the in ptr.
1555 * Once srvstr_get_path() uses talloc it'll
1556 * be a talloced ptr anyway.
1558 *pp_name_out
= discard_const_p(char, name_in
);