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 "../auth/auth_util.h"
32 #include "lib/param/loadparm.h"
33 #include "libcli/security/security.h"
34 #include "librpc/gen_ndr/ndr_dfsblobs.h"
35 #include "lib/tsocket/tsocket.h"
36 #include "lib/global_contexts.h"
37 #include "source3/lib/substitute.h"
39 /**********************************************************************
40 Parse a DFS pathname of the form /hostname/service/reqpath
41 into the dfs_path structure.
43 NB. srvstr_get_path_internal() now *always* calls
44 check_path_syntax_XXX() on an incoming name, so
45 the path separator is now always '/', even from
48 Unfortunately, due to broken clients who might set the
49 SVAL(inbuf,smb_flg2) & FLAGS2_DFS_PATHNAMES bit and then
50 send a local path, we have to cope with that too....
52 If conn != NULL then ensure the provided service is
53 the one pointed to by the connection.
55 This version does everything using pointers within one copy of the
56 pathname string, talloced on the struct dfs_path pointer (which
57 must be talloced). This may be too clever to live....
59 **********************************************************************/
61 static NTSTATUS parse_dfs_path(TALLOC_CTX *ctx,
62 connection_struct *conn,
64 bool allow_broken_path,
67 char **_remaining_path)
69 const struct loadparm_substitution *lp_sub =
70 loadparm_s3_global_substitution();
71 char *hostname = NULL;
72 char *pathname_local = NULL;
74 char *servicename = NULL;
78 pathname_local = talloc_strdup(ctx, pathname);
79 if (pathname_local == NULL) {
80 return NT_STATUS_NO_MEMORY;
83 * parse_dfs_path() can be called from
84 * get_referred_path() and create_junction()
85 * which use Windows DFS paths of \server\share.
86 * Ensure we only have to cope with '/' separators.
88 string_replace(pathname_local, '\\', '/');
90 /* Get a pointer to the terminating '\0' */
91 eos_ptr = &pathname_local[strlen(pathname_local)];
95 * Non-broken DFS paths *must* start with the
99 if (allow_broken_path && (*p != '/')) {
100 DBG_ERR("path %s doesn't start with /\n", p);
102 * Possibly client sent a local path by mistake.
103 * Try and convert to a local path.
104 * Note that this is an SMB1-only fallback
105 * to cope with known broken SMB1 clients.
108 hostname = eos_ptr; /* "" */
109 servicename = eos_ptr; /* "" */
111 DBG_ERR("trying to convert %s to a local path\n", p);
116 * Safe to use on talloc'ed string as it only shrinks.
117 * It also doesn't affect the eos_ptr.
119 trim_char(p, '/', '/');
121 DBG_DEBUG("p = |%s| after trimming /'s\n", p);
124 /* Parse out hostname. */
127 DBG_ERR("can't parse hostname from path %s\n", pathname_local);
129 * Possibly client sent a local path by mistake.
130 * Try and convert to a local path.
133 hostname = eos_ptr; /* "" */
134 servicename = eos_ptr; /* "" */
137 DBG_ERR("trying to convert %s to a local path\n", p);
141 hostname = pathname_local;
143 DBG_DEBUG("hostname: %s\n", hostname);
145 /* Parse out servicename. */
147 p = strchr(servicename, '/');
152 /* Is this really our servicename ? */
153 if (conn && !( strequal(servicename, lp_servicename(talloc_tos(), lp_sub, SNUM(conn)))
154 || (strequal(servicename, HOMES_NAME)
155 && strequal(lp_servicename(talloc_tos(), lp_sub, SNUM(conn)),
156 get_current_username()) )) ) {
157 DBG_ERR("%s is not our servicename\n", servicename);
160 * Possibly client sent a local path by mistake.
161 * Try and convert to a local path.
164 /* Repair the path - replace the sepchar's
172 hostname = eos_ptr; /* "" */
173 servicename = eos_ptr; /* "" */
176 DBG_ERR("trying to convert %s to a local path\n",
181 servicename = servicename;
183 DBG_DEBUG("servicename: %s\n", servicename);
186 /* Client sent self referral \server\share. */
187 reqpath = eos_ptr; /* "" */
196 * As check_path_syntax_XXX() has already been
197 * called we know this is a normal path containing
205 DBG_DEBUG("rest of the path: %s\n", reqpath);
207 if (_hostname != NULL) {
208 *_hostname = talloc_strdup(ctx, hostname);
209 if (*_hostname == NULL) {
210 return NT_STATUS_NO_MEMORY;
213 if (_servicename != NULL) {
214 *_servicename = talloc_strdup(ctx, servicename);
215 if (*_servicename == NULL) {
216 return NT_STATUS_NO_MEMORY;
219 if (_remaining_path != NULL) {
220 *_remaining_path = talloc_strdup(ctx, reqpath);
221 if (*_remaining_path == NULL) {
222 return NT_STATUS_NO_MEMORY;
225 TALLOC_FREE(pathname_local);
229 /********************************************************
230 Fake up a connection struct for the VFS layer, for use in
231 applications (such as the python bindings), that do not want the
232 global working directory changed under them.
234 SMB_VFS_CONNECT requires root privileges.
235 *********************************************************/
237 static NTSTATUS create_conn_struct_as_root(TALLOC_CTX *ctx,
238 struct tevent_context *ev,
239 struct messaging_context *msg,
240 connection_struct **pconn,
243 const struct auth_session_info *session_info)
245 connection_struct *conn;
247 const char *vfs_user;
248 struct smbd_server_connection *sconn;
249 const char *servicename = lp_const_servicename(snum);
252 sconn = talloc_zero(ctx, struct smbd_server_connection);
254 return NT_STATUS_NO_MEMORY;
258 sconn->msg_ctx = msg;
260 conn = conn_new(sconn);
263 return NT_STATUS_NO_MEMORY;
266 /* Now we have conn, we need to make sconn a child of conn,
267 * for a proper talloc tree */
268 talloc_steal(conn, sconn);
270 if (snum == -1 && servicename == NULL) {
271 servicename = "Unknown Service (snum == -1)";
274 connpath = talloc_strdup(conn, path);
277 return NT_STATUS_NO_MEMORY;
279 connpath = talloc_string_sub(conn,
285 return NT_STATUS_NO_MEMORY;
288 /* needed for smbd_vfs_init() */
290 conn->params->service = snum;
291 conn->cnum = TID_FIELD_INVALID;
293 SMB_ASSERT(session_info != NULL);
295 conn->session_info = copy_session_info(conn, session_info);
296 if (conn->session_info == NULL) {
297 DBG_ERR("copy_serverinfo failed\n");
299 return NT_STATUS_NO_MEMORY;
302 /* unix_info could be NULL in session_info */
303 if (conn->session_info->unix_info != NULL) {
304 vfs_user = conn->session_info->unix_info->unix_name;
306 vfs_user = get_current_username();
309 conn_setup_case_options(conn);
311 set_conn_connectpath(conn, connpath);
314 * New code to check if there's a share security descriptor
315 * added from NT server manager. This is done after the
316 * smb.conf checks are done as we need a uid and token. JRA.
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 DBG_WARNING("connection to %s "
328 "denied due to security "
332 return NT_STATUS_ACCESS_DENIED;
334 conn->read_only = true;
337 if (!smbd_vfs_init(conn)) {
338 NTSTATUS status = map_nt_error_from_unix(errno);
339 DEBUG(0,("create_conn_struct: smbd_vfs_init failed.\n"));
344 /* this must be the first filesystem operation that we do */
345 if (SMB_VFS_CONNECT(conn, servicename, vfs_user) < 0) {
346 DEBUG(0,("VFS connect failed!\n"));
348 return NT_STATUS_UNSUCCESSFUL;
351 ok = canonicalize_connect_path(conn);
353 DBG_ERR("Failed to canonicalize sharepath\n");
355 return NT_STATUS_ACCESS_DENIED;
358 conn->fs_capabilities = SMB_VFS_FS_CAPABILITIES(conn, &conn->ts_res);
359 conn->tcon_done = true;
360 *pconn = talloc_move(ctx, &conn);
365 static int conn_struct_tos_destructor(struct conn_struct_tos *c)
367 if (c->oldcwd_fname != NULL) {
368 vfs_ChDir(c->conn, c->oldcwd_fname);
369 TALLOC_FREE(c->oldcwd_fname);
371 SMB_VFS_DISCONNECT(c->conn);
376 /********************************************************
377 Fake up a connection struct for the VFS layer, for use in
378 applications (such as the python bindings), that do not want the
379 global working directory changed under them.
381 SMB_VFS_CONNECT requires root privileges.
382 This temporary uses become_root() and unbecome_root().
384 But further impersonation has to be cone by the caller.
385 *********************************************************/
386 NTSTATUS create_conn_struct_tos(struct messaging_context *msg,
389 const struct auth_session_info *session_info,
390 struct conn_struct_tos **_c)
392 struct conn_struct_tos *c = NULL;
393 struct tevent_context *ev = NULL;
398 c = talloc_zero(talloc_tos(), struct conn_struct_tos);
400 return NT_STATUS_NO_MEMORY;
403 ev = samba_tevent_context_init(c);
406 return NT_STATUS_NO_MEMORY;
410 status = create_conn_struct_as_root(c,
418 if (!NT_STATUS_IS_OK(status)) {
423 talloc_set_destructor(c, conn_struct_tos_destructor);
429 /********************************************************
430 Fake up a connection struct for the VFS layer.
431 Note: this performs a vfs connect and CHANGES CWD !!!! JRA.
433 See also the comment for create_conn_struct_tos() above!
435 The CWD change is reverted by the destructor of
436 conn_struct_tos when the current talloc_tos() is destroyed.
437 *********************************************************/
438 NTSTATUS create_conn_struct_tos_cwd(struct messaging_context *msg,
441 const struct auth_session_info *session_info,
442 struct conn_struct_tos **_c)
444 struct conn_struct_tos *c = NULL;
445 struct smb_filename smb_fname_connectpath = {0};
450 status = create_conn_struct_tos(msg,
455 if (!NT_STATUS_IS_OK(status)) {
460 * Windows seems to insist on doing trans2getdfsreferral() calls on
461 * the IPC$ share as the anonymous user. If we try to chdir as that
462 * user we will fail.... WTF ? JRA.
465 c->oldcwd_fname = vfs_GetWd(c, c->conn);
466 if (c->oldcwd_fname == NULL) {
467 status = map_nt_error_from_unix(errno);
468 DEBUG(3, ("vfs_GetWd failed: %s\n", strerror(errno)));
473 smb_fname_connectpath = (struct smb_filename) {
474 .base_name = c->conn->connectpath
477 if (vfs_ChDir(c->conn, &smb_fname_connectpath) != 0) {
478 status = map_nt_error_from_unix(errno);
479 DBG_NOTICE("Can't ChDir to new conn path %s. "
481 c->conn->connectpath, strerror(errno));
482 TALLOC_FREE(c->oldcwd_fname);
491 /********************************************************
492 Fake up a connection struct for the VFS layer.
493 This takes an TALLOC_CTX and tevent_context from the
494 caller and the resulting connection_struct is stable
495 across the lifetime of mem_ctx and ev.
497 Note: this performs a vfs connect and changes cwd.
499 See also the comment for create_conn_struct_tos() above!
500 *********************************************************/
502 NTSTATUS create_conn_struct_cwd(TALLOC_CTX *mem_ctx,
503 struct tevent_context *ev,
504 struct messaging_context *msg,
505 const struct auth_session_info *session_info,
508 struct connection_struct **c)
513 status = create_conn_struct_as_root(mem_ctx,
524 static void shuffle_strlist(char **list, int count)
530 for (i = count; i > 1; i--) {
531 r = generate_random() % i;
539 /**********************************************************************
540 Parse the contents of a symlink to verify if it is an msdfs referral
541 A valid referral is of the form:
543 msdfs:server1\share1,server2\share2
544 msdfs:server1\share1\pathname,server2\share2\pathname
545 msdfs:server1/share1,server2/share2
546 msdfs:server1/share1/pathname,server2/share2/pathname.
548 Note that the alternate paths returned here must be of the canonicalized
552 \server\share\path\to\file,
554 even in posix path mode. This is because we have no knowledge if the
555 server we're referring to understands posix paths.
556 **********************************************************************/
558 bool parse_msdfs_symlink(TALLOC_CTX *ctx,
559 bool shuffle_referrals,
561 struct referral **ppreflist,
566 char **alt_path = NULL;
568 struct referral *reflist = NULL;
571 temp = talloc_strdup(ctx, target);
575 prot = strtok_r(temp, ":", &saveptr);
577 DEBUG(0,("parse_msdfs_symlink: invalid path !\n"));
582 alt_path = talloc_array(ctx, char *, MAX_REFERRAL_COUNT);
588 /* parse out the alternate paths */
589 while((count<MAX_REFERRAL_COUNT) &&
590 ((alt_path[count] = strtok_r(NULL, ",", &saveptr)) != NULL)) {
594 /* shuffle alternate paths */
595 if (shuffle_referrals) {
596 shuffle_strlist(alt_path, count);
599 DBG_DEBUG("count=%zu\n", count);
602 reflist = talloc_zero_array(ctx,
603 struct referral, count);
604 if(reflist == NULL) {
606 TALLOC_FREE(alt_path);
613 for(i=0;i<count;i++) {
616 /* Canonicalize link target.
617 * Replace all /'s in the path by a \ */
618 string_replace(alt_path[i], '/', '\\');
620 /* Remove leading '\\'s */
622 while (*p && (*p == '\\')) {
626 reflist[i].alternate_path = talloc_asprintf(reflist,
629 if (!reflist[i].alternate_path) {
631 TALLOC_FREE(alt_path);
632 TALLOC_FREE(reflist);
636 reflist[i].proximity = 0;
637 reflist[i].ttl = REFERRAL_TTL;
638 DBG_DEBUG("Created alt path: %s\n",
639 reflist[i].alternate_path);
642 if (ppreflist != NULL) {
643 *ppreflist = reflist;
645 TALLOC_FREE(reflist);
647 if (prefcount != NULL) {
651 TALLOC_FREE(alt_path);
655 /**********************************************************************
656 Returns true if the unix path is a valid msdfs symlink.
657 **********************************************************************/
659 bool is_msdfs_link(struct files_struct *dirfsp,
660 struct smb_filename *atname)
662 NTSTATUS status = SMB_VFS_READ_DFS_PATHAT(dirfsp->conn,
668 return (NT_STATUS_IS_OK(status));
671 /*****************************************************************
672 Used by other functions to decide if a dfs path is remote,
673 and to get the list of referred locations for that remote path.
675 consumedcntp: how much of the dfs path is being redirected. the client
676 should try the remaining path on the redirected server.
678 If this returns NT_STATUS_PATH_NOT_COVERED the contents of the msdfs
679 link redirect are in targetpath.
680 *****************************************************************/
682 static NTSTATUS dfs_path_lookup(TALLOC_CTX *ctx,
683 connection_struct *conn,
684 const char *dfspath, /* Incoming complete dfs path */
685 const char *reqpath, /* Parsed out remaining path. */
688 size_t *consumedcntp,
689 struct referral **ppreflist,
690 size_t *preferral_count)
695 struct smb_filename *smb_fname = NULL;
696 struct smb_filename *parent_fname = NULL;
697 struct smb_filename *atname = NULL;
698 char *canon_dfspath = NULL; /* Canonicalized dfs path. (only '/'
701 DEBUG(10,("dfs_path_lookup: Conn path = %s reqpath = %s\n",
702 conn->connectpath, reqpath));
705 * Note the unix path conversion here we're doing we
706 * throw away. We're looking for a symlink for a dfs
707 * resolution, if we don't find it we'll do another
708 * unix_convert later in the codepath.
711 status = unix_convert(ctx, conn, reqpath, 0, &smb_fname,
714 if (!NT_STATUS_IS_OK(status)) {
715 if (!NT_STATUS_EQUAL(status,
716 NT_STATUS_OBJECT_PATH_NOT_FOUND)) {
719 if (smb_fname == NULL || smb_fname->base_name == NULL) {
724 /* Optimization - check if we can redirect the whole path. */
725 status = parent_pathref(ctx,
730 if (NT_STATUS_IS_OK(status)) {
732 * We must have a parent_fname->fsp before
733 * we can call SMB_VFS_READ_DFS_PATHAT().
735 status = SMB_VFS_READ_DFS_PATHAT(conn,
741 /* We're now done with parent_fname and atname. */
742 TALLOC_FREE(parent_fname);
744 if (NT_STATUS_IS_OK(status)) {
745 DBG_INFO("%s resolves to a valid dfs link\n",
749 *consumedcntp = strlen(dfspath);
751 status = NT_STATUS_PATH_NOT_COVERED;
756 /* Prepare to test only for '/' components in the given path,
757 * so if a Windows path replace all '\\' characters with '/'.
758 * For a POSIX DFS path we know all separators are already '/'. */
760 canon_dfspath = talloc_strdup(ctx, dfspath);
761 if (!canon_dfspath) {
762 status = NT_STATUS_NO_MEMORY;
765 string_replace(canon_dfspath, '\\', '/');
768 * localpath comes out of unix_convert, so it has
769 * no trailing backslash. Make sure that canon_dfspath hasn't either.
770 * Fix for bug #4860 from Jan Martin <Jan.Martin@rwedea.com>.
773 trim_char(canon_dfspath,0,'/');
776 * Redirect if any component in the path is a link.
777 * We do this by walking backwards through the
778 * local path, chopping off the last component
779 * in both the local path and the canonicalized
780 * DFS path. If we hit a DFS link then we're done.
783 p = strrchr_m(smb_fname->base_name, '/');
785 q = strrchr_m(canon_dfspath, '/');
795 * Ensure parent_pathref() calls vfs_stat() on
796 * the newly truncated path.
798 SET_STAT_INVALID(smb_fname->st);
799 status = parent_pathref(ctx,
804 if (NT_STATUS_IS_OK(status)) {
806 * We must have a parent_fname->fsp before
807 * we can call SMB_VFS_READ_DFS_PATHAT().
809 status = SMB_VFS_READ_DFS_PATHAT(conn,
816 /* We're now done with parent_fname and atname. */
817 TALLOC_FREE(parent_fname);
819 if (NT_STATUS_IS_OK(status)) {
820 DBG_INFO("Redirecting %s because "
821 "parent %s is a dfs link\n",
823 smb_fname_str_dbg(smb_fname));
826 *consumedcntp = strlen(canon_dfspath);
827 DBG_DEBUG("Path consumed: %s "
833 status = NT_STATUS_PATH_NOT_COVERED;
838 /* Step back on the filesystem. */
839 p = strrchr_m(smb_fname->base_name, '/');
842 /* And in the canonicalized dfs path. */
843 q = strrchr_m(canon_dfspath, '/');
847 if ((ucf_flags & UCF_GMT_PATHNAME) && _twrp != NULL) {
848 *_twrp = smb_fname->twrp;
851 status = NT_STATUS_OK;
854 /* This should already be free, but make sure. */
855 TALLOC_FREE(parent_fname);
856 TALLOC_FREE(smb_fname);
860 /*****************************************************************
861 Decides if a dfs pathname should be redirected or not.
862 If not, the pathname is converted to a tcon-relative local unix path
864 search_wcard_flag: this flag performs 2 functions both related
865 to searches. See resolve_dfs_path() and parse_dfs_path_XX()
868 This function can return NT_STATUS_OK, meaning use the returned path as-is
869 (mapped into a local path).
870 or NT_STATUS_NOT_COVERED meaning return a DFS redirect, or
871 any other NT_STATUS error which is a genuine error to be
872 returned to the client.
873 *****************************************************************/
875 NTSTATUS dfs_redirect(TALLOC_CTX *ctx,
876 connection_struct *conn,
879 bool allow_broken_path,
883 const struct loadparm_substitution *lp_sub =
884 loadparm_s3_global_substitution();
885 char *hostname = NULL;
886 char *servicename = NULL;
887 char *reqpath = NULL;
890 status = parse_dfs_path(ctx,
897 if (!NT_STATUS_IS_OK(status)) {
901 if (reqpath[0] == '\0') {
902 *pp_path_out = talloc_strdup(ctx, "");
904 return NT_STATUS_NO_MEMORY;
906 DEBUG(5,("dfs_redirect: self-referral.\n"));
910 /* If dfs pathname for a non-dfs share, convert to tcon-relative
911 path and return OK */
913 if (!lp_msdfs_root(SNUM(conn))) {
914 *pp_path_out = talloc_strdup(ctx, reqpath);
916 return NT_STATUS_NO_MEMORY;
921 /* If it looked like a local path (zero hostname/servicename)
922 * just treat as a tcon-relative path. */
924 if (hostname[0] == '\0' && servicename[0] == '\0') {
925 *pp_path_out = talloc_strdup(ctx, reqpath);
927 return NT_STATUS_NO_MEMORY;
932 if (!( strequal(servicename, lp_servicename(talloc_tos(), lp_sub, SNUM(conn)))
933 || (strequal(servicename, HOMES_NAME)
934 && strequal(lp_servicename(talloc_tos(), lp_sub, SNUM(conn)),
935 conn->session_info->unix_info->sanitized_username) )) ) {
937 /* The given sharename doesn't match this connection. */
938 return NT_STATUS_OBJECT_PATH_NOT_FOUND;
941 status = dfs_path_lookup(ctx,
947 NULL, /* size_t *consumedcntp */
948 NULL, /* struct referral **ppreflist */
949 NULL); /* size_t *preferral_count */
950 if (!NT_STATUS_IS_OK(status)) {
951 if (NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
952 DEBUG(3,("dfs_redirect: Redirecting %s\n", path_in));
954 DEBUG(10,("dfs_redirect: dfs_path_lookup "
955 "failed for %s with %s\n",
956 path_in, nt_errstr(status) ));
961 DEBUG(3,("dfs_redirect: Not redirecting %s.\n", path_in));
963 /* Form non-dfs tcon-relative path */
964 *pp_path_out = talloc_strdup(ctx, reqpath);
966 return NT_STATUS_NO_MEMORY;
969 DEBUG(3,("dfs_redirect: Path %s converted to non-dfs path %s\n",
976 /**********************************************************************
977 Return a self referral.
978 **********************************************************************/
980 static NTSTATUS self_ref(TALLOC_CTX *ctx,
981 const char *dfs_path,
982 struct junction_map *jucn,
983 size_t *consumedcntp,
984 bool *self_referralp)
986 struct referral *ref;
988 *self_referralp = True;
990 jucn->referral_count = 1;
991 if((ref = talloc_zero(ctx, struct referral)) == NULL) {
992 return NT_STATUS_NO_MEMORY;
995 ref->alternate_path = talloc_strdup(ctx, dfs_path);
996 if (!ref->alternate_path) {
998 return NT_STATUS_NO_MEMORY;
1001 ref->ttl = REFERRAL_TTL;
1002 jucn->referral_list = ref;
1003 *consumedcntp = strlen(dfs_path);
1004 return NT_STATUS_OK;
1007 /**********************************************************************
1008 Gets valid referrals for a dfs path and fills up the
1009 junction_map structure.
1010 **********************************************************************/
1012 NTSTATUS get_referred_path(TALLOC_CTX *ctx,
1013 struct auth_session_info *session_info,
1014 const char *dfs_path,
1015 const struct tsocket_address *remote_address,
1016 const struct tsocket_address *local_address,
1017 bool allow_broken_path,
1018 struct junction_map *jucn,
1019 size_t *consumedcntp,
1020 bool *self_referralp)
1022 TALLOC_CTX *frame = talloc_stackframe();
1023 const struct loadparm_substitution *lp_sub =
1024 loadparm_s3_global_substitution();
1025 struct conn_struct_tos *c = NULL;
1026 struct connection_struct *conn = NULL;
1027 char *servicename = NULL;
1028 char *reqpath = NULL;
1030 NTSTATUS status = NT_STATUS_NOT_FOUND;
1032 *self_referralp = False;
1034 status = parse_dfs_path(frame,
1041 if (!NT_STATUS_IS_OK(status)) {
1046 jucn->service_name = talloc_strdup(ctx, servicename);
1047 jucn->volume_name = talloc_strdup(ctx, reqpath);
1048 if (!jucn->service_name || !jucn->volume_name) {
1050 return NT_STATUS_NO_MEMORY;
1053 /* Verify the share is a dfs root */
1054 snum = lp_servicenumber(jucn->service_name);
1056 char *service_name = NULL;
1057 if ((snum = find_service(ctx, jucn->service_name, &service_name)) < 0) {
1059 return NT_STATUS_NOT_FOUND;
1061 if (!service_name) {
1063 return NT_STATUS_NO_MEMORY;
1065 TALLOC_FREE(jucn->service_name);
1066 jucn->service_name = talloc_strdup(ctx, service_name);
1067 if (!jucn->service_name) {
1069 return NT_STATUS_NO_MEMORY;
1073 if (!lp_msdfs_root(snum) && (*lp_msdfs_proxy(talloc_tos(), lp_sub, snum) == '\0')) {
1074 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not "
1076 servicename, dfs_path));
1078 return NT_STATUS_NOT_FOUND;
1082 * Self referrals are tested with a anonymous IPC connection and
1083 * a GET_DFS_REFERRAL call to \\server\share. (which means
1084 * dp.reqpath[0] points to an empty string). create_conn_struct cd's
1085 * into the directory and will fail if it cannot (as the anonymous
1086 * user). Cope with this.
1089 if (reqpath[0] == '\0') {
1091 struct referral *ref;
1094 if (*lp_msdfs_proxy(talloc_tos(), lp_sub, snum) == '\0') {
1096 return self_ref(ctx,
1104 * It's an msdfs proxy share. Redirect to
1105 * the configured target share.
1108 tmp = talloc_asprintf(frame, "msdfs:%s",
1109 lp_msdfs_proxy(frame, lp_sub, snum));
1112 return NT_STATUS_NO_MEMORY;
1115 if (!parse_msdfs_symlink(ctx,
1116 lp_msdfs_shuffle_referrals(snum),
1121 return NT_STATUS_INVALID_PARAMETER;
1123 jucn->referral_count = refcount;
1124 jucn->referral_list = ref;
1125 *consumedcntp = strlen(dfs_path);
1127 return NT_STATUS_OK;
1130 status = create_conn_struct_tos_cwd(global_messaging_context(),
1132 lp_path(frame, lp_sub, snum),
1135 if (!NT_STATUS_IS_OK(status)) {
1144 * The remote and local address should be passed down to
1145 * create_conn_struct_cwd.
1147 if (conn->sconn->remote_address == NULL) {
1148 conn->sconn->remote_address =
1149 tsocket_address_copy(remote_address, conn->sconn);
1150 if (conn->sconn->remote_address == NULL) {
1152 return NT_STATUS_NO_MEMORY;
1155 if (conn->sconn->local_address == NULL) {
1156 conn->sconn->local_address =
1157 tsocket_address_copy(local_address, conn->sconn);
1158 if (conn->sconn->local_address == NULL) {
1160 return NT_STATUS_NO_MEMORY;
1164 /* If this is a DFS path dfs_lookup should return
1165 * NT_STATUS_PATH_NOT_COVERED. */
1167 status = dfs_path_lookup(ctx,
1174 &jucn->referral_list,
1175 &jucn->referral_count);
1177 if (!NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
1178 DEBUG(3,("get_referred_path: No valid referrals for path %s\n",
1180 if (NT_STATUS_IS_OK(status)) {
1182 * We are in an error path here (we
1183 * know it's not a DFS path), but
1184 * dfs_path_lookup() can return
1185 * NT_STATUS_OK. Ensure we always
1186 * return a valid error code.
1188 * #9588 - ACLs are not inherited to directories
1191 status = NT_STATUS_NOT_FOUND;
1196 status = NT_STATUS_OK;
1202 /******************************************************************
1203 Set up the DFS referral for the dfs pathname. This call returns
1204 the amount of the path covered by this server, and where the
1205 client should be redirected to. This is the meat of the
1206 TRANS2_GET_DFS_REFERRAL call.
1207 ******************************************************************/
1209 int setup_dfs_referral(connection_struct *orig_conn,
1210 const char *dfs_path,
1211 int max_referral_level,
1212 char **ppdata, NTSTATUS *pstatus)
1214 char *pdata = *ppdata;
1216 struct dfs_GetDFSReferral *r;
1217 DATA_BLOB blob = data_blob_null;
1219 enum ndr_err_code ndr_err;
1221 r = talloc_zero(talloc_tos(), struct dfs_GetDFSReferral);
1223 *pstatus = NT_STATUS_NO_MEMORY;
1227 r->in.req.max_referral_level = max_referral_level;
1228 r->in.req.servername = talloc_strdup(r, dfs_path);
1229 if (r->in.req.servername == NULL) {
1231 *pstatus = NT_STATUS_NO_MEMORY;
1235 status = SMB_VFS_GET_DFS_REFERRALS(orig_conn, r);
1236 if (!NT_STATUS_IS_OK(status)) {
1242 ndr_err = ndr_push_struct_blob(&blob, r,
1244 (ndr_push_flags_fn_t)ndr_push_dfs_referral_resp);
1245 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1247 *pstatus = NT_STATUS_INVALID_PARAMETER;
1251 pdata = (char *)SMB_REALLOC(pdata, blob.length);
1254 DEBUG(0,("referral setup:"
1255 "malloc failed for Realloc!\n"));
1259 reply_size = blob.length;
1260 memcpy(pdata, blob.data, blob.length);
1263 *pstatus = NT_STATUS_OK;
1267 /**********************************************************************
1268 The following functions are called by the NETDFS RPC pipe functions
1269 **********************************************************************/
1271 /*********************************************************************
1272 Creates a junction structure from a DFS pathname
1273 **********************************************************************/
1275 bool create_junction(TALLOC_CTX *ctx,
1276 const char *dfs_path,
1277 bool allow_broken_path,
1278 struct junction_map *jucn)
1280 const struct loadparm_substitution *lp_sub =
1281 loadparm_s3_global_substitution();
1283 char *hostname = NULL;
1284 char *servicename = NULL;
1285 char *reqpath = NULL;
1288 status = parse_dfs_path(ctx,
1295 if (!NT_STATUS_IS_OK(status)) {
1299 /* check if path is dfs : validate first token */
1300 if (!is_myname_or_ipaddr(hostname)) {
1301 DEBUG(4,("create_junction: Invalid hostname %s "
1303 hostname, dfs_path));
1307 /* Check for a non-DFS share */
1308 snum = lp_servicenumber(servicename);
1310 if(snum < 0 || !lp_msdfs_root(snum)) {
1311 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1316 jucn->service_name = talloc_strdup(ctx, servicename);
1317 jucn->volume_name = talloc_strdup(ctx, reqpath);
1318 jucn->comment = lp_comment(ctx, lp_sub, snum);
1320 if (!jucn->service_name || !jucn->volume_name || ! jucn->comment) {
1326 /**********************************************************************
1327 Forms a valid Unix pathname from the junction
1328 **********************************************************************/
1330 static bool junction_to_local_path_tos(const struct junction_map *jucn,
1331 struct auth_session_info *session_info,
1333 connection_struct **conn_out)
1335 const struct loadparm_substitution *lp_sub =
1336 loadparm_s3_global_substitution();
1337 struct conn_struct_tos *c = NULL;
1339 char *path_out = NULL;
1342 snum = lp_servicenumber(jucn->service_name);
1346 status = create_conn_struct_tos_cwd(global_messaging_context(),
1348 lp_path(talloc_tos(), lp_sub, snum),
1351 if (!NT_STATUS_IS_OK(status)) {
1355 path_out = talloc_asprintf(c,
1357 lp_path(talloc_tos(), lp_sub, snum),
1359 if (path_out == NULL) {
1363 *pp_path_out = path_out;
1364 *conn_out = c->conn;
1369 * Create a msdfs string in Samba format we can store
1370 * in a filesystem object (currently a symlink).
1373 char *msdfs_link_string(TALLOC_CTX *ctx,
1374 const struct referral *reflist,
1375 size_t referral_count)
1377 char *refpath = NULL;
1378 bool insert_comma = false;
1379 char *msdfs_link = NULL;
1382 /* Form the msdfs_link contents */
1383 msdfs_link = talloc_strdup(ctx, "msdfs:");
1384 if (msdfs_link == NULL) {
1388 for( i= 0; i < referral_count; i++) {
1389 refpath = talloc_strdup(ctx, reflist[i].alternate_path);
1391 if (refpath == NULL) {
1395 /* Alternate paths always use Windows separators. */
1396 trim_char(refpath, '\\', '\\');
1397 if (*refpath == '\0') {
1399 insert_comma = false;
1403 if (i > 0 && insert_comma) {
1404 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1408 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1413 if (msdfs_link == NULL) {
1417 if (!insert_comma) {
1418 insert_comma = true;
1421 TALLOC_FREE(refpath);
1428 TALLOC_FREE(refpath);
1429 TALLOC_FREE(msdfs_link);
1433 bool create_msdfs_link(const struct junction_map *jucn,
1434 struct auth_session_info *session_info)
1436 TALLOC_CTX *frame = talloc_stackframe();
1438 connection_struct *conn;
1439 struct smb_filename *smb_fname = NULL;
1440 struct smb_filename *parent_fname = NULL;
1441 struct smb_filename *at_fname = NULL;
1446 ok = junction_to_local_path_tos(jucn, session_info, &path, &conn);
1451 if (!CAN_WRITE(conn)) {
1452 const struct loadparm_substitution *lp_sub =
1453 loadparm_s3_global_substitution();
1454 int snum = lp_servicenumber(jucn->service_name);
1456 DBG_WARNING("Can't create DFS entry on read-only share %s\n",
1457 lp_servicename(frame, lp_sub, snum));
1461 smb_fname = synthetic_smb_fname(frame,
1467 if (smb_fname == NULL) {
1471 status = parent_pathref(frame,
1476 if (!NT_STATUS_IS_OK(status)) {
1480 status = SMB_VFS_CREATE_DFS_PATHAT(conn,
1483 jucn->referral_list,
1484 jucn->referral_count);
1485 if (!NT_STATUS_IS_OK(status)) {
1486 if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_COLLISION)) {
1487 int retval = SMB_VFS_UNLINKAT(conn,
1495 status = SMB_VFS_CREATE_DFS_PATHAT(conn,
1498 jucn->referral_list,
1499 jucn->referral_count);
1500 if (!NT_STATUS_IS_OK(status)) {
1501 DBG_WARNING("SMB_VFS_CREATE_DFS_PATHAT failed "
1516 bool remove_msdfs_link(const struct junction_map *jucn,
1517 struct auth_session_info *session_info)
1519 TALLOC_CTX *frame = talloc_stackframe();
1521 connection_struct *conn;
1523 struct smb_filename *smb_fname;
1524 struct smb_filename *parent_fname = NULL;
1525 struct smb_filename *at_fname = NULL;
1530 ok = junction_to_local_path_tos(jucn, session_info, &path, &conn);
1536 if (!CAN_WRITE(conn)) {
1537 const struct loadparm_substitution *lp_sub =
1538 loadparm_s3_global_substitution();
1539 int snum = lp_servicenumber(jucn->service_name);
1541 DBG_WARNING("Can't remove DFS entry on read-only share %s\n",
1542 lp_servicename(frame, lp_sub, snum));
1547 smb_fname = synthetic_smb_fname(frame,
1553 if (smb_fname == NULL) {
1559 status = parent_pathref(frame,
1564 if (!NT_STATUS_IS_OK(status)) {
1569 retval = SMB_VFS_UNLINKAT(conn,
1581 /*********************************************************************
1582 Return the number of DFS links at the root of this share.
1583 *********************************************************************/
1585 static size_t count_dfs_links(TALLOC_CTX *ctx,
1586 struct auth_session_info *session_info,
1589 TALLOC_CTX *frame = talloc_stackframe();
1590 const struct loadparm_substitution *lp_sub =
1591 loadparm_s3_global_substitution();
1593 const char *dname = NULL;
1594 char *talloced = NULL;
1595 const char *connect_path = lp_path(frame, lp_sub, snum);
1596 const char *msdfs_proxy = lp_msdfs_proxy(frame, lp_sub, snum);
1597 struct conn_struct_tos *c = NULL;
1598 connection_struct *conn = NULL;
1600 struct smb_filename *smb_fname = NULL;
1601 struct smb_Dir *dir_hnd = NULL;
1604 if(*connect_path == '\0') {
1610 * Fake up a connection struct for the VFS layer.
1613 status = create_conn_struct_tos_cwd(global_messaging_context(),
1618 if (!NT_STATUS_IS_OK(status)) {
1619 DEBUG(3, ("create_conn_struct failed: %s\n",
1620 nt_errstr(status)));
1626 /* Count a link for the msdfs root - convention */
1629 /* No more links if this is an msdfs proxy. */
1630 if (*msdfs_proxy != '\0') {
1634 smb_fname = synthetic_smb_fname(frame,
1640 if (smb_fname == NULL) {
1644 /* Now enumerate all dfs links */
1645 status = OpenDir(frame,
1651 if (!NT_STATUS_IS_OK(status)) {
1652 errno = map_errno_from_nt_status(status);
1656 while ((dname = ReadDirName(dir_hnd, &offset, NULL, &talloced))
1659 struct smb_filename *smb_dname =
1660 synthetic_smb_fname(frame,
1666 if (smb_dname == NULL) {
1669 if (is_msdfs_link(dir_hnd_fetch_fsp(dir_hnd), smb_dname)) {
1670 if (cnt + 1 < cnt) {
1676 TALLOC_FREE(talloced);
1677 TALLOC_FREE(smb_dname);
1685 /*********************************************************************
1686 *********************************************************************/
1688 static int form_junctions(TALLOC_CTX *ctx,
1689 struct auth_session_info *session_info,
1691 struct junction_map *jucn,
1694 TALLOC_CTX *frame = talloc_stackframe();
1695 const struct loadparm_substitution *lp_sub =
1696 loadparm_s3_global_substitution();
1698 const char *dname = NULL;
1699 char *talloced = NULL;
1700 const char *connect_path = lp_path(frame, lp_sub, snum);
1701 char *service_name = lp_servicename(frame, lp_sub, snum);
1702 const char *msdfs_proxy = lp_msdfs_proxy(frame, lp_sub, snum);
1703 struct conn_struct_tos *c = NULL;
1704 connection_struct *conn = NULL;
1705 struct referral *ref = NULL;
1706 struct smb_filename *smb_fname = NULL;
1707 struct smb_Dir *dir_hnd = NULL;
1711 if (jn_remain == 0) {
1716 if(*connect_path == '\0') {
1722 * Fake up a connection struct for the VFS layer.
1725 status = create_conn_struct_tos_cwd(global_messaging_context(),
1730 if (!NT_STATUS_IS_OK(status)) {
1731 DEBUG(3, ("create_conn_struct failed: %s\n",
1732 nt_errstr(status)));
1738 /* form a junction for the msdfs root - convention
1739 DO NOT REMOVE THIS: NT clients will not work with us
1740 if this is not present
1742 jucn[cnt].service_name = talloc_strdup(ctx,service_name);
1743 jucn[cnt].volume_name = talloc_strdup(ctx, "");
1744 if (!jucn[cnt].service_name || !jucn[cnt].volume_name) {
1747 jucn[cnt].comment = "";
1748 jucn[cnt].referral_count = 1;
1750 ref = jucn[cnt].referral_list = talloc_zero(ctx, struct referral);
1751 if (jucn[cnt].referral_list == NULL) {
1756 ref->ttl = REFERRAL_TTL;
1757 if (*msdfs_proxy != '\0') {
1758 ref->alternate_path = talloc_strdup(ctx,
1761 ref->alternate_path = talloc_asprintf(ctx,
1763 get_local_machine_name(),
1767 if (!ref->alternate_path) {
1772 /* Don't enumerate if we're an msdfs proxy. */
1773 if (*msdfs_proxy != '\0') {
1777 smb_fname = synthetic_smb_fname(frame,
1783 if (smb_fname == NULL) {
1787 /* Now enumerate all dfs links */
1788 status = OpenDir(frame,
1794 if (!NT_STATUS_IS_OK(status)) {
1795 errno = map_errno_from_nt_status(status);
1799 while ((dname = ReadDirName(dir_hnd, &offset, NULL, &talloced))
1802 struct smb_filename *smb_dname = NULL;
1804 if (cnt >= jn_remain) {
1805 DEBUG(2, ("form_junctions: ran out of MSDFS "
1807 TALLOC_FREE(talloced);
1810 smb_dname = synthetic_smb_fname(talloc_tos(),
1816 if (smb_dname == NULL) {
1817 TALLOC_FREE(talloced);
1821 status = SMB_VFS_READ_DFS_PATHAT(conn,
1825 &jucn[cnt].referral_list,
1826 &jucn[cnt].referral_count);
1828 if (NT_STATUS_IS_OK(status)) {
1829 jucn[cnt].service_name = talloc_strdup(ctx,
1831 jucn[cnt].volume_name = talloc_strdup(ctx, dname);
1832 if (!jucn[cnt].service_name || !jucn[cnt].volume_name) {
1833 TALLOC_FREE(talloced);
1836 jucn[cnt].comment = "";
1839 TALLOC_FREE(talloced);
1840 TALLOC_FREE(smb_dname);
1848 struct junction_map *enum_msdfs_links(TALLOC_CTX *ctx,
1849 struct auth_session_info *session_info,
1852 struct junction_map *jn = NULL;
1854 size_t jn_count = 0;
1858 if(!lp_host_msdfs()) {
1862 /* Ensure all the usershares are loaded. */
1864 load_registry_shares();
1865 sharecount = load_usershare_shares(NULL, connections_snum_used);
1868 for(i=0;i < sharecount;i++) {
1869 if(lp_msdfs_root(i)) {
1870 jn_count += count_dfs_links(ctx, session_info, i);
1873 if (jn_count == 0) {
1876 jn = talloc_array(ctx, struct junction_map, jn_count);
1880 for(i=0; i < sharecount; i++) {
1881 if (*p_num_jn >= jn_count) {
1884 if(lp_msdfs_root(i)) {
1885 *p_num_jn += form_junctions(ctx,
1889 jn_count - *p_num_jn);