[PATCH 3/3] x509: add CRL signature verification support

Timofei Novikov <[email protected]>
Newsgroups org.kernel.vger.keyrings,org.kernel.vger.linux-crypto
Message-ID <[email protected]>
Verify the digital signature of Certificate Revocation Lists (CRLs)
before processing their entries.  Extract signature parameters via
x509_get_sig_params() and preserve the TBS data during parsing to
enable signature verification with verify_signature().

Add the keyring parameter to x509_load_crl_list() to specify which
keyring to use for CRL issuer lookup.  If no keyring is provided,
signature verification is skipped.

Signed-off-by: Timofei Novikov <[email protected]>
---
 crypto/asymmetric_keys/x509_cert_parser.c | 11 ++++++++
 crypto/asymmetric_keys/x509_crl.asn1      |  2 +-
 crypto/asymmetric_keys/x509_loader.c      | 31 ++++++++++++++++++++++-
 crypto/asymmetric_keys/x509_parser.h      |  2 +-
 include/keys/asymmetric-type.h            |  3 ++-
 5 files changed, 45 insertions(+), 4 deletions(-)

diff --git a/crypto/asymmetric_keys/x509_cert_parser.c b/crypto/asymmetric_keys/x509_cert_parser.c
index 76b61f36c082..39ef4cf4b05d 100644
--- a/crypto/asymmetric_keys/x509_cert_parser.c
+++ b/crypto/asymmetric_keys/x509_cert_parser.c
@@ -907,14 +907,24 @@ struct x509_crl_context *x509_crl_parse(const void *data, size_t datalen)
 	if (ret < 0)
 		return ERR_PTR(ret);
 
+	/*
+	 * Extract signature parameters (sig->s, sig->digest, etc.)
+	 * This does what we previously tried to do manually.
+	 */
+	ret = x509_get_sig_params(cert);
+	if (ret < 0)
+		return ERR_PTR(ret);
+
 	/* Transfer parsed data to crl_context */
 	INIT_LIST_HEAD(&crl_ctx->revoked_list);
 	crl_ctx->indirect_crl = ctx->indirect_crl;
 	crl_ctx->raw_issuer = cert->raw_issuer;
 	crl_ctx->raw_issuer_size = cert->raw_issuer_size;
+	crl_ctx->sig = cert->sig;
 	list_splice(&ctx->revoked_list, &crl_ctx->revoked_list);
 
 	/* Detach pointers from cert before auto-free */
+	cert->sig = NULL;
 	cert->raw_issuer = NULL;
 	cert->raw_serial = NULL;
 
@@ -930,6 +940,7 @@ void x509_crl_free(struct x509_crl_context *ctx)
 		kfree(entry->serial);
 		kfree(entry);
 	}
+	public_key_signature_free(ctx->sig);
 	kfree(ctx);
 }
 
diff --git a/crypto/asymmetric_keys/x509_crl.asn1 b/crypto/asymmetric_keys/x509_crl.asn1
index 3ec8f5a32aca..910af624d828 100644
--- a/crypto/asymmetric_keys/x509_crl.asn1
+++ b/crypto/asymmetric_keys/x509_crl.asn1
@@ -16,7 +16,7 @@ TBSCertList ::= SEQUENCE {
 	nextUpdate		Time OPTIONAL ({ x509_note_not_after }) ,
 	revokedCertificates	SEQUENCE OF RevokedCertificate OPTIONAL,
 	crlExtensions		[ 0 ] Extensions OPTIONAL
-	}
+	} ({ x509_note_tbs_certificate })
 
 RevokedCertificate ::= SEQUENCE {
 	userCertificate		CertificateSerialNumber ({ crl_note_serial }),
diff --git a/crypto/asymmetric_keys/x509_loader.c b/crypto/asymmetric_keys/x509_loader.c
index 344e6bf31612..7c1a098b2ecb 100644
--- a/crypto/asymmetric_keys/x509_loader.c
+++ b/crypto/asymmetric_keys/x509_loader.c
@@ -6,6 +6,7 @@
 #include <keys/system_keyring.h>
 #include <linux/slab.h>
 #include <crypto/sha2.h>
+#include <crypto/public_key.h>
 #include "x509_parser.h"
 
 int x509_load_certificate_list(const u8 cert_list[],
@@ -74,7 +75,30 @@ static const void *crl_entry_issuer(const struct x509_crl_context *crl,
 	return crl->raw_issuer;
 }
 
-int x509_load_crl_list(const u8 crl_list[], const unsigned long list_size)
+static int x509_crl_verify_signature(struct x509_crl_context *crl_ctx,
+				     const struct key *keyring)
+{
+	struct key *key;
+	int ret = -ENOKEY;
+
+	if (!crl_ctx->raw_issuer || !crl_ctx->sig)
+		return -ENOKEY;
+
+	key = find_asymmetric_key(keyring,
+				  crl_ctx->sig->auth_ids[0],
+				  crl_ctx->sig->auth_ids[1],
+				  crl_ctx->sig->auth_ids[2],
+				  false);
+	if (IS_ERR(key))
+		return PTR_ERR(key);
+
+	ret = verify_signature(key, crl_ctx->sig);
+	key_put(key);
+	return ret;
+}
+
+int x509_load_crl_list(const u8 crl_list[], const unsigned long list_size,
+		       const struct key *keyring)
 {
 	const u8 *p = crl_list, *end = p + list_size;
 
@@ -107,6 +131,11 @@ int x509_load_crl_list(const u8 crl_list[], const unsigned long list_size)
 			pr_err("Problem parsing CRL (%ld)\n", PTR_ERR(crl_ctx));
 			goto next_crl;
 		}
+		if (keyring && x509_crl_verify_signature(crl_ctx, keyring) < 0) {
+			pr_warn("CRL signature verification failed, skipping\n");
+			x509_crl_free(crl_ctx);
+			goto next_crl;
+		}
 		list_for_each_entry_safe(entry, tmp, &crl_ctx->revoked_list, list) {
 			struct asymmetric_key_id *kid;
 			const void *issuer;
diff --git a/crypto/asymmetric_keys/x509_parser.h b/crypto/asymmetric_keys/x509_parser.h
index 13cc0bc3d7e2..7c48ccb1008c 100644
--- a/crypto/asymmetric_keys/x509_parser.h
+++ b/crypto/asymmetric_keys/x509_parser.h
@@ -49,9 +49,9 @@ struct x509_certificate {
 struct x509_crl_context {
 	const void *raw_issuer;
 	size_t raw_issuer_size;
+	struct public_key_signature *sig;
 	struct list_head revoked_list;
 	bool indirect_crl;
-	u8 crl_reason;
 };
 
 struct x509_revoked_entry {
diff --git a/include/keys/asymmetric-type.h b/include/keys/asymmetric-type.h
index 635644c8dcf1..db586a4c2981 100644
--- a/include/keys/asymmetric-type.h
+++ b/include/keys/asymmetric-type.h
@@ -87,7 +87,8 @@ extern struct key *find_asymmetric_key(struct key *keyring,
 int x509_load_certificate_list(const u8 cert_list[], const unsigned long list_size,
 			       const struct key *keyring);
 #ifdef CONFIG_SYSTEM_BLACKLIST_KEYRING
-int x509_load_crl_list(const u8 crl_list[], const unsigned long list_size);
+int x509_load_crl_list(const u8 crl_list[], const unsigned long list_size,
+		       const struct key *keyring);
 #endif
 
 /*
-- 
2.43.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.