79c4324644
Change-Id: I2d302dda68298877c65c99147f5bf22186a59aac
173 lines
5.0 KiB
Diff
173 lines
5.0 KiB
Diff
From beed3295acf786cec520a8a0aec5efcd2ca12b23 Mon Sep 17 00:00:00 2001
|
|
From: liuxiangdong <liuxiangdong5@huawei.com>
|
|
Date: Fri, 14 Jul 2023 05:11:57 +0800
|
|
Subject: [PATCH] 9pfs: prevent opening special files (CVE-2023-2861) The 9p
|
|
protocol does not specifically define how server shall behave when client
|
|
tries to open a special file, however from security POV it does make sense
|
|
for 9p server to prohibit opening any special file on host side in general. A
|
|
sane Linux 9p client for instance would never attempt to open a special file
|
|
on host side, it would always handle those exclusively on its guest side. A
|
|
malicious client however could potentially escape from the exported 9p tree
|
|
by creating and opening a device file on host side.
|
|
|
|
With QEMU this could only be exploited in the following unsafe setups:
|
|
|
|
- Running QEMU binary as root AND 9p 'local' fs driver AND 'passthrough'
|
|
security model.
|
|
|
|
or
|
|
|
|
- Using 9p 'proxy' fs driver (which is running its helper daemon as
|
|
root).
|
|
|
|
These setups were already discouraged for safety reasons before,
|
|
however for obvious reasons we are now tightening behaviour on this.
|
|
|
|
Fixes: CVE-2023-2861
|
|
Reported-by: Yanwu Shen <ywsPlz@gmail.com>
|
|
Reported-by: Jietao Xiao <shawtao1125@gmail.com>
|
|
Reported-by: Jinku Li <jkli@xidian.edu.cn>
|
|
Reported-by: Wenbo Shen <shenwenbo@zju.edu.cn>
|
|
Signed-off-by: Christian Schoenebeck <qemu_oss@crudebyte.com>
|
|
Reviewed-by: Greg Kurz <groug@kaod.org>
|
|
Reviewed-by: Michael Tokarev <mjt@tls.msk.ru>
|
|
Message-Id: <E1q6w7r-0000Q0-NM@lizzy.crudebyte.com>
|
|
---
|
|
fsdev/virtfs-proxy-helper.c | 27 +++++++++++++++++++++++--
|
|
hw/9pfs/9p-util.h | 40 +++++++++++++++++++++++++++++++++++++
|
|
2 files changed, 65 insertions(+), 2 deletions(-)
|
|
|
|
diff --git a/fsdev/virtfs-proxy-helper.c b/fsdev/virtfs-proxy-helper.c
|
|
index 15c0e79b06..f9e4669a5b 100644
|
|
--- a/fsdev/virtfs-proxy-helper.c
|
|
+++ b/fsdev/virtfs-proxy-helper.c
|
|
@@ -26,6 +26,7 @@
|
|
#include "qemu/xattr.h"
|
|
#include "9p-iov-marshal.h"
|
|
#include "hw/9pfs/9p-proxy.h"
|
|
+#include "hw/9pfs/9p-util.h"
|
|
#include "fsdev/9p-iov-marshal.h"
|
|
|
|
#define PROGNAME "virtfs-proxy-helper"
|
|
@@ -338,6 +339,28 @@ static void resetugid(int suid, int sgid)
|
|
}
|
|
}
|
|
|
|
+/*
|
|
+ * Open regular file or directory. Attempts to open any special file are
|
|
+ * rejected.
|
|
+ *
|
|
+ * returns file descriptor or -1 on error
|
|
+ */
|
|
+static int open_regular(const char *pathname, int flags, mode_t mode)
|
|
+{
|
|
+ int fd;
|
|
+
|
|
+ fd = open(pathname, flags, mode);
|
|
+ if (fd < 0) {
|
|
+ return fd;
|
|
+ }
|
|
+
|
|
+ if (close_if_special_file(fd) < 0) {
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ return fd;
|
|
+}
|
|
+
|
|
/*
|
|
* send response in two parts
|
|
* 1) ProxyHeader
|
|
@@ -682,7 +705,7 @@ static int do_create(struct iovec *iovec)
|
|
if (ret < 0) {
|
|
goto unmarshal_err_out;
|
|
}
|
|
- ret = open(path.data, flags, mode);
|
|
+ ret = open_regular(path.data, flags, mode);
|
|
if (ret < 0) {
|
|
ret = -errno;
|
|
}
|
|
@@ -707,7 +730,7 @@ static int do_open(struct iovec *iovec)
|
|
if (ret < 0) {
|
|
goto err_out;
|
|
}
|
|
- ret = open(path.data, flags);
|
|
+ ret = open_regular(path.data, flags, 0);
|
|
if (ret < 0) {
|
|
ret = -errno;
|
|
}
|
|
diff --git a/hw/9pfs/9p-util.h b/hw/9pfs/9p-util.h
|
|
index 546f46dc7d..23000e917f 100644
|
|
--- a/hw/9pfs/9p-util.h
|
|
+++ b/hw/9pfs/9p-util.h
|
|
@@ -13,12 +13,16 @@
|
|
#ifndef QEMU_9P_UTIL_H
|
|
#define QEMU_9P_UTIL_H
|
|
|
|
+#include "qemu/error-report.h"
|
|
+
|
|
#ifdef O_PATH
|
|
#define O_PATH_9P_UTIL O_PATH
|
|
#else
|
|
#define O_PATH_9P_UTIL 0
|
|
#endif
|
|
|
|
+#define qemu_fstat fstat
|
|
+
|
|
static inline void close_preserve_errno(int fd)
|
|
{
|
|
int serrno = errno;
|
|
@@ -26,6 +30,38 @@ static inline void close_preserve_errno(int fd)
|
|
errno = serrno;
|
|
}
|
|
|
|
+/**
|
|
+ * close_if_special_file() - Close @fd if neither regular file nor directory.
|
|
+ *
|
|
+ * @fd: file descriptor of open file
|
|
+ * Return: 0 on regular file or directory, -1 otherwise
|
|
+ *
|
|
+ * CVE-2023-2861: Prohibit opening any special file directly on host
|
|
+ * (especially device files), as a compromised client could potentially gain
|
|
+ * access outside exported tree under certain, unsafe setups. We expect
|
|
+ * client to handle I/O on special files exclusively on guest side.
|
|
+ */
|
|
+static inline int close_if_special_file(int fd)
|
|
+{
|
|
+ struct stat stbuf;
|
|
+
|
|
+ if (qemu_fstat(fd, &stbuf) < 0) {
|
|
+ close_preserve_errno(fd);
|
|
+ return -1;
|
|
+ }
|
|
+ if (!S_ISREG(stbuf.st_mode) && !S_ISDIR(stbuf.st_mode)) {
|
|
+ error_report_once(
|
|
+ "9p: broken or compromised client detected; attempt to open "
|
|
+ "special file (i.e. neither regular file, nor directory)"
|
|
+ );
|
|
+ close(fd);
|
|
+ errno = ENXIO;
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
static inline int openat_dir(int dirfd, const char *name)
|
|
{
|
|
return openat(dirfd, name,
|
|
@@ -56,6 +92,10 @@ again:
|
|
return -1;
|
|
}
|
|
|
|
+ if (close_if_special_file(fd) < 0) {
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
serrno = errno;
|
|
/* O_NONBLOCK was only needed to open the file. Let's drop it. We don't
|
|
* do that with O_PATH since fcntl(F_SETFL) isn't supported, and openat()
|
|
--
|
|
2.41.0.windows.1
|
|
|