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.
222 Note: this performs a vfs connect and CHANGES CWD !!!! JRA.
223 *********************************************************/
225 NTSTATUS
create_conn_struct(TALLOC_CTX
*ctx
,
226 struct tevent_context
*ev
,
227 struct messaging_context
*msg
,
228 connection_struct
**pconn
,
231 const struct auth_session_info
*session_info
,
234 connection_struct
*conn
;
237 const char *vfs_user
;
239 conn
= talloc_zero(ctx
, connection_struct
);
241 return NT_STATUS_NO_MEMORY
;
244 connpath
= talloc_strdup(conn
, path
);
247 return NT_STATUS_NO_MEMORY
;
249 connpath
= talloc_string_sub(conn
,
252 lp_servicename(talloc_tos(), snum
));
255 return NT_STATUS_NO_MEMORY
;
258 conn
->sconn
= talloc_zero(conn
, struct smbd_server_connection
);
259 if (conn
->sconn
== NULL
) {
261 return NT_STATUS_NO_MEMORY
;
264 conn
->sconn
->ev_ctx
= ev
;
265 conn
->sconn
->msg_ctx
= msg
;
266 conn
->sconn
->sock
= -1;
267 conn
->sconn
->smb1
.echo_handler
.trusted_fd
= -1;
268 conn
->sconn
->smb1
.echo_handler
.socket_lock_fd
= -1;
270 /* needed for smbd_vfs_init() */
272 if (!(conn
->params
= talloc_zero(conn
, struct share_params
))) {
273 DEBUG(0, ("TALLOC failed\n"));
275 return NT_STATUS_NO_MEMORY
;
278 conn
->params
->service
= snum
;
279 conn
->cnum
= TID_FIELD_INVALID
;
281 DLIST_ADD(conn
->sconn
->connections
, conn
);
282 conn
->sconn
->num_connections
++;
284 if (session_info
!= NULL
) {
285 conn
->session_info
= copy_session_info(conn
, session_info
);
286 if (conn
->session_info
== NULL
) {
287 DEBUG(0, ("copy_serverinfo failed\n"));
289 return NT_STATUS_NO_MEMORY
;
291 vfs_user
= conn
->session_info
->unix_info
->unix_name
;
293 /* use current authenticated user in absence of session_info */
294 vfs_user
= get_current_username();
297 set_conn_connectpath(conn
, connpath
);
300 * New code to check if there's a share security descripter
301 * added from NT server manager. This is done after the
302 * smb.conf checks are done as we need a uid and token. JRA.
305 if (conn
->session_info
) {
306 share_access_check(conn
->session_info
->security_token
,
307 lp_servicename(talloc_tos(), snum
),
308 MAXIMUM_ALLOWED_ACCESS
,
309 &conn
->share_access
);
311 if ((conn
->share_access
& FILE_WRITE_DATA
) == 0) {
312 if ((conn
->share_access
& FILE_READ_DATA
) == 0) {
313 /* No access, read or write. */
314 DEBUG(0,("create_conn_struct: connection to %s "
315 "denied due to security "
317 lp_servicename(talloc_tos(), snum
)));
319 return NT_STATUS_ACCESS_DENIED
;
321 conn
->read_only
= true;
325 conn
->share_access
= 0;
326 conn
->read_only
= true;
329 if (!smbd_vfs_init(conn
)) {
330 NTSTATUS status
= map_nt_error_from_unix(errno
);
331 DEBUG(0,("create_conn_struct: smbd_vfs_init failed.\n"));
336 /* this must be the first filesystem operation that we do */
337 if (SMB_VFS_CONNECT(conn
, lp_servicename(talloc_tos(), snum
), vfs_user
) < 0) {
338 DEBUG(0,("VFS connect failed!\n"));
340 return NT_STATUS_UNSUCCESSFUL
;
343 conn
->fs_capabilities
= SMB_VFS_FS_CAPABILITIES(conn
, &conn
->ts_res
);
346 * Windows seems to insist on doing trans2getdfsreferral() calls on
347 * the IPC$ share as the anonymous user. If we try to chdir as that
348 * user we will fail.... WTF ? JRA.
351 oldcwd
= vfs_GetWd(ctx
, conn
);
352 if (oldcwd
== NULL
) {
353 NTSTATUS status
= map_nt_error_from_unix(errno
);
354 DEBUG(3, ("vfs_GetWd failed: %s\n", strerror(errno
)));
359 if (vfs_ChDir(conn
,conn
->connectpath
) != 0) {
360 NTSTATUS status
= map_nt_error_from_unix(errno
);
361 DEBUG(3,("create_conn_struct: Can't ChDir to new conn path %s. "
363 conn
->connectpath
, strerror(errno
) ));
374 /**********************************************************************
375 Parse the contents of a symlink to verify if it is an msdfs referral
376 A valid referral is of the form:
378 msdfs:server1\share1,server2\share2
379 msdfs:server1\share1\pathname,server2\share2\pathname
380 msdfs:server1/share1,server2/share2
381 msdfs:server1/share1/pathname,server2/share2/pathname.
383 Note that the alternate paths returned here must be of the canonicalized
387 \server\share\path\to\file,
389 even in posix path mode. This is because we have no knowledge if the
390 server we're referring to understands posix paths.
391 **********************************************************************/
393 static bool parse_msdfs_symlink(TALLOC_CTX
*ctx
,
395 struct referral
**preflist
,
400 char **alt_path
= NULL
;
402 struct referral
*reflist
;
405 temp
= talloc_strdup(ctx
, target
);
409 prot
= strtok_r(temp
, ":", &saveptr
);
411 DEBUG(0,("parse_msdfs_symlink: invalid path !\n"));
415 alt_path
= talloc_array(ctx
, char *, MAX_REFERRAL_COUNT
);
420 /* parse out the alternate paths */
421 while((count
<MAX_REFERRAL_COUNT
) &&
422 ((alt_path
[count
] = strtok_r(NULL
, ",", &saveptr
)) != NULL
)) {
426 DEBUG(10,("parse_msdfs_symlink: count=%d\n", count
));
429 reflist
= *preflist
= talloc_zero_array(ctx
,
430 struct referral
, count
);
431 if(reflist
== NULL
) {
432 TALLOC_FREE(alt_path
);
436 reflist
= *preflist
= NULL
;
439 for(i
=0;i
<count
;i
++) {
442 /* Canonicalize link target.
443 * Replace all /'s in the path by a \ */
444 string_replace(alt_path
[i
], '/', '\\');
446 /* Remove leading '\\'s */
448 while (*p
&& (*p
== '\\')) {
452 reflist
[i
].alternate_path
= talloc_asprintf(ctx
,
455 if (!reflist
[i
].alternate_path
) {
459 reflist
[i
].proximity
= 0;
460 reflist
[i
].ttl
= REFERRAL_TTL
;
461 DEBUG(10, ("parse_msdfs_symlink: Created alt path: %s\n",
462 reflist
[i
].alternate_path
));
467 TALLOC_FREE(alt_path
);
471 /**********************************************************************
472 Returns true if the unix path is a valid msdfs symlink and also
473 returns the target string from inside the link.
474 **********************************************************************/
476 static bool is_msdfs_link_internal(TALLOC_CTX
*ctx
,
477 connection_struct
*conn
,
479 char **pp_link_target
,
480 SMB_STRUCT_STAT
*sbufp
)
482 int referral_len
= 0;
483 #if defined(HAVE_BROKEN_READLINK)
484 char link_target_buf
[PATH_MAX
];
486 char link_target_buf
[7];
489 char *link_target
= NULL
;
490 struct smb_filename smb_fname
;
492 if (pp_link_target
) {
494 link_target
= talloc_array(ctx
, char, bufsize
);
498 *pp_link_target
= link_target
;
500 bufsize
= sizeof(link_target_buf
);
501 link_target
= link_target_buf
;
504 ZERO_STRUCT(smb_fname
);
505 smb_fname
.base_name
= discard_const_p(char, path
);
507 if (SMB_VFS_LSTAT(conn
, &smb_fname
) != 0) {
508 DEBUG(5,("is_msdfs_link_read_target: %s does not exist.\n",
512 if (!S_ISLNK(smb_fname
.st
.st_ex_mode
)) {
513 DEBUG(5,("is_msdfs_link_read_target: %s is not a link.\n",
518 *sbufp
= smb_fname
.st
;
521 referral_len
= SMB_VFS_READLINK(conn
, path
, link_target
, bufsize
- 1);
522 if (referral_len
== -1) {
523 DEBUG(0,("is_msdfs_link_read_target: Error reading "
524 "msdfs link %s: %s\n",
525 path
, strerror(errno
)));
528 link_target
[referral_len
] = '\0';
530 DEBUG(5,("is_msdfs_link_internal: %s -> %s\n",path
,
533 if (!strnequal(link_target
, "msdfs:", 6)) {
540 if (link_target
!= link_target_buf
) {
541 TALLOC_FREE(link_target
);
546 /**********************************************************************
547 Returns true if the unix path is a valid msdfs symlink.
548 **********************************************************************/
550 bool is_msdfs_link(connection_struct
*conn
,
552 SMB_STRUCT_STAT
*sbufp
)
554 return is_msdfs_link_internal(talloc_tos(),
561 /*****************************************************************
562 Used by other functions to decide if a dfs path is remote,
563 and to get the list of referred locations for that remote path.
565 search_flag: For findfirsts, dfs links themselves are not
566 redirected, but paths beyond the links are. For normal smb calls,
567 even dfs links need to be redirected.
569 consumedcntp: how much of the dfs path is being redirected. the client
570 should try the remaining path on the redirected server.
572 If this returns NT_STATUS_PATH_NOT_COVERED the contents of the msdfs
573 link redirect are in targetpath.
574 *****************************************************************/
576 static NTSTATUS
dfs_path_lookup(TALLOC_CTX
*ctx
,
577 connection_struct
*conn
,
578 const char *dfspath
, /* Incoming complete dfs path */
579 const struct dfs_path
*pdp
, /* Parsed out
580 server+share+extrapath. */
581 bool search_flag
, /* Called from a findfirst ? */
583 char **pp_targetpath
)
588 struct smb_filename
*smb_fname
= NULL
;
589 char *canon_dfspath
= NULL
; /* Canonicalized dfs path. (only '/'
592 DEBUG(10,("dfs_path_lookup: Conn path = %s reqpath = %s\n",
593 conn
->connectpath
, pdp
->reqpath
));
596 * Note the unix path conversion here we're doing we
597 * throw away. We're looking for a symlink for a dfs
598 * resolution, if we don't find it we'll do another
599 * unix_convert later in the codepath.
602 status
= unix_convert(ctx
, conn
, pdp
->reqpath
, &smb_fname
,
603 search_flag
? UCF_ALWAYS_ALLOW_WCARD_LCOMP
: 0);
605 if (!NT_STATUS_IS_OK(status
)) {
606 if (!NT_STATUS_EQUAL(status
,
607 NT_STATUS_OBJECT_PATH_NOT_FOUND
)) {
610 if (smb_fname
== NULL
|| smb_fname
->base_name
== NULL
) {
615 /* Optimization - check if we can redirect the whole path. */
617 if (is_msdfs_link_internal(ctx
, conn
, smb_fname
->base_name
,
618 pp_targetpath
, NULL
)) {
620 DEBUG(6,("dfs_path_lookup (FindFirst) No redirection "
621 "for dfs link %s.\n", dfspath
));
622 status
= NT_STATUS_OK
;
626 DEBUG(6,("dfs_path_lookup: %s resolves to a "
627 "valid dfs link %s.\n", dfspath
,
628 pp_targetpath
? *pp_targetpath
: ""));
631 *consumedcntp
= strlen(dfspath
);
633 status
= NT_STATUS_PATH_NOT_COVERED
;
637 /* Prepare to test only for '/' components in the given path,
638 * so if a Windows path replace all '\\' characters with '/'.
639 * For a POSIX DFS path we know all separators are already '/'. */
641 canon_dfspath
= talloc_strdup(ctx
, dfspath
);
642 if (!canon_dfspath
) {
643 status
= NT_STATUS_NO_MEMORY
;
646 if (!pdp
->posix_path
) {
647 string_replace(canon_dfspath
, '\\', '/');
651 * localpath comes out of unix_convert, so it has
652 * no trailing backslash. Make sure that canon_dfspath hasn't either.
653 * Fix for bug #4860 from Jan Martin <Jan.Martin@rwedea.com>.
656 trim_char(canon_dfspath
,0,'/');
659 * Redirect if any component in the path is a link.
660 * We do this by walking backwards through the
661 * local path, chopping off the last component
662 * in both the local path and the canonicalized
663 * DFS path. If we hit a DFS link then we're done.
666 p
= strrchr_m(smb_fname
->base_name
, '/');
668 q
= strrchr_m(canon_dfspath
, '/');
677 if (is_msdfs_link_internal(ctx
, conn
,
678 smb_fname
->base_name
, pp_targetpath
,
680 DEBUG(4, ("dfs_path_lookup: Redirecting %s because "
681 "parent %s is dfs link\n", dfspath
,
682 smb_fname_str_dbg(smb_fname
)));
685 *consumedcntp
= strlen(canon_dfspath
);
686 DEBUG(10, ("dfs_path_lookup: Path consumed: %s "
692 status
= NT_STATUS_PATH_NOT_COVERED
;
696 /* Step back on the filesystem. */
697 p
= strrchr_m(smb_fname
->base_name
, '/');
700 /* And in the canonicalized dfs path. */
701 q
= strrchr_m(canon_dfspath
, '/');
705 status
= NT_STATUS_OK
;
707 TALLOC_FREE(smb_fname
);
711 /*****************************************************************
712 Decides if a dfs pathname should be redirected or not.
713 If not, the pathname is converted to a tcon-relative local unix path
715 search_wcard_flag: this flag performs 2 functions both related
716 to searches. See resolve_dfs_path() and parse_dfs_path_XX()
719 This function can return NT_STATUS_OK, meaning use the returned path as-is
720 (mapped into a local path).
721 or NT_STATUS_NOT_COVERED meaning return a DFS redirect, or
722 any other NT_STATUS error which is a genuine error to be
723 returned to the client.
724 *****************************************************************/
726 static NTSTATUS
dfs_redirect(TALLOC_CTX
*ctx
,
727 connection_struct
*conn
,
729 bool search_wcard_flag
,
730 bool allow_broken_path
,
732 bool *ppath_contains_wcard
)
735 struct dfs_path
*pdp
= talloc(ctx
, struct dfs_path
);
738 return NT_STATUS_NO_MEMORY
;
741 status
= parse_dfs_path(conn
, path_in
, search_wcard_flag
,
742 allow_broken_path
, pdp
,
743 ppath_contains_wcard
);
744 if (!NT_STATUS_IS_OK(status
)) {
749 if (pdp
->reqpath
[0] == '\0') {
751 *pp_path_out
= talloc_strdup(ctx
, "");
753 return NT_STATUS_NO_MEMORY
;
755 DEBUG(5,("dfs_redirect: self-referral.\n"));
759 /* If dfs pathname for a non-dfs share, convert to tcon-relative
760 path and return OK */
762 if (!lp_msdfs_root(SNUM(conn
))) {
763 *pp_path_out
= talloc_strdup(ctx
, pdp
->reqpath
);
766 return NT_STATUS_NO_MEMORY
;
771 /* If it looked like a local path (zero hostname/servicename)
772 * just treat as a tcon-relative path. */
774 if (pdp
->hostname
[0] == '\0' && pdp
->servicename
[0] == '\0') {
775 *pp_path_out
= talloc_strdup(ctx
, pdp
->reqpath
);
778 return NT_STATUS_NO_MEMORY
;
783 if (!( strequal(pdp
->servicename
, lp_servicename(talloc_tos(), SNUM(conn
)))
784 || (strequal(pdp
->servicename
, HOMES_NAME
)
785 && strequal(lp_servicename(talloc_tos(), SNUM(conn
)),
786 conn
->session_info
->unix_info
->sanitized_username
) )) ) {
788 /* The given sharename doesn't match this connection. */
791 return NT_STATUS_OBJECT_PATH_NOT_FOUND
;
794 status
= dfs_path_lookup(ctx
, conn
, path_in
, pdp
,
795 search_wcard_flag
, NULL
, NULL
);
796 if (!NT_STATUS_IS_OK(status
)) {
797 if (NT_STATUS_EQUAL(status
, NT_STATUS_PATH_NOT_COVERED
)) {
798 DEBUG(3,("dfs_redirect: Redirecting %s\n", path_in
));
800 DEBUG(10,("dfs_redirect: dfs_path_lookup "
801 "failed for %s with %s\n",
802 path_in
, nt_errstr(status
) ));
807 DEBUG(3,("dfs_redirect: Not redirecting %s.\n", path_in
));
809 /* Form non-dfs tcon-relative path */
810 *pp_path_out
= talloc_strdup(ctx
, pdp
->reqpath
);
813 return NT_STATUS_NO_MEMORY
;
816 DEBUG(3,("dfs_redirect: Path %s converted to non-dfs path %s\n",
823 /**********************************************************************
824 Return a self referral.
825 **********************************************************************/
827 static NTSTATUS
self_ref(TALLOC_CTX
*ctx
,
828 const char *dfs_path
,
829 struct junction_map
*jucn
,
831 bool *self_referralp
)
833 struct referral
*ref
;
835 *self_referralp
= True
;
837 jucn
->referral_count
= 1;
838 if((ref
= talloc_zero(ctx
, struct referral
)) == NULL
) {
839 return NT_STATUS_NO_MEMORY
;
842 ref
->alternate_path
= talloc_strdup(ctx
, dfs_path
);
843 if (!ref
->alternate_path
) {
845 return NT_STATUS_NO_MEMORY
;
848 ref
->ttl
= REFERRAL_TTL
;
849 jucn
->referral_list
= ref
;
850 *consumedcntp
= strlen(dfs_path
);
854 /**********************************************************************
855 Gets valid referrals for a dfs path and fills up the
856 junction_map structure.
857 **********************************************************************/
859 NTSTATUS
get_referred_path(TALLOC_CTX
*ctx
,
860 const char *dfs_path
,
861 bool allow_broken_path
,
862 struct junction_map
*jucn
,
864 bool *self_referralp
)
866 struct connection_struct
*conn
;
867 char *targetpath
= NULL
;
869 NTSTATUS status
= NT_STATUS_NOT_FOUND
;
871 struct dfs_path
*pdp
= talloc(ctx
, struct dfs_path
);
875 return NT_STATUS_NO_MEMORY
;
878 *self_referralp
= False
;
880 status
= parse_dfs_path(NULL
, dfs_path
, False
, allow_broken_path
,
882 if (!NT_STATUS_IS_OK(status
)) {
886 jucn
->service_name
= talloc_strdup(ctx
, pdp
->servicename
);
887 jucn
->volume_name
= talloc_strdup(ctx
, pdp
->reqpath
);
888 if (!jucn
->service_name
|| !jucn
->volume_name
) {
890 return NT_STATUS_NO_MEMORY
;
893 /* Verify the share is a dfs root */
894 snum
= lp_servicenumber(jucn
->service_name
);
896 char *service_name
= NULL
;
897 if ((snum
= find_service(ctx
, jucn
->service_name
, &service_name
)) < 0) {
898 return NT_STATUS_NOT_FOUND
;
901 return NT_STATUS_NO_MEMORY
;
903 TALLOC_FREE(jucn
->service_name
);
904 jucn
->service_name
= talloc_strdup(ctx
, service_name
);
905 if (!jucn
->service_name
) {
907 return NT_STATUS_NO_MEMORY
;
911 if (!lp_msdfs_root(snum
) && (*lp_msdfs_proxy(talloc_tos(), snum
) == '\0')) {
912 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not "
914 pdp
->servicename
, dfs_path
));
916 return NT_STATUS_NOT_FOUND
;
920 * Self referrals are tested with a anonymous IPC connection and
921 * a GET_DFS_REFERRAL call to \\server\share. (which means
922 * dp.reqpath[0] points to an empty string). create_conn_struct cd's
923 * into the directory and will fail if it cannot (as the anonymous
924 * user). Cope with this.
927 if (pdp
->reqpath
[0] == '\0') {
929 struct referral
*ref
;
931 if (*lp_msdfs_proxy(talloc_tos(), snum
) == '\0') {
941 * It's an msdfs proxy share. Redirect to
942 * the configured target share.
945 jucn
->referral_count
= 1;
946 if ((ref
= talloc_zero(ctx
, struct referral
)) == NULL
) {
948 return NT_STATUS_NO_MEMORY
;
951 if (!(tmp
= talloc_strdup(ctx
, lp_msdfs_proxy(talloc_tos(), snum
)))) {
953 return NT_STATUS_NO_MEMORY
;
956 trim_string(tmp
, "\\", 0);
958 ref
->alternate_path
= talloc_asprintf(ctx
, "\\%s", tmp
);
961 if (!ref
->alternate_path
) {
963 return NT_STATUS_NO_MEMORY
;
966 if (pdp
->reqpath
[0] != '\0') {
967 ref
->alternate_path
= talloc_asprintf_append(
971 if (!ref
->alternate_path
) {
973 return NT_STATUS_NO_MEMORY
;
977 ref
->ttl
= REFERRAL_TTL
;
978 jucn
->referral_list
= ref
;
979 *consumedcntp
= strlen(dfs_path
);
984 status
= create_conn_struct(ctx
,
985 server_event_context(),
986 server_messaging_context(),
988 lp_pathname(talloc_tos(), snum
), NULL
, &oldpath
);
989 if (!NT_STATUS_IS_OK(status
)) {
994 /* If this is a DFS path dfs_lookup should return
995 * NT_STATUS_PATH_NOT_COVERED. */
997 status
= dfs_path_lookup(ctx
, conn
, dfs_path
, pdp
,
998 False
, consumedcntp
, &targetpath
);
1000 if (!NT_STATUS_EQUAL(status
, NT_STATUS_PATH_NOT_COVERED
)) {
1001 DEBUG(3,("get_referred_path: No valid referrals for path %s\n",
1006 /* We know this is a valid dfs link. Parse the targetpath. */
1007 if (!parse_msdfs_symlink(ctx
, targetpath
,
1008 &jucn
->referral_list
,
1009 &jucn
->referral_count
)) {
1010 DEBUG(3,("get_referred_path: failed to parse symlink "
1011 "target %s\n", targetpath
));
1012 status
= NT_STATUS_NOT_FOUND
;
1016 status
= NT_STATUS_OK
;
1018 vfs_ChDir(conn
, oldpath
);
1019 SMB_VFS_DISCONNECT(conn
);
1025 /******************************************************************
1026 Set up the DFS referral for the dfs pathname. This call returns
1027 the amount of the path covered by this server, and where the
1028 client should be redirected to. This is the meat of the
1029 TRANS2_GET_DFS_REFERRAL call.
1030 ******************************************************************/
1032 int setup_dfs_referral(connection_struct
*orig_conn
,
1033 const char *dfs_path
,
1034 int max_referral_level
,
1035 char **ppdata
, NTSTATUS
*pstatus
)
1037 char *pdata
= *ppdata
;
1039 struct dfs_GetDFSReferral
*r
;
1040 DATA_BLOB blob
= data_blob_null
;
1042 enum ndr_err_code ndr_err
;
1044 r
= talloc_zero(talloc_tos(), struct dfs_GetDFSReferral
);
1046 *pstatus
= NT_STATUS_NO_MEMORY
;
1050 r
->in
.req
.max_referral_level
= max_referral_level
;
1051 r
->in
.req
.servername
= talloc_strdup(r
, dfs_path
);
1052 if (r
->in
.req
.servername
== NULL
) {
1054 *pstatus
= NT_STATUS_NO_MEMORY
;
1058 status
= SMB_VFS_GET_DFS_REFERRALS(orig_conn
, r
);
1059 if (!NT_STATUS_IS_OK(status
)) {
1065 ndr_err
= ndr_push_struct_blob(&blob
, r
,
1067 (ndr_push_flags_fn_t
)ndr_push_dfs_referral_resp
);
1068 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
1070 *pstatus
= NT_STATUS_INVALID_PARAMETER
;
1074 pdata
= (char *)SMB_REALLOC(pdata
, blob
.length
);
1077 DEBUG(0,("referral setup:"
1078 "malloc failed for Realloc!\n"));
1082 reply_size
= blob
.length
;
1083 memcpy(pdata
, blob
.data
, blob
.length
);
1086 *pstatus
= NT_STATUS_OK
;
1090 /**********************************************************************
1091 The following functions are called by the NETDFS RPC pipe functions
1092 **********************************************************************/
1094 /*********************************************************************
1095 Creates a junction structure from a DFS pathname
1096 **********************************************************************/
1098 bool create_junction(TALLOC_CTX
*ctx
,
1099 const char *dfs_path
,
1100 bool allow_broken_path
,
1101 struct junction_map
*jucn
)
1105 struct dfs_path
*pdp
= talloc(ctx
,struct dfs_path
);
1111 status
= parse_dfs_path(NULL
, dfs_path
, False
, allow_broken_path
,
1113 if (!NT_STATUS_IS_OK(status
)) {
1117 /* check if path is dfs : validate first token */
1118 if (!is_myname_or_ipaddr(pdp
->hostname
)) {
1119 DEBUG(4,("create_junction: Invalid hostname %s "
1121 pdp
->hostname
, dfs_path
));
1126 /* Check for a non-DFS share */
1127 snum
= lp_servicenumber(pdp
->servicename
);
1129 if(snum
< 0 || !lp_msdfs_root(snum
)) {
1130 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1136 jucn
->service_name
= talloc_strdup(ctx
, pdp
->servicename
);
1137 jucn
->volume_name
= talloc_strdup(ctx
, pdp
->reqpath
);
1138 jucn
->comment
= lp_comment(ctx
, snum
);
1141 if (!jucn
->service_name
|| !jucn
->volume_name
|| ! jucn
->comment
) {
1147 /**********************************************************************
1148 Forms a valid Unix pathname from the junction
1149 **********************************************************************/
1151 static bool junction_to_local_path(const struct junction_map
*jucn
,
1153 connection_struct
**conn_out
,
1159 snum
= lp_servicenumber(jucn
->service_name
);
1163 status
= create_conn_struct(talloc_tos(),
1164 server_event_context(),
1165 server_messaging_context(),
1167 snum
, lp_pathname(talloc_tos(), snum
), NULL
, oldpath
);
1168 if (!NT_STATUS_IS_OK(status
)) {
1172 *pp_path_out
= talloc_asprintf(*conn_out
,
1174 lp_pathname(talloc_tos(), snum
),
1176 if (!*pp_path_out
) {
1177 vfs_ChDir(*conn_out
, *oldpath
);
1178 SMB_VFS_DISCONNECT(*conn_out
);
1179 conn_free(*conn_out
);
1185 bool create_msdfs_link(const struct junction_map
*jucn
)
1189 char *msdfs_link
= NULL
;
1190 connection_struct
*conn
;
1192 bool insert_comma
= False
;
1195 if(!junction_to_local_path(jucn
, &path
, &conn
, &cwd
)) {
1199 /* Form the msdfs_link contents */
1200 msdfs_link
= talloc_strdup(conn
, "msdfs:");
1204 for(i
=0; i
<jucn
->referral_count
; i
++) {
1205 char *refpath
= jucn
->referral_list
[i
].alternate_path
;
1207 /* Alternate paths always use Windows separators. */
1208 trim_char(refpath
, '\\', '\\');
1209 if(*refpath
== '\0') {
1211 insert_comma
= False
;
1215 if (i
> 0 && insert_comma
) {
1216 msdfs_link
= talloc_asprintf_append_buffer(msdfs_link
,
1220 msdfs_link
= talloc_asprintf_append_buffer(msdfs_link
,
1228 if (!insert_comma
) {
1229 insert_comma
= True
;
1233 DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
1236 if(SMB_VFS_SYMLINK(conn
, msdfs_link
, path
) < 0) {
1237 if (errno
== EEXIST
) {
1238 struct smb_filename
*smb_fname
= NULL
;
1241 status
= create_synthetic_smb_fname(talloc_tos(), path
,
1244 if (!NT_STATUS_IS_OK(status
)) {
1245 errno
= map_errno_from_nt_status(status
);
1249 if(SMB_VFS_UNLINK(conn
, smb_fname
)!=0) {
1250 TALLOC_FREE(smb_fname
);
1253 TALLOC_FREE(smb_fname
);
1255 if (SMB_VFS_SYMLINK(conn
, msdfs_link
, path
) < 0) {
1256 DEBUG(1,("create_msdfs_link: symlink failed "
1257 "%s -> %s\nError: %s\n",
1258 path
, msdfs_link
, strerror(errno
)));
1266 vfs_ChDir(conn
, cwd
);
1267 SMB_VFS_DISCONNECT(conn
);
1272 bool remove_msdfs_link(const struct junction_map
*jucn
)
1276 connection_struct
*conn
;
1278 struct smb_filename
*smb_fname
= NULL
;
1281 if (!junction_to_local_path(jucn
, &path
, &conn
, &cwd
)) {
1285 status
= create_synthetic_smb_fname(talloc_tos(), path
,
1288 if (!NT_STATUS_IS_OK(status
)) {
1289 errno
= map_errno_from_nt_status(status
);
1293 if( SMB_VFS_UNLINK(conn
, smb_fname
) == 0 ) {
1297 TALLOC_FREE(smb_fname
);
1298 vfs_ChDir(conn
, cwd
);
1299 SMB_VFS_DISCONNECT(conn
);
1304 /*********************************************************************
1305 Return the number of DFS links at the root of this share.
1306 *********************************************************************/
1308 static int count_dfs_links(TALLOC_CTX
*ctx
, int snum
)
1312 const char *dname
= NULL
;
1313 char *talloced
= NULL
;
1314 const char *connect_path
= lp_pathname(talloc_tos(), snum
);
1315 const char *msdfs_proxy
= lp_msdfs_proxy(talloc_tos(), snum
);
1316 connection_struct
*conn
;
1320 if(*connect_path
== '\0') {
1325 * Fake up a connection struct for the VFS layer.
1328 status
= create_conn_struct(talloc_tos(),
1329 server_event_context(),
1330 server_messaging_context(),
1332 snum
, connect_path
, NULL
, &cwd
);
1333 if (!NT_STATUS_IS_OK(status
)) {
1334 DEBUG(3, ("create_conn_struct failed: %s\n",
1335 nt_errstr(status
)));
1339 /* Count a link for the msdfs root - convention */
1342 /* No more links if this is an msdfs proxy. */
1343 if (*msdfs_proxy
!= '\0') {
1347 /* Now enumerate all dfs links */
1348 dirp
= SMB_VFS_OPENDIR(conn
, ".", NULL
, 0);
1353 while ((dname
= vfs_readdirname(conn
, dirp
, NULL
, &talloced
))
1355 if (is_msdfs_link(conn
,
1360 TALLOC_FREE(talloced
);
1363 SMB_VFS_CLOSEDIR(conn
,dirp
);
1366 vfs_ChDir(conn
, cwd
);
1367 SMB_VFS_DISCONNECT(conn
);
1372 /*********************************************************************
1373 *********************************************************************/
1375 static int form_junctions(TALLOC_CTX
*ctx
,
1377 struct junction_map
*jucn
,
1382 const char *dname
= NULL
;
1383 char *talloced
= NULL
;
1384 const char *connect_path
= lp_pathname(talloc_tos(), snum
);
1385 char *service_name
= lp_servicename(talloc_tos(), snum
);
1386 const char *msdfs_proxy
= lp_msdfs_proxy(talloc_tos(), snum
);
1387 connection_struct
*conn
;
1388 struct referral
*ref
= NULL
;
1392 if (jn_remain
== 0) {
1396 if(*connect_path
== '\0') {
1401 * Fake up a connection struct for the VFS layer.
1404 status
= create_conn_struct(ctx
,
1405 server_event_context(),
1406 server_messaging_context(),
1407 &conn
, snum
, connect_path
, NULL
,
1409 if (!NT_STATUS_IS_OK(status
)) {
1410 DEBUG(3, ("create_conn_struct failed: %s\n",
1411 nt_errstr(status
)));
1415 /* form a junction for the msdfs root - convention
1416 DO NOT REMOVE THIS: NT clients will not work with us
1417 if this is not present
1419 jucn
[cnt
].service_name
= talloc_strdup(ctx
,service_name
);
1420 jucn
[cnt
].volume_name
= talloc_strdup(ctx
, "");
1421 if (!jucn
[cnt
].service_name
|| !jucn
[cnt
].volume_name
) {
1424 jucn
[cnt
].comment
= "";
1425 jucn
[cnt
].referral_count
= 1;
1427 ref
= jucn
[cnt
].referral_list
= talloc_zero(ctx
, struct referral
);
1428 if (jucn
[cnt
].referral_list
== NULL
) {
1433 ref
->ttl
= REFERRAL_TTL
;
1434 if (*msdfs_proxy
!= '\0') {
1435 ref
->alternate_path
= talloc_strdup(ctx
,
1438 ref
->alternate_path
= talloc_asprintf(ctx
,
1440 get_local_machine_name(),
1444 if (!ref
->alternate_path
) {
1449 /* Don't enumerate if we're an msdfs proxy. */
1450 if (*msdfs_proxy
!= '\0') {
1454 /* Now enumerate all dfs links */
1455 dirp
= SMB_VFS_OPENDIR(conn
, ".", NULL
, 0);
1460 while ((dname
= vfs_readdirname(conn
, dirp
, NULL
, &talloced
))
1462 char *link_target
= NULL
;
1463 if (cnt
>= jn_remain
) {
1464 DEBUG(2, ("form_junctions: ran out of MSDFS "
1466 TALLOC_FREE(talloced
);
1469 if (is_msdfs_link_internal(ctx
,
1471 dname
, &link_target
,
1473 if (parse_msdfs_symlink(ctx
,
1475 &jucn
[cnt
].referral_list
,
1476 &jucn
[cnt
].referral_count
)) {
1478 jucn
[cnt
].service_name
= talloc_strdup(ctx
,
1480 jucn
[cnt
].volume_name
= talloc_strdup(ctx
,
1482 if (!jucn
[cnt
].service_name
||
1483 !jucn
[cnt
].volume_name
) {
1484 TALLOC_FREE(talloced
);
1487 jucn
[cnt
].comment
= "";
1490 TALLOC_FREE(link_target
);
1492 TALLOC_FREE(talloced
);
1498 SMB_VFS_CLOSEDIR(conn
,dirp
);
1501 vfs_ChDir(conn
, cwd
);
1506 struct junction_map
*enum_msdfs_links(TALLOC_CTX
*ctx
, size_t *p_num_jn
)
1508 struct junction_map
*jn
= NULL
;
1510 size_t jn_count
= 0;
1514 if(!lp_host_msdfs()) {
1518 /* Ensure all the usershares are loaded. */
1520 load_registry_shares();
1521 sharecount
= load_usershare_shares(NULL
, connections_snum_used
);
1524 for(i
=0;i
< sharecount
;i
++) {
1525 if(lp_msdfs_root(i
)) {
1526 jn_count
+= count_dfs_links(ctx
, i
);
1529 if (jn_count
== 0) {
1532 jn
= talloc_array(ctx
, struct junction_map
, jn_count
);
1536 for(i
=0; i
< sharecount
; i
++) {
1537 if (*p_num_jn
>= jn_count
) {
1540 if(lp_msdfs_root(i
)) {
1541 *p_num_jn
+= form_junctions(ctx
, i
,
1543 jn_count
- *p_num_jn
);
1549 /******************************************************************************
1550 Core function to resolve a dfs pathname possibly containing a wildcard. If
1551 ppath_contains_wcard != NULL, it will be set to true if a wildcard is
1552 detected during dfs resolution.
1553 ******************************************************************************/
1555 NTSTATUS
resolve_dfspath_wcard(TALLOC_CTX
*ctx
,
1556 connection_struct
*conn
,
1558 const char *name_in
,
1560 bool allow_broken_path
,
1562 bool *ppath_contains_wcard
)
1564 bool path_contains_wcard
;
1565 NTSTATUS status
= NT_STATUS_OK
;
1567 if (dfs_pathnames
) {
1568 status
= dfs_redirect(ctx
,
1574 &path_contains_wcard
);
1576 if (NT_STATUS_IS_OK(status
) && ppath_contains_wcard
!= NULL
) {
1577 *ppath_contains_wcard
= path_contains_wcard
;
1581 * Cheat and just return a copy of the in ptr.
1582 * Once srvstr_get_path() uses talloc it'll
1583 * be a talloced ptr anyway.
1585 *pp_name_out
= discard_const_p(char, name_in
);