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:
Linus Torvalds 2018-08-15 22:54:12 -07:00
commit f91e654474
16 changed files with 128 additions and 47 deletions

View File

@ -229,7 +229,7 @@ static struct crypto_alg *crypto_larval_lookup(const char *name, u32 type,
mask &= ~(CRYPTO_ALG_LARVAL | CRYPTO_ALG_DEAD); mask &= ~(CRYPTO_ALG_LARVAL | CRYPTO_ALG_DEAD);
alg = crypto_alg_lookup(name, type, mask); alg = crypto_alg_lookup(name, type, mask);
if (!alg) { if (!alg && !(mask & CRYPTO_NOLOAD)) {
request_module("crypto-%s", name); request_module("crypto-%s", name);
if (!((type ^ CRYPTO_ALG_NEED_FALLBACK) & mask & if (!((type ^ CRYPTO_ALG_NEED_FALLBACK) & mask &

View File

@ -112,6 +112,11 @@
*/ */
#define CRYPTO_ALG_OPTIONAL_KEY 0x00004000 #define CRYPTO_ALG_OPTIONAL_KEY 0x00004000
/*
* Don't trigger module loading
*/
#define CRYPTO_NOLOAD 0x00008000
/* /*
* Transform masks and values (for crt_flags). * Transform masks and values (for crt_flags).
*/ */

View File

@ -44,4 +44,17 @@ static inline void integrity_load_keys(void)
} }
#endif /* CONFIG_INTEGRITY */ #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 */ #endif /* _LINUX_INTEGRITY_H */

View File

@ -148,6 +148,7 @@
#define AUDIT_INTEGRITY_PCR 1804 /* PCR invalidation msgs */ #define AUDIT_INTEGRITY_PCR 1804 /* PCR invalidation msgs */
#define AUDIT_INTEGRITY_RULE 1805 /* policy rule */ #define AUDIT_INTEGRITY_RULE 1805 /* policy rule */
#define AUDIT_INTEGRITY_EVM_XATTR 1806 /* New EVM-covered xattr */ #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. */ #define AUDIT_KERNEL 2000 /* Asynchronous audit record. NOT A REQUEST. */

View File

@ -115,3 +115,26 @@ int asymmetric_verify(struct key *keyring, const char *sig,
pr_debug("%s() = %d\n", __func__, ret); pr_debug("%s() = %d\n", __func__, ret);
return 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;
}

View File

@ -4,6 +4,7 @@ config EVM
select ENCRYPTED_KEYS select ENCRYPTED_KEYS
select CRYPTO_HMAC select CRYPTO_HMAC
select CRYPTO_SHA1 select CRYPTO_SHA1
select CRYPTO_HASH_INFO
default n default n
help help
EVM protects a file's security extended attributes against EVM protects a file's security extended attributes against

View File

@ -47,6 +47,11 @@ extern struct crypto_shash *hash_tfm;
/* List of EVM protected security xattrs */ /* List of EVM protected security xattrs */
extern struct list_head evm_config_xattrnames; 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_init_key(void);
int evm_update_evmxattr(struct dentry *dentry, int evm_update_evmxattr(struct dentry *dentry,
const char *req_xattr_name, const char *req_xattr_name,
@ -54,10 +59,11 @@ int evm_update_evmxattr(struct dentry *dentry,
size_t req_xattr_value_len); size_t req_xattr_value_len);
int evm_calc_hmac(struct dentry *dentry, const char *req_xattr_name, int evm_calc_hmac(struct dentry *dentry, const char *req_xattr_name,
const char *req_xattr_value, 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, int evm_calc_hash(struct dentry *dentry, const char *req_xattr_name,
const char *req_xattr_value, 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, int evm_init_hmac(struct inode *inode, const struct xattr *xattr,
char *hmac_val); char *hmac_val);
int evm_init_secfs(void); int evm_init_secfs(void);

View File

@ -21,6 +21,7 @@
#include <linux/evm.h> #include <linux/evm.h>
#include <keys/encrypted-type.h> #include <keys/encrypted-type.h>
#include <crypto/hash.h> #include <crypto/hash.h>
#include <crypto/hash_info.h>
#include "evm.h" #include "evm.h"
#define EVMKEY "evm-key" #define EVMKEY "evm-key"
@ -29,7 +30,7 @@ static unsigned char evmkey[MAX_KEY_SIZE];
static int evmkey_len = MAX_KEY_SIZE; static int evmkey_len = MAX_KEY_SIZE;
struct crypto_shash *hmac_tfm; struct crypto_shash *hmac_tfm;
struct crypto_shash *hash_tfm; static struct crypto_shash *evm_tfm[HASH_ALGO__LAST];
static DEFINE_MUTEX(mutex); static DEFINE_MUTEX(mutex);
@ -38,7 +39,6 @@ static DEFINE_MUTEX(mutex);
static unsigned long evm_set_key_flags; static unsigned long evm_set_key_flags;
static char * const evm_hmac = "hmac(sha1)"; static char * const evm_hmac = "hmac(sha1)";
static char * const evm_hash = "sha1";
/** /**
* evm_set_key() - set EVM HMAC key from the kernel * evm_set_key() - set EVM HMAC key from the kernel
@ -74,10 +74,10 @@ busy:
} }
EXPORT_SYMBOL_GPL(evm_set_key); 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; long rc;
char *algo; const char *algo;
struct crypto_shash **tfm; struct crypto_shash **tfm;
struct shash_desc *desc; struct shash_desc *desc;
@ -89,15 +89,16 @@ static struct shash_desc *init_desc(char type)
tfm = &hmac_tfm; tfm = &hmac_tfm;
algo = evm_hmac; algo = evm_hmac;
} else { } else {
tfm = &hash_tfm; tfm = &evm_tfm[hash_algo];
algo = evm_hash; algo = hash_algo_name[hash_algo];
} }
if (*tfm == NULL) { if (*tfm == NULL) {
mutex_lock(&mutex); mutex_lock(&mutex);
if (*tfm) if (*tfm)
goto out; 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)) { if (IS_ERR(*tfm)) {
rc = PTR_ERR(*tfm); rc = PTR_ERR(*tfm);
pr_err("Can not allocate %s (reason: %ld)\n", algo, rc); pr_err("Can not allocate %s (reason: %ld)\n", algo, rc);
@ -189,7 +190,7 @@ static int evm_calc_hmac_or_hash(struct dentry *dentry,
const char *req_xattr_name, const char *req_xattr_name,
const char *req_xattr_value, const char *req_xattr_value,
size_t req_xattr_value_len, size_t req_xattr_value_len,
char type, char *digest) uint8_t type, struct evm_digest *data)
{ {
struct inode *inode = d_backing_inode(dentry); struct inode *inode = d_backing_inode(dentry);
struct xattr_list *xattr; 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) inode->i_sb->s_user_ns != &init_user_ns)
return -EOPNOTSUPP; return -EOPNOTSUPP;
desc = init_desc(type); desc = init_desc(type, data->hdr.algo);
if (IS_ERR(desc)) if (IS_ERR(desc))
return PTR_ERR(desc); return PTR_ERR(desc);
data->hdr.length = crypto_shash_digestsize(desc->tfm);
error = -ENODATA; error = -ENODATA;
list_for_each_entry_rcu(xattr, &evm_config_xattrnames, list) { list_for_each_entry_rcu(xattr, &evm_config_xattrnames, list) {
bool is_ima = false; bool is_ima = false;
@ -239,7 +242,7 @@ static int evm_calc_hmac_or_hash(struct dentry *dentry,
if (is_ima) if (is_ima)
ima_present = true; 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 */ /* Portable EVM signatures must include an IMA hash */
if (type == EVM_XATTR_PORTABLE_DIGSIG && !ima_present) 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, int evm_calc_hmac(struct dentry *dentry, const char *req_xattr_name,
const char *req_xattr_value, size_t req_xattr_value_len, 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, 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, int evm_calc_hash(struct dentry *dentry, const char *req_xattr_name,
const char *req_xattr_value, size_t req_xattr_value_len, 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, 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) 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) const char *xattr_value, size_t xattr_value_len)
{ {
struct inode *inode = d_backing_inode(dentry); struct inode *inode = d_backing_inode(dentry);
struct evm_ima_xattr_data xattr_data; struct evm_digest data;
int rc = 0; int rc = 0;
/* /*
@ -316,13 +319,14 @@ int evm_update_evmxattr(struct dentry *dentry, const char *xattr_name,
if (rc) if (rc)
return -EPERM; return -EPERM;
data.hdr.algo = HASH_ALGO_SHA1;
rc = evm_calc_hmac(dentry, xattr_name, xattr_value, rc = evm_calc_hmac(dentry, xattr_name, xattr_value,
xattr_value_len, xattr_data.digest); xattr_value_len, &data);
if (rc == 0) { 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, rc = __vfs_setxattr_noperm(dentry, XATTR_NAME_EVM,
&xattr_data, &data.hdr.xattr.data[1],
sizeof(xattr_data), 0); SHA1_DIGEST_SIZE + 1, 0);
} else if (rc == -ENODATA && (inode->i_opflags & IOP_XATTR)) { } else if (rc == -ENODATA && (inode->i_opflags & IOP_XATTR)) {
rc = __vfs_removexattr(dentry, XATTR_NAME_EVM); 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; struct shash_desc *desc;
desc = init_desc(EVM_XATTR_HMAC); desc = init_desc(EVM_XATTR_HMAC, HASH_ALGO_SHA1);
if (IS_ERR(desc)) { if (IS_ERR(desc)) {
pr_info("init_desc failed\n"); pr_info("init_desc failed\n");
return PTR_ERR(desc); return PTR_ERR(desc);

View File

@ -25,6 +25,7 @@
#include <linux/magic.h> #include <linux/magic.h>
#include <crypto/hash.h> #include <crypto/hash.h>
#include <crypto/hash_info.h>
#include <crypto/algapi.h> #include <crypto/algapi.h>
#include "evm.h" #include "evm.h"
@ -134,8 +135,9 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry,
struct integrity_iint_cache *iint) struct integrity_iint_cache *iint)
{ {
struct evm_ima_xattr_data *xattr_data = NULL; 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; enum integrity_status evm_status = INTEGRITY_PASS;
struct evm_digest digest;
struct inode *inode; struct inode *inode;
int rc, xattr_len; int rc, xattr_len;
@ -171,25 +173,28 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry,
evm_status = INTEGRITY_FAIL; evm_status = INTEGRITY_FAIL;
goto out; goto out;
} }
digest.hdr.algo = HASH_ALGO_SHA1;
rc = evm_calc_hmac(dentry, xattr_name, xattr_value, rc = evm_calc_hmac(dentry, xattr_name, xattr_value,
xattr_value_len, calc.digest); xattr_value_len, &digest);
if (rc) if (rc)
break; break;
rc = crypto_memneq(xattr_data->digest, calc.digest, rc = crypto_memneq(xattr_data->digest, digest.digest,
sizeof(calc.digest)); SHA1_DIGEST_SIZE);
if (rc) if (rc)
rc = -EINVAL; rc = -EINVAL;
break; break;
case EVM_IMA_XATTR_DIGSIG: case EVM_IMA_XATTR_DIGSIG:
case EVM_XATTR_PORTABLE_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, rc = evm_calc_hash(dentry, xattr_name, xattr_value,
xattr_value_len, xattr_data->type, xattr_value_len, xattr_data->type, &digest);
calc.digest);
if (rc) if (rc)
break; break;
rc = integrity_digsig_verify(INTEGRITY_KEYRING_EVM, rc = integrity_digsig_verify(INTEGRITY_KEYRING_EVM,
(const char *)xattr_data, xattr_len, (const char *)xattr_data, xattr_len,
calc.digest, sizeof(calc.digest)); digest.digest, digest.hdr.length);
if (!rc) { if (!rc) {
inode = d_backing_inode(dentry); inode = d_backing_inode(dentry);

View File

@ -193,8 +193,8 @@ static ssize_t evm_write_xattrs(struct file *file, const char __user *buf,
return -E2BIG; return -E2BIG;
ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_INTEGRITY_EVM_XATTR); ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_INTEGRITY_EVM_XATTR);
if (IS_ERR(ab)) if (!ab)
return PTR_ERR(ab); return -ENOMEM;
xattr = kmalloc(sizeof(struct xattr_list), GFP_KERNEL); xattr = kmalloc(sizeof(struct xattr_list), GFP_KERNEL);
if (!xattr) { if (!xattr) {

View File

@ -219,10 +219,13 @@ static int __init integrity_fs_init(void)
{ {
integrity_dir = securityfs_create_dir("integrity", NULL); integrity_dir = securityfs_create_dir("integrity", NULL);
if (IS_ERR(integrity_dir)) { if (IS_ERR(integrity_dir)) {
pr_err("Unable to create integrity sysfs dir: %ld\n", int ret = PTR_ERR(integrity_dir);
PTR_ERR(integrity_dir));
if (ret != -ENODEV)
pr_err("Unable to create integrity sysfs dir: %d\n",
ret);
integrity_dir = NULL; integrity_dir = NULL;
return PTR_ERR(integrity_dir); return ret;
} }
return 0; return 0;

View File

@ -12,6 +12,7 @@ config IMA
select TCG_TIS if TCG_TPM && X86 select TCG_TIS if TCG_TPM && X86
select TCG_CRB if TCG_TPM && ACPI select TCG_CRB if TCG_TPM && ACPI
select TCG_IBMVTPM if TCG_TPM && PPC_PSERIES select TCG_IBMVTPM if TCG_TPM && PPC_PSERIES
select INTEGRITY_AUDIT if AUDIT
help help
The Trusted Computing Group(TCG) runtime Integrity The Trusted Computing Group(TCG) runtime Integrity
Measurement Architecture(IMA) maintains a list of hash Measurement Architecture(IMA) maintains a list of hash

View File

@ -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, static void ima_log_string_op(struct audit_buffer *ab, char *key, char *value,
bool (*rule_operator)(kuid_t, kuid_t)) bool (*rule_operator)(kuid_t, kuid_t))
{ {
if (!ab)
return;
if (rule_operator == &uid_gt) if (rule_operator == &uid_gt)
audit_log_format(ab, "%s>", key); audit_log_format(ab, "%s>", key);
else if (rule_operator == &uid_lt) else if (rule_operator == &uid_lt)
audit_log_format(ab, "%s<", key); audit_log_format(ab, "%s<", key);
else else
audit_log_format(ab, "%s=", key); audit_log_format(ab, "%s=", key);
audit_log_untrustedstring(ab, value); audit_log_format(ab, "%s ", value);
audit_log_format(ab, " ");
} }
static void ima_log_string(struct audit_buffer *ab, char *key, char *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; bool uid_token;
int result = 0; 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->uid = INVALID_UID;
entry->fowner = INVALID_UID; entry->fowner = INVALID_UID;

View File

@ -15,6 +15,7 @@
#include <linux/integrity.h> #include <linux/integrity.h>
#include <crypto/sha.h> #include <crypto/sha.h>
#include <linux/key.h> #include <linux/key.h>
#include <linux/audit.h>
/* iint action cache flags */ /* iint action cache flags */
#define IMA_MEASURE 0x00000001 #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, void integrity_audit_msg(int audit_msgno, struct inode *inode,
const unsigned char *fname, const char *op, const unsigned char *fname, const char *op,
const char *cause, int result, int info); 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 #else
static inline void integrity_audit_msg(int audit_msgno, struct inode *inode, static inline void integrity_audit_msg(int audit_msgno, struct inode *inode,
const unsigned char *fname, const unsigned char *fname,
@ -206,4 +214,11 @@ static inline void integrity_audit_msg(int audit_msgno, struct inode *inode,
int result, int info) 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 #endif

View File

@ -45,11 +45,7 @@ void integrity_audit_msg(int audit_msgno, struct inode *inode,
from_kuid(&init_user_ns, audit_get_loginuid(current)), from_kuid(&init_user_ns, audit_get_loginuid(current)),
audit_get_sessionid(current)); audit_get_sessionid(current));
audit_log_task_context(ab); audit_log_task_context(ab);
audit_log_format(ab, " op="); audit_log_format(ab, " op=%s cause=%s comm=", op, cause);
audit_log_string(ab, op);
audit_log_format(ab, " cause=");
audit_log_string(ab, cause);
audit_log_format(ab, " comm=");
audit_log_untrustedstring(ab, get_task_comm(name, current)); audit_log_untrustedstring(ab, get_task_comm(name, current));
if (fname) { if (fname) {
audit_log_format(ab, " name="); audit_log_format(ab, " name=");

View File

@ -1032,7 +1032,12 @@ int security_kernel_create_files_as(struct cred *new, struct inode *inode)
int security_kernel_module_request(char *kmod_name) 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) int security_kernel_read_file(struct file *file, enum kernel_read_file_id id)