aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--security/integrity/evm/evm.h12
-rw-r--r--security/integrity/evm/evm_crypto.c66
-rw-r--r--security/integrity/evm/evm_main.c94
3 files changed, 142 insertions, 30 deletions
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 5dd5b140242c..847a2d7dff17 100644
--- a/security/integrity/evm/evm_crypto.c
+++ b/security/integrity/evm/evm_crypto.c
@@ -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);
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);
}
/*