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 <[email protected]>
---
 .../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

Reply via email to