From e3615238be5de5326c2f89d6a953d408d80caa28 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 15 Jan 2013 15:33:42 +0000 Subject: [PATCH 19/28] PKCS#7: Find intersection between PKCS#7 message and known, trusted keys Find the intersection between the X.509 certificate chain contained in a PKCS#7 message and a set of keys that we already know and trust. Signed-off-by: David Howells Reviewed-by: Kees Cook --- crypto/asymmetric_keys/Makefile | 1 crypto/asymmetric_keys/pkcs7_trust.c | 148 +++++++++++++++++++++++++++++++++++ include/crypto/pkcs7.h | 7 + 3 files changed, 156 insertions(+) create mode 100644 crypto/asymmetric_keys/pkcs7_trust.c Index: linux-2.6/crypto/asymmetric_keys/Makefile =================================================================== --- linux-2.6.orig/crypto/asymmetric_keys/Makefile 2014-06-17 09:13:06.928968826 -0400 +++ linux-2.6/crypto/asymmetric_keys/Makefile 2014-06-17 09:13:50.532971395 -0400 @@ -33,6 +33,7 @@ obj-$(CONFIG_PKCS7_MESSAGE_PARSER) += pk pkcs7_message-y := \ pkcs7-asn1.o \ pkcs7_parser.o \ + pkcs7_trust.o \ pkcs7_verify.o $(obj)/pkcs7_parser.o: $(obj)/pkcs7-asn1.h Index: linux-2.6/crypto/asymmetric_keys/pkcs7_trust.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6/crypto/asymmetric_keys/pkcs7_trust.c 2014-06-17 09:13:50.532971395 -0400 @@ -0,0 +1,148 @@ +/* Validate the trust chain of a PKCS#7 message. + * + * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#define pr_fmt(fmt) "PKCS7: "fmt +#include +#include +#include +#include +#include +#include +#include +#include "public_key.h" +#include "pkcs7_parser.h" + +/* + * Request an asymmetric key. + */ +static struct key *pkcs7_request_asymmetric_key( + struct key *keyring, + const char *signer, size_t signer_len, + const char *authority, size_t auth_len) +{ + key_ref_t key; + char *id; + + kenter(",%zu,,%zu", signer_len, auth_len); + + /* Construct an identifier. */ + id = kmalloc(signer_len + 2 + auth_len + 1, GFP_KERNEL); + if (!id) + return ERR_PTR(-ENOMEM); + + memcpy(id, signer, signer_len); + id[signer_len + 0] = ':'; + id[signer_len + 1] = ' '; + memcpy(id + signer_len + 2, authority, auth_len); + id[signer_len + 2 + auth_len] = 0; + + pr_debug("Look up: \"%s\"\n", id); + + key = keyring_search(make_key_ref(keyring, 1), + &key_type_asymmetric, id); + if (IS_ERR(key)) + pr_debug("Request for module key '%s' err %ld\n", + id, PTR_ERR(key)); + kfree(id); + + if (IS_ERR(key)) { + switch (PTR_ERR(key)) { + /* Hide some search errors */ + case -EACCES: + case -ENOTDIR: + case -EAGAIN: + return ERR_PTR(-ENOKEY); + default: + return ERR_CAST(key); + } + } + + pr_devel("<==%s() = 0 [%x]\n", __func__, key_serial(key_ref_to_ptr(key))); + return key_ref_to_ptr(key); +} + +/* + * Validate that the certificate chain inside the PKCS#7 message intersects + * keys we already know and trust. + */ +int pkcs7_validate_trust(void *p7, struct key *trust_keyring, bool *_trusted) +{ + struct pkcs7_message *pkcs7 = p7; + struct public_key_signature *sig = &pkcs7->sig; + struct x509_certificate *x509, *last = NULL; + struct key *key; + bool trusted; + int ret; + + kenter(""); + + for (x509 = pkcs7->signer; x509; x509 = x509->next) { + /* Look to see if this certificate is present in the trusted + * keys. + */ + key = pkcs7_request_asymmetric_key( + trust_keyring, + x509->subject, strlen(x509->subject), + x509->fingerprint, strlen(x509->fingerprint)); + if (!IS_ERR(key)) + /* One of the X.509 certificates in the PKCS#7 message + * is apparently the same as one we already trust. + * Verify that the trusted variant can also validate + * the signature on the descendent. + */ + goto matched; + if (key == ERR_PTR(-ENOMEM)) + return -ENOMEM; + + /* Self-signed certificates form roots of their own, and if we + * don't know them, then we can't accept them. + */ + if (x509->next == x509) { + kleave(" = -EKEYREJECTED [unknown self-signed]"); + return -EKEYREJECTED; + } + + might_sleep(); + last = x509; + sig = &last->sig; + } + + /* No match - see if the root certificate has a signer amongst the + * trusted keys. + */ + if (!last || !last->issuer || !last->authority) { + kleave(" = -EKEYREJECTED [no backref]"); + return -EKEYREJECTED; + } + + key = pkcs7_request_asymmetric_key( + trust_keyring, + last->issuer, strlen(last->issuer), + last->authority, strlen(last->authority)); + if (IS_ERR(key)) + return PTR_ERR(key) == -ENOMEM ? -ENOMEM : -EKEYREJECTED; + +matched: + ret = verify_signature(key, sig); + trusted = test_bit(KEY_FLAG_TRUSTED, &key->flags); + key_put(key); + if (ret < 0) { + if (ret == -ENOMEM) + return ret; + kleave(" = -EKEYREJECTED [verify %d]", ret); + return -EKEYREJECTED; + } + + *_trusted = trusted; + kleave(" = 0"); + return 0; +} +EXPORT_SYMBOL_GPL(pkcs7_validate_trust); Index: linux-2.6/include/crypto/pkcs7.h =================================================================== --- linux-2.6.orig/include/crypto/pkcs7.h 2014-06-17 09:13:06.929968826 -0400 +++ linux-2.6/include/crypto/pkcs7.h 2014-06-17 09:13:50.533971395 -0400 @@ -9,6 +9,8 @@ * 2 of the Licence, or (at your option) any later version. */ +struct key; + /* * pkcs7_parser.c */ @@ -17,6 +19,11 @@ extern void pkcs7_free_message(void *pkc extern int pkcs7_get_content_data(void *pkcs7, const void **data, size_t *datalen); +/* + * pkcs7_trust.c + */ +extern int pkcs7_validate_trust(void *pkcs7, struct key *trust_keyring, + bool *_trusted); /* * pkcs7_verify.c