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 | |
| 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')
| -rw-r--r-- | security/integrity/Makefile | 1 | ||||
| -rw-r--r-- | security/integrity/digsig.c | 8 | ||||
| -rw-r--r-- | security/integrity/digsig_asymmetric.c | 59 | ||||
| -rw-r--r-- | security/integrity/efi_secureboot.c | 56 | ||||
| -rw-r--r-- | security/integrity/evm/evm.h | 3 | ||||
| -rw-r--r-- | security/integrity/evm/evm_main.c | 69 | ||||
| -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 | ||||
| -rw-r--r-- | security/integrity/integrity.h | 15 | ||||
| -rw-r--r-- | security/integrity/platform_certs/load_uefi.c | 2 |
16 files changed, 337 insertions, 533 deletions
diff --git a/security/integrity/Makefile b/security/integrity/Makefile index 92b63039c654..45dfdedbdad4 100644 --- a/security/integrity/Makefile +++ b/security/integrity/Makefile @@ -18,6 +18,7 @@ integrity-$(CONFIG_LOAD_IPL_KEYS) += platform_certs/load_ipl_s390.o integrity-$(CONFIG_LOAD_PPC_KEYS) += platform_certs/efi_parser.o \ platform_certs/load_powerpc.o \ platform_certs/keyring_handler.o +integrity-$(CONFIG_EFI) += efi_secureboot.o # The relative order of the 'ima' and 'evm' LSMs depends on the order below. obj-$(CONFIG_IMA) += ima/ obj-$(CONFIG_EVM) += evm/ diff --git a/security/integrity/digsig.c b/security/integrity/digsig.c index 75c684cce370..1ed686154d7a 100644 --- a/security/integrity/digsig.c +++ b/security/integrity/digsig.c @@ -59,7 +59,7 @@ static struct key *integrity_keyring_from_id(const unsigned int id) } int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen, - const char *digest, int digestlen) + const char *digest, int digestlen, u8 algo) { struct key *keyring; @@ -76,9 +76,11 @@ int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen, return digsig_verify(keyring, sig + 1, siglen - 1, digest, digestlen); case 2: /* regular file data hash based signature */ - case 3: /* struct ima_file_id data based signature */ return asymmetric_verify(keyring, sig, siglen, digest, - digestlen); + digestlen); + case 3: /* struct ima_file_id data based signature */ + return asymmetric_verify_v3(keyring, sig, siglen, digest, + digestlen, algo); } return -EOPNOTSUPP; diff --git a/security/integrity/digsig_asymmetric.c b/security/integrity/digsig_asymmetric.c index 87be85f477d1..6e68ec3becbd 100644 --- a/security/integrity/digsig_asymmetric.c +++ b/security/integrity/digsig_asymmetric.c @@ -131,3 +131,62 @@ out: pr_debug("%s() = %d\n", __func__, ret); return ret; } + +/* + * 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. + * + * 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_max_digest_data *hash) +{ + struct ima_file_id file_id = {.hash_type = type, .hash_algorithm = algo}; + size_t digest_size = hash_digest_size[algo]; + struct crypto_shash *tfm; + size_t file_id_size; + int rc; + + if (type != IMA_VERITY_DIGSIG && type != EVM_IMA_XATTR_DIGSIG && + type != EVM_XATTR_PORTABLE_DIGSIG) + return -EINVAL; + + tfm = crypto_alloc_shash(hash_algo_name[algo], 0, 0); + if (IS_ERR(tfm)) + return PTR_ERR(tfm); + + memcpy(file_id.hash, digest, digest_size); + + /* Calculate the ima_file_id struct hash on the portion used. */ + file_id_size = sizeof(file_id) - (HASH_MAX_DIGESTSIZE - digest_size); + + hash->hdr.algo = algo; + hash->hdr.length = digest_size; + rc = crypto_shash_tfm_digest(tfm, (const u8 *)&file_id, file_id_size, + hash->digest); + + crypto_free_shash(tfm); + return rc; +} + +int asymmetric_verify_v3(struct key *keyring, const char *sig, int siglen, + const char *data, int datalen, u8 algo) +{ + struct signature_v2_hdr *hdr = (struct signature_v2_hdr *)sig; + struct ima_max_digest_data hash; + int rc; + + rc = calc_file_id_hash(hdr->type, algo, data, &hash); + if (rc) + return -EINVAL; + + return asymmetric_verify(keyring, sig, siglen, hash.digest, + hash.hdr.length); +} diff --git a/security/integrity/efi_secureboot.c b/security/integrity/efi_secureboot.c new file mode 100644 index 000000000000..bfd4260a83a3 --- /dev/null +++ b/security/integrity/efi_secureboot.c @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: GPL-1.0+ +/* + * Copyright (C) 2018 IBM Corporation + */ +#include <linux/efi.h> +#include <linux/secure_boot.h> +#include <asm/efi.h> + +#ifndef arch_efi_boot_mode +#define arch_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("integrity: 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("integrity: secureboot mode disabled\n"); + else if (mode == efi_secureboot_mode_unknown) + pr_info("integrity: secureboot mode unknown\n"); + else + pr_info("integrity: secureboot mode enabled\n"); + return mode; +} + +/* + * Query secure boot status + * + * Note don't call this function too early e.g. in __setup hook otherwise the + * kernel may hang when calling efi_get_secureboot_mode. + * + */ +bool arch_get_secureboot(void) +{ + static enum efi_secureboot_mode sb_mode; + static bool initialized; + + if (!initialized && efi_enabled(EFI_BOOT)) { + sb_mode = arch_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; +} diff --git a/security/integrity/evm/evm.h b/security/integrity/evm/evm.h index 51aba5a54275..694552aceaf8 100644 --- a/security/integrity/evm/evm.h +++ b/security/integrity/evm/evm.h @@ -20,11 +20,12 @@ #define EVM_INIT_HMAC 0x0001 #define EVM_INIT_X509 0x0002 #define EVM_ALLOW_METADATA_WRITES 0x0004 +#define EVM_SIGV3_REQUIRED 0x0008 #define EVM_SETUP_COMPLETE 0x80000000 /* userland has signaled key load */ #define EVM_KEY_MASK (EVM_INIT_HMAC | EVM_INIT_X509) #define EVM_INIT_MASK (EVM_INIT_HMAC | EVM_INIT_X509 | EVM_SETUP_COMPLETE | \ - EVM_ALLOW_METADATA_WRITES) + EVM_ALLOW_METADATA_WRITES | EVM_SIGV3_REQUIRED) struct xattr_list { struct list_head list; diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c index 41b053c900f2..b59e3f121b8a 100644 --- a/security/integrity/evm/evm_main.c +++ b/security/integrity/evm/evm_main.c @@ -72,17 +72,25 @@ static struct xattr_list evm_config_default_xattrnames[] = { LIST_HEAD(evm_config_xattrnames); +static char *evm_cmdline __initdata; +core_param(evm, evm_cmdline, charp, 0); + static int evm_fixmode __ro_after_init; -static int __init evm_set_fixmode(char *str) +static void __init evm_set_fixmode(void) { - if (strncmp(str, "fix", 3) == 0) - evm_fixmode = 1; - else - pr_err("invalid \"%s\" mode", str); + if (!evm_cmdline) + return; - return 1; + if (strncmp(evm_cmdline, "fix", 3) == 0) { + if (arch_get_secureboot()) { + pr_info("Secure boot enabled: ignoring evm=fix"); + return; + } + evm_fixmode = 1; + } else { + pr_err("invalid \"%s\" mode", evm_cmdline); + } } -__setup("evm=", evm_set_fixmode); static void __init evm_init_config(void) { @@ -128,6 +136,14 @@ static bool evm_hmac_disabled(void) return true; } +static bool evm_sigv3_required(void) +{ + if (evm_initialized & EVM_SIGV3_REQUIRED) + return true; + + return false; +} + static int evm_find_protected_xattrs(struct dentry *dentry) { struct inode *inode = d_backing_inode(dentry); @@ -250,6 +266,12 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry, } hdr = (struct signature_v2_hdr *)xattr_data; + + if (evm_sigv3_required() && hdr->version != 3) { + evm_status = INTEGRITY_FAIL; + goto out; + } + digest.hdr.algo = hdr->hash_algo; rc = evm_calc_hash(dentry, xattr_name, xattr_value, xattr_value_len, xattr_data->type, &digest, @@ -258,7 +280,8 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry, break; rc = integrity_digsig_verify(INTEGRITY_KEYRING_EVM, (const char *)xattr_data, xattr_len, - digest.digest, digest.hdr.length); + digest.digest, digest.hdr.length, + digest.hdr.algo); if (!rc) { if (xattr_data->type == EVM_XATTR_PORTABLE_DIGSIG) { if (iint) @@ -788,6 +811,34 @@ bool evm_revalidate_status(const char *xattr_name) } /** + * evm_fix_hmac - Calculate the HMAC and add it to security.evm for fix mode + * @dentry: pointer to the affected dentry which doesn't yet have security.evm + * xattr + * @xattr_name: pointer to the affected extended attribute name + * @xattr_value: pointer to the new extended attribute value + * @xattr_value_len: pointer to the new extended attribute value length + * + * Expects to be called with i_mutex locked. + * + * Return: 0 on success, -EPERM/-ENOMEM/-EOPNOTSUPP on failure + */ +int evm_fix_hmac(struct dentry *dentry, const char *xattr_name, + const char *xattr_value, size_t xattr_value_len) + +{ + if (!evm_fixmode || !evm_revalidate_status((xattr_name))) + return -EPERM; + + if (!(evm_initialized & EVM_INIT_HMAC)) + return -EPERM; + + if (is_unsupported_hmac_fs(dentry)) + return -EOPNOTSUPP; + + return evm_update_evmxattr(dentry, xattr_name, xattr_value, xattr_value_len); +} + +/** * evm_inode_post_setxattr - update 'security.evm' to reflect the changes * @dentry: pointer to the affected dentry * @xattr_name: pointer to the affected extended attribute name @@ -1119,6 +1170,8 @@ static int __init init_evm(void) evm_init_config(); + evm_set_fixmode(); + error = integrity_init_keyring(INTEGRITY_KEYRING_EVM); if (error) goto error; 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; /* |
