summaryrefslogtreecommitdiffstats
path: root/crypto
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2014-08-06 17:06:39 +0200
committerLinus Torvalds <torvalds@linux-foundation.org>2014-08-06 17:06:39 +0200
commitbb2cbf5e9367d8598fecd0c48dead69560750223 (patch)
treefb2c620451b90f41a31726bdd82077813f941e39 /crypto
parentMerge branch 'timers-core-for-linus' of git://git.kernel.org/pub/scm/linux/ke... (diff)
parentMerge tag 'keys-next-20140805' of git://git.kernel.org/pub/scm/linux/kernel/g... (diff)
downloadlinux-bb2cbf5e9367d8598fecd0c48dead69560750223.tar.xz
linux-bb2cbf5e9367d8598fecd0c48dead69560750223.zip
Merge branch 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security
Pull security subsystem updates from James Morris: "In this release: - PKCS#7 parser for the key management subsystem from David Howells - appoint Kees Cook as seccomp maintainer - bugfixes and general maintenance across the subsystem" * 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security: (94 commits) X.509: Need to export x509_request_asymmetric_key() netlabel: shorter names for the NetLabel catmap funcs/structs netlabel: fix the catmap walking functions netlabel: fix the horribly broken catmap functions netlabel: fix a problem when setting bits below the previously lowest bit PKCS#7: X.509 certificate issuer and subject are mandatory fields in the ASN.1 tpm: simplify code by using %*phN specifier tpm: Provide a generic means to override the chip returned timeouts tpm: missing tpm_chip_put in tpm_get_random() tpm: Properly clean sysfs entries in error path tpm: Add missing tpm_do_selftest to ST33 I2C driver PKCS#7: Use x509_request_asymmetric_key() Revert "selinux: fix the default socket labeling in sock_graft()" X.509: x509_request_asymmetric_keys() doesn't need string length arguments PKCS#7: fix sparse non static symbol warning KEYS: revert encrypted key change ima: add support for measuring and appraising firmware firmware_class: perform new LSM checks security: introduce kernel_fw_from_file hook PKCS#7: Missing inclusion of linux/err.h ...
Diffstat (limited to 'crypto')
-rw-r--r--crypto/asymmetric_keys/Kconfig34
-rw-r--r--crypto/asymmetric_keys/Makefile37
-rw-r--r--crypto/asymmetric_keys/asymmetric_keys.h2
-rw-r--r--crypto/asymmetric_keys/asymmetric_type.c78
-rw-r--r--crypto/asymmetric_keys/mscode.asn128
-rw-r--r--crypto/asymmetric_keys/mscode_parser.c126
-rw-r--r--crypto/asymmetric_keys/pkcs7.asn1127
-rw-r--r--crypto/asymmetric_keys/pkcs7_key_type.c100
-rw-r--r--crypto/asymmetric_keys/pkcs7_parser.c396
-rw-r--r--crypto/asymmetric_keys/pkcs7_parser.h61
-rw-r--r--crypto/asymmetric_keys/pkcs7_trust.c166
-rw-r--r--crypto/asymmetric_keys/pkcs7_verify.c321
-rw-r--r--crypto/asymmetric_keys/verify_pefile.c457
-rw-r--r--crypto/asymmetric_keys/verify_pefile.h42
-rw-r--r--crypto/asymmetric_keys/x509.asn12
-rw-r--r--crypto/asymmetric_keys/x509_cert_parser.c20
-rw-r--r--crypto/asymmetric_keys/x509_parser.h13
-rw-r--r--crypto/asymmetric_keys/x509_public_key.c115
18 files changed, 2075 insertions, 50 deletions
diff --git a/crypto/asymmetric_keys/Kconfig b/crypto/asymmetric_keys/Kconfig
index 03a6eb95ab50..4870f28403f5 100644
--- a/crypto/asymmetric_keys/Kconfig
+++ b/crypto/asymmetric_keys/Kconfig
@@ -22,7 +22,6 @@ config ASYMMETRIC_PUBLIC_KEY_SUBTYPE
config PUBLIC_KEY_ALGO_RSA
tristate "RSA public-key algorithm"
- select MPILIB_EXTRA
select MPILIB
help
This option enables support for the RSA algorithm (PKCS#1, RFC3447).
@@ -33,8 +32,39 @@ config X509_CERTIFICATE_PARSER
select ASN1
select OID_REGISTRY
help
- This option procides support for parsing X.509 format blobs for key
+ This option provides support for parsing X.509 format blobs for key
data and provides the ability to instantiate a crypto key from a
public key packet found inside the certificate.
+config PKCS7_MESSAGE_PARSER
+ tristate "PKCS#7 message parser"
+ depends on X509_CERTIFICATE_PARSER
+ select ASN1
+ select OID_REGISTRY
+ help
+ This option provides support for parsing PKCS#7 format messages for
+ signature data and provides the ability to verify the signature.
+
+config PKCS7_TEST_KEY
+ tristate "PKCS#7 testing key type"
+ depends on PKCS7_MESSAGE_PARSER
+ select SYSTEM_TRUSTED_KEYRING
+ help
+ This option provides a type of key that can be loaded up from a
+ PKCS#7 message - provided the message is signed by a trusted key. If
+ it is, the PKCS#7 wrapper is discarded and reading the key returns
+ just the payload. If it isn't, adding the key will fail with an
+ error.
+
+ This is intended for testing the PKCS#7 parser.
+
+config SIGNED_PE_FILE_VERIFICATION
+ bool "Support for PE file signature verification"
+ depends on PKCS7_MESSAGE_PARSER=y
+ select ASN1
+ select OID_REGISTRY
+ help
+ This option provides support for verifying the signature(s) on a
+ signed PE binary.
+
endif # ASYMMETRIC_KEY_TYPE
diff --git a/crypto/asymmetric_keys/Makefile b/crypto/asymmetric_keys/Makefile
index 0727204aab68..e47fcd9ac5e8 100644
--- a/crypto/asymmetric_keys/Makefile
+++ b/crypto/asymmetric_keys/Makefile
@@ -25,3 +25,40 @@ $(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_rsakey-asn1.c x509_rsakey-asn1.h
+
+#
+# PKCS#7 message handling
+#
+obj-$(CONFIG_PKCS7_MESSAGE_PARSER) += pkcs7_message.o
+pkcs7_message-y := \
+ pkcs7-asn1.o \
+ pkcs7_parser.o \
+ pkcs7_trust.o \
+ pkcs7_verify.o
+
+$(obj)/pkcs7_parser.o: $(obj)/pkcs7-asn1.h
+$(obj)/pkcs7-asn1.o: $(obj)/pkcs7-asn1.c $(obj)/pkcs7-asn1.h
+
+clean-files += pkcs7-asn1.c pkcs7-asn1.h
+
+#
+# PKCS#7 parser testing key
+#
+obj-$(CONFIG_PKCS7_TEST_KEY) += pkcs7_test_key.o
+pkcs7_test_key-y := \
+ pkcs7_key_type.o
+
+#
+# Signed PE binary-wrapped key handling
+#
+obj-$(CONFIG_SIGNED_PE_FILE_VERIFICATION) += verify_signed_pefile.o
+
+verify_signed_pefile-y := \
+ verify_pefile.o \
+ mscode_parser.o \
+ mscode-asn1.o
+
+$(obj)/mscode_parser.o: $(obj)/mscode-asn1.h $(obj)/mscode-asn1.h
+$(obj)/mscode-asn1.o: $(obj)/mscode-asn1.c $(obj)/mscode-asn1.h
+
+clean-files += mscode-asn1.c mscode-asn1.h
diff --git a/crypto/asymmetric_keys/asymmetric_keys.h b/crypto/asymmetric_keys/asymmetric_keys.h
index 515b63430812..a63c551c6557 100644
--- a/crypto/asymmetric_keys/asymmetric_keys.h
+++ b/crypto/asymmetric_keys/asymmetric_keys.h
@@ -9,6 +9,8 @@
* 2 of the Licence, or (at your option) any later version.
*/
+int asymmetric_keyid_match(const char *kid, const char *id);
+
static inline const char *asymmetric_key_id(const struct key *key)
{
return key->type_data.p[1];
diff --git a/crypto/asymmetric_keys/asymmetric_type.c b/crypto/asymmetric_keys/asymmetric_type.c
index b77eb5304788..eb8cd46961a5 100644
--- a/crypto/asymmetric_keys/asymmetric_type.c
+++ b/crypto/asymmetric_keys/asymmetric_type.c
@@ -23,6 +23,35 @@ static LIST_HEAD(asymmetric_key_parsers);
static DECLARE_RWSEM(asymmetric_key_parsers_sem);
/*
+ * Match asymmetric key id with partial match
+ * @id: key id to match in a form "id:<id>"
+ */
+int asymmetric_keyid_match(const char *kid, const char *id)
+{
+ size_t idlen, kidlen;
+
+ if (!kid || !id)
+ return 0;
+
+ /* make it possible to use id as in the request: "id:<id>" */
+ if (strncmp(id, "id:", 3) == 0)
+ id += 3;
+
+ /* Anything after here requires a partial match on the ID string */
+ idlen = strlen(id);
+ kidlen = strlen(kid);
+ if (idlen > kidlen)
+ return 0;
+
+ kid += kidlen - idlen;
+ if (strcasecmp(id, kid) != 0)
+ return 0;
+
+ return 1;
+}
+EXPORT_SYMBOL_GPL(asymmetric_keyid_match);
+
+/*
* Match asymmetric keys on (part of) their name
* We have some shorthand methods for matching keys. We allow:
*
@@ -34,9 +63,8 @@ static int asymmetric_key_match(const struct key *key, const void *description)
{
const struct asymmetric_key_subtype *subtype = asymmetric_key_subtype(key);
const char *spec = description;
- const char *id, *kid;
+ const char *id;
ptrdiff_t speclen;
- size_t idlen, kidlen;
if (!subtype || !spec || !*spec)
return 0;
@@ -55,23 +83,8 @@ static int asymmetric_key_match(const struct key *key, const void *description)
speclen = id - spec;
id++;
- /* Anything after here requires a partial match on the ID string */
- kid = asymmetric_key_id(key);
- if (!kid)
- return 0;
-
- idlen = strlen(id);
- kidlen = strlen(kid);
- if (idlen > kidlen)
- return 0;
-
- kid += kidlen - idlen;
- if (strcasecmp(id, kid) != 0)
- return 0;
-
- if (speclen == 2 &&
- memcmp(spec, "id", 2) == 0)
- return 1;
+ if (speclen == 2 && memcmp(spec, "id", 2) == 0)
+ return asymmetric_keyid_match(asymmetric_key_id(key), id);
if (speclen == subtype->name_len &&
memcmp(spec, subtype->name, speclen) == 0)
@@ -156,7 +169,7 @@ static void asymmetric_key_free_preparse(struct key_preparsed_payload *prep)
pr_devel("==>%s()\n", __func__);
if (subtype) {
- subtype->destroy(prep->payload);
+ subtype->destroy(prep->payload[0]);
module_put(subtype->owner);
}
kfree(prep->type_data[1]);
@@ -164,29 +177,6 @@ static void asymmetric_key_free_preparse(struct key_preparsed_payload *prep)
}
/*
- * Instantiate a asymmetric_key defined key. The key was preparsed, so we just
- * have to transfer the data here.
- */
-static int asymmetric_key_instantiate(struct key *key, struct key_preparsed_payload *prep)
-{
- int ret;
-
- pr_devel("==>%s()\n", __func__);
-
- ret = key_payload_reserve(key, prep->quotalen);
- if (ret == 0) {
- key->type_data.p[0] = prep->type_data[0];
- key->type_data.p[1] = prep->type_data[1];
- key->payload.data = prep->payload;
- prep->type_data[0] = NULL;
- prep->type_data[1] = NULL;
- prep->payload = NULL;
- }
- pr_devel("<==%s() = %d\n", __func__, ret);
- return ret;
-}
-
-/*
* dispose of the data dangling from the corpse of a asymmetric key
*/
static void asymmetric_key_destroy(struct key *key)
@@ -205,7 +195,7 @@ struct key_type key_type_asymmetric = {
.name = "asymmetric",
.preparse = asymmetric_key_preparse,
.free_preparse = asymmetric_key_free_preparse,
- .instantiate = asymmetric_key_instantiate,
+ .instantiate = generic_key_instantiate,
.match = asymmetric_key_match,
.destroy = asymmetric_key_destroy,
.describe = asymmetric_key_describe,
diff --git a/crypto/asymmetric_keys/mscode.asn1 b/crypto/asymmetric_keys/mscode.asn1
new file mode 100644
index 000000000000..6d09ba48c41c
--- /dev/null
+++ b/crypto/asymmetric_keys/mscode.asn1
@@ -0,0 +1,28 @@
+--- Microsoft individual code signing data blob parser
+---
+--- 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.
+---
+
+MSCode ::= SEQUENCE {
+ type SEQUENCE {
+ contentType ContentType,
+ parameters ANY
+ },
+ content SEQUENCE {
+ digestAlgorithm DigestAlgorithmIdentifier,
+ digest OCTET STRING ({ mscode_note_digest })
+ }
+}
+
+ContentType ::= OBJECT IDENTIFIER ({ mscode_note_content_type })
+
+DigestAlgorithmIdentifier ::= SEQUENCE {
+ algorithm OBJECT IDENTIFIER ({ mscode_note_digest_algo }),
+ parameters ANY OPTIONAL
+}
diff --git a/crypto/asymmetric_keys/mscode_parser.c b/crypto/asymmetric_keys/mscode_parser.c
new file mode 100644
index 000000000000..214a992123cd
--- /dev/null
+++ b/crypto/asymmetric_keys/mscode_parser.c
@@ -0,0 +1,126 @@
+/* Parse a Microsoft Individual Code Signing blob
+ *
+ * Copyright (C) 2014 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) "MSCODE: "fmt
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/oid_registry.h>
+#include <crypto/pkcs7.h>
+#include "verify_pefile.h"
+#include "mscode-asn1.h"
+
+/*
+ * Parse a Microsoft Individual Code Signing blob
+ */
+int mscode_parse(struct pefile_context *ctx)
+{
+ const void *content_data;
+ size_t data_len;
+ int ret;
+
+ ret = pkcs7_get_content_data(ctx->pkcs7, &content_data, &data_len, 1);
+
+ if (ret) {
+ pr_debug("PKCS#7 message does not contain data\n");
+ return ret;
+ }
+
+ pr_devel("Data: %zu [%*ph]\n", data_len, (unsigned)(data_len),
+ content_data);
+
+ return asn1_ber_decoder(&mscode_decoder, ctx, content_data, data_len);
+}
+
+/*
+ * Check the content type OID
+ */
+int mscode_note_content_type(void *context, size_t hdrlen,
+ unsigned char tag,
+ const void *value, size_t vlen)
+{
+ enum OID oid;
+
+ oid = look_up_OID(value, vlen);
+ if (oid == OID__NR) {
+ char buffer[50];
+
+ sprint_oid(value, vlen, buffer, sizeof(buffer));
+ pr_err("Unknown OID: %s\n", buffer);
+ return -EBADMSG;
+ }
+
+ /*
+ * pesign utility had a bug where it was putting
+ * OID_msIndividualSPKeyPurpose instead of OID_msPeImageDataObjId
+ * So allow both OIDs.
+ */
+ if (oid != OID_msPeImageDataObjId &&
+ oid != OID_msIndividualSPKeyPurpose) {
+ pr_err("Unexpected content type OID %u\n", oid);
+ return -EBADMSG;
+ }
+
+ return 0;
+}
+
+/*
+ * Note the digest algorithm OID
+ */
+int mscode_note_digest_algo(void *context, size_t hdrlen,
+ unsigned char tag,
+ const void *value, size_t vlen)
+{
+ struct pefile_context *ctx = context;
+ char buffer[50];
+ enum OID oid;
+
+ oid = look_up_OID(value, vlen);
+ switch (oid) {
+ case OID_md4:
+ ctx->digest_algo = HASH_ALGO_MD4;
+ break;
+ case OID_md5:
+ ctx->digest_algo = HASH_ALGO_MD5;
+ break;
+ case OID_sha1:
+ ctx->digest_algo = HASH_ALGO_SHA1;
+ break;
+ case OID_sha256:
+ ctx->digest_algo = HASH_ALGO_SHA256;
+ break;
+
+ case OID__NR:
+ sprint_oid(value, vlen, buffer, sizeof(buffer));
+ pr_err("Unknown OID: %s\n", buffer);
+ return -EBADMSG;
+
+ default:
+ pr_err("Unsupported content type: %u\n", oid);
+ return -ENOPKG;
+ }
+
+ return 0;
+}
+
+/*
+ * Note the digest we're guaranteeing with this certificate
+ */
+int mscode_note_digest(void *context, size_t hdrlen,
+ unsigned char tag,
+ const void *value, size_t vlen)
+{
+ struct pefile_context *ctx = context;
+
+ ctx->digest = value;
+ ctx->digest_len = vlen;
+ return 0;
+}
diff --git a/crypto/asymmetric_keys/pkcs7.asn1 b/crypto/asymmetric_keys/pkcs7.asn1
new file mode 100644
index 000000000000..a5a14ef28c86
--- /dev/null
+++ b/crypto/asymmetric_keys/pkcs7.asn1
@@ -0,0 +1,127 @@
+PKCS7ContentInfo ::= SEQUENCE {
+ contentType ContentType,
+ content [0] EXPLICIT SignedData OPTIONAL
+}
+
+ContentType ::= OBJECT IDENTIFIER ({ pkcs7_note_OID })
+
+SignedData ::= SEQUENCE {
+ version INTEGER,
+ digestAlgorithms DigestAlgorithmIdentifiers,
+ contentInfo ContentInfo,
+ certificates CHOICE {
+ certSet [0] IMPLICIT ExtendedCertificatesAndCertificates,
+ certSequence [2] IMPLICIT Certificates
+ } OPTIONAL ({ pkcs7_note_certificate_list }),
+ crls CHOICE {
+ crlSet [1] IMPLICIT CertificateRevocationLists,
+ crlSequence [3] IMPLICIT CRLSequence
+ } OPTIONAL,
+ signerInfos SignerInfos
+}
+
+ContentInfo ::= SEQUENCE {
+ contentType ContentType,
+ content [0] EXPLICIT Data OPTIONAL
+}
+
+Data ::= ANY ({ pkcs7_note_data })
+
+DigestAlgorithmIdentifiers ::= CHOICE {
+ daSet SET OF DigestAlgorithmIdentifier,
+ daSequence SEQUENCE OF DigestAlgorithmIdentifier
+}
+
+DigestAlgorithmIdentifier ::= SEQUENCE {
+ algorithm OBJECT IDENTIFIER ({ pkcs7_note_OID }),
+ parameters ANY OPTIONAL
+}
+
+--
+-- Certificates and certificate lists
+--
+ExtendedCertificatesAndCertificates ::= SET OF ExtendedCertificateOrCertificate
+
+ExtendedCertificateOrCertificate ::= CHOICE {
+ certificate Certificate, -- X.509
+ extendedCertificate [0] IMPLICIT ExtendedCertificate -- PKCS#6
+}
+
+ExtendedCertificate ::= Certificate -- cheating
+
+Certificates ::= SEQUENCE OF Certificate
+
+CertificateRevocationLists ::= SET OF CertificateList
+
+CertificateList ::= SEQUENCE OF Certificate -- This may be defined incorrectly
+
+CRLSequence ::= SEQUENCE OF CertificateList
+
+Certificate ::= ANY ({ pkcs7_extract_cert }) -- X.509
+
+--
+-- Signer information
+--
+SignerInfos ::= CHOICE {
+ siSet SET OF SignerInfo,
+ siSequence SEQUENCE OF SignerInfo
+}
+
+SignerInfo ::= SEQUENCE {
+ version INTEGER,
+ issuerAndSerialNumber IssuerAndSerialNumber,
+ digestAlgorithm DigestAlgorithmIdentifier ({ pkcs7_sig_note_digest_algo }),
+ authenticatedAttributes CHOICE {
+ aaSet [0] IMPLICIT SetOfAuthenticatedAttribute
+ ({ pkcs7_sig_note_set_of_authattrs }),
+ aaSequence [2] EXPLICIT SEQUENCE OF AuthenticatedAttribute
+ -- Explicit because easier to compute digest on
+ -- sequence of attributes and then reuse encoded
+ -- sequence in aaSequence.
+ } OPTIONAL,
+ digestEncryptionAlgorithm
+ DigestEncryptionAlgorithmIdentifier ({ pkcs7_sig_note_pkey_algo }),
+ encryptedDigest EncryptedDigest,
+ unauthenticatedAttributes CHOICE {
+ uaSet [1] IMPLICIT SET OF UnauthenticatedAttribute,
+ uaSequence [3] IMPLICIT SEQUENCE OF UnauthenticatedAttribute
+ } OPTIONAL
+} ({ pkcs7_note_signed_info })
+
+IssuerAndSerialNumber ::= SEQUENCE {
+ issuer Name ({ pkcs7_sig_note_issuer }),
+ serialNumber CertificateSerialNumber ({ pkcs7_sig_note_serial })
+}
+
+CertificateSerialNumber ::= INTEGER
+
+SetOfAuthenticatedAttribute ::= SET OF AuthenticatedAttribute
+
+AuthenticatedAttribute ::= SEQUENCE {
+ type OBJECT IDENTIFIER ({ pkcs7_note_OID }),
+ values SET OF ANY ({ pkcs7_sig_note_authenticated_attr })
+}
+
+UnauthenticatedAttribute ::= SEQUENCE {
+ type OBJECT IDENTIFIER ({ pkcs7_note_OID }),
+ values SET OF ANY
+}
+
+DigestEncryptionAlgorithmIdentifier ::= SEQUENCE {
+ algorithm OBJECT IDENTIFIER ({ pkcs7_note_OID }),
+ parameters ANY OPTIONAL
+}
+
+EncryptedDigest ::= OCTET STRING ({ pkcs7_sig_note_signature })
+
+---
+--- X.500 Name
+---
+Name ::= SEQUENCE OF RelativeDistinguishedName
+
+RelativeDistinguishedName ::= SET OF AttributeValueAssertion
+
+AttributeValueAssertion ::= SEQUENCE {
+ attributeType OBJECT IDENTIFIER ({ pkcs7_note_OID }),
+ attributeValue ANY
+}
diff --git a/crypto/asymmetric_keys/pkcs7_key_type.c b/crypto/asymmetric_keys/pkcs7_key_type.c
new file mode 100644
index 000000000000..3de5fb011de0
--- /dev/null
+++ b/crypto/asymmetric_keys/pkcs7_key_type.c
@@ -0,0 +1,100 @@
+/* Testing module to load key from trusted PKCS#7 message
+ *
+ * Copyright (C) 2014 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) "PKCS7key: "fmt
+#include <linux/key.h>
+#include <linux/err.h>
+#include <linux/key-type.h>
+#include <crypto/pkcs7.h>
+#include <keys/user-type.h>
+#include <keys/system_keyring.h>
+#include "pkcs7_parser.h"
+
+/*
+ * Preparse a PKCS#7 wrapped and validated data blob.
+ */
+static int pkcs7_preparse(struct key_preparsed_payload *prep)
+{
+ struct pkcs7_message *pkcs7;
+ const void *data, *saved_prep_data;
+ size_t datalen, saved_prep_datalen;
+ bool trusted;
+ int ret;
+
+ kenter("");
+
+ saved_prep_data = prep->data;
+ saved_prep_datalen = prep->datalen;
+ pkcs7 = pkcs7_parse_message(saved_prep_data, saved_prep_datalen);
+ if (IS_ERR(pkcs7)) {
+ ret = PTR_ERR(pkcs7);
+ goto error;
+ }
+
+ ret = pkcs7_verify(pkcs7);
+ if (ret < 0)
+ goto error_free;
+
+ ret = pkcs7_validate_trust(pkcs7, system_trusted_keyring, &trusted);
+ if (ret < 0)
+ goto error_free;
+ if (!trusted)
+ pr_warn("PKCS#7 message doesn't chain back to a trusted key\n");
+
+ ret = pkcs7_get_content_data(pkcs7, &data, &datalen, false);
+ if (ret < 0)
+ goto error_free;
+
+ prep->data = data;
+ prep->datalen = datalen;
+ ret = user_preparse(prep);
+ prep->data = saved_prep_data;
+ prep->datalen = saved_prep_datalen;
+
+error_free:
+ pkcs7_free_message(pkcs7);
+error:
+ kleave(" = %d", ret);
+ return ret;
+}
+
+/*
+ * user defined keys take an arbitrary string as the description and an
+ * arbitrary blob of data as the payload
+ */
+static struct key_type key_type_pkcs7 = {
+ .name = "pkcs7_test",
+ .def_lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT,
+ .preparse = pkcs7_preparse,
+ .free_preparse = user_free_preparse,
+ .instantiate = generic_key_instantiate,
+ .match = user_match,
+ .revoke = user_revoke,
+ .destroy = user_destroy,
+ .describe = user_describe,
+ .read = user_read,
+};
+
+/*
+ * Module stuff
+ */
+static int __init pkcs7_key_init(void)
+{
+ return register_key_type(&key_type_pkcs7);
+}
+
+static void __exit pkcs7_key_cleanup(void)
+{
+ unregister_key_type(&key_type_pkcs7);
+}
+
+module_init(pkcs7_key_init);
+module_exit(pkcs7_key_cleanup);
diff --git a/crypto/asymmetric_keys/pkcs7_parser.c b/crypto/asymmetric_keys/pkcs7_parser.c
new file mode 100644
index 000000000000..42e56aa7d277
--- /dev/null
+++ b/crypto/asymmetric_keys/pkcs7_parser.c
@@ -0,0 +1,396 @@
+/* PKCS#7 parser
+ *
+ * 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 <linux/kernel.h>
+#include <linux/export.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/oid_registry.h>
+#include "public_key.h"
+#include "pkcs7_parser.h"
+#include "pkcs7-asn1.h"
+
+struct pkcs7_parse_context {
+ struct pkcs7_message *msg; /* Message being constructed */
+ struct pkcs7_signed_info *sinfo; /* SignedInfo being constructed */
+ struct pkcs7_signed_info **ppsinfo;
+ struct x509_certificate *certs; /* Certificate cache */
+ struct x509_certificate **ppcerts;
+ unsigned long data; /* Start of data */
+ enum OID last_oid; /* Last OID encountered */
+ unsigned x509_index;
+ unsigned sinfo_index;
+};
+
+/**
+ * pkcs7_free_message - Free a PKCS#7 message
+ * @pkcs7: The PKCS#7 message to free
+ */
+void pkcs7_free_message(struct pkcs7_message *pkcs7)
+{
+ struct x509_certificate *cert;
+ struct pkcs7_signed_info *sinfo;
+
+ if (pkcs7) {
+ while (pkcs7->certs) {
+ cert = pkcs7->certs;
+ pkcs7->certs = cert->next;
+ x509_free_certificate(cert);
+ }
+ while (pkcs7->crl) {
+ cert = pkcs7->crl;
+ pkcs7->crl = cert->next;
+ x509_free_certificate(cert);
+ }
+ while (pkcs7->signed_infos) {
+ sinfo = pkcs7->signed_infos;
+ pkcs7->signed_infos = sinfo->next;
+ mpi_free(sinfo->sig.mpi[0]);
+ kfree(sinfo->sig.digest);
+ kfree(sinfo);
+ }
+ kfree(pkcs7);
+ }
+}
+EXPORT_SYMBOL_GPL(pkcs7_free_message);
+
+/**
+ * pkcs7_parse_message - Parse a PKCS#7 message
+ * @data: The raw binary ASN.1 encoded message to be parsed
+ * @datalen: The size of the encoded message
+ */
+struct pkcs7_message *pkcs7_parse_message(const void *data, size_t datalen)
+{
+ struct pkcs7_parse_context *ctx;
+ struct pkcs7_message *msg;
+ long ret;
+
+ ret = -ENOMEM;
+ msg = kzalloc(sizeof(struct pkcs7_message), GFP_KERNEL);
+ if (!msg)
+ goto error_no_sig;
+ ctx = kzalloc(sizeof(struct pkcs7_parse_context), GFP_KERNEL);
+ if (!ctx)
+ goto error_no_ctx;
+ ctx->sinfo = kzalloc(sizeof(struct pkcs7_signed_info), GFP_KERNEL);
+ if (!ctx->sinfo)
+ goto error_no_sinfo;
+
+ ctx->msg = msg;
+ ctx->data = (unsigned long)data;
+ ctx->ppcerts = &ctx->certs;
+ ctx->ppsinfo = &ctx->msg->signed_infos;
+
+ /* Attempt to decode the signature */
+ ret = asn1_ber_decoder(&pkcs7_decoder, ctx, data, datalen);
+ if (ret < 0)
+ goto error_decode;
+
+ while (ctx->certs) {
+ struct x509_certificate *cert = ctx->certs;
+ ctx->certs = cert->next;
+ x509_free_certificate(cert);
+ }
+ mpi_free(ctx->sinfo->sig.mpi[0]);
+ kfree(ctx->sinfo->sig.digest);
+ kfree(ctx->sinfo);
+ kfree(ctx);
+ return msg;
+
+error_decode:
+ mpi_free(ctx->sinfo->sig.mpi[0]);
+ kfree(ctx->sinfo->sig.digest);
+ kfree(ctx->sinfo);
+error_no_sinfo:
+ kfree(ctx);
+error_no_ctx:
+ pkcs7_free_message(msg);
+error_no_sig:
+ return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(pkcs7_parse_message);
+
+/**
+ * pkcs7_get_content_data - Get access to the PKCS#7 content
+ * @pkcs7: The preparsed PKCS#7 message to access
+ * @_data: Place to return a pointer to the data
+ * @_data_len: Place to return the data length
+ * @want_wrapper: True if the ASN.1 object header should be included in the data
+ *
+ * Get access to the data content of the PKCS#7 message, including, optionally,
+ * the header of the ASN.1 object that contains it. Returns -ENODATA if the
+ * data object was missing from the message.
+ */
+int pkcs7_get_content_data(const struct pkcs7_message *pkcs7,
+ const void **_data, size_t *_data_len,
+ bool want_wrapper)
+{
+ size_t wrapper;
+
+ if (!pkcs7->data)
+ return -ENODATA;
+
+ wrapper = want_wrapper ? pkcs7->data_hdrlen : 0;
+ *_data = pkcs7->data - wrapper;
+ *_data_len = pkcs7->data_len + wrapper;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(pkcs7_get_content_data);
+
+/*
+ * Note an OID when we find one for later processing when we know how
+ * to interpret it.
+ */
+int pkcs7_note_OID(void *context, size_t hdrlen,
+ unsigned char tag,
+ const void *value, size_t vlen)
+{
+ struct pkcs7_parse_context *ctx = context;
+
+ ctx->last_oid = look_up_OID(value, vlen);
+ if (ctx->last_oid == OID__NR) {
+ char buffer[50];
+ sprint_oid(value, vlen, buffer, sizeof(buffer));
+ printk("PKCS7: Unknown OID: [%lu] %s\n",
+ (unsigned long)value - ctx->data, buffer);
+ }
+ return 0;
+}
+
+/*
+ * Note the digest algorithm for the signature.
+ */
+int pkcs7_sig_note_digest_algo(void *context, size_t hdrlen,
+ unsigned char tag,
+ const void *value, size_t vlen)
+{
+ struct pkcs7_parse_context *ctx = context;
+
+ switch (ctx->last_oid) {
+ case OID_md4:
+ ctx->sinfo->sig.pkey_hash_algo = HASH_ALGO_MD4;
+ break;
+ case OID_md5:
+ ctx->sinfo->sig.pkey_hash_algo = HASH_ALGO_MD5;
+ break;
+ case OID_sha1:
+ ctx->sinfo->sig.pkey_hash_algo = HASH_ALGO_SHA1;
+ break;
+ case OID_sha256:
+ ctx->sinfo->sig.pkey_hash_algo = HASH_ALGO_SHA256;
+ break;
+ default:
+ printk("Unsupported digest algo: %u\n", ctx->last_oid);
+ return -ENOPKG;
+ }
+ return 0;
+}
+
+/*
+ * Note the public key algorithm for the signature.
+ */
+int pkcs7_sig_note_pkey_algo(void *context, size_t hdrlen,
+ unsigned char tag,
+ const void *value, size_t vlen)
+{
+ struct pkcs7_parse_context *ctx = context;
+
+ switch (ctx->last_oid) {
+ case OID_rsaEncryption:
+ ctx->sinfo->sig.pkey_algo = PKEY_ALGO_RSA;
+ break;
+ default:
+ printk("Unsupported pkey algo: %u\n", ctx->last_oid);
+ return -ENOPKG;
+ }
+ return 0;
+}
+
+/*
+ * Extract a certificate and store it in the context.
+ */
+int pkcs7_extract_cert(void *context, size_t hdrlen,
+ unsigned char tag,
+ const void *value, size_t vlen)
+{
+ struct pkcs7_parse_context *ctx = context;
+ struct x509_certificate *x509;
+
+ if (tag != ((ASN1_UNIV << 6) | ASN1_CONS_BIT | ASN1_SEQ)) {
+ pr_debug("Cert began with tag %02x at %lu\n",
+ tag, (unsigned long)ctx - ctx->data);
+ return -EBADMSG;
+ }
+
+ /* We have to correct for the header so that the X.509 parser can start
+ * from the beginning. Note that since X.509 stipulates DER, there
+ * probably shouldn't be an EOC trailer - but it is in PKCS#7 (which
+ * stipulates BER).
+ */
+ value -= hdrlen;
+ vlen += hdrlen;
+
+ if (((u8*)value)[1] == 0x80)
+ vlen += 2; /* Indefinite length - there should be an EOC */
+
+ x509 = x509_cert_parse(value, vlen);
+ if (IS_ERR(x509))
+ return PTR_ERR(x509);
+
+ pr_debug("Got cert for %s\n", x509->subject);
+ pr_debug("- fingerprint %s\n", x509->fingerprint);
+
+ x509->index = ++ctx->x509_index;
+ *ctx->ppcerts = x509;
+ ctx->ppcerts = &x509->next;
+ return 0;
+}
+
+/*
+ * Save the certificate list
+ */
+int pkcs7_note_certificate_list(void *context, size_t hdrlen,
+ unsigned char tag,
+ const void *value, size_t vlen)
+{
+ struct pkcs7_parse_context *ctx = context;
+
+ pr_devel("Got cert list (%02x)\n", tag);
+
+ *ctx->ppcerts = ctx->msg->certs;
+ ctx->msg->certs = ctx->certs;
+ ctx->certs = NULL;
+ ctx->ppcerts = &ctx->certs;
+ return 0;
+}
+
+/*
+ * Extract the data from the message and store that and its content type OID in
+ * the context.
+ */
+int pkcs7_note_data(void *context, size_t hdrlen,
+ unsigned char tag,
+ const void *value, size_t vlen)
+{
+ struct pkcs7_parse_context *ctx = context;
+
+ pr_debug("Got data\n");
+
+ ctx->msg->data = value;
+ ctx->msg->data_len = vlen;
+ ctx->msg->data_hdrlen = hdrlen;
+ ctx->msg->data_type = ctx->last_oid;
+ return 0;
+}
+
+/*
+ * Parse authenticated attributes
+ */
+int pkcs7_sig_note_authenticated_attr(void *context, size_t hdrlen,
+ unsigned char tag,
+ const void *value, size_t vlen)
+{
+ struct pkcs7_parse_context *ctx = context;
+
+ pr_devel("AuthAttr: %02x %zu [%*ph]\n", tag, vlen, (unsigned)vlen, value);
+
+ switch (ctx->last_oid) {
+ case OID_messageDigest:
+ if (tag != ASN1_OTS)
+ return -EBADMSG;
+ ctx->sinfo->msgdigest = value;
+ ctx->sinfo->msgdigest_len = vlen;
+ return 0;
+ default:
+ return 0;
+ }
+}
+
+/*
+ * Note the set of auth attributes for digestion purposes [RFC2315 9.3]
+ */
+int pkcs7_sig_note_set_of_authattrs(void *context, size_t hdrlen,
+ unsigned char tag,
+ const void *value, size_t vlen)
+{
+ struct pkcs7_parse_context *ctx = context;
+
+ /* We need to switch the 'CONT 0' to a 'SET OF' when we digest */
+ ctx->sinfo->authattrs = value - (hdrlen - 1);
+ ctx->sinfo->authattrs_len = vlen + (hdrlen - 1);
+ return 0;
+}
+
+/*
+ * Note the issuing certificate serial number
+ */
+int pkcs7_sig_note_serial(void *context, size_t hdrlen,
+ unsigned char tag,
+ const void *value, size_t vlen)
+{
+ struct pkcs7_parse_context *ctx = context;
+ ctx->sinfo->raw_serial = value;
+ ctx->sinfo->raw_serial_size = vlen;
+ return 0;
+}
+
+/*
+ * Note the issuer's name
+ */
+int pkcs7_sig_note_issuer(void *context, size_t hdrlen,
+ unsigned char tag,
+ const void *value, size_t vlen)
+{
+ struct pkcs7_parse_context *ctx = context;
+ ctx->sinfo->raw_issuer = value;
+ ctx->sinfo->raw_issuer_size = vlen;
+ return 0;
+}
+
+/*
+ * Note the signature data
+ */
+int pkcs7_sig_note_signature(void *context, size_t hdrlen,
+ unsigned char tag,
+ const void *value, size_t vlen)
+{
+ struct pkcs7_parse_context *ctx = context;
+ MPI mpi;
+
+ BUG_ON(ctx->sinfo->sig.pkey_algo != PKEY_ALGO_RSA);
+
+ mpi = mpi_read_raw_data(value, vlen);
+ if (!mpi)
+ return -ENOMEM;
+
+ ctx->sinfo->sig.mpi[0] = mpi;
+ ctx->sinfo->sig.nr_mpi = 1;
+ return 0;
+}
+
+/*
+ * Note a signature information block
+ */
+int pkcs7_note_signed_info(void *context, size_t hdrlen,
+ unsigned char tag,
+ const void *value, size_t vlen)
+{
+ struct pkcs7_parse_context *ctx = context;
+
+ ctx->sinfo->index = ++ctx->sinfo_index;
+ *ctx->ppsinfo = ctx->sinfo;
+ ctx->ppsinfo = &ctx->sinfo->next;
+ ctx->sinfo = kzalloc(sizeof(struct pkcs7_signed_info), GFP_KERNEL);
+ if (!ctx->sinfo)
+ return -ENOMEM;
+ return 0;
+}
diff --git a/crypto/asymmetric_keys/pkcs7_parser.h b/crypto/asymmetric_keys/pkcs7_parser.h
new file mode 100644
index 000000000000..d25f4d15370f
--- /dev/null
+++ b/crypto/asymmetric_keys/pkcs7_parser.h
@@ -0,0 +1,61 @@
+/* PKCS#7 crypto data parser internal definitions
+ *
+ * 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.
+ */
+
+#include <linux/oid_registry.h>
+#include <crypto/pkcs7.h>
+#include "x509_parser.h"
+
+#define kenter(FMT, ...) \
+ pr_devel("==> %s("FMT")\n", __func__, ##__VA_ARGS__)
+#define kleave(FMT, ...) \
+ pr_devel("<== %s()"FMT"\n", __func__, ##__VA_ARGS__)
+
+struct pkcs7_signed_info {
+ struct pkcs7_signed_info *next;
+ struct x509_certificate *signer; /* Signing certificate (in msg->certs) */
+ unsigned index;
+ bool trusted;
+
+ /* Message digest - the digest of the Content Data (or NULL) */
+ const void *msgdigest;
+ unsigned msgdigest_len;
+
+ /* Authenticated Attribute data (or NULL) */
+ unsigned authattrs_len;
+ const void *authattrs;
+
+ /* Issuing cert serial number and issuer's name */
+ const void *raw_serial;
+ unsigned raw_serial_size;
+ unsigned raw_issuer_size;
+ const void *raw_issuer;
+
+ /* Message signature.
+ *
+ * This contains the generated digest of _either_ the Content Data or
+ * the Authenticated Attributes [RFC2315 9.3]. If the latter, one of
+ * the attributes contains the digest of the the Content Data within
+ * it.
+ */
+ struct public_key_signature sig;
+};
+
+struct pkcs7_message {
+ struct x509_certificate *certs; /* Certificate list */
+ struct x509_certificate *crl; /* Revocation list */
+ struct pkcs7_signed_info *signed_infos;
+
+ /* Content Data (or NULL) */
+ enum OID data_type; /* Type of Data */
+ size_t data_len; /* Length of Data */
+ size_t data_hdrlen; /* Length of Data ASN.1 header */
+ const void *data; /* Content Data (or 0) */
+};
diff --git a/crypto/asymmetric_keys/pkcs7_trust.c b/crypto/asymmetric_keys/pkcs7_trust.c
new file mode 100644
index 000000000000..e666eb011a85
--- /dev/null
+++ b/crypto/asymmetric_keys/pkcs7_trust.c
@@ -0,0 +1,166 @@
+/* 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 <linux/kernel.h>
+#include <linux/export.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/asn1.h>
+#include <linux/key.h>
+#include <keys/asymmetric-type.h>
+#include "public_key.h"
+#include "pkcs7_parser.h"
+
+/**
+ * Check the trust on one PKCS#7 SignedInfo block.
+ */
+int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7,
+ struct pkcs7_signed_info *sinfo,
+ struct key *trust_keyring)
+{
+ struct public_key_signature *sig = &sinfo->sig;
+ struct x509_certificate *x509, *last = NULL, *p;
+ struct key *key;
+ bool trusted;
+ int ret;
+
+ kenter(",%u,", sinfo->index);
+
+ for (x509 = sinfo->signer; x509; x509 = x509->signer) {
+ if (x509->seen) {
+ if (x509->verified) {
+ trusted = x509->trusted;
+ goto verified;
+ }
+ kleave(" = -ENOKEY [cached]");
+ return -ENOKEY;
+ }
+ x509->seen = true;
+
+ /* Look to see if this certificate is present in the trusted
+ * keys.
+ */
+ key = x509_request_asymmetric_key(trust_keyring, x509->subject,
+ 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 descendant.
+ */
+ 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(" = -ENOKEY [unknown self-signed]");
+ return -ENOKEY;
+ }
+
+ 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(" = -ENOKEY [no backref]");
+ return -ENOKEY;
+ }
+
+ key = x509_request_asymmetric_key(trust_keyring, last->issuer,
+ last->authority);
+ if (IS_ERR(key))
+ return PTR_ERR(key) == -ENOMEM ? -ENOMEM : -ENOKEY;
+ x509 = last;
+
+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;
+ }
+
+verified:
+ x509->verified = true;
+ for (p = sinfo->signer; p != x509; p = p->signer) {
+ p->verified = true;
+ p->trusted = trusted;
+ }
+ sinfo->trusted = trusted;
+ kleave(" = 0");
+ return 0;
+}
+
+/**
+ * pkcs7_validate_trust - Validate PKCS#7 trust chain
+ * @pkcs7: The PKCS#7 certificate to validate
+ * @trust_keyring: Signing certificates to use as starting points
+ * @_trusted: Set to true if trustworth, false otherwise
+ *
+ * Validate that the certificate chain inside the PKCS#7 message intersects
+ * keys we already know and trust.
+ *
+ * Returns, in order of descending priority:
+ *
+ * (*) -EKEYREJECTED if a signature failed to match for which we have a valid
+ * key, or:
+ *
+ * (*) 0 if at least one signature chain intersects with the keys in the trust
+ * keyring, or:
+ *
+ * (*) -ENOPKG if a suitable crypto module couldn't be found for a check on a
+ * chain.
+ *
+ * (*) -ENOKEY if we couldn't find a match for any of the signature chains in
+ * the message.
+ *
+ * May also return -ENOMEM.
+ */
+int pkcs7_validate_trust(struct pkcs7_message *pkcs7,
+ struct key *trust_keyring,
+ bool *_trusted)
+{
+ struct pkcs7_signed_info *sinfo;
+ struct x509_certificate *p;
+ int cached_ret = 0, ret;
+
+ for (p = pkcs7->certs; p; p = p->next)
+ p->seen = false;
+
+ for (sinfo = pkcs7->signed_infos; sinfo; sinfo = sinfo->next) {
+ ret = pkcs7_validate_trust_one(pkcs7, sinfo, trust_keyring);
+ if (ret < 0) {
+ if (ret == -ENOPKG) {
+ cached_ret = -ENOPKG;
+ } else if (ret == -ENOKEY) {
+ if (cached_ret == 0)
+ cached_ret = -ENOKEY;
+ } else {
+ return ret;
+ }
+ }
+ *_trusted |= sinfo->trusted;
+ }
+
+ return cached_ret;
+}
+EXPORT_SYMBOL_GPL(pkcs7_validate_trust);
diff --git a/crypto/asymmetric_keys/pkcs7_verify.c b/crypto/asymmetric_keys/pkcs7_verify.c
new file mode 100644
index 000000000000..c62cf8006e1f
--- /dev/null
+++ b/crypto/asymmetric_keys/pkcs7_verify.c
@@ -0,0 +1,321 @@
+/* Verify the signature on 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 <linux/kernel.h>
+#include <linux/export.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/asn1.h>
+#include <crypto/hash.h>
+#include "public_key.h"
+#include "pkcs7_parser.h"
+
+/*
+ * Digest the relevant parts of the PKCS#7 data
+ */
+static int pkcs7_digest(struct pkcs7_message *pkcs7,
+ struct pkcs7_signed_info *sinfo)
+{
+ struct crypto_shash *tfm;
+ struct shash_desc *desc;
+ size_t digest_size, desc_size;
+ void *digest;
+ int ret;
+
+ kenter(",%u,%u", sinfo->index, sinfo->sig.pkey_hash_algo);
+
+ if (sinfo->sig.pkey_hash_algo >= PKEY_HASH__LAST ||
+ !hash_algo_name[sinfo->sig.pkey_hash_algo])
+ return -ENOPKG;
+
+ /* Allocate the hashing algorithm we're going to need and find out how
+ * big the hash operational data will be.
+ */
+ tfm = crypto_alloc_shash(hash_algo_name[sinfo->sig.pkey_hash_algo],
+ 0, 0);
+ if (IS_ERR(tfm))
+ return (PTR_ERR(tfm) == -ENOENT) ? -ENOPKG : PTR_ERR(tfm);
+
+ desc_size = crypto_shash_descsize(tfm) + sizeof(*desc);
+ sinfo->sig.digest_size = digest_size = crypto_shash_digestsize(tfm);
+
+ ret = -ENOMEM;
+ digest = kzalloc(digest_size + desc_size, GFP_KERNEL);
+ if (!digest)
+ goto error_no_desc;
+
+ desc = digest + digest_size;
+ desc->tfm = tfm;
+ desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
+
+ /* Digest the message [RFC2315 9.3] */
+ ret = crypto_shash_init(desc);
+ if (ret < 0)
+ goto error;
+ ret = crypto_shash_finup(desc, pkcs7->data, pkcs7->data_len, digest);
+ if (ret < 0)
+ goto error;
+ pr_devel("MsgDigest = [%*ph]\n", 8, digest);
+
+ /* However, if there are authenticated attributes, there must be a
+ * message digest attribute amongst them which corresponds to the
+ * digest we just calculated.
+ */
+ if (sinfo->msgdigest) {
+ u8 tag;
+
+ if (sinfo->msgdigest_len != sinfo->sig.digest_size) {
+ pr_debug("Sig %u: Invalid digest size (%u)\n",
+ sinfo->index, sinfo->msgdigest_len);
+ ret = -EBADMSG;
+ goto error;
+ }
+
+ if (memcmp(digest, sinfo->msgdigest, sinfo->msgdigest_len) != 0) {
+ pr_debug("Sig %u: Message digest doesn't match\n",
+ sinfo->index);
+ ret = -EKEYREJECTED;
+ goto error;
+ }
+
+ /* We then calculate anew, using the authenticated attributes
+ * as the contents of the digest instead. Note that we need to
+ * convert the attributes from a CONT.0 into a SET before we
+ * hash it.
+ */
+ memset(digest, 0, sinfo->sig.digest_size);
+
+ ret = crypto_shash_init(desc);
+ if (ret < 0)
+ goto error;
+ tag = ASN1_CONS_BIT | ASN1_SET;
+ ret = crypto_shash_update(desc, &tag, 1);
+ if (ret < 0)
+ goto error;
+ ret = crypto_shash_finup(desc, sinfo->authattrs,
+ sinfo->authattrs_len, digest);
+ if (ret < 0)
+ goto error;
+ pr_devel("AADigest = [%*ph]\n", 8, digest);
+ }
+
+ sinfo->sig.digest = digest;
+ digest = NULL;
+
+error:
+ kfree(digest);
+error_no_desc:
+ crypto_free_shash(tfm);
+ kleave(" = %d", ret);
+ return ret;
+}
+
+/*
+ * Find the key (X.509 certificate) to use to verify a PKCS#7 message. PKCS#7
+ * uses the issuer's name and the issuing certificate serial number for
+ * matching purposes. These must match the certificate issuer's name (not
+ * subject's name) and the certificate serial number [RFC 2315 6.7].
+ */
+static int pkcs7_find_key(struct pkcs7_message *pkcs7,
+ struct pkcs7_signed_info *sinfo)
+{
+ struct x509_certificate *x509;
+ unsigned certix = 1;
+
+ kenter("%u,%u,%u",
+ sinfo->index, sinfo->raw_serial_size, sinfo->raw_issuer_size);
+
+ for (x509 = pkcs7->certs; x509; x509 = x509->next, certix++) {
+ /* I'm _assuming_ that the generator of the PKCS#7 message will
+ * encode the fields from the X.509 cert in the same way in the
+ * PKCS#7 message - but I can't be 100% sure of that. It's
+ * possible this will need element-by-element comparison.
+ */
+ if (x509->raw_serial_size != sinfo->raw_serial_size ||
+ memcmp(x509->raw_serial, sinfo->raw_serial,
+ sinfo->raw_serial_size) != 0)
+ continue;
+ pr_devel("Sig %u: Found cert serial match X.509[%u]\n",
+ sinfo->index, certix);
+
+ if (x509->raw_issuer_size != sinfo->raw_issuer_size ||
+ memcmp(x509->raw_issuer, sinfo->raw_issuer,
+ sinfo->raw_issuer_size) != 0) {
+ pr_warn("Sig %u: X.509 subject and PKCS#7 issuer don't match\n",
+ sinfo->index);
+ continue;
+ }
+
+ if (x509->pub->pkey_algo != sinfo->sig.pkey_algo) {
+ pr_warn("Sig %u: X.509 algo and PKCS#7 sig algo don't match\n",
+ sinfo->index);
+ continue;
+ }
+
+ sinfo->signer = x509;
+ return 0;
+ }
+ pr_warn("Sig %u: Issuing X.509 cert not found (#%*ph)\n",
+ sinfo->index, sinfo->raw_serial_size, sinfo->raw_serial);
+ return -ENOKEY;
+}
+
+/*
+ * Verify the internal certificate chain as best we can.
+ */
+static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7,
+ struct pkcs7_signed_info *sinfo)
+{
+ struct x509_certificate *x509 = sinfo->signer, *p;
+ int ret;
+
+ kenter("");
+
+ for (p = pkcs7->certs; p; p = p->next)
+ p->seen = false;
+
+ for (;;) {
+ pr_debug("verify %s: %s\n", x509->subject, x509->fingerprint);
+ x509->seen = true;
+ ret = x509_get_sig_params(x509);
+ if (ret < 0)
+ return ret;
+
+ pr_debug("- issuer %s\n", x509->issuer);
+ if (x509->authority)
+ pr_debug("- authkeyid %s\n", x509->authority);
+
+ if (!x509->authority ||
+ strcmp(x509->subject, x509->issuer) == 0) {
+ /* If there's no authority certificate specified, then
+ * the certificate must be self-signed and is the root
+ * of the chain. Likewise if the cert is its own
+ * authority.
+ */
+ pr_debug("- no auth?\n");
+ if (x509->raw_subject_size != x509->raw_issuer_size ||
+ memcmp(x509->raw_subject, x509->raw_issuer,
+ x509->raw_issuer_size) != 0)
+ return 0;
+
+ ret = x509_check_signature(x509->pub, x509);
+ if (ret < 0)
+ return ret;
+ x509->signer = x509;
+ pr_debug("- self-signed\n");
+ return 0;
+ }
+
+ /* Look through the X.509 certificates in the PKCS#7 message's
+ * list to see if the next one is there.
+ */
+ pr_debug("- want %s\n", x509->authority);
+ for (p = pkcs7->certs; p; p = p->next) {
+ pr_debug("- cmp [%u] %s\n", p->index, p->fingerprint);
+ if (p->raw_subject_size == x509->raw_issuer_size &&
+ strcmp(p->fingerprint, x509->authority) == 0 &&
+ memcmp(p->raw_subject, x509->raw_issuer,
+ x509->raw_issuer_size) == 0)
+ goto found_issuer;
+ }
+
+ /* We didn't find the root of this chain */
+ pr_debug("- top\n");
+ return 0;
+
+ found_issuer:
+ pr_debug("- issuer %s\n", p->subject);
+ if (p->seen) {
+ pr_warn("Sig %u: X.509 chain contains loop\n",
+ sinfo->index);
+ return 0;
+ }
+ ret = x509_check_signature(p->pub, x509);
+ if (ret < 0)
+ return ret;
+ x509->signer = p;
+ if (x509 == p) {
+ pr_debug("- self-signed\n");
+ return 0;
+ }
+ x509 = p;
+ might_sleep();
+ }
+}
+
+/*
+ * Verify one signed information block from a PKCS#7 message.
+ */
+static int pkcs7_verify_one(struct pkcs7_message *pkcs7,
+ struct pkcs7_signed_info *sinfo)
+{
+ int ret;
+
+ kenter(",%u", sinfo->index);
+
+ /* First of all, digest the data in the PKCS#7 message and the
+ * signed information block
+ */
+ ret = pkcs7_digest(pkcs7, sinfo);
+ if (ret < 0)
+ return ret;
+
+ /* Find the key for the signature */
+ ret = pkcs7_find_key(pkcs7, sinfo);
+ if (ret < 0)
+ return ret;
+
+ pr_devel("Using X.509[%u] for sig %u\n",
+ sinfo->signer->index, sinfo->index);
+
+ /* Verify the PKCS#7 binary against the key */
+ ret = public_key_verify_signature(sinfo->signer->pub, &sinfo->sig);
+ if (ret < 0)
+ return ret;
+
+ pr_devel("Verified signature %u\n", sinfo->index);
+
+ /* Verify the internal certificate chain */
+ return pkcs7_verify_sig_chain(pkcs7, sinfo);
+}
+
+/**
+ * pkcs7_verify - Verify a PKCS#7 message
+ * @pkcs7: The PKCS#7 message to be verified
+ */
+int pkcs7_verify(struct pkcs7_message *pkcs7)
+{
+ struct pkcs7_signed_info *sinfo;
+ struct x509_certificate *x509;
+ int ret, n;
+
+ kenter("");
+
+ for (n = 0, x509 = pkcs7->certs; x509; x509 = x509->next, n++) {
+ ret = x509_get_sig_params(x509);
+ if (ret < 0)
+ return ret;
+ pr_debug("X.509[%u] %s\n", n, x509->authority);
+ }
+
+ for (sinfo = pkcs7->signed_infos; sinfo; sinfo = sinfo->next) {
+ ret = pkcs7_verify_one(pkcs7, sinfo);
+ if (ret < 0) {
+ kleave(" = %d", ret);
+ return ret;
+ }
+ }
+
+ kleave(" = 0");
+ return 0;
+}
+EXPORT_SYMBOL_GPL(pkcs7_verify);
diff --git a/crypto/asymmetric_keys/verify_pefile.c b/crypto/asymmetric_keys/verify_pefile.c
new file mode 100644
index 000000000000..79175e6ea0b2
--- /dev/null
+++ b/crypto/asymmetric_keys/verify_pefile.c
@@ -0,0 +1,457 @@
+/* Parse a signed PE binary
+ *
+ * Copyright (C) 2014 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) "PEFILE: "fmt
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/pe.h>
+#include <linux/asn1.h>
+#include <crypto/pkcs7.h>
+#include <crypto/hash.h>
+#include "verify_pefile.h"
+
+/*
+ * Parse a PE binary.
+ */
+static int pefile_parse_binary(const void *pebuf, unsigned int pelen,
+ struct pefile_context *ctx)
+{
+ const struct mz_hdr *mz = pebuf;
+ const struct pe_hdr *pe;
+ const struct pe32_opt_hdr *pe32;
+ const struct pe32plus_opt_hdr *pe64;
+ const struct data_directory *ddir;
+ const struct data_dirent *dde;
+ const struct section_header *secs, *sec;
+ size_t cursor, datalen = pelen;
+
+ kenter("");
+
+#define chkaddr(base, x, s) \
+ do { \
+ if ((x) < base || (s) >= datalen || (x) > datalen - (s)) \
+ return -ELIBBAD; \
+ } while (0)
+
+ chkaddr(0, 0, sizeof(*mz));
+ if (mz->magic != MZ_MAGIC)
+ return -ELIBBAD;
+ cursor = sizeof(*mz);
+
+ chkaddr(cursor, mz->peaddr, sizeof(*pe));
+ pe = pebuf + mz->peaddr;
+ if (pe->magic != PE_MAGIC)
+ return -ELIBBAD;
+ cursor = mz->peaddr + sizeof(*pe);
+
+ chkaddr(0, cursor, sizeof(pe32->magic));
+ pe32 = pebuf + cursor;
+ pe64 = pebuf + cursor;
+
+ switch (pe32->magic) {
+ case PE_OPT_MAGIC_PE32:
+ chkaddr(0, cursor, sizeof(*pe32));
+ ctx->image_checksum_offset =
+ (unsigned long)&pe32->csum - (unsigned long)pebuf;
+ ctx->header_size = pe32->header_size;
+ cursor += sizeof(*pe32);
+ ctx->n_data_dirents = pe32->data_dirs;
+ break;
+
+ case PE_OPT_MAGIC_PE32PLUS:
+ chkaddr(0, cursor, sizeof(*pe64));
+ ctx->image_checksum_offset =
+ (unsigned long)&pe64->csum - (unsigned long)pebuf;
+ ctx->header_size = pe64->header_size;
+ cursor += sizeof(*pe64);
+ ctx->n_data_dirents = pe64->data_dirs;
+ break;
+
+ default:
+ pr_debug("Unknown PEOPT magic = %04hx\n", pe32->magic);
+ return -ELIBBAD;
+ }
+
+ pr_debug("checksum @ %x\n", ctx->image_checksum_offset);
+ pr_debug("header size = %x\n", ctx->header_size);
+
+ if (cursor >= ctx->header_size || ctx->header_size >= datalen)
+ return -ELIBBAD;
+
+ if (ctx->n_data_dirents > (ctx->header_size - cursor) / sizeof(*dde))
+ return -ELIBBAD;
+
+ ddir = pebuf + cursor;
+ cursor += sizeof(*dde) * ctx->n_data_dirents;
+
+ ctx->cert_dirent_offset =
+ (unsigned long)&ddir->certs - (unsigned long)pebuf;
+ ctx->certs_size = ddir->certs.size;
+
+ if (!ddir->certs.virtual_address || !ddir->certs.size) {
+ pr_debug("Unsigned PE binary\n");
+ return -EKEYREJECTED;
+ }
+
+ chkaddr(ctx->header_size, ddir->certs.virtual_address,
+ ddir->certs.size);
+ ctx->sig_offset = ddir->certs.virtual_address;
+ ctx->sig_len = ddir->certs.size;
+ pr_debug("cert = %x @%x [%*ph]\n",
+ ctx->sig_len, ctx->sig_offset,
+ ctx->sig_len, pebuf + ctx->sig_offset);
+
+ ctx->n_sections = pe->sections;
+ if (ctx->n_sections > (ctx->header_size - cursor) / sizeof(*sec))
+ return -ELIBBAD;
+ ctx->secs = secs = pebuf + cursor;
+
+ return 0;
+}
+
+/*
+ * Check and strip the PE wrapper from around the signature and check that the
+ * remnant looks something like PKCS#7.
+ */
+static int pefile_strip_sig_wrapper(const void *pebuf,
+ struct pefile_context *ctx)
+{
+ struct win_certificate wrapper;
+ const u8 *pkcs7;
+
+ if (ctx->sig_len < sizeof(wrapper)) {
+ pr_debug("Signature wrapper too short\n");
+ return -ELIBBAD;
+ }
+
+ memcpy(&wrapper, pebuf + ctx->sig_offset, sizeof(wrapper));
+ pr_debug("sig wrapper = { %x, %x, %x }\n",
+ wrapper.length, wrapper.revision, wrapper.cert_type);
+
+ /* Both pesign and sbsign round up the length of certificate table
+ * (in optional header data directories) to 8 byte alignment.
+ */
+ if (round_up(wrapper.length, 8) != ctx->sig_len) {
+ pr_debug("Signature wrapper len wrong\n");
+ return -ELIBBAD;
+ }
+ if (wrapper.revision != WIN_CERT_REVISION_2_0) {
+ pr_debug("Signature is not revision 2.0\n");
+ return -ENOTSUPP;
+ }
+ if (wrapper.cert_type != WIN_CERT_TYPE_PKCS_SIGNED_DATA) {
+ pr_debug("Signature certificate type is not PKCS\n");
+ return -ENOTSUPP;
+ }
+
+ /* Looks like actual pkcs signature length is in wrapper->length.
+ * size obtained from data dir entries lists the total size of
+ * certificate table which is also aligned to octawrod boundary.
+ *
+ * So set signature length field appropriately.
+ */
+ ctx->sig_len = wrapper.length;
+ ctx->sig_offset += sizeof(wrapper);
+ ctx->sig_len -= sizeof(wrapper);
+ if (ctx->sig_len == 0) {
+ pr_debug("Signature data missing\n");
+ return -EKEYREJECTED;
+ }
+
+ /* What's left should a PKCS#7 cert */
+ pkcs7 = pebuf + ctx->sig_offset;
+ if (pkcs7[0] == (ASN1_CONS_BIT | ASN1_SEQ)) {
+ if (pkcs7[1] == 0x82 &&
+ pkcs7[2] == (((ctx->sig_len - 4) >> 8) & 0xff) &&
+ pkcs7[3] == ((ctx->sig_len - 4) & 0xff))
+ return 0;
+ if (pkcs7[1] == 0x80)
+ return 0;
+ if (pkcs7[1] > 0x82)
+ return -EMSGSIZE;
+ }
+
+ pr_debug("Signature data not PKCS#7\n");
+ return -ELIBBAD;
+}
+
+/*
+ * Compare two sections for canonicalisation.
+ */
+static int pefile_compare_shdrs(const void *a, const void *b)
+{
+ const struct section_header *shdra = a;
+ const struct section_header *shdrb = b;
+ int rc;
+
+ if (shdra->data_addr > shdrb->data_addr)
+ return 1;
+ if (shdrb->data_addr > shdra->data_addr)
+ return -1;
+
+ if (shdra->virtual_address > shdrb->virtual_address)
+ return 1;
+ if (shdrb->virtual_address > shdra->virtual_address)
+ return -1;
+
+ rc = strcmp(shdra->name, shdrb->name);
+ if (rc != 0)
+ return rc;
+
+ if (shdra->virtual_size > shdrb->virtual_size)
+ return 1;
+ if (shdrb->virtual_size > shdra->virtual_size)
+ return -1;
+
+ if (shdra->raw_data_size > shdrb->raw_data_size)
+ return 1;
+ if (shdrb->raw_data_size > shdra->raw_data_size)
+ return -1;
+
+ return 0;
+}
+
+/*
+ * Load the contents of the PE binary into the digest, leaving out the image
+ * checksum and the certificate data block.
+ */
+static int pefile_digest_pe_contents(const void *pebuf, unsigned int pelen,
+ struct pefile_context *ctx,
+ struct shash_desc *desc)
+{
+ unsigned *canon, tmp, loop, i, hashed_bytes;
+ int ret;
+
+ /* Digest the header and data directory, but leave out the image
+ * checksum and the data dirent for the signature.
+ */
+ ret = crypto_shash_update(desc, pebuf, ctx->image_checksum_offset);
+ if (ret < 0)
+ return ret;
+
+ tmp = ctx->image_checksum_offset + sizeof(uint32_t);
+ ret = crypto_shash_update(desc, pebuf + tmp,
+ ctx->cert_dirent_offset - tmp);
+ if (ret < 0)
+ return ret;
+
+ tmp = ctx->cert_dirent_offset + sizeof(struct data_dirent);
+ ret = crypto_shash_update(desc, pebuf + tmp, ctx->header_size - tmp);
+ if (ret < 0)
+ return ret;
+
+ canon = kcalloc(ctx->n_sections, sizeof(unsigned), GFP_KERNEL);
+ if (!canon)
+ return -ENOMEM;
+
+ /* We have to canonicalise the section table, so we perform an
+ * insertion sort.
+ */
+ canon[0] = 0;
+ for (loop = 1; loop < ctx->n_sections; loop++) {
+ for (i = 0; i < loop; i++) {
+ if (pefile_compare_shdrs(&ctx->secs[canon[i]],
+ &ctx->secs[loop]) > 0) {
+ memmove(&canon[i + 1], &canon[i],
+ (loop - i) * sizeof(canon[0]));
+ break;
+ }
+ }
+ canon[i] = loop;
+ }
+
+ hashed_bytes = ctx->header_size;
+ for (loop = 0; loop < ctx->n_sections; loop++) {
+ i = canon[loop];
+ if (ctx->secs[i].raw_data_size == 0)
+ continue;
+ ret = crypto_shash_update(desc,
+ pebuf + ctx->secs[i].data_addr,
+ ctx->secs[i].raw_data_size);
+ if (ret < 0) {
+ kfree(canon);
+ return ret;
+ }
+ hashed_bytes += ctx->secs[i].raw_data_size;
+ }
+ kfree(canon);
+
+ if (pelen > hashed_bytes) {
+ tmp = hashed_bytes + ctx->certs_size;
+ ret = crypto_shash_update(desc,
+ pebuf + hashed_bytes,
+ pelen - tmp);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+/*
+ * Digest the contents of the PE binary, leaving out the image checksum and the
+ * certificate data block.
+ */
+static int pefile_digest_pe(const void *pebuf, unsigned int pelen,
+ struct pefile_context *ctx)
+{
+ struct crypto_shash *tfm;
+ struct shash_desc *desc;
+ size_t digest_size, desc_size;
+ void *digest;
+ int ret;
+
+ kenter(",%u", ctx->digest_algo);
+
+ /* Allocate the hashing algorithm we're going to need and find out how
+ * big the hash operational data will be.
+ */
+ tfm = crypto_alloc_shash(hash_algo_name[ctx->digest_algo], 0, 0);
+ if (IS_ERR(tfm))
+ return (PTR_ERR(tfm) == -ENOENT) ? -ENOPKG : PTR_ERR(tfm);
+
+ desc_size = crypto_shash_descsize(tfm) + sizeof(*desc);
+ digest_size = crypto_shash_digestsize(tfm);
+
+ if (digest_size != ctx->digest_len) {
+ pr_debug("Digest size mismatch (%zx != %x)\n",
+ digest_size, ctx->digest_len);
+ ret = -EBADMSG;
+ goto error_no_desc;
+ }
+ pr_debug("Digest: desc=%zu size=%zu\n", desc_size, digest_size);
+
+ ret = -ENOMEM;
+ desc = kzalloc(desc_size + digest_size, GFP_KERNEL);
+ if (!desc)
+ goto error_no_desc;
+
+ desc->tfm = tfm;
+ desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
+ ret = crypto_shash_init(desc);
+ if (ret < 0)
+ goto error;
+
+ ret = pefile_digest_pe_contents(pebuf, pelen, ctx, desc);
+ if (ret < 0)
+ goto error;
+
+ digest = (void *)desc + desc_size;
+ ret = crypto_shash_final(desc, digest);
+ if (ret < 0)
+ goto error;
+
+ pr_debug("Digest calc = [%*ph]\n", ctx->digest_len, digest);
+
+ /* Check that the PE file digest matches that in the MSCODE part of the
+ * PKCS#7 certificate.
+ */
+ if (memcmp(digest, ctx->digest, ctx->digest_len) != 0) {
+ pr_debug("Digest mismatch\n");
+ ret = -EKEYREJECTED;
+ } else {
+ pr_debug("The digests match!\n");
+ }
+
+error:
+ kfree(desc);
+error_no_desc:
+ crypto_free_shash(tfm);
+ kleave(" = %d", ret);
+ return ret;
+}
+
+/**
+ * verify_pefile_signature - Verify the signature on a PE binary image
+ * @pebuf: Buffer containing the PE binary image
+ * @pelen: Length of the binary image
+ * @trust_keyring: Signing certificates to use as starting points
+ * @_trusted: Set to true if trustworth, false otherwise
+ *
+ * Validate that the certificate chain inside the PKCS#7 message inside the PE
+ * binary image intersects keys we already know and trust.
+ *
+ * Returns, in order of descending priority:
+ *
+ * (*) -ELIBBAD if the image cannot be parsed, or:
+ *
+ * (*) -EKEYREJECTED if a signature failed to match for which we have a valid
+ * key, or:
+ *
+ * (*) 0 if at least one signature chain intersects with the keys in the trust
+ * keyring, or:
+ *
+ * (*) -ENOPKG if a suitable crypto module couldn't be found for a check on a
+ * chain.
+ *
+ * (*) -ENOKEY if we couldn't find a match for any of the signature chains in
+ * the message.
+ *
+ * May also return -ENOMEM.
+ */
+int verify_pefile_signature(const void *pebuf, unsigned pelen,
+ struct key *trusted_keyring, bool *_trusted)
+{
+ struct pkcs7_message *pkcs7;
+ struct pefile_context ctx;
+ const void *data;
+ size_t datalen;
+ int ret;
+
+ kenter("");
+
+ memset(&ctx, 0, sizeof(ctx));
+ ret = pefile_parse_binary(pebuf, pelen, &ctx);
+ if (ret < 0)
+ return ret;
+
+ ret = pefile_strip_sig_wrapper(pebuf, &ctx);
+ if (ret < 0)
+ return ret;
+
+ pkcs7 = pkcs7_parse_message(pebuf + ctx.sig_offset, ctx.sig_len);
+ if (IS_ERR(pkcs7))
+ return PTR_ERR(pkcs7);
+ ctx.pkcs7 = pkcs7;
+
+ ret = pkcs7_get_content_data(ctx.pkcs7, &data, &datalen, false);
+ if (ret < 0 || datalen == 0) {
+ pr_devel("PKCS#7 message does not contain data\n");
+ ret = -EBADMSG;
+ goto error;
+ }
+
+ ret = mscode_parse(&ctx);
+ if (ret < 0)
+ goto error;
+
+ pr_debug("Digest: %u [%*ph]\n",
+ ctx.digest_len, ctx.digest_len, ctx.digest);
+
+ /* Generate the digest and check against the PKCS7 certificate
+ * contents.
+ */
+ ret = pefile_digest_pe(pebuf, pelen, &ctx);
+ if (ret < 0)
+ goto error;
+
+ ret = pkcs7_verify(pkcs7);
+ if (ret < 0)
+ goto error;
+
+ ret = pkcs7_validate_trust(pkcs7, trusted_keyring, _trusted);
+
+error:
+ pkcs7_free_message(ctx.pkcs7);
+ return ret;
+}
diff --git a/crypto/asymmetric_keys/verify_pefile.h b/crypto/asymmetric_keys/verify_pefile.h
new file mode 100644
index 000000000000..55d5f7ebc45a
--- /dev/null
+++ b/crypto/asymmetric_keys/verify_pefile.h
@@ -0,0 +1,42 @@
+/* PE Binary parser bits
+ *
+ * Copyright (C) 2014 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.
+ */
+
+#include <linux/verify_pefile.h>
+#include <crypto/pkcs7.h>
+#include <crypto/hash_info.h>
+
+struct pefile_context {
+ unsigned header_size;
+ unsigned image_checksum_offset;
+ unsigned cert_dirent_offset;
+ unsigned n_data_dirents;
+ unsigned n_sections;
+ unsigned certs_size;
+ unsigned sig_offset;
+ unsigned sig_len;
+ const struct section_header *secs;
+ struct pkcs7_message *pkcs7;
+
+ /* PKCS#7 MS Individual Code Signing content */
+ const void *digest; /* Digest */
+ unsigned digest_len; /* Digest length */
+ enum hash_algo digest_algo; /* Digest algorithm */
+};
+
+#define kenter(FMT, ...) \
+ pr_devel("==> %s("FMT")\n", __func__, ##__VA_ARGS__)
+#define kleave(FMT, ...) \
+ pr_devel("<== %s()"FMT"\n", __func__, ##__VA_ARGS__)
+
+/*
+ * mscode_parser.c
+ */
+extern int mscode_parse(struct pefile_context *ctx);
diff --git a/crypto/asymmetric_keys/x509.asn1 b/crypto/asymmetric_keys/x509.asn1
index bf32b3dff088..aae0cde414e2 100644
--- a/crypto/asymmetric_keys/x509.asn1
+++ b/crypto/asymmetric_keys/x509.asn1
@@ -6,7 +6,7 @@ Certificate ::= SEQUENCE {
TBSCertificate ::= SEQUENCE {
version [ 0 ] Version DEFAULT,
- serialNumber CertificateSerialNumber,
+ serialNumber CertificateSerialNumber ({ x509_note_serial }),
signature AlgorithmIdentifier ({ x509_note_pkey_algo }),
issuer Name ({ x509_note_issuer }),
validity Validity,
diff --git a/crypto/asymmetric_keys/x509_cert_parser.c b/crypto/asymmetric_keys/x509_cert_parser.c
index 29893162497c..ac72348c186a 100644
--- a/crypto/asymmetric_keys/x509_cert_parser.c
+++ b/crypto/asymmetric_keys/x509_cert_parser.c
@@ -11,6 +11,7 @@
#define pr_fmt(fmt) "X.509: "fmt
#include <linux/kernel.h>
+#include <linux/export.h>
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/oid_registry.h>
@@ -52,6 +53,7 @@ void x509_free_certificate(struct x509_certificate *cert)
kfree(cert);
}
}
+EXPORT_SYMBOL_GPL(x509_free_certificate);
/*
* Parse an X.509 certificate
@@ -97,6 +99,7 @@ error_no_ctx:
error_no_cert:
return ERR_PTR(ret);
}
+EXPORT_SYMBOL_GPL(x509_cert_parse);
/*
* Note an OID when we find one for later processing when we know how
@@ -211,6 +214,19 @@ int x509_note_signature(void *context, size_t hdrlen,
}
/*
+ * Note the certificate serial number
+ */
+int x509_note_serial(void *context, size_t hdrlen,
+ unsigned char tag,
+ const void *value, size_t vlen)
+{
+ struct x509_parse_context *ctx = context;
+ ctx->cert->raw_serial = value;
+ ctx->cert->raw_serial_size = vlen;
+ return 0;
+}
+
+/*
* Note some of the name segments from which we'll fabricate a name.
*/
int x509_extract_name_segment(void *context, size_t hdrlen,
@@ -322,6 +338,8 @@ int x509_note_issuer(void *context, size_t hdrlen,
const void *value, size_t vlen)
{
struct x509_parse_context *ctx = context;
+ ctx->cert->raw_issuer = value;
+ ctx->cert->raw_issuer_size = vlen;
return x509_fabricate_name(ctx, hdrlen, tag, &ctx->cert->issuer, vlen);
}
@@ -330,6 +348,8 @@ int x509_note_subject(void *context, size_t hdrlen,
const void *value, size_t vlen)
{
struct x509_parse_context *ctx = context;
+ ctx->cert->raw_subject = value;
+ ctx->cert->raw_subject_size = vlen;
return x509_fabricate_name(ctx, hdrlen, tag, &ctx->cert->subject, vlen);
}
diff --git a/crypto/asymmetric_keys/x509_parser.h b/crypto/asymmetric_keys/x509_parser.h
index 87d9cc26f630..1b76f207c1f3 100644
--- a/crypto/asymmetric_keys/x509_parser.h
+++ b/crypto/asymmetric_keys/x509_parser.h
@@ -14,7 +14,9 @@
struct x509_certificate {
struct x509_certificate *next;
+ struct x509_certificate *signer; /* Certificate that signed this one */
struct public_key *pub; /* Public key details */
+ struct public_key_signature sig; /* Signature parameters */
char *issuer; /* Name of certificate issuer */
char *subject; /* Name of certificate subject */
char *fingerprint; /* Key fingerprint as hex */
@@ -25,7 +27,16 @@ struct x509_certificate {
unsigned tbs_size; /* Size of signed data */
unsigned raw_sig_size; /* Size of sigature */
const void *raw_sig; /* Signature data */
- struct public_key_signature sig; /* Signature parameters */
+ const void *raw_serial; /* Raw serial number in ASN.1 */
+ unsigned raw_serial_size;
+ unsigned raw_issuer_size;
+ const void *raw_issuer; /* Raw issuer name in ASN.1 */
+ const void *raw_subject; /* Raw subject name in ASN.1 */
+ unsigned raw_subject_size;
+ unsigned index;
+ bool seen; /* Infinite recursion prevention */
+ bool verified;
+ bool trusted;
};
/*
diff --git a/crypto/asymmetric_keys/x509_public_key.c b/crypto/asymmetric_keys/x509_public_key.c
index 382ef0d2ff2e..f3d62307e6ee 100644
--- a/crypto/asymmetric_keys/x509_public_key.c
+++ b/crypto/asymmetric_keys/x509_public_key.c
@@ -18,11 +18,86 @@
#include <linux/asn1_decoder.h>
#include <keys/asymmetric-subtype.h>
#include <keys/asymmetric-parser.h>
+#include <keys/system_keyring.h>
#include <crypto/hash.h>
#include "asymmetric_keys.h"
#include "public_key.h"
#include "x509_parser.h"
+static bool use_builtin_keys;
+static char *ca_keyid;
+
+#ifndef MODULE
+static int __init ca_keys_setup(char *str)
+{
+ if (!str) /* default system keyring */
+ return 1;
+
+ if (strncmp(str, "id:", 3) == 0)
+ ca_keyid = str; /* owner key 'id:xxxxxx' */
+ else if (strcmp(str, "builtin") == 0)
+ use_builtin_keys = true;
+
+ return 1;
+}
+__setup("ca_keys=", ca_keys_setup);
+#endif
+
+/**
+ * x509_request_asymmetric_key - Request a key by X.509 certificate params.
+ * @keyring: The keys to search.
+ * @subject: The name of the subject to whom the key belongs.
+ * @key_id: The subject key ID as a hex string.
+ *
+ * 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.
+ */
+struct key *x509_request_asymmetric_key(struct key *keyring,
+ const char *subject,
+ const char *key_id)
+{
+ key_ref_t key;
+ size_t subject_len = strlen(subject), key_id_len = strlen(key_id);
+ char *id;
+
+ /* Construct an identifier "<subjname>:<keyid>". */
+ id = kmalloc(subject_len + 2 + key_id_len + 1, GFP_KERNEL);
+ if (!id)
+ return ERR_PTR(-ENOMEM);
+
+ memcpy(id, subject, subject_len);
+ id[subject_len + 0] = ':';
+ id[subject_len + 1] = ' ';
+ memcpy(id + subject_len + 2, key_id, key_id_len);
+ id[subject_len + 2 + key_id_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 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);
+}
+EXPORT_SYMBOL_GPL(x509_request_asymmetric_key);
+
/*
* Set up the signature parameters in an X.509 certificate. This involves
* digesting the signed data and extracting the signature.
@@ -103,6 +178,38 @@ int x509_check_signature(const struct public_key *pub,
EXPORT_SYMBOL_GPL(x509_check_signature);
/*
+ * Check the new certificate against the ones in the trust keyring. If one of
+ * those is the signing key and validates the new certificate, then mark the
+ * new certificate as being trusted.
+ *
+ * Return 0 if the new certificate was successfully validated, 1 if we couldn't
+ * find a matching parent certificate in the trusted list and an error if there
+ * is a matching certificate but the signature check fails.
+ */
+static int x509_validate_trust(struct x509_certificate *cert,
+ struct key *trust_keyring)
+{
+ struct key *key;
+ int ret = 1;
+
+ if (!trust_keyring)
+ return -EOPNOTSUPP;
+
+ if (ca_keyid && !asymmetric_keyid_match(cert->authority, ca_keyid))
+ return -EPERM;
+
+ key = x509_request_asymmetric_key(trust_keyring,
+ cert->issuer, cert->authority);
+ if (!IS_ERR(key)) {
+ if (!use_builtin_keys
+ || test_bit(KEY_FLAG_BUILTIN, &key->flags))
+ ret = x509_check_signature(key->payload.data, cert);
+ key_put(key);
+ }
+ return ret;
+}
+
+/*
* Attempt to parse a data blob for a key as an X509 certificate.
*/
static int x509_key_preparse(struct key_preparsed_payload *prep)
@@ -155,9 +262,13 @@ static int x509_key_preparse(struct key_preparsed_payload *prep)
/* Check the signature on the key if it appears to be self-signed */
if (!cert->authority ||
strcmp(cert->fingerprint, cert->authority) == 0) {
- ret = x509_check_signature(cert->pub, cert);
+ ret = x509_check_signature(cert->pub, cert); /* self-signed */
if (ret < 0)
goto error_free_cert;
+ } else if (!prep->trusted) {
+ ret = x509_validate_trust(cert, get_system_trusted_keyring());
+ if (!ret)
+ prep->trusted = 1;
}
/* Propose a description */
@@ -177,7 +288,7 @@ static int x509_key_preparse(struct key_preparsed_payload *prep)
__module_get(public_key_subtype.owner);
prep->type_data[0] = &public_key_subtype;
prep->type_data[1] = cert->fingerprint;
- prep->payload = cert->pub;
+ prep->payload[0] = cert->pub;
prep->description = desc;
prep->quotalen = 100;