Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
SUSE:SLE-12-SP2:GA
qemu-linux-user.7444
0234-9pfs-local-link-don-t-follow-symlin.patch
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File 0234-9pfs-local-link-don-t-follow-symlin.patch of Package qemu-linux-user.7444
From d98d929e34ce82525387b2607658c05b9bb26226 Mon Sep 17 00:00:00 2001 From: Greg Kurz <groug@kaod.org> Date: Sun, 26 Feb 2017 23:44:20 +0100 Subject: [PATCH] 9pfs: local: link: don't follow symlinks The local_link() callback is vulnerable to symlink attacks because it calls: (1) link() which follows symbolic links for all path elements but the rightmost one (2) local_create_mapped_attr_dir()->mkdir() which follows symbolic links for all path elements but the rightmost one This patch converts local_link() to rely on opendir_nofollow() and linkat() to fix (1), mkdirat() to fix (2). This partly fixes CVE-2016-9602. Signed-off-by: Greg Kurz <groug@kaod.org> Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> (cherry picked from commit ad0b46e6ac769b187cb4dcf0065675ef8a198a5e) [BR: Fix and/or infrastructure for BSC#1020427 CVE-2016-9602] Signed-off-by: Bruce Rogers <brogers@suse.com> --- hw/9pfs/9p-local.c | 84 +++++++++++++++++++++++++++++++++++------------------- 1 file changed, 55 insertions(+), 29 deletions(-) diff --git a/hw/9pfs/9p-local.c b/hw/9pfs/9p-local.c index e25a576aff..ccc947c845 100644 --- a/hw/9pfs/9p-local.c +++ b/hw/9pfs/9p-local.c @@ -72,6 +72,13 @@ static void renameat_preserve_errno(int odirfd, const char *opath, int ndirfd, errno = serrno; } +static void unlinkat_preserve_errno(int dirfd, const char *path, int flags) +{ + int serrno = errno; + unlinkat(dirfd, path, flags); + errno = serrno; +} + #define VIRTFS_META_DIR ".virtfs_metadata" static char *local_mapped_attr_path(FsContext *ctx, const char *path) @@ -913,49 +920,68 @@ out: static int local_link(FsContext *ctx, V9fsPath *oldpath, V9fsPath *dirpath, const char *name) { - int ret; - V9fsString newpath; - char *buffer, *buffer1; - int serrno; + char *odirpath = g_path_get_dirname(oldpath->data); + char *oname = g_path_get_basename(oldpath->data); + int ret = -1; + int odirfd, ndirfd; - v9fs_string_init(&newpath); - v9fs_string_sprintf(&newpath, "%s/%s", dirpath->data, name); + odirfd = local_opendir_nofollow(ctx, odirpath); + if (odirfd == -1) { + goto out; + } - buffer = rpath(ctx, oldpath->data); - buffer1 = rpath(ctx, newpath.data); - ret = link(buffer, buffer1); - g_free(buffer); - if (ret < 0) { + ndirfd = local_opendir_nofollow(ctx, dirpath->data); + if (ndirfd == -1) { + close_preserve_errno(odirfd); goto out; } + ret = linkat(odirfd, oname, ndirfd, name, 0); + if (ret < 0) { + goto out_close; + } + /* now link the virtfs_metadata files */ if (ctx->export_flags & V9FS_SM_MAPPED_FILE) { - char *vbuffer, *vbuffer1; + int omap_dirfd, nmap_dirfd; - /* Link the .virtfs_metadata files. Create the metada directory */ - ret = local_create_mapped_attr_dir(ctx, newpath.data); - if (ret < 0) { - goto err_out; + ret = mkdirat(ndirfd, VIRTFS_META_DIR, 0700); + if (ret < 0 && errno != EEXIST) { + goto err_undo_link; } - vbuffer = local_mapped_attr_path(ctx, oldpath->data); - vbuffer1 = local_mapped_attr_path(ctx, newpath.data); - ret = link(vbuffer, vbuffer1); - g_free(vbuffer); - g_free(vbuffer1); + + omap_dirfd = openat_dir(odirfd, VIRTFS_META_DIR); + if (omap_dirfd == -1) { + goto err; + } + + nmap_dirfd = openat_dir(ndirfd, VIRTFS_META_DIR); + if (nmap_dirfd == -1) { + close_preserve_errno(omap_dirfd); + goto err; + } + + ret = linkat(omap_dirfd, oname, nmap_dirfd, name, 0); + close_preserve_errno(nmap_dirfd); + close_preserve_errno(omap_dirfd); if (ret < 0 && errno != ENOENT) { - goto err_out; + goto err_undo_link; } + + ret = 0; } - goto out; + goto out_close; -err_out: - serrno = errno; - remove(buffer1); - errno = serrno; +err: + ret = -1; +err_undo_link: + unlinkat_preserve_errno(ndirfd, name, 0); +out_close: + close_preserve_errno(ndirfd); + close_preserve_errno(odirfd); out: - g_free(buffer1); - v9fs_string_free(&newpath); + g_free(oname); + g_free(odirpath); return ret; }
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor