summaryrefslogtreecommitdiff
path: root/hw/9pfs
diff options
context:
space:
mode:
authorGreg Kurz <groug@kaod.org>2017-02-26 23:45:09 +0100
committerMichael Roth <mdroth@linux.vnet.ibm.com>2017-03-16 12:08:24 -0500
commit9c5cb58970d76a0fe41759c738e14f8bb16cc89d (patch)
tree44bf19371ee1335f789c19c647d7e7fb16c75fa7 /hw/9pfs
parentc8c9aab1731c2c79248bd24121c09e8e270f6007 (diff)
downloadqemu-9c5cb58970d76a0fe41759c738e14f8bb16cc89d.tar.gz
9pfs: local: open2: don't follow symlinks
The local_open2() callback is vulnerable to symlink attacks because it calls: (1) open() which follows symbolic links for all path elements but the rightmost one (2) local_set_xattr()->setxattr() which follows symbolic links for all path elements (3) local_set_mapped_file_attr() which calls in turn local_fopen() and mkdir(), both functions following symbolic links for all path elements but the rightmost one (4) local_post_create_passthrough() which calls in turn lchown() and chmod(), both functions also following symbolic links This patch converts local_open2() to rely on opendir_nofollow() and mkdirat() to fix (1), as well as local_set_xattrat(), local_set_mapped_file_attrat() and local_set_cred_passthrough() to fix (2), (3) and (4) respectively. Since local_open2() already opens a descriptor to the target file, local_set_cred_passthrough() is modified to reuse it instead of opening a new one. The mapped and mapped-file security modes are supposed to be identical, except for the place where credentials and file modes are stored. While here, we also make that explicit by sharing the call to openat(). This partly fixes CVE-2016-9602. Signed-off-by: Greg Kurz <groug@kaod.org> Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> (cherry picked from commit a565fea56546e254b7610305b07711f0a3bda0c7) Signed-off-by: Greg Kurz <gkurz@linux.vnet.ibm.com> Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
Diffstat (limited to 'hw/9pfs')
-rw-r--r--hw/9pfs/9p-local.c56
1 files changed, 19 insertions, 37 deletions
diff --git a/hw/9pfs/9p-local.c b/hw/9pfs/9p-local.c
index 76ac2054e2..ea293e8f1e 100644
--- a/hw/9pfs/9p-local.c
+++ b/hw/9pfs/9p-local.c
@@ -888,62 +888,45 @@ static int local_fstat(FsContext *fs_ctx, int fid_type,
static int local_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name,
int flags, FsCred *credp, V9fsFidOpenState *fs)
{
- char *path;
int fd = -1;
int err = -1;
- int serrno = 0;
- V9fsString fullname;
- char *buffer = NULL;
+ int dirfd;
/*
* Mark all the open to not follow symlinks
*/
flags |= O_NOFOLLOW;
- v9fs_string_init(&fullname);
- v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name);
- path = fullname.data;
+ dirfd = local_opendir_nofollow(fs_ctx, dir_path->data);
+ if (dirfd == -1) {
+ return -1;
+ }
/* Determine the security model */
- if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
- buffer = rpath(fs_ctx, path);
- fd = open(buffer, flags, SM_LOCAL_MODE_BITS);
+ if (fs_ctx->export_flags & V9FS_SM_MAPPED ||
+ fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
+ fd = openat_file(dirfd, name, flags, SM_LOCAL_MODE_BITS);
if (fd == -1) {
- err = fd;
goto out;
}
credp->fc_mode = credp->fc_mode|S_IFREG;
- /* Set cleint credentials in xattr */
- err = local_set_xattr(buffer, credp);
- if (err == -1) {
- serrno = errno;
- goto err_end;
- }
- } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
- buffer = rpath(fs_ctx, path);
- fd = open(buffer, flags, SM_LOCAL_MODE_BITS);
- if (fd == -1) {
- err = fd;
- goto out;
+ if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
+ /* Set cleint credentials in xattr */
+ err = local_set_xattrat(dirfd, name, credp);
+ } else {
+ err = local_set_mapped_file_attrat(dirfd, name, credp);
}
- credp->fc_mode = credp->fc_mode|S_IFREG;
- /* Set client credentials in .virtfs_metadata directory files */
- err = local_set_mapped_file_attr(fs_ctx, path, credp);
if (err == -1) {
- serrno = errno;
goto err_end;
}
} else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
(fs_ctx->export_flags & V9FS_SM_NONE)) {
- buffer = rpath(fs_ctx, path);
- fd = open(buffer, flags, credp->fc_mode);
+ fd = openat_file(dirfd, name, flags, credp->fc_mode);
if (fd == -1) {
- err = fd;
goto out;
}
- err = local_post_create_passthrough(fs_ctx, path, credp);
+ err = local_set_cred_passthrough(fs_ctx, dirfd, name, credp);
if (err == -1) {
- serrno = errno;
goto err_end;
}
}
@@ -952,12 +935,11 @@ static int local_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name,
goto out;
err_end:
- close(fd);
- remove(buffer);
- errno = serrno;
+ unlinkat_preserve_errno(dirfd, name,
+ flags & O_DIRECTORY ? AT_REMOVEDIR : 0);
+ close_preserve_errno(fd);
out:
- g_free(buffer);
- v9fs_string_free(&fullname);
+ close_preserve_errno(dirfd);
return err;
}