Merge branch 'next-integrity' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security
Pull integrity updates from James Morris: "This adds support for EVM signatures based on larger digests, contains a new audit record AUDIT_INTEGRITY_POLICY_RULE to differentiate the IMA policy rules from the IMA-audit messages, addresses two deadlocks due to either loading or searching for crypto algorithms, and cleans up the audit messages" * 'next-integrity' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security: EVM: fix return value check in evm_write_xattrs() integrity: prevent deadlock during digsig verification. evm: Allow non-SHA1 digital signatures evm: Don't deadlock if a crypto algorithm is unavailable integrity: silence warning when CONFIG_SECURITYFS is not enabled ima: Differentiate auditing policy rules from "audit" actions ima: Do not audit if CONFIG_INTEGRITY_AUDIT is not set ima: Use audit_log_format() rather than audit_log_string() ima: Call audit_log_string() rather than logging it untrusted
This commit is contained in:
commit
f91e654474
|
@ -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 &
|
||||
|
|
|
@ -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).
|
||||
*/
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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. */
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include <linux/evm.h>
|
||||
#include <keys/encrypted-type.h>
|
||||
#include <crypto/hash.h>
|
||||
#include <crypto/hash_info.h>
|
||||
#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 @@ int evm_set_key(void *key, size_t keylen)
|
|||
}
|
||||
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 @@ static int evm_calc_hmac_or_hash(struct dentry *dentry,
|
|||
|
||||
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);
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include <linux/magic.h>
|
||||
|
||||
#include <crypto/hash.h>
|
||||
#include <crypto/hash_info.h>
|
||||
#include <crypto/algapi.h>
|
||||
#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);
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include <linux/integrity.h>
|
||||
#include <crypto/sha.h>
|
||||
#include <linux/key.h>
|
||||
#include <linux/audit.h>
|
||||
|
||||
/* 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
|
||||
|
|
|
@ -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=");
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue