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 9402591f12aa..34cc72f8fb6a 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 tuple (fh,uuid) is a universal unique identifier for a copy up origin, * where: 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/params.c b/fs/overlayfs/params.c index a63160dbb0f9..575a60b76a6c 100644 --- a/fs/overlayfs/params.c +++ b/fs/overlayfs/params.c @@ -55,6 +55,7 @@ enum { Opt_userxattr, Opt_xino, Opt_metacopy, + Opt_verity, Opt_volatile, }; @@ -101,6 +102,23 @@ static int ovl_redirect_mode_def(void) OVL_REDIRECT_NOFOLLOW; } +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; +} + #define fsparam_string_empty(NAME, OPT) \ __fsparam(fs_param_is_string, NAME, OPT, fs_param_can_be_empty, NULL) @@ -116,6 +134,7 @@ const struct fs_parameter_spec ovl_parameter_spec[] = { fsparam_flag("userxattr", Opt_userxattr), fsparam_enum("xino", Opt_xino, ovl_parameter_xino), fsparam_enum("metacopy", Opt_metacopy, ovl_parameter_bool), + fsparam_enum("verity", Opt_verity, ovl_parameter_verity), fsparam_flag("volatile", Opt_volatile), {} }; @@ -572,6 +591,9 @@ static int ovl_parse_param(struct fs_context *fc, struct fs_parameter *param) config->metacopy = result.uint_32; ctx->set.metacopy = true; break; + case Opt_verity: + config->verity_mode = result.uint_32; + break; case Opt_volatile: config->ovl_volatile = true; break; @@ -762,6 +784,18 @@ 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. @@ -769,13 +803,18 @@ 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 @@ -812,7 +851,7 @@ 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"); @@ -825,6 +864,14 @@ 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 @@ -836,7 +883,7 @@ 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) { @@ -848,6 +895,11 @@ 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 @@ -909,5 +961,8 @@ 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; }