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 char link_target_buf[7];
418 char *link_target = NULL;
420 if (pp_link_target) {
422 link_target = TALLOC_ARRAY(ctx, char, bufsize);
426 *pp_link_target = link_target;
428 bufsize = sizeof(link_target_buf);
429 link_target = link_target_buf;
436 if (SMB_VFS_LSTAT(conn, path, sbufp) != 0) {
437 DEBUG(5,("is_msdfs_link_read_target: %s does not exist.\n",
442 if (!S_ISLNK(sbufp->st_mode)) {
443 DEBUG(5,("is_msdfs_link_read_target: %s is not a link.\n",
448 referral_len = SMB_VFS_READLINK(conn, path, link_target, bufsize - 1);
449 if (referral_len == -1) {
450 DEBUG(0,("is_msdfs_link_read_target: Error reading "
451 "msdfs link %s: %s\n",
452 path, strerror(errno)));
455 link_target[referral_len] = '\0';
457 DEBUG(5,("is_msdfs_link_internal: %s -> %s\n",path,
460 if (!strnequal(link_target, "msdfs:", 6)) {
467 if (link_target != link_target_buf) {
468 TALLOC_FREE(link_target);
473 /**********************************************************************
474 Returns true if the unix path is a valid msdfs symlink.
475 **********************************************************************/
477 bool is_msdfs_link(connection_struct *conn,
479 SMB_STRUCT_STAT *sbufp)
481 return is_msdfs_link_internal(talloc_tos(),
488 /*****************************************************************
489 Used by other functions to decide if a dfs path is remote,
490 and to get the list of referred locations for that remote path.
492 search_flag: For findfirsts, dfs links themselves are not
493 redirected, but paths beyond the links are. For normal smb calls,
494 even dfs links need to be redirected.
496 consumedcntp: how much of the dfs path is being redirected. the client
497 should try the remaining path on the redirected server.
499 If this returns NT_STATUS_PATH_NOT_COVERED the contents of the msdfs
500 link redirect are in targetpath.
501 *****************************************************************/
503 static NTSTATUS dfs_path_lookup(TALLOC_CTX *ctx,
504 connection_struct *conn,
505 const char *dfspath, /* Incoming complete dfs path */
506 const struct dfs_path *pdp, /* Parsed out
507 server+share+extrapath. */
508 bool search_flag, /* Called from a findfirst ? */
510 char **pp_targetpath)
514 SMB_STRUCT_STAT sbuf;
516 char *localpath = NULL;
517 char *canon_dfspath = NULL; /* Canonicalized dfs path. (only '/'
520 DEBUG(10,("dfs_path_lookup: Conn path = %s reqpath = %s\n",
521 conn->connectpath, pdp->reqpath));
524 * Note the unix path conversion here we're doing we can
525 * throw away. We're looking for a symlink for a dfs
526 * resolution, if we don't find it we'll do another
527 * unix_convert later in the codepath.
528 * If we needed to remember what we'd resolved in
529 * dp->reqpath (as the original code did) we'd
530 * copy (localhost, dp->reqpath) on any code
531 * path below that returns True - but I don't
532 * think this is needed. JRA.
535 status = unix_convert(ctx, conn, pdp->reqpath, search_flag, &localpath,
537 if (!NT_STATUS_IS_OK(status) && !NT_STATUS_EQUAL(status,
538 NT_STATUS_OBJECT_PATH_NOT_FOUND)) {
542 /* Optimization - check if we can redirect the whole path. */
544 if (is_msdfs_link_internal(ctx, conn, localpath, pp_targetpath, NULL)) {
546 DEBUG(6,("dfs_path_lookup (FindFirst) No redirection "
547 "for dfs link %s.\n", dfspath));
551 DEBUG(6,("dfs_path_lookup: %s resolves to a "
552 "valid dfs link %s.\n", dfspath,
553 pp_targetpath ? *pp_targetpath : ""));
556 *consumedcntp = strlen(dfspath);
558 return NT_STATUS_PATH_NOT_COVERED;
561 /* Prepare to test only for '/' components in the given path,
562 * so if a Windows path replace all '\\' characters with '/'.
563 * For a POSIX DFS path we know all separators are already '/'. */
565 canon_dfspath = talloc_strdup(ctx, dfspath);
566 if (!canon_dfspath) {
567 return NT_STATUS_NO_MEMORY;
569 if (!pdp->posix_path) {
570 string_replace(canon_dfspath, '\\', '/');
574 * localpath comes out of unix_convert, so it has
575 * no trailing backslash. Make sure that canon_dfspath hasn't either.
576 * Fix for bug #4860 from Jan Martin <Jan.Martin@rwedea.com>.
579 trim_char(canon_dfspath,0,'/');
582 * Redirect if any component in the path is a link.
583 * We do this by walking backwards through the
584 * local path, chopping off the last component
585 * in both the local path and the canonicalized
586 * DFS path. If we hit a DFS link then we're done.
589 p = strrchr_m(localpath, '/');
591 q = strrchr_m(canon_dfspath, '/');
600 if (is_msdfs_link_internal(ctx, conn,
601 localpath, pp_targetpath, NULL)) {
602 DEBUG(4, ("dfs_path_lookup: Redirecting %s because "
603 "parent %s is dfs link\n", dfspath, localpath));
606 *consumedcntp = strlen(canon_dfspath);
607 DEBUG(10, ("dfs_path_lookup: Path consumed: %s "
613 return NT_STATUS_PATH_NOT_COVERED;
616 /* Step back on the filesystem. */
617 p = strrchr_m(localpath, '/');
620 /* And in the canonicalized dfs path. */
621 q = strrchr_m(canon_dfspath, '/');
628 /*****************************************************************
629 Decides if a dfs pathname should be redirected or not.
630 If not, the pathname is converted to a tcon-relative local unix path
632 search_wcard_flag: this flag performs 2 functions both related
633 to searches. See resolve_dfs_path() and parse_dfs_path_XX()
636 This function can return NT_STATUS_OK, meaning use the returned path as-is
637 (mapped into a local path).
638 or NT_STATUS_NOT_COVERED meaning return a DFS redirect, or
639 any other NT_STATUS error which is a genuine error to be
640 returned to the client.
641 *****************************************************************/
643 static NTSTATUS dfs_redirect(TALLOC_CTX *ctx,
644 connection_struct *conn,
646 bool search_wcard_flag,
648 bool *ppath_contains_wcard)
651 struct dfs_path *pdp = TALLOC_P(ctx, struct dfs_path);
654 return NT_STATUS_NO_MEMORY;
657 status = parse_dfs_path(conn, path_in, search_wcard_flag, pdp,
658 ppath_contains_wcard);
659 if (!NT_STATUS_IS_OK(status)) {
664 if (pdp->reqpath[0] == '\0') {
666 *pp_path_out = talloc_strdup(ctx, "");
668 return NT_STATUS_NO_MEMORY;
670 DEBUG(5,("dfs_redirect: self-referral.\n"));
674 /* If dfs pathname for a non-dfs share, convert to tcon-relative
675 path and return OK */
677 if (!lp_msdfs_root(SNUM(conn))) {
678 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
681 return NT_STATUS_NO_MEMORY;
686 /* If it looked like a local path (zero hostname/servicename)
687 * just treat as a tcon-relative path. */
689 if (pdp->hostname[0] == '\0' && pdp->servicename[0] == '\0') {
690 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
693 return NT_STATUS_NO_MEMORY;
698 if (!( strequal(pdp->servicename, lp_servicename(SNUM(conn)))
699 || (strequal(pdp->servicename, HOMES_NAME)
700 && strequal(lp_servicename(SNUM(conn)),
701 conn->server_info->sanitized_username) )) ) {
703 /* The given sharename doesn't match this connection. */
706 return NT_STATUS_OBJECT_PATH_NOT_FOUND;
709 status = dfs_path_lookup(ctx, conn, path_in, pdp,
710 search_wcard_flag, NULL, NULL);
711 if (!NT_STATUS_IS_OK(status)) {
712 if (NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
713 DEBUG(3,("dfs_redirect: Redirecting %s\n", path_in));
715 DEBUG(10,("dfs_redirect: dfs_path_lookup "
716 "failed for %s with %s\n",
717 path_in, nt_errstr(status) ));
722 DEBUG(3,("dfs_redirect: Not redirecting %s.\n", path_in));
724 /* Form non-dfs tcon-relative path */
725 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
728 return NT_STATUS_NO_MEMORY;
731 DEBUG(3,("dfs_redirect: Path %s converted to non-dfs path %s\n",
738 /**********************************************************************
739 Return a self referral.
740 **********************************************************************/
742 static NTSTATUS self_ref(TALLOC_CTX *ctx,
743 const char *dfs_path,
744 struct junction_map *jucn,
746 bool *self_referralp)
748 struct referral *ref;
750 *self_referralp = True;
752 jucn->referral_count = 1;
753 if((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) {
754 return NT_STATUS_NO_MEMORY;
757 ref->alternate_path = talloc_strdup(ctx, dfs_path);
758 if (!ref->alternate_path) {
759 return NT_STATUS_NO_MEMORY;
762 ref->ttl = REFERRAL_TTL;
763 jucn->referral_list = ref;
764 *consumedcntp = strlen(dfs_path);
768 /**********************************************************************
769 Gets valid referrals for a dfs path and fills up the
770 junction_map structure.
771 **********************************************************************/
773 NTSTATUS get_referred_path(TALLOC_CTX *ctx,
774 const char *dfs_path,
775 struct junction_map *jucn,
777 bool *self_referralp)
779 struct connection_struct *conn;
780 char *targetpath = NULL;
782 NTSTATUS status = NT_STATUS_NOT_FOUND;
784 struct dfs_path *pdp = TALLOC_P(ctx, struct dfs_path);
788 return NT_STATUS_NO_MEMORY;
791 *self_referralp = False;
793 status = parse_dfs_path(NULL, dfs_path, False, pdp, &dummy);
794 if (!NT_STATUS_IS_OK(status)) {
798 jucn->service_name = talloc_strdup(ctx, pdp->servicename);
799 jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
800 if (!jucn->service_name || !jucn->volume_name) {
802 return NT_STATUS_NO_MEMORY;
805 /* Verify the share is a dfs root */
806 snum = lp_servicenumber(jucn->service_name);
808 fstring service_name;
809 fstrcpy(service_name, jucn->service_name);
810 if ((snum = find_service(service_name)) < 0) {
811 return NT_STATUS_NOT_FOUND;
813 TALLOC_FREE(jucn->service_name);
814 jucn->service_name = talloc_strdup(ctx, service_name);
815 if (!jucn->service_name) {
817 return NT_STATUS_NO_MEMORY;
821 if (!lp_msdfs_root(snum) && (*lp_msdfs_proxy(snum) == '\0')) {
822 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not "
824 pdp->servicename, dfs_path));
826 return NT_STATUS_NOT_FOUND;
830 * Self referrals are tested with a anonymous IPC connection and
831 * a GET_DFS_REFERRAL call to \\server\share. (which means
832 * dp.reqpath[0] points to an empty string). create_conn_struct cd's
833 * into the directory and will fail if it cannot (as the anonymous
834 * user). Cope with this.
837 if (pdp->reqpath[0] == '\0') {
839 struct referral *ref;
841 if (*lp_msdfs_proxy(snum) == '\0') {
851 * It's an msdfs proxy share. Redirect to
852 * the configured target share.
855 jucn->referral_count = 1;
856 if ((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) {
858 return NT_STATUS_NO_MEMORY;
861 if (!(tmp = talloc_strdup(ctx, lp_msdfs_proxy(snum)))) {
863 return NT_STATUS_NO_MEMORY;
866 trim_string(tmp, "\\", 0);
868 ref->alternate_path = talloc_asprintf(ctx, "\\%s", tmp);
871 if (!ref->alternate_path) {
873 return NT_STATUS_NO_MEMORY;
876 if (pdp->reqpath[0] != '\0') {
877 ref->alternate_path = talloc_asprintf_append(
881 if (!ref->alternate_path) {
883 return NT_STATUS_NO_MEMORY;
887 ref->ttl = REFERRAL_TTL;
888 jucn->referral_list = ref;
889 *consumedcntp = strlen(dfs_path);
894 status = create_conn_struct(ctx, &conn, snum, lp_pathname(snum),
896 if (!NT_STATUS_IS_OK(status)) {
901 /* If this is a DFS path dfs_lookup should return
902 * NT_STATUS_PATH_NOT_COVERED. */
904 status = dfs_path_lookup(ctx, conn, dfs_path, pdp,
905 False, consumedcntp, &targetpath);
907 if (!NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
908 DEBUG(3,("get_referred_path: No valid referrals for path %s\n",
910 vfs_ChDir(conn, oldpath);
911 conn_free_internal(conn);
916 /* We know this is a valid dfs link. Parse the targetpath. */
917 if (!parse_msdfs_symlink(ctx, targetpath,
918 &jucn->referral_list,
919 &jucn->referral_count)) {
920 DEBUG(3,("get_referred_path: failed to parse symlink "
921 "target %s\n", targetpath ));
922 vfs_ChDir(conn, oldpath);
923 conn_free_internal(conn);
925 return NT_STATUS_NOT_FOUND;
928 vfs_ChDir(conn, oldpath);
929 conn_free_internal(conn);
934 static int setup_ver2_dfs_referral(const char *pathname,
936 struct junction_map *junction,
939 char* pdata = *ppdata;
941 smb_ucs2_t *uni_requestedpath = NULL;
942 int uni_reqpathoffset1,uni_reqpathoffset2;
944 int requestedpathlen=0;
949 DEBUG(10,("Setting up version2 referral\nRequested path:\n"));
951 requestedpathlen = rpcstr_push_talloc(talloc_tos(),
952 &uni_requestedpath, pathname);
953 if (uni_requestedpath == NULL || requestedpathlen == 0) {
958 dump_data(0, (unsigned char *)uni_requestedpath,
962 DEBUG(10,("ref count = %u\n",junction->referral_count));
964 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
965 VERSION2_REFERRAL_SIZE * junction->referral_count;
967 uni_reqpathoffset2 = uni_reqpathoffset1 + requestedpathlen;
969 uni_curroffset = uni_reqpathoffset2 + requestedpathlen;
971 reply_size = REFERRAL_HEADER_SIZE +
972 VERSION2_REFERRAL_SIZE*junction->referral_count +
973 2 * requestedpathlen;
974 DEBUG(10,("reply_size: %u\n",reply_size));
976 /* add up the unicode lengths of all the referral paths */
977 for(i=0;i<junction->referral_count;i++) {
978 DEBUG(10,("referral %u : %s\n",
980 junction->referral_list[i].alternate_path));
982 (strlen(junction->referral_list[i].alternate_path)+1)*2;
985 DEBUG(10,("reply_size = %u\n",reply_size));
986 /* add the unexplained 0x16 bytes */
989 pdata = (char *)SMB_REALLOC(pdata,reply_size);
991 DEBUG(0,("Realloc failed!\n"));
996 /* copy in the dfs requested paths.. required for offset calculations */
997 memcpy(pdata+uni_reqpathoffset1,uni_requestedpath,requestedpathlen);
998 memcpy(pdata+uni_reqpathoffset2,uni_requestedpath,requestedpathlen);
1000 /* create the header */
1001 SSVAL(pdata,0,requestedpathlen - 2); /* UCS2 of path consumed minus
1003 /* number of referral in this pkt */
1004 SSVAL(pdata,2,junction->referral_count);
1006 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
1008 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
1012 /* add the referral elements */
1013 for(i=0;i<junction->referral_count;i++) {
1014 struct referral* ref = &junction->referral_list[i];
1017 SSVAL(pdata,offset,2); /* version 2 */
1018 SSVAL(pdata,offset+2,VERSION2_REFERRAL_SIZE);
1020 SSVAL(pdata,offset+4,1);
1022 SSVAL(pdata,offset+4,0);
1025 /* ref_flags :use path_consumed bytes? */
1026 SSVAL(pdata,offset+6,0);
1027 SIVAL(pdata,offset+8,ref->proximity);
1028 SIVAL(pdata,offset+12,ref->ttl);
1030 SSVAL(pdata,offset+16,uni_reqpathoffset1-offset);
1031 SSVAL(pdata,offset+18,uni_reqpathoffset2-offset);
1032 /* copy referred path into current offset */
1033 unilen = rpcstr_push(pdata+uni_curroffset,
1034 ref->alternate_path,
1035 reply_size - uni_curroffset,
1038 SSVAL(pdata,offset+20,uni_curroffset-offset);
1040 uni_curroffset += unilen;
1041 offset += VERSION2_REFERRAL_SIZE;
1043 /* add in the unexplained 22 (0x16) bytes at the end */
1044 memset(pdata+uni_curroffset,'\0',0x16);
1048 static int setup_ver3_dfs_referral(const char *pathname,
1050 struct junction_map *junction,
1053 char *pdata = *ppdata;
1055 smb_ucs2_t *uni_reqpath = NULL;
1056 int uni_reqpathoffset1, uni_reqpathoffset2;
1063 DEBUG(10,("setting up version3 referral\n"));
1065 reqpathlen = rpcstr_push_talloc(talloc_tos(), &uni_reqpath, pathname);
1066 if (uni_reqpath == NULL || reqpathlen == 0) {
1071 dump_data(0, (unsigned char *)uni_reqpath,
1075 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
1076 VERSION3_REFERRAL_SIZE * junction->referral_count;
1077 uni_reqpathoffset2 = uni_reqpathoffset1 + reqpathlen;
1078 reply_size = uni_curroffset = uni_reqpathoffset2 + reqpathlen;
1080 for(i=0;i<junction->referral_count;i++) {
1081 DEBUG(10,("referral %u : %s\n",
1083 junction->referral_list[i].alternate_path));
1085 (strlen(junction->referral_list[i].alternate_path)+1)*2;
1088 pdata = (char *)SMB_REALLOC(pdata,reply_size);
1090 DEBUG(0,("version3 referral setup:"
1091 "malloc failed for Realloc!\n"));
1096 /* create the header */
1097 SSVAL(pdata,0,reqpathlen - 2); /* UCS2 of path consumed minus
1099 SSVAL(pdata,2,junction->referral_count); /* number of referral */
1101 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
1103 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
1106 /* copy in the reqpaths */
1107 memcpy(pdata+uni_reqpathoffset1,uni_reqpath,reqpathlen);
1108 memcpy(pdata+uni_reqpathoffset2,uni_reqpath,reqpathlen);
1111 for(i=0;i<junction->referral_count;i++) {
1112 struct referral* ref = &(junction->referral_list[i]);
1115 SSVAL(pdata,offset,3); /* version 3 */
1116 SSVAL(pdata,offset+2,VERSION3_REFERRAL_SIZE);
1118 SSVAL(pdata,offset+4,1);
1120 SSVAL(pdata,offset+4,0);
1123 /* ref_flags :use path_consumed bytes? */
1124 SSVAL(pdata,offset+6,0);
1125 SIVAL(pdata,offset+8,ref->ttl);
1127 SSVAL(pdata,offset+12,uni_reqpathoffset1-offset);
1128 SSVAL(pdata,offset+14,uni_reqpathoffset2-offset);
1129 /* copy referred path into current offset */
1130 unilen = rpcstr_push(pdata+uni_curroffset,ref->alternate_path,
1131 reply_size - uni_curroffset,
1132 STR_UNICODE | STR_TERMINATE);
1133 SSVAL(pdata,offset+16,uni_curroffset-offset);
1134 /* copy 0x10 bytes of 00's in the ServiceSite GUID */
1135 memset(pdata+offset+18,'\0',16);
1137 uni_curroffset += unilen;
1138 offset += VERSION3_REFERRAL_SIZE;
1143 /******************************************************************
1144 Set up the DFS referral for the dfs pathname. This call returns
1145 the amount of the path covered by this server, and where the
1146 client should be redirected to. This is the meat of the
1147 TRANS2_GET_DFS_REFERRAL call.
1148 ******************************************************************/
1150 int setup_dfs_referral(connection_struct *orig_conn,
1151 const char *dfs_path,
1152 int max_referral_level,
1153 char **ppdata, NTSTATUS *pstatus)
1155 struct junction_map *junction = NULL;
1156 int consumedcnt = 0;
1157 bool self_referral = False;
1159 char *pathnamep = NULL;
1160 char *local_dfs_path = NULL;
1163 if (!(ctx=talloc_init("setup_dfs_referral"))) {
1164 *pstatus = NT_STATUS_NO_MEMORY;
1168 /* get the junction entry */
1170 talloc_destroy(ctx);
1171 *pstatus = NT_STATUS_NOT_FOUND;
1176 * Trim pathname sent by client so it begins with only one backslash.
1177 * Two backslashes confuse some dfs clients
1180 local_dfs_path = talloc_strdup(ctx,dfs_path);
1181 if (!local_dfs_path) {
1182 *pstatus = NT_STATUS_NO_MEMORY;
1183 talloc_destroy(ctx);
1186 pathnamep = local_dfs_path;
1187 while (IS_DIRECTORY_SEP(pathnamep[0]) &&
1188 IS_DIRECTORY_SEP(pathnamep[1])) {
1192 junction = TALLOC_ZERO_P(ctx, struct junction_map);
1194 *pstatus = NT_STATUS_NO_MEMORY;
1195 talloc_destroy(ctx);
1199 /* The following call can change cwd. */
1200 *pstatus = get_referred_path(ctx, pathnamep, junction,
1201 &consumedcnt, &self_referral);
1202 if (!NT_STATUS_IS_OK(*pstatus)) {
1203 vfs_ChDir(orig_conn,orig_conn->connectpath);
1204 talloc_destroy(ctx);
1207 vfs_ChDir(orig_conn,orig_conn->connectpath);
1209 if (!self_referral) {
1210 pathnamep[consumedcnt] = '\0';
1212 if( DEBUGLVL( 3 ) ) {
1214 dbgtext("setup_dfs_referral: Path %s to "
1215 "alternate path(s):",
1217 for(i=0;i<junction->referral_count;i++)
1219 junction->referral_list[i].alternate_path);
1224 /* create the referral depeding on version */
1225 DEBUG(10,("max_referral_level :%d\n",max_referral_level));
1227 if (max_referral_level < 2) {
1228 max_referral_level = 2;
1230 if (max_referral_level > 3) {
1231 max_referral_level = 3;
1234 switch(max_referral_level) {
1236 reply_size = setup_ver2_dfs_referral(pathnamep,
1241 reply_size = setup_ver3_dfs_referral(pathnamep, ppdata,
1242 junction, self_referral);
1245 DEBUG(0,("setup_dfs_referral: Invalid dfs referral "
1247 max_referral_level));
1248 talloc_destroy(ctx);
1249 *pstatus = NT_STATUS_INVALID_LEVEL;
1254 DEBUGADD(0,("DFS Referral pdata:\n"));
1255 dump_data(0,(uint8 *)*ppdata,reply_size);
1258 talloc_destroy(ctx);
1259 *pstatus = NT_STATUS_OK;
1263 /**********************************************************************
1264 The following functions are called by the NETDFS RPC pipe functions
1265 **********************************************************************/
1267 /*********************************************************************
1268 Creates a junction structure from a DFS pathname
1269 **********************************************************************/
1271 bool create_junction(TALLOC_CTX *ctx,
1272 const char *dfs_path,
1273 struct junction_map *jucn)
1277 struct dfs_path *pdp = TALLOC_P(ctx,struct dfs_path);
1283 status = parse_dfs_path(NULL, dfs_path, False, pdp, &dummy);
1284 if (!NT_STATUS_IS_OK(status)) {
1288 /* check if path is dfs : validate first token */
1289 if (!is_myname_or_ipaddr(pdp->hostname)) {
1290 DEBUG(4,("create_junction: Invalid hostname %s "
1292 pdp->hostname, dfs_path));
1297 /* Check for a non-DFS share */
1298 snum = lp_servicenumber(pdp->servicename);
1300 if(snum < 0 || !lp_msdfs_root(snum)) {
1301 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1307 jucn->service_name = talloc_strdup(ctx, pdp->servicename);
1308 jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
1309 jucn->comment = talloc_strdup(ctx, lp_comment(snum));
1312 if (!jucn->service_name || !jucn->volume_name || ! jucn->comment) {
1318 /**********************************************************************
1319 Forms a valid Unix pathname from the junction
1320 **********************************************************************/
1322 static bool junction_to_local_path(const struct junction_map *jucn,
1324 connection_struct **conn_out,
1330 snum = lp_servicenumber(jucn->service_name);
1334 status = create_conn_struct(talloc_tos(), conn_out, snum,
1335 lp_pathname(snum), NULL, oldpath);
1336 if (!NT_STATUS_IS_OK(status)) {
1340 *pp_path_out = talloc_asprintf(*conn_out,
1344 if (!*pp_path_out) {
1345 vfs_ChDir(*conn_out, *oldpath);
1346 conn_free_internal(*conn_out);
1352 bool create_msdfs_link(const struct junction_map *jucn)
1356 char *msdfs_link = NULL;
1357 connection_struct *conn;
1359 bool insert_comma = False;
1362 if(!junction_to_local_path(jucn, &path, &conn, &cwd)) {
1366 /* Form the msdfs_link contents */
1367 msdfs_link = talloc_strdup(conn, "msdfs:");
1371 for(i=0; i<jucn->referral_count; i++) {
1372 char *refpath = jucn->referral_list[i].alternate_path;
1374 /* Alternate paths always use Windows separators. */
1375 trim_char(refpath, '\\', '\\');
1376 if(*refpath == '\0') {
1378 insert_comma = False;
1382 if (i > 0 && insert_comma) {
1383 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1387 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1395 if (!insert_comma) {
1396 insert_comma = True;
1400 DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
1403 if(SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
1404 if (errno == EEXIST) {
1405 if(SMB_VFS_UNLINK(conn,path)!=0) {
1409 if (SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
1410 DEBUG(1,("create_msdfs_link: symlink failed "
1411 "%s -> %s\nError: %s\n",
1412 path, msdfs_link, strerror(errno)));
1420 vfs_ChDir(conn, cwd);
1421 conn_free_internal(conn);
1425 bool remove_msdfs_link(const struct junction_map *jucn)
1429 connection_struct *conn;
1432 if (!junction_to_local_path(jucn, &path, &conn, &cwd)) {
1436 if( SMB_VFS_UNLINK(conn, path) == 0 ) {
1440 vfs_ChDir(conn, cwd);
1441 conn_free_internal(conn);
1445 /*********************************************************************
1446 Return the number of DFS links at the root of this share.
1447 *********************************************************************/
1449 static int count_dfs_links(TALLOC_CTX *ctx, int snum)
1452 SMB_STRUCT_DIR *dirp = NULL;
1454 const char *connect_path = lp_pathname(snum);
1455 const char *msdfs_proxy = lp_msdfs_proxy(snum);
1456 connection_struct *conn;
1460 if(*connect_path == '\0') {
1465 * Fake up a connection struct for the VFS layer.
1468 status = create_conn_struct(talloc_tos(), &conn, snum, connect_path,
1470 if (!NT_STATUS_IS_OK(status)) {
1471 DEBUG(3, ("create_conn_struct failed: %s\n",
1472 nt_errstr(status)));
1476 /* Count a link for the msdfs root - convention */
1479 /* No more links if this is an msdfs proxy. */
1480 if (*msdfs_proxy != '\0') {
1484 /* Now enumerate all dfs links */
1485 dirp = SMB_VFS_OPENDIR(conn, ".", NULL, 0);
1490 while ((dname = vfs_readdirname(conn, dirp, NULL)) != NULL) {
1491 if (is_msdfs_link(conn,
1498 SMB_VFS_CLOSEDIR(conn,dirp);
1501 vfs_ChDir(conn, cwd);
1502 conn_free_internal(conn);
1506 /*********************************************************************
1507 *********************************************************************/
1509 static int form_junctions(TALLOC_CTX *ctx,
1511 struct junction_map *jucn,
1515 SMB_STRUCT_DIR *dirp = NULL;
1517 const char *connect_path = lp_pathname(snum);
1518 char *service_name = lp_servicename(snum);
1519 const char *msdfs_proxy = lp_msdfs_proxy(snum);
1520 connection_struct *conn;
1521 struct referral *ref = NULL;
1525 if (jn_remain == 0) {
1529 if(*connect_path == '\0') {
1534 * Fake up a connection struct for the VFS layer.
1537 status = create_conn_struct(ctx, &conn, snum, connect_path, NULL,
1539 if (!NT_STATUS_IS_OK(status)) {
1540 DEBUG(3, ("create_conn_struct failed: %s\n",
1541 nt_errstr(status)));
1545 /* form a junction for the msdfs root - convention
1546 DO NOT REMOVE THIS: NT clients will not work with us
1547 if this is not present
1549 jucn[cnt].service_name = talloc_strdup(ctx,service_name);
1550 jucn[cnt].volume_name = talloc_strdup(ctx, "");
1551 if (!jucn[cnt].service_name || !jucn[cnt].volume_name) {
1554 jucn[cnt].comment = "";
1555 jucn[cnt].referral_count = 1;
1557 ref = jucn[cnt].referral_list = TALLOC_ZERO_P(ctx, struct referral);
1558 if (jucn[cnt].referral_list == NULL) {
1563 ref->ttl = REFERRAL_TTL;
1564 if (*msdfs_proxy != '\0') {
1565 ref->alternate_path = talloc_strdup(ctx,
1568 ref->alternate_path = talloc_asprintf(ctx,
1570 get_local_machine_name(),
1574 if (!ref->alternate_path) {
1579 /* Don't enumerate if we're an msdfs proxy. */
1580 if (*msdfs_proxy != '\0') {
1584 /* Now enumerate all dfs links */
1585 dirp = SMB_VFS_OPENDIR(conn, ".", NULL, 0);
1590 while ((dname = vfs_readdirname(conn, dirp, NULL)) != NULL) {
1591 char *link_target = NULL;
1592 if (cnt >= jn_remain) {
1593 DEBUG(2, ("form_junctions: ran out of MSDFS "
1597 if (is_msdfs_link_internal(ctx,
1599 dname, &link_target,
1601 if (parse_msdfs_symlink(ctx,
1603 &jucn[cnt].referral_list,
1604 &jucn[cnt].referral_count)) {
1606 jucn[cnt].service_name = talloc_strdup(ctx,
1608 jucn[cnt].volume_name = talloc_strdup(ctx,
1610 if (!jucn[cnt].service_name ||
1611 !jucn[cnt].volume_name) {
1614 jucn[cnt].comment = "";
1617 TALLOC_FREE(link_target);
1624 SMB_VFS_CLOSEDIR(conn,dirp);
1627 vfs_ChDir(conn, cwd);
1628 conn_free_internal(conn);
1632 struct junction_map *enum_msdfs_links(TALLOC_CTX *ctx, size_t *p_num_jn)
1634 struct junction_map *jn = NULL;
1636 size_t jn_count = 0;
1640 if(!lp_host_msdfs()) {
1644 /* Ensure all the usershares are loaded. */
1646 load_registry_shares();
1647 sharecount = load_usershare_shares();
1650 for(i=0;i < sharecount;i++) {
1651 if(lp_msdfs_root(i)) {
1652 jn_count += count_dfs_links(ctx, i);
1655 if (jn_count == 0) {
1658 jn = TALLOC_ARRAY(ctx, struct junction_map, jn_count);
1662 for(i=0; i < sharecount; i++) {
1663 if (*p_num_jn >= jn_count) {
1666 if(lp_msdfs_root(i)) {
1667 *p_num_jn += form_junctions(ctx, i,
1669 jn_count - *p_num_jn);
1675 /******************************************************************************
1676 Core function to resolve a dfs pathname.
1677 ******************************************************************************/
1679 NTSTATUS resolve_dfspath(TALLOC_CTX *ctx,
1680 connection_struct *conn,
1682 const char *name_in,
1685 NTSTATUS status = NT_STATUS_OK;
1687 if (dfs_pathnames) {
1688 status = dfs_redirect(ctx,
1696 * Cheat and just return a copy of the in ptr.
1697 * Once srvstr_get_path() uses talloc it'll
1698 * be a talloced ptr anyway.
1700 *pp_name_out = CONST_DISCARD(char *,name_in);
1705 /******************************************************************************
1706 Core function to resolve a dfs pathname possibly containing a wildcard.
1707 This function is identical to the above except for the bool param to
1708 dfs_redirect but I need this to be separate so it's really clear when
1709 we're allowing wildcards and when we're not. JRA.
1710 ******************************************************************************/
1712 NTSTATUS resolve_dfspath_wcard(TALLOC_CTX *ctx,
1713 connection_struct *conn,
1715 const char *name_in,
1717 bool *ppath_contains_wcard)
1719 NTSTATUS status = NT_STATUS_OK;
1720 if (dfs_pathnames) {
1721 status = dfs_redirect(ctx,
1726 ppath_contains_wcard);
1729 * Cheat and just return a copy of the in ptr.
1730 * Once srvstr_get_path() uses talloc it'll
1731 * be a talloced ptr anyway.
1733 *pp_name_out = CONST_DISCARD(char *,name_in);