summaryrefslogtreecommitdiff
path: root/security/integrity/ima
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-04-17 15:42:01 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2026-04-17 15:42:01 -0700
commit9cdca336677b4d15579ec462e33c8a330ab3a9de (patch)
treee46e6f04db9594619094ebefb265113012e60b0e /security/integrity/ima
parentba314ed1bff907321ab4091a4e46c4d9f24b5e39 (diff)
parent82bbd447199ff1441031d2eaf9afe041550cf525 (diff)
Merge tag 'integrity-v7.1' of git://git.kernel.org/pub/scm/linux/kernel/git/zohar/linux-integrity
Pull integrity updates from Mimi Zohar: "There are two main changes, one feature removal, some code cleanup, and a number of bug fixes. Main changes: - Detecting secure boot mode was limited to IMA. Make detecting secure boot mode accessible to EVM and other LSMs - IMA sigv3 support was limited to fsverity. Add IMA sigv3 support for IMA regular file hashes and EVM portable signatures Remove: - Remove IMA support for asychronous hash calculation originally added for hardware acceleration Cleanup: - Remove unnecessary Kconfig CONFIG_MODULE_SIG and CONFIG_KEXEC_SIG tests - Add descriptions of the IMA atomic flags Bug fixes: - Like IMA, properly limit EVM "fix" mode - Define and call evm_fix_hmac() to update security.evm - Fallback to using i_version to detect file change for filesystems that do not support STATX_CHANGE_COOKIE - Address missing kernel support for configured (new) TPM hash algorithms - Add missing crypto_shash_final() return value" * tag 'integrity-v7.1' of git://git.kernel.org/pub/scm/linux/kernel/git/zohar/linux-integrity: evm: Enforce signatures version 3 with new EVM policy 'bit 3' integrity: Allow sigv3 verification on EVM_XATTR_PORTABLE_DIGSIG ima: add support to require IMA sigv3 signatures ima: add regular file data hash signature version 3 support ima: Define asymmetric_verify_v3() to verify IMA sigv3 signatures ima: remove buggy support for asynchronous hashes integrity: Eliminate weak definition of arch_get_secureboot() ima: Add code comments to explain IMA iint cache atomic_flags ima_fs: Correctly create securityfs files for unsupported hash algos ima: check return value of crypto_shash_final() in boot aggregate ima: Define and use a digest_size field in the ima_algo_desc structure powerpc/ima: Drop unnecessary check for CONFIG_MODULE_SIG ima: efi: Drop unnecessary check for CONFIG_MODULE_SIG/CONFIG_KEXEC_SIG ima: fallback to using i_version to detect file change evm: fix security.evm for a file with IMA signature s390: Drop unnecessary CONFIG_IMA_SECURE_AND_OR_TRUSTED_BOOT evm: Don't enable fix mode when secure boot is enabled integrity: Make arch_ima_get_secureboot integrity-wide
Diffstat (limited to 'security/integrity/ima')
-rw-r--r--security/integrity/ima/ima.h29
-rw-r--r--security/integrity/ima/ima_api.c13
-rw-r--r--security/integrity/ima/ima_appraise.c79
-rw-r--r--security/integrity/ima/ima_crypto.c390
-rw-r--r--security/integrity/ima/ima_efi.c53
-rw-r--r--security/integrity/ima/ima_fs.c34
-rw-r--r--security/integrity/ima/ima_main.c37
-rw-r--r--security/integrity/ima/ima_policy.c22
8 files changed, 139 insertions, 518 deletions
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index 89ebe98ffc5e..69e9bf0b82c6 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -53,6 +53,7 @@ extern atomic_t ima_setxattr_allowed_hash_algorithms;
struct ima_algo_desc {
struct crypto_shash *tfm;
enum hash_algo algo;
+ unsigned int digest_size;
};
/* set during initialization */
@@ -144,6 +145,7 @@ struct ima_kexec_hdr {
#define IMA_DIGSIG_REQUIRED 0x01000000
#define IMA_PERMIT_DIRECTIO 0x02000000
#define IMA_NEW_FILE 0x04000000
+#define IMA_SIGV3_REQUIRED 0x08000000
#define IMA_FAIL_UNVERIFIABLE_SIGS 0x10000000
#define IMA_MODSIG_ALLOWED 0x20000000
#define IMA_CHECK_BLACKLIST 0x40000000
@@ -176,7 +178,32 @@ struct ima_kexec_hdr {
IMA_BPRM_APPRAISED | IMA_READ_APPRAISED | \
IMA_CREDS_APPRAISED)
-/* IMA iint cache atomic_flags */
+/*
+ * IMA iint cache atomic_flags
+ *
+ * IMA_CHANGE_ATTR - indicates that chATTR() was called (chmod, chown, chgrp)
+ * and file attributes have changed. On file open, it causes IMA to clear
+ * iint->flags to re-evaluate policy and perform IMA functions again.
+ *
+ * IMA_CHANGE_XATTR - indicates that setxattr or removexattr was called and
+ * extended attributes have changed. On file open, it causes IMA to clear
+ * iint->flags IMA_DONE_MASK to re-appraise.
+ *
+ * IMA_UPDATE_XATTR - indicates that security.ima needs to be updated. It is
+ * cleared if file policy changes and no update is needed.
+ *
+ * IMA_DIGSIG - indicates that file security.ima has signature and file
+ * security.ima must not update on file close.
+ *
+ * IMA_MAY_EMIT_TOMTOU - indicates to add Time-of-Measure-Time-of-Use (ToMToU)
+ * integrity violation (a file that is already opened for read is opened for
+ * write) to the measurement list and to also emit an audit message.
+ *
+ * IMA_EMITTED_OPENWRITERS - indicates to add open-writers integrity violation
+ * (a file that is already opened for write is opened for read) to the
+ * measurement list and to also emit an audit message.
+ *
+ */
#define IMA_CHANGE_XATTR 0
#define IMA_UPDATE_XATTR 1
#define IMA_CHANGE_ATTR 2
diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c
index d15becc8c640..0916f24f005f 100644
--- a/security/integrity/ima/ima_api.c
+++ b/security/integrity/ima/ima_api.c
@@ -269,15 +269,20 @@ int ima_collect_measurement(struct ima_iint_cache *iint, struct file *file,
goto out;
/*
- * Detecting file change is based on i_version. On filesystems
- * which do not support i_version, support was originally limited
- * to an initial measurement/appraisal/audit, but was modified to
- * assume the file changed.
+ * Detect file change based on STATX_CHANGE_COOKIE, when supported,
+ * and fallback to detecting file change based on i_version.
+ *
+ * On filesystems which did not support i_version, support was
+ * originally limited to an initial measurement/appraisal/audit,
+ * but was later modified to assume the file changed.
*/
result = vfs_getattr_nosec(&file->f_path, &stat, STATX_CHANGE_COOKIE,
AT_STATX_SYNC_AS_STAT);
if (!result && (stat.result_mask & STATX_CHANGE_COOKIE))
i_version = stat.change_cookie;
+ else if (IS_I_VERSION(real_inode))
+ i_version = inode_peek_iversion(real_inode);
+
hash.hdr.algo = algo;
hash.hdr.length = hash_digest_size[algo];
diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c
index 16c20c578ea8..de963b9f3634 100644
--- a/security/integrity/ima/ima_appraise.c
+++ b/security/integrity/ima/ima_appraise.c
@@ -27,7 +27,7 @@ core_param(ima_appraise, ima_appraise_cmdline_default, charp, 0);
void __init ima_appraise_parse_cmdline(void)
{
const char *str = ima_appraise_cmdline_default;
- bool sb_state = arch_ima_get_secureboot();
+ bool sb_state = arch_get_secureboot();
int appraisal_state = ima_appraise;
if (!str)
@@ -235,40 +235,6 @@ int ima_read_xattr(struct dentry *dentry,
}
/*
- * calc_file_id_hash - calculate the hash of the ima_file_id struct data
- * @type: xattr type [enum evm_ima_xattr_type]
- * @algo: hash algorithm [enum hash_algo]
- * @digest: pointer to the digest to be hashed
- * @hash: (out) pointer to the hash
- *
- * IMA signature version 3 disambiguates the data that is signed by
- * indirectly signing the hash of the ima_file_id structure data.
- *
- * Signing the ima_file_id struct is currently only supported for
- * IMA_VERITY_DIGSIG type xattrs.
- *
- * Return 0 on success, error code otherwise.
- */
-static int calc_file_id_hash(enum evm_ima_xattr_type type,
- enum hash_algo algo, const u8 *digest,
- struct ima_digest_data *hash)
-{
- struct ima_file_id file_id = {
- .hash_type = IMA_VERITY_DIGSIG, .hash_algorithm = algo};
- unsigned int unused = HASH_MAX_DIGESTSIZE - hash_digest_size[algo];
-
- if (type != IMA_VERITY_DIGSIG)
- return -EINVAL;
-
- memcpy(file_id.hash, digest, hash_digest_size[algo]);
-
- hash->algo = algo;
- hash->length = hash_digest_size[algo];
-
- return ima_calc_buffer_hash(&file_id, sizeof(file_id) - unused, hash);
-}
-
-/*
* xattr_verify - verify xattr digest or signature
*
* Verify whether the hash or signature matches the file contents.
@@ -279,7 +245,6 @@ static int xattr_verify(enum ima_hooks func, struct ima_iint_cache *iint,
struct evm_ima_xattr_data *xattr_value, int xattr_len,
enum integrity_status *status, const char **cause)
{
- struct ima_max_digest_data hash;
struct signature_v2_hdr *sig;
int rc = -EINVAL, hash_start = 0;
int mask;
@@ -332,16 +297,24 @@ static int xattr_verify(enum ima_hooks func, struct ima_iint_cache *iint,
}
sig = (typeof(sig))xattr_value;
- if (sig->version >= 3) {
+ if (sig->version > 3) {
*cause = "invalid-signature-version";
*status = INTEGRITY_FAIL;
break;
}
+
+ if ((iint->flags & IMA_SIGV3_REQUIRED) && sig->version != 3) {
+ *cause = "IMA-sigv3-required";
+ *status = INTEGRITY_FAIL;
+ break;
+ }
+
rc = integrity_digsig_verify(INTEGRITY_KEYRING_IMA,
(const char *)xattr_value,
xattr_len,
iint->ima_hash->digest,
- iint->ima_hash->length);
+ iint->ima_hash->length,
+ iint->ima_hash->algo);
if (rc == -EOPNOTSUPP) {
*status = INTEGRITY_UNKNOWN;
break;
@@ -352,7 +325,9 @@ static int xattr_verify(enum ima_hooks func, struct ima_iint_cache *iint,
(const char *)xattr_value,
xattr_len,
iint->ima_hash->digest,
- iint->ima_hash->length);
+ iint->ima_hash->length,
+ iint->ima_hash->algo);
+
if (rc) {
*cause = "invalid-signature";
*status = INTEGRITY_FAIL;
@@ -378,21 +353,16 @@ static int xattr_verify(enum ima_hooks func, struct ima_iint_cache *iint,
break;
}
- rc = calc_file_id_hash(IMA_VERITY_DIGSIG, iint->ima_hash->algo,
- iint->ima_hash->digest,
- container_of(&hash.hdr,
- struct ima_digest_data, hdr));
- if (rc) {
- *cause = "sigv3-hashing-error";
- *status = INTEGRITY_FAIL;
- break;
- }
-
rc = integrity_digsig_verify(INTEGRITY_KEYRING_IMA,
(const char *)xattr_value,
- xattr_len, hash.digest,
- hash.hdr.length);
- if (rc) {
+ xattr_len,
+ iint->ima_hash->digest,
+ iint->ima_hash->length,
+ iint->ima_hash->algo);
+ if (rc == -EOPNOTSUPP) {
+ *status = INTEGRITY_UNKNOWN;
+ break;
+ } else if (rc) {
*cause = "invalid-verity-signature";
*status = INTEGRITY_FAIL;
} else {
@@ -591,6 +561,11 @@ out:
xattr_value->type != EVM_IMA_XATTR_DIGSIG)) {
if (!ima_fix_xattr(dentry, iint))
status = INTEGRITY_PASS;
+ } else if (status == INTEGRITY_NOLABEL) {
+ if (!evm_fix_hmac(dentry, XATTR_NAME_IMA,
+ (const char *)xattr_value,
+ xattr_len))
+ status = INTEGRITY_PASS;
}
/*
diff --git a/security/integrity/ima/ima_crypto.c b/security/integrity/ima/ima_crypto.c
index aff61643415d..0d72b48249ee 100644
--- a/security/integrity/ima/ima_crypto.c
+++ b/security/integrity/ima/ima_crypto.c
@@ -11,51 +11,15 @@
*/
#include <linux/kernel.h>
-#include <linux/moduleparam.h>
-#include <linux/ratelimit.h>
#include <linux/file.h>
#include <linux/crypto.h>
-#include <linux/scatterlist.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <crypto/hash.h>
#include "ima.h"
-/* minimum file size for ahash use */
-static unsigned long ima_ahash_minsize;
-module_param_named(ahash_minsize, ima_ahash_minsize, ulong, 0644);
-MODULE_PARM_DESC(ahash_minsize, "Minimum file size for ahash use");
-
-/* default is 0 - 1 page. */
-static int ima_maxorder;
-static unsigned int ima_bufsize = PAGE_SIZE;
-
-static int param_set_bufsize(const char *val, const struct kernel_param *kp)
-{
- unsigned long long size;
- int order;
-
- size = memparse(val, NULL);
- order = get_order(size);
- if (order > MAX_PAGE_ORDER)
- return -EINVAL;
- ima_maxorder = order;
- ima_bufsize = PAGE_SIZE << order;
- return 0;
-}
-
-static const struct kernel_param_ops param_ops_bufsize = {
- .set = param_set_bufsize,
- .get = param_get_uint,
-};
-#define param_check_bufsize(name, p) __param_check(name, p, unsigned int)
-
-module_param_named(ahash_bufsize, ima_bufsize, bufsize, 0644);
-MODULE_PARM_DESC(ahash_bufsize, "Maximum ahash buffer size");
-
static struct crypto_shash *ima_shash_tfm;
-static struct crypto_ahash *ima_ahash_tfm;
int ima_sha1_idx __ro_after_init;
int ima_hash_algo_idx __ro_after_init;
@@ -109,6 +73,7 @@ static struct crypto_shash *ima_alloc_tfm(enum hash_algo algo)
int __init ima_init_crypto(void)
{
+ unsigned int digest_size;
enum hash_algo algo;
long rc;
int i;
@@ -147,7 +112,9 @@ int __init ima_init_crypto(void)
for (i = 0; i < NR_BANKS(ima_tpm_chip); i++) {
algo = ima_tpm_chip->allocated_banks[i].crypto_id;
+ digest_size = ima_tpm_chip->allocated_banks[i].digest_size;
ima_algo_array[i].algo = algo;
+ ima_algo_array[i].digest_size = digest_size;
/* unknown TPM algorithm */
if (algo == HASH_ALGO__LAST)
@@ -183,12 +150,15 @@ int __init ima_init_crypto(void)
}
ima_algo_array[ima_sha1_idx].algo = HASH_ALGO_SHA1;
+ ima_algo_array[ima_sha1_idx].digest_size = SHA1_DIGEST_SIZE;
}
if (ima_hash_algo_idx >= NR_BANKS(ima_tpm_chip) &&
ima_hash_algo_idx != ima_sha1_idx) {
+ digest_size = hash_digest_size[ima_hash_algo];
ima_algo_array[ima_hash_algo_idx].tfm = ima_shash_tfm;
ima_algo_array[ima_hash_algo_idx].algo = ima_hash_algo;
+ ima_algo_array[ima_hash_algo_idx].digest_size = digest_size;
}
return 0;
@@ -220,234 +190,6 @@ static void ima_free_tfm(struct crypto_shash *tfm)
crypto_free_shash(tfm);
}
-/**
- * ima_alloc_pages() - Allocate contiguous pages.
- * @max_size: Maximum amount of memory to allocate.
- * @allocated_size: Returned size of actual allocation.
- * @last_warn: Should the min_size allocation warn or not.
- *
- * Tries to do opportunistic allocation for memory first trying to allocate
- * max_size amount of memory and then splitting that until zero order is
- * reached. Allocation is tried without generating allocation warnings unless
- * last_warn is set. Last_warn set affects only last allocation of zero order.
- *
- * By default, ima_maxorder is 0 and it is equivalent to kmalloc(GFP_KERNEL)
- *
- * Return pointer to allocated memory, or NULL on failure.
- */
-static void *ima_alloc_pages(loff_t max_size, size_t *allocated_size,
- int last_warn)
-{
- void *ptr;
- int order = ima_maxorder;
- gfp_t gfp_mask = __GFP_RECLAIM | __GFP_NOWARN | __GFP_NORETRY;
-
- if (order)
- order = min(get_order(max_size), order);
-
- for (; order; order--) {
- ptr = (void *)__get_free_pages(gfp_mask, order);
- if (ptr) {
- *allocated_size = PAGE_SIZE << order;
- return ptr;
- }
- }
-
- /* order is zero - one page */
-
- gfp_mask = GFP_KERNEL;
-
- if (!last_warn)
- gfp_mask |= __GFP_NOWARN;
-
- ptr = (void *)__get_free_pages(gfp_mask, 0);
- if (ptr) {
- *allocated_size = PAGE_SIZE;
- return ptr;
- }
-
- *allocated_size = 0;
- return NULL;
-}
-
-/**
- * ima_free_pages() - Free pages allocated by ima_alloc_pages().
- * @ptr: Pointer to allocated pages.
- * @size: Size of allocated buffer.
- */
-static void ima_free_pages(void *ptr, size_t size)
-{
- if (!ptr)
- return;
- free_pages((unsigned long)ptr, get_order(size));
-}
-
-static struct crypto_ahash *ima_alloc_atfm(enum hash_algo algo)
-{
- struct crypto_ahash *tfm = ima_ahash_tfm;
- int rc;
-
- if (algo < 0 || algo >= HASH_ALGO__LAST)
- algo = ima_hash_algo;
-
- if (algo != ima_hash_algo || !tfm) {
- tfm = crypto_alloc_ahash(hash_algo_name[algo], 0, 0);
- if (!IS_ERR(tfm)) {
- if (algo == ima_hash_algo)
- ima_ahash_tfm = tfm;
- } else {
- rc = PTR_ERR(tfm);
- pr_err("Can not allocate %s (reason: %d)\n",
- hash_algo_name[algo], rc);
- }
- }
- return tfm;
-}
-
-static void ima_free_atfm(struct crypto_ahash *tfm)
-{
- if (tfm != ima_ahash_tfm)
- crypto_free_ahash(tfm);
-}
-
-static inline int ahash_wait(int err, struct crypto_wait *wait)
-{
-
- err = crypto_wait_req(err, wait);
-
- if (err)
- pr_crit_ratelimited("ahash calculation failed: err: %d\n", err);
-
- return err;
-}
-
-static int ima_calc_file_hash_atfm(struct file *file,
- struct ima_digest_data *hash,
- struct crypto_ahash *tfm)
-{
- loff_t i_size, offset;
- char *rbuf[2] = { NULL, };
- int rc, rbuf_len, active = 0, ahash_rc = 0;
- struct ahash_request *req;
- struct scatterlist sg[1];
- struct crypto_wait wait;
- size_t rbuf_size[2];
-
- hash->length = crypto_ahash_digestsize(tfm);
-
- req = ahash_request_alloc(tfm, GFP_KERNEL);
- if (!req)
- return -ENOMEM;
-
- crypto_init_wait(&wait);
- ahash_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG |
- CRYPTO_TFM_REQ_MAY_SLEEP,
- crypto_req_done, &wait);
-
- rc = ahash_wait(crypto_ahash_init(req), &wait);
- if (rc)
- goto out1;
-
- i_size = i_size_read(file_inode(file));
-
- if (i_size == 0)
- goto out2;
-
- /*
- * Try to allocate maximum size of memory.
- * Fail if even a single page cannot be allocated.
- */
- rbuf[0] = ima_alloc_pages(i_size, &rbuf_size[0], 1);
- if (!rbuf[0]) {
- rc = -ENOMEM;
- goto out1;
- }
-
- /* Only allocate one buffer if that is enough. */
- if (i_size > rbuf_size[0]) {
- /*
- * Try to allocate secondary buffer. If that fails fallback to
- * using single buffering. Use previous memory allocation size
- * as baseline for possible allocation size.
- */
- rbuf[1] = ima_alloc_pages(i_size - rbuf_size[0],
- &rbuf_size[1], 0);
- }
-
- for (offset = 0; offset < i_size; offset += rbuf_len) {
- if (!rbuf[1] && offset) {
- /* Not using two buffers, and it is not the first
- * read/request, wait for the completion of the
- * previous ahash_update() request.
- */
- rc = ahash_wait(ahash_rc, &wait);
- if (rc)
- goto out3;
- }
- /* read buffer */
- rbuf_len = min_t(loff_t, i_size - offset, rbuf_size[active]);
- rc = integrity_kernel_read(file, offset, rbuf[active],
- rbuf_len);
- if (rc != rbuf_len) {
- if (rc >= 0)
- rc = -EINVAL;
- /*
- * Forward current rc, do not overwrite with return value
- * from ahash_wait()
- */
- ahash_wait(ahash_rc, &wait);
- goto out3;
- }
-
- if (rbuf[1] && offset) {
- /* Using two buffers, and it is not the first
- * read/request, wait for the completion of the
- * previous ahash_update() request.
- */
- rc = ahash_wait(ahash_rc, &wait);
- if (rc)
- goto out3;
- }
-
- sg_init_one(&sg[0], rbuf[active], rbuf_len);
- ahash_request_set_crypt(req, sg, NULL, rbuf_len);
-
- ahash_rc = crypto_ahash_update(req);
-
- if (rbuf[1])
- active = !active; /* swap buffers, if we use two */
- }
- /* wait for the last update request to complete */
- rc = ahash_wait(ahash_rc, &wait);
-out3:
- ima_free_pages(rbuf[0], rbuf_size[0]);
- ima_free_pages(rbuf[1], rbuf_size[1]);
-out2:
- if (!rc) {
- ahash_request_set_crypt(req, NULL, hash->digest, 0);
- rc = ahash_wait(crypto_ahash_final(req), &wait);
- }
-out1:
- ahash_request_free(req);
- return rc;
-}
-
-static int ima_calc_file_ahash(struct file *file, struct ima_digest_data *hash)
-{
- struct crypto_ahash *tfm;
- int rc;
-
- tfm = ima_alloc_atfm(hash->algo);
- if (IS_ERR(tfm))
- return PTR_ERR(tfm);
-
- rc = ima_calc_file_hash_atfm(file, hash, tfm);
-
- ima_free_atfm(tfm);
-
- return rc;
-}
-
static int ima_calc_file_hash_tfm(struct file *file,
struct ima_digest_data *hash,
struct crypto_shash *tfm)
@@ -499,41 +241,15 @@ out:
return rc;
}
-static int ima_calc_file_shash(struct file *file, struct ima_digest_data *hash)
-{
- struct crypto_shash *tfm;
- int rc;
-
- tfm = ima_alloc_tfm(hash->algo);
- if (IS_ERR(tfm))
- return PTR_ERR(tfm);
-
- rc = ima_calc_file_hash_tfm(file, hash, tfm);
-
- ima_free_tfm(tfm);
-
- return rc;
-}
-
/*
* ima_calc_file_hash - calculate file hash
- *
- * Asynchronous hash (ahash) allows using HW acceleration for calculating
- * a hash. ahash performance varies for different data sizes on different
- * crypto accelerators. shash performance might be better for smaller files.
- * The 'ima.ahash_minsize' module parameter allows specifying the best
- * minimum file size for using ahash on the system.
- *
- * If the ima.ahash_minsize parameter is not specified, this function uses
- * shash for the hash calculation. If ahash fails, it falls back to using
- * shash.
*/
int ima_calc_file_hash(struct file *file, struct ima_digest_data *hash)
{
- loff_t i_size;
int rc;
struct file *f = file;
bool new_file_instance = false;
+ struct crypto_shash *tfm;
/*
* For consistency, fail file's opened with the O_DIRECT flag on
@@ -557,16 +273,13 @@ int ima_calc_file_hash(struct file *file, struct ima_digest_data *hash)
new_file_instance = true;
}
- i_size = i_size_read(file_inode(f));
-
- if (ima_ahash_minsize && i_size >= ima_ahash_minsize) {
- rc = ima_calc_file_ahash(f, hash);
- if (!rc)
- goto out;
+ tfm = ima_alloc_tfm(hash->algo);
+ if (IS_ERR(tfm)) {
+ rc = PTR_ERR(tfm);
+ } else {
+ rc = ima_calc_file_hash_tfm(f, hash, tfm);
+ ima_free_tfm(tfm);
}
-
- rc = ima_calc_file_shash(f, hash);
-out:
if (new_file_instance)
fput(f);
return rc;
@@ -655,63 +368,6 @@ int ima_calc_field_array_hash(struct ima_field_data *field_data,
return rc;
}
-static int calc_buffer_ahash_atfm(const void *buf, loff_t len,
- struct ima_digest_data *hash,
- struct crypto_ahash *tfm)
-{
- struct ahash_request *req;
- struct scatterlist sg;
- struct crypto_wait wait;
- int rc, ahash_rc = 0;
-
- hash->length = crypto_ahash_digestsize(tfm);
-
- req = ahash_request_alloc(tfm, GFP_KERNEL);
- if (!req)
- return -ENOMEM;
-
- crypto_init_wait(&wait);
- ahash_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG |
- CRYPTO_TFM_REQ_MAY_SLEEP,
- crypto_req_done, &wait);
-
- rc = ahash_wait(crypto_ahash_init(req), &wait);
- if (rc)
- goto out;
-
- sg_init_one(&sg, buf, len);
- ahash_request_set_crypt(req, &sg, NULL, len);
-
- ahash_rc = crypto_ahash_update(req);
-
- /* wait for the update request to complete */
- rc = ahash_wait(ahash_rc, &wait);
- if (!rc) {
- ahash_request_set_crypt(req, NULL, hash->digest, 0);
- rc = ahash_wait(crypto_ahash_final(req), &wait);
- }
-out:
- ahash_request_free(req);
- return rc;
-}
-
-static int calc_buffer_ahash(const void *buf, loff_t len,
- struct ima_digest_data *hash)
-{
- struct crypto_ahash *tfm;
- int rc;
-
- tfm = ima_alloc_atfm(hash->algo);
- if (IS_ERR(tfm))
- return PTR_ERR(tfm);
-
- rc = calc_buffer_ahash_atfm(buf, len, hash, tfm);
-
- ima_free_atfm(tfm);
-
- return rc;
-}
-
static int calc_buffer_shash_tfm(const void *buf, loff_t size,
struct ima_digest_data *hash,
struct crypto_shash *tfm)
@@ -742,8 +398,8 @@ static int calc_buffer_shash_tfm(const void *buf, loff_t size,
return rc;
}
-static int calc_buffer_shash(const void *buf, loff_t len,
- struct ima_digest_data *hash)
+int ima_calc_buffer_hash(const void *buf, loff_t len,
+ struct ima_digest_data *hash)
{
struct crypto_shash *tfm;
int rc;
@@ -758,20 +414,6 @@ static int calc_buffer_shash(const void *buf, loff_t len,
return rc;
}
-int ima_calc_buffer_hash(const void *buf, loff_t len,
- struct ima_digest_data *hash)
-{
- int rc;
-
- if (ima_ahash_minsize && len >= ima_ahash_minsize) {
- rc = calc_buffer_ahash(buf, len, hash);
- if (!rc)
- return 0;
- }
-
- return calc_buffer_shash(buf, len, hash);
-}
-
static void ima_pcrread(u32 idx, struct tpm_digest *d)
{
if (!ima_tpm_chip)
@@ -832,7 +474,7 @@ static int ima_calc_boot_aggregate_tfm(char *digest, u16 alg_id,
}
}
if (!rc)
- crypto_shash_final(shash, digest);
+ rc = crypto_shash_final(shash, digest);
return rc;
}
diff --git a/security/integrity/ima/ima_efi.c b/security/integrity/ima/ima_efi.c
index 138029bfcce1..bca57d836cb9 100644
--- a/security/integrity/ima/ima_efi.c
+++ b/security/integrity/ima/ima_efi.c
@@ -2,52 +2,9 @@
/*
* Copyright (C) 2018 IBM Corporation
*/
-#include <linux/efi.h>
#include <linux/module.h>
#include <linux/ima.h>
-#include <asm/efi.h>
-
-#ifndef arch_ima_efi_boot_mode
-#define arch_ima_efi_boot_mode efi_secureboot_mode_unset
-#endif
-
-static enum efi_secureboot_mode get_sb_mode(void)
-{
- enum efi_secureboot_mode mode;
-
- if (!efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE)) {
- pr_info("ima: secureboot mode unknown, no efi\n");
- return efi_secureboot_mode_unknown;
- }
-
- mode = efi_get_secureboot_mode(efi.get_variable);
- if (mode == efi_secureboot_mode_disabled)
- pr_info("ima: secureboot mode disabled\n");
- else if (mode == efi_secureboot_mode_unknown)
- pr_info("ima: secureboot mode unknown\n");
- else
- pr_info("ima: secureboot mode enabled\n");
- return mode;
-}
-
-bool arch_ima_get_secureboot(void)
-{
- static enum efi_secureboot_mode sb_mode;
- static bool initialized;
-
- if (!initialized && efi_enabled(EFI_BOOT)) {
- sb_mode = arch_ima_efi_boot_mode;
-
- if (sb_mode == efi_secureboot_mode_unset)
- sb_mode = get_sb_mode();
- initialized = true;
- }
-
- if (sb_mode == efi_secureboot_mode_enabled)
- return true;
- else
- return false;
-}
+#include <linux/secure_boot.h>
/* secureboot arch rules */
static const char * const sb_arch_rules[] = {
@@ -67,11 +24,9 @@ static const char * const sb_arch_rules[] = {
const char * const *arch_get_ima_policy(void)
{
- if (IS_ENABLED(CONFIG_IMA_ARCH_POLICY) && arch_ima_get_secureboot()) {
- if (IS_ENABLED(CONFIG_MODULE_SIG))
- set_module_sig_enforced();
- if (IS_ENABLED(CONFIG_KEXEC_SIG))
- set_kexec_sig_enforced();
+ if (IS_ENABLED(CONFIG_IMA_ARCH_POLICY) && arch_get_secureboot()) {
+ set_module_sig_enforced();
+ set_kexec_sig_enforced();
return sb_arch_rules;
}
return NULL;
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
index 012a58959ff0..ca4931a95098 100644
--- a/security/integrity/ima/ima_fs.c
+++ b/security/integrity/ima/ima_fs.c
@@ -132,16 +132,12 @@ int ima_measurements_show(struct seq_file *m, void *v)
char *template_name;
u32 pcr, namelen, template_data_len; /* temporary fields */
bool is_ima_template = false;
- enum hash_algo algo;
int i, algo_idx;
algo_idx = ima_sha1_idx;
- algo = HASH_ALGO_SHA1;
- if (m->file != NULL) {
+ if (m->file != NULL)
algo_idx = (unsigned long)file_inode(m->file)->i_private;
- algo = ima_algo_array[algo_idx].algo;
- }
/* get entry */
e = qe->entry;
@@ -160,7 +156,8 @@ int ima_measurements_show(struct seq_file *m, void *v)
ima_putc(m, &pcr, sizeof(e->pcr));
/* 2nd: template digest */
- ima_putc(m, e->digests[algo_idx].digest, hash_digest_size[algo]);
+ ima_putc(m, e->digests[algo_idx].digest,
+ ima_algo_array[algo_idx].digest_size);
/* 3rd: template name size */
namelen = !ima_canonical_fmt ? strlen(template_name) :
@@ -229,16 +226,12 @@ static int ima_ascii_measurements_show(struct seq_file *m, void *v)
struct ima_queue_entry *qe = v;
struct ima_template_entry *e;
char *template_name;
- enum hash_algo algo;
int i, algo_idx;
algo_idx = ima_sha1_idx;
- algo = HASH_ALGO_SHA1;
- if (m->file != NULL) {
+ if (m->file != NULL)
algo_idx = (unsigned long)file_inode(m->file)->i_private;
- algo = ima_algo_array[algo_idx].algo;
- }
/* get entry */
e = qe->entry;
@@ -252,7 +245,8 @@ static int ima_ascii_measurements_show(struct seq_file *m, void *v)
seq_printf(m, "%2d ", e->pcr);
/* 2nd: template hash */
- ima_print_digest(m, e->digests[algo_idx].digest, hash_digest_size[algo]);
+ ima_print_digest(m, e->digests[algo_idx].digest,
+ ima_algo_array[algo_idx].digest_size);
/* 3th: template name */
seq_printf(m, " %s", template_name);
@@ -404,16 +398,24 @@ static int __init create_securityfs_measurement_lists(void)
char file_name[NAME_MAX + 1];
struct dentry *dentry;
- sprintf(file_name, "ascii_runtime_measurements_%s",
- hash_algo_name[algo]);
+ if (algo == HASH_ALGO__LAST)
+ sprintf(file_name, "ascii_runtime_measurements_tpm_alg_%x",
+ ima_tpm_chip->allocated_banks[i].alg_id);
+ else
+ sprintf(file_name, "ascii_runtime_measurements_%s",
+ hash_algo_name[algo]);
dentry = securityfs_create_file(file_name, S_IRUSR | S_IRGRP,
ima_dir, (void *)(uintptr_t)i,
&ima_ascii_measurements_ops);
if (IS_ERR(dentry))
return PTR_ERR(dentry);
- sprintf(file_name, "binary_runtime_measurements_%s",
- hash_algo_name[algo]);
+ if (algo == HASH_ALGO__LAST)
+ sprintf(file_name, "binary_runtime_measurements_tpm_alg_%x",
+ ima_tpm_chip->allocated_banks[i].alg_id);
+ else
+ sprintf(file_name, "binary_runtime_measurements_%s",
+ hash_algo_name[algo]);
dentry = securityfs_create_file(file_name, S_IRUSR | S_IRGRP,
ima_dir, (void *)(uintptr_t)i,
&ima_measurements_ops);
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index 1d6229b156fb..5cea53fc36df 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -180,6 +180,29 @@ static void ima_rdwr_violation_check(struct file *file,
"invalid_pcr", "open_writers");
}
+/*
+ * Detect file change based on STATX_CHANGE_COOKIE, when supported, and
+ * fallback to detecting file change based on i_version. On filesystems
+ * which do not support either, assume the file changed.
+ */
+static bool ima_detect_file_change(struct ima_iint_cache *iint,
+ struct inode *inode, struct file *file)
+{
+ struct kstat stat;
+ int result;
+
+ result = vfs_getattr_nosec(&file->f_path, &stat, STATX_CHANGE_COOKIE,
+ AT_STATX_SYNC_AS_STAT);
+
+ if (!result && stat.result_mask & STATX_CHANGE_COOKIE)
+ return stat.change_cookie != iint->real_inode.version;
+
+ if (IS_I_VERSION(inode))
+ return !inode_eq_iversion(inode, iint->real_inode.version);
+
+ return true;
+}
+
static void ima_check_last_writer(struct ima_iint_cache *iint,
struct inode *inode, struct file *file)
{
@@ -191,18 +214,13 @@ static void ima_check_last_writer(struct ima_iint_cache *iint,
mutex_lock(&iint->mutex);
if (atomic_read(&inode->i_writecount) == 1) {
- struct kstat stat;
-
clear_bit(IMA_EMITTED_OPENWRITERS, &iint->atomic_flags);
update = test_and_clear_bit(IMA_UPDATE_XATTR,
&iint->atomic_flags);
- if ((iint->flags & IMA_NEW_FILE) ||
- vfs_getattr_nosec(&file->f_path, &stat,
- STATX_CHANGE_COOKIE,
- AT_STATX_SYNC_AS_STAT) ||
- !(stat.result_mask & STATX_CHANGE_COOKIE) ||
- stat.change_cookie != iint->real_inode.version) {
+
+ if (iint->flags & IMA_NEW_FILE ||
+ ima_detect_file_change(iint, inode, file)) {
iint->flags &= ~(IMA_DONE_MASK | IMA_NEW_FILE);
iint->measured_pcrs = 0;
if (update)
@@ -953,8 +971,7 @@ static int ima_load_data(enum kernel_load_data_id id, bool contents)
switch (id) {
case LOADING_KEXEC_IMAGE:
- if (IS_ENABLED(CONFIG_KEXEC_SIG)
- && arch_ima_get_secureboot()) {
+ if (IS_ENABLED(CONFIG_KEXEC_SIG) && arch_get_secureboot()) {
pr_err("impossible to appraise a kernel image without a file descriptor; try using kexec_file_load syscall.\n");
return -EACCES;
}
diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c
index bf2d7ba4c14a..f7f940a76922 100644
--- a/security/integrity/ima/ima_policy.c
+++ b/security/integrity/ima/ima_policy.c
@@ -1298,7 +1298,8 @@ static bool ima_validate_rule(struct ima_rule_entry *entry)
IMA_GID | IMA_EGID |
IMA_FGROUP | IMA_DIGSIG_REQUIRED |
IMA_PERMIT_DIRECTIO | IMA_VALIDATE_ALGOS |
- IMA_CHECK_BLACKLIST | IMA_VERITY_REQUIRED))
+ IMA_CHECK_BLACKLIST | IMA_VERITY_REQUIRED |
+ IMA_SIGV3_REQUIRED))
return false;
break;
@@ -1833,9 +1834,7 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
break;
case Opt_digest_type:
ima_log_string(ab, "digest_type", args[0].from);
- if (entry->flags & IMA_DIGSIG_REQUIRED)
- result = -EINVAL;
- else if ((strcmp(args[0].from, "verity")) == 0)
+ if ((strcmp(args[0].from, "verity")) == 0)
entry->flags |= IMA_VERITY_REQUIRED;
else
result = -EINVAL;
@@ -1849,14 +1848,13 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
else
entry->flags |= IMA_DIGSIG_REQUIRED | IMA_CHECK_BLACKLIST;
} else if (strcmp(args[0].from, "sigv3") == 0) {
- /* Only fsverity supports sigv3 for now */
- if (entry->flags & IMA_VERITY_REQUIRED)
- entry->flags |= IMA_DIGSIG_REQUIRED | IMA_CHECK_BLACKLIST;
- else
- result = -EINVAL;
+ entry->flags |= IMA_SIGV3_REQUIRED |
+ IMA_DIGSIG_REQUIRED |
+ IMA_CHECK_BLACKLIST;
} else if (IS_ENABLED(CONFIG_IMA_APPRAISE_MODSIG) &&
strcmp(args[0].from, "imasig|modsig") == 0) {
- if (entry->flags & IMA_VERITY_REQUIRED)
+ if ((entry->flags & IMA_VERITY_REQUIRED) ||
+ (entry->flags & IMA_SIGV3_REQUIRED))
result = -EINVAL;
else
entry->flags |= IMA_DIGSIG_REQUIRED |
@@ -1941,7 +1939,7 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
/* d-ngv2 template field recommended for unsigned fs-verity digests */
if (!result && entry->action == MEASURE &&
- entry->flags & IMA_VERITY_REQUIRED) {
+ (entry->flags & IMA_VERITY_REQUIRED)) {
template_desc = entry->template ? entry->template :
ima_template_desc_current();
check_template_field(template_desc, "d-ngv2",
@@ -2309,7 +2307,7 @@ int ima_policy_show(struct seq_file *m, void *v)
if (entry->template)
seq_printf(m, "template=%s ", entry->template->name);
if (entry->flags & IMA_DIGSIG_REQUIRED) {
- if (entry->flags & IMA_VERITY_REQUIRED)
+ if (entry->flags & IMA_SIGV3_REQUIRED)
seq_puts(m, "appraise_type=sigv3 ");
else if (entry->flags & IMA_MODSIG_ALLOWED)
seq_puts(m, "appraise_type=imasig|modsig ");