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 This version does everything using pointers within one copy of the
40 pathname string, talloced on the struct dfs_path pointer (which
41 must be talloced). This may be too clever to live....
43 **********************************************************************/
45 static NTSTATUS
parse_dfs_path(const char *pathname
,
47 struct dfs_path
*pdp
, /* MUST BE TALLOCED */
48 bool *ppath_contains_wcard
)
53 NTSTATUS status
= NT_STATUS_OK
;
59 * This is the only talloc we should need to do
60 * on the struct dfs_path. All the pointers inside
61 * it should point to offsets within this string.
64 pathname_local
= talloc_strdup(pdp
, pathname
);
65 if (!pathname_local
) {
66 return NT_STATUS_NO_MEMORY
;
68 /* Get a pointer to the terminating '\0' */
69 eos_ptr
= &pathname_local
[strlen(pathname_local
)];
70 p
= temp
= pathname_local
;
72 pdp
->posix_path
= (lp_posix_pathnames() && *pathname
== '/');
74 sepchar
= pdp
->posix_path
? '/' : '\\';
76 if (*pathname
!= sepchar
) {
77 DEBUG(10,("parse_dfs_path: path %s doesn't start with %c\n",
80 * Possibly client sent a local path by mistake.
81 * Try and convert to a local path.
84 pdp
->hostname
= eos_ptr
; /* "" */
85 pdp
->servicename
= eos_ptr
; /* "" */
87 /* We've got no info about separators. */
88 pdp
->posix_path
= lp_posix_pathnames();
90 DEBUG(10,("parse_dfs_path: trying to convert %s to a "
97 * Safe to use on talloc'ed string as it only shrinks.
98 * It also doesn't affect the eos_ptr.
100 trim_char(temp
,sepchar
,sepchar
);
102 DEBUG(10,("parse_dfs_path: temp = |%s| after trimming %c's\n",
106 /* Parse out hostname. */
107 p
= strchr_m(temp
,sepchar
);
109 DEBUG(10,("parse_dfs_path: can't parse hostname from path %s\n",
112 * Possibly client sent a local path by mistake.
113 * Try and convert to a local path.
116 pdp
->hostname
= eos_ptr
; /* "" */
117 pdp
->servicename
= eos_ptr
; /* "" */
120 DEBUG(10,("parse_dfs_path: trying to convert %s "
126 pdp
->hostname
= temp
;
128 DEBUG(10,("parse_dfs_path: hostname: %s\n",pdp
->hostname
));
130 /* If we got a hostname, is it ours (or an IP address) ? */
131 if (!is_myname_or_ipaddr(pdp
->hostname
)) {
134 DEBUG(10,("parse_dfs_path: hostname %s isn't ours. "
135 "Try local path from path %s\n",
136 pdp
->hostname
, temp
));
138 * Possibly client sent a local path by mistake.
139 * Try and convert to a local path.
142 pdp
->hostname
= eos_ptr
; /* "" */
143 pdp
->servicename
= eos_ptr
; /* "" */
146 DEBUG(10,("parse_dfs_path: trying to convert %s "
152 /* Parse out servicename. */
154 p
= strchr_m(temp
,sepchar
);
156 pdp
->servicename
= temp
;
157 pdp
->reqpath
= eos_ptr
; /* "" */
161 pdp
->servicename
= temp
;
162 DEBUG(10,("parse_dfs_path: servicename: %s\n",pdp
->servicename
));
168 *ppath_contains_wcard
= False
;
172 /* Rest is reqpath. */
173 if (pdp
->posix_path
) {
174 status
= check_path_syntax_posix(pdp
->reqpath
);
177 status
= check_path_syntax_wcard(pdp
->reqpath
,
178 ppath_contains_wcard
);
180 status
= check_path_syntax(pdp
->reqpath
);
184 if (!NT_STATUS_IS_OK(status
)) {
185 DEBUG(10,("parse_dfs_path: '%s' failed with %s\n",
186 p
, nt_errstr(status
) ));
190 DEBUG(10,("parse_dfs_path: rest of the path: %s\n",pdp
->reqpath
));
194 /********************************************************
195 Fake up a connection struct for the VFS layer.
196 Note this CHANGES CWD !!!! JRA.
197 *********************************************************/
199 static NTSTATUS
create_conn_struct(TALLOC_CTX
*ctx
,
200 connection_struct
*conn
,
208 connpath
= talloc_strdup(ctx
, path
);
210 return NT_STATUS_NO_MEMORY
;
212 connpath
= talloc_string_sub(ctx
,
215 lp_servicename(snum
));
217 return NT_STATUS_NO_MEMORY
;
220 /* needed for smbd_vfs_init() */
222 if ((conn
->mem_ctx
=talloc_init("connection_struct")) == NULL
) {
223 DEBUG(0,("talloc_init(connection_struct) failed!\n"));
224 return NT_STATUS_NO_MEMORY
;
227 if (!(conn
->params
= TALLOC_ZERO_P(conn
->mem_ctx
,
228 struct share_params
))) {
229 DEBUG(0, ("TALLOC failed\n"));
230 return NT_STATUS_NO_MEMORY
;
233 conn
->params
->service
= snum
;
235 set_conn_connectpath(conn
, connpath
);
237 if (!smbd_vfs_init(conn
)) {
238 NTSTATUS status
= map_nt_error_from_unix(errno
);
239 DEBUG(0,("create_conn_struct: smbd_vfs_init failed.\n"));
240 conn_free_internal(conn
);
245 * Windows seems to insist on doing trans2getdfsreferral() calls on
246 * the IPC$ share as the anonymous user. If we try to chdir as that
247 * user we will fail.... WTF ? JRA.
250 if (vfs_ChDir(conn
,conn
->connectpath
) != 0) {
251 NTSTATUS status
= map_nt_error_from_unix(errno
);
252 DEBUG(3,("create_conn_struct: Can't ChDir to new conn path %s. "
254 conn
->connectpath
, strerror(errno
) ));
255 conn_free_internal(conn
);
262 /**********************************************************************
263 Parse the contents of a symlink to verify if it is an msdfs referral
264 A valid referral is of the form:
266 msdfs:server1\share1,server2\share2
267 msdfs:server1\share1\pathname,server2\share2\pathname
268 msdfs:server1/share1,server2/share2
269 msdfs:server1/share1/pathname,server2/share2/pathname.
271 Note that the alternate paths returned here must be of the canonicalized
275 \server\share\path\to\file,
277 even in posix path mode. This is because we have no knowledge if the
278 server we're referring to understands posix paths.
279 **********************************************************************/
281 static bool parse_msdfs_symlink(TALLOC_CTX
*ctx
,
283 struct referral
**preflist
,
288 char **alt_path
= NULL
;
290 struct referral
*reflist
;
293 temp
= talloc_strdup(ctx
, target
);
297 prot
= strtok_r(temp
, ":", &saveptr
);
299 DEBUG(0,("parse_msdfs_symlink: invalid path !\n"));
303 alt_path
= TALLOC_ARRAY(ctx
, char *, MAX_REFERRAL_COUNT
);
308 /* parse out the alternate paths */
309 while((count
<MAX_REFERRAL_COUNT
) &&
310 ((alt_path
[count
] = strtok_r(NULL
, ",", &saveptr
)) != NULL
)) {
314 DEBUG(10,("parse_msdfs_symlink: count=%d\n", count
));
317 reflist
= *preflist
= TALLOC_ZERO_ARRAY(ctx
,
318 struct referral
, count
);
319 if(reflist
== NULL
) {
320 TALLOC_FREE(alt_path
);
324 reflist
= *preflist
= NULL
;
327 for(i
=0;i
<count
;i
++) {
330 /* Canonicalize link target.
331 * Replace all /'s in the path by a \ */
332 string_replace(alt_path
[i
], '/', '\\');
334 /* Remove leading '\\'s */
336 while (*p
&& (*p
== '\\')) {
340 reflist
[i
].alternate_path
= talloc_asprintf(ctx
,
343 if (!reflist
[i
].alternate_path
) {
347 reflist
[i
].proximity
= 0;
348 reflist
[i
].ttl
= REFERRAL_TTL
;
349 DEBUG(10, ("parse_msdfs_symlink: Created alt path: %s\n",
350 reflist
[i
].alternate_path
));
354 TALLOC_FREE(alt_path
);
358 /**********************************************************************
359 Returns true if the unix path is a valid msdfs symlink and also
360 returns the target string from inside the link.
361 **********************************************************************/
363 static bool is_msdfs_link_internal(TALLOC_CTX
*ctx
,
364 connection_struct
*conn
,
366 char **pp_link_target
,
367 SMB_STRUCT_STAT
*sbufp
)
370 int referral_len
= 0;
371 char link_target_buf
[7];
373 char *link_target
= NULL
;
375 if (pp_link_target
) {
377 link_target
= TALLOC_ARRAY(ctx
, char, bufsize
);
381 *pp_link_target
= link_target
;
383 bufsize
= sizeof(link_target_buf
);
384 link_target
= link_target_buf
;
391 if (SMB_VFS_LSTAT(conn
, path
, sbufp
) != 0) {
392 DEBUG(5,("is_msdfs_link_read_target: %s does not exist.\n",
397 if (!S_ISLNK(sbufp
->st_mode
)) {
398 DEBUG(5,("is_msdfs_link_read_target: %s is not a link.\n",
403 referral_len
= SMB_VFS_READLINK(conn
, path
, link_target
, bufsize
- 1);
404 if (referral_len
== -1) {
405 DEBUG(0,("is_msdfs_link_read_target: Error reading "
406 "msdfs link %s: %s\n",
407 path
, strerror(errno
)));
410 link_target
[referral_len
] = '\0';
412 DEBUG(5,("is_msdfs_link_internal: %s -> %s\n",path
,
415 if (!strnequal(link_target
, "msdfs:", 6)) {
422 if (link_target
!= link_target_buf
) {
423 TALLOC_FREE(link_target
);
428 /**********************************************************************
429 Returns true if the unix path is a valid msdfs symlink.
430 **********************************************************************/
432 bool is_msdfs_link(connection_struct
*conn
,
434 SMB_STRUCT_STAT
*sbufp
)
436 return is_msdfs_link_internal(talloc_tos(),
443 /*****************************************************************
444 Used by other functions to decide if a dfs path is remote,
445 and to get the list of referred locations for that remote path.
447 search_flag: For findfirsts, dfs links themselves are not
448 redirected, but paths beyond the links are. For normal smb calls,
449 even dfs links need to be redirected.
451 consumedcntp: how much of the dfs path is being redirected. the client
452 should try the remaining path on the redirected server.
454 If this returns NT_STATUS_PATH_NOT_COVERED the contents of the msdfs
455 link redirect are in targetpath.
456 *****************************************************************/
458 static NTSTATUS
dfs_path_lookup(TALLOC_CTX
*ctx
,
459 connection_struct
*conn
,
460 const char *dfspath
, /* Incoming complete dfs path */
461 const struct dfs_path
*pdp
, /* Parsed out
462 server+share+extrapath. */
463 bool search_flag
, /* Called from a findfirst ? */
465 char **pp_targetpath
)
469 SMB_STRUCT_STAT sbuf
;
471 char *localpath
= NULL
;
472 char *canon_dfspath
= NULL
; /* Canonicalized dfs path. (only '/'
475 DEBUG(10,("dfs_path_lookup: Conn path = %s reqpath = %s\n",
476 conn
->connectpath
, pdp
->reqpath
));
479 * Note the unix path conversion here we're doing we can
480 * throw away. We're looking for a symlink for a dfs
481 * resolution, if we don't find it we'll do another
482 * unix_convert later in the codepath.
483 * If we needed to remember what we'd resolved in
484 * dp->reqpath (as the original code did) we'd
485 * copy (localhost, dp->reqpath) on any code
486 * path below that returns True - but I don't
487 * think this is needed. JRA.
490 status
= unix_convert(ctx
, conn
, pdp
->reqpath
, search_flag
, &localpath
,
492 if (!NT_STATUS_IS_OK(status
) && !NT_STATUS_EQUAL(status
,
493 NT_STATUS_OBJECT_PATH_NOT_FOUND
)) {
497 /* Optimization - check if we can redirect the whole path. */
499 if (is_msdfs_link_internal(ctx
, conn
, localpath
, pp_targetpath
, NULL
)) {
501 DEBUG(6,("dfs_path_lookup (FindFirst) No redirection "
502 "for dfs link %s.\n", dfspath
));
506 DEBUG(6,("dfs_path_lookup: %s resolves to a "
507 "valid dfs link %s.\n", dfspath
,
508 pp_targetpath
? *pp_targetpath
: ""));
511 *consumedcntp
= strlen(dfspath
);
513 return NT_STATUS_PATH_NOT_COVERED
;
516 /* Prepare to test only for '/' components in the given path,
517 * so if a Windows path replace all '\\' characters with '/'.
518 * For a POSIX DFS path we know all separators are already '/'. */
520 canon_dfspath
= talloc_strdup(ctx
, dfspath
);
521 if (!canon_dfspath
) {
522 return NT_STATUS_NO_MEMORY
;
524 if (!pdp
->posix_path
) {
525 string_replace(canon_dfspath
, '\\', '/');
529 * localpath comes out of unix_convert, so it has
530 * no trailing backslash. Make sure that canon_dfspath hasn't either.
531 * Fix for bug #4860 from Jan Martin <Jan.Martin@rwedea.com>.
534 trim_char(canon_dfspath
,0,'/');
537 * Redirect if any component in the path is a link.
538 * We do this by walking backwards through the
539 * local path, chopping off the last component
540 * in both the local path and the canonicalized
541 * DFS path. If we hit a DFS link then we're done.
544 p
= strrchr_m(localpath
, '/');
546 q
= strrchr_m(canon_dfspath
, '/');
555 if (is_msdfs_link_internal(ctx
, conn
,
556 localpath
, pp_targetpath
, NULL
)) {
557 DEBUG(4, ("dfs_path_lookup: Redirecting %s because "
558 "parent %s is dfs link\n", dfspath
, localpath
));
561 *consumedcntp
= strlen(canon_dfspath
);
562 DEBUG(10, ("dfs_path_lookup: Path consumed: %s "
568 return NT_STATUS_PATH_NOT_COVERED
;
571 /* Step back on the filesystem. */
572 p
= strrchr_m(localpath
, '/');
575 /* And in the canonicalized dfs path. */
576 q
= strrchr_m(canon_dfspath
, '/');
583 /*****************************************************************
584 Decides if a dfs pathname should be redirected or not.
585 If not, the pathname is converted to a tcon-relative local unix path
587 search_wcard_flag: this flag performs 2 functions both related
588 to searches. See resolve_dfs_path() and parse_dfs_path_XX()
591 This function can return NT_STATUS_OK, meaning use the returned path as-is
592 (mapped into a local path).
593 or NT_STATUS_NOT_COVERED meaning return a DFS redirect, or
594 any other NT_STATUS error which is a genuine error to be
595 returned to the client.
596 *****************************************************************/
598 static NTSTATUS
dfs_redirect(TALLOC_CTX
*ctx
,
599 connection_struct
*conn
,
601 bool search_wcard_flag
,
603 bool *ppath_contains_wcard
)
606 struct dfs_path
*pdp
= TALLOC_P(ctx
, struct dfs_path
);
609 return NT_STATUS_NO_MEMORY
;
612 status
= parse_dfs_path(path_in
, search_wcard_flag
, pdp
,
613 ppath_contains_wcard
);
614 if (!NT_STATUS_IS_OK(status
)) {
619 if (pdp
->reqpath
[0] == '\0') {
621 *pp_path_out
= talloc_strdup(ctx
, "");
623 return NT_STATUS_NO_MEMORY
;
625 DEBUG(5,("dfs_redirect: self-referral.\n"));
629 /* If dfs pathname for a non-dfs share, convert to tcon-relative
630 path and return OK */
632 if (!lp_msdfs_root(SNUM(conn
))) {
633 *pp_path_out
= talloc_strdup(ctx
, pdp
->reqpath
);
636 return NT_STATUS_NO_MEMORY
;
641 /* If it looked like a local path (zero hostname/servicename)
642 * just treat as a tcon-relative path. */
644 if (pdp
->hostname
[0] == '\0' && pdp
->servicename
[0] == '\0') {
645 *pp_path_out
= talloc_strdup(ctx
, pdp
->reqpath
);
648 return NT_STATUS_NO_MEMORY
;
653 if (!( strequal(pdp
->servicename
, lp_servicename(SNUM(conn
)))
654 || (strequal(pdp
->servicename
, HOMES_NAME
)
655 && strequal(lp_servicename(SNUM(conn
)),
656 get_current_username()) )) ) {
658 /* The given sharename doesn't match this connection. */
661 return NT_STATUS_OBJECT_PATH_NOT_FOUND
;
664 status
= dfs_path_lookup(ctx
, conn
, path_in
, pdp
,
665 search_wcard_flag
, NULL
, NULL
);
666 if (!NT_STATUS_IS_OK(status
)) {
667 if (NT_STATUS_EQUAL(status
, NT_STATUS_PATH_NOT_COVERED
)) {
668 DEBUG(3,("dfs_redirect: Redirecting %s\n", path_in
));
670 DEBUG(10,("dfs_redirect: dfs_path_lookup "
671 "failed for %s with %s\n",
672 path_in
, nt_errstr(status
) ));
677 DEBUG(3,("dfs_redirect: Not redirecting %s.\n", path_in
));
679 /* Form non-dfs tcon-relative path */
680 *pp_path_out
= talloc_strdup(ctx
, pdp
->reqpath
);
683 return NT_STATUS_NO_MEMORY
;
686 DEBUG(3,("dfs_redirect: Path %s converted to non-dfs path %s\n",
693 /**********************************************************************
694 Return a self referral.
695 **********************************************************************/
697 static NTSTATUS
self_ref(TALLOC_CTX
*ctx
,
698 const char *dfs_path
,
699 struct junction_map
*jucn
,
701 bool *self_referralp
)
703 struct referral
*ref
;
705 *self_referralp
= True
;
707 jucn
->referral_count
= 1;
708 if((ref
= TALLOC_ZERO_P(ctx
, struct referral
)) == NULL
) {
709 return NT_STATUS_NO_MEMORY
;
712 ref
->alternate_path
= talloc_strdup(ctx
, dfs_path
);
713 if (!ref
->alternate_path
) {
714 return NT_STATUS_NO_MEMORY
;
717 ref
->ttl
= REFERRAL_TTL
;
718 jucn
->referral_list
= ref
;
719 *consumedcntp
= strlen(dfs_path
);
723 /**********************************************************************
724 Gets valid referrals for a dfs path and fills up the
725 junction_map structure.
726 **********************************************************************/
728 NTSTATUS
get_referred_path(TALLOC_CTX
*ctx
,
729 const char *dfs_path
,
730 struct junction_map
*jucn
,
732 bool *self_referralp
)
734 struct connection_struct conns
;
735 struct connection_struct
*conn
= &conns
;
736 char *targetpath
= NULL
;
738 NTSTATUS status
= NT_STATUS_NOT_FOUND
;
740 struct dfs_path
*pdp
= TALLOC_P(ctx
, struct dfs_path
);
743 return NT_STATUS_NO_MEMORY
;
747 *self_referralp
= False
;
749 status
= parse_dfs_path(dfs_path
, False
, pdp
, &dummy
);
750 if (!NT_STATUS_IS_OK(status
)) {
754 /* Verify hostname in path */
755 if (!is_myname_or_ipaddr(pdp
->hostname
)) {
756 DEBUG(3, ("get_referred_path: Invalid hostname %s in path %s\n",
757 pdp
->hostname
, dfs_path
));
759 return NT_STATUS_NOT_FOUND
;
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(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
);