From nobody Sun Feb 8 14:56:10 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=fail; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=redhat.com Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1634226210573498.0250763065686; Thu, 14 Oct 2021 08:43:30 -0700 (PDT) Received: from localhost ([::1]:51634 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1mb2tE-00067n-Dp for importer@patchew.org; Thu, 14 Oct 2021 11:43:29 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:48690) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1mb2hx-0005Qk-UE for qemu-devel@nongnu.org; Thu, 14 Oct 2021 11:31:49 -0400 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]:33382) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1mb2ht-0003Tt-GS for qemu-devel@nongnu.org; Thu, 14 Oct 2021 11:31:48 -0400 Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-566-cz2saie2PcCMrxXQqjiSJw-1; Thu, 14 Oct 2021 11:31:39 -0400 Received: from smtp.corp.redhat.com (int-mx05.intmail.prod.int.phx2.redhat.com [10.5.11.15]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 94B431018720; Thu, 14 Oct 2021 15:31:38 +0000 (UTC) Received: from horse.redhat.com (unknown [10.22.16.190]) by smtp.corp.redhat.com (Postfix) with ESMTP id A573C5F4EA; Thu, 14 Oct 2021 15:31:37 +0000 (UTC) Received: by horse.redhat.com (Postfix, from userid 10451) id 40782220C99; Thu, 14 Oct 2021 11:31:37 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1634225503; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=qbSG4H6F1x3IG/KzKA2fJiDn0CgHWoBPsFV8P9Z6FMA=; b=J0PS9BPXHBYmpQ0CrzK7VmogLcA4SML53UhEjkb7cBHB/s1vWmX4Nc1qgHvY0BcZQNX8q4 ypGPRRQDZnH7IX5qfJbnDAVzHWoEbUn83ozO8TKiKSeRvSNEnvhTdRNuSSXZzxaeQOMxBX 5VpwcAXSCP1fmA6e7mNxcHcR9JKFPaM= X-MC-Unique: cz2saie2PcCMrxXQqjiSJw-1 From: Vivek Goyal To: qemu-devel@nongnu.org, virtio-fs@redhat.com Subject: [PATCH v2 1/6] fuse: Header file changes for FUSE_SECURITY_CTX Date: Thu, 14 Oct 2021 11:31:21 -0400 Message-Id: <20211014153126.575173-2-vgoyal@redhat.com> In-Reply-To: <20211014153126.575173-1-vgoyal@redhat.com> References: <20211014153126.575173-1-vgoyal@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.15 Authentication-Results: relay.mimecast.com; auth=pass smtp.auth=CUSA124A263 smtp.mailfrom=vgoyal@redhat.com X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=170.10.133.124; envelope-from=vgoyal@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -28 X-Spam_score: -2.9 X-Spam_bar: -- X-Spam_report: (-2.9 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.049, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_LOW=-0.7, RCVD_IN_MSPIKE_H2=-0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: miklos@szeredi.hu, chirantan@chromium.org, stephen.smalley.work@gmail.com, dwalsh@redhat.com, dgilbert@redhat.com, omosnace@redhat.com, casey@schaufler-ca.com, vgoyal@redhat.com Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail-DKIM: fail (Header signature does not verify) X-ZM-MESSAGEID: 1634226211114100001 Content-Type: text/plain; charset="utf-8" These are just header file changes which should show up in qemu if corresponding kernel changes get merged. Signed-off-by: Vivek Goyal --- include/standard-headers/linux/fuse.h | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/include/standard-headers/linux/fuse.h b/include/standard-heade= rs/linux/fuse.h index cce105bfba..adf70b884c 100644 --- a/include/standard-headers/linux/fuse.h +++ b/include/standard-headers/linux/fuse.h @@ -181,6 +181,10 @@ * - add FUSE_OPEN_KILL_SUIDGID * - extend fuse_setxattr_in, add FUSE_SETXATTR_EXT * - add FUSE_SETXATTR_ACL_KILL_SGID + * + * 7.35 + * - add FUSE_SECURITY_CTX flag for fuse_init_out + * - add security context to create, mkdir, symlink, and mknod requests */ =20 #ifndef _LINUX_FUSE_H @@ -212,7 +216,7 @@ #define FUSE_KERNEL_VERSION 7 =20 /** Minor version number of this interface */ -#define FUSE_KERNEL_MINOR_VERSION 33 +#define FUSE_KERNEL_MINOR_VERSION 35 =20 /** The node ID of the root inode */ #define FUSE_ROOT_ID 1 @@ -329,6 +333,8 @@ struct fuse_file_lock { * write/truncate sgid is killed only if file has group * execute permission. (Same as Linux VFS behavior). * FUSE_SETXATTR_EXT: Server supports extended struct fuse_setxattr_in + * FUSE_SECURITY_CTX: add security context to create, mkdir, symlink, and + * mknod */ #define FUSE_ASYNC_READ (1 << 0) #define FUSE_POSIX_LOCKS (1 << 1) @@ -360,6 +366,7 @@ struct fuse_file_lock { #define FUSE_SUBMOUNTS (1 << 27) #define FUSE_HANDLE_KILLPRIV_V2 (1 << 28) #define FUSE_SETXATTR_EXT (1 << 29) +#define FUSE_SECURITY_CTX (1 << 30) =20 /** * CUSE INIT request/reply flags @@ -967,4 +974,14 @@ struct fuse_removemapping_one { #define FUSE_REMOVEMAPPING_MAX_ENTRY \ (PAGE_SIZE / sizeof(struct fuse_removemapping_one)) =20 +struct fuse_secctx { + uint32_t size; + uint32_t padding; +}; + +struct fuse_secctxs { + uint32_t nr_secctx; + uint32_t padding; +}; + #endif /* _LINUX_FUSE_H */ --=20 2.31.1 From nobody Sun Feb 8 14:56:10 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=fail; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=redhat.com Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1634225929406852.5555309208082; Thu, 14 Oct 2021 08:38:49 -0700 (PDT) Received: from localhost ([::1]:44156 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1mb2oi-00010t-9o for importer@patchew.org; Thu, 14 Oct 2021 11:38:48 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:48686) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1mb2hx-0005QL-Cm for qemu-devel@nongnu.org; Thu, 14 Oct 2021 11:31:49 -0400 Received: from us-smtp-delivery-124.mimecast.com ([216.205.24.124]:48451) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1mb2ht-0003TX-Gh for qemu-devel@nongnu.org; Thu, 14 Oct 2021 11:31:49 -0400 Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-207-Oihhv5lSOwSvRjFL5MnpMA-1; Thu, 14 Oct 2021 11:31:39 -0400 Received: from smtp.corp.redhat.com (int-mx08.intmail.prod.int.phx2.redhat.com [10.5.11.23]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id A85DF8010E9; Thu, 14 Oct 2021 15:31:38 +0000 (UTC) Received: from horse.redhat.com (unknown [10.22.16.190]) by smtp.corp.redhat.com (Postfix) with ESMTP id 9E97619D9D; Thu, 14 Oct 2021 15:31:37 +0000 (UTC) Received: by horse.redhat.com (Postfix, from userid 10451) id 44F94220CB6; Thu, 14 Oct 2021 11:31:37 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1634225502; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=KOStDXPZa11zEUngGviDBHWdk2GOkCsF4FoRpMzUjp8=; b=e7QglLng0c3uyJMVlQxy1jImgqYYF39w+0OZfac2+j0/H3DEZNcEADqB3iiUDCWo6AlB6I WYBHdGcLeJNnKQ264+KeOptBECBbtfwiGjC8iUfRLRjDbbRhCo0NsleEUHDT8MFBS3svgi RAS1Urzb+BF0fzYiyw9yj1xe120KFbg= X-MC-Unique: Oihhv5lSOwSvRjFL5MnpMA-1 From: Vivek Goyal To: qemu-devel@nongnu.org, virtio-fs@redhat.com Subject: [PATCH v2 2/6] virtiofsd, fuse_lowlevel.c: Add capability to parse security context Date: Thu, 14 Oct 2021 11:31:22 -0400 Message-Id: <20211014153126.575173-3-vgoyal@redhat.com> In-Reply-To: <20211014153126.575173-1-vgoyal@redhat.com> References: <20211014153126.575173-1-vgoyal@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.84 on 10.5.11.23 Authentication-Results: relay.mimecast.com; auth=pass smtp.auth=CUSA124A263 smtp.mailfrom=vgoyal@redhat.com X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=216.205.24.124; envelope-from=vgoyal@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -8 X-Spam_score: -0.9 X-Spam_bar: / X-Spam_report: (-0.9 / 5.0 requ) DKIMWL_WL_HIGH=-0.049, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_LOW=-0.7, RCVD_IN_MSPIKE_H2=-0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: miklos@szeredi.hu, chirantan@chromium.org, stephen.smalley.work@gmail.com, dwalsh@redhat.com, dgilbert@redhat.com, omosnace@redhat.com, casey@schaufler-ca.com, vgoyal@redhat.com Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail-DKIM: fail (Header signature does not verify) X-ZM-MESSAGEID: 1634225931829100001 Content-Type: text/plain; charset="utf-8" Add capability to enable and parse security context as sent by client and put into fuse_req. Filesystems now can get security context from request and set it on files during creation. Signed-off-by: Vivek Goyal --- tools/virtiofsd/fuse_common.h | 5 ++ tools/virtiofsd/fuse_i.h | 7 +++ tools/virtiofsd/fuse_lowlevel.c | 91 +++++++++++++++++++++++++++++++++ 3 files changed, 103 insertions(+) diff --git a/tools/virtiofsd/fuse_common.h b/tools/virtiofsd/fuse_common.h index 0c2665b977..6f3485d1dc 100644 --- a/tools/virtiofsd/fuse_common.h +++ b/tools/virtiofsd/fuse_common.h @@ -377,6 +377,11 @@ struct fuse_file_info { */ #define FUSE_CAP_SETXATTR_EXT (1 << 29) =20 +/** + * Indicates that file server supports creating file security context + */ +#define FUSE_CAP_SECURITY_CTX (1 << 30) + /** * Ioctl flags * diff --git a/tools/virtiofsd/fuse_i.h b/tools/virtiofsd/fuse_i.h index 492e002181..a5572fa4ae 100644 --- a/tools/virtiofsd/fuse_i.h +++ b/tools/virtiofsd/fuse_i.h @@ -15,6 +15,12 @@ struct fv_VuDev; struct fv_QueueInfo; =20 +struct fuse_security_context { + const char *name; + uint32_t ctxlen; + const void *ctx; +}; + struct fuse_req { struct fuse_session *se; uint64_t unique; @@ -35,6 +41,7 @@ struct fuse_req { } u; struct fuse_req *next; struct fuse_req *prev; + struct fuse_security_context secctx; }; =20 struct fuse_notify_req { diff --git a/tools/virtiofsd/fuse_lowlevel.c b/tools/virtiofsd/fuse_lowleve= l.c index e4679c73ab..94bea4a3c9 100644 --- a/tools/virtiofsd/fuse_lowlevel.c +++ b/tools/virtiofsd/fuse_lowlevel.c @@ -886,11 +886,59 @@ static void do_readlink(fuse_req_t req, fuse_ino_t no= deid, } } =20 +static int parse_secctx_fill_req(fuse_req_t req, struct fuse_mbuf_iter *it= er) +{ + struct fuse_secctxs *fsecctxs; + struct fuse_secctx *fsecctx; + const void *secctx; + const char *name; + + fsecctxs =3D fuse_mbuf_iter_advance(iter, sizeof(*fsecctxs)); + if (!fsecctxs) { + return -EINVAL; + } + + /* + * As of now maximum of one security context is supported. It can + * change in future though. + */ + if (fsecctxs->nr_secctx > 1) { + return -EINVAL; + } + + /* No security context sent. Maybe no LSM supports it */ + if (!fsecctxs->nr_secctx) { + return 0; + } + + fsecctx =3D fuse_mbuf_iter_advance(iter, sizeof(*fsecctx)); + if (!fsecctx) { + return -EINVAL; + } + + name =3D fuse_mbuf_iter_advance_str(iter); + if (!name) { + return -EINVAL; + } + + secctx =3D fuse_mbuf_iter_advance(iter, fsecctx->size); + if (!secctx) { + return -EINVAL; + } + + req->secctx.name =3D name; + req->secctx.ctx =3D secctx; + req->secctx.ctxlen =3D fsecctx->size; + return 0; +} + static void do_mknod(fuse_req_t req, fuse_ino_t nodeid, struct fuse_mbuf_iter *iter) { struct fuse_mknod_in *arg; const char *name; + bool secctx_enabled =3D req->se->conn.want & FUSE_CAP_SECURITY_CTX; + int err; =20 arg =3D fuse_mbuf_iter_advance(iter, sizeof(*arg)); name =3D fuse_mbuf_iter_advance_str(iter); @@ -901,6 +949,13 @@ static void do_mknod(fuse_req_t req, fuse_ino_t nodeid, =20 req->ctx.umask =3D arg->umask; =20 + if (secctx_enabled) { + err =3D parse_secctx_fill_req(req, iter); + if (err) { + fuse_reply_err(req, -err); + } + } + if (req->se->op.mknod) { req->se->op.mknod(req, nodeid, name, arg->mode, arg->rdev); } else { @@ -913,6 +968,8 @@ static void do_mkdir(fuse_req_t req, fuse_ino_t nodeid, { struct fuse_mkdir_in *arg; const char *name; + bool secctx_enabled =3D req->se->conn.want & FUSE_CAP_SECURITY_CTX; + int err; =20 arg =3D fuse_mbuf_iter_advance(iter, sizeof(*arg)); name =3D fuse_mbuf_iter_advance_str(iter); @@ -923,6 +980,13 @@ static void do_mkdir(fuse_req_t req, fuse_ino_t nodeid, =20 req->ctx.umask =3D arg->umask; =20 + if (secctx_enabled) { + err =3D parse_secctx_fill_req(req, iter); + if (err) { + fuse_reply_err(req, err); + } + } + if (req->se->op.mkdir) { req->se->op.mkdir(req, nodeid, name, arg->mode); } else { @@ -969,12 +1033,21 @@ static void do_symlink(fuse_req_t req, fuse_ino_t no= deid, { const char *name =3D fuse_mbuf_iter_advance_str(iter); const char *linkname =3D fuse_mbuf_iter_advance_str(iter); + bool secctx_enabled =3D req->se->conn.want & FUSE_CAP_SECURITY_CTX; + int err; =20 if (!name || !linkname) { fuse_reply_err(req, EINVAL); return; } =20 + if (secctx_enabled) { + err =3D parse_secctx_fill_req(req, iter); + if (err) { + fuse_reply_err(req, err); + } + } + if (req->se->op.symlink) { req->se->op.symlink(req, linkname, nodeid, name); } else { @@ -1048,6 +1121,8 @@ static void do_link(fuse_req_t req, fuse_ino_t nodeid, static void do_create(fuse_req_t req, fuse_ino_t nodeid, struct fuse_mbuf_iter *iter) { + bool secctx_enabled =3D req->se->conn.want & FUSE_CAP_SECURITY_CTX; + if (req->se->op.create) { struct fuse_create_in *arg; struct fuse_file_info fi; @@ -1060,6 +1135,15 @@ static void do_create(fuse_req_t req, fuse_ino_t nod= eid, return; } =20 + if (secctx_enabled) { + int err; + err =3D parse_secctx_fill_req(req, iter); + if (err) { + fuse_reply_err(req, err); + return; + } + } + memset(&fi, 0, sizeof(fi)); fi.flags =3D arg->flags; fi.kill_priv =3D arg->open_flags & FUSE_OPEN_KILL_SUIDGID; @@ -1997,6 +2081,9 @@ static void do_init(fuse_req_t req, fuse_ino_t nodeid, if (arg->flags & FUSE_SETXATTR_EXT) { se->conn.capable |=3D FUSE_CAP_SETXATTR_EXT; } + if (arg->flags & FUSE_SECURITY_CTX) { + se->conn.capable |=3D FUSE_CAP_SECURITY_CTX; + } #ifdef HAVE_SPLICE #ifdef HAVE_VMSPLICE se->conn.capable |=3D FUSE_CAP_SPLICE_WRITE | FUSE_CAP_SPLICE_MOVE; @@ -2136,6 +2223,10 @@ static void do_init(fuse_req_t req, fuse_ino_t nodei= d, outarg.flags |=3D FUSE_SETXATTR_EXT; } =20 + if (se->conn.want & FUSE_CAP_SECURITY_CTX) { + outarg.flags |=3D FUSE_SECURITY_CTX; + } + fuse_log(FUSE_LOG_DEBUG, " INIT: %u.%u\n", outarg.major, outarg.mino= r); fuse_log(FUSE_LOG_DEBUG, " flags=3D0x%08x\n", outarg.flags); fuse_log(FUSE_LOG_DEBUG, " max_readahead=3D0x%08x\n", outarg.max_rea= dahead); --=20 2.31.1 From nobody Sun Feb 8 14:56:10 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=fail; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=redhat.com Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1634226411732949.1902013380685; Thu, 14 Oct 2021 08:46:51 -0700 (PDT) Received: from localhost ([::1]:57144 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1mb2wU-0001TP-PJ for importer@patchew.org; Thu, 14 Oct 2021 11:46:50 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:48754) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1mb2iB-0005co-3r for qemu-devel@nongnu.org; Thu, 14 Oct 2021 11:32:03 -0400 Received: from us-smtp-delivery-124.mimecast.com ([170.10.129.124]:60473) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1mb2hu-0003W3-RW for qemu-devel@nongnu.org; Thu, 14 Oct 2021 11:32:02 -0400 Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-49-xCSsCeKKPxuosHb_i_diDg-1; Thu, 14 Oct 2021 11:31:44 -0400 Received: from smtp.corp.redhat.com (int-mx04.intmail.prod.int.phx2.redhat.com [10.5.11.14]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id AD2C8101872A; Thu, 14 Oct 2021 15:31:42 +0000 (UTC) Received: from horse.redhat.com (unknown [10.22.16.190]) by smtp.corp.redhat.com (Postfix) with ESMTP id ACF4B5DA60; Thu, 14 Oct 2021 15:31:37 +0000 (UTC) Received: by horse.redhat.com (Postfix, from userid 10451) id 4CB852237BF; Thu, 14 Oct 2021 11:31:37 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1634225505; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=AbU3zBpkTZISMheH/ABBF4b9YCeJ+lPJimNymFsSkZM=; b=Vr0JKGhHyTeRYmsQJ1fkEhQifQCnXnpb5gAkHcz3Z/i3giWHNol/f8ApPhzA3MyTbZbXFw iEEyVfSmcTUt0TatlinpC2U9F+9SBu6vSIIvGQf3zj9mP7zxZNz9Gi1+FYhRCYvCgS/cDx QGnnyNIwUuI2gIesJMihEetiO5yudbE= X-MC-Unique: xCSsCeKKPxuosHb_i_diDg-1 From: Vivek Goyal To: qemu-devel@nongnu.org, virtio-fs@redhat.com Subject: [PATCH v2 3/6] virtiofsd: Move core file creation code in separate function Date: Thu, 14 Oct 2021 11:31:23 -0400 Message-Id: <20211014153126.575173-4-vgoyal@redhat.com> In-Reply-To: <20211014153126.575173-1-vgoyal@redhat.com> References: <20211014153126.575173-1-vgoyal@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.14 Authentication-Results: relay.mimecast.com; auth=pass smtp.auth=CUSA124A263 smtp.mailfrom=vgoyal@redhat.com X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=170.10.129.124; envelope-from=vgoyal@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.049, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, SPF_HELO_NONE=0.001, T_SPF_TEMPERROR=0.01 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: miklos@szeredi.hu, chirantan@chromium.org, stephen.smalley.work@gmail.com, dwalsh@redhat.com, dgilbert@redhat.com, omosnace@redhat.com, casey@schaufler-ca.com, vgoyal@redhat.com Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail-DKIM: fail (Header signature does not verify) X-ZM-MESSAGEID: 1634226412731100001 Content-Type: text/plain; charset="utf-8" Move core file creation bits in a separate function. Soon this is going to get more complex as file creation need to set security context also. And there will be multiple modes of file creation in next patch. Signed-off-by: Vivek Goyal --- tools/virtiofsd/passthrough_ll.c | 36 ++++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough= _ll.c index 64b5b4fbb1..54978b7fae 100644 --- a/tools/virtiofsd/passthrough_ll.c +++ b/tools/virtiofsd/passthrough_ll.c @@ -1976,6 +1976,30 @@ static int lo_do_open(struct lo_data *lo, struct lo_= inode *inode, return 0; } =20 +static int do_lo_create(fuse_req_t req, struct lo_inode *parent_inode, + const char *name, mode_t mode, + struct fuse_file_info *fi, int* open_fd) +{ + int err =3D 0, fd; + struct lo_cred old =3D {}; + struct lo_data *lo =3D lo_data(req); + + err =3D lo_change_cred(req, &old, lo->change_umask); + if (err) { + return err; + } + + /* Try to create a new file but don't open existing files */ + fd =3D openat(parent_inode->fd, name, fi->flags | O_CREAT | O_EXCL, mo= de); + if (fd =3D=3D -1) { + err =3D errno; + } else { + *open_fd =3D fd; + } + lo_restore_cred(&old, lo->change_umask); + return err; +} + static void lo_create(fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode, struct fuse_file_info *fi) { @@ -1985,7 +2009,6 @@ static void lo_create(fuse_req_t req, fuse_ino_t pare= nt, const char *name, struct lo_inode *inode =3D NULL; struct fuse_entry_param e; int err; - struct lo_cred old =3D {}; =20 fuse_log(FUSE_LOG_DEBUG, "lo_create(parent=3D%" PRIu64 ", name=3D%s)" " kill_priv=3D%d\n", parent, name, fi->kill_priv); @@ -2001,18 +2024,9 @@ static void lo_create(fuse_req_t req, fuse_ino_t par= ent, const char *name, return; } =20 - err =3D lo_change_cred(req, &old, lo->change_umask); - if (err) { - goto out; - } - update_open_flags(lo->writeback, lo->allow_direct_io, fi); =20 - /* Try to create a new file but don't open existing files */ - fd =3D openat(parent_inode->fd, name, fi->flags | O_CREAT | O_EXCL, mo= de); - err =3D fd =3D=3D -1 ? errno : 0; - - lo_restore_cred(&old, lo->change_umask); + err =3D do_lo_create(req, parent_inode, name, mode, fi, &fd); =20 /* Ignore the error if file exists and O_EXCL was not given */ if (err && (err !=3D EEXIST || (fi->flags & O_EXCL))) { --=20 2.31.1 From nobody Sun Feb 8 14:56:10 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=fail; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=redhat.com Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1634226148383932.0693748942707; Thu, 14 Oct 2021 08:42:28 -0700 (PDT) Received: from localhost ([::1]:49918 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1mb2sF-0004xs-8R for importer@patchew.org; Thu, 14 Oct 2021 11:42:27 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:48738) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1mb2i3-0005Wo-Mx for qemu-devel@nongnu.org; Thu, 14 Oct 2021 11:31:55 -0400 Received: from us-smtp-delivery-124.mimecast.com ([216.205.24.124]:33495) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1mb2ht-0003UD-VH for qemu-devel@nongnu.org; Thu, 14 Oct 2021 11:31:55 -0400 Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-123-VV7Jt-TqP2eEM6z1NQ4lFw-1; Thu, 14 Oct 2021 11:31:40 -0400 Received: from smtp.corp.redhat.com (int-mx08.intmail.prod.int.phx2.redhat.com [10.5.11.23]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 017259126F; Thu, 14 Oct 2021 15:31:39 +0000 (UTC) Received: from horse.redhat.com (unknown [10.22.16.190]) by smtp.corp.redhat.com (Postfix) with ESMTP id A828219724; Thu, 14 Oct 2021 15:31:37 +0000 (UTC) Received: by horse.redhat.com (Postfix, from userid 10451) id 52A1922384A; Thu, 14 Oct 2021 11:31:37 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1634225503; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=pVp4k8MpUq+7WgrexbP9CQAZtH8dGUhP313jrqMCaQs=; b=R3pf3hiOKc0exkbAATyLnZTNwcOauFIuOldEz8YRKaoat8Sr/e8TnhLOELsc6eDtvIH9ON VthPjMjzcyfZFn7PgCTBxeWPyRfiobY2rFhy28P+zcgtwm1fAX3LUBt4I+4q7q8sPGlqpW sEq4gy+DECbJP9YKuXjM7/qZ144N3pU= X-MC-Unique: VV7Jt-TqP2eEM6z1NQ4lFw-1 From: Vivek Goyal To: qemu-devel@nongnu.org, virtio-fs@redhat.com Subject: [PATCH v2 4/6] virtiofsd: Create new file with fscreate set Date: Thu, 14 Oct 2021 11:31:24 -0400 Message-Id: <20211014153126.575173-5-vgoyal@redhat.com> In-Reply-To: <20211014153126.575173-1-vgoyal@redhat.com> References: <20211014153126.575173-1-vgoyal@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.84 on 10.5.11.23 Authentication-Results: relay.mimecast.com; auth=pass smtp.auth=CUSA124A263 smtp.mailfrom=vgoyal@redhat.com X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=216.205.24.124; envelope-from=vgoyal@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -27 X-Spam_score: -2.8 X-Spam_bar: -- X-Spam_report: (-2.8 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.049, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_LOW=-0.7, RCVD_IN_MSPIKE_H2=-0.001, SPF_HELO_NONE=0.001, T_SPF_TEMPERROR=0.01 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: miklos@szeredi.hu, chirantan@chromium.org, stephen.smalley.work@gmail.com, dwalsh@redhat.com, dgilbert@redhat.com, omosnace@redhat.com, casey@schaufler-ca.com, vgoyal@redhat.com Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail-DKIM: fail (Header signature does not verify) X-ZM-MESSAGEID: 1634226150055100001 Content-Type: text/plain; charset="utf-8" This patch adds support to set /proc/thread-self/attr/fscreate before file creation. It is set to a value as sent by client. This will allow for atomic creation of security context on files w.r.t file creation. This is primarily useful when either there is no SELinux enabled on host or host and guest policies are in sync and don't conflict. Signed-off-by: Vivek Goyal --- tools/virtiofsd/passthrough_ll.c | 317 ++++++++++++++++++++++++++++--- 1 file changed, 290 insertions(+), 27 deletions(-) diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough= _ll.c index 54978b7fae..7a714b1b5e 100644 --- a/tools/virtiofsd/passthrough_ll.c +++ b/tools/virtiofsd/passthrough_ll.c @@ -172,10 +172,14 @@ struct lo_data { =20 /* An O_PATH file descriptor to /proc/self/fd/ */ int proc_self_fd; + /* An O_PATH file descriptor to /proc/self/task/ */ + int proc_self_task; int user_killpriv_v2, killpriv_v2; /* If set, virtiofsd is responsible for setting umask during creation = */ bool change_umask; int user_posix_acl, posix_acl; + /* Keeps track if /proc//attr/fscreate should be used or not */ + bool use_fscreate; }; =20 static const struct fuse_opt lo_opts[] =3D { @@ -229,6 +233,11 @@ static struct lo_inode *lo_find(struct lo_data *lo, st= ruct stat *st, static int xattr_map_client(const struct lo_data *lo, const char *client_n= ame, char **out_name); =20 +#define FCHDIR_NOFAIL(fd) do { \ + int fchdir_res =3D fchdir(fd); \ + assert(fchdir_res =3D=3D 0); \ + } while (0) + static bool is_dot_or_dotdot(const char *name) { return name[0] =3D=3D '.' && @@ -255,6 +264,33 @@ static struct lo_data *lo_data(fuse_req_t req) return (struct lo_data *)fuse_req_userdata(req); } =20 +/* + * Tries to figure out if /proc//attr/fscrate is usable or not. With + * selinux=3D0, read from fscreate returns -EINVAL. + * + * TODO: Link with libselinux and use is_selinux_enabled() instead down + * the line. It probably will be more reliable indicator. + */ +static bool is_fscreate_usable(struct lo_data *lo) +{ + char procname[64]; + int fscreate_fd; + size_t bytes_read; + + sprintf(procname, "%d/attr/fscreate", gettid()); + fscreate_fd =3D openat(lo->proc_self_task, procname, O_RDWR); + if (fscreate_fd =3D=3D -1) { + return false; + } + + bytes_read =3D read(fscreate_fd, procname, 64); + close(fscreate_fd); + if (bytes_read =3D=3D -1) { + return false; + } + return true; +} + /* * Load capng's state from our saved state if the current thread * hadn't previously been loaded. @@ -1259,16 +1295,140 @@ static void lo_restore_cred_gain_cap(struct lo_cre= d *old, bool restore_umask, } } =20 +/* Helpers to set/reset fscreate */ +static int open_set_proc_fscreate(struct lo_data *lo, const void *ctx, + size_t ctxlen, int *fd) +{ + char procname[64]; + int fscreate_fd, err =3D 0; + size_t written; + + sprintf(procname, "%d/attr/fscreate", gettid()); + fscreate_fd =3D openat(lo->proc_self_task, procname, O_WRONLY); + err =3D fscreate_fd =3D=3D -1 ? errno : 0; + if (err) { + return err; + } + + written =3D write(fscreate_fd, ctx, ctxlen); + err =3D written =3D=3D -1 ? errno : 0; + if (err) { + goto out; + } + + *fd =3D fscreate_fd; + return 0; +out: + close(fscreate_fd); + return err; +} + +static void close_reset_proc_fscreate(int fd) +{ + if ((write(fd, NULL, 0)) =3D=3D -1) { + fuse_log(FUSE_LOG_WARNING, "Failed to reset fscreate. err=3D%d\n",= errno); + } + close(fd); + return; +} + +static int do_mknod_symlink_secctx(fuse_req_t req, struct lo_inode *dir, + const char *name, const char *secctx_na= me) +{ + int path_fd, err; + char procname[64]; + struct lo_data *lo =3D lo_data(req); + + if (!req->secctx.ctxlen) { + return 0; + } + + /* Open newly created element with O_PATH */ + path_fd =3D openat(dir->fd, name, O_PATH | O_NOFOLLOW); + err =3D path_fd =3D=3D -1 ? errno : 0; + if (err) { + return err; + } + sprintf(procname, "%i", path_fd); + FCHDIR_NOFAIL(lo->proc_self_fd); + /* Set security context. This is not atomic w.r.t file creation */ + err =3D setxattr(procname, secctx_name, req->secctx.ctx, req->secctx.c= txlen, + 0); + if (err) { + err =3D errno; + } + FCHDIR_NOFAIL(lo->root.fd); + close(path_fd); + return err; +} + +static int do_mknod_symlink(fuse_req_t req, struct lo_inode *dir, + const char *name, mode_t mode, dev_t rdev, + const char *link) +{ + int err, fscreate_fd =3D -1; + const char *secctx_name =3D req->secctx.name; + struct lo_cred old =3D {}; + struct lo_data *lo =3D lo_data(req); + char *mapped_name =3D NULL; + bool secctx_enabled =3D req->secctx.ctxlen; + bool do_fscreate =3D false; + + if (secctx_enabled && lo->xattrmap) { + err =3D xattr_map_client(lo, req->secctx.name, &mapped_name); + if (err < 0) { + return -err; + } + secctx_name =3D mapped_name; + } + + /* + * If security xattr has not been remapped and selinux is enabled on + * host, set fscreate and no need to do a setxattr() after file creati= on + */ + if (secctx_enabled && !mapped_name && lo->use_fscreate) { + do_fscreate =3D true; + err =3D open_set_proc_fscreate(lo, req->secctx.ctx, req->secctx.ct= xlen, + &fscreate_fd); + if (err) { + goto out; + } + } + + err =3D lo_change_cred(req, &old, lo->change_umask && !S_ISLNK(mode)); + if (err) { + goto out; + } + + err =3D mknod_wrapper(dir->fd, name, link, mode, rdev); + err =3D err =3D=3D -1 ? errno : 0; + lo_restore_cred(&old, lo->change_umask && !S_ISLNK(mode)); + if (err) { + goto out; + } + + if (!do_fscreate) { + err =3D do_mknod_symlink_secctx(req, dir, name, secctx_name); + if (err) { + unlinkat(dir->fd, name, S_ISDIR(mode) ? AT_REMOVEDIR : 0); + } + } +out: + if (fscreate_fd !=3D -1) { + close_reset_proc_fscreate(fscreate_fd); + } + g_free(mapped_name); + return err; +} + static void lo_mknod_symlink(fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode, dev_t rdev, const char *link) { - int res; int saverr; struct lo_data *lo =3D lo_data(req); struct lo_inode *dir; struct fuse_entry_param e; - struct lo_cred old =3D {}; =20 if (is_empty(name)) { fuse_reply_err(req, ENOENT); @@ -1286,21 +1446,11 @@ static void lo_mknod_symlink(fuse_req_t req, fuse_i= no_t parent, return; } =20 - saverr =3D lo_change_cred(req, &old, lo->change_umask && !S_ISLNK(mode= )); + saverr =3D do_mknod_symlink(req, dir, name, mode, rdev, link); if (saverr) { goto out; } =20 - res =3D mknod_wrapper(dir->fd, name, link, mode, rdev); - - saverr =3D errno; - - lo_restore_cred(&old, lo->change_umask && !S_ISLNK(mode)); - - if (res =3D=3D -1) { - goto out; - } - saverr =3D lo_do_lookup(req, parent, name, &e, NULL); if (saverr) { goto out; @@ -1976,13 +2126,16 @@ static int lo_do_open(struct lo_data *lo, struct lo= _inode *inode, return 0; } =20 -static int do_lo_create(fuse_req_t req, struct lo_inode *parent_inode, - const char *name, mode_t mode, - struct fuse_file_info *fi, int* open_fd) +static int do_create_nosecctx(fuse_req_t req, struct lo_inode *parent_inod= e, + const char *name, mode_t mode, + struct fuse_file_info *fi, int *open_fd) { - int err =3D 0, fd; + int err, fd; struct lo_cred old =3D {}; struct lo_data *lo =3D lo_data(req); + int flags; + + flags =3D fi->flags | O_CREAT | O_EXCL; =20 err =3D lo_change_cred(req, &old, lo->change_umask); if (err) { @@ -1990,13 +2143,106 @@ static int do_lo_create(fuse_req_t req, struct lo_= inode *parent_inode, } =20 /* Try to create a new file but don't open existing files */ - fd =3D openat(parent_inode->fd, name, fi->flags | O_CREAT | O_EXCL, mo= de); - if (fd =3D=3D -1) { - err =3D errno; - } else { + fd =3D openat(parent_inode->fd, name, flags, mode); + err =3D fd =3D=3D -1 ? errno : 0; + lo_restore_cred(&old, lo->change_umask); + if (!err) { *open_fd =3D fd; } - lo_restore_cred(&old, lo->change_umask); + return err; +} + +static int do_create_secctx_fscreate(fuse_req_t req, + struct lo_inode *parent_inode, + const char *name, mode_t mode, + struct fuse_file_info *fi, int *open_= fd) +{ + int err =3D 0, fd =3D -1, fscreate_fd =3D -1; + struct lo_data *lo =3D lo_data(req); + + err =3D open_set_proc_fscreate(lo, req->secctx.ctx, req->secctx.ctxlen, + &fscreate_fd); + if (err) { + return err; + } + + err =3D do_create_nosecctx(req, parent_inode, name, mode, fi, &fd); + + close_reset_proc_fscreate(fscreate_fd); + if (!err) { + *open_fd =3D fd; + } + return err; +} + +static int do_create_secctx_noatomic(fuse_req_t req, + struct lo_inode *parent_inode, + const char *name, mode_t mode, + struct fuse_file_info *fi, + const char *secctx_name, int *open_fd) +{ + int err =3D 0, fd =3D -1; + + err =3D do_create_nosecctx(req, parent_inode, name, mode, fi, &fd); + if (err) { + goto out; + } + + /* Set security context. This is not atomic w.r.t file creation */ + err =3D fsetxattr(fd, secctx_name, req->secctx.ctx, req->secctx.ctxlen= , 0); + err =3D err =3D=3D -1 ? errno : 0; +out: + if (!err) { + *open_fd =3D fd; + } else { + if (fd !=3D -1) { + close(fd); + unlinkat(parent_inode->fd, name, 0); + } + } + return err; +} + +static int do_lo_create(fuse_req_t req, struct lo_inode *parent_inode, + const char *name, mode_t mode, + struct fuse_file_info *fi, int *open_fd) +{ + struct lo_data *lo =3D lo_data(req); + char *mapped_name =3D NULL; + int err; + const char *ctxname =3D req->secctx.name; + bool secctx_enabled =3D req->secctx.ctxlen; + + if (secctx_enabled && lo->xattrmap) { + err =3D xattr_map_client(lo, req->secctx.name, &mapped_name); + if (err < 0) { + return -err; + } + + ctxname =3D mapped_name; + } + + if (secctx_enabled) { + /* + * If security.selinux has not been remapped and selinux is enable= d, + * use fscreate to set context before file creation. + * Otherwise fallback to non-atomic method of file creation + * and xattr settting. + */ + if (!mapped_name && lo->use_fscreate) { + err =3D do_create_secctx_fscreate(req, parent_inode, name, mod= e, fi, + open_fd); + goto out; + } + + err =3D do_create_secctx_noatomic(req, parent_inode, name, mode, f= i, + ctxname, open_fd); + } else { + err =3D do_create_nosecctx(req, parent_inode, name, mode, fi, open= _fd); + } + +out: + g_free(mapped_name); return err; } =20 @@ -2831,11 +3077,6 @@ static int xattr_map_server(const struct lo_data *lo= , const char *server_name, return -ENODATA; } =20 -#define FCHDIR_NOFAIL(fd) do { \ - int fchdir_res =3D fchdir(fd); \ - assert(fchdir_res =3D=3D 0); \ - } while (0) - static bool block_xattr(struct lo_data *lo, const char *name) { /* @@ -3497,6 +3738,15 @@ static void setup_namespaces(struct lo_data *lo, str= uct fuse_session *se) exit(1); } =20 + /* Get the /proc/self/task descriptor */ + lo->proc_self_task =3D open("/proc/self/task/", O_PATH); + if (lo->proc_self_task =3D=3D -1) { + fuse_log(FUSE_LOG_ERR, "open(/proc/self/task, O_PATH): %m\n"); + exit(1); + } + + lo->use_fscreate =3D is_fscreate_usable(lo); + /* * We only need /proc/self/fd. Prevent ".." from accessing parent * directories of /proc/self/fd by bind-mounting it over /proc. Since = / was @@ -3713,6 +3963,14 @@ static void setup_chroot(struct lo_data *lo) exit(1); } =20 + lo->proc_self_task =3D open("/proc/self/task", O_PATH); + if (lo->proc_self_fd =3D=3D -1) { + fuse_log(FUSE_LOG_ERR, "open(\"/proc/self/task\", O_PATH): %m\n"); + exit(1); + } + + lo->use_fscreate =3D is_fscreate_usable(lo); + /* * Make the shared directory the file system root so that FUSE_OPEN * (lo_open()) cannot escape the shared directory by opening a symlink. @@ -3898,6 +4156,10 @@ static void fuse_lo_data_cleanup(struct lo_data *lo) close(lo->proc_self_fd); } =20 + if (lo->proc_self_task >=3D 0) { + close(lo->proc_self_task); + } + if (lo->root.fd >=3D 0) { close(lo->root.fd); } @@ -3925,6 +4187,7 @@ int main(int argc, char *argv[]) .posix_lock =3D 0, .allow_direct_io =3D 0, .proc_self_fd =3D -1, + .proc_self_task =3D -1, .user_killpriv_v2 =3D -1, .user_posix_acl =3D -1, }; --=20 2.31.1 From nobody Sun Feb 8 14:56:10 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=fail; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=redhat.com Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1634226730238748.3486849546257; Thu, 14 Oct 2021 08:52:10 -0700 (PDT) Received: from localhost ([::1]:36490 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1mb31d-0006j2-7z for importer@patchew.org; Thu, 14 Oct 2021 11:52:09 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:48698) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1mb2hz-0005Rm-Aq for qemu-devel@nongnu.org; Thu, 14 Oct 2021 11:31:52 -0400 Received: from us-smtp-delivery-124.mimecast.com ([216.205.24.124]:36041) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1mb2hs-0003Ua-R7 for qemu-devel@nongnu.org; Thu, 14 Oct 2021 11:31:50 -0400 Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-157-8YWL1v3bOGWnZC0jnoJANA-1; Thu, 14 Oct 2021 11:31:41 -0400 Received: from smtp.corp.redhat.com (int-mx08.intmail.prod.int.phx2.redhat.com [10.5.11.23]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id CD017801AFC; Thu, 14 Oct 2021 15:31:39 +0000 (UTC) Received: from horse.redhat.com (unknown [10.22.16.190]) by smtp.corp.redhat.com (Postfix) with ESMTP id EDB0A1972D; Thu, 14 Oct 2021 15:31:38 +0000 (UTC) Received: by horse.redhat.com (Postfix, from userid 10451) id 580BA223892; Thu, 14 Oct 2021 11:31:37 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1634225504; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=3T5bD787+9LQh658bYXKz6+jsogqmxJULPNKoKazUno=; b=bxb9PHnPk6ZeumYRhXHAOTGhgXOoPKOLfC5XENJDthA9NQ7Z0qwrJ0GQ0eNx6Oq8D/meNA 2T0QZ9tgSUCjr6xpCmMM72GWppsJ0gm9k7rrAE+Dv9+dd3oP0/5xHLpDw2et5O9CtHqYNo 0XBeAPRxqAZ3FlPXG+lspuOVJexF3Os= X-MC-Unique: 8YWL1v3bOGWnZC0jnoJANA-1 From: Vivek Goyal To: qemu-devel@nongnu.org, virtio-fs@redhat.com Subject: [PATCH v2 5/6] virtiofsd: Create new file using O_TMPFILE and set security context Date: Thu, 14 Oct 2021 11:31:25 -0400 Message-Id: <20211014153126.575173-6-vgoyal@redhat.com> In-Reply-To: <20211014153126.575173-1-vgoyal@redhat.com> References: <20211014153126.575173-1-vgoyal@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.84 on 10.5.11.23 Authentication-Results: relay.mimecast.com; auth=pass smtp.auth=CUSA124A263 smtp.mailfrom=vgoyal@redhat.com X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=216.205.24.124; envelope-from=vgoyal@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -28 X-Spam_score: -2.9 X-Spam_bar: -- X-Spam_report: (-2.9 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.049, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_LOW=-0.7, RCVD_IN_MSPIKE_H2=-0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: miklos@szeredi.hu, chirantan@chromium.org, stephen.smalley.work@gmail.com, dwalsh@redhat.com, dgilbert@redhat.com, omosnace@redhat.com, casey@schaufler-ca.com, vgoyal@redhat.com Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail-DKIM: fail (Header signature does not verify) X-ZM-MESSAGEID: 1634226731722100001 Content-Type: text/plain; charset="utf-8" If guest and host policies can't work with each other, then guest security context (selinux label) needs to be set into an xattr. Say remap guest security.selinux xattr to trusted.virtiofs.security.selinux. That means setting "fscreate" is not going to help as that's ony useful for security.selinux xattr on host. So we need another method which is atomic. Use O_TMPFILE to create new file, set xattr and then linkat() to proper place. But this works only for regular files. So dir, symlinks will continue to be non-atomic. Also if host filesystem does not support O_TMPFILE, we fallback to non-atomic behavior. Signed-off-by: Vivek Goyal --- tools/virtiofsd/passthrough_ll.c | 80 ++++++++++++++++++++++++++++---- 1 file changed, 72 insertions(+), 8 deletions(-) diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough= _ll.c index 7a714b1b5e..4505c0c363 100644 --- a/tools/virtiofsd/passthrough_ll.c +++ b/tools/virtiofsd/passthrough_ll.c @@ -2128,14 +2128,29 @@ static int lo_do_open(struct lo_data *lo, struct lo= _inode *inode, =20 static int do_create_nosecctx(fuse_req_t req, struct lo_inode *parent_inod= e, const char *name, mode_t mode, - struct fuse_file_info *fi, int *open_fd) + struct fuse_file_info *fi, int *open_fd, + bool tmpfile) { int err, fd; struct lo_cred old =3D {}; struct lo_data *lo =3D lo_data(req); int flags; =20 - flags =3D fi->flags | O_CREAT | O_EXCL; + if (tmpfile) { + flags =3D fi->flags | O_TMPFILE; + /* + * Don't use O_EXCL as we want to link file later. Also reset O_CR= EAT + * otherwise openat() returns -EINVAL. + */ + flags &=3D ~(O_CREAT | O_EXCL); + + /* O_TMPFILE needs either O_RDWR or O_WRONLY */ + if ((flags & O_ACCMODE) =3D=3D O_RDONLY) { + flags |=3D O_RDWR; + } + } else { + flags =3D fi->flags | O_CREAT | O_EXCL; + } =20 err =3D lo_change_cred(req, &old, lo->change_umask); if (err) { @@ -2166,7 +2181,7 @@ static int do_create_secctx_fscreate(fuse_req_t req, return err; } =20 - err =3D do_create_nosecctx(req, parent_inode, name, mode, fi, &fd); + err =3D do_create_nosecctx(req, parent_inode, name, mode, fi, &fd, fal= se); =20 close_reset_proc_fscreate(fscreate_fd); if (!err) { @@ -2175,6 +2190,44 @@ static int do_create_secctx_fscreate(fuse_req_t req, return err; } =20 +static int do_create_secctx_tmpfile(fuse_req_t req, + struct lo_inode *parent_inode, + const char *name, mode_t mode, + struct fuse_file_info *fi, + const char *secctx_name, int *open_fd) +{ + int err, fd =3D -1; + struct lo_data *lo =3D lo_data(req); + char procname[64]; + + err =3D do_create_nosecctx(req, parent_inode, ".", mode, fi, &fd, true= ); + if (err) { + return err; + } + + err =3D fsetxattr(fd, secctx_name, req->secctx.ctx, req->secctx.ctxlen= , 0); + if (err) { + err =3D errno; + goto out; + } + + /* Security context set on file. Link it in place */ + sprintf(procname, "%d", fd); + FCHDIR_NOFAIL(lo->proc_self_fd); + err =3D linkat(AT_FDCWD, procname, parent_inode->fd, name, + AT_SYMLINK_FOLLOW); + err =3D err =3D=3D -1 ? errno : 0; + FCHDIR_NOFAIL(lo->root.fd); + +out: + if (!err) { + *open_fd =3D fd; + } else if (fd !=3D -1) { + close(fd); + } + return err; +} + static int do_create_secctx_noatomic(fuse_req_t req, struct lo_inode *parent_inode, const char *name, mode_t mode, @@ -2183,7 +2236,7 @@ static int do_create_secctx_noatomic(fuse_req_t req, { int err =3D 0, fd =3D -1; =20 - err =3D do_create_nosecctx(req, parent_inode, name, mode, fi, &fd); + err =3D do_create_nosecctx(req, parent_inode, name, mode, fi, &fd, fal= se); if (err) { goto out; } @@ -2225,20 +2278,31 @@ static int do_lo_create(fuse_req_t req, struct lo_i= node *parent_inode, if (secctx_enabled) { /* * If security.selinux has not been remapped and selinux is enable= d, - * use fscreate to set context before file creation. - * Otherwise fallback to non-atomic method of file creation - * and xattr settting. + * use fscreate to set context before file creation. If not, use + * tmpfile method for regular files. Otherwise fallback to + * non-atomic method of file creation and xattr settting. */ if (!mapped_name && lo->use_fscreate) { err =3D do_create_secctx_fscreate(req, parent_inode, name, mod= e, fi, open_fd); goto out; + } else if (S_ISREG(mode)) { + err =3D do_create_secctx_tmpfile(req, parent_inode, name, mode= , fi, + ctxname, open_fd); + /* + * If filesystem does not support O_TMPFILE, fallback to non-a= tomic + * method. + */ + if (!err || err !=3D EOPNOTSUPP) { + goto out; + } } =20 err =3D do_create_secctx_noatomic(req, parent_inode, name, mode, f= i, ctxname, open_fd); } else { - err =3D do_create_nosecctx(req, parent_inode, name, mode, fi, open= _fd); + err =3D do_create_nosecctx(req, parent_inode, name, mode, fi, open= _fd, + false); } =20 out: --=20 2.31.1 From nobody Sun Feb 8 14:56:10 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=fail; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=redhat.com Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1634227863760181.70325293971848; Thu, 14 Oct 2021 09:11:03 -0700 (PDT) Received: from localhost ([::1]:44646 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1mb3Jt-00068T-J1 for importer@patchew.org; Thu, 14 Oct 2021 12:11:01 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:48688) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1mb2hx-0005Qg-Rk for qemu-devel@nongnu.org; Thu, 14 Oct 2021 11:31:49 -0400 Received: from us-smtp-delivery-124.mimecast.com ([216.205.24.124]:38132) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1mb2ht-0003UG-GX for qemu-devel@nongnu.org; Thu, 14 Oct 2021 11:31:49 -0400 Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-383-I1hqp1VSOUyfI9kFHxPiEQ-1; Thu, 14 Oct 2021 11:31:40 -0400 Received: from smtp.corp.redhat.com (int-mx08.intmail.prod.int.phx2.redhat.com [10.5.11.23]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 38E0F8042F4; Thu, 14 Oct 2021 15:31:39 +0000 (UTC) Received: from horse.redhat.com (unknown [10.22.16.190]) by smtp.corp.redhat.com (Postfix) with ESMTP id ED8E119D9D; Thu, 14 Oct 2021 15:31:38 +0000 (UTC) Received: by horse.redhat.com (Postfix, from userid 10451) id 5CEF9223EA1; Thu, 14 Oct 2021 11:31:37 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1634225504; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=uW5lxn1bHHfc/OHP6BJKdh+0nYCDz46voC85rbxp/us=; b=AsLG8QBOBLJYoCedLwbh1ukd9XNv+lmOfU7RGfgCxDxmq7uaVdPUzm2h6+v05fSVmD1HaX VlmS3UwgzMrB1YaGiglcEK8TA5qQiKDXcWkDyouSx3KLW8gCd4ByaHhKzuAVOkHt68xhXX PW+HOXJM2fxIQ6NDO1od1JAkt+tvQI4= X-MC-Unique: I1hqp1VSOUyfI9kFHxPiEQ-1 From: Vivek Goyal To: qemu-devel@nongnu.org, virtio-fs@redhat.com Subject: [PATCH v2 6/6] virtiofsd: Add an option to enable/disable security label Date: Thu, 14 Oct 2021 11:31:26 -0400 Message-Id: <20211014153126.575173-7-vgoyal@redhat.com> In-Reply-To: <20211014153126.575173-1-vgoyal@redhat.com> References: <20211014153126.575173-1-vgoyal@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.84 on 10.5.11.23 Authentication-Results: relay.mimecast.com; auth=pass smtp.auth=CUSA124A263 smtp.mailfrom=vgoyal@redhat.com X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=216.205.24.124; envelope-from=vgoyal@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -28 X-Spam_score: -2.9 X-Spam_bar: -- X-Spam_report: (-2.9 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.049, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_LOW=-0.7, RCVD_IN_MSPIKE_H2=-0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: miklos@szeredi.hu, chirantan@chromium.org, stephen.smalley.work@gmail.com, dwalsh@redhat.com, dgilbert@redhat.com, omosnace@redhat.com, casey@schaufler-ca.com, vgoyal@redhat.com Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail-DKIM: fail (Header signature does not verify) X-ZM-MESSAGEID: 1634227866351100001 Content-Type: text/plain; charset="utf-8" Provide an option "-o security_label/no_security_label" to enable/disable security label functionality. By default these are turned off. If enabled, server will indicate to client that it is capable of handling one security label during file creation. Typically this is expected to be a SELinux label. File server will set this label on the file. It will try to set it atomically wherever possible. But its not possible in all the cases. Signed-off-by: Vivek Goyal --- docs/tools/virtiofsd.rst | 7 +++++++ tools/virtiofsd/helper.c | 1 + tools/virtiofsd/passthrough_ll.c | 15 +++++++++++++++ 3 files changed, 23 insertions(+) diff --git a/docs/tools/virtiofsd.rst b/docs/tools/virtiofsd.rst index cc31402830..54699b2013 100644 --- a/docs/tools/virtiofsd.rst +++ b/docs/tools/virtiofsd.rst @@ -104,6 +104,13 @@ Options * posix_acl|no_posix_acl - Enable/disable posix acl support. Posix ACLs are disabled by default. =20 + * security_label|no_security_label - + Enable/disable security label support. Security labels are disabled by + default. This will allow client to send a MAC label of file during + file creation. Typically this is expected to be SELinux security + label. Server will try to set that label on newly created file + atomically wherever possible. + .. option:: --socket-path=3DPATH =20 Listen on vhost-user UNIX domain socket at PATH. diff --git a/tools/virtiofsd/helper.c b/tools/virtiofsd/helper.c index a8295d975a..e226fc590f 100644 --- a/tools/virtiofsd/helper.c +++ b/tools/virtiofsd/helper.c @@ -187,6 +187,7 @@ void fuse_cmdline_help(void) " default: no_allow_direct_io\n" " -o announce_submounts Announce sub-mount points to th= e guest\n" " -o posix_acl/no_posix_acl Enable/Disable posix_acl. (defa= ult: disabled)\n" + " -o security_label/no_security_label Enable/Disable securi= ty label. (default: disabled)\n" ); } =20 diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough= _ll.c index 4505c0c363..4334885619 100644 --- a/tools/virtiofsd/passthrough_ll.c +++ b/tools/virtiofsd/passthrough_ll.c @@ -180,6 +180,7 @@ struct lo_data { int user_posix_acl, posix_acl; /* Keeps track if /proc//attr/fscreate should be used or not */ bool use_fscreate; + int user_security_label; }; =20 static const struct fuse_opt lo_opts[] =3D { @@ -214,6 +215,8 @@ static const struct fuse_opt lo_opts[] =3D { { "no_killpriv_v2", offsetof(struct lo_data, user_killpriv_v2), 0 }, { "posix_acl", offsetof(struct lo_data, user_posix_acl), 1 }, { "no_posix_acl", offsetof(struct lo_data, user_posix_acl), 0 }, + { "security_label", offsetof(struct lo_data, user_security_label), 1 }, + { "no_security_label", offsetof(struct lo_data, user_security_label), = 0 }, FUSE_OPT_END }; static bool use_syslog =3D false; @@ -770,6 +773,17 @@ static void lo_init(void *userdata, struct fuse_conn_i= nfo *conn) fuse_log(FUSE_LOG_DEBUG, "lo_init: disabling posix_acl\n"); conn->want &=3D ~FUSE_CAP_POSIX_ACL; } + + if (lo->user_security_label =3D=3D 1) { + if (!(conn->capable & FUSE_CAP_SECURITY_CTX)) { + fuse_log(FUSE_LOG_ERR, "lo_init: Can not enable security label= ." + " kernel does not support FUSE_SECURITY_CTX capabilit= y.\n"); + } + conn->want |=3D FUSE_CAP_SECURITY_CTX; + } else { + fuse_log(FUSE_LOG_DEBUG, "lo_init: disabling security label\n"); + conn->want &=3D ~FUSE_CAP_SECURITY_CTX; + } } =20 static void lo_getattr(fuse_req_t req, fuse_ino_t ino, @@ -4254,6 +4268,7 @@ int main(int argc, char *argv[]) .proc_self_task =3D -1, .user_killpriv_v2 =3D -1, .user_posix_acl =3D -1, + .user_security_label =3D -1, }; struct lo_map_elem *root_elem; struct lo_map_elem *reserve_elem; --=20 2.31.1