mvadkert / rpms / qemu

Forked from rpms/qemu 6 years ago
Clone
335584f
From: Greg Kurz <groug@kaod.org>
335584f
Date: Fri, 5 May 2017 14:48:08 +0200
335584f
Subject: [PATCH] 9pfs: local: forbid client access to metadata (CVE-2017-7493)
335584f
335584f
When using the mapped-file security mode, we shouldn't let the client mess
335584f
with the metadata. The current code already tries to hide the metadata dir
335584f
from the client by skipping it in local_readdir(). But the client can still
335584f
access or modify it through several other operations. This can be used to
335584f
escalate privileges in the guest.
335584f
335584f
Affected backend operations are:
335584f
- local_mknod()
335584f
- local_mkdir()
335584f
- local_open2()
335584f
- local_symlink()
335584f
- local_link()
335584f
- local_unlinkat()
335584f
- local_renameat()
335584f
- local_rename()
335584f
- local_name_to_path()
335584f
335584f
Other operations are safe because they are only passed a fid path, which
335584f
is computed internally in local_name_to_path().
335584f
335584f
This patch converts all the functions listed above to fail and return
335584f
EINVAL when being passed the name of the metadata dir. This may look
335584f
like a poor choice for errno, but there's no such thing as an illegal
335584f
path name on Linux and I could not think of anything better.
335584f
335584f
This fixes CVE-2017-7493.
335584f
335584f
Reported-by: Leo Gaspard <leo@gaspard.io>
335584f
Signed-off-by: Greg Kurz <groug@kaod.org>
335584f
Reviewed-by: Eric Blake <eblake@redhat.com>
335584f
(cherry picked from commit 7a95434e0ca8a037fd8aa1a2e2461f92585eb77b)
335584f
---
335584f
 hw/9pfs/9p-local.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++--
335584f
 1 file changed, 56 insertions(+), 2 deletions(-)
335584f
335584f
diff --git a/hw/9pfs/9p-local.c b/hw/9pfs/9p-local.c
335584f
index f3ebca4f7a..a2486566af 100644
335584f
--- a/hw/9pfs/9p-local.c
335584f
+++ b/hw/9pfs/9p-local.c
335584f
@@ -452,6 +452,11 @@ static off_t local_telldir(FsContext *ctx, V9fsFidOpenState *fs)
335584f
     return telldir(fs->dir.stream);
335584f
 }
335584f
 
335584f
+static bool local_is_mapped_file_metadata(FsContext *fs_ctx, const char *name)
335584f
+{
335584f
+    return !strcmp(name, VIRTFS_META_DIR);
335584f
+}
335584f
+
335584f
 static struct dirent *local_readdir(FsContext *ctx, V9fsFidOpenState *fs)
335584f
 {
335584f
     struct dirent *entry;
335584f
@@ -465,8 +470,8 @@ again:
335584f
     if (ctx->export_flags & V9FS_SM_MAPPED) {
335584f
         entry->d_type = DT_UNKNOWN;
335584f
     } else if (ctx->export_flags & V9FS_SM_MAPPED_FILE) {
335584f
-        if (!strcmp(entry->d_name, VIRTFS_META_DIR)) {
335584f
-            /* skp the meta data directory */
335584f
+        if (local_is_mapped_file_metadata(ctx, entry->d_name)) {
335584f
+            /* skip the meta data directory */
335584f
             goto again;
335584f
         }
335584f
         entry->d_type = DT_UNKNOWN;
335584f
@@ -559,6 +564,12 @@ static int local_mknod(FsContext *fs_ctx, V9fsPath *dir_path,
335584f
     int err = -1;
335584f
     int dirfd;
335584f
 
335584f
+    if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE &&
335584f
+        local_is_mapped_file_metadata(fs_ctx, name)) {
335584f
+        errno = EINVAL;
335584f
+        return -1;
335584f
+    }
335584f
+
335584f
     dirfd = local_opendir_nofollow(fs_ctx, dir_path->data);
335584f
     if (dirfd == -1) {
335584f
         return -1;
335584f
@@ -605,6 +616,12 @@ static int local_mkdir(FsContext *fs_ctx, V9fsPath *dir_path,
335584f
     int err = -1;
335584f
     int dirfd;
335584f
 
335584f
+    if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE &&
335584f
+        local_is_mapped_file_metadata(fs_ctx, name)) {
335584f
+        errno = EINVAL;
335584f
+        return -1;
335584f
+    }
335584f
+
335584f
     dirfd = local_opendir_nofollow(fs_ctx, dir_path->data);
335584f
     if (dirfd == -1) {
335584f
         return -1;
335584f
@@ -694,6 +711,12 @@ static int local_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name,
335584f
     int err = -1;
335584f
     int dirfd;
335584f
 
335584f
+    if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE &&
335584f
+        local_is_mapped_file_metadata(fs_ctx, name)) {
335584f
+        errno = EINVAL;
335584f
+        return -1;
335584f
+    }
335584f
+
335584f
     /*
335584f
      * Mark all the open to not follow symlinks
335584f
      */
335584f
@@ -752,6 +775,12 @@ static int local_symlink(FsContext *fs_ctx, const char *oldpath,
335584f
     int err = -1;
335584f
     int dirfd;
335584f
 
335584f
+    if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE &&
335584f
+        local_is_mapped_file_metadata(fs_ctx, name)) {
335584f
+        errno = EINVAL;
335584f
+        return -1;
335584f
+    }
335584f
+
335584f
     dirfd = local_opendir_nofollow(fs_ctx, dir_path->data);
335584f
     if (dirfd == -1) {
335584f
         return -1;
335584f
@@ -826,6 +855,12 @@ static int local_link(FsContext *ctx, V9fsPath *oldpath,
335584f
     int ret = -1;
335584f
     int odirfd, ndirfd;
335584f
 
335584f
+    if (ctx->export_flags & V9FS_SM_MAPPED_FILE &&
335584f
+        local_is_mapped_file_metadata(ctx, name)) {
335584f
+        errno = EINVAL;
335584f
+        return -1;
335584f
+    }
335584f
+
335584f
     odirfd = local_opendir_nofollow(ctx, odirpath);
335584f
     if (odirfd == -1) {
335584f
         goto out;
335584f
@@ -1096,6 +1131,12 @@ static int local_lremovexattr(FsContext *ctx, V9fsPath *fs_path,
335584f
 static int local_name_to_path(FsContext *ctx, V9fsPath *dir_path,
335584f
                               const char *name, V9fsPath *target)
335584f
 {
335584f
+    if (ctx->export_flags & V9FS_SM_MAPPED_FILE &&
335584f
+        local_is_mapped_file_metadata(ctx, name)) {
335584f
+        errno = EINVAL;
335584f
+        return -1;
335584f
+    }
335584f
+
335584f
     if (dir_path) {
335584f
         v9fs_path_sprintf(target, "%s/%s", dir_path->data, name);
335584f
     } else if (strcmp(name, "/")) {
335584f
@@ -1116,6 +1157,13 @@ static int local_renameat(FsContext *ctx, V9fsPath *olddir,
335584f
     int ret;
335584f
     int odirfd, ndirfd;
335584f
 
335584f
+    if (ctx->export_flags & V9FS_SM_MAPPED_FILE &&
335584f
+        (local_is_mapped_file_metadata(ctx, old_name) ||
335584f
+         local_is_mapped_file_metadata(ctx, new_name))) {
335584f
+        errno = EINVAL;
335584f
+        return -1;
335584f
+    }
335584f
+
335584f
     odirfd = local_opendir_nofollow(ctx, olddir->data);
335584f
     if (odirfd == -1) {
335584f
         return -1;
335584f
@@ -1206,6 +1254,12 @@ static int local_unlinkat(FsContext *ctx, V9fsPath *dir,
335584f
     int ret;
335584f
     int dirfd;
335584f
 
335584f
+    if (ctx->export_flags & V9FS_SM_MAPPED_FILE &&
335584f
+        local_is_mapped_file_metadata(ctx, name)) {
335584f
+        errno = EINVAL;
335584f
+        return -1;
335584f
+    }
335584f
+
335584f
     dirfd = local_opendir_nofollow(ctx, dir->data);
335584f
     if (dirfd == -1) {
335584f
         return -1;