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 "system/filesys.h"
26 #include "smbd/smbd.h"
27 #include "smbd/globals.h"
30 #include "lib/param/loadparm.h"
32 /**********************************************************************
33 Parse a DFS pathname of the form \hostname\service\reqpath
34 into the dfs_path structure.
35 If POSIX pathnames is true, the pathname may also be of the
36 form /hostname/service/reqpath.
37 We cope with either here.
39 Unfortunately, due to broken clients who might set the
40 SVAL(inbuf,smb_flg2) & FLAGS2_DFS_PATHNAMES bit and then
41 send a local path, we have to cope with that too....
43 If conn != NULL then ensure the provided service is
44 the one pointed to by the connection.
46 This version does everything using pointers within one copy of the
47 pathname string, talloced on the struct dfs_path pointer (which
48 must be talloced). This may be too clever to live....
50 **********************************************************************/
52 static NTSTATUS parse_dfs_path(connection_struct *conn,
55 struct dfs_path *pdp, /* MUST BE TALLOCED */
56 bool *ppath_contains_wcard)
58 struct smbd_server_connection *sconn = smbd_server_conn;
63 NTSTATUS status = NT_STATUS_OK;
69 * This is the only talloc we should need to do
70 * on the struct dfs_path. All the pointers inside
71 * it should point to offsets within this string.
74 pathname_local = talloc_strdup(pdp, pathname);
75 if (!pathname_local) {
76 return NT_STATUS_NO_MEMORY;
78 /* Get a pointer to the terminating '\0' */
79 eos_ptr = &pathname_local[strlen(pathname_local)];
80 p = temp = pathname_local;
82 pdp->posix_path = (lp_posix_pathnames() && *pathname == '/');
84 sepchar = pdp->posix_path ? '/' : '\\';
86 if (!sconn->using_smb2 && (*pathname != sepchar)) {
87 DEBUG(10,("parse_dfs_path: path %s doesn't start with %c\n",
90 * Possibly client sent a local path by mistake.
91 * Try and convert to a local path.
94 pdp->hostname = eos_ptr; /* "" */
95 pdp->servicename = eos_ptr; /* "" */
97 /* We've got no info about separators. */
98 pdp->posix_path = lp_posix_pathnames();
100 DEBUG(10,("parse_dfs_path: trying to convert %s to a "
107 * Safe to use on talloc'ed string as it only shrinks.
108 * It also doesn't affect the eos_ptr.
110 trim_char(temp,sepchar,sepchar);
112 DEBUG(10,("parse_dfs_path: temp = |%s| after trimming %c's\n",
116 /* Parse out hostname. */
117 p = strchr_m(temp,sepchar);
119 DEBUG(10,("parse_dfs_path: can't parse hostname from path %s\n",
122 * Possibly client sent a local path by mistake.
123 * Try and convert to a local path.
126 pdp->hostname = eos_ptr; /* "" */
127 pdp->servicename = eos_ptr; /* "" */
130 DEBUG(10,("parse_dfs_path: trying to convert %s "
136 pdp->hostname = temp;
138 DEBUG(10,("parse_dfs_path: hostname: %s\n",pdp->hostname));
140 /* Parse out servicename. */
142 p = strchr_m(servicename,sepchar);
147 /* Is this really our servicename ? */
148 if (conn && !( strequal(servicename, lp_servicename(SNUM(conn)))
149 || (strequal(servicename, HOMES_NAME)
150 && strequal(lp_servicename(SNUM(conn)),
151 get_current_username()) )) ) {
152 DEBUG(10,("parse_dfs_path: %s is not our servicename\n",
156 * Possibly client sent a local path by mistake.
157 * Try and convert to a local path.
160 pdp->hostname = eos_ptr; /* "" */
161 pdp->servicename = eos_ptr; /* "" */
163 /* Repair the path - replace the sepchar's
166 *servicename = sepchar;
172 DEBUG(10,("parse_dfs_path: trying to convert %s "
178 pdp->servicename = servicename;
180 DEBUG(10,("parse_dfs_path: servicename: %s\n",pdp->servicename));
183 /* Client sent self referral \server\share. */
184 pdp->reqpath = eos_ptr; /* "" */
192 *ppath_contains_wcard = False;
196 /* Rest is reqpath. */
197 if (pdp->posix_path) {
198 status = check_path_syntax_posix(pdp->reqpath);
201 status = check_path_syntax_wcard(pdp->reqpath,
202 ppath_contains_wcard);
204 status = check_path_syntax(pdp->reqpath);
208 if (!NT_STATUS_IS_OK(status)) {
209 DEBUG(10,("parse_dfs_path: '%s' failed with %s\n",
210 p, nt_errstr(status) ));
214 DEBUG(10,("parse_dfs_path: rest of the path: %s\n",pdp->reqpath));
218 /********************************************************
219 Fake up a connection struct for the VFS layer.
220 Note: this performs a vfs connect and CHANGES CWD !!!! JRA.
221 *********************************************************/
223 NTSTATUS create_conn_struct(TALLOC_CTX *ctx,
224 connection_struct **pconn,
227 const struct auth_serversupplied_info *session_info,
230 connection_struct *conn;
233 const char *vfs_user;
235 conn = talloc_zero(ctx, connection_struct);
237 return NT_STATUS_NO_MEMORY;
240 connpath = talloc_strdup(conn, path);
243 return NT_STATUS_NO_MEMORY;
245 connpath = talloc_string_sub(conn,
248 lp_servicename(snum));
251 return NT_STATUS_NO_MEMORY;
254 /* needed for smbd_vfs_init() */
256 if (!(conn->params = talloc_zero(conn, struct share_params))) {
257 DEBUG(0, ("TALLOC failed\n"));
259 return NT_STATUS_NO_MEMORY;
262 conn->params->service = snum;
264 conn->sconn = smbd_server_conn;
265 conn->sconn->num_tcons_open++;
267 if (session_info != NULL) {
268 conn->session_info = copy_serverinfo(conn, session_info);
269 if (conn->session_info == NULL) {
270 DEBUG(0, ("copy_serverinfo failed\n"));
272 return NT_STATUS_NO_MEMORY;
274 vfs_user = conn->session_info->unix_name;
276 /* use current authenticated user in absence of session_info */
277 vfs_user = get_current_username();
280 set_conn_connectpath(conn, connpath);
282 if (!smbd_vfs_init(conn)) {
283 NTSTATUS status = map_nt_error_from_unix(errno);
284 DEBUG(0,("create_conn_struct: smbd_vfs_init failed.\n"));
289 /* this must be the first filesystem operation that we do */
290 if (SMB_VFS_CONNECT(conn, lp_servicename(snum), vfs_user) < 0) {
291 DEBUG(0,("VFS connect failed!\n"));
293 return NT_STATUS_UNSUCCESSFUL;
296 conn->fs_capabilities = SMB_VFS_FS_CAPABILITIES(conn, &conn->ts_res);
299 * Windows seems to insist on doing trans2getdfsreferral() calls on
300 * the IPC$ share as the anonymous user. If we try to chdir as that
301 * user we will fail.... WTF ? JRA.
304 oldcwd = vfs_GetWd(ctx, conn);
305 if (oldcwd == NULL) {
306 NTSTATUS status = map_nt_error_from_unix(errno);
307 DEBUG(3, ("vfs_GetWd failed: %s\n", strerror(errno)));
312 if (vfs_ChDir(conn,conn->connectpath) != 0) {
313 NTSTATUS status = map_nt_error_from_unix(errno);
314 DEBUG(3,("create_conn_struct: Can't ChDir to new conn path %s. "
316 conn->connectpath, strerror(errno) ));
327 /**********************************************************************
328 Parse the contents of a symlink to verify if it is an msdfs referral
329 A valid referral is of the form:
331 msdfs:server1\share1,server2\share2
332 msdfs:server1\share1\pathname,server2\share2\pathname
333 msdfs:server1/share1,server2/share2
334 msdfs:server1/share1/pathname,server2/share2/pathname.
336 Note that the alternate paths returned here must be of the canonicalized
340 \server\share\path\to\file,
342 even in posix path mode. This is because we have no knowledge if the
343 server we're referring to understands posix paths.
344 **********************************************************************/
346 static bool parse_msdfs_symlink(TALLOC_CTX *ctx,
348 struct referral **preflist,
353 char **alt_path = NULL;
355 struct referral *reflist;
358 temp = talloc_strdup(ctx, target);
362 prot = strtok_r(temp, ":", &saveptr);
364 DEBUG(0,("parse_msdfs_symlink: invalid path !\n"));
368 alt_path = talloc_array(ctx, char *, MAX_REFERRAL_COUNT);
373 /* parse out the alternate paths */
374 while((count<MAX_REFERRAL_COUNT) &&
375 ((alt_path[count] = strtok_r(NULL, ",", &saveptr)) != NULL)) {
379 DEBUG(10,("parse_msdfs_symlink: count=%d\n", count));
382 reflist = *preflist = talloc_zero_array(ctx,
383 struct referral, count);
384 if(reflist == NULL) {
385 TALLOC_FREE(alt_path);
389 reflist = *preflist = NULL;
392 for(i=0;i<count;i++) {
395 /* Canonicalize link target.
396 * Replace all /'s in the path by a \ */
397 string_replace(alt_path[i], '/', '\\');
399 /* Remove leading '\\'s */
401 while (*p && (*p == '\\')) {
405 reflist[i].alternate_path = talloc_asprintf(ctx,
408 if (!reflist[i].alternate_path) {
412 reflist[i].proximity = 0;
413 reflist[i].ttl = REFERRAL_TTL;
414 DEBUG(10, ("parse_msdfs_symlink: Created alt path: %s\n",
415 reflist[i].alternate_path));
420 TALLOC_FREE(alt_path);
424 /**********************************************************************
425 Returns true if the unix path is a valid msdfs symlink and also
426 returns the target string from inside the link.
427 **********************************************************************/
429 static bool is_msdfs_link_internal(TALLOC_CTX *ctx,
430 connection_struct *conn,
432 char **pp_link_target,
433 SMB_STRUCT_STAT *sbufp)
435 int referral_len = 0;
436 #if defined(HAVE_BROKEN_READLINK)
437 char link_target_buf[PATH_MAX];
439 char link_target_buf[7];
442 char *link_target = NULL;
443 struct smb_filename smb_fname;
445 if (pp_link_target) {
447 link_target = talloc_array(ctx, char, bufsize);
451 *pp_link_target = link_target;
453 bufsize = sizeof(link_target_buf);
454 link_target = link_target_buf;
457 ZERO_STRUCT(smb_fname);
458 smb_fname.base_name = discard_const_p(char, path);
460 if (SMB_VFS_LSTAT(conn, &smb_fname) != 0) {
461 DEBUG(5,("is_msdfs_link_read_target: %s does not exist.\n",
465 if (!S_ISLNK(smb_fname.st.st_ex_mode)) {
466 DEBUG(5,("is_msdfs_link_read_target: %s is not a link.\n",
471 *sbufp = smb_fname.st;
474 referral_len = SMB_VFS_READLINK(conn, path, link_target, bufsize - 1);
475 if (referral_len == -1) {
476 DEBUG(0,("is_msdfs_link_read_target: Error reading "
477 "msdfs link %s: %s\n",
478 path, strerror(errno)));
481 link_target[referral_len] = '\0';
483 DEBUG(5,("is_msdfs_link_internal: %s -> %s\n",path,
486 if (!strnequal(link_target, "msdfs:", 6)) {
493 if (link_target != link_target_buf) {
494 TALLOC_FREE(link_target);
499 /**********************************************************************
500 Returns true if the unix path is a valid msdfs symlink.
501 **********************************************************************/
503 bool is_msdfs_link(connection_struct *conn,
505 SMB_STRUCT_STAT *sbufp)
507 return is_msdfs_link_internal(talloc_tos(),
514 /*****************************************************************
515 Used by other functions to decide if a dfs path is remote,
516 and to get the list of referred locations for that remote path.
518 search_flag: For findfirsts, dfs links themselves are not
519 redirected, but paths beyond the links are. For normal smb calls,
520 even dfs links need to be redirected.
522 consumedcntp: how much of the dfs path is being redirected. the client
523 should try the remaining path on the redirected server.
525 If this returns NT_STATUS_PATH_NOT_COVERED the contents of the msdfs
526 link redirect are in targetpath.
527 *****************************************************************/
529 static NTSTATUS dfs_path_lookup(TALLOC_CTX *ctx,
530 connection_struct *conn,
531 const char *dfspath, /* Incoming complete dfs path */
532 const struct dfs_path *pdp, /* Parsed out
533 server+share+extrapath. */
534 bool search_flag, /* Called from a findfirst ? */
536 char **pp_targetpath)
541 struct smb_filename *smb_fname = NULL;
542 char *canon_dfspath = NULL; /* Canonicalized dfs path. (only '/'
545 DEBUG(10,("dfs_path_lookup: Conn path = %s reqpath = %s\n",
546 conn->connectpath, pdp->reqpath));
549 * Note the unix path conversion here we're doing we can
550 * throw away. We're looking for a symlink for a dfs
551 * resolution, if we don't find it we'll do another
552 * unix_convert later in the codepath.
553 * If we needed to remember what we'd resolved in
554 * dp->reqpath (as the original code did) we'd
555 * copy (localhost, dp->reqpath) on any code
556 * path below that returns True - but I don't
557 * think this is needed. JRA.
560 status = unix_convert(ctx, conn, pdp->reqpath, &smb_fname,
561 search_flag ? UCF_ALWAYS_ALLOW_WCARD_LCOMP : 0);
563 if (!NT_STATUS_IS_OK(status)) {
564 if (!NT_STATUS_EQUAL(status,
565 NT_STATUS_OBJECT_PATH_NOT_FOUND)) {
569 /* Create an smb_fname to use below. */
570 status = create_synthetic_smb_fname(ctx, pdp->reqpath, NULL,
572 if (!NT_STATUS_IS_OK(status)) {
577 /* Optimization - check if we can redirect the whole path. */
579 if (is_msdfs_link_internal(ctx, conn, smb_fname->base_name,
580 pp_targetpath, NULL)) {
582 DEBUG(6,("dfs_path_lookup (FindFirst) No redirection "
583 "for dfs link %s.\n", dfspath));
584 status = NT_STATUS_OK;
588 DEBUG(6,("dfs_path_lookup: %s resolves to a "
589 "valid dfs link %s.\n", dfspath,
590 pp_targetpath ? *pp_targetpath : ""));
593 *consumedcntp = strlen(dfspath);
595 status = NT_STATUS_PATH_NOT_COVERED;
599 /* Prepare to test only for '/' components in the given path,
600 * so if a Windows path replace all '\\' characters with '/'.
601 * For a POSIX DFS path we know all separators are already '/'. */
603 canon_dfspath = talloc_strdup(ctx, dfspath);
604 if (!canon_dfspath) {
605 status = NT_STATUS_NO_MEMORY;
608 if (!pdp->posix_path) {
609 string_replace(canon_dfspath, '\\', '/');
613 * localpath comes out of unix_convert, so it has
614 * no trailing backslash. Make sure that canon_dfspath hasn't either.
615 * Fix for bug #4860 from Jan Martin <Jan.Martin@rwedea.com>.
618 trim_char(canon_dfspath,0,'/');
621 * Redirect if any component in the path is a link.
622 * We do this by walking backwards through the
623 * local path, chopping off the last component
624 * in both the local path and the canonicalized
625 * DFS path. If we hit a DFS link then we're done.
628 p = strrchr_m(smb_fname->base_name, '/');
630 q = strrchr_m(canon_dfspath, '/');
639 if (is_msdfs_link_internal(ctx, conn,
640 smb_fname->base_name, pp_targetpath,
642 DEBUG(4, ("dfs_path_lookup: Redirecting %s because "
643 "parent %s is dfs link\n", dfspath,
644 smb_fname_str_dbg(smb_fname)));
647 *consumedcntp = strlen(canon_dfspath);
648 DEBUG(10, ("dfs_path_lookup: Path consumed: %s "
654 status = NT_STATUS_PATH_NOT_COVERED;
658 /* Step back on the filesystem. */
659 p = strrchr_m(smb_fname->base_name, '/');
662 /* And in the canonicalized dfs path. */
663 q = strrchr_m(canon_dfspath, '/');
667 status = NT_STATUS_OK;
669 TALLOC_FREE(smb_fname);
673 /*****************************************************************
674 Decides if a dfs pathname should be redirected or not.
675 If not, the pathname is converted to a tcon-relative local unix path
677 search_wcard_flag: this flag performs 2 functions both related
678 to searches. See resolve_dfs_path() and parse_dfs_path_XX()
681 This function can return NT_STATUS_OK, meaning use the returned path as-is
682 (mapped into a local path).
683 or NT_STATUS_NOT_COVERED meaning return a DFS redirect, or
684 any other NT_STATUS error which is a genuine error to be
685 returned to the client.
686 *****************************************************************/
688 static NTSTATUS dfs_redirect(TALLOC_CTX *ctx,
689 connection_struct *conn,
691 bool search_wcard_flag,
693 bool *ppath_contains_wcard)
696 struct dfs_path *pdp = talloc(ctx, struct dfs_path);
699 return NT_STATUS_NO_MEMORY;
702 status = parse_dfs_path(conn, path_in, search_wcard_flag, pdp,
703 ppath_contains_wcard);
704 if (!NT_STATUS_IS_OK(status)) {
709 if (pdp->reqpath[0] == '\0') {
711 *pp_path_out = talloc_strdup(ctx, "");
713 return NT_STATUS_NO_MEMORY;
715 DEBUG(5,("dfs_redirect: self-referral.\n"));
719 /* If dfs pathname for a non-dfs share, convert to tcon-relative
720 path and return OK */
722 if (!lp_msdfs_root(SNUM(conn))) {
723 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
726 return NT_STATUS_NO_MEMORY;
731 /* If it looked like a local path (zero hostname/servicename)
732 * just treat as a tcon-relative path. */
734 if (pdp->hostname[0] == '\0' && pdp->servicename[0] == '\0') {
735 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
738 return NT_STATUS_NO_MEMORY;
743 if (!( strequal(pdp->servicename, lp_servicename(SNUM(conn)))
744 || (strequal(pdp->servicename, HOMES_NAME)
745 && strequal(lp_servicename(SNUM(conn)),
746 conn->session_info->sanitized_username) )) ) {
748 /* The given sharename doesn't match this connection. */
751 return NT_STATUS_OBJECT_PATH_NOT_FOUND;
754 status = dfs_path_lookup(ctx, conn, path_in, pdp,
755 search_wcard_flag, NULL, NULL);
756 if (!NT_STATUS_IS_OK(status)) {
757 if (NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
758 DEBUG(3,("dfs_redirect: Redirecting %s\n", path_in));
760 DEBUG(10,("dfs_redirect: dfs_path_lookup "
761 "failed for %s with %s\n",
762 path_in, nt_errstr(status) ));
767 DEBUG(3,("dfs_redirect: Not redirecting %s.\n", path_in));
769 /* Form non-dfs tcon-relative path */
770 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
773 return NT_STATUS_NO_MEMORY;
776 DEBUG(3,("dfs_redirect: Path %s converted to non-dfs path %s\n",
783 /**********************************************************************
784 Return a self referral.
785 **********************************************************************/
787 static NTSTATUS self_ref(TALLOC_CTX *ctx,
788 const char *dfs_path,
789 struct junction_map *jucn,
791 bool *self_referralp)
793 struct referral *ref;
795 *self_referralp = True;
797 jucn->referral_count = 1;
798 if((ref = talloc_zero(ctx, struct referral)) == NULL) {
799 return NT_STATUS_NO_MEMORY;
802 ref->alternate_path = talloc_strdup(ctx, dfs_path);
803 if (!ref->alternate_path) {
804 return NT_STATUS_NO_MEMORY;
807 ref->ttl = REFERRAL_TTL;
808 jucn->referral_list = ref;
809 *consumedcntp = strlen(dfs_path);
813 /**********************************************************************
814 Gets valid referrals for a dfs path and fills up the
815 junction_map structure.
816 **********************************************************************/
818 NTSTATUS get_referred_path(TALLOC_CTX *ctx,
819 const char *dfs_path,
820 struct junction_map *jucn,
822 bool *self_referralp)
824 struct connection_struct *conn;
825 char *targetpath = NULL;
827 NTSTATUS status = NT_STATUS_NOT_FOUND;
829 struct dfs_path *pdp = talloc(ctx, struct dfs_path);
833 return NT_STATUS_NO_MEMORY;
836 *self_referralp = False;
838 status = parse_dfs_path(NULL, dfs_path, False, pdp, &dummy);
839 if (!NT_STATUS_IS_OK(status)) {
843 jucn->service_name = talloc_strdup(ctx, pdp->servicename);
844 jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
845 if (!jucn->service_name || !jucn->volume_name) {
847 return NT_STATUS_NO_MEMORY;
850 /* Verify the share is a dfs root */
851 snum = lp_servicenumber(jucn->service_name);
853 char *service_name = NULL;
854 if ((snum = find_service(ctx, jucn->service_name, &service_name)) < 0) {
855 return NT_STATUS_NOT_FOUND;
858 return NT_STATUS_NO_MEMORY;
860 TALLOC_FREE(jucn->service_name);
861 jucn->service_name = talloc_strdup(ctx, service_name);
862 if (!jucn->service_name) {
864 return NT_STATUS_NO_MEMORY;
868 if (!lp_msdfs_root(snum) && (*lp_msdfs_proxy(snum) == '\0')) {
869 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not "
871 pdp->servicename, dfs_path));
873 return NT_STATUS_NOT_FOUND;
877 * Self referrals are tested with a anonymous IPC connection and
878 * a GET_DFS_REFERRAL call to \\server\share. (which means
879 * dp.reqpath[0] points to an empty string). create_conn_struct cd's
880 * into the directory and will fail if it cannot (as the anonymous
881 * user). Cope with this.
884 if (pdp->reqpath[0] == '\0') {
886 struct referral *ref;
888 if (*lp_msdfs_proxy(snum) == '\0') {
898 * It's an msdfs proxy share. Redirect to
899 * the configured target share.
902 jucn->referral_count = 1;
903 if ((ref = talloc_zero(ctx, struct referral)) == NULL) {
905 return NT_STATUS_NO_MEMORY;
908 if (!(tmp = talloc_strdup(ctx, lp_msdfs_proxy(snum)))) {
910 return NT_STATUS_NO_MEMORY;
913 trim_string(tmp, "\\", 0);
915 ref->alternate_path = talloc_asprintf(ctx, "\\%s", tmp);
918 if (!ref->alternate_path) {
920 return NT_STATUS_NO_MEMORY;
923 if (pdp->reqpath[0] != '\0') {
924 ref->alternate_path = talloc_asprintf_append(
928 if (!ref->alternate_path) {
930 return NT_STATUS_NO_MEMORY;
934 ref->ttl = REFERRAL_TTL;
935 jucn->referral_list = ref;
936 *consumedcntp = strlen(dfs_path);
941 status = create_conn_struct(ctx, &conn, snum, lp_pathname(snum),
943 if (!NT_STATUS_IS_OK(status)) {
948 /* If this is a DFS path dfs_lookup should return
949 * NT_STATUS_PATH_NOT_COVERED. */
951 status = dfs_path_lookup(ctx, conn, dfs_path, pdp,
952 False, consumedcntp, &targetpath);
954 if (!NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
955 DEBUG(3,("get_referred_path: No valid referrals for path %s\n",
960 /* We know this is a valid dfs link. Parse the targetpath. */
961 if (!parse_msdfs_symlink(ctx, targetpath,
962 &jucn->referral_list,
963 &jucn->referral_count)) {
964 DEBUG(3,("get_referred_path: failed to parse symlink "
965 "target %s\n", targetpath ));
966 status = NT_STATUS_NOT_FOUND;
970 status = NT_STATUS_OK;
972 vfs_ChDir(conn, oldpath);
973 SMB_VFS_DISCONNECT(conn);
979 static int setup_ver2_dfs_referral(const char *pathname,
981 struct junction_map *junction,
984 char* pdata = *ppdata;
986 smb_ucs2_t *uni_requestedpath = NULL;
987 int uni_reqpathoffset1,uni_reqpathoffset2;
989 int requestedpathlen=0;
994 DEBUG(10,("Setting up version2 referral\nRequested path:\n"));
996 requestedpathlen = rpcstr_push_talloc(talloc_tos(),
997 &uni_requestedpath, pathname);
998 if (uni_requestedpath == NULL || requestedpathlen == 0) {
1003 dump_data(0, (unsigned char *)uni_requestedpath,
1007 DEBUG(10,("ref count = %u\n",junction->referral_count));
1009 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
1010 VERSION2_REFERRAL_SIZE * junction->referral_count;
1012 uni_reqpathoffset2 = uni_reqpathoffset1 + requestedpathlen;
1014 uni_curroffset = uni_reqpathoffset2 + requestedpathlen;
1016 reply_size = REFERRAL_HEADER_SIZE +
1017 VERSION2_REFERRAL_SIZE*junction->referral_count +
1018 2 * requestedpathlen;
1019 DEBUG(10,("reply_size: %u\n",reply_size));
1021 /* add up the unicode lengths of all the referral paths */
1022 for(i=0;i<junction->referral_count;i++) {
1023 DEBUG(10,("referral %u : %s\n",
1025 junction->referral_list[i].alternate_path));
1027 (strlen(junction->referral_list[i].alternate_path)+1)*2;
1030 DEBUG(10,("reply_size = %u\n",reply_size));
1031 /* add the unexplained 0x16 bytes */
1034 pdata = (char *)SMB_REALLOC(pdata,reply_size);
1036 DEBUG(0,("Realloc failed!\n"));
1041 /* copy in the dfs requested paths.. required for offset calculations */
1042 memcpy(pdata+uni_reqpathoffset1,uni_requestedpath,requestedpathlen);
1043 memcpy(pdata+uni_reqpathoffset2,uni_requestedpath,requestedpathlen);
1045 /* create the header */
1046 SSVAL(pdata,0,requestedpathlen - 2); /* UCS2 of path consumed minus
1048 /* number of referral in this pkt */
1049 SSVAL(pdata,2,junction->referral_count);
1051 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
1053 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
1057 /* add the referral elements */
1058 for(i=0;i<junction->referral_count;i++) {
1059 struct referral* ref = &junction->referral_list[i];
1062 SSVAL(pdata,offset,2); /* version 2 */
1063 SSVAL(pdata,offset+2,VERSION2_REFERRAL_SIZE);
1065 SSVAL(pdata,offset+4,1);
1067 SSVAL(pdata,offset+4,0);
1070 /* ref_flags :use path_consumed bytes? */
1071 SSVAL(pdata,offset+6,0);
1072 SIVAL(pdata,offset+8,ref->proximity);
1073 SIVAL(pdata,offset+12,ref->ttl);
1075 SSVAL(pdata,offset+16,uni_reqpathoffset1-offset);
1076 SSVAL(pdata,offset+18,uni_reqpathoffset2-offset);
1077 /* copy referred path into current offset */
1078 unilen = rpcstr_push(pdata+uni_curroffset,
1079 ref->alternate_path,
1080 reply_size - uni_curroffset,
1083 SSVAL(pdata,offset+20,uni_curroffset-offset);
1085 uni_curroffset += unilen;
1086 offset += VERSION2_REFERRAL_SIZE;
1088 /* add in the unexplained 22 (0x16) bytes at the end */
1089 memset(pdata+uni_curroffset,'\0',0x16);
1093 static int setup_ver3_dfs_referral(const char *pathname,
1095 struct junction_map *junction,
1098 char *pdata = *ppdata;
1100 smb_ucs2_t *uni_reqpath = NULL;
1101 int uni_reqpathoffset1, uni_reqpathoffset2;
1108 DEBUG(10,("setting up version3 referral\n"));
1110 reqpathlen = rpcstr_push_talloc(talloc_tos(), &uni_reqpath, pathname);
1111 if (uni_reqpath == NULL || reqpathlen == 0) {
1116 dump_data(0, (unsigned char *)uni_reqpath,
1120 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
1121 VERSION3_REFERRAL_SIZE * junction->referral_count;
1122 uni_reqpathoffset2 = uni_reqpathoffset1 + reqpathlen;
1123 reply_size = uni_curroffset = uni_reqpathoffset2 + reqpathlen;
1125 for(i=0;i<junction->referral_count;i++) {
1126 DEBUG(10,("referral %u : %s\n",
1128 junction->referral_list[i].alternate_path));
1130 (strlen(junction->referral_list[i].alternate_path)+1)*2;
1133 pdata = (char *)SMB_REALLOC(pdata,reply_size);
1135 DEBUG(0,("version3 referral setup:"
1136 "malloc failed for Realloc!\n"));
1141 /* create the header */
1142 SSVAL(pdata,0,reqpathlen - 2); /* UCS2 of path consumed minus
1144 SSVAL(pdata,2,junction->referral_count); /* number of referral */
1146 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
1148 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
1151 /* copy in the reqpaths */
1152 memcpy(pdata+uni_reqpathoffset1,uni_reqpath,reqpathlen);
1153 memcpy(pdata+uni_reqpathoffset2,uni_reqpath,reqpathlen);
1156 for(i=0;i<junction->referral_count;i++) {
1157 struct referral* ref = &(junction->referral_list[i]);
1160 SSVAL(pdata,offset,3); /* version 3 */
1161 SSVAL(pdata,offset+2,VERSION3_REFERRAL_SIZE);
1163 SSVAL(pdata,offset+4,1);
1165 SSVAL(pdata,offset+4,0);
1168 /* ref_flags :use path_consumed bytes? */
1169 SSVAL(pdata,offset+6,0);
1170 SIVAL(pdata,offset+8,ref->ttl);
1172 SSVAL(pdata,offset+12,uni_reqpathoffset1-offset);
1173 SSVAL(pdata,offset+14,uni_reqpathoffset2-offset);
1174 /* copy referred path into current offset */
1175 unilen = rpcstr_push(pdata+uni_curroffset,ref->alternate_path,
1176 reply_size - uni_curroffset,
1177 STR_UNICODE | STR_TERMINATE);
1178 SSVAL(pdata,offset+16,uni_curroffset-offset);
1179 /* copy 0x10 bytes of 00's in the ServiceSite GUID */
1180 memset(pdata+offset+18,'\0',16);
1182 uni_curroffset += unilen;
1183 offset += VERSION3_REFERRAL_SIZE;
1188 /******************************************************************
1189 Set up the DFS referral for the dfs pathname. This call returns
1190 the amount of the path covered by this server, and where the
1191 client should be redirected to. This is the meat of the
1192 TRANS2_GET_DFS_REFERRAL call.
1193 ******************************************************************/
1195 int setup_dfs_referral(connection_struct *orig_conn,
1196 const char *dfs_path,
1197 int max_referral_level,
1198 char **ppdata, NTSTATUS *pstatus)
1200 struct junction_map *junction = NULL;
1201 int consumedcnt = 0;
1202 bool self_referral = False;
1204 char *pathnamep = NULL;
1205 char *local_dfs_path = NULL;
1208 if (!(ctx=talloc_init("setup_dfs_referral"))) {
1209 *pstatus = NT_STATUS_NO_MEMORY;
1213 /* get the junction entry */
1215 talloc_destroy(ctx);
1216 *pstatus = NT_STATUS_NOT_FOUND;
1221 * Trim pathname sent by client so it begins with only one backslash.
1222 * Two backslashes confuse some dfs clients
1225 local_dfs_path = talloc_strdup(ctx,dfs_path);
1226 if (!local_dfs_path) {
1227 *pstatus = NT_STATUS_NO_MEMORY;
1228 talloc_destroy(ctx);
1231 pathnamep = local_dfs_path;
1232 while (IS_DIRECTORY_SEP(pathnamep[0]) &&
1233 IS_DIRECTORY_SEP(pathnamep[1])) {
1237 junction = talloc_zero(ctx, struct junction_map);
1239 *pstatus = NT_STATUS_NO_MEMORY;
1240 talloc_destroy(ctx);
1244 /* The following call can change cwd. */
1245 *pstatus = get_referred_path(ctx, pathnamep, junction,
1246 &consumedcnt, &self_referral);
1247 if (!NT_STATUS_IS_OK(*pstatus)) {
1248 vfs_ChDir(orig_conn,orig_conn->connectpath);
1249 talloc_destroy(ctx);
1252 vfs_ChDir(orig_conn,orig_conn->connectpath);
1254 if (!self_referral) {
1255 pathnamep[consumedcnt] = '\0';
1257 if( DEBUGLVL( 3 ) ) {
1259 dbgtext("setup_dfs_referral: Path %s to "
1260 "alternate path(s):",
1262 for(i=0;i<junction->referral_count;i++)
1264 junction->referral_list[i].alternate_path);
1269 /* create the referral depeding on version */
1270 DEBUG(10,("max_referral_level :%d\n",max_referral_level));
1272 if (max_referral_level < 2) {
1273 max_referral_level = 2;
1275 if (max_referral_level > 3) {
1276 max_referral_level = 3;
1279 switch(max_referral_level) {
1281 reply_size = setup_ver2_dfs_referral(pathnamep,
1286 reply_size = setup_ver3_dfs_referral(pathnamep, ppdata,
1287 junction, self_referral);
1290 DEBUG(0,("setup_dfs_referral: Invalid dfs referral "
1292 max_referral_level));
1293 talloc_destroy(ctx);
1294 *pstatus = NT_STATUS_INVALID_LEVEL;
1299 DEBUGADD(0,("DFS Referral pdata:\n"));
1300 dump_data(0,(uint8 *)*ppdata,reply_size);
1303 talloc_destroy(ctx);
1304 *pstatus = NT_STATUS_OK;
1308 /**********************************************************************
1309 The following functions are called by the NETDFS RPC pipe functions
1310 **********************************************************************/
1312 /*********************************************************************
1313 Creates a junction structure from a DFS pathname
1314 **********************************************************************/
1316 bool create_junction(TALLOC_CTX *ctx,
1317 const char *dfs_path,
1318 struct junction_map *jucn)
1322 struct dfs_path *pdp = talloc(ctx,struct dfs_path);
1328 status = parse_dfs_path(NULL, dfs_path, False, pdp, &dummy);
1329 if (!NT_STATUS_IS_OK(status)) {
1333 /* check if path is dfs : validate first token */
1334 if (!is_myname_or_ipaddr(pdp->hostname)) {
1335 DEBUG(4,("create_junction: Invalid hostname %s "
1337 pdp->hostname, dfs_path));
1342 /* Check for a non-DFS share */
1343 snum = lp_servicenumber(pdp->servicename);
1345 if(snum < 0 || !lp_msdfs_root(snum)) {
1346 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1352 jucn->service_name = talloc_strdup(ctx, pdp->servicename);
1353 jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
1354 jucn->comment = talloc_strdup(ctx, lp_comment(snum));
1357 if (!jucn->service_name || !jucn->volume_name || ! jucn->comment) {
1363 /**********************************************************************
1364 Forms a valid Unix pathname from the junction
1365 **********************************************************************/
1367 static bool junction_to_local_path(const struct junction_map *jucn,
1369 connection_struct **conn_out,
1375 snum = lp_servicenumber(jucn->service_name);
1379 status = create_conn_struct(talloc_tos(), conn_out, snum,
1380 lp_pathname(snum), NULL, oldpath);
1381 if (!NT_STATUS_IS_OK(status)) {
1385 *pp_path_out = talloc_asprintf(*conn_out,
1389 if (!*pp_path_out) {
1390 vfs_ChDir(*conn_out, *oldpath);
1391 SMB_VFS_DISCONNECT(*conn_out);
1392 conn_free(*conn_out);
1398 bool create_msdfs_link(const struct junction_map *jucn)
1402 char *msdfs_link = NULL;
1403 connection_struct *conn;
1405 bool insert_comma = False;
1408 if(!junction_to_local_path(jucn, &path, &conn, &cwd)) {
1412 /* Form the msdfs_link contents */
1413 msdfs_link = talloc_strdup(conn, "msdfs:");
1417 for(i=0; i<jucn->referral_count; i++) {
1418 char *refpath = jucn->referral_list[i].alternate_path;
1420 /* Alternate paths always use Windows separators. */
1421 trim_char(refpath, '\\', '\\');
1422 if(*refpath == '\0') {
1424 insert_comma = False;
1428 if (i > 0 && insert_comma) {
1429 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1433 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1441 if (!insert_comma) {
1442 insert_comma = True;
1446 DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
1449 if(SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
1450 if (errno == EEXIST) {
1451 struct smb_filename *smb_fname = NULL;
1454 status = create_synthetic_smb_fname(talloc_tos(), path,
1457 if (!NT_STATUS_IS_OK(status)) {
1458 errno = map_errno_from_nt_status(status);
1462 if(SMB_VFS_UNLINK(conn, smb_fname)!=0) {
1463 TALLOC_FREE(smb_fname);
1466 TALLOC_FREE(smb_fname);
1468 if (SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
1469 DEBUG(1,("create_msdfs_link: symlink failed "
1470 "%s -> %s\nError: %s\n",
1471 path, msdfs_link, strerror(errno)));
1479 vfs_ChDir(conn, cwd);
1480 SMB_VFS_DISCONNECT(conn);
1485 bool remove_msdfs_link(const struct junction_map *jucn)
1489 connection_struct *conn;
1491 struct smb_filename *smb_fname = NULL;
1494 if (!junction_to_local_path(jucn, &path, &conn, &cwd)) {
1498 status = create_synthetic_smb_fname(talloc_tos(), path,
1501 if (!NT_STATUS_IS_OK(status)) {
1502 errno = map_errno_from_nt_status(status);
1506 if( SMB_VFS_UNLINK(conn, smb_fname) == 0 ) {
1510 TALLOC_FREE(smb_fname);
1511 vfs_ChDir(conn, cwd);
1512 SMB_VFS_DISCONNECT(conn);
1517 /*********************************************************************
1518 Return the number of DFS links at the root of this share.
1519 *********************************************************************/
1521 static int count_dfs_links(TALLOC_CTX *ctx, int snum)
1524 SMB_STRUCT_DIR *dirp = NULL;
1525 const char *dname = NULL;
1526 char *talloced = NULL;
1527 const char *connect_path = lp_pathname(snum);
1528 const char *msdfs_proxy = lp_msdfs_proxy(snum);
1529 connection_struct *conn;
1533 if(*connect_path == '\0') {
1538 * Fake up a connection struct for the VFS layer.
1541 status = create_conn_struct(talloc_tos(), &conn, snum, connect_path,
1543 if (!NT_STATUS_IS_OK(status)) {
1544 DEBUG(3, ("create_conn_struct failed: %s\n",
1545 nt_errstr(status)));
1549 /* Count a link for the msdfs root - convention */
1552 /* No more links if this is an msdfs proxy. */
1553 if (*msdfs_proxy != '\0') {
1557 /* Now enumerate all dfs links */
1558 dirp = SMB_VFS_OPENDIR(conn, ".", NULL, 0);
1563 while ((dname = vfs_readdirname(conn, dirp, NULL, &talloced))
1565 if (is_msdfs_link(conn,
1570 TALLOC_FREE(talloced);
1573 SMB_VFS_CLOSEDIR(conn,dirp);
1576 vfs_ChDir(conn, cwd);
1577 SMB_VFS_DISCONNECT(conn);
1582 /*********************************************************************
1583 *********************************************************************/
1585 static int form_junctions(TALLOC_CTX *ctx,
1587 struct junction_map *jucn,
1591 SMB_STRUCT_DIR *dirp = NULL;
1592 const char *dname = NULL;
1593 char *talloced = NULL;
1594 const char *connect_path = lp_pathname(snum);
1595 char *service_name = lp_servicename(snum);
1596 const char *msdfs_proxy = lp_msdfs_proxy(snum);
1597 connection_struct *conn;
1598 struct referral *ref = NULL;
1602 if (jn_remain == 0) {
1606 if(*connect_path == '\0') {
1611 * Fake up a connection struct for the VFS layer.
1614 status = create_conn_struct(ctx, &conn, snum, connect_path, NULL,
1616 if (!NT_STATUS_IS_OK(status)) {
1617 DEBUG(3, ("create_conn_struct failed: %s\n",
1618 nt_errstr(status)));
1622 /* form a junction for the msdfs root - convention
1623 DO NOT REMOVE THIS: NT clients will not work with us
1624 if this is not present
1626 jucn[cnt].service_name = talloc_strdup(ctx,service_name);
1627 jucn[cnt].volume_name = talloc_strdup(ctx, "");
1628 if (!jucn[cnt].service_name || !jucn[cnt].volume_name) {
1631 jucn[cnt].comment = "";
1632 jucn[cnt].referral_count = 1;
1634 ref = jucn[cnt].referral_list = talloc_zero(ctx, struct referral);
1635 if (jucn[cnt].referral_list == NULL) {
1640 ref->ttl = REFERRAL_TTL;
1641 if (*msdfs_proxy != '\0') {
1642 ref->alternate_path = talloc_strdup(ctx,
1645 ref->alternate_path = talloc_asprintf(ctx,
1647 get_local_machine_name(),
1651 if (!ref->alternate_path) {
1656 /* Don't enumerate if we're an msdfs proxy. */
1657 if (*msdfs_proxy != '\0') {
1661 /* Now enumerate all dfs links */
1662 dirp = SMB_VFS_OPENDIR(conn, ".", NULL, 0);
1667 while ((dname = vfs_readdirname(conn, dirp, NULL, &talloced))
1669 char *link_target = NULL;
1670 if (cnt >= jn_remain) {
1671 DEBUG(2, ("form_junctions: ran out of MSDFS "
1673 TALLOC_FREE(talloced);
1676 if (is_msdfs_link_internal(ctx,
1678 dname, &link_target,
1680 if (parse_msdfs_symlink(ctx,
1682 &jucn[cnt].referral_list,
1683 &jucn[cnt].referral_count)) {
1685 jucn[cnt].service_name = talloc_strdup(ctx,
1687 jucn[cnt].volume_name = talloc_strdup(ctx,
1689 if (!jucn[cnt].service_name ||
1690 !jucn[cnt].volume_name) {
1691 TALLOC_FREE(talloced);
1694 jucn[cnt].comment = "";
1697 TALLOC_FREE(link_target);
1699 TALLOC_FREE(talloced);
1705 SMB_VFS_CLOSEDIR(conn,dirp);
1708 vfs_ChDir(conn, cwd);
1713 struct junction_map *enum_msdfs_links(struct smbd_server_connection *sconn,
1714 TALLOC_CTX *ctx, size_t *p_num_jn)
1716 struct junction_map *jn = NULL;
1718 size_t jn_count = 0;
1722 if(!lp_host_msdfs()) {
1726 /* Ensure all the usershares are loaded. */
1728 load_registry_shares();
1729 sharecount = load_usershare_shares(sconn);
1732 for(i=0;i < sharecount;i++) {
1733 if(lp_msdfs_root(i)) {
1734 jn_count += count_dfs_links(ctx, i);
1737 if (jn_count == 0) {
1740 jn = talloc_array(ctx, struct junction_map, jn_count);
1744 for(i=0; i < sharecount; i++) {
1745 if (*p_num_jn >= jn_count) {
1748 if(lp_msdfs_root(i)) {
1749 *p_num_jn += form_junctions(ctx, i,
1751 jn_count - *p_num_jn);
1757 /******************************************************************************
1758 Core function to resolve a dfs pathname possibly containing a wildcard. If
1759 ppath_contains_wcard != NULL, it will be set to true if a wildcard is
1760 detected during dfs resolution.
1761 ******************************************************************************/
1763 NTSTATUS resolve_dfspath_wcard(TALLOC_CTX *ctx,
1764 connection_struct *conn,
1766 const char *name_in,
1769 bool *ppath_contains_wcard)
1771 bool path_contains_wcard;
1772 NTSTATUS status = NT_STATUS_OK;
1774 if (dfs_pathnames) {
1775 status = dfs_redirect(ctx,
1780 &path_contains_wcard);
1782 if (NT_STATUS_IS_OK(status) && ppath_contains_wcard != NULL) {
1783 *ppath_contains_wcard = path_contains_wcard;
1787 * Cheat and just return a copy of the in ptr.
1788 * Once srvstr_get_path() uses talloc it'll
1789 * be a talloced ptr anyway.
1791 *pp_name_out = discard_const_p(char, name_in);