mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: David Howells <dhowells@redhat.com>
To: mcgrof@gmail.com
Cc: mjg59@srcf.ucam.org, keyrings@linux-nfs.org,
	gregkh@linuxfoundation.org, kyle@kernel.org,
	linux-wireless@vger.kernel.org, linux-kernel@vger.kernel.org,
	seth.forshee@canonical.com,
	linux-security-module@vger.kernel.org, zohar@linux.vnet.ibm.com,
	dwmw2@infradead.org
Subject: [PATCH 24/27] X.509: Restrict the usage of a key based on information in X.509 certificate
Date: Fri, 17 Jul 2015 17:19:39 +0100	[thread overview]
Message-ID: <20150717161939.2578.78401.stgit@warthog.procyon.org.uk> (raw)
In-Reply-To: <20150717161456.2578.95678.stgit@warthog.procyon.org.uk>

Use X.509 extendedKeyUsage extension [RFC5280 4.2.1.12] to hold restriction
information as to the purpose of the key.  The following changes are made:

 (1) The kernel's X.509 parser is modified to extract this information and
     stash it in the public_key struct.

 (2) The kernel indicates in /proc/keys the restriction if one is found.

 (3) Autogenerated module signing key certificates are marked with a module
     signing only restriction.

The extendedKeyUsage extension takes a sequence of OIDs to indicate the set
of restricted cases.  To this end, I have assigned three OIDs in Red Hat's
OID space:

	1.3.6.1.4.1.2312.16	Kernel OIDs
	1.3.6.1.4.1.2312.16.1	- X.509 extendedKeyUsage restriction set
	1.3.6.1.4.1.2312.16.1.1	  - Firmware signing only
	1.3.6.1.4.1.2312.16.1.2	  - Module signing only
	1.3.6.1.4.1.2312.16.1.3	  - Kexecable image signing only

I would propose a fourth, key signing only, but that should perhaps be
handled through the keyUsage extension [RFC5280 4.2.1.3] setting
keyCertSign.  We might also add file signing only and IMA/Integrity signing
only restrictions.

I am treating these as mutually exclusive.  A key with a restriction is
rejected if it also gives a second restriction.


To mark a key as being for firmware signing only, for example, the "openssl
req" command can be given an extension specifier to mark the X.509
certificate.  Assuming a config script is used, this would be done by
including the following in the extension list:

	extendedKeyUsage=critical,1.3.6.1.4.1.2312.16.1.1

This adds it to the extendedKeyUsage extension.  Another, perhaps more
convenient way to do it would be to add our own extension type, eg:

	1.3.6.1.4.1.2312.16.1.1=critical,ASN1:NULL

This would easier to deal with since we examine all the extensions anyway,
and we could parameterise it, but the first option is probably the correct
way.

Also, do we need to break the firmware restriction space down by class or
manufacturer?  Or will one restriction do?

Signed-off-by: David Howells <dhowells@redhat.com>
---

 crypto/asymmetric_keys/Makefile           |    4 ++
 crypto/asymmetric_keys/asymmetric_type.c  |    3 +
 crypto/asymmetric_keys/public_key.c       |   23 +++++++++
 crypto/asymmetric_keys/x509_cert_parser.c |   72 +++++++++++++++++++++++++++++
 crypto/asymmetric_keys/x509_extusage.asn1 |    3 +
 include/crypto/public_key.h               |   16 ++++++
 include/keys/asymmetric-subtype.h         |    3 +
 include/linux/oid_registry.h              |    5 ++
 kernel/Makefile                           |    2 +
 lib/oid_registry.c                        |    1 
 10 files changed, 131 insertions(+), 1 deletion(-)
 create mode 100644 crypto/asymmetric_keys/x509_extusage.asn1

diff --git a/crypto/asymmetric_keys/Makefile b/crypto/asymmetric_keys/Makefile
index cd1406f9b14a..ac18b7f64a77 100644
--- a/crypto/asymmetric_keys/Makefile
+++ b/crypto/asymmetric_keys/Makefile
@@ -16,6 +16,7 @@ obj-$(CONFIG_X509_CERTIFICATE_PARSER) += x509_key_parser.o
 x509_key_parser-y := \
 	x509-asn1.o \
 	x509_akid-asn1.o \
+	x509_extusage-asn1.o \
 	x509_rsakey-asn1.o \
 	x509_cert_parser.o \
 	x509_public_key.o
@@ -23,13 +24,16 @@ x509_key_parser-y := \
 $(obj)/x509_cert_parser.o: \
 	$(obj)/x509-asn1.h \
 	$(obj)/x509_akid-asn1.h \
+	$(obj)/x509_extusage-asn1.h \
 	$(obj)/x509_rsakey-asn1.h
 $(obj)/x509-asn1.o: $(obj)/x509-asn1.c $(obj)/x509-asn1.h
 $(obj)/x509_akid-asn1.o: $(obj)/x509_akid-asn1.c $(obj)/x509_akid-asn1.h
+$(obj)/x509_extusage-asn1.o: $(obj)/x509_extusage-asn1.c $(obj)/x509_extusage-asn1.h
 $(obj)/x509_rsakey-asn1.o: $(obj)/x509_rsakey-asn1.c $(obj)/x509_rsakey-asn1.h
 
 clean-files	+= x509-asn1.c x509-asn1.h
 clean-files	+= x509_akid-asn1.c x509_akid-asn1.h
+clean-files	+= x509_extusage-asn1.c x509_extusage-asn1.h
 clean-files	+= x509_rsakey-asn1.c x509_rsakey-asn1.h
 
 #
diff --git a/crypto/asymmetric_keys/asymmetric_type.c b/crypto/asymmetric_keys/asymmetric_type.c
index b0e4ed23d668..411deb91ee70 100644
--- a/crypto/asymmetric_keys/asymmetric_type.c
+++ b/crypto/asymmetric_keys/asymmetric_type.c
@@ -254,7 +254,8 @@ static void asymmetric_key_describe(const struct key *key, struct seq_file *m)
 		}
 
 		seq_puts(m, " [");
-		/* put something here to indicate the key's capabilities */
+		if (subtype->describe_caps)
+			subtype->describe_caps(key, m);
 		seq_putc(m, ']');
 	}
 }
diff --git a/crypto/asymmetric_keys/public_key.c b/crypto/asymmetric_keys/public_key.c
index 2f6e4fb1a1ea..b627dc35f0aa 100644
--- a/crypto/asymmetric_keys/public_key.c
+++ b/crypto/asymmetric_keys/public_key.c
@@ -42,6 +42,16 @@ const char *const pkey_id_type_name[PKEY_ID_TYPE__LAST] = {
 };
 EXPORT_SYMBOL_GPL(pkey_id_type_name);
 
+static const char *const key_usage_restrictions[NR__KEY_USAGE_RESTRICTIONS] = {
+	[KEY_USAGE_NOT_SPECIFIED]		= "unrestricted",
+	[KEY_RESTRICTED_USAGE]			= "unspecified",
+	[KEY_RESTRICTED_TO_OTHER]		= "other use",
+	[KEY_RESTRICTED_TO_MODULE_SIGNING]	= "module sig",
+	[KEY_RESTRICTED_TO_FIRMWARE_SIGNING]	= "firmware sig",
+	[KEY_RESTRICTED_TO_KEXEC_SIGNING]	= "kexec sig",
+	[KEY_RESTRICTED_TO_KEY_SIGNING]		= "key sig",
+};
+
 /*
  * Provide a part of a description of the key for /proc/keys.
  */
@@ -56,6 +66,18 @@ static void public_key_describe(const struct key *asymmetric_key,
 }
 
 /*
+ * Describe capabilities/restrictions of the key for /proc/keys.
+ */
+static void public_key_describe_caps(const struct key *asymmetric_key,
+				     struct seq_file *m)
+{
+	struct public_key *key = asymmetric_key->payload.data;
+
+	if (key)
+		seq_puts(m, key_usage_restrictions[key->usage_restriction]);
+}
+
+/*
  * Destroy a public key algorithm key.
  */
 void public_key_destroy(void *payload)
@@ -123,6 +145,7 @@ struct asymmetric_key_subtype public_key_subtype = {
 	.name			= "public_key",
 	.name_len		= sizeof("public_key") - 1,
 	.describe		= public_key_describe,
+	.describe_caps		= public_key_describe_caps,
 	.destroy		= public_key_destroy,
 	.verify_signature	= public_key_verify_signature_2,
 };
diff --git a/crypto/asymmetric_keys/x509_cert_parser.c b/crypto/asymmetric_keys/x509_cert_parser.c
index 849fd760923e..da2fa399f859 100644
--- a/crypto/asymmetric_keys/x509_cert_parser.c
+++ b/crypto/asymmetric_keys/x509_cert_parser.c
@@ -19,6 +19,7 @@
 #include "x509_parser.h"
 #include "x509-asn1.h"
 #include "x509_akid-asn1.h"
+#include "x509_extusage-asn1.h"
 #include "x509_rsakey-asn1.h"
 
 struct x509_parse_context {
@@ -40,6 +41,8 @@ struct x509_parse_context {
 	const void	*raw_akid;		/* Raw authorityKeyId in ASN.1 */
 	const void	*akid_raw_issuer;	/* Raw directoryName in authorityKeyId */
 	unsigned	akid_raw_issuer_size;
+	unsigned	raw_extusage_size;
+	const void	*raw_extusage;		/* Raw extKeyUsage in ASN.1 */
 };
 
 /*
@@ -91,6 +94,20 @@ struct x509_certificate *x509_cert_parse(const void *data, size_t datalen)
 	if (ret < 0)
 		goto error_decode;
 
+	/* Decode the extended key usage information */
+	if (ctx->raw_extusage) {
+		pr_devel("EXTUSAGE: %u %*phN\n",
+			 ctx->raw_extusage_size, ctx->raw_extusage_size,
+			 ctx->raw_extusage);
+		ret = asn1_ber_decoder(&x509_extusage_decoder, ctx,
+				       ctx->raw_extusage,
+				       ctx->raw_extusage_size);
+		if (ret < 0) {
+			pr_warn("Couldn't decode extKeyUsage\n");
+			goto error_decode;
+		}
+	}
+
 	/* Decode the AuthorityKeyIdentifier */
 	if (ctx->raw_akid) {
 		pr_devel("AKID: %u %*phN\n",
@@ -469,6 +486,14 @@ int x509_process_extension(void *context, size_t hdrlen,
 		return 0;
 	}
 
+	if (ctx->last_oid == OID_extKeyUsage) {
+		/* Get hold of the extended key usage information */
+		ctx->raw_extusage = v;
+		ctx->raw_extusage_size = vlen;
+		ctx->cert->pub->usage_restriction = KEY_RESTRICTED_USAGE;
+		return 0;
+	}
+
 	return 0;
 }
 
@@ -601,3 +626,50 @@ int x509_akid_note_serial(void *context, size_t hdrlen,
 	ctx->cert->akid_id = kid;
 	return 0;
 }
+
+/*
+ * Note restriction to a purpose
+ */
+int x509_extusage_note_purpose(void *context, size_t hdrlen,
+			       unsigned char tag,
+			       const void *value, size_t vlen)
+{
+	struct x509_parse_context *ctx = context;
+	enum key_usage_restriction restriction;
+	char buffer[50];
+	enum OID oid;
+
+	sprint_oid(value, vlen, buffer, sizeof(buffer));
+	pr_debug("ExtUsage: %s\n", buffer);
+
+	oid = look_up_OID(value, vlen);
+	if (oid == OID__NR) {
+		pr_debug("Unknown extension: [%lu] %s\n",
+			 (unsigned long)value - ctx->data, buffer);
+		return 0;
+	}
+
+	switch (oid) {
+	case OID_firmwareSigningOnlyKey:
+		restriction = KEY_RESTRICTED_TO_FIRMWARE_SIGNING;
+		break;
+	case OID_moduleSigningOnlyKey:
+		restriction = KEY_RESTRICTED_TO_MODULE_SIGNING;
+		break;
+	case OID_kexecSigningOnlyKey:
+		restriction = KEY_RESTRICTED_TO_KEXEC_SIGNING;
+		break;
+	default:
+		restriction = KEY_RESTRICTED_TO_OTHER;
+		break;
+	}
+
+	if (ctx->cert->pub->usage_restriction != KEY_RESTRICTED_USAGE) {
+		pr_warn("Rejecting certificate with multiple restrictions\n");
+		return -EKEYREJECTED;
+	}
+
+	ctx->cert->pub->usage_restriction = restriction;
+	pr_debug("usage restriction %u\n", restriction);
+	return 0;
+}
diff --git a/crypto/asymmetric_keys/x509_extusage.asn1 b/crypto/asymmetric_keys/x509_extusage.asn1
new file mode 100644
index 000000000000..ffe12ae644b5
--- /dev/null
+++ b/crypto/asymmetric_keys/x509_extusage.asn1
@@ -0,0 +1,3 @@
+ExtKeyUsageSyntax ::= SEQUENCE OF KeyPurposeId
+
+KeyPurposeId ::= OBJECT IDENTIFIER ({ x509_extusage_note_purpose })
diff --git a/include/crypto/public_key.h b/include/crypto/public_key.h
index fda097e079a4..afcab5f40559 100644
--- a/include/crypto/public_key.h
+++ b/include/crypto/public_key.h
@@ -40,6 +40,21 @@ enum pkey_id_type {
 extern const char *const pkey_id_type_name[PKEY_ID_TYPE__LAST];
 
 /*
+ * The use to which an asymmetric key is restricted.
+ */
+enum key_usage_restriction {
+	KEY_USAGE_NOT_SPECIFIED,
+	KEY_RESTRICTED_USAGE,
+	KEY_RESTRICTED_TO_OTHER,
+	KEY_RESTRICTED_TO_MODULE_SIGNING,
+	KEY_RESTRICTED_TO_FIRMWARE_SIGNING,
+	KEY_RESTRICTED_TO_KEXEC_SIGNING,
+	KEY_RESTRICTED_TO_KEY_SIGNING,
+	NR__KEY_USAGE_RESTRICTIONS
+};
+
+
+/*
  * Cryptographic data for the public-key subtype of the asymmetric key type.
  *
  * Note that this may include private part of the key as well as the public
@@ -52,6 +67,7 @@ struct public_key {
 #define PKEY_CAN_DECRYPT	0x02
 #define PKEY_CAN_SIGN		0x04
 #define PKEY_CAN_VERIFY		0x08
+	enum key_usage_restriction usage_restriction : 8;
 	enum pkey_algo pkey_algo : 8;
 	enum pkey_id_type id_type : 8;
 	union {
diff --git a/include/keys/asymmetric-subtype.h b/include/keys/asymmetric-subtype.h
index 4b840e822209..b6a47f09ef2b 100644
--- a/include/keys/asymmetric-subtype.h
+++ b/include/keys/asymmetric-subtype.h
@@ -31,6 +31,9 @@ struct asymmetric_key_subtype {
 	/* Describe a key of this subtype for /proc/keys */
 	void (*describe)(const struct key *key, struct seq_file *m);
 
+	/* Describe capabilities/restrictions of a key of this subtype */
+	void (*describe_caps)(const struct key *key, struct seq_file *m);
+
 	/* Destroy a key of this subtype */
 	void (*destroy)(void *payload);
 
diff --git a/include/linux/oid_registry.h b/include/linux/oid_registry.h
index c2bbf672b84e..1af63a2616a8 100644
--- a/include/linux/oid_registry.h
+++ b/include/linux/oid_registry.h
@@ -88,6 +88,11 @@ enum OID {
 	OID_authorityKeyIdentifier,	/* 2.5.29.35 */
 	OID_extKeyUsage,		/* 2.5.29.37 */
 
+	/* Red Hat-space kernel OIDs for X.509 extendedKeyUsage */
+	OID_firmwareSigningOnlyKey,	/* 1.3.6.1.4.1.2312.16.1.1 */
+	OID_moduleSigningOnlyKey,	/* 1.3.6.1.4.1.2312.16.1.2 */
+	OID_kexecSigningOnlyKey,	/* 1.3.6.1.4.1.2312.16.1.3 */
+
 	OID__NR
 };
 
diff --git a/kernel/Makefile b/kernel/Makefile
index 9e31922138d0..5827b4247016 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -238,6 +238,8 @@ x509.genkey:
 	@echo >>x509.genkey "keyUsage=digitalSignature"
 	@echo >>x509.genkey "subjectKeyIdentifier=hash"
 	@echo >>x509.genkey "authorityKeyIdentifier=keyid"
+	@echo >>x509.genkey "# Set moduleSigningOnlyKey restriction"
+	@echo >>x509.genkey "extendedKeyUsage=critical,1.3.6.1.4.1.2312.16.1.2"
 endif
 
 $(eval $(call config_filename,MODULE_SIG_KEY))
diff --git a/lib/oid_registry.c b/lib/oid_registry.c
index 318f382a010d..41df9b4a5fe7 100644
--- a/lib/oid_registry.c
+++ b/lib/oid_registry.c
@@ -115,6 +115,7 @@ int sprint_oid(const void *data, size_t datasize, char *buffer, size_t bufsize)
 	size_t ret;
 	int count;
 
+	buffer[0] = 0;
 	if (v >= end)
 		return -EBADMSG;
 


  parent reply	other threads:[~2015-07-17 16:19 UTC|newest]

Thread overview: 28+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-07-17 16:14 [PATCH 00/27] MODSIGN: Use PKCS#7 for module signatures David Howells
2015-07-17 16:15 ` [PATCH 01/27] X.509: Extract both parts of the AuthorityKeyIdentifier David Howells
2015-07-17 16:15 ` [PATCH 02/27] X.509: Support X.509 lookup by Issuer+Serial form AuthorityKeyIdentifier David Howells
2015-07-17 16:15 ` [PATCH 03/27] PKCS#7: Allow detached data to be supplied for signature checking purposes David Howells
2015-07-17 16:15 ` [PATCH 04/27] MODSIGN: Provide a utility to append a PKCS#7 signature to a module David Howells
2015-07-17 16:15 ` [PATCH 05/27] MODSIGN: Use PKCS#7 messages as module signatures David Howells
2015-07-17 16:16 ` [PATCH 06/27] sign-file: Add option to only create signature file David Howells
2015-07-17 16:16 ` [PATCH 07/27] system_keyring.c doesn't need to #include module-internal.h David Howells
2015-07-17 16:16 ` [PATCH 08/27] MODSIGN: Extract the blob PKCS#7 signature verifier from module signing David Howells
2015-07-17 16:16 ` [PATCH 09/27] modsign: Abort modules_install when signing fails David Howells
2015-07-17 16:16 ` [PATCH 10/27] modsign: Allow password to be specified for signing key David Howells
2015-07-17 16:17 ` [PATCH 11/27] modsign: Allow signing key to be PKCS#11 David Howells
2015-07-17 16:17 ` [PATCH 12/27] modsign: Allow external signing key to be specified David Howells
2015-07-17 16:17 ` [PATCH 13/27] modsign: Extract signing cert from CONFIG_MODULE_SIG_KEY if needed David Howells
2015-07-17 16:17 ` [PATCH 14/27] modsign: Use single PEM file for autogenerated key David Howells
2015-07-17 16:17 ` [PATCH 15/27] modsign: Add explicit CONFIG_SYSTEM_TRUSTED_KEYS option David Howells
2015-07-17 16:18 ` [PATCH 16/27] PKCS#7: Check content type and versions David Howells
2015-07-17 16:18 ` [PATCH 17/27] ASN.1: Add an ASN.1 compiler option to dump the element tree David Howells
2015-07-17 16:18 ` [PATCH 18/27] ASN.1: Fix handling of CHOICE in ASN.1 compiler David Howells
2015-07-17 16:18 ` [PATCH 19/27] X.509: Change recorded SKID & AKID to not include Subject or Issuer David Howells
2015-07-17 16:18 ` [PATCH 20/27] PKCS#7: Support CMS messages also [RFC5652] David Howells
2015-07-17 16:19 ` [PATCH 21/27] sign-file: Generate CMS message as signature instead of PKCS#7 David Howells
2015-07-17 16:19 ` [PATCH 22/27] extract-cert: Cope with multiple X.509 certificates in a single file David Howells
2015-07-17 16:19 ` [PATCH 23/27] modsign: Use extract-cert to process CONFIG_SYSTEM_TRUSTED_KEYS David Howells
2015-07-17 16:19 ` David Howells [this message]
2015-07-17 16:19 ` [PATCH 25/27] X.509: Parse the keyUsage extension to detect key-signing keys David Howells
2015-07-17 16:20 ` [PATCH 26/27] PKCS#7: Add an optional authenticated attribute to hold firmware name David Howells
2015-07-17 16:20 ` [PATCH 27/27] KEYS: Restrict signature verification to keys appropriate to the purpose David Howells

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=20150717161939.2578.78401.stgit@warthog.procyon.org.uk \
    --to=dhowells@redhat.com \
    --cc=dwmw2@infradead.org \
    --cc=gregkh@linuxfoundation.org \
    --cc=keyrings@linux-nfs.org \
    --cc=kyle@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-security-module@vger.kernel.org \
    --cc=linux-wireless@vger.kernel.org \
    --cc=mcgrof@gmail.com \
    --cc=mjg59@srcf.ucam.org \
    --cc=seth.forshee@canonical.com \
    --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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®