Skip to content

Commit

Permalink
9pfs: local: rename: use renameat
Browse files Browse the repository at this point in the history
The local_rename() callback is vulnerable to symlink attacks because it
uses rename() which follows symbolic links in all path elements but the
rightmost one.

This patch simply transforms local_rename() into a wrapper around
local_renameat() which is symlink-attack safe.

This partly fixes CVE-2016-9602.

Signed-off-by: Greg Kurz <groug@kaod.org>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
  • Loading branch information
gkurz committed Feb 28, 2017
1 parent 99f2cf4 commit d2767ed
Showing 1 changed file with 27 additions and 30 deletions.
57 changes: 27 additions & 30 deletions hw/9pfs/9p-local.c
Expand Up @@ -964,36 +964,6 @@ static int local_truncate(FsContext *ctx, V9fsPath *fs_path, off_t size)
return ret;
}

static int local_rename(FsContext *ctx, const char *oldpath,
const char *newpath)
{
int err;
char *buffer, *buffer1;

if (ctx->export_flags & V9FS_SM_MAPPED_FILE) {
err = local_create_mapped_attr_dir(ctx, newpath);
if (err < 0) {
return err;
}
/* rename the .virtfs_metadata files */
buffer = local_mapped_attr_path(ctx, oldpath);
buffer1 = local_mapped_attr_path(ctx, newpath);
err = rename(buffer, buffer1);
g_free(buffer);
g_free(buffer1);
if (err < 0 && errno != ENOENT) {
return err;
}
}

buffer = rpath(ctx, oldpath);
buffer1 = rpath(ctx, newpath);
err = rename(buffer, buffer1);
g_free(buffer);
g_free(buffer1);
return err;
}

static int local_chown(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp)
{
char *buffer;
Expand Down Expand Up @@ -1254,6 +1224,33 @@ static int local_renameat(FsContext *ctx, V9fsPath *olddir,
return ret;
}

static void v9fs_path_init_dirname(V9fsPath *path, const char *str)
{
path->data = g_path_get_dirname(str);
path->size = strlen(path->data) + 1;
}

static int local_rename(FsContext *ctx, const char *oldpath,
const char *newpath)
{
int err;
char *oname = g_path_get_basename(oldpath);
char *nname = g_path_get_basename(newpath);
V9fsPath olddir, newdir;

v9fs_path_init_dirname(&olddir, oldpath);
v9fs_path_init_dirname(&newdir, newpath);

err = local_renameat(ctx, &olddir, oname, &newdir, nname);

v9fs_path_free(&newdir);
v9fs_path_free(&olddir);
g_free(nname);
g_free(oname);

return err;
}

static int local_unlinkat(FsContext *ctx, V9fsPath *dir,
const char *name, int flags)
{
Expand Down

0 comments on commit d2767ed

Please sign in to comment.