@@ -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;
}
@@ -13,6 +13,8 @@
#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
@@ -95,6 +97,7 @@ static inline int errno_to_dotl(int err) {
#endif
#define qemu_openat openat
+#define qemu_fstat fstat
#define qemu_fstatat fstatat
#define qemu_mkdirat mkdirat
#define qemu_renameat renameat
@@ -108,6 +111,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 qemu_openat(dirfd, name,
@@ -142,6 +177,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()