From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from [140.186.70.92] (port=52342 helo=eggs.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1PIHLA-0007wJ-Bv for qemu-devel@nongnu.org; Tue, 16 Nov 2010 03:56:39 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1PIHL6-0005oD-VA for qemu-devel@nongnu.org; Tue, 16 Nov 2010 03:56:34 -0500 Received: from e23smtp06.au.ibm.com ([202.81.31.148]:42884) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1PIHL5-0005o4-T0 for qemu-devel@nongnu.org; Tue, 16 Nov 2010 03:56:32 -0500 Received: from d23relay03.au.ibm.com (d23relay03.au.ibm.com [202.81.31.245]) by e23smtp06.au.ibm.com (8.14.4/8.13.1) with ESMTP id oAG8uQea006418 for ; Tue, 16 Nov 2010 19:56:26 +1100 Received: from d23av04.au.ibm.com (d23av04.au.ibm.com [9.190.235.139]) by d23relay03.au.ibm.com (8.13.8/8.13.8/NCO v10.0) with ESMTP id oAG8uTNr1015886 for ; Tue, 16 Nov 2010 19:56:29 +1100 Received: from d23av04.au.ibm.com (loopback [127.0.0.1]) by d23av04.au.ibm.com (8.14.4/8.13.1/NCO v10.0 AVout) with ESMTP id oAG8uSj4017490 for ; Tue, 16 Nov 2010 19:56:28 +1100 From: "M. Mohan Kumar" Date: Tue, 16 Nov 2010 14:26:25 +0530 Message-Id: <1289897785-17234-1-git-send-email-mohan@in.ibm.com> In-Reply-To: <1289897699-17135-1-git-send-email-mohan@in.ibm.com> References: <1289897699-17135-1-git-send-email-mohan@in.ibm.com> Subject: [Qemu-devel] [PATCH 2/2] [V2] virtio-9p: Use chroot interface in passthrough model List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: qemu-devel@nongnu.org Cc: stefanha@gmail.com Make use of chroot interfaces for passthrough security model to fix the vulnerability in following symbolic links. Signed-off-by: M. Mohan Kumar --- hw/virtio-9p-local.c | 294 ++++++++++++++++++++++++++++++++++++++----------- 1 files changed, 228 insertions(+), 66 deletions(-) diff --git a/hw/virtio-9p-local.c b/hw/virtio-9p-local.c index 656bfb3..cd55db5 100644 --- a/hw/virtio-9p-local.c +++ b/hw/virtio-9p-local.c @@ -19,16 +19,101 @@ #include #include #include +#include + +static int get_fd(FsContext *fs_ctx, const char *path, int flags, FsCred *credp) +{ + V9fsOpenRequest request; + int fd, error = 0; + + memset(&request, 0, sizeof(request)); + request.data.path_len = strlen(path); + request.path.path = qemu_strdup(path); + request.data.flags = flags; + if (credp) { + request.data.mode = credp->fc_mode; + request.data.uid = credp->fc_uid; + request.data.gid = credp->fc_gid; + request.data.dev = credp->fc_rdev; + } + fd = v9fs_getfd(&request, &error, fs_ctx); + if (fd == -2) { + /* Socket read/write fail */ + errno = EIO; + return -1; + } + if (error) { + errno = error; + } else { + errno = error; + } + qemu_strdup(request.path.path); + return fd; +} +static int get_pfd(FsContext *fs_ctx, const char *path) +{ + V9fsOpenRequest request; + int fd, error = 0; + char *dpath = qemu_strdup(path); + + memset(&request, 0, sizeof(request)); + request.path.path = dirname(dpath); + request.data.path_len = strlen(request.path.path); + request.data.flags = O_RDONLY | O_DIRECTORY | O_NOFOLLOW; + fd = v9fs_getfd(&request, &error, fs_ctx); + if (fd == -2) { + /* Socket read/write fail */ + errno = EIO; + return -1; + } + if (error) { + errno = error; + } else { + errno = 0; + } + qemu_free(dpath); + return fd; +} + +static int do_symlink(FsContext *fs_ctx, const char *oldpath, + const char *newpath, FsCred *credp) +{ + V9fsOpenRequest request; + int fd, error = 0; + + memset(&request, 0, sizeof(request)); + request.data.path_len = strlen(newpath); + request.path.path = qemu_strdup(newpath); + request.data.oldpath_len = strlen(oldpath); + request.path.old_path = qemu_strdup(oldpath); + request.data.flags = S_IFLNK | O_CREAT; + + if (credp) { + request.data.mode = credp->fc_mode; + request.data.uid = credp->fc_uid; + request.data.gid = credp->fc_gid; + request.data.dev = credp->fc_rdev; + } + fd = v9fs_getfd(&request, &error, fs_ctx); + if (error) { + errno = error; + } else { + errno = error; + } + qemu_strdup(request.path.path); + return fd; +} static int local_lstat(FsContext *fs_ctx, const char *path, struct stat *stbuf) { int err; - err = lstat(rpath(fs_ctx, path), stbuf); - if (err) { - return err; - } + if (fs_ctx->fs_sm == SM_MAPPED) { + err = lstat(rpath(fs_ctx, path), stbuf); + if (err) { + return err; + } /* Actual credentials are part of extended attrs */ uid_t tmp_uid; gid_t tmp_gid; @@ -50,6 +135,22 @@ static int local_lstat(FsContext *fs_ctx, const char *path, struct stat *stbuf) sizeof(dev_t)) > 0) { stbuf->st_rdev = tmp_dev; } + } else if (fs_ctx->fs_sm == SM_PASSTHROUGH) { + int pfd; + char *base, *basep; + + base = qemu_strdup(path); + basep = basename(base); + + pfd = get_pfd(fs_ctx, path); + err = fstatat(pfd, basep, stbuf, AT_SYMLINK_NOFOLLOW); + close(pfd); + free(base); + } else { + err = lstat(rpath(fs_ctx, path), stbuf); + if (err) { + return err; + } } return err; } @@ -88,21 +189,13 @@ static int local_set_xattr(const char *path, FsCred *credp) return 0; } -static int local_post_create_passthrough(FsContext *fs_ctx, const char *path, - FsCred *credp) +static int local_post_create_none(FsContext *fs_ctx, const char *path, + FsCred *credp) { if (chmod(rpath(fs_ctx, path), credp->fc_mode & 07777) < 0) { return -1; } - if (lchown(rpath(fs_ctx, path), credp->fc_uid, credp->fc_gid) < 0) { - /* - * If we fail to change ownership and if we are - * using security model none. Ignore the error - */ - if (fs_ctx->fs_sm != SM_NONE) { - return -1; - } - } + lchown(rpath(fs_ctx, path), credp->fc_uid, credp->fc_gid); return 0; } @@ -121,9 +214,16 @@ static ssize_t local_readlink(FsContext *fs_ctx, const char *path, } while (tsize == -1 && errno == EINTR); close(fd); return tsize; - } else if ((fs_ctx->fs_sm == SM_PASSTHROUGH) || - (fs_ctx->fs_sm == SM_NONE)) { + } else if (fs_ctx->fs_sm == SM_NONE) { tsize = readlink(rpath(fs_ctx, path), buf, bufsz); + } else if (fs_ctx->fs_sm == SM_PASSTHROUGH) { + int pfd; + char *base; + base = qemu_strdup(path); + pfd = get_pfd(fs_ctx, path); + tsize = readlinkat(pfd, basename(base), buf, bufsz); + qemu_free(base); + close(pfd); } return tsize; } @@ -140,7 +240,11 @@ static int local_closedir(FsContext *ctx, DIR *dir) static int local_open(FsContext *ctx, const char *path, int flags) { - return open(rpath(ctx, path), flags); + if (ctx->fs_sm == SM_PASSTHROUGH) { + return get_fd(ctx, path, flags, 0); + } else { + return open(rpath(ctx, path), flags); + } } static DIR *local_opendir(FsContext *ctx, const char *path) @@ -225,17 +329,25 @@ static int local_mknod(FsContext *fs_ctx, const char *path, FsCred *credp) serrno = errno; goto err_end; } - } else if ((fs_ctx->fs_sm == SM_PASSTHROUGH) || - (fs_ctx->fs_sm == SM_NONE)) { + } else if (fs_ctx->fs_sm == SM_NONE) { err = mknod(rpath(fs_ctx, path), credp->fc_mode, credp->fc_rdev); if (err == -1) { return err; } - err = local_post_create_passthrough(fs_ctx, path, credp); + err = local_post_create_none(fs_ctx, path, credp); if (err == -1) { serrno = errno; goto err_end; } + } else if (fs_ctx->fs_sm == SM_PASSTHROUGH) { + int fd; + fd = get_fd(fs_ctx, path, O_CREAT | S_IFCHR, credp); + if (fd < 0) { + err = fd; + serrno = errno; + goto err_end; + } + err = 0; } return err; @@ -262,17 +374,25 @@ static int local_mkdir(FsContext *fs_ctx, const char *path, FsCred *credp) serrno = errno; goto err_end; } - } else if ((fs_ctx->fs_sm == SM_PASSTHROUGH) || - (fs_ctx->fs_sm == SM_NONE)) { + } else if (fs_ctx->fs_sm == SM_NONE) { err = mkdir(rpath(fs_ctx, path), credp->fc_mode); if (err == -1) { return err; } - err = local_post_create_passthrough(fs_ctx, path, credp); + err = local_post_create_none(fs_ctx, path, credp); if (err == -1) { serrno = errno; goto err_end; } + } else if (fs_ctx->fs_sm == SM_PASSTHROUGH) { + int fd; + fd = get_fd(fs_ctx, path, O_CREAT | S_IFDIR, credp); + if (fd < 0) { + err = fd; + serrno = errno; + goto err_end; + } + err = 0; } return err; @@ -332,17 +452,18 @@ static int local_open2(FsContext *fs_ctx, const char *path, int flags, serrno = errno; goto err_end; } - } else if ((fs_ctx->fs_sm == SM_PASSTHROUGH) || - (fs_ctx->fs_sm == SM_NONE)) { + } else if (fs_ctx->fs_sm == SM_NONE) { fd = open(rpath(fs_ctx, path), flags, credp->fc_mode); if (fd == -1) { return fd; } - err = local_post_create_passthrough(fs_ctx, path, credp); + err = local_post_create_none(fs_ctx, path, credp); if (err == -1) { serrno = errno; goto err_end; } + } else if (fs_ctx->fs_sm == SM_PASSTHROUGH) { + fd = get_fd(fs_ctx, path, flags, credp); } return fd; @@ -389,23 +510,17 @@ static int local_symlink(FsContext *fs_ctx, const char *oldpath, serrno = errno; goto err_end; } - } else if ((fs_ctx->fs_sm == SM_PASSTHROUGH) || - (fs_ctx->fs_sm == SM_NONE)) { + } else if (fs_ctx->fs_sm == SM_NONE) { err = symlink(oldpath, rpath(fs_ctx, newpath)); if (err) { return err; } - err = lchown(rpath(fs_ctx, newpath), credp->fc_uid, credp->fc_gid); + lchown(rpath(fs_ctx, newpath), credp->fc_uid, credp->fc_gid); + } else if (fs_ctx->fs_sm == SM_PASSTHROUGH) { + err = do_symlink(fs_ctx, oldpath, newpath, credp); if (err == -1) { - /* - * If we fail to change ownership and if we are - * using security model none. Ignore the error - */ - if (fs_ctx->fs_sm != SM_NONE) { - serrno = errno; - goto err_end; - } else - err = 0; + serrno = errno; + goto err_end; } } return err; @@ -418,22 +533,33 @@ err_end: static int local_link(FsContext *ctx, const char *oldpath, const char *newpath) { - char *tmp = qemu_strdup(rpath(ctx, oldpath)); int err, serrno = 0; - if (tmp == NULL) { - return -ENOMEM; - } - - err = link(tmp, rpath(ctx, newpath)); - if (err == -1) { - serrno = errno; - } - - qemu_free(tmp); - - if (err == -1) { - errno = serrno; + if (ctx->fs_sm == SM_PASSTHROUGH) { + int opfd, npfd; + char *obase, *nbase; + obase = qemu_strdup(oldpath); + nbase = qemu_strdup(newpath); + opfd = get_pfd(ctx, obase); + npfd = get_pfd(ctx, nbase); + err = linkat(opfd, basename(obase), npfd, basename(nbase), 0); + if (err == -1) { + serrno = errno; + } + qemu_free(obase); + qemu_free(nbase); + close(opfd); + close(npfd); + } else { + char *tmp = qemu_strdup(rpath(ctx, oldpath)); + if (tmp == NULL) { + return -ENOMEM; + } + err = link(tmp, rpath(ctx, newpath)); + if (err == -1) { + serrno = errno; + } + qemu_free(tmp); } return err; @@ -441,24 +567,49 @@ static int local_link(FsContext *ctx, const char *oldpath, const char *newpath) static int local_truncate(FsContext *ctx, const char *path, off_t size) { - return truncate(rpath(ctx, path), size); + if (ctx->fs_sm == SM_PASSTHROUGH) { + int fd, retval; + fd = get_fd(ctx, path, O_RDWR, 0); + retval = ftruncate(fd, size); + close(fd); + return retval; + } else { + return truncate(rpath(ctx, path), size); + } } static int local_rename(FsContext *ctx, const char *oldpath, const char *newpath) { - char *tmp; - int err; - - tmp = qemu_strdup(rpath(ctx, oldpath)); + int err, serrno = 0; - err = rename(tmp, rpath(ctx, newpath)); - if (err == -1) { - int serrno = errno; - qemu_free(tmp); - errno = serrno; + if (ctx->fs_sm == SM_PASSTHROUGH) { + int opfd, npfd; + char *obase, *nbase; + obase = qemu_strdup(oldpath); + nbase = qemu_strdup(newpath); + opfd = get_pfd(ctx, obase); + npfd = get_pfd(ctx, nbase); + err = renameat(opfd, basename(obase), npfd, basename(nbase)); + if (err == -1) { + serrno = errno; + } + qemu_free(obase); + qemu_free(nbase); + close(opfd); + close(npfd); } else { - qemu_free(tmp); + char *tmp; + tmp = qemu_strdup(rpath(ctx, oldpath)); + + err = rename(tmp, rpath(ctx, newpath)); + if (err == -1) { + int serrno = errno; + qemu_free(tmp); + errno = serrno; + } else { + qemu_free(tmp); + } } return err; @@ -480,9 +631,20 @@ static int local_chown(FsContext *fs_ctx, const char *path, FsCred *credp) } static int local_utimensat(FsContext *s, const char *path, - const struct timespec *buf) -{ - return utimensat(AT_FDCWD, rpath(s, path), buf, AT_SYMLINK_NOFOLLOW); + const struct timespec *buf) +{ + if (s->fs_sm == SM_PASSTHROUGH) { + int pfd, retval; + char *base; + base = qemu_strdup(path); + pfd = get_pfd(s, path); + retval = utimensat(pfd, basename(base), buf, + AT_SYMLINK_NOFOLLOW); + close(pfd); + return retval; + } else { + return utimensat(AT_FDCWD, rpath(s, path), buf, AT_SYMLINK_NOFOLLOW); + } } static int local_remove(FsContext *ctx, const char *path) -- 1.7.0.4