From patchwork Thu May 28 15:48:44 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Howells X-Patchwork-Id: 6499701 X-Patchwork-Delegate: kvalo@adurom.com Return-Path: X-Original-To: patchwork-linux-wireless@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork2.web.kernel.org (Postfix) with ESMTP id 01ACDC0020 for ; Thu, 28 May 2015 15:49:10 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 992E620642 for ; Thu, 28 May 2015 15:49:08 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id AD7DD20645 for ; Thu, 28 May 2015 15:49:06 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754533AbbE1PtA (ORCPT ); Thu, 28 May 2015 11:49:00 -0400 Received: from mx1.redhat.com ([209.132.183.28]:55184 "EHLO mx1.redhat.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753490AbbE1Psu (ORCPT ); Thu, 28 May 2015 11:48:50 -0400 Received: from int-mx14.intmail.prod.int.phx2.redhat.com (int-mx14.intmail.prod.int.phx2.redhat.com [10.5.11.27]) by mx1.redhat.com (Postfix) with ESMTPS id 76789C6586; Thu, 28 May 2015 15:48:50 +0000 (UTC) Received: from warthog.procyon.org.uk (ovpn-112-71.phx2.redhat.com [10.3.112.71]) by int-mx14.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id t4SFmipw029741; Thu, 28 May 2015 11:48:45 -0400 Organization: Red Hat UK Ltd. Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SI4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Subject: [PATCH 17/20] X.509: Restrict the usage of a key based on information in X.509 certificate [ver #5] From: David Howells 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 Date: Thu, 28 May 2015 16:48:44 +0100 Message-ID: <20150528154843.1259.88406.stgit@warthog.procyon.org.uk> In-Reply-To: <20150528154605.1259.42518.stgit@warthog.procyon.org.uk> References: <20150528154605.1259.42518.stgit@warthog.procyon.org.uk> User-Agent: StGit/0.17.1-dirty MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.68 on 10.5.11.27 Sender: linux-wireless-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-wireless@vger.kernel.org X-Spam-Status: No, score=-6.9 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_HI, T_RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP 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 temporarily used three unassigned OIDs from RH OID space. These will need to be replaced with real assigned OIDs: 1.3.6.1.4.1.2312.99.2 Key is restricted to firmware signing 1.3.6.1.4.1.2312.99.3 Key is restricted to module signing 1.3.6.1.4.1.2312.99.4 Key is restricted to kexecable image signing I would propose a fourth, key signing, but that should perhaps be handled through the keyUsage extension [RFC5280 4.2.1.3] setting keyCertSign. 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.99.2 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.99.2=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? Not-yet-signed-off-by: David Howells --- 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 | 12 +++++ include/keys/asymmetric-subtype.h | 3 + include/linux/oid_registry.h | 3 + kernel/Makefile | 1 lib/oid_registry.c | 1 10 files changed, 124 insertions(+), 1 deletion(-) create mode 100644 crypto/asymmetric_keys/x509_extusage.asn1 -- To unsubscribe from this list: send the line "unsubscribe linux-wireless" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html 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 bcbbbd794e1d..9047175fe818 100644 --- a/crypto/asymmetric_keys/asymmetric_type.c +++ b/crypto/asymmetric_keys/asymmetric_type.c @@ -246,7 +246,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..7790f58d00bf 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 public_key_restrictions[NR__PKEY_USAGE_RESTRICTION] = { + [PKEY_USAGE_NOT_SPECIFIED] = "unrestricted", + [PKEY_RESTRICTED_USAGE] = "unspecified", + [PKEY_RESTRICTED_TO_OTHER] = "other use", + [PKEY_RESTRICTED_TO_MODULE_SIGNING] = "module sig", + [PKEY_RESTRICTED_TO_FIRMWARE_SIGNING] = "firmware sig", + [PKEY_RESTRICTED_TO_KEXEC_SIGNING] = "kexec sig", + [PKEY_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, public_key_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 6c130dd56f35..4d9e9a7f8fd2 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", @@ -471,6 +488,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 = PKEY_RESTRICTED_USAGE; + return 0; + } + return 0; } @@ -605,3 +630,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 pkey_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 = PKEY_RESTRICTED_TO_FIRMWARE_SIGNING; + break; + case OID_moduleSigningOnlyKey: + restriction = PKEY_RESTRICTED_TO_MODULE_SIGNING; + break; + case OID_kexecSigningOnlyKey: + restriction = PKEY_RESTRICTED_TO_KEXEC_SIGNING; + break; + default: + restriction = PKEY_RESTRICTED_TO_OTHER; + break; + } + + if (ctx->cert->pub->usage_restriction != PKEY_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..d15c74cb1f98 100644 --- a/include/crypto/public_key.h +++ b/include/crypto/public_key.h @@ -37,6 +37,17 @@ enum pkey_id_type { PKEY_ID_TYPE__LAST }; +enum pkey_usage_restriction { + PKEY_USAGE_NOT_SPECIFIED, + PKEY_RESTRICTED_USAGE, + PKEY_RESTRICTED_TO_OTHER, + PKEY_RESTRICTED_TO_MODULE_SIGNING, + PKEY_RESTRICTED_TO_FIRMWARE_SIGNING, + PKEY_RESTRICTED_TO_KEXEC_SIGNING, + PKEY_RESTRICTED_TO_KEY_SIGNING, + NR__PKEY_USAGE_RESTRICTION +}; + extern const char *const pkey_id_type_name[PKEY_ID_TYPE__LAST]; /* @@ -52,6 +63,7 @@ struct public_key { #define PKEY_CAN_DECRYPT 0x02 #define PKEY_CAN_SIGN 0x04 #define PKEY_CAN_VERIFY 0x08 + enum pkey_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 db8d382699b5..46b3b10e2dc1 100644 --- a/include/linux/oid_registry.h +++ b/include/linux/oid_registry.h @@ -90,6 +90,9 @@ enum OID { /* Signing */ OID_firmwareName, /* 1.3.6.1.4.1.2312.99.1 */ + OID_firmwareSigningOnlyKey, /* 1.3.6.1.4.1.2312.99.2 */ + OID_moduleSigningOnlyKey, /* 1.3.6.1.4.1.2312.99.3 */ + OID_kexecSigningOnlyKey, /* 1.3.6.1.4.1.2312.99.4 */ OID__NR }; diff --git a/kernel/Makefile b/kernel/Makefile index 2d03e870ba8d..1b1aea6fe655 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -239,6 +239,7 @@ x509.genkey: @echo >>x509.genkey "keyUsage=digitalSignature" @echo >>x509.genkey "subjectKeyIdentifier=hash" @echo >>x509.genkey "authorityKeyIdentifier=keyid" + @echo >>x509.genkey "extendedKeyUsage=critical,1.3.6.1.4.1.2312.99.3" 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;