From nobody Sun Feb 8 21:28:25 2026 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 1490052816785223.03427328708415; Mon, 20 Mar 2017 16:33:36 -0700 (PDT) Received: from localhost ([::1]:35600 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1cq6nv-0004aF-Bm for importer@patchew.org; Mon, 20 Mar 2017 19:33:35 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:41331) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1cq6Qs-0001rV-CI for qemu-devel@nongnu.org; Mon, 20 Mar 2017 19:09:55 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1cq6Qm-00054j-Ct for qemu-devel@nongnu.org; Mon, 20 Mar 2017 19:09:46 -0400 Received: from mx0a-001b2d01.pphosted.com ([148.163.156.1]:44695) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1cq6Qm-00053j-3F for qemu-devel@nongnu.org; Mon, 20 Mar 2017 19:09:40 -0400 Received: from pps.filterd (m0098409.ppops.net [127.0.0.1]) by mx0a-001b2d01.pphosted.com (8.16.0.20/8.16.0.20) with SMTP id v2KN8XZF109648 for ; Mon, 20 Mar 2017 19:09:39 -0400 Received: from e18.ny.us.ibm.com (e18.ny.us.ibm.com [129.33.205.208]) by mx0a-001b2d01.pphosted.com with ESMTP id 29ak40pc5w-1 (version=TLSv1.2 cipher=AES256-SHA bits=256 verify=NOT) for ; Mon, 20 Mar 2017 19:09:38 -0400 Received: from localhost by e18.ny.us.ibm.com with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted for from ; Mon, 20 Mar 2017 19:09:37 -0400 Received: from b01cxnp23033.gho.pok.ibm.com (9.57.198.28) by e18.ny.us.ibm.com (146.89.104.205) with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted; Mon, 20 Mar 2017 19:09:35 -0400 Received: from b01ledav002.gho.pok.ibm.com (b01ledav002.gho.pok.ibm.com [9.57.199.107]) by b01cxnp23033.gho.pok.ibm.com (8.14.9/8.14.9/NCO v10.0) with ESMTP id v2KN9aLA37683430; Mon, 20 Mar 2017 23:09:36 GMT Received: from b01ledav002.gho.pok.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id 81399124035; Mon, 20 Mar 2017 19:09:30 -0400 (EDT) Received: from localhost (unknown [9.53.92.194]) by b01ledav002.gho.pok.ibm.com (Postfix) with ESMTP id 6625E12403D; Mon, 20 Mar 2017 19:09:30 -0400 (EDT) From: Michael Roth To: qemu-devel@nongnu.org Date: Mon, 20 Mar 2017 18:07:28 -0500 X-Mailer: git-send-email 2.7.4 In-Reply-To: <1490051325-3770-1-git-send-email-mdroth@linux.vnet.ibm.com> References: <1490051325-3770-1-git-send-email-mdroth@linux.vnet.ibm.com> X-TM-AS-GCONF: 00 x-cbid: 17032023-0044-0000-0000-000002E066E1 X-IBM-SpamModules-Scores: X-IBM-SpamModules-Versions: BY=3.00006819; HX=3.00000240; KW=3.00000007; PH=3.00000004; SC=3.00000206; SDB=6.00836551; UDB=6.00411161; IPR=6.00614321; BA=6.00005224; NDR=6.00000001; ZLA=6.00000005; ZF=6.00000009; ZB=6.00000000; ZP=6.00000000; ZH=6.00000000; ZU=6.00000002; MB=3.00014732; XFM=3.00000013; UTC=2017-03-20 23:09:35 X-IBM-AV-DETECTION: SAVI=unused REMOTE=unused XFE=unused x-cbparentid: 17032023-0045-0000-0000-0000070E678A Message-Id: <1490051325-3770-5-git-send-email-mdroth@linux.vnet.ibm.com> X-Proofpoint-Virus-Version: vendor=fsecure engine=2.50.10432:, , definitions=2017-03-20_16:, , 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-1702020001 definitions=main-1703200196 X-detected-operating-system: by eggs.gnu.org: GNU/Linux 3.x [generic] [fuzzy] X-Received-From: 148.163.156.1 Subject: [Qemu-devel] [PATCH 04/81] 9pfs: introduce relative_openat_nofollow() helper 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: Greg Kurz , qemu-stable@nongnu.org, 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" From: Greg Kurz When using the passthrough security mode, symbolic links created by the guest are actual symbolic links on the host file system. Since the resolution of symbolic links during path walk is supposed to occur on the client side. The server should hence never receive any path pointing to an actual symbolic link. This isn't guaranteed by the protocol though, and malicious code in the guest can trick the server to issue various syscalls on paths whose one or more elements are symbolic links. In the case of the "local" backend using the "passthrough" or "none" security modes, the guest can directly create symbolic links to arbitrary locations on the host (as per spec). The "mapped-xattr" and "mapped-file" security modes are also affected to a lesser extent as they require some help from an external entity to create actual symbolic links on the host, i.e. another guest using "passthrough" mode for example. The current code hence relies on O_NOFOLLOW and "l*()" variants of system calls. Unfortunately, this only applies to the rightmost path component. A guest could maliciously replace any component in a trusted path with a symbolic link. This could allow any guest to escape a virtfs shared folder. This patch introduces a variant of the openat() syscall that successively opens each path element with O_NOFOLLOW. When passing a file descriptor pointing to a trusted directory, one is guaranteed to be returned a file descriptor pointing to a path which is beneath the trusted directory. This will be used by subsequent patches to implement symlink-safe path walk for any access to the backend. Symbolic links aren't the only threats actually: a malicious guest could change a path element to point to other types of file with undesirable effects: - a named pipe or any other thing that would cause openat() to block - a terminal device which would become QEMU's controlling terminal These issues can be addressed with O_NONBLOCK and O_NOCTTY. Two helpers are introduced: one to open intermediate path elements and one to open the rightmost path element. Suggested-by: Jann Horn Signed-off-by: Greg Kurz Reviewed-by: Stefan Hajnoczi (renamed openat_nofollow() to relative_openat_nofollow(), assert path is relative and doesn't contain '//', fixed side-effect in assert, Greg Kurz) Signed-off-by: Greg Kurz (cherry picked from commit 6482a961636d66cc10928dde5d4d908206e5f65a) Signed-off-by: Greg Kurz Signed-off-by: Michael Roth --- hw/9pfs/9p-util.c | 57 +++++++++++++++++++++++++++++++++++++++++++++++= ++++ hw/9pfs/9p-util.h | 50 ++++++++++++++++++++++++++++++++++++++++++++ hw/9pfs/Makefile.objs | 2 +- 3 files changed, 108 insertions(+), 1 deletion(-) create mode 100644 hw/9pfs/9p-util.c create mode 100644 hw/9pfs/9p-util.h diff --git a/hw/9pfs/9p-util.c b/hw/9pfs/9p-util.c new file mode 100644 index 0000000..54134b0 --- /dev/null +++ b/hw/9pfs/9p-util.c @@ -0,0 +1,57 @@ +/* + * 9p utilities + * + * Copyright IBM, Corp. 2017 + * + * Authors: + * Greg Kurz + * + * This work is licensed under the terms of the GNU GPL, version 2 or late= r. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "9p-util.h" + +int relative_openat_nofollow(int dirfd, const char *path, int flags, + mode_t mode) +{ + int fd; + + fd =3D dup(dirfd); + if (fd =3D=3D -1) { + return -1; + } + + while (*path) { + const char *c; + int next_fd; + char *head; + + /* Only relative paths without consecutive slashes */ + assert(path[0] !=3D '/'); + + head =3D g_strdup(path); + c =3D strchr(path, '/'); + if (c) { + head[c - path] =3D 0; + next_fd =3D openat_dir(fd, head); + } else { + next_fd =3D openat_file(fd, head, flags, mode); + } + g_free(head); + if (next_fd =3D=3D -1) { + close_preserve_errno(fd); + return -1; + } + close(fd); + fd =3D next_fd; + + if (!c) { + break; + } + path =3D c + 1; + } + + return fd; +} diff --git a/hw/9pfs/9p-util.h b/hw/9pfs/9p-util.h new file mode 100644 index 0000000..e80b5a5 --- /dev/null +++ b/hw/9pfs/9p-util.h @@ -0,0 +1,50 @@ +/* + * 9p utilities + * + * Copyright IBM, Corp. 2017 + * + * Authors: + * Greg Kurz + * + * This work is licensed under the terms of the GNU GPL, version 2 or late= r. + * See the COPYING file in the top-level directory. + */ + +#ifndef QEMU_9P_UTIL_H +#define QEMU_9P_UTIL_H + +static inline void close_preserve_errno(int fd) +{ + int serrno =3D errno; + close(fd); + errno =3D serrno; +} + +static inline int openat_dir(int dirfd, const char *name) +{ + return openat(dirfd, name, O_DIRECTORY | O_RDONLY | O_PATH); +} + +static inline int openat_file(int dirfd, const char *name, int flags, + mode_t mode) +{ + int fd, serrno, ret; + + fd =3D openat(dirfd, name, flags | O_NOFOLLOW | O_NOCTTY | O_NONBLOCK, + mode); + if (fd =3D=3D -1) { + return -1; + } + + serrno =3D errno; + /* O_NONBLOCK was only needed to open the file. Let's drop it. */ + ret =3D fcntl(fd, F_SETFL, flags); + assert(!ret); + errno =3D serrno; + return fd; +} + +int relative_openat_nofollow(int dirfd, const char *path, int flags, + mode_t mode); + +#endif diff --git a/hw/9pfs/Makefile.objs b/hw/9pfs/Makefile.objs index da0ae0c..32197e6 100644 --- a/hw/9pfs/Makefile.objs +++ b/hw/9pfs/Makefile.objs @@ -1,4 +1,4 @@ -common-obj-y =3D 9p.o +common-obj-y =3D 9p.o 9p-util.o common-obj-y +=3D 9p-local.o 9p-xattr.o common-obj-y +=3D 9p-xattr-user.o 9p-posix-acl.o common-obj-y +=3D coth.o cofs.o codir.o cofile.o --=20 2.7.4