}
static int non_widelink_open(struct connection_struct *conn,
- const char *conn_rootdir,
+ const struct smb_filename *conn_rootdir_fname,
files_struct *fsp,
struct smb_filename *smb_fname,
int flags,
****************************************************************************/
static int process_symlink_open(struct connection_struct *conn,
- const char *conn_rootdir,
+ const struct smb_filename *conn_rootdir_fname,
files_struct *fsp,
struct smb_filename *smb_fname,
int flags,
{
int fd = -1;
char *link_target = NULL;
+ struct smb_filename target_fname = {0};
int link_len = -1;
- char *oldwd = NULL;
+ struct smb_filename *oldwd_fname = NULL;
size_t rootdir_len = 0;
+ struct smb_filename *resolved_fname = NULL;
char *resolved_name = NULL;
bool matched = false;
int saved_errno = 0;
/* Read the link target. */
link_len = SMB_VFS_READLINK(conn,
- smb_fname->base_name,
+ smb_fname,
link_target,
PATH_MAX - 1);
if (link_len == -1) {
/* Ensure it's at least null terminated. */
link_target[link_len] = '\0';
+ target_fname = (struct smb_filename){ .base_name = link_target };
/* Convert to an absolute path. */
- resolved_name = SMB_VFS_REALPATH(conn, link_target);
- if (resolved_name == NULL) {
+ resolved_fname = SMB_VFS_REALPATH(conn, talloc_tos(), &target_fname);
+ if (resolved_fname == NULL) {
goto out;
}
+ resolved_name = resolved_fname->base_name;
/*
* We know conn_rootdir starts with '/' and
* does not end in '/'. FIXME ! Should we
* smb_assert this ?
*/
- rootdir_len = strlen(conn_rootdir);
+ rootdir_len = strlen(conn_rootdir_fname->base_name);
- matched = (strncmp(conn_rootdir, resolved_name, rootdir_len) == 0);
+ matched = (strncmp(conn_rootdir_fname->base_name,
+ resolved_name,
+ rootdir_len) == 0);
if (!matched) {
errno = EACCES;
goto out;
*/
if (resolved_name[rootdir_len] == '\0') {
/* Link to the root of the share. */
- smb_fname->base_name = talloc_strdup(talloc_tos(), ".");
- if (smb_fname->base_name == NULL) {
- errno = ENOMEM;
- goto out;
- }
+ TALLOC_FREE(smb_fname->base_name);
+ smb_fname->base_name = talloc_strdup(smb_fname, ".");
} else if (resolved_name[rootdir_len] == '/') {
- smb_fname->base_name = &resolved_name[rootdir_len+1];
+ TALLOC_FREE(smb_fname->base_name);
+ smb_fname->base_name = talloc_strdup(smb_fname,
+ &resolved_name[rootdir_len+1]);
} else {
errno = EACCES;
goto out;
}
- oldwd = vfs_GetWd(talloc_tos(), conn);
- if (oldwd == NULL) {
+ if (smb_fname->base_name == NULL) {
+ errno = ENOMEM;
+ goto out;
+ }
+
+ oldwd_fname = vfs_GetWd(talloc_tos(), conn);
+ if (oldwd_fname == NULL) {
goto out;
}
/* Ensure we operate from the root of the share. */
- if (vfs_ChDir(conn, conn_rootdir) == -1) {
+ if (vfs_ChDir(conn, conn_rootdir_fname) == -1) {
goto out;
}
/* And do it all again.. */
fd = non_widelink_open(conn,
- conn_rootdir,
+ conn_rootdir_fname,
fsp,
smb_fname,
flags,
out:
- SAFE_FREE(resolved_name);
+ TALLOC_FREE(resolved_fname);
TALLOC_FREE(link_target);
- if (oldwd != NULL) {
- int ret = vfs_ChDir(conn, oldwd);
+ if (oldwd_fname != NULL) {
+ int ret = vfs_ChDir(conn, oldwd_fname);
if (ret == -1) {
smb_panic("unable to get back to old directory\n");
}
- TALLOC_FREE(oldwd);
+ TALLOC_FREE(oldwd_fname);
}
if (saved_errno != 0) {
errno = saved_errno;
****************************************************************************/
static int non_widelink_open(struct connection_struct *conn,
- const char *conn_rootdir,
+ const struct smb_filename *conn_rootdir_fname,
files_struct *fsp,
struct smb_filename *smb_fname,
int flags,
int fd = -1;
struct smb_filename *smb_fname_rel = NULL;
int saved_errno = 0;
- char *oldwd = NULL;
+ struct smb_filename *oldwd_fname = NULL;
char *parent_dir = NULL;
+ struct smb_filename parent_dir_fname = {0};
const char *final_component = NULL;
if (!parent_dirname(talloc_tos(),
goto out;
}
- oldwd = vfs_GetWd(talloc_tos(), conn);
- if (oldwd == NULL) {
+ parent_dir_fname = (struct smb_filename) { .base_name = parent_dir };
+
+ oldwd_fname = vfs_GetWd(talloc_tos(), conn);
+ if (oldwd_fname == NULL) {
goto out;
}
/* Pin parent directory in place. */
- if (vfs_ChDir(conn, parent_dir) == -1) {
+ if (vfs_ChDir(conn, &parent_dir_fname) == -1) {
goto out;
}
/* Ensure the relative path is below the share. */
- status = check_reduced_name(conn, final_component);
+ status = check_reduced_name(conn, parent_dir, final_component);
if (!NT_STATUS_IS_OK(status)) {
saved_errno = map_errno_from_nt_status(status);
goto out;
if (fd == -1) {
saved_errno = link_errno_convert(errno);
- if (saved_errno == ELOOP) {
+ /*
+ * Trying to open a symlink to a directory with O_NOFOLLOW and
+ * O_DIRECTORY can return either of ELOOP and ENOTDIR. So
+ * ENOTDIR really means: might be a symlink, but we're not sure.
+ * In this case, we just assume there's a symlink. If we were
+ * wrong, process_symlink_open() will return EINVAL. We check
+ * this below, and fall back to returning the initial
+ * saved_errno.
+ *
+ * BUG: https://bugzilla.samba.org/show_bug.cgi?id=12860
+ */
+ if (saved_errno == ELOOP || saved_errno == ENOTDIR) {
if (fsp->posix_flags & FSP_POSIX_FLAGS_OPEN) {
/* Never follow symlinks on posix open. */
goto out;
goto out;
}
/*
- * We have a symlink. Follow in userspace
+ * We may have a symlink. Follow in userspace
* to ensure it's under the share definition.
*/
fd = process_symlink_open(conn,
- conn_rootdir,
+ conn_rootdir_fname,
fsp,
smb_fname_rel,
flags,
mode,
link_depth);
if (fd == -1) {
+ if (saved_errno == ENOTDIR &&
+ errno == EINVAL) {
+ /*
+ * O_DIRECTORY on neither a directory,
+ * nor a symlink. Just return
+ * saved_errno from initial open()
+ */
+ goto out;
+ }
saved_errno =
link_errno_convert(errno);
}
TALLOC_FREE(parent_dir);
TALLOC_FREE(smb_fname_rel);
- if (oldwd != NULL) {
- int ret = vfs_ChDir(conn, oldwd);
+ if (oldwd_fname != NULL) {
+ int ret = vfs_ChDir(conn, oldwd_fname);
if (ret == -1) {
smb_panic("unable to get back to old directory\n");
}
- TALLOC_FREE(oldwd);
+ TALLOC_FREE(oldwd_fname);
}
if (saved_errno != 0) {
errno = saved_errno;
flags |= O_NOFOLLOW;
}
- fsp->fh->fd = SMB_VFS_OPEN(conn, smb_fname, fsp, flags, mode);
+ /* Ensure path is below share definition. */
+ if (!lp_widelinks(SNUM(conn))) {
+ struct smb_filename *conn_rootdir_fname = NULL;
+ const char *conn_rootdir = SMB_VFS_CONNECTPATH(conn,
+ smb_fname->base_name);
+ int saved_errno = 0;
+
+ if (conn_rootdir == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ conn_rootdir_fname = synthetic_smb_fname(talloc_tos(),
+ conn_rootdir,
+ NULL,
+ NULL,
+ 0);
+ if (conn_rootdir_fname == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /*
+ * Only follow symlinks within a share
+ * definition.
+ */
+ fsp->fh->fd = non_widelink_open(conn,
+ conn_rootdir_fname,
+ fsp,
+ smb_fname,
+ flags,
+ mode,
+ 0);
+ if (fsp->fh->fd == -1) {
+ saved_errno = errno;
+ }
+ TALLOC_FREE(conn_rootdir_fname);
+ if (saved_errno != 0) {
+ errno = saved_errno;
+ }
+ } else {
+ fsp->fh->fd = SMB_VFS_OPEN(conn, smb_fname, fsp, flags, mode);
+ }
+
if (fsp->fh->fd == -1) {
int posix_errno = link_errno_convert(errno);
status = map_nt_error_from_unix(posix_errno);
TALLOC_FREE(smb_fname_parent);
}
-NTSTATUS change_dir_owner_to_parent(connection_struct *conn,
- const char *inherit_from_dir,
- const char *fname,
- SMB_STRUCT_STAT *psbuf)
+static NTSTATUS change_dir_owner_to_parent(connection_struct *conn,
+ const char *inherit_from_dir,
+ struct smb_filename *smb_dname,
+ SMB_STRUCT_STAT *psbuf)
{
struct smb_filename *smb_fname_parent;
struct smb_filename *smb_fname_cwd = NULL;
- char *saved_dir = NULL;
+ struct smb_filename *saved_dir_fname = NULL;
TALLOC_CTX *ctx = talloc_tos();
NTSTATUS status = NT_STATUS_OK;
int ret;
should work on any UNIX (thanks tridge :-). JRA.
*/
- saved_dir = vfs_GetWd(ctx,conn);
- if (!saved_dir) {
+ saved_dir_fname = vfs_GetWd(ctx,conn);
+ if (!saved_dir_fname) {
status = map_nt_error_from_unix(errno);
DEBUG(0,("change_dir_owner_to_parent: failed to get "
"current working directory. Error was %s\n",
}
/* Chdir into the new path. */
- if (vfs_ChDir(conn, fname) == -1) {
+ if (vfs_ChDir(conn, smb_dname) == -1) {
status = map_nt_error_from_unix(errno);
DEBUG(0,("change_dir_owner_to_parent: failed to change "
"current working directory to %s. Error "
- "was %s\n", fname, strerror(errno) ));
+ "was %s\n", smb_dname->base_name, strerror(errno) ));
goto chdir;
}
status = map_nt_error_from_unix(errno);
DEBUG(0,("change_dir_owner_to_parent: failed to stat "
"directory '.' (%s) Error was %s\n",
- fname, strerror(errno)));
+ smb_dname->base_name, strerror(errno)));
goto chdir;
}
smb_fname_cwd->st.st_ex_ino != psbuf->st_ex_ino) {
DEBUG(0,("change_dir_owner_to_parent: "
"device/inode on directory %s changed. "
- "Refusing to chown !\n", fname ));
+ "Refusing to chown !\n",
+ smb_dname->base_name ));
status = NT_STATUS_ACCESS_DENIED;
goto chdir;
}
/* Already this uid - no need to change. */
DEBUG(10,("change_dir_owner_to_parent: directory %s "
"is already owned by uid %d\n",
- fname,
+ smb_dname->base_name,
(int)smb_fname_cwd->st.st_ex_uid ));
status = NT_STATUS_OK;
goto chdir;
status = map_nt_error_from_unix(errno);
DEBUG(10,("change_dir_owner_to_parent: failed to chown "
"directory %s to parent directory uid %u. "
- "Error was %s\n", fname,
+ "Error was %s\n",
+ smb_dname->base_name,
(unsigned int)smb_fname_parent->st.st_ex_uid,
strerror(errno) ));
} else {
DEBUG(10,("change_dir_owner_to_parent: changed ownership of new "
"directory %s to parent directory uid %u.\n",
- fname, (unsigned int)smb_fname_parent->st.st_ex_uid ));
+ smb_dname->base_name,
+ (unsigned int)smb_fname_parent->st.st_ex_uid ));
/* Ensure the uid entry is updated. */
psbuf->st_ex_uid = smb_fname_parent->st.st_ex_uid;
}
chdir:
- vfs_ChDir(conn,saved_dir);
+ vfs_ChDir(conn, saved_dir_fname);
out:
+ TALLOC_FREE(saved_dir_fname);
TALLOC_FREE(smb_fname_parent);
TALLOC_FREE(smb_fname_cwd);
return status;
/* Inherit the ACL if required */
if (lp_inherit_permissions(SNUM(conn))) {
inherit_access_posix_acl(conn, parent_dir,
- smb_fname->base_name,
+ smb_fname,
unx_mode);
need_re_stat = true;
}
if (lp_inherit_permissions(SNUM(conn))) {
inherit_access_posix_acl(conn, parent_dir,
- smb_dname->base_name, mode);
+ smb_dname, mode);
need_re_stat = true;
}
/* Change the owner if required. */
if (lp_inherit_owner(SNUM(conn)) != INHERIT_OWNER_NO) {
change_dir_owner_to_parent(conn, parent_dir,
- smb_dname->base_name,
+ smb_dname,
&smb_dname->st);
need_re_stat = true;
}
files_struct *dir_fsp;
char *parent_fname = NULL;
char *new_base_name = NULL;
- uint32_t ucf_flags = ((req != NULL && req->posix_pathnames) ?
- UCF_POSIX_PATHNAMES : 0);
+ uint32_t ucf_flags = ucf_flags_from_smb_request(req);
NTSTATUS status;
if (root_dir_fid == 0 || !smb_fname) {
status = filename_convert(req,
conn,
- req->flags2 & FLAGS2_DFS_PATHNAMES,
new_base_name,
ucf_flags,
NULL,