[PATCH 6/7] evm: Allow choice of hash algorithm for HMAC

From: Roberto Sassu
Date: Fri Apr 09 2021 - 07:45:12 EST


Commit 5feeb61183dd ("evm: Allow non-SHA1 digital signatures") introduced
the possibility to use a different hash algorithm for signatures, but kept
the algorithm for the HMAC hard-coded (SHA1). Switching to a different
algorithm for HMAC would require to change the code in different places.

This patch introduces a new global variable called evm_hash_algo, and
consistently uses it whenever EVM performs HMAC-related operations. This
variable can be modified at kernel build time with the new configuration
option called CONFIG_EVM_DEFAULT_HASH, or at run-time with the new option
evm_hash=.

Signed-off-by: Roberto Sassu <roberto.sassu@xxxxxxxxxx>
---
.../admin-guide/kernel-parameters.txt | 8 +++
security/integrity/evm/Kconfig | 34 ++++++++++++
security/integrity/evm/evm.h | 2 +
security/integrity/evm/evm_crypto.c | 55 +++++++++++++++++--
security/integrity/evm/evm_main.c | 13 +++--
security/integrity/integrity.h | 3 +-
6 files changed, 105 insertions(+), 10 deletions(-)

diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
index 265f7657f59d..f61ce44c5d8e 100644
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -1343,6 +1343,14 @@
Permit 'security.evm' to be updated regardless of
current integrity status.

+ evm_hash= [EVM] Hash algorithm used to calculate the HMAC.
+ Format: { md5 | sha1 | rmd160 | sha256 | sha384
+ | sha512 | ... }
+ default: "sha256"
+
+ The list of supported hash algorithms is defined
+ in crypto/hash_info.h.
+
failslab=
fail_usercopy=
fail_page_alloc=
diff --git a/security/integrity/evm/Kconfig b/security/integrity/evm/Kconfig
index a6e19d23e700..234077b24283 100644
--- a/security/integrity/evm/Kconfig
+++ b/security/integrity/evm/Kconfig
@@ -13,6 +13,40 @@ config EVM

If you are unsure how to answer this question, answer N.

+choice
+ prompt "Default EVM hash algorithm"
+ default EVM_DEFAULT_HASH_SHA256
+ depends on EVM
+ help
+ Select the default hash algorithm used for the HMAC. The compiled
+ default hash algorithm can be overwritten using the kernel command
+ line 'evm_hash=' option.
+
+ config EVM_DEFAULT_HASH_SHA1
+ bool "SHA1"
+ depends on CRYPTO_SHA1=y
+
+ config EVM_DEFAULT_HASH_SHA256
+ bool "SHA256 (default)"
+ depends on CRYPTO_SHA256=y
+
+ config EVM_DEFAULT_HASH_SHA512
+ bool "SHA512"
+ depends on CRYPTO_SHA512=y
+
+ config EVM_DEFAULT_HASH_WP512
+ bool "WP512"
+ depends on CRYPTO_WP512=y
+endchoice
+
+config EVM_DEFAULT_HASH
+ string
+ depends on EVM
+ default "sha1" if EVM_DEFAULT_HASH_SHA1
+ default "sha256" if EVM_DEFAULT_HASH_SHA256
+ default "sha512" if EVM_DEFAULT_HASH_SHA512
+ default "wp512" if EVM_DEFAULT_HASH_WP512
+
config EVM_ATTR_FSUUID
bool "FSUUID (version 2)"
default y
diff --git a/security/integrity/evm/evm.h b/security/integrity/evm/evm.h
index f2fef2b5ed51..ae590f71ce7d 100644
--- a/security/integrity/evm/evm.h
+++ b/security/integrity/evm/evm.h
@@ -32,6 +32,7 @@ struct xattr_list {
};

extern int evm_initialized;
+extern enum hash_algo evm_hash_algo;

#define EVM_ATTR_FSUUID 0x0001

@@ -49,6 +50,7 @@ struct evm_digest {
} __packed;

int evm_init_key(void);
+int __init evm_init_crypto(void);
int evm_update_evmxattr(struct dentry *dentry,
const char *req_xattr_name,
const char *req_xattr_value,
diff --git a/security/integrity/evm/evm_crypto.c b/security/integrity/evm/evm_crypto.c
index d76b006cbcc4..b66264b53d5d 100644
--- a/security/integrity/evm/evm_crypto.c
+++ b/security/integrity/evm/evm_crypto.c
@@ -33,7 +33,24 @@ static DEFINE_MUTEX(mutex);

static unsigned long evm_set_key_flags;

-static const char evm_hmac[] = "hmac(sha1)";
+enum hash_algo evm_hash_algo __ro_after_init = HASH_ALGO_SHA1;
+
+static int hash_setup_done;
+static int __init hash_setup(char *str)
+{
+ int i;
+
+ i = match_string(hash_algo_name, HASH_ALGO__LAST, str);
+ if (i < 0) {
+ pr_err("invalid hash algorithm \"%s\"", str);
+ return 1;
+ }
+
+ evm_hash_algo = i;
+ hash_setup_done = 1;
+ return 1;
+}
+__setup("evm_hash=", hash_setup);

/**
* evm_set_key() - set EVM HMAC key from the kernel
@@ -74,8 +91,12 @@ static struct shash_desc *init_desc(char type, uint8_t hash_algo)
long rc;
const char *algo;
struct crypto_shash **tfm, *tmp_tfm = NULL;
+ char evm_hmac[CRYPTO_MAX_ALG_NAME];
struct shash_desc *desc;

+ snprintf(evm_hmac, sizeof(evm_hmac), "hmac(%s)",
+ hash_algo_name[evm_hash_algo]);
+
if (type == EVM_XATTR_HMAC) {
if (!(evm_initialized & EVM_INIT_HMAC)) {
pr_err_once("HMAC key is not set\n");
@@ -317,7 +338,7 @@ int evm_update_evmxattr(struct dentry *dentry, const char *xattr_name,
if (rc)
return -EPERM;

- data.hdr.algo = HASH_ALGO_SHA1;
+ data.hdr.algo = evm_hash_algo;
rc = evm_calc_hmac(dentry, xattr_name, xattr_value,
xattr_value_len, &data);
if (rc == 0) {
@@ -325,7 +346,8 @@ int evm_update_evmxattr(struct dentry *dentry, const char *xattr_name,
rc = __vfs_setxattr_noperm(&init_user_ns, dentry,
XATTR_NAME_EVM,
&data.hdr.xattr.data[1],
- SHA1_DIGEST_SIZE + 1, 0);
+ hash_digest_size[evm_hash_algo] + 1,
+ 0);
} else if (rc == -ENODATA && (inode->i_opflags & IOP_XATTR)) {
rc = __vfs_removexattr(&init_user_ns, dentry, XATTR_NAME_EVM);
}
@@ -337,7 +359,7 @@ int evm_init_hmac(struct inode *inode, const struct xattr *lsm_xattr,
{
struct shash_desc *desc;

- desc = init_desc(EVM_XATTR_HMAC, HASH_ALGO_SHA1);
+ desc = init_desc(EVM_XATTR_HMAC, evm_hash_algo);
if (IS_ERR(desc)) {
pr_info("init_desc failed\n");
return PTR_ERR(desc);
@@ -350,7 +372,7 @@ int evm_init_hmac(struct inode *inode, const struct xattr *lsm_xattr,
}

/*
- * Get the key from the TPM for the SHA1-HMAC
+ * Get the key from the TPM for the HMAC
*/
int evm_init_key(void)
{
@@ -373,3 +395,26 @@ int evm_init_key(void)
key_put(evm_key);
return rc;
}
+
+/*
+ * Configure the hash algorithm for the HMAC
+ */
+int __init evm_init_crypto(void)
+{
+ int i;
+
+ if (hash_setup_done)
+ return 0;
+
+ i = match_string(hash_algo_name, HASH_ALGO__LAST,
+ CONFIG_EVM_DEFAULT_HASH);
+ if (i < 0) {
+ pr_err("invalid hash algorithm \"%s\"",
+ CONFIG_EVM_DEFAULT_HASH);
+ return -EINVAL;
+ }
+
+ evm_hash_algo = i;
+
+ return 0;
+}
diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c
index 8e80af97021e..cb3754e0cc60 100644
--- a/security/integrity/evm/evm_main.c
+++ b/security/integrity/evm/evm_main.c
@@ -187,18 +187,18 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry,
/* check value type */
switch (xattr_data->type) {
case EVM_XATTR_HMAC:
- if (xattr_len != sizeof(struct evm_xattr)) {
+ if (xattr_len != hash_digest_size[evm_hash_algo] + 1) {
evm_status = INTEGRITY_FAIL;
goto out;
}

- digest.hdr.algo = HASH_ALGO_SHA1;
+ digest.hdr.algo = evm_hash_algo;
rc = evm_calc_hmac(dentry, xattr_name, xattr_value,
xattr_value_len, &digest);
if (rc)
break;
rc = crypto_memneq(xattr_data->data, digest.digest,
- SHA1_DIGEST_SIZE);
+ hash_digest_size[evm_hash_algo]);
if (rc)
rc = -EINVAL;
break;
@@ -722,7 +722,7 @@ int evm_inode_init_security(struct inode *inode,
goto out;

evm_xattr->value = xattr_data;
- evm_xattr->value_len = sizeof(*xattr_data);
+ evm_xattr->value_len = hash_digest_size[evm_hash_algo] + 1;
evm_xattr->name = XATTR_EVM_SUFFIX;
return 0;
out:
@@ -759,6 +759,11 @@ static int __init init_evm(void)
goto error;
}

+ error = evm_init_crypto();
+ if (error < 0) {
+ pr_info("Error initializing crypto\n");
+ goto error;
+ }
error:
if (error != 0) {
if (!list_empty(&evm_config_xattrnames)) {
diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h
index be501a63ae30..74919b638f52 100644
--- a/security/integrity/integrity.h
+++ b/security/integrity/integrity.h
@@ -17,6 +17,7 @@
#include <crypto/sha1.h>
#include <linux/key.h>
#include <linux/audit.h>
+#include <crypto/hash_info.h>

/* iint action cache flags */
#define IMA_MEASURE 0x00000001
@@ -89,7 +90,7 @@ struct evm_ima_xattr_data {
/* Only used in the EVM HMAC code. */
struct evm_xattr {
struct evm_ima_xattr_data data;
- u8 digest[SHA1_DIGEST_SIZE];
+ u8 digest[SHA512_DIGEST_SIZE];
} __packed;

#define IMA_MAX_DIGEST_SIZE 64
--
2.26.2