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 /* Parse out servicename. */
132 p
= strchr_m(temp
,sepchar
);
134 pdp
->servicename
= temp
;
135 pdp
->reqpath
= eos_ptr
; /* "" */
139 pdp
->servicename
= temp
;
140 DEBUG(10,("parse_dfs_path: servicename: %s\n",pdp
->servicename
));
146 *ppath_contains_wcard
= False
;
150 /* Rest is reqpath. */
151 if (pdp
->posix_path
) {
152 status
= check_path_syntax_posix(pdp
->reqpath
);
155 status
= check_path_syntax_wcard(pdp
->reqpath
,
156 ppath_contains_wcard
);
158 status
= check_path_syntax(pdp
->reqpath
);
162 if (!NT_STATUS_IS_OK(status
)) {
163 DEBUG(10,("parse_dfs_path: '%s' failed with %s\n",
164 p
, nt_errstr(status
) ));
168 DEBUG(10,("parse_dfs_path: rest of the path: %s\n",pdp
->reqpath
));
172 /********************************************************
173 Fake up a connection struct for the VFS layer.
174 Note this CHANGES CWD !!!! JRA.
175 *********************************************************/
177 static NTSTATUS
create_conn_struct(TALLOC_CTX
*ctx
,
178 connection_struct
*conn
,
186 connpath
= talloc_strdup(ctx
, path
);
188 return NT_STATUS_NO_MEMORY
;
190 connpath
= talloc_string_sub(ctx
,
193 lp_servicename(snum
));
195 return NT_STATUS_NO_MEMORY
;
198 /* needed for smbd_vfs_init() */
200 if ((conn
->mem_ctx
=talloc_init("connection_struct")) == NULL
) {
201 DEBUG(0,("talloc_init(connection_struct) failed!\n"));
202 return NT_STATUS_NO_MEMORY
;
205 if (!(conn
->params
= TALLOC_ZERO_P(conn
->mem_ctx
,
206 struct share_params
))) {
207 DEBUG(0, ("TALLOC failed\n"));
208 return NT_STATUS_NO_MEMORY
;
211 conn
->params
->service
= snum
;
213 set_conn_connectpath(conn
, connpath
);
215 if (!smbd_vfs_init(conn
)) {
216 NTSTATUS status
= map_nt_error_from_unix(errno
);
217 DEBUG(0,("create_conn_struct: smbd_vfs_init failed.\n"));
218 conn_free_internal(conn
);
223 * Windows seems to insist on doing trans2getdfsreferral() calls on
224 * the IPC$ share as the anonymous user. If we try to chdir as that
225 * user we will fail.... WTF ? JRA.
228 if (vfs_ChDir(conn
,conn
->connectpath
) != 0) {
229 NTSTATUS status
= map_nt_error_from_unix(errno
);
230 DEBUG(3,("create_conn_struct: Can't ChDir to new conn path %s. "
232 conn
->connectpath
, strerror(errno
) ));
233 conn_free_internal(conn
);
240 /**********************************************************************
241 Parse the contents of a symlink to verify if it is an msdfs referral
242 A valid referral is of the form:
244 msdfs:server1\share1,server2\share2
245 msdfs:server1\share1\pathname,server2\share2\pathname
246 msdfs:server1/share1,server2/share2
247 msdfs:server1/share1/pathname,server2/share2/pathname.
249 Note that the alternate paths returned here must be of the canonicalized
253 \server\share\path\to\file,
255 even in posix path mode. This is because we have no knowledge if the
256 server we're referring to understands posix paths.
257 **********************************************************************/
259 static bool parse_msdfs_symlink(TALLOC_CTX
*ctx
,
261 struct referral
**preflist
,
266 char **alt_path
= NULL
;
268 struct referral
*reflist
;
271 temp
= talloc_strdup(ctx
, target
);
275 prot
= strtok_r(temp
, ":", &saveptr
);
277 DEBUG(0,("parse_msdfs_symlink: invalid path !\n"));
281 alt_path
= TALLOC_ARRAY(ctx
, char *, MAX_REFERRAL_COUNT
);
286 /* parse out the alternate paths */
287 while((count
<MAX_REFERRAL_COUNT
) &&
288 ((alt_path
[count
] = strtok_r(NULL
, ",", &saveptr
)) != NULL
)) {
292 DEBUG(10,("parse_msdfs_symlink: count=%d\n", count
));
295 reflist
= *preflist
= TALLOC_ZERO_ARRAY(ctx
,
296 struct referral
, count
);
297 if(reflist
== NULL
) {
298 TALLOC_FREE(alt_path
);
302 reflist
= *preflist
= NULL
;
305 for(i
=0;i
<count
;i
++) {
308 /* Canonicalize link target.
309 * Replace all /'s in the path by a \ */
310 string_replace(alt_path
[i
], '/', '\\');
312 /* Remove leading '\\'s */
314 while (*p
&& (*p
== '\\')) {
318 reflist
[i
].alternate_path
= talloc_asprintf(ctx
,
321 if (!reflist
[i
].alternate_path
) {
325 reflist
[i
].proximity
= 0;
326 reflist
[i
].ttl
= REFERRAL_TTL
;
327 DEBUG(10, ("parse_msdfs_symlink: Created alt path: %s\n",
328 reflist
[i
].alternate_path
));
332 TALLOC_FREE(alt_path
);
336 /**********************************************************************
337 Returns true if the unix path is a valid msdfs symlink and also
338 returns the target string from inside the link.
339 **********************************************************************/
341 static bool is_msdfs_link_internal(TALLOC_CTX
*ctx
,
342 connection_struct
*conn
,
344 char **pp_link_target
,
345 SMB_STRUCT_STAT
*sbufp
)
348 int referral_len
= 0;
349 char link_target_buf
[7];
351 char *link_target
= NULL
;
353 if (pp_link_target
) {
355 link_target
= TALLOC_ARRAY(ctx
, char, bufsize
);
359 *pp_link_target
= link_target
;
361 bufsize
= sizeof(link_target_buf
);
362 link_target
= link_target_buf
;
369 if (SMB_VFS_LSTAT(conn
, path
, sbufp
) != 0) {
370 DEBUG(5,("is_msdfs_link_read_target: %s does not exist.\n",
375 if (!S_ISLNK(sbufp
->st_mode
)) {
376 DEBUG(5,("is_msdfs_link_read_target: %s is not a link.\n",
381 referral_len
= SMB_VFS_READLINK(conn
, path
, link_target
, bufsize
- 1);
382 if (referral_len
== -1) {
383 DEBUG(0,("is_msdfs_link_read_target: Error reading "
384 "msdfs link %s: %s\n",
385 path
, strerror(errno
)));
388 link_target
[referral_len
] = '\0';
390 DEBUG(5,("is_msdfs_link_internal: %s -> %s\n",path
,
393 if (!strnequal(link_target
, "msdfs:", 6)) {
400 if (link_target
!= link_target_buf
) {
401 TALLOC_FREE(link_target
);
406 /**********************************************************************
407 Returns true if the unix path is a valid msdfs symlink.
408 **********************************************************************/
410 bool is_msdfs_link(connection_struct
*conn
,
412 SMB_STRUCT_STAT
*sbufp
)
414 return is_msdfs_link_internal(talloc_tos(),
421 /*****************************************************************
422 Used by other functions to decide if a dfs path is remote,
423 and to get the list of referred locations for that remote path.
425 search_flag: For findfirsts, dfs links themselves are not
426 redirected, but paths beyond the links are. For normal smb calls,
427 even dfs links need to be redirected.
429 consumedcntp: how much of the dfs path is being redirected. the client
430 should try the remaining path on the redirected server.
432 If this returns NT_STATUS_PATH_NOT_COVERED the contents of the msdfs
433 link redirect are in targetpath.
434 *****************************************************************/
436 static NTSTATUS
dfs_path_lookup(TALLOC_CTX
*ctx
,
437 connection_struct
*conn
,
438 const char *dfspath
, /* Incoming complete dfs path */
439 const struct dfs_path
*pdp
, /* Parsed out
440 server+share+extrapath. */
441 bool search_flag
, /* Called from a findfirst ? */
443 char **pp_targetpath
)
447 SMB_STRUCT_STAT sbuf
;
449 char *localpath
= NULL
;
450 char *canon_dfspath
= NULL
; /* Canonicalized dfs path. (only '/'
453 DEBUG(10,("dfs_path_lookup: Conn path = %s reqpath = %s\n",
454 conn
->connectpath
, pdp
->reqpath
));
457 * Note the unix path conversion here we're doing we can
458 * throw away. We're looking for a symlink for a dfs
459 * resolution, if we don't find it we'll do another
460 * unix_convert later in the codepath.
461 * If we needed to remember what we'd resolved in
462 * dp->reqpath (as the original code did) we'd
463 * copy (localhost, dp->reqpath) on any code
464 * path below that returns True - but I don't
465 * think this is needed. JRA.
468 status
= unix_convert(ctx
, conn
, pdp
->reqpath
, search_flag
, &localpath
,
470 if (!NT_STATUS_IS_OK(status
) && !NT_STATUS_EQUAL(status
,
471 NT_STATUS_OBJECT_PATH_NOT_FOUND
)) {
475 /* Optimization - check if we can redirect the whole path. */
477 if (is_msdfs_link_internal(ctx
, conn
, localpath
, pp_targetpath
, NULL
)) {
479 DEBUG(6,("dfs_path_lookup (FindFirst) No redirection "
480 "for dfs link %s.\n", dfspath
));
484 DEBUG(6,("dfs_path_lookup: %s resolves to a "
485 "valid dfs link %s.\n", dfspath
,
486 pp_targetpath
? *pp_targetpath
: ""));
489 *consumedcntp
= strlen(dfspath
);
491 return NT_STATUS_PATH_NOT_COVERED
;
494 /* Prepare to test only for '/' components in the given path,
495 * so if a Windows path replace all '\\' characters with '/'.
496 * For a POSIX DFS path we know all separators are already '/'. */
498 canon_dfspath
= talloc_strdup(ctx
, dfspath
);
499 if (!canon_dfspath
) {
500 return NT_STATUS_NO_MEMORY
;
502 if (!pdp
->posix_path
) {
503 string_replace(canon_dfspath
, '\\', '/');
507 * localpath comes out of unix_convert, so it has
508 * no trailing backslash. Make sure that canon_dfspath hasn't either.
509 * Fix for bug #4860 from Jan Martin <Jan.Martin@rwedea.com>.
512 trim_char(canon_dfspath
,0,'/');
515 * Redirect if any component in the path is a link.
516 * We do this by walking backwards through the
517 * local path, chopping off the last component
518 * in both the local path and the canonicalized
519 * DFS path. If we hit a DFS link then we're done.
522 p
= strrchr_m(localpath
, '/');
524 q
= strrchr_m(canon_dfspath
, '/');
533 if (is_msdfs_link_internal(ctx
, conn
,
534 localpath
, pp_targetpath
, NULL
)) {
535 DEBUG(4, ("dfs_path_lookup: Redirecting %s because "
536 "parent %s is dfs link\n", dfspath
, localpath
));
539 *consumedcntp
= strlen(canon_dfspath
);
540 DEBUG(10, ("dfs_path_lookup: Path consumed: %s "
546 return NT_STATUS_PATH_NOT_COVERED
;
549 /* Step back on the filesystem. */
550 p
= strrchr_m(localpath
, '/');
553 /* And in the canonicalized dfs path. */
554 q
= strrchr_m(canon_dfspath
, '/');
561 /*****************************************************************
562 Decides if a dfs pathname should be redirected or not.
563 If not, the pathname is converted to a tcon-relative local unix path
565 search_wcard_flag: this flag performs 2 functions both related
566 to searches. See resolve_dfs_path() and parse_dfs_path_XX()
569 This function can return NT_STATUS_OK, meaning use the returned path as-is
570 (mapped into a local path).
571 or NT_STATUS_NOT_COVERED meaning return a DFS redirect, or
572 any other NT_STATUS error which is a genuine error to be
573 returned to the client.
574 *****************************************************************/
576 static NTSTATUS
dfs_redirect(TALLOC_CTX
*ctx
,
577 connection_struct
*conn
,
579 bool search_wcard_flag
,
581 bool *ppath_contains_wcard
)
584 struct dfs_path
*pdp
= TALLOC_P(ctx
, struct dfs_path
);
587 return NT_STATUS_NO_MEMORY
;
590 status
= parse_dfs_path(path_in
, search_wcard_flag
, pdp
,
591 ppath_contains_wcard
);
592 if (!NT_STATUS_IS_OK(status
)) {
597 if (pdp
->reqpath
[0] == '\0') {
599 *pp_path_out
= talloc_strdup(ctx
, "");
601 return NT_STATUS_NO_MEMORY
;
603 DEBUG(5,("dfs_redirect: self-referral.\n"));
607 /* If dfs pathname for a non-dfs share, convert to tcon-relative
608 path and return OK */
610 if (!lp_msdfs_root(SNUM(conn
))) {
611 *pp_path_out
= talloc_strdup(ctx
, pdp
->reqpath
);
614 return NT_STATUS_NO_MEMORY
;
619 /* If it looked like a local path (zero hostname/servicename)
620 * just treat as a tcon-relative path. */
622 if (pdp
->hostname
[0] == '\0' && pdp
->servicename
[0] == '\0') {
623 *pp_path_out
= talloc_strdup(ctx
, pdp
->reqpath
);
626 return NT_STATUS_NO_MEMORY
;
631 if (!( strequal(pdp
->servicename
, lp_servicename(SNUM(conn
)))
632 || (strequal(pdp
->servicename
, HOMES_NAME
)
633 && strequal(lp_servicename(SNUM(conn
)),
634 get_current_username()) )) ) {
636 /* The given sharename doesn't match this connection. */
639 return NT_STATUS_OBJECT_PATH_NOT_FOUND
;
642 status
= dfs_path_lookup(ctx
, conn
, path_in
, pdp
,
643 search_wcard_flag
, NULL
, NULL
);
644 if (!NT_STATUS_IS_OK(status
)) {
645 if (NT_STATUS_EQUAL(status
, NT_STATUS_PATH_NOT_COVERED
)) {
646 DEBUG(3,("dfs_redirect: Redirecting %s\n", path_in
));
648 DEBUG(10,("dfs_redirect: dfs_path_lookup "
649 "failed for %s with %s\n",
650 path_in
, nt_errstr(status
) ));
655 DEBUG(3,("dfs_redirect: Not redirecting %s.\n", path_in
));
657 /* Form non-dfs tcon-relative path */
658 *pp_path_out
= talloc_strdup(ctx
, pdp
->reqpath
);
661 return NT_STATUS_NO_MEMORY
;
664 DEBUG(3,("dfs_redirect: Path %s converted to non-dfs path %s\n",
671 /**********************************************************************
672 Return a self referral.
673 **********************************************************************/
675 static NTSTATUS
self_ref(TALLOC_CTX
*ctx
,
676 const char *dfs_path
,
677 struct junction_map
*jucn
,
679 bool *self_referralp
)
681 struct referral
*ref
;
683 *self_referralp
= True
;
685 jucn
->referral_count
= 1;
686 if((ref
= TALLOC_ZERO_P(ctx
, struct referral
)) == NULL
) {
687 return NT_STATUS_NO_MEMORY
;
690 ref
->alternate_path
= talloc_strdup(ctx
, dfs_path
);
691 if (!ref
->alternate_path
) {
692 return NT_STATUS_NO_MEMORY
;
695 ref
->ttl
= REFERRAL_TTL
;
696 jucn
->referral_list
= ref
;
697 *consumedcntp
= strlen(dfs_path
);
701 /**********************************************************************
702 Gets valid referrals for a dfs path and fills up the
703 junction_map structure.
704 **********************************************************************/
706 NTSTATUS
get_referred_path(TALLOC_CTX
*ctx
,
707 const char *dfs_path
,
708 struct junction_map
*jucn
,
710 bool *self_referralp
)
712 struct connection_struct conns
;
713 struct connection_struct
*conn
= &conns
;
714 char *targetpath
= NULL
;
716 NTSTATUS status
= NT_STATUS_NOT_FOUND
;
718 struct dfs_path
*pdp
= TALLOC_P(ctx
, struct dfs_path
);
721 return NT_STATUS_NO_MEMORY
;
725 *self_referralp
= False
;
727 status
= parse_dfs_path(dfs_path
, False
, pdp
, &dummy
);
728 if (!NT_STATUS_IS_OK(status
)) {
732 jucn
->service_name
= talloc_strdup(ctx
, pdp
->servicename
);
733 jucn
->volume_name
= talloc_strdup(ctx
, pdp
->reqpath
);
734 if (!jucn
->service_name
|| !jucn
->volume_name
) {
736 return NT_STATUS_NO_MEMORY
;
739 /* Verify the share is a dfs root */
740 snum
= lp_servicenumber(jucn
->service_name
);
742 fstring service_name
;
743 fstrcpy(service_name
, jucn
->service_name
);
744 if ((snum
= find_service(service_name
)) < 0) {
745 return NT_STATUS_NOT_FOUND
;
747 TALLOC_FREE(jucn
->service_name
);
748 jucn
->service_name
= talloc_strdup(ctx
, service_name
);
749 if (!jucn
->service_name
) {
751 return NT_STATUS_NO_MEMORY
;
755 if (!lp_msdfs_root(snum
) && (*lp_msdfs_proxy(snum
) == '\0')) {
756 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not "
758 pdp
->servicename
, dfs_path
));
760 return NT_STATUS_NOT_FOUND
;
764 * Self referrals are tested with a anonymous IPC connection and
765 * a GET_DFS_REFERRAL call to \\server\share. (which means
766 * dp.reqpath[0] points to an empty string). create_conn_struct cd's
767 * into the directory and will fail if it cannot (as the anonymous
768 * user). Cope with this.
771 if (pdp
->reqpath
[0] == '\0') {
773 struct referral
*ref
;
775 if (*lp_msdfs_proxy(snum
) == '\0') {
785 * It's an msdfs proxy share. Redirect to
786 * the configured target share.
789 jucn
->referral_count
= 1;
790 if ((ref
= TALLOC_ZERO_P(ctx
, struct referral
)) == NULL
) {
792 return NT_STATUS_NO_MEMORY
;
795 if (!(tmp
= talloc_strdup(ctx
, lp_msdfs_proxy(snum
)))) {
797 return NT_STATUS_NO_MEMORY
;
800 trim_string(tmp
, "\\", 0);
802 ref
->alternate_path
= talloc_asprintf(ctx
, "\\%s", tmp
);
805 if (!ref
->alternate_path
) {
807 return NT_STATUS_NO_MEMORY
;
810 if (pdp
->reqpath
[0] != '\0') {
811 ref
->alternate_path
= talloc_asprintf_append(
815 if (!ref
->alternate_path
) {
817 return NT_STATUS_NO_MEMORY
;
821 ref
->ttl
= REFERRAL_TTL
;
822 jucn
->referral_list
= ref
;
823 *consumedcntp
= strlen(dfs_path
);
828 status
= create_conn_struct(ctx
, conn
, snum
, lp_pathname(snum
));
829 if (!NT_STATUS_IS_OK(status
)) {
834 /* If this is a DFS path dfs_lookup should return
835 * NT_STATUS_PATH_NOT_COVERED. */
837 status
= dfs_path_lookup(ctx
, conn
, dfs_path
, pdp
,
838 False
, consumedcntp
, &targetpath
);
840 if (!NT_STATUS_EQUAL(status
, NT_STATUS_PATH_NOT_COVERED
)) {
841 DEBUG(3,("get_referred_path: No valid referrals for path %s\n",
843 conn_free_internal(conn
);
848 /* We know this is a valid dfs link. Parse the targetpath. */
849 if (!parse_msdfs_symlink(ctx
, targetpath
,
850 &jucn
->referral_list
,
851 &jucn
->referral_count
)) {
852 DEBUG(3,("get_referred_path: failed to parse symlink "
853 "target %s\n", targetpath
));
854 conn_free_internal(conn
);
856 return NT_STATUS_NOT_FOUND
;
859 conn_free_internal(conn
);
864 static int setup_ver2_dfs_referral(const char *pathname
,
866 struct junction_map
*junction
,
870 char* pdata
= *ppdata
;
872 smb_ucs2_t
*uni_requestedpath
= NULL
;
873 int uni_reqpathoffset1
,uni_reqpathoffset2
;
875 int requestedpathlen
=0;
880 DEBUG(10,("Setting up version2 referral\nRequested path:\n"));
882 requestedpathlen
= rpcstr_push_talloc(talloc_tos(),
883 &uni_requestedpath
, pathname
);
884 if (uni_requestedpath
== NULL
|| requestedpathlen
== 0) {
889 dump_data(0, (unsigned char *)uni_requestedpath
,
893 DEBUG(10,("ref count = %u\n",junction
->referral_count
));
895 uni_reqpathoffset1
= REFERRAL_HEADER_SIZE
+
896 VERSION2_REFERRAL_SIZE
* junction
->referral_count
;
898 uni_reqpathoffset2
= uni_reqpathoffset1
+ requestedpathlen
;
900 uni_curroffset
= uni_reqpathoffset2
+ requestedpathlen
;
902 reply_size
= REFERRAL_HEADER_SIZE
+
903 VERSION2_REFERRAL_SIZE
*junction
->referral_count
+
904 2 * requestedpathlen
;
905 DEBUG(10,("reply_size: %u\n",reply_size
));
907 /* add up the unicode lengths of all the referral paths */
908 for(i
=0;i
<junction
->referral_count
;i
++) {
909 DEBUG(10,("referral %u : %s\n",
911 junction
->referral_list
[i
].alternate_path
));
913 (strlen(junction
->referral_list
[i
].alternate_path
)+1)*2;
916 DEBUG(10,("reply_size = %u\n",reply_size
));
917 /* add the unexplained 0x16 bytes */
920 pdata
= (char *)SMB_REALLOC(pdata
,reply_size
);
922 DEBUG(0,("Realloc failed!\n"));
927 /* copy in the dfs requested paths.. required for offset calculations */
928 memcpy(pdata
+uni_reqpathoffset1
,uni_requestedpath
,requestedpathlen
);
929 memcpy(pdata
+uni_reqpathoffset2
,uni_requestedpath
,requestedpathlen
);
931 /* create the header */
932 SSVAL(pdata
,0,consumedcnt
* 2); /* path consumed */
933 /* number of referral in this pkt */
934 SSVAL(pdata
,2,junction
->referral_count
);
936 SIVAL(pdata
,4,DFSREF_REFERRAL_SERVER
| DFSREF_STORAGE_SERVER
);
938 SIVAL(pdata
,4,DFSREF_STORAGE_SERVER
);
942 /* add the referral elements */
943 for(i
=0;i
<junction
->referral_count
;i
++) {
944 struct referral
* ref
= &junction
->referral_list
[i
];
947 SSVAL(pdata
,offset
,2); /* version 2 */
948 SSVAL(pdata
,offset
+2,VERSION2_REFERRAL_SIZE
);
950 SSVAL(pdata
,offset
+4,1);
952 SSVAL(pdata
,offset
+4,0);
955 /* ref_flags :use path_consumed bytes? */
956 SSVAL(pdata
,offset
+6,0);
957 SIVAL(pdata
,offset
+8,ref
->proximity
);
958 SIVAL(pdata
,offset
+12,ref
->ttl
);
960 SSVAL(pdata
,offset
+16,uni_reqpathoffset1
-offset
);
961 SSVAL(pdata
,offset
+18,uni_reqpathoffset2
-offset
);
962 /* copy referred path into current offset */
963 unilen
= rpcstr_push(pdata
+uni_curroffset
,
965 reply_size
- uni_curroffset
,
968 SSVAL(pdata
,offset
+20,uni_curroffset
-offset
);
970 uni_curroffset
+= unilen
;
971 offset
+= VERSION2_REFERRAL_SIZE
;
973 /* add in the unexplained 22 (0x16) bytes at the end */
974 memset(pdata
+uni_curroffset
,'\0',0x16);
978 static int setup_ver3_dfs_referral(const char *pathname
,
980 struct junction_map
*junction
,
984 char *pdata
= *ppdata
;
986 smb_ucs2_t
*uni_reqpath
= NULL
;
987 int uni_reqpathoffset1
, uni_reqpathoffset2
;
994 DEBUG(10,("setting up version3 referral\n"));
996 reqpathlen
= rpcstr_push_talloc(talloc_tos(), &uni_reqpath
, pathname
);
997 if (uni_reqpath
== NULL
|| reqpathlen
== 0) {
1002 dump_data(0, (unsigned char *)uni_reqpath
,
1006 uni_reqpathoffset1
= REFERRAL_HEADER_SIZE
+
1007 VERSION3_REFERRAL_SIZE
* junction
->referral_count
;
1008 uni_reqpathoffset2
= uni_reqpathoffset1
+ reqpathlen
;
1009 reply_size
= uni_curroffset
= uni_reqpathoffset2
+ reqpathlen
;
1011 for(i
=0;i
<junction
->referral_count
;i
++) {
1012 DEBUG(10,("referral %u : %s\n",
1014 junction
->referral_list
[i
].alternate_path
));
1016 (strlen(junction
->referral_list
[i
].alternate_path
)+1)*2;
1019 pdata
= (char *)SMB_REALLOC(pdata
,reply_size
);
1021 DEBUG(0,("version3 referral setup:"
1022 "malloc failed for Realloc!\n"));
1027 /* create the header */
1028 SSVAL(pdata
,0,consumedcnt
* 2); /* path consumed */
1029 SSVAL(pdata
,2,junction
->referral_count
); /* number of referral */
1031 SIVAL(pdata
,4,DFSREF_REFERRAL_SERVER
| DFSREF_STORAGE_SERVER
);
1033 SIVAL(pdata
,4,DFSREF_STORAGE_SERVER
);
1036 /* copy in the reqpaths */
1037 memcpy(pdata
+uni_reqpathoffset1
,uni_reqpath
,reqpathlen
);
1038 memcpy(pdata
+uni_reqpathoffset2
,uni_reqpath
,reqpathlen
);
1041 for(i
=0;i
<junction
->referral_count
;i
++) {
1042 struct referral
* ref
= &(junction
->referral_list
[i
]);
1045 SSVAL(pdata
,offset
,3); /* version 3 */
1046 SSVAL(pdata
,offset
+2,VERSION3_REFERRAL_SIZE
);
1048 SSVAL(pdata
,offset
+4,1);
1050 SSVAL(pdata
,offset
+4,0);
1053 /* ref_flags :use path_consumed bytes? */
1054 SSVAL(pdata
,offset
+6,0);
1055 SIVAL(pdata
,offset
+8,ref
->ttl
);
1057 SSVAL(pdata
,offset
+12,uni_reqpathoffset1
-offset
);
1058 SSVAL(pdata
,offset
+14,uni_reqpathoffset2
-offset
);
1059 /* copy referred path into current offset */
1060 unilen
= rpcstr_push(pdata
+uni_curroffset
,ref
->alternate_path
,
1061 reply_size
- uni_curroffset
,
1062 STR_UNICODE
| STR_TERMINATE
);
1063 SSVAL(pdata
,offset
+16,uni_curroffset
-offset
);
1064 /* copy 0x10 bytes of 00's in the ServiceSite GUID */
1065 memset(pdata
+offset
+18,'\0',16);
1067 uni_curroffset
+= unilen
;
1068 offset
+= VERSION3_REFERRAL_SIZE
;
1073 /******************************************************************
1074 Set up the DFS referral for the dfs pathname. This call returns
1075 the amount of the path covered by this server, and where the
1076 client should be redirected to. This is the meat of the
1077 TRANS2_GET_DFS_REFERRAL call.
1078 ******************************************************************/
1080 int setup_dfs_referral(connection_struct
*orig_conn
,
1081 const char *dfs_path
,
1082 int max_referral_level
,
1083 char **ppdata
, NTSTATUS
*pstatus
)
1085 struct junction_map
*junction
= NULL
;
1086 int consumedcnt
= 0;
1087 bool self_referral
= False
;
1089 char *pathnamep
= NULL
;
1090 char *local_dfs_path
= NULL
;
1093 if (!(ctx
=talloc_init("setup_dfs_referral"))) {
1094 *pstatus
= NT_STATUS_NO_MEMORY
;
1098 /* get the junction entry */
1100 talloc_destroy(ctx
);
1101 *pstatus
= NT_STATUS_NOT_FOUND
;
1106 * Trim pathname sent by client so it begins with only one backslash.
1107 * Two backslashes confuse some dfs clients
1110 local_dfs_path
= talloc_strdup(ctx
,dfs_path
);
1111 if (!local_dfs_path
) {
1112 *pstatus
= NT_STATUS_NO_MEMORY
;
1113 talloc_destroy(ctx
);
1116 pathnamep
= local_dfs_path
;
1117 while (IS_DIRECTORY_SEP(pathnamep
[0]) &&
1118 IS_DIRECTORY_SEP(pathnamep
[1])) {
1122 junction
= TALLOC_ZERO_P(ctx
, struct junction_map
);
1124 *pstatus
= NT_STATUS_NO_MEMORY
;
1125 talloc_destroy(ctx
);
1129 /* The following call can change cwd. */
1130 *pstatus
= get_referred_path(ctx
, pathnamep
, junction
,
1131 &consumedcnt
, &self_referral
);
1132 if (!NT_STATUS_IS_OK(*pstatus
)) {
1133 vfs_ChDir(orig_conn
,orig_conn
->connectpath
);
1134 talloc_destroy(ctx
);
1137 vfs_ChDir(orig_conn
,orig_conn
->connectpath
);
1139 if (!self_referral
) {
1140 pathnamep
[consumedcnt
] = '\0';
1142 if( DEBUGLVL( 3 ) ) {
1144 dbgtext("setup_dfs_referral: Path %s to "
1145 "alternate path(s):",
1147 for(i
=0;i
<junction
->referral_count
;i
++)
1149 junction
->referral_list
[i
].alternate_path
);
1154 /* create the referral depeding on version */
1155 DEBUG(10,("max_referral_level :%d\n",max_referral_level
));
1157 if (max_referral_level
< 2) {
1158 max_referral_level
= 2;
1160 if (max_referral_level
> 3) {
1161 max_referral_level
= 3;
1164 switch(max_referral_level
) {
1166 reply_size
= setup_ver2_dfs_referral(pathnamep
,
1168 consumedcnt
, self_referral
);
1171 reply_size
= setup_ver3_dfs_referral(pathnamep
, ppdata
,
1172 junction
, consumedcnt
, self_referral
);
1175 DEBUG(0,("setup_dfs_referral: Invalid dfs referral "
1177 max_referral_level
));
1178 talloc_destroy(ctx
);
1179 *pstatus
= NT_STATUS_INVALID_LEVEL
;
1184 DEBUGADD(0,("DFS Referral pdata:\n"));
1185 dump_data(0,(uint8
*)*ppdata
,reply_size
);
1188 talloc_destroy(ctx
);
1189 *pstatus
= NT_STATUS_OK
;
1193 /**********************************************************************
1194 The following functions are called by the NETDFS RPC pipe functions
1195 **********************************************************************/
1197 /*********************************************************************
1198 Creates a junction structure from a DFS pathname
1199 **********************************************************************/
1201 bool create_junction(TALLOC_CTX
*ctx
,
1202 const char *dfs_path
,
1203 struct junction_map
*jucn
)
1207 struct dfs_path
*pdp
= TALLOC_P(ctx
,struct dfs_path
);
1213 status
= parse_dfs_path(dfs_path
, False
, pdp
, &dummy
);
1214 if (!NT_STATUS_IS_OK(status
)) {
1218 /* check if path is dfs : validate first token */
1219 if (!is_myname_or_ipaddr(pdp
->hostname
)) {
1220 DEBUG(4,("create_junction: Invalid hostname %s "
1222 pdp
->hostname
, dfs_path
));
1227 /* Check for a non-DFS share */
1228 snum
= lp_servicenumber(pdp
->servicename
);
1230 if(snum
< 0 || !lp_msdfs_root(snum
)) {
1231 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1237 jucn
->service_name
= talloc_strdup(ctx
, pdp
->servicename
);
1238 jucn
->volume_name
= talloc_strdup(ctx
, pdp
->reqpath
);
1239 jucn
->comment
= talloc_strdup(ctx
, lp_comment(snum
));
1242 if (!jucn
->service_name
|| !jucn
->volume_name
|| ! jucn
->comment
) {
1248 /**********************************************************************
1249 Forms a valid Unix pathname from the junction
1250 **********************************************************************/
1252 static bool junction_to_local_path(const struct junction_map
*jucn
,
1254 connection_struct
*conn_out
)
1258 snum
= lp_servicenumber(jucn
->service_name
);
1262 if (!NT_STATUS_IS_OK(create_conn_struct(talloc_tos(),
1264 lp_pathname(snum
)))) {
1268 *pp_path_out
= talloc_asprintf(conn_out
->mem_ctx
,
1272 if (!*pp_path_out
) {
1278 bool create_msdfs_link(const struct junction_map
*jucn
,
1282 char *msdfs_link
= NULL
;
1283 connection_struct conns
;
1284 connection_struct
*conn
= &conns
;
1286 bool insert_comma
= False
;
1291 if(!junction_to_local_path(jucn
, &path
, conn
)) {
1295 /* Form the msdfs_link contents */
1296 msdfs_link
= talloc_strdup(conn
->mem_ctx
, "msdfs:");
1300 for(i
=0; i
<jucn
->referral_count
; i
++) {
1301 char *refpath
= jucn
->referral_list
[i
].alternate_path
;
1303 /* Alternate paths always use Windows separators. */
1304 trim_char(refpath
, '\\', '\\');
1305 if(*refpath
== '\0') {
1307 insert_comma
= False
;
1311 if (i
> 0 && insert_comma
) {
1312 msdfs_link
= talloc_asprintf_append_buffer(msdfs_link
,
1316 msdfs_link
= talloc_asprintf_append_buffer(msdfs_link
,
1324 if (!insert_comma
) {
1325 insert_comma
= True
;
1329 DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
1333 if(SMB_VFS_UNLINK(conn
,path
)!=0) {
1338 if(SMB_VFS_SYMLINK(conn
, msdfs_link
, path
) < 0) {
1339 DEBUG(1,("create_msdfs_link: symlink failed "
1340 "%s -> %s\nError: %s\n",
1341 path
, msdfs_link
, strerror(errno
)));
1349 conn_free_internal(conn
);
1353 bool remove_msdfs_link(const struct junction_map
*jucn
)
1356 connection_struct conns
;
1357 connection_struct
*conn
= &conns
;
1362 if( junction_to_local_path(jucn
, &path
, conn
) ) {
1363 if( SMB_VFS_UNLINK(conn
, path
) == 0 ) {
1368 conn_free_internal(conn
);
1372 /*********************************************************************
1373 Return the number of DFS links at the root of this share.
1374 *********************************************************************/
1376 static int count_dfs_links(TALLOC_CTX
*ctx
, int snum
)
1379 SMB_STRUCT_DIR
*dirp
= NULL
;
1381 const char *connect_path
= lp_pathname(snum
);
1382 const char *msdfs_proxy
= lp_msdfs_proxy(snum
);
1383 connection_struct conn
;
1387 if(*connect_path
== '\0') {
1392 * Fake up a connection struct for the VFS layer.
1395 if (!NT_STATUS_IS_OK(create_conn_struct(talloc_tos(),
1396 &conn
, snum
, connect_path
))) {
1400 /* Count a link for the msdfs root - convention */
1403 /* No more links if this is an msdfs proxy. */
1404 if (*msdfs_proxy
!= '\0') {
1408 /* Now enumerate all dfs links */
1409 dirp
= SMB_VFS_OPENDIR(&conn
, ".", NULL
, 0);
1414 while ((dname
= vfs_readdirname(&conn
, dirp
)) != NULL
) {
1415 if (is_msdfs_link(&conn
,
1422 SMB_VFS_CLOSEDIR(&conn
,dirp
);
1426 conn_free_internal(&conn
);
1430 /*********************************************************************
1431 *********************************************************************/
1433 static int form_junctions(TALLOC_CTX
*ctx
,
1435 struct junction_map
*jucn
,
1439 SMB_STRUCT_DIR
*dirp
= NULL
;
1441 const char *connect_path
= lp_pathname(snum
);
1442 char *service_name
= lp_servicename(snum
);
1443 const char *msdfs_proxy
= lp_msdfs_proxy(snum
);
1444 connection_struct conn
;
1445 struct referral
*ref
= NULL
;
1449 if (jn_remain
== 0) {
1453 if(*connect_path
== '\0') {
1458 * Fake up a connection struct for the VFS layer.
1461 if (!NT_STATUS_IS_OK(create_conn_struct(ctx
, &conn
, snum
, connect_path
))) {
1465 /* form a junction for the msdfs root - convention
1466 DO NOT REMOVE THIS: NT clients will not work with us
1467 if this is not present
1469 jucn
[cnt
].service_name
= talloc_strdup(ctx
,service_name
);
1470 jucn
[cnt
].volume_name
= talloc_strdup(ctx
, "");
1471 if (!jucn
[cnt
].service_name
|| jucn
[cnt
].volume_name
) {
1474 jucn
[cnt
].referral_count
= 1;
1476 ref
= jucn
[cnt
].referral_list
= TALLOC_ZERO_P(ctx
, struct referral
);
1477 if (jucn
[cnt
].referral_list
== NULL
) {
1482 ref
->ttl
= REFERRAL_TTL
;
1483 if (*msdfs_proxy
!= '\0') {
1484 ref
->alternate_path
= talloc_strdup(ctx
,
1487 ref
->alternate_path
= talloc_asprintf(ctx
,
1489 get_local_machine_name(),
1493 if (!ref
->alternate_path
) {
1498 /* Don't enumerate if we're an msdfs proxy. */
1499 if (*msdfs_proxy
!= '\0') {
1503 /* Now enumerate all dfs links */
1504 dirp
= SMB_VFS_OPENDIR(&conn
, ".", NULL
, 0);
1509 while ((dname
= vfs_readdirname(&conn
, dirp
)) != NULL
) {
1510 char *link_target
= NULL
;
1511 if (cnt
>= jn_remain
) {
1512 SMB_VFS_CLOSEDIR(&conn
,dirp
);
1513 DEBUG(2, ("form_junctions: ran out of MSDFS "
1517 if (is_msdfs_link_internal(ctx
,
1519 dname
, &link_target
,
1521 if (parse_msdfs_symlink(ctx
,
1523 &jucn
[cnt
].referral_list
,
1524 &jucn
[cnt
].referral_count
)) {
1526 jucn
[cnt
].service_name
= talloc_strdup(ctx
,
1528 jucn
[cnt
].volume_name
= talloc_strdup(ctx
,
1530 if (!jucn
[cnt
].service_name
||
1531 !jucn
[cnt
].volume_name
) {
1542 SMB_VFS_CLOSEDIR(&conn
,dirp
);
1545 conn_free_internal(&conn
);
1549 struct junction_map
*enum_msdfs_links(TALLOC_CTX
*ctx
, size_t *p_num_jn
)
1551 struct junction_map
*jn
= NULL
;
1553 size_t jn_count
= 0;
1557 if(!lp_host_msdfs()) {
1561 /* Ensure all the usershares are loaded. */
1563 load_registry_shares();
1564 sharecount
= load_usershare_shares();
1567 for(i
=0;i
< sharecount
;i
++) {
1568 if(lp_msdfs_root(i
)) {
1569 jn_count
+= count_dfs_links(ctx
, i
);
1572 if (jn_count
== 0) {
1575 jn
= TALLOC_ARRAY(ctx
, struct junction_map
, jn_count
);
1579 for(i
=0; i
< sharecount
; i
++) {
1580 if (*p_num_jn
>= jn_count
) {
1583 if(lp_msdfs_root(i
)) {
1584 *p_num_jn
+= form_junctions(ctx
, i
,
1586 jn_count
- *p_num_jn
);
1592 /******************************************************************************
1593 Core function to resolve a dfs pathname.
1594 ******************************************************************************/
1596 NTSTATUS
resolve_dfspath(TALLOC_CTX
*ctx
,
1597 connection_struct
*conn
,
1599 const char *name_in
,
1602 NTSTATUS status
= NT_STATUS_OK
;
1604 if (dfs_pathnames
) {
1605 status
= dfs_redirect(ctx
,
1613 * Cheat and just return a copy of the in ptr.
1614 * Once srvstr_get_path() uses talloc it'll
1615 * be a talloced ptr anyway.
1617 *pp_name_out
= CONST_DISCARD(char *,name_in
);
1622 /******************************************************************************
1623 Core function to resolve a dfs pathname possibly containing a wildcard.
1624 This function is identical to the above except for the bool param to
1625 dfs_redirect but I need this to be separate so it's really clear when
1626 we're allowing wildcards and when we're not. JRA.
1627 ******************************************************************************/
1629 NTSTATUS
resolve_dfspath_wcard(TALLOC_CTX
*ctx
,
1630 connection_struct
*conn
,
1632 const char *name_in
,
1634 bool *ppath_contains_wcard
)
1636 NTSTATUS status
= NT_STATUS_OK
;
1637 if (dfs_pathnames
) {
1638 status
= dfs_redirect(ctx
,
1643 ppath_contains_wcard
);
1646 * Cheat and just return a copy of the in ptr.
1647 * Once srvstr_get_path() uses talloc it'll
1648 * be a talloced ptr anyway.
1650 *pp_name_out
= CONST_DISCARD(char *,name_in
);