Commit 74774e24 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge tag 'fsverity-for-linus' of git://git.kernel.org/pub/scm/fs/fsverity/linux

Pull fsverity updates from Eric Biggers:
 "Several updates for fs/verity/:

   - Do all hashing with the shash API instead of with the ahash API.

     This simplifies the code and reduces API overhead. It should also
     make things slightly easier for XFS's upcoming support for
     fsverity. It does drop fsverity's support for off-CPU hash
     accelerators, but that support was incomplete and not known to be
     used

   - Update and export fsverity_get_digest() so that it's ready for
     overlayfs's upcoming support for fsverity checking of lowerdata

   - Improve the documentation for builtin signature support

   - Fix a bug in the large folio support"

* tag 'fsverity-for-linus' of git://git.kernel.org/pub/scm/fs/fsverity/linux:
  fsverity: improve documentation for builtin signature support
  fsverity: rework fsverity_get_digest() again
  fsverity: simplify error handling in verify_data_block()
  fsverity: don't use bio_first_page_all() in fsverity_verify_bio()
  fsverity: constify fsverity_hash_alg
  fsverity: use shash API instead of ahash API
parents 4d483ab7 672d6ef4
Loading
Loading
Loading
Loading
+126 −66
Original line number Diff line number Diff line
@@ -38,20 +38,14 @@ fail at runtime.
Use cases
=========

By itself, the base fs-verity feature only provides integrity
protection, i.e. detection of accidental (non-malicious) corruption.
By itself, fs-verity only provides integrity protection, i.e.
detection of accidental (non-malicious) corruption.

However, because fs-verity makes retrieving the file hash extremely
efficient, it's primarily meant to be used as a tool to support
authentication (detection of malicious modifications) or auditing
(logging file hashes before use).

Trusted userspace code (e.g. operating system code running on a
read-only partition that is itself authenticated by dm-verity) can
authenticate the contents of an fs-verity file by using the
`FS_IOC_MEASURE_VERITY`_ ioctl to retrieve its hash, then verifying a
digital signature of it.

A standard file hash could be used instead of fs-verity.  However,
this is inefficient if the file is large and only a small portion may
be accessed.  This is often the case for Android application package
@@ -69,24 +63,31 @@ still be used on read-only filesystems. fs-verity is for files that
must live on a read-write filesystem because they are independently
updated and potentially user-installed, so dm-verity cannot be used.

The base fs-verity feature is a hashing mechanism only; actually
authenticating the files may be done by:

* Userspace-only

* Builtin signature verification + userspace policy

  fs-verity optionally supports a simple signature verification
  mechanism where users can configure the kernel to require that
  all fs-verity files be signed by a key loaded into a keyring;
  see `Built-in signature verification`_.

* Integrity Measurement Architecture (IMA)

  IMA supports including fs-verity file digests and signatures in the
  IMA measurement list and verifying fs-verity based file signatures
  stored as security.ima xattrs, based on policy.

fs-verity does not mandate a particular scheme for authenticating its
file hashes.  (Similarly, dm-verity does not mandate a particular
scheme for authenticating its block device root hashes.)  Options for
authenticating fs-verity file hashes include:

- Trusted userspace code.  Often, the userspace code that accesses
  files can be trusted to authenticate them.  Consider e.g. an
  application that wants to authenticate data files before using them,
  or an application loader that is part of the operating system (which
  is already authenticated in a different way, such as by being loaded
  from a read-only partition that uses dm-verity) and that wants to
  authenticate applications before loading them.  In these cases, this
  trusted userspace code can authenticate a file's contents by
  retrieving its fs-verity digest using `FS_IOC_MEASURE_VERITY`_, then
  verifying a signature of it using any userspace cryptographic
  library that supports digital signatures.

- Integrity Measurement Architecture (IMA).  IMA supports fs-verity
  file digests as an alternative to its traditional full file digests.
  "IMA appraisal" enforces that files contain a valid, matching
  signature in their "security.ima" extended attribute, as controlled
  by the IMA policy.  For more information, see the IMA documentation.

- Trusted userspace code in combination with `Built-in signature
  verification`_.  This approach should be used only with great care.

User API
========
@@ -111,8 +112,7 @@ follows::
    };

This structure contains the parameters of the Merkle tree to build for
the file, and optionally contains a signature.  It must be initialized
as follows:
the file.  It must be initialized as follows:

- ``version`` must be 1.
- ``hash_algorithm`` must be the identifier for the hash algorithm to
@@ -129,12 +129,14 @@ as follows:
  file or device.  Currently the maximum salt size is 32 bytes.
- ``salt_ptr`` is the pointer to the salt, or NULL if no salt is
  provided.
- ``sig_size`` is the size of the signature in bytes, or 0 if no
  signature is provided.  Currently the signature is (somewhat
  arbitrarily) limited to 16128 bytes.  See `Built-in signature
  verification`_ for more information.
- ``sig_ptr``  is the pointer to the signature, or NULL if no
  signature is provided.
- ``sig_size`` is the size of the builtin signature in bytes, or 0 if no
  builtin signature is provided.  Currently the builtin signature is
  (somewhat arbitrarily) limited to 16128 bytes.
- ``sig_ptr``  is the pointer to the builtin signature, or NULL if no
  builtin signature is provided.  A builtin signature is only needed
  if the `Built-in signature verification`_ feature is being used.  It
  is not needed for IMA appraisal, and it is not needed if the file
  signature is being handled entirely in userspace.
- All reserved fields must be zeroed.

FS_IOC_ENABLE_VERITY causes the filesystem to build a Merkle tree for
@@ -158,7 +160,7 @@ fatal signal), no changes are made to the file.
FS_IOC_ENABLE_VERITY can fail with the following errors:

- ``EACCES``: the process does not have write access to the file
- ``EBADMSG``: the signature is malformed
- ``EBADMSG``: the builtin signature is malformed
- ``EBUSY``: this ioctl is already running on the file
- ``EEXIST``: the file already has verity enabled
- ``EFAULT``: the caller provided inaccessible memory
@@ -168,10 +170,10 @@ FS_IOC_ENABLE_VERITY can fail with the following errors:
  reserved bits are set; or the file descriptor refers to neither a
  regular file nor a directory.
- ``EISDIR``: the file descriptor refers to a directory
- ``EKEYREJECTED``: the signature doesn't match the file
- ``EMSGSIZE``: the salt or signature is too long
- ``ENOKEY``: the fs-verity keyring doesn't contain the certificate
  needed to verify the signature
- ``EKEYREJECTED``: the builtin signature doesn't match the file
- ``EMSGSIZE``: the salt or builtin signature is too long
- ``ENOKEY``: the ".fs-verity" keyring doesn't contain the certificate
  needed to verify the builtin signature
- ``ENOPKG``: fs-verity recognizes the hash algorithm, but it's not
  available in the kernel's crypto API as currently configured (e.g.
  for SHA-512, missing CONFIG_CRYPTO_SHA512).
@@ -180,8 +182,8 @@ FS_IOC_ENABLE_VERITY can fail with the following errors:
  support; or the filesystem superblock has not had the 'verity'
  feature enabled on it; or the filesystem does not support fs-verity
  on this file.  (See `Filesystem support`_.)
- ``EPERM``: the file is append-only; or, a signature is required and
  one was not provided.
- ``EPERM``: the file is append-only; or, a builtin signature is
  required and one was not provided.
- ``EROFS``: the filesystem is read-only
- ``ETXTBSY``: someone has the file open for writing.  This can be the
  caller's file descriptor, another open file descriptor, or the file
@@ -270,9 +272,9 @@ This ioctl takes in a pointer to the following structure::
- ``FS_VERITY_METADATA_TYPE_DESCRIPTOR`` reads the fs-verity
  descriptor.  See `fs-verity descriptor`_.

- ``FS_VERITY_METADATA_TYPE_SIGNATURE`` reads the signature which was
  passed to FS_IOC_ENABLE_VERITY, if any.  See `Built-in signature
  verification`_.
- ``FS_VERITY_METADATA_TYPE_SIGNATURE`` reads the builtin signature
  which was passed to FS_IOC_ENABLE_VERITY, if any.  See `Built-in
  signature verification`_.

The semantics are similar to those of ``pread()``.  ``offset``
specifies the offset in bytes into the metadata item to read from, and
@@ -299,7 +301,7 @@ FS_IOC_READ_VERITY_METADATA can fail with the following errors:
  overflowed
- ``ENODATA``: the file is not a verity file, or
  FS_VERITY_METADATA_TYPE_SIGNATURE was requested but the file doesn't
  have a built-in signature
  have a builtin signature
- ``ENOTTY``: this type of filesystem does not implement fs-verity, or
  this ioctl is not yet implemented on it
- ``EOPNOTSUPP``: the kernel was not configured with fs-verity
@@ -347,8 +349,8 @@ non-verity one, with the following exceptions:
  with EIO (for read()) or SIGBUS (for mmap() reads).

- If the sysctl "fs.verity.require_signatures" is set to 1 and the
  file is not signed by a key in the fs-verity keyring, then opening
  the file will fail.  See `Built-in signature verification`_.
  file is not signed by a key in the ".fs-verity" keyring, then
  opening the file will fail.  See `Built-in signature verification`_.

Direct access to the Merkle tree is not supported.  Therefore, if a
verity file is copied, or is backed up and restored, then it will lose
@@ -433,20 +435,25 @@ root hash as well as other fields such as the file size::
Built-in signature verification
===============================

With CONFIG_FS_VERITY_BUILTIN_SIGNATURES=y, fs-verity supports putting
a portion of an authentication policy (see `Use cases`_) in the
kernel.  Specifically, it adds support for:
CONFIG_FS_VERITY_BUILTIN_SIGNATURES=y adds supports for in-kernel
verification of fs-verity builtin signatures.

**IMPORTANT**!  Please take great care before using this feature.
It is not the only way to do signatures with fs-verity, and the
alternatives (such as userspace signature verification, and IMA
appraisal) can be much better.  It's also easy to fall into a trap
of thinking this feature solves more problems than it actually does.

Enabling this option adds the following:

1. At fs-verity module initialization time, a keyring ".fs-verity" is
   created.  The root user can add trusted X.509 certificates to this
   keyring using the add_key() system call, then (when done)
   optionally use keyctl_restrict_keyring() to prevent additional
   certificates from being added.
1. At boot time, the kernel creates a keyring named ".fs-verity".  The
   root user can add trusted X.509 certificates to this keyring using
   the add_key() system call.

2. `FS_IOC_ENABLE_VERITY`_ accepts a pointer to a PKCS#7 formatted
   detached signature in DER format of the file's fs-verity digest.
   On success, this signature is persisted alongside the Merkle tree.
   Then, any time the file is opened, the kernel will verify the
   On success, the ioctl persists the signature alongside the Merkle
   tree.  Then, any time the file is opened, the kernel verifies the
   file's actual digest against this signature, using the certificates
   in the ".fs-verity" keyring.

@@ -454,8 +461,8 @@ kernel. Specifically, it adds support for:
   When set to 1, the kernel requires that all verity files have a
   correctly signed digest as described in (2).

fs-verity file digests must be signed in the following format, which
is similar to the structure used by `FS_IOC_MEASURE_VERITY`_::
The data that the signature as described in (2) must be a signature of
is the fs-verity file digest in the following format::

    struct fsverity_formatted_digest {
            char magic[8];                  /* must be "FSVerity" */
@@ -464,13 +471,66 @@ is similar to the structure used by `FS_IOC_MEASURE_VERITY`_::
            __u8 digest[];
    };

fs-verity's built-in signature verification support is meant as a
relatively simple mechanism that can be used to provide some level of
authenticity protection for verity files, as an alternative to doing
the signature verification in userspace or using IMA-appraisal.
However, with this mechanism, userspace programs still need to check
that the verity bit is set, and there is no protection against verity
files being swapped around.
That's it.  It should be emphasized again that fs-verity builtin
signatures are not the only way to do signatures with fs-verity.  See
`Use cases`_ for an overview of ways in which fs-verity can be used.
fs-verity builtin signatures have some major limitations that should
be carefully considered before using them:

- Builtin signature verification does *not* make the kernel enforce
  that any files actually have fs-verity enabled.  Thus, it is not a
  complete authentication policy.  Currently, if it is used, the only
  way to complete the authentication policy is for trusted userspace
  code to explicitly check whether files have fs-verity enabled with a
  signature before they are accessed.  (With
  fs.verity.require_signatures=1, just checking whether fs-verity is
  enabled suffices.)  But, in this case the trusted userspace code
  could just store the signature alongside the file and verify it
  itself using a cryptographic library, instead of using this feature.

- A file's builtin signature can only be set at the same time that
  fs-verity is being enabled on the file.  Changing or deleting the
  builtin signature later requires re-creating the file.

- Builtin signature verification uses the same set of public keys for
  all fs-verity enabled files on the system.  Different keys cannot be
  trusted for different files; each key is all or nothing.

- The sysctl fs.verity.require_signatures applies system-wide.
  Setting it to 1 only works when all users of fs-verity on the system
  agree that it should be set to 1.  This limitation can prevent
  fs-verity from being used in cases where it would be helpful.

- Builtin signature verification can only use signature algorithms
  that are supported by the kernel.  For example, the kernel does not
  yet support Ed25519, even though this is often the signature
  algorithm that is recommended for new cryptographic designs.

- fs-verity builtin signatures are in PKCS#7 format, and the public
  keys are in X.509 format.  These formats are commonly used,
  including by some other kernel features (which is why the fs-verity
  builtin signatures use them), and are very feature rich.
  Unfortunately, history has shown that code that parses and handles
  these formats (which are from the 1990s and are based on ASN.1)
  often has vulnerabilities as a result of their complexity.  This
  complexity is not inherent to the cryptography itself.

  fs-verity users who do not need advanced features of X.509 and
  PKCS#7 should strongly consider using simpler formats, such as plain
  Ed25519 keys and signatures, and verifying signatures in userspace.

  fs-verity users who choose to use X.509 and PKCS#7 anyway should
  still consider that verifying those signatures in userspace is more
  flexible (for other reasons mentioned earlier in this document) and
  eliminates the need to enable CONFIG_FS_VERITY_BUILTIN_SIGNATURES
  and its associated increase in kernel attack surface.  In some cases
  it can even be necessary, since advanced X.509 and PKCS#7 features
  do not always work as intended with the kernel.  For example, the
  kernel does not check X.509 certificate validity times.

  Note: IMA appraisal, which supports fs-verity, does not use PKCS#7
  for its signatures, so it partially avoids the issues discussed
  here.  IMA appraisal does use X.509.

Filesystem support
==================
+8 −8
Original line number Diff line number Diff line
@@ -39,14 +39,14 @@ config FS_VERITY_BUILTIN_SIGNATURES
	depends on FS_VERITY
	select SYSTEM_DATA_VERIFICATION
	help
	  Support verifying signatures of verity files against the X.509
	  certificates that have been loaded into the ".fs-verity"
	  kernel keyring.
	  This option adds support for in-kernel verification of
	  fs-verity builtin signatures.

	  This is meant as a relatively simple mechanism that can be
	  used to provide an authenticity guarantee for verity files, as
	  an alternative to IMA appraisal.  Userspace programs still
	  need to check that the verity bit is set in order to get an
	  authenticity guarantee.
	  Please take great care before using this feature.  It is not
	  the only way to do signatures with fs-verity, and the
	  alternatives (such as userspace signature verification, and
	  IMA appraisal) can be much better.  For details about the
	  limitations of this feature, see
	  Documentation/filesystems/fsverity.rst.

	  If unsure, say N.
+7 −14
Original line number Diff line number Diff line
@@ -7,6 +7,7 @@

#include "fsverity_private.h"

#include <crypto/hash.h>
#include <linux/mount.h>
#include <linux/sched/signal.h>
#include <linux/uaccess.h>
@@ -20,7 +21,7 @@ struct block_buffer {
/* Hash a block, writing the result to the next level's pending block buffer. */
static int hash_one_block(struct inode *inode,
			  const struct merkle_tree_params *params,
			  struct ahash_request *req, struct block_buffer *cur)
			  struct block_buffer *cur)
{
	struct block_buffer *next = cur + 1;
	int err;
@@ -36,8 +37,7 @@ static int hash_one_block(struct inode *inode,
	/* Zero-pad the block if it's shorter than the block size. */
	memset(&cur->data[cur->filled], 0, params->block_size - cur->filled);

	err = fsverity_hash_block(params, inode, req, virt_to_page(cur->data),
				  offset_in_page(cur->data),
	err = fsverity_hash_block(params, inode, cur->data,
				  &next->data[next->filled]);
	if (err)
		return err;
@@ -76,7 +76,6 @@ static int build_merkle_tree(struct file *filp,
	struct inode *inode = file_inode(filp);
	const u64 data_size = inode->i_size;
	const int num_levels = params->num_levels;
	struct ahash_request *req;
	struct block_buffer _buffers[1 + FS_VERITY_MAX_LEVELS + 1] = {};
	struct block_buffer *buffers = &_buffers[1];
	unsigned long level_offset[FS_VERITY_MAX_LEVELS];
@@ -90,9 +89,6 @@ static int build_merkle_tree(struct file *filp,
		return 0;
	}

	/* This allocation never fails, since it's mempool-backed. */
	req = fsverity_alloc_hash_request(params->hash_alg, GFP_KERNEL);

	/*
	 * Allocate the block buffers.  Buffer "-1" is for data blocks.
	 * Buffers 0 <= level < num_levels are for the actual tree levels.
@@ -130,7 +126,7 @@ static int build_merkle_tree(struct file *filp,
			fsverity_err(inode, "Short read of file data");
			goto out;
		}
		err = hash_one_block(inode, params, req, &buffers[-1]);
		err = hash_one_block(inode, params, &buffers[-1]);
		if (err)
			goto out;
		for (level = 0; level < num_levels; level++) {
@@ -141,8 +137,7 @@ static int build_merkle_tree(struct file *filp,
			}
			/* Next block at @level is full */

			err = hash_one_block(inode, params, req,
					     &buffers[level]);
			err = hash_one_block(inode, params, &buffers[level]);
			if (err)
				goto out;
			err = write_merkle_tree_block(inode,
@@ -162,8 +157,7 @@ static int build_merkle_tree(struct file *filp,
	/* Finish all nonempty pending tree blocks. */
	for (level = 0; level < num_levels; level++) {
		if (buffers[level].filled != 0) {
			err = hash_one_block(inode, params, req,
					     &buffers[level]);
			err = hash_one_block(inode, params, &buffers[level]);
			if (err)
				goto out;
			err = write_merkle_tree_block(inode,
@@ -183,7 +177,6 @@ static int build_merkle_tree(struct file *filp,
out:
	for (level = -1; level < num_levels; level++)
		kfree(buffers[level].data);
	fsverity_free_hash_request(params->hash_alg, req);
	return err;
}

@@ -215,7 +208,7 @@ static int enable_verity(struct file *filp,
	}
	desc->salt_size = arg->salt_size;

	/* Get the signature if the user provided one */
	/* Get the builtin signature if the user provided one */
	if (arg->sig_size &&
	    copy_from_user(desc->signature, u64_to_user_ptr(arg->sig_ptr),
			   arg->sig_size)) {
+7 −16
Original line number Diff line number Diff line
@@ -11,9 +11,6 @@
#define pr_fmt(fmt) "fs-verity: " fmt

#include <linux/fsverity.h>
#include <linux/mempool.h>

struct ahash_request;

/*
 * Implementation limit: maximum depth of the Merkle tree.  For now 8 is plenty;
@@ -23,11 +20,10 @@ struct ahash_request;

/* A hash algorithm supported by fs-verity */
struct fsverity_hash_alg {
	struct crypto_ahash *tfm; /* hash tfm, allocated on demand */
	struct crypto_shash *tfm; /* hash tfm, allocated on demand */
	const char *name;	  /* crypto API name, e.g. sha256 */
	unsigned int digest_size; /* digest size in bytes, e.g. 32 for SHA-256 */
	unsigned int block_size;  /* block size in bytes, e.g. 64 for SHA-256 */
	mempool_t req_pool;	  /* mempool with a preallocated hash request */
	/*
	 * The HASH_ALGO_* constant for this algorithm.  This is different from
	 * FS_VERITY_HASH_ALG_*, which uses a different numbering scheme.
@@ -37,7 +33,7 @@ struct fsverity_hash_alg {

/* Merkle tree parameters: hash algorithm, initial hash state, and topology */
struct merkle_tree_params {
	struct fsverity_hash_alg *hash_alg; /* the hash algorithm */
	const struct fsverity_hash_alg *hash_alg; /* the hash algorithm */
	const u8 *hashstate;		/* initial hash state or NULL */
	unsigned int digest_size;	/* same as hash_alg->digest_size */
	unsigned int block_size;	/* size of data and tree blocks */
@@ -83,18 +79,13 @@ struct fsverity_info {

extern struct fsverity_hash_alg fsverity_hash_algs[];

struct fsverity_hash_alg *fsverity_get_hash_alg(const struct inode *inode,
const struct fsverity_hash_alg *fsverity_get_hash_alg(const struct inode *inode,
						      unsigned int num);
struct ahash_request *fsverity_alloc_hash_request(struct fsverity_hash_alg *alg,
						  gfp_t gfp_flags);
void fsverity_free_hash_request(struct fsverity_hash_alg *alg,
				struct ahash_request *req);
const u8 *fsverity_prepare_hash_state(struct fsverity_hash_alg *alg,
const u8 *fsverity_prepare_hash_state(const struct fsverity_hash_alg *alg,
				      const u8 *salt, size_t salt_size);
int fsverity_hash_block(const struct merkle_tree_params *params,
			const struct inode *inode, struct ahash_request *req,
			struct page *page, unsigned int offset, u8 *out);
int fsverity_hash_buffer(struct fsverity_hash_alg *alg,
			const struct inode *inode, const void *data, u8 *out);
int fsverity_hash_buffer(const struct fsverity_hash_alg *alg,
			 const void *data, size_t size, u8 *out);
void __init fsverity_check_hash_algs(void);

+25 −114

File changed.

Preview size limit exceeded, changes collapsed.

Loading