1
0
mirror of https://github.com/samba-team/samba.git synced 2025-01-06 13:18:07 +03:00

samba: tag release samba-4.4.12

-----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iD8DBQBY04N8bzORW2Vot+oRAtYcAJ0fK71YTWsyyBXyM/dWJ6lQi8XQnACeOZum
 JRlcRLhXCnNXZ47zSDHwTMU=
 =ag2T
 -----END PGP SIGNATURE-----

Merge tag 'samba-4.4.12' into v4-4-test

samba: tag release samba-4.4.12
This commit is contained in:
Karolin Seeger 2017-03-23 10:18:52 +01:00
commit f2ae4c7984
5 changed files with 496 additions and 65 deletions

View File

@ -1,3 +1,74 @@
==============================
Release Notes for Samba 4.4.12
March 23, 2017
==============================
This is a security release in order to address the following defect:
o CVE-2017-2619 (Symlink race allows access outside share definition)
=======
Details
=======
o CVE-2017-2619:
All versions of Samba prior to 4.6.1, 4.5.7, 4.4.11 are vulnerable to
a malicious client using a symlink race to allow access to areas of
the server file system not exported under the share definition.
Samba uses the realpath() system call to ensure when a client requests
access to a pathname that it is under the exported share path on the
server file system.
Clients that have write access to the exported part of the file system
via SMB1 unix extensions or NFS to create symlinks can race the server
by renaming a realpath() checked path and then creating a symlink. If
the client wins the race it can cause the server to access the new
symlink target after the exported share path check has been done. This
new symlink target can point to anywhere on the server file system.
This is a difficult race to win, but theoretically possible. Note that
the proof of concept code supplied wins the race reliably only when
the server is slowed down using the strace utility running on the
server. Exploitation of this bug has not been seen in the wild.
Changes since 4.4.11:
---------------------
o Jeremy Allison <jra@samba.org>
* BUG 12496: CVE-2017-2619: Symlink race permits opening files outside share
directory.
o Ralph Boehme <slow@samba.org>
* BUG 12496: CVE-2017-2619: Symlink race permits opening files outside share
directory.
#######################################
Reporting bugs & Development Discussion
#######################################
Please discuss this release on the samba-technical mailing list or by
joining the #samba-technical IRC channel on irc.freenode.net.
If you do report problems then please try to send high quality
feedback. If you don't provide vital information to help us track down
the problem then you will probably be ignored. All bug reports should
be filed under the "Samba 4.1 and newer" product in the project's Bugzilla
database (https://bugzilla.samba.org/).
======================================================================
== Our Code, Our Bugs, Our Responsibility.
== The Samba Team
======================================================================
Release notes for older releases follow:
----------------------------------------
==============================
Release Notes for Samba 4.4.11
March 16, 2017
@ -50,8 +121,8 @@ database (https://bugzilla.samba.org/).
======================================================================
Release notes for older releases follow:
----------------------------------------
----------------------------------------------------------------------
==============================
Release Notes for Samba 4.4.10

View File

@ -1588,7 +1588,8 @@ static int smb_Dir_destructor(struct smb_Dir *dirp)
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_t attr)
@ -1600,27 +1601,21 @@ struct smb_Dir *OpenDir(TALLOC_CTX *mem_ctx, connection_struct *conn,
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:
@ -1628,6 +1623,76 @@ struct smb_Dir *OpenDir(TALLOC_CTX *mem_ctx, connection_struct *conn,
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.
********************************************************************/
@ -1641,7 +1706,17 @@ static struct smb_Dir *OpenDir_fsp(TALLOC_CTX *mem_ctx, connection_struct *conn,
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;
@ -1653,36 +1728,33 @@ static struct smb_Dir *OpenDir_fsp(TALLOC_CTX *mem_ctx, connection_struct *conn,
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;

View File

@ -351,6 +351,268 @@ static NTSTATUS check_base_file_access(struct connection_struct *conn,
access_mask);
}
/****************************************************************************
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;
}
smb_fname_rel = synthetic_smb_fname(talloc_tos(),
final_component,
smb_fname->stream_name,
&smb_fname->st);
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_flags & FSP_POSIX_FLAGS_OPEN) {
/* Never follow symlinks on posix open. */
goto out;
}
if (!lp_follow_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.
****************************************************************************/
@ -363,8 +625,7 @@ NTSTATUS fd_open(struct connection_struct *conn,
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.
*/
@ -372,29 +633,31 @@ NTSTATUS fd_open(struct connection_struct *conn,
if ((fsp->posix_flags & FSP_POSIX_FLAGS_OPEN) || !lp_follow_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) {
int posix_errno = errno;
#ifdef O_NOFOLLOW
#if defined(ENOTSUP) && defined(OSF1)
/* handle special Tru64 errno */
if (errno == ENOTSUP) {
posix_errno = ELOOP;
}
#endif /* ENOTSUP */
#ifdef EFTYPE
/* fix broken NetBSD errno */
if (errno == EFTYPE) {
posix_errno = ELOOP;
}
#endif /* EFTYPE */
/* fix broken FreeBSD errno */
if (errno == EMLINK) {
posix_errno = ELOOP;
}
#endif /* O_NOFOLLOW */
int posix_errno = link_errno_convert(errno);
status = map_nt_error_from_unix(posix_errno);
if (errno == EMFILE) {
static time_t last_warned = 0L;

View File

@ -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_query_directory_send(TALLOC_CTX *mem_ctx,
struct tevent_context *ev,
@ -322,7 +323,23 @@ static struct tevent_req *smbd_smb2_query_directory_send(TALLOC_CTX *mem_ctx,
}
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 (!smbreq->posix_pathnames) {

View File

@ -674,7 +674,7 @@ bool fill_result(void *private_data,
return true;
}
enum continue_type {CONT_SINGLE, CONT_INDEX, CONT_RESTART};
enum continue_type {CONT_SINGLE, CONT_INDEX, CONT_RESTART, CONT_REOPEN};
static NTSTATUS multiple_smb2_search(struct smb2_tree *tree,
TALLOC_CTX *tctx,
@ -700,6 +700,9 @@ static NTSTATUS multiple_smb2_search(struct smb2_tree *tree,
/* The search should start from the beginning everytime */
f.in.continue_flags = SMB2_CONTINUE_FLAG_RESTART;
if (cont_type == CONT_REOPEN) {
f.in.continue_flags = SMB2_CONTINUE_FLAG_REOPEN;
}
do {
status = smb2_find_level(tree, tree, &f, &count, &d);
@ -803,18 +806,23 @@ static bool test_many_files(struct torture_context *tctx,
{"SMB2_FIND_BOTH_DIRECTORY_INFO", "SINGLE", SMB2_FIND_BOTH_DIRECTORY_INFO, RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO, CONT_SINGLE},
{"SMB2_FIND_BOTH_DIRECTORY_INFO", "INDEX", SMB2_FIND_BOTH_DIRECTORY_INFO, RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO, CONT_INDEX},
{"SMB2_FIND_BOTH_DIRECTORY_INFO", "RESTART", SMB2_FIND_BOTH_DIRECTORY_INFO, RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO, CONT_RESTART},
{"SMB2_FIND_BOTH_DIRECTORY_INFO", "REOPEN", SMB2_FIND_BOTH_DIRECTORY_INFO, RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO, CONT_REOPEN},
{"SMB2_FIND_DIRECTORY_INFO", "SINGLE", SMB2_FIND_DIRECTORY_INFO, RAW_SEARCH_DATA_DIRECTORY_INFO, CONT_SINGLE},
{"SMB2_FIND_DIRECTORY_INFO", "INDEX", SMB2_FIND_DIRECTORY_INFO, RAW_SEARCH_DATA_DIRECTORY_INFO, CONT_INDEX},
{"SMB2_FIND_DIRECTORY_INFO", "RESTART", SMB2_FIND_DIRECTORY_INFO, RAW_SEARCH_DATA_DIRECTORY_INFO, CONT_RESTART},
{"SMB2_FIND_DIRECTORY_INFO", "REOPEN", SMB2_FIND_DIRECTORY_INFO, RAW_SEARCH_DATA_DIRECTORY_INFO, CONT_REOPEN},
{"SMB2_FIND_FULL_DIRECTORY_INFO", "SINGLE", SMB2_FIND_FULL_DIRECTORY_INFO, RAW_SEARCH_DATA_FULL_DIRECTORY_INFO, CONT_SINGLE},
{"SMB2_FIND_FULL_DIRECTORY_INFO", "INDEX", SMB2_FIND_FULL_DIRECTORY_INFO, RAW_SEARCH_DATA_FULL_DIRECTORY_INFO, CONT_INDEX},
{"SMB2_FIND_FULL_DIRECTORY_INFO", "RESTART", SMB2_FIND_FULL_DIRECTORY_INFO, RAW_SEARCH_DATA_FULL_DIRECTORY_INFO, CONT_RESTART},
{"SMB2_FIND_FULL_DIRECTORY_INFO", "REOPEN", SMB2_FIND_FULL_DIRECTORY_INFO, RAW_SEARCH_DATA_FULL_DIRECTORY_INFO, CONT_REOPEN},
{"SMB2_FIND_ID_FULL_DIRECTORY_INFO", "SINGLE", SMB2_FIND_ID_FULL_DIRECTORY_INFO, RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO, CONT_SINGLE},
{"SMB2_FIND_ID_FULL_DIRECTORY_INFO", "INDEX", SMB2_FIND_ID_FULL_DIRECTORY_INFO, RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO, CONT_INDEX},
{"SMB2_FIND_ID_FULL_DIRECTORY_INFO", "RESTART", SMB2_FIND_ID_FULL_DIRECTORY_INFO, RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO, CONT_RESTART},
{"SMB2_FIND_ID_FULL_DIRECTORY_INFO", "REOPEN", SMB2_FIND_ID_FULL_DIRECTORY_INFO, RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO, CONT_REOPEN},
{"SMB2_FIND_ID_BOTH_DIRECTORY_INFO", "SINGLE", SMB2_FIND_ID_BOTH_DIRECTORY_INFO, RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO, CONT_SINGLE},
{"SMB2_FIND_ID_BOTH_DIRECTORY_INFO", "INDEX", SMB2_FIND_ID_BOTH_DIRECTORY_INFO, RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO, CONT_INDEX},
{"SMB2_FIND_ID_BOTH_DIRECTORY_INFO", "RESTART", SMB2_FIND_ID_BOTH_DIRECTORY_INFO, RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO, CONT_RESTART}
{"SMB2_FIND_ID_BOTH_DIRECTORY_INFO", "RESTART", SMB2_FIND_ID_BOTH_DIRECTORY_INFO, RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO, CONT_RESTART},
{"SMB2_FIND_ID_BOTH_DIRECTORY_INFO", "REOPEN", SMB2_FIND_ID_BOTH_DIRECTORY_INFO, RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO, CONT_REOPEN},
};
smb2_deltree(tree, DNAME);