From nobody Thu Nov 6 18:51:50 2025 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) client-ip=208.118.235.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zoho.com; spf=pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; Return-Path: Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) by mx.zohomail.com with SMTPS id 1488237424334158.90106552154236; Mon, 27 Feb 2017 15:17:04 -0800 (PST) Received: from localhost ([::1]:57507 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ciUXO-0005RE-W4 for importer@patchew.org; Mon, 27 Feb 2017 18:17:03 -0500 Received: from eggs.gnu.org ([2001:4830:134:3::10]:46956) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ciUHo-0000be-8h for qemu-devel@nongnu.org; Mon, 27 Feb 2017 18:00:59 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1ciUHm-00041s-Kh for qemu-devel@nongnu.org; Mon, 27 Feb 2017 18:00:56 -0500 Received: from mx0b-001b2d01.pphosted.com ([148.163.158.5]:46106 helo=mx0a-001b2d01.pphosted.com) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1ciUHm-00041F-Eb for qemu-devel@nongnu.org; Mon, 27 Feb 2017 18:00:54 -0500 Received: from pps.filterd (m0098421.ppops.net [127.0.0.1]) by mx0a-001b2d01.pphosted.com (8.16.0.20/8.16.0.20) with SMTP id v1RMrZFc126019 for ; Mon, 27 Feb 2017 18:00:53 -0500 Received: from e06smtp07.uk.ibm.com (e06smtp07.uk.ibm.com [195.75.94.103]) by mx0a-001b2d01.pphosted.com with ESMTP id 28vs23tg8j-1 (version=TLSv1.2 cipher=AES256-SHA bits=256 verify=NOT) for ; Mon, 27 Feb 2017 18:00:53 -0500 Received: from localhost by e06smtp07.uk.ibm.com with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted for from ; Mon, 27 Feb 2017 23:00:51 -0000 Received: from d06dlp02.portsmouth.uk.ibm.com (9.149.20.14) by e06smtp07.uk.ibm.com (192.168.101.137) with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted; Mon, 27 Feb 2017 23:00:49 -0000 Received: from b06cxnps4074.portsmouth.uk.ibm.com (d06relay11.portsmouth.uk.ibm.com [9.149.109.196]) by d06dlp02.portsmouth.uk.ibm.com (Postfix) with ESMTP id 3EAEC2190019; Mon, 27 Feb 2017 22:59:50 +0000 (GMT) Received: from d06av26.portsmouth.uk.ibm.com (d06av26.portsmouth.uk.ibm.com [9.149.105.62]) by b06cxnps4074.portsmouth.uk.ibm.com (8.14.9/8.14.9/NCO v10.0) with ESMTP id v1RN0mQ566388030; Mon, 27 Feb 2017 23:00:48 GMT Received: from d06av26.portsmouth.uk.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id 7A5D3AE05F; Mon, 27 Feb 2017 21:58:38 +0000 (GMT) Received: from d06av26.portsmouth.uk.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id 69AA9AE04D; Mon, 27 Feb 2017 21:58:38 +0000 (GMT) Received: from smtp.lab.toulouse-stg.fr.ibm.com (unknown [9.101.4.1]) by d06av26.portsmouth.uk.ibm.com (Postfix) with ESMTP; Mon, 27 Feb 2017 21:58:38 +0000 (GMT) Received: from bahia.lan (icon-9-164-183-34.megacenter.de.ibm.com [9.164.183.34]) by smtp.lab.toulouse-stg.fr.ibm.com (Postfix) with ESMTP id EF646220711; Tue, 28 Feb 2017 00:00:47 +0100 (CET) From: Greg Kurz To: qemu-devel@nongnu.org Date: Tue, 28 Feb 2017 00:00:10 +0100 X-Mailer: git-send-email 2.7.4 In-Reply-To: <1488236421-30983-1-git-send-email-groug@kaod.org> References: <1488236421-30983-1-git-send-email-groug@kaod.org> X-TM-AS-GCONF: 00 X-Content-Scanned: Fidelis XPS MAILER x-cbid: 17022723-0028-0000-0000-000002B2F605 X-IBM-AV-DETECTION: SAVI=unused REMOTE=unused XFE=unused x-cbparentid: 17022723-0029-0000-0000-000022860012 Message-Id: <1488236421-30983-21-git-send-email-groug@kaod.org> X-Proofpoint-Virus-Version: vendor=fsecure engine=2.50.10432:, , definitions=2017-02-27_17:, , signatures=0 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 spamscore=0 suspectscore=13 malwarescore=0 phishscore=0 adultscore=0 bulkscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.0.1-1612050000 definitions=main-1702270213 X-detected-operating-system: by eggs.gnu.org: GNU/Linux 3.x [generic] [fuzzy] X-Received-From: 148.163.158.5 Subject: [Qemu-devel] [PULL 20/31] 9pfs: local: lstat: don't follow symlinks X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Peter Maydell , "Aneesh Kumar K.V" , Greg Kurz Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail: RSF_0 Z_629925259 SPT_0 Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" The local_lstat() callback is vulnerable to symlink attacks because it calls: (1) lstat() which follows symbolic links in all path elements but the rightmost one (2) getxattr() which follows symbolic links in all path elements (3) local_mapped_file_attr()->local_fopen()->openat(O_NOFOLLOW) which follows symbolic links in all path elements but the rightmost one This patch converts local_lstat() to rely on opendir_nofollow() and fstatat(AT_SYMLINK_NOFOLLOW) to fix (1), fgetxattrat_nofollow() to fix (2). A new local_fopenat() helper is introduced as a replacement to local_fopen() to fix (3). No effort is made to factor out code because local_fopen() will be dropped when all users have been converted to call local_fopenat(). This partly fixes CVE-2016-9602. Signed-off-by: Greg Kurz Reviewed-by: Stefan Hajnoczi --- hw/9pfs/9p-local.c | 78 ++++++++++++++++++++++++++++++++++++++++++--------= ---- 1 file changed, 61 insertions(+), 17 deletions(-) diff --git a/hw/9pfs/9p-local.c b/hw/9pfs/9p-local.c index add1b2e83dbe..ee3c1bd0a2be 100644 --- a/hw/9pfs/9p-local.c +++ b/hw/9pfs/9p-local.c @@ -111,17 +111,49 @@ static FILE *local_fopen(const char *path, const char= *mode) return fp; } =20 +static FILE *local_fopenat(int dirfd, const char *name, const char *mode) +{ + int fd, o_mode =3D 0; + FILE *fp; + int flags; + /* + * only supports two modes + */ + if (mode[0] =3D=3D 'r') { + flags =3D O_RDONLY; + } else if (mode[0] =3D=3D 'w') { + flags =3D O_WRONLY | O_TRUNC | O_CREAT; + o_mode =3D S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWO= TH; + } else { + return NULL; + } + fd =3D openat_file(dirfd, name, flags, o_mode); + if (fd =3D=3D -1) { + return NULL; + } + fp =3D fdopen(fd, mode); + if (!fp) { + close(fd); + } + return fp; +} + #define ATTR_MAX 100 -static void local_mapped_file_attr(FsContext *ctx, const char *path, +static void local_mapped_file_attr(int dirfd, const char *name, struct stat *stbuf) { FILE *fp; char buf[ATTR_MAX]; - char *attr_path; + int map_dirfd; =20 - attr_path =3D local_mapped_attr_path(ctx, path); - fp =3D local_fopen(attr_path, "r"); - g_free(attr_path); + map_dirfd =3D openat(dirfd, VIRTFS_META_DIR, + O_RDONLY | O_DIRECTORY | O_NOFOLLOW); + if (map_dirfd =3D=3D -1) { + return; + } + + fp =3D local_fopenat(map_dirfd, name, "r"); + close_preserve_errno(map_dirfd); if (!fp) { return; } @@ -143,12 +175,17 @@ static void local_mapped_file_attr(FsContext *ctx, co= nst char *path, =20 static int local_lstat(FsContext *fs_ctx, V9fsPath *fs_path, struct stat *= stbuf) { - int err; - char *buffer; - char *path =3D fs_path->data; + int err =3D -1; + char *dirpath =3D g_path_get_dirname(fs_path->data); + char *name =3D g_path_get_basename(fs_path->data); + int dirfd; =20 - buffer =3D rpath(fs_ctx, path); - err =3D lstat(buffer, stbuf); + dirfd =3D local_opendir_nofollow(fs_ctx, dirpath); + if (dirfd =3D=3D -1) { + goto out; + } + + err =3D fstatat(dirfd, name, stbuf, AT_SYMLINK_NOFOLLOW); if (err) { goto err_out; } @@ -158,25 +195,32 @@ static int local_lstat(FsContext *fs_ctx, V9fsPath *f= s_path, struct stat *stbuf) gid_t tmp_gid; mode_t tmp_mode; dev_t tmp_dev; - if (getxattr(buffer, "user.virtfs.uid", &tmp_uid, sizeof(uid_t)) >= 0) { + + if (fgetxattrat_nofollow(dirfd, name, "user.virtfs.uid", &tmp_uid, + sizeof(uid_t)) > 0) { stbuf->st_uid =3D le32_to_cpu(tmp_uid); } - if (getxattr(buffer, "user.virtfs.gid", &tmp_gid, sizeof(gid_t)) >= 0) { + if (fgetxattrat_nofollow(dirfd, name, "user.virtfs.gid", &tmp_gid, + sizeof(gid_t)) > 0) { stbuf->st_gid =3D le32_to_cpu(tmp_gid); } - if (getxattr(buffer, "user.virtfs.mode", - &tmp_mode, sizeof(mode_t)) > 0) { + if (fgetxattrat_nofollow(dirfd, name, "user.virtfs.mode", &tmp_mod= e, + sizeof(mode_t)) > 0) { stbuf->st_mode =3D le32_to_cpu(tmp_mode); } - if (getxattr(buffer, "user.virtfs.rdev", &tmp_dev, sizeof(dev_t)) = > 0) { + if (fgetxattrat_nofollow(dirfd, name, "user.virtfs.rdev", &tmp_dev, + sizeof(dev_t)) > 0) { stbuf->st_rdev =3D le64_to_cpu(tmp_dev); } } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { - local_mapped_file_attr(fs_ctx, path, stbuf); + local_mapped_file_attr(dirfd, name, stbuf); } =20 err_out: - g_free(buffer); + close_preserve_errno(dirfd); +out: + g_free(name); + g_free(dirpath); return err; } =20 --=20 2.7.4