---
src/evmctl.c | 11 +++++--
src/libimaevm.c | 77 ++++++++++++++++++++++++++++++++++++++++++-------
2 files changed, 75 insertions(+), 13 deletions(-)
diff --git a/src/evmctl.c b/src/evmctl.c
index c7c8c8cf6e89..6af72255bf72 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)
@@ -1574,7 +1574,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);
@@ -1620,11 +1621,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 af145bd62ac9..739ace5459e5 100644
--- a/src/libimaevm.c
+++ b/src/libimaevm.c
@@ -424,10 +424,21 @@ void init_public_keys(const char *keyfiles)
}
/*
+ * Verify a signature, prefixed with the signature_v2_hdr, either based
+ * directly or indirectly on the file data hash.
+ *
+ * version 2: directly based on the file data hash (e.g. sha*sum)
+ * version 3: indirectly based on the hash of the struct ima_file_id, which
+ * contains the xattr type (enum evm_ima_xattr_type), the hash
+ * algorithm (enum hash_algo), and the file data hash
+ * (e.g. fsverity digest).
+ *
* Return: 0 verification good, 1 verification bad, -1 error.
+ *
+ * (Note: signature_v2_hdr struct does not contain the 'type'.)
*/
-static int verify_hash_v2(const char *file, const unsigned char *hash, int size,
- unsigned char *sig, int siglen)
+static int verify_hash_common(const char *file, const unsigned char *hash,
+ int size, unsigned char *sig, int siglen)
{
int ret = -1;
EVP_PKEY *pkey, *pkey_free = NULL;
@@ -497,6 +508,39 @@ err:
return ret;
}
+/*
+ * Verify a signature, prefixed with the signature_v2_hdr, directly based
+ * on the file data hash.
+ *
+ * Return: 0 verification good, 1 verification bad, -1 error.
+ */
+static int verify_hash_v2(const char *file, const unsigned char *hash,
+ int size, unsigned char *sig, int siglen)
+{
+ /* note: signature_v2_hdr does not contain 'type', use sig + 1 */
+ return verify_hash_common(file, hash, size, sig + 1, siglen - 1);
+}
+
+/*
+ * Verify a signature, prefixed with the signature_v2_hdr, indirectly based
+ * on the file data hash.
+ *
+ * Return: 0 verification good, 1 verification bad, -1 error.
+ */
+static int verify_hash_v3(const char *file, const unsigned char *hash,
+ int size, unsigned char *sig, int siglen)
+{
+ unsigned char sigv3_hash[MAX_DIGEST_SIZE];
+ int ret;
+
+ ret = calc_hash_sigv3(sig[0], NULL, hash, sigv3_hash);
+ if (ret < 0)
+ return ret;
+
+ /* note: signature_v2_hdr does not contain 'type', use sig + 1 */
+ return verify_hash_common(file, sigv3_hash, size, sig + 1, siglen - 1);
+}
+
#define HASH_MAX_DIGESTSIZE 512
struct ima_file_id {
@@ -536,6 +580,9 @@ int calc_hash_sigv3(enum evm_ima_xattr_type type, const char *algo,
return -EINVAL;
}
+ 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;
@@ -624,7 +671,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;
@@ -633,11 +680,11 @@ int imaevm_hash_algo_from_sig(unsigned char *sig)
return -1;
}
-int verify_hash(const char *file, const unsigned char *hash, int size, unsigned char *sig,
- int siglen)
+int verify_hash(const char *file, const unsigned char *hash, int size,
+ unsigned char *sig, int siglen)
{
/* 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 */
@@ -645,9 +692,12 @@ 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_v1(file, hash, size, sig + 1, siglen - 1,
+ key);
+ } else if (sig[1] == DIGSIG_VERSION_2) {
return verify_hash_v2(file, hash, size, sig, siglen);
+ } else if (sig[1] == DIGSIG_VERSION_3) {
+ return verify_hash_v3(file, hash, size, sig, siglen);
} else
return -1;
}
@@ -658,11 +708,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);
@@ -676,14 +731,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);
}
/*