Skip to content

Commit

Permalink
9pfs: local: open/opendir: don't follow symlinks
Browse files Browse the repository at this point in the history
The local_open() and local_opendir() callbacks are vulnerable to symlink
attacks because they call:

(1) open(O_NOFOLLOW) which follows symbolic links in all path elements but
    the rightmost one
(2) opendir() which follows symbolic links in all path elements

This patch converts both callbacks to use new helpers based on
openat_nofollow() to only open files and directories if they are
below the virtfs shared folder

This partly fixes CVE-2016-9602.

Signed-off-by: Greg Kurz <groug@kaod.org>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
  • Loading branch information
gkurz committed Feb 28, 2017
1 parent 0e35a37 commit 996a0d7
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 10 deletions.
37 changes: 27 additions & 10 deletions hw/9pfs/9p-local.c
Expand Up @@ -13,6 +13,7 @@

#include "qemu/osdep.h"
#include "9p.h"
#include "9p-local.h"
#include "9p-xattr.h"
#include "9p-util.h"
#include "fsdev/qemu-fsdev.h" /* local_ops */
Expand Down Expand Up @@ -48,6 +49,24 @@ typedef struct {
int mountfd;
} LocalData;

int local_open_nofollow(FsContext *fs_ctx, const char *path, int flags,
mode_t mode)
{
LocalData *data = fs_ctx->private;

/* All paths are relative to the path data->mountfd points to */
while (*path == '/') {
path++;
}

return relative_openat_nofollow(data->mountfd, path, flags, mode);
}

int local_opendir_nofollow(FsContext *fs_ctx, const char *path)
{
return local_open_nofollow(fs_ctx, path, O_DIRECTORY | O_RDONLY, 0);
}

#define VIRTFS_META_DIR ".virtfs_metadata"

static char *local_mapped_attr_path(FsContext *ctx, const char *path)
Expand Down Expand Up @@ -359,13 +378,9 @@ static int local_closedir(FsContext *ctx, V9fsFidOpenState *fs)
static int local_open(FsContext *ctx, V9fsPath *fs_path,
int flags, V9fsFidOpenState *fs)
{
char *buffer;
char *path = fs_path->data;
int fd;

buffer = rpath(ctx, path);
fd = open(buffer, flags | O_NOFOLLOW);
g_free(buffer);
fd = local_open_nofollow(ctx, fs_path->data, flags, 0);
if (fd == -1) {
return -1;
}
Expand All @@ -376,13 +391,15 @@ static int local_open(FsContext *ctx, V9fsPath *fs_path,
static int local_opendir(FsContext *ctx,
V9fsPath *fs_path, V9fsFidOpenState *fs)
{
char *buffer;
char *path = fs_path->data;
int dirfd;
DIR *stream;

buffer = rpath(ctx, path);
stream = opendir(buffer);
g_free(buffer);
dirfd = local_opendir_nofollow(ctx, fs_path->data);
if (dirfd == -1) {
return -1;
}

stream = fdopendir(dirfd);
if (!stream) {
return -1;
}
Expand Down
20 changes: 20 additions & 0 deletions hw/9pfs/9p-local.h
@@ -0,0 +1,20 @@
/*
* 9p local backend utilities
*
* Copyright IBM, Corp. 2017
*
* Authors:
* Greg Kurz <groug@kaod.org>
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*/

#ifndef QEMU_9P_LOCAL_H
#define QEMU_9P_LOCAL_H

int local_open_nofollow(FsContext *fs_ctx, const char *path, int flags,
mode_t mode);
int local_opendir_nofollow(FsContext *fs_ctx, const char *path);

#endif

0 comments on commit 996a0d7

Please sign in to comment.