ovl: Handle verity during copy-up
authorAlexander Larsson <alexl@redhat.com>
Wed, 19 Apr 2023 11:58:45 +0000 (13:58 +0200)
committerAmir Goldstein <amir73il@gmail.com>
Sat, 12 Aug 2023 16:02:38 +0000 (19:02 +0300)
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 <alexl@redhat.com>
Reviewed-by: Amir Goldstein <amir73il@gmail.com>
Signed-off-by: Amir Goldstein <amir73il@gmail.com>
fs/overlayfs/copy_up.c
fs/overlayfs/overlayfs.h
fs/overlayfs/util.c

index 68f01fd..2ead7c9 100644 (file)
@@ -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,20 @@ 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))
+                       return -EIO;
+               err = ovl_get_verity_digest(ofs, &lowerdatapath, &metacopy_data);
+               if (err)
+                       return err;
+
+               if (metacopy_data.digest_algo)
+                       c->metacopy_digest = true;
+
+               err = ovl_set_metacopy_xattr(ofs, temp, &metacopy_data);
                if (err)
                        return err;
        }
@@ -751,9 +764,15 @@ static int ovl_copy_up_workdir(struct ovl_copy_up_ctx *c)
        if (err)
                goto cleanup;
 
-       if (!c->metacopy)
-               ovl_set_upperdata(d_inode(c->dentry));
        inode = d_inode(c->dentry);
+       if (c->metacopy_digest)
+               ovl_set_flag(OVL_HAS_DIGEST, inode);
+       else
+               ovl_clear_flag(OVL_HAS_DIGEST, inode);
+       ovl_clear_flag(OVL_VERIFIED_DIGEST, inode);
+
+       if (!c->metacopy)
+               ovl_set_upperdata(inode);
        ovl_inode_update(inode, temp);
        if (S_ISDIR(inode->i_mode))
                ovl_set_flag(OVL_WHITEOUTS, inode);
@@ -813,6 +832,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 +943,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_active(d_inode(lowerdata.dentry))) {
+                       return false;
+               }
+       }
+
        return true;
 }
 
@@ -984,6 +1022,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);
index 02ce600..488bd14 100644 (file)
@@ -487,11 +487,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_get_verity_digest(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)
index 1df6230..500133f 100644 (file)
@@ -1188,7 +1188,7 @@ err_free:
 }
 
 /* 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);
        struct file *filp;
@@ -1262,6 +1262,37 @@ int ovl_validate_verity(struct ovl_fs *ofs,
        return 0;
 }
 
+int ovl_get_verity_digest(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
  *