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 static NTSTATUS
create_conn_struct(TALLOC_CTX
*ctx
,
219 connection_struct
*conn
,
227 connpath
= talloc_strdup(ctx
, path
);
229 return NT_STATUS_NO_MEMORY
;
231 connpath
= talloc_string_sub(ctx
,
234 lp_servicename(snum
));
236 return NT_STATUS_NO_MEMORY
;
239 /* needed for smbd_vfs_init() */
241 if ((conn
->mem_ctx
=talloc_init("connection_struct")) == NULL
) {
242 DEBUG(0,("talloc_init(connection_struct) failed!\n"));
243 return NT_STATUS_NO_MEMORY
;
246 if (!(conn
->params
= TALLOC_ZERO_P(conn
->mem_ctx
,
247 struct share_params
))) {
248 DEBUG(0, ("TALLOC failed\n"));
249 return NT_STATUS_NO_MEMORY
;
252 conn
->params
->service
= snum
;
254 set_conn_connectpath(conn
, connpath
);
256 if (!smbd_vfs_init(conn
)) {
257 NTSTATUS status
= map_nt_error_from_unix(errno
);
258 DEBUG(0,("create_conn_struct: smbd_vfs_init failed.\n"));
259 conn_free_internal(conn
);
264 * Windows seems to insist on doing trans2getdfsreferral() calls on
265 * the IPC$ share as the anonymous user. If we try to chdir as that
266 * user we will fail.... WTF ? JRA.
269 if (vfs_ChDir(conn
,conn
->connectpath
) != 0) {
270 NTSTATUS status
= map_nt_error_from_unix(errno
);
271 DEBUG(3,("create_conn_struct: Can't ChDir to new conn path %s. "
273 conn
->connectpath
, strerror(errno
) ));
274 conn_free_internal(conn
);
281 /**********************************************************************
282 Parse the contents of a symlink to verify if it is an msdfs referral
283 A valid referral is of the form:
285 msdfs:server1\share1,server2\share2
286 msdfs:server1\share1\pathname,server2\share2\pathname
287 msdfs:server1/share1,server2/share2
288 msdfs:server1/share1/pathname,server2/share2/pathname.
290 Note that the alternate paths returned here must be of the canonicalized
294 \server\share\path\to\file,
296 even in posix path mode. This is because we have no knowledge if the
297 server we're referring to understands posix paths.
298 **********************************************************************/
300 static bool parse_msdfs_symlink(TALLOC_CTX
*ctx
,
302 struct referral
**preflist
,
307 char **alt_path
= NULL
;
309 struct referral
*reflist
;
312 temp
= talloc_strdup(ctx
, target
);
316 prot
= strtok_r(temp
, ":", &saveptr
);
318 DEBUG(0,("parse_msdfs_symlink: invalid path !\n"));
322 alt_path
= TALLOC_ARRAY(ctx
, char *, MAX_REFERRAL_COUNT
);
327 /* parse out the alternate paths */
328 while((count
<MAX_REFERRAL_COUNT
) &&
329 ((alt_path
[count
] = strtok_r(NULL
, ",", &saveptr
)) != NULL
)) {
333 DEBUG(10,("parse_msdfs_symlink: count=%d\n", count
));
336 reflist
= *preflist
= TALLOC_ZERO_ARRAY(ctx
,
337 struct referral
, count
);
338 if(reflist
== NULL
) {
339 TALLOC_FREE(alt_path
);
343 reflist
= *preflist
= NULL
;
346 for(i
=0;i
<count
;i
++) {
349 /* Canonicalize link target.
350 * Replace all /'s in the path by a \ */
351 string_replace(alt_path
[i
], '/', '\\');
353 /* Remove leading '\\'s */
355 while (*p
&& (*p
== '\\')) {
359 reflist
[i
].alternate_path
= talloc_asprintf(ctx
,
362 if (!reflist
[i
].alternate_path
) {
366 reflist
[i
].proximity
= 0;
367 reflist
[i
].ttl
= REFERRAL_TTL
;
368 DEBUG(10, ("parse_msdfs_symlink: Created alt path: %s\n",
369 reflist
[i
].alternate_path
));
373 TALLOC_FREE(alt_path
);
377 /**********************************************************************
378 Returns true if the unix path is a valid msdfs symlink and also
379 returns the target string from inside the link.
380 **********************************************************************/
382 static bool is_msdfs_link_internal(TALLOC_CTX
*ctx
,
383 connection_struct
*conn
,
385 char **pp_link_target
,
386 SMB_STRUCT_STAT
*sbufp
)
389 int referral_len
= 0;
390 char link_target_buf
[7];
392 char *link_target
= NULL
;
394 if (pp_link_target
) {
396 link_target
= TALLOC_ARRAY(ctx
, char, bufsize
);
400 *pp_link_target
= link_target
;
402 bufsize
= sizeof(link_target_buf
);
403 link_target
= link_target_buf
;
410 if (SMB_VFS_LSTAT(conn
, path
, sbufp
) != 0) {
411 DEBUG(5,("is_msdfs_link_read_target: %s does not exist.\n",
416 if (!S_ISLNK(sbufp
->st_mode
)) {
417 DEBUG(5,("is_msdfs_link_read_target: %s is not a link.\n",
422 referral_len
= SMB_VFS_READLINK(conn
, path
, link_target
, bufsize
- 1);
423 if (referral_len
== -1) {
424 DEBUG(0,("is_msdfs_link_read_target: Error reading "
425 "msdfs link %s: %s\n",
426 path
, strerror(errno
)));
429 link_target
[referral_len
] = '\0';
431 DEBUG(5,("is_msdfs_link_internal: %s -> %s\n",path
,
434 if (!strnequal(link_target
, "msdfs:", 6)) {
441 if (link_target
!= link_target_buf
) {
442 TALLOC_FREE(link_target
);
447 /**********************************************************************
448 Returns true if the unix path is a valid msdfs symlink.
449 **********************************************************************/
451 bool is_msdfs_link(connection_struct
*conn
,
453 SMB_STRUCT_STAT
*sbufp
)
455 return is_msdfs_link_internal(talloc_tos(),
462 /*****************************************************************
463 Used by other functions to decide if a dfs path is remote,
464 and to get the list of referred locations for that remote path.
466 search_flag: For findfirsts, dfs links themselves are not
467 redirected, but paths beyond the links are. For normal smb calls,
468 even dfs links need to be redirected.
470 consumedcntp: how much of the dfs path is being redirected. the client
471 should try the remaining path on the redirected server.
473 If this returns NT_STATUS_PATH_NOT_COVERED the contents of the msdfs
474 link redirect are in targetpath.
475 *****************************************************************/
477 static NTSTATUS
dfs_path_lookup(TALLOC_CTX
*ctx
,
478 connection_struct
*conn
,
479 const char *dfspath
, /* Incoming complete dfs path */
480 const struct dfs_path
*pdp
, /* Parsed out
481 server+share+extrapath. */
482 bool search_flag
, /* Called from a findfirst ? */
484 char **pp_targetpath
)
488 SMB_STRUCT_STAT sbuf
;
490 char *localpath
= NULL
;
491 char *canon_dfspath
= NULL
; /* Canonicalized dfs path. (only '/'
494 DEBUG(10,("dfs_path_lookup: Conn path = %s reqpath = %s\n",
495 conn
->connectpath
, pdp
->reqpath
));
498 * Note the unix path conversion here we're doing we can
499 * throw away. We're looking for a symlink for a dfs
500 * resolution, if we don't find it we'll do another
501 * unix_convert later in the codepath.
502 * If we needed to remember what we'd resolved in
503 * dp->reqpath (as the original code did) we'd
504 * copy (localhost, dp->reqpath) on any code
505 * path below that returns True - but I don't
506 * think this is needed. JRA.
509 status
= unix_convert(ctx
, conn
, pdp
->reqpath
, search_flag
, &localpath
,
511 if (!NT_STATUS_IS_OK(status
) && !NT_STATUS_EQUAL(status
,
512 NT_STATUS_OBJECT_PATH_NOT_FOUND
)) {
516 /* Optimization - check if we can redirect the whole path. */
518 if (is_msdfs_link_internal(ctx
, conn
, localpath
, pp_targetpath
, NULL
)) {
520 DEBUG(6,("dfs_path_lookup (FindFirst) No redirection "
521 "for dfs link %s.\n", dfspath
));
525 DEBUG(6,("dfs_path_lookup: %s resolves to a "
526 "valid dfs link %s.\n", dfspath
,
527 pp_targetpath
? *pp_targetpath
: ""));
530 *consumedcntp
= strlen(dfspath
);
532 return NT_STATUS_PATH_NOT_COVERED
;
535 /* Prepare to test only for '/' components in the given path,
536 * so if a Windows path replace all '\\' characters with '/'.
537 * For a POSIX DFS path we know all separators are already '/'. */
539 canon_dfspath
= talloc_strdup(ctx
, dfspath
);
540 if (!canon_dfspath
) {
541 return NT_STATUS_NO_MEMORY
;
543 if (!pdp
->posix_path
) {
544 string_replace(canon_dfspath
, '\\', '/');
548 * localpath comes out of unix_convert, so it has
549 * no trailing backslash. Make sure that canon_dfspath hasn't either.
550 * Fix for bug #4860 from Jan Martin <Jan.Martin@rwedea.com>.
553 trim_char(canon_dfspath
,0,'/');
556 * Redirect if any component in the path is a link.
557 * We do this by walking backwards through the
558 * local path, chopping off the last component
559 * in both the local path and the canonicalized
560 * DFS path. If we hit a DFS link then we're done.
563 p
= strrchr_m(localpath
, '/');
565 q
= strrchr_m(canon_dfspath
, '/');
574 if (is_msdfs_link_internal(ctx
, conn
,
575 localpath
, pp_targetpath
, NULL
)) {
576 DEBUG(4, ("dfs_path_lookup: Redirecting %s because "
577 "parent %s is dfs link\n", dfspath
, localpath
));
580 *consumedcntp
= strlen(canon_dfspath
);
581 DEBUG(10, ("dfs_path_lookup: Path consumed: %s "
587 return NT_STATUS_PATH_NOT_COVERED
;
590 /* Step back on the filesystem. */
591 p
= strrchr_m(localpath
, '/');
594 /* And in the canonicalized dfs path. */
595 q
= strrchr_m(canon_dfspath
, '/');
602 /*****************************************************************
603 Decides if a dfs pathname should be redirected or not.
604 If not, the pathname is converted to a tcon-relative local unix path
606 search_wcard_flag: this flag performs 2 functions both related
607 to searches. See resolve_dfs_path() and parse_dfs_path_XX()
610 This function can return NT_STATUS_OK, meaning use the returned path as-is
611 (mapped into a local path).
612 or NT_STATUS_NOT_COVERED meaning return a DFS redirect, or
613 any other NT_STATUS error which is a genuine error to be
614 returned to the client.
615 *****************************************************************/
617 static NTSTATUS
dfs_redirect(TALLOC_CTX
*ctx
,
618 connection_struct
*conn
,
620 bool search_wcard_flag
,
622 bool *ppath_contains_wcard
)
625 struct dfs_path
*pdp
= TALLOC_P(ctx
, struct dfs_path
);
628 return NT_STATUS_NO_MEMORY
;
631 status
= parse_dfs_path(conn
, path_in
, search_wcard_flag
, pdp
,
632 ppath_contains_wcard
);
633 if (!NT_STATUS_IS_OK(status
)) {
638 if (pdp
->reqpath
[0] == '\0') {
640 *pp_path_out
= talloc_strdup(ctx
, "");
642 return NT_STATUS_NO_MEMORY
;
644 DEBUG(5,("dfs_redirect: self-referral.\n"));
648 /* If dfs pathname for a non-dfs share, convert to tcon-relative
649 path and return OK */
651 if (!lp_msdfs_root(SNUM(conn
))) {
652 *pp_path_out
= talloc_strdup(ctx
, pdp
->reqpath
);
655 return NT_STATUS_NO_MEMORY
;
660 /* If it looked like a local path (zero hostname/servicename)
661 * just treat as a tcon-relative path. */
663 if (pdp
->hostname
[0] == '\0' && pdp
->servicename
[0] == '\0') {
664 *pp_path_out
= talloc_strdup(ctx
, pdp
->reqpath
);
667 return NT_STATUS_NO_MEMORY
;
672 status
= dfs_path_lookup(ctx
, conn
, path_in
, pdp
,
673 search_wcard_flag
, NULL
, NULL
);
674 if (!NT_STATUS_IS_OK(status
)) {
675 if (NT_STATUS_EQUAL(status
, NT_STATUS_PATH_NOT_COVERED
)) {
676 DEBUG(3,("dfs_redirect: Redirecting %s\n", path_in
));
678 DEBUG(10,("dfs_redirect: dfs_path_lookup "
679 "failed for %s with %s\n",
680 path_in
, nt_errstr(status
) ));
685 DEBUG(3,("dfs_redirect: Not redirecting %s.\n", path_in
));
687 /* Form non-dfs tcon-relative path */
688 *pp_path_out
= talloc_strdup(ctx
, pdp
->reqpath
);
691 return NT_STATUS_NO_MEMORY
;
694 DEBUG(3,("dfs_redirect: Path %s converted to non-dfs path %s\n",
701 /**********************************************************************
702 Return a self referral.
703 **********************************************************************/
705 static NTSTATUS
self_ref(TALLOC_CTX
*ctx
,
706 const char *dfs_path
,
707 struct junction_map
*jucn
,
709 bool *self_referralp
)
711 struct referral
*ref
;
713 *self_referralp
= True
;
715 jucn
->referral_count
= 1;
716 if((ref
= TALLOC_ZERO_P(ctx
, struct referral
)) == NULL
) {
717 return NT_STATUS_NO_MEMORY
;
720 ref
->alternate_path
= talloc_strdup(ctx
, dfs_path
);
721 if (!ref
->alternate_path
) {
722 return NT_STATUS_NO_MEMORY
;
725 ref
->ttl
= REFERRAL_TTL
;
726 jucn
->referral_list
= ref
;
727 *consumedcntp
= strlen(dfs_path
);
731 /**********************************************************************
732 Gets valid referrals for a dfs path and fills up the
733 junction_map structure.
734 **********************************************************************/
736 NTSTATUS
get_referred_path(TALLOC_CTX
*ctx
,
737 const char *dfs_path
,
738 struct junction_map
*jucn
,
740 bool *self_referralp
)
742 struct connection_struct conns
;
743 struct connection_struct
*conn
= &conns
;
744 char *targetpath
= NULL
;
746 NTSTATUS status
= NT_STATUS_NOT_FOUND
;
748 struct dfs_path
*pdp
= TALLOC_P(ctx
, struct dfs_path
);
751 return NT_STATUS_NO_MEMORY
;
755 *self_referralp
= False
;
757 status
= parse_dfs_path(NULL
, dfs_path
, False
, pdp
, &dummy
);
758 if (!NT_STATUS_IS_OK(status
)) {
762 jucn
->service_name
= talloc_strdup(ctx
, pdp
->servicename
);
763 jucn
->volume_name
= talloc_strdup(ctx
, pdp
->reqpath
);
764 if (!jucn
->service_name
|| !jucn
->volume_name
) {
766 return NT_STATUS_NO_MEMORY
;
769 /* Verify the share is a dfs root */
770 snum
= lp_servicenumber(jucn
->service_name
);
772 fstring service_name
;
773 fstrcpy(service_name
, jucn
->service_name
);
774 if ((snum
= find_service(service_name
)) < 0) {
775 return NT_STATUS_NOT_FOUND
;
777 TALLOC_FREE(jucn
->service_name
);
778 jucn
->service_name
= talloc_strdup(ctx
, service_name
);
779 if (!jucn
->service_name
) {
781 return NT_STATUS_NO_MEMORY
;
785 if (!lp_msdfs_root(snum
) && (*lp_msdfs_proxy(snum
) == '\0')) {
786 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not "
788 pdp
->servicename
, dfs_path
));
790 return NT_STATUS_NOT_FOUND
;
794 * Self referrals are tested with a anonymous IPC connection and
795 * a GET_DFS_REFERRAL call to \\server\share. (which means
796 * dp.reqpath[0] points to an empty string). create_conn_struct cd's
797 * into the directory and will fail if it cannot (as the anonymous
798 * user). Cope with this.
801 if (pdp
->reqpath
[0] == '\0') {
803 struct referral
*ref
;
805 if (*lp_msdfs_proxy(snum
) == '\0') {
815 * It's an msdfs proxy share. Redirect to
816 * the configured target share.
819 jucn
->referral_count
= 1;
820 if ((ref
= TALLOC_ZERO_P(ctx
, struct referral
)) == NULL
) {
822 return NT_STATUS_NO_MEMORY
;
825 if (!(tmp
= talloc_strdup(ctx
, lp_msdfs_proxy(snum
)))) {
827 return NT_STATUS_NO_MEMORY
;
830 trim_string(tmp
, "\\", 0);
832 ref
->alternate_path
= talloc_asprintf(ctx
, "\\%s", tmp
);
835 if (!ref
->alternate_path
) {
837 return NT_STATUS_NO_MEMORY
;
840 if (pdp
->reqpath
[0] != '\0') {
841 ref
->alternate_path
= talloc_asprintf_append(
845 if (!ref
->alternate_path
) {
847 return NT_STATUS_NO_MEMORY
;
851 ref
->ttl
= REFERRAL_TTL
;
852 jucn
->referral_list
= ref
;
853 *consumedcntp
= strlen(dfs_path
);
858 status
= create_conn_struct(ctx
, conn
, snum
, lp_pathname(snum
));
859 if (!NT_STATUS_IS_OK(status
)) {
864 /* If this is a DFS path dfs_lookup should return
865 * NT_STATUS_PATH_NOT_COVERED. */
867 status
= dfs_path_lookup(ctx
, conn
, dfs_path
, pdp
,
868 False
, consumedcntp
, &targetpath
);
870 if (!NT_STATUS_EQUAL(status
, NT_STATUS_PATH_NOT_COVERED
)) {
871 DEBUG(3,("get_referred_path: No valid referrals for path %s\n",
873 conn_free_internal(conn
);
878 /* We know this is a valid dfs link. Parse the targetpath. */
879 if (!parse_msdfs_symlink(ctx
, targetpath
,
880 &jucn
->referral_list
,
881 &jucn
->referral_count
)) {
882 DEBUG(3,("get_referred_path: failed to parse symlink "
883 "target %s\n", targetpath
));
884 conn_free_internal(conn
);
886 return NT_STATUS_NOT_FOUND
;
889 conn_free_internal(conn
);
894 static int setup_ver2_dfs_referral(const char *pathname
,
896 struct junction_map
*junction
,
900 char* pdata
= *ppdata
;
902 smb_ucs2_t
*uni_requestedpath
= NULL
;
903 int uni_reqpathoffset1
,uni_reqpathoffset2
;
905 int requestedpathlen
=0;
910 DEBUG(10,("Setting up version2 referral\nRequested path:\n"));
912 requestedpathlen
= rpcstr_push_talloc(talloc_tos(),
913 &uni_requestedpath
, pathname
);
914 if (uni_requestedpath
== NULL
|| requestedpathlen
== 0) {
919 dump_data(0, (unsigned char *)uni_requestedpath
,
923 DEBUG(10,("ref count = %u\n",junction
->referral_count
));
925 uni_reqpathoffset1
= REFERRAL_HEADER_SIZE
+
926 VERSION2_REFERRAL_SIZE
* junction
->referral_count
;
928 uni_reqpathoffset2
= uni_reqpathoffset1
+ requestedpathlen
;
930 uni_curroffset
= uni_reqpathoffset2
+ requestedpathlen
;
932 reply_size
= REFERRAL_HEADER_SIZE
+
933 VERSION2_REFERRAL_SIZE
*junction
->referral_count
+
934 2 * requestedpathlen
;
935 DEBUG(10,("reply_size: %u\n",reply_size
));
937 /* add up the unicode lengths of all the referral paths */
938 for(i
=0;i
<junction
->referral_count
;i
++) {
939 DEBUG(10,("referral %u : %s\n",
941 junction
->referral_list
[i
].alternate_path
));
943 (strlen(junction
->referral_list
[i
].alternate_path
)+1)*2;
946 DEBUG(10,("reply_size = %u\n",reply_size
));
947 /* add the unexplained 0x16 bytes */
950 pdata
= (char *)SMB_REALLOC(pdata
,reply_size
);
952 DEBUG(0,("Realloc failed!\n"));
957 /* copy in the dfs requested paths.. required for offset calculations */
958 memcpy(pdata
+uni_reqpathoffset1
,uni_requestedpath
,requestedpathlen
);
959 memcpy(pdata
+uni_reqpathoffset2
,uni_requestedpath
,requestedpathlen
);
961 /* create the header */
962 SSVAL(pdata
,0,consumedcnt
* 2); /* path consumed */
963 /* number of referral in this pkt */
964 SSVAL(pdata
,2,junction
->referral_count
);
966 SIVAL(pdata
,4,DFSREF_REFERRAL_SERVER
| DFSREF_STORAGE_SERVER
);
968 SIVAL(pdata
,4,DFSREF_STORAGE_SERVER
);
972 /* add the referral elements */
973 for(i
=0;i
<junction
->referral_count
;i
++) {
974 struct referral
* ref
= &junction
->referral_list
[i
];
977 SSVAL(pdata
,offset
,2); /* version 2 */
978 SSVAL(pdata
,offset
+2,VERSION2_REFERRAL_SIZE
);
980 SSVAL(pdata
,offset
+4,1);
982 SSVAL(pdata
,offset
+4,0);
985 /* ref_flags :use path_consumed bytes? */
986 SSVAL(pdata
,offset
+6,0);
987 SIVAL(pdata
,offset
+8,ref
->proximity
);
988 SIVAL(pdata
,offset
+12,ref
->ttl
);
990 SSVAL(pdata
,offset
+16,uni_reqpathoffset1
-offset
);
991 SSVAL(pdata
,offset
+18,uni_reqpathoffset2
-offset
);
992 /* copy referred path into current offset */
993 unilen
= rpcstr_push(pdata
+uni_curroffset
,
995 reply_size
- uni_curroffset
,
998 SSVAL(pdata
,offset
+20,uni_curroffset
-offset
);
1000 uni_curroffset
+= unilen
;
1001 offset
+= VERSION2_REFERRAL_SIZE
;
1003 /* add in the unexplained 22 (0x16) bytes at the end */
1004 memset(pdata
+uni_curroffset
,'\0',0x16);
1008 static int setup_ver3_dfs_referral(const char *pathname
,
1010 struct junction_map
*junction
,
1014 char *pdata
= *ppdata
;
1016 smb_ucs2_t
*uni_reqpath
= NULL
;
1017 int uni_reqpathoffset1
, uni_reqpathoffset2
;
1024 DEBUG(10,("setting up version3 referral\n"));
1026 reqpathlen
= rpcstr_push_talloc(talloc_tos(), &uni_reqpath
, pathname
);
1027 if (uni_reqpath
== NULL
|| reqpathlen
== 0) {
1032 dump_data(0, (unsigned char *)uni_reqpath
,
1036 uni_reqpathoffset1
= REFERRAL_HEADER_SIZE
+
1037 VERSION3_REFERRAL_SIZE
* junction
->referral_count
;
1038 uni_reqpathoffset2
= uni_reqpathoffset1
+ reqpathlen
;
1039 reply_size
= uni_curroffset
= uni_reqpathoffset2
+ reqpathlen
;
1041 for(i
=0;i
<junction
->referral_count
;i
++) {
1042 DEBUG(10,("referral %u : %s\n",
1044 junction
->referral_list
[i
].alternate_path
));
1046 (strlen(junction
->referral_list
[i
].alternate_path
)+1)*2;
1049 pdata
= (char *)SMB_REALLOC(pdata
,reply_size
);
1051 DEBUG(0,("version3 referral setup:"
1052 "malloc failed for Realloc!\n"));
1057 /* create the header */
1058 SSVAL(pdata
,0,consumedcnt
* 2); /* path consumed */
1059 SSVAL(pdata
,2,junction
->referral_count
); /* number of referral */
1061 SIVAL(pdata
,4,DFSREF_REFERRAL_SERVER
| DFSREF_STORAGE_SERVER
);
1063 SIVAL(pdata
,4,DFSREF_STORAGE_SERVER
);
1066 /* copy in the reqpaths */
1067 memcpy(pdata
+uni_reqpathoffset1
,uni_reqpath
,reqpathlen
);
1068 memcpy(pdata
+uni_reqpathoffset2
,uni_reqpath
,reqpathlen
);
1071 for(i
=0;i
<junction
->referral_count
;i
++) {
1072 struct referral
* ref
= &(junction
->referral_list
[i
]);
1075 SSVAL(pdata
,offset
,3); /* version 3 */
1076 SSVAL(pdata
,offset
+2,VERSION3_REFERRAL_SIZE
);
1078 SSVAL(pdata
,offset
+4,1);
1080 SSVAL(pdata
,offset
+4,0);
1083 /* ref_flags :use path_consumed bytes? */
1084 SSVAL(pdata
,offset
+6,0);
1085 SIVAL(pdata
,offset
+8,ref
->ttl
);
1087 SSVAL(pdata
,offset
+12,uni_reqpathoffset1
-offset
);
1088 SSVAL(pdata
,offset
+14,uni_reqpathoffset2
-offset
);
1089 /* copy referred path into current offset */
1090 unilen
= rpcstr_push(pdata
+uni_curroffset
,ref
->alternate_path
,
1091 reply_size
- uni_curroffset
,
1092 STR_UNICODE
| STR_TERMINATE
);
1093 SSVAL(pdata
,offset
+16,uni_curroffset
-offset
);
1094 /* copy 0x10 bytes of 00's in the ServiceSite GUID */
1095 memset(pdata
+offset
+18,'\0',16);
1097 uni_curroffset
+= unilen
;
1098 offset
+= VERSION3_REFERRAL_SIZE
;
1103 /******************************************************************
1104 Set up the DFS referral for the dfs pathname. This call returns
1105 the amount of the path covered by this server, and where the
1106 client should be redirected to. This is the meat of the
1107 TRANS2_GET_DFS_REFERRAL call.
1108 ******************************************************************/
1110 int setup_dfs_referral(connection_struct
*orig_conn
,
1111 const char *dfs_path
,
1112 int max_referral_level
,
1113 char **ppdata
, NTSTATUS
*pstatus
)
1115 struct junction_map
*junction
= NULL
;
1116 int consumedcnt
= 0;
1117 bool self_referral
= False
;
1119 char *pathnamep
= NULL
;
1120 char *local_dfs_path
= NULL
;
1123 if (!(ctx
=talloc_init("setup_dfs_referral"))) {
1124 *pstatus
= NT_STATUS_NO_MEMORY
;
1128 /* get the junction entry */
1130 talloc_destroy(ctx
);
1131 *pstatus
= NT_STATUS_NOT_FOUND
;
1136 * Trim pathname sent by client so it begins with only one backslash.
1137 * Two backslashes confuse some dfs clients
1140 local_dfs_path
= talloc_strdup(ctx
,dfs_path
);
1141 if (!local_dfs_path
) {
1142 *pstatus
= NT_STATUS_NO_MEMORY
;
1143 talloc_destroy(ctx
);
1146 pathnamep
= local_dfs_path
;
1147 while (IS_DIRECTORY_SEP(pathnamep
[0]) &&
1148 IS_DIRECTORY_SEP(pathnamep
[1])) {
1152 junction
= TALLOC_ZERO_P(ctx
, struct junction_map
);
1154 *pstatus
= NT_STATUS_NO_MEMORY
;
1155 talloc_destroy(ctx
);
1159 /* The following call can change cwd. */
1160 *pstatus
= get_referred_path(ctx
, pathnamep
, junction
,
1161 &consumedcnt
, &self_referral
);
1162 if (!NT_STATUS_IS_OK(*pstatus
)) {
1163 vfs_ChDir(orig_conn
,orig_conn
->connectpath
);
1164 talloc_destroy(ctx
);
1167 vfs_ChDir(orig_conn
,orig_conn
->connectpath
);
1169 if (!self_referral
) {
1170 pathnamep
[consumedcnt
] = '\0';
1172 if( DEBUGLVL( 3 ) ) {
1174 dbgtext("setup_dfs_referral: Path %s to "
1175 "alternate path(s):",
1177 for(i
=0;i
<junction
->referral_count
;i
++)
1179 junction
->referral_list
[i
].alternate_path
);
1184 /* create the referral depeding on version */
1185 DEBUG(10,("max_referral_level :%d\n",max_referral_level
));
1187 if (max_referral_level
< 2) {
1188 max_referral_level
= 2;
1190 if (max_referral_level
> 3) {
1191 max_referral_level
= 3;
1194 switch(max_referral_level
) {
1196 reply_size
= setup_ver2_dfs_referral(pathnamep
,
1198 consumedcnt
, self_referral
);
1201 reply_size
= setup_ver3_dfs_referral(pathnamep
, ppdata
,
1202 junction
, consumedcnt
, self_referral
);
1205 DEBUG(0,("setup_dfs_referral: Invalid dfs referral "
1207 max_referral_level
));
1208 talloc_destroy(ctx
);
1209 *pstatus
= NT_STATUS_INVALID_LEVEL
;
1214 DEBUGADD(0,("DFS Referral pdata:\n"));
1215 dump_data(0,(uint8
*)*ppdata
,reply_size
);
1218 talloc_destroy(ctx
);
1219 *pstatus
= NT_STATUS_OK
;
1223 /**********************************************************************
1224 The following functions are called by the NETDFS RPC pipe functions
1225 **********************************************************************/
1227 /*********************************************************************
1228 Creates a junction structure from a DFS pathname
1229 **********************************************************************/
1231 bool create_junction(TALLOC_CTX
*ctx
,
1232 const char *dfs_path
,
1233 struct junction_map
*jucn
)
1237 struct dfs_path
*pdp
= TALLOC_P(ctx
,struct dfs_path
);
1243 status
= parse_dfs_path(NULL
, dfs_path
, False
, pdp
, &dummy
);
1244 if (!NT_STATUS_IS_OK(status
)) {
1248 /* check if path is dfs : validate first token */
1249 if (!is_myname_or_ipaddr(pdp
->hostname
)) {
1250 DEBUG(4,("create_junction: Invalid hostname %s "
1252 pdp
->hostname
, dfs_path
));
1257 /* Check for a non-DFS share */
1258 snum
= lp_servicenumber(pdp
->servicename
);
1260 if(snum
< 0 || !lp_msdfs_root(snum
)) {
1261 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1267 jucn
->service_name
= talloc_strdup(ctx
, pdp
->servicename
);
1268 jucn
->volume_name
= talloc_strdup(ctx
, pdp
->reqpath
);
1269 jucn
->comment
= talloc_strdup(ctx
, lp_comment(snum
));
1272 if (!jucn
->service_name
|| !jucn
->volume_name
|| ! jucn
->comment
) {
1278 /**********************************************************************
1279 Forms a valid Unix pathname from the junction
1280 **********************************************************************/
1282 static bool junction_to_local_path(const struct junction_map
*jucn
,
1284 connection_struct
*conn_out
)
1288 snum
= lp_servicenumber(jucn
->service_name
);
1292 if (!NT_STATUS_IS_OK(create_conn_struct(talloc_tos(),
1294 lp_pathname(snum
)))) {
1298 *pp_path_out
= talloc_asprintf(conn_out
->mem_ctx
,
1302 if (!*pp_path_out
) {
1308 bool create_msdfs_link(const struct junction_map
*jucn
,
1312 char *msdfs_link
= NULL
;
1313 connection_struct conns
;
1314 connection_struct
*conn
= &conns
;
1316 bool insert_comma
= False
;
1321 if(!junction_to_local_path(jucn
, &path
, conn
)) {
1325 /* Form the msdfs_link contents */
1326 msdfs_link
= talloc_strdup(conn
->mem_ctx
, "msdfs:");
1330 for(i
=0; i
<jucn
->referral_count
; i
++) {
1331 char *refpath
= jucn
->referral_list
[i
].alternate_path
;
1333 /* Alternate paths always use Windows separators. */
1334 trim_char(refpath
, '\\', '\\');
1335 if(*refpath
== '\0') {
1337 insert_comma
= False
;
1341 if (i
> 0 && insert_comma
) {
1342 msdfs_link
= talloc_asprintf_append_buffer(msdfs_link
,
1346 msdfs_link
= talloc_asprintf_append_buffer(msdfs_link
,
1354 if (!insert_comma
) {
1355 insert_comma
= True
;
1359 DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
1363 if(SMB_VFS_UNLINK(conn
,path
)!=0) {
1368 if(SMB_VFS_SYMLINK(conn
, msdfs_link
, path
) < 0) {
1369 DEBUG(1,("create_msdfs_link: symlink failed "
1370 "%s -> %s\nError: %s\n",
1371 path
, msdfs_link
, strerror(errno
)));
1379 conn_free_internal(conn
);
1383 bool remove_msdfs_link(const struct junction_map
*jucn
)
1386 connection_struct conns
;
1387 connection_struct
*conn
= &conns
;
1392 if( junction_to_local_path(jucn
, &path
, conn
) ) {
1393 if( SMB_VFS_UNLINK(conn
, path
) == 0 ) {
1398 conn_free_internal(conn
);
1402 /*********************************************************************
1403 Return the number of DFS links at the root of this share.
1404 *********************************************************************/
1406 static int count_dfs_links(TALLOC_CTX
*ctx
, int snum
)
1409 SMB_STRUCT_DIR
*dirp
= NULL
;
1411 const char *connect_path
= lp_pathname(snum
);
1412 const char *msdfs_proxy
= lp_msdfs_proxy(snum
);
1413 connection_struct conn
;
1417 if(*connect_path
== '\0') {
1422 * Fake up a connection struct for the VFS layer.
1425 if (!NT_STATUS_IS_OK(create_conn_struct(talloc_tos(),
1426 &conn
, snum
, connect_path
))) {
1430 /* Count a link for the msdfs root - convention */
1433 /* No more links if this is an msdfs proxy. */
1434 if (*msdfs_proxy
!= '\0') {
1438 /* Now enumerate all dfs links */
1439 dirp
= SMB_VFS_OPENDIR(&conn
, ".", NULL
, 0);
1444 while ((dname
= vfs_readdirname(&conn
, dirp
)) != NULL
) {
1445 if (is_msdfs_link(&conn
,
1452 SMB_VFS_CLOSEDIR(&conn
,dirp
);
1456 conn_free_internal(&conn
);
1460 /*********************************************************************
1461 *********************************************************************/
1463 static int form_junctions(TALLOC_CTX
*ctx
,
1465 struct junction_map
*jucn
,
1469 SMB_STRUCT_DIR
*dirp
= NULL
;
1471 const char *connect_path
= lp_pathname(snum
);
1472 char *service_name
= lp_servicename(snum
);
1473 const char *msdfs_proxy
= lp_msdfs_proxy(snum
);
1474 connection_struct conn
;
1475 struct referral
*ref
= NULL
;
1479 if (jn_remain
== 0) {
1483 if(*connect_path
== '\0') {
1488 * Fake up a connection struct for the VFS layer.
1491 if (!NT_STATUS_IS_OK(create_conn_struct(ctx
, &conn
, snum
, connect_path
))) {
1495 /* form a junction for the msdfs root - convention
1496 DO NOT REMOVE THIS: NT clients will not work with us
1497 if this is not present
1499 jucn
[cnt
].service_name
= talloc_strdup(ctx
,service_name
);
1500 jucn
[cnt
].volume_name
= talloc_strdup(ctx
, "");
1501 if (!jucn
[cnt
].service_name
|| !jucn
[cnt
].volume_name
) {
1504 jucn
[cnt
].referral_count
= 1;
1506 ref
= jucn
[cnt
].referral_list
= TALLOC_ZERO_P(ctx
, struct referral
);
1507 if (jucn
[cnt
].referral_list
== NULL
) {
1512 ref
->ttl
= REFERRAL_TTL
;
1513 if (*msdfs_proxy
!= '\0') {
1514 ref
->alternate_path
= talloc_strdup(ctx
,
1517 ref
->alternate_path
= talloc_asprintf(ctx
,
1519 get_local_machine_name(),
1523 if (!ref
->alternate_path
) {
1528 /* Don't enumerate if we're an msdfs proxy. */
1529 if (*msdfs_proxy
!= '\0') {
1533 /* Now enumerate all dfs links */
1534 dirp
= SMB_VFS_OPENDIR(&conn
, ".", NULL
, 0);
1539 while ((dname
= vfs_readdirname(&conn
, dirp
)) != NULL
) {
1540 char *link_target
= NULL
;
1541 if (cnt
>= jn_remain
) {
1542 SMB_VFS_CLOSEDIR(&conn
,dirp
);
1543 DEBUG(2, ("form_junctions: ran out of MSDFS "
1547 if (is_msdfs_link_internal(ctx
,
1549 dname
, &link_target
,
1551 if (parse_msdfs_symlink(ctx
,
1553 &jucn
[cnt
].referral_list
,
1554 &jucn
[cnt
].referral_count
)) {
1556 jucn
[cnt
].service_name
= talloc_strdup(ctx
,
1558 jucn
[cnt
].volume_name
= talloc_strdup(ctx
,
1560 if (!jucn
[cnt
].service_name
||
1561 !jucn
[cnt
].volume_name
) {
1572 SMB_VFS_CLOSEDIR(&conn
,dirp
);
1575 conn_free_internal(&conn
);
1579 struct junction_map
*enum_msdfs_links(TALLOC_CTX
*ctx
, size_t *p_num_jn
)
1581 struct junction_map
*jn
= NULL
;
1583 size_t jn_count
= 0;
1587 if(!lp_host_msdfs()) {
1591 /* Ensure all the usershares are loaded. */
1593 load_registry_shares();
1594 sharecount
= load_usershare_shares();
1597 for(i
=0;i
< sharecount
;i
++) {
1598 if(lp_msdfs_root(i
)) {
1599 jn_count
+= count_dfs_links(ctx
, i
);
1602 if (jn_count
== 0) {
1605 jn
= TALLOC_ARRAY(ctx
, struct junction_map
, jn_count
);
1609 for(i
=0; i
< sharecount
; i
++) {
1610 if (*p_num_jn
>= jn_count
) {
1613 if(lp_msdfs_root(i
)) {
1614 *p_num_jn
+= form_junctions(ctx
, i
,
1616 jn_count
- *p_num_jn
);
1622 /******************************************************************************
1623 Core function to resolve a dfs pathname.
1624 ******************************************************************************/
1626 NTSTATUS
resolve_dfspath(TALLOC_CTX
*ctx
,
1627 connection_struct
*conn
,
1629 const char *name_in
,
1632 NTSTATUS status
= NT_STATUS_OK
;
1634 if (dfs_pathnames
) {
1635 status
= dfs_redirect(ctx
,
1643 * Cheat and just return a copy of the in ptr.
1644 * Once srvstr_get_path() uses talloc it'll
1645 * be a talloced ptr anyway.
1647 *pp_name_out
= CONST_DISCARD(char *,name_in
);
1652 /******************************************************************************
1653 Core function to resolve a dfs pathname possibly containing a wildcard.
1654 This function is identical to the above except for the bool param to
1655 dfs_redirect but I need this to be separate so it's really clear when
1656 we're allowing wildcards and when we're not. JRA.
1657 ******************************************************************************/
1659 NTSTATUS
resolve_dfspath_wcard(TALLOC_CTX
*ctx
,
1660 connection_struct
*conn
,
1662 const char *name_in
,
1664 bool *ppath_contains_wcard
)
1666 NTSTATUS status
= NT_STATUS_OK
;
1667 if (dfs_pathnames
) {
1668 status
= dfs_redirect(ctx
,
1673 ppath_contains_wcard
);
1676 * Cheat and just return a copy of the in ptr.
1677 * Once srvstr_get_path() uses talloc it'll
1678 * be a talloced ptr anyway.
1680 *pp_name_out
= CONST_DISCARD(char *,name_in
);