Skip to content

Commit 184996e

Browse files
alexlarssonamir73il
authored andcommitted
ovl: Validate verity xattr when resolving lowerdata
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 <[email protected]> Reviewed-by: Amir Goldstein <[email protected]> Signed-off-by: Amir Goldstein <[email protected]>
1 parent bf07089 commit 184996e

File tree

6 files changed

+178
-8
lines changed

6 files changed

+178
-8
lines changed

fs/overlayfs/copy_up.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1078,7 +1078,7 @@ static int ovl_copy_up_flags(struct dentry *dentry, int flags)
10781078
* not very important to optimize this case, so do lazy lowerdata lookup
10791079
* before any copy up, so we can do it before taking ovl_inode_lock().
10801080
*/
1081-
err = ovl_maybe_lookup_lowerdata(dentry);
1081+
err = ovl_verify_lowerdata(dentry);
10821082
if (err)
10831083
return err;
10841084

fs/overlayfs/file.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -115,8 +115,8 @@ static int ovl_real_fdget_meta(const struct file *file, struct fd *real,
115115
if (allow_meta) {
116116
ovl_path_real(dentry, &realpath);
117117
} else {
118-
/* lazy lookup of lowerdata */
119-
err = ovl_maybe_lookup_lowerdata(dentry);
118+
/* lazy lookup and verify of lowerdata */
119+
err = ovl_verify_lowerdata(dentry);
120120
if (err)
121121
return err;
122122

@@ -159,8 +159,8 @@ static int ovl_open(struct inode *inode, struct file *file)
159159
struct path realpath;
160160
int err;
161161

162-
/* lazy lookup of lowerdata */
163-
err = ovl_maybe_lookup_lowerdata(dentry);
162+
/* lazy lookup and verify lowerdata */
163+
err = ovl_verify_lowerdata(dentry);
164164
if (err)
165165
return err;
166166

fs/overlayfs/namei.c

Lines changed: 71 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -889,8 +889,58 @@ static int ovl_fix_origin(struct ovl_fs *ofs, struct dentry *dentry,
889889
return err;
890890
}
891891

892+
static int ovl_maybe_validate_verity(struct dentry *dentry)
893+
{
894+
struct ovl_fs *ofs = dentry->d_sb->s_fs_info;
895+
struct inode *inode = d_inode(dentry);
896+
struct path datapath, metapath;
897+
int err;
898+
899+
if (!ofs->config.verity_mode ||
900+
!ovl_is_metacopy_dentry(dentry) ||
901+
ovl_test_flag(OVL_VERIFIED_DIGEST, inode))
902+
return 0;
903+
904+
if (!ovl_test_flag(OVL_HAS_DIGEST, inode)) {
905+
if (ofs->config.verity_mode == OVL_VERITY_REQUIRE) {
906+
pr_warn_ratelimited("metacopy file '%pd' has no digest specified\n",
907+
dentry);
908+
return -EIO;
909+
}
910+
return 0;
911+
}
912+
913+
ovl_path_lowerdata(dentry, &datapath);
914+
if (!datapath.dentry)
915+
return -EIO;
916+
917+
ovl_path_real(dentry, &metapath);
918+
if (!metapath.dentry)
919+
return -EIO;
920+
921+
err = ovl_inode_lock_interruptible(inode);
922+
if (err)
923+
return err;
924+
925+
if (!ovl_test_flag(OVL_VERIFIED_DIGEST, inode)) {
926+
const struct cred *old_cred;
927+
928+
old_cred = ovl_override_creds(dentry->d_sb);
929+
930+
err = ovl_validate_verity(ofs, &metapath, &datapath);
931+
if (err == 0)
932+
ovl_set_flag(OVL_VERIFIED_DIGEST, inode);
933+
934+
revert_creds(old_cred);
935+
}
936+
937+
ovl_inode_unlock(inode);
938+
939+
return err;
940+
}
941+
892942
/* Lazy lookup of lowerdata */
893-
int ovl_maybe_lookup_lowerdata(struct dentry *dentry)
943+
static int ovl_maybe_lookup_lowerdata(struct dentry *dentry)
894944
{
895945
struct inode *inode = d_inode(dentry);
896946
const char *redirect = ovl_lowerdata_redirect(inode);
@@ -935,6 +985,17 @@ int ovl_maybe_lookup_lowerdata(struct dentry *dentry)
935985
goto out;
936986
}
937987

988+
int ovl_verify_lowerdata(struct dentry *dentry)
989+
{
990+
int err;
991+
992+
err = ovl_maybe_lookup_lowerdata(dentry);
993+
if (err)
994+
return err;
995+
996+
return ovl_maybe_validate_verity(dentry);
997+
}
998+
938999
struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
9391000
unsigned int flags)
9401001
{
@@ -955,6 +1016,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
9551016
unsigned int i;
9561017
int err;
9571018
bool uppermetacopy = false;
1019+
int metacopy_size = 0;
9581020
struct ovl_lookup_data d = {
9591021
.sb = dentry->d_sb,
9601022
.name = dentry->d_name,
@@ -999,6 +1061,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
9991061

10001062
if (d.metacopy)
10011063
uppermetacopy = true;
1064+
metacopy_size = d.metacopy;
10021065
}
10031066

10041067
if (d.redirect) {
@@ -1076,6 +1139,9 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
10761139
origin = this;
10771140
}
10781141

1142+
if (!upperdentry && !d.is_dir && !ctr && d.metacopy)
1143+
metacopy_size = d.metacopy;
1144+
10791145
if (d.metacopy && ctr) {
10801146
/*
10811147
* Do not store intermediate metacopy dentries in
@@ -1215,6 +1281,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
12151281
if (err < 0)
12161282
goto out_free_oe;
12171283
uppermetacopy = err;
1284+
metacopy_size = err;
12181285
}
12191286

12201287
if (upperdentry || ctr) {
@@ -1236,6 +1303,9 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
12361303
goto out_free_oe;
12371304
if (upperdentry && !uppermetacopy)
12381305
ovl_set_flag(OVL_UPPERDATA, inode);
1306+
1307+
if (metacopy_size > OVL_METACOPY_MIN_SIZE)
1308+
ovl_set_flag(OVL_HAS_DIGEST, inode);
12391309
}
12401310

12411311
ovl_dentry_init_reval(dentry, upperdentry, OVL_I_E(inode));

fs/overlayfs/overlayfs.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ enum ovl_inode_flag {
5050
OVL_UPPERDATA,
5151
/* Inode number will remain constant over copy up. */
5252
OVL_CONST_INO,
53+
OVL_HAS_DIGEST,
54+
OVL_VERIFIED_DIGEST,
5355
};
5456

5557
enum ovl_entry_flag {
@@ -481,8 +483,15 @@ void ovl_nlink_end(struct dentry *dentry);
481483
int ovl_lock_rename_workdir(struct dentry *workdir, struct dentry *upperdir);
482484
int ovl_check_metacopy_xattr(struct ovl_fs *ofs, const struct path *path,
483485
struct ovl_metacopy *data);
486+
int ovl_set_metacopy_xattr(struct ovl_fs *ofs, struct dentry *d,
487+
struct ovl_metacopy *metacopy);
484488
bool ovl_is_metacopy_dentry(struct dentry *dentry);
485489
char *ovl_get_redirect_xattr(struct ovl_fs *ofs, const struct path *path, int padding);
490+
int ovl_get_verity_xattr(struct ovl_fs *ofs, const struct path *path,
491+
u8 *digest_buf, int *buf_length);
492+
int ovl_validate_verity(struct ovl_fs *ofs,
493+
struct path *metapath,
494+
struct path *datapath);
486495
int ovl_sync_status(struct ovl_fs *ofs);
487496

488497
static inline void ovl_set_flag(unsigned long flag, struct inode *inode)
@@ -602,7 +611,7 @@ struct dentry *ovl_get_index_fh(struct ovl_fs *ofs, struct ovl_fh *fh);
602611
struct dentry *ovl_lookup_index(struct ovl_fs *ofs, struct dentry *upper,
603612
struct dentry *origin, bool verify);
604613
int ovl_path_next(int idx, struct dentry *dentry, struct path *path);
605-
int ovl_maybe_lookup_lowerdata(struct dentry *dentry);
614+
int ovl_verify_lowerdata(struct dentry *dentry);
606615
struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
607616
unsigned int flags);
608617
bool ovl_lower_positive(struct dentry *dentry);

fs/overlayfs/super.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ static struct dentry *ovl_d_real(struct dentry *dentry,
3232
const struct inode *inode)
3333
{
3434
struct dentry *real = NULL, *lower;
35+
int err;
3536

3637
/* It's an overlay file */
3738
if (inode && d_inode(dentry) == inode)
@@ -58,7 +59,9 @@ static struct dentry *ovl_d_real(struct dentry *dentry,
5859
* uprobes on offset within the file, so lowerdata should be available
5960
* when setting the uprobe.
6061
*/
61-
ovl_maybe_lookup_lowerdata(dentry);
62+
err = ovl_verify_lowerdata(dentry);
63+
if (err)
64+
goto bug;
6265
lower = ovl_dentry_lowerdata(dentry);
6366
if (!lower)
6467
goto bug;

fs/overlayfs/util.c

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include <linux/cred.h>
1111
#include <linux/xattr.h>
1212
#include <linux/exportfs.h>
13+
#include <linux/file.h>
1314
#include <linux/fileattr.h>
1415
#include <linux/uuid.h>
1516
#include <linux/namei.h>
@@ -1112,6 +1113,18 @@ int ovl_check_metacopy_xattr(struct ovl_fs *ofs, const struct path *path,
11121113
return res;
11131114
}
11141115

1116+
int ovl_set_metacopy_xattr(struct ovl_fs *ofs, struct dentry *d, struct ovl_metacopy *metacopy)
1117+
{
1118+
size_t len = metacopy->len;
1119+
1120+
/* If no flags or digest fall back to empty metacopy file */
1121+
if (metacopy->version == 0 && metacopy->flags == 0 && metacopy->digest_algo == 0)
1122+
len = 0;
1123+
1124+
return ovl_check_setxattr(ofs, d, OVL_XATTR_METACOPY,
1125+
metacopy, len, -EOPNOTSUPP);
1126+
}
1127+
11151128
bool ovl_is_metacopy_dentry(struct dentry *dentry)
11161129
{
11171130
struct ovl_entry *oe = OVL_E(dentry);
@@ -1174,6 +1187,81 @@ char *ovl_get_redirect_xattr(struct ovl_fs *ofs, const struct path *path, int pa
11741187
return ERR_PTR(res);
11751188
}
11761189

1190+
/* Call with mounter creds as it may open the file */
1191+
static int ovl_ensure_verity_loaded(struct path *datapath)
1192+
{
1193+
struct inode *inode = d_inode(datapath->dentry);
1194+
struct file *filp;
1195+
1196+
if (!fsverity_active(inode) && IS_VERITY(inode)) {
1197+
/*
1198+
* If this inode was not yet opened, the verity info hasn't been
1199+
* loaded yet, so we need to do that here to force it into memory.
1200+
*/
1201+
filp = kernel_file_open(datapath, O_RDONLY, inode, current_cred());
1202+
if (IS_ERR(filp))
1203+
return PTR_ERR(filp);
1204+
fput(filp);
1205+
}
1206+
1207+
return 0;
1208+
}
1209+
1210+
int ovl_validate_verity(struct ovl_fs *ofs,
1211+
struct path *metapath,
1212+
struct path *datapath)
1213+
{
1214+
struct ovl_metacopy metacopy_data;
1215+
u8 actual_digest[FS_VERITY_MAX_DIGEST_SIZE];
1216+
int xattr_digest_size, digest_size;
1217+
int xattr_size, err;
1218+
u8 verity_algo;
1219+
1220+
if (!ofs->config.verity_mode ||
1221+
/* Verity only works on regular files */
1222+
!S_ISREG(d_inode(metapath->dentry)->i_mode))
1223+
return 0;
1224+
1225+
xattr_size = ovl_check_metacopy_xattr(ofs, metapath, &metacopy_data);
1226+
if (xattr_size < 0)
1227+
return xattr_size;
1228+
1229+
if (!xattr_size || !metacopy_data.digest_algo) {
1230+
if (ofs->config.verity_mode == OVL_VERITY_REQUIRE) {
1231+
pr_warn_ratelimited("metacopy file '%pd' has no digest specified\n",
1232+
metapath->dentry);
1233+
return -EIO;
1234+
}
1235+
return 0;
1236+
}
1237+
1238+
xattr_digest_size = ovl_metadata_digest_size(&metacopy_data);
1239+
1240+
err = ovl_ensure_verity_loaded(datapath);
1241+
if (err < 0) {
1242+
pr_warn_ratelimited("lower file '%pd' failed to load fs-verity info\n",
1243+
datapath->dentry);
1244+
return -EIO;
1245+
}
1246+
1247+
digest_size = fsverity_get_digest(d_inode(datapath->dentry), actual_digest,
1248+
&verity_algo, NULL);
1249+
if (digest_size == 0) {
1250+
pr_warn_ratelimited("lower file '%pd' has no fs-verity digest\n", datapath->dentry);
1251+
return -EIO;
1252+
}
1253+
1254+
if (xattr_digest_size != digest_size ||
1255+
metacopy_data.digest_algo != verity_algo ||
1256+
memcmp(metacopy_data.digest, actual_digest, xattr_digest_size) != 0) {
1257+
pr_warn_ratelimited("lower file '%pd' has the wrong fs-verity digest\n",
1258+
datapath->dentry);
1259+
return -EIO;
1260+
}
1261+
1262+
return 0;
1263+
}
1264+
11771265
/*
11781266
* ovl_sync_status() - Check fs sync status for volatile mounts
11791267
*

0 commit comments

Comments
 (0)