qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [PATCH v2] 9pfs: prevent opening special files (CVE-2023-2861)


From: Christian Schoenebeck
Subject: Re: [PATCH v2] 9pfs: prevent opening special files (CVE-2023-2861)
Date: Wed, 07 Jun 2023 13:02:17 +0200

On Tuesday, June 6, 2023 6:00:28 PM CEST Greg Kurz wrote:
> Hi Christian,
> 
> On Tue, 06 Jun 2023 15:57:50 +0200
> Christian Schoenebeck <qemu_oss@crudebyte.com> wrote:
> 
> > 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>
> > ---
> >  v1 -> v2:
> >  - Add equivalent fix for 'proxy' fs driver.
> >  - Minor adjustments on commit log.
> > 
> 
> Note that this might be a bit confusing for reviewers since
> v1 was never posted to qemu-devel. Technically, this should
> have been posted without the v2 tag.

I felt it wouldn't make it any better, as it might otherwise confuse those who
already got the previous two patch emails.

> >  fsdev/virtfs-proxy-helper.c | 48 +++++++++++++++++++++++++++++++++++--
> >  hw/9pfs/9p-util.h           | 29 ++++++++++++++++++++++
> >  2 files changed, 75 insertions(+), 2 deletions(-)
> > 
> > diff --git a/fsdev/virtfs-proxy-helper.c b/fsdev/virtfs-proxy-helper.c
> > index 5cafcd7703..f311519fa3 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,49 @@ 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;
> > +    struct stat stbuf;
> > +
> > +    fd = open(pathname, flags, mode);
> > +    if (fd < 0) {
> > +        return fd;
> > +    }
> > +
> > +    /* 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.
> > +     */
> > +    if (qemu_fstat(fd, &stbuf) < 0) {
> > +        close_preserve_errno(fd);
> > +        return -1;
> > +    }
> > +    if (!S_ISREG(stbuf.st_mode) && !S_ISDIR(stbuf.st_mode)) {
> > +        /* Tcreate and Tlcreate 9p messages mandate to immediately open the
> > +         * created file for I/O. So this is not (necessarily) due to a 
> > broken
> > +         * client, and hence no error message is to be reported in this 
> > case.
> > +         */
> > +        if (!(flags & O_CREAT)) {
> 
> Tlcreate is explicitly about creating regular files only (see [1] and
> v9fs_lcreate()) and I don't quite see how open() could successfully
> create a regular file and the resulting fd is fstat'ed as something
> else.
> 
> Tcreate seems to cover more types but again only regular files (with O_CREAT)
> or directories (without O_CREAT) are expected here (see v9fs_create()).
> 
> Unless I'm missing something, it seems that the comment and the O_CREAT
> check should be removed.
> 
> [1] 
> https://github.com/chaos/diod/blob/master/protocol.md#lcreate----create-regular-file

You are right about Tlcreate, but for Tcreate 9p2000.u specifies, quote:

"In addition to creating directories with DMDIR, 9P2000.u allows the creation
 of symlinks (DMSYMLINK), devices (DMDEVICE), named pipes (DMNAMEPIPE), and
 sockets (DMSOCKET)."

http://ericvh.github.io/9p-rfc/rfc9p2000.u.html#anchor17

So I just remove mentioning Tlcreate in the comment?

> 
> > +            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 fd;
> > +}
> > +
> >  /*
> >   * send response in two parts
> >   * 1) ProxyHeader
> > @@ -682,7 +726,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 +751,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 c314cf381d..9da1a0538d 100644
> > --- a/hw/9pfs/9p-util.h
> > +++ b/hw/9pfs/9p-util.h
> > @@ -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
> > @@ -118,6 +121,7 @@ static inline int openat_file(int dirfd, const char 
> > *name, int flags,
> >                                mode_t mode)
> >  {
> >      int fd, serrno, ret;
> > +    struct stat stbuf;
> >  
> >  #ifndef CONFIG_DARWIN
> >  again:
> > @@ -142,6 +146,31 @@ again:
> >          return -1;
> >      }
> >  
> > +    /* 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.
> > +     */
> > +    if (qemu_fstat(fd, &stbuf) < 0) {
> > +        close_preserve_errno(fd);
> > +        return -1;
> > +    }
> > +    if (!S_ISREG(stbuf.st_mode) && !S_ISDIR(stbuf.st_mode)) {
> > +        /* Tcreate and Tlcreate 9p messages mandate to immediately open the
> > +         * created file for I/O. So this is not (necessarily) due to a 
> > broken
> > +         * client, and hence no error message is to be reported in this 
> > case.
> > +         */
> 
> Same remark as with the proxy helper.
> 
> If you agree with my suggestions, feel free to add my R-b right away.
> 
> Cheers,

I'll definitely take the time for another (v3) round in this case. Thanks!

> --
> Greg
> 
> > +        if (!(flags & O_CREAT)) {
> > +            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;
> > +    }
> > +
> >      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()
> 
> 





reply via email to

[Prev in Thread] Current Thread [Next in Thread]