From patchwork Tue Jun 20 10:15:16 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alexander Larsson X-Patchwork-Id: 13285569 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 484148464 for ; Tue, 20 Jun 2023 10:15:38 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1687256137; 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=7ZhzI4A6Uh38ta+x0+sJVS+q13rnYn7FB1fQoMrfiQ4=; b=fcn7/v7EWPwpiFtZGX/7Vs97E0cICj5qR5xOHSFIEy7Y017ZKkhMeRYkdDqzg7ccskl1+o DWqp6awEBMz1ri5ETNb7z4MhiO1fYUzLih19YLn2O0gnN/73STVicOndDfACJ7Or00NcB2 nWKvdWJ6hoqnvsG9AZzzSICSYBtxpp4= 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-557-FmRLuwLAOTiMkqI9Ot167w-1; Tue, 20 Jun 2023 06:15:36 -0400 X-MC-Unique: FmRLuwLAOTiMkqI9Ot167w-1 Received: by mail-lj1-f197.google.com with SMTP id 38308e7fff4ca-2b465ab1cc0so23665061fa.2 for ; Tue, 20 Jun 2023 03:15:35 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1687256134; x=1689848134; 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=7ZhzI4A6Uh38ta+x0+sJVS+q13rnYn7FB1fQoMrfiQ4=; b=JpHX6h7nggViuxCyR2k1NNHRyH3HmvMCFGzd/5CmxdhXp2R4q4TPxVCrdrr5/vivvd JdlH8r3PrioOgEqp+GGljvR6SL/uoPdbPBk5YQa+8btv2OmV2sh24KnUOAcakRTD/QUP 8h0IOO3pSKNg+qBJsTt3wJqbHFBWP0vuzEzx5EnBScUpJZMdCMDKZCawnqmqyZKfwSO+ FUXyJDL/W6CxvFRoq7/MWYujVopiEsRgySCIdlRd30fqjkxQDwb6yzY8mpnCH+QNuNi+ lGvZpcxbzWAWKwOuXpnOc2GGZUYu80l5JbV20MWwUWqZv2M7kvCyvu51J+A61t8QD2S8 4xDw== X-Gm-Message-State: AC+VfDzneLtWQJDcUBTAXvYpw6iQNiatG84xpqVsD/NBsU1xOxt+AVPX hDC42hC+mwVJGgKobTXrysPMAzmWhsdSJiAaaY4RLJcRB1tSSq+/elqx0oI8UemoFcrJflLE9nd TYk/LiAKqjTnYewipb1I= X-Received: by 2002:a2e:9548:0:b0:2ad:99dd:de07 with SMTP id t8-20020a2e9548000000b002ad99ddde07mr6444940ljh.16.1687256134270; Tue, 20 Jun 2023 03:15:34 -0700 (PDT) X-Google-Smtp-Source: ACHHUZ7wJjwLBOmqIy6c28JNpw1HsbtQlQdxP4iSNXtxNiHSi12hDOgX7qWV4Jz8fFSxBg2IIxhy8Q== X-Received: by 2002:a2e:9548:0:b0:2ad:99dd:de07 with SMTP id t8-20020a2e9548000000b002ad99ddde07mr6444925ljh.16.1687256134001; Tue, 20 Jun 2023 03:15:34 -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 h9-20020a2e9009000000b002b326e7e76csm337280ljg.64.2023.06.20.03.15.33 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 20 Jun 2023 03:15:33 -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/3] ovl: Add framework for verity support Date: Tue, 20 Jun 2023 12:15:16 +0200 Message-Id: <42967a7766fe73deca9ab9412923e11df1ceef50.1687255035.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 which contains a fs-verity digest that need to match the actual 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 --- Documentation/filesystems/fsverity.rst | 2 + Documentation/filesystems/overlayfs.rst | 48 +++++++++++++++++++ fs/overlayfs/overlayfs.h | 7 +++ fs/overlayfs/ovl_entry.h | 1 + fs/overlayfs/super.c | 64 ++++++++++++++++++++++--- 5 files changed, 116 insertions(+), 6 deletions(-) diff --git a/Documentation/filesystems/fsverity.rst b/Documentation/filesystems/fsverity.rst index ede672dedf11..b3ba548e7b86 100644 --- a/Documentation/filesystems/fsverity.rst +++ b/Documentation/filesystems/fsverity.rst @@ -324,6 +324,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..b639d9efe9ae 100644 --- a/Documentation/filesystems/overlayfs.rst +++ b/Documentation/filesystems/overlayfs.rst @@ -405,6 +405,54 @@ 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 +"trusted.overlay.verity" xattr is set on the new metacopy file. This +specifies the expected fs-verity digest of the lowerdata file, which +is used to verify the content of the lower file at 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 verity xattr is never 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 xattr will be set + from the source file fs-verity digest (if it has one). +- "require": + Same as "on", but additionally all metacopy files must specify a + verity xattr. 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 5b6ac03af192..7414d6d8fb1c 100644 --- a/fs/overlayfs/overlayfs.h +++ b/fs/overlayfs/overlayfs.h @@ -70,12 +70,19 @@ 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; bool redirect; bool nfs_export; bool index; + bool verity; }; /* 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 ed4b35c9d647..3f8bbd158a2a 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), {} }; @@ -557,6 +579,10 @@ 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; + ctx->set.verity = true; + break; default: pr_err("unrecognized mount option \"%s\" or missing value\n", param->key); @@ -596,6 +622,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. @@ -610,11 +648,12 @@ static int ovl_fs_params_verify(const struct ovl_fs_context *ctx, ovl_redirect_mode(config)); return -EINVAL; } + if (set.verity && 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 - * in this conflict. - */ pr_info("disabling metacopy due to redirect_dir=%s\n", ovl_redirect_mode(config)); config->metacopy = false; @@ -646,7 +685,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"); @@ -659,6 +698,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 (set.verity) { + /* + * 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 @@ -670,7 +717,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) { @@ -682,6 +729,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 Tue Jun 20 10:15:17 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alexander Larsson X-Patchwork-Id: 13285570 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 B071B8464 for ; Tue, 20 Jun 2023 10:15:41 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1687256140; 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=YyXLyLcrae35VQgkA1DToRhqM9Qs9H5q9zjjJ5/E0f4=; b=OkTJN9W+N54v4oUCv6rblxrhbMb1rBke0BEAE4K/pWynMwAJLfckaVYjt6SrM9GybKj2vi qdWFu3zpfFvwTZzAxKdvG2QRzmzjP3s11yMfE+kMej74scrHHNchQMCBHUOMsS2GjRNGHB l0wlrwB/rqoMs/7JZEKm430CXMonklE= 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-522-oZOtQjj3Mxyne31LgHr0VA-1; Tue, 20 Jun 2023 06:15:38 -0400 X-MC-Unique: oZOtQjj3Mxyne31LgHr0VA-1 Received: by mail-lj1-f199.google.com with SMTP id 38308e7fff4ca-2b448b13faaso34306471fa.2 for ; Tue, 20 Jun 2023 03:15:38 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1687256137; x=1689848137; 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=YyXLyLcrae35VQgkA1DToRhqM9Qs9H5q9zjjJ5/E0f4=; b=L+T3LjWqDovt0aoKZF0diorJLpu/NCplX3qRmmO4t5rJgoTyLRlos7y+p7zBKfVFt0 WmMLO2j9nPACEzsG14vepEkoNqEcZ9ETjVTt3xMeyO2qZMJ2H8gzI1v6ka+WjeveI8PG r/RFC/hNfOYZs0+rbdkDJjC/vdBdNCeK14M1onwoppTchBq3t8ZljZxZdAW0MLlqJtW1 fJZMoEdVd48Aw3/GDZMVeIDr1XcjSdRAXjGw3AP2b5QnYXu3LW+6K4RHgVmqne825I3L n4/WcMPqAMI7jtwl8xLiCyTSQ23JXgv7HmjwL3eybP9jWkQdjk6sTbRaM6XKH6JkHwEL Jl5w== X-Gm-Message-State: AC+VfDy3K4wfssvqv0V74Ecbq0iR51V8nVBNoSEN5p5KvcG1EjcuP35M HbGHO3lMyhL94gAceoxfnYYRR/ZGN9rPzKLT6LBsIDpCj60UdKYqYa5FPhCulPXuaQon5lyJj/Z xpT81FgwFMXR3827m9Dg= X-Received: by 2002:a2e:9098:0:b0:2b4:68a3:90df with SMTP id l24-20020a2e9098000000b002b468a390dfmr4853341ljg.2.1687256136881; Tue, 20 Jun 2023 03:15:36 -0700 (PDT) X-Google-Smtp-Source: ACHHUZ5L3xzdavc9TprYY89wQBHrZxMia387CLz8qp5WxqaRDZgTjmgbk1g3ZaM6XLLexnX1+F+plg== X-Received: by 2002:a2e:9098:0:b0:2b4:68a3:90df with SMTP id l24-20020a2e9098000000b002b468a390dfmr4853326ljg.2.1687256136496; Tue, 20 Jun 2023 03:15:36 -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 h9-20020a2e9009000000b002b326e7e76csm337280ljg.64.2023.06.20.03.15.35 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 20 Jun 2023 03:15:36 -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/3] ovl: Validate verity xattr when resolving lowerdata Date: Tue, 20 Jun 2023 12:15:17 +0200 Message-Id: <41928d51510a72b97c257574a61d2bcc3aff49d1.1687255035.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 metacopy xattr is extended from currently empty to a versioned header with length, flags and an optional digest. During lookup we 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 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 --- fs/overlayfs/copy_up.c | 2 +- fs/overlayfs/file.c | 8 +-- fs/overlayfs/namei.c | 97 +++++++++++++++++++++++++++--- fs/overlayfs/overlayfs.h | 43 ++++++++++--- fs/overlayfs/super.c | 5 +- fs/overlayfs/util.c | 126 +++++++++++++++++++++++++++++++++++++-- 6 files changed, 256 insertions(+), 25 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 39737c2aaa84..6583d08fdb7a 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 57adf911735f..0ba8266a8125 100644 --- a/fs/overlayfs/namei.c +++ b/fs/overlayfs/namei.c @@ -26,6 +26,7 @@ struct ovl_lookup_data { bool last; char *redirect; bool metacopy; + bool metacopy_digest; /* Referring to last redirect xattr */ bool absolute_redirect; }; @@ -233,6 +234,7 @@ static int ovl_lookup_single(struct dentry *base, struct ovl_lookup_data *d, { struct dentry *this; struct path path; + int metacopy_size; int err; bool last_element = !post[0]; @@ -270,11 +272,14 @@ 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); - if (err < 0) + metacopy_size = ovl_check_metacopy_xattr(OVL_FS(d->sb), &path, NULL); + if (metacopy_size < 0) { + err = metacopy_size; goto out_err; + } - d->metacopy = err; + d->metacopy = metacopy_size; + d->metacopy_digest = d->metacopy && metacopy_size > OVL_METACOPY_MIN_SIZE; d->stop = !d->metacopy; if (!d->metacopy || d->last) goto out; @@ -889,8 +894,59 @@ 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_HAS_DIGEST, inode) && + !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 +991,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) { @@ -952,9 +1019,11 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, bool upperopaque = false; char *upperredirect = NULL; struct dentry *this; + int metacopy_size; unsigned int i; int err; bool uppermetacopy = false; + bool metacopy_digest = false; struct ovl_lookup_data d = { .sb = dentry->d_sb, .name = dentry->d_name, @@ -964,6 +1033,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, .last = ovl_redirect_follow(ofs) ? false : !ovl_numlower(poe), .redirect = NULL, .metacopy = false, + .metacopy_digest = false, }; if (dentry->d_name.len > ofs->namelen) @@ -997,8 +1067,10 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, if (err) goto out_put_upper; - if (d.metacopy) + if (d.metacopy) { uppermetacopy = true; + metacopy_digest = d.metacopy_digest; + } } if (d.redirect) { @@ -1076,6 +1148,9 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, origin = this; } + if (!upperdentry && !d.is_dir && !ctr && d.metacopy) + metacopy_digest = d.metacopy_digest; + if (d.metacopy && ctr) { /* * Do not store intermediate metacopy dentries in @@ -1211,10 +1286,13 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, upperredirect = NULL; goto out_free_oe; } - err = ovl_check_metacopy_xattr(ofs, &upperpath); - if (err < 0) + metacopy_size = ovl_check_metacopy_xattr(ofs, &upperpath, NULL); + if (metacopy_size < 0) { + err = metacopy_size; goto out_free_oe; - uppermetacopy = err; + } + uppermetacopy = metacopy_size; + metacopy_digest = metacopy_size > OVL_METACOPY_MIN_SIZE; } if (upperdentry || ctr) { @@ -1236,6 +1314,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 ((uppermetacopy || ctr > 1) && metacopy_digest) + 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 7414d6d8fb1c..c2213a8ad16e 100644 --- a/fs/overlayfs/overlayfs.h +++ b/fs/overlayfs/overlayfs.h @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -49,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 { @@ -141,6 +144,24 @@ 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, not including unused 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) +{ + 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) { @@ -241,7 +262,7 @@ static inline ssize_t ovl_do_getxattr(const struct path *path, const char *name, WARN_ON(path->dentry->d_sb != path->mnt->mnt_sb); err = vfs_getxattr(mnt_idmap(path->mnt), path->dentry, - name, value, size); + name, value, size); len = (value && err > 0) ? err : 0; pr_debug("getxattr(%pd2, \"%s\", \"%*pE\", %zu, 0) = %i\n", @@ -263,9 +284,9 @@ static inline ssize_t ovl_getxattr_upper(struct ovl_fs *ofs, } static inline ssize_t ovl_path_getxattr(struct ovl_fs *ofs, - const struct path *path, - enum ovl_xattr ox, void *value, - size_t size) + const struct path *path, + enum ovl_xattr ox, void *value, + size_t size) { return ovl_do_getxattr(path, ovl_xattr(ofs, ox), value, size); } @@ -352,7 +373,7 @@ static inline struct file *ovl_do_tmpfile(struct ovl_fs *ofs, { struct path path = { .mnt = ovl_upper_mnt(ofs), .dentry = dentry }; struct file *file = vfs_tmpfile_open(ovl_upper_mnt_idmap(ofs), &path, mode, - O_LARGEFILE | O_WRONLY, current_cred()); + O_LARGEFILE | O_WRONLY, current_cred()); int err = PTR_ERR_OR_ZERO(file); pr_debug("tmpfile(%pd2, 0%o) = %i\n", dentry, mode, err); @@ -490,9 +511,17 @@ 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); +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) @@ -612,7 +641,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 3f8bbd158a2a..2175e64d3b64 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 7ef9e13c404a..66448964f753 100644 --- a/fs/overlayfs/util.c +++ b/fs/overlayfs/util.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -1054,8 +1055,9 @@ 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 */ +int ovl_check_metacopy_xattr(struct ovl_fs *ofs, const struct path *path, + struct ovl_metacopy *data) { int res; @@ -1063,7 +1065,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,12 +1080,48 @@ 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; } +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); @@ -1145,6 +1184,85 @@ 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. + * We use open_with_fake_path to avoid ENFILE. + */ + filp = open_with_fake_path(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]; + u8 verity_algo; + int xattr_digest_size; + int digest_size; + int err; + + if (!ofs->config.verity_mode || + /* Verity only works on regular files */ + !S_ISREG(d_inode(metapath->dentry)->i_mode)) + return 0; + + err = ovl_check_metacopy_xattr(ofs, metapath, &metacopy_data); + if (err < 0) + return err; + + if (err == 0 || metacopy_data.digest_algo == 0) { + 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 Tue Jun 20 10:15:18 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alexander Larsson X-Patchwork-Id: 13285571 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 A1C4DFC06 for ; Tue, 20 Jun 2023 10:15:43 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1687256142; 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=0/plbFkiB3xiNJdo1Kzyd7qBv1wjmF9S1CTNqi0Uzy0=; b=LAGoiFEKsjXCi/BoCyyac+hnqVULGOTJOZUUKZclxF9m8FPI+xcQ4/aOjnTDKKt+8oN5X8 /Ojc0L7m9SMom65m9q6wL/zKivLH0LkGSkkhNyMLHNep+ELlI4UF9n0kjX10nauM6YxcLb IHrF3hIWsImbYNb1ki+zo3/UEZV6AzU= 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-589-QAyzDipROpCKFkmw9K0hkw-1; Tue, 20 Jun 2023 06:15:40 -0400 X-MC-Unique: QAyzDipROpCKFkmw9K0hkw-1 Received: by mail-lj1-f199.google.com with SMTP id 38308e7fff4ca-2b46e684046so21183381fa.0 for ; Tue, 20 Jun 2023 03:15:40 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1687256139; x=1689848139; 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=0/plbFkiB3xiNJdo1Kzyd7qBv1wjmF9S1CTNqi0Uzy0=; b=EzkdKconjfAr380X63moBb0EBvczT/YEkBniBd0cKL7J7WV/7thvJiRWiQ12ieVQsn cHwOvAV+YJ4ieKKqqasQ+toap7rVJQDfo72WZYCWB+W3ls0eIxw6eFjuPOHiZg4l2ApE 28rigFBNyb/O7piyLBtNF0ARvHFg4iviA5ANrFht0jYAuDQxMKTa4x6tk4E6qK9myXyy wEmk1vvD56xA5jvlSTuf+fSaD+6zjpJKqXEdZEvv3QwAAX0Sx3xYunxRzPfqZ73m9pk5 DfSoWVb6kMwr96f7qV2sKhWQbtYCtQRxpVeaKF4seSKohN+WYg+ab1c8jnTQGG/rFAU7 MbkA== X-Gm-Message-State: AC+VfDxmIiShamy/917lI10ufa1bxCL1dQqRn4aSwn3Y5wOg6L4YD0Fv HL6hPWuOhn2qb56KGyBe4u1B5A/i7b9U3URnu95Lxw0/3xtbm5FFgU7yXFV4tvDoyEmzQ025aU7 kPS0Q69mDDB8yOteTZac= X-Received: by 2002:a05:651c:207:b0:2b4:7d01:f174 with SMTP id y7-20020a05651c020700b002b47d01f174mr3137471ljn.13.1687256138821; Tue, 20 Jun 2023 03:15:38 -0700 (PDT) X-Google-Smtp-Source: ACHHUZ5zUpI2uK5iNmJMcQO1hgQnDuYukd3V6JqYdBUG8DG3WFWavofShZx4ReeDS/OvBH5jDmGgQw== X-Received: by 2002:a05:651c:207:b0:2b4:7d01:f174 with SMTP id y7-20020a05651c020700b002b47d01f174mr3137459ljn.13.1687256138490; Tue, 20 Jun 2023 03:15:38 -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 h9-20020a2e9009000000b002b326e7e76csm337280ljg.64.2023.06.20.03.15.37 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 20 Jun 2023 03:15:38 -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/3] ovl: Handle verity during copy-up Date: Tue, 20 Jun 2023 12:15:18 +0200 Message-Id: <14eaa0223125470ec8cf38b6185b2a94b14ee313.1687255035.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 --- fs/overlayfs/copy_up.c | 45 ++++++++++++++++++++++++++++++++++++++-- fs/overlayfs/overlayfs.h | 3 +++ fs/overlayfs/util.c | 32 +++++++++++++++++++++++++++- 3 files changed, 77 insertions(+), 3 deletions(-) diff --git a/fs/overlayfs/copy_up.c b/fs/overlayfs/copy_up.c index 68f01fd7f211..6e6c25836e52 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 != 0) + 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 c2213a8ad16e..eef4a3243e8a 100644 --- a/fs/overlayfs/overlayfs.h +++ b/fs/overlayfs/overlayfs.h @@ -517,11 +517,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 66448964f753..3841f04baf35 100644 --- a/fs/overlayfs/util.c +++ b/fs/overlayfs/util.c @@ -1185,7 +1185,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; @@ -1263,6 +1263,36 @@ 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) { + 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 *