diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2026-04-17 15:42:01 -0700 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2026-04-17 15:42:01 -0700 |
| commit | 9cdca336677b4d15579ec462e33c8a330ab3a9de (patch) | |
| tree | e46e6f04db9594619094ebefb265113012e60b0e /security/integrity/ima | |
| parent | ba314ed1bff907321ab4091a4e46c4d9f24b5e39 (diff) | |
| parent | 82bbd447199ff1441031d2eaf9afe041550cf525 (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.h | 29 | ||||
| -rw-r--r-- | security/integrity/ima/ima_api.c | 13 | ||||
| -rw-r--r-- | security/integrity/ima/ima_appraise.c | 79 | ||||
| -rw-r--r-- | security/integrity/ima/ima_crypto.c | 390 | ||||
| -rw-r--r-- | security/integrity/ima/ima_efi.c | 53 | ||||
| -rw-r--r-- | security/integrity/ima/ima_fs.c | 34 | ||||
| -rw-r--r-- | security/integrity/ima/ima_main.c | 37 | ||||
| -rw-r--r-- | security/integrity/ima/ima_policy.c | 22 |
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 "); |
