2 Unix SMB/Netbios implementation.
4 MSDFS services for Samba
5 Copyright (C) Shirish Kalele 2000
6 Copyright (C) Jeremy Allison 2007
7 Copyright (C) Robin McCorkell 2015
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 3 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>.
24 #define DBGC_CLASS DBGC_MSDFS
26 #include "system/filesys.h"
27 #include "smbd/smbd.h"
28 #include "smbd/globals.h"
31 #include "lib/param/loadparm.h"
32 #include "libcli/security/security.h"
33 #include "librpc/gen_ndr/ndr_dfsblobs.h"
34 #include "lib/tsocket/tsocket.h"
36 /**********************************************************************
37 Parse a DFS pathname of the form \hostname\service\reqpath
38 into the dfs_path structure.
39 If POSIX pathnames is true, the pathname may also be of the
40 form /hostname/service/reqpath.
41 We cope with either here.
43 Unfortunately, due to broken clients who might set the
44 SVAL(inbuf,smb_flg2) & FLAGS2_DFS_PATHNAMES bit and then
45 send a local path, we have to cope with that too....
47 If conn != NULL then ensure the provided service is
48 the one pointed to by the connection.
50 This version does everything using pointers within one copy of the
51 pathname string, talloced on the struct dfs_path pointer (which
52 must be talloced). This may be too clever to live....
54 **********************************************************************/
56 static NTSTATUS parse_dfs_path(connection_struct *conn,
59 bool allow_broken_path,
60 struct dfs_path *pdp, /* MUST BE TALLOCED */
61 bool *ppath_contains_wcard)
67 NTSTATUS status = NT_STATUS_OK;
73 * This is the only talloc we should need to do
74 * on the struct dfs_path. All the pointers inside
75 * it should point to offsets within this string.
78 pathname_local = talloc_strdup(pdp, pathname);
79 if (!pathname_local) {
80 return NT_STATUS_NO_MEMORY;
82 /* Get a pointer to the terminating '\0' */
83 eos_ptr = &pathname_local[strlen(pathname_local)];
84 p = temp = pathname_local;
87 * Non-broken DFS paths *must* start with the
88 * path separator. For Windows this is always '\\',
89 * for posix paths this is always '/'.
92 if (*pathname == '/') {
93 pdp->posix_path = true;
96 pdp->posix_path = false;
100 if (allow_broken_path && (*pathname != sepchar)) {
101 DEBUG(10,("parse_dfs_path: path %s doesn't start with %c\n",
102 pathname, sepchar ));
104 * Possibly client sent a local path by mistake.
105 * Try and convert to a local path.
106 * Note that this is an SMB1-only fallback
107 * to cope with known broken SMB1 clients.
110 pdp->hostname = eos_ptr; /* "" */
111 pdp->servicename = eos_ptr; /* "" */
113 /* We've got no info about separators. */
114 pdp->posix_path = lp_posix_pathnames();
116 DEBUG(10,("parse_dfs_path: trying to convert %s to a "
123 * Safe to use on talloc'ed string as it only shrinks.
124 * It also doesn't affect the eos_ptr.
126 trim_char(temp,sepchar,sepchar);
128 DEBUG(10,("parse_dfs_path: temp = |%s| after trimming %c's\n",
132 /* Parse out hostname. */
133 p = strchr_m(temp,sepchar);
135 DEBUG(10,("parse_dfs_path: can't parse hostname from path %s\n",
138 * Possibly client sent a local path by mistake.
139 * Try and convert to a local path.
142 pdp->hostname = eos_ptr; /* "" */
143 pdp->servicename = eos_ptr; /* "" */
146 DEBUG(10,("parse_dfs_path: trying to convert %s "
152 pdp->hostname = temp;
154 DEBUG(10,("parse_dfs_path: hostname: %s\n",pdp->hostname));
156 /* Parse out servicename. */
158 p = strchr_m(servicename,sepchar);
163 /* Is this really our servicename ? */
164 if (conn && !( strequal(servicename, lp_servicename(talloc_tos(), SNUM(conn)))
165 || (strequal(servicename, HOMES_NAME)
166 && strequal(lp_servicename(talloc_tos(), SNUM(conn)),
167 get_current_username()) )) ) {
168 DEBUG(10,("parse_dfs_path: %s is not our servicename\n",
172 * Possibly client sent a local path by mistake.
173 * Try and convert to a local path.
176 pdp->hostname = eos_ptr; /* "" */
177 pdp->servicename = eos_ptr; /* "" */
179 /* Repair the path - replace the sepchar's
182 *servicename = sepchar;
188 DEBUG(10,("parse_dfs_path: trying to convert %s "
194 pdp->servicename = servicename;
196 DEBUG(10,("parse_dfs_path: servicename: %s\n",pdp->servicename));
199 /* Client sent self referral \server\share. */
200 pdp->reqpath = eos_ptr; /* "" */
208 *ppath_contains_wcard = False;
212 /* Rest is reqpath. */
213 if (pdp->posix_path) {
214 status = check_path_syntax_posix(pdp->reqpath);
217 status = check_path_syntax_wcard(pdp->reqpath,
218 ppath_contains_wcard);
220 status = check_path_syntax(pdp->reqpath);
224 if (!NT_STATUS_IS_OK(status)) {
225 DEBUG(10,("parse_dfs_path: '%s' failed with %s\n",
226 p, nt_errstr(status) ));
230 DEBUG(10,("parse_dfs_path: rest of the path: %s\n",pdp->reqpath));
234 /********************************************************
235 Fake up a connection struct for the VFS layer, for use in
236 applications (such as the python bindings), that do not want the
237 global working directory changed under them.
239 SMB_VFS_CONNECT requires root privileges.
240 *********************************************************/
242 static NTSTATUS create_conn_struct_as_root(TALLOC_CTX *ctx,
243 struct tevent_context *ev,
244 struct messaging_context *msg,
245 connection_struct **pconn,
248 const struct auth_session_info *session_info)
250 connection_struct *conn;
252 const char *vfs_user;
253 struct smbd_server_connection *sconn;
254 const char *servicename = lp_const_servicename(snum);
256 sconn = talloc_zero(ctx, struct smbd_server_connection);
258 return NT_STATUS_NO_MEMORY;
262 sconn->msg_ctx = msg;
264 conn = conn_new(sconn);
267 return NT_STATUS_NO_MEMORY;
270 /* Now we have conn, we need to make sconn a child of conn,
271 * for a proper talloc tree */
272 talloc_steal(conn, sconn);
274 if (snum == -1 && servicename == NULL) {
275 servicename = "Unknown Service (snum == -1)";
278 connpath = talloc_strdup(conn, path);
281 return NT_STATUS_NO_MEMORY;
283 connpath = talloc_string_sub(conn,
289 return NT_STATUS_NO_MEMORY;
292 /* needed for smbd_vfs_init() */
294 conn->params->service = snum;
295 conn->cnum = TID_FIELD_INVALID;
297 if (session_info != NULL) {
298 conn->session_info = copy_session_info(conn, session_info);
299 if (conn->session_info == NULL) {
300 DEBUG(0, ("copy_serverinfo failed\n"));
302 return NT_STATUS_NO_MEMORY;
304 vfs_user = conn->session_info->unix_info->unix_name;
306 /* use current authenticated user in absence of session_info */
307 vfs_user = get_current_username();
310 set_conn_connectpath(conn, connpath);
313 * New code to check if there's a share security descriptor
314 * added from NT server manager. This is done after the
315 * smb.conf checks are done as we need a uid and token. JRA.
318 if (conn->session_info) {
319 share_access_check(conn->session_info->security_token,
321 MAXIMUM_ALLOWED_ACCESS,
322 &conn->share_access);
324 if ((conn->share_access & FILE_WRITE_DATA) == 0) {
325 if ((conn->share_access & FILE_READ_DATA) == 0) {
326 /* No access, read or write. */
327 DEBUG(3,("create_conn_struct: connection to %s "
328 "denied due to security "
332 return NT_STATUS_ACCESS_DENIED;
334 conn->read_only = true;
338 conn->share_access = 0;
339 conn->read_only = true;
342 if (!smbd_vfs_init(conn)) {
343 NTSTATUS status = map_nt_error_from_unix(errno);
344 DEBUG(0,("create_conn_struct: smbd_vfs_init failed.\n"));
349 /* this must be the first filesystem operation that we do */
350 if (SMB_VFS_CONNECT(conn, servicename, vfs_user) < 0) {
351 DEBUG(0,("VFS connect failed!\n"));
353 return NT_STATUS_UNSUCCESSFUL;
356 conn->fs_capabilities = SMB_VFS_FS_CAPABILITIES(conn, &conn->ts_res);
362 /********************************************************
363 Fake up a connection struct for the VFS layer, for use in
364 applications (such as the python bindings), that do not want the
365 global working directory changed under them.
367 SMB_VFS_CONNECT requires root privileges.
368 *********************************************************/
370 NTSTATUS create_conn_struct(TALLOC_CTX *ctx,
371 struct tevent_context *ev,
372 struct messaging_context *msg,
373 connection_struct **pconn,
376 const struct auth_session_info *session_info)
380 status = create_conn_struct_as_root(ctx, ev,
389 /********************************************************
390 Fake up a connection struct for the VFS layer.
391 Note: this performs a vfs connect and CHANGES CWD !!!! JRA.
393 The old working directory is returned on *poldcwd, allocated on ctx.
394 *********************************************************/
396 NTSTATUS create_conn_struct_cwd(TALLOC_CTX *ctx,
397 struct tevent_context *ev,
398 struct messaging_context *msg,
399 connection_struct **pconn,
402 const struct auth_session_info *session_info,
403 struct smb_filename **poldcwd_fname)
405 connection_struct *conn;
407 struct smb_filename *smb_fname_oldcwd = NULL;
408 struct smb_filename smb_fname_connectpath = {0};
410 NTSTATUS status = create_conn_struct(ctx, ev,
414 if (!NT_STATUS_IS_OK(status)) {
419 * Windows seems to insist on doing trans2getdfsreferral() calls on
420 * the IPC$ share as the anonymous user. If we try to chdir as that
421 * user we will fail.... WTF ? JRA.
424 oldcwd = vfs_GetWd(ctx, conn);
425 if (oldcwd == NULL) {
426 status = map_nt_error_from_unix(errno);
427 DEBUG(3, ("vfs_GetWd failed: %s\n", strerror(errno)));
432 smb_fname_oldcwd = synthetic_smb_fname(ctx,
437 if (smb_fname_oldcwd == NULL) {
438 status = NT_STATUS_NO_MEMORY;
443 smb_fname_connectpath = (struct smb_filename) {
444 .base_name = conn->connectpath
447 if (vfs_ChDir(conn, &smb_fname_connectpath) != 0) {
448 status = map_nt_error_from_unix(errno);
449 DEBUG(3,("create_conn_struct: Can't ChDir to new conn path %s. "
451 conn->connectpath, strerror(errno) ));
452 TALLOC_FREE(smb_fname_oldcwd);
458 *poldcwd_fname = smb_fname_oldcwd;
463 static void shuffle_strlist(char **list, int count)
469 for (i = count; i > 1; i--) {
470 r = generate_random() % i;
478 /**********************************************************************
479 Parse the contents of a symlink to verify if it is an msdfs referral
480 A valid referral is of the form:
482 msdfs:server1\share1,server2\share2
483 msdfs:server1\share1\pathname,server2\share2\pathname
484 msdfs:server1/share1,server2/share2
485 msdfs:server1/share1/pathname,server2/share2/pathname.
487 Note that the alternate paths returned here must be of the canonicalized
491 \server\share\path\to\file,
493 even in posix path mode. This is because we have no knowledge if the
494 server we're referring to understands posix paths.
495 **********************************************************************/
497 static bool parse_msdfs_symlink(TALLOC_CTX *ctx,
500 struct referral **preflist,
505 char **alt_path = NULL;
507 struct referral *reflist;
510 temp = talloc_strdup(ctx, target);
514 prot = strtok_r(temp, ":", &saveptr);
516 DEBUG(0,("parse_msdfs_symlink: invalid path !\n"));
520 alt_path = talloc_array(ctx, char *, MAX_REFERRAL_COUNT);
525 /* parse out the alternate paths */
526 while((count<MAX_REFERRAL_COUNT) &&
527 ((alt_path[count] = strtok_r(NULL, ",", &saveptr)) != NULL)) {
531 /* shuffle alternate paths */
532 if (lp_msdfs_shuffle_referrals(snum)) {
533 shuffle_strlist(alt_path, count);
536 DEBUG(10,("parse_msdfs_symlink: count=%d\n", count));
539 reflist = *preflist = talloc_zero_array(ctx,
540 struct referral, count);
541 if(reflist == NULL) {
542 TALLOC_FREE(alt_path);
546 reflist = *preflist = NULL;
549 for(i=0;i<count;i++) {
552 /* Canonicalize link target.
553 * Replace all /'s in the path by a \ */
554 string_replace(alt_path[i], '/', '\\');
556 /* Remove leading '\\'s */
558 while (*p && (*p == '\\')) {
562 reflist[i].alternate_path = talloc_asprintf(ctx,
565 if (!reflist[i].alternate_path) {
569 reflist[i].proximity = 0;
570 reflist[i].ttl = REFERRAL_TTL;
571 DEBUG(10, ("parse_msdfs_symlink: Created alt path: %s\n",
572 reflist[i].alternate_path));
577 TALLOC_FREE(alt_path);
581 /**********************************************************************
582 Returns true if the unix path is a valid msdfs symlink and also
583 returns the target string from inside the link.
584 **********************************************************************/
586 static bool is_msdfs_link_internal(TALLOC_CTX *ctx,
587 connection_struct *conn,
588 struct smb_filename *smb_fname,
589 char **pp_link_target)
591 int referral_len = 0;
592 #if defined(HAVE_BROKEN_READLINK)
593 char link_target_buf[PATH_MAX];
595 char link_target_buf[7];
598 char *link_target = NULL;
600 if (pp_link_target) {
602 link_target = talloc_array(ctx, char, bufsize);
606 *pp_link_target = link_target;
608 bufsize = sizeof(link_target_buf);
609 link_target = link_target_buf;
612 if (SMB_VFS_LSTAT(conn, smb_fname) != 0) {
613 DEBUG(5,("is_msdfs_link_read_target: %s does not exist.\n",
614 smb_fname->base_name));
617 if (!S_ISLNK(smb_fname->st.st_ex_mode)) {
618 DEBUG(5,("is_msdfs_link_read_target: %s is not a link.\n",
619 smb_fname->base_name));
623 referral_len = SMB_VFS_READLINK(conn, smb_fname,
624 link_target, bufsize - 1);
625 if (referral_len == -1) {
626 DEBUG(0,("is_msdfs_link_read_target: Error reading "
627 "msdfs link %s: %s\n",
628 smb_fname->base_name, strerror(errno)));
631 link_target[referral_len] = '\0';
633 DEBUG(5,("is_msdfs_link_internal: %s -> %s\n", smb_fname->base_name,
636 if (!strnequal(link_target, "msdfs:", 6)) {
643 if (link_target != link_target_buf) {
644 TALLOC_FREE(link_target);
649 /**********************************************************************
650 Returns true if the unix path is a valid msdfs symlink.
651 **********************************************************************/
653 bool is_msdfs_link(connection_struct *conn,
654 struct smb_filename *smb_fname)
656 return is_msdfs_link_internal(talloc_tos(),
662 /*****************************************************************
663 Used by other functions to decide if a dfs path is remote,
664 and to get the list of referred locations for that remote path.
666 search_flag: For findfirsts, dfs links themselves are not
667 redirected, but paths beyond the links are. For normal smb calls,
668 even dfs links need to be redirected.
670 consumedcntp: how much of the dfs path is being redirected. the client
671 should try the remaining path on the redirected server.
673 If this returns NT_STATUS_PATH_NOT_COVERED the contents of the msdfs
674 link redirect are in targetpath.
675 *****************************************************************/
677 static NTSTATUS dfs_path_lookup(TALLOC_CTX *ctx,
678 connection_struct *conn,
679 const char *dfspath, /* Incoming complete dfs path */
680 const struct dfs_path *pdp, /* Parsed out
681 server+share+extrapath. */
684 char **pp_targetpath)
689 struct smb_filename *smb_fname = NULL;
690 char *canon_dfspath = NULL; /* Canonicalized dfs path. (only '/'
693 DEBUG(10,("dfs_path_lookup: Conn path = %s reqpath = %s\n",
694 conn->connectpath, pdp->reqpath));
697 * Note the unix path conversion here we're doing we
698 * throw away. We're looking for a symlink for a dfs
699 * resolution, if we don't find it we'll do another
700 * unix_convert later in the codepath.
703 status = unix_convert(ctx, conn, pdp->reqpath, &smb_fname,
706 if (!NT_STATUS_IS_OK(status)) {
707 if (!NT_STATUS_EQUAL(status,
708 NT_STATUS_OBJECT_PATH_NOT_FOUND)) {
711 if (smb_fname == NULL || smb_fname->base_name == NULL) {
716 /* Optimization - check if we can redirect the whole path. */
718 if (is_msdfs_link_internal(ctx, conn, smb_fname, pp_targetpath)) {
719 /* XX_ALLOW_WCARD_XXX is called from search functions. */
721 (UCF_COND_ALLOW_WCARD_LCOMP|
722 UCF_ALWAYS_ALLOW_WCARD_LCOMP)) {
723 DEBUG(6,("dfs_path_lookup (FindFirst) No redirection "
724 "for dfs link %s.\n", dfspath));
725 status = NT_STATUS_OK;
729 DEBUG(6,("dfs_path_lookup: %s resolves to a "
730 "valid dfs link %s.\n", dfspath,
731 pp_targetpath ? *pp_targetpath : ""));
734 *consumedcntp = strlen(dfspath);
736 status = NT_STATUS_PATH_NOT_COVERED;
740 /* Prepare to test only for '/' components in the given path,
741 * so if a Windows path replace all '\\' characters with '/'.
742 * For a POSIX DFS path we know all separators are already '/'. */
744 canon_dfspath = talloc_strdup(ctx, dfspath);
745 if (!canon_dfspath) {
746 status = NT_STATUS_NO_MEMORY;
749 if (!pdp->posix_path) {
750 string_replace(canon_dfspath, '\\', '/');
754 * localpath comes out of unix_convert, so it has
755 * no trailing backslash. Make sure that canon_dfspath hasn't either.
756 * Fix for bug #4860 from Jan Martin <Jan.Martin@rwedea.com>.
759 trim_char(canon_dfspath,0,'/');
762 * Redirect if any component in the path is a link.
763 * We do this by walking backwards through the
764 * local path, chopping off the last component
765 * in both the local path and the canonicalized
766 * DFS path. If we hit a DFS link then we're done.
769 p = strrchr_m(smb_fname->base_name, '/');
771 q = strrchr_m(canon_dfspath, '/');
780 if (is_msdfs_link_internal(ctx, conn,
781 smb_fname, pp_targetpath)) {
782 DEBUG(4, ("dfs_path_lookup: Redirecting %s because "
783 "parent %s is dfs link\n", dfspath,
784 smb_fname_str_dbg(smb_fname)));
787 *consumedcntp = strlen(canon_dfspath);
788 DEBUG(10, ("dfs_path_lookup: Path consumed: %s "
794 status = NT_STATUS_PATH_NOT_COVERED;
798 /* Step back on the filesystem. */
799 p = strrchr_m(smb_fname->base_name, '/');
802 /* And in the canonicalized dfs path. */
803 q = strrchr_m(canon_dfspath, '/');
807 status = NT_STATUS_OK;
809 TALLOC_FREE(smb_fname);
813 /*****************************************************************
814 Decides if a dfs pathname should be redirected or not.
815 If not, the pathname is converted to a tcon-relative local unix path
817 search_wcard_flag: this flag performs 2 functions both related
818 to searches. See resolve_dfs_path() and parse_dfs_path_XX()
821 This function can return NT_STATUS_OK, meaning use the returned path as-is
822 (mapped into a local path).
823 or NT_STATUS_NOT_COVERED meaning return a DFS redirect, or
824 any other NT_STATUS error which is a genuine error to be
825 returned to the client.
826 *****************************************************************/
828 static NTSTATUS dfs_redirect(TALLOC_CTX *ctx,
829 connection_struct *conn,
832 bool allow_broken_path,
834 bool *ppath_contains_wcard)
837 bool search_wcard_flag = (ucf_flags &
838 (UCF_COND_ALLOW_WCARD_LCOMP|UCF_ALWAYS_ALLOW_WCARD_LCOMP));
839 struct dfs_path *pdp = talloc(ctx, struct dfs_path);
842 return NT_STATUS_NO_MEMORY;
845 status = parse_dfs_path(conn, path_in, search_wcard_flag,
846 allow_broken_path, pdp,
847 ppath_contains_wcard);
848 if (!NT_STATUS_IS_OK(status)) {
853 if (pdp->reqpath[0] == '\0') {
855 *pp_path_out = talloc_strdup(ctx, "");
857 return NT_STATUS_NO_MEMORY;
859 DEBUG(5,("dfs_redirect: self-referral.\n"));
863 /* If dfs pathname for a non-dfs share, convert to tcon-relative
864 path and return OK */
866 if (!lp_msdfs_root(SNUM(conn))) {
867 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
870 return NT_STATUS_NO_MEMORY;
875 /* If it looked like a local path (zero hostname/servicename)
876 * just treat as a tcon-relative path. */
878 if (pdp->hostname[0] == '\0' && pdp->servicename[0] == '\0') {
879 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
882 return NT_STATUS_NO_MEMORY;
887 if (!( strequal(pdp->servicename, lp_servicename(talloc_tos(), SNUM(conn)))
888 || (strequal(pdp->servicename, HOMES_NAME)
889 && strequal(lp_servicename(talloc_tos(), SNUM(conn)),
890 conn->session_info->unix_info->sanitized_username) )) ) {
892 /* The given sharename doesn't match this connection. */
895 return NT_STATUS_OBJECT_PATH_NOT_FOUND;
898 status = dfs_path_lookup(ctx, conn, path_in, pdp,
899 ucf_flags, NULL, NULL);
900 if (!NT_STATUS_IS_OK(status)) {
901 if (NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
902 DEBUG(3,("dfs_redirect: Redirecting %s\n", path_in));
904 DEBUG(10,("dfs_redirect: dfs_path_lookup "
905 "failed for %s with %s\n",
906 path_in, nt_errstr(status) ));
911 DEBUG(3,("dfs_redirect: Not redirecting %s.\n", path_in));
913 /* Form non-dfs tcon-relative path */
914 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
917 return NT_STATUS_NO_MEMORY;
920 DEBUG(3,("dfs_redirect: Path %s converted to non-dfs path %s\n",
927 /**********************************************************************
928 Return a self referral.
929 **********************************************************************/
931 static NTSTATUS self_ref(TALLOC_CTX *ctx,
932 const char *dfs_path,
933 struct junction_map *jucn,
935 bool *self_referralp)
937 struct referral *ref;
939 *self_referralp = True;
941 jucn->referral_count = 1;
942 if((ref = talloc_zero(ctx, struct referral)) == NULL) {
943 return NT_STATUS_NO_MEMORY;
946 ref->alternate_path = talloc_strdup(ctx, dfs_path);
947 if (!ref->alternate_path) {
949 return NT_STATUS_NO_MEMORY;
952 ref->ttl = REFERRAL_TTL;
953 jucn->referral_list = ref;
954 *consumedcntp = strlen(dfs_path);
958 /**********************************************************************
959 Gets valid referrals for a dfs path and fills up the
960 junction_map structure.
961 **********************************************************************/
963 NTSTATUS get_referred_path(TALLOC_CTX *ctx,
964 const char *dfs_path,
965 const struct tsocket_address *remote_address,
966 const struct tsocket_address *local_address,
967 bool allow_broken_path,
968 struct junction_map *jucn,
970 bool *self_referralp)
972 struct connection_struct *conn;
973 char *targetpath = NULL;
975 NTSTATUS status = NT_STATUS_NOT_FOUND;
977 struct dfs_path *pdp = talloc(ctx, struct dfs_path);
978 struct smb_filename *oldcwd_fname = NULL;
981 return NT_STATUS_NO_MEMORY;
984 *self_referralp = False;
986 status = parse_dfs_path(NULL, dfs_path, False, allow_broken_path,
988 if (!NT_STATUS_IS_OK(status)) {
992 jucn->service_name = talloc_strdup(ctx, pdp->servicename);
993 jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
994 if (!jucn->service_name || !jucn->volume_name) {
996 return NT_STATUS_NO_MEMORY;
999 /* Verify the share is a dfs root */
1000 snum = lp_servicenumber(jucn->service_name);
1002 char *service_name = NULL;
1003 if ((snum = find_service(ctx, jucn->service_name, &service_name)) < 0) {
1004 return NT_STATUS_NOT_FOUND;
1006 if (!service_name) {
1007 return NT_STATUS_NO_MEMORY;
1009 TALLOC_FREE(jucn->service_name);
1010 jucn->service_name = talloc_strdup(ctx, service_name);
1011 if (!jucn->service_name) {
1013 return NT_STATUS_NO_MEMORY;
1017 if (!lp_msdfs_root(snum) && (*lp_msdfs_proxy(talloc_tos(), snum) == '\0')) {
1018 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not "
1020 pdp->servicename, dfs_path));
1022 return NT_STATUS_NOT_FOUND;
1026 * Self referrals are tested with a anonymous IPC connection and
1027 * a GET_DFS_REFERRAL call to \\server\share. (which means
1028 * dp.reqpath[0] points to an empty string). create_conn_struct cd's
1029 * into the directory and will fail if it cannot (as the anonymous
1030 * user). Cope with this.
1033 if (pdp->reqpath[0] == '\0') {
1035 struct referral *ref;
1038 if (*lp_msdfs_proxy(talloc_tos(), snum) == '\0') {
1040 return self_ref(ctx,
1048 * It's an msdfs proxy share. Redirect to
1049 * the configured target share.
1052 tmp = talloc_asprintf(talloc_tos(), "msdfs:%s",
1053 lp_msdfs_proxy(talloc_tos(), snum));
1056 return NT_STATUS_NO_MEMORY;
1059 if (!parse_msdfs_symlink(ctx, snum, tmp, &ref, &refcount)) {
1062 return NT_STATUS_INVALID_PARAMETER;
1065 jucn->referral_count = refcount;
1066 jucn->referral_list = ref;
1067 *consumedcntp = strlen(dfs_path);
1069 return NT_STATUS_OK;
1072 status = create_conn_struct_cwd(ctx,
1073 server_event_context(),
1074 server_messaging_context(),
1077 lp_path(talloc_tos(), snum),
1080 if (!NT_STATUS_IS_OK(status)) {
1088 * The remote and local address should be passed down to
1089 * create_conn_struct_cwd.
1091 if (conn->sconn->remote_address == NULL) {
1092 conn->sconn->remote_address =
1093 tsocket_address_copy(remote_address, conn->sconn);
1094 if (conn->sconn->remote_address == NULL) {
1096 return NT_STATUS_NO_MEMORY;
1099 if (conn->sconn->local_address == NULL) {
1100 conn->sconn->local_address =
1101 tsocket_address_copy(local_address, conn->sconn);
1102 if (conn->sconn->local_address == NULL) {
1104 return NT_STATUS_NO_MEMORY;
1108 /* If this is a DFS path dfs_lookup should return
1109 * NT_STATUS_PATH_NOT_COVERED. */
1111 status = dfs_path_lookup(ctx, conn, dfs_path, pdp,
1112 0, consumedcntp, &targetpath);
1114 if (!NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
1115 DEBUG(3,("get_referred_path: No valid referrals for path %s\n",
1117 if (NT_STATUS_IS_OK(status)) {
1119 * We are in an error path here (we
1120 * know it's not a DFS path), but
1121 * dfs_path_lookup() can return
1122 * NT_STATUS_OK. Ensure we always
1123 * return a valid error code.
1125 * #9588 - ACLs are not inherited to directories
1128 status = NT_STATUS_NOT_FOUND;
1133 /* We know this is a valid dfs link. Parse the targetpath. */
1134 if (!parse_msdfs_symlink(ctx, snum, targetpath,
1135 &jucn->referral_list,
1136 &jucn->referral_count)) {
1137 DEBUG(3,("get_referred_path: failed to parse symlink "
1138 "target %s\n", targetpath ));
1139 status = NT_STATUS_NOT_FOUND;
1143 status = NT_STATUS_OK;
1145 vfs_ChDir(conn, oldcwd_fname);
1146 TALLOC_FREE(oldcwd_fname);
1147 SMB_VFS_DISCONNECT(conn);
1153 /******************************************************************
1154 Set up the DFS referral for the dfs pathname. This call returns
1155 the amount of the path covered by this server, and where the
1156 client should be redirected to. This is the meat of the
1157 TRANS2_GET_DFS_REFERRAL call.
1158 ******************************************************************/
1160 int setup_dfs_referral(connection_struct *orig_conn,
1161 const char *dfs_path,
1162 int max_referral_level,
1163 char **ppdata, NTSTATUS *pstatus)
1165 char *pdata = *ppdata;
1167 struct dfs_GetDFSReferral *r;
1168 DATA_BLOB blob = data_blob_null;
1170 enum ndr_err_code ndr_err;
1172 r = talloc_zero(talloc_tos(), struct dfs_GetDFSReferral);
1174 *pstatus = NT_STATUS_NO_MEMORY;
1178 r->in.req.max_referral_level = max_referral_level;
1179 r->in.req.servername = talloc_strdup(r, dfs_path);
1180 if (r->in.req.servername == NULL) {
1182 *pstatus = NT_STATUS_NO_MEMORY;
1186 status = SMB_VFS_GET_DFS_REFERRALS(orig_conn, r);
1187 if (!NT_STATUS_IS_OK(status)) {
1193 ndr_err = ndr_push_struct_blob(&blob, r,
1195 (ndr_push_flags_fn_t)ndr_push_dfs_referral_resp);
1196 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1198 *pstatus = NT_STATUS_INVALID_PARAMETER;
1202 pdata = (char *)SMB_REALLOC(pdata, blob.length);
1205 DEBUG(0,("referral setup:"
1206 "malloc failed for Realloc!\n"));
1210 reply_size = blob.length;
1211 memcpy(pdata, blob.data, blob.length);
1214 *pstatus = NT_STATUS_OK;
1218 /**********************************************************************
1219 The following functions are called by the NETDFS RPC pipe functions
1220 **********************************************************************/
1222 /*********************************************************************
1223 Creates a junction structure from a DFS pathname
1224 **********************************************************************/
1226 bool create_junction(TALLOC_CTX *ctx,
1227 const char *dfs_path,
1228 bool allow_broken_path,
1229 struct junction_map *jucn)
1233 struct dfs_path *pdp = talloc(ctx,struct dfs_path);
1239 status = parse_dfs_path(NULL, dfs_path, False, allow_broken_path,
1241 if (!NT_STATUS_IS_OK(status)) {
1245 /* check if path is dfs : validate first token */
1246 if (!is_myname_or_ipaddr(pdp->hostname)) {
1247 DEBUG(4,("create_junction: Invalid hostname %s "
1249 pdp->hostname, dfs_path));
1254 /* Check for a non-DFS share */
1255 snum = lp_servicenumber(pdp->servicename);
1257 if(snum < 0 || !lp_msdfs_root(snum)) {
1258 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1264 jucn->service_name = talloc_strdup(ctx, pdp->servicename);
1265 jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
1266 jucn->comment = lp_comment(ctx, snum);
1269 if (!jucn->service_name || !jucn->volume_name || ! jucn->comment) {
1275 /**********************************************************************
1276 Forms a valid Unix pathname from the junction
1277 **********************************************************************/
1279 static bool junction_to_local_path(const struct junction_map *jucn,
1281 connection_struct **conn_out,
1282 struct smb_filename **oldpath_fname)
1287 snum = lp_servicenumber(jucn->service_name);
1291 status = create_conn_struct_cwd(talloc_tos(),
1292 server_event_context(),
1293 server_messaging_context(),
1296 lp_path(talloc_tos(), snum),
1299 if (!NT_STATUS_IS_OK(status)) {
1303 *pp_path_out = talloc_asprintf(*conn_out,
1305 lp_path(talloc_tos(), snum),
1307 if (!*pp_path_out) {
1308 vfs_ChDir(*conn_out, *oldpath_fname);
1309 SMB_VFS_DISCONNECT(*conn_out);
1310 conn_free(*conn_out);
1316 bool create_msdfs_link(const struct junction_map *jucn)
1319 struct smb_filename *cwd_fname = NULL;
1320 char *msdfs_link = NULL;
1321 connection_struct *conn;
1323 bool insert_comma = False;
1325 struct smb_filename *smb_fname = NULL;
1327 if(!junction_to_local_path(jucn, &path, &conn, &cwd_fname)) {
1331 /* Form the msdfs_link contents */
1332 msdfs_link = talloc_strdup(conn, "msdfs:");
1336 for(i=0; i<jucn->referral_count; i++) {
1337 char *refpath = jucn->referral_list[i].alternate_path;
1339 /* Alternate paths always use Windows separators. */
1340 trim_char(refpath, '\\', '\\');
1341 if(*refpath == '\0') {
1343 insert_comma = False;
1347 if (i > 0 && insert_comma) {
1348 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1352 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1360 if (!insert_comma) {
1361 insert_comma = True;
1365 DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
1368 smb_fname = synthetic_smb_fname(talloc_tos(),
1373 if (smb_fname == NULL) {
1378 if(SMB_VFS_SYMLINK(conn, msdfs_link, smb_fname) < 0) {
1379 if (errno == EEXIST) {
1380 if(SMB_VFS_UNLINK(conn, smb_fname)!=0) {
1381 TALLOC_FREE(smb_fname);
1385 if (SMB_VFS_SYMLINK(conn, msdfs_link, smb_fname) < 0) {
1386 DEBUG(1,("create_msdfs_link: symlink failed "
1387 "%s -> %s\nError: %s\n",
1388 path, msdfs_link, strerror(errno)));
1396 TALLOC_FREE(smb_fname);
1397 vfs_ChDir(conn, cwd_fname);
1398 TALLOC_FREE(cwd_fname);
1399 SMB_VFS_DISCONNECT(conn);
1404 bool remove_msdfs_link(const struct junction_map *jucn)
1407 struct smb_filename *cwd_fname = NULL;
1408 connection_struct *conn;
1410 struct smb_filename *smb_fname;
1412 if (!junction_to_local_path(jucn, &path, &conn, &cwd_fname)) {
1416 smb_fname = synthetic_smb_fname(talloc_tos(),
1421 if (smb_fname == NULL) {
1426 if( SMB_VFS_UNLINK(conn, smb_fname) == 0 ) {
1430 TALLOC_FREE(smb_fname);
1431 vfs_ChDir(conn, cwd_fname);
1432 TALLOC_FREE(cwd_fname);
1433 SMB_VFS_DISCONNECT(conn);
1438 /*********************************************************************
1439 Return the number of DFS links at the root of this share.
1440 *********************************************************************/
1442 static int count_dfs_links(TALLOC_CTX *ctx, int snum)
1446 const char *dname = NULL;
1447 char *talloced = NULL;
1448 const char *connect_path = lp_path(talloc_tos(), snum);
1449 const char *msdfs_proxy = lp_msdfs_proxy(talloc_tos(), snum);
1450 connection_struct *conn;
1452 struct smb_filename *cwd_fname = NULL;
1453 struct smb_filename *smb_fname = NULL;
1455 if(*connect_path == '\0') {
1460 * Fake up a connection struct for the VFS layer.
1463 status = create_conn_struct_cwd(talloc_tos(),
1464 server_event_context(),
1465 server_messaging_context(),
1471 if (!NT_STATUS_IS_OK(status)) {
1472 DEBUG(3, ("create_conn_struct failed: %s\n",
1473 nt_errstr(status)));
1477 /* Count a link for the msdfs root - convention */
1480 /* No more links if this is an msdfs proxy. */
1481 if (*msdfs_proxy != '\0') {
1485 smb_fname = synthetic_smb_fname(talloc_tos(),
1490 if (smb_fname == NULL) {
1494 /* Now enumerate all dfs links */
1495 dirp = SMB_VFS_OPENDIR(conn, smb_fname, NULL, 0);
1500 while ((dname = vfs_readdirname(conn, dirp, NULL, &talloced))
1502 struct smb_filename *smb_dname =
1503 synthetic_smb_fname(talloc_tos(),
1508 if (smb_dname == NULL) {
1511 if (is_msdfs_link(conn, smb_dname)) {
1514 TALLOC_FREE(talloced);
1515 TALLOC_FREE(smb_dname);
1518 SMB_VFS_CLOSEDIR(conn,dirp);
1521 TALLOC_FREE(smb_fname);
1522 vfs_ChDir(conn, cwd_fname);
1523 TALLOC_FREE(cwd_fname);
1524 SMB_VFS_DISCONNECT(conn);
1529 /*********************************************************************
1530 *********************************************************************/
1532 static int form_junctions(TALLOC_CTX *ctx,
1534 struct junction_map *jucn,
1539 const char *dname = NULL;
1540 char *talloced = NULL;
1541 const char *connect_path = lp_path(talloc_tos(), snum);
1542 char *service_name = lp_servicename(talloc_tos(), snum);
1543 const char *msdfs_proxy = lp_msdfs_proxy(talloc_tos(), snum);
1544 connection_struct *conn;
1545 struct referral *ref = NULL;
1546 struct smb_filename *cwd_fname = NULL;
1547 struct smb_filename *smb_fname = NULL;
1550 if (jn_remain == 0) {
1554 if(*connect_path == '\0') {
1559 * Fake up a connection struct for the VFS layer.
1562 status = create_conn_struct_cwd(ctx,
1563 server_event_context(),
1564 server_messaging_context(),
1570 if (!NT_STATUS_IS_OK(status)) {
1571 DEBUG(3, ("create_conn_struct failed: %s\n",
1572 nt_errstr(status)));
1576 /* form a junction for the msdfs root - convention
1577 DO NOT REMOVE THIS: NT clients will not work with us
1578 if this is not present
1580 jucn[cnt].service_name = talloc_strdup(ctx,service_name);
1581 jucn[cnt].volume_name = talloc_strdup(ctx, "");
1582 if (!jucn[cnt].service_name || !jucn[cnt].volume_name) {
1585 jucn[cnt].comment = "";
1586 jucn[cnt].referral_count = 1;
1588 ref = jucn[cnt].referral_list = talloc_zero(ctx, struct referral);
1589 if (jucn[cnt].referral_list == NULL) {
1594 ref->ttl = REFERRAL_TTL;
1595 if (*msdfs_proxy != '\0') {
1596 ref->alternate_path = talloc_strdup(ctx,
1599 ref->alternate_path = talloc_asprintf(ctx,
1601 get_local_machine_name(),
1605 if (!ref->alternate_path) {
1610 /* Don't enumerate if we're an msdfs proxy. */
1611 if (*msdfs_proxy != '\0') {
1615 smb_fname = synthetic_smb_fname(talloc_tos(),
1620 if (smb_fname == NULL) {
1624 /* Now enumerate all dfs links */
1625 dirp = SMB_VFS_OPENDIR(conn, smb_fname, NULL, 0);
1630 while ((dname = vfs_readdirname(conn, dirp, NULL, &talloced))
1632 char *link_target = NULL;
1633 struct smb_filename *smb_dname = NULL;
1635 if (cnt >= jn_remain) {
1636 DEBUG(2, ("form_junctions: ran out of MSDFS "
1638 TALLOC_FREE(talloced);
1641 smb_dname = synthetic_smb_fname(talloc_tos(),
1646 if (smb_dname == NULL) {
1647 TALLOC_FREE(talloced);
1650 if (is_msdfs_link_internal(ctx,
1652 smb_dname, &link_target)) {
1653 if (parse_msdfs_symlink(ctx, snum,
1655 &jucn[cnt].referral_list,
1656 &jucn[cnt].referral_count)) {
1658 jucn[cnt].service_name = talloc_strdup(ctx,
1660 jucn[cnt].volume_name = talloc_strdup(ctx,
1662 if (!jucn[cnt].service_name ||
1663 !jucn[cnt].volume_name) {
1664 TALLOC_FREE(talloced);
1667 jucn[cnt].comment = "";
1670 TALLOC_FREE(link_target);
1672 TALLOC_FREE(talloced);
1673 TALLOC_FREE(smb_dname);
1679 SMB_VFS_CLOSEDIR(conn,dirp);
1682 TALLOC_FREE(smb_fname);
1683 vfs_ChDir(conn, cwd_fname);
1684 TALLOC_FREE(cwd_fname);
1689 struct junction_map *enum_msdfs_links(TALLOC_CTX *ctx, size_t *p_num_jn)
1691 struct junction_map *jn = NULL;
1693 size_t jn_count = 0;
1697 if(!lp_host_msdfs()) {
1701 /* Ensure all the usershares are loaded. */
1703 load_registry_shares();
1704 sharecount = load_usershare_shares(NULL, connections_snum_used);
1707 for(i=0;i < sharecount;i++) {
1708 if(lp_msdfs_root(i)) {
1709 jn_count += count_dfs_links(ctx, i);
1712 if (jn_count == 0) {
1715 jn = talloc_array(ctx, struct junction_map, jn_count);
1719 for(i=0; i < sharecount; i++) {
1720 if (*p_num_jn >= jn_count) {
1723 if(lp_msdfs_root(i)) {
1724 *p_num_jn += form_junctions(ctx, i,
1726 jn_count - *p_num_jn);
1732 /******************************************************************************
1733 Core function to resolve a dfs pathname possibly containing a wildcard. If
1734 ppath_contains_wcard != NULL, it will be set to true if a wildcard is
1735 detected during dfs resolution.
1736 ******************************************************************************/
1738 NTSTATUS resolve_dfspath_wcard(TALLOC_CTX *ctx,
1739 connection_struct *conn,
1740 const char *name_in,
1742 bool allow_broken_path,
1744 bool *ppath_contains_wcard)
1746 bool path_contains_wcard = false;
1747 NTSTATUS status = NT_STATUS_OK;
1749 status = dfs_redirect(ctx,
1755 &path_contains_wcard);
1757 if (NT_STATUS_IS_OK(status) &&
1758 ppath_contains_wcard != NULL &&
1759 path_contains_wcard) {
1760 *ppath_contains_wcard = path_contains_wcard;