diff mbox series

[v2,ima-evm-utils,3/3] Verify an fs-verity file digest based signature

Message ID 20220512183056.307597-3-zohar@linux.ibm.com (mailing list archive)
State New, archived
Headers show
Series [v2,ima-evm-utils,1/3] initialize errno in cmd_sign_hash() | expand

Commit Message

Mimi Zohar May 12, 2022, 6:30 p.m. UTC
ima-evm-utils does not attempt to calculate or even read the fs-verity
file hash, but can verify the fs-verity signature based on the fsverity
file hash, both contained in the measurement list record.

For the time being only fs-verity supports signature format v3 (sigv3).
To differentiate between the existing IMA and fs-verity file signatures,
modify the verify_hash() 'sig' argument to be a pointer to the entire
xattr, including the xattr type.

Example:
evmctl ima_measurement --key <DER encoded public key> \
 --verify-sig /sys/kernel/security/ima/binary_runtime_measurements

Signed-off-by: Mimi Zohar <zohar@linux.ibm.com>
---
 src/evmctl.c    | 11 +++++++++--
 src/libimaevm.c | 50 +++++++++++++++++++++++++++++++++++++------------
 2 files changed, 47 insertions(+), 14 deletions(-)

Comments

Stefan Berger May 13, 2022, 2:41 p.m. UTC | #1
On 5/12/22 14:30, Mimi Zohar wrote:
> ima-evm-utils does not attempt to calculate or even read the fs-verity
> file hash, but can verify the fs-verity signature based on the fsverity
> file hash, both contained in the measurement list record.
> 
> For the time being only fs-verity supports signature format v3 (sigv3).
> To differentiate between the existing IMA and fs-verity file signatures,
> modify the verify_hash() 'sig' argument to be a pointer to the entire
> xattr, including the xattr type.
> 
> Example:
> evmctl ima_measurement --key <DER encoded public key> \
>   --verify-sig /sys/kernel/security/ima/binary_runtime_measurements
> 
> Signed-off-by: Mimi Zohar <zohar@linux.ibm.com>
> ---
>   src/evmctl.c    | 11 +++++++++--
>   src/libimaevm.c | 50 +++++++++++++++++++++++++++++++++++++------------
>   2 files changed, 47 insertions(+), 14 deletions(-)
> 
> diff --git a/src/evmctl.c b/src/evmctl.c
> index 9152b0a5c7c2..593eed80f96a 100644
> --- a/src/evmctl.c
> +++ b/src/evmctl.c
> @@ -909,7 +909,7 @@ static int verify_evm(const char *file)
>   		return mdlen;
>   	assert(mdlen <= sizeof(hash));
>   
> -	return verify_hash(file, hash, mdlen, sig + 1, len - 1);
> +	return verify_hash(file, hash, mdlen, sig, len);
>   }
>   
>   static int cmd_verify_evm(struct command *cmd)
> @@ -1572,7 +1572,8 @@ void ima_ng_show(struct template_entry *entry)
>   	fieldp += field_len;
>   	total_len -= field_len;
>   
> -	if (!strcmp(entry->name, "ima-sig")) {
> +	if (!strcmp(entry->name, "ima-sig") ||
> +	    !strcmp(entry->name, "ima-sigv2")) {
>   		/* get signature */
>   		field_len = *(uint32_t *)fieldp;
>   		fieldp += sizeof(field_len);
> @@ -1618,11 +1619,17 @@ void ima_ng_show(struct template_entry *entry)
>   			log_info(" ");
>   			log_dump(sig, sig_len);
>   		}
> +
> +		/*
> +		 * Either verify the signature against the hash contained in
> +		 * the measurement list or calculate the hash.
> +		 */
>   		if (verify_list_sig)
>   			err = ima_verify_signature(path, sig, sig_len,
>   						   digest, digest_len);
>   		else
>   			err = ima_verify_signature(path, sig, sig_len, NULL, 0);
> +
>   		if (!err && imaevm_params.verbose > LOG_INFO)
>   			log_info("%s: verification is OK\n", path);
>   	} else {
> diff --git a/src/libimaevm.c b/src/libimaevm.c
> index 7c2ed5fb0556..8a37551132d6 100644
> --- a/src/libimaevm.c
> +++ b/src/libimaevm.c
> @@ -534,11 +534,21 @@ int calc_hash_sigv3(enum evm_ima_xattr_type type, const char *algo,
>   		return -EINVAL;
>   	}
>   
> -	if ((hash_algo = imaevm_get_hash_algo(algo)) < 0) {
> -		log_err("Hash algorithm %s not supported\n", algo);
> -		return -EINVAL;
> +	/*
> +	 * Calculating the hash is based on the fsverity hash algorithm,
> +	 * while verifying the signature is based on the hash algorithm
> +	 * contained in the signature header.
> +	 */
> +	if (algo) {
> +		if ((hash_algo = imaevm_get_hash_algo(algo)) < 0) {
> +			log_err("Hash algorithm %s not supported\n", algo);
> +			return -EINVAL;
> +		}
> +		file_id.hash_algorithm = hash_algo;
> +	} else {
> +		algo = imaevm_params.hash_algo;
> +		file_id.hash_algorithm = imaevm_get_hash_algo(algo);

You should probably compare this also against '< 0' as it was before. If 
you do it outside the if-else the two cases can share this:

	if (!algo)
		algo = imaevm_params.hash_algo;

	if ((hash_algo = imaevm_get_hash_algo(algo)) < 0) {
		log_err("Hash algorithm %s not supported\n", algo);
		return -EINVAL;
	}
	file_id.hash_algorithm = hash_algo;
diff mbox series

Patch

diff --git a/src/evmctl.c b/src/evmctl.c
index 9152b0a5c7c2..593eed80f96a 100644
--- a/src/evmctl.c
+++ b/src/evmctl.c
@@ -909,7 +909,7 @@  static int verify_evm(const char *file)
 		return mdlen;
 	assert(mdlen <= sizeof(hash));
 
-	return verify_hash(file, hash, mdlen, sig + 1, len - 1);
+	return verify_hash(file, hash, mdlen, sig, len);
 }
 
 static int cmd_verify_evm(struct command *cmd)
@@ -1572,7 +1572,8 @@  void ima_ng_show(struct template_entry *entry)
 	fieldp += field_len;
 	total_len -= field_len;
 
-	if (!strcmp(entry->name, "ima-sig")) {
+	if (!strcmp(entry->name, "ima-sig") ||
+	    !strcmp(entry->name, "ima-sigv2")) {
 		/* get signature */
 		field_len = *(uint32_t *)fieldp;
 		fieldp += sizeof(field_len);
@@ -1618,11 +1619,17 @@  void ima_ng_show(struct template_entry *entry)
 			log_info(" ");
 			log_dump(sig, sig_len);
 		}
+
+		/*
+		 * Either verify the signature against the hash contained in
+		 * the measurement list or calculate the hash.
+		 */
 		if (verify_list_sig)
 			err = ima_verify_signature(path, sig, sig_len,
 						   digest, digest_len);
 		else
 			err = ima_verify_signature(path, sig, sig_len, NULL, 0);
+
 		if (!err && imaevm_params.verbose > LOG_INFO)
 			log_info("%s: verification is OK\n", path);
 	} else {
diff --git a/src/libimaevm.c b/src/libimaevm.c
index 7c2ed5fb0556..8a37551132d6 100644
--- a/src/libimaevm.c
+++ b/src/libimaevm.c
@@ -534,11 +534,21 @@  int calc_hash_sigv3(enum evm_ima_xattr_type type, const char *algo,
 		return -EINVAL;
 	}
 
-	if ((hash_algo = imaevm_get_hash_algo(algo)) < 0) {
-		log_err("Hash algorithm %s not supported\n", algo);
-		return -EINVAL;
+	/*
+	 * Calculating the hash is based on the fsverity hash algorithm,
+	 * while verifying the signature is based on the hash algorithm
+	 * contained in the signature header.
+	 */
+	if (algo) {
+		if ((hash_algo = imaevm_get_hash_algo(algo)) < 0) {
+			log_err("Hash algorithm %s not supported\n", algo);
+			return -EINVAL;
+		}
+		file_id.hash_algorithm = hash_algo;
+	} else {
+		algo = imaevm_params.hash_algo;
+		file_id.hash_algorithm = imaevm_get_hash_algo(algo);
 	}
-	file_id.hash_algorithm = hash_algo;
 
 	md = EVP_get_digestbyname(algo);
 	if (!md) {
@@ -622,7 +632,7 @@  int imaevm_hash_algo_from_sig(unsigned char *sig)
 		default:
 			return -1;
 		}
-	} else if (sig[0] == DIGSIG_VERSION_2) {
+	} else if (sig[0] == DIGSIG_VERSION_2 || sig[0] == DIGSIG_VERSION_3) {
 		hashalgo = ((struct signature_v2_hdr *)sig)->hash_algo;
 		if (hashalgo >= PKEY_HASH__LAST)
 			return -1;
@@ -634,8 +644,11 @@  int imaevm_hash_algo_from_sig(unsigned char *sig)
 int verify_hash(const char *file, const unsigned char *hash, int size, unsigned char *sig,
 		int siglen)
 {
+	unsigned char sigv3_hash[MAX_DIGEST_SIZE];
+	int ret;
+
 	/* Get signature type from sig header */
-	if (sig[0] == DIGSIG_VERSION_1) {
+	if (sig[1] == DIGSIG_VERSION_1) {
 		const char *key = NULL;
 
 		/* Read pubkey from RSA key */
@@ -643,9 +656,17 @@  int verify_hash(const char *file, const unsigned char *hash, int size, unsigned
 			key = "/etc/keys/pubkey_evm.pem";
 		else
 			key = imaevm_params.keyfile;
-		return verify_hash_v1(file, hash, size, sig, siglen, key);
-	} else if (sig[0] == DIGSIG_VERSION_2) {
-		return verify_hash_v2(file, hash, size, sig, siglen);
+		return verify_hash_v1(file, hash, size, sig + 1, siglen - 1,
+				      key);
+	} else if (sig[1] == DIGSIG_VERSION_2) {
+		return verify_hash_v2(file, hash, size, sig + 1, siglen - 1);
+	} else if (sig[1] == DIGSIG_VERSION_3) {
+		ret = calc_hash_sigv3(sig[0], NULL, hash, sigv3_hash);
+		if (ret < 0)
+			return ret;
+
+		return verify_hash_v2(file, sigv3_hash, size, sig + 1,
+				      siglen - 1);
 	} else
 		return -1;
 }
@@ -656,11 +677,16 @@  int ima_verify_signature(const char *file, unsigned char *sig, int siglen,
 	unsigned char hash[MAX_DIGEST_SIZE];
 	int hashlen, sig_hash_algo;
 
-	if (sig[0] != EVM_IMA_XATTR_DIGSIG) {
+	if ((sig[0] != EVM_IMA_XATTR_DIGSIG) && (sig[0] != IMA_VERITY_DIGSIG)) {
 		log_err("%s: xattr ima has no signature\n", file);
 		return -1;
 	}
 
+	if (!digest && sig[0] == IMA_VERITY_DIGSIG) {
+		log_err("%s: calculating the fs-verity digest is not supported\n", file);
+		return -1;
+	}
+
 	sig_hash_algo = imaevm_hash_algo_from_sig(sig + 1);
 	if (sig_hash_algo < 0) {
 		log_err("%s: Invalid signature\n", file);
@@ -674,14 +700,14 @@  int ima_verify_signature(const char *file, unsigned char *sig, int siglen,
 	 * measurement list, not by calculating the local file digest.
 	 */
 	if (digestlen > 0)
-	    return verify_hash(file, digest, digestlen, sig + 1, siglen - 1);
+		return verify_hash(file, digest, digestlen, sig, siglen);
 
 	hashlen = ima_calc_hash(file, hash);
 	if (hashlen <= 1)
 		return hashlen;
 	assert(hashlen <= sizeof(hash));
 
-	return verify_hash(file, hash, hashlen, sig + 1, siglen - 1);
+	return verify_hash(file, hash, hashlen, sig, siglen);
 }
 
 /*