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
25 #include "smbd/globals.h"
27 /**********************************************************************
28 Parse a DFS pathname of the form \hostname\service\reqpath
29 into the dfs_path structure.
30 If POSIX pathnames is true, the pathname may also be of the
31 form /hostname/service/reqpath.
32 We cope with either here.
34 Unfortunately, due to broken clients who might set the
35 SVAL(inbuf,smb_flg2) & FLAGS2_DFS_PATHNAMES bit and then
36 send a local path, we have to cope with that too....
38 If conn != NULL then ensure the provided service is
39 the one pointed to by the connection.
41 This version does everything using pointers within one copy of the
42 pathname string, talloced on the struct dfs_path pointer (which
43 must be talloced). This may be too clever to live....
45 **********************************************************************/
47 static NTSTATUS parse_dfs_path(connection_struct *conn,
50 struct dfs_path *pdp, /* MUST BE TALLOCED */
51 bool *ppath_contains_wcard)
57 NTSTATUS status = NT_STATUS_OK;
63 * This is the only talloc we should need to do
64 * on the struct dfs_path. All the pointers inside
65 * it should point to offsets within this string.
68 pathname_local = talloc_strdup(pdp, pathname);
69 if (!pathname_local) {
70 return NT_STATUS_NO_MEMORY;
72 /* Get a pointer to the terminating '\0' */
73 eos_ptr = &pathname_local[strlen(pathname_local)];
74 p = temp = pathname_local;
76 pdp->posix_path = (lp_posix_pathnames() && *pathname == '/');
78 sepchar = pdp->posix_path ? '/' : '\\';
80 if (*pathname != sepchar) {
81 DEBUG(10,("parse_dfs_path: path %s doesn't start with %c\n",
84 * Possibly client sent a local path by mistake.
85 * Try and convert to a local path.
88 pdp->hostname = eos_ptr; /* "" */
89 pdp->servicename = eos_ptr; /* "" */
91 /* We've got no info about separators. */
92 pdp->posix_path = lp_posix_pathnames();
94 DEBUG(10,("parse_dfs_path: trying to convert %s to a "
101 * Safe to use on talloc'ed string as it only shrinks.
102 * It also doesn't affect the eos_ptr.
104 trim_char(temp,sepchar,sepchar);
106 DEBUG(10,("parse_dfs_path: temp = |%s| after trimming %c's\n",
110 /* Parse out hostname. */
111 p = strchr_m(temp,sepchar);
113 DEBUG(10,("parse_dfs_path: can't parse hostname from path %s\n",
116 * Possibly client sent a local path by mistake.
117 * Try and convert to a local path.
120 pdp->hostname = eos_ptr; /* "" */
121 pdp->servicename = eos_ptr; /* "" */
124 DEBUG(10,("parse_dfs_path: trying to convert %s "
130 pdp->hostname = temp;
132 DEBUG(10,("parse_dfs_path: hostname: %s\n",pdp->hostname));
134 /* Parse out servicename. */
136 p = strchr_m(servicename,sepchar);
141 /* Is this really our servicename ? */
142 if (conn && !( strequal(servicename, lp_servicename(SNUM(conn)))
143 || (strequal(servicename, HOMES_NAME)
144 && strequal(lp_servicename(SNUM(conn)),
145 get_current_username()) )) ) {
146 DEBUG(10,("parse_dfs_path: %s is not our servicename\n",
150 * Possibly client sent a local path by mistake.
151 * Try and convert to a local path.
154 pdp->hostname = eos_ptr; /* "" */
155 pdp->servicename = eos_ptr; /* "" */
157 /* Repair the path - replace the sepchar's
160 *servicename = sepchar;
166 DEBUG(10,("parse_dfs_path: trying to convert %s "
172 pdp->servicename = servicename;
174 DEBUG(10,("parse_dfs_path: servicename: %s\n",pdp->servicename));
177 /* Client sent self referral \server\share. */
178 pdp->reqpath = eos_ptr; /* "" */
186 *ppath_contains_wcard = False;
190 /* Rest is reqpath. */
191 if (pdp->posix_path) {
192 status = check_path_syntax_posix(pdp->reqpath);
195 status = check_path_syntax_wcard(pdp->reqpath,
196 ppath_contains_wcard);
198 status = check_path_syntax(pdp->reqpath);
202 if (!NT_STATUS_IS_OK(status)) {
203 DEBUG(10,("parse_dfs_path: '%s' failed with %s\n",
204 p, nt_errstr(status) ));
208 DEBUG(10,("parse_dfs_path: rest of the path: %s\n",pdp->reqpath));
212 /********************************************************
213 Fake up a connection struct for the VFS layer.
214 Note this CHANGES CWD !!!! JRA.
215 *********************************************************/
217 NTSTATUS create_conn_struct(TALLOC_CTX *ctx,
218 connection_struct **pconn,
221 struct auth_serversupplied_info *server_info,
224 connection_struct *conn;
228 conn = TALLOC_ZERO_P(ctx, connection_struct);
230 return NT_STATUS_NO_MEMORY;
233 connpath = talloc_strdup(conn, path);
236 return NT_STATUS_NO_MEMORY;
238 connpath = talloc_string_sub(conn,
241 lp_servicename(snum));
244 return NT_STATUS_NO_MEMORY;
247 /* needed for smbd_vfs_init() */
249 if (!(conn->params = TALLOC_ZERO_P(conn, struct share_params))) {
250 DEBUG(0, ("TALLOC failed\n"));
252 return NT_STATUS_NO_MEMORY;
255 conn->params->service = snum;
257 if (server_info != NULL) {
258 conn->server_info = copy_serverinfo(conn, server_info);
259 if (conn->server_info == NULL) {
260 DEBUG(0, ("copy_serverinfo failed\n"));
262 return NT_STATUS_NO_MEMORY;
266 set_conn_connectpath(conn, connpath);
268 if (!smbd_vfs_init(conn)) {
269 NTSTATUS status = map_nt_error_from_unix(errno);
270 DEBUG(0,("create_conn_struct: smbd_vfs_init failed.\n"));
271 conn_free_internal(conn);
275 conn->fs_capabilities = SMB_VFS_FS_CAPABILITIES(conn);
278 * Windows seems to insist on doing trans2getdfsreferral() calls on
279 * the IPC$ share as the anonymous user. If we try to chdir as that
280 * user we will fail.... WTF ? JRA.
283 oldcwd = vfs_GetWd(ctx, conn);
284 if (oldcwd == NULL) {
285 NTSTATUS status = map_nt_error_from_unix(errno);
286 DEBUG(3, ("vfs_GetWd failed: %s\n", strerror(errno)));
287 conn_free_internal(conn);
291 if (vfs_ChDir(conn,conn->connectpath) != 0) {
292 NTSTATUS status = map_nt_error_from_unix(errno);
293 DEBUG(3,("create_conn_struct: Can't ChDir to new conn path %s. "
295 conn->connectpath, strerror(errno) ));
296 conn_free_internal(conn);
306 /**********************************************************************
307 Parse the contents of a symlink to verify if it is an msdfs referral
308 A valid referral is of the form:
310 msdfs:server1\share1,server2\share2
311 msdfs:server1\share1\pathname,server2\share2\pathname
312 msdfs:server1/share1,server2/share2
313 msdfs:server1/share1/pathname,server2/share2/pathname.
315 Note that the alternate paths returned here must be of the canonicalized
319 \server\share\path\to\file,
321 even in posix path mode. This is because we have no knowledge if the
322 server we're referring to understands posix paths.
323 **********************************************************************/
325 static bool parse_msdfs_symlink(TALLOC_CTX *ctx,
327 struct referral **preflist,
332 char **alt_path = NULL;
334 struct referral *reflist;
337 temp = talloc_strdup(ctx, target);
341 prot = strtok_r(temp, ":", &saveptr);
343 DEBUG(0,("parse_msdfs_symlink: invalid path !\n"));
347 alt_path = TALLOC_ARRAY(ctx, char *, MAX_REFERRAL_COUNT);
352 /* parse out the alternate paths */
353 while((count<MAX_REFERRAL_COUNT) &&
354 ((alt_path[count] = strtok_r(NULL, ",", &saveptr)) != NULL)) {
358 DEBUG(10,("parse_msdfs_symlink: count=%d\n", count));
361 reflist = *preflist = TALLOC_ZERO_ARRAY(ctx,
362 struct referral, count);
363 if(reflist == NULL) {
364 TALLOC_FREE(alt_path);
368 reflist = *preflist = NULL;
371 for(i=0;i<count;i++) {
374 /* Canonicalize link target.
375 * Replace all /'s in the path by a \ */
376 string_replace(alt_path[i], '/', '\\');
378 /* Remove leading '\\'s */
380 while (*p && (*p == '\\')) {
384 reflist[i].alternate_path = talloc_asprintf(ctx,
387 if (!reflist[i].alternate_path) {
391 reflist[i].proximity = 0;
392 reflist[i].ttl = REFERRAL_TTL;
393 DEBUG(10, ("parse_msdfs_symlink: Created alt path: %s\n",
394 reflist[i].alternate_path));
399 TALLOC_FREE(alt_path);
403 /**********************************************************************
404 Returns true if the unix path is a valid msdfs symlink and also
405 returns the target string from inside the link.
406 **********************************************************************/
408 static bool is_msdfs_link_internal(TALLOC_CTX *ctx,
409 connection_struct *conn,
411 char **pp_link_target,
412 SMB_STRUCT_STAT *sbufp)
414 int referral_len = 0;
415 #if defined(HAVE_BROKEN_READLINK)
416 char link_target_buf[PATH_MAX];
418 char link_target_buf[7];
421 char *link_target = NULL;
422 struct smb_filename *smb_fname = NULL;
425 if (pp_link_target) {
427 link_target = TALLOC_ARRAY(ctx, char, bufsize);
431 *pp_link_target = link_target;
433 bufsize = sizeof(link_target_buf);
434 link_target = link_target_buf;
437 status = create_synthetic_smb_fname(talloc_tos(), path, NULL, NULL,
439 if (!NT_STATUS_IS_OK(status)) {
443 if (SMB_VFS_LSTAT(conn, smb_fname) != 0) {
444 DEBUG(5,("is_msdfs_link_read_target: %s does not exist.\n",
446 TALLOC_FREE(smb_fname);
449 if (!S_ISLNK(smb_fname->st.st_ex_mode)) {
450 DEBUG(5,("is_msdfs_link_read_target: %s is not a link.\n",
452 TALLOC_FREE(smb_fname);
456 *sbufp = smb_fname->st;
458 TALLOC_FREE(smb_fname);
460 referral_len = SMB_VFS_READLINK(conn, path, link_target, bufsize - 1);
461 if (referral_len == -1) {
462 DEBUG(0,("is_msdfs_link_read_target: Error reading "
463 "msdfs link %s: %s\n",
464 path, strerror(errno)));
467 link_target[referral_len] = '\0';
469 DEBUG(5,("is_msdfs_link_internal: %s -> %s\n",path,
472 if (!strnequal(link_target, "msdfs:", 6)) {
479 if (link_target != link_target_buf) {
480 TALLOC_FREE(link_target);
485 /**********************************************************************
486 Returns true if the unix path is a valid msdfs symlink.
487 **********************************************************************/
489 bool is_msdfs_link(connection_struct *conn,
491 SMB_STRUCT_STAT *sbufp)
493 return is_msdfs_link_internal(talloc_tos(),
500 /*****************************************************************
501 Used by other functions to decide if a dfs path is remote,
502 and to get the list of referred locations for that remote path.
504 search_flag: For findfirsts, dfs links themselves are not
505 redirected, but paths beyond the links are. For normal smb calls,
506 even dfs links need to be redirected.
508 consumedcntp: how much of the dfs path is being redirected. the client
509 should try the remaining path on the redirected server.
511 If this returns NT_STATUS_PATH_NOT_COVERED the contents of the msdfs
512 link redirect are in targetpath.
513 *****************************************************************/
515 static NTSTATUS dfs_path_lookup(TALLOC_CTX *ctx,
516 connection_struct *conn,
517 const char *dfspath, /* Incoming complete dfs path */
518 const struct dfs_path *pdp, /* Parsed out
519 server+share+extrapath. */
520 bool search_flag, /* Called from a findfirst ? */
522 char **pp_targetpath)
527 struct smb_filename *smb_fname = NULL;
528 char *localpath = NULL;
529 char *canon_dfspath = NULL; /* Canonicalized dfs path. (only '/'
532 DEBUG(10,("dfs_path_lookup: Conn path = %s reqpath = %s\n",
533 conn->connectpath, pdp->reqpath));
536 * Note the unix path conversion here we're doing we can
537 * throw away. We're looking for a symlink for a dfs
538 * resolution, if we don't find it we'll do another
539 * unix_convert later in the codepath.
540 * If we needed to remember what we'd resolved in
541 * dp->reqpath (as the original code did) we'd
542 * copy (localhost, dp->reqpath) on any code
543 * path below that returns True - but I don't
544 * think this is needed. JRA.
547 status = unix_convert(ctx, conn, pdp->reqpath, &smb_fname,
548 search_flag ? UCF_ALLOW_WCARD_LCOMP : 0);
550 if (!NT_STATUS_IS_OK(status) && !NT_STATUS_EQUAL(status,
551 NT_STATUS_OBJECT_PATH_NOT_FOUND)) {
555 status = get_full_smb_filename(ctx, smb_fname, &localpath);
556 if (!NT_STATUS_IS_OK(status)) {
557 TALLOC_FREE(smb_fname);
561 TALLOC_FREE(smb_fname);
563 /* Optimization - check if we can redirect the whole path. */
565 if (is_msdfs_link_internal(ctx, conn, localpath, pp_targetpath, NULL)) {
567 DEBUG(6,("dfs_path_lookup (FindFirst) No redirection "
568 "for dfs link %s.\n", dfspath));
572 DEBUG(6,("dfs_path_lookup: %s resolves to a "
573 "valid dfs link %s.\n", dfspath,
574 pp_targetpath ? *pp_targetpath : ""));
577 *consumedcntp = strlen(dfspath);
579 return NT_STATUS_PATH_NOT_COVERED;
582 /* Prepare to test only for '/' components in the given path,
583 * so if a Windows path replace all '\\' characters with '/'.
584 * For a POSIX DFS path we know all separators are already '/'. */
586 canon_dfspath = talloc_strdup(ctx, dfspath);
587 if (!canon_dfspath) {
588 return NT_STATUS_NO_MEMORY;
590 if (!pdp->posix_path) {
591 string_replace(canon_dfspath, '\\', '/');
595 * localpath comes out of unix_convert, so it has
596 * no trailing backslash. Make sure that canon_dfspath hasn't either.
597 * Fix for bug #4860 from Jan Martin <Jan.Martin@rwedea.com>.
600 trim_char(canon_dfspath,0,'/');
603 * Redirect if any component in the path is a link.
604 * We do this by walking backwards through the
605 * local path, chopping off the last component
606 * in both the local path and the canonicalized
607 * DFS path. If we hit a DFS link then we're done.
610 p = strrchr_m(localpath, '/');
612 q = strrchr_m(canon_dfspath, '/');
621 if (is_msdfs_link_internal(ctx, conn,
622 localpath, pp_targetpath, NULL)) {
623 DEBUG(4, ("dfs_path_lookup: Redirecting %s because "
624 "parent %s is dfs link\n", dfspath, localpath));
627 *consumedcntp = strlen(canon_dfspath);
628 DEBUG(10, ("dfs_path_lookup: Path consumed: %s "
634 return NT_STATUS_PATH_NOT_COVERED;
637 /* Step back on the filesystem. */
638 p = strrchr_m(localpath, '/');
641 /* And in the canonicalized dfs path. */
642 q = strrchr_m(canon_dfspath, '/');
649 /*****************************************************************
650 Decides if a dfs pathname should be redirected or not.
651 If not, the pathname is converted to a tcon-relative local unix path
653 search_wcard_flag: this flag performs 2 functions both related
654 to searches. See resolve_dfs_path() and parse_dfs_path_XX()
657 This function can return NT_STATUS_OK, meaning use the returned path as-is
658 (mapped into a local path).
659 or NT_STATUS_NOT_COVERED meaning return a DFS redirect, or
660 any other NT_STATUS error which is a genuine error to be
661 returned to the client.
662 *****************************************************************/
664 static NTSTATUS dfs_redirect(TALLOC_CTX *ctx,
665 connection_struct *conn,
667 bool search_wcard_flag,
669 bool *ppath_contains_wcard)
672 struct dfs_path *pdp = TALLOC_P(ctx, struct dfs_path);
675 return NT_STATUS_NO_MEMORY;
678 status = parse_dfs_path(conn, path_in, search_wcard_flag, pdp,
679 ppath_contains_wcard);
680 if (!NT_STATUS_IS_OK(status)) {
685 if (pdp->reqpath[0] == '\0') {
687 *pp_path_out = talloc_strdup(ctx, "");
689 return NT_STATUS_NO_MEMORY;
691 DEBUG(5,("dfs_redirect: self-referral.\n"));
695 /* If dfs pathname for a non-dfs share, convert to tcon-relative
696 path and return OK */
698 if (!lp_msdfs_root(SNUM(conn))) {
699 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
702 return NT_STATUS_NO_MEMORY;
707 /* If it looked like a local path (zero hostname/servicename)
708 * just treat as a tcon-relative path. */
710 if (pdp->hostname[0] == '\0' && pdp->servicename[0] == '\0') {
711 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
714 return NT_STATUS_NO_MEMORY;
719 if (!( strequal(pdp->servicename, lp_servicename(SNUM(conn)))
720 || (strequal(pdp->servicename, HOMES_NAME)
721 && strequal(lp_servicename(SNUM(conn)),
722 conn->server_info->sanitized_username) )) ) {
724 /* The given sharename doesn't match this connection. */
727 return NT_STATUS_OBJECT_PATH_NOT_FOUND;
730 status = dfs_path_lookup(ctx, conn, path_in, pdp,
731 search_wcard_flag, NULL, NULL);
732 if (!NT_STATUS_IS_OK(status)) {
733 if (NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
734 DEBUG(3,("dfs_redirect: Redirecting %s\n", path_in));
736 DEBUG(10,("dfs_redirect: dfs_path_lookup "
737 "failed for %s with %s\n",
738 path_in, nt_errstr(status) ));
743 DEBUG(3,("dfs_redirect: Not redirecting %s.\n", path_in));
745 /* Form non-dfs tcon-relative path */
746 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
749 return NT_STATUS_NO_MEMORY;
752 DEBUG(3,("dfs_redirect: Path %s converted to non-dfs path %s\n",
759 /**********************************************************************
760 Return a self referral.
761 **********************************************************************/
763 static NTSTATUS self_ref(TALLOC_CTX *ctx,
764 const char *dfs_path,
765 struct junction_map *jucn,
767 bool *self_referralp)
769 struct referral *ref;
771 *self_referralp = True;
773 jucn->referral_count = 1;
774 if((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) {
775 return NT_STATUS_NO_MEMORY;
778 ref->alternate_path = talloc_strdup(ctx, dfs_path);
779 if (!ref->alternate_path) {
780 return NT_STATUS_NO_MEMORY;
783 ref->ttl = REFERRAL_TTL;
784 jucn->referral_list = ref;
785 *consumedcntp = strlen(dfs_path);
789 /**********************************************************************
790 Gets valid referrals for a dfs path and fills up the
791 junction_map structure.
792 **********************************************************************/
794 NTSTATUS get_referred_path(TALLOC_CTX *ctx,
795 const char *dfs_path,
796 struct junction_map *jucn,
798 bool *self_referralp)
800 struct connection_struct *conn;
801 char *targetpath = NULL;
803 NTSTATUS status = NT_STATUS_NOT_FOUND;
805 struct dfs_path *pdp = TALLOC_P(ctx, struct dfs_path);
809 return NT_STATUS_NO_MEMORY;
812 *self_referralp = False;
814 status = parse_dfs_path(NULL, dfs_path, False, pdp, &dummy);
815 if (!NT_STATUS_IS_OK(status)) {
819 jucn->service_name = talloc_strdup(ctx, pdp->servicename);
820 jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
821 if (!jucn->service_name || !jucn->volume_name) {
823 return NT_STATUS_NO_MEMORY;
826 /* Verify the share is a dfs root */
827 snum = lp_servicenumber(jucn->service_name);
829 fstring service_name;
830 fstrcpy(service_name, jucn->service_name);
831 if ((snum = find_service(service_name)) < 0) {
832 return NT_STATUS_NOT_FOUND;
834 TALLOC_FREE(jucn->service_name);
835 jucn->service_name = talloc_strdup(ctx, service_name);
836 if (!jucn->service_name) {
838 return NT_STATUS_NO_MEMORY;
842 if (!lp_msdfs_root(snum) && (*lp_msdfs_proxy(snum) == '\0')) {
843 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not "
845 pdp->servicename, dfs_path));
847 return NT_STATUS_NOT_FOUND;
851 * Self referrals are tested with a anonymous IPC connection and
852 * a GET_DFS_REFERRAL call to \\server\share. (which means
853 * dp.reqpath[0] points to an empty string). create_conn_struct cd's
854 * into the directory and will fail if it cannot (as the anonymous
855 * user). Cope with this.
858 if (pdp->reqpath[0] == '\0') {
860 struct referral *ref;
862 if (*lp_msdfs_proxy(snum) == '\0') {
872 * It's an msdfs proxy share. Redirect to
873 * the configured target share.
876 jucn->referral_count = 1;
877 if ((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) {
879 return NT_STATUS_NO_MEMORY;
882 if (!(tmp = talloc_strdup(ctx, lp_msdfs_proxy(snum)))) {
884 return NT_STATUS_NO_MEMORY;
887 trim_string(tmp, "\\", 0);
889 ref->alternate_path = talloc_asprintf(ctx, "\\%s", tmp);
892 if (!ref->alternate_path) {
894 return NT_STATUS_NO_MEMORY;
897 if (pdp->reqpath[0] != '\0') {
898 ref->alternate_path = talloc_asprintf_append(
902 if (!ref->alternate_path) {
904 return NT_STATUS_NO_MEMORY;
908 ref->ttl = REFERRAL_TTL;
909 jucn->referral_list = ref;
910 *consumedcntp = strlen(dfs_path);
915 status = create_conn_struct(ctx, &conn, snum, lp_pathname(snum),
917 if (!NT_STATUS_IS_OK(status)) {
922 /* If this is a DFS path dfs_lookup should return
923 * NT_STATUS_PATH_NOT_COVERED. */
925 status = dfs_path_lookup(ctx, conn, dfs_path, pdp,
926 False, consumedcntp, &targetpath);
928 if (!NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
929 DEBUG(3,("get_referred_path: No valid referrals for path %s\n",
931 vfs_ChDir(conn, oldpath);
932 conn_free_internal(conn);
937 /* We know this is a valid dfs link. Parse the targetpath. */
938 if (!parse_msdfs_symlink(ctx, targetpath,
939 &jucn->referral_list,
940 &jucn->referral_count)) {
941 DEBUG(3,("get_referred_path: failed to parse symlink "
942 "target %s\n", targetpath ));
943 vfs_ChDir(conn, oldpath);
944 conn_free_internal(conn);
946 return NT_STATUS_NOT_FOUND;
949 vfs_ChDir(conn, oldpath);
950 conn_free_internal(conn);
955 static int setup_ver2_dfs_referral(const char *pathname,
957 struct junction_map *junction,
960 char* pdata = *ppdata;
962 smb_ucs2_t *uni_requestedpath = NULL;
963 int uni_reqpathoffset1,uni_reqpathoffset2;
965 int requestedpathlen=0;
970 DEBUG(10,("Setting up version2 referral\nRequested path:\n"));
972 requestedpathlen = rpcstr_push_talloc(talloc_tos(),
973 &uni_requestedpath, pathname);
974 if (uni_requestedpath == NULL || requestedpathlen == 0) {
979 dump_data(0, (unsigned char *)uni_requestedpath,
983 DEBUG(10,("ref count = %u\n",junction->referral_count));
985 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
986 VERSION2_REFERRAL_SIZE * junction->referral_count;
988 uni_reqpathoffset2 = uni_reqpathoffset1 + requestedpathlen;
990 uni_curroffset = uni_reqpathoffset2 + requestedpathlen;
992 reply_size = REFERRAL_HEADER_SIZE +
993 VERSION2_REFERRAL_SIZE*junction->referral_count +
994 2 * requestedpathlen;
995 DEBUG(10,("reply_size: %u\n",reply_size));
997 /* add up the unicode lengths of all the referral paths */
998 for(i=0;i<junction->referral_count;i++) {
999 DEBUG(10,("referral %u : %s\n",
1001 junction->referral_list[i].alternate_path));
1003 (strlen(junction->referral_list[i].alternate_path)+1)*2;
1006 DEBUG(10,("reply_size = %u\n",reply_size));
1007 /* add the unexplained 0x16 bytes */
1010 pdata = (char *)SMB_REALLOC(pdata,reply_size);
1012 DEBUG(0,("Realloc failed!\n"));
1017 /* copy in the dfs requested paths.. required for offset calculations */
1018 memcpy(pdata+uni_reqpathoffset1,uni_requestedpath,requestedpathlen);
1019 memcpy(pdata+uni_reqpathoffset2,uni_requestedpath,requestedpathlen);
1021 /* create the header */
1022 SSVAL(pdata,0,requestedpathlen - 2); /* UCS2 of path consumed minus
1024 /* number of referral in this pkt */
1025 SSVAL(pdata,2,junction->referral_count);
1027 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
1029 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
1033 /* add the referral elements */
1034 for(i=0;i<junction->referral_count;i++) {
1035 struct referral* ref = &junction->referral_list[i];
1038 SSVAL(pdata,offset,2); /* version 2 */
1039 SSVAL(pdata,offset+2,VERSION2_REFERRAL_SIZE);
1041 SSVAL(pdata,offset+4,1);
1043 SSVAL(pdata,offset+4,0);
1046 /* ref_flags :use path_consumed bytes? */
1047 SSVAL(pdata,offset+6,0);
1048 SIVAL(pdata,offset+8,ref->proximity);
1049 SIVAL(pdata,offset+12,ref->ttl);
1051 SSVAL(pdata,offset+16,uni_reqpathoffset1-offset);
1052 SSVAL(pdata,offset+18,uni_reqpathoffset2-offset);
1053 /* copy referred path into current offset */
1054 unilen = rpcstr_push(pdata+uni_curroffset,
1055 ref->alternate_path,
1056 reply_size - uni_curroffset,
1059 SSVAL(pdata,offset+20,uni_curroffset-offset);
1061 uni_curroffset += unilen;
1062 offset += VERSION2_REFERRAL_SIZE;
1064 /* add in the unexplained 22 (0x16) bytes at the end */
1065 memset(pdata+uni_curroffset,'\0',0x16);
1069 static int setup_ver3_dfs_referral(const char *pathname,
1071 struct junction_map *junction,
1074 char *pdata = *ppdata;
1076 smb_ucs2_t *uni_reqpath = NULL;
1077 int uni_reqpathoffset1, uni_reqpathoffset2;
1084 DEBUG(10,("setting up version3 referral\n"));
1086 reqpathlen = rpcstr_push_talloc(talloc_tos(), &uni_reqpath, pathname);
1087 if (uni_reqpath == NULL || reqpathlen == 0) {
1092 dump_data(0, (unsigned char *)uni_reqpath,
1096 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
1097 VERSION3_REFERRAL_SIZE * junction->referral_count;
1098 uni_reqpathoffset2 = uni_reqpathoffset1 + reqpathlen;
1099 reply_size = uni_curroffset = uni_reqpathoffset2 + reqpathlen;
1101 for(i=0;i<junction->referral_count;i++) {
1102 DEBUG(10,("referral %u : %s\n",
1104 junction->referral_list[i].alternate_path));
1106 (strlen(junction->referral_list[i].alternate_path)+1)*2;
1109 pdata = (char *)SMB_REALLOC(pdata,reply_size);
1111 DEBUG(0,("version3 referral setup:"
1112 "malloc failed for Realloc!\n"));
1117 /* create the header */
1118 SSVAL(pdata,0,reqpathlen - 2); /* UCS2 of path consumed minus
1120 SSVAL(pdata,2,junction->referral_count); /* number of referral */
1122 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
1124 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
1127 /* copy in the reqpaths */
1128 memcpy(pdata+uni_reqpathoffset1,uni_reqpath,reqpathlen);
1129 memcpy(pdata+uni_reqpathoffset2,uni_reqpath,reqpathlen);
1132 for(i=0;i<junction->referral_count;i++) {
1133 struct referral* ref = &(junction->referral_list[i]);
1136 SSVAL(pdata,offset,3); /* version 3 */
1137 SSVAL(pdata,offset+2,VERSION3_REFERRAL_SIZE);
1139 SSVAL(pdata,offset+4,1);
1141 SSVAL(pdata,offset+4,0);
1144 /* ref_flags :use path_consumed bytes? */
1145 SSVAL(pdata,offset+6,0);
1146 SIVAL(pdata,offset+8,ref->ttl);
1148 SSVAL(pdata,offset+12,uni_reqpathoffset1-offset);
1149 SSVAL(pdata,offset+14,uni_reqpathoffset2-offset);
1150 /* copy referred path into current offset */
1151 unilen = rpcstr_push(pdata+uni_curroffset,ref->alternate_path,
1152 reply_size - uni_curroffset,
1153 STR_UNICODE | STR_TERMINATE);
1154 SSVAL(pdata,offset+16,uni_curroffset-offset);
1155 /* copy 0x10 bytes of 00's in the ServiceSite GUID */
1156 memset(pdata+offset+18,'\0',16);
1158 uni_curroffset += unilen;
1159 offset += VERSION3_REFERRAL_SIZE;
1164 /******************************************************************
1165 Set up the DFS referral for the dfs pathname. This call returns
1166 the amount of the path covered by this server, and where the
1167 client should be redirected to. This is the meat of the
1168 TRANS2_GET_DFS_REFERRAL call.
1169 ******************************************************************/
1171 int setup_dfs_referral(connection_struct *orig_conn,
1172 const char *dfs_path,
1173 int max_referral_level,
1174 char **ppdata, NTSTATUS *pstatus)
1176 struct junction_map *junction = NULL;
1177 int consumedcnt = 0;
1178 bool self_referral = False;
1180 char *pathnamep = NULL;
1181 char *local_dfs_path = NULL;
1184 if (!(ctx=talloc_init("setup_dfs_referral"))) {
1185 *pstatus = NT_STATUS_NO_MEMORY;
1189 /* get the junction entry */
1191 talloc_destroy(ctx);
1192 *pstatus = NT_STATUS_NOT_FOUND;
1197 * Trim pathname sent by client so it begins with only one backslash.
1198 * Two backslashes confuse some dfs clients
1201 local_dfs_path = talloc_strdup(ctx,dfs_path);
1202 if (!local_dfs_path) {
1203 *pstatus = NT_STATUS_NO_MEMORY;
1204 talloc_destroy(ctx);
1207 pathnamep = local_dfs_path;
1208 while (IS_DIRECTORY_SEP(pathnamep[0]) &&
1209 IS_DIRECTORY_SEP(pathnamep[1])) {
1213 junction = TALLOC_ZERO_P(ctx, struct junction_map);
1215 *pstatus = NT_STATUS_NO_MEMORY;
1216 talloc_destroy(ctx);
1220 /* The following call can change cwd. */
1221 *pstatus = get_referred_path(ctx, pathnamep, junction,
1222 &consumedcnt, &self_referral);
1223 if (!NT_STATUS_IS_OK(*pstatus)) {
1224 vfs_ChDir(orig_conn,orig_conn->connectpath);
1225 talloc_destroy(ctx);
1228 vfs_ChDir(orig_conn,orig_conn->connectpath);
1230 if (!self_referral) {
1231 pathnamep[consumedcnt] = '\0';
1233 if( DEBUGLVL( 3 ) ) {
1235 dbgtext("setup_dfs_referral: Path %s to "
1236 "alternate path(s):",
1238 for(i=0;i<junction->referral_count;i++)
1240 junction->referral_list[i].alternate_path);
1245 /* create the referral depeding on version */
1246 DEBUG(10,("max_referral_level :%d\n",max_referral_level));
1248 if (max_referral_level < 2) {
1249 max_referral_level = 2;
1251 if (max_referral_level > 3) {
1252 max_referral_level = 3;
1255 switch(max_referral_level) {
1257 reply_size = setup_ver2_dfs_referral(pathnamep,
1262 reply_size = setup_ver3_dfs_referral(pathnamep, ppdata,
1263 junction, self_referral);
1266 DEBUG(0,("setup_dfs_referral: Invalid dfs referral "
1268 max_referral_level));
1269 talloc_destroy(ctx);
1270 *pstatus = NT_STATUS_INVALID_LEVEL;
1275 DEBUGADD(0,("DFS Referral pdata:\n"));
1276 dump_data(0,(uint8 *)*ppdata,reply_size);
1279 talloc_destroy(ctx);
1280 *pstatus = NT_STATUS_OK;
1284 /**********************************************************************
1285 The following functions are called by the NETDFS RPC pipe functions
1286 **********************************************************************/
1288 /*********************************************************************
1289 Creates a junction structure from a DFS pathname
1290 **********************************************************************/
1292 bool create_junction(TALLOC_CTX *ctx,
1293 const char *dfs_path,
1294 struct junction_map *jucn)
1298 struct dfs_path *pdp = TALLOC_P(ctx,struct dfs_path);
1304 status = parse_dfs_path(NULL, dfs_path, False, pdp, &dummy);
1305 if (!NT_STATUS_IS_OK(status)) {
1309 /* check if path is dfs : validate first token */
1310 if (!is_myname_or_ipaddr(pdp->hostname)) {
1311 DEBUG(4,("create_junction: Invalid hostname %s "
1313 pdp->hostname, dfs_path));
1318 /* Check for a non-DFS share */
1319 snum = lp_servicenumber(pdp->servicename);
1321 if(snum < 0 || !lp_msdfs_root(snum)) {
1322 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1328 jucn->service_name = talloc_strdup(ctx, pdp->servicename);
1329 jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
1330 jucn->comment = talloc_strdup(ctx, lp_comment(snum));
1333 if (!jucn->service_name || !jucn->volume_name || ! jucn->comment) {
1339 /**********************************************************************
1340 Forms a valid Unix pathname from the junction
1341 **********************************************************************/
1343 static bool junction_to_local_path(const struct junction_map *jucn,
1345 connection_struct **conn_out,
1351 snum = lp_servicenumber(jucn->service_name);
1355 status = create_conn_struct(talloc_tos(), conn_out, snum,
1356 lp_pathname(snum), NULL, oldpath);
1357 if (!NT_STATUS_IS_OK(status)) {
1361 *pp_path_out = talloc_asprintf(*conn_out,
1365 if (!*pp_path_out) {
1366 vfs_ChDir(*conn_out, *oldpath);
1367 conn_free_internal(*conn_out);
1373 bool create_msdfs_link(const struct junction_map *jucn)
1377 char *msdfs_link = NULL;
1378 connection_struct *conn;
1380 bool insert_comma = False;
1383 if(!junction_to_local_path(jucn, &path, &conn, &cwd)) {
1387 /* Form the msdfs_link contents */
1388 msdfs_link = talloc_strdup(conn, "msdfs:");
1392 for(i=0; i<jucn->referral_count; i++) {
1393 char *refpath = jucn->referral_list[i].alternate_path;
1395 /* Alternate paths always use Windows separators. */
1396 trim_char(refpath, '\\', '\\');
1397 if(*refpath == '\0') {
1399 insert_comma = False;
1403 if (i > 0 && insert_comma) {
1404 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1408 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1416 if (!insert_comma) {
1417 insert_comma = True;
1421 DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
1424 if(SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
1425 if (errno == EEXIST) {
1426 struct smb_filename *smb_fname = NULL;
1429 status = create_synthetic_smb_fname(talloc_tos(), path,
1432 if (!NT_STATUS_IS_OK(status)) {
1433 errno = map_errno_from_nt_status(status);
1437 if(SMB_VFS_UNLINK(conn, smb_fname)!=0) {
1438 TALLOC_FREE(smb_fname);
1441 TALLOC_FREE(smb_fname);
1443 if (SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
1444 DEBUG(1,("create_msdfs_link: symlink failed "
1445 "%s -> %s\nError: %s\n",
1446 path, msdfs_link, strerror(errno)));
1454 vfs_ChDir(conn, cwd);
1455 conn_free_internal(conn);
1459 bool remove_msdfs_link(const struct junction_map *jucn)
1463 connection_struct *conn;
1465 struct smb_filename *smb_fname = NULL;
1468 if (!junction_to_local_path(jucn, &path, &conn, &cwd)) {
1472 status = create_synthetic_smb_fname(talloc_tos(), path,
1475 if (!NT_STATUS_IS_OK(status)) {
1476 errno = map_errno_from_nt_status(status);
1480 if( SMB_VFS_UNLINK(conn, smb_fname) == 0 ) {
1484 TALLOC_FREE(smb_fname);
1485 vfs_ChDir(conn, cwd);
1486 conn_free_internal(conn);
1490 /*********************************************************************
1491 Return the number of DFS links at the root of this share.
1492 *********************************************************************/
1494 static int count_dfs_links(TALLOC_CTX *ctx, int snum)
1497 SMB_STRUCT_DIR *dirp = NULL;
1499 const char *connect_path = lp_pathname(snum);
1500 const char *msdfs_proxy = lp_msdfs_proxy(snum);
1501 connection_struct *conn;
1505 if(*connect_path == '\0') {
1510 * Fake up a connection struct for the VFS layer.
1513 status = create_conn_struct(talloc_tos(), &conn, snum, connect_path,
1515 if (!NT_STATUS_IS_OK(status)) {
1516 DEBUG(3, ("create_conn_struct failed: %s\n",
1517 nt_errstr(status)));
1521 /* Count a link for the msdfs root - convention */
1524 /* No more links if this is an msdfs proxy. */
1525 if (*msdfs_proxy != '\0') {
1529 /* Now enumerate all dfs links */
1530 dirp = SMB_VFS_OPENDIR(conn, ".", NULL, 0);
1535 while ((dname = vfs_readdirname(conn, dirp, NULL)) != NULL) {
1536 if (is_msdfs_link(conn,
1543 SMB_VFS_CLOSEDIR(conn,dirp);
1546 vfs_ChDir(conn, cwd);
1547 conn_free_internal(conn);
1551 /*********************************************************************
1552 *********************************************************************/
1554 static int form_junctions(TALLOC_CTX *ctx,
1556 struct junction_map *jucn,
1560 SMB_STRUCT_DIR *dirp = NULL;
1562 const char *connect_path = lp_pathname(snum);
1563 char *service_name = lp_servicename(snum);
1564 const char *msdfs_proxy = lp_msdfs_proxy(snum);
1565 connection_struct *conn;
1566 struct referral *ref = NULL;
1570 if (jn_remain == 0) {
1574 if(*connect_path == '\0') {
1579 * Fake up a connection struct for the VFS layer.
1582 status = create_conn_struct(ctx, &conn, snum, connect_path, NULL,
1584 if (!NT_STATUS_IS_OK(status)) {
1585 DEBUG(3, ("create_conn_struct failed: %s\n",
1586 nt_errstr(status)));
1590 /* form a junction for the msdfs root - convention
1591 DO NOT REMOVE THIS: NT clients will not work with us
1592 if this is not present
1594 jucn[cnt].service_name = talloc_strdup(ctx,service_name);
1595 jucn[cnt].volume_name = talloc_strdup(ctx, "");
1596 if (!jucn[cnt].service_name || !jucn[cnt].volume_name) {
1599 jucn[cnt].comment = "";
1600 jucn[cnt].referral_count = 1;
1602 ref = jucn[cnt].referral_list = TALLOC_ZERO_P(ctx, struct referral);
1603 if (jucn[cnt].referral_list == NULL) {
1608 ref->ttl = REFERRAL_TTL;
1609 if (*msdfs_proxy != '\0') {
1610 ref->alternate_path = talloc_strdup(ctx,
1613 ref->alternate_path = talloc_asprintf(ctx,
1615 get_local_machine_name(),
1619 if (!ref->alternate_path) {
1624 /* Don't enumerate if we're an msdfs proxy. */
1625 if (*msdfs_proxy != '\0') {
1629 /* Now enumerate all dfs links */
1630 dirp = SMB_VFS_OPENDIR(conn, ".", NULL, 0);
1635 while ((dname = vfs_readdirname(conn, dirp, NULL)) != NULL) {
1636 char *link_target = NULL;
1637 if (cnt >= jn_remain) {
1638 DEBUG(2, ("form_junctions: ran out of MSDFS "
1642 if (is_msdfs_link_internal(ctx,
1644 dname, &link_target,
1646 if (parse_msdfs_symlink(ctx,
1648 &jucn[cnt].referral_list,
1649 &jucn[cnt].referral_count)) {
1651 jucn[cnt].service_name = talloc_strdup(ctx,
1653 jucn[cnt].volume_name = talloc_strdup(ctx,
1655 if (!jucn[cnt].service_name ||
1656 !jucn[cnt].volume_name) {
1659 jucn[cnt].comment = "";
1662 TALLOC_FREE(link_target);
1669 SMB_VFS_CLOSEDIR(conn,dirp);
1672 vfs_ChDir(conn, cwd);
1673 conn_free_internal(conn);
1677 struct junction_map *enum_msdfs_links(TALLOC_CTX *ctx, size_t *p_num_jn)
1679 struct junction_map *jn = NULL;
1681 size_t jn_count = 0;
1685 if(!lp_host_msdfs()) {
1689 /* Ensure all the usershares are loaded. */
1691 load_registry_shares();
1692 sharecount = load_usershare_shares();
1695 for(i=0;i < sharecount;i++) {
1696 if(lp_msdfs_root(i)) {
1697 jn_count += count_dfs_links(ctx, i);
1700 if (jn_count == 0) {
1703 jn = TALLOC_ARRAY(ctx, struct junction_map, jn_count);
1707 for(i=0; i < sharecount; i++) {
1708 if (*p_num_jn >= jn_count) {
1711 if(lp_msdfs_root(i)) {
1712 *p_num_jn += form_junctions(ctx, i,
1714 jn_count - *p_num_jn);
1720 /******************************************************************************
1721 Core function to resolve a dfs pathname possibly containing a wildcard. If
1722 ppath_contains_wcard != NULL, it will be set to true if a wildcard is
1723 detected during dfs resolution.
1724 ******************************************************************************/
1726 NTSTATUS resolve_dfspath_wcard(TALLOC_CTX *ctx,
1727 connection_struct *conn,
1729 const char *name_in,
1731 bool *ppath_contains_wcard)
1733 bool path_contains_wcard;
1734 NTSTATUS status = NT_STATUS_OK;
1736 if (dfs_pathnames) {
1737 status = dfs_redirect(ctx,
1742 &path_contains_wcard);
1744 if (NT_STATUS_IS_OK(status) && ppath_contains_wcard != NULL) {
1745 *ppath_contains_wcard = path_contains_wcard;
1749 * Cheat and just return a copy of the in ptr.
1750 * Once srvstr_get_path() uses talloc it'll
1751 * be a talloced ptr anyway.
1753 *pp_name_out = CONST_DISCARD(char *,name_in);