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)
415 int referral_len = 0;
416 #if defined(HAVE_BROKEN_READLINK)
417 char link_target_buf[PATH_MAX];
419 char link_target_buf[7];
422 char *link_target = NULL;
424 if (pp_link_target) {
426 link_target = TALLOC_ARRAY(ctx, char, bufsize);
430 *pp_link_target = link_target;
432 bufsize = sizeof(link_target_buf);
433 link_target = link_target_buf;
440 if (vfs_lstat_smb_fname(conn, path, sbufp) != 0) {
441 DEBUG(5,("is_msdfs_link_read_target: %s does not exist.\n",
446 if (!S_ISLNK(sbufp->st_ex_mode)) {
447 DEBUG(5,("is_msdfs_link_read_target: %s is not a link.\n",
452 referral_len = SMB_VFS_READLINK(conn, path, link_target, bufsize - 1);
453 if (referral_len == -1) {
454 DEBUG(0,("is_msdfs_link_read_target: Error reading "
455 "msdfs link %s: %s\n",
456 path, strerror(errno)));
459 link_target[referral_len] = '\0';
461 DEBUG(5,("is_msdfs_link_internal: %s -> %s\n",path,
464 if (!strnequal(link_target, "msdfs:", 6)) {
471 if (link_target != link_target_buf) {
472 TALLOC_FREE(link_target);
477 /**********************************************************************
478 Returns true if the unix path is a valid msdfs symlink.
479 **********************************************************************/
481 bool is_msdfs_link(connection_struct *conn,
483 SMB_STRUCT_STAT *sbufp)
485 return is_msdfs_link_internal(talloc_tos(),
492 /*****************************************************************
493 Used by other functions to decide if a dfs path is remote,
494 and to get the list of referred locations for that remote path.
496 search_flag: For findfirsts, dfs links themselves are not
497 redirected, but paths beyond the links are. For normal smb calls,
498 even dfs links need to be redirected.
500 consumedcntp: how much of the dfs path is being redirected. the client
501 should try the remaining path on the redirected server.
503 If this returns NT_STATUS_PATH_NOT_COVERED the contents of the msdfs
504 link redirect are in targetpath.
505 *****************************************************************/
507 static NTSTATUS dfs_path_lookup(TALLOC_CTX *ctx,
508 connection_struct *conn,
509 const char *dfspath, /* Incoming complete dfs path */
510 const struct dfs_path *pdp, /* Parsed out
511 server+share+extrapath. */
512 bool search_flag, /* Called from a findfirst ? */
514 char **pp_targetpath)
519 struct smb_filename *smb_fname = NULL;
520 char *localpath = NULL;
521 char *canon_dfspath = NULL; /* Canonicalized dfs path. (only '/'
524 DEBUG(10,("dfs_path_lookup: Conn path = %s reqpath = %s\n",
525 conn->connectpath, pdp->reqpath));
528 * Note the unix path conversion here we're doing we can
529 * throw away. We're looking for a symlink for a dfs
530 * resolution, if we don't find it we'll do another
531 * unix_convert later in the codepath.
532 * If we needed to remember what we'd resolved in
533 * dp->reqpath (as the original code did) we'd
534 * copy (localhost, dp->reqpath) on any code
535 * path below that returns True - but I don't
536 * think this is needed. JRA.
539 status = unix_convert(ctx, conn, pdp->reqpath, &smb_fname,
540 search_flag ? UCF_ALLOW_WCARD_LCOMP : 0);
542 if (!NT_STATUS_IS_OK(status) && !NT_STATUS_EQUAL(status,
543 NT_STATUS_OBJECT_PATH_NOT_FOUND)) {
547 status = get_full_smb_filename(ctx, smb_fname, &localpath);
548 if (!NT_STATUS_IS_OK(status)) {
549 TALLOC_FREE(smb_fname);
553 TALLOC_FREE(smb_fname);
555 /* Optimization - check if we can redirect the whole path. */
557 if (is_msdfs_link_internal(ctx, conn, localpath, pp_targetpath, NULL)) {
559 DEBUG(6,("dfs_path_lookup (FindFirst) No redirection "
560 "for dfs link %s.\n", dfspath));
564 DEBUG(6,("dfs_path_lookup: %s resolves to a "
565 "valid dfs link %s.\n", dfspath,
566 pp_targetpath ? *pp_targetpath : ""));
569 *consumedcntp = strlen(dfspath);
571 return NT_STATUS_PATH_NOT_COVERED;
574 /* Prepare to test only for '/' components in the given path,
575 * so if a Windows path replace all '\\' characters with '/'.
576 * For a POSIX DFS path we know all separators are already '/'. */
578 canon_dfspath = talloc_strdup(ctx, dfspath);
579 if (!canon_dfspath) {
580 return NT_STATUS_NO_MEMORY;
582 if (!pdp->posix_path) {
583 string_replace(canon_dfspath, '\\', '/');
587 * localpath comes out of unix_convert, so it has
588 * no trailing backslash. Make sure that canon_dfspath hasn't either.
589 * Fix for bug #4860 from Jan Martin <Jan.Martin@rwedea.com>.
592 trim_char(canon_dfspath,0,'/');
595 * Redirect if any component in the path is a link.
596 * We do this by walking backwards through the
597 * local path, chopping off the last component
598 * in both the local path and the canonicalized
599 * DFS path. If we hit a DFS link then we're done.
602 p = strrchr_m(localpath, '/');
604 q = strrchr_m(canon_dfspath, '/');
613 if (is_msdfs_link_internal(ctx, conn,
614 localpath, pp_targetpath, NULL)) {
615 DEBUG(4, ("dfs_path_lookup: Redirecting %s because "
616 "parent %s is dfs link\n", dfspath, localpath));
619 *consumedcntp = strlen(canon_dfspath);
620 DEBUG(10, ("dfs_path_lookup: Path consumed: %s "
626 return NT_STATUS_PATH_NOT_COVERED;
629 /* Step back on the filesystem. */
630 p = strrchr_m(localpath, '/');
633 /* And in the canonicalized dfs path. */
634 q = strrchr_m(canon_dfspath, '/');
641 /*****************************************************************
642 Decides if a dfs pathname should be redirected or not.
643 If not, the pathname is converted to a tcon-relative local unix path
645 search_wcard_flag: this flag performs 2 functions both related
646 to searches. See resolve_dfs_path() and parse_dfs_path_XX()
649 This function can return NT_STATUS_OK, meaning use the returned path as-is
650 (mapped into a local path).
651 or NT_STATUS_NOT_COVERED meaning return a DFS redirect, or
652 any other NT_STATUS error which is a genuine error to be
653 returned to the client.
654 *****************************************************************/
656 static NTSTATUS dfs_redirect(TALLOC_CTX *ctx,
657 connection_struct *conn,
659 bool search_wcard_flag,
661 bool *ppath_contains_wcard)
664 struct dfs_path *pdp = TALLOC_P(ctx, struct dfs_path);
667 return NT_STATUS_NO_MEMORY;
670 status = parse_dfs_path(conn, path_in, search_wcard_flag, pdp,
671 ppath_contains_wcard);
672 if (!NT_STATUS_IS_OK(status)) {
677 if (pdp->reqpath[0] == '\0') {
679 *pp_path_out = talloc_strdup(ctx, "");
681 return NT_STATUS_NO_MEMORY;
683 DEBUG(5,("dfs_redirect: self-referral.\n"));
687 /* If dfs pathname for a non-dfs share, convert to tcon-relative
688 path and return OK */
690 if (!lp_msdfs_root(SNUM(conn))) {
691 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
694 return NT_STATUS_NO_MEMORY;
699 /* If it looked like a local path (zero hostname/servicename)
700 * just treat as a tcon-relative path. */
702 if (pdp->hostname[0] == '\0' && pdp->servicename[0] == '\0') {
703 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
706 return NT_STATUS_NO_MEMORY;
711 if (!( strequal(pdp->servicename, lp_servicename(SNUM(conn)))
712 || (strequal(pdp->servicename, HOMES_NAME)
713 && strequal(lp_servicename(SNUM(conn)),
714 conn->server_info->sanitized_username) )) ) {
716 /* The given sharename doesn't match this connection. */
719 return NT_STATUS_OBJECT_PATH_NOT_FOUND;
722 status = dfs_path_lookup(ctx, conn, path_in, pdp,
723 search_wcard_flag, NULL, NULL);
724 if (!NT_STATUS_IS_OK(status)) {
725 if (NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
726 DEBUG(3,("dfs_redirect: Redirecting %s\n", path_in));
728 DEBUG(10,("dfs_redirect: dfs_path_lookup "
729 "failed for %s with %s\n",
730 path_in, nt_errstr(status) ));
735 DEBUG(3,("dfs_redirect: Not redirecting %s.\n", path_in));
737 /* Form non-dfs tcon-relative path */
738 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
741 return NT_STATUS_NO_MEMORY;
744 DEBUG(3,("dfs_redirect: Path %s converted to non-dfs path %s\n",
751 /**********************************************************************
752 Return a self referral.
753 **********************************************************************/
755 static NTSTATUS self_ref(TALLOC_CTX *ctx,
756 const char *dfs_path,
757 struct junction_map *jucn,
759 bool *self_referralp)
761 struct referral *ref;
763 *self_referralp = True;
765 jucn->referral_count = 1;
766 if((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) {
767 return NT_STATUS_NO_MEMORY;
770 ref->alternate_path = talloc_strdup(ctx, dfs_path);
771 if (!ref->alternate_path) {
772 return NT_STATUS_NO_MEMORY;
775 ref->ttl = REFERRAL_TTL;
776 jucn->referral_list = ref;
777 *consumedcntp = strlen(dfs_path);
781 /**********************************************************************
782 Gets valid referrals for a dfs path and fills up the
783 junction_map structure.
784 **********************************************************************/
786 NTSTATUS get_referred_path(TALLOC_CTX *ctx,
787 const char *dfs_path,
788 struct junction_map *jucn,
790 bool *self_referralp)
792 struct connection_struct *conn;
793 char *targetpath = NULL;
795 NTSTATUS status = NT_STATUS_NOT_FOUND;
797 struct dfs_path *pdp = TALLOC_P(ctx, struct dfs_path);
801 return NT_STATUS_NO_MEMORY;
804 *self_referralp = False;
806 status = parse_dfs_path(NULL, dfs_path, False, pdp, &dummy);
807 if (!NT_STATUS_IS_OK(status)) {
811 jucn->service_name = talloc_strdup(ctx, pdp->servicename);
812 jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
813 if (!jucn->service_name || !jucn->volume_name) {
815 return NT_STATUS_NO_MEMORY;
818 /* Verify the share is a dfs root */
819 snum = lp_servicenumber(jucn->service_name);
821 fstring service_name;
822 fstrcpy(service_name, jucn->service_name);
823 if ((snum = find_service(service_name)) < 0) {
824 return NT_STATUS_NOT_FOUND;
826 TALLOC_FREE(jucn->service_name);
827 jucn->service_name = talloc_strdup(ctx, service_name);
828 if (!jucn->service_name) {
830 return NT_STATUS_NO_MEMORY;
834 if (!lp_msdfs_root(snum) && (*lp_msdfs_proxy(snum) == '\0')) {
835 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not "
837 pdp->servicename, dfs_path));
839 return NT_STATUS_NOT_FOUND;
843 * Self referrals are tested with a anonymous IPC connection and
844 * a GET_DFS_REFERRAL call to \\server\share. (which means
845 * dp.reqpath[0] points to an empty string). create_conn_struct cd's
846 * into the directory and will fail if it cannot (as the anonymous
847 * user). Cope with this.
850 if (pdp->reqpath[0] == '\0') {
852 struct referral *ref;
854 if (*lp_msdfs_proxy(snum) == '\0') {
864 * It's an msdfs proxy share. Redirect to
865 * the configured target share.
868 jucn->referral_count = 1;
869 if ((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) {
871 return NT_STATUS_NO_MEMORY;
874 if (!(tmp = talloc_strdup(ctx, lp_msdfs_proxy(snum)))) {
876 return NT_STATUS_NO_MEMORY;
879 trim_string(tmp, "\\", 0);
881 ref->alternate_path = talloc_asprintf(ctx, "\\%s", tmp);
884 if (!ref->alternate_path) {
886 return NT_STATUS_NO_MEMORY;
889 if (pdp->reqpath[0] != '\0') {
890 ref->alternate_path = talloc_asprintf_append(
894 if (!ref->alternate_path) {
896 return NT_STATUS_NO_MEMORY;
900 ref->ttl = REFERRAL_TTL;
901 jucn->referral_list = ref;
902 *consumedcntp = strlen(dfs_path);
907 status = create_conn_struct(ctx, &conn, snum, lp_pathname(snum),
909 if (!NT_STATUS_IS_OK(status)) {
914 /* If this is a DFS path dfs_lookup should return
915 * NT_STATUS_PATH_NOT_COVERED. */
917 status = dfs_path_lookup(ctx, conn, dfs_path, pdp,
918 False, consumedcntp, &targetpath);
920 if (!NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
921 DEBUG(3,("get_referred_path: No valid referrals for path %s\n",
923 vfs_ChDir(conn, oldpath);
924 conn_free_internal(conn);
929 /* We know this is a valid dfs link. Parse the targetpath. */
930 if (!parse_msdfs_symlink(ctx, targetpath,
931 &jucn->referral_list,
932 &jucn->referral_count)) {
933 DEBUG(3,("get_referred_path: failed to parse symlink "
934 "target %s\n", targetpath ));
935 vfs_ChDir(conn, oldpath);
936 conn_free_internal(conn);
938 return NT_STATUS_NOT_FOUND;
941 vfs_ChDir(conn, oldpath);
942 conn_free_internal(conn);
947 static int setup_ver2_dfs_referral(const char *pathname,
949 struct junction_map *junction,
952 char* pdata = *ppdata;
954 smb_ucs2_t *uni_requestedpath = NULL;
955 int uni_reqpathoffset1,uni_reqpathoffset2;
957 int requestedpathlen=0;
962 DEBUG(10,("Setting up version2 referral\nRequested path:\n"));
964 requestedpathlen = rpcstr_push_talloc(talloc_tos(),
965 &uni_requestedpath, pathname);
966 if (uni_requestedpath == NULL || requestedpathlen == 0) {
971 dump_data(0, (unsigned char *)uni_requestedpath,
975 DEBUG(10,("ref count = %u\n",junction->referral_count));
977 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
978 VERSION2_REFERRAL_SIZE * junction->referral_count;
980 uni_reqpathoffset2 = uni_reqpathoffset1 + requestedpathlen;
982 uni_curroffset = uni_reqpathoffset2 + requestedpathlen;
984 reply_size = REFERRAL_HEADER_SIZE +
985 VERSION2_REFERRAL_SIZE*junction->referral_count +
986 2 * requestedpathlen;
987 DEBUG(10,("reply_size: %u\n",reply_size));
989 /* add up the unicode lengths of all the referral paths */
990 for(i=0;i<junction->referral_count;i++) {
991 DEBUG(10,("referral %u : %s\n",
993 junction->referral_list[i].alternate_path));
995 (strlen(junction->referral_list[i].alternate_path)+1)*2;
998 DEBUG(10,("reply_size = %u\n",reply_size));
999 /* add the unexplained 0x16 bytes */
1002 pdata = (char *)SMB_REALLOC(pdata,reply_size);
1004 DEBUG(0,("Realloc failed!\n"));
1009 /* copy in the dfs requested paths.. required for offset calculations */
1010 memcpy(pdata+uni_reqpathoffset1,uni_requestedpath,requestedpathlen);
1011 memcpy(pdata+uni_reqpathoffset2,uni_requestedpath,requestedpathlen);
1013 /* create the header */
1014 SSVAL(pdata,0,requestedpathlen - 2); /* UCS2 of path consumed minus
1016 /* number of referral in this pkt */
1017 SSVAL(pdata,2,junction->referral_count);
1019 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
1021 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
1025 /* add the referral elements */
1026 for(i=0;i<junction->referral_count;i++) {
1027 struct referral* ref = &junction->referral_list[i];
1030 SSVAL(pdata,offset,2); /* version 2 */
1031 SSVAL(pdata,offset+2,VERSION2_REFERRAL_SIZE);
1033 SSVAL(pdata,offset+4,1);
1035 SSVAL(pdata,offset+4,0);
1038 /* ref_flags :use path_consumed bytes? */
1039 SSVAL(pdata,offset+6,0);
1040 SIVAL(pdata,offset+8,ref->proximity);
1041 SIVAL(pdata,offset+12,ref->ttl);
1043 SSVAL(pdata,offset+16,uni_reqpathoffset1-offset);
1044 SSVAL(pdata,offset+18,uni_reqpathoffset2-offset);
1045 /* copy referred path into current offset */
1046 unilen = rpcstr_push(pdata+uni_curroffset,
1047 ref->alternate_path,
1048 reply_size - uni_curroffset,
1051 SSVAL(pdata,offset+20,uni_curroffset-offset);
1053 uni_curroffset += unilen;
1054 offset += VERSION2_REFERRAL_SIZE;
1056 /* add in the unexplained 22 (0x16) bytes at the end */
1057 memset(pdata+uni_curroffset,'\0',0x16);
1061 static int setup_ver3_dfs_referral(const char *pathname,
1063 struct junction_map *junction,
1066 char *pdata = *ppdata;
1068 smb_ucs2_t *uni_reqpath = NULL;
1069 int uni_reqpathoffset1, uni_reqpathoffset2;
1076 DEBUG(10,("setting up version3 referral\n"));
1078 reqpathlen = rpcstr_push_talloc(talloc_tos(), &uni_reqpath, pathname);
1079 if (uni_reqpath == NULL || reqpathlen == 0) {
1084 dump_data(0, (unsigned char *)uni_reqpath,
1088 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
1089 VERSION3_REFERRAL_SIZE * junction->referral_count;
1090 uni_reqpathoffset2 = uni_reqpathoffset1 + reqpathlen;
1091 reply_size = uni_curroffset = uni_reqpathoffset2 + reqpathlen;
1093 for(i=0;i<junction->referral_count;i++) {
1094 DEBUG(10,("referral %u : %s\n",
1096 junction->referral_list[i].alternate_path));
1098 (strlen(junction->referral_list[i].alternate_path)+1)*2;
1101 pdata = (char *)SMB_REALLOC(pdata,reply_size);
1103 DEBUG(0,("version3 referral setup:"
1104 "malloc failed for Realloc!\n"));
1109 /* create the header */
1110 SSVAL(pdata,0,reqpathlen - 2); /* UCS2 of path consumed minus
1112 SSVAL(pdata,2,junction->referral_count); /* number of referral */
1114 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
1116 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
1119 /* copy in the reqpaths */
1120 memcpy(pdata+uni_reqpathoffset1,uni_reqpath,reqpathlen);
1121 memcpy(pdata+uni_reqpathoffset2,uni_reqpath,reqpathlen);
1124 for(i=0;i<junction->referral_count;i++) {
1125 struct referral* ref = &(junction->referral_list[i]);
1128 SSVAL(pdata,offset,3); /* version 3 */
1129 SSVAL(pdata,offset+2,VERSION3_REFERRAL_SIZE);
1131 SSVAL(pdata,offset+4,1);
1133 SSVAL(pdata,offset+4,0);
1136 /* ref_flags :use path_consumed bytes? */
1137 SSVAL(pdata,offset+6,0);
1138 SIVAL(pdata,offset+8,ref->ttl);
1140 SSVAL(pdata,offset+12,uni_reqpathoffset1-offset);
1141 SSVAL(pdata,offset+14,uni_reqpathoffset2-offset);
1142 /* copy referred path into current offset */
1143 unilen = rpcstr_push(pdata+uni_curroffset,ref->alternate_path,
1144 reply_size - uni_curroffset,
1145 STR_UNICODE | STR_TERMINATE);
1146 SSVAL(pdata,offset+16,uni_curroffset-offset);
1147 /* copy 0x10 bytes of 00's in the ServiceSite GUID */
1148 memset(pdata+offset+18,'\0',16);
1150 uni_curroffset += unilen;
1151 offset += VERSION3_REFERRAL_SIZE;
1156 /******************************************************************
1157 Set up the DFS referral for the dfs pathname. This call returns
1158 the amount of the path covered by this server, and where the
1159 client should be redirected to. This is the meat of the
1160 TRANS2_GET_DFS_REFERRAL call.
1161 ******************************************************************/
1163 int setup_dfs_referral(connection_struct *orig_conn,
1164 const char *dfs_path,
1165 int max_referral_level,
1166 char **ppdata, NTSTATUS *pstatus)
1168 struct junction_map *junction = NULL;
1169 int consumedcnt = 0;
1170 bool self_referral = False;
1172 char *pathnamep = NULL;
1173 char *local_dfs_path = NULL;
1176 if (!(ctx=talloc_init("setup_dfs_referral"))) {
1177 *pstatus = NT_STATUS_NO_MEMORY;
1181 /* get the junction entry */
1183 talloc_destroy(ctx);
1184 *pstatus = NT_STATUS_NOT_FOUND;
1189 * Trim pathname sent by client so it begins with only one backslash.
1190 * Two backslashes confuse some dfs clients
1193 local_dfs_path = talloc_strdup(ctx,dfs_path);
1194 if (!local_dfs_path) {
1195 *pstatus = NT_STATUS_NO_MEMORY;
1196 talloc_destroy(ctx);
1199 pathnamep = local_dfs_path;
1200 while (IS_DIRECTORY_SEP(pathnamep[0]) &&
1201 IS_DIRECTORY_SEP(pathnamep[1])) {
1205 junction = TALLOC_ZERO_P(ctx, struct junction_map);
1207 *pstatus = NT_STATUS_NO_MEMORY;
1208 talloc_destroy(ctx);
1212 /* The following call can change cwd. */
1213 *pstatus = get_referred_path(ctx, pathnamep, junction,
1214 &consumedcnt, &self_referral);
1215 if (!NT_STATUS_IS_OK(*pstatus)) {
1216 vfs_ChDir(orig_conn,orig_conn->connectpath);
1217 talloc_destroy(ctx);
1220 vfs_ChDir(orig_conn,orig_conn->connectpath);
1222 if (!self_referral) {
1223 pathnamep[consumedcnt] = '\0';
1225 if( DEBUGLVL( 3 ) ) {
1227 dbgtext("setup_dfs_referral: Path %s to "
1228 "alternate path(s):",
1230 for(i=0;i<junction->referral_count;i++)
1232 junction->referral_list[i].alternate_path);
1237 /* create the referral depeding on version */
1238 DEBUG(10,("max_referral_level :%d\n",max_referral_level));
1240 if (max_referral_level < 2) {
1241 max_referral_level = 2;
1243 if (max_referral_level > 3) {
1244 max_referral_level = 3;
1247 switch(max_referral_level) {
1249 reply_size = setup_ver2_dfs_referral(pathnamep,
1254 reply_size = setup_ver3_dfs_referral(pathnamep, ppdata,
1255 junction, self_referral);
1258 DEBUG(0,("setup_dfs_referral: Invalid dfs referral "
1260 max_referral_level));
1261 talloc_destroy(ctx);
1262 *pstatus = NT_STATUS_INVALID_LEVEL;
1267 DEBUGADD(0,("DFS Referral pdata:\n"));
1268 dump_data(0,(uint8 *)*ppdata,reply_size);
1271 talloc_destroy(ctx);
1272 *pstatus = NT_STATUS_OK;
1276 /**********************************************************************
1277 The following functions are called by the NETDFS RPC pipe functions
1278 **********************************************************************/
1280 /*********************************************************************
1281 Creates a junction structure from a DFS pathname
1282 **********************************************************************/
1284 bool create_junction(TALLOC_CTX *ctx,
1285 const char *dfs_path,
1286 struct junction_map *jucn)
1290 struct dfs_path *pdp = TALLOC_P(ctx,struct dfs_path);
1296 status = parse_dfs_path(NULL, dfs_path, False, pdp, &dummy);
1297 if (!NT_STATUS_IS_OK(status)) {
1301 /* check if path is dfs : validate first token */
1302 if (!is_myname_or_ipaddr(pdp->hostname)) {
1303 DEBUG(4,("create_junction: Invalid hostname %s "
1305 pdp->hostname, dfs_path));
1310 /* Check for a non-DFS share */
1311 snum = lp_servicenumber(pdp->servicename);
1313 if(snum < 0 || !lp_msdfs_root(snum)) {
1314 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1320 jucn->service_name = talloc_strdup(ctx, pdp->servicename);
1321 jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
1322 jucn->comment = talloc_strdup(ctx, lp_comment(snum));
1325 if (!jucn->service_name || !jucn->volume_name || ! jucn->comment) {
1331 /**********************************************************************
1332 Forms a valid Unix pathname from the junction
1333 **********************************************************************/
1335 static bool junction_to_local_path(const struct junction_map *jucn,
1337 connection_struct **conn_out,
1343 snum = lp_servicenumber(jucn->service_name);
1347 status = create_conn_struct(talloc_tos(), conn_out, snum,
1348 lp_pathname(snum), NULL, oldpath);
1349 if (!NT_STATUS_IS_OK(status)) {
1353 *pp_path_out = talloc_asprintf(*conn_out,
1357 if (!*pp_path_out) {
1358 vfs_ChDir(*conn_out, *oldpath);
1359 conn_free_internal(*conn_out);
1365 bool create_msdfs_link(const struct junction_map *jucn)
1369 char *msdfs_link = NULL;
1370 connection_struct *conn;
1372 bool insert_comma = False;
1375 if(!junction_to_local_path(jucn, &path, &conn, &cwd)) {
1379 /* Form the msdfs_link contents */
1380 msdfs_link = talloc_strdup(conn, "msdfs:");
1384 for(i=0; i<jucn->referral_count; i++) {
1385 char *refpath = jucn->referral_list[i].alternate_path;
1387 /* Alternate paths always use Windows separators. */
1388 trim_char(refpath, '\\', '\\');
1389 if(*refpath == '\0') {
1391 insert_comma = False;
1395 if (i > 0 && insert_comma) {
1396 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1400 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1408 if (!insert_comma) {
1409 insert_comma = True;
1413 DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
1416 if(SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
1417 if (errno == EEXIST) {
1418 struct smb_filename *smb_fname = NULL;
1421 status = create_synthetic_smb_fname(talloc_tos(), path,
1424 if (!NT_STATUS_IS_OK(status)) {
1425 errno = map_errno_from_nt_status(status);
1429 if(SMB_VFS_UNLINK(conn, smb_fname)!=0) {
1430 TALLOC_FREE(smb_fname);
1433 TALLOC_FREE(smb_fname);
1435 if (SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
1436 DEBUG(1,("create_msdfs_link: symlink failed "
1437 "%s -> %s\nError: %s\n",
1438 path, msdfs_link, strerror(errno)));
1446 vfs_ChDir(conn, cwd);
1447 conn_free_internal(conn);
1451 bool remove_msdfs_link(const struct junction_map *jucn)
1455 connection_struct *conn;
1457 struct smb_filename *smb_fname = NULL;
1460 if (!junction_to_local_path(jucn, &path, &conn, &cwd)) {
1464 status = create_synthetic_smb_fname(talloc_tos(), path,
1467 if (!NT_STATUS_IS_OK(status)) {
1468 errno = map_errno_from_nt_status(status);
1472 if( SMB_VFS_UNLINK(conn, smb_fname) == 0 ) {
1476 TALLOC_FREE(smb_fname);
1477 vfs_ChDir(conn, cwd);
1478 conn_free_internal(conn);
1482 /*********************************************************************
1483 Return the number of DFS links at the root of this share.
1484 *********************************************************************/
1486 static int count_dfs_links(TALLOC_CTX *ctx, int snum)
1489 SMB_STRUCT_DIR *dirp = NULL;
1491 const char *connect_path = lp_pathname(snum);
1492 const char *msdfs_proxy = lp_msdfs_proxy(snum);
1493 connection_struct *conn;
1497 if(*connect_path == '\0') {
1502 * Fake up a connection struct for the VFS layer.
1505 status = create_conn_struct(talloc_tos(), &conn, snum, connect_path,
1507 if (!NT_STATUS_IS_OK(status)) {
1508 DEBUG(3, ("create_conn_struct failed: %s\n",
1509 nt_errstr(status)));
1513 /* Count a link for the msdfs root - convention */
1516 /* No more links if this is an msdfs proxy. */
1517 if (*msdfs_proxy != '\0') {
1521 /* Now enumerate all dfs links */
1522 dirp = SMB_VFS_OPENDIR(conn, ".", NULL, 0);
1527 while ((dname = vfs_readdirname(conn, dirp, NULL)) != NULL) {
1528 if (is_msdfs_link(conn,
1535 SMB_VFS_CLOSEDIR(conn,dirp);
1538 vfs_ChDir(conn, cwd);
1539 conn_free_internal(conn);
1543 /*********************************************************************
1544 *********************************************************************/
1546 static int form_junctions(TALLOC_CTX *ctx,
1548 struct junction_map *jucn,
1552 SMB_STRUCT_DIR *dirp = NULL;
1554 const char *connect_path = lp_pathname(snum);
1555 char *service_name = lp_servicename(snum);
1556 const char *msdfs_proxy = lp_msdfs_proxy(snum);
1557 connection_struct *conn;
1558 struct referral *ref = NULL;
1562 if (jn_remain == 0) {
1566 if(*connect_path == '\0') {
1571 * Fake up a connection struct for the VFS layer.
1574 status = create_conn_struct(ctx, &conn, snum, connect_path, NULL,
1576 if (!NT_STATUS_IS_OK(status)) {
1577 DEBUG(3, ("create_conn_struct failed: %s\n",
1578 nt_errstr(status)));
1582 /* form a junction for the msdfs root - convention
1583 DO NOT REMOVE THIS: NT clients will not work with us
1584 if this is not present
1586 jucn[cnt].service_name = talloc_strdup(ctx,service_name);
1587 jucn[cnt].volume_name = talloc_strdup(ctx, "");
1588 if (!jucn[cnt].service_name || !jucn[cnt].volume_name) {
1591 jucn[cnt].comment = "";
1592 jucn[cnt].referral_count = 1;
1594 ref = jucn[cnt].referral_list = TALLOC_ZERO_P(ctx, struct referral);
1595 if (jucn[cnt].referral_list == NULL) {
1600 ref->ttl = REFERRAL_TTL;
1601 if (*msdfs_proxy != '\0') {
1602 ref->alternate_path = talloc_strdup(ctx,
1605 ref->alternate_path = talloc_asprintf(ctx,
1607 get_local_machine_name(),
1611 if (!ref->alternate_path) {
1616 /* Don't enumerate if we're an msdfs proxy. */
1617 if (*msdfs_proxy != '\0') {
1621 /* Now enumerate all dfs links */
1622 dirp = SMB_VFS_OPENDIR(conn, ".", NULL, 0);
1627 while ((dname = vfs_readdirname(conn, dirp, NULL)) != NULL) {
1628 char *link_target = NULL;
1629 if (cnt >= jn_remain) {
1630 DEBUG(2, ("form_junctions: ran out of MSDFS "
1634 if (is_msdfs_link_internal(ctx,
1636 dname, &link_target,
1638 if (parse_msdfs_symlink(ctx,
1640 &jucn[cnt].referral_list,
1641 &jucn[cnt].referral_count)) {
1643 jucn[cnt].service_name = talloc_strdup(ctx,
1645 jucn[cnt].volume_name = talloc_strdup(ctx,
1647 if (!jucn[cnt].service_name ||
1648 !jucn[cnt].volume_name) {
1651 jucn[cnt].comment = "";
1654 TALLOC_FREE(link_target);
1661 SMB_VFS_CLOSEDIR(conn,dirp);
1664 vfs_ChDir(conn, cwd);
1665 conn_free_internal(conn);
1669 struct junction_map *enum_msdfs_links(TALLOC_CTX *ctx, size_t *p_num_jn)
1671 struct junction_map *jn = NULL;
1673 size_t jn_count = 0;
1677 if(!lp_host_msdfs()) {
1681 /* Ensure all the usershares are loaded. */
1683 load_registry_shares();
1684 sharecount = load_usershare_shares();
1687 for(i=0;i < sharecount;i++) {
1688 if(lp_msdfs_root(i)) {
1689 jn_count += count_dfs_links(ctx, i);
1692 if (jn_count == 0) {
1695 jn = TALLOC_ARRAY(ctx, struct junction_map, jn_count);
1699 for(i=0; i < sharecount; i++) {
1700 if (*p_num_jn >= jn_count) {
1703 if(lp_msdfs_root(i)) {
1704 *p_num_jn += form_junctions(ctx, i,
1706 jn_count - *p_num_jn);
1712 /******************************************************************************
1713 Core function to resolve a dfs pathname.
1714 ******************************************************************************/
1716 NTSTATUS resolve_dfspath(TALLOC_CTX *ctx,
1717 connection_struct *conn,
1719 const char *name_in,
1722 NTSTATUS status = NT_STATUS_OK;
1724 if (dfs_pathnames) {
1725 status = dfs_redirect(ctx,
1733 * Cheat and just return a copy of the in ptr.
1734 * Once srvstr_get_path() uses talloc it'll
1735 * be a talloced ptr anyway.
1737 *pp_name_out = CONST_DISCARD(char *,name_in);
1742 /******************************************************************************
1743 Core function to resolve a dfs pathname possibly containing a wildcard.
1744 This function is identical to the above except for the bool param to
1745 dfs_redirect but I need this to be separate so it's really clear when
1746 we're allowing wildcards and when we're not. JRA.
1747 ******************************************************************************/
1749 NTSTATUS resolve_dfspath_wcard(TALLOC_CTX *ctx,
1750 connection_struct *conn,
1752 const char *name_in,
1754 bool *ppath_contains_wcard)
1756 NTSTATUS status = NT_STATUS_OK;
1757 if (dfs_pathnames) {
1758 status = dfs_redirect(ctx,
1763 ppath_contains_wcard);
1766 * Cheat and just return a copy of the in ptr.
1767 * Once srvstr_get_path() uses talloc it'll
1768 * be a talloced ptr anyway.
1770 *pp_name_out = CONST_DISCARD(char *,name_in);