All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v3 ima-evm-utils 0/3] fs-verity file signature support
@ 2022-05-20 16:11 Mimi Zohar
  2022-05-20 16:11 ` [PATCH v3 ima-evm-utils 1/3] Reset 'errno' after failure to open or access a file Mimi Zohar
                   ` (2 more replies)
  0 siblings, 3 replies; 7+ messages in thread
From: Mimi Zohar @ 2022-05-20 16:11 UTC (permalink / raw)
  To: linux-integrity; +Cc: Mimi Zohar, Eric Biggers, Stefan Berger

Extend the existing file list digest signing and the IMA measurement
list file signature verification to support the new sigv3 format
version.  Neither file digest signing nor signature verification
require calculating the fs-verity file digest.

evmctl examples of signing fs-verity file hashes and verifying the
fs-verity file signatures are included the respective patch
description.

Changelog v3:

- Refactor the existing file hash signing function so that both
signature format version 2 and 3 may use it.  Signature v2 directly
signs the file hash, while signature v3 indirectly signs the file hash.

- Addressed Stefan Berger's comments: properly clear errno, properly
limit the hash algorithm name size to address an out of bounds error.
Instead of allowing the maximum hash algorithm name size, use the
current fs-verity supported maximum hash algorithm size.

- Based on Eric Bigger's recommendation of using "fsverity digest"
instead of "fsverity measure", replaced all references.


Mimi Zohar (3):
  Reset 'errno' after failure to open or access a file
  Sign an fs-verity file digest
  Verify an fs-verity file digest based signature

 README          |   3 +-
 src/evmctl.c    | 126 ++++++++++++++++++++++++++++++------
 src/imaevm.h    |   5 +-
 src/libimaevm.c | 166 ++++++++++++++++++++++++++++++++++++++++++++----
 src/utils.c     |   5 +-
 5 files changed, 272 insertions(+), 33 deletions(-)

-- 
2.27.0


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH v3 ima-evm-utils 1/3] Reset 'errno' after failure to open or access a file
  2022-05-20 16:11 [PATCH v3 ima-evm-utils 0/3] fs-verity file signature support Mimi Zohar
@ 2022-05-20 16:11 ` Mimi Zohar
  2022-05-22 20:24   ` Stefan Berger
  2022-05-20 16:11 ` [PATCH v3 ima-evm-utils 2/3] Sign an fs-verity file digest Mimi Zohar
  2022-05-20 16:11 ` [PATCH v3 ima-evm-utils 3/3] Verify an fs-verity file digest based signature Mimi Zohar
  2 siblings, 1 reply; 7+ messages in thread
From: Mimi Zohar @ 2022-05-20 16:11 UTC (permalink / raw)
  To: linux-integrity; +Cc: Mimi Zohar, Eric Biggers, Stefan Berger

Not being able to open a file is not necessarily a problem. If
and when it occurs, an informational or error message with the
actual filename is emitted as needed.

Reset 'errno' to prevent the "errno: No such file or directory (2)"
generic message.

Signed-off-by: Mimi Zohar <zohar@linux.ibm.com>
---
 src/evmctl.c    | 16 ++++++++++++++--
 src/libimaevm.c |  4 ++++
 src/utils.c     |  5 ++++-
 3 files changed, 22 insertions(+), 3 deletions(-)

diff --git a/src/evmctl.c b/src/evmctl.c
index 8bdd34817408..101cd407e05d 100644
--- a/src/evmctl.c
+++ b/src/evmctl.c
@@ -181,6 +181,7 @@ static int bin2file(const char *file, const char *ext, const unsigned char *data
 	fp = fopen(name, "w");
 	if (!fp) {
 		log_err("Failed to open: %s\n", name);
+		errno = 0;
 		return -1;
 	}
 	err = fwrite(data, len, 1, fp);
@@ -206,6 +207,7 @@ static unsigned char *file2bin(const char *file, const char *ext, int *size)
 	fp = fopen(name, "r");
 	if (!fp) {
 		log_err("Failed to open: %s\n", name);
+		errno = 0;
 		return NULL;
 	}
 	if (fstat(fileno(fp), &stats) == -1) {
@@ -312,8 +314,10 @@ static int get_uuid(struct stat *st, char *uuid)
 	sprintf(path, "blkid -s UUID -o value /dev/block/%u:%u", major, minor);
 
 	fp = popen(path, "r");
-	if (!fp)
+	if (!fp) {
+		errno = 0;
 		goto err;
+	}
 
 	len = fread(_uuid, 1, sizeof(_uuid), fp);
 	pclose(fp);
@@ -370,6 +374,7 @@ static int calc_evm_hash(const char *file, unsigned char *hash)
 
 			if (fd < 0) {
 				log_err("Failed to open: %s\n", file);
+				errno = 0;
 				return -1;
 			}
 			if (ioctl(fd, FS_IOC_GETVERSION, &generation)) {
@@ -1122,6 +1127,7 @@ static int calc_evm_hmac(const char *file, const char *keyfile, unsigned char *h
 
 		if (fd < 0) {
 			log_err("Failed to open %s\n", file);
+			errno = 0;
 			goto out;
 		}
 		if (ioctl(fd, FS_IOC_GETVERSION, &generation)) {
@@ -1312,6 +1318,7 @@ static int ima_fix(const char *path)
 	fd = open(path, O_RDONLY);
 	if (fd < 0) {
 		log_errno("Failed to open file: %s", path);
+		errno = 0;
 		return -1;
 	}
 
@@ -1828,8 +1835,10 @@ static int read_sysfs_pcrs(int num_banks, struct tpm_bank_info *tpm_banks)
 	int i, result;
 
 	fp = fopen(pcrs, "r");
-	if (!fp)
+	if (!fp) {
 		fp = fopen(misc_pcrs, "r");
+		errno = 0;
+	}
 	if (!fp)
 		return -1;
 
@@ -1892,6 +1901,7 @@ static int read_file_pcrs(int num_banks, struct tpm_bank_info *tpm_banks)
 		fp = fopen(path, "r");
 		if (!fp) {
 			log_err("Could not open '%s'\n", path);
+			errno = 0;
 			return -1;
 		}
 
@@ -1984,6 +1994,7 @@ static int ima_measurement(const char *file)
 	fp = fopen(file, "rb");
 	if (!fp) {
 		log_err("Failed to open measurement file: %s\n", file);
+		errno = 0;
 		return -1;
 	}
 
@@ -2229,6 +2240,7 @@ static int read_binary_bios_measurements(char *file, struct tpm_bank_info *bank)
 	fp = fopen(file, "r");
 	if (!fp) {
 		log_errno("Failed to open TPM 1.2 event log.\n");
+		errno = 0;
 		return 1;
 	}
 
diff --git a/src/libimaevm.c b/src/libimaevm.c
index 388b726f1e3a..a4f2ec40684d 100644
--- a/src/libimaevm.c
+++ b/src/libimaevm.c
@@ -144,6 +144,7 @@ static int add_file_hash(const char *file, EVP_MD_CTX *ctx)
 	fp = fopen(file, "r");
 	if (!fp) {
 		log_err("Failed to open: %s\n", file);
+		errno = 0;
 		return -1;
 	}
 
@@ -258,6 +259,7 @@ EVP_PKEY *read_pub_pkey(const char *keyfile, int x509)
 	if (!fp) {
 		if (imaevm_params.verbose > LOG_INFO)
 			log_info("Failed to open keyfile: %s\n", keyfile);
+		errno = 0;
 		return NULL;
 	}
 
@@ -735,6 +737,7 @@ static int read_keyid_from_cert(uint32_t *keyid_be, const char *certfile, int tr
 
 	if (!(fp = fopen(certfile, "r"))) {
 		log_err("Cannot open %s: %s\n", certfile, strerror(errno));
+		errno = 0;
 		return -1;
 	}
 	if (!PEM_read_X509(fp, &x, NULL, NULL)) {
@@ -826,6 +829,7 @@ static EVP_PKEY *read_priv_pkey(const char *keyfile, const char *keypass)
 		fp = fopen(keyfile, "r");
 		if (!fp) {
 			log_err("Failed to open keyfile: %s\n", keyfile);
+			errno = 0;
 			return NULL;
 		}
 		pkey = PEM_read_PrivateKey(fp, NULL, NULL, (void *)keypass);
diff --git a/src/utils.c b/src/utils.c
index 294dac554392..1026d44776da 100644
--- a/src/utils.c
+++ b/src/utils.c
@@ -11,6 +11,7 @@
 #include <string.h>
 #include <sys/stat.h>
 #include <unistd.h>
+#include <errno.h>
 
 #include "utils.h"
 
@@ -26,8 +27,10 @@ static int file_exist(const char *path)
 {
 	struct stat st;
 
-	if (!access(path, R_OK) && !stat(path, &st) && S_ISREG(st.st_mode))
+	if (!access(path, R_OK) && !stat(path, &st) && S_ISREG(st.st_mode)) {
+		errno = 0;
 		return 1;
+	}
 
 	return 0;
 }
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH v3 ima-evm-utils 2/3] Sign an fs-verity file digest
  2022-05-20 16:11 [PATCH v3 ima-evm-utils 0/3] fs-verity file signature support Mimi Zohar
  2022-05-20 16:11 ` [PATCH v3 ima-evm-utils 1/3] Reset 'errno' after failure to open or access a file Mimi Zohar
@ 2022-05-20 16:11 ` Mimi Zohar
  2022-05-23  0:32   ` Stefan Berger
  2022-05-20 16:11 ` [PATCH v3 ima-evm-utils 3/3] Verify an fs-verity file digest based signature Mimi Zohar
  2 siblings, 1 reply; 7+ messages in thread
From: Mimi Zohar @ 2022-05-20 16:11 UTC (permalink / raw)
  To: linux-integrity; +Cc: Mimi Zohar, Eric Biggers, Stefan Berger

Sign fs-verity file digests provided in the format as produced by
"fsverity digest".  The output is of the same format as the input,
but with the file signature appended.  Use setfattr to write the
signature as security.ima xattr.

fsverity digest format: <algo>:<hash> <pathname>
output format: <algo>:<hash> <pathname> <signature>

Instead of directly signing the fsverity hash, to disambiguate the
original IMA signatures from the fs-verity signatures stored in the
security.ima xattr a new signature format version 3 (sigv3) was
defined as the hash of the xattr type (enum evm_ima_xattr_type),
the hash algorithm (enum hash_algo), and the hash.

Example:
fsverity digest <pathname> | evmctl sign_hash --veritysig \
 --key <pem encoded private key>

Signed-off-by: Mimi Zohar <zohar@linux.ibm.com>
---
 README          |  3 +-
 src/evmctl.c    | 99 +++++++++++++++++++++++++++++++++++++++++--------
 src/imaevm.h    |  5 ++-
 src/libimaevm.c | 85 ++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 175 insertions(+), 17 deletions(-)

diff --git a/README b/README
index 5b5ecb52a74b..ffe46ad75728 100644
--- a/README
+++ b/README
@@ -34,7 +34,7 @@ COMMANDS
  ima_hash file
  ima_measurement [--ignore-violations] [--verify-sig [--key "key1, key2, ..."]]  [--pcrs [hash-algorithm,]file [--pcrs hash-algorithm,file] ...] file
  ima_fix [-t fdsxm] path
- sign_hash [--key key] [--pass password]
+ sign_hash [--veritysig] [--key key] [--pass password]
  hmac [--imahash | --imasig ] file
 
 
@@ -43,6 +43,7 @@ OPTIONS
 
   -a, --hashalgo     sha1, sha224, sha256, sha384, sha512
   -s, --imasig       make IMA signature
+      --veritysig    sign an fs-verity file digest hash
   -d, --imahash      make IMA hash
   -f, --sigfile      store IMA signature in .sig file instead of xattr
       --xattr-user   store xattrs in user namespace (for testing purposes)
diff --git a/src/evmctl.c b/src/evmctl.c
index 101cd407e05d..c7c8c8cf6e89 100644
--- a/src/evmctl.c
+++ b/src/evmctl.c
@@ -135,6 +135,7 @@ static int msize;
 static dev_t fs_dev;
 static bool evm_immutable;
 static bool evm_portable;
+static bool veritysig;
 
 #define HMAC_FLAG_NO_UUID	0x0001
 #define HMAC_FLAG_CAPS_SET	0x0002
@@ -731,33 +732,97 @@ static int cmd_sign_ima(struct command *cmd)
 	return do_cmd(cmd, sign_ima_file);
 }
 
+/*
+ * Sign file hash(es) provided in the format as produced by either
+ * sha*sum or "fsverity digest".
+ *
+ * sha*sum format: <hash> <pathname>
+ * fsverity digest format: <algo>:<hash> <pathname>
+ *
+ * To disambiguate the resulting file signatures, a new signature format
+ * version 3 (sigv3) was defined as the hash of the xattr type (enum
+ * evm_ima_xattr_type), the hash algorithm (enum hash_algo), and the hash.
+ *
+ * Either directly sign the sha*sum hash or indirectly sign the fsverity
+ * hash (sigv3).
+ *
+ * The output is the same format as the input with the resulting file
+ * signature appended.
+ */
 static int cmd_sign_hash(struct command *cmd)
 {
+	unsigned char sigv3_hash[MAX_DIGEST_SIZE];
+	unsigned char sig[MAX_SIGNATURE_SIZE];
+	unsigned char hash[MAX_DIGEST_SIZE];
+	int siglen, algolen = 0, hashlen = 0;
+	char *line = NULL, *token, *hashp;
+	size_t line_len = 0;
 	const char *key;
-	char *token, *line = NULL;
-	int hashlen = 0;
-	size_t line_len;
+	char algo[7];	/* Current maximum fsverity hash algo name length */
 	ssize_t len;
-	unsigned char hash[MAX_DIGEST_SIZE];
-	unsigned char sig[MAX_SIGNATURE_SIZE] = "\x03";
-	int siglen;
+	int ret;
 
 	key = imaevm_params.keyfile ? : "/etc/keys/privkey_evm.pem";
 
-	/* support reading hash (eg. output of shasum) */
 	while ((len = getline(&line, &line_len, stdin)) > 0) {
 		/* remove end of line */
 		if (line[len - 1] == '\n')
 			line[--len] = '\0';
 
-		/* find the end of the hash */
-		token = strpbrk(line, ", \t");
-		hashlen = token ? token - line : strlen(line);
+		/*
+		 * Before either directly or indirectly signing the hash,
+		 * convert the hex-ascii hash representation to binary.
+		 */
+		if (veritysig) {
+
+			/* split the algorithm from the hash */
+			hashp = strpbrk(line, ":");
+			if (hashp)	/* pointer to the delimiter */
+				algolen = hashp - line;
+
+			if (!hashp || algolen <= 0 ||
+			    algolen >= sizeof(algo)) {
+				log_err("Missing/invalid fsverity hash algorithm\n");
+				continue;
+			}
+
+			strncpy(algo, line, algolen);
+			algo[algolen] = '\0';	/* Nul terminate algorithm */
+
+			hashp++;
+			token = strpbrk(line, ", \t");
+			hashlen = token - hashp;
+			if (hashlen <= 0) {
+				log_err("Missing fsverity hash\n");
+				continue;
+			}
+
+			assert(hashlen / 2 <= sizeof(hash));
+			hex2bin(hash, hashp, hashlen / 2);
+
+			ret = calc_hash_sigv3(IMA_VERITY_DIGSIG, algo, hash,
+					      sigv3_hash);
+			if (ret < 0 || ret == 1) {
+				log_info("Failure to calculate fs-verity hash\n");
+				continue;
+			}
+
+			siglen = sign_hash(algo, sigv3_hash, hashlen / 2,
+					   key, NULL, sig + 1);
+
+			sig[0] = IMA_VERITY_DIGSIG;
+			sig[1] = DIGSIG_VERSION_3;	/* sigv3 */
+		} else {
+			token = strpbrk(line, ", \t");
+			hashlen = token ? token - line : strlen(line);
+			assert(hashlen / 2 <= sizeof(hash));
+			hex2bin(hash, line, hashlen / 2);
+
+			siglen = sign_hash(imaevm_params.hash_algo, hash,
+					   hashlen / 2, key, NULL, sig + 1);
+			sig[0] = EVM_IMA_XATTR_DIGSIG;
+		}
 
-		assert(hashlen / 2 <= sizeof(hash));
-		hex2bin(hash, line, hashlen / 2);
-		siglen = sign_hash(imaevm_params.hash_algo, hash, hashlen / 2,
-				 key, NULL, sig + 1);
 		if (siglen <= 1)
 			return siglen;
 		assert(siglen < sizeof(sig));
@@ -2568,7 +2633,7 @@ struct command cmds[] = {
 	{"ima_boot_aggregate", cmd_ima_bootaggr, 0, "[--pcrs hash-algorithm,file] [TPM 1.2 BIOS event log]", "Calculate per TPM bank boot_aggregate digests\n"},
 	{"ima_fix", cmd_ima_fix, 0, "[-t fdsxm] path", "Recursively fix IMA/EVM xattrs in fix mode.\n"},
 	{"ima_clear", cmd_ima_clear, 0, "[-t fdsxm] path", "Recursively remove IMA/EVM xattrs.\n"},
-	{"sign_hash", cmd_sign_hash, 0, "[--key key] [--pass [password]", "Sign hashes from shaXsum output.\n"},
+	{"sign_hash", cmd_sign_hash, 0, "[--veritysig] [--key key] [--pass [password]", "Sign hashes from either shaXsum or \"fsverity digest\" output.\n"},
 #ifdef DEBUG
 	{"hmac", cmd_hmac_evm, 0, "[--imahash | --imasig ] file", "Sign file metadata with HMAC using symmetric key (for testing purpose).\n"},
 #endif
@@ -2608,6 +2673,7 @@ static struct option opts[] = {
 	{"verify-bank", 2, 0, 143},
 	{"keyid", 1, 0, 144},
 	{"keyid-from-cert", 1, 0, 145},
+	{"veritysig", 0, 0, 146},
 	{}
 
 };
@@ -2838,6 +2904,9 @@ int main(int argc, char *argv[])
 			}
 			imaevm_params.keyid = keyid;
 			break;
+		case 146:
+			veritysig = 1;
+			break;
 		case '?':
 			exit(1);
 			break;
diff --git a/src/imaevm.h b/src/imaevm.h
index 0d53a0232ae4..afcf1e042014 100644
--- a/src/imaevm.h
+++ b/src/imaevm.h
@@ -93,6 +93,7 @@ enum evm_ima_xattr_type {
 	EVM_IMA_XATTR_DIGSIG,
 	IMA_XATTR_DIGEST_NG,
 	EVM_XATTR_PORTABLE_DIGSIG,
+	IMA_VERITY_DIGSIG,
 };
 
 struct h_misc {
@@ -138,7 +139,8 @@ enum digest_algo {
 
 enum digsig_version {
 	DIGSIG_VERSION_1 = 1,
-	DIGSIG_VERSION_2
+	DIGSIG_VERSION_2,
+	DIGSIG_VERSION_3	/* hash of ima_file_id struct (portion used) */
 };
 
 struct pubkey_hdr {
@@ -233,5 +235,6 @@ int ima_verify_signature(const char *file, unsigned char *sig, int siglen, unsig
 void init_public_keys(const char *keyfiles);
 int imaevm_hash_algo_from_sig(unsigned char *sig);
 const char *imaevm_hash_algo_by_id(int algo);
+int calc_hash_sigv3(enum evm_ima_xattr_type type, const char *algo, const unsigned char *in_hash, unsigned char *out_hash);
 
 #endif
diff --git a/src/libimaevm.c b/src/libimaevm.c
index a4f2ec40684d..af145bd62ac9 100644
--- a/src/libimaevm.c
+++ b/src/libimaevm.c
@@ -497,6 +497,91 @@ err:
 	return ret;
 }
 
+#define HASH_MAX_DIGESTSIZE 512
+
+struct ima_file_id {
+	__u8 hash_type;		/* xattr type [enum evm_ima_xattr_type] */
+	__u8 hash_algorithm;	/* Digest algorithm [enum hash_algo] */
+	__u8 hash[HASH_MAX_DIGESTSIZE];
+} __packed;
+
+/*
+ * Calculate the signature format version 3 hash based on the portion
+ * of the ima_file_id structure used, not the entire structure.
+ *
+ * For openssl errors return 1, other errors return -EINVAL.
+ */
+int calc_hash_sigv3(enum evm_ima_xattr_type type, const char *algo,
+		    const unsigned char *in_hash, unsigned char *out_hash)
+{
+	struct ima_file_id file_id = { .hash_type = IMA_VERITY_DIGSIG };
+	uint8_t *data = (uint8_t *) &file_id;
+
+	const EVP_MD *md;
+	EVP_MD_CTX *pctx;
+	unsigned int mdlen;
+	int err;
+#if OPENSSL_VERSION_NUMBER < 0x10100000
+	EVP_MD_CTX ctx;
+	pctx = &ctx;
+#else
+	pctx = EVP_MD_CTX_new();
+#endif
+	int hash_algo;
+	int hash_size;
+	unsigned int unused;
+
+	if (type != IMA_VERITY_DIGSIG) {
+		log_err("Only fsverity supports signature format v3 (sigv3)\n");
+		return -EINVAL;
+	}
+
+	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;
+
+	md = EVP_get_digestbyname(algo);
+	if (!md) {
+		log_err("EVP_get_digestbyname(%s) failed\n", algo);
+		err = 1;
+		goto err;
+	}
+
+	hash_size = EVP_MD_size(md);
+	memcpy(file_id.hash, in_hash, hash_size);
+
+	err = EVP_DigestInit(pctx, md);
+	if (!err) {
+		log_err("EVP_DigestInit() failed\n");
+		err = 1;
+		goto err;
+	}
+
+	unused = HASH_MAX_DIGESTSIZE - hash_size;
+	if (!EVP_DigestUpdate(pctx, data, sizeof(file_id) - unused)) {
+		log_err("EVP_DigestUpdate() failed\n");
+		err = 1;
+		goto err;
+	}
+
+	err = EVP_DigestFinal(pctx, out_hash, &mdlen);
+	if (!err) {
+		log_err("EVP_DigestFinal() failed\n");
+		err = 1;
+		goto err;
+	}
+	err = mdlen;
+err:
+	if (err == 1)
+		output_openssl_errors();
+#if OPENSSL_VERSION_NUMBER >= 0x10100000
+	EVP_MD_CTX_free(pctx);
+#endif
+	return err;
+}
+
 int imaevm_get_hash_algo(const char *algo)
 {
 	int i;
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH v3 ima-evm-utils 3/3] Verify an fs-verity file digest based signature
  2022-05-20 16:11 [PATCH v3 ima-evm-utils 0/3] fs-verity file signature support Mimi Zohar
  2022-05-20 16:11 ` [PATCH v3 ima-evm-utils 1/3] Reset 'errno' after failure to open or access a file Mimi Zohar
  2022-05-20 16:11 ` [PATCH v3 ima-evm-utils 2/3] Sign an fs-verity file digest Mimi Zohar
@ 2022-05-20 16:11 ` Mimi Zohar
  2022-05-23  0:39   ` Stefan Berger
  2 siblings, 1 reply; 7+ messages in thread
From: Mimi Zohar @ 2022-05-20 16:11 UTC (permalink / raw)
  To: linux-integrity; +Cc: Mimi Zohar, Eric Biggers, Stefan Berger

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)

Signed-off-by: Mimi Zohar <zohar@linux.ibm.com>
---
 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);
 }
 
 /*
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: [PATCH v3 ima-evm-utils 1/3] Reset 'errno' after failure to open or access a file
  2022-05-20 16:11 ` [PATCH v3 ima-evm-utils 1/3] Reset 'errno' after failure to open or access a file Mimi Zohar
@ 2022-05-22 20:24   ` Stefan Berger
  0 siblings, 0 replies; 7+ messages in thread
From: Stefan Berger @ 2022-05-22 20:24 UTC (permalink / raw)
  To: Mimi Zohar, linux-integrity; +Cc: Eric Biggers



On 5/20/22 12:11, Mimi Zohar wrote:
> Not being able to open a file is not necessarily a problem. If
> and when it occurs, an informational or error message with the
> actual filename is emitted as needed.
> 
> Reset 'errno' to prevent the "errno: No such file or directory (2)"
> generic message.
> 
> Signed-off-by: Mimi Zohar <zohar@linux.ibm.com>
> ---
>   src/evmctl.c    | 16 ++++++++++++++--
>   src/libimaevm.c |  4 ++++
>   src/utils.c     |  5 ++++-
>   3 files changed, 22 insertions(+), 3 deletions(-)
> 

> diff --git a/src/utils.c b/src/utils.c
> index 294dac554392..1026d44776da 100644
> --- a/src/utils.c
> +++ b/src/utils.c
> @@ -11,6 +11,7 @@
>   #include <string.h>
>   #include <sys/stat.h>
>   #include <unistd.h>
> +#include <errno.h>
>   
>   #include "utils.h"
>   
> @@ -26,8 +27,10 @@ static int file_exist(const char *path)
>   {
>   	struct stat st;
>   
> -	if (!access(path, R_OK) && !stat(path, &st) && S_ISREG(st.st_mode))
> +	if (!access(path, R_OK) && !stat(path, &st) && S_ISREG(st.st_mode)) {
> +		errno = 0;

!access and !stat are actually successes, so resetting errno in this 
particular place should not be necessary.

>   		return 1;
> +	}
>   
>   	return 0;
>   }

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH v3 ima-evm-utils 2/3] Sign an fs-verity file digest
  2022-05-20 16:11 ` [PATCH v3 ima-evm-utils 2/3] Sign an fs-verity file digest Mimi Zohar
@ 2022-05-23  0:32   ` Stefan Berger
  0 siblings, 0 replies; 7+ messages in thread
From: Stefan Berger @ 2022-05-23  0:32 UTC (permalink / raw)
  To: Mimi Zohar, linux-integrity; +Cc: Eric Biggers



On 5/20/22 12:11, Mimi Zohar wrote:
> Sign fs-verity file digests provided in the format as produced by
> "fsverity digest".  The output is of the same format as the input,
> but with the file signature appended.  Use setfattr to write the
> signature as security.ima xattr.
> 
> fsverity digest format: <algo>:<hash> <pathname>
> output format: <algo>:<hash> <pathname> <signature>
> 
> Instead of directly signing the fsverity hash, to disambiguate the
> original IMA signatures from the fs-verity signatures stored in the
> security.ima xattr a new signature format version 3 (sigv3) was
> defined as the hash of the xattr type (enum evm_ima_xattr_type),
> the hash algorithm (enum hash_algo), and the hash.
> 
> Example:
> fsverity digest <pathname> | evmctl sign_hash --veritysig \
>   --key <pem encoded private key>
> 
> Signed-off-by: Mimi Zohar <zohar@linux.ibm.com>
> ---
>   README          |  3 +-
>   src/evmctl.c    | 99 +++++++++++++++++++++++++++++++++++++++++--------
>   src/imaevm.h    |  5 ++-
>   src/libimaevm.c | 85 ++++++++++++++++++++++++++++++++++++++++++
>   4 files changed, 175 insertions(+), 17 deletions(-)
> 
> diff --git a/README b/README
> index 5b5ecb52a74b..ffe46ad75728 100644
> --- a/README
> +++ b/README
> @@ -34,7 +34,7 @@ COMMANDS
>    ima_hash file
>    ima_measurement [--ignore-violations] [--verify-sig [--key "key1, key2, ..."]]  [--pcrs [hash-algorithm,]file [--pcrs hash-algorithm,file] ...] file
>    ima_fix [-t fdsxm] path
> - sign_hash [--key key] [--pass password]
> + sign_hash [--veritysig] [--key key] [--pass password]
>    hmac [--imahash | --imasig ] file
>   
>   
> @@ -43,6 +43,7 @@ OPTIONS
>   
>     -a, --hashalgo     sha1, sha224, sha256, sha384, sha512
>     -s, --imasig       make IMA signature
> +      --veritysig    sign an fs-verity file digest hash
>     -d, --imahash      make IMA hash
>     -f, --sigfile      store IMA signature in .sig file instead of xattr
>         --xattr-user   store xattrs in user namespace (for testing purposes)
> diff --git a/src/evmctl.c b/src/evmctl.c
> index 101cd407e05d..c7c8c8cf6e89 100644
> --- a/src/evmctl.c
> +++ b/src/evmctl.c
> @@ -135,6 +135,7 @@ static int msize;
>   static dev_t fs_dev;
>   static bool evm_immutable;
>   static bool evm_portable;
> +static bool veritysig;
>   
>   #define HMAC_FLAG_NO_UUID	0x0001
>   #define HMAC_FLAG_CAPS_SET	0x0002
> @@ -731,33 +732,97 @@ static int cmd_sign_ima(struct command *cmd)
>   	return do_cmd(cmd, sign_ima_file);
>   }
>   
> +/*
> + * Sign file hash(es) provided in the format as produced by either
> + * sha*sum or "fsverity digest".
> + *
> + * sha*sum format: <hash> <pathname>
> + * fsverity digest format: <algo>:<hash> <pathname>
> + *
> + * To disambiguate the resulting file signatures, a new signature format
> + * version 3 (sigv3) was defined as the hash of the xattr type (enum
> + * evm_ima_xattr_type), the hash algorithm (enum hash_algo), and the hash.
> + *
> + * Either directly sign the sha*sum hash or indirectly sign the fsverity
> + * hash (sigv3).
> + *
> + * The output is the same format as the input with the resulting file
> + * signature appended.
> + */
>   static int cmd_sign_hash(struct command *cmd)
>   {
> +	unsigned char sigv3_hash[MAX_DIGEST_SIZE];
> +	unsigned char sig[MAX_SIGNATURE_SIZE];
> +	unsigned char hash[MAX_DIGEST_SIZE];
> +	int siglen, algolen = 0, hashlen = 0;
> +	char *line = NULL, *token, *hashp;
> +	size_t line_len = 0;
>   	const char *key;
> -	char *token, *line = NULL;
> -	int hashlen = 0;
> -	size_t line_len;
> +	char algo[7];	/* Current maximum fsverity hash algo name length */
>   	ssize_t len;
> -	unsigned char hash[MAX_DIGEST_SIZE];
> -	unsigned char sig[MAX_SIGNATURE_SIZE] = "\x03";
> -	int siglen;
> +	int ret;
>   
>   	key = imaevm_params.keyfile ? : "/etc/keys/privkey_evm.pem";
>   
> -	/* support reading hash (eg. output of shasum) */
>   	while ((len = getline(&line, &line_len, stdin)) > 0) {
>   		/* remove end of line */
>   		if (line[len - 1] == '\n')
>   			line[--len] = '\0';
>   
> -		/* find the end of the hash */
> -		token = strpbrk(line, ", \t");
> -		hashlen = token ? token - line : strlen(line);
> +		/*
> +		 * Before either directly or indirectly signing the hash,
> +		 * convert the hex-ascii hash representation to binary.
> +		 */
> +		if (veritysig) {
> +
> +			/* split the algorithm from the hash */
> +			hashp = strpbrk(line, ":");
> +			if (hashp)	/* pointer to the delimiter */
> +				algolen = hashp - line;
> +
> +			if (!hashp || algolen <= 0 ||
> +			    algolen >= sizeof(algo)) {
> +				log_err("Missing/invalid fsverity hash algorithm\n");
> +				continue;
> +			}
> +
> +			strncpy(algo, line, algolen);
> +			algo[algolen] = '\0';	/* Nul terminate algorithm */
> +
> +			hashp++;
> +			token = strpbrk(line, ", \t");
> +			hashlen = token - hashp;
> +			if (hashlen <= 0) {
> +				log_err("Missing fsverity hash\n");
> +				continue;
> +			}
> +
> +			assert(hashlen / 2 <= sizeof(hash));
> +			hex2bin(hash, hashp, hashlen / 2);
> +
> +			ret = calc_hash_sigv3(IMA_VERITY_DIGSIG, algo, hash,
> +					      sigv3_hash);
> +			if (ret < 0 || ret == 1) {
> +				log_info("Failure to calculate fs-verity hash\n");
> +				continue;
> +			}
> +
> +			siglen = sign_hash(algo, sigv3_hash, hashlen / 2,
> +					   key, NULL, sig + 1);
> +
> +			sig[0] = IMA_VERITY_DIGSIG;
> +			sig[1] = DIGSIG_VERSION_3;	/* sigv3 */
> +		} else {
> +			token = strpbrk(line, ", \t");
> +			hashlen = token ? token - line : strlen(line);
> +			assert(hashlen / 2 <= sizeof(hash));
> +			hex2bin(hash, line, hashlen / 2);
> +
> +			siglen = sign_hash(imaevm_params.hash_algo, hash,
> +					   hashlen / 2, key, NULL, sig + 1);
> +			sig[0] = EVM_IMA_XATTR_DIGSIG;
> +		}
>   
> -		assert(hashlen / 2 <= sizeof(hash));
> -		hex2bin(hash, line, hashlen / 2);
> -		siglen = sign_hash(imaevm_params.hash_algo, hash, hashlen / 2,
> -				 key, NULL, sig + 1);
>   		if (siglen <= 1)
>   			return siglen;
>   		assert(siglen < sizeof(sig));
> @@ -2568,7 +2633,7 @@ struct command cmds[] = {
>   	{"ima_boot_aggregate", cmd_ima_bootaggr, 0, "[--pcrs hash-algorithm,file] [TPM 1.2 BIOS event log]", "Calculate per TPM bank boot_aggregate digests\n"},
>   	{"ima_fix", cmd_ima_fix, 0, "[-t fdsxm] path", "Recursively fix IMA/EVM xattrs in fix mode.\n"},
>   	{"ima_clear", cmd_ima_clear, 0, "[-t fdsxm] path", "Recursively remove IMA/EVM xattrs.\n"},
> -	{"sign_hash", cmd_sign_hash, 0, "[--key key] [--pass [password]", "Sign hashes from shaXsum output.\n"},
> +	{"sign_hash", cmd_sign_hash, 0, "[--veritysig] [--key key] [--pass [password]", "Sign hashes from either shaXsum or \"fsverity digest\" output.\n"},

In the readme change above the [--pass password] above looks right. 
While you are at it you could remove the additional '[' here.

>   #ifdef DEBUG
>   	{"hmac", cmd_hmac_evm, 0, "[--imahash | --imasig ] file", "Sign file metadata with HMAC using symmetric key (for testing purpose).\n"},
>   #endif
> @@ -2608,6 +2673,7 @@ static struct option opts[] = {
>   	{"verify-bank", 2, 0, 143},
>   	{"keyid", 1, 0, 144},
>   	{"keyid-from-cert", 1, 0, 145},
> +	{"veritysig", 0, 0, 146},
>   	{}
>   
>   };
> @@ -2838,6 +2904,9 @@ int main(int argc, char *argv[])
>   			}
>   			imaevm_params.keyid = keyid;
>   			break;
> +		case 146:
> +			veritysig = 1;
> +			break;
>   		case '?':
>   			exit(1);
>   			break;
> diff --git a/src/imaevm.h b/src/imaevm.h
> index 0d53a0232ae4..afcf1e042014 100644
> --- a/src/imaevm.h
> +++ b/src/imaevm.h
> @@ -93,6 +93,7 @@ enum evm_ima_xattr_type {
>   	EVM_IMA_XATTR_DIGSIG,
>   	IMA_XATTR_DIGEST_NG,
>   	EVM_XATTR_PORTABLE_DIGSIG,
> +	IMA_VERITY_DIGSIG,
>   };
>   
>   struct h_misc {
> @@ -138,7 +139,8 @@ enum digest_algo {
>   
>   enum digsig_version {
>   	DIGSIG_VERSION_1 = 1,
> -	DIGSIG_VERSION_2
> +	DIGSIG_VERSION_2,
> +	DIGSIG_VERSION_3	/* hash of ima_file_id struct (portion used) */
>   };
>   
>   struct pubkey_hdr {
> @@ -233,5 +235,6 @@ int ima_verify_signature(const char *file, unsigned char *sig, int siglen, unsig
>   void init_public_keys(const char *keyfiles);
>   int imaevm_hash_algo_from_sig(unsigned char *sig);
>   const char *imaevm_hash_algo_by_id(int algo);
> +int calc_hash_sigv3(enum evm_ima_xattr_type type, const char *algo, const unsigned char *in_hash, unsigned char *out_hash);
>   
>   #endif
> diff --git a/src/libimaevm.c b/src/libimaevm.c
> index a4f2ec40684d..af145bd62ac9 100644
> --- a/src/libimaevm.c
> +++ b/src/libimaevm.c
> @@ -497,6 +497,91 @@ err:
>   	return ret;
>   }
>   
> +#define HASH_MAX_DIGESTSIZE 512

SHA 512 has 64 bytes, which seems to be the biggest hash. Also the 
kernel defines this to 64... 512 seems excessive.

> +
> +struct ima_file_id {
> +	__u8 hash_type;		/* xattr type [enum evm_ima_xattr_type] */
> +	__u8 hash_algorithm;	/* Digest algorithm [enum hash_algo] */
> +	__u8 hash[HASH_MAX_DIGESTSIZE];
> +} __packed;
> +
> +/*
> + * Calculate the signature format version 3 hash based on the portion
> + * of the ima_file_id structure used, not the entire structure.
> + *
> + * For openssl errors return 1, other errors return -EINVAL.
> + */
> +int calc_hash_sigv3(enum evm_ima_xattr_type type, const char *algo,
> +		    const unsigned char *in_hash, unsigned char *out_hash)
> +{
> +	struct ima_file_id file_id = { .hash_type = IMA_VERITY_DIGSIG };
> +	uint8_t *data = (uint8_t *) &file_id;
> +
> +	const EVP_MD *md;
> +	EVP_MD_CTX *pctx;
> +	unsigned int mdlen;
> +	int err;
> +#if OPENSSL_VERSION_NUMBER < 0x10100000
> +	EVP_MD_CTX ctx;
> +	pctx = &ctx;
> +#else
> +	pctx = EVP_MD_CTX_new();
> +#endif
> +	int hash_algo;
> +	int hash_size;
> +	unsigned int unused;
> +
> +	if (type != IMA_VERITY_DIGSIG) {
> +		log_err("Only fsverity supports signature format v3 (sigv3)\n");
> +		return -EINVAL;
> +	}
> +
> +	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;
> +
> +	md = EVP_get_digestbyname(algo);
> +	if (!md) {
> +		log_err("EVP_get_digestbyname(%s) failed\n", algo);
> +		err = 1;
> +		goto err;
> +	}
> +
> +	hash_size = EVP_MD_size(md);
> +	memcpy(file_id.hash, in_hash, hash_size);
> +
> +	err = EVP_DigestInit(pctx, md);
> +	if (!err) {
> +		log_err("EVP_DigestInit() failed\n");
> +		err = 1;
> +		goto err;
> +	}
> +
> +	unused = HASH_MAX_DIGESTSIZE - hash_size;
> +	if (!EVP_DigestUpdate(pctx, data, sizeof(file_id) - unused)) {
> +		log_err("EVP_DigestUpdate() failed\n");
> +		err = 1;
> +		goto err;
> +	}
> +
> +	err = EVP_DigestFinal(pctx, out_hash, &mdlen);
> +	if (!err) {
> +		log_err("EVP_DigestFinal() failed\n");
> +		err = 1;
> +		goto err;
> +	}
> +	err = mdlen;
> +err:
> +	if (err == 1)
> +		output_openssl_errors();
> +#if OPENSSL_VERSION_NUMBER >= 0x10100000
> +	EVP_MD_CTX_free(pctx);
> +#endif
> +	return err;
> +}
> +
>   int imaevm_get_hash_algo(const char *algo)
>   {
>   	int i;


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH v3 ima-evm-utils 3/3] Verify an fs-verity file digest based signature
  2022-05-20 16:11 ` [PATCH v3 ima-evm-utils 3/3] Verify an fs-verity file digest based signature Mimi Zohar
@ 2022-05-23  0:39   ` Stefan Berger
  0 siblings, 0 replies; 7+ messages in thread
From: Stefan Berger @ 2022-05-23  0:39 UTC (permalink / raw)
  To: Mimi Zohar, linux-integrity; +Cc: Eric Biggers



On 5/20/22 12:11, 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.
> 
> 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)
> 
> Signed-off-by: Mimi Zohar <zohar@linux.ibm.com>

Reviewed-by: Stefan Berger <stefanb@linux.ibm.com>

> ---
>   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);
>   }
>   
>   /*

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2022-05-23  0:39 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-05-20 16:11 [PATCH v3 ima-evm-utils 0/3] fs-verity file signature support Mimi Zohar
2022-05-20 16:11 ` [PATCH v3 ima-evm-utils 1/3] Reset 'errno' after failure to open or access a file Mimi Zohar
2022-05-22 20:24   ` Stefan Berger
2022-05-20 16:11 ` [PATCH v3 ima-evm-utils 2/3] Sign an fs-verity file digest Mimi Zohar
2022-05-23  0:32   ` Stefan Berger
2022-05-20 16:11 ` [PATCH v3 ima-evm-utils 3/3] Verify an fs-verity file digest based signature Mimi Zohar
2022-05-23  0:39   ` Stefan Berger

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.