diff options
author | James Morris <jmorris@namei.org> | 2012-01-09 12:16:48 +1100 |
---|---|---|
committer | James Morris <jmorris@namei.org> | 2012-01-09 12:16:48 +1100 |
commit | 8fcc99549522fc7a0bbaeb5755855ab0d9a59ce8 (patch) | |
tree | a118eaef15d4ba22247f45ee01537ecc906cd161 /security/integrity | |
parent | 805a6af8dba5dfdd35ec35dc52ec0122400b2610 (diff) | |
parent | 7b7e5916aa2f46e57f8bd8cb89c34620ebfda5da (diff) | |
download | linux-8fcc99549522fc7a0bbaeb5755855ab0d9a59ce8.tar.gz linux-8fcc99549522fc7a0bbaeb5755855ab0d9a59ce8.tar.bz2 linux-8fcc99549522fc7a0bbaeb5755855ab0d9a59ce8.zip |
Merge branch 'next' into for-linus
Conflicts:
security/integrity/evm/evm_crypto.c
Resolved upstream fix vs. next conflict manually.
Signed-off-by: James Morris <jmorris@namei.org>
Diffstat (limited to 'security/integrity')
-rw-r--r-- | security/integrity/Kconfig | 14 | ||||
-rw-r--r-- | security/integrity/Makefile | 1 | ||||
-rw-r--r-- | security/integrity/digsig.c | 48 | ||||
-rw-r--r-- | security/integrity/evm/evm.h | 12 | ||||
-rw-r--r-- | security/integrity/evm/evm_crypto.c | 76 | ||||
-rw-r--r-- | security/integrity/evm/evm_main.c | 94 | ||||
-rw-r--r-- | security/integrity/ima/ima_api.c | 4 | ||||
-rw-r--r-- | security/integrity/ima/ima_queue.c | 17 | ||||
-rw-r--r-- | security/integrity/integrity.h | 21 |
9 files changed, 244 insertions, 43 deletions
diff --git a/security/integrity/Kconfig b/security/integrity/Kconfig index 4bf00acf7937..d384ea921482 100644 --- a/security/integrity/Kconfig +++ b/security/integrity/Kconfig @@ -3,5 +3,19 @@ config INTEGRITY def_bool y depends on IMA || EVM +config INTEGRITY_DIGSIG + boolean "Digital signature verification using multiple keyrings" + depends on INTEGRITY && KEYS + default n + select DIGSIG + help + This option enables digital signature verification support + using multiple keyrings. It defines separate keyrings for each + of the different use cases - evm, ima, and modules. + Different keyrings improves search performance, but also allow + to "lock" certain keyring to prevent adding new keys. + This is useful for evm and module keyrings, when keys are + usually only added from initramfs. + source security/integrity/ima/Kconfig source security/integrity/evm/Kconfig diff --git a/security/integrity/Makefile b/security/integrity/Makefile index 0ae44aea6516..bece0563ee5e 100644 --- a/security/integrity/Makefile +++ b/security/integrity/Makefile @@ -3,6 +3,7 @@ # obj-$(CONFIG_INTEGRITY) += integrity.o +obj-$(CONFIG_INTEGRITY_DIGSIG) += digsig.o integrity-y := iint.o diff --git a/security/integrity/digsig.c b/security/integrity/digsig.c new file mode 100644 index 000000000000..2dc167d7cde9 --- /dev/null +++ b/security/integrity/digsig.c @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2011 Intel Corporation + * + * Author: + * Dmitry Kasatkin <dmitry.kasatkin@intel.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/err.h> +#include <linux/rbtree.h> +#include <linux/key-type.h> +#include <linux/digsig.h> + +#include "integrity.h" + +static struct key *keyring[INTEGRITY_KEYRING_MAX]; + +static const char *keyring_name[INTEGRITY_KEYRING_MAX] = { + "_evm", + "_module", + "_ima", +}; + +int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen, + const char *digest, int digestlen) +{ + if (id >= INTEGRITY_KEYRING_MAX) + return -EINVAL; + + if (!keyring[id]) { + keyring[id] = + request_key(&key_type_keyring, keyring_name[id], NULL); + if (IS_ERR(keyring[id])) { + int err = PTR_ERR(keyring[id]); + pr_err("no %s keyring: %d\n", keyring_name[id], err); + keyring[id] = NULL; + return err; + } + } + + return digsig_verify(keyring[id], sig, siglen, digest, digestlen); +} diff --git a/security/integrity/evm/evm.h b/security/integrity/evm/evm.h index d320f5197437..c885247ebcf7 100644 --- a/security/integrity/evm/evm.h +++ b/security/integrity/evm/evm.h @@ -12,14 +12,21 @@ * File: evm.h * */ + +#ifndef __INTEGRITY_EVM_H +#define __INTEGRITY_EVM_H + #include <linux/xattr.h> #include <linux/security.h> + #include "../integrity.h" extern int evm_initialized; extern char *evm_hmac; +extern char *evm_hash; extern struct crypto_shash *hmac_tfm; +extern struct crypto_shash *hash_tfm; /* List of EVM protected security xattrs */ extern char *evm_config_xattrnames[]; @@ -32,7 +39,12 @@ extern int evm_update_evmxattr(struct dentry *dentry, extern 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); +extern int evm_calc_hash(struct dentry *dentry, const char *req_xattr_name, + const char *req_xattr_value, + size_t req_xattr_value_len, char *digest); extern int evm_init_hmac(struct inode *inode, const struct xattr *xattr, char *hmac_val); extern int evm_init_secfs(void); extern void evm_cleanup_secfs(void); + +#endif diff --git a/security/integrity/evm/evm_crypto.c b/security/integrity/evm/evm_crypto.c index 8738deff26fa..49a464f5595b 100644 --- a/security/integrity/evm/evm_crypto.c +++ b/security/integrity/evm/evm_crypto.c @@ -26,44 +26,56 @@ 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 DEFINE_MUTEX(mutex); -static struct shash_desc *init_desc(void) +static struct shash_desc *init_desc(char type) { - int rc; + long rc; + char *algo; + struct crypto_shash **tfm; struct shash_desc *desc; - if (hmac_tfm == NULL) { + if (type == EVM_XATTR_HMAC) { + tfm = &hmac_tfm; + algo = evm_hmac; + } else { + tfm = &hash_tfm; + algo = evm_hash; + } + + if (*tfm == NULL) { mutex_lock(&mutex); - if (hmac_tfm) + if (*tfm) goto out; - hmac_tfm = crypto_alloc_shash(evm_hmac, 0, CRYPTO_ALG_ASYNC); - if (IS_ERR(hmac_tfm)) { - pr_err("Can not allocate %s (reason: %ld)\n", - evm_hmac, PTR_ERR(hmac_tfm)); - rc = PTR_ERR(hmac_tfm); - hmac_tfm = NULL; + *tfm = crypto_alloc_shash(algo, 0, CRYPTO_ALG_ASYNC); + if (IS_ERR(*tfm)) { + rc = PTR_ERR(*tfm); + pr_err("Can not allocate %s (reason: %ld)\n", algo, rc); + *tfm = NULL; mutex_unlock(&mutex); return ERR_PTR(rc); } - rc = crypto_shash_setkey(hmac_tfm, evmkey, evmkey_len); - if (rc) { - crypto_free_shash(hmac_tfm); - hmac_tfm = NULL; - mutex_unlock(&mutex); - return ERR_PTR(rc); + if (type == EVM_XATTR_HMAC) { + rc = crypto_shash_setkey(*tfm, evmkey, evmkey_len); + if (rc) { + crypto_free_shash(*tfm); + *tfm = NULL; + mutex_unlock(&mutex); + return ERR_PTR(rc); + } } out: mutex_unlock(&mutex); } - desc = kmalloc(sizeof(*desc) + crypto_shash_descsize(hmac_tfm), + desc = kmalloc(sizeof(*desc) + crypto_shash_descsize(*tfm), GFP_KERNEL); if (!desc) return ERR_PTR(-ENOMEM); - desc->tfm = hmac_tfm; + desc->tfm = *tfm; desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP; rc = crypto_shash_init(desc); @@ -108,9 +120,11 @@ static void hmac_add_misc(struct shash_desc *desc, struct inode *inode, * the hmac using the requested xattr value. Don't alloc/free memory for * each xattr, but attempt to re-use the previously allocated memory. */ -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) +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) { struct inode *inode = dentry->d_inode; struct shash_desc *desc; @@ -122,7 +136,7 @@ int evm_calc_hmac(struct dentry *dentry, const char *req_xattr_name, if (!inode->i_op || !inode->i_op->getxattr) return -EOPNOTSUPP; - desc = init_desc(); + desc = init_desc(type); if (IS_ERR(desc)) return PTR_ERR(desc); @@ -156,6 +170,22 @@ out: return error; } +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) +{ + return evm_calc_hmac_or_hash(dentry, req_xattr_name, req_xattr_value, + req_xattr_value_len, EVM_XATTR_HMAC, digest); +} + +int evm_calc_hash(struct dentry *dentry, const char *req_xattr_name, + const char *req_xattr_value, size_t req_xattr_value_len, + char *digest) +{ + return evm_calc_hmac_or_hash(dentry, req_xattr_name, req_xattr_value, + req_xattr_value_len, IMA_XATTR_DIGEST, digest); +} + /* * Calculate the hmac and update security.evm xattr * @@ -186,7 +216,7 @@ int evm_init_hmac(struct inode *inode, const struct xattr *lsm_xattr, { struct shash_desc *desc; - desc = init_desc(); + desc = init_desc(EVM_XATTR_HMAC); if (IS_ERR(desc)) { printk(KERN_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 92d3d99a9f7b..8901501425f4 100644 --- a/security/integrity/evm/evm_main.c +++ b/security/integrity/evm/evm_main.c @@ -25,6 +25,7 @@ int evm_initialized; char *evm_hmac = "hmac(sha1)"; +char *evm_hash = "sha1"; char *evm_config_xattrnames[] = { #ifdef CONFIG_SECURITY_SELINUX @@ -46,6 +47,29 @@ static int __init evm_set_fixmode(char *str) } __setup("evm=", evm_set_fixmode); +static int evm_find_protected_xattrs(struct dentry *dentry) +{ + struct inode *inode = dentry->d_inode; + char **xattr; + int error; + int count = 0; + + if (!inode->i_op || !inode->i_op->getxattr) + return -EOPNOTSUPP; + + for (xattr = evm_config_xattrnames; *xattr != NULL; xattr++) { + error = inode->i_op->getxattr(dentry, *xattr, NULL, 0); + if (error < 0) { + if (error == -ENODATA) + continue; + return error; + } + count++; + } + + return count; +} + /* * evm_verify_hmac - calculate and compare the HMAC with the EVM xattr * @@ -65,32 +89,72 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry, size_t xattr_value_len, struct integrity_iint_cache *iint) { - struct evm_ima_xattr_data xattr_data; + struct evm_ima_xattr_data *xattr_data = NULL; + struct evm_ima_xattr_data calc; enum integrity_status evm_status = INTEGRITY_PASS; - int rc; + int rc, xattr_len; if (iint && iint->evm_status == INTEGRITY_PASS) return iint->evm_status; /* if status is not PASS, try to check again - against -ENOMEM */ - rc = evm_calc_hmac(dentry, xattr_name, xattr_value, - xattr_value_len, xattr_data.digest); - if (rc < 0) { - evm_status = (rc == -ENODATA) - ? INTEGRITY_NOXATTRS : INTEGRITY_FAIL; + /* first need to know the sig type */ + rc = vfs_getxattr_alloc(dentry, XATTR_NAME_EVM, (char **)&xattr_data, 0, + GFP_NOFS); + if (rc <= 0) { + if (rc == 0) + evm_status = INTEGRITY_FAIL; /* empty */ + else if (rc == -ENODATA) { + rc = evm_find_protected_xattrs(dentry); + if (rc > 0) + evm_status = INTEGRITY_NOLABEL; + else if (rc == 0) + evm_status = INTEGRITY_NOXATTRS; /* new file */ + } goto out; } - xattr_data.type = EVM_XATTR_HMAC; - rc = vfs_xattr_cmp(dentry, XATTR_NAME_EVM, (u8 *)&xattr_data, - sizeof xattr_data, GFP_NOFS); - if (rc < 0) - evm_status = (rc == -ENODATA) - ? INTEGRITY_NOLABEL : INTEGRITY_FAIL; + xattr_len = rc - 1; + + /* check value type */ + switch (xattr_data->type) { + case EVM_XATTR_HMAC: + rc = evm_calc_hmac(dentry, xattr_name, xattr_value, + xattr_value_len, calc.digest); + if (rc) + break; + rc = memcmp(xattr_data->digest, calc.digest, + sizeof(calc.digest)); + if (rc) + rc = -EINVAL; + break; + case EVM_IMA_XATTR_DIGSIG: + rc = evm_calc_hash(dentry, xattr_name, xattr_value, + xattr_value_len, calc.digest); + if (rc) + break; + rc = integrity_digsig_verify(INTEGRITY_KEYRING_EVM, + xattr_data->digest, xattr_len, + calc.digest, sizeof(calc.digest)); + if (!rc) { + /* we probably want to replace rsa with hmac here */ + evm_update_evmxattr(dentry, xattr_name, xattr_value, + xattr_value_len); + } + break; + default: + rc = -EINVAL; + break; + } + + if (rc) + evm_status = (rc == -ENODATA) ? + INTEGRITY_NOXATTRS : INTEGRITY_FAIL; out: if (iint) iint->evm_status = evm_status; + kfree(xattr_data); return evm_status; } @@ -354,6 +418,8 @@ static int __init init_evm(void) printk(KERN_INFO "EVM: Error registering secfs\n"); goto err; } + + return 0; err: return error; } @@ -363,6 +429,8 @@ static void __exit cleanup_evm(void) evm_cleanup_secfs(); if (hmac_tfm) crypto_free_shash(hmac_tfm); + if (hash_tfm) + crypto_free_shash(hash_tfm); } /* diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c index 0d50df04ccc4..88a2788b981d 100644 --- a/security/integrity/ima/ima_api.c +++ b/security/integrity/ima/ima_api.c @@ -178,8 +178,8 @@ void ima_store_measurement(struct integrity_iint_cache *iint, strncpy(entry->template.file_name, filename, IMA_EVENT_NAME_LEN_MAX); result = ima_store_template(entry, violation, inode); - if (!result) + if (!result || result == -EEXIST) iint->flags |= IMA_MEASURED; - else + if (result < 0) kfree(entry); } diff --git a/security/integrity/ima/ima_queue.c b/security/integrity/ima/ima_queue.c index 8e28f04a5e2e..55a6271bce7a 100644 --- a/security/integrity/ima/ima_queue.c +++ b/security/integrity/ima/ima_queue.c @@ -23,6 +23,8 @@ #include <linux/slab.h> #include "ima.h" +#define AUDIT_CAUSE_LEN_MAX 32 + LIST_HEAD(ima_measurements); /* list of all measurements */ /* key: inode (before secure-hashing a file) */ @@ -94,7 +96,8 @@ static int ima_pcr_extend(const u8 *hash) result = tpm_pcr_extend(TPM_ANY_NUM, CONFIG_IMA_MEASURE_PCR_IDX, hash); if (result != 0) - pr_err("IMA: Error Communicating to TPM chip\n"); + pr_err("IMA: Error Communicating to TPM chip, result: %d\n", + result); return result; } @@ -106,14 +109,16 @@ int ima_add_template_entry(struct ima_template_entry *entry, int violation, { u8 digest[IMA_DIGEST_SIZE]; const char *audit_cause = "hash_added"; + char tpm_audit_cause[AUDIT_CAUSE_LEN_MAX]; int audit_info = 1; - int result = 0; + int result = 0, tpmresult = 0; mutex_lock(&ima_extend_list_mutex); if (!violation) { memcpy(digest, entry->digest, sizeof digest); if (ima_lookup_digest_entry(digest)) { audit_cause = "hash_exists"; + result = -EEXIST; goto out; } } @@ -128,9 +133,11 @@ int ima_add_template_entry(struct ima_template_entry *entry, int violation, if (violation) /* invalidate pcr */ memset(digest, 0xff, sizeof digest); - result = ima_pcr_extend(digest); - if (result != 0) { - audit_cause = "TPM error"; + tpmresult = ima_pcr_extend(digest); + if (tpmresult != 0) { + snprintf(tpm_audit_cause, AUDIT_CAUSE_LEN_MAX, "TPM_error(%d)", + tpmresult); + audit_cause = tpm_audit_cause; audit_info = 0; } out: diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h index 3143a3c39868..4da6ba81d153 100644 --- a/security/integrity/integrity.h +++ b/security/integrity/integrity.h @@ -46,5 +46,26 @@ struct integrity_iint_cache { struct integrity_iint_cache *integrity_iint_insert(struct inode *inode); struct integrity_iint_cache *integrity_iint_find(struct inode *inode); +#define INTEGRITY_KEYRING_EVM 0 +#define INTEGRITY_KEYRING_MODULE 1 +#define INTEGRITY_KEYRING_IMA 2 +#define INTEGRITY_KEYRING_MAX 3 + +#ifdef CONFIG_INTEGRITY_DIGSIG + +int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen, + const char *digest, int digestlen); + +#else + +static inline int integrity_digsig_verify(const unsigned int id, + const char *sig, int siglen, + const char *digest, int digestlen) +{ + return -EOPNOTSUPP; +} + +#endif /* CONFIG_INTEGRITY_DIGSIG */ + /* set during initialization */ extern int iint_initialized; |