diff --git a/crypto/api.c b/crypto/api.c index 0ee632bba064..7aca9f86c5f3 100644 --- a/crypto/api.c +++ b/crypto/api.c @@ -229,7 +229,7 @@ static struct crypto_alg *crypto_larval_lookup(const char *name, u32 type, mask &= ~(CRYPTO_ALG_LARVAL | CRYPTO_ALG_DEAD); alg = crypto_alg_lookup(name, type, mask); - if (!alg) { + if (!alg && !(mask & CRYPTO_NOLOAD)) { request_module("crypto-%s", name); if (!((type ^ CRYPTO_ALG_NEED_FALLBACK) & mask & diff --git a/include/linux/crypto.h b/include/linux/crypto.h index 6eb06101089f..e8839d3a7559 100644 --- a/include/linux/crypto.h +++ b/include/linux/crypto.h @@ -112,6 +112,11 @@ */ #define CRYPTO_ALG_OPTIONAL_KEY 0x00004000 +/* + * Don't trigger module loading + */ +#define CRYPTO_NOLOAD 0x00008000 + /* * Transform masks and values (for crt_flags). */ diff --git a/include/linux/integrity.h b/include/linux/integrity.h index 858d3f4a2241..54c853ec2fd1 100644 --- a/include/linux/integrity.h +++ b/include/linux/integrity.h @@ -44,4 +44,17 @@ static inline void integrity_load_keys(void) } #endif /* CONFIG_INTEGRITY */ +#ifdef CONFIG_INTEGRITY_ASYMMETRIC_KEYS + +extern int integrity_kernel_module_request(char *kmod_name); + +#else + +static inline int integrity_kernel_module_request(char *kmod_name) +{ + return 0; +} + +#endif /* CONFIG_INTEGRITY_ASYMMETRIC_KEYS */ + #endif /* _LINUX_INTEGRITY_H */ diff --git a/include/uapi/linux/audit.h b/include/uapi/linux/audit.h index 4e3eaba84175..818ae690ab79 100644 --- a/include/uapi/linux/audit.h +++ b/include/uapi/linux/audit.h @@ -148,6 +148,7 @@ #define AUDIT_INTEGRITY_PCR 1804 /* PCR invalidation msgs */ #define AUDIT_INTEGRITY_RULE 1805 /* policy rule */ #define AUDIT_INTEGRITY_EVM_XATTR 1806 /* New EVM-covered xattr */ +#define AUDIT_INTEGRITY_POLICY_RULE 1807 /* IMA policy rules */ #define AUDIT_KERNEL 2000 /* Asynchronous audit record. NOT A REQUEST. */ diff --git a/security/integrity/digsig_asymmetric.c b/security/integrity/digsig_asymmetric.c index ab6a029062a1..6dc075144508 100644 --- a/security/integrity/digsig_asymmetric.c +++ b/security/integrity/digsig_asymmetric.c @@ -115,3 +115,26 @@ int asymmetric_verify(struct key *keyring, const char *sig, pr_debug("%s() = %d\n", __func__, ret); return ret; } + +/** + * integrity_kernel_module_request - prevent crypto-pkcs1pad(rsa,*) requests + * @kmod_name: kernel module name + * + * We have situation, when public_key_verify_signature() in case of RSA + * algorithm use alg_name to store internal information in order to + * construct an algorithm on the fly, but crypto_larval_lookup() will try + * to use alg_name in order to load kernel module with same name. + * Since we don't have any real "crypto-pkcs1pad(rsa,*)" kernel modules, + * we are safe to fail such module request from crypto_larval_lookup(). + * + * In this way we prevent modprobe execution during digsig verification + * and avoid possible deadlock if modprobe and/or it's dependencies + * also signed with digsig. + */ +int integrity_kernel_module_request(char *kmod_name) +{ + if (strncmp(kmod_name, "crypto-pkcs1pad(rsa,", 20) == 0) + return -EINVAL; + + return 0; +} diff --git a/security/integrity/evm/Kconfig b/security/integrity/evm/Kconfig index d593346d0bba..60221852b26a 100644 --- a/security/integrity/evm/Kconfig +++ b/security/integrity/evm/Kconfig @@ -4,6 +4,7 @@ config EVM select ENCRYPTED_KEYS select CRYPTO_HMAC select CRYPTO_SHA1 + select CRYPTO_HASH_INFO default n help EVM protects a file's security extended attributes against diff --git a/security/integrity/evm/evm.h b/security/integrity/evm/evm.h index 1257c3c24723..c3f437f5db10 100644 --- a/security/integrity/evm/evm.h +++ b/security/integrity/evm/evm.h @@ -47,6 +47,11 @@ extern struct crypto_shash *hash_tfm; /* List of EVM protected security xattrs */ extern struct list_head evm_config_xattrnames; +struct evm_digest { + struct ima_digest_data hdr; + char digest[IMA_MAX_DIGEST_SIZE]; +} __packed; + int evm_init_key(void); int evm_update_evmxattr(struct dentry *dentry, const char *req_xattr_name, @@ -54,10 +59,11 @@ int evm_update_evmxattr(struct dentry *dentry, size_t req_xattr_value_len); int evm_calc_hmac(struct dentry *dentry, const char *req_xattr_name, const char *req_xattr_value, - size_t req_xattr_value_len, char *digest); + size_t req_xattr_value_len, struct evm_digest *data); int evm_calc_hash(struct dentry *dentry, const char *req_xattr_name, const char *req_xattr_value, - size_t req_xattr_value_len, char type, char *digest); + size_t req_xattr_value_len, char type, + struct evm_digest *data); int evm_init_hmac(struct inode *inode, const struct xattr *xattr, char *hmac_val); int evm_init_secfs(void); diff --git a/security/integrity/evm/evm_crypto.c b/security/integrity/evm/evm_crypto.c index b60524310855..8a3905bb02c7 100644 --- a/security/integrity/evm/evm_crypto.c +++ b/security/integrity/evm/evm_crypto.c @@ -21,6 +21,7 @@ #include #include #include +#include #include "evm.h" #define EVMKEY "evm-key" @@ -29,7 +30,7 @@ static unsigned char evmkey[MAX_KEY_SIZE]; static int evmkey_len = MAX_KEY_SIZE; struct crypto_shash *hmac_tfm; -struct crypto_shash *hash_tfm; +static struct crypto_shash *evm_tfm[HASH_ALGO__LAST]; static DEFINE_MUTEX(mutex); @@ -38,7 +39,6 @@ static DEFINE_MUTEX(mutex); static unsigned long evm_set_key_flags; static char * const evm_hmac = "hmac(sha1)"; -static char * const evm_hash = "sha1"; /** * evm_set_key() - set EVM HMAC key from the kernel @@ -74,10 +74,10 @@ busy: } EXPORT_SYMBOL_GPL(evm_set_key); -static struct shash_desc *init_desc(char type) +static struct shash_desc *init_desc(char type, uint8_t hash_algo) { long rc; - char *algo; + const char *algo; struct crypto_shash **tfm; struct shash_desc *desc; @@ -89,15 +89,16 @@ static struct shash_desc *init_desc(char type) tfm = &hmac_tfm; algo = evm_hmac; } else { - tfm = &hash_tfm; - algo = evm_hash; + tfm = &evm_tfm[hash_algo]; + algo = hash_algo_name[hash_algo]; } if (*tfm == NULL) { mutex_lock(&mutex); if (*tfm) goto out; - *tfm = crypto_alloc_shash(algo, 0, CRYPTO_ALG_ASYNC); + *tfm = crypto_alloc_shash(algo, 0, + CRYPTO_ALG_ASYNC | CRYPTO_NOLOAD); if (IS_ERR(*tfm)) { rc = PTR_ERR(*tfm); pr_err("Can not allocate %s (reason: %ld)\n", algo, rc); @@ -186,10 +187,10 @@ static void hmac_add_misc(struct shash_desc *desc, struct inode *inode, * each xattr, but attempt to re-use the previously allocated memory. */ static int evm_calc_hmac_or_hash(struct dentry *dentry, - const char *req_xattr_name, - const char *req_xattr_value, - size_t req_xattr_value_len, - char type, char *digest) + const char *req_xattr_name, + const char *req_xattr_value, + size_t req_xattr_value_len, + uint8_t type, struct evm_digest *data) { struct inode *inode = d_backing_inode(dentry); struct xattr_list *xattr; @@ -204,10 +205,12 @@ static int evm_calc_hmac_or_hash(struct dentry *dentry, inode->i_sb->s_user_ns != &init_user_ns) return -EOPNOTSUPP; - desc = init_desc(type); + desc = init_desc(type, data->hdr.algo); if (IS_ERR(desc)) return PTR_ERR(desc); + data->hdr.length = crypto_shash_digestsize(desc->tfm); + error = -ENODATA; list_for_each_entry_rcu(xattr, &evm_config_xattrnames, list) { bool is_ima = false; @@ -239,7 +242,7 @@ static int evm_calc_hmac_or_hash(struct dentry *dentry, if (is_ima) ima_present = true; } - hmac_add_misc(desc, inode, type, digest); + hmac_add_misc(desc, inode, type, data->digest); /* Portable EVM signatures must include an IMA hash */ if (type == EVM_XATTR_PORTABLE_DIGSIG && !ima_present) @@ -252,18 +255,18 @@ out: int evm_calc_hmac(struct dentry *dentry, const char *req_xattr_name, const char *req_xattr_value, size_t req_xattr_value_len, - char *digest) + struct evm_digest *data) { return evm_calc_hmac_or_hash(dentry, req_xattr_name, req_xattr_value, - req_xattr_value_len, EVM_XATTR_HMAC, digest); + req_xattr_value_len, EVM_XATTR_HMAC, data); } int evm_calc_hash(struct dentry *dentry, const char *req_xattr_name, const char *req_xattr_value, size_t req_xattr_value_len, - char type, char *digest) + char type, struct evm_digest *data) { return evm_calc_hmac_or_hash(dentry, req_xattr_name, req_xattr_value, - req_xattr_value_len, type, digest); + req_xattr_value_len, type, data); } static int evm_is_immutable(struct dentry *dentry, struct inode *inode) @@ -303,7 +306,7 @@ int evm_update_evmxattr(struct dentry *dentry, const char *xattr_name, const char *xattr_value, size_t xattr_value_len) { struct inode *inode = d_backing_inode(dentry); - struct evm_ima_xattr_data xattr_data; + struct evm_digest data; int rc = 0; /* @@ -316,13 +319,14 @@ int evm_update_evmxattr(struct dentry *dentry, const char *xattr_name, if (rc) return -EPERM; + data.hdr.algo = HASH_ALGO_SHA1; rc = evm_calc_hmac(dentry, xattr_name, xattr_value, - xattr_value_len, xattr_data.digest); + xattr_value_len, &data); if (rc == 0) { - xattr_data.type = EVM_XATTR_HMAC; + data.hdr.xattr.sha1.type = EVM_XATTR_HMAC; rc = __vfs_setxattr_noperm(dentry, XATTR_NAME_EVM, - &xattr_data, - sizeof(xattr_data), 0); + &data.hdr.xattr.data[1], + SHA1_DIGEST_SIZE + 1, 0); } else if (rc == -ENODATA && (inode->i_opflags & IOP_XATTR)) { rc = __vfs_removexattr(dentry, XATTR_NAME_EVM); } @@ -334,7 +338,7 @@ int evm_init_hmac(struct inode *inode, const struct xattr *lsm_xattr, { struct shash_desc *desc; - desc = init_desc(EVM_XATTR_HMAC); + desc = init_desc(EVM_XATTR_HMAC, HASH_ALGO_SHA1); if (IS_ERR(desc)) { pr_info("init_desc failed\n"); return PTR_ERR(desc); diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c index f9eff5041e4c..7f3f54d89a6e 100644 --- a/security/integrity/evm/evm_main.c +++ b/security/integrity/evm/evm_main.c @@ -25,6 +25,7 @@ #include #include +#include #include #include "evm.h" @@ -134,8 +135,9 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry, struct integrity_iint_cache *iint) { struct evm_ima_xattr_data *xattr_data = NULL; - struct evm_ima_xattr_data calc; + struct signature_v2_hdr *hdr; enum integrity_status evm_status = INTEGRITY_PASS; + struct evm_digest digest; struct inode *inode; int rc, xattr_len; @@ -171,25 +173,28 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry, evm_status = INTEGRITY_FAIL; goto out; } + + digest.hdr.algo = HASH_ALGO_SHA1; rc = evm_calc_hmac(dentry, xattr_name, xattr_value, - xattr_value_len, calc.digest); + xattr_value_len, &digest); if (rc) break; - rc = crypto_memneq(xattr_data->digest, calc.digest, - sizeof(calc.digest)); + rc = crypto_memneq(xattr_data->digest, digest.digest, + SHA1_DIGEST_SIZE); if (rc) rc = -EINVAL; break; case EVM_IMA_XATTR_DIGSIG: case EVM_XATTR_PORTABLE_DIGSIG: + hdr = (struct signature_v2_hdr *)xattr_data; + digest.hdr.algo = hdr->hash_algo; rc = evm_calc_hash(dentry, xattr_name, xattr_value, - xattr_value_len, xattr_data->type, - calc.digest); + xattr_value_len, xattr_data->type, &digest); if (rc) break; rc = integrity_digsig_verify(INTEGRITY_KEYRING_EVM, (const char *)xattr_data, xattr_len, - calc.digest, sizeof(calc.digest)); + digest.digest, digest.hdr.length); if (!rc) { inode = d_backing_inode(dentry); diff --git a/security/integrity/evm/evm_secfs.c b/security/integrity/evm/evm_secfs.c index 637eb999e340..77de71b7794c 100644 --- a/security/integrity/evm/evm_secfs.c +++ b/security/integrity/evm/evm_secfs.c @@ -193,8 +193,8 @@ static ssize_t evm_write_xattrs(struct file *file, const char __user *buf, return -E2BIG; ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_INTEGRITY_EVM_XATTR); - if (IS_ERR(ab)) - return PTR_ERR(ab); + if (!ab) + return -ENOMEM; xattr = kmalloc(sizeof(struct xattr_list), GFP_KERNEL); if (!xattr) { diff --git a/security/integrity/iint.c b/security/integrity/iint.c index 149faa81f6f0..5a6810041e5c 100644 --- a/security/integrity/iint.c +++ b/security/integrity/iint.c @@ -219,10 +219,13 @@ static int __init integrity_fs_init(void) { integrity_dir = securityfs_create_dir("integrity", NULL); if (IS_ERR(integrity_dir)) { - pr_err("Unable to create integrity sysfs dir: %ld\n", - PTR_ERR(integrity_dir)); + int ret = PTR_ERR(integrity_dir); + + if (ret != -ENODEV) + pr_err("Unable to create integrity sysfs dir: %d\n", + ret); integrity_dir = NULL; - return PTR_ERR(integrity_dir); + return ret; } return 0; diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig index 004919d9bf09..13b446328dda 100644 --- a/security/integrity/ima/Kconfig +++ b/security/integrity/ima/Kconfig @@ -12,6 +12,7 @@ config IMA select TCG_TIS if TCG_TPM && X86 select TCG_CRB if TCG_TPM && ACPI select TCG_IBMVTPM if TCG_TPM && PPC_PSERIES + select INTEGRITY_AUDIT if AUDIT help The Trusted Computing Group(TCG) runtime Integrity Measurement Architecture(IMA) maintains a list of hash diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index 1659abb344f9..8c9499867c91 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -657,14 +657,16 @@ static int ima_lsm_rule_init(struct ima_rule_entry *entry, static void ima_log_string_op(struct audit_buffer *ab, char *key, char *value, bool (*rule_operator)(kuid_t, kuid_t)) { + if (!ab) + return; + if (rule_operator == &uid_gt) audit_log_format(ab, "%s>", key); else if (rule_operator == &uid_lt) audit_log_format(ab, "%s<", key); else audit_log_format(ab, "%s=", key); - audit_log_untrustedstring(ab, value); - audit_log_format(ab, " "); + audit_log_format(ab, "%s ", value); } static void ima_log_string(struct audit_buffer *ab, char *key, char *value) { @@ -679,7 +681,8 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) bool uid_token; int result = 0; - ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_INTEGRITY_RULE); + ab = integrity_audit_log_start(audit_context(), GFP_KERNEL, + AUDIT_INTEGRITY_POLICY_RULE); entry->uid = INVALID_UID; entry->fowner = INVALID_UID; diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h index 0bb372eed62a..e60473b13a8d 100644 --- a/security/integrity/integrity.h +++ b/security/integrity/integrity.h @@ -15,6 +15,7 @@ #include #include #include +#include /* iint action cache flags */ #define IMA_MEASURE 0x00000001 @@ -199,6 +200,13 @@ static inline void evm_load_x509(void) void integrity_audit_msg(int audit_msgno, struct inode *inode, const unsigned char *fname, const char *op, const char *cause, int result, int info); + +static inline struct audit_buffer * +integrity_audit_log_start(struct audit_context *ctx, gfp_t gfp_mask, int type) +{ + return audit_log_start(ctx, gfp_mask, type); +} + #else static inline void integrity_audit_msg(int audit_msgno, struct inode *inode, const unsigned char *fname, @@ -206,4 +214,11 @@ static inline void integrity_audit_msg(int audit_msgno, struct inode *inode, int result, int info) { } + +static inline struct audit_buffer * +integrity_audit_log_start(struct audit_context *ctx, gfp_t gfp_mask, int type) +{ + return NULL; +} + #endif diff --git a/security/integrity/integrity_audit.c b/security/integrity/integrity_audit.c index ab10a25310a1..82c98f7d217e 100644 --- a/security/integrity/integrity_audit.c +++ b/security/integrity/integrity_audit.c @@ -45,11 +45,7 @@ void integrity_audit_msg(int audit_msgno, struct inode *inode, from_kuid(&init_user_ns, audit_get_loginuid(current)), audit_get_sessionid(current)); audit_log_task_context(ab); - audit_log_format(ab, " op="); - audit_log_string(ab, op); - audit_log_format(ab, " cause="); - audit_log_string(ab, cause); - audit_log_format(ab, " comm="); + audit_log_format(ab, " op=%s cause=%s comm=", op, cause); audit_log_untrustedstring(ab, get_task_comm(name, current)); if (fname) { audit_log_format(ab, " name="); diff --git a/security/security.c b/security/security.c index ab4f96347ebb..47cfff01d7ec 100644 --- a/security/security.c +++ b/security/security.c @@ -1032,7 +1032,12 @@ int security_kernel_create_files_as(struct cred *new, struct inode *inode) int security_kernel_module_request(char *kmod_name) { - return call_int_hook(kernel_module_request, 0, kmod_name); + int ret; + + ret = call_int_hook(kernel_module_request, 0, kmod_name); + if (ret) + return ret; + return integrity_kernel_module_request(kmod_name); } int security_kernel_read_file(struct file *file, enum kernel_read_file_id id)