Skip to contentSkip to conversation

evm: Allow non-SHA1 digital signatures

PATCH Superseded Matthew Garrett 2 participants 2 messages May 11, 2018 – May 14, 2018

Subsystems (4)

v2 of 4 Superseded Latest is v4 →

lore
Share & tools
lore
#0 lore raw

PATCH v2 1/1 evm: Allow non-SHA1 digital signatures

SHA1 is reasonable in HMAC constructs, but it's desirable to be able to use stronger hashes in digital signatures. Modify the EVM crypto code so the hash type is imported from the digital signature and passed down to the hash calculation code, and return the digest size to higher layers for validation.

---

Reworked to take Mimi and Thiago's comments into account - this simplifies evm_calc_hmac_or_hash, but makes evm_update_evmxattr a little more complicated as a result.

 security/integrity/evm/evm.h        |  5 ++-
 security/integrity/evm/evm_crypto.c | 57 +++++++++++++++++------------
 security/integrity/evm/evm_main.c   | 29 +++++++++++----
 3 files changed, 58 insertions(+), 33 deletions(-)
security/integrity/evm/evm.h +3 -2
index 45c4a89c02ff..84ffae97e8a9 100644
@@ -49,10 +49,11 @@ int evm_update_evmxattr(struct dentry *dentry,
 			size_t req_xattr_value_len);
 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);
+		  size_t req_xattr_value_len, struct ima_digest_data *data);
 int evm_calc_hash(struct dentry *dentry, const char *req_xattr_name,
 		  const char *req_xattr_value,
-		  size_t req_xattr_value_len, char type, char *digest);
+		  size_t req_xattr_value_len, char type,
+		  struct ima_digest_data *data);
 int evm_init_hmac(struct inode *inode, const struct xattr *xattr,
 		  char *hmac_val);
 int evm_init_secfs(void);
security/integrity/evm/evm_crypto.c +33 -24

147 lines not on this page · raw message

security/integrity/evm/evm_main.c +22 -7
index 9ea9c19a545c..20aaa54c473a 100644
@@ -25,6 +25,7 @@
 #include <linux/magic.h>
 
 #include <crypto/hash.h>
+#include <crypto/hash_info.h>
 #include <crypto/algapi.h>
 #include "evm.h"
 
@@ -122,8 +123,9 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry,
 					     struct integrity_iint_cache *iint)
 {
 	struct evm_ima_xattr_data *xattr_data = NULL;
-	struct evm_ima_xattr_data calc;
+	struct signature_v2_hdr *hdr;
 	enum integrity_status evm_status = INTEGRITY_PASS;
+	struct ima_digest_data *digest = NULL;
 	struct inode *inode;
 	int rc, xattr_len;
 
@@ -155,29 +157,41 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry,
 	/* check value type */
 	switch (xattr_data->type) {
 	case EVM_XATTR_HMAC:
+		digest = kmalloc(sizeof(*digest) + SHA1_DIGEST_SIZE,
+				 GFP_KERNEL);
+		if (!digest) {
+			rc = -ENOMEM;
+			break;
+		}
 		if (xattr_len != sizeof(struct evm_ima_xattr_data)) {
 			evm_status = INTEGRITY_FAIL;
 			goto out;
 		}
+
+		digest->algo = HASH_ALGO_SHA1;
 		rc = evm_calc_hmac(dentry, xattr_name, xattr_value,
-				   xattr_value_len, calc.digest);
+				   xattr_value_len, digest);
 		if (rc)
 			break;
-		rc = crypto_memneq(xattr_data->digest, calc.digest,
-			    sizeof(calc.digest));
+		rc = crypto_memneq(xattr_data->digest, digest,
+				   SHA1_DIGEST_SIZE);
 		if (rc)
 			rc = -EINVAL;
 		break;
 	case EVM_IMA_XATTR_DIGSIG:
 	case EVM_XATTR_PORTABLE_DIGSIG:
+		digest = kmalloc(sizeof(*digest) + IMA_MAX_DIGEST_SIZE,
+				 GFP_KERNEL);
+		hdr = (struct signature_v2_hdr *)xattr_data;
+
+		digest->algo = hdr->hash_algo;
 		rc = evm_calc_hash(dentry, xattr_name, xattr_value,
-				   xattr_value_len, xattr_data->type,
-				   calc.digest);
+				   xattr_value_len, xattr_data->type, digest);
 		if (rc)
 			break;
 		rc = integrity_digsig_verify(INTEGRITY_KEYRING_EVM,
 					(const char *)xattr_data, xattr_len,
-					calc.digest, sizeof(calc.digest));
+					digest->digest, digest->length);
 		if (!rc) {
 			inode = d_backing_inode(dentry);
 
@@ -203,6 +217,7 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry,
 		evm_status = (rc == -ENODATA) ?
 				INTEGRITY_NOXATTRS : INTEGRITY_FAIL;
 out:
+	kfree(digest);
 	if (iint)
 		iint->evm_status = evm_status;
 	kfree(xattr_data);
Signature · 1 line
2.17.0.441.gb46fe60e1d-goog
#1 lore raw
Quoted 11 lines · Matthew Garrett

static int evm_is_immutable(struct dentry *dentry, struct inode *inode)

security/integrity/evm/evm_crypto.c +1 -0
@@ -303,6 +305,7 @@ int evm_update_evmxattr(struct dentry *dentry, const char *xattr_name,
 {
 	struct inode *inode = d_backing_inode(dentry);
 	struct evm_ima_xattr_data xattr_data;
+	struct ima_digest_data *data;
 	int rc = 0;
 
 	/*

security/integrity/evm/evm_crypto.c int evm_update_evmxattr(struct dentry *dentry, const char *xattr_name, · line 323

Quoted 6 lines
security/integrity/evm/evm_crypto.c +3 -0
@@ -315,10 +318,16 @@ int evm_update_evmxattr(struct dentry *dentry, const char *xattr_name,
 	if (rc)
 		return -EPERM;
 
+	data = kmalloc(sizeof(*data) + SHA1_DIGEST_SIZE, GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;

Instead of allocating and freeing the buffer each time an HMAC is updated or verified, like before, why not define a new struct which is allocated on the stack both here and in evm_verify_hmac()?

security/integrity/evm/evm_crypto.c int evm_update_evmxattr(struct dentry *dentry, const char *xattr_name, · line 333

Quoted 11 lines
security/integrity/evm/evm_crypto.c +4 -1
+
+	data->algo = HASH_ALGO_SHA1;
 	rc = evm_calc_hmac(dentry, xattr_name, xattr_value,
-			   xattr_value_len, xattr_data.digest);
+			   xattr_value_len, data);
 	if (rc == 0) {
 		xattr_data.type = EVM_XATTR_HMAC;
+		memcpy(&xattr_data.digest, data->digest, SHA1_DIGEST_SIZE);
 		rc = __vfs_setxattr_noperm(dentry, XATTR_NAME_EVM,
 					   &xattr_data,
 					   sizeof(xattr_data), 0);

ima_fix_xattr() supports writing both the original IMA_XATTR_DIGEST format, which was sha1 based, and the new IMA_XATTR_DIGEST_NG format, which supports larger digests. It writes security.ima directly from ima_digest_data struct. No need for copying it.

Mimi