summaryrefslogtreecommitdiffstats
path: root/crypto/asymmetric_keys/x509_public_key.c
diff options
context:
space:
mode:
authorDavid Howells <dhowells@redhat.com>2015-07-20 21:16:26 +0100
committerDavid Howells <dhowells@redhat.com>2015-08-07 16:26:13 +0100
commit4573b64a31cd8cb4cfeb1d1b95536cfe71980cf4 (patch)
tree0c019ea809c625e90a5f4b34f48831ed64c9565a /crypto/asymmetric_keys/x509_public_key.c
parentb92e6570a992c7d793a209db282f68159368201c (diff)
downloadlinux-4573b64a31cd8cb4cfeb1d1b95536cfe71980cf4.tar.gz
linux-4573b64a31cd8cb4cfeb1d1b95536cfe71980cf4.tar.bz2
linux-4573b64a31cd8cb4cfeb1d1b95536cfe71980cf4.zip
X.509: Support X.509 lookup by Issuer+Serial form AuthorityKeyIdentifier
If an X.509 certificate has an AuthorityKeyIdentifier extension that provides an issuer and serialNumber, then make it so that these are used in preference to the keyIdentifier field also held therein for searching for the signing certificate. If both the issuer+serialNumber and the keyIdentifier are supplied, then the certificate is looked up by the former but the latter is checked as well. If the latter doesn't match the subjectKeyIdentifier of the parent certificate, EKEYREJECTED is returned. This makes it possible to chain X.509 certificates based on the issuer and serialNumber fields rather than on subjectKeyIdentifier. This is necessary as we are having to deal with keys that are represented by X.509 certificates that lack a subjectKeyIdentifier. Signed-off-by: David Howells <dhowells@redhat.com> Tested-by: Vivek Goyal <vgoyal@redhat.com>
Diffstat (limited to 'crypto/asymmetric_keys/x509_public_key.c')
-rw-r--r--crypto/asymmetric_keys/x509_public_key.c84
1 files changed, 58 insertions, 26 deletions
diff --git a/crypto/asymmetric_keys/x509_public_key.c b/crypto/asymmetric_keys/x509_public_key.c
index bb55d6074d5f..6b060b290e77 100644
--- a/crypto/asymmetric_keys/x509_public_key.c
+++ b/crypto/asymmetric_keys/x509_public_key.c
@@ -65,23 +65,37 @@ __setup("ca_keys=", ca_keys_setup);
/**
* x509_request_asymmetric_key - Request a key by X.509 certificate params.
* @keyring: The keys to search.
- * @kid: The key ID.
+ * @id: The issuer & serialNumber to look for or NULL.
+ * @skid: The subjectKeyIdentifier to look for or NULL.
* @partial: Use partial match if true, exact if false.
*
- * Find a key in the given keyring by subject name and key ID. These might,
- * for instance, be the issuer name and the authority key ID of an X.509
- * certificate that needs to be verified.
+ * Find a key in the given keyring by identifier. The preferred identifier is
+ * the issuer + serialNumber and the fallback identifier is the
+ * subjectKeyIdentifier. If both are given, the lookup is by the former, but
+ * the latter must also match.
*/
struct key *x509_request_asymmetric_key(struct key *keyring,
- const struct asymmetric_key_id *kid,
+ const struct asymmetric_key_id *id,
+ const struct asymmetric_key_id *skid,
bool partial)
{
- key_ref_t key;
- char *id, *p;
-
+ struct key *key;
+ key_ref_t ref;
+ const char *lookup;
+ char *req, *p;
+ int len;
+
+ if (id) {
+ lookup = id->data;
+ len = id->len;
+ } else {
+ lookup = skid->data;
+ len = skid->len;
+ }
+
/* Construct an identifier "id:<keyid>". */
- p = id = kmalloc(2 + 1 + kid->len * 2 + 1, GFP_KERNEL);
- if (!id)
+ p = req = kmalloc(2 + 1 + len * 2 + 1, GFP_KERNEL);
+ if (!req)
return ERR_PTR(-ENOMEM);
if (partial) {
@@ -92,32 +106,48 @@ struct key *x509_request_asymmetric_key(struct key *keyring,
*p++ = 'x';
}
*p++ = ':';
- p = bin2hex(p, kid->data, kid->len);
+ p = bin2hex(p, lookup, len);
*p = 0;
- pr_debug("Look up: \"%s\"\n", id);
+ pr_debug("Look up: \"%s\"\n", req);
- key = keyring_search(make_key_ref(keyring, 1),
- &key_type_asymmetric, id);
- if (IS_ERR(key))
- pr_debug("Request for key '%s' err %ld\n", id, PTR_ERR(key));
- kfree(id);
+ ref = keyring_search(make_key_ref(keyring, 1),
+ &key_type_asymmetric, req);
+ if (IS_ERR(ref))
+ pr_debug("Request for key '%s' err %ld\n", req, PTR_ERR(ref));
+ kfree(req);
- if (IS_ERR(key)) {
- switch (PTR_ERR(key)) {
+ if (IS_ERR(ref)) {
+ switch (PTR_ERR(ref)) {
/* Hide some search errors */
case -EACCES:
case -ENOTDIR:
case -EAGAIN:
return ERR_PTR(-ENOKEY);
default:
- return ERR_CAST(key);
+ return ERR_CAST(ref);
+ }
+ }
+
+ key = key_ref_to_ptr(ref);
+ if (id && skid) {
+ const struct asymmetric_key_ids *kids = asymmetric_key_ids(key);
+ if (!kids->id[1]) {
+ pr_debug("issuer+serial match, but expected SKID missing\n");
+ goto reject;
+ }
+ if (!asymmetric_key_id_same(skid, kids->id[1])) {
+ pr_debug("issuer+serial match, but SKID does not\n");
+ goto reject;
}
}
+
+ pr_devel("<==%s() = 0 [%x]\n", __func__, key_serial(key));
+ return key;
- pr_devel("<==%s() = 0 [%x]\n", __func__,
- key_serial(key_ref_to_ptr(key)));
- return key_ref_to_ptr(key);
+reject:
+ key_put(key);
+ return ERR_PTR(-EKEYREJECTED);
}
EXPORT_SYMBOL_GPL(x509_request_asymmetric_key);
@@ -230,7 +260,8 @@ static int x509_validate_trust(struct x509_certificate *cert,
if (ca_keyid && !asymmetric_key_id_partial(cert->akid_skid, ca_keyid))
return -EPERM;
- key = x509_request_asymmetric_key(trust_keyring, cert->akid_skid,
+ key = x509_request_asymmetric_key(trust_keyring,
+ cert->akid_id, cert->akid_skid,
false);
if (!IS_ERR(key)) {
if (!use_builtin_keys
@@ -287,8 +318,9 @@ static int x509_key_preparse(struct key_preparsed_payload *prep)
cert->pub->id_type = PKEY_ID_X509;
/* Check the signature on the key if it appears to be self-signed */
- if (!cert->akid_skid ||
- asymmetric_key_id_same(cert->skid, cert->akid_skid)) {
+ if ((!cert->akid_skid && !cert->akid_id) ||
+ asymmetric_key_id_same(cert->skid, cert->akid_skid) ||
+ asymmetric_key_id_same(cert->id, cert->akid_id)) {
ret = x509_check_signature(cert->pub, cert); /* self-signed */
if (ret < 0)
goto error_free_cert;