Merge branch 'next-evm-digsig' of git://git.kernel.org/pub/scm/linux/kernel/git/kasatkin/linux-digsig into next
This commit is contained in:
@@ -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
|
||||
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
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#
|
||||
|
||||
obj-$(CONFIG_INTEGRITY) += integrity.o
|
||||
obj-$(CONFIG_INTEGRITY_DIGSIG) += digsig.o
|
||||
|
||||
integrity-y := iint.o
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -26,34 +26,48 @@ 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 shash_desc *init_desc(void)
|
||||
static struct shash_desc *init_desc(const char type)
|
||||
{
|
||||
int rc;
|
||||
char *algo;
|
||||
struct crypto_shash **tfm;
|
||||
struct shash_desc *desc;
|
||||
|
||||
if (hmac_tfm == NULL) {
|
||||
hmac_tfm = crypto_alloc_shash(evm_hmac, 0, CRYPTO_ALG_ASYNC);
|
||||
if (IS_ERR(hmac_tfm)) {
|
||||
if (type == EVM_XATTR_HMAC) {
|
||||
tfm = &hmac_tfm;
|
||||
algo = evm_hmac;
|
||||
} else {
|
||||
tfm = &hash_tfm;
|
||||
algo = evm_hash;
|
||||
}
|
||||
|
||||
if (*tfm == NULL) {
|
||||
*tfm = crypto_alloc_shash(algo, 0, CRYPTO_ALG_ASYNC);
|
||||
if (IS_ERR(*tfm)) {
|
||||
pr_err("Can not allocate %s (reason: %ld)\n",
|
||||
evm_hmac, PTR_ERR(hmac_tfm));
|
||||
rc = PTR_ERR(hmac_tfm);
|
||||
hmac_tfm = NULL;
|
||||
algo, PTR_ERR(*tfm));
|
||||
rc = PTR_ERR(*tfm);
|
||||
*tfm = NULL;
|
||||
return ERR_PTR(rc);
|
||||
}
|
||||
}
|
||||
|
||||
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_setkey(hmac_tfm, evmkey, evmkey_len);
|
||||
if (rc)
|
||||
goto out;
|
||||
if (type == EVM_XATTR_HMAC) {
|
||||
rc = crypto_shash_setkey(*tfm, evmkey, evmkey_len);
|
||||
if (rc)
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = crypto_shash_init(desc);
|
||||
out:
|
||||
if (rc) {
|
||||
@@ -97,9 +111,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;
|
||||
@@ -111,7 +127,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);
|
||||
|
||||
@@ -145,6 +161,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
|
||||
*
|
||||
@@ -175,7 +207,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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user