All of lore.kernel.org
 help / color / mirror / Atom feed
From: Vitaly Chikunov <vt@altlinux.org>
To: Mimi Zohar <zohar@linux.vnet.ibm.com>,
	Dmitry Kasatkin <dmitry.kasatkin@gmail.com>,
	linux-integrity@vger.kernel.org
Subject: [PATCH ima-evm-utils v9 2/3] Allow manual setting keyid from a cert file
Date: Fri, 16 Jul 2021 18:16:01 +0300	[thread overview]
Message-ID: <20210716151602.3575106-3-vt@altlinux.org> (raw)
In-Reply-To: <20210716151602.3575106-1-vt@altlinux.org>

Allow user to specify `--keyid-from-cert cert.pem' to extract keyid from
SKID of the certificate file. PEM or DER format is auto-detected.

This commit creates ABI change for libimaevm, due to adding new function
ima_read_keyid(). Newer clients cannot work with older libimaevm.
Together with previous commit it creates backward-incompatible ABI
change, thus soname should be incremented on release.

Signed-off-by: Vitaly Chikunov <vt@altlinux.org>
---
 README                 |   2 +
 src/evmctl.c           |  14 +++++
 src/imaevm.h           |   1 +
 src/libimaevm.c        | 114 +++++++++++++++++++++++++++++++++++++++++
 tests/sign_verify.test |   1 +
 5 files changed, 132 insertions(+)

diff --git a/README b/README
index 20fa009..a130519 100644
--- a/README
+++ b/README
@@ -49,6 +49,8 @@ OPTIONS
       --rsa          use RSA key type and signing scheme v1
   -k, --key          path to signing key (default: /etc/keys/{privkey,pubkey}_evm.pem)
       --keyid n      overwrite signature keyid with a 32-bit value in hex (for signing)
+      --keyid-from-cert file
+                     read keyid value from SKID of a x509 cert file
   -o, --portable     generate portable EVM signatures
   -p, --pass         password for encrypted signing key
   -r, --recursive    recurse into directories (sign)
diff --git a/src/evmctl.c b/src/evmctl.c
index f272250..78be387 100644
--- a/src/evmctl.c
+++ b/src/evmctl.c
@@ -42,6 +42,7 @@
 #include <sys/param.h>
 #include <sys/stat.h>
 #include <sys/ioctl.h>
+#include <arpa/inet.h>
 #include <fcntl.h>
 #include <unistd.h>
 #include <stdlib.h>
@@ -57,12 +58,14 @@
 #include <termios.h>
 #include <assert.h>
 
+#include <openssl/asn1.h>
 #include <openssl/sha.h>
 #include <openssl/pem.h>
 #include <openssl/hmac.h>
 #include <openssl/err.h>
 #include <openssl/rsa.h>
 #include <openssl/engine.h>
+#include <openssl/x509v3.h>
 #include "hash_info.h"
 #include "pcr.h"
 #include "utils.h"
@@ -2515,6 +2518,8 @@ static void usage(void)
 		"      --rsa          use RSA key type and signing scheme v1\n"
 		"  -k, --key          path to signing key (default: /etc/keys/{privkey,pubkey}_evm.pem)\n"
 		"      --keyid n      overwrite signature keyid with a 32-bit value in hex (for signing)\n"
+		"      --keyid-from-cert file\n"
+		"                     read keyid value from SKID of a x509 cert file\n"
 		"  -o, --portable     generate portable EVM signatures\n"
 		"  -p, --pass         password for encrypted signing key\n"
 		"  -r, --recursive    recurse into directories (sign)\n"
@@ -2596,6 +2601,7 @@ static struct option opts[] = {
 	{"pcrs", 1, 0, 142},
 	{"verify-bank", 2, 0, 143},
 	{"keyid", 1, 0, 144},
+	{"keyid-from-cert", 1, 0, 145},
 	{}
 
 };
@@ -2805,6 +2811,14 @@ int main(int argc, char *argv[])
 			}
 			imaevm_params.keyid = keyid;
 			break;
+		case 145:
+			keyid = imaevm_read_keyid(optarg);
+			if (keyid == 0) {
+				log_err("Error reading keyid.\n");
+				exit(1);
+			}
+			imaevm_params.keyid = keyid;
+			break;
 		case '?':
 			exit(1);
 			break;
diff --git a/src/imaevm.h b/src/imaevm.h
index fe244f1..491f136 100644
--- a/src/imaevm.h
+++ b/src/imaevm.h
@@ -219,6 +219,7 @@ EVP_PKEY *read_pub_pkey(const char *keyfile, int x509);
 void calc_keyid_v1(uint8_t *keyid, char *str, const unsigned char *pkey, int len);
 void calc_keyid_v2(uint32_t *keyid, char *str, EVP_PKEY *pkey);
 int key2bin(RSA *key, unsigned char *pub);
+uint32_t imaevm_read_keyid(const char *certfile);
 
 int sign_hash(const char *algo, const unsigned char *hash, int size, const char *keyfile, const char *keypass, unsigned char *sig);
 int verify_hash(const char *file, const unsigned char *hash, int size, unsigned char *sig, int siglen);
diff --git a/src/libimaevm.c b/src/libimaevm.c
index c59082b..ce1e276 100644
--- a/src/libimaevm.c
+++ b/src/libimaevm.c
@@ -53,10 +53,12 @@
 #include <assert.h>
 #include <ctype.h>
 
+#include <openssl/asn1.h>
 #include <openssl/crypto.h>
 #include <openssl/pem.h>
 #include <openssl/evp.h>
 #include <openssl/x509.h>
+#include <openssl/x509v3.h>
 #include <openssl/err.h>
 
 #include "imaevm.h"
@@ -745,6 +747,118 @@ void calc_keyid_v2(uint32_t *keyid, char *str, EVP_PKEY *pkey)
 	X509_PUBKEY_free(pk);
 }
 
+/*
+ * Extract SKID from x509 in openssl portable way.
+ */
+static const unsigned char *x509_get_skid(X509 *x, int *len)
+{
+#if OPENSSL_VERSION_NUMBER < 0x10100000
+	ASN1_STRING *skid;
+
+	/*
+	 * This will cache extensions.
+	 * OpenSSL uses this method itself.
+	 */
+	if (X509_check_purpose(x, -1, -1) != 1)
+		return NULL;
+	skid = x->skid;
+#else
+	const ASN1_OCTET_STRING *skid = X509_get0_subject_key_id(x);
+#endif
+	if (len)
+		*len = ASN1_STRING_length(skid);
+#if OPENSSL_VERSION_NUMBER < 0x10100000
+	return ASN1_STRING_data(x->skid);
+#else
+	return ASN1_STRING_get0_data(skid);
+#endif
+}
+
+/*
+ * read_keyid_from_cert() - Read keyid from SKID from x509 certificate file
+ * @keyid_be:	Output 32-bit keyid in network order (BE);
+ * @certfile:	Input filename.
+ * @try_der:	true:  try to read in DER from if there is no PEM,
+ *		       cert is considered mandatory and error will be issued
+ *		       if there is no cert;
+ *		false: only try to read in PEM form, cert is considered
+ *		       optional.
+ * Return:	0 on success, -1 on error.
+ */
+static int read_keyid_from_cert(uint32_t *keyid_be, const char *certfile, int try_der)
+{
+	X509 *x = NULL;
+	FILE *fp;
+	const unsigned char *skid;
+	int skid_len;
+
+	if (!(fp = fopen(certfile, "r"))) {
+		log_err("Cannot open %s: %s\n", certfile, strerror(errno));
+		return -1;
+	}
+	if (!PEM_read_X509(fp, &x, NULL, NULL)) {
+		if (ERR_GET_REASON(ERR_peek_last_error()) == PEM_R_NO_START_LINE) {
+			ERR_clear_error();
+			if (try_der) {
+				rewind(fp);
+				d2i_X509_fp(fp, &x);
+			} else {
+				/*
+				 * Cert is optional and there is just no PEM
+				 * header, then issue debug message and stop
+				 * trying.
+				 */
+				log_debug("%s: x509 certificate not found\n",
+					  certfile);
+				fclose(fp);
+				return -1;
+			}
+		}
+	}
+	fclose(fp);
+	if (!x) {
+		ERR_print_errors_fp(stderr);
+		log_err("read keyid: %s: Error reading x509 certificate\n",
+			certfile);
+	}
+
+	if (!(skid = x509_get_skid(x, &skid_len))) {
+		log_err("read keyid: %s: SKID not found\n", certfile);
+		goto err_free;
+	}
+	if (skid_len < sizeof(*keyid_be)) {
+		log_err("read keyid: %s: SKID too short (len %d)\n", certfile,
+			skid_len);
+		goto err_free;
+	}
+	memcpy(keyid_be, skid + skid_len - sizeof(*keyid_be), sizeof(*keyid_be));
+	log_info("keyid %04x (from %s)\n", ntohl(*keyid_be), certfile);
+	X509_free(x);
+	return 0;
+
+err_free:
+	X509_free(x);
+	return -1;
+}
+
+/*
+ * imaevm_read_keyid() - Read 32-bit keyid from the cert file
+ * @certfile:	File with certificate in PEM or DER form.
+ *
+ * Try to read keyid from Subject Key Identifier (SKID) of x509 certificate.
+ * Autodetect if cert is in PEM (tried first) or DER encoding.
+ *
+ * Return: 0 on error or 32-bit keyid in host order otherwise.
+ */
+uint32_t imaevm_read_keyid(const char *certfile)
+{
+	uint32_t keyid_be = 0;
+
+	read_keyid_from_cert(&keyid_be, certfile, true);
+	/* On error keyid_be will not be set, returning 0. */
+	return ntohl(keyid_be);
+}
+
 static EVP_PKEY *read_priv_pkey(const char *keyfile, const char *keypass)
 {
 	FILE *fp;
diff --git a/tests/sign_verify.test b/tests/sign_verify.test
index eccf5fa..1fdd786 100755
--- a/tests/sign_verify.test
+++ b/tests/sign_verify.test
@@ -366,6 +366,7 @@ sign_verify  rsa1024  md5     0x030201:K:0080
 sign_verify  rsa1024  sha1    0x030202:K:0080
 sign_verify  rsa1024  sha224  0x030207:K:0080
 expect_pass check_sign TYPE=ima KEY=rsa1024 ALG=sha256 PREFIX=0x030204aabbccdd0080 OPTS=--keyid=aabbccdd
+expect_pass check_sign TYPE=ima KEY=rsa1024 ALG=sha256 PREFIX=0x030204:K:0080 OPTS=--keyid-from-cert=test-rsa1024.cer
 sign_verify  rsa1024  sha256  0x030204:K:0080
   try_different_keys
   try_different_sigs
-- 
2.29.3


  parent reply	other threads:[~2021-07-16 15:16 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-07-16 15:15 [PATCH ima-evm-utils v9 0/3] ima-evm-utils: Add --keyid option Vitaly Chikunov
2021-07-16 15:16 ` [PATCH ima-evm-utils v9 1/3] Allow manual setting keyid for signing Vitaly Chikunov
2021-07-16 15:16 ` Vitaly Chikunov [this message]
2021-07-16 15:16 ` [PATCH ima-evm-utils v9 3/3] Read keyid from the cert appended to the key file Vitaly Chikunov
2021-07-16 21:58 ` [PATCH ima-evm-utils v9 0/3] ima-evm-utils: Add --keyid option Mimi Zohar

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20210716151602.3575106-3-vt@altlinux.org \
    --to=vt@altlinux.org \
    --cc=dmitry.kasatkin@gmail.com \
    --cc=linux-integrity@vger.kernel.org \
    --cc=zohar@linux.vnet.ibm.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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.