Commit 15647eb3 authored by Dmitry Kasatkin's avatar Dmitry Kasatkin

evm: digital signature verification support

This patch adds support for digital signature verification to EVM.
With this feature file metadata can be protected using digital
signature instead of an HMAC. When building an image,
which has to be flashed to different devices, an HMAC cannot
be used to sign file metadata, because the HMAC key should be
different on every device.
Signed-off-by: default avatarDmitry Kasatkin <dmitry.kasatkin@intel.com>
Acked-by: default avatarMimi Zohar <zohar@us.ibm.com>
parent 8607c501
...@@ -12,14 +12,21 @@ ...@@ -12,14 +12,21 @@
* File: evm.h * File: evm.h
* *
*/ */
#ifndef __INTEGRITY_EVM_H
#define __INTEGRITY_EVM_H
#include <linux/xattr.h> #include <linux/xattr.h>
#include <linux/security.h> #include <linux/security.h>
#include "../integrity.h" #include "../integrity.h"
extern int evm_initialized; extern int evm_initialized;
extern char *evm_hmac; extern char *evm_hmac;
extern char *evm_hash;
extern struct crypto_shash *hmac_tfm; extern struct crypto_shash *hmac_tfm;
extern struct crypto_shash *hash_tfm;
/* List of EVM protected security xattrs */ /* List of EVM protected security xattrs */
extern char *evm_config_xattrnames[]; extern char *evm_config_xattrnames[];
...@@ -32,7 +39,12 @@ extern int evm_update_evmxattr(struct dentry *dentry, ...@@ -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, extern 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, 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, extern int evm_init_hmac(struct inode *inode, const struct xattr *xattr,
char *hmac_val); char *hmac_val);
extern int evm_init_secfs(void); extern int evm_init_secfs(void);
extern void evm_cleanup_secfs(void); extern void evm_cleanup_secfs(void);
#endif
...@@ -26,34 +26,48 @@ static unsigned char evmkey[MAX_KEY_SIZE]; ...@@ -26,34 +26,48 @@ 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 shash_desc *init_desc(void) static struct shash_desc *init_desc(const char type)
{ {
int rc; int rc;
char *algo;
struct crypto_shash **tfm;
struct shash_desc *desc; struct shash_desc *desc;
if (hmac_tfm == NULL) { if (type == EVM_XATTR_HMAC) {
hmac_tfm = crypto_alloc_shash(evm_hmac, 0, CRYPTO_ALG_ASYNC); tfm = &hmac_tfm;
if (IS_ERR(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", pr_err("Can not allocate %s (reason: %ld)\n",
evm_hmac, PTR_ERR(hmac_tfm)); algo, PTR_ERR(*tfm));
rc = PTR_ERR(hmac_tfm); rc = PTR_ERR(*tfm);
hmac_tfm = NULL; *tfm = NULL;
return ERR_PTR(rc); return ERR_PTR(rc);
} }
} }
desc = kmalloc(sizeof(*desc) + crypto_shash_descsize(hmac_tfm), desc = kmalloc(sizeof(*desc) + crypto_shash_descsize(*tfm),
GFP_KERNEL); GFP_KERNEL);
if (!desc) if (!desc)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
desc->tfm = hmac_tfm; desc->tfm = *tfm;
desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP; desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
rc = crypto_shash_setkey(hmac_tfm, evmkey, evmkey_len); if (type == EVM_XATTR_HMAC) {
rc = crypto_shash_setkey(*tfm, evmkey, evmkey_len);
if (rc) if (rc)
goto out; goto out;
}
rc = crypto_shash_init(desc); rc = crypto_shash_init(desc);
out: out:
if (rc) { if (rc) {
...@@ -97,9 +111,11 @@ static void hmac_add_misc(struct shash_desc *desc, struct inode *inode, ...@@ -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 * the hmac using the requested xattr value. Don't alloc/free memory for
* each xattr, but attempt to re-use the previously allocated memory. * each xattr, but attempt to re-use the previously allocated memory.
*/ */
int evm_calc_hmac(struct dentry *dentry, const char *req_xattr_name, static int evm_calc_hmac_or_hash(struct dentry *dentry,
const char *req_xattr_value, size_t req_xattr_value_len, const char *req_xattr_name,
char *digest) const char *req_xattr_value,
size_t req_xattr_value_len,
char type, char *digest)
{ {
struct inode *inode = dentry->d_inode; struct inode *inode = dentry->d_inode;
struct shash_desc *desc; struct shash_desc *desc;
...@@ -111,7 +127,7 @@ int evm_calc_hmac(struct dentry *dentry, const char *req_xattr_name, ...@@ -111,7 +127,7 @@ int evm_calc_hmac(struct dentry *dentry, const char *req_xattr_name,
if (!inode->i_op || !inode->i_op->getxattr) if (!inode->i_op || !inode->i_op->getxattr)
return -EOPNOTSUPP; return -EOPNOTSUPP;
desc = init_desc(); desc = init_desc(type);
if (IS_ERR(desc)) if (IS_ERR(desc))
return PTR_ERR(desc); return PTR_ERR(desc);
...@@ -145,6 +161,22 @@ int evm_calc_hmac(struct dentry *dentry, const char *req_xattr_name, ...@@ -145,6 +161,22 @@ int evm_calc_hmac(struct dentry *dentry, const char *req_xattr_name,
return error; 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 * Calculate the hmac and update security.evm xattr
* *
...@@ -175,7 +207,7 @@ int evm_init_hmac(struct inode *inode, const struct xattr *lsm_xattr, ...@@ -175,7 +207,7 @@ int evm_init_hmac(struct inode *inode, const struct xattr *lsm_xattr,
{ {
struct shash_desc *desc; struct shash_desc *desc;
desc = init_desc(); desc = init_desc(EVM_XATTR_HMAC);
if (IS_ERR(desc)) { if (IS_ERR(desc)) {
printk(KERN_INFO "init_desc failed\n"); printk(KERN_INFO "init_desc failed\n");
return PTR_ERR(desc); return PTR_ERR(desc);
......
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
int evm_initialized; int evm_initialized;
char *evm_hmac = "hmac(sha1)"; char *evm_hmac = "hmac(sha1)";
char *evm_hash = "sha1";
char *evm_config_xattrnames[] = { char *evm_config_xattrnames[] = {
#ifdef CONFIG_SECURITY_SELINUX #ifdef CONFIG_SECURITY_SELINUX
...@@ -46,6 +47,29 @@ static int __init evm_set_fixmode(char *str) ...@@ -46,6 +47,29 @@ static int __init evm_set_fixmode(char *str)
} }
__setup("evm=", evm_set_fixmode); __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 * 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, ...@@ -65,32 +89,72 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry,
size_t xattr_value_len, size_t xattr_value_len,
struct integrity_iint_cache *iint) 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; enum integrity_status evm_status = INTEGRITY_PASS;
int rc; int rc, xattr_len;
if (iint && iint->evm_status == INTEGRITY_PASS) if (iint && iint->evm_status == INTEGRITY_PASS)
return iint->evm_status; return iint->evm_status;
/* if status is not PASS, try to check again - against -ENOMEM */ /* if status is not PASS, try to check again - against -ENOMEM */
rc = evm_calc_hmac(dentry, xattr_name, xattr_value, /* first need to know the sig type */
xattr_value_len, xattr_data.digest); rc = vfs_getxattr_alloc(dentry, XATTR_NAME_EVM, (char **)&xattr_data, 0,
if (rc < 0) { GFP_NOFS);
evm_status = (rc == -ENODATA) if (rc <= 0) {
? INTEGRITY_NOXATTRS : INTEGRITY_FAIL; 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; goto out;
} }
xattr_data.type = EVM_XATTR_HMAC; xattr_len = rc - 1;
rc = vfs_xattr_cmp(dentry, XATTR_NAME_EVM, (u8 *)&xattr_data,
sizeof xattr_data, GFP_NOFS); /* check value type */
if (rc < 0) switch (xattr_data->type) {
evm_status = (rc == -ENODATA) case EVM_XATTR_HMAC:
? INTEGRITY_NOLABEL : INTEGRITY_FAIL; 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: out:
if (iint) if (iint)
iint->evm_status = evm_status; iint->evm_status = evm_status;
kfree(xattr_data);
return evm_status; return evm_status;
} }
...@@ -354,6 +418,8 @@ static int __init init_evm(void) ...@@ -354,6 +418,8 @@ static int __init init_evm(void)
printk(KERN_INFO "EVM: Error registering secfs\n"); printk(KERN_INFO "EVM: Error registering secfs\n");
goto err; goto err;
} }
return 0;
err: err:
return error; return error;
} }
...@@ -363,6 +429,8 @@ static void __exit cleanup_evm(void) ...@@ -363,6 +429,8 @@ static void __exit cleanup_evm(void)
evm_cleanup_secfs(); evm_cleanup_secfs();
if (hmac_tfm) if (hmac_tfm)
crypto_free_shash(hmac_tfm); crypto_free_shash(hmac_tfm);
if (hash_tfm)
crypto_free_shash(hash_tfm);
} }
/* /*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment