Commit 32761fdc authored by David Howells's avatar David Howells

X.509: Restrict the usage of a key based on information in X.509 certificate

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: default avatarDavid Howells <dhowells@redhat.com>
parent d43eff2f
......@@ -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
#
......
......@@ -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, ']');
}
}
......
......@@ -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.
*/
......@@ -55,6 +65,18 @@ static void public_key_describe(const struct key *asymmetric_key,
pkey_id_type_name[key->id_type], key->algo->name);
}
/*
* 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.
*/
......@@ -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,
};
......
......@@ -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;
}
ExtKeyUsageSyntax ::= SEQUENCE OF KeyPurposeId
KeyPurposeId ::= OBJECT IDENTIFIER ({ x509_extusage_note_purpose })
......@@ -39,6 +39,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.
*
......@@ -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 {
......
......@@ -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);
......
......@@ -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
};
......
......@@ -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))
......
......@@ -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;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment