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"
31 #include "libcli/security/security.h"
33 /**********************************************************************
34 Parse a DFS pathname of the form \hostname\service\reqpath
35 into the dfs_path structure.
36 If POSIX pathnames is true, the pathname may also be of the
37 form /hostname/service/reqpath.
38 We cope with either here.
40 Unfortunately, due to broken clients who might set the
41 SVAL(inbuf,smb_flg2) & FLAGS2_DFS_PATHNAMES bit and then
42 send a local path, we have to cope with that too....
44 If conn != NULL then ensure the provided service is
45 the one pointed to by the connection.
47 This version does everything using pointers within one copy of the
48 pathname string, talloced on the struct dfs_path pointer (which
49 must be talloced). This may be too clever to live....
51 **********************************************************************/
53 static NTSTATUS parse_dfs_path(connection_struct *conn,
56 bool allow_broken_path,
57 struct dfs_path *pdp, /* MUST BE TALLOCED */
58 bool *ppath_contains_wcard)
64 NTSTATUS status = NT_STATUS_OK;
70 * This is the only talloc we should need to do
71 * on the struct dfs_path. All the pointers inside
72 * it should point to offsets within this string.
75 pathname_local = talloc_strdup(pdp, pathname);
76 if (!pathname_local) {
77 return NT_STATUS_NO_MEMORY;
79 /* Get a pointer to the terminating '\0' */
80 eos_ptr = &pathname_local[strlen(pathname_local)];
81 p = temp = pathname_local;
83 pdp->posix_path = (lp_posix_pathnames() && *pathname == '/');
85 sepchar = pdp->posix_path ? '/' : '\\';
87 if (allow_broken_path && (*pathname != sepchar)) {
88 DEBUG(10,("parse_dfs_path: path %s doesn't start with %c\n",
91 * Possibly client sent a local path by mistake.
92 * Try and convert to a local path.
95 pdp->hostname = eos_ptr; /* "" */
96 pdp->servicename = eos_ptr; /* "" */
98 /* We've got no info about separators. */
99 pdp->posix_path = lp_posix_pathnames();
101 DEBUG(10,("parse_dfs_path: trying to convert %s to a "
108 * Safe to use on talloc'ed string as it only shrinks.
109 * It also doesn't affect the eos_ptr.
111 trim_char(temp,sepchar,sepchar);
113 DEBUG(10,("parse_dfs_path: temp = |%s| after trimming %c's\n",
117 /* Parse out hostname. */
118 p = strchr_m(temp,sepchar);
120 DEBUG(10,("parse_dfs_path: can't parse hostname from path %s\n",
123 * Possibly client sent a local path by mistake.
124 * Try and convert to a local path.
127 pdp->hostname = eos_ptr; /* "" */
128 pdp->servicename = eos_ptr; /* "" */
131 DEBUG(10,("parse_dfs_path: trying to convert %s "
137 pdp->hostname = temp;
139 DEBUG(10,("parse_dfs_path: hostname: %s\n",pdp->hostname));
141 /* Parse out servicename. */
143 p = strchr_m(servicename,sepchar);
148 /* Is this really our servicename ? */
149 if (conn && !( strequal(servicename, lp_servicename(SNUM(conn)))
150 || (strequal(servicename, HOMES_NAME)
151 && strequal(lp_servicename(SNUM(conn)),
152 get_current_username()) )) ) {
153 DEBUG(10,("parse_dfs_path: %s is not our servicename\n",
157 * Possibly client sent a local path by mistake.
158 * Try and convert to a local path.
161 pdp->hostname = eos_ptr; /* "" */
162 pdp->servicename = eos_ptr; /* "" */
164 /* Repair the path - replace the sepchar's
167 *servicename = sepchar;
173 DEBUG(10,("parse_dfs_path: trying to convert %s "
179 pdp->servicename = servicename;
181 DEBUG(10,("parse_dfs_path: servicename: %s\n",pdp->servicename));
184 /* Client sent self referral \server\share. */
185 pdp->reqpath = eos_ptr; /* "" */
193 *ppath_contains_wcard = False;
197 /* Rest is reqpath. */
198 if (pdp->posix_path) {
199 status = check_path_syntax_posix(pdp->reqpath);
202 status = check_path_syntax_wcard(pdp->reqpath,
203 ppath_contains_wcard);
205 status = check_path_syntax(pdp->reqpath);
209 if (!NT_STATUS_IS_OK(status)) {
210 DEBUG(10,("parse_dfs_path: '%s' failed with %s\n",
211 p, nt_errstr(status) ));
215 DEBUG(10,("parse_dfs_path: rest of the path: %s\n",pdp->reqpath));
219 /********************************************************
220 Fake up a connection struct for the VFS layer.
221 Note: this performs a vfs connect and CHANGES CWD !!!! JRA.
222 *********************************************************/
224 NTSTATUS create_conn_struct(TALLOC_CTX *ctx,
225 struct smbd_server_connection *sconn,
226 connection_struct **pconn,
229 const struct auth_session_info *session_info,
232 connection_struct *conn;
235 const char *vfs_user;
237 conn = talloc_zero(ctx, connection_struct);
239 return NT_STATUS_NO_MEMORY;
242 connpath = talloc_strdup(conn, path);
245 return NT_STATUS_NO_MEMORY;
247 connpath = talloc_string_sub(conn,
250 lp_servicename(snum));
253 return NT_STATUS_NO_MEMORY;
256 /* needed for smbd_vfs_init() */
258 if (!(conn->params = talloc_zero(conn, struct share_params))) {
259 DEBUG(0, ("TALLOC failed\n"));
261 return NT_STATUS_NO_MEMORY;
264 conn->params->service = snum;
267 conn->sconn->num_tcons_open++;
269 if (session_info != NULL) {
270 conn->session_info = copy_session_info(conn, session_info);
271 if (conn->session_info == NULL) {
272 DEBUG(0, ("copy_serverinfo failed\n"));
274 return NT_STATUS_NO_MEMORY;
276 vfs_user = conn->session_info->unix_info->unix_name;
278 /* use current authenticated user in absence of session_info */
279 vfs_user = get_current_username();
282 set_conn_connectpath(conn, connpath);
285 * New code to check if there's a share security descripter
286 * added from NT server manager. This is done after the
287 * smb.conf checks are done as we need a uid and token. JRA.
290 if (conn->session_info) {
291 share_access_check(conn->session_info->security_token,
292 lp_servicename(snum), MAXIMUM_ALLOWED_ACCESS,
293 &conn->share_access);
295 if ((conn->share_access & FILE_WRITE_DATA) == 0) {
296 if ((conn->share_access & FILE_READ_DATA) == 0) {
297 /* No access, read or write. */
298 DEBUG(0,("create_conn_struct: connection to %s "
299 "denied due to security "
301 lp_servicename(snum)));
303 return NT_STATUS_ACCESS_DENIED;
305 conn->read_only = true;
309 conn->share_access = 0;
310 conn->read_only = true;
313 if (!smbd_vfs_init(conn)) {
314 NTSTATUS status = map_nt_error_from_unix(errno);
315 DEBUG(0,("create_conn_struct: smbd_vfs_init failed.\n"));
320 /* this must be the first filesystem operation that we do */
321 if (SMB_VFS_CONNECT(conn, lp_servicename(snum), vfs_user) < 0) {
322 DEBUG(0,("VFS connect failed!\n"));
324 return NT_STATUS_UNSUCCESSFUL;
327 conn->fs_capabilities = SMB_VFS_FS_CAPABILITIES(conn, &conn->ts_res);
330 * Windows seems to insist on doing trans2getdfsreferral() calls on
331 * the IPC$ share as the anonymous user. If we try to chdir as that
332 * user we will fail.... WTF ? JRA.
335 oldcwd = vfs_GetWd(ctx, conn);
336 if (oldcwd == NULL) {
337 NTSTATUS status = map_nt_error_from_unix(errno);
338 DEBUG(3, ("vfs_GetWd failed: %s\n", strerror(errno)));
343 if (vfs_ChDir(conn,conn->connectpath) != 0) {
344 NTSTATUS status = map_nt_error_from_unix(errno);
345 DEBUG(3,("create_conn_struct: Can't ChDir to new conn path %s. "
347 conn->connectpath, strerror(errno) ));
358 /**********************************************************************
359 Parse the contents of a symlink to verify if it is an msdfs referral
360 A valid referral is of the form:
362 msdfs:server1\share1,server2\share2
363 msdfs:server1\share1\pathname,server2\share2\pathname
364 msdfs:server1/share1,server2/share2
365 msdfs:server1/share1/pathname,server2/share2/pathname.
367 Note that the alternate paths returned here must be of the canonicalized
371 \server\share\path\to\file,
373 even in posix path mode. This is because we have no knowledge if the
374 server we're referring to understands posix paths.
375 **********************************************************************/
377 static bool parse_msdfs_symlink(TALLOC_CTX *ctx,
379 struct referral **preflist,
384 char **alt_path = NULL;
386 struct referral *reflist;
389 temp = talloc_strdup(ctx, target);
393 prot = strtok_r(temp, ":", &saveptr);
395 DEBUG(0,("parse_msdfs_symlink: invalid path !\n"));
399 alt_path = talloc_array(ctx, char *, MAX_REFERRAL_COUNT);
404 /* parse out the alternate paths */
405 while((count<MAX_REFERRAL_COUNT) &&
406 ((alt_path[count] = strtok_r(NULL, ",", &saveptr)) != NULL)) {
410 DEBUG(10,("parse_msdfs_symlink: count=%d\n", count));
413 reflist = *preflist = talloc_zero_array(ctx,
414 struct referral, count);
415 if(reflist == NULL) {
416 TALLOC_FREE(alt_path);
420 reflist = *preflist = NULL;
423 for(i=0;i<count;i++) {
426 /* Canonicalize link target.
427 * Replace all /'s in the path by a \ */
428 string_replace(alt_path[i], '/', '\\');
430 /* Remove leading '\\'s */
432 while (*p && (*p == '\\')) {
436 reflist[i].alternate_path = talloc_asprintf(ctx,
439 if (!reflist[i].alternate_path) {
443 reflist[i].proximity = 0;
444 reflist[i].ttl = REFERRAL_TTL;
445 DEBUG(10, ("parse_msdfs_symlink: Created alt path: %s\n",
446 reflist[i].alternate_path));
451 TALLOC_FREE(alt_path);
455 /**********************************************************************
456 Returns true if the unix path is a valid msdfs symlink and also
457 returns the target string from inside the link.
458 **********************************************************************/
460 static bool is_msdfs_link_internal(TALLOC_CTX *ctx,
461 connection_struct *conn,
463 char **pp_link_target,
464 SMB_STRUCT_STAT *sbufp)
466 int referral_len = 0;
467 #if defined(HAVE_BROKEN_READLINK)
468 char link_target_buf[PATH_MAX];
470 char link_target_buf[7];
473 char *link_target = NULL;
474 struct smb_filename smb_fname;
476 if (pp_link_target) {
478 link_target = talloc_array(ctx, char, bufsize);
482 *pp_link_target = link_target;
484 bufsize = sizeof(link_target_buf);
485 link_target = link_target_buf;
488 ZERO_STRUCT(smb_fname);
489 smb_fname.base_name = discard_const_p(char, path);
491 if (SMB_VFS_LSTAT(conn, &smb_fname) != 0) {
492 DEBUG(5,("is_msdfs_link_read_target: %s does not exist.\n",
496 if (!S_ISLNK(smb_fname.st.st_ex_mode)) {
497 DEBUG(5,("is_msdfs_link_read_target: %s is not a link.\n",
502 *sbufp = smb_fname.st;
505 referral_len = SMB_VFS_READLINK(conn, path, link_target, bufsize - 1);
506 if (referral_len == -1) {
507 DEBUG(0,("is_msdfs_link_read_target: Error reading "
508 "msdfs link %s: %s\n",
509 path, strerror(errno)));
512 link_target[referral_len] = '\0';
514 DEBUG(5,("is_msdfs_link_internal: %s -> %s\n",path,
517 if (!strnequal(link_target, "msdfs:", 6)) {
524 if (link_target != link_target_buf) {
525 TALLOC_FREE(link_target);
530 /**********************************************************************
531 Returns true if the unix path is a valid msdfs symlink.
532 **********************************************************************/
534 bool is_msdfs_link(connection_struct *conn,
536 SMB_STRUCT_STAT *sbufp)
538 return is_msdfs_link_internal(talloc_tos(),
545 /*****************************************************************
546 Used by other functions to decide if a dfs path is remote,
547 and to get the list of referred locations for that remote path.
549 search_flag: For findfirsts, dfs links themselves are not
550 redirected, but paths beyond the links are. For normal smb calls,
551 even dfs links need to be redirected.
553 consumedcntp: how much of the dfs path is being redirected. the client
554 should try the remaining path on the redirected server.
556 If this returns NT_STATUS_PATH_NOT_COVERED the contents of the msdfs
557 link redirect are in targetpath.
558 *****************************************************************/
560 static NTSTATUS dfs_path_lookup(TALLOC_CTX *ctx,
561 connection_struct *conn,
562 const char *dfspath, /* Incoming complete dfs path */
563 const struct dfs_path *pdp, /* Parsed out
564 server+share+extrapath. */
565 bool search_flag, /* Called from a findfirst ? */
567 char **pp_targetpath)
572 struct smb_filename *smb_fname = NULL;
573 char *canon_dfspath = NULL; /* Canonicalized dfs path. (only '/'
576 DEBUG(10,("dfs_path_lookup: Conn path = %s reqpath = %s\n",
577 conn->connectpath, pdp->reqpath));
580 * Note the unix path conversion here we're doing we can
581 * throw away. We're looking for a symlink for a dfs
582 * resolution, if we don't find it we'll do another
583 * unix_convert later in the codepath.
584 * If we needed to remember what we'd resolved in
585 * dp->reqpath (as the original code did) we'd
586 * copy (localhost, dp->reqpath) on any code
587 * path below that returns True - but I don't
588 * think this is needed. JRA.
591 status = unix_convert(ctx, conn, pdp->reqpath, &smb_fname,
592 search_flag ? UCF_ALWAYS_ALLOW_WCARD_LCOMP : 0);
594 if (!NT_STATUS_IS_OK(status)) {
595 if (!NT_STATUS_EQUAL(status,
596 NT_STATUS_OBJECT_PATH_NOT_FOUND)) {
600 /* Create an smb_fname to use below. */
601 status = create_synthetic_smb_fname(ctx, pdp->reqpath, NULL,
603 if (!NT_STATUS_IS_OK(status)) {
608 /* Optimization - check if we can redirect the whole path. */
610 if (is_msdfs_link_internal(ctx, conn, smb_fname->base_name,
611 pp_targetpath, NULL)) {
613 DEBUG(6,("dfs_path_lookup (FindFirst) No redirection "
614 "for dfs link %s.\n", dfspath));
615 status = NT_STATUS_OK;
619 DEBUG(6,("dfs_path_lookup: %s resolves to a "
620 "valid dfs link %s.\n", dfspath,
621 pp_targetpath ? *pp_targetpath : ""));
624 *consumedcntp = strlen(dfspath);
626 status = NT_STATUS_PATH_NOT_COVERED;
630 /* Prepare to test only for '/' components in the given path,
631 * so if a Windows path replace all '\\' characters with '/'.
632 * For a POSIX DFS path we know all separators are already '/'. */
634 canon_dfspath = talloc_strdup(ctx, dfspath);
635 if (!canon_dfspath) {
636 status = NT_STATUS_NO_MEMORY;
639 if (!pdp->posix_path) {
640 string_replace(canon_dfspath, '\\', '/');
644 * localpath comes out of unix_convert, so it has
645 * no trailing backslash. Make sure that canon_dfspath hasn't either.
646 * Fix for bug #4860 from Jan Martin <Jan.Martin@rwedea.com>.
649 trim_char(canon_dfspath,0,'/');
652 * Redirect if any component in the path is a link.
653 * We do this by walking backwards through the
654 * local path, chopping off the last component
655 * in both the local path and the canonicalized
656 * DFS path. If we hit a DFS link then we're done.
659 p = strrchr_m(smb_fname->base_name, '/');
661 q = strrchr_m(canon_dfspath, '/');
670 if (is_msdfs_link_internal(ctx, conn,
671 smb_fname->base_name, pp_targetpath,
673 DEBUG(4, ("dfs_path_lookup: Redirecting %s because "
674 "parent %s is dfs link\n", dfspath,
675 smb_fname_str_dbg(smb_fname)));
678 *consumedcntp = strlen(canon_dfspath);
679 DEBUG(10, ("dfs_path_lookup: Path consumed: %s "
685 status = NT_STATUS_PATH_NOT_COVERED;
689 /* Step back on the filesystem. */
690 p = strrchr_m(smb_fname->base_name, '/');
693 /* And in the canonicalized dfs path. */
694 q = strrchr_m(canon_dfspath, '/');
698 status = NT_STATUS_OK;
700 TALLOC_FREE(smb_fname);
704 /*****************************************************************
705 Decides if a dfs pathname should be redirected or not.
706 If not, the pathname is converted to a tcon-relative local unix path
708 search_wcard_flag: this flag performs 2 functions both related
709 to searches. See resolve_dfs_path() and parse_dfs_path_XX()
712 This function can return NT_STATUS_OK, meaning use the returned path as-is
713 (mapped into a local path).
714 or NT_STATUS_NOT_COVERED meaning return a DFS redirect, or
715 any other NT_STATUS error which is a genuine error to be
716 returned to the client.
717 *****************************************************************/
719 static NTSTATUS dfs_redirect(TALLOC_CTX *ctx,
720 connection_struct *conn,
722 bool search_wcard_flag,
723 bool allow_broken_path,
725 bool *ppath_contains_wcard)
728 struct dfs_path *pdp = talloc(ctx, struct dfs_path);
731 return NT_STATUS_NO_MEMORY;
734 status = parse_dfs_path(conn, path_in, search_wcard_flag,
735 allow_broken_path, pdp,
736 ppath_contains_wcard);
737 if (!NT_STATUS_IS_OK(status)) {
742 if (pdp->reqpath[0] == '\0') {
744 *pp_path_out = talloc_strdup(ctx, "");
746 return NT_STATUS_NO_MEMORY;
748 DEBUG(5,("dfs_redirect: self-referral.\n"));
752 /* If dfs pathname for a non-dfs share, convert to tcon-relative
753 path and return OK */
755 if (!lp_msdfs_root(SNUM(conn))) {
756 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
759 return NT_STATUS_NO_MEMORY;
764 /* If it looked like a local path (zero hostname/servicename)
765 * just treat as a tcon-relative path. */
767 if (pdp->hostname[0] == '\0' && pdp->servicename[0] == '\0') {
768 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
771 return NT_STATUS_NO_MEMORY;
776 if (!( strequal(pdp->servicename, lp_servicename(SNUM(conn)))
777 || (strequal(pdp->servicename, HOMES_NAME)
778 && strequal(lp_servicename(SNUM(conn)),
779 conn->session_info->unix_info->sanitized_username) )) ) {
781 /* The given sharename doesn't match this connection. */
784 return NT_STATUS_OBJECT_PATH_NOT_FOUND;
787 status = dfs_path_lookup(ctx, conn, path_in, pdp,
788 search_wcard_flag, NULL, NULL);
789 if (!NT_STATUS_IS_OK(status)) {
790 if (NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
791 DEBUG(3,("dfs_redirect: Redirecting %s\n", path_in));
793 DEBUG(10,("dfs_redirect: dfs_path_lookup "
794 "failed for %s with %s\n",
795 path_in, nt_errstr(status) ));
800 DEBUG(3,("dfs_redirect: Not redirecting %s.\n", path_in));
802 /* Form non-dfs tcon-relative path */
803 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
806 return NT_STATUS_NO_MEMORY;
809 DEBUG(3,("dfs_redirect: Path %s converted to non-dfs path %s\n",
816 /**********************************************************************
817 Return a self referral.
818 **********************************************************************/
820 static NTSTATUS self_ref(TALLOC_CTX *ctx,
821 const char *dfs_path,
822 struct junction_map *jucn,
824 bool *self_referralp)
826 struct referral *ref;
828 *self_referralp = True;
830 jucn->referral_count = 1;
831 if((ref = talloc_zero(ctx, struct referral)) == NULL) {
832 return NT_STATUS_NO_MEMORY;
835 ref->alternate_path = talloc_strdup(ctx, dfs_path);
836 if (!ref->alternate_path) {
837 return NT_STATUS_NO_MEMORY;
840 ref->ttl = REFERRAL_TTL;
841 jucn->referral_list = ref;
842 *consumedcntp = strlen(dfs_path);
846 /**********************************************************************
847 Gets valid referrals for a dfs path and fills up the
848 junction_map structure.
849 **********************************************************************/
851 NTSTATUS get_referred_path(TALLOC_CTX *ctx,
852 const char *dfs_path,
853 struct junction_map *jucn,
855 bool *self_referralp)
857 struct connection_struct *conn;
858 char *targetpath = NULL;
860 NTSTATUS status = NT_STATUS_NOT_FOUND;
862 struct dfs_path *pdp = talloc(ctx, struct dfs_path);
866 return NT_STATUS_NO_MEMORY;
869 *self_referralp = False;
871 status = parse_dfs_path(NULL, dfs_path, False,
872 !smbd_server_conn->using_smb2, pdp, &dummy);
873 if (!NT_STATUS_IS_OK(status)) {
877 jucn->service_name = talloc_strdup(ctx, pdp->servicename);
878 jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
879 if (!jucn->service_name || !jucn->volume_name) {
881 return NT_STATUS_NO_MEMORY;
884 /* Verify the share is a dfs root */
885 snum = lp_servicenumber(jucn->service_name);
887 char *service_name = NULL;
888 if ((snum = find_service(ctx, jucn->service_name, &service_name)) < 0) {
889 return NT_STATUS_NOT_FOUND;
892 return NT_STATUS_NO_MEMORY;
894 TALLOC_FREE(jucn->service_name);
895 jucn->service_name = talloc_strdup(ctx, service_name);
896 if (!jucn->service_name) {
898 return NT_STATUS_NO_MEMORY;
902 if (!lp_msdfs_root(snum) && (*lp_msdfs_proxy(snum) == '\0')) {
903 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not "
905 pdp->servicename, dfs_path));
907 return NT_STATUS_NOT_FOUND;
911 * Self referrals are tested with a anonymous IPC connection and
912 * a GET_DFS_REFERRAL call to \\server\share. (which means
913 * dp.reqpath[0] points to an empty string). create_conn_struct cd's
914 * into the directory and will fail if it cannot (as the anonymous
915 * user). Cope with this.
918 if (pdp->reqpath[0] == '\0') {
920 struct referral *ref;
922 if (*lp_msdfs_proxy(snum) == '\0') {
932 * It's an msdfs proxy share. Redirect to
933 * the configured target share.
936 jucn->referral_count = 1;
937 if ((ref = talloc_zero(ctx, struct referral)) == NULL) {
939 return NT_STATUS_NO_MEMORY;
942 if (!(tmp = talloc_strdup(ctx, lp_msdfs_proxy(snum)))) {
944 return NT_STATUS_NO_MEMORY;
947 trim_string(tmp, "\\", 0);
949 ref->alternate_path = talloc_asprintf(ctx, "\\%s", tmp);
952 if (!ref->alternate_path) {
954 return NT_STATUS_NO_MEMORY;
957 if (pdp->reqpath[0] != '\0') {
958 ref->alternate_path = talloc_asprintf_append(
962 if (!ref->alternate_path) {
964 return NT_STATUS_NO_MEMORY;
968 ref->ttl = REFERRAL_TTL;
969 jucn->referral_list = ref;
970 *consumedcntp = strlen(dfs_path);
975 status = create_conn_struct(ctx, smbd_server_conn, &conn, snum,
976 lp_pathname(snum), NULL, &oldpath);
977 if (!NT_STATUS_IS_OK(status)) {
982 /* If this is a DFS path dfs_lookup should return
983 * NT_STATUS_PATH_NOT_COVERED. */
985 status = dfs_path_lookup(ctx, conn, dfs_path, pdp,
986 False, consumedcntp, &targetpath);
988 if (!NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
989 DEBUG(3,("get_referred_path: No valid referrals for path %s\n",
994 /* We know this is a valid dfs link. Parse the targetpath. */
995 if (!parse_msdfs_symlink(ctx, targetpath,
996 &jucn->referral_list,
997 &jucn->referral_count)) {
998 DEBUG(3,("get_referred_path: failed to parse symlink "
999 "target %s\n", targetpath ));
1000 status = NT_STATUS_NOT_FOUND;
1004 status = NT_STATUS_OK;
1006 vfs_ChDir(conn, oldpath);
1007 SMB_VFS_DISCONNECT(conn);
1013 static int setup_ver2_dfs_referral(const char *pathname,
1015 struct junction_map *junction,
1018 char* pdata = *ppdata;
1020 smb_ucs2_t *uni_requestedpath = NULL;
1021 int uni_reqpathoffset1,uni_reqpathoffset2;
1023 int requestedpathlen=0;
1028 DEBUG(10,("Setting up version2 referral\nRequested path:\n"));
1030 requestedpathlen = rpcstr_push_talloc(talloc_tos(),
1031 &uni_requestedpath, pathname);
1032 if (uni_requestedpath == NULL || requestedpathlen == 0) {
1037 dump_data(0, (unsigned char *)uni_requestedpath,
1041 DEBUG(10,("ref count = %u\n",junction->referral_count));
1043 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
1044 VERSION2_REFERRAL_SIZE * junction->referral_count;
1046 uni_reqpathoffset2 = uni_reqpathoffset1 + requestedpathlen;
1048 uni_curroffset = uni_reqpathoffset2 + requestedpathlen;
1050 reply_size = REFERRAL_HEADER_SIZE +
1051 VERSION2_REFERRAL_SIZE*junction->referral_count +
1052 2 * requestedpathlen;
1053 DEBUG(10,("reply_size: %u\n",reply_size));
1055 /* add up the unicode lengths of all the referral paths */
1056 for(i=0;i<junction->referral_count;i++) {
1057 DEBUG(10,("referral %u : %s\n",
1059 junction->referral_list[i].alternate_path));
1061 (strlen(junction->referral_list[i].alternate_path)+1)*2;
1064 DEBUG(10,("reply_size = %u\n",reply_size));
1065 /* add the unexplained 0x16 bytes */
1068 pdata = (char *)SMB_REALLOC(pdata,reply_size);
1070 DEBUG(0,("Realloc failed!\n"));
1075 /* copy in the dfs requested paths.. required for offset calculations */
1076 memcpy(pdata+uni_reqpathoffset1,uni_requestedpath,requestedpathlen);
1077 memcpy(pdata+uni_reqpathoffset2,uni_requestedpath,requestedpathlen);
1079 /* create the header */
1080 SSVAL(pdata,0,requestedpathlen - 2); /* UCS2 of path consumed minus
1082 /* number of referral in this pkt */
1083 SSVAL(pdata,2,junction->referral_count);
1085 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
1087 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
1091 /* add the referral elements */
1092 for(i=0;i<junction->referral_count;i++) {
1093 struct referral* ref = &junction->referral_list[i];
1096 SSVAL(pdata,offset,2); /* version 2 */
1097 SSVAL(pdata,offset+2,VERSION2_REFERRAL_SIZE);
1099 SSVAL(pdata,offset+4,1);
1101 SSVAL(pdata,offset+4,0);
1104 /* ref_flags :use path_consumed bytes? */
1105 SSVAL(pdata,offset+6,0);
1106 SIVAL(pdata,offset+8,ref->proximity);
1107 SIVAL(pdata,offset+12,ref->ttl);
1109 SSVAL(pdata,offset+16,uni_reqpathoffset1-offset);
1110 SSVAL(pdata,offset+18,uni_reqpathoffset2-offset);
1111 /* copy referred path into current offset */
1112 unilen = rpcstr_push(pdata+uni_curroffset,
1113 ref->alternate_path,
1114 reply_size - uni_curroffset,
1117 SSVAL(pdata,offset+20,uni_curroffset-offset);
1119 uni_curroffset += unilen;
1120 offset += VERSION2_REFERRAL_SIZE;
1122 /* add in the unexplained 22 (0x16) bytes at the end */
1123 memset(pdata+uni_curroffset,'\0',0x16);
1127 static int setup_ver3_dfs_referral(const char *pathname,
1129 struct junction_map *junction,
1132 char *pdata = *ppdata;
1134 smb_ucs2_t *uni_reqpath = NULL;
1135 int uni_reqpathoffset1, uni_reqpathoffset2;
1142 DEBUG(10,("setting up version3 referral\n"));
1144 reqpathlen = rpcstr_push_talloc(talloc_tos(), &uni_reqpath, pathname);
1145 if (uni_reqpath == NULL || reqpathlen == 0) {
1150 dump_data(0, (unsigned char *)uni_reqpath,
1154 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
1155 VERSION3_REFERRAL_SIZE * junction->referral_count;
1156 uni_reqpathoffset2 = uni_reqpathoffset1 + reqpathlen;
1157 reply_size = uni_curroffset = uni_reqpathoffset2 + reqpathlen;
1159 for(i=0;i<junction->referral_count;i++) {
1160 DEBUG(10,("referral %u : %s\n",
1162 junction->referral_list[i].alternate_path));
1164 (strlen(junction->referral_list[i].alternate_path)+1)*2;
1167 pdata = (char *)SMB_REALLOC(pdata,reply_size);
1169 DEBUG(0,("version3 referral setup:"
1170 "malloc failed for Realloc!\n"));
1175 /* create the header */
1176 SSVAL(pdata,0,reqpathlen - 2); /* UCS2 of path consumed minus
1178 SSVAL(pdata,2,junction->referral_count); /* number of referral */
1180 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
1182 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
1185 /* copy in the reqpaths */
1186 memcpy(pdata+uni_reqpathoffset1,uni_reqpath,reqpathlen);
1187 memcpy(pdata+uni_reqpathoffset2,uni_reqpath,reqpathlen);
1190 for(i=0;i<junction->referral_count;i++) {
1191 struct referral* ref = &(junction->referral_list[i]);
1194 SSVAL(pdata,offset,3); /* version 3 */
1195 SSVAL(pdata,offset+2,VERSION3_REFERRAL_SIZE);
1197 SSVAL(pdata,offset+4,1);
1199 SSVAL(pdata,offset+4,0);
1202 /* ref_flags :use path_consumed bytes? */
1203 SSVAL(pdata,offset+6,0);
1204 SIVAL(pdata,offset+8,ref->ttl);
1206 SSVAL(pdata,offset+12,uni_reqpathoffset1-offset);
1207 SSVAL(pdata,offset+14,uni_reqpathoffset2-offset);
1208 /* copy referred path into current offset */
1209 unilen = rpcstr_push(pdata+uni_curroffset,ref->alternate_path,
1210 reply_size - uni_curroffset,
1211 STR_UNICODE | STR_TERMINATE);
1212 SSVAL(pdata,offset+16,uni_curroffset-offset);
1213 /* copy 0x10 bytes of 00's in the ServiceSite GUID */
1214 memset(pdata+offset+18,'\0',16);
1216 uni_curroffset += unilen;
1217 offset += VERSION3_REFERRAL_SIZE;
1222 /******************************************************************
1223 Set up the DFS referral for the dfs pathname. This call returns
1224 the amount of the path covered by this server, and where the
1225 client should be redirected to. This is the meat of the
1226 TRANS2_GET_DFS_REFERRAL call.
1227 ******************************************************************/
1229 int setup_dfs_referral(connection_struct *orig_conn,
1230 const char *dfs_path,
1231 int max_referral_level,
1232 char **ppdata, NTSTATUS *pstatus)
1234 struct junction_map *junction = NULL;
1235 int consumedcnt = 0;
1236 bool self_referral = False;
1238 char *pathnamep = NULL;
1239 char *local_dfs_path = NULL;
1242 if (!(ctx=talloc_init("setup_dfs_referral"))) {
1243 *pstatus = NT_STATUS_NO_MEMORY;
1247 /* get the junction entry */
1249 talloc_destroy(ctx);
1250 *pstatus = NT_STATUS_NOT_FOUND;
1255 * Trim pathname sent by client so it begins with only one backslash.
1256 * Two backslashes confuse some dfs clients
1259 local_dfs_path = talloc_strdup(ctx,dfs_path);
1260 if (!local_dfs_path) {
1261 *pstatus = NT_STATUS_NO_MEMORY;
1262 talloc_destroy(ctx);
1265 pathnamep = local_dfs_path;
1266 while (IS_DIRECTORY_SEP(pathnamep[0]) &&
1267 IS_DIRECTORY_SEP(pathnamep[1])) {
1271 junction = talloc_zero(ctx, struct junction_map);
1273 *pstatus = NT_STATUS_NO_MEMORY;
1274 talloc_destroy(ctx);
1278 /* The following call can change cwd. */
1279 *pstatus = get_referred_path(ctx, pathnamep, junction,
1280 &consumedcnt, &self_referral);
1281 if (!NT_STATUS_IS_OK(*pstatus)) {
1282 vfs_ChDir(orig_conn,orig_conn->connectpath);
1283 talloc_destroy(ctx);
1286 vfs_ChDir(orig_conn,orig_conn->connectpath);
1288 if (!self_referral) {
1289 pathnamep[consumedcnt] = '\0';
1291 if( DEBUGLVL( 3 ) ) {
1293 dbgtext("setup_dfs_referral: Path %s to "
1294 "alternate path(s):",
1296 for(i=0;i<junction->referral_count;i++)
1298 junction->referral_list[i].alternate_path);
1303 /* create the referral depeding on version */
1304 DEBUG(10,("max_referral_level :%d\n",max_referral_level));
1306 if (max_referral_level < 2) {
1307 max_referral_level = 2;
1309 if (max_referral_level > 3) {
1310 max_referral_level = 3;
1313 switch(max_referral_level) {
1315 reply_size = setup_ver2_dfs_referral(pathnamep,
1320 reply_size = setup_ver3_dfs_referral(pathnamep, ppdata,
1321 junction, self_referral);
1324 DEBUG(0,("setup_dfs_referral: Invalid dfs referral "
1326 max_referral_level));
1327 talloc_destroy(ctx);
1328 *pstatus = NT_STATUS_INVALID_LEVEL;
1333 DEBUGADD(0,("DFS Referral pdata:\n"));
1334 dump_data(0,(uint8 *)*ppdata,reply_size);
1337 talloc_destroy(ctx);
1338 *pstatus = NT_STATUS_OK;
1342 /**********************************************************************
1343 The following functions are called by the NETDFS RPC pipe functions
1344 **********************************************************************/
1346 /*********************************************************************
1347 Creates a junction structure from a DFS pathname
1348 **********************************************************************/
1350 bool create_junction(TALLOC_CTX *ctx,
1351 const char *dfs_path,
1352 struct junction_map *jucn)
1356 struct dfs_path *pdp = talloc(ctx,struct dfs_path);
1362 status = parse_dfs_path(NULL, dfs_path, False,
1363 !smbd_server_conn->using_smb2, pdp, &dummy);
1364 if (!NT_STATUS_IS_OK(status)) {
1368 /* check if path is dfs : validate first token */
1369 if (!is_myname_or_ipaddr(pdp->hostname)) {
1370 DEBUG(4,("create_junction: Invalid hostname %s "
1372 pdp->hostname, dfs_path));
1377 /* Check for a non-DFS share */
1378 snum = lp_servicenumber(pdp->servicename);
1380 if(snum < 0 || !lp_msdfs_root(snum)) {
1381 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1387 jucn->service_name = talloc_strdup(ctx, pdp->servicename);
1388 jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
1389 jucn->comment = talloc_strdup(ctx, lp_comment(snum));
1392 if (!jucn->service_name || !jucn->volume_name || ! jucn->comment) {
1398 /**********************************************************************
1399 Forms a valid Unix pathname from the junction
1400 **********************************************************************/
1402 static bool junction_to_local_path(const struct junction_map *jucn,
1404 connection_struct **conn_out,
1410 snum = lp_servicenumber(jucn->service_name);
1414 status = create_conn_struct(talloc_tos(), smbd_server_conn, conn_out,
1415 snum, lp_pathname(snum), NULL, oldpath);
1416 if (!NT_STATUS_IS_OK(status)) {
1420 *pp_path_out = talloc_asprintf(*conn_out,
1424 if (!*pp_path_out) {
1425 vfs_ChDir(*conn_out, *oldpath);
1426 SMB_VFS_DISCONNECT(*conn_out);
1427 conn_free(*conn_out);
1433 bool create_msdfs_link(const struct junction_map *jucn)
1437 char *msdfs_link = NULL;
1438 connection_struct *conn;
1440 bool insert_comma = False;
1443 if(!junction_to_local_path(jucn, &path, &conn, &cwd)) {
1447 /* Form the msdfs_link contents */
1448 msdfs_link = talloc_strdup(conn, "msdfs:");
1452 for(i=0; i<jucn->referral_count; i++) {
1453 char *refpath = jucn->referral_list[i].alternate_path;
1455 /* Alternate paths always use Windows separators. */
1456 trim_char(refpath, '\\', '\\');
1457 if(*refpath == '\0') {
1459 insert_comma = False;
1463 if (i > 0 && insert_comma) {
1464 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1468 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1476 if (!insert_comma) {
1477 insert_comma = True;
1481 DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
1484 if(SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
1485 if (errno == EEXIST) {
1486 struct smb_filename *smb_fname = NULL;
1489 status = create_synthetic_smb_fname(talloc_tos(), path,
1492 if (!NT_STATUS_IS_OK(status)) {
1493 errno = map_errno_from_nt_status(status);
1497 if(SMB_VFS_UNLINK(conn, smb_fname)!=0) {
1498 TALLOC_FREE(smb_fname);
1501 TALLOC_FREE(smb_fname);
1503 if (SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
1504 DEBUG(1,("create_msdfs_link: symlink failed "
1505 "%s -> %s\nError: %s\n",
1506 path, msdfs_link, strerror(errno)));
1514 vfs_ChDir(conn, cwd);
1515 SMB_VFS_DISCONNECT(conn);
1520 bool remove_msdfs_link(const struct junction_map *jucn)
1524 connection_struct *conn;
1526 struct smb_filename *smb_fname = NULL;
1529 if (!junction_to_local_path(jucn, &path, &conn, &cwd)) {
1533 status = create_synthetic_smb_fname(talloc_tos(), path,
1536 if (!NT_STATUS_IS_OK(status)) {
1537 errno = map_errno_from_nt_status(status);
1541 if( SMB_VFS_UNLINK(conn, smb_fname) == 0 ) {
1545 TALLOC_FREE(smb_fname);
1546 vfs_ChDir(conn, cwd);
1547 SMB_VFS_DISCONNECT(conn);
1552 /*********************************************************************
1553 Return the number of DFS links at the root of this share.
1554 *********************************************************************/
1556 static int count_dfs_links(TALLOC_CTX *ctx, int snum)
1559 SMB_STRUCT_DIR *dirp = NULL;
1560 const char *dname = NULL;
1561 char *talloced = NULL;
1562 const char *connect_path = lp_pathname(snum);
1563 const char *msdfs_proxy = lp_msdfs_proxy(snum);
1564 connection_struct *conn;
1568 if(*connect_path == '\0') {
1573 * Fake up a connection struct for the VFS layer.
1576 status = create_conn_struct(talloc_tos(), smbd_server_conn, &conn,
1577 snum, connect_path, NULL, &cwd);
1578 if (!NT_STATUS_IS_OK(status)) {
1579 DEBUG(3, ("create_conn_struct failed: %s\n",
1580 nt_errstr(status)));
1584 /* Count a link for the msdfs root - convention */
1587 /* No more links if this is an msdfs proxy. */
1588 if (*msdfs_proxy != '\0') {
1592 /* Now enumerate all dfs links */
1593 dirp = SMB_VFS_OPENDIR(conn, ".", NULL, 0);
1598 while ((dname = vfs_readdirname(conn, dirp, NULL, &talloced))
1600 if (is_msdfs_link(conn,
1605 TALLOC_FREE(talloced);
1608 SMB_VFS_CLOSEDIR(conn,dirp);
1611 vfs_ChDir(conn, cwd);
1612 SMB_VFS_DISCONNECT(conn);
1617 /*********************************************************************
1618 *********************************************************************/
1620 static int form_junctions(TALLOC_CTX *ctx,
1622 struct junction_map *jucn,
1626 SMB_STRUCT_DIR *dirp = NULL;
1627 const char *dname = NULL;
1628 char *talloced = NULL;
1629 const char *connect_path = lp_pathname(snum);
1630 char *service_name = lp_servicename(snum);
1631 const char *msdfs_proxy = lp_msdfs_proxy(snum);
1632 connection_struct *conn;
1633 struct referral *ref = NULL;
1637 if (jn_remain == 0) {
1641 if(*connect_path == '\0') {
1646 * Fake up a connection struct for the VFS layer.
1649 status = create_conn_struct(ctx, smbd_server_conn, &conn, snum, connect_path, NULL,
1651 if (!NT_STATUS_IS_OK(status)) {
1652 DEBUG(3, ("create_conn_struct failed: %s\n",
1653 nt_errstr(status)));
1657 /* form a junction for the msdfs root - convention
1658 DO NOT REMOVE THIS: NT clients will not work with us
1659 if this is not present
1661 jucn[cnt].service_name = talloc_strdup(ctx,service_name);
1662 jucn[cnt].volume_name = talloc_strdup(ctx, "");
1663 if (!jucn[cnt].service_name || !jucn[cnt].volume_name) {
1666 jucn[cnt].comment = "";
1667 jucn[cnt].referral_count = 1;
1669 ref = jucn[cnt].referral_list = talloc_zero(ctx, struct referral);
1670 if (jucn[cnt].referral_list == NULL) {
1675 ref->ttl = REFERRAL_TTL;
1676 if (*msdfs_proxy != '\0') {
1677 ref->alternate_path = talloc_strdup(ctx,
1680 ref->alternate_path = talloc_asprintf(ctx,
1682 get_local_machine_name(),
1686 if (!ref->alternate_path) {
1691 /* Don't enumerate if we're an msdfs proxy. */
1692 if (*msdfs_proxy != '\0') {
1696 /* Now enumerate all dfs links */
1697 dirp = SMB_VFS_OPENDIR(conn, ".", NULL, 0);
1702 while ((dname = vfs_readdirname(conn, dirp, NULL, &talloced))
1704 char *link_target = NULL;
1705 if (cnt >= jn_remain) {
1706 DEBUG(2, ("form_junctions: ran out of MSDFS "
1708 TALLOC_FREE(talloced);
1711 if (is_msdfs_link_internal(ctx,
1713 dname, &link_target,
1715 if (parse_msdfs_symlink(ctx,
1717 &jucn[cnt].referral_list,
1718 &jucn[cnt].referral_count)) {
1720 jucn[cnt].service_name = talloc_strdup(ctx,
1722 jucn[cnt].volume_name = talloc_strdup(ctx,
1724 if (!jucn[cnt].service_name ||
1725 !jucn[cnt].volume_name) {
1726 TALLOC_FREE(talloced);
1729 jucn[cnt].comment = "";
1732 TALLOC_FREE(link_target);
1734 TALLOC_FREE(talloced);
1740 SMB_VFS_CLOSEDIR(conn,dirp);
1743 vfs_ChDir(conn, cwd);
1748 struct junction_map *enum_msdfs_links(struct smbd_server_connection *sconn,
1749 TALLOC_CTX *ctx, size_t *p_num_jn)
1751 struct junction_map *jn = NULL;
1753 size_t jn_count = 0;
1757 if(!lp_host_msdfs()) {
1761 /* Ensure all the usershares are loaded. */
1763 load_registry_shares();
1764 sharecount = load_usershare_shares(sconn);
1767 for(i=0;i < sharecount;i++) {
1768 if(lp_msdfs_root(i)) {
1769 jn_count += count_dfs_links(ctx, i);
1772 if (jn_count == 0) {
1775 jn = talloc_array(ctx, struct junction_map, jn_count);
1779 for(i=0; i < sharecount; i++) {
1780 if (*p_num_jn >= jn_count) {
1783 if(lp_msdfs_root(i)) {
1784 *p_num_jn += form_junctions(ctx, i,
1786 jn_count - *p_num_jn);
1792 /******************************************************************************
1793 Core function to resolve a dfs pathname possibly containing a wildcard. If
1794 ppath_contains_wcard != NULL, it will be set to true if a wildcard is
1795 detected during dfs resolution.
1796 ******************************************************************************/
1798 NTSTATUS resolve_dfspath_wcard(TALLOC_CTX *ctx,
1799 connection_struct *conn,
1801 const char *name_in,
1804 bool *ppath_contains_wcard)
1806 bool path_contains_wcard;
1807 NTSTATUS status = NT_STATUS_OK;
1809 if (dfs_pathnames) {
1810 status = dfs_redirect(ctx,
1814 !smbd_server_conn->using_smb2,
1816 &path_contains_wcard);
1818 if (NT_STATUS_IS_OK(status) && ppath_contains_wcard != NULL) {
1819 *ppath_contains_wcard = path_contains_wcard;
1823 * Cheat and just return a copy of the in ptr.
1824 * Once srvstr_get_path() uses talloc it'll
1825 * be a talloced ptr anyway.
1827 *pp_name_out = discard_const_p(char, name_in);