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
));
374 TALLOC_FREE(alt_path
);
378 /**********************************************************************
379 Returns true if the unix path is a valid msdfs symlink and also
380 returns the target string from inside the link.
381 **********************************************************************/
383 static bool is_msdfs_link_internal(TALLOC_CTX
*ctx
,
384 connection_struct
*conn
,
386 char **pp_link_target
,
387 SMB_STRUCT_STAT
*sbufp
)
390 int referral_len
= 0;
391 char link_target_buf
[7];
393 char *link_target
= NULL
;
395 if (pp_link_target
) {
397 link_target
= TALLOC_ARRAY(ctx
, char, bufsize
);
401 *pp_link_target
= link_target
;
403 bufsize
= sizeof(link_target_buf
);
404 link_target
= link_target_buf
;
411 if (SMB_VFS_LSTAT(conn
, path
, sbufp
) != 0) {
412 DEBUG(5,("is_msdfs_link_read_target: %s does not exist.\n",
417 if (!S_ISLNK(sbufp
->st_mode
)) {
418 DEBUG(5,("is_msdfs_link_read_target: %s is not a link.\n",
423 referral_len
= SMB_VFS_READLINK(conn
, path
, link_target
, bufsize
- 1);
424 if (referral_len
== -1) {
425 DEBUG(0,("is_msdfs_link_read_target: Error reading "
426 "msdfs link %s: %s\n",
427 path
, strerror(errno
)));
430 link_target
[referral_len
] = '\0';
432 DEBUG(5,("is_msdfs_link_internal: %s -> %s\n",path
,
435 if (!strnequal(link_target
, "msdfs:", 6)) {
442 if (link_target
!= link_target_buf
) {
443 TALLOC_FREE(link_target
);
448 /**********************************************************************
449 Returns true if the unix path is a valid msdfs symlink.
450 **********************************************************************/
452 bool is_msdfs_link(connection_struct
*conn
,
454 SMB_STRUCT_STAT
*sbufp
)
456 return is_msdfs_link_internal(talloc_tos(),
463 /*****************************************************************
464 Used by other functions to decide if a dfs path is remote,
465 and to get the list of referred locations for that remote path.
467 search_flag: For findfirsts, dfs links themselves are not
468 redirected, but paths beyond the links are. For normal smb calls,
469 even dfs links need to be redirected.
471 consumedcntp: how much of the dfs path is being redirected. the client
472 should try the remaining path on the redirected server.
474 If this returns NT_STATUS_PATH_NOT_COVERED the contents of the msdfs
475 link redirect are in targetpath.
476 *****************************************************************/
478 static NTSTATUS
dfs_path_lookup(TALLOC_CTX
*ctx
,
479 connection_struct
*conn
,
480 const char *dfspath
, /* Incoming complete dfs path */
481 const struct dfs_path
*pdp
, /* Parsed out
482 server+share+extrapath. */
483 bool search_flag
, /* Called from a findfirst ? */
485 char **pp_targetpath
)
489 SMB_STRUCT_STAT sbuf
;
491 char *localpath
= NULL
;
492 char *canon_dfspath
= NULL
; /* Canonicalized dfs path. (only '/'
495 DEBUG(10,("dfs_path_lookup: Conn path = %s reqpath = %s\n",
496 conn
->connectpath
, pdp
->reqpath
));
499 * Note the unix path conversion here we're doing we can
500 * throw away. We're looking for a symlink for a dfs
501 * resolution, if we don't find it we'll do another
502 * unix_convert later in the codepath.
503 * If we needed to remember what we'd resolved in
504 * dp->reqpath (as the original code did) we'd
505 * copy (localhost, dp->reqpath) on any code
506 * path below that returns True - but I don't
507 * think this is needed. JRA.
510 status
= unix_convert(ctx
, conn
, pdp
->reqpath
, search_flag
, &localpath
,
512 if (!NT_STATUS_IS_OK(status
) && !NT_STATUS_EQUAL(status
,
513 NT_STATUS_OBJECT_PATH_NOT_FOUND
)) {
517 /* Optimization - check if we can redirect the whole path. */
519 if (is_msdfs_link_internal(ctx
, conn
, localpath
, pp_targetpath
, NULL
)) {
521 DEBUG(6,("dfs_path_lookup (FindFirst) No redirection "
522 "for dfs link %s.\n", dfspath
));
526 DEBUG(6,("dfs_path_lookup: %s resolves to a "
527 "valid dfs link %s.\n", dfspath
,
528 pp_targetpath
? *pp_targetpath
: ""));
531 *consumedcntp
= strlen(dfspath
);
533 return NT_STATUS_PATH_NOT_COVERED
;
536 /* Prepare to test only for '/' components in the given path,
537 * so if a Windows path replace all '\\' characters with '/'.
538 * For a POSIX DFS path we know all separators are already '/'. */
540 canon_dfspath
= talloc_strdup(ctx
, dfspath
);
541 if (!canon_dfspath
) {
542 return NT_STATUS_NO_MEMORY
;
544 if (!pdp
->posix_path
) {
545 string_replace(canon_dfspath
, '\\', '/');
549 * localpath comes out of unix_convert, so it has
550 * no trailing backslash. Make sure that canon_dfspath hasn't either.
551 * Fix for bug #4860 from Jan Martin <Jan.Martin@rwedea.com>.
554 trim_char(canon_dfspath
,0,'/');
557 * Redirect if any component in the path is a link.
558 * We do this by walking backwards through the
559 * local path, chopping off the last component
560 * in both the local path and the canonicalized
561 * DFS path. If we hit a DFS link then we're done.
564 p
= strrchr_m(localpath
, '/');
566 q
= strrchr_m(canon_dfspath
, '/');
575 if (is_msdfs_link_internal(ctx
, conn
,
576 localpath
, pp_targetpath
, NULL
)) {
577 DEBUG(4, ("dfs_path_lookup: Redirecting %s because "
578 "parent %s is dfs link\n", dfspath
, localpath
));
581 *consumedcntp
= strlen(canon_dfspath
);
582 DEBUG(10, ("dfs_path_lookup: Path consumed: %s "
588 return NT_STATUS_PATH_NOT_COVERED
;
591 /* Step back on the filesystem. */
592 p
= strrchr_m(localpath
, '/');
595 /* And in the canonicalized dfs path. */
596 q
= strrchr_m(canon_dfspath
, '/');
603 /*****************************************************************
604 Decides if a dfs pathname should be redirected or not.
605 If not, the pathname is converted to a tcon-relative local unix path
607 search_wcard_flag: this flag performs 2 functions both related
608 to searches. See resolve_dfs_path() and parse_dfs_path_XX()
611 This function can return NT_STATUS_OK, meaning use the returned path as-is
612 (mapped into a local path).
613 or NT_STATUS_NOT_COVERED meaning return a DFS redirect, or
614 any other NT_STATUS error which is a genuine error to be
615 returned to the client.
616 *****************************************************************/
618 static NTSTATUS
dfs_redirect(TALLOC_CTX
*ctx
,
619 connection_struct
*conn
,
621 bool search_wcard_flag
,
623 bool *ppath_contains_wcard
)
626 struct dfs_path
*pdp
= TALLOC_P(ctx
, struct dfs_path
);
629 return NT_STATUS_NO_MEMORY
;
632 status
= parse_dfs_path(conn
, path_in
, search_wcard_flag
, pdp
,
633 ppath_contains_wcard
);
634 if (!NT_STATUS_IS_OK(status
)) {
639 if (pdp
->reqpath
[0] == '\0') {
641 *pp_path_out
= talloc_strdup(ctx
, "");
643 return NT_STATUS_NO_MEMORY
;
645 DEBUG(5,("dfs_redirect: self-referral.\n"));
649 /* If dfs pathname for a non-dfs share, convert to tcon-relative
650 path and return OK */
652 if (!lp_msdfs_root(SNUM(conn
))) {
653 *pp_path_out
= talloc_strdup(ctx
, pdp
->reqpath
);
656 return NT_STATUS_NO_MEMORY
;
661 /* If it looked like a local path (zero hostname/servicename)
662 * just treat as a tcon-relative path. */
664 if (pdp
->hostname
[0] == '\0' && pdp
->servicename
[0] == '\0') {
665 *pp_path_out
= talloc_strdup(ctx
, pdp
->reqpath
);
668 return NT_STATUS_NO_MEMORY
;
673 status
= dfs_path_lookup(ctx
, conn
, path_in
, pdp
,
674 search_wcard_flag
, NULL
, NULL
);
675 if (!NT_STATUS_IS_OK(status
)) {
676 if (NT_STATUS_EQUAL(status
, NT_STATUS_PATH_NOT_COVERED
)) {
677 DEBUG(3,("dfs_redirect: Redirecting %s\n", path_in
));
679 DEBUG(10,("dfs_redirect: dfs_path_lookup "
680 "failed for %s with %s\n",
681 path_in
, nt_errstr(status
) ));
686 DEBUG(3,("dfs_redirect: Not redirecting %s.\n", path_in
));
688 /* Form non-dfs tcon-relative path */
689 *pp_path_out
= talloc_strdup(ctx
, pdp
->reqpath
);
692 return NT_STATUS_NO_MEMORY
;
695 DEBUG(3,("dfs_redirect: Path %s converted to non-dfs path %s\n",
702 /**********************************************************************
703 Return a self referral.
704 **********************************************************************/
706 static NTSTATUS
self_ref(TALLOC_CTX
*ctx
,
707 const char *dfs_path
,
708 struct junction_map
*jucn
,
710 bool *self_referralp
)
712 struct referral
*ref
;
714 *self_referralp
= True
;
716 jucn
->referral_count
= 1;
717 if((ref
= TALLOC_ZERO_P(ctx
, struct referral
)) == NULL
) {
718 return NT_STATUS_NO_MEMORY
;
721 ref
->alternate_path
= talloc_strdup(ctx
, dfs_path
);
722 if (!ref
->alternate_path
) {
723 return NT_STATUS_NO_MEMORY
;
726 ref
->ttl
= REFERRAL_TTL
;
727 jucn
->referral_list
= ref
;
728 *consumedcntp
= strlen(dfs_path
);
732 /**********************************************************************
733 Gets valid referrals for a dfs path and fills up the
734 junction_map structure.
735 **********************************************************************/
737 NTSTATUS
get_referred_path(TALLOC_CTX
*ctx
,
738 const char *dfs_path
,
739 struct junction_map
*jucn
,
741 bool *self_referralp
)
743 struct connection_struct conns
;
744 struct connection_struct
*conn
= &conns
;
745 char *targetpath
= NULL
;
747 NTSTATUS status
= NT_STATUS_NOT_FOUND
;
749 struct dfs_path
*pdp
= TALLOC_P(ctx
, struct dfs_path
);
752 return NT_STATUS_NO_MEMORY
;
756 *self_referralp
= False
;
758 status
= parse_dfs_path(NULL
, dfs_path
, False
, pdp
, &dummy
);
759 if (!NT_STATUS_IS_OK(status
)) {
763 jucn
->service_name
= talloc_strdup(ctx
, pdp
->servicename
);
764 jucn
->volume_name
= talloc_strdup(ctx
, pdp
->reqpath
);
765 if (!jucn
->service_name
|| !jucn
->volume_name
) {
767 return NT_STATUS_NO_MEMORY
;
770 /* Verify the share is a dfs root */
771 snum
= lp_servicenumber(jucn
->service_name
);
773 fstring service_name
;
774 fstrcpy(service_name
, jucn
->service_name
);
775 if ((snum
= find_service(service_name
)) < 0) {
776 return NT_STATUS_NOT_FOUND
;
778 TALLOC_FREE(jucn
->service_name
);
779 jucn
->service_name
= talloc_strdup(ctx
, service_name
);
780 if (!jucn
->service_name
) {
782 return NT_STATUS_NO_MEMORY
;
786 if (!lp_msdfs_root(snum
) && (*lp_msdfs_proxy(snum
) == '\0')) {
787 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not "
789 pdp
->servicename
, dfs_path
));
791 return NT_STATUS_NOT_FOUND
;
795 * Self referrals are tested with a anonymous IPC connection and
796 * a GET_DFS_REFERRAL call to \\server\share. (which means
797 * dp.reqpath[0] points to an empty string). create_conn_struct cd's
798 * into the directory and will fail if it cannot (as the anonymous
799 * user). Cope with this.
802 if (pdp
->reqpath
[0] == '\0') {
804 struct referral
*ref
;
806 if (*lp_msdfs_proxy(snum
) == '\0') {
816 * It's an msdfs proxy share. Redirect to
817 * the configured target share.
820 jucn
->referral_count
= 1;
821 if ((ref
= TALLOC_ZERO_P(ctx
, struct referral
)) == NULL
) {
823 return NT_STATUS_NO_MEMORY
;
826 if (!(tmp
= talloc_strdup(ctx
, lp_msdfs_proxy(snum
)))) {
828 return NT_STATUS_NO_MEMORY
;
831 trim_string(tmp
, "\\", 0);
833 ref
->alternate_path
= talloc_asprintf(ctx
, "\\%s", tmp
);
836 if (!ref
->alternate_path
) {
838 return NT_STATUS_NO_MEMORY
;
841 if (pdp
->reqpath
[0] != '\0') {
842 ref
->alternate_path
= talloc_asprintf_append(
846 if (!ref
->alternate_path
) {
848 return NT_STATUS_NO_MEMORY
;
852 ref
->ttl
= REFERRAL_TTL
;
853 jucn
->referral_list
= ref
;
854 *consumedcntp
= strlen(dfs_path
);
859 status
= create_conn_struct(ctx
, conn
, snum
, lp_pathname(snum
));
860 if (!NT_STATUS_IS_OK(status
)) {
865 /* If this is a DFS path dfs_lookup should return
866 * NT_STATUS_PATH_NOT_COVERED. */
868 status
= dfs_path_lookup(ctx
, conn
, dfs_path
, pdp
,
869 False
, consumedcntp
, &targetpath
);
871 if (!NT_STATUS_EQUAL(status
, NT_STATUS_PATH_NOT_COVERED
)) {
872 DEBUG(3,("get_referred_path: No valid referrals for path %s\n",
874 conn_free_internal(conn
);
879 /* We know this is a valid dfs link. Parse the targetpath. */
880 if (!parse_msdfs_symlink(ctx
, targetpath
,
881 &jucn
->referral_list
,
882 &jucn
->referral_count
)) {
883 DEBUG(3,("get_referred_path: failed to parse symlink "
884 "target %s\n", targetpath
));
885 conn_free_internal(conn
);
887 return NT_STATUS_NOT_FOUND
;
890 conn_free_internal(conn
);
895 static int setup_ver2_dfs_referral(const char *pathname
,
897 struct junction_map
*junction
,
901 char* pdata
= *ppdata
;
903 smb_ucs2_t
*uni_requestedpath
= NULL
;
904 int uni_reqpathoffset1
,uni_reqpathoffset2
;
906 int requestedpathlen
=0;
911 DEBUG(10,("Setting up version2 referral\nRequested path:\n"));
913 requestedpathlen
= rpcstr_push_talloc(talloc_tos(),
914 &uni_requestedpath
, pathname
);
915 if (uni_requestedpath
== NULL
|| requestedpathlen
== 0) {
920 dump_data(0, (unsigned char *)uni_requestedpath
,
924 DEBUG(10,("ref count = %u\n",junction
->referral_count
));
926 uni_reqpathoffset1
= REFERRAL_HEADER_SIZE
+
927 VERSION2_REFERRAL_SIZE
* junction
->referral_count
;
929 uni_reqpathoffset2
= uni_reqpathoffset1
+ requestedpathlen
;
931 uni_curroffset
= uni_reqpathoffset2
+ requestedpathlen
;
933 reply_size
= REFERRAL_HEADER_SIZE
+
934 VERSION2_REFERRAL_SIZE
*junction
->referral_count
+
935 2 * requestedpathlen
;
936 DEBUG(10,("reply_size: %u\n",reply_size
));
938 /* add up the unicode lengths of all the referral paths */
939 for(i
=0;i
<junction
->referral_count
;i
++) {
940 DEBUG(10,("referral %u : %s\n",
942 junction
->referral_list
[i
].alternate_path
));
944 (strlen(junction
->referral_list
[i
].alternate_path
)+1)*2;
947 DEBUG(10,("reply_size = %u\n",reply_size
));
948 /* add the unexplained 0x16 bytes */
951 pdata
= (char *)SMB_REALLOC(pdata
,reply_size
);
953 DEBUG(0,("Realloc failed!\n"));
958 /* copy in the dfs requested paths.. required for offset calculations */
959 memcpy(pdata
+uni_reqpathoffset1
,uni_requestedpath
,requestedpathlen
);
960 memcpy(pdata
+uni_reqpathoffset2
,uni_requestedpath
,requestedpathlen
);
962 /* create the header */
963 SSVAL(pdata
,0,consumedcnt
* 2); /* path consumed */
964 /* number of referral in this pkt */
965 SSVAL(pdata
,2,junction
->referral_count
);
967 SIVAL(pdata
,4,DFSREF_REFERRAL_SERVER
| DFSREF_STORAGE_SERVER
);
969 SIVAL(pdata
,4,DFSREF_STORAGE_SERVER
);
973 /* add the referral elements */
974 for(i
=0;i
<junction
->referral_count
;i
++) {
975 struct referral
* ref
= &junction
->referral_list
[i
];
978 SSVAL(pdata
,offset
,2); /* version 2 */
979 SSVAL(pdata
,offset
+2,VERSION2_REFERRAL_SIZE
);
981 SSVAL(pdata
,offset
+4,1);
983 SSVAL(pdata
,offset
+4,0);
986 /* ref_flags :use path_consumed bytes? */
987 SSVAL(pdata
,offset
+6,0);
988 SIVAL(pdata
,offset
+8,ref
->proximity
);
989 SIVAL(pdata
,offset
+12,ref
->ttl
);
991 SSVAL(pdata
,offset
+16,uni_reqpathoffset1
-offset
);
992 SSVAL(pdata
,offset
+18,uni_reqpathoffset2
-offset
);
993 /* copy referred path into current offset */
994 unilen
= rpcstr_push(pdata
+uni_curroffset
,
996 reply_size
- uni_curroffset
,
999 SSVAL(pdata
,offset
+20,uni_curroffset
-offset
);
1001 uni_curroffset
+= unilen
;
1002 offset
+= VERSION2_REFERRAL_SIZE
;
1004 /* add in the unexplained 22 (0x16) bytes at the end */
1005 memset(pdata
+uni_curroffset
,'\0',0x16);
1009 static int setup_ver3_dfs_referral(const char *pathname
,
1011 struct junction_map
*junction
,
1015 char *pdata
= *ppdata
;
1017 smb_ucs2_t
*uni_reqpath
= NULL
;
1018 int uni_reqpathoffset1
, uni_reqpathoffset2
;
1025 DEBUG(10,("setting up version3 referral\n"));
1027 reqpathlen
= rpcstr_push_talloc(talloc_tos(), &uni_reqpath
, pathname
);
1028 if (uni_reqpath
== NULL
|| reqpathlen
== 0) {
1033 dump_data(0, (unsigned char *)uni_reqpath
,
1037 uni_reqpathoffset1
= REFERRAL_HEADER_SIZE
+
1038 VERSION3_REFERRAL_SIZE
* junction
->referral_count
;
1039 uni_reqpathoffset2
= uni_reqpathoffset1
+ reqpathlen
;
1040 reply_size
= uni_curroffset
= uni_reqpathoffset2
+ reqpathlen
;
1042 for(i
=0;i
<junction
->referral_count
;i
++) {
1043 DEBUG(10,("referral %u : %s\n",
1045 junction
->referral_list
[i
].alternate_path
));
1047 (strlen(junction
->referral_list
[i
].alternate_path
)+1)*2;
1050 pdata
= (char *)SMB_REALLOC(pdata
,reply_size
);
1052 DEBUG(0,("version3 referral setup:"
1053 "malloc failed for Realloc!\n"));
1058 /* create the header */
1059 SSVAL(pdata
,0,consumedcnt
* 2); /* path consumed */
1060 SSVAL(pdata
,2,junction
->referral_count
); /* number of referral */
1062 SIVAL(pdata
,4,DFSREF_REFERRAL_SERVER
| DFSREF_STORAGE_SERVER
);
1064 SIVAL(pdata
,4,DFSREF_STORAGE_SERVER
);
1067 /* copy in the reqpaths */
1068 memcpy(pdata
+uni_reqpathoffset1
,uni_reqpath
,reqpathlen
);
1069 memcpy(pdata
+uni_reqpathoffset2
,uni_reqpath
,reqpathlen
);
1072 for(i
=0;i
<junction
->referral_count
;i
++) {
1073 struct referral
* ref
= &(junction
->referral_list
[i
]);
1076 SSVAL(pdata
,offset
,3); /* version 3 */
1077 SSVAL(pdata
,offset
+2,VERSION3_REFERRAL_SIZE
);
1079 SSVAL(pdata
,offset
+4,1);
1081 SSVAL(pdata
,offset
+4,0);
1084 /* ref_flags :use path_consumed bytes? */
1085 SSVAL(pdata
,offset
+6,0);
1086 SIVAL(pdata
,offset
+8,ref
->ttl
);
1088 SSVAL(pdata
,offset
+12,uni_reqpathoffset1
-offset
);
1089 SSVAL(pdata
,offset
+14,uni_reqpathoffset2
-offset
);
1090 /* copy referred path into current offset */
1091 unilen
= rpcstr_push(pdata
+uni_curroffset
,ref
->alternate_path
,
1092 reply_size
- uni_curroffset
,
1093 STR_UNICODE
| STR_TERMINATE
);
1094 SSVAL(pdata
,offset
+16,uni_curroffset
-offset
);
1095 /* copy 0x10 bytes of 00's in the ServiceSite GUID */
1096 memset(pdata
+offset
+18,'\0',16);
1098 uni_curroffset
+= unilen
;
1099 offset
+= VERSION3_REFERRAL_SIZE
;
1104 /******************************************************************
1105 Set up the DFS referral for the dfs pathname. This call returns
1106 the amount of the path covered by this server, and where the
1107 client should be redirected to. This is the meat of the
1108 TRANS2_GET_DFS_REFERRAL call.
1109 ******************************************************************/
1111 int setup_dfs_referral(connection_struct
*orig_conn
,
1112 const char *dfs_path
,
1113 int max_referral_level
,
1114 char **ppdata
, NTSTATUS
*pstatus
)
1116 struct junction_map
*junction
= NULL
;
1117 int consumedcnt
= 0;
1118 bool self_referral
= False
;
1120 char *pathnamep
= NULL
;
1121 char *local_dfs_path
= NULL
;
1124 if (!(ctx
=talloc_init("setup_dfs_referral"))) {
1125 *pstatus
= NT_STATUS_NO_MEMORY
;
1129 /* get the junction entry */
1131 talloc_destroy(ctx
);
1132 *pstatus
= NT_STATUS_NOT_FOUND
;
1137 * Trim pathname sent by client so it begins with only one backslash.
1138 * Two backslashes confuse some dfs clients
1141 local_dfs_path
= talloc_strdup(ctx
,dfs_path
);
1142 if (!local_dfs_path
) {
1143 *pstatus
= NT_STATUS_NO_MEMORY
;
1144 talloc_destroy(ctx
);
1147 pathnamep
= local_dfs_path
;
1148 while (IS_DIRECTORY_SEP(pathnamep
[0]) &&
1149 IS_DIRECTORY_SEP(pathnamep
[1])) {
1153 junction
= TALLOC_ZERO_P(ctx
, struct junction_map
);
1155 *pstatus
= NT_STATUS_NO_MEMORY
;
1156 talloc_destroy(ctx
);
1160 /* The following call can change cwd. */
1161 *pstatus
= get_referred_path(ctx
, pathnamep
, junction
,
1162 &consumedcnt
, &self_referral
);
1163 if (!NT_STATUS_IS_OK(*pstatus
)) {
1164 vfs_ChDir(orig_conn
,orig_conn
->connectpath
);
1165 talloc_destroy(ctx
);
1168 vfs_ChDir(orig_conn
,orig_conn
->connectpath
);
1170 if (!self_referral
) {
1171 pathnamep
[consumedcnt
] = '\0';
1173 if( DEBUGLVL( 3 ) ) {
1175 dbgtext("setup_dfs_referral: Path %s to "
1176 "alternate path(s):",
1178 for(i
=0;i
<junction
->referral_count
;i
++)
1180 junction
->referral_list
[i
].alternate_path
);
1185 /* create the referral depeding on version */
1186 DEBUG(10,("max_referral_level :%d\n",max_referral_level
));
1188 if (max_referral_level
< 2) {
1189 max_referral_level
= 2;
1191 if (max_referral_level
> 3) {
1192 max_referral_level
= 3;
1195 switch(max_referral_level
) {
1197 reply_size
= setup_ver2_dfs_referral(pathnamep
,
1199 consumedcnt
, self_referral
);
1202 reply_size
= setup_ver3_dfs_referral(pathnamep
, ppdata
,
1203 junction
, consumedcnt
, self_referral
);
1206 DEBUG(0,("setup_dfs_referral: Invalid dfs referral "
1208 max_referral_level
));
1209 talloc_destroy(ctx
);
1210 *pstatus
= NT_STATUS_INVALID_LEVEL
;
1215 DEBUGADD(0,("DFS Referral pdata:\n"));
1216 dump_data(0,(uint8
*)*ppdata
,reply_size
);
1219 talloc_destroy(ctx
);
1220 *pstatus
= NT_STATUS_OK
;
1224 /**********************************************************************
1225 The following functions are called by the NETDFS RPC pipe functions
1226 **********************************************************************/
1228 /*********************************************************************
1229 Creates a junction structure from a DFS pathname
1230 **********************************************************************/
1232 bool create_junction(TALLOC_CTX
*ctx
,
1233 const char *dfs_path
,
1234 struct junction_map
*jucn
)
1238 struct dfs_path
*pdp
= TALLOC_P(ctx
,struct dfs_path
);
1244 status
= parse_dfs_path(NULL
, dfs_path
, False
, pdp
, &dummy
);
1245 if (!NT_STATUS_IS_OK(status
)) {
1249 /* check if path is dfs : validate first token */
1250 if (!is_myname_or_ipaddr(pdp
->hostname
)) {
1251 DEBUG(4,("create_junction: Invalid hostname %s "
1253 pdp
->hostname
, dfs_path
));
1258 /* Check for a non-DFS share */
1259 snum
= lp_servicenumber(pdp
->servicename
);
1261 if(snum
< 0 || !lp_msdfs_root(snum
)) {
1262 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1268 jucn
->service_name
= talloc_strdup(ctx
, pdp
->servicename
);
1269 jucn
->volume_name
= talloc_strdup(ctx
, pdp
->reqpath
);
1270 jucn
->comment
= talloc_strdup(ctx
, lp_comment(snum
));
1273 if (!jucn
->service_name
|| !jucn
->volume_name
|| ! jucn
->comment
) {
1279 /**********************************************************************
1280 Forms a valid Unix pathname from the junction
1281 **********************************************************************/
1283 static bool junction_to_local_path(const struct junction_map
*jucn
,
1285 connection_struct
*conn_out
)
1289 snum
= lp_servicenumber(jucn
->service_name
);
1293 if (!NT_STATUS_IS_OK(create_conn_struct(talloc_tos(),
1295 lp_pathname(snum
)))) {
1299 *pp_path_out
= talloc_asprintf(conn_out
->mem_ctx
,
1303 if (!*pp_path_out
) {
1309 bool create_msdfs_link(const struct junction_map
*jucn
,
1313 char *msdfs_link
= NULL
;
1314 connection_struct conns
;
1315 connection_struct
*conn
= &conns
;
1317 bool insert_comma
= False
;
1322 if(!junction_to_local_path(jucn
, &path
, conn
)) {
1326 /* Form the msdfs_link contents */
1327 msdfs_link
= talloc_strdup(conn
->mem_ctx
, "msdfs:");
1331 for(i
=0; i
<jucn
->referral_count
; i
++) {
1332 char *refpath
= jucn
->referral_list
[i
].alternate_path
;
1334 /* Alternate paths always use Windows separators. */
1335 trim_char(refpath
, '\\', '\\');
1336 if(*refpath
== '\0') {
1338 insert_comma
= False
;
1342 if (i
> 0 && insert_comma
) {
1343 msdfs_link
= talloc_asprintf_append_buffer(msdfs_link
,
1347 msdfs_link
= talloc_asprintf_append_buffer(msdfs_link
,
1355 if (!insert_comma
) {
1356 insert_comma
= True
;
1360 DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
1364 if(SMB_VFS_UNLINK(conn
,path
)!=0) {
1369 if(SMB_VFS_SYMLINK(conn
, msdfs_link
, path
) < 0) {
1370 DEBUG(1,("create_msdfs_link: symlink failed "
1371 "%s -> %s\nError: %s\n",
1372 path
, msdfs_link
, strerror(errno
)));
1380 conn_free_internal(conn
);
1384 bool remove_msdfs_link(const struct junction_map
*jucn
)
1387 connection_struct conns
;
1388 connection_struct
*conn
= &conns
;
1393 if( junction_to_local_path(jucn
, &path
, conn
) ) {
1394 if( SMB_VFS_UNLINK(conn
, path
) == 0 ) {
1399 conn_free_internal(conn
);
1403 /*********************************************************************
1404 Return the number of DFS links at the root of this share.
1405 *********************************************************************/
1407 static int count_dfs_links(TALLOC_CTX
*ctx
, int snum
)
1410 SMB_STRUCT_DIR
*dirp
= NULL
;
1412 const char *connect_path
= lp_pathname(snum
);
1413 const char *msdfs_proxy
= lp_msdfs_proxy(snum
);
1414 connection_struct conn
;
1418 if(*connect_path
== '\0') {
1423 * Fake up a connection struct for the VFS layer.
1426 if (!NT_STATUS_IS_OK(create_conn_struct(talloc_tos(),
1427 &conn
, snum
, connect_path
))) {
1431 /* Count a link for the msdfs root - convention */
1434 /* No more links if this is an msdfs proxy. */
1435 if (*msdfs_proxy
!= '\0') {
1439 /* Now enumerate all dfs links */
1440 dirp
= SMB_VFS_OPENDIR(&conn
, ".", NULL
, 0);
1445 while ((dname
= vfs_readdirname(&conn
, dirp
)) != NULL
) {
1446 if (is_msdfs_link(&conn
,
1453 SMB_VFS_CLOSEDIR(&conn
,dirp
);
1457 conn_free_internal(&conn
);
1461 /*********************************************************************
1462 *********************************************************************/
1464 static int form_junctions(TALLOC_CTX
*ctx
,
1466 struct junction_map
*jucn
,
1470 SMB_STRUCT_DIR
*dirp
= NULL
;
1472 const char *connect_path
= lp_pathname(snum
);
1473 char *service_name
= lp_servicename(snum
);
1474 const char *msdfs_proxy
= lp_msdfs_proxy(snum
);
1475 connection_struct conn
;
1476 struct referral
*ref
= NULL
;
1480 if (jn_remain
== 0) {
1484 if(*connect_path
== '\0') {
1489 * Fake up a connection struct for the VFS layer.
1492 if (!NT_STATUS_IS_OK(create_conn_struct(ctx
, &conn
, snum
, connect_path
))) {
1496 /* form a junction for the msdfs root - convention
1497 DO NOT REMOVE THIS: NT clients will not work with us
1498 if this is not present
1500 jucn
[cnt
].service_name
= talloc_strdup(ctx
,service_name
);
1501 jucn
[cnt
].volume_name
= talloc_strdup(ctx
, "");
1502 if (!jucn
[cnt
].service_name
|| !jucn
[cnt
].volume_name
) {
1505 jucn
[cnt
].referral_count
= 1;
1507 ref
= jucn
[cnt
].referral_list
= TALLOC_ZERO_P(ctx
, struct referral
);
1508 if (jucn
[cnt
].referral_list
== NULL
) {
1513 ref
->ttl
= REFERRAL_TTL
;
1514 if (*msdfs_proxy
!= '\0') {
1515 ref
->alternate_path
= talloc_strdup(ctx
,
1518 ref
->alternate_path
= talloc_asprintf(ctx
,
1520 get_local_machine_name(),
1524 if (!ref
->alternate_path
) {
1529 /* Don't enumerate if we're an msdfs proxy. */
1530 if (*msdfs_proxy
!= '\0') {
1534 /* Now enumerate all dfs links */
1535 dirp
= SMB_VFS_OPENDIR(&conn
, ".", NULL
, 0);
1540 while ((dname
= vfs_readdirname(&conn
, dirp
)) != NULL
) {
1541 char *link_target
= NULL
;
1542 if (cnt
>= jn_remain
) {
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
);