1
0
mirror of https://git.code.sf.net/p/linux-ima/ima-evm-utils synced 2025-04-28 22:53:37 +02:00

Verify an fs-verity file digest based signature

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.

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

Modify 'sig' argument of verify_hash() to be the full xattr in order to
differentiate signatures types.

Note:
Kernel commit b1aaab22e263 ("ima: pass full xattr with the signature")
added the 'type' to signature_v2_hdr struct, which hasn't been reflected
here. (todo)

Reviewed-by: Stefan Berger <stefanb@linux.ibm.com>
Signed-off-by: Mimi Zohar <zohar@linux.ibm.com>
This commit is contained in:
Mimi Zohar 2021-11-24 08:35:20 -05:00
parent fc46af121e
commit 3d77138811
2 changed files with 76 additions and 14 deletions

View File

@ -918,7 +918,7 @@ static int verify_evm(const char *file)
return mdlen; return mdlen;
assert(mdlen <= sizeof(hash)); 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) static int cmd_verify_evm(struct command *cmd)
@ -1583,7 +1583,8 @@ void ima_ng_show(struct template_entry *entry)
fieldp += field_len; fieldp += field_len;
total_len -= 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 */ /* get signature */
field_len = *(uint32_t *)fieldp; field_len = *(uint32_t *)fieldp;
fieldp += sizeof(field_len); fieldp += sizeof(field_len);
@ -1629,11 +1630,17 @@ void ima_ng_show(struct template_entry *entry)
log_info(" "); log_info(" ");
log_dump(sig, sig_len); 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) if (verify_list_sig)
err = ima_verify_signature(path, sig, sig_len, err = ima_verify_signature(path, sig, sig_len,
digest, digest_len); digest, digest_len);
else else
err = ima_verify_signature(path, sig, sig_len, NULL, 0); err = ima_verify_signature(path, sig, sig_len, NULL, 0);
if (!err && imaevm_params.verbose > LOG_INFO) if (!err && imaevm_params.verbose > LOG_INFO)
log_info("%s: verification is OK\n", path); log_info("%s: verification is OK\n", path);
} else { } else {

View File

@ -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. * 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, static int verify_hash_common(const char *file, const unsigned char *hash,
unsigned char *sig, int siglen) int size, unsigned char *sig, int siglen)
{ {
int ret = -1; int ret = -1;
EVP_PKEY *pkey, *pkey_free = NULL; EVP_PKEY *pkey, *pkey_free = NULL;
@ -497,6 +508,39 @@ err:
return ret; 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 64 /* kernel HASH_MAX_DIGESTSIZE is 64 bytes */ #define HASH_MAX_DIGESTSIZE 64 /* kernel HASH_MAX_DIGESTSIZE is 64 bytes */
struct ima_file_id { struct ima_file_id {
@ -537,6 +581,9 @@ int calc_hash_sigv3(enum evm_ima_xattr_type type, const char *algo,
return -EINVAL; return -EINVAL;
} }
if (!algo)
algo = imaevm_params.hash_algo;
if ((hash_algo = imaevm_get_hash_algo(algo)) < 0) { if ((hash_algo = imaevm_get_hash_algo(algo)) < 0) {
log_err("Hash algorithm %s not supported\n", algo); log_err("Hash algorithm %s not supported\n", algo);
return -EINVAL; return -EINVAL;
@ -625,7 +672,7 @@ int imaevm_hash_algo_from_sig(unsigned char *sig)
default: default:
return -1; 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; hashalgo = ((struct signature_v2_hdr *)sig)->hash_algo;
if (hashalgo >= PKEY_HASH__LAST) if (hashalgo >= PKEY_HASH__LAST)
return -1; return -1;
@ -634,11 +681,11 @@ int imaevm_hash_algo_from_sig(unsigned char *sig)
return -1; return -1;
} }
int verify_hash(const char *file, const unsigned char *hash, int size, unsigned char *sig, int verify_hash(const char *file, const unsigned char *hash, int size,
int siglen) unsigned char *sig, int siglen)
{ {
/* Get signature type from sig header */ /* Get signature type from sig header */
if (sig[0] == DIGSIG_VERSION_1) { if (sig[1] == DIGSIG_VERSION_1) {
const char *key = NULL; const char *key = NULL;
/* Read pubkey from RSA key */ /* Read pubkey from RSA key */
@ -646,9 +693,12 @@ int verify_hash(const char *file, const unsigned char *hash, int size, unsigned
key = "/etc/keys/pubkey_evm.pem"; key = "/etc/keys/pubkey_evm.pem";
else else
key = imaevm_params.keyfile; key = imaevm_params.keyfile;
return verify_hash_v1(file, hash, size, sig, siglen, key); return verify_hash_v1(file, hash, size, sig + 1, siglen - 1,
} else if (sig[0] == DIGSIG_VERSION_2) { key);
} else if (sig[1] == DIGSIG_VERSION_2) {
return verify_hash_v2(file, hash, size, sig, siglen); 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 } else
return -1; return -1;
} }
@ -659,11 +709,16 @@ int ima_verify_signature(const char *file, unsigned char *sig, int siglen,
unsigned char hash[MAX_DIGEST_SIZE]; unsigned char hash[MAX_DIGEST_SIZE];
int hashlen, sig_hash_algo; 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); log_err("%s: xattr ima has no signature\n", file);
return -1; 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); sig_hash_algo = imaevm_hash_algo_from_sig(sig + 1);
if (sig_hash_algo < 0) { if (sig_hash_algo < 0) {
log_err("%s: Invalid signature\n", file); log_err("%s: Invalid signature\n", file);
@ -676,15 +731,15 @@ int ima_verify_signature(const char *file, unsigned char *sig, int siglen,
* Validate the signature based on the digest included in the * Validate the signature based on the digest included in the
* measurement list, not by calculating the local file digest. * measurement list, not by calculating the local file digest.
*/ */
if (digestlen > 0) if (digest && 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); hashlen = ima_calc_hash(file, hash);
if (hashlen <= 1) if (hashlen <= 1)
return hashlen; return hashlen;
assert(hashlen <= sizeof(hash)); assert(hashlen <= sizeof(hash));
return verify_hash(file, hash, hashlen, sig + 1, siglen - 1); return verify_hash(file, hash, hashlen, sig, siglen);
} }
/* /*