On Thu, Mar 23, 2017 at 11:30:09AM +0100, Mathieu Parent wrote: > Hi, > > Today samba has released a security fix for a symlink race (leading to > information disclosure). > > Salvatore will take care of the jessie upload, I have uploaded for > sid, but we have not done anything on the wheezy side. > > See attached the backported patches for 3.6 (those are from the samba > bugzilla which is still embargoed). > > Please take care of it. > Hello all, I have been able to figure out the minimum changes to cherry pick from the v3-6-stable branch in upstream Git. The commits are: 8234c6a 629e302 0a3b024 bc3714f d302cb6 94f7d0c 33ead72 66ee839 77cacee I was able to concatenate them into a single patch, which applied with only two offsets. After that the patch from upstream (3-6-racefix) applied with a bunch of small offsets. I have attached the consolidated and quilt-refreshed versions of both patches to this email. The patch containing the cherry picked commits which I have determined to be pre-requisites for upstream's patch is called 3-6-racefix-prereq.patch. The other patch file is the quilt-refreshed version of upstream's patch. Both of the attached patches apply cleanly to the 3.6.6-6+deb7u11 version of samba currently in wheezy. I have also built a 3.6.6-6+deb7u12 package with the two patches. The packages can be found here: https://people.debian.org/~roberto/ I still need to clean up the changelog entry. The packages could use some testing as well. I will try to do some testing, but give the scope of the changes (~850 lines of diff in total) more testing would certainly be a good thing. Also, I would appreciate any suggestions/feedback on minimizing the prereq patch. Regards, -Roberto -- Roberto C. Sánchez http://people.connexer.com/~roberto http://www.connexer.com
--- samba-3.6.6.orig/source3/smbd/dir.c
+++ samba-3.6.6/source3/smbd/dir.c
@@ -50,6 +50,8 @@
struct name_cache_entry *name_cache;
unsigned int name_cache_index;
unsigned int file_number;
+ files_struct *fsp; /* Back pointer to containing fsp, only
+ set from OpenDir_fsp(). */
};
struct dptr_struct {
@@ -1428,7 +1430,9 @@
if (fsp->is_directory && fsp->fh->fd != -1) {
dirp->dir = SMB_VFS_FDOPENDIR(fsp, mask, attr);
- if (dirp->dir == NULL) {
+ if (dirp->dir != NULL) {
+ dirp->fsp = fsp;
+ } else {
DEBUG(10,("OpenDir_fsp: SMB_VFS_FDOPENDIR on %s returned "
"NULL (%s)\n",
dirp->dir_path,
--- samba-3.6.6.orig/source3/modules/vfs_dirsort.c
+++ samba-3.6.6/source3/modules/vfs_dirsort.c
@@ -30,40 +30,60 @@
struct dirsort_privates {
long pos;
SMB_STRUCT_DIRENT *directory_list;
- long number_of_entries;
- time_t mtime;
+ unsigned int number_of_entries;
+ struct timespec mtime;
SMB_STRUCT_DIR *source_directory;
- int fd;
+ files_struct *fsp; /* If open via FDOPENDIR. */
+ struct smb_filename *smb_fname; /* If open via OPENDIR */
};
static void free_dirsort_privates(void **datap) {
- struct dirsort_privates *data = (struct dirsort_privates *) *datap;
- SAFE_FREE(data->directory_list);
- SAFE_FREE(data);
- *datap = NULL;
-
- return;
+ TALLOC_FREE(*datap);
}
-static bool open_and_sort_dir (vfs_handle_struct *handle)
+static bool get_sorted_dir_mtime(vfs_handle_struct *handle,
+ struct dirsort_privates *data,
+ struct timespec *ret_mtime)
{
- SMB_STRUCT_DIRENT *dp;
- struct stat dir_stat;
- long current_pos;
- struct dirsort_privates *data = NULL;
+ int ret;
+ struct timespec mtime;
- SMB_VFS_HANDLE_GET_DATA(handle, data, struct dirsort_privates,
- return false);
+ if (data->fsp) {
+ ret = fsp_stat(data->fsp);
+ mtime = data->fsp->fsp_name->st.st_ex_mtime;
+ } else {
+ ret = SMB_VFS_STAT(handle->conn, data->smb_fname);
+ mtime = data->smb_fname->st.st_ex_mtime;
+ }
+
+ if (ret == -1) {
+ return false;
+ }
+
+ *ret_mtime = mtime;
+
+ return true;
+}
+
+static bool open_and_sort_dir(vfs_handle_struct *handle,
+ struct dirsort_privates *data)
+{
+ unsigned int i = 0;
+ unsigned int total_count = 0;
data->number_of_entries = 0;
- if (fstat(data->fd, &dir_stat) == 0) {
- data->mtime = dir_stat.st_mtime;
+ if (get_sorted_dir_mtime(handle, data, &data->mtime) == false) {
+ return false;
}
while (SMB_VFS_NEXT_READDIR(handle, data->source_directory, NULL)
!= NULL) {
- data->number_of_entries++;
+ total_count++;
+ }
+
+ if (total_count == 0) {
+ return false;
}
/* Open the underlying directory and count the number of entries
@@ -71,21 +91,26 @@
SMB_VFS_NEXT_REWINDDIR(handle, data->source_directory);
/* Set up an array and read the directory entries into it */
- SAFE_FREE(data->directory_list); /* destroy previous cache if needed */
- data->directory_list = (SMB_STRUCT_DIRENT *)SMB_MALLOC(
- data->number_of_entries * sizeof(SMB_STRUCT_DIRENT));
+ TALLOC_FREE(data->directory_list); /* destroy previous cache if needed */
+ data->directory_list = talloc_zero_array(data,
+ SMB_STRUCT_DIRENT,
+ total_count);
if (!data->directory_list) {
return false;
}
- current_pos = data->pos;
- data->pos = 0;
- while ((dp = SMB_VFS_NEXT_READDIR(handle, data->source_directory,
- NULL)) != NULL) {
- data->directory_list[data->pos++] = *dp;
+ for (i = 0; i < total_count; i++) {
+ SMB_STRUCT_DIRENT *dp = SMB_VFS_NEXT_READDIR(handle,
+ data->source_directory,
+ NULL);
+ if (dp == NULL) {
+ break;
+ }
+ data->directory_list[i] = *dp;
}
+ data->number_of_entries = i;
+
/* Sort the directory entries by name */
- data->pos = current_pos;
TYPESAFE_QSORT(data->directory_list, data->number_of_entries, compare_dirent);
return true;
}
@@ -94,12 +119,11 @@
const char *fname, const char *mask,
uint32 attr)
{
+ NTSTATUS status;
struct dirsort_privates *data = NULL;
/* set up our private data about this directory */
- data = (struct dirsort_privates *)SMB_MALLOC(
- sizeof(struct dirsort_privates));
-
+ data = talloc_zero(handle->conn, struct dirsort_privates);
if (!data) {
return NULL;
}
@@ -107,20 +131,34 @@
data->directory_list = NULL;
data->pos = 0;
+ status = create_synthetic_smb_fname(data,
+ fname,
+ NULL,
+ NULL,
+ &data->smb_fname);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(data);
+ return NULL;
+ }
+
/* Open the underlying directory and count the number of entries */
data->source_directory = SMB_VFS_NEXT_OPENDIR(handle, fname, mask,
attr);
- data->fd = dirfd(data->source_directory);
-
- SMB_VFS_HANDLE_SET_DATA(handle, data, free_dirsort_privates,
- struct dirsort_privates, return NULL);
+ if (data->source_directory == NULL) {
+ TALLOC_FREE(data);
+ return NULL;
+ }
- if (!open_and_sort_dir(handle)) {
+ if (!open_and_sort_dir(handle, data)) {
SMB_VFS_NEXT_CLOSEDIR(handle,data->source_directory);
+ TALLOC_FREE(data);
return NULL;
}
+ SMB_VFS_HANDLE_SET_DATA(handle, data, free_dirsort_privates,
+ struct dirsort_privates, return NULL);
+
return data->source_directory;
}
@@ -132,37 +170,35 @@
struct dirsort_privates *data = NULL;
/* set up our private data about this directory */
- data = (struct dirsort_privates *)SMB_MALLOC(
- sizeof(struct dirsort_privates));
-
+ data = talloc_zero(handle->conn, struct dirsort_privates);
if (!data) {
return NULL;
}
data->directory_list = NULL;
data->pos = 0;
+ data->fsp = fsp;
/* Open the underlying directory and count the number of entries */
data->source_directory = SMB_VFS_NEXT_FDOPENDIR(handle, fsp, mask,
attr);
if (data->source_directory == NULL) {
- SAFE_FREE(data);
+ TALLOC_FREE(data);
return NULL;
}
- data->fd = dirfd(data->source_directory);
-
- SMB_VFS_HANDLE_SET_DATA(handle, data, free_dirsort_privates,
- struct dirsort_privates, return NULL);
-
- if (!open_and_sort_dir(handle)) {
+ if (!open_and_sort_dir(handle, data)) {
SMB_VFS_NEXT_CLOSEDIR(handle,data->source_directory);
+ TALLOC_FREE(data);
/* fd is now closed. */
fsp->fh->fd = -1;
return NULL;
}
+ SMB_VFS_HANDLE_SET_DATA(handle, data, free_dirsort_privates,
+ struct dirsort_privates, return NULL);
+
return data->source_directory;
}
@@ -171,21 +207,18 @@
SMB_STRUCT_STAT *sbuf)
{
struct dirsort_privates *data = NULL;
- time_t current_mtime;
- struct stat dir_stat;
+ struct timespec current_mtime;
SMB_VFS_HANDLE_GET_DATA(handle, data, struct dirsort_privates,
return NULL);
- if (fstat(data->fd, &dir_stat) == -1) {
+ if (get_sorted_dir_mtime(handle, data, ¤t_mtime) == false) {
return NULL;
}
- current_mtime = dir_stat.st_mtime;
-
/* throw away cache and re-read the directory if we've changed */
- if (current_mtime > data->mtime) {
- open_and_sort_dir(handle);
+ if (timespec_compare(¤t_mtime, &data->mtime) > 1) {
+ open_and_sort_dir(handle, data);
}
if (data->pos >= data->number_of_entries) {
From ec1bca1d5315549e945c93cbf5e3abdb695de782 Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra@samba.org>
Date: Mon, 20 Mar 2017 11:32:19 -0700
Subject: [PATCH 01/15] CVE-2017-2619: s3/smbd: re-open directory after
dptr_CloseDir()
dptr_CloseDir() will close and invalidate the fsp's file descriptor, we
have to reopen it.
Bug: https://bugzilla.samba.org/show_bug.cgi?id=12496
Signed-off-by: Ralph Bohme <slow@samba.org>
Signed-off-by: Jeremy Allison <jra@samba.org>
---
source3/smbd/open.c | 2 +-
source3/smbd/proto.h | 2 ++
source3/smbd/smb2_find.c | 17 +++++++++++++++++
3 files changed, 20 insertions(+), 1 deletion(-)
--- samba-3.6.6.orig/source3/smbd/open.c
+++ samba-3.6.6/source3/smbd/open.c
@@ -187,10 +187,277 @@
}
/****************************************************************************
+ Handle differing symlink errno's
+****************************************************************************/
+
+static int link_errno_convert(int err)
+{
+#if defined(ENOTSUP) && defined(OSF1)
+ /* handle special Tru64 errno */
+ if (err == ENOTSUP) {
+ err = ELOOP;
+ }
+#endif /* ENOTSUP */
+#ifdef EFTYPE
+ /* fix broken NetBSD errno */
+ if (err == EFTYPE) {
+ err = ELOOP;
+ }
+#endif /* EFTYPE */
+ /* fix broken FreeBSD errno */
+ if (err == EMLINK) {
+ err = ELOOP;
+ }
+ return err;
+}
+
+static int non_widelink_open(struct connection_struct *conn,
+ const char *conn_rootdir,
+ files_struct *fsp,
+ struct smb_filename *smb_fname,
+ int flags,
+ mode_t mode,
+ unsigned int link_depth);
+
+/****************************************************************************
+ Follow a symlink in userspace.
+****************************************************************************/
+
+static int process_symlink_open(struct connection_struct *conn,
+ const char *conn_rootdir,
+ files_struct *fsp,
+ struct smb_filename *smb_fname,
+ int flags,
+ mode_t mode,
+ unsigned int link_depth)
+{
+ int fd = -1;
+ char *link_target = NULL;
+ int link_len = -1;
+ char *oldwd = NULL;
+ size_t rootdir_len = 0;
+ char *resolved_name = NULL;
+ bool matched = false;
+ int saved_errno = 0;
+
+ /*
+ * Ensure we don't get stuck in a symlink loop.
+ */
+ link_depth++;
+ if (link_depth >= 20) {
+ errno = ELOOP;
+ goto out;
+ }
+
+ /* Allocate space for the link target. */
+ link_target = talloc_array(talloc_tos(), char, PATH_MAX);
+ if (link_target == NULL) {
+ errno = ENOMEM;
+ goto out;
+ }
+
+ /* Read the link target. */
+ link_len = SMB_VFS_READLINK(conn,
+ smb_fname->base_name,
+ link_target,
+ PATH_MAX - 1);
+ if (link_len == -1) {
+ goto out;
+ }
+
+ /* Ensure it's at least null terminated. */
+ link_target[link_len] = '\0';
+
+ /* Convert to an absolute path. */
+ resolved_name = SMB_VFS_REALPATH(conn, link_target);
+ if (resolved_name == NULL) {
+ goto out;
+ }
+
+ /*
+ * We know conn_rootdir starts with '/' and
+ * does not end in '/'. FIXME ! Should we
+ * smb_assert this ?
+ */
+ rootdir_len = strlen(conn_rootdir);
+
+ matched = (strncmp(conn_rootdir, resolved_name, rootdir_len) == 0);
+ if (!matched) {
+ errno = EACCES;
+ goto out;
+ }
+
+ /*
+ * Turn into a path relative to the share root.
+ */
+ 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;
+ }
+ } else if (resolved_name[rootdir_len] == '/') {
+ smb_fname->base_name = &resolved_name[rootdir_len+1];
+ } else {
+ errno = EACCES;
+ goto out;
+ }
+
+ oldwd = vfs_GetWd(talloc_tos(), conn);
+ if (oldwd == NULL) {
+ goto out;
+ }
+
+ /* Ensure we operate from the root of the share. */
+ if (vfs_ChDir(conn, conn_rootdir) == -1) {
+ goto out;
+ }
+
+ /* And do it all again.. */
+ fd = non_widelink_open(conn,
+ conn_rootdir,
+ fsp,
+ smb_fname,
+ flags,
+ mode,
+ link_depth);
+ if (fd == -1) {
+ saved_errno = errno;
+ }
+
+ out:
+
+ SAFE_FREE(resolved_name);
+ TALLOC_FREE(link_target);
+ if (oldwd != NULL) {
+ int ret = vfs_ChDir(conn, oldwd);
+ if (ret == -1) {
+ smb_panic("unable to get back to old directory\n");
+ }
+ TALLOC_FREE(oldwd);
+ }
+ if (saved_errno != 0) {
+ errno = saved_errno;
+ }
+ return fd;
+}
+
+/****************************************************************************
+ Non-widelink open.
+****************************************************************************/
+
+static int non_widelink_open(struct connection_struct *conn,
+ const char *conn_rootdir,
+ files_struct *fsp,
+ struct smb_filename *smb_fname,
+ int flags,
+ mode_t mode,
+ unsigned int link_depth)
+{
+ NTSTATUS status;
+ int fd = -1;
+ struct smb_filename *smb_fname_rel = NULL;
+ int saved_errno = 0;
+ char *oldwd = NULL;
+ char *parent_dir = NULL;
+ const char *final_component = NULL;
+
+ if (!parent_dirname(talloc_tos(),
+ smb_fname->base_name,
+ &parent_dir,
+ &final_component)) {
+ goto out;
+ }
+
+ oldwd = vfs_GetWd(talloc_tos(), conn);
+ if (oldwd == NULL) {
+ goto out;
+ }
+
+ /* Pin parent directory in place. */
+ if (vfs_ChDir(conn, parent_dir) == -1) {
+ goto out;
+ }
+
+ /* Ensure the relative path is below the share. */
+ status = check_reduced_name(conn, final_component);
+ if (!NT_STATUS_IS_OK(status)) {
+ saved_errno = map_errno_from_nt_status(status);
+ goto out;
+ }
+
+ status = create_synthetic_smb_fname(talloc_tos(),
+ final_component,
+ smb_fname->stream_name,
+ &smb_fname->st,
+ &smb_fname_rel);
+ if (!NT_STATUS_IS_OK(status)) {
+ saved_errno = map_errno_from_nt_status(status);
+ goto out;
+ }
+
+ flags |= O_NOFOLLOW;
+
+ {
+ struct smb_filename *tmp_name = fsp->fsp_name;
+ fsp->fsp_name = smb_fname_rel;
+ fd = SMB_VFS_OPEN(conn, smb_fname_rel, fsp, flags, mode);
+ fsp->fsp_name = tmp_name;
+ }
+
+ if (fd == -1) {
+ saved_errno = link_errno_convert(errno);
+ if (saved_errno == ELOOP) {
+ if (fsp->posix_open) {
+ /* Never follow symlinks on posix open. */
+ goto out;
+ }
+ if (!lp_symlinks(SNUM(conn))) {
+ /* Explicitly no symlinks. */
+ goto out;
+ }
+ /*
+ * We have a symlink. Follow in userspace
+ * to ensure it's under the share definition.
+ */
+ fd = process_symlink_open(conn,
+ conn_rootdir,
+ fsp,
+ smb_fname_rel,
+ flags,
+ mode,
+ link_depth);
+ if (fd == -1) {
+ saved_errno =
+ link_errno_convert(errno);
+ }
+ }
+ }
+
+ out:
+
+ TALLOC_FREE(parent_dir);
+ TALLOC_FREE(smb_fname_rel);
+
+ if (oldwd != NULL) {
+ int ret = vfs_ChDir(conn, oldwd);
+ if (ret == -1) {
+ smb_panic("unable to get back to old directory\n");
+ }
+ TALLOC_FREE(oldwd);
+ }
+ if (saved_errno != 0) {
+ errno = saved_errno;
+ }
+ return fd;
+}
+
+/****************************************************************************
fd support routines - attempt to do a dos_open.
****************************************************************************/
-static NTSTATUS fd_open(struct connection_struct *conn,
+NTSTATUS fd_open(struct connection_struct *conn,
files_struct *fsp,
int flags,
mode_t mode)
@@ -198,8 +465,7 @@
struct smb_filename *smb_fname = fsp->fsp_name;
NTSTATUS status = NT_STATUS_OK;
-#ifdef O_NOFOLLOW
- /*
+ /*
* Never follow symlinks on a POSIX client. The
* client should be doing this.
*/
@@ -207,12 +473,33 @@
if (fsp->posix_open || !lp_symlinks(SNUM(conn))) {
flags |= O_NOFOLLOW;
}
-#endif
- fsp->fh->fd = SMB_VFS_OPEN(conn, smb_fname, fsp, flags, mode);
+ /* Ensure path is below share definition. */
+ if (!lp_widelinks(SNUM(conn))) {
+ const char *conn_rootdir = SMB_VFS_CONNECTPATH(conn,
+ smb_fname->base_name);
+ if (conn_rootdir == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ /*
+ * Only follow symlinks within a share
+ * definition.
+ */
+ fsp->fh->fd = non_widelink_open(conn,
+ conn_rootdir,
+ fsp,
+ smb_fname,
+ flags,
+ mode,
+ 0);
+ } else {
+ fsp->fh->fd = SMB_VFS_OPEN(conn, smb_fname, fsp, flags, mode);
+ }
+
if (fsp->fh->fd == -1) {
- status = map_nt_error_from_unix(errno);
- if (errno == EMFILE) {
+ int posix_errno = link_errno_convert(errno);
+ status = map_nt_error_from_unix(posix_errno);
+ if (posix_errno == EMFILE) {
static time_t last_warned = 0L;
if (time((time_t *) NULL) > last_warned) {
--- samba-3.6.6.orig/source3/smbd/proto.h
+++ samba-3.6.6/source3/smbd/proto.h
@@ -592,6 +592,8 @@
const struct security_token *token,
uint32_t access_desired,
uint32_t *access_granted);
+NTSTATUS fd_open(struct connection_struct *conn, files_struct *fsp,
+ int flags, mode_t mode);
NTSTATUS fd_close(files_struct *fsp);
void change_file_owner_to_parent(connection_struct *conn,
const char *inherit_from_dir,
--- samba-3.6.6.orig/source3/smbd/smb2_find.c
+++ samba-3.6.6/source3/smbd/smb2_find.c
@@ -24,6 +24,7 @@
#include "../libcli/smb/smb_common.h"
#include "trans2.h"
#include "../lib/util/tevent_ntstatus.h"
+#include "system/filesys.h"
static struct tevent_req *smbd_smb2_find_send(TALLOC_CTX *mem_ctx,
struct tevent_context *ev,
@@ -300,7 +301,23 @@
}
if (in_flags & SMB2_CONTINUE_FLAG_REOPEN) {
+ int flags;
+
dptr_CloseDir(fsp);
+
+ /*
+ * dptr_CloseDir() will close and invalidate the fsp's file
+ * descriptor, we have to reopen it.
+ */
+
+ flags = O_RDONLY;
+#ifdef O_DIRECTORY
+ flags |= O_DIRECTORY;
+#endif
+ status = fd_open(conn, fsp, flags, 0);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
}
if (fsp->dptr == NULL) {
--- samba-3.6.6.orig/source3/modules/vfs_dirsort.c
+++ samba-3.6.6/source3/modules/vfs_dirsort.c
@@ -141,6 +141,10 @@
return NULL;
}
+ if (ISDOT(data->smb_fname->base_name)) {
+ data->smb_fname->base_name = vfs_GetWd(data, handle->conn);
+ }
+
/* Open the underlying directory and count the number of entries */
data->source_directory = SMB_VFS_NEXT_OPENDIR(handle, fname, mask,
attr);
--- samba-3.6.6.orig/source3/modules/vfs_streams_xattr.c
+++ samba-3.6.6/source3/modules/vfs_streams_xattr.c
@@ -229,7 +229,7 @@
return -1;
}
- sbuf->st_ex_size = get_xattr_size(handle->conn, fsp->base_fsp,
+ sbuf->st_ex_size = get_xattr_size(handle->conn, fsp,
io->base, io->xattr_name);
if (sbuf->st_ex_size == -1) {
return -1;
@@ -364,6 +364,7 @@
char *xattr_name = NULL;
int baseflags;
int hostfd = -1;
+ int ret;
DEBUG(10, ("streams_xattr_open called for %s\n",
smb_fname_str_dbg(smb_fname)));
@@ -375,133 +376,125 @@
/* If the default stream is requested, just open the base file. */
if (is_ntfs_default_stream_smb_fname(smb_fname)) {
char *tmp_stream_name;
- int ret;
tmp_stream_name = smb_fname->stream_name;
smb_fname->stream_name = NULL;
ret = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
- smb_fname->stream_name = tmp_stream_name;
-
- return ret;
- }
+ smb_fname->stream_name = tmp_stream_name;
- status = streams_xattr_get_name(talloc_tos(), smb_fname->stream_name,
- &xattr_name);
- if (!NT_STATUS_IS_OK(status)) {
- errno = map_errno_from_nt_status(status);
- goto fail;
- }
+ return ret;
+ }
- /* Create an smb_filename with stream_name == NULL. */
- status = create_synthetic_smb_fname(talloc_tos(),
- smb_fname->base_name,
- NULL, NULL,
- &smb_fname_base);
- if (!NT_STATUS_IS_OK(status)) {
- errno = map_errno_from_nt_status(status);
- goto fail;
- }
+ status = streams_xattr_get_name(talloc_tos(), smb_fname->stream_name,
+ &xattr_name);
+ if (!NT_STATUS_IS_OK(status)) {
+ errno = map_errno_from_nt_status(status);
+ goto fail;
+ }
- /*
- * We use baseflags to turn off nasty side-effects when opening the
- * underlying file.
- */
- baseflags = flags;
- baseflags &= ~O_TRUNC;
- baseflags &= ~O_EXCL;
- baseflags &= ~O_CREAT;
-
- hostfd = SMB_VFS_OPEN(handle->conn, smb_fname_base, fsp,
- baseflags, mode);
-
- TALLOC_FREE(smb_fname_base);
-
- /* It is legit to open a stream on a directory, but the base
- * fd has to be read-only.
- */
- if ((hostfd == -1) && (errno == EISDIR)) {
- baseflags &= ~O_ACCMODE;
- baseflags |= O_RDONLY;
- hostfd = SMB_VFS_OPEN(handle->conn, smb_fname, fsp, baseflags,
- mode);
- }
+ /* Create an smb_filename with stream_name == NULL. */
+ status = create_synthetic_smb_fname(talloc_tos(),
+ smb_fname->base_name,
+ NULL, NULL,
+ &smb_fname_base);
+ if (!NT_STATUS_IS_OK(status)) {
+ errno = map_errno_from_nt_status(status);
+ goto fail;
+ }
- if (hostfd == -1) {
- goto fail;
- }
+ /*
+ * We use baseflags to turn off nasty side-effects when opening the
+ * underlying file.
+ */
+ baseflags = flags;
+ baseflags &= ~O_TRUNC;
+ baseflags &= ~O_EXCL;
+ baseflags &= ~O_CREAT;
- status = get_ea_value(talloc_tos(), handle->conn, NULL,
- smb_fname->base_name, xattr_name, &ea);
+ hostfd = SMB_VFS_OPEN(handle->conn, smb_fname_base, fsp,
+ baseflags, mode);
- DEBUG(10, ("get_ea_value returned %s\n", nt_errstr(status)));
+ TALLOC_FREE(smb_fname_base);
- if (!NT_STATUS_IS_OK(status)
- && !NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
- /*
- * The base file is not there. This is an error even if we got
- * O_CREAT, the higher levels should have created the base
- * file for us.
+ /* It is legit to open a stream on a directory, but the base
+ * fd has to be read-only.
*/
- DEBUG(10, ("streams_xattr_open: base file %s not around, "
- "returning ENOENT\n", smb_fname->base_name));
- errno = ENOENT;
- goto fail;
- }
+ if ((hostfd == -1) && (errno == EISDIR)) {
+ baseflags &= ~O_ACCMODE;
+ baseflags |= O_RDONLY;
+ hostfd = SMB_VFS_OPEN(handle->conn, smb_fname, fsp, baseflags,
+ mode);
+ }
- if (!NT_STATUS_IS_OK(status)) {
- /*
- * The attribute does not exist
- */
+ if (hostfd == -1) {
+ goto fail;
+ }
+
+ status = get_ea_value(talloc_tos(), handle->conn, NULL,
+ smb_fname->base_name, xattr_name, &ea);
- if (flags & O_CREAT) {
+ DEBUG(10, ("get_ea_value returned %s\n", nt_errstr(status)));
+
+ if (!NT_STATUS_IS_OK(status)
+ && !NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
/*
- * Darn, xattrs need at least 1 byte
+ * The base file is not there. This is an error even if we got
+ * O_CREAT, the higher levels should have created the base
+ * file for us.
*/
- char null = '\0';
+ DEBUG(10, ("streams_xattr_open: base file %s not around, "
+ "returning ENOENT\n", smb_fname->base_name));
+ errno = ENOENT;
+ goto fail;
+ }
- DEBUG(10, ("creating attribute %s on file %s\n",
- xattr_name, smb_fname->base_name));
+ if (!NT_STATUS_IS_OK(status)) {
+ /*
+ * The attribute does not exist
+ */
+ if (flags & O_CREAT) {
+ /*
+ * Darn, xattrs need at least 1 byte
+ */
+ char null = '\0';
+
+ DEBUG(10, ("creating attribute %s on file %s\n",
+ xattr_name, smb_fname->base_name));
+
+ fsp->fh->fd = hostfd;
+ ret = SMB_VFS_FSETXATTR(fsp, xattr_name,
+ &null, sizeof(null),
+ flags & O_EXCL ? XATTR_CREATE : 0);
+ fsp->fh->fd = -1;
+ if (ret != 0) {
+ goto fail;
+ }
+ }
+ }
+
+ if (flags & O_TRUNC) {
+ char null = '\0';
if (fsp->base_fsp->fh->fd != -1) {
- if (SMB_VFS_FSETXATTR(
- fsp->base_fsp, xattr_name,
- &null, sizeof(null),
- flags & O_EXCL ? XATTR_CREATE : 0) == -1) {
+ if (SMB_VFS_FSETXATTR(
+ fsp->base_fsp, xattr_name,
+ &null, sizeof(null),
+ flags & O_EXCL ? XATTR_CREATE : 0) == -1) {
goto fail;
}
} else {
- if (SMB_VFS_SETXATTR(
- handle->conn, smb_fname->base_name,
- xattr_name, &null, sizeof(null),
- flags & O_EXCL ? XATTR_CREATE : 0) == -1) {
+ if (SMB_VFS_SETXATTR(
+ handle->conn, smb_fname->base_name,
+ xattr_name, &null, sizeof(null),
+ flags & O_EXCL ? XATTR_CREATE : 0) == -1) {
goto fail;
}
}
}
- }
-
- if (flags & O_TRUNC) {
- char null = '\0';
- if (fsp->base_fsp->fh->fd != -1) {
- if (SMB_VFS_FSETXATTR(
- fsp->base_fsp, xattr_name,
- &null, sizeof(null),
- flags & O_EXCL ? XATTR_CREATE : 0) == -1) {
- goto fail;
- }
- } else {
- if (SMB_VFS_SETXATTR(
- handle->conn, smb_fname->base_name,
- xattr_name, &null, sizeof(null),
- flags & O_EXCL ? XATTR_CREATE : 0) == -1) {
- goto fail;
- }
- }
- }
- sio = (struct stream_io *)VFS_ADD_FSP_EXTENSION(handle, fsp,
+ sio = (struct stream_io *)VFS_ADD_FSP_EXTENSION(handle, fsp,
struct stream_io,
NULL);
if (sio == NULL) {
@@ -511,8 +504,15 @@
sio->xattr_name = talloc_strdup(VFS_MEMCTX_FSP_EXTENSION(handle, fsp),
xattr_name);
+ /*
+ * sio->base needs to be a copy of fsp->fsp_name->base_name,
+ * making it identical to streams_xattr_recheck(). If the
+ * open is changing directories, fsp->fsp_name->base_name
+ * will be the full path from the share root, whilst
+ * smb_fname will be relative to the $cwd.
+ */
sio->base = talloc_strdup(VFS_MEMCTX_FSP_EXTENSION(handle, fsp),
- smb_fname->base_name);
+ fsp->fsp_name->base_name);
sio->fsp_name_ptr = fsp->fsp_name;
sio->handle = handle;
sio->fsp = fsp;
@@ -861,7 +861,7 @@
return -1;
}
- status = get_ea_value(talloc_tos(), handle->conn, fsp->base_fsp,
+ status = get_ea_value(talloc_tos(), handle->conn, fsp,
sio->base, sio->xattr_name, &ea);
if (!NT_STATUS_IS_OK(status)) {
return -1;
@@ -885,13 +885,13 @@
memcpy(ea.value.data + offset, data, n);
- if (fsp->base_fsp->fh->fd != -1) {
- ret = SMB_VFS_FSETXATTR(fsp->base_fsp,
+ if (fsp->fh->fd != -1) {
+ ret = SMB_VFS_FSETXATTR(fsp,
sio->xattr_name,
ea.value.data, ea.value.length, 0);
} else {
ret = SMB_VFS_SETXATTR(fsp->conn,
- fsp->base_fsp->fsp_name->base_name,
+ fsp->fsp_name->base_name,
sio->xattr_name,
ea.value.data, ea.value.length, 0);
}
@@ -925,7 +925,7 @@
return -1;
}
- status = get_ea_value(talloc_tos(), handle->conn, fsp->base_fsp,
+ status = get_ea_value(talloc_tos(), handle->conn, fsp,
sio->base, sio->xattr_name, &ea);
if (!NT_STATUS_IS_OK(status)) {
return -1;
@@ -970,7 +970,7 @@
return -1;
}
- status = get_ea_value(talloc_tos(), handle->conn, fsp->base_fsp,
+ status = get_ea_value(talloc_tos(), handle->conn, fsp,
sio->base, sio->xattr_name, &ea);
if (!NT_STATUS_IS_OK(status)) {
return -1;
@@ -995,13 +995,13 @@
ea.value.length = offset + 1;
ea.value.data[offset] = 0;
- if (fsp->base_fsp->fh->fd != -1) {
- ret = SMB_VFS_FSETXATTR(fsp->base_fsp,
+ if (fsp->fh->fd != -1) {
+ ret = SMB_VFS_FSETXATTR(fsp,
sio->xattr_name,
ea.value.data, ea.value.length, 0);
} else {
ret = SMB_VFS_SETXATTR(fsp->conn,
- fsp->base_fsp->fsp_name->base_name,
+ fsp->fsp_name->base_name,
sio->xattr_name,
ea.value.data, ea.value.length, 0);
}
--- samba-3.6.6.orig/source3/smbd/dir.c
+++ samba-3.6.6/source3/smbd/dir.c
@@ -1358,7 +1358,8 @@
Open a directory.
********************************************************************/
-struct smb_Dir *OpenDir(TALLOC_CTX *mem_ctx, connection_struct *conn,
+static struct smb_Dir *OpenDir_internal(TALLOC_CTX *mem_ctx,
+ connection_struct *conn,
const char *name,
const char *mask,
uint32 attr)
@@ -1370,27 +1371,21 @@
return NULL;
}
- dirp->conn = conn;
- dirp->name_cache_size = lp_directory_name_cache_size(SNUM(conn));
-
- dirp->dir_path = talloc_strdup(dirp, name);
- if (!dirp->dir_path) {
- errno = ENOMEM;
+ dirp->dir = SMB_VFS_OPENDIR(conn, name, mask, attr);
+ if (!dirp->dir) {
+ DEBUG(5,("OpenDir: Can't open %s. %s\n", name,
+ strerror(errno) ));
goto fail;
}
+ dirp->conn = conn;
+ dirp->name_cache_size = lp_directory_name_cache_size(SNUM(conn));
+
if (sconn && !sconn->using_smb2) {
sconn->searches.dirhandles_open++;
}
talloc_set_destructor(dirp, smb_Dir_destructor);
- dirp->dir = SMB_VFS_OPENDIR(conn, dirp->dir_path, mask, attr);
- if (!dirp->dir) {
- DEBUG(5,("OpenDir: Can't open %s. %s\n", dirp->dir_path,
- strerror(errno) ));
- goto fail;
- }
-
return dirp;
fail:
@@ -1398,6 +1393,76 @@
return NULL;
}
+/****************************************************************************
+ Open a directory handle by pathname, ensuring it's under the share path.
+****************************************************************************/
+
+static struct smb_Dir *open_dir_safely(TALLOC_CTX *ctx,
+ connection_struct *conn,
+ const char *name,
+ const char *wcard,
+ uint32_t attr)
+{
+ struct smb_Dir *dir_hnd = NULL;
+ char *saved_dir = vfs_GetWd(ctx, conn);
+ NTSTATUS status;
+
+ if (saved_dir == NULL) {
+ return NULL;
+ }
+
+ if (vfs_ChDir(conn, name) == -1) {
+ goto out;
+ }
+
+ /*
+ * Now the directory is pinned, use
+ * REALPATH to ensure we can access it.
+ */
+ status = check_name(conn, ".");
+ if (!NT_STATUS_IS_OK(status)) {
+ goto out;
+ }
+
+ dir_hnd = OpenDir_internal(ctx,
+ conn,
+ ".",
+ wcard,
+ attr);
+
+ if (dir_hnd == NULL) {
+ goto out;
+ }
+
+ /*
+ * OpenDir_internal only gets "." as the dir name.
+ * Store the real dir name here.
+ */
+
+ dir_hnd->dir_path = talloc_strdup(dir_hnd, name);
+ if (!dir_hnd->dir_path) {
+ errno = ENOMEM;
+ }
+
+ out:
+
+ vfs_ChDir(conn, saved_dir);
+ TALLOC_FREE(saved_dir);
+ return dir_hnd;
+}
+
+struct smb_Dir *OpenDir(TALLOC_CTX *mem_ctx, connection_struct *conn,
+ const char *name,
+ const char *mask,
+ uint32_t attr)
+{
+ return open_dir_safely(mem_ctx,
+ conn,
+ name,
+ mask,
+ attr);
+}
+
/*******************************************************************
Open a directory from an fsp.
********************************************************************/
@@ -1411,7 +1476,17 @@
struct smbd_server_connection *sconn = conn->sconn;
if (!dirp) {
- return NULL;
+ goto fail;
+ }
+
+ if (!fsp->is_directory) {
+ errno = EBADF;
+ goto fail;
+ }
+
+ if (fsp->fh->fd == -1) {
+ errno = EBADF;
+ goto fail;
}
dirp->conn = conn;
@@ -1423,36 +1498,33 @@
goto fail;
}
- if (sconn && !sconn->using_smb2) {
- sconn->searches.dirhandles_open++;
- }
- talloc_set_destructor(dirp, smb_Dir_destructor);
-
- if (fsp->is_directory && fsp->fh->fd != -1) {
- dirp->dir = SMB_VFS_FDOPENDIR(fsp, mask, attr);
- if (dirp->dir != NULL) {
- dirp->fsp = fsp;
- } else {
- DEBUG(10,("OpenDir_fsp: SMB_VFS_FDOPENDIR on %s returned "
- "NULL (%s)\n",
- dirp->dir_path,
- strerror(errno)));
- if (errno != ENOSYS) {
- return NULL;
- }
+ dirp->dir = SMB_VFS_FDOPENDIR(fsp, mask, attr);
+ if (dirp->dir != NULL) {
+ dirp->fsp = fsp;
+ } else {
+ DEBUG(10,("OpenDir_fsp: SMB_VFS_FDOPENDIR on %s returned "
+ "NULL (%s)\n",
+ dirp->dir_path,
+ strerror(errno)));
+ if (errno != ENOSYS) {
+ goto fail;
}
}
if (dirp->dir == NULL) {
- /* FDOPENDIR didn't work. Use OPENDIR instead. */
- dirp->dir = SMB_VFS_OPENDIR(conn, dirp->dir_path, mask, attr);
+ /* FDOPENDIR is not supported. Use OPENDIR instead. */
+ TALLOC_FREE(dirp);
+ return open_dir_safely(mem_ctx,
+ conn,
+ fsp->fsp_name->base_name,
+ mask,
+ attr);
}
- if (!dirp->dir) {
- DEBUG(5,("OpenDir_fsp: Can't open %s. %s\n", dirp->dir_path,
- strerror(errno) ));
- goto fail;
+ if (sconn && !sconn->using_smb2) {
+ sconn->searches.dirhandles_open++;
}
+ talloc_set_destructor(dirp, smb_Dir_destructor);
return dirp;
Attachment:
signature.asc
Description: Digital signature