提交 9f4ba82b 编写于 作者: G Greg Kurz 提交者: Michael Roth

9pfs: local: symlink: don't follow symlinks

The local_symlink() callback is vulnerable to symlink attacks because it
calls:

(1) symlink() which follows symbolic links for all path elements but the
    rightmost one
(2) open(O_NOFOLLOW) which follows symbolic links for all path elements but
    the rightmost one
(3) local_set_xattr()->setxattr() which follows symbolic links for all
    path elements
(4) local_set_mapped_file_attr() which calls in turn local_fopen() and
    mkdir(), both functions following symbolic links for all path
    elements but the rightmost one

This patch converts local_symlink() to rely on opendir_nofollow() and
symlinkat() to fix (1), openat(O_NOFOLLOW) to fix (2), as well as
local_set_xattrat() and local_set_mapped_file_attrat() to fix (3) and
(4) respectively.

This partly fixes CVE-2016-9602.
Signed-off-by: NGreg Kurz <groug@kaod.org>
Reviewed-by: NStefan Hajnoczi <stefanha@redhat.com>
(cherry picked from commit 38771613)
Signed-off-by: NGreg Kurz <gkurz@linux.vnet.ibm.com>
Signed-off-by: NMichael Roth <mdroth@linux.vnet.ibm.com>
上级 62d1dbbf
...@@ -979,23 +979,22 @@ static int local_symlink(FsContext *fs_ctx, const char *oldpath, ...@@ -979,23 +979,22 @@ static int local_symlink(FsContext *fs_ctx, const char *oldpath,
V9fsPath *dir_path, const char *name, FsCred *credp) V9fsPath *dir_path, const char *name, FsCred *credp)
{ {
int err = -1; int err = -1;
int serrno = 0; int dirfd;
char *newpath;
V9fsString fullname;
char *buffer = NULL;
v9fs_string_init(&fullname); dirfd = local_opendir_nofollow(fs_ctx, dir_path->data);
v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name); if (dirfd == -1) {
newpath = fullname.data; return -1;
}
/* Determine the security model */ /* Determine the security model */
if (fs_ctx->export_flags & V9FS_SM_MAPPED) { if (fs_ctx->export_flags & V9FS_SM_MAPPED ||
fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
int fd; int fd;
ssize_t oldpath_size, write_size; ssize_t oldpath_size, write_size;
buffer = rpath(fs_ctx, newpath);
fd = open(buffer, O_CREAT|O_EXCL|O_RDWR|O_NOFOLLOW, SM_LOCAL_MODE_BITS); fd = openat_file(dirfd, name, O_CREAT | O_EXCL | O_RDWR,
SM_LOCAL_MODE_BITS);
if (fd == -1) { if (fd == -1) {
err = fd;
goto out; goto out;
} }
/* Write the oldpath (target) to the file. */ /* Write the oldpath (target) to the file. */
...@@ -1003,78 +1002,48 @@ static int local_symlink(FsContext *fs_ctx, const char *oldpath, ...@@ -1003,78 +1002,48 @@ static int local_symlink(FsContext *fs_ctx, const char *oldpath,
do { do {
write_size = write(fd, (void *)oldpath, oldpath_size); write_size = write(fd, (void *)oldpath, oldpath_size);
} while (write_size == -1 && errno == EINTR); } while (write_size == -1 && errno == EINTR);
close_preserve_errno(fd);
if (write_size != oldpath_size) { if (write_size != oldpath_size) {
serrno = errno;
close(fd);
err = -1;
goto err_end; goto err_end;
} }
close(fd);
/* Set cleint credentials in symlink's xattr */ /* Set cleint credentials in symlink's xattr */
credp->fc_mode = credp->fc_mode|S_IFLNK; credp->fc_mode = credp->fc_mode | S_IFLNK;
err = local_set_xattr(buffer, credp);
if (err == -1) {
serrno = errno;
goto err_end;
}
} else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
int fd;
ssize_t oldpath_size, write_size;
buffer = rpath(fs_ctx, newpath);
fd = open(buffer, O_CREAT|O_EXCL|O_RDWR|O_NOFOLLOW, SM_LOCAL_MODE_BITS);
if (fd == -1) {
err = fd;
goto out;
}
/* Write the oldpath (target) to the file. */
oldpath_size = strlen(oldpath);
do {
write_size = write(fd, (void *)oldpath, oldpath_size);
} while (write_size == -1 && errno == EINTR);
if (write_size != oldpath_size) { if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
serrno = errno; err = local_set_xattrat(dirfd, name, credp);
close(fd); } else {
err = -1; err = local_set_mapped_file_attrat(dirfd, name, credp);
goto err_end;
} }
close(fd);
/* Set cleint credentials in symlink's xattr */
credp->fc_mode = credp->fc_mode|S_IFLNK;
err = local_set_mapped_file_attr(fs_ctx, newpath, credp);
if (err == -1) { if (err == -1) {
serrno = errno;
goto err_end; goto err_end;
} }
} else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) || } else if (fs_ctx->export_flags & V9FS_SM_PASSTHROUGH ||
(fs_ctx->export_flags & V9FS_SM_NONE)) { fs_ctx->export_flags & V9FS_SM_NONE) {
buffer = rpath(fs_ctx, newpath); err = symlinkat(oldpath, dirfd, name);
err = symlink(oldpath, buffer);
if (err) { if (err) {
goto out; goto out;
} }
err = lchown(buffer, credp->fc_uid, credp->fc_gid); err = fchownat(dirfd, name, credp->fc_uid, credp->fc_gid,
AT_SYMLINK_NOFOLLOW);
if (err == -1) { if (err == -1) {
/* /*
* If we fail to change ownership and if we are * If we fail to change ownership and if we are
* using security model none. Ignore the error * using security model none. Ignore the error
*/ */
if ((fs_ctx->export_flags & V9FS_SEC_MASK) != V9FS_SM_NONE) { if ((fs_ctx->export_flags & V9FS_SEC_MASK) != V9FS_SM_NONE) {
serrno = errno;
goto err_end; goto err_end;
} else } else {
err = 0; err = 0;
}
} }
} }
goto out; goto out;
err_end: err_end:
remove(buffer); unlinkat_preserve_errno(dirfd, name, 0);
errno = serrno;
out: out:
g_free(buffer); close_preserve_errno(dirfd);
v9fs_string_free(&fullname);
return err; return err;
} }
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册