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
26 extern uint32 global_client_caps
;
28 /**********************************************************************
29 Parse a DFS pathname of the form \hostname\service\reqpath
30 into the dfs_path structure.
31 If POSIX pathnames is true, the pathname may also be of the
32 form /hostname/service/reqpath.
33 We cope with either here.
35 Unfortunately, due to broken clients who might set the
36 SVAL(inbuf,smb_flg2) & FLAGS2_DFS_PATHNAMES bit and then
37 send a local path, we have to cope with that too....
39 If conn != NULL then ensure the provided service is
40 the one pointed to by the connection.
42 This version does everything using pointers within one copy of the
43 pathname string, talloced on the struct dfs_path pointer (which
44 must be talloced). This may be too clever to live....
46 **********************************************************************/
48 static NTSTATUS
parse_dfs_path(connection_struct
*conn
,
51 struct dfs_path
*pdp
, /* MUST BE TALLOCED */
52 bool *ppath_contains_wcard
)
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 (*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 if (server_info
!= NULL
) {
259 conn
->server_info
= copy_serverinfo(conn
, server_info
);
260 if (conn
->server_info
== NULL
) {
261 DEBUG(0, ("copy_serverinfo failed\n"));
263 return NT_STATUS_NO_MEMORY
;
267 set_conn_connectpath(conn
, connpath
);
269 if (!smbd_vfs_init(conn
)) {
270 NTSTATUS status
= map_nt_error_from_unix(errno
);
271 DEBUG(0,("create_conn_struct: smbd_vfs_init failed.\n"));
272 conn_free_internal(conn
);
277 * Windows seems to insist on doing trans2getdfsreferral() calls on
278 * the IPC$ share as the anonymous user. If we try to chdir as that
279 * user we will fail.... WTF ? JRA.
282 oldcwd
= vfs_GetWd(ctx
, conn
);
283 if (oldcwd
== NULL
) {
284 NTSTATUS status
= map_nt_error_from_unix(errno
);
285 DEBUG(3, ("vfs_GetWd failed: %s\n", strerror(errno
)));
286 conn_free_internal(conn
);
290 if (vfs_ChDir(conn
,conn
->connectpath
) != 0) {
291 NTSTATUS status
= map_nt_error_from_unix(errno
);
292 DEBUG(3,("create_conn_struct: Can't ChDir to new conn path %s. "
294 conn
->connectpath
, strerror(errno
) ));
295 conn_free_internal(conn
);
305 /**********************************************************************
306 Parse the contents of a symlink to verify if it is an msdfs referral
307 A valid referral is of the form:
309 msdfs:server1\share1,server2\share2
310 msdfs:server1\share1\pathname,server2\share2\pathname
311 msdfs:server1/share1,server2/share2
312 msdfs:server1/share1/pathname,server2/share2/pathname.
314 Note that the alternate paths returned here must be of the canonicalized
318 \server\share\path\to\file,
320 even in posix path mode. This is because we have no knowledge if the
321 server we're referring to understands posix paths.
322 **********************************************************************/
324 static bool parse_msdfs_symlink(TALLOC_CTX
*ctx
,
326 struct referral
**preflist
,
331 char **alt_path
= NULL
;
333 struct referral
*reflist
;
336 temp
= talloc_strdup(ctx
, target
);
340 prot
= strtok_r(temp
, ":", &saveptr
);
342 DEBUG(0,("parse_msdfs_symlink: invalid path !\n"));
346 alt_path
= TALLOC_ARRAY(ctx
, char *, MAX_REFERRAL_COUNT
);
351 /* parse out the alternate paths */
352 while((count
<MAX_REFERRAL_COUNT
) &&
353 ((alt_path
[count
] = strtok_r(NULL
, ",", &saveptr
)) != NULL
)) {
357 DEBUG(10,("parse_msdfs_symlink: count=%d\n", count
));
360 reflist
= *preflist
= TALLOC_ZERO_ARRAY(ctx
,
361 struct referral
, count
);
362 if(reflist
== NULL
) {
363 TALLOC_FREE(alt_path
);
367 reflist
= *preflist
= NULL
;
370 for(i
=0;i
<count
;i
++) {
373 /* Canonicalize link target.
374 * Replace all /'s in the path by a \ */
375 string_replace(alt_path
[i
], '/', '\\');
377 /* Remove leading '\\'s */
379 while (*p
&& (*p
== '\\')) {
383 reflist
[i
].alternate_path
= talloc_asprintf(ctx
,
386 if (!reflist
[i
].alternate_path
) {
390 reflist
[i
].proximity
= 0;
391 reflist
[i
].ttl
= REFERRAL_TTL
;
392 DEBUG(10, ("parse_msdfs_symlink: Created alt path: %s\n",
393 reflist
[i
].alternate_path
));
398 TALLOC_FREE(alt_path
);
402 /**********************************************************************
403 Returns true if the unix path is a valid msdfs symlink and also
404 returns the target string from inside the link.
405 **********************************************************************/
407 static bool is_msdfs_link_internal(TALLOC_CTX
*ctx
,
408 connection_struct
*conn
,
410 char **pp_link_target
,
411 SMB_STRUCT_STAT
*sbufp
)
414 int referral_len
= 0;
415 char link_target_buf
[7];
417 char *link_target
= NULL
;
419 if (pp_link_target
) {
421 link_target
= TALLOC_ARRAY(ctx
, char, bufsize
);
425 *pp_link_target
= link_target
;
427 bufsize
= sizeof(link_target_buf
);
428 link_target
= link_target_buf
;
435 if (SMB_VFS_LSTAT(conn
, path
, sbufp
) != 0) {
436 DEBUG(5,("is_msdfs_link_read_target: %s does not exist.\n",
441 if (!S_ISLNK(sbufp
->st_mode
)) {
442 DEBUG(5,("is_msdfs_link_read_target: %s is not a link.\n",
447 referral_len
= SMB_VFS_READLINK(conn
, path
, link_target
, bufsize
- 1);
448 if (referral_len
== -1) {
449 DEBUG(0,("is_msdfs_link_read_target: Error reading "
450 "msdfs link %s: %s\n",
451 path
, strerror(errno
)));
454 link_target
[referral_len
] = '\0';
456 DEBUG(5,("is_msdfs_link_internal: %s -> %s\n",path
,
459 if (!strnequal(link_target
, "msdfs:", 6)) {
466 if (link_target
!= link_target_buf
) {
467 TALLOC_FREE(link_target
);
472 /**********************************************************************
473 Returns true if the unix path is a valid msdfs symlink.
474 **********************************************************************/
476 bool is_msdfs_link(connection_struct
*conn
,
478 SMB_STRUCT_STAT
*sbufp
)
480 return is_msdfs_link_internal(talloc_tos(),
487 /*****************************************************************
488 Used by other functions to decide if a dfs path is remote,
489 and to get the list of referred locations for that remote path.
491 search_flag: For findfirsts, dfs links themselves are not
492 redirected, but paths beyond the links are. For normal smb calls,
493 even dfs links need to be redirected.
495 consumedcntp: how much of the dfs path is being redirected. the client
496 should try the remaining path on the redirected server.
498 If this returns NT_STATUS_PATH_NOT_COVERED the contents of the msdfs
499 link redirect are in targetpath.
500 *****************************************************************/
502 static NTSTATUS
dfs_path_lookup(TALLOC_CTX
*ctx
,
503 connection_struct
*conn
,
504 const char *dfspath
, /* Incoming complete dfs path */
505 const struct dfs_path
*pdp
, /* Parsed out
506 server+share+extrapath. */
507 bool search_flag
, /* Called from a findfirst ? */
509 char **pp_targetpath
)
513 SMB_STRUCT_STAT sbuf
;
515 char *localpath
= NULL
;
516 char *canon_dfspath
= NULL
; /* Canonicalized dfs path. (only '/'
519 DEBUG(10,("dfs_path_lookup: Conn path = %s reqpath = %s\n",
520 conn
->connectpath
, pdp
->reqpath
));
523 * Note the unix path conversion here we're doing we can
524 * throw away. We're looking for a symlink for a dfs
525 * resolution, if we don't find it we'll do another
526 * unix_convert later in the codepath.
527 * If we needed to remember what we'd resolved in
528 * dp->reqpath (as the original code did) we'd
529 * copy (localhost, dp->reqpath) on any code
530 * path below that returns True - but I don't
531 * think this is needed. JRA.
534 status
= unix_convert(ctx
, conn
, pdp
->reqpath
, search_flag
, &localpath
,
536 if (!NT_STATUS_IS_OK(status
) && !NT_STATUS_EQUAL(status
,
537 NT_STATUS_OBJECT_PATH_NOT_FOUND
)) {
541 /* Optimization - check if we can redirect the whole path. */
543 if (is_msdfs_link_internal(ctx
, conn
, localpath
, pp_targetpath
, NULL
)) {
545 DEBUG(6,("dfs_path_lookup (FindFirst) No redirection "
546 "for dfs link %s.\n", dfspath
));
550 DEBUG(6,("dfs_path_lookup: %s resolves to a "
551 "valid dfs link %s.\n", dfspath
,
552 pp_targetpath
? *pp_targetpath
: ""));
555 *consumedcntp
= strlen(dfspath
);
557 return NT_STATUS_PATH_NOT_COVERED
;
560 /* Prepare to test only for '/' components in the given path,
561 * so if a Windows path replace all '\\' characters with '/'.
562 * For a POSIX DFS path we know all separators are already '/'. */
564 canon_dfspath
= talloc_strdup(ctx
, dfspath
);
565 if (!canon_dfspath
) {
566 return NT_STATUS_NO_MEMORY
;
568 if (!pdp
->posix_path
) {
569 string_replace(canon_dfspath
, '\\', '/');
573 * localpath comes out of unix_convert, so it has
574 * no trailing backslash. Make sure that canon_dfspath hasn't either.
575 * Fix for bug #4860 from Jan Martin <Jan.Martin@rwedea.com>.
578 trim_char(canon_dfspath
,0,'/');
581 * Redirect if any component in the path is a link.
582 * We do this by walking backwards through the
583 * local path, chopping off the last component
584 * in both the local path and the canonicalized
585 * DFS path. If we hit a DFS link then we're done.
588 p
= strrchr_m(localpath
, '/');
590 q
= strrchr_m(canon_dfspath
, '/');
599 if (is_msdfs_link_internal(ctx
, conn
,
600 localpath
, pp_targetpath
, NULL
)) {
601 DEBUG(4, ("dfs_path_lookup: Redirecting %s because "
602 "parent %s is dfs link\n", dfspath
, localpath
));
605 *consumedcntp
= strlen(canon_dfspath
);
606 DEBUG(10, ("dfs_path_lookup: Path consumed: %s "
612 return NT_STATUS_PATH_NOT_COVERED
;
615 /* Step back on the filesystem. */
616 p
= strrchr_m(localpath
, '/');
619 /* And in the canonicalized dfs path. */
620 q
= strrchr_m(canon_dfspath
, '/');
627 /*****************************************************************
628 Decides if a dfs pathname should be redirected or not.
629 If not, the pathname is converted to a tcon-relative local unix path
631 search_wcard_flag: this flag performs 2 functions both related
632 to searches. See resolve_dfs_path() and parse_dfs_path_XX()
635 This function can return NT_STATUS_OK, meaning use the returned path as-is
636 (mapped into a local path).
637 or NT_STATUS_NOT_COVERED meaning return a DFS redirect, or
638 any other NT_STATUS error which is a genuine error to be
639 returned to the client.
640 *****************************************************************/
642 static NTSTATUS
dfs_redirect(TALLOC_CTX
*ctx
,
643 connection_struct
*conn
,
645 bool search_wcard_flag
,
647 bool *ppath_contains_wcard
)
650 struct dfs_path
*pdp
= TALLOC_P(ctx
, struct dfs_path
);
653 return NT_STATUS_NO_MEMORY
;
656 status
= parse_dfs_path(conn
, path_in
, search_wcard_flag
, pdp
,
657 ppath_contains_wcard
);
658 if (!NT_STATUS_IS_OK(status
)) {
663 if (pdp
->reqpath
[0] == '\0') {
665 *pp_path_out
= talloc_strdup(ctx
, "");
667 return NT_STATUS_NO_MEMORY
;
669 DEBUG(5,("dfs_redirect: self-referral.\n"));
673 /* If dfs pathname for a non-dfs share, convert to tcon-relative
674 path and return OK */
676 if (!lp_msdfs_root(SNUM(conn
))) {
677 *pp_path_out
= talloc_strdup(ctx
, pdp
->reqpath
);
680 return NT_STATUS_NO_MEMORY
;
685 /* If it looked like a local path (zero hostname/servicename)
686 * just treat as a tcon-relative path. */
688 if (pdp
->hostname
[0] == '\0' && pdp
->servicename
[0] == '\0') {
689 *pp_path_out
= talloc_strdup(ctx
, pdp
->reqpath
);
692 return NT_STATUS_NO_MEMORY
;
697 if (!( strequal(pdp
->servicename
, lp_servicename(SNUM(conn
)))
698 || (strequal(pdp
->servicename
, HOMES_NAME
)
699 && strequal(lp_servicename(SNUM(conn
)),
700 conn
->server_info
->sanitized_username
) )) ) {
702 /* The given sharename doesn't match this connection. */
705 return NT_STATUS_OBJECT_PATH_NOT_FOUND
;
708 status
= dfs_path_lookup(ctx
, conn
, path_in
, pdp
,
709 search_wcard_flag
, NULL
, NULL
);
710 if (!NT_STATUS_IS_OK(status
)) {
711 if (NT_STATUS_EQUAL(status
, NT_STATUS_PATH_NOT_COVERED
)) {
712 DEBUG(3,("dfs_redirect: Redirecting %s\n", path_in
));
714 DEBUG(10,("dfs_redirect: dfs_path_lookup "
715 "failed for %s with %s\n",
716 path_in
, nt_errstr(status
) ));
721 DEBUG(3,("dfs_redirect: Not redirecting %s.\n", path_in
));
723 /* Form non-dfs tcon-relative path */
724 *pp_path_out
= talloc_strdup(ctx
, pdp
->reqpath
);
727 return NT_STATUS_NO_MEMORY
;
730 DEBUG(3,("dfs_redirect: Path %s converted to non-dfs path %s\n",
737 /**********************************************************************
738 Return a self referral.
739 **********************************************************************/
741 static NTSTATUS
self_ref(TALLOC_CTX
*ctx
,
742 const char *dfs_path
,
743 struct junction_map
*jucn
,
745 bool *self_referralp
)
747 struct referral
*ref
;
749 *self_referralp
= True
;
751 jucn
->referral_count
= 1;
752 if((ref
= TALLOC_ZERO_P(ctx
, struct referral
)) == NULL
) {
753 return NT_STATUS_NO_MEMORY
;
756 ref
->alternate_path
= talloc_strdup(ctx
, dfs_path
);
757 if (!ref
->alternate_path
) {
758 return NT_STATUS_NO_MEMORY
;
761 ref
->ttl
= REFERRAL_TTL
;
762 jucn
->referral_list
= ref
;
763 *consumedcntp
= strlen(dfs_path
);
767 /**********************************************************************
768 Gets valid referrals for a dfs path and fills up the
769 junction_map structure.
770 **********************************************************************/
772 NTSTATUS
get_referred_path(TALLOC_CTX
*ctx
,
773 const char *dfs_path
,
774 struct junction_map
*jucn
,
776 bool *self_referralp
)
778 struct connection_struct
*conn
;
779 char *targetpath
= NULL
;
781 NTSTATUS status
= NT_STATUS_NOT_FOUND
;
783 struct dfs_path
*pdp
= TALLOC_P(ctx
, struct dfs_path
);
787 return NT_STATUS_NO_MEMORY
;
790 *self_referralp
= False
;
792 status
= parse_dfs_path(NULL
, dfs_path
, False
, pdp
, &dummy
);
793 if (!NT_STATUS_IS_OK(status
)) {
797 jucn
->service_name
= talloc_strdup(ctx
, pdp
->servicename
);
798 jucn
->volume_name
= talloc_strdup(ctx
, pdp
->reqpath
);
799 if (!jucn
->service_name
|| !jucn
->volume_name
) {
801 return NT_STATUS_NO_MEMORY
;
804 /* Verify the share is a dfs root */
805 snum
= lp_servicenumber(jucn
->service_name
);
807 fstring service_name
;
808 fstrcpy(service_name
, jucn
->service_name
);
809 if ((snum
= find_service(service_name
)) < 0) {
810 return NT_STATUS_NOT_FOUND
;
812 TALLOC_FREE(jucn
->service_name
);
813 jucn
->service_name
= talloc_strdup(ctx
, service_name
);
814 if (!jucn
->service_name
) {
816 return NT_STATUS_NO_MEMORY
;
820 if (!lp_msdfs_root(snum
) && (*lp_msdfs_proxy(snum
) == '\0')) {
821 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not "
823 pdp
->servicename
, dfs_path
));
825 return NT_STATUS_NOT_FOUND
;
829 * Self referrals are tested with a anonymous IPC connection and
830 * a GET_DFS_REFERRAL call to \\server\share. (which means
831 * dp.reqpath[0] points to an empty string). create_conn_struct cd's
832 * into the directory and will fail if it cannot (as the anonymous
833 * user). Cope with this.
836 if (pdp
->reqpath
[0] == '\0') {
838 struct referral
*ref
;
840 if (*lp_msdfs_proxy(snum
) == '\0') {
850 * It's an msdfs proxy share. Redirect to
851 * the configured target share.
854 jucn
->referral_count
= 1;
855 if ((ref
= TALLOC_ZERO_P(ctx
, struct referral
)) == NULL
) {
857 return NT_STATUS_NO_MEMORY
;
860 if (!(tmp
= talloc_strdup(ctx
, lp_msdfs_proxy(snum
)))) {
862 return NT_STATUS_NO_MEMORY
;
865 trim_string(tmp
, "\\", 0);
867 ref
->alternate_path
= talloc_asprintf(ctx
, "\\%s", tmp
);
870 if (!ref
->alternate_path
) {
872 return NT_STATUS_NO_MEMORY
;
875 if (pdp
->reqpath
[0] != '\0') {
876 ref
->alternate_path
= talloc_asprintf_append(
880 if (!ref
->alternate_path
) {
882 return NT_STATUS_NO_MEMORY
;
886 ref
->ttl
= REFERRAL_TTL
;
887 jucn
->referral_list
= ref
;
888 *consumedcntp
= strlen(dfs_path
);
893 status
= create_conn_struct(ctx
, &conn
, snum
, lp_pathname(snum
),
895 if (!NT_STATUS_IS_OK(status
)) {
900 /* If this is a DFS path dfs_lookup should return
901 * NT_STATUS_PATH_NOT_COVERED. */
903 status
= dfs_path_lookup(ctx
, conn
, dfs_path
, pdp
,
904 False
, consumedcntp
, &targetpath
);
906 if (!NT_STATUS_EQUAL(status
, NT_STATUS_PATH_NOT_COVERED
)) {
907 DEBUG(3,("get_referred_path: No valid referrals for path %s\n",
909 vfs_ChDir(conn
, oldpath
);
910 conn_free_internal(conn
);
915 /* We know this is a valid dfs link. Parse the targetpath. */
916 if (!parse_msdfs_symlink(ctx
, targetpath
,
917 &jucn
->referral_list
,
918 &jucn
->referral_count
)) {
919 DEBUG(3,("get_referred_path: failed to parse symlink "
920 "target %s\n", targetpath
));
921 vfs_ChDir(conn
, oldpath
);
922 conn_free_internal(conn
);
924 return NT_STATUS_NOT_FOUND
;
927 vfs_ChDir(conn
, oldpath
);
928 conn_free_internal(conn
);
933 static int setup_ver2_dfs_referral(const char *pathname
,
935 struct junction_map
*junction
,
938 char* pdata
= *ppdata
;
940 smb_ucs2_t
*uni_requestedpath
= NULL
;
941 int uni_reqpathoffset1
,uni_reqpathoffset2
;
943 int requestedpathlen
=0;
948 DEBUG(10,("Setting up version2 referral\nRequested path:\n"));
950 requestedpathlen
= rpcstr_push_talloc(talloc_tos(),
951 &uni_requestedpath
, pathname
);
952 if (uni_requestedpath
== NULL
|| requestedpathlen
== 0) {
957 dump_data(0, (unsigned char *)uni_requestedpath
,
961 DEBUG(10,("ref count = %u\n",junction
->referral_count
));
963 uni_reqpathoffset1
= REFERRAL_HEADER_SIZE
+
964 VERSION2_REFERRAL_SIZE
* junction
->referral_count
;
966 uni_reqpathoffset2
= uni_reqpathoffset1
+ requestedpathlen
;
968 uni_curroffset
= uni_reqpathoffset2
+ requestedpathlen
;
970 reply_size
= REFERRAL_HEADER_SIZE
+
971 VERSION2_REFERRAL_SIZE
*junction
->referral_count
+
972 2 * requestedpathlen
;
973 DEBUG(10,("reply_size: %u\n",reply_size
));
975 /* add up the unicode lengths of all the referral paths */
976 for(i
=0;i
<junction
->referral_count
;i
++) {
977 DEBUG(10,("referral %u : %s\n",
979 junction
->referral_list
[i
].alternate_path
));
981 (strlen(junction
->referral_list
[i
].alternate_path
)+1)*2;
984 DEBUG(10,("reply_size = %u\n",reply_size
));
985 /* add the unexplained 0x16 bytes */
988 pdata
= (char *)SMB_REALLOC(pdata
,reply_size
);
990 DEBUG(0,("Realloc failed!\n"));
995 /* copy in the dfs requested paths.. required for offset calculations */
996 memcpy(pdata
+uni_reqpathoffset1
,uni_requestedpath
,requestedpathlen
);
997 memcpy(pdata
+uni_reqpathoffset2
,uni_requestedpath
,requestedpathlen
);
999 /* create the header */
1000 SSVAL(pdata
,0,requestedpathlen
- 2); /* UCS2 of path consumed minus
1002 /* number of referral in this pkt */
1003 SSVAL(pdata
,2,junction
->referral_count
);
1005 SIVAL(pdata
,4,DFSREF_REFERRAL_SERVER
| DFSREF_STORAGE_SERVER
);
1007 SIVAL(pdata
,4,DFSREF_STORAGE_SERVER
);
1011 /* add the referral elements */
1012 for(i
=0;i
<junction
->referral_count
;i
++) {
1013 struct referral
* ref
= &junction
->referral_list
[i
];
1016 SSVAL(pdata
,offset
,2); /* version 2 */
1017 SSVAL(pdata
,offset
+2,VERSION2_REFERRAL_SIZE
);
1019 SSVAL(pdata
,offset
+4,1);
1021 SSVAL(pdata
,offset
+4,0);
1024 /* ref_flags :use path_consumed bytes? */
1025 SSVAL(pdata
,offset
+6,0);
1026 SIVAL(pdata
,offset
+8,ref
->proximity
);
1027 SIVAL(pdata
,offset
+12,ref
->ttl
);
1029 SSVAL(pdata
,offset
+16,uni_reqpathoffset1
-offset
);
1030 SSVAL(pdata
,offset
+18,uni_reqpathoffset2
-offset
);
1031 /* copy referred path into current offset */
1032 unilen
= rpcstr_push(pdata
+uni_curroffset
,
1033 ref
->alternate_path
,
1034 reply_size
- uni_curroffset
,
1037 SSVAL(pdata
,offset
+20,uni_curroffset
-offset
);
1039 uni_curroffset
+= unilen
;
1040 offset
+= VERSION2_REFERRAL_SIZE
;
1042 /* add in the unexplained 22 (0x16) bytes at the end */
1043 memset(pdata
+uni_curroffset
,'\0',0x16);
1047 static int setup_ver3_dfs_referral(const char *pathname
,
1049 struct junction_map
*junction
,
1052 char *pdata
= *ppdata
;
1054 smb_ucs2_t
*uni_reqpath
= NULL
;
1055 int uni_reqpathoffset1
, uni_reqpathoffset2
;
1062 DEBUG(10,("setting up version3 referral\n"));
1064 reqpathlen
= rpcstr_push_talloc(talloc_tos(), &uni_reqpath
, pathname
);
1065 if (uni_reqpath
== NULL
|| reqpathlen
== 0) {
1070 dump_data(0, (unsigned char *)uni_reqpath
,
1074 uni_reqpathoffset1
= REFERRAL_HEADER_SIZE
+
1075 VERSION3_REFERRAL_SIZE
* junction
->referral_count
;
1076 uni_reqpathoffset2
= uni_reqpathoffset1
+ reqpathlen
;
1077 reply_size
= uni_curroffset
= uni_reqpathoffset2
+ reqpathlen
;
1079 for(i
=0;i
<junction
->referral_count
;i
++) {
1080 DEBUG(10,("referral %u : %s\n",
1082 junction
->referral_list
[i
].alternate_path
));
1084 (strlen(junction
->referral_list
[i
].alternate_path
)+1)*2;
1087 pdata
= (char *)SMB_REALLOC(pdata
,reply_size
);
1089 DEBUG(0,("version3 referral setup:"
1090 "malloc failed for Realloc!\n"));
1095 /* create the header */
1096 SSVAL(pdata
,0,reqpathlen
- 2); /* UCS2 of path consumed minus
1098 SSVAL(pdata
,2,junction
->referral_count
); /* number of referral */
1100 SIVAL(pdata
,4,DFSREF_REFERRAL_SERVER
| DFSREF_STORAGE_SERVER
);
1102 SIVAL(pdata
,4,DFSREF_STORAGE_SERVER
);
1105 /* copy in the reqpaths */
1106 memcpy(pdata
+uni_reqpathoffset1
,uni_reqpath
,reqpathlen
);
1107 memcpy(pdata
+uni_reqpathoffset2
,uni_reqpath
,reqpathlen
);
1110 for(i
=0;i
<junction
->referral_count
;i
++) {
1111 struct referral
* ref
= &(junction
->referral_list
[i
]);
1114 SSVAL(pdata
,offset
,3); /* version 3 */
1115 SSVAL(pdata
,offset
+2,VERSION3_REFERRAL_SIZE
);
1117 SSVAL(pdata
,offset
+4,1);
1119 SSVAL(pdata
,offset
+4,0);
1122 /* ref_flags :use path_consumed bytes? */
1123 SSVAL(pdata
,offset
+6,0);
1124 SIVAL(pdata
,offset
+8,ref
->ttl
);
1126 SSVAL(pdata
,offset
+12,uni_reqpathoffset1
-offset
);
1127 SSVAL(pdata
,offset
+14,uni_reqpathoffset2
-offset
);
1128 /* copy referred path into current offset */
1129 unilen
= rpcstr_push(pdata
+uni_curroffset
,ref
->alternate_path
,
1130 reply_size
- uni_curroffset
,
1131 STR_UNICODE
| STR_TERMINATE
);
1132 SSVAL(pdata
,offset
+16,uni_curroffset
-offset
);
1133 /* copy 0x10 bytes of 00's in the ServiceSite GUID */
1134 memset(pdata
+offset
+18,'\0',16);
1136 uni_curroffset
+= unilen
;
1137 offset
+= VERSION3_REFERRAL_SIZE
;
1142 /******************************************************************
1143 Set up the DFS referral for the dfs pathname. This call returns
1144 the amount of the path covered by this server, and where the
1145 client should be redirected to. This is the meat of the
1146 TRANS2_GET_DFS_REFERRAL call.
1147 ******************************************************************/
1149 int setup_dfs_referral(connection_struct
*orig_conn
,
1150 const char *dfs_path
,
1151 int max_referral_level
,
1152 char **ppdata
, NTSTATUS
*pstatus
)
1154 struct junction_map
*junction
= NULL
;
1155 int consumedcnt
= 0;
1156 bool self_referral
= False
;
1158 char *pathnamep
= NULL
;
1159 char *local_dfs_path
= NULL
;
1162 if (!(ctx
=talloc_init("setup_dfs_referral"))) {
1163 *pstatus
= NT_STATUS_NO_MEMORY
;
1167 /* get the junction entry */
1169 talloc_destroy(ctx
);
1170 *pstatus
= NT_STATUS_NOT_FOUND
;
1175 * Trim pathname sent by client so it begins with only one backslash.
1176 * Two backslashes confuse some dfs clients
1179 local_dfs_path
= talloc_strdup(ctx
,dfs_path
);
1180 if (!local_dfs_path
) {
1181 *pstatus
= NT_STATUS_NO_MEMORY
;
1182 talloc_destroy(ctx
);
1185 pathnamep
= local_dfs_path
;
1186 while (IS_DIRECTORY_SEP(pathnamep
[0]) &&
1187 IS_DIRECTORY_SEP(pathnamep
[1])) {
1191 junction
= TALLOC_ZERO_P(ctx
, struct junction_map
);
1193 *pstatus
= NT_STATUS_NO_MEMORY
;
1194 talloc_destroy(ctx
);
1198 /* The following call can change cwd. */
1199 *pstatus
= get_referred_path(ctx
, pathnamep
, junction
,
1200 &consumedcnt
, &self_referral
);
1201 if (!NT_STATUS_IS_OK(*pstatus
)) {
1202 vfs_ChDir(orig_conn
,orig_conn
->connectpath
);
1203 talloc_destroy(ctx
);
1206 vfs_ChDir(orig_conn
,orig_conn
->connectpath
);
1208 if (!self_referral
) {
1209 pathnamep
[consumedcnt
] = '\0';
1211 if( DEBUGLVL( 3 ) ) {
1213 dbgtext("setup_dfs_referral: Path %s to "
1214 "alternate path(s):",
1216 for(i
=0;i
<junction
->referral_count
;i
++)
1218 junction
->referral_list
[i
].alternate_path
);
1223 /* create the referral depeding on version */
1224 DEBUG(10,("max_referral_level :%d\n",max_referral_level
));
1226 if (max_referral_level
< 2) {
1227 max_referral_level
= 2;
1229 if (max_referral_level
> 3) {
1230 max_referral_level
= 3;
1233 switch(max_referral_level
) {
1235 reply_size
= setup_ver2_dfs_referral(pathnamep
,
1240 reply_size
= setup_ver3_dfs_referral(pathnamep
, ppdata
,
1241 junction
, self_referral
);
1244 DEBUG(0,("setup_dfs_referral: Invalid dfs referral "
1246 max_referral_level
));
1247 talloc_destroy(ctx
);
1248 *pstatus
= NT_STATUS_INVALID_LEVEL
;
1253 DEBUGADD(0,("DFS Referral pdata:\n"));
1254 dump_data(0,(uint8
*)*ppdata
,reply_size
);
1257 talloc_destroy(ctx
);
1258 *pstatus
= NT_STATUS_OK
;
1262 /**********************************************************************
1263 The following functions are called by the NETDFS RPC pipe functions
1264 **********************************************************************/
1266 /*********************************************************************
1267 Creates a junction structure from a DFS pathname
1268 **********************************************************************/
1270 bool create_junction(TALLOC_CTX
*ctx
,
1271 const char *dfs_path
,
1272 struct junction_map
*jucn
)
1276 struct dfs_path
*pdp
= TALLOC_P(ctx
,struct dfs_path
);
1282 status
= parse_dfs_path(NULL
, dfs_path
, False
, pdp
, &dummy
);
1283 if (!NT_STATUS_IS_OK(status
)) {
1287 /* check if path is dfs : validate first token */
1288 if (!is_myname_or_ipaddr(pdp
->hostname
)) {
1289 DEBUG(4,("create_junction: Invalid hostname %s "
1291 pdp
->hostname
, dfs_path
));
1296 /* Check for a non-DFS share */
1297 snum
= lp_servicenumber(pdp
->servicename
);
1299 if(snum
< 0 || !lp_msdfs_root(snum
)) {
1300 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1306 jucn
->service_name
= talloc_strdup(ctx
, pdp
->servicename
);
1307 jucn
->volume_name
= talloc_strdup(ctx
, pdp
->reqpath
);
1308 jucn
->comment
= talloc_strdup(ctx
, lp_comment(snum
));
1311 if (!jucn
->service_name
|| !jucn
->volume_name
|| ! jucn
->comment
) {
1317 /**********************************************************************
1318 Forms a valid Unix pathname from the junction
1319 **********************************************************************/
1321 static bool junction_to_local_path(const struct junction_map
*jucn
,
1323 connection_struct
**conn_out
,
1329 snum
= lp_servicenumber(jucn
->service_name
);
1333 status
= create_conn_struct(talloc_tos(), conn_out
, snum
,
1334 lp_pathname(snum
), NULL
, oldpath
);
1335 if (!NT_STATUS_IS_OK(status
)) {
1339 *pp_path_out
= talloc_asprintf(*conn_out
,
1343 if (!*pp_path_out
) {
1344 vfs_ChDir(*conn_out
, *oldpath
);
1345 conn_free_internal(*conn_out
);
1351 bool create_msdfs_link(const struct junction_map
*jucn
)
1355 char *msdfs_link
= NULL
;
1356 connection_struct
*conn
;
1358 bool insert_comma
= False
;
1361 if(!junction_to_local_path(jucn
, &path
, &conn
, &cwd
)) {
1365 /* Form the msdfs_link contents */
1366 msdfs_link
= talloc_strdup(conn
, "msdfs:");
1370 for(i
=0; i
<jucn
->referral_count
; i
++) {
1371 char *refpath
= jucn
->referral_list
[i
].alternate_path
;
1373 /* Alternate paths always use Windows separators. */
1374 trim_char(refpath
, '\\', '\\');
1375 if(*refpath
== '\0') {
1377 insert_comma
= False
;
1381 if (i
> 0 && insert_comma
) {
1382 msdfs_link
= talloc_asprintf_append_buffer(msdfs_link
,
1386 msdfs_link
= talloc_asprintf_append_buffer(msdfs_link
,
1394 if (!insert_comma
) {
1395 insert_comma
= True
;
1399 DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
1402 if(SMB_VFS_SYMLINK(conn
, msdfs_link
, path
) < 0) {
1403 if (errno
== EEXIST
) {
1404 if(SMB_VFS_UNLINK(conn
,path
)!=0) {
1408 if (SMB_VFS_SYMLINK(conn
, msdfs_link
, path
) < 0) {
1409 DEBUG(1,("create_msdfs_link: symlink failed "
1410 "%s -> %s\nError: %s\n",
1411 path
, msdfs_link
, strerror(errno
)));
1419 vfs_ChDir(conn
, cwd
);
1420 conn_free_internal(conn
);
1424 bool remove_msdfs_link(const struct junction_map
*jucn
)
1428 connection_struct
*conn
;
1431 if (!junction_to_local_path(jucn
, &path
, &conn
, &cwd
)) {
1435 if( SMB_VFS_UNLINK(conn
, path
) == 0 ) {
1439 vfs_ChDir(conn
, cwd
);
1440 conn_free_internal(conn
);
1444 /*********************************************************************
1445 Return the number of DFS links at the root of this share.
1446 *********************************************************************/
1448 static int count_dfs_links(TALLOC_CTX
*ctx
, int snum
)
1451 SMB_STRUCT_DIR
*dirp
= NULL
;
1453 const char *connect_path
= lp_pathname(snum
);
1454 const char *msdfs_proxy
= lp_msdfs_proxy(snum
);
1455 connection_struct
*conn
;
1459 if(*connect_path
== '\0') {
1464 * Fake up a connection struct for the VFS layer.
1467 status
= create_conn_struct(talloc_tos(), &conn
, snum
, connect_path
,
1469 if (!NT_STATUS_IS_OK(status
)) {
1470 DEBUG(3, ("create_conn_struct failed: %s\n",
1471 nt_errstr(status
)));
1475 /* Count a link for the msdfs root - convention */
1478 /* No more links if this is an msdfs proxy. */
1479 if (*msdfs_proxy
!= '\0') {
1483 /* Now enumerate all dfs links */
1484 dirp
= SMB_VFS_OPENDIR(conn
, ".", NULL
, 0);
1489 while ((dname
= vfs_readdirname(conn
, dirp
)) != NULL
) {
1490 if (is_msdfs_link(conn
,
1497 SMB_VFS_CLOSEDIR(conn
,dirp
);
1500 vfs_ChDir(conn
, cwd
);
1501 conn_free_internal(conn
);
1505 /*********************************************************************
1506 *********************************************************************/
1508 static int form_junctions(TALLOC_CTX
*ctx
,
1510 struct junction_map
*jucn
,
1514 SMB_STRUCT_DIR
*dirp
= NULL
;
1516 const char *connect_path
= lp_pathname(snum
);
1517 char *service_name
= lp_servicename(snum
);
1518 const char *msdfs_proxy
= lp_msdfs_proxy(snum
);
1519 connection_struct
*conn
;
1520 struct referral
*ref
= NULL
;
1524 if (jn_remain
== 0) {
1528 if(*connect_path
== '\0') {
1533 * Fake up a connection struct for the VFS layer.
1536 status
= create_conn_struct(ctx
, &conn
, snum
, connect_path
, NULL
,
1538 if (!NT_STATUS_IS_OK(status
)) {
1539 DEBUG(3, ("create_conn_struct failed: %s\n",
1540 nt_errstr(status
)));
1544 /* form a junction for the msdfs root - convention
1545 DO NOT REMOVE THIS: NT clients will not work with us
1546 if this is not present
1548 jucn
[cnt
].service_name
= talloc_strdup(ctx
,service_name
);
1549 jucn
[cnt
].volume_name
= talloc_strdup(ctx
, "");
1550 if (!jucn
[cnt
].service_name
|| !jucn
[cnt
].volume_name
) {
1553 jucn
[cnt
].comment
= "";
1554 jucn
[cnt
].referral_count
= 1;
1556 ref
= jucn
[cnt
].referral_list
= TALLOC_ZERO_P(ctx
, struct referral
);
1557 if (jucn
[cnt
].referral_list
== NULL
) {
1562 ref
->ttl
= REFERRAL_TTL
;
1563 if (*msdfs_proxy
!= '\0') {
1564 ref
->alternate_path
= talloc_strdup(ctx
,
1567 ref
->alternate_path
= talloc_asprintf(ctx
,
1569 get_local_machine_name(),
1573 if (!ref
->alternate_path
) {
1578 /* Don't enumerate if we're an msdfs proxy. */
1579 if (*msdfs_proxy
!= '\0') {
1583 /* Now enumerate all dfs links */
1584 dirp
= SMB_VFS_OPENDIR(conn
, ".", NULL
, 0);
1589 while ((dname
= vfs_readdirname(conn
, dirp
)) != NULL
) {
1590 char *link_target
= NULL
;
1591 if (cnt
>= jn_remain
) {
1592 DEBUG(2, ("form_junctions: ran out of MSDFS "
1596 if (is_msdfs_link_internal(ctx
,
1598 dname
, &link_target
,
1600 if (parse_msdfs_symlink(ctx
,
1602 &jucn
[cnt
].referral_list
,
1603 &jucn
[cnt
].referral_count
)) {
1605 jucn
[cnt
].service_name
= talloc_strdup(ctx
,
1607 jucn
[cnt
].volume_name
= talloc_strdup(ctx
,
1609 if (!jucn
[cnt
].service_name
||
1610 !jucn
[cnt
].volume_name
) {
1613 jucn
[cnt
].comment
= "";
1616 TALLOC_FREE(link_target
);
1623 SMB_VFS_CLOSEDIR(conn
,dirp
);
1626 vfs_ChDir(conn
, cwd
);
1627 conn_free_internal(conn
);
1631 struct junction_map
*enum_msdfs_links(TALLOC_CTX
*ctx
, size_t *p_num_jn
)
1633 struct junction_map
*jn
= NULL
;
1635 size_t jn_count
= 0;
1639 if(!lp_host_msdfs()) {
1643 /* Ensure all the usershares are loaded. */
1645 load_registry_shares();
1646 sharecount
= load_usershare_shares();
1649 for(i
=0;i
< sharecount
;i
++) {
1650 if(lp_msdfs_root(i
)) {
1651 jn_count
+= count_dfs_links(ctx
, i
);
1654 if (jn_count
== 0) {
1657 jn
= TALLOC_ARRAY(ctx
, struct junction_map
, jn_count
);
1661 for(i
=0; i
< sharecount
; i
++) {
1662 if (*p_num_jn
>= jn_count
) {
1665 if(lp_msdfs_root(i
)) {
1666 *p_num_jn
+= form_junctions(ctx
, i
,
1668 jn_count
- *p_num_jn
);
1674 /******************************************************************************
1675 Core function to resolve a dfs pathname.
1676 ******************************************************************************/
1678 NTSTATUS
resolve_dfspath(TALLOC_CTX
*ctx
,
1679 connection_struct
*conn
,
1681 const char *name_in
,
1684 NTSTATUS status
= NT_STATUS_OK
;
1686 if (dfs_pathnames
) {
1687 status
= dfs_redirect(ctx
,
1695 * Cheat and just return a copy of the in ptr.
1696 * Once srvstr_get_path() uses talloc it'll
1697 * be a talloced ptr anyway.
1699 *pp_name_out
= CONST_DISCARD(char *,name_in
);
1704 /******************************************************************************
1705 Core function to resolve a dfs pathname possibly containing a wildcard.
1706 This function is identical to the above except for the bool param to
1707 dfs_redirect but I need this to be separate so it's really clear when
1708 we're allowing wildcards and when we're not. JRA.
1709 ******************************************************************************/
1711 NTSTATUS
resolve_dfspath_wcard(TALLOC_CTX
*ctx
,
1712 connection_struct
*conn
,
1714 const char *name_in
,
1716 bool *ppath_contains_wcard
)
1718 NTSTATUS status
= NT_STATUS_OK
;
1719 if (dfs_pathnames
) {
1720 status
= dfs_redirect(ctx
,
1725 ppath_contains_wcard
);
1728 * Cheat and just return a copy of the in ptr.
1729 * Once srvstr_get_path() uses talloc it'll
1730 * be a talloced ptr anyway.
1732 *pp_name_out
= CONST_DISCARD(char *,name_in
);