forked from Minki/linux
evm: Allow non-SHA1 digital signatures
SHA1 is reasonable in HMAC constructs, but it's desirable to be able to use stronger hashes in digital signatures. Modify the EVM crypto code so the hash type is imported from the digital signature and passed down to the hash calculation code, and return the digest size to higher layers for validation. Signed-off-by: Matthew Garrett <mjg59@google.com> Signed-off-by: Mimi Zohar <zohar@linux.vnet.ibm.com>
This commit is contained in:
parent
e2861fa716
commit
5feeb61183
@ -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
|
||||||
|
@ -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);
|
||||||
|
@ -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,8 +89,8 @@ 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) {
|
||||||
@ -187,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.
|
* each xattr, but attempt to re-use the previously allocated memory.
|
||||||
*/
|
*/
|
||||||
static int evm_calc_hmac_or_hash(struct dentry *dentry,
|
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;
|
||||||
@ -205,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;
|
||||||
@ -240,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)
|
||||||
@ -253,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)
|
||||||
@ -304,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;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -317,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);
|
||||||
}
|
}
|
||||||
@ -335,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);
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user