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 "smbd/globals.h"
27 /**********************************************************************
28 Parse a DFS pathname of the form \hostname\service\reqpath
29 into the dfs_path structure.
30 If POSIX pathnames is true, the pathname may also be of the
31 form /hostname/service/reqpath.
32 We cope with either here.
34 Unfortunately, due to broken clients who might set the
35 SVAL(inbuf,smb_flg2) & FLAGS2_DFS_PATHNAMES bit and then
36 send a local path, we have to cope with that too....
38 If conn != NULL then ensure the provided service is
39 the one pointed to by the connection.
41 This version does everything using pointers within one copy of the
42 pathname string, talloced on the struct dfs_path pointer (which
43 must be talloced). This may be too clever to live....
45 **********************************************************************/
47 static NTSTATUS
parse_dfs_path(connection_struct
*conn
,
50 struct dfs_path
*pdp
, /* MUST BE TALLOCED */
51 bool *ppath_contains_wcard
)
53 struct smbd_server_connection
*sconn
= smbd_server_conn
;
58 NTSTATUS status
= NT_STATUS_OK
;
64 * This is the only talloc we should need to do
65 * on the struct dfs_path. All the pointers inside
66 * it should point to offsets within this string.
69 pathname_local
= talloc_strdup(pdp
, pathname
);
70 if (!pathname_local
) {
71 return NT_STATUS_NO_MEMORY
;
73 /* Get a pointer to the terminating '\0' */
74 eos_ptr
= &pathname_local
[strlen(pathname_local
)];
75 p
= temp
= pathname_local
;
77 pdp
->posix_path
= (lp_posix_pathnames() && *pathname
== '/');
79 sepchar
= pdp
->posix_path
? '/' : '\\';
81 if (!sconn
->using_smb2
&& (*pathname
!= sepchar
)) {
82 DEBUG(10,("parse_dfs_path: path %s doesn't start with %c\n",
85 * Possibly client sent a local path by mistake.
86 * Try and convert to a local path.
89 pdp
->hostname
= eos_ptr
; /* "" */
90 pdp
->servicename
= eos_ptr
; /* "" */
92 /* We've got no info about separators. */
93 pdp
->posix_path
= lp_posix_pathnames();
95 DEBUG(10,("parse_dfs_path: trying to convert %s to a "
102 * Safe to use on talloc'ed string as it only shrinks.
103 * It also doesn't affect the eos_ptr.
105 trim_char(temp
,sepchar
,sepchar
);
107 DEBUG(10,("parse_dfs_path: temp = |%s| after trimming %c's\n",
111 /* Parse out hostname. */
112 p
= strchr_m(temp
,sepchar
);
114 DEBUG(10,("parse_dfs_path: can't parse hostname from path %s\n",
117 * Possibly client sent a local path by mistake.
118 * Try and convert to a local path.
121 pdp
->hostname
= eos_ptr
; /* "" */
122 pdp
->servicename
= eos_ptr
; /* "" */
125 DEBUG(10,("parse_dfs_path: trying to convert %s "
131 pdp
->hostname
= temp
;
133 DEBUG(10,("parse_dfs_path: hostname: %s\n",pdp
->hostname
));
135 /* Parse out servicename. */
137 p
= strchr_m(servicename
,sepchar
);
142 /* Is this really our servicename ? */
143 if (conn
&& !( strequal(servicename
, lp_servicename(SNUM(conn
)))
144 || (strequal(servicename
, HOMES_NAME
)
145 && strequal(lp_servicename(SNUM(conn
)),
146 get_current_username()) )) ) {
147 DEBUG(10,("parse_dfs_path: %s is not our servicename\n",
151 * Possibly client sent a local path by mistake.
152 * Try and convert to a local path.
155 pdp
->hostname
= eos_ptr
; /* "" */
156 pdp
->servicename
= eos_ptr
; /* "" */
158 /* Repair the path - replace the sepchar's
161 *servicename
= sepchar
;
167 DEBUG(10,("parse_dfs_path: trying to convert %s "
173 pdp
->servicename
= servicename
;
175 DEBUG(10,("parse_dfs_path: servicename: %s\n",pdp
->servicename
));
178 /* Client sent self referral \server\share. */
179 pdp
->reqpath
= eos_ptr
; /* "" */
187 *ppath_contains_wcard
= False
;
191 /* Rest is reqpath. */
192 if (pdp
->posix_path
) {
193 status
= check_path_syntax_posix(pdp
->reqpath
);
196 status
= check_path_syntax_wcard(pdp
->reqpath
,
197 ppath_contains_wcard
);
199 status
= check_path_syntax(pdp
->reqpath
);
203 if (!NT_STATUS_IS_OK(status
)) {
204 DEBUG(10,("parse_dfs_path: '%s' failed with %s\n",
205 p
, nt_errstr(status
) ));
209 DEBUG(10,("parse_dfs_path: rest of the path: %s\n",pdp
->reqpath
));
213 /********************************************************
214 Fake up a connection struct for the VFS layer.
215 Note this CHANGES CWD !!!! JRA.
216 *********************************************************/
218 NTSTATUS
create_conn_struct(TALLOC_CTX
*ctx
,
219 connection_struct
**pconn
,
222 struct auth_serversupplied_info
*server_info
,
225 connection_struct
*conn
;
229 conn
= TALLOC_ZERO_P(ctx
, connection_struct
);
231 return NT_STATUS_NO_MEMORY
;
234 connpath
= talloc_strdup(conn
, path
);
237 return NT_STATUS_NO_MEMORY
;
239 connpath
= talloc_string_sub(conn
,
242 lp_servicename(snum
));
245 return NT_STATUS_NO_MEMORY
;
248 /* needed for smbd_vfs_init() */
250 if (!(conn
->params
= TALLOC_ZERO_P(conn
, struct share_params
))) {
251 DEBUG(0, ("TALLOC failed\n"));
253 return NT_STATUS_NO_MEMORY
;
256 conn
->params
->service
= snum
;
258 conn
->sconn
= smbd_server_conn
;
259 conn
->sconn
->smb1
.tcons
.num_open
+= 1;
261 if (server_info
!= NULL
) {
262 conn
->server_info
= copy_serverinfo(conn
, server_info
);
263 if (conn
->server_info
== NULL
) {
264 DEBUG(0, ("copy_serverinfo failed\n"));
266 return NT_STATUS_NO_MEMORY
;
270 set_conn_connectpath(conn
, connpath
);
272 if (!smbd_vfs_init(conn
)) {
273 NTSTATUS status
= map_nt_error_from_unix(errno
);
274 DEBUG(0,("create_conn_struct: smbd_vfs_init failed.\n"));
279 conn
->fs_capabilities
= SMB_VFS_FS_CAPABILITIES(conn
, &conn
->ts_res
);
282 * Windows seems to insist on doing trans2getdfsreferral() calls on
283 * the IPC$ share as the anonymous user. If we try to chdir as that
284 * user we will fail.... WTF ? JRA.
287 oldcwd
= vfs_GetWd(ctx
, conn
);
288 if (oldcwd
== NULL
) {
289 NTSTATUS status
= map_nt_error_from_unix(errno
);
290 DEBUG(3, ("vfs_GetWd failed: %s\n", strerror(errno
)));
295 if (vfs_ChDir(conn
,conn
->connectpath
) != 0) {
296 NTSTATUS status
= map_nt_error_from_unix(errno
);
297 DEBUG(3,("create_conn_struct: Can't ChDir to new conn path %s. "
299 conn
->connectpath
, strerror(errno
) ));
310 /**********************************************************************
311 Parse the contents of a symlink to verify if it is an msdfs referral
312 A valid referral is of the form:
314 msdfs:server1\share1,server2\share2
315 msdfs:server1\share1\pathname,server2\share2\pathname
316 msdfs:server1/share1,server2/share2
317 msdfs:server1/share1/pathname,server2/share2/pathname.
319 Note that the alternate paths returned here must be of the canonicalized
323 \server\share\path\to\file,
325 even in posix path mode. This is because we have no knowledge if the
326 server we're referring to understands posix paths.
327 **********************************************************************/
329 static bool parse_msdfs_symlink(TALLOC_CTX
*ctx
,
331 struct referral
**preflist
,
336 char **alt_path
= NULL
;
338 struct referral
*reflist
;
341 temp
= talloc_strdup(ctx
, target
);
345 prot
= strtok_r(temp
, ":", &saveptr
);
347 DEBUG(0,("parse_msdfs_symlink: invalid path !\n"));
351 alt_path
= TALLOC_ARRAY(ctx
, char *, MAX_REFERRAL_COUNT
);
356 /* parse out the alternate paths */
357 while((count
<MAX_REFERRAL_COUNT
) &&
358 ((alt_path
[count
] = strtok_r(NULL
, ",", &saveptr
)) != NULL
)) {
362 DEBUG(10,("parse_msdfs_symlink: count=%d\n", count
));
365 reflist
= *preflist
= TALLOC_ZERO_ARRAY(ctx
,
366 struct referral
, count
);
367 if(reflist
== NULL
) {
368 TALLOC_FREE(alt_path
);
372 reflist
= *preflist
= NULL
;
375 for(i
=0;i
<count
;i
++) {
378 /* Canonicalize link target.
379 * Replace all /'s in the path by a \ */
380 string_replace(alt_path
[i
], '/', '\\');
382 /* Remove leading '\\'s */
384 while (*p
&& (*p
== '\\')) {
388 reflist
[i
].alternate_path
= talloc_asprintf(ctx
,
391 if (!reflist
[i
].alternate_path
) {
395 reflist
[i
].proximity
= 0;
396 reflist
[i
].ttl
= REFERRAL_TTL
;
397 DEBUG(10, ("parse_msdfs_symlink: Created alt path: %s\n",
398 reflist
[i
].alternate_path
));
403 TALLOC_FREE(alt_path
);
407 /**********************************************************************
408 Returns true if the unix path is a valid msdfs symlink and also
409 returns the target string from inside the link.
410 **********************************************************************/
412 static bool is_msdfs_link_internal(TALLOC_CTX
*ctx
,
413 connection_struct
*conn
,
415 char **pp_link_target
,
416 SMB_STRUCT_STAT
*sbufp
)
418 int referral_len
= 0;
419 #if defined(HAVE_BROKEN_READLINK)
420 char link_target_buf
[PATH_MAX
];
422 char link_target_buf
[7];
425 char *link_target
= NULL
;
426 struct smb_filename smb_fname
;
428 if (pp_link_target
) {
430 link_target
= TALLOC_ARRAY(ctx
, char, bufsize
);
434 *pp_link_target
= link_target
;
436 bufsize
= sizeof(link_target_buf
);
437 link_target
= link_target_buf
;
440 ZERO_STRUCT(smb_fname
);
441 smb_fname
.base_name
= discard_const_p(char, path
);
443 if (SMB_VFS_LSTAT(conn
, &smb_fname
) != 0) {
444 DEBUG(5,("is_msdfs_link_read_target: %s does not exist.\n",
448 if (!S_ISLNK(smb_fname
.st
.st_ex_mode
)) {
449 DEBUG(5,("is_msdfs_link_read_target: %s is not a link.\n",
454 *sbufp
= smb_fname
.st
;
457 referral_len
= SMB_VFS_READLINK(conn
, path
, link_target
, bufsize
- 1);
458 if (referral_len
== -1) {
459 DEBUG(0,("is_msdfs_link_read_target: Error reading "
460 "msdfs link %s: %s\n",
461 path
, strerror(errno
)));
464 link_target
[referral_len
] = '\0';
466 DEBUG(5,("is_msdfs_link_internal: %s -> %s\n",path
,
469 if (!strnequal(link_target
, "msdfs:", 6)) {
476 if (link_target
!= link_target_buf
) {
477 TALLOC_FREE(link_target
);
482 /**********************************************************************
483 Returns true if the unix path is a valid msdfs symlink.
484 **********************************************************************/
486 bool is_msdfs_link(connection_struct
*conn
,
488 SMB_STRUCT_STAT
*sbufp
)
490 return is_msdfs_link_internal(talloc_tos(),
497 /*****************************************************************
498 Used by other functions to decide if a dfs path is remote,
499 and to get the list of referred locations for that remote path.
501 search_flag: For findfirsts, dfs links themselves are not
502 redirected, but paths beyond the links are. For normal smb calls,
503 even dfs links need to be redirected.
505 consumedcntp: how much of the dfs path is being redirected. the client
506 should try the remaining path on the redirected server.
508 If this returns NT_STATUS_PATH_NOT_COVERED the contents of the msdfs
509 link redirect are in targetpath.
510 *****************************************************************/
512 static NTSTATUS
dfs_path_lookup(TALLOC_CTX
*ctx
,
513 connection_struct
*conn
,
514 const char *dfspath
, /* Incoming complete dfs path */
515 const struct dfs_path
*pdp
, /* Parsed out
516 server+share+extrapath. */
517 bool search_flag
, /* Called from a findfirst ? */
519 char **pp_targetpath
)
524 struct smb_filename
*smb_fname
= NULL
;
525 char *canon_dfspath
= NULL
; /* Canonicalized dfs path. (only '/'
528 DEBUG(10,("dfs_path_lookup: Conn path = %s reqpath = %s\n",
529 conn
->connectpath
, pdp
->reqpath
));
532 * Note the unix path conversion here we're doing we can
533 * throw away. We're looking for a symlink for a dfs
534 * resolution, if we don't find it we'll do another
535 * unix_convert later in the codepath.
536 * If we needed to remember what we'd resolved in
537 * dp->reqpath (as the original code did) we'd
538 * copy (localhost, dp->reqpath) on any code
539 * path below that returns True - but I don't
540 * think this is needed. JRA.
543 status
= unix_convert(ctx
, conn
, pdp
->reqpath
, &smb_fname
,
544 search_flag
? UCF_ALWAYS_ALLOW_WCARD_LCOMP
: 0);
546 if (!NT_STATUS_IS_OK(status
)) {
547 if (!NT_STATUS_EQUAL(status
,
548 NT_STATUS_OBJECT_PATH_NOT_FOUND
)) {
552 /* Create an smb_fname to use below. */
553 status
= create_synthetic_smb_fname(ctx
, pdp
->reqpath
, NULL
,
555 if (!NT_STATUS_IS_OK(status
)) {
560 /* Optimization - check if we can redirect the whole path. */
562 if (is_msdfs_link_internal(ctx
, conn
, smb_fname
->base_name
,
563 pp_targetpath
, NULL
)) {
565 DEBUG(6,("dfs_path_lookup (FindFirst) No redirection "
566 "for dfs link %s.\n", dfspath
));
567 status
= NT_STATUS_OK
;
571 DEBUG(6,("dfs_path_lookup: %s resolves to a "
572 "valid dfs link %s.\n", dfspath
,
573 pp_targetpath
? *pp_targetpath
: ""));
576 *consumedcntp
= strlen(dfspath
);
578 status
= NT_STATUS_PATH_NOT_COVERED
;
582 /* Prepare to test only for '/' components in the given path,
583 * so if a Windows path replace all '\\' characters with '/'.
584 * For a POSIX DFS path we know all separators are already '/'. */
586 canon_dfspath
= talloc_strdup(ctx
, dfspath
);
587 if (!canon_dfspath
) {
588 status
= NT_STATUS_NO_MEMORY
;
591 if (!pdp
->posix_path
) {
592 string_replace(canon_dfspath
, '\\', '/');
596 * localpath comes out of unix_convert, so it has
597 * no trailing backslash. Make sure that canon_dfspath hasn't either.
598 * Fix for bug #4860 from Jan Martin <Jan.Martin@rwedea.com>.
601 trim_char(canon_dfspath
,0,'/');
604 * Redirect if any component in the path is a link.
605 * We do this by walking backwards through the
606 * local path, chopping off the last component
607 * in both the local path and the canonicalized
608 * DFS path. If we hit a DFS link then we're done.
611 p
= strrchr_m(smb_fname
->base_name
, '/');
613 q
= strrchr_m(canon_dfspath
, '/');
622 if (is_msdfs_link_internal(ctx
, conn
,
623 smb_fname
->base_name
, pp_targetpath
,
625 DEBUG(4, ("dfs_path_lookup: Redirecting %s because "
626 "parent %s is dfs link\n", dfspath
,
627 smb_fname_str_dbg(smb_fname
)));
630 *consumedcntp
= strlen(canon_dfspath
);
631 DEBUG(10, ("dfs_path_lookup: Path consumed: %s "
637 status
= NT_STATUS_PATH_NOT_COVERED
;
641 /* Step back on the filesystem. */
642 p
= strrchr_m(smb_fname
->base_name
, '/');
645 /* And in the canonicalized dfs path. */
646 q
= strrchr_m(canon_dfspath
, '/');
650 status
= NT_STATUS_OK
;
652 TALLOC_FREE(smb_fname
);
656 /*****************************************************************
657 Decides if a dfs pathname should be redirected or not.
658 If not, the pathname is converted to a tcon-relative local unix path
660 search_wcard_flag: this flag performs 2 functions both related
661 to searches. See resolve_dfs_path() and parse_dfs_path_XX()
664 This function can return NT_STATUS_OK, meaning use the returned path as-is
665 (mapped into a local path).
666 or NT_STATUS_NOT_COVERED meaning return a DFS redirect, or
667 any other NT_STATUS error which is a genuine error to be
668 returned to the client.
669 *****************************************************************/
671 static NTSTATUS
dfs_redirect(TALLOC_CTX
*ctx
,
672 connection_struct
*conn
,
674 bool search_wcard_flag
,
676 bool *ppath_contains_wcard
)
679 struct dfs_path
*pdp
= TALLOC_P(ctx
, struct dfs_path
);
682 return NT_STATUS_NO_MEMORY
;
685 status
= parse_dfs_path(conn
, path_in
, search_wcard_flag
, pdp
,
686 ppath_contains_wcard
);
687 if (!NT_STATUS_IS_OK(status
)) {
692 if (pdp
->reqpath
[0] == '\0') {
694 *pp_path_out
= talloc_strdup(ctx
, "");
696 return NT_STATUS_NO_MEMORY
;
698 DEBUG(5,("dfs_redirect: self-referral.\n"));
702 /* If dfs pathname for a non-dfs share, convert to tcon-relative
703 path and return OK */
705 if (!lp_msdfs_root(SNUM(conn
))) {
706 *pp_path_out
= talloc_strdup(ctx
, pdp
->reqpath
);
709 return NT_STATUS_NO_MEMORY
;
714 /* If it looked like a local path (zero hostname/servicename)
715 * just treat as a tcon-relative path. */
717 if (pdp
->hostname
[0] == '\0' && pdp
->servicename
[0] == '\0') {
718 *pp_path_out
= talloc_strdup(ctx
, pdp
->reqpath
);
721 return NT_STATUS_NO_MEMORY
;
726 if (!( strequal(pdp
->servicename
, lp_servicename(SNUM(conn
)))
727 || (strequal(pdp
->servicename
, HOMES_NAME
)
728 && strequal(lp_servicename(SNUM(conn
)),
729 conn
->server_info
->sanitized_username
) )) ) {
731 /* The given sharename doesn't match this connection. */
734 return NT_STATUS_OBJECT_PATH_NOT_FOUND
;
737 status
= dfs_path_lookup(ctx
, conn
, path_in
, pdp
,
738 search_wcard_flag
, NULL
, NULL
);
739 if (!NT_STATUS_IS_OK(status
)) {
740 if (NT_STATUS_EQUAL(status
, NT_STATUS_PATH_NOT_COVERED
)) {
741 DEBUG(3,("dfs_redirect: Redirecting %s\n", path_in
));
743 DEBUG(10,("dfs_redirect: dfs_path_lookup "
744 "failed for %s with %s\n",
745 path_in
, nt_errstr(status
) ));
750 DEBUG(3,("dfs_redirect: Not redirecting %s.\n", path_in
));
752 /* Form non-dfs tcon-relative path */
753 *pp_path_out
= talloc_strdup(ctx
, pdp
->reqpath
);
756 return NT_STATUS_NO_MEMORY
;
759 DEBUG(3,("dfs_redirect: Path %s converted to non-dfs path %s\n",
766 /**********************************************************************
767 Return a self referral.
768 **********************************************************************/
770 static NTSTATUS
self_ref(TALLOC_CTX
*ctx
,
771 const char *dfs_path
,
772 struct junction_map
*jucn
,
774 bool *self_referralp
)
776 struct referral
*ref
;
778 *self_referralp
= True
;
780 jucn
->referral_count
= 1;
781 if((ref
= TALLOC_ZERO_P(ctx
, struct referral
)) == NULL
) {
782 return NT_STATUS_NO_MEMORY
;
785 ref
->alternate_path
= talloc_strdup(ctx
, dfs_path
);
786 if (!ref
->alternate_path
) {
787 return NT_STATUS_NO_MEMORY
;
790 ref
->ttl
= REFERRAL_TTL
;
791 jucn
->referral_list
= ref
;
792 *consumedcntp
= strlen(dfs_path
);
796 /**********************************************************************
797 Gets valid referrals for a dfs path and fills up the
798 junction_map structure.
799 **********************************************************************/
801 NTSTATUS
get_referred_path(TALLOC_CTX
*ctx
,
802 const char *dfs_path
,
803 struct junction_map
*jucn
,
805 bool *self_referralp
)
807 struct connection_struct
*conn
;
808 char *targetpath
= NULL
;
810 NTSTATUS status
= NT_STATUS_NOT_FOUND
;
812 struct dfs_path
*pdp
= TALLOC_P(ctx
, struct dfs_path
);
816 return NT_STATUS_NO_MEMORY
;
819 *self_referralp
= False
;
821 status
= parse_dfs_path(NULL
, dfs_path
, False
, pdp
, &dummy
);
822 if (!NT_STATUS_IS_OK(status
)) {
826 jucn
->service_name
= talloc_strdup(ctx
, pdp
->servicename
);
827 jucn
->volume_name
= talloc_strdup(ctx
, pdp
->reqpath
);
828 if (!jucn
->service_name
|| !jucn
->volume_name
) {
830 return NT_STATUS_NO_MEMORY
;
833 /* Verify the share is a dfs root */
834 snum
= lp_servicenumber(jucn
->service_name
);
836 fstring service_name
;
837 fstrcpy(service_name
, jucn
->service_name
);
838 if ((snum
= find_service(service_name
)) < 0) {
839 return NT_STATUS_NOT_FOUND
;
841 TALLOC_FREE(jucn
->service_name
);
842 jucn
->service_name
= talloc_strdup(ctx
, service_name
);
843 if (!jucn
->service_name
) {
845 return NT_STATUS_NO_MEMORY
;
849 if (!lp_msdfs_root(snum
) && (*lp_msdfs_proxy(snum
) == '\0')) {
850 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not "
852 pdp
->servicename
, dfs_path
));
854 return NT_STATUS_NOT_FOUND
;
858 * Self referrals are tested with a anonymous IPC connection and
859 * a GET_DFS_REFERRAL call to \\server\share. (which means
860 * dp.reqpath[0] points to an empty string). create_conn_struct cd's
861 * into the directory and will fail if it cannot (as the anonymous
862 * user). Cope with this.
865 if (pdp
->reqpath
[0] == '\0') {
867 struct referral
*ref
;
869 if (*lp_msdfs_proxy(snum
) == '\0') {
879 * It's an msdfs proxy share. Redirect to
880 * the configured target share.
883 jucn
->referral_count
= 1;
884 if ((ref
= TALLOC_ZERO_P(ctx
, struct referral
)) == NULL
) {
886 return NT_STATUS_NO_MEMORY
;
889 if (!(tmp
= talloc_strdup(ctx
, lp_msdfs_proxy(snum
)))) {
891 return NT_STATUS_NO_MEMORY
;
894 trim_string(tmp
, "\\", 0);
896 ref
->alternate_path
= talloc_asprintf(ctx
, "\\%s", tmp
);
899 if (!ref
->alternate_path
) {
901 return NT_STATUS_NO_MEMORY
;
904 if (pdp
->reqpath
[0] != '\0') {
905 ref
->alternate_path
= talloc_asprintf_append(
909 if (!ref
->alternate_path
) {
911 return NT_STATUS_NO_MEMORY
;
915 ref
->ttl
= REFERRAL_TTL
;
916 jucn
->referral_list
= ref
;
917 *consumedcntp
= strlen(dfs_path
);
922 status
= create_conn_struct(ctx
, &conn
, snum
, lp_pathname(snum
),
924 if (!NT_STATUS_IS_OK(status
)) {
929 /* If this is a DFS path dfs_lookup should return
930 * NT_STATUS_PATH_NOT_COVERED. */
932 status
= dfs_path_lookup(ctx
, conn
, dfs_path
, pdp
,
933 False
, consumedcntp
, &targetpath
);
935 if (!NT_STATUS_EQUAL(status
, NT_STATUS_PATH_NOT_COVERED
)) {
936 DEBUG(3,("get_referred_path: No valid referrals for path %s\n",
938 vfs_ChDir(conn
, oldpath
);
944 /* We know this is a valid dfs link. Parse the targetpath. */
945 if (!parse_msdfs_symlink(ctx
, targetpath
,
946 &jucn
->referral_list
,
947 &jucn
->referral_count
)) {
948 DEBUG(3,("get_referred_path: failed to parse symlink "
949 "target %s\n", targetpath
));
950 vfs_ChDir(conn
, oldpath
);
953 return NT_STATUS_NOT_FOUND
;
956 vfs_ChDir(conn
, oldpath
);
962 static int setup_ver2_dfs_referral(const char *pathname
,
964 struct junction_map
*junction
,
967 char* pdata
= *ppdata
;
969 smb_ucs2_t
*uni_requestedpath
= NULL
;
970 int uni_reqpathoffset1
,uni_reqpathoffset2
;
972 int requestedpathlen
=0;
977 DEBUG(10,("Setting up version2 referral\nRequested path:\n"));
979 requestedpathlen
= rpcstr_push_talloc(talloc_tos(),
980 &uni_requestedpath
, pathname
);
981 if (uni_requestedpath
== NULL
|| requestedpathlen
== 0) {
986 dump_data(0, (unsigned char *)uni_requestedpath
,
990 DEBUG(10,("ref count = %u\n",junction
->referral_count
));
992 uni_reqpathoffset1
= REFERRAL_HEADER_SIZE
+
993 VERSION2_REFERRAL_SIZE
* junction
->referral_count
;
995 uni_reqpathoffset2
= uni_reqpathoffset1
+ requestedpathlen
;
997 uni_curroffset
= uni_reqpathoffset2
+ requestedpathlen
;
999 reply_size
= REFERRAL_HEADER_SIZE
+
1000 VERSION2_REFERRAL_SIZE
*junction
->referral_count
+
1001 2 * requestedpathlen
;
1002 DEBUG(10,("reply_size: %u\n",reply_size
));
1004 /* add up the unicode lengths of all the referral paths */
1005 for(i
=0;i
<junction
->referral_count
;i
++) {
1006 DEBUG(10,("referral %u : %s\n",
1008 junction
->referral_list
[i
].alternate_path
));
1010 (strlen(junction
->referral_list
[i
].alternate_path
)+1)*2;
1013 DEBUG(10,("reply_size = %u\n",reply_size
));
1014 /* add the unexplained 0x16 bytes */
1017 pdata
= (char *)SMB_REALLOC(pdata
,reply_size
);
1019 DEBUG(0,("Realloc failed!\n"));
1024 /* copy in the dfs requested paths.. required for offset calculations */
1025 memcpy(pdata
+uni_reqpathoffset1
,uni_requestedpath
,requestedpathlen
);
1026 memcpy(pdata
+uni_reqpathoffset2
,uni_requestedpath
,requestedpathlen
);
1028 /* create the header */
1029 SSVAL(pdata
,0,requestedpathlen
- 2); /* UCS2 of path consumed minus
1031 /* number of referral in this pkt */
1032 SSVAL(pdata
,2,junction
->referral_count
);
1034 SIVAL(pdata
,4,DFSREF_REFERRAL_SERVER
| DFSREF_STORAGE_SERVER
);
1036 SIVAL(pdata
,4,DFSREF_STORAGE_SERVER
);
1040 /* add the referral elements */
1041 for(i
=0;i
<junction
->referral_count
;i
++) {
1042 struct referral
* ref
= &junction
->referral_list
[i
];
1045 SSVAL(pdata
,offset
,2); /* version 2 */
1046 SSVAL(pdata
,offset
+2,VERSION2_REFERRAL_SIZE
);
1048 SSVAL(pdata
,offset
+4,1);
1050 SSVAL(pdata
,offset
+4,0);
1053 /* ref_flags :use path_consumed bytes? */
1054 SSVAL(pdata
,offset
+6,0);
1055 SIVAL(pdata
,offset
+8,ref
->proximity
);
1056 SIVAL(pdata
,offset
+12,ref
->ttl
);
1058 SSVAL(pdata
,offset
+16,uni_reqpathoffset1
-offset
);
1059 SSVAL(pdata
,offset
+18,uni_reqpathoffset2
-offset
);
1060 /* copy referred path into current offset */
1061 unilen
= rpcstr_push(pdata
+uni_curroffset
,
1062 ref
->alternate_path
,
1063 reply_size
- uni_curroffset
,
1066 SSVAL(pdata
,offset
+20,uni_curroffset
-offset
);
1068 uni_curroffset
+= unilen
;
1069 offset
+= VERSION2_REFERRAL_SIZE
;
1071 /* add in the unexplained 22 (0x16) bytes at the end */
1072 memset(pdata
+uni_curroffset
,'\0',0x16);
1076 static int setup_ver3_dfs_referral(const char *pathname
,
1078 struct junction_map
*junction
,
1081 char *pdata
= *ppdata
;
1083 smb_ucs2_t
*uni_reqpath
= NULL
;
1084 int uni_reqpathoffset1
, uni_reqpathoffset2
;
1091 DEBUG(10,("setting up version3 referral\n"));
1093 reqpathlen
= rpcstr_push_talloc(talloc_tos(), &uni_reqpath
, pathname
);
1094 if (uni_reqpath
== NULL
|| reqpathlen
== 0) {
1099 dump_data(0, (unsigned char *)uni_reqpath
,
1103 uni_reqpathoffset1
= REFERRAL_HEADER_SIZE
+
1104 VERSION3_REFERRAL_SIZE
* junction
->referral_count
;
1105 uni_reqpathoffset2
= uni_reqpathoffset1
+ reqpathlen
;
1106 reply_size
= uni_curroffset
= uni_reqpathoffset2
+ reqpathlen
;
1108 for(i
=0;i
<junction
->referral_count
;i
++) {
1109 DEBUG(10,("referral %u : %s\n",
1111 junction
->referral_list
[i
].alternate_path
));
1113 (strlen(junction
->referral_list
[i
].alternate_path
)+1)*2;
1116 pdata
= (char *)SMB_REALLOC(pdata
,reply_size
);
1118 DEBUG(0,("version3 referral setup:"
1119 "malloc failed for Realloc!\n"));
1124 /* create the header */
1125 SSVAL(pdata
,0,reqpathlen
- 2); /* UCS2 of path consumed minus
1127 SSVAL(pdata
,2,junction
->referral_count
); /* number of referral */
1129 SIVAL(pdata
,4,DFSREF_REFERRAL_SERVER
| DFSREF_STORAGE_SERVER
);
1131 SIVAL(pdata
,4,DFSREF_STORAGE_SERVER
);
1134 /* copy in the reqpaths */
1135 memcpy(pdata
+uni_reqpathoffset1
,uni_reqpath
,reqpathlen
);
1136 memcpy(pdata
+uni_reqpathoffset2
,uni_reqpath
,reqpathlen
);
1139 for(i
=0;i
<junction
->referral_count
;i
++) {
1140 struct referral
* ref
= &(junction
->referral_list
[i
]);
1143 SSVAL(pdata
,offset
,3); /* version 3 */
1144 SSVAL(pdata
,offset
+2,VERSION3_REFERRAL_SIZE
);
1146 SSVAL(pdata
,offset
+4,1);
1148 SSVAL(pdata
,offset
+4,0);
1151 /* ref_flags :use path_consumed bytes? */
1152 SSVAL(pdata
,offset
+6,0);
1153 SIVAL(pdata
,offset
+8,ref
->ttl
);
1155 SSVAL(pdata
,offset
+12,uni_reqpathoffset1
-offset
);
1156 SSVAL(pdata
,offset
+14,uni_reqpathoffset2
-offset
);
1157 /* copy referred path into current offset */
1158 unilen
= rpcstr_push(pdata
+uni_curroffset
,ref
->alternate_path
,
1159 reply_size
- uni_curroffset
,
1160 STR_UNICODE
| STR_TERMINATE
);
1161 SSVAL(pdata
,offset
+16,uni_curroffset
-offset
);
1162 /* copy 0x10 bytes of 00's in the ServiceSite GUID */
1163 memset(pdata
+offset
+18,'\0',16);
1165 uni_curroffset
+= unilen
;
1166 offset
+= VERSION3_REFERRAL_SIZE
;
1171 /******************************************************************
1172 Set up the DFS referral for the dfs pathname. This call returns
1173 the amount of the path covered by this server, and where the
1174 client should be redirected to. This is the meat of the
1175 TRANS2_GET_DFS_REFERRAL call.
1176 ******************************************************************/
1178 int setup_dfs_referral(connection_struct
*orig_conn
,
1179 const char *dfs_path
,
1180 int max_referral_level
,
1181 char **ppdata
, NTSTATUS
*pstatus
)
1183 struct junction_map
*junction
= NULL
;
1184 int consumedcnt
= 0;
1185 bool self_referral
= False
;
1187 char *pathnamep
= NULL
;
1188 char *local_dfs_path
= NULL
;
1191 if (!(ctx
=talloc_init("setup_dfs_referral"))) {
1192 *pstatus
= NT_STATUS_NO_MEMORY
;
1196 /* get the junction entry */
1198 talloc_destroy(ctx
);
1199 *pstatus
= NT_STATUS_NOT_FOUND
;
1204 * Trim pathname sent by client so it begins with only one backslash.
1205 * Two backslashes confuse some dfs clients
1208 local_dfs_path
= talloc_strdup(ctx
,dfs_path
);
1209 if (!local_dfs_path
) {
1210 *pstatus
= NT_STATUS_NO_MEMORY
;
1211 talloc_destroy(ctx
);
1214 pathnamep
= local_dfs_path
;
1215 while (IS_DIRECTORY_SEP(pathnamep
[0]) &&
1216 IS_DIRECTORY_SEP(pathnamep
[1])) {
1220 junction
= TALLOC_ZERO_P(ctx
, struct junction_map
);
1222 *pstatus
= NT_STATUS_NO_MEMORY
;
1223 talloc_destroy(ctx
);
1227 /* The following call can change cwd. */
1228 *pstatus
= get_referred_path(ctx
, pathnamep
, junction
,
1229 &consumedcnt
, &self_referral
);
1230 if (!NT_STATUS_IS_OK(*pstatus
)) {
1231 vfs_ChDir(orig_conn
,orig_conn
->connectpath
);
1232 talloc_destroy(ctx
);
1235 vfs_ChDir(orig_conn
,orig_conn
->connectpath
);
1237 if (!self_referral
) {
1238 pathnamep
[consumedcnt
] = '\0';
1240 if( DEBUGLVL( 3 ) ) {
1242 dbgtext("setup_dfs_referral: Path %s to "
1243 "alternate path(s):",
1245 for(i
=0;i
<junction
->referral_count
;i
++)
1247 junction
->referral_list
[i
].alternate_path
);
1252 /* create the referral depeding on version */
1253 DEBUG(10,("max_referral_level :%d\n",max_referral_level
));
1255 if (max_referral_level
< 2) {
1256 max_referral_level
= 2;
1258 if (max_referral_level
> 3) {
1259 max_referral_level
= 3;
1262 switch(max_referral_level
) {
1264 reply_size
= setup_ver2_dfs_referral(pathnamep
,
1269 reply_size
= setup_ver3_dfs_referral(pathnamep
, ppdata
,
1270 junction
, self_referral
);
1273 DEBUG(0,("setup_dfs_referral: Invalid dfs referral "
1275 max_referral_level
));
1276 talloc_destroy(ctx
);
1277 *pstatus
= NT_STATUS_INVALID_LEVEL
;
1282 DEBUGADD(0,("DFS Referral pdata:\n"));
1283 dump_data(0,(uint8
*)*ppdata
,reply_size
);
1286 talloc_destroy(ctx
);
1287 *pstatus
= NT_STATUS_OK
;
1291 /**********************************************************************
1292 The following functions are called by the NETDFS RPC pipe functions
1293 **********************************************************************/
1295 /*********************************************************************
1296 Creates a junction structure from a DFS pathname
1297 **********************************************************************/
1299 bool create_junction(TALLOC_CTX
*ctx
,
1300 const char *dfs_path
,
1301 struct junction_map
*jucn
)
1305 struct dfs_path
*pdp
= TALLOC_P(ctx
,struct dfs_path
);
1311 status
= parse_dfs_path(NULL
, dfs_path
, False
, pdp
, &dummy
);
1312 if (!NT_STATUS_IS_OK(status
)) {
1316 /* check if path is dfs : validate first token */
1317 if (!is_myname_or_ipaddr(pdp
->hostname
)) {
1318 DEBUG(4,("create_junction: Invalid hostname %s "
1320 pdp
->hostname
, dfs_path
));
1325 /* Check for a non-DFS share */
1326 snum
= lp_servicenumber(pdp
->servicename
);
1328 if(snum
< 0 || !lp_msdfs_root(snum
)) {
1329 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1335 jucn
->service_name
= talloc_strdup(ctx
, pdp
->servicename
);
1336 jucn
->volume_name
= talloc_strdup(ctx
, pdp
->reqpath
);
1337 jucn
->comment
= talloc_strdup(ctx
, lp_comment(snum
));
1340 if (!jucn
->service_name
|| !jucn
->volume_name
|| ! jucn
->comment
) {
1346 /**********************************************************************
1347 Forms a valid Unix pathname from the junction
1348 **********************************************************************/
1350 static bool junction_to_local_path(const struct junction_map
*jucn
,
1352 connection_struct
**conn_out
,
1358 snum
= lp_servicenumber(jucn
->service_name
);
1362 status
= create_conn_struct(talloc_tos(), conn_out
, snum
,
1363 lp_pathname(snum
), NULL
, oldpath
);
1364 if (!NT_STATUS_IS_OK(status
)) {
1368 *pp_path_out
= talloc_asprintf(*conn_out
,
1372 if (!*pp_path_out
) {
1373 vfs_ChDir(*conn_out
, *oldpath
);
1374 conn_free(*conn_out
);
1380 bool create_msdfs_link(const struct junction_map
*jucn
)
1384 char *msdfs_link
= NULL
;
1385 connection_struct
*conn
;
1387 bool insert_comma
= False
;
1390 if(!junction_to_local_path(jucn
, &path
, &conn
, &cwd
)) {
1394 /* Form the msdfs_link contents */
1395 msdfs_link
= talloc_strdup(conn
, "msdfs:");
1399 for(i
=0; i
<jucn
->referral_count
; i
++) {
1400 char *refpath
= jucn
->referral_list
[i
].alternate_path
;
1402 /* Alternate paths always use Windows separators. */
1403 trim_char(refpath
, '\\', '\\');
1404 if(*refpath
== '\0') {
1406 insert_comma
= False
;
1410 if (i
> 0 && insert_comma
) {
1411 msdfs_link
= talloc_asprintf_append_buffer(msdfs_link
,
1415 msdfs_link
= talloc_asprintf_append_buffer(msdfs_link
,
1423 if (!insert_comma
) {
1424 insert_comma
= True
;
1428 DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
1431 if(SMB_VFS_SYMLINK(conn
, msdfs_link
, path
) < 0) {
1432 if (errno
== EEXIST
) {
1433 struct smb_filename
*smb_fname
= NULL
;
1436 status
= create_synthetic_smb_fname(talloc_tos(), path
,
1439 if (!NT_STATUS_IS_OK(status
)) {
1440 errno
= map_errno_from_nt_status(status
);
1444 if(SMB_VFS_UNLINK(conn
, smb_fname
)!=0) {
1445 TALLOC_FREE(smb_fname
);
1448 TALLOC_FREE(smb_fname
);
1450 if (SMB_VFS_SYMLINK(conn
, msdfs_link
, path
) < 0) {
1451 DEBUG(1,("create_msdfs_link: symlink failed "
1452 "%s -> %s\nError: %s\n",
1453 path
, msdfs_link
, strerror(errno
)));
1461 vfs_ChDir(conn
, cwd
);
1466 bool remove_msdfs_link(const struct junction_map
*jucn
)
1470 connection_struct
*conn
;
1472 struct smb_filename
*smb_fname
= NULL
;
1475 if (!junction_to_local_path(jucn
, &path
, &conn
, &cwd
)) {
1479 status
= create_synthetic_smb_fname(talloc_tos(), path
,
1482 if (!NT_STATUS_IS_OK(status
)) {
1483 errno
= map_errno_from_nt_status(status
);
1487 if( SMB_VFS_UNLINK(conn
, smb_fname
) == 0 ) {
1491 TALLOC_FREE(smb_fname
);
1492 vfs_ChDir(conn
, cwd
);
1497 /*********************************************************************
1498 Return the number of DFS links at the root of this share.
1499 *********************************************************************/
1501 static int count_dfs_links(TALLOC_CTX
*ctx
, int snum
)
1504 SMB_STRUCT_DIR
*dirp
= NULL
;
1505 const char *dname
= NULL
;
1506 char *talloced
= NULL
;
1507 const char *connect_path
= lp_pathname(snum
);
1508 const char *msdfs_proxy
= lp_msdfs_proxy(snum
);
1509 connection_struct
*conn
;
1513 if(*connect_path
== '\0') {
1518 * Fake up a connection struct for the VFS layer.
1521 status
= create_conn_struct(talloc_tos(), &conn
, snum
, connect_path
,
1523 if (!NT_STATUS_IS_OK(status
)) {
1524 DEBUG(3, ("create_conn_struct failed: %s\n",
1525 nt_errstr(status
)));
1529 /* Count a link for the msdfs root - convention */
1532 /* No more links if this is an msdfs proxy. */
1533 if (*msdfs_proxy
!= '\0') {
1537 /* Now enumerate all dfs links */
1538 dirp
= SMB_VFS_OPENDIR(conn
, ".", NULL
, 0);
1543 while ((dname
= vfs_readdirname(conn
, dirp
, NULL
, &talloced
))
1545 if (is_msdfs_link(conn
,
1550 TALLOC_FREE(talloced
);
1553 SMB_VFS_CLOSEDIR(conn
,dirp
);
1556 vfs_ChDir(conn
, cwd
);
1561 /*********************************************************************
1562 *********************************************************************/
1564 static int form_junctions(TALLOC_CTX
*ctx
,
1566 struct junction_map
*jucn
,
1570 SMB_STRUCT_DIR
*dirp
= NULL
;
1571 const char *dname
= NULL
;
1572 char *talloced
= NULL
;
1573 const char *connect_path
= lp_pathname(snum
);
1574 char *service_name
= lp_servicename(snum
);
1575 const char *msdfs_proxy
= lp_msdfs_proxy(snum
);
1576 connection_struct
*conn
;
1577 struct referral
*ref
= NULL
;
1581 if (jn_remain
== 0) {
1585 if(*connect_path
== '\0') {
1590 * Fake up a connection struct for the VFS layer.
1593 status
= create_conn_struct(ctx
, &conn
, snum
, connect_path
, NULL
,
1595 if (!NT_STATUS_IS_OK(status
)) {
1596 DEBUG(3, ("create_conn_struct failed: %s\n",
1597 nt_errstr(status
)));
1601 /* form a junction for the msdfs root - convention
1602 DO NOT REMOVE THIS: NT clients will not work with us
1603 if this is not present
1605 jucn
[cnt
].service_name
= talloc_strdup(ctx
,service_name
);
1606 jucn
[cnt
].volume_name
= talloc_strdup(ctx
, "");
1607 if (!jucn
[cnt
].service_name
|| !jucn
[cnt
].volume_name
) {
1610 jucn
[cnt
].comment
= "";
1611 jucn
[cnt
].referral_count
= 1;
1613 ref
= jucn
[cnt
].referral_list
= TALLOC_ZERO_P(ctx
, struct referral
);
1614 if (jucn
[cnt
].referral_list
== NULL
) {
1619 ref
->ttl
= REFERRAL_TTL
;
1620 if (*msdfs_proxy
!= '\0') {
1621 ref
->alternate_path
= talloc_strdup(ctx
,
1624 ref
->alternate_path
= talloc_asprintf(ctx
,
1626 get_local_machine_name(),
1630 if (!ref
->alternate_path
) {
1635 /* Don't enumerate if we're an msdfs proxy. */
1636 if (*msdfs_proxy
!= '\0') {
1640 /* Now enumerate all dfs links */
1641 dirp
= SMB_VFS_OPENDIR(conn
, ".", NULL
, 0);
1646 while ((dname
= vfs_readdirname(conn
, dirp
, NULL
, &talloced
))
1648 char *link_target
= NULL
;
1649 if (cnt
>= jn_remain
) {
1650 DEBUG(2, ("form_junctions: ran out of MSDFS "
1652 TALLOC_FREE(talloced
);
1655 if (is_msdfs_link_internal(ctx
,
1657 dname
, &link_target
,
1659 if (parse_msdfs_symlink(ctx
,
1661 &jucn
[cnt
].referral_list
,
1662 &jucn
[cnt
].referral_count
)) {
1664 jucn
[cnt
].service_name
= talloc_strdup(ctx
,
1666 jucn
[cnt
].volume_name
= talloc_strdup(ctx
,
1668 if (!jucn
[cnt
].service_name
||
1669 !jucn
[cnt
].volume_name
) {
1670 TALLOC_FREE(talloced
);
1673 jucn
[cnt
].comment
= "";
1676 TALLOC_FREE(link_target
);
1678 TALLOC_FREE(talloced
);
1684 SMB_VFS_CLOSEDIR(conn
,dirp
);
1687 vfs_ChDir(conn
, cwd
);
1692 struct junction_map
*enum_msdfs_links(TALLOC_CTX
*ctx
, size_t *p_num_jn
)
1694 struct junction_map
*jn
= NULL
;
1696 size_t jn_count
= 0;
1700 if(!lp_host_msdfs()) {
1704 /* Ensure all the usershares are loaded. */
1706 load_registry_shares();
1707 sharecount
= load_usershare_shares();
1710 for(i
=0;i
< sharecount
;i
++) {
1711 if(lp_msdfs_root(i
)) {
1712 jn_count
+= count_dfs_links(ctx
, i
);
1715 if (jn_count
== 0) {
1718 jn
= TALLOC_ARRAY(ctx
, struct junction_map
, jn_count
);
1722 for(i
=0; i
< sharecount
; i
++) {
1723 if (*p_num_jn
>= jn_count
) {
1726 if(lp_msdfs_root(i
)) {
1727 *p_num_jn
+= form_junctions(ctx
, i
,
1729 jn_count
- *p_num_jn
);
1735 /******************************************************************************
1736 Core function to resolve a dfs pathname possibly containing a wildcard. If
1737 ppath_contains_wcard != NULL, it will be set to true if a wildcard is
1738 detected during dfs resolution.
1739 ******************************************************************************/
1741 NTSTATUS
resolve_dfspath_wcard(TALLOC_CTX
*ctx
,
1742 connection_struct
*conn
,
1744 const char *name_in
,
1747 bool *ppath_contains_wcard
)
1749 bool path_contains_wcard
;
1750 NTSTATUS status
= NT_STATUS_OK
;
1752 if (dfs_pathnames
) {
1753 status
= dfs_redirect(ctx
,
1758 &path_contains_wcard
);
1760 if (NT_STATUS_IS_OK(status
) && ppath_contains_wcard
!= NULL
) {
1761 *ppath_contains_wcard
= path_contains_wcard
;
1765 * Cheat and just return a copy of the in ptr.
1766 * Once srvstr_get_path() uses talloc it'll
1767 * be a talloced ptr anyway.
1769 *pp_name_out
= CONST_DISCARD(char *,name_in
);