From nobody Thu Nov 6 20:01:59 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 1488150802022860.5858859546058; Sun, 26 Feb 2017 15:13:22 -0800 (PST) Received: from localhost ([::1]:48887 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ci80G-0004cc-Oc for importer@patchew.org; Sun, 26 Feb 2017 18:13:20 -0500 Received: from eggs.gnu.org ([2001:4830:134:3::10]:51052) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ci7Z3-0007YD-VE for qemu-devel@nongnu.org; Sun, 26 Feb 2017 17:45:17 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1ci7Z0-0003DM-Rp for qemu-devel@nongnu.org; Sun, 26 Feb 2017 17:45:14 -0500 Received: from mx0b-001b2d01.pphosted.com ([148.163.158.5]:60056 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 1ci7Z0-0003DF-LP for qemu-devel@nongnu.org; Sun, 26 Feb 2017 17:45:10 -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 v1QMi31v131897 for ; Sun, 26 Feb 2017 17:45:10 -0500 Received: from e36.co.us.ibm.com (e36.co.us.ibm.com [32.97.110.154]) by mx0a-001b2d01.pphosted.com with ESMTP id 28uqvkssjv-1 (version=TLSv1.2 cipher=AES256-SHA bits=256 verify=NOT) for ; Sun, 26 Feb 2017 17:45:09 -0500 Received: from localhost by e36.co.us.ibm.com with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted for from ; Sun, 26 Feb 2017 15:45:09 -0700 Received: from d03dlp03.boulder.ibm.com (9.17.202.179) by e36.co.us.ibm.com (192.168.1.136) with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted; Sun, 26 Feb 2017 15:45:05 -0700 Received: from b01cxnp22036.gho.pok.ibm.com (b01cxnp22036.gho.pok.ibm.com [9.57.198.26]) by d03dlp03.boulder.ibm.com (Postfix) with ESMTP id 735FC19D8041; Sun, 26 Feb 2017 15:44:16 -0700 (MST) Received: from b01ledav002.gho.pok.ibm.com (b01ledav002.gho.pok.ibm.com [9.57.199.107]) by b01cxnp22036.gho.pok.ibm.com (8.14.9/8.14.9/NCO v10.0) with ESMTP id v1QMj4Rv55050412; Sun, 26 Feb 2017 22:45:04 GMT Received: from b01ledav002.gho.pok.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id C8B6A12403D; Sun, 26 Feb 2017 17:45:02 -0500 (EST) Received: from [192.168.66.23] (unknown [9.164.183.34]) by b01ledav002.gho.pok.ibm.com (Postfix) with ESMTP id 91E44124035; Sun, 26 Feb 2017 17:45:01 -0500 (EST) From: Greg Kurz To: qemu-devel@nongnu.org Date: Sun, 26 Feb 2017 23:45:02 +0100 In-Reply-To: <148814889214.28146.16915712763478774662.stgit@bahia> References: <148814889214.28146.16915712763478774662.stgit@bahia> User-Agent: StGit/0.17.1-20-gc0b1b-dirty MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-TM-AS-GCONF: 00 X-Content-Scanned: Fidelis XPS MAILER x-cbid: 17022622-0020-0000-0000-00000B7C3633 X-IBM-SpamModules-Scores: X-IBM-SpamModules-Versions: BY=3.00006689; HX=3.00000240; KW=3.00000007; PH=3.00000004; SC=3.00000204; SDB=6.00827425; UDB=6.00405424; IPR=6.00604941; BA=6.00005172; NDR=6.00000001; ZLA=6.00000005; ZF=6.00000009; ZB=6.00000000; ZP=6.00000000; ZH=6.00000000; ZU=6.00000002; MB=3.00014449; XFM=3.00000011; UTC=2017-02-26 22:45:07 X-IBM-AV-DETECTION: SAVI=unused REMOTE=unused XFE=unused x-cbparentid: 17022622-0021-0000-0000-00005A725FE4 Message-Id: <148814910201.28146.7217483545833853712.stgit@bahia> X-Proofpoint-Virus-Version: vendor=fsecure engine=2.50.10432:, , definitions=2017-02-26_11:, , signatures=0 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 spamscore=0 suspectscore=3 malwarescore=0 phishscore=0 adultscore=0 bulkscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.0.1-1612050000 definitions=main-1702260234 X-detected-operating-system: by eggs.gnu.org: GNU/Linux 3.x [generic] [fuzzy] X-Received-From: 148.163.158.5 Subject: [Qemu-devel] [PATCH v2 26/28] 9pfs: local: mkdir: 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: Jann Horn , Prasad J Pandit , Greg Kurz , "Aneesh Kumar K.V" , Stefan Hajnoczi Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail: RSF_0 Z_629925259 SPT_0 The local_mkdir() callback is vulnerable to symlink attacks because it calls: (1) mkdir() which follows symbolic links for all path elements but the rightmost one (2) local_set_xattr()->setxattr() which follows symbolic links for all path elements (3) 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 (4) local_post_create_passthrough() which calls in turn lchown() and chmod(), both functions also following symbolic links This patch converts local_mkdir() to rely on opendir_nofollow() and mkdirat() to fix (1), as well as local_set_xattrat(), local_set_mapped_file_attrat() and local_set_cred_passthrough() to fix (2), (3) and (4) respectively. The mapped and mapped-file security modes are supposed to be identical, except for the place where credentials and file modes are stored. While here, we also make that explicit by sharing the call to mkdirat(). This partly fixes CVE-2016-9602. Signed-off-by: Greg Kurz Reviewed-by: Stefan Hajnoczi --- hw/9pfs/9p-local.c | 55 +++++++++++++++++++-----------------------------= ---- 1 file changed, 20 insertions(+), 35 deletions(-) diff --git a/hw/9pfs/9p-local.c b/hw/9pfs/9p-local.c index 4766175dda72..9b28bb530ae9 100644 --- a/hw/9pfs/9p-local.c +++ b/hw/9pfs/9p-local.c @@ -799,62 +799,47 @@ out: static int local_mkdir(FsContext *fs_ctx, V9fsPath *dir_path, const char *name, FsCred *credp) { - char *path; int err =3D -1; - int serrno =3D 0; - V9fsString fullname; - char *buffer =3D NULL; + int dirfd; =20 - v9fs_string_init(&fullname); - v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name); - path =3D fullname.data; + dirfd =3D local_opendir_nofollow(fs_ctx, dir_path->data); + if (dirfd =3D=3D -1) { + return -1; + } =20 - /* Determine the security model */ - if (fs_ctx->export_flags & V9FS_SM_MAPPED) { - buffer =3D rpath(fs_ctx, path); - err =3D mkdir(buffer, SM_LOCAL_DIR_MODE_BITS); + if (fs_ctx->export_flags & V9FS_SM_MAPPED || + fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { + err =3D mkdirat(dirfd, name, SM_LOCAL_DIR_MODE_BITS); if (err =3D=3D -1) { goto out; } - credp->fc_mode =3D credp->fc_mode|S_IFDIR; - err =3D local_set_xattr(buffer, credp); - if (err =3D=3D -1) { - serrno =3D errno; - goto err_end; - } - } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { - buffer =3D rpath(fs_ctx, path); - err =3D mkdir(buffer, SM_LOCAL_DIR_MODE_BITS); - if (err =3D=3D -1) { - goto out; + credp->fc_mode =3D credp->fc_mode | S_IFDIR; + + if (fs_ctx->export_flags & V9FS_SM_MAPPED) { + err =3D local_set_xattrat(dirfd, name, credp); + } else { + err =3D local_set_mapped_file_attrat(dirfd, name, credp); } - credp->fc_mode =3D credp->fc_mode|S_IFDIR; - err =3D local_set_mapped_file_attr(fs_ctx, path, credp); if (err =3D=3D -1) { - serrno =3D errno; goto err_end; } - } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) || - (fs_ctx->export_flags & V9FS_SM_NONE)) { - buffer =3D rpath(fs_ctx, path); - err =3D mkdir(buffer, credp->fc_mode); + } else if (fs_ctx->export_flags & V9FS_SM_PASSTHROUGH || + fs_ctx->export_flags & V9FS_SM_NONE) { + err =3D mkdirat(dirfd, name, credp->fc_mode); if (err =3D=3D -1) { goto out; } - err =3D local_post_create_passthrough(fs_ctx, path, credp); + err =3D local_set_cred_passthrough(fs_ctx, dirfd, name, credp); if (err =3D=3D -1) { - serrno =3D errno; goto err_end; } } goto out; =20 err_end: - remove(buffer); - errno =3D serrno; + unlinkat_preserve_errno(dirfd, name, AT_REMOVEDIR); out: - g_free(buffer); - v9fs_string_free(&fullname); + close_preserve_errno(dirfd); return err; } =20