From patchwork Wed Jun 21 11:18:25 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alexander Larsson X-Patchwork-Id: 13287180 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id BDE2ADF5C for ; Wed, 21 Jun 2023 11:18:50 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1687346329; 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=fCuJ11M8PXky/LzyNEp52jQ/rFklAhN4unnyWApjkPs=; b=YGuAl0LWS7/XnGpBh56efvoLeRV+9auDw8lfay4uKiz1RmOG9yOqZ8PSMTtX8Fx0KxUVmv yRlgkpyjA8XzOkB2F+b4yzFZVCuiahdSAI3MtYSaNkkApyyR7FCU+Dm5GJHlsLrjktHfU3 UwRdPUjbVQ2cGnLg326LWW7h48th3YA= Received: from mail-lj1-f198.google.com (mail-lj1-f198.google.com [209.85.208.198]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-408-CgMMOWv6OJ285Cnz4I_Gaw-1; Wed, 21 Jun 2023 07:18:48 -0400 X-MC-Unique: CgMMOWv6OJ285Cnz4I_Gaw-1 Received: by mail-lj1-f198.google.com with SMTP id 38308e7fff4ca-2b341f911acso27509471fa.1 for ; Wed, 21 Jun 2023 04:18:47 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1687346326; x=1689938326; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=fCuJ11M8PXky/LzyNEp52jQ/rFklAhN4unnyWApjkPs=; b=iIvtHahQtbkGHPq5xxp0bm9WuN/Cfy61TAisinj+cMSPvUTX3qUUWcrq3k6f9TQoQu NIhMYN+YSBDvSzGKqR+ugLJsrc2o8Z9OYEBk3WjjMQDIo2sUqdipanA1cF4hQw9BBlWB E7Twe7lzct6wwFQ5tKuDup7qncZrjdnvJ/dYdJl+D+5Mu1KCfNrzt0+AAnUCj0JE5VHQ R7TdHAAD/K9kfB6TwYd+lwr/gJd8Uoj0PVBDCW1D6GQmp0eZAM5ZDnJ2v/P565vjtCZw 6LG7HTzYY0qvCL8apVrSMHXQkbQ8I4vrQKz7OAzoR7bi2AprQqZ76KZC6eo+YIlGgQcr PhVw== X-Gm-Message-State: AC+VfDyhF3IiJo4fL0iOX3SICkiBEJtoc8IVnygXZnKerD/KMfnFfThv UOWD27zV1Gswt/NjXCdMDIur+VVnzYaczo9IhJV8s9PY3n/kCXNK07/CCh7n5c+enBc1Ef6OgJK fOjcnwmOUrdO8trxFgGw= X-Received: by 2002:a2e:9b01:0:b0:2a2:ac00:4de4 with SMTP id u1-20020a2e9b01000000b002a2ac004de4mr5394690lji.22.1687346326552; Wed, 21 Jun 2023 04:18:46 -0700 (PDT) X-Google-Smtp-Source: ACHHUZ4mrkIyt3YWrRCZSiU548w7Ar3SFjsaykdv6TWFGzlW9gKekCh66NaFjz01Dki+9xzOUVH1Ug== X-Received: by 2002:a2e:9b01:0:b0:2a2:ac00:4de4 with SMTP id u1-20020a2e9b01000000b002a2ac004de4mr5394679lji.22.1687346326149; Wed, 21 Jun 2023 04:18:46 -0700 (PDT) Received: from localhost.localdomain (c-e6a5e255.022-110-73746f36.bbcust.telenor.se. [85.226.165.230]) by smtp.googlemail.com with ESMTPSA id 3-20020a05651c00c300b002b31ec01c97sm864436ljr.15.2023.06.21.04.18.45 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 21 Jun 2023 04:18:45 -0700 (PDT) From: Alexander Larsson To: miklos@szeredi.hu Cc: linux-unionfs@vger.kernel.org, amir73il@gmail.com, ebiggers@kernel.org, tytso@mit.edu, fsverity@lists.linux.dev, Alexander Larsson Subject: [PATCH v4 1/4] ovl: Add framework for verity support Date: Wed, 21 Jun 2023 13:18:25 +0200 Message-Id: <8bbfe13980cc9aa70e347811280b62eba930ffd2.1687345663.git.alexl@redhat.com> X-Mailer: git-send-email 2.40.1 In-Reply-To: References: Precedence: bulk X-Mailing-List: fsverity@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com This adds the scaffolding (docs, config, mount options) for supporting the new digest field in the metacopy xattr. This contains a fs-verity digest that need to match the fs-verity digest of the lowerdata file. The mount option "verity" specifies how this xattr is handled. If you enable verity ("verity=on") all existing xattrs are validated before use, and during metacopy we generate verity xattr in the upper metacopy file (if the source file has verity enabled). This means later accesses can guarantee that the same data is used. Additionally you can use "verity=require". In this mode all metacopy files must have a valid verity xattr. For this to work metadata copy-up must be able to create a verity xattr (so that later accesses are validated). Therefore, in this mode, if the lower data file doesn't have fs-verity enabled we fall back to a full copy rather than a metacopy. Actual implementation follows in a separate commit. Signed-off-by: Alexander Larsson Reviewed-by: Amir Goldstein Acked-by: Eric Biggers --- Documentation/filesystems/fsverity.rst | 2 + Documentation/filesystems/overlayfs.rst | 47 +++++++++++++++++++ fs/overlayfs/overlayfs.h | 6 +++ fs/overlayfs/ovl_entry.h | 1 + fs/overlayfs/super.c | 61 +++++++++++++++++++++++-- 5 files changed, 114 insertions(+), 3 deletions(-) diff --git a/Documentation/filesystems/fsverity.rst b/Documentation/filesystems/fsverity.rst index cb845e8e5435..13e4b18e5dbb 100644 --- a/Documentation/filesystems/fsverity.rst +++ b/Documentation/filesystems/fsverity.rst @@ -326,6 +326,8 @@ the file has fs-verity enabled. This can perform better than FS_IOC_GETFLAGS and FS_IOC_MEASURE_VERITY because it doesn't require opening the file, and opening verity files can be expensive. +.. _accessing_verity_files: + Accessing verity files ====================== diff --git a/Documentation/filesystems/overlayfs.rst b/Documentation/filesystems/overlayfs.rst index eb7d2c88ddec..b63e0db03631 100644 --- a/Documentation/filesystems/overlayfs.rst +++ b/Documentation/filesystems/overlayfs.rst @@ -405,6 +405,53 @@ when a "metacopy" file in one of the lower layers above it, has a "redirect" to the absolute path of the "lower data" file in the "data-only" lower layer. +fs-verity support +---------------------- + +During metadata copy up of a lower file, if the source file has +fs-verity enabled and overlay verity support is enabled, then the +digest of the lower file is added to the "trusted.overlay.metacopy" +xattr. This is then used to verify the content of the lower file +each the time the metacopy file is opened. + +When a layer containing verity xattrs is used, it means that any such +metacopy file in the upper layer is guaranteed to match the content +that was in the lower at the time of the copy-up. If at any time +(during a mount, after a remount, etc) such a file in the lower is +replaced or modified in any way, access to the corresponding file in +overlayfs will result in EIO errors (either on open, due to overlayfs +digest check, or from a later read due to fs-verity) and a detailed +error is printed to the kernel logs. For more details of how fs-verity +file access works, see :ref:`Documentation/filesystems/fsverity.rst +`. + +Verity can be used as a general robustness check to detect accidental +changes in the overlayfs directories in use. But, with additional care +it can also give more powerful guarantees. For example, if the upper +layer is fully trusted (by using dm-verity or something similar), then +an untrusted lower layer can be used to supply validated file content +for all metacopy files. If additionally the untrusted lower +directories are specified as "Data-only", then they can only supply +such file content, and the entire mount can be trusted to match the +upper layer. + +This feature is controlled by the "verity" mount option, which +supports these values: + +- "off": + The metacopy digest is never generated or used. This is the + default if verity option is not specified. +- "on": + Whenever a metacopy files specifies an expected digest, the + corresponding data file must match the specified digest. When + generating a metacopy file the verity digest will be set in it + based on the source file (if it has one). +- "require": + Same as "on", but additionally all metacopy files must specify a + digest (or EIO is returned on open). This means metadata copy up + will only be used if the data file has fs-verity enabled, + otherwise a full copy-up is used. + Sharing and copying layers -------------------------- diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h index 4142d1a457ff..cf92a9aaf934 100644 --- a/fs/overlayfs/overlayfs.h +++ b/fs/overlayfs/overlayfs.h @@ -70,6 +70,12 @@ enum { OVL_XINO_ON, }; +enum { + OVL_VERITY_OFF, + OVL_VERITY_ON, + OVL_VERITY_REQUIRE, +}; + /* The set of options that user requested explicitly via mount options */ struct ovl_opt_set { bool metacopy; diff --git a/fs/overlayfs/ovl_entry.h b/fs/overlayfs/ovl_entry.h index 306e1ecdc96d..e999c73fb0c3 100644 --- a/fs/overlayfs/ovl_entry.h +++ b/fs/overlayfs/ovl_entry.h @@ -10,6 +10,7 @@ struct ovl_config { char *workdir; bool default_permissions; int redirect_mode; + int verity_mode; bool index; bool uuid; bool nfs_export; diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index c14c52560fd6..a4eb9abd4b52 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c @@ -366,6 +366,23 @@ static inline int ovl_xino_def(void) return ovl_xino_auto_def ? OVL_XINO_AUTO : OVL_XINO_OFF; } +static const struct constant_table ovl_parameter_verity[] = { + { "off", OVL_VERITY_OFF }, + { "on", OVL_VERITY_ON }, + { "require", OVL_VERITY_REQUIRE }, + {} +}; + +static const char *ovl_verity_mode(struct ovl_config *config) +{ + return ovl_parameter_verity[config->verity_mode].name; +} + +static int ovl_verity_mode_def(void) +{ + return OVL_VERITY_OFF; +} + /** * ovl_show_options * @m: the seq_file handle @@ -414,6 +431,9 @@ static int ovl_show_options(struct seq_file *m, struct dentry *dentry) seq_puts(m, ",volatile"); if (ofs->config.userxattr) seq_puts(m, ",userxattr"); + if (ofs->config.verity_mode != ovl_verity_mode_def()) + seq_printf(m, ",verity=%s", + ovl_verity_mode(&ofs->config)); return 0; } @@ -463,6 +483,7 @@ enum { Opt_xino, Opt_metacopy, Opt_volatile, + Opt_verity, }; static const struct constant_table ovl_parameter_bool[] = { @@ -487,6 +508,7 @@ static const struct fs_parameter_spec ovl_parameter_spec[] = { fsparam_enum("xino", Opt_xino, ovl_parameter_xino), fsparam_enum("metacopy", Opt_metacopy, ovl_parameter_bool), fsparam_flag("volatile", Opt_volatile), + fsparam_enum("verity", Opt_verity, ovl_parameter_verity), {} }; @@ -568,6 +590,9 @@ static int ovl_parse_param(struct fs_context *fc, struct fs_parameter *param) case Opt_userxattr: config->userxattr = true; break; + case Opt_verity: + config->verity_mode = result.uint_32; + break; default: pr_err("unrecognized mount option \"%s\" or missing value\n", param->key); @@ -607,6 +632,18 @@ static int ovl_fs_params_verify(const struct ovl_fs_context *ctx, config->ovl_volatile = false; } + /* Resolve verity -> metacopy dependency */ + if (config->verity_mode && !config->metacopy) { + /* Don't allow explicit specified conflicting combinations */ + if (set.metacopy) { + pr_err("conflicting options: metacopy=off,verity=%s\n", + ovl_verity_mode(config)); + return -EINVAL; + } + /* Otherwise automatically enable metacopy. */ + config->metacopy = true; + } + /* * This is to make the logic below simpler. It doesn't make any other * difference, since redirect_dir=on is only used for upper. @@ -614,13 +651,18 @@ static int ovl_fs_params_verify(const struct ovl_fs_context *ctx, if (!config->upperdir && config->redirect_mode == OVL_REDIRECT_FOLLOW) config->redirect_mode = OVL_REDIRECT_ON; - /* Resolve metacopy -> redirect_dir dependency */ + /* Resolve verity -> metacopy -> redirect_dir dependency */ if (config->metacopy && config->redirect_mode != OVL_REDIRECT_ON) { if (set.metacopy && set.redirect) { pr_err("conflicting options: metacopy=on,redirect_dir=%s\n", ovl_redirect_mode(config)); return -EINVAL; } + if (config->verity_mode && set.redirect) { + pr_err("conflicting options: verity=%s,redirect_dir=%s\n", + ovl_verity_mode(config), ovl_redirect_mode(config)); + return -EINVAL; + } if (set.redirect) { /* * There was an explicit redirect_dir=... that resulted @@ -657,7 +699,7 @@ static int ovl_fs_params_verify(const struct ovl_fs_context *ctx, } } - /* Resolve nfs_export -> !metacopy dependency */ + /* Resolve nfs_export -> !metacopy && !verity dependency */ if (config->nfs_export && config->metacopy) { if (set.nfs_export && set.metacopy) { pr_err("conflicting options: nfs_export=on,metacopy=on\n"); @@ -670,6 +712,14 @@ static int ovl_fs_params_verify(const struct ovl_fs_context *ctx, */ pr_info("disabling nfs_export due to metacopy=on\n"); config->nfs_export = false; + } else if (config->verity_mode) { + /* + * There was an explicit verity=.. that resulted + * in this conflict. + */ + pr_info("disabling nfs_export due to verity=%s\n", + ovl_verity_mode(config)); + config->nfs_export = false; } else { /* * There was an explicit nfs_export=on that resulted @@ -681,7 +731,7 @@ static int ovl_fs_params_verify(const struct ovl_fs_context *ctx, } - /* Resolve userxattr -> !redirect && !metacopy dependency */ + /* Resolve userxattr -> !redirect && !metacopy && !verity dependency */ if (config->userxattr) { if (set.redirect && config->redirect_mode != OVL_REDIRECT_NOFOLLOW) { @@ -693,6 +743,11 @@ static int ovl_fs_params_verify(const struct ovl_fs_context *ctx, pr_err("conflicting options: userxattr,metacopy=on\n"); return -EINVAL; } + if (config->verity_mode) { + pr_err("conflicting options: userxattr,verity=%s\n", + ovl_verity_mode(config)); + return -EINVAL; + } /* * Silently disable default setting of redirect and metacopy. * This shall be the default in the future as well: these From patchwork Wed Jun 21 11:18:26 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alexander Larsson X-Patchwork-Id: 13287181 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 63C23FC0E for ; Wed, 21 Jun 2023 11:18:52 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1687346331; 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=baEB8SFr5XmkqSog/87DNGoOof8ryDCan+YiIoUGM/Q=; b=cfWSwyOmxlH7C/5kUzTzelyFb5g8pfG1DstcGi7GC8sx91PL1pdmwlGV8vh58/tbjBL628 DzpucFFSPUogibAac2vZ8uvqTPx/hoqPZzVlvzGWhZqr5n5pYE4fyjhfxlfwAwBoLe5nxd SIsqv/d94S9ZN1AwEPH9y70gqB8iL+o= Received: from mail-lf1-f69.google.com (mail-lf1-f69.google.com [209.85.167.69]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-456-WT0gYUgzM_Kw8oeGzOIAcg-1; Wed, 21 Jun 2023 07:18:49 -0400 X-MC-Unique: WT0gYUgzM_Kw8oeGzOIAcg-1 Received: by mail-lf1-f69.google.com with SMTP id 2adb3069b0e04-4edc7406cbaso4501376e87.2 for ; Wed, 21 Jun 2023 04:18:49 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1687346328; x=1689938328; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=baEB8SFr5XmkqSog/87DNGoOof8ryDCan+YiIoUGM/Q=; b=hxl5L278ukKwDmYkjCHZQtNukk5KBpdJ4+hL2Q3CbcVs/4FqqzG6K3Kq/uBO0a0vBl ANJSpgcXRF/28R7rUbu4xNTUzA9Mi8jysIEGQA0cj93zIzIccBWsEYmm1nXMysvthMGr 0EtL/Heu5P96zE3jQd2kijAN9XY2AnM41ifbAA5JjXa1lN/H7BQ6krctLgSMFZb48hL8 3PZDhq99vMVwKmJ5xf/f1mWuf6RIs/qldeM0DTcbTg80DtPA9Gz+5ZVisOwpLfXljTGU LwhdTK7RUgZRss1vxAYvVfvMZiA9jCFfnC2EoUSEVw2zkABVUhmxvxRxZsptpP67w8xp 12AA== X-Gm-Message-State: AC+VfDyoZY0L6RKOPhtBzzatKSyyp/cD7weA+Z0C8YOx0Av8disbfdi/ 0WSCJOZ7/EVa7U1fu0rEekk5WdN5T6nv2b+g6Is0Fl+a6efsFdnxuxdA25J/QQSJ0TfPEggP0QQ zck05IDMJqZelIprqabw= X-Received: by 2002:a2e:95c4:0:b0:2b4:6a20:f12d with SMTP id y4-20020a2e95c4000000b002b46a20f12dmr7851552ljh.43.1687346328165; Wed, 21 Jun 2023 04:18:48 -0700 (PDT) X-Google-Smtp-Source: ACHHUZ5MN8FKocPJuJbfQI7I14mRobJ/26QLR1UZUG/uuIlZQ36wK95bo08ig3IUW2Cib9RymMfe6g== X-Received: by 2002:a2e:95c4:0:b0:2b4:6a20:f12d with SMTP id y4-20020a2e95c4000000b002b46a20f12dmr7851537ljh.43.1687346327936; Wed, 21 Jun 2023 04:18:47 -0700 (PDT) Received: from localhost.localdomain (c-e6a5e255.022-110-73746f36.bbcust.telenor.se. [85.226.165.230]) by smtp.googlemail.com with ESMTPSA id 3-20020a05651c00c300b002b31ec01c97sm864436ljr.15.2023.06.21.04.18.47 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 21 Jun 2023 04:18:47 -0700 (PDT) From: Alexander Larsson To: miklos@szeredi.hu Cc: linux-unionfs@vger.kernel.org, amir73il@gmail.com, ebiggers@kernel.org, tytso@mit.edu, fsverity@lists.linux.dev, Alexander Larsson Subject: [PATCH v4 2/4] ovl: Add versioned header for overlay.metacopy xattr Date: Wed, 21 Jun 2023 13:18:26 +0200 Message-Id: X-Mailer: git-send-email 2.40.1 In-Reply-To: References: Precedence: bulk X-Mailing-List: fsverity@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Historically overlay.metacopy was a zero-size xattr, and it's existence marked a metacopy file. This change adds a versioned header with a flag field, a length and a digest. The initial use-case of this will be for validating a fs-verity digest, but the flags field could also be used later for other new features. ovl_check_metacopy_xattr() now returns the size of the xattr, emulating a size of OVL_METACOPY_MIN_SIZE for empty xattrs to distinguish it from the no-xattr case. Signed-off-by: Alexander Larsson Reviewed-by: Amir Goldstein --- fs/overlayfs/namei.c | 10 +++++----- fs/overlayfs/overlayfs.h | 24 +++++++++++++++++++++++- fs/overlayfs/util.c | 37 +++++++++++++++++++++++++++++++++---- 3 files changed, 61 insertions(+), 10 deletions(-) diff --git a/fs/overlayfs/namei.c b/fs/overlayfs/namei.c index 57adf911735f..3dd480253710 100644 --- a/fs/overlayfs/namei.c +++ b/fs/overlayfs/namei.c @@ -25,7 +25,7 @@ struct ovl_lookup_data { bool stop; bool last; char *redirect; - bool metacopy; + int metacopy; /* Referring to last redirect xattr */ bool absolute_redirect; }; @@ -270,7 +270,7 @@ static int ovl_lookup_single(struct dentry *base, struct ovl_lookup_data *d, d->stop = true; goto put_and_out; } - err = ovl_check_metacopy_xattr(OVL_FS(d->sb), &path); + err = ovl_check_metacopy_xattr(OVL_FS(d->sb), &path, NULL); if (err < 0) goto out_err; @@ -963,7 +963,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, .stop = false, .last = ovl_redirect_follow(ofs) ? false : !ovl_numlower(poe), .redirect = NULL, - .metacopy = false, + .metacopy = 0, }; if (dentry->d_name.len > ofs->namelen) @@ -1120,7 +1120,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, /* Defer lookup of lowerdata in data-only layers to first access */ if (d.metacopy && ctr && ofs->numdatalayer && d.absolute_redirect) { - d.metacopy = false; + d.metacopy = 0; ctr++; } @@ -1211,7 +1211,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, upperredirect = NULL; goto out_free_oe; } - err = ovl_check_metacopy_xattr(ofs, &upperpath); + err = ovl_check_metacopy_xattr(ofs, &upperpath, NULL); if (err < 0) goto out_free_oe; uppermetacopy = err; diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h index cf92a9aaf934..6d4e08df0dfe 100644 --- a/fs/overlayfs/overlayfs.h +++ b/fs/overlayfs/overlayfs.h @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -140,6 +141,26 @@ struct ovl_fh { #define OVL_FH_FID_OFFSET (OVL_FH_WIRE_OFFSET + \ offsetof(struct ovl_fb, fid)) +/* On-disk format for "metacopy" xattr (if non-zero size) */ +struct ovl_metacopy { + u8 version; /* 0 */ + u8 len; /* size of this header + used digest bytes */ + u8 flags; + u8 digest_algo; /* FS_VERITY_HASH_ALG_* constant, 0 for no digest */ + u8 digest[FS_VERITY_MAX_DIGEST_SIZE]; /* Only the used part on disk */ +} __packed; + +#define OVL_METACOPY_MAX_SIZE (sizeof(struct ovl_metacopy)) +#define OVL_METACOPY_MIN_SIZE (OVL_METACOPY_MAX_SIZE - FS_VERITY_MAX_DIGEST_SIZE) +#define OVL_METACOPY_INIT { 0, OVL_METACOPY_MIN_SIZE } + +static inline int ovl_metadata_digest_size(const struct ovl_metacopy *metacopy) +{ + if (metacopy->len < OVL_METACOPY_MIN_SIZE) + return 0; + return (int)metacopy->len - OVL_METACOPY_MIN_SIZE; +} + extern const char *const ovl_xattr_table[][2]; static inline const char *ovl_xattr(struct ovl_fs *ofs, enum ovl_xattr ox) { @@ -490,7 +511,8 @@ bool ovl_need_index(struct dentry *dentry); int ovl_nlink_start(struct dentry *dentry); void ovl_nlink_end(struct dentry *dentry); int ovl_lock_rename_workdir(struct dentry *workdir, struct dentry *upperdir); -int ovl_check_metacopy_xattr(struct ovl_fs *ofs, const struct path *path); +int ovl_check_metacopy_xattr(struct ovl_fs *ofs, const struct path *path, + struct ovl_metacopy *data); bool ovl_is_metacopy_dentry(struct dentry *dentry); char *ovl_get_redirect_xattr(struct ovl_fs *ofs, const struct path *path, int padding); int ovl_sync_status(struct ovl_fs *ofs); diff --git a/fs/overlayfs/util.c b/fs/overlayfs/util.c index 7ef9e13c404a..921747223991 100644 --- a/fs/overlayfs/util.c +++ b/fs/overlayfs/util.c @@ -1054,8 +1054,12 @@ int ovl_lock_rename_workdir(struct dentry *workdir, struct dentry *upperdir) return -EIO; } -/* err < 0, 0 if no metacopy xattr, 1 if metacopy xattr found */ -int ovl_check_metacopy_xattr(struct ovl_fs *ofs, const struct path *path) +/* + * err < 0, 0 if no metacopy xattr, metacopy data size if xattr found. + * an empty xattr returns OVL_METACOPY_MIN_SIZE to distinguish from no xattr value. + */ +int ovl_check_metacopy_xattr(struct ovl_fs *ofs, const struct path *path, + struct ovl_metacopy *data) { int res; @@ -1063,7 +1067,8 @@ int ovl_check_metacopy_xattr(struct ovl_fs *ofs, const struct path *path) if (!S_ISREG(d_inode(path->dentry)->i_mode)) return 0; - res = ovl_path_getxattr(ofs, path, OVL_XATTR_METACOPY, NULL, 0); + res = ovl_path_getxattr(ofs, path, OVL_XATTR_METACOPY, + data, data ? OVL_METACOPY_MAX_SIZE : 0); if (res < 0) { if (res == -ENODATA || res == -EOPNOTSUPP) return 0; @@ -1077,7 +1082,31 @@ int ovl_check_metacopy_xattr(struct ovl_fs *ofs, const struct path *path) goto out; } - return 1; + if (res == 0) { + /* Emulate empty data for zero size metacopy xattr */ + res = OVL_METACOPY_MIN_SIZE; + if (data) { + memset(data, 0, res); + data->len = res; + } + } else if (res < OVL_METACOPY_MIN_SIZE) { + pr_warn_ratelimited("metacopy file '%pd' has too small xattr\n", + path->dentry); + return -EIO; + } else if (data) { + if (data->version != 0) { + pr_warn_ratelimited("metacopy file '%pd' has unsupported version\n", + path->dentry); + return -EIO; + } + if (res != data->len) { + pr_warn_ratelimited("metacopy file '%pd' has invalid xattr size\n", + path->dentry); + return -EIO; + } + } + + return res; out: pr_warn_ratelimited("failed to get metacopy (%i)\n", res); return res; From patchwork Wed Jun 21 11:18:27 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alexander Larsson X-Patchwork-Id: 13287183 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 23B39DF5C for ; Wed, 21 Jun 2023 11:18:59 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1687346338; 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=Rjo399+wcuyE2zVId1qURGU7w+RhUWmDcqipWvpFaXU=; b=SEP9TDO56KN7MMBni52aeIuzrg1ZpAoXs6GdlaORhmx9vdLT2OrdK+huncuBZ+s1uTiQob 6y4rQxmbIKEdF1cejPm1BOOsi4HLZXtaviUAZyB3+RXIJclLpzHQkpwiGwjxYFkojxTegj vT5yMeGnJS6RRXgKjKcLHUxLwlmwuRA= Received: from mail-lj1-f197.google.com (mail-lj1-f197.google.com [209.85.208.197]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-526-nL5GOCR0MX2MnR8bW2tPhA-1; Wed, 21 Jun 2023 07:18:51 -0400 X-MC-Unique: nL5GOCR0MX2MnR8bW2tPhA-1 Received: by mail-lj1-f197.google.com with SMTP id 38308e7fff4ca-2b1d8fa4629so45130211fa.0 for ; Wed, 21 Jun 2023 04:18:51 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1687346330; x=1689938330; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=Rjo399+wcuyE2zVId1qURGU7w+RhUWmDcqipWvpFaXU=; b=BLoTeD4yZ1/vypv7M/z//qcDH8JoeOxuj0YOuxUXRND7/JMQxOltRRFBOmkdxyI27K Z5RtH7j4KMTzVnxOnU9lyxSClGUHmDtak98tU6PSSEfNyY4iq9af56eALqE9/RvocTx/ lt7/36kH1KxVfWy0jAqWWuyZNa070Ts9BYSGrY8AiXScF+VdOX2osoqOjDCcpP4IcybQ t/W3Qk47uBJcpfXSOi0FMjATdevX9HZaCIAlPwE1ADee9xrzkpXSVUA3AhHtA36MwVGM TNmSgoQ5P4YPQ4X2SzsOnbvogkG8q/VFnLxfQFzYcGaNjpsnR147aHV+WfNy9wjyYYMo o1Vw== X-Gm-Message-State: AC+VfDxt/wfaKAJW3jcMGk1LakkD6rAeU/wgXUSZpDoPjzEgstQe9Uzm ZPuHX6LBY0zFcyCHbBHQ1j8sm1pjP5wBRta7Jxu40+ykGP5zyu4+NISmvYqhb2nvAhpbf7+e03v VSQlS2IkjMNC28O0hnDU= X-Received: by 2002:a2e:8805:0:b0:2b5:7fea:b361 with SMTP id x5-20020a2e8805000000b002b57feab361mr2464192ljh.40.1687346329955; Wed, 21 Jun 2023 04:18:49 -0700 (PDT) X-Google-Smtp-Source: ACHHUZ4WCAYHA7FAlWXrEc4skfJmABBcHqB/G5vEsf7aHbox0fd6sL2lkE0d62IgOIEV+R2abbQ84g== X-Received: by 2002:a2e:8805:0:b0:2b5:7fea:b361 with SMTP id x5-20020a2e8805000000b002b57feab361mr2464183ljh.40.1687346329578; Wed, 21 Jun 2023 04:18:49 -0700 (PDT) Received: from localhost.localdomain (c-e6a5e255.022-110-73746f36.bbcust.telenor.se. [85.226.165.230]) by smtp.googlemail.com with ESMTPSA id 3-20020a05651c00c300b002b31ec01c97sm864436ljr.15.2023.06.21.04.18.48 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 21 Jun 2023 04:18:49 -0700 (PDT) From: Alexander Larsson To: miklos@szeredi.hu Cc: linux-unionfs@vger.kernel.org, amir73il@gmail.com, ebiggers@kernel.org, tytso@mit.edu, fsverity@lists.linux.dev, Alexander Larsson Subject: [PATCH v4 3/4] ovl: Validate verity xattr when resolving lowerdata Date: Wed, 21 Jun 2023 13:18:27 +0200 Message-Id: <5dfdecee8f0260729c4a8e8150587f128a731ccb.1687345663.git.alexl@redhat.com> X-Mailer: git-send-email 2.40.1 In-Reply-To: References: Precedence: bulk X-Mailing-List: fsverity@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com The new digest field in the metacopy xattr is used during lookup to record whether the header contained a digest in the OVL_HAS_DIGEST flags. When accessing file data the first time, if OVL_HAS_DIGEST is set, we reload the metadata and check that the source lowerdata inode matches the specified digest in it (according to the enabled verity options). If the verity check passes we store this info in the inode flags as OVL_VERIFIED_DIGEST, so that we can avoid doing it again if the inode remains in memory. The verification is done in ovl_maybe_validate_verity() which needs to be called in the same places as ovl_maybe_lookup_lowerdata(), so there is a new ovl_verify_lowerdata() helper that calls these in the right order, and all current callers of ovl_maybe_lookup_lowerdata() are changed to call it instead. Signed-off-by: Alexander Larsson Reviewed-by: Amir Goldstein --- fs/overlayfs/copy_up.c | 2 +- fs/overlayfs/file.c | 8 ++-- fs/overlayfs/namei.c | 72 +++++++++++++++++++++++++++++++- fs/overlayfs/overlayfs.h | 11 ++++- fs/overlayfs/super.c | 5 ++- fs/overlayfs/util.c | 90 ++++++++++++++++++++++++++++++++++++++++ 6 files changed, 180 insertions(+), 8 deletions(-) diff --git a/fs/overlayfs/copy_up.c b/fs/overlayfs/copy_up.c index 568f743a5584..68f01fd7f211 100644 --- a/fs/overlayfs/copy_up.c +++ b/fs/overlayfs/copy_up.c @@ -1078,7 +1078,7 @@ static int ovl_copy_up_flags(struct dentry *dentry, int flags) * not very important to optimize this case, so do lazy lowerdata lookup * before any copy up, so we can do it before taking ovl_inode_lock(). */ - err = ovl_maybe_lookup_lowerdata(dentry); + err = ovl_verify_lowerdata(dentry); if (err) return err; diff --git a/fs/overlayfs/file.c b/fs/overlayfs/file.c index cb53c84108fc..859a833c1035 100644 --- a/fs/overlayfs/file.c +++ b/fs/overlayfs/file.c @@ -115,8 +115,8 @@ static int ovl_real_fdget_meta(const struct file *file, struct fd *real, if (allow_meta) { ovl_path_real(dentry, &realpath); } else { - /* lazy lookup of lowerdata */ - err = ovl_maybe_lookup_lowerdata(dentry); + /* lazy lookup and verify of lowerdata */ + err = ovl_verify_lowerdata(dentry); if (err) return err; @@ -159,8 +159,8 @@ static int ovl_open(struct inode *inode, struct file *file) struct path realpath; int err; - /* lazy lookup of lowerdata */ - err = ovl_maybe_lookup_lowerdata(dentry); + /* lazy lookup and verify lowerdata */ + err = ovl_verify_lowerdata(dentry); if (err) return err; diff --git a/fs/overlayfs/namei.c b/fs/overlayfs/namei.c index 3dd480253710..d00ec43f2376 100644 --- a/fs/overlayfs/namei.c +++ b/fs/overlayfs/namei.c @@ -889,8 +889,58 @@ static int ovl_fix_origin(struct ovl_fs *ofs, struct dentry *dentry, return err; } +static int ovl_maybe_validate_verity(struct dentry *dentry) +{ + struct ovl_fs *ofs = dentry->d_sb->s_fs_info; + struct inode *inode = d_inode(dentry); + struct path datapath, metapath; + int err; + + if (!ofs->config.verity_mode || + !ovl_is_metacopy_dentry(dentry) || + ovl_test_flag(OVL_VERIFIED_DIGEST, inode)) + return 0; + + if (!ovl_test_flag(OVL_HAS_DIGEST, inode)) { + if (ofs->config.verity_mode == OVL_VERITY_REQUIRE) { + pr_warn_ratelimited("metacopy file '%pd' has no digest specified\n", + dentry); + return -EIO; + } + return 0; + } + + ovl_path_lowerdata(dentry, &datapath); + if (!datapath.dentry) + return -EIO; + + ovl_path_real(dentry, &metapath); + if (!metapath.dentry) + return -EIO; + + err = ovl_inode_lock_interruptible(inode); + if (err) + return err; + + if (!ovl_test_flag(OVL_VERIFIED_DIGEST, inode)) { + const struct cred *old_cred; + + old_cred = ovl_override_creds(dentry->d_sb); + + err = ovl_validate_verity(ofs, &metapath, &datapath); + if (err == 0) + ovl_set_flag(OVL_VERIFIED_DIGEST, inode); + + revert_creds(old_cred); + } + + ovl_inode_unlock(inode); + + return err; +} + /* Lazy lookup of lowerdata */ -int ovl_maybe_lookup_lowerdata(struct dentry *dentry) +static int ovl_maybe_lookup_lowerdata(struct dentry *dentry) { struct inode *inode = d_inode(dentry); const char *redirect = ovl_lowerdata_redirect(inode); @@ -935,6 +985,17 @@ int ovl_maybe_lookup_lowerdata(struct dentry *dentry) goto out; } +int ovl_verify_lowerdata(struct dentry *dentry) +{ + int err; + + err = ovl_maybe_lookup_lowerdata(dentry); + if (err) + return err; + + return ovl_maybe_validate_verity(dentry); +} + struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags) { @@ -955,6 +1016,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, unsigned int i; int err; bool uppermetacopy = false; + int metacopy_size = 0; struct ovl_lookup_data d = { .sb = dentry->d_sb, .name = dentry->d_name, @@ -999,6 +1061,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, if (d.metacopy) uppermetacopy = true; + metacopy_size = d.metacopy; } if (d.redirect) { @@ -1076,6 +1139,9 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, origin = this; } + if (!upperdentry && !d.is_dir && !ctr && d.metacopy) + metacopy_size = d.metacopy; + if (d.metacopy && ctr) { /* * Do not store intermediate metacopy dentries in @@ -1215,6 +1281,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, if (err < 0) goto out_free_oe; uppermetacopy = err; + metacopy_size = err; } if (upperdentry || ctr) { @@ -1236,6 +1303,9 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, goto out_free_oe; if (upperdentry && !uppermetacopy) ovl_set_flag(OVL_UPPERDATA, inode); + + if (metacopy_size > OVL_METACOPY_MIN_SIZE) + ovl_set_flag(OVL_HAS_DIGEST, inode); } ovl_dentry_init_reval(dentry, upperdentry, OVL_I_E(inode)); diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h index 6d4e08df0dfe..9fbbc077643b 100644 --- a/fs/overlayfs/overlayfs.h +++ b/fs/overlayfs/overlayfs.h @@ -50,6 +50,8 @@ enum ovl_inode_flag { OVL_UPPERDATA, /* Inode number will remain constant over copy up. */ OVL_CONST_INO, + OVL_HAS_DIGEST, + OVL_VERIFIED_DIGEST, }; enum ovl_entry_flag { @@ -513,8 +515,15 @@ void ovl_nlink_end(struct dentry *dentry); int ovl_lock_rename_workdir(struct dentry *workdir, struct dentry *upperdir); int ovl_check_metacopy_xattr(struct ovl_fs *ofs, const struct path *path, struct ovl_metacopy *data); +int ovl_set_metacopy_xattr(struct ovl_fs *ofs, struct dentry *d, + struct ovl_metacopy *metacopy); bool ovl_is_metacopy_dentry(struct dentry *dentry); char *ovl_get_redirect_xattr(struct ovl_fs *ofs, const struct path *path, int padding); +int ovl_get_verity_xattr(struct ovl_fs *ofs, const struct path *path, + u8 *digest_buf, int *buf_length); +int ovl_validate_verity(struct ovl_fs *ofs, + struct path *metapath, + struct path *datapath); int ovl_sync_status(struct ovl_fs *ofs); static inline void ovl_set_flag(unsigned long flag, struct inode *inode) @@ -634,7 +643,7 @@ struct dentry *ovl_get_index_fh(struct ovl_fs *ofs, struct ovl_fh *fh); struct dentry *ovl_lookup_index(struct ovl_fs *ofs, struct dentry *upper, struct dentry *origin, bool verify); int ovl_path_next(int idx, struct dentry *dentry, struct path *path); -int ovl_maybe_lookup_lowerdata(struct dentry *dentry); +int ovl_verify_lowerdata(struct dentry *dentry); struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags); bool ovl_lower_positive(struct dentry *dentry); diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index a4eb9abd4b52..457a5bc439cb 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c @@ -63,6 +63,7 @@ static struct dentry *ovl_d_real(struct dentry *dentry, const struct inode *inode) { struct dentry *real = NULL, *lower; + int err; /* It's an overlay file */ if (inode && d_inode(dentry) == inode) @@ -89,7 +90,9 @@ static struct dentry *ovl_d_real(struct dentry *dentry, * uprobes on offset within the file, so lowerdata should be available * when setting the uprobe. */ - ovl_maybe_lookup_lowerdata(dentry); + err = ovl_verify_lowerdata(dentry); + if (err) + goto bug; lower = ovl_dentry_lowerdata(dentry); if (!lower) goto bug; diff --git a/fs/overlayfs/util.c b/fs/overlayfs/util.c index 921747223991..927a1133859d 100644 --- a/fs/overlayfs/util.c +++ b/fs/overlayfs/util.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -1112,6 +1113,18 @@ int ovl_check_metacopy_xattr(struct ovl_fs *ofs, const struct path *path, return res; } +int ovl_set_metacopy_xattr(struct ovl_fs *ofs, struct dentry *d, struct ovl_metacopy *metacopy) +{ + size_t len = metacopy->len; + + /* If no flags or digest fall back to empty metacopy file */ + if (metacopy->version == 0 && metacopy->flags == 0 && metacopy->digest_algo == 0) + len = 0; + + return ovl_check_setxattr(ofs, d, OVL_XATTR_METACOPY, + metacopy, len, -EOPNOTSUPP); +} + bool ovl_is_metacopy_dentry(struct dentry *dentry) { struct ovl_entry *oe = OVL_E(dentry); @@ -1174,6 +1187,83 @@ char *ovl_get_redirect_xattr(struct ovl_fs *ofs, const struct path *path, int pa return ERR_PTR(res); } +/* Call with mounter creds as it may open the file */ +static int ovl_ensure_verity_loaded(struct path *datapath) +{ + struct inode *inode = d_inode(datapath->dentry); + const struct fsverity_info *vi; + struct file *filp; + + vi = fsverity_get_info(inode); + if (vi == NULL && IS_VERITY(inode)) { + /* + * If this inode was not yet opened, the verity info hasn't been + * loaded yet, so we need to do that here to force it into memory. + */ + filp = kernel_file_open(datapath, O_RDONLY, inode, current_cred()); + if (IS_ERR(filp)) + return PTR_ERR(filp); + fput(filp); + } + + return 0; +} + +int ovl_validate_verity(struct ovl_fs *ofs, + struct path *metapath, + struct path *datapath) +{ + struct ovl_metacopy metacopy_data; + u8 actual_digest[FS_VERITY_MAX_DIGEST_SIZE]; + int xattr_digest_size, digest_size; + int xattr_size, err; + u8 verity_algo; + + if (!ofs->config.verity_mode || + /* Verity only works on regular files */ + !S_ISREG(d_inode(metapath->dentry)->i_mode)) + return 0; + + xattr_size = ovl_check_metacopy_xattr(ofs, metapath, &metacopy_data); + if (xattr_size < 0) + return xattr_size; + + if (!xattr_size || !metacopy_data.digest_algo) { + if (ofs->config.verity_mode == OVL_VERITY_REQUIRE) { + pr_warn_ratelimited("metacopy file '%pd' has no digest specified\n", + metapath->dentry); + return -EIO; + } + return 0; + } + + xattr_digest_size = ovl_metadata_digest_size(&metacopy_data); + + err = ovl_ensure_verity_loaded(datapath); + if (err < 0) { + pr_warn_ratelimited("lower file '%pd' failed to load fs-verity info\n", + datapath->dentry); + return -EIO; + } + + digest_size = fsverity_get_digest(d_inode(datapath->dentry), actual_digest, + &verity_algo, NULL); + if (digest_size == 0) { + pr_warn_ratelimited("lower file '%pd' has no fs-verity digest\n", datapath->dentry); + return -EIO; + } + + if (xattr_digest_size != digest_size || + metacopy_data.digest_algo != verity_algo || + memcmp(metacopy_data.digest, actual_digest, xattr_digest_size) != 0) { + pr_warn_ratelimited("lower file '%pd' has the wrong fs-verity digest\n", + datapath->dentry); + return -EIO; + } + + return 0; +} + /* * ovl_sync_status() - Check fs sync status for volatile mounts * From patchwork Wed Jun 21 11:18:28 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alexander Larsson X-Patchwork-Id: 13287182 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 1CF7BDF5C for ; Wed, 21 Jun 2023 11:18:55 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1687346334; 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=oOUnohaMXefRhkI4iyZGMnRzQ4QyShTyqNghobmvALI=; b=jMN+DRvSfllYAftsanLQC/vJpleUpD1i0Lbu8WM8oaabTC2OD0Yth7imU+7yy8PlJfuh0N 1WNhwhhnWoKD5EF/XE18oNhaHubdijESp+onqcDXQ9R+WZWBUfY0xLdkNHo0DC3dTwRos2 WG0X8lG6XsYB9zR5/HQlfmr9ZOr12PA= Received: from mail-lj1-f199.google.com (mail-lj1-f199.google.com [209.85.208.199]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-453-3hF0KDLLMkGxTiRUBRF0nQ-1; Wed, 21 Jun 2023 07:18:53 -0400 X-MC-Unique: 3hF0KDLLMkGxTiRUBRF0nQ-1 Received: by mail-lj1-f199.google.com with SMTP id 38308e7fff4ca-2b477057d95so27117831fa.3 for ; Wed, 21 Jun 2023 04:18:53 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1687346332; x=1689938332; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=oOUnohaMXefRhkI4iyZGMnRzQ4QyShTyqNghobmvALI=; b=j4hHlkiDpee/Lp0js3moha0SWRuitFbmYIdNQGzXgHwPunVRJgaAsY8mEYRyhfK4eM arsHcc1FVc9PLq2HJKoA1/PUm7Ere1DHcwVW8W0x+g50yJp46FpntDiiv2vNYG4NnEl3 H+BmsY7uVP+x6YoMQIkKVlYKp65p8H+SCQe1NVB25QBXBQNQY4tMb9Zk1cnnhaltzBHn WGJFBitCQvno32fvuh9EbjjTIk/ORtTbiy1KrPXD94r7rQpZhRf7IVqSPmiQrHNcfg9Q pbYGxJzxjAoKTBXqz1JPsiqSlde/PCoRxTWKnMNsXTxgiP9fu9vpfZnGZ0ypNd3J2Jf6 S6OQ== X-Gm-Message-State: AC+VfDz/ZYjDOv4BxMkFe1chRRbasC3mQlYDb99CfoOP5QNrnuxei+df QVAfP8q0fnFNtTHo6UZkdjhivVKyjykYQvsSFAG46h8KTlUk1UsBj2nP2LSZRz9rJ8t6Izln+Kw RidecXf4btAoC8XH9elE= X-Received: by 2002:a2e:888f:0:b0:2b4:85d1:e299 with SMTP id k15-20020a2e888f000000b002b485d1e299mr3885351lji.5.1687346331824; Wed, 21 Jun 2023 04:18:51 -0700 (PDT) X-Google-Smtp-Source: ACHHUZ7mDgx4/rXl3EP8KFgYC8B1RJgfoSNPso3A7CQxIOB+JxEl7ZVWS4zkTAcsHrCwWAIPagrNdA== X-Received: by 2002:a2e:888f:0:b0:2b4:85d1:e299 with SMTP id k15-20020a2e888f000000b002b485d1e299mr3885343lji.5.1687346331607; Wed, 21 Jun 2023 04:18:51 -0700 (PDT) Received: from localhost.localdomain (c-e6a5e255.022-110-73746f36.bbcust.telenor.se. [85.226.165.230]) by smtp.googlemail.com with ESMTPSA id 3-20020a05651c00c300b002b31ec01c97sm864436ljr.15.2023.06.21.04.18.50 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 21 Jun 2023 04:18:51 -0700 (PDT) From: Alexander Larsson To: miklos@szeredi.hu Cc: linux-unionfs@vger.kernel.org, amir73il@gmail.com, ebiggers@kernel.org, tytso@mit.edu, fsverity@lists.linux.dev, Alexander Larsson Subject: [PATCH v4 4/4] ovl: Handle verity during copy-up Date: Wed, 21 Jun 2023 13:18:28 +0200 Message-Id: <8771725be2a8b7d65ea6c50a69bb6392b9e903aa.1687345663.git.alexl@redhat.com> X-Mailer: git-send-email 2.40.1 In-Reply-To: References: Precedence: bulk X-Mailing-List: fsverity@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com During regular metacopy, if lowerdata file has fs-verity enabled, and the verity option is enabled, we add the digest to the metacopy xattr. If verity is required, and lowerdata does not have fs-verity enabled, fall back to full copy-up (or the generated metacopy would not validate). Signed-off-by: Alexander Larsson Reviewed-by: Amir Goldstein --- fs/overlayfs/copy_up.c | 45 ++++++++++++++++++++++++++++++++++++++-- fs/overlayfs/overlayfs.h | 3 +++ fs/overlayfs/util.c | 33 ++++++++++++++++++++++++++++- 3 files changed, 78 insertions(+), 3 deletions(-) diff --git a/fs/overlayfs/copy_up.c b/fs/overlayfs/copy_up.c index 68f01fd7f211..fce7d048673c 100644 --- a/fs/overlayfs/copy_up.c +++ b/fs/overlayfs/copy_up.c @@ -544,6 +544,7 @@ struct ovl_copy_up_ctx { bool origin; bool indexed; bool metacopy; + bool metacopy_digest; }; static int ovl_link_up(struct ovl_copy_up_ctx *c) @@ -641,8 +642,21 @@ static int ovl_copy_up_metadata(struct ovl_copy_up_ctx *c, struct dentry *temp) } if (c->metacopy) { - err = ovl_check_setxattr(ofs, temp, OVL_XATTR_METACOPY, - NULL, 0, -EOPNOTSUPP); + struct path lowerdatapath; + struct ovl_metacopy metacopy_data = OVL_METACOPY_INIT; + + ovl_path_lowerdata(c->dentry, &lowerdatapath); + if (WARN_ON_ONCE(lowerdatapath.dentry == NULL)) + err = -EIO; + else + err = ovl_set_verity_xattr_from(ofs, &lowerdatapath, &metacopy_data); + + if (metacopy_data.digest_algo) + c->metacopy_digest = true; + + if (!err) + err = ovl_set_metacopy_xattr(ofs, temp, &metacopy_data); + if (err) return err; } @@ -751,6 +765,12 @@ static int ovl_copy_up_workdir(struct ovl_copy_up_ctx *c) if (err) goto cleanup; + if (c->metacopy_digest) + ovl_set_flag(OVL_HAS_DIGEST, d_inode(c->dentry)); + else + ovl_clear_flag(OVL_HAS_DIGEST, d_inode(c->dentry)); + ovl_clear_flag(OVL_VERIFIED_DIGEST, d_inode(c->dentry)); + if (!c->metacopy) ovl_set_upperdata(d_inode(c->dentry)); inode = d_inode(c->dentry); @@ -813,6 +833,12 @@ static int ovl_copy_up_tmpfile(struct ovl_copy_up_ctx *c) if (err) goto out_fput; + if (c->metacopy_digest) + ovl_set_flag(OVL_HAS_DIGEST, d_inode(c->dentry)); + else + ovl_clear_flag(OVL_HAS_DIGEST, d_inode(c->dentry)); + ovl_clear_flag(OVL_VERIFIED_DIGEST, d_inode(c->dentry)); + if (!c->metacopy) ovl_set_upperdata(d_inode(c->dentry)); ovl_inode_update(d_inode(c->dentry), dget(temp)); @@ -918,6 +944,19 @@ static bool ovl_need_meta_copy_up(struct dentry *dentry, umode_t mode, if (flags && ((OPEN_FMODE(flags) & FMODE_WRITE) || (flags & O_TRUNC))) return false; + /* Fall back to full copy if no fsverity on source data and we require verity */ + if (ofs->config.verity_mode == OVL_VERITY_REQUIRE) { + struct path lowerdata; + + ovl_path_lowerdata(dentry, &lowerdata); + + if (WARN_ON_ONCE(lowerdata.dentry == NULL) || + ovl_ensure_verity_loaded(&lowerdata) || + !fsverity_get_info(d_inode(lowerdata.dentry))) { + return false; + } + } + return true; } @@ -984,6 +1023,8 @@ static int ovl_copy_up_meta_inode_data(struct ovl_copy_up_ctx *c) if (err) goto out_free; + ovl_clear_flag(OVL_HAS_DIGEST, d_inode(c->dentry)); + ovl_clear_flag(OVL_VERIFIED_DIGEST, d_inode(c->dentry)); ovl_set_upperdata(d_inode(c->dentry)); out_free: kfree(capability); diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h index 9fbbc077643b..e728360c66ff 100644 --- a/fs/overlayfs/overlayfs.h +++ b/fs/overlayfs/overlayfs.h @@ -519,11 +519,14 @@ int ovl_set_metacopy_xattr(struct ovl_fs *ofs, struct dentry *d, struct ovl_metacopy *metacopy); bool ovl_is_metacopy_dentry(struct dentry *dentry); char *ovl_get_redirect_xattr(struct ovl_fs *ofs, const struct path *path, int padding); +int ovl_ensure_verity_loaded(struct path *path); int ovl_get_verity_xattr(struct ovl_fs *ofs, const struct path *path, u8 *digest_buf, int *buf_length); int ovl_validate_verity(struct ovl_fs *ofs, struct path *metapath, struct path *datapath); +int ovl_set_verity_xattr_from(struct ovl_fs *ofs, struct path *src, + struct ovl_metacopy *metacopy); int ovl_sync_status(struct ovl_fs *ofs); static inline void ovl_set_flag(unsigned long flag, struct inode *inode) diff --git a/fs/overlayfs/util.c b/fs/overlayfs/util.c index 927a1133859d..439e23496713 100644 --- a/fs/overlayfs/util.c +++ b/fs/overlayfs/util.c @@ -1188,7 +1188,7 @@ char *ovl_get_redirect_xattr(struct ovl_fs *ofs, const struct path *path, int pa } /* Call with mounter creds as it may open the file */ -static int ovl_ensure_verity_loaded(struct path *datapath) +int ovl_ensure_verity_loaded(struct path *datapath) { struct inode *inode = d_inode(datapath->dentry); const struct fsverity_info *vi; @@ -1264,6 +1264,37 @@ int ovl_validate_verity(struct ovl_fs *ofs, return 0; } +int ovl_set_verity_xattr_from(struct ovl_fs *ofs, struct path *src, + struct ovl_metacopy *metacopy) +{ + int err, digest_size; + + if (!ofs->config.verity_mode || !S_ISREG(d_inode(src->dentry)->i_mode)) + return 0; + + err = ovl_ensure_verity_loaded(src); + if (err < 0) { + pr_warn_ratelimited("lower file '%pd' failed to load fs-verity info\n", + src->dentry); + return -EIO; + } + + digest_size = fsverity_get_digest(d_inode(src->dentry), + metacopy->digest, &metacopy->digest_algo, NULL); + if (digest_size == 0 || + WARN_ON_ONCE(digest_size > FS_VERITY_MAX_DIGEST_SIZE)) { + if (ofs->config.verity_mode == OVL_VERITY_REQUIRE) { + pr_warn_ratelimited("lower file '%pd' has no fs-verity digest\n", + src->dentry); + return -EIO; + } + return 0; + } + + metacopy->len += digest_size; + return 0; +} + /* * ovl_sync_status() - Check fs sync status for volatile mounts *