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

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
This diff is collapsed.
...@@ -39,14 +39,14 @@ config FS_VERITY_BUILTIN_SIGNATURES ...@@ -39,14 +39,14 @@ config FS_VERITY_BUILTIN_SIGNATURES
depends on FS_VERITY depends on FS_VERITY
select SYSTEM_DATA_VERIFICATION select SYSTEM_DATA_VERIFICATION
help help
Support verifying signatures of verity files against the X.509 This option adds support for in-kernel verification of
certificates that have been loaded into the ".fs-verity" fs-verity builtin signatures.
kernel keyring.
This is meant as a relatively simple mechanism that can be Please take great care before using this feature. It is not
used to provide an authenticity guarantee for verity files, as the only way to do signatures with fs-verity, and the
an alternative to IMA appraisal. Userspace programs still alternatives (such as userspace signature verification, and
need to check that the verity bit is set in order to get an IMA appraisal) can be much better. For details about the
authenticity guarantee. limitations of this feature, see
Documentation/filesystems/fsverity.rst.
If unsure, say N. If unsure, say N.
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include "fsverity_private.h" #include "fsverity_private.h"
#include <crypto/hash.h>
#include <linux/mount.h> #include <linux/mount.h>
#include <linux/sched/signal.h> #include <linux/sched/signal.h>
#include <linux/uaccess.h> #include <linux/uaccess.h>
...@@ -20,7 +21,7 @@ struct block_buffer { ...@@ -20,7 +21,7 @@ struct block_buffer {
/* Hash a block, writing the result to the next level's pending block buffer. */ /* Hash a block, writing the result to the next level's pending block buffer. */
static int hash_one_block(struct inode *inode, static int hash_one_block(struct inode *inode,
const struct merkle_tree_params *params, const struct merkle_tree_params *params,
struct ahash_request *req, struct block_buffer *cur) struct block_buffer *cur)
{ {
struct block_buffer *next = cur + 1; struct block_buffer *next = cur + 1;
int err; int err;
...@@ -36,8 +37,7 @@ static int hash_one_block(struct inode *inode, ...@@ -36,8 +37,7 @@ static int hash_one_block(struct inode *inode,
/* Zero-pad the block if it's shorter than the block size. */ /* Zero-pad the block if it's shorter than the block size. */
memset(&cur->data[cur->filled], 0, params->block_size - cur->filled); memset(&cur->data[cur->filled], 0, params->block_size - cur->filled);
err = fsverity_hash_block(params, inode, req, virt_to_page(cur->data), err = fsverity_hash_block(params, inode, cur->data,
offset_in_page(cur->data),
&next->data[next->filled]); &next->data[next->filled]);
if (err) if (err)
return err; return err;
...@@ -76,7 +76,6 @@ static int build_merkle_tree(struct file *filp, ...@@ -76,7 +76,6 @@ static int build_merkle_tree(struct file *filp,
struct inode *inode = file_inode(filp); struct inode *inode = file_inode(filp);
const u64 data_size = inode->i_size; const u64 data_size = inode->i_size;
const int num_levels = params->num_levels; 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[1 + FS_VERITY_MAX_LEVELS + 1] = {};
struct block_buffer *buffers = &_buffers[1]; struct block_buffer *buffers = &_buffers[1];
unsigned long level_offset[FS_VERITY_MAX_LEVELS]; unsigned long level_offset[FS_VERITY_MAX_LEVELS];
...@@ -90,9 +89,6 @@ static int build_merkle_tree(struct file *filp, ...@@ -90,9 +89,6 @@ static int build_merkle_tree(struct file *filp,
return 0; 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. * Allocate the block buffers. Buffer "-1" is for data blocks.
* Buffers 0 <= level < num_levels are for the actual tree levels. * Buffers 0 <= level < num_levels are for the actual tree levels.
...@@ -130,7 +126,7 @@ static int build_merkle_tree(struct file *filp, ...@@ -130,7 +126,7 @@ static int build_merkle_tree(struct file *filp,
fsverity_err(inode, "Short read of file data"); fsverity_err(inode, "Short read of file data");
goto out; goto out;
} }
err = hash_one_block(inode, params, req, &buffers[-1]); err = hash_one_block(inode, params, &buffers[-1]);
if (err) if (err)
goto out; goto out;
for (level = 0; level < num_levels; level++) { for (level = 0; level < num_levels; level++) {
...@@ -141,8 +137,7 @@ static int build_merkle_tree(struct file *filp, ...@@ -141,8 +137,7 @@ static int build_merkle_tree(struct file *filp,
} }
/* Next block at @level is full */ /* Next block at @level is full */
err = hash_one_block(inode, params, req, err = hash_one_block(inode, params, &buffers[level]);
&buffers[level]);
if (err) if (err)
goto out; goto out;
err = write_merkle_tree_block(inode, err = write_merkle_tree_block(inode,
...@@ -162,8 +157,7 @@ static int build_merkle_tree(struct file *filp, ...@@ -162,8 +157,7 @@ static int build_merkle_tree(struct file *filp,
/* Finish all nonempty pending tree blocks. */ /* Finish all nonempty pending tree blocks. */
for (level = 0; level < num_levels; level++) { for (level = 0; level < num_levels; level++) {
if (buffers[level].filled != 0) { if (buffers[level].filled != 0) {
err = hash_one_block(inode, params, req, err = hash_one_block(inode, params, &buffers[level]);
&buffers[level]);
if (err) if (err)
goto out; goto out;
err = write_merkle_tree_block(inode, err = write_merkle_tree_block(inode,
...@@ -183,7 +177,6 @@ static int build_merkle_tree(struct file *filp, ...@@ -183,7 +177,6 @@ static int build_merkle_tree(struct file *filp,
out: out:
for (level = -1; level < num_levels; level++) for (level = -1; level < num_levels; level++)
kfree(buffers[level].data); kfree(buffers[level].data);
fsverity_free_hash_request(params->hash_alg, req);
return err; return err;
} }
...@@ -215,7 +208,7 @@ static int enable_verity(struct file *filp, ...@@ -215,7 +208,7 @@ static int enable_verity(struct file *filp,
} }
desc->salt_size = arg->salt_size; 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 && if (arg->sig_size &&
copy_from_user(desc->signature, u64_to_user_ptr(arg->sig_ptr), copy_from_user(desc->signature, u64_to_user_ptr(arg->sig_ptr),
arg->sig_size)) { arg->sig_size)) {
......
...@@ -11,9 +11,6 @@ ...@@ -11,9 +11,6 @@
#define pr_fmt(fmt) "fs-verity: " fmt #define pr_fmt(fmt) "fs-verity: " fmt
#include <linux/fsverity.h> #include <linux/fsverity.h>
#include <linux/mempool.h>
struct ahash_request;
/* /*
* Implementation limit: maximum depth of the Merkle tree. For now 8 is plenty; * Implementation limit: maximum depth of the Merkle tree. For now 8 is plenty;
...@@ -23,11 +20,10 @@ struct ahash_request; ...@@ -23,11 +20,10 @@ struct ahash_request;
/* A hash algorithm supported by fs-verity */ /* A hash algorithm supported by fs-verity */
struct fsverity_hash_alg { 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 */ 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 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 */ 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 * The HASH_ALGO_* constant for this algorithm. This is different from
* FS_VERITY_HASH_ALG_*, which uses a different numbering scheme. * FS_VERITY_HASH_ALG_*, which uses a different numbering scheme.
...@@ -37,7 +33,7 @@ struct fsverity_hash_alg { ...@@ -37,7 +33,7 @@ struct fsverity_hash_alg {
/* Merkle tree parameters: hash algorithm, initial hash state, and topology */ /* Merkle tree parameters: hash algorithm, initial hash state, and topology */
struct merkle_tree_params { 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 */ const u8 *hashstate; /* initial hash state or NULL */
unsigned int digest_size; /* same as hash_alg->digest_size */ unsigned int digest_size; /* same as hash_alg->digest_size */
unsigned int block_size; /* size of data and tree blocks */ unsigned int block_size; /* size of data and tree blocks */
...@@ -83,18 +79,13 @@ struct fsverity_info { ...@@ -83,18 +79,13 @@ struct fsverity_info {
extern struct fsverity_hash_alg fsverity_hash_algs[]; 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); unsigned int num);
struct ahash_request *fsverity_alloc_hash_request(struct fsverity_hash_alg *alg, const u8 *fsverity_prepare_hash_state(const 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 *salt, size_t salt_size); const u8 *salt, size_t salt_size);
int fsverity_hash_block(const struct merkle_tree_params *params, int fsverity_hash_block(const struct merkle_tree_params *params,
const struct inode *inode, struct ahash_request *req, const struct inode *inode, const void *data, u8 *out);
struct page *page, unsigned int offset, u8 *out); int fsverity_hash_buffer(const struct fsverity_hash_alg *alg,
int fsverity_hash_buffer(struct fsverity_hash_alg *alg,
const void *data, size_t size, u8 *out); const void *data, size_t size, u8 *out);
void __init fsverity_check_hash_algs(void); void __init fsverity_check_hash_algs(void);
......
...@@ -8,7 +8,6 @@ ...@@ -8,7 +8,6 @@
#include "fsverity_private.h" #include "fsverity_private.h"
#include <crypto/hash.h> #include <crypto/hash.h>
#include <linux/scatterlist.h>
/* The hash algorithms supported by fs-verity */ /* The hash algorithms supported by fs-verity */
struct fsverity_hash_alg fsverity_hash_algs[] = { struct fsverity_hash_alg fsverity_hash_algs[] = {
...@@ -40,11 +39,11 @@ static DEFINE_MUTEX(fsverity_hash_alg_init_mutex); ...@@ -40,11 +39,11 @@ static DEFINE_MUTEX(fsverity_hash_alg_init_mutex);
* *
* Return: pointer to the hash alg on success, else an ERR_PTR() * Return: pointer to the hash alg on success, else an ERR_PTR()
*/ */
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) unsigned int num)
{ {
struct fsverity_hash_alg *alg; struct fsverity_hash_alg *alg;
struct crypto_ahash *tfm; struct crypto_shash *tfm;
int err; int err;
if (num >= ARRAY_SIZE(fsverity_hash_algs) || if (num >= ARRAY_SIZE(fsverity_hash_algs) ||
...@@ -63,11 +62,7 @@ struct fsverity_hash_alg *fsverity_get_hash_alg(const struct inode *inode, ...@@ -63,11 +62,7 @@ struct fsverity_hash_alg *fsverity_get_hash_alg(const struct inode *inode,
if (alg->tfm != NULL) if (alg->tfm != NULL)
goto out_unlock; goto out_unlock;
/* tfm = crypto_alloc_shash(alg->name, 0, 0);
* Using the shash API would make things a bit simpler, but the ahash
* API is preferable as it allows the use of crypto accelerators.
*/
tfm = crypto_alloc_ahash(alg->name, 0, 0);
if (IS_ERR(tfm)) { if (IS_ERR(tfm)) {
if (PTR_ERR(tfm) == -ENOENT) { if (PTR_ERR(tfm) == -ENOENT) {
fsverity_warn(inode, fsverity_warn(inode,
...@@ -84,68 +79,26 @@ struct fsverity_hash_alg *fsverity_get_hash_alg(const struct inode *inode, ...@@ -84,68 +79,26 @@ struct fsverity_hash_alg *fsverity_get_hash_alg(const struct inode *inode,
} }
err = -EINVAL; err = -EINVAL;
if (WARN_ON_ONCE(alg->digest_size != crypto_ahash_digestsize(tfm))) if (WARN_ON_ONCE(alg->digest_size != crypto_shash_digestsize(tfm)))
goto err_free_tfm; goto err_free_tfm;
if (WARN_ON_ONCE(alg->block_size != crypto_ahash_blocksize(tfm))) if (WARN_ON_ONCE(alg->block_size != crypto_shash_blocksize(tfm)))
goto err_free_tfm;
err = mempool_init_kmalloc_pool(&alg->req_pool, 1,
sizeof(struct ahash_request) +
crypto_ahash_reqsize(tfm));
if (err)
goto err_free_tfm; goto err_free_tfm;
pr_info("%s using implementation \"%s\"\n", pr_info("%s using implementation \"%s\"\n",
alg->name, crypto_ahash_driver_name(tfm)); alg->name, crypto_shash_driver_name(tfm));
/* pairs with smp_load_acquire() above */ /* pairs with smp_load_acquire() above */
smp_store_release(&alg->tfm, tfm); smp_store_release(&alg->tfm, tfm);
goto out_unlock; goto out_unlock;
err_free_tfm: err_free_tfm:
crypto_free_ahash(tfm); crypto_free_shash(tfm);
alg = ERR_PTR(err); alg = ERR_PTR(err);
out_unlock: out_unlock:
mutex_unlock(&fsverity_hash_alg_init_mutex); mutex_unlock(&fsverity_hash_alg_init_mutex);
return alg; return alg;
} }
/**
* fsverity_alloc_hash_request() - allocate a hash request object
* @alg: the hash algorithm for which to allocate the request
* @gfp_flags: memory allocation flags
*
* This is mempool-backed, so this never fails if __GFP_DIRECT_RECLAIM is set in
* @gfp_flags. However, in that case this might need to wait for all
* previously-allocated requests to be freed. So to avoid deadlocks, callers
* must never need multiple requests at a time to make forward progress.
*
* Return: the request object on success; NULL on failure (but see above)
*/
struct ahash_request *fsverity_alloc_hash_request(struct fsverity_hash_alg *alg,
gfp_t gfp_flags)
{
struct ahash_request *req = mempool_alloc(&alg->req_pool, gfp_flags);
if (req)
ahash_request_set_tfm(req, alg->tfm);
return req;
}
/**
* fsverity_free_hash_request() - free a hash request object
* @alg: the hash algorithm
* @req: the hash request object to free
*/
void fsverity_free_hash_request(struct fsverity_hash_alg *alg,
struct ahash_request *req)
{
if (req) {
ahash_request_zero(req);
mempool_free(req, &alg->req_pool);
}
}
/** /**
* fsverity_prepare_hash_state() - precompute the initial hash state * fsverity_prepare_hash_state() - precompute the initial hash state
* @alg: hash algorithm * @alg: hash algorithm
...@@ -155,27 +108,24 @@ void fsverity_free_hash_request(struct fsverity_hash_alg *alg, ...@@ -155,27 +108,24 @@ void fsverity_free_hash_request(struct fsverity_hash_alg *alg,
* Return: NULL if the salt is empty, otherwise the kmalloc()'ed precomputed * Return: NULL if the salt is empty, otherwise the kmalloc()'ed precomputed
* initial hash state on success or an ERR_PTR() on failure. * initial hash state on success or an ERR_PTR() on failure.
*/ */
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) const u8 *salt, size_t salt_size)
{ {
u8 *hashstate = NULL; u8 *hashstate = NULL;
struct ahash_request *req = NULL; SHASH_DESC_ON_STACK(desc, alg->tfm);
u8 *padded_salt = NULL; u8 *padded_salt = NULL;
size_t padded_salt_size; size_t padded_salt_size;
struct scatterlist sg;
DECLARE_CRYPTO_WAIT(wait);
int err; int err;
desc->tfm = alg->tfm;
if (salt_size == 0) if (salt_size == 0)
return NULL; return NULL;
hashstate = kmalloc(crypto_ahash_statesize(alg->tfm), GFP_KERNEL); hashstate = kmalloc(crypto_shash_statesize(alg->tfm), GFP_KERNEL);
if (!hashstate) if (!hashstate)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
/* This allocation never fails, since it's mempool-backed. */
req = fsverity_alloc_hash_request(alg, GFP_KERNEL);
/* /*
* Zero-pad the salt to the next multiple of the input size of the hash * Zero-pad the salt to the next multiple of the input size of the hash
* algorithm's compression function, e.g. 64 bytes for SHA-256 or 128 * algorithm's compression function, e.g. 64 bytes for SHA-256 or 128
...@@ -190,26 +140,18 @@ const u8 *fsverity_prepare_hash_state(struct fsverity_hash_alg *alg, ...@@ -190,26 +140,18 @@ const u8 *fsverity_prepare_hash_state(struct fsverity_hash_alg *alg,
goto err_free; goto err_free;
} }
memcpy(padded_salt, salt, salt_size); memcpy(padded_salt, salt, salt_size);
err = crypto_shash_init(desc);
sg_init_one(&sg, padded_salt, padded_salt_size);
ahash_request_set_callback(req, CRYPTO_TFM_REQ_MAY_SLEEP |
CRYPTO_TFM_REQ_MAY_BACKLOG,
crypto_req_done, &wait);
ahash_request_set_crypt(req, &sg, NULL, padded_salt_size);
err = crypto_wait_req(crypto_ahash_init(req), &wait);
if (err) if (err)
goto err_free; goto err_free;
err = crypto_wait_req(crypto_ahash_update(req), &wait); err = crypto_shash_update(desc, padded_salt, padded_salt_size);
if (err) if (err)
goto err_free; goto err_free;
err = crypto_ahash_export(req, hashstate); err = crypto_shash_export(desc, hashstate);
if (err) if (err)
goto err_free; goto err_free;
out: out:
fsverity_free_hash_request(alg, req);
kfree(padded_salt); kfree(padded_salt);
return hashstate; return hashstate;
...@@ -223,9 +165,7 @@ const u8 *fsverity_prepare_hash_state(struct fsverity_hash_alg *alg, ...@@ -223,9 +165,7 @@ const u8 *fsverity_prepare_hash_state(struct fsverity_hash_alg *alg,
* fsverity_hash_block() - hash a single data or hash block * fsverity_hash_block() - hash a single data or hash block
* @params: the Merkle tree's parameters * @params: the Merkle tree's parameters
* @inode: inode for which the hashing is being done * @inode: inode for which the hashing is being done
* @req: preallocated hash request * @data: virtual address of a buffer containing the block to hash
* @page: the page containing the block to hash
* @offset: the offset of the block within @page
* @out: output digest, size 'params->digest_size' bytes * @out: output digest, size 'params->digest_size' bytes
* *
* Hash a single data or hash block. The hash is salted if a salt is specified * Hash a single data or hash block. The hash is salted if a salt is specified
...@@ -234,33 +174,24 @@ const u8 *fsverity_prepare_hash_state(struct fsverity_hash_alg *alg, ...@@ -234,33 +174,24 @@ const u8 *fsverity_prepare_hash_state(struct fsverity_hash_alg *alg,
* Return: 0 on success, -errno on failure * Return: 0 on success, -errno on failure
*/ */
int fsverity_hash_block(const struct merkle_tree_params *params, int fsverity_hash_block(const struct merkle_tree_params *params,
const struct inode *inode, struct ahash_request *req, const struct inode *inode, const void *data, u8 *out)
struct page *page, unsigned int offset, u8 *out)
{ {
struct scatterlist sg; SHASH_DESC_ON_STACK(desc, params->hash_alg->tfm);
DECLARE_CRYPTO_WAIT(wait);
int err; int err;
sg_init_table(&sg, 1); desc->tfm = params->hash_alg->tfm;
sg_set_page(&sg, page, params->block_size, offset);
ahash_request_set_callback(req, CRYPTO_TFM_REQ_MAY_SLEEP |
CRYPTO_TFM_REQ_MAY_BACKLOG,
crypto_req_done, &wait);
ahash_request_set_crypt(req, &sg, out, params->block_size);
if (params->hashstate) { if (params->hashstate) {
err = crypto_ahash_import(req, params->hashstate); err = crypto_shash_import(desc, params->hashstate);
if (err) { if (err) {
fsverity_err(inode, fsverity_err(inode,
"Error %d importing hash state", err); "Error %d importing hash state", err);
return err; return err;
} }
err = crypto_ahash_finup(req); err = crypto_shash_finup(desc, data, params->block_size, out);
} else { } else {
err = crypto_ahash_digest(req); err = crypto_shash_digest(desc, data, params->block_size, out);
} }
err = crypto_wait_req(err, &wait);
if (err) if (err)
fsverity_err(inode, "Error %d computing block hash", err); fsverity_err(inode, "Error %d computing block hash", err);
return err; return err;
...@@ -273,32 +204,12 @@ int fsverity_hash_block(const struct merkle_tree_params *params, ...@@ -273,32 +204,12 @@ int fsverity_hash_block(const struct merkle_tree_params *params,
* @size: size of data to hash, in bytes * @size: size of data to hash, in bytes
* @out: output digest, size 'alg->digest_size' bytes * @out: output digest, size 'alg->digest_size' bytes
* *
* Hash some data which is located in physically contiguous memory (i.e. memory
* allocated by kmalloc(), not by vmalloc()). No salt is used.
*
* Return: 0 on success, -errno on failure * Return: 0 on success, -errno on failure
*/ */
int fsverity_hash_buffer(struct fsverity_hash_alg *alg, int fsverity_hash_buffer(const struct fsverity_hash_alg *alg,
const void *data, size_t size, u8 *out) const void *data, size_t size, u8 *out)
{ {
struct ahash_request *req; return crypto_shash_tfm_digest(alg->tfm, data, size, out);
struct scatterlist sg;
DECLARE_CRYPTO_WAIT(wait);
int err;
/* This allocation never fails, since it's mempool-backed. */
req = fsverity_alloc_hash_request(alg, GFP_KERNEL);
sg_init_one(&sg, data, size);
ahash_request_set_callback(req, CRYPTO_TFM_REQ_MAY_SLEEP |
CRYPTO_TFM_REQ_MAY_BACKLOG,
crypto_req_done, &wait);
ahash_request_set_crypt(req, &sg, out, size);
err = crypto_wait_req(crypto_ahash_digest(req), &wait);
fsverity_free_hash_request(alg, req);
return err;
} }
void __init fsverity_check_hash_algs(void) void __init fsverity_check_hash_algs(void)
......
...@@ -61,27 +61,42 @@ EXPORT_SYMBOL_GPL(fsverity_ioctl_measure); ...@@ -61,27 +61,42 @@ EXPORT_SYMBOL_GPL(fsverity_ioctl_measure);
/** /**
* fsverity_get_digest() - get a verity file's digest * fsverity_get_digest() - get a verity file's digest
* @inode: inode to get digest of * @inode: inode to get digest of
* @digest: (out) pointer to the digest * @raw_digest: (out) the raw file digest
* @alg: (out) pointer to the hash algorithm enumeration * @alg: (out) the digest's algorithm, as a FS_VERITY_HASH_ALG_* value
* @halg: (out) the digest's algorithm, as a HASH_ALGO_* value
* *
* Return the file hash algorithm and digest of an fsverity protected file. * Retrieves the fsverity digest of the given file. The file must have been
* Assumption: before calling this, the file must have been opened. * opened at least once since the inode was last loaded into the inode cache;
* otherwise this function will not recognize when fsverity is enabled.
* *
* Return: 0 on success, -errno on failure * The file's fsverity digest consists of @raw_digest in combination with either
* @alg or @halg. (The caller can choose which one of @alg or @halg to use.)
*
* IMPORTANT: Callers *must* make use of one of the two algorithm IDs, since
* @raw_digest is meaningless without knowing which algorithm it uses! fsverity
* provides no security guarantee for users who ignore the algorithm ID, even if
* they use the digest size (since algorithms can share the same digest size).
*
* Return: The size of the raw digest in bytes, or 0 if the file doesn't have
* fsverity enabled.
*/ */
int fsverity_get_digest(struct inode *inode, int fsverity_get_digest(struct inode *inode,
u8 digest[FS_VERITY_MAX_DIGEST_SIZE], u8 raw_digest[FS_VERITY_MAX_DIGEST_SIZE],
enum hash_algo *alg) u8 *alg, enum hash_algo *halg)
{ {
const struct fsverity_info *vi; const struct fsverity_info *vi;
const struct fsverity_hash_alg *hash_alg; const struct fsverity_hash_alg *hash_alg;
vi = fsverity_get_info(inode); vi = fsverity_get_info(inode);
if (!vi) if (!vi)
return -ENODATA; /* not a verity file */ return 0; /* not a verity file */
hash_alg = vi->tree_params.hash_alg; hash_alg = vi->tree_params.hash_alg;
memcpy(digest, vi->file_digest, hash_alg->digest_size); memcpy(raw_digest, vi->file_digest, hash_alg->digest_size);
*alg = hash_alg->algo_id; if (alg)
return 0; *alg = hash_alg - fsverity_hash_algs;
if (halg)
*halg = hash_alg->algo_id;
return hash_alg->digest_size;
} }
EXPORT_SYMBOL_GPL(fsverity_get_digest);
...@@ -32,7 +32,7 @@ int fsverity_init_merkle_tree_params(struct merkle_tree_params *params, ...@@ -32,7 +32,7 @@ int fsverity_init_merkle_tree_params(struct merkle_tree_params *params,
unsigned int log_blocksize, unsigned int log_blocksize,
const u8 *salt, size_t salt_size) const u8 *salt, size_t salt_size)
{ {
struct fsverity_hash_alg *hash_alg; const struct fsverity_hash_alg *hash_alg;
int err; int err;
u64 blocks; u64 blocks;
u64 blocks_in_level[FS_VERITY_MAX_LEVELS]; u64 blocks_in_level[FS_VERITY_MAX_LEVELS];
...@@ -156,9 +156,9 @@ int fsverity_init_merkle_tree_params(struct merkle_tree_params *params, ...@@ -156,9 +156,9 @@ int fsverity_init_merkle_tree_params(struct merkle_tree_params *params,
/* /*
* Compute the file digest by hashing the fsverity_descriptor excluding the * Compute the file digest by hashing the fsverity_descriptor excluding the
* signature and with the sig_size field set to 0. * builtin signature and with the sig_size field set to 0.
*/ */
static int compute_file_digest(struct fsverity_hash_alg *hash_alg, static int compute_file_digest(const struct fsverity_hash_alg *hash_alg,
struct fsverity_descriptor *desc, struct fsverity_descriptor *desc,
u8 *file_digest) u8 *file_digest)
{ {
...@@ -174,7 +174,7 @@ static int compute_file_digest(struct fsverity_hash_alg *hash_alg, ...@@ -174,7 +174,7 @@ static int compute_file_digest(struct fsverity_hash_alg *hash_alg,
/* /*
* Create a new fsverity_info from the given fsverity_descriptor (with optional * Create a new fsverity_info from the given fsverity_descriptor (with optional
* appended signature), and check the signature if present. The * appended builtin signature), and check the signature if present. The
* fsverity_descriptor must have already undergone basic validation. * fsverity_descriptor must have already undergone basic validation.
*/ */
struct fsverity_info *fsverity_create_info(const struct inode *inode, struct fsverity_info *fsverity_create_info(const struct inode *inode,
...@@ -319,8 +319,8 @@ static bool validate_fsverity_descriptor(struct inode *inode, ...@@ -319,8 +319,8 @@ static bool validate_fsverity_descriptor(struct inode *inode,
} }
/* /*
* Read the inode's fsverity_descriptor (with optional appended signature) from * Read the inode's fsverity_descriptor (with optional appended builtin
* the filesystem, and do basic validation of it. * signature) from the filesystem, and do basic validation of it.
*/ */
int fsverity_get_descriptor(struct inode *inode, int fsverity_get_descriptor(struct inode *inode,
struct fsverity_descriptor **desc_ret) struct fsverity_descriptor **desc_ret)
......
...@@ -105,7 +105,7 @@ static int fsverity_read_descriptor(struct inode *inode, ...@@ -105,7 +105,7 @@ static int fsverity_read_descriptor(struct inode *inode,
if (res) if (res)
return res; return res;
/* don't include the signature */ /* don't include the builtin signature */
desc_size = offsetof(struct fsverity_descriptor, signature); desc_size = offsetof(struct fsverity_descriptor, signature);
desc->sig_size = 0; desc->sig_size = 0;
...@@ -131,7 +131,7 @@ static int fsverity_read_signature(struct inode *inode, ...@@ -131,7 +131,7 @@ static int fsverity_read_signature(struct inode *inode,
} }
/* /*
* Include only the signature. Note that fsverity_get_descriptor() * Include only the builtin signature. fsverity_get_descriptor()
* already verified that sig_size is in-bounds. * already verified that sig_size is in-bounds.
*/ */
res = fsverity_read_buffer(buf, offset, length, desc->signature, res = fsverity_read_buffer(buf, offset, length, desc->signature,
......
...@@ -5,6 +5,14 @@ ...@@ -5,6 +5,14 @@
* Copyright 2019 Google LLC * Copyright 2019 Google LLC
*/ */
/*
* This file implements verification of fs-verity builtin signatures. 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.
*/
#include "fsverity_private.h" #include "fsverity_private.h"
#include <linux/cred.h> #include <linux/cred.h>
......
...@@ -12,38 +12,6 @@ ...@@ -12,38 +12,6 @@
static struct workqueue_struct *fsverity_read_workqueue; static struct workqueue_struct *fsverity_read_workqueue;
static inline int cmp_hashes(const struct fsverity_info *vi,
const u8 *want_hash, const u8 *real_hash,
u64 data_pos, int level)
{
const unsigned int hsize = vi->tree_params.digest_size;
if (memcmp(want_hash, real_hash, hsize) == 0)
return 0;
fsverity_err(vi->inode,
"FILE CORRUPTED! pos=%llu, level=%d, want_hash=%s:%*phN, real_hash=%s:%*phN",
data_pos, level,
vi->tree_params.hash_alg->name, hsize, want_hash,
vi->tree_params.hash_alg->name, hsize, real_hash);
return -EBADMSG;
}
static bool data_is_zeroed(struct inode *inode, struct page *page,
unsigned int len, unsigned int offset)
{
void *virt = kmap_local_page(page);
if (memchr_inv(virt + offset, 0, len)) {
kunmap_local(virt);
fsverity_err(inode,
"FILE CORRUPTED! Data past EOF is not zeroed");
return false;
}
kunmap_local(virt);
return true;
}
/* /*
* Returns true if the hash block with index @hblock_idx in the tree, located in * Returns true if the hash block with index @hblock_idx in the tree, located in
* @hpage, has already been verified. * @hpage, has already been verified.
...@@ -122,9 +90,7 @@ static bool is_hash_block_verified(struct fsverity_info *vi, struct page *hpage, ...@@ -122,9 +90,7 @@ static bool is_hash_block_verified(struct fsverity_info *vi, struct page *hpage,
*/ */
static bool static bool
verify_data_block(struct inode *inode, struct fsverity_info *vi, verify_data_block(struct inode *inode, struct fsverity_info *vi,
struct ahash_request *req, struct page *data_page, const void *data, u64 data_pos, unsigned long max_ra_pages)
u64 data_pos, unsigned int dblock_offset_in_page,
unsigned long max_ra_pages)
{ {
const struct merkle_tree_params *params = &vi->tree_params; const struct merkle_tree_params *params = &vi->tree_params;
const unsigned int hsize = params->digest_size; const unsigned int hsize = params->digest_size;
...@@ -136,11 +102,11 @@ verify_data_block(struct inode *inode, struct fsverity_info *vi, ...@@ -136,11 +102,11 @@ verify_data_block(struct inode *inode, struct fsverity_info *vi,
struct { struct {
/* Page containing the hash block */ /* Page containing the hash block */
struct page *page; struct page *page;
/* Mapped address of the hash block (will be within @page) */
const void *addr;
/* Index of the hash block in the tree overall */ /* Index of the hash block in the tree overall */
unsigned long index; unsigned long index;
/* Byte offset of the hash block within @page */ /* Byte offset of the wanted hash relative to @addr */
unsigned int offset_in_page;
/* Byte offset of the wanted hash within @page */
unsigned int hoffset; unsigned int hoffset;
} hblocks[FS_VERITY_MAX_LEVELS]; } hblocks[FS_VERITY_MAX_LEVELS];
/* /*
...@@ -148,7 +114,9 @@ verify_data_block(struct inode *inode, struct fsverity_info *vi, ...@@ -148,7 +114,9 @@ verify_data_block(struct inode *inode, struct fsverity_info *vi,
* index of that block's hash within the current level. * index of that block's hash within the current level.
*/ */
u64 hidx = data_pos >> params->log_blocksize; u64 hidx = data_pos >> params->log_blocksize;
int err;
/* Up to 1 + FS_VERITY_MAX_LEVELS pages may be mapped at once */
BUILD_BUG_ON(1 + FS_VERITY_MAX_LEVELS > KM_MAX_IDX);
if (unlikely(data_pos >= inode->i_size)) { if (unlikely(data_pos >= inode->i_size)) {
/* /*
...@@ -159,8 +127,12 @@ verify_data_block(struct inode *inode, struct fsverity_info *vi, ...@@ -159,8 +127,12 @@ verify_data_block(struct inode *inode, struct fsverity_info *vi,
* any part past EOF should be all zeroes. Therefore, we need * any part past EOF should be all zeroes. Therefore, we need
* to verify that any data blocks fully past EOF are all zeroes. * to verify that any data blocks fully past EOF are all zeroes.
*/ */
return data_is_zeroed(inode, data_page, params->block_size, if (memchr_inv(data, 0, params->block_size)) {
dblock_offset_in_page); fsverity_err(inode,
"FILE CORRUPTED! Data past EOF is not zeroed");
return false;
}
return true;
} }
/* /*
...@@ -175,6 +147,7 @@ verify_data_block(struct inode *inode, struct fsverity_info *vi, ...@@ -175,6 +147,7 @@ verify_data_block(struct inode *inode, struct fsverity_info *vi,
unsigned int hblock_offset_in_page; unsigned int hblock_offset_in_page;
unsigned int hoffset; unsigned int hoffset;
struct page *hpage; struct page *hpage;
const void *haddr;
/* /*
* The index of the block in the current level; also the index * The index of the block in the current level; also the index
...@@ -192,30 +165,30 @@ verify_data_block(struct inode *inode, struct fsverity_info *vi, ...@@ -192,30 +165,30 @@ verify_data_block(struct inode *inode, struct fsverity_info *vi,
hblock_offset_in_page = hblock_offset_in_page =
(hblock_idx << params->log_blocksize) & ~PAGE_MASK; (hblock_idx << params->log_blocksize) & ~PAGE_MASK;
/* Byte offset of the hash within the page */ /* Byte offset of the hash within the block */
hoffset = hblock_offset_in_page + hoffset = (hidx << params->log_digestsize) &
((hidx << params->log_digestsize) & (params->block_size - 1);
(params->block_size - 1));
hpage = inode->i_sb->s_vop->read_merkle_tree_page(inode, hpage = inode->i_sb->s_vop->read_merkle_tree_page(inode,
hpage_idx, level == 0 ? min(max_ra_pages, hpage_idx, level == 0 ? min(max_ra_pages,
params->tree_pages - hpage_idx) : 0); params->tree_pages - hpage_idx) : 0);
if (IS_ERR(hpage)) { if (IS_ERR(hpage)) {
err = PTR_ERR(hpage);
fsverity_err(inode, fsverity_err(inode,
"Error %d reading Merkle tree page %lu", "Error %ld reading Merkle tree page %lu",
err, hpage_idx); PTR_ERR(hpage), hpage_idx);
goto out; goto error;
} }
haddr = kmap_local_page(hpage) + hblock_offset_in_page;
if (is_hash_block_verified(vi, hpage, hblock_idx)) { if (is_hash_block_verified(vi, hpage, hblock_idx)) {
memcpy_from_page(_want_hash, hpage, hoffset, hsize); memcpy(_want_hash, haddr + hoffset, hsize);
want_hash = _want_hash; want_hash = _want_hash;
kunmap_local(haddr);
put_page(hpage); put_page(hpage);
goto descend; goto descend;
} }
hblocks[level].page = hpage; hblocks[level].page = hpage;
hblocks[level].addr = haddr;
hblocks[level].index = hblock_idx; hblocks[level].index = hblock_idx;
hblocks[level].offset_in_page = hblock_offset_in_page;
hblocks[level].hoffset = hoffset; hblocks[level].hoffset = hoffset;
hidx = next_hidx; hidx = next_hidx;
} }
...@@ -225,18 +198,14 @@ verify_data_block(struct inode *inode, struct fsverity_info *vi, ...@@ -225,18 +198,14 @@ verify_data_block(struct inode *inode, struct fsverity_info *vi,
/* Descend the tree verifying hash blocks. */ /* Descend the tree verifying hash blocks. */
for (; level > 0; level--) { for (; level > 0; level--) {
struct page *hpage = hblocks[level - 1].page; struct page *hpage = hblocks[level - 1].page;
const void *haddr = hblocks[level - 1].addr;
unsigned long hblock_idx = hblocks[level - 1].index; unsigned long hblock_idx = hblocks[level - 1].index;
unsigned int hblock_offset_in_page =
hblocks[level - 1].offset_in_page;
unsigned int hoffset = hblocks[level - 1].hoffset; unsigned int hoffset = hblocks[level - 1].hoffset;
err = fsverity_hash_block(params, inode, req, hpage, if (fsverity_hash_block(params, inode, haddr, real_hash) != 0)
hblock_offset_in_page, real_hash); goto error;
if (err) if (memcmp(want_hash, real_hash, hsize) != 0)
goto out; goto corrupted;
err = cmp_hashes(vi, want_hash, real_hash, data_pos, level - 1);
if (err)
goto out;
/* /*
* Mark the hash block as verified. This must be atomic and * Mark the hash block as verified. This must be atomic and
* idempotent, as the same hash block might be verified by * idempotent, as the same hash block might be verified by
...@@ -246,29 +215,39 @@ verify_data_block(struct inode *inode, struct fsverity_info *vi, ...@@ -246,29 +215,39 @@ verify_data_block(struct inode *inode, struct fsverity_info *vi,
set_bit(hblock_idx, vi->hash_block_verified); set_bit(hblock_idx, vi->hash_block_verified);
else else
SetPageChecked(hpage); SetPageChecked(hpage);
memcpy_from_page(_want_hash, hpage, hoffset, hsize); memcpy(_want_hash, haddr + hoffset, hsize);
want_hash = _want_hash; want_hash = _want_hash;
kunmap_local(haddr);
put_page(hpage); put_page(hpage);
} }
/* Finally, verify the data block. */ /* Finally, verify the data block. */
err = fsverity_hash_block(params, inode, req, data_page, if (fsverity_hash_block(params, inode, data, real_hash) != 0)
dblock_offset_in_page, real_hash); goto error;
if (err) if (memcmp(want_hash, real_hash, hsize) != 0)
goto out; goto corrupted;
err = cmp_hashes(vi, want_hash, real_hash, data_pos, -1); return true;
out:
for (; level > 0; level--)
put_page(hblocks[level - 1].page);
return err == 0; corrupted:
fsverity_err(inode,
"FILE CORRUPTED! pos=%llu, level=%d, want_hash=%s:%*phN, real_hash=%s:%*phN",
data_pos, level - 1,
params->hash_alg->name, hsize, want_hash,
params->hash_alg->name, hsize, real_hash);
error:
for (; level > 0; level--) {
kunmap_local(hblocks[level - 1].addr);
put_page(hblocks[level - 1].page);
}
return false;
} }
static bool static bool
verify_data_blocks(struct inode *inode, struct fsverity_info *vi, verify_data_blocks(struct folio *data_folio, size_t len, size_t offset,
struct ahash_request *req, struct folio *data_folio, unsigned long max_ra_pages)
size_t len, size_t offset, unsigned long max_ra_pages)
{ {
struct inode *inode = data_folio->mapping->host;
struct fsverity_info *vi = inode->i_verity_info;
const unsigned int block_size = vi->tree_params.block_size; const unsigned int block_size = vi->tree_params.block_size;
u64 pos = (u64)data_folio->index << PAGE_SHIFT; u64 pos = (u64)data_folio->index << PAGE_SHIFT;
...@@ -278,11 +257,14 @@ verify_data_blocks(struct inode *inode, struct fsverity_info *vi, ...@@ -278,11 +257,14 @@ verify_data_blocks(struct inode *inode, struct fsverity_info *vi,
folio_test_uptodate(data_folio))) folio_test_uptodate(data_folio)))
return false; return false;
do { do {
struct page *data_page = void *data;
folio_page(data_folio, offset >> PAGE_SHIFT); bool valid;
if (!verify_data_block(inode, vi, req, data_page, pos + offset, data = kmap_local_folio(data_folio, offset);
offset & ~PAGE_MASK, max_ra_pages)) valid = verify_data_block(inode, vi, data, pos + offset,
max_ra_pages);
kunmap_local(data);
if (!valid)
return false; return false;
offset += block_size; offset += block_size;
len -= block_size; len -= block_size;
...@@ -304,19 +286,7 @@ verify_data_blocks(struct inode *inode, struct fsverity_info *vi, ...@@ -304,19 +286,7 @@ verify_data_blocks(struct inode *inode, struct fsverity_info *vi,
*/ */
bool fsverity_verify_blocks(struct folio *folio, size_t len, size_t offset) bool fsverity_verify_blocks(struct folio *folio, size_t len, size_t offset)
{ {
struct inode *inode = folio->mapping->host; return verify_data_blocks(folio, len, offset, 0);
struct fsverity_info *vi = inode->i_verity_info;
struct ahash_request *req;
bool valid;
/* This allocation never fails, since it's mempool-backed. */
req = fsverity_alloc_hash_request(vi->tree_params.hash_alg, GFP_NOFS);
valid = verify_data_blocks(inode, vi, req, folio, len, offset, 0);
fsverity_free_hash_request(vi->tree_params.hash_alg, req);
return valid;
} }
EXPORT_SYMBOL_GPL(fsverity_verify_blocks); EXPORT_SYMBOL_GPL(fsverity_verify_blocks);
...@@ -337,15 +307,9 @@ EXPORT_SYMBOL_GPL(fsverity_verify_blocks); ...@@ -337,15 +307,9 @@ EXPORT_SYMBOL_GPL(fsverity_verify_blocks);
*/ */
void fsverity_verify_bio(struct bio *bio) void fsverity_verify_bio(struct bio *bio)
{ {
struct inode *inode = bio_first_page_all(bio)->mapping->host;
struct fsverity_info *vi = inode->i_verity_info;
struct ahash_request *req;
struct folio_iter fi; struct folio_iter fi;
unsigned long max_ra_pages = 0; unsigned long max_ra_pages = 0;
/* This allocation never fails, since it's mempool-backed. */
req = fsverity_alloc_hash_request(vi->tree_params.hash_alg, GFP_NOFS);
if (bio->bi_opf & REQ_RAHEAD) { if (bio->bi_opf & REQ_RAHEAD) {
/* /*
* If this bio is for data readahead, then we also do readahead * If this bio is for data readahead, then we also do readahead
...@@ -360,14 +324,12 @@ void fsverity_verify_bio(struct bio *bio) ...@@ -360,14 +324,12 @@ void fsverity_verify_bio(struct bio *bio)
} }
bio_for_each_folio_all(fi, bio) { bio_for_each_folio_all(fi, bio) {
if (!verify_data_blocks(inode, vi, req, fi.folio, fi.length, if (!verify_data_blocks(fi.folio, fi.length, fi.offset,
fi.offset, max_ra_pages)) { max_ra_pages)) {
bio->bi_status = BLK_STS_IOERR; bio->bi_status = BLK_STS_IOERR;
break; break;
} }
} }
fsverity_free_hash_request(vi->tree_params.hash_alg, req);
} }
EXPORT_SYMBOL_GPL(fsverity_verify_bio); EXPORT_SYMBOL_GPL(fsverity_verify_bio);
#endif /* CONFIG_BLOCK */ #endif /* CONFIG_BLOCK */
......
...@@ -143,8 +143,8 @@ int fsverity_ioctl_enable(struct file *filp, const void __user *arg); ...@@ -143,8 +143,8 @@ int fsverity_ioctl_enable(struct file *filp, const void __user *arg);
int fsverity_ioctl_measure(struct file *filp, void __user *arg); int fsverity_ioctl_measure(struct file *filp, void __user *arg);
int fsverity_get_digest(struct inode *inode, int fsverity_get_digest(struct inode *inode,
u8 digest[FS_VERITY_MAX_DIGEST_SIZE], u8 raw_digest[FS_VERITY_MAX_DIGEST_SIZE],
enum hash_algo *alg); u8 *alg, enum hash_algo *halg);
/* open.c */ /* open.c */
...@@ -197,10 +197,14 @@ static inline int fsverity_ioctl_measure(struct file *filp, void __user *arg) ...@@ -197,10 +197,14 @@ static inline int fsverity_ioctl_measure(struct file *filp, void __user *arg)
} }
static inline int fsverity_get_digest(struct inode *inode, static inline int fsverity_get_digest(struct inode *inode,
u8 digest[FS_VERITY_MAX_DIGEST_SIZE], u8 raw_digest[FS_VERITY_MAX_DIGEST_SIZE],
enum hash_algo *alg) u8 *alg, enum hash_algo *halg)
{ {
return -EOPNOTSUPP; /*
* fsverity is not enabled in the kernel configuration, so always report
* that the file doesn't have fsverity enabled (digest size 0).
*/
return 0;
} }
/* open.c */ /* open.c */
......
...@@ -202,19 +202,19 @@ int ima_get_action(struct mnt_idmap *idmap, struct inode *inode, ...@@ -202,19 +202,19 @@ int ima_get_action(struct mnt_idmap *idmap, struct inode *inode,
allowed_algos); allowed_algos);
} }
static int ima_get_verity_digest(struct integrity_iint_cache *iint, static bool ima_get_verity_digest(struct integrity_iint_cache *iint,
struct ima_max_digest_data *hash) struct ima_max_digest_data *hash)
{ {
enum hash_algo verity_alg; enum hash_algo alg;
int ret; int digest_len;
/* /*
* On failure, 'measure' policy rules will result in a file data * On failure, 'measure' policy rules will result in a file data
* hash containing 0's. * hash containing 0's.
*/ */
ret = fsverity_get_digest(iint->inode, hash->digest, &verity_alg); digest_len = fsverity_get_digest(iint->inode, hash->digest, NULL, &alg);
if (ret) if (digest_len == 0)
return ret; return false;
/* /*
* Unlike in the case of actually calculating the file hash, in * Unlike in the case of actually calculating the file hash, in
...@@ -223,9 +223,9 @@ static int ima_get_verity_digest(struct integrity_iint_cache *iint, ...@@ -223,9 +223,9 @@ static int ima_get_verity_digest(struct integrity_iint_cache *iint,
* mismatch between the verity algorithm and the xattr signature * mismatch between the verity algorithm and the xattr signature
* algorithm, if one exists, will be detected later. * algorithm, if one exists, will be detected later.
*/ */
hash->hdr.algo = verity_alg; hash->hdr.algo = alg;
hash->hdr.length = hash_digest_size[verity_alg]; hash->hdr.length = digest_len;
return 0; return true;
} }
/* /*
...@@ -276,16 +276,9 @@ int ima_collect_measurement(struct integrity_iint_cache *iint, ...@@ -276,16 +276,9 @@ int ima_collect_measurement(struct integrity_iint_cache *iint,
memset(&hash.digest, 0, sizeof(hash.digest)); memset(&hash.digest, 0, sizeof(hash.digest));
if (iint->flags & IMA_VERITY_REQUIRED) { if (iint->flags & IMA_VERITY_REQUIRED) {
result = ima_get_verity_digest(iint, &hash); if (!ima_get_verity_digest(iint, &hash)) {
switch (result) {
case 0:
break;
case -ENODATA:
audit_cause = "no-verity-digest"; audit_cause = "no-verity-digest";
break; result = -ENODATA;
default:
audit_cause = "invalid-verity-digest";
break;
} }
} else if (buf) { } else if (buf) {
result = ima_calc_buffer_hash(buf, size, &hash.hdr); result = ima_calc_buffer_hash(buf, size, &hash.hdr);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment