Re: [PATCH v4 3/4] ovl: Validate verity xattr when resolving lowerdata

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Wed, Jun 21, 2023 at 2:18 PM Alexander Larsson <alexl@xxxxxxxxxx> wrote:
>
> 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 <alexl@xxxxxxxxxx>

Nice! looks much cleaner than v4.

Reviewed-by: Amir Goldstein <amir73il@xxxxxxxxx>


> ---
>  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 <linux/cred.h>
>  #include <linux/xattr.h>
>  #include <linux/exportfs.h>
> +#include <linux/file.h>
>  #include <linux/fileattr.h>
>  #include <linux/uuid.h>
>  #include <linux/namei.h>
> @@ -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
>   *
> --
> 2.40.1
>




[Index of Archives]     [Linux Filesystems Devel]     [Linux NFS]     [Linux NILFS]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux