From 84aabd46bf8791d0c6fc8db4dc65d45093f70aab Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 1 Jul 2014 16:40:19 +0100 Subject: X.509: Add bits needed for PKCS#7 PKCS#7 validation requires access to the serial number and the raw names in an X.509 certificate. Signed-off-by: David Howells Reviewed-by: Kees Cook Reviewed-by: Josh Boyer --- crypto/asymmetric_keys/x509.asn1 | 2 +- crypto/asymmetric_keys/x509_cert_parser.c | 17 +++++++++++++++++ crypto/asymmetric_keys/x509_parser.h | 13 ++++++++++++- 3 files changed, 30 insertions(+), 2 deletions(-) (limited to 'crypto') 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..4a8df29ab713 100644 --- a/crypto/asymmetric_keys/x509_cert_parser.c +++ b/crypto/asymmetric_keys/x509_cert_parser.c @@ -210,6 +210,19 @@ int x509_note_signature(void *context, size_t hdrlen, return 0; } +/* + * 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. */ @@ -322,6 +335,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 +345,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; }; /* -- cgit v1.2.3 From ace0107a3936774e9da2c30cbdc99ad13c103c10 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 1 Jul 2014 16:40:19 +0100 Subject: X.509: Export certificate parse and free functions Export certificate parse and free functions for use by modules. Signed-off-by: David Howells Acked-by: Vivek Goyal Reviewed-by: Kees Cook Reviewed-by: Josh Boyer --- crypto/asymmetric_keys/x509_cert_parser.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'crypto') diff --git a/crypto/asymmetric_keys/x509_cert_parser.c b/crypto/asymmetric_keys/x509_cert_parser.c index 4a8df29ab713..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 +#include #include #include #include @@ -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 -- cgit v1.2.3 From 2e3fadbf730fd0d13c891d5e555af3e7f39ca3f4 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 1 Jul 2014 16:40:19 +0100 Subject: PKCS#7: Implement a parser [RFC 2315] Implement a parser for a PKCS#7 signed-data message as described in part of RFC 2315. Signed-off-by: David Howells Acked-by: Vivek Goyal Reviewed-by: Kees Cook --- crypto/asymmetric_keys/Kconfig | 9 + crypto/asymmetric_keys/Makefile | 13 ++ crypto/asymmetric_keys/pkcs7.asn1 | 127 +++++++++++ crypto/asymmetric_keys/pkcs7_parser.c | 396 ++++++++++++++++++++++++++++++++++ crypto/asymmetric_keys/pkcs7_parser.h | 61 ++++++ include/crypto/pkcs7.h | 23 ++ include/linux/oid_registry.h | 1 + 7 files changed, 630 insertions(+) create mode 100644 crypto/asymmetric_keys/pkcs7.asn1 create mode 100644 crypto/asymmetric_keys/pkcs7_parser.c create mode 100644 crypto/asymmetric_keys/pkcs7_parser.h create mode 100644 include/crypto/pkcs7.h (limited to 'crypto') diff --git a/crypto/asymmetric_keys/Kconfig b/crypto/asymmetric_keys/Kconfig index 03a6eb95ab50..a7cec9dd6154 100644 --- a/crypto/asymmetric_keys/Kconfig +++ b/crypto/asymmetric_keys/Kconfig @@ -37,4 +37,13 @@ config X509_CERTIFICATE_PARSER 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. + endif # ASYMMETRIC_KEY_TYPE diff --git a/crypto/asymmetric_keys/Makefile b/crypto/asymmetric_keys/Makefile index 0727204aab68..59d8cade5cfe 100644 --- a/crypto/asymmetric_keys/Makefile +++ b/crypto/asymmetric_keys/Makefile @@ -25,3 +25,16 @@ $(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 + +$(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 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_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 +#include +#include +#include +#include +#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 +#include +#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/include/crypto/pkcs7.h b/include/crypto/pkcs7.h new file mode 100644 index 000000000000..8f2628fbdfce --- /dev/null +++ b/include/crypto/pkcs7.h @@ -0,0 +1,23 @@ +/* PKCS#7 crypto data 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. + */ + +struct pkcs7_message; + +/* + * pkcs7_parser.c + */ +extern struct pkcs7_message *pkcs7_parse_message(const void *data, + size_t datalen); +extern void pkcs7_free_message(struct pkcs7_message *pkcs7); + +extern int pkcs7_get_content_data(const struct pkcs7_message *pkcs7, + const void **_data, size_t *_datalen, + bool want_wrapper); diff --git a/include/linux/oid_registry.h b/include/linux/oid_registry.h index 6926db724258..edeff85cb1e8 100644 --- a/include/linux/oid_registry.h +++ b/include/linux/oid_registry.h @@ -55,6 +55,7 @@ enum OID { OID_certAuthInfoAccess, /* 1.3.6.1.5.5.7.1.1 */ OID_msOutlookExpress, /* 1.3.6.1.4.1.311.16.4 */ OID_sha1, /* 1.3.14.3.2.26 */ + OID_sha256, /* 2.16.840.1.101.3.4.2.1 */ /* Distinguished Name attribute IDs [RFC 2256] */ OID_commonName, /* 2.5.4.3 */ -- cgit v1.2.3 From 9f0d33146e2ae81342a493c579c0e0c1aa84a527 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 1 Jul 2014 16:40:19 +0100 Subject: PKCS#7: Digest the data in a signed-data message Digest the data in a PKCS#7 signed-data message and attach to the public_key_signature struct contained in the pkcs7_message struct. Signed-off-by: David Howells Acked-by: Vivek Goyal Reviewed-by: Kees Cook --- crypto/asymmetric_keys/Makefile | 3 +- crypto/asymmetric_keys/pkcs7_verify.c | 173 ++++++++++++++++++++++++++++++++++ 2 files changed, 175 insertions(+), 1 deletion(-) create mode 100644 crypto/asymmetric_keys/pkcs7_verify.c (limited to 'crypto') diff --git a/crypto/asymmetric_keys/Makefile b/crypto/asymmetric_keys/Makefile index 59d8cade5cfe..b6b39e7bea01 100644 --- a/crypto/asymmetric_keys/Makefile +++ b/crypto/asymmetric_keys/Makefile @@ -32,7 +32,8 @@ clean-files += x509_rsakey-asn1.c x509_rsakey-asn1.h obj-$(CONFIG_PKCS7_MESSAGE_PARSER) += pkcs7_message.o pkcs7_message-y := \ pkcs7-asn1.o \ - pkcs7_parser.o + pkcs7_parser.o \ + pkcs7_verify.o $(obj)/pkcs7_parser.o: $(obj)/pkcs7-asn1.h $(obj)/pkcs7-asn1.o: $(obj)/pkcs7-asn1.c $(obj)/pkcs7-asn1.h diff --git a/crypto/asymmetric_keys/pkcs7_verify.c b/crypto/asymmetric_keys/pkcs7_verify.c new file mode 100644 index 000000000000..0bb408a5b64f --- /dev/null +++ b/crypto/asymmetric_keys/pkcs7_verify.c @@ -0,0 +1,173 @@ +/* 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 +#include +#include +#include +#include +#include +#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; +} + +/* +/* + * 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; + + return 0; +} + +/** + * 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); -- cgit v1.2.3 From a4730357ee724f8c64f0292541ba3da8a95510fb Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 1 Jul 2014 16:40:19 +0100 Subject: PKCS#7: Find the right key in the PKCS#7 key list and verify the signature Find the appropriate key in the PKCS#7 key list and verify the signature with it. There may be several keys in there forming a chain. Any link in that chain or the root of that chain may be in our keyrings. Signed-off-by: David Howells Acked-by: Vivek Goyal Reviewed-by: Kees Cook --- crypto/asymmetric_keys/pkcs7_verify.c | 64 +++++++++++++++++++++++++++++++++++ include/crypto/pkcs7.h | 5 +++ 2 files changed, 69 insertions(+) (limited to 'crypto') diff --git a/crypto/asymmetric_keys/pkcs7_verify.c b/crypto/asymmetric_keys/pkcs7_verify.c index 0bb408a5b64f..745e5c86a35e 100644 --- a/crypto/asymmetric_keys/pkcs7_verify.c +++ b/crypto/asymmetric_keys/pkcs7_verify.c @@ -120,6 +120,55 @@ error_no_desc: } /* + * 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 one signed information block from a PKCS#7 message. */ @@ -137,6 +186,21 @@ static int pkcs7_verify_one(struct pkcs7_message *pkcs7, 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); + return 0; } diff --git a/include/crypto/pkcs7.h b/include/crypto/pkcs7.h index 8f2628fbdfce..8ba0f3810d67 100644 --- a/include/crypto/pkcs7.h +++ b/include/crypto/pkcs7.h @@ -21,3 +21,8 @@ extern void pkcs7_free_message(struct pkcs7_message *pkcs7); extern int pkcs7_get_content_data(const struct pkcs7_message *pkcs7, const void **_data, size_t *_datalen, bool want_wrapper); + +/* + * pkcs7_verify.c + */ +extern int pkcs7_verify(struct pkcs7_message *pkcs7); -- cgit v1.2.3 From 8c76d79393ccc9b89d9af402d79a49a9cd43c5aa Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 1 Jul 2014 16:40:19 +0100 Subject: PKCS#7: Verify internal certificate chain Verify certificate chain in the X.509 certificates contained within the PKCS#7 message as far as possible. If any signature that we should be able to verify fails, we reject the whole lot. Signed-off-by: David Howells Acked-by: Vivek Goyal Reviewed-by: Kees Cook --- crypto/asymmetric_keys/pkcs7_verify.c | 88 ++++++++++++++++++++++++++++++++++- 1 file changed, 87 insertions(+), 1 deletion(-) (limited to 'crypto') diff --git a/crypto/asymmetric_keys/pkcs7_verify.c b/crypto/asymmetric_keys/pkcs7_verify.c index 745e5c86a35e..51ff36f3a913 100644 --- a/crypto/asymmetric_keys/pkcs7_verify.c +++ b/crypto/asymmetric_keys/pkcs7_verify.c @@ -169,6 +169,91 @@ static int pkcs7_find_key(struct pkcs7_message *pkcs7, 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; + + if (x509->issuer) + pr_debug("- issuer %s\n", x509->issuer); + if (x509->authority) + pr_debug("- authkeyid %s\n", x509->authority); + + if (!x509->authority || + (x509->subject && + 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. */ @@ -201,7 +286,8 @@ static int pkcs7_verify_one(struct pkcs7_message *pkcs7, pr_devel("Verified signature %u\n", sinfo->index); - return 0; + /* Verify the internal certificate chain */ + return pkcs7_verify_sig_chain(pkcs7, sinfo); } /** -- cgit v1.2.3 From 08815b62d700e4fbeb72a01986ad051c3dd84a15 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 1 Jul 2014 16:40:20 +0100 Subject: 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 Acked-by: Vivek Goyal Reviewed-by: Kees Cook --- crypto/asymmetric_keys/Makefile | 1 + crypto/asymmetric_keys/pkcs7_trust.c | 219 +++++++++++++++++++++++++++++++++++ include/crypto/pkcs7.h | 8 ++ 3 files changed, 228 insertions(+) create mode 100644 crypto/asymmetric_keys/pkcs7_trust.c (limited to 'crypto') diff --git a/crypto/asymmetric_keys/Makefile b/crypto/asymmetric_keys/Makefile index b6b39e7bea01..d63cb4320b96 100644 --- a/crypto/asymmetric_keys/Makefile +++ b/crypto/asymmetric_keys/Makefile @@ -33,6 +33,7 @@ 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 diff --git a/crypto/asymmetric_keys/pkcs7_trust.c b/crypto/asymmetric_keys/pkcs7_trust.c new file mode 100644 index 000000000000..b6b045131403 --- /dev/null +++ b/crypto/asymmetric_keys/pkcs7_trust.c @@ -0,0 +1,219 @@ +/* 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); +} + +/** + * 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 = 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 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 = 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 : -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/include/crypto/pkcs7.h b/include/crypto/pkcs7.h index 8ba0f3810d67..691c79172a26 100644 --- a/include/crypto/pkcs7.h +++ b/include/crypto/pkcs7.h @@ -9,6 +9,7 @@ * 2 of the Licence, or (at your option) any later version. */ +struct key; struct pkcs7_message; /* @@ -22,6 +23,13 @@ extern int pkcs7_get_content_data(const struct pkcs7_message *pkcs7, const void **_data, size_t *_datalen, bool want_wrapper); +/* + * pkcs7_trust.c + */ +extern int pkcs7_validate_trust(struct pkcs7_message *pkcs7, + struct key *trust_keyring, + bool *_trusted); + /* * pkcs7_verify.c */ -- cgit v1.2.3 From 22d01afb210ff77fc480a1fc531cd59a4f32157a Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 1 Jul 2014 19:06:18 +0100 Subject: PKCS#7: Provide a key type for testing PKCS#7 Provide a key type for testing the PKCS#7 parser. It is given a non-detached PKCS#7 message as payload: keyctl padd pkcs7_test a @s out stuff.txt: echo "The quick red fox jumped over the lazy brown dog" >stuff.txt certs: key1.x509 key2.x509 key3.x509 key4.x509 cat key{1,3}.x509 >$@ ############################################################################### # # Generate a signed key # # openssl x509 -text -inform PEM -noout -in key2.x509 # ############################################################################### key2.x509: key2.x509_unsigned key1.priv key1.x509 openssl x509 \ -req -in key2.x509_unsigned \ -out key2.x509 \ -extfile key2.genkey -extensions myexts \ -CA key1.x509 \ -CAkey key1.priv \ -CAcreateserial key2.priv key2.x509_unsigned: key2.genkey openssl req -new -nodes -utf8 -sha1 -days 36500 \ -batch -outform PEM \ -config key2.genkey \ -keyout key2.priv \ -out key2.x509_unsigned key2.genkey: @echo Generating X.509 key generation config @echo >$@ "[ req ]" @echo >>$@ "default_bits = 4096" @echo >>$@ "distinguished_name = req_distinguished_name" @echo >>$@ "prompt = no" @echo >>$@ "string_mask = utf8only" @echo >>$@ "x509_extensions = myexts" @echo >>$@ @echo >>$@ "[ req_distinguished_name ]" @echo >>$@ "O = Magrathea" @echo >>$@ "CN = PKCS7 key 2" @echo >>$@ "emailAddress = slartibartfast@magrathea.h2g2" @echo >>$@ @echo >>$@ "[ myexts ]" @echo >>$@ "basicConstraints=critical,CA:FALSE" @echo >>$@ "keyUsage=digitalSignature" @echo >>$@ "subjectKeyIdentifier=hash" @echo >>$@ "authorityKeyIdentifier=keyid" ############################################################################### # # Generate a couple of signing keys # # openssl x509 -text -inform PEM -noout -in key1.x509 # ############################################################################### key1.x509: key1.x509_unsigned key4.priv key4.x509 openssl x509 \ -req -in key1.x509_unsigned \ -out key1.x509 \ -extfile key1.genkey -extensions myexts \ -CA key4.x509 \ -CAkey key4.priv \ -CAcreateserial key1.priv key1.x509_unsigned: key1.genkey openssl req -new -nodes -utf8 -sha1 -days 36500 \ -batch -outform PEM \ -config key1.genkey \ -keyout key1.priv \ -out key1.x509_unsigned key1.genkey: @echo Generating X.509 key generation config @echo >$@ "[ req ]" @echo >>$@ "default_bits = 4096" @echo >>$@ "distinguished_name = req_distinguished_name" @echo >>$@ "prompt = no" @echo >>$@ "string_mask = utf8only" @echo >>$@ "x509_extensions = myexts" @echo >>$@ @echo >>$@ "[ req_distinguished_name ]" @echo >>$@ "O = Magrathea" @echo >>$@ "CN = PKCS7 key 1" @echo >>$@ "emailAddress = slartibartfast@magrathea.h2g2" @echo >>$@ @echo >>$@ "[ myexts ]" @echo >>$@ "basicConstraints=critical,CA:TRUE" @echo >>$@ "keyUsage=digitalSignature,keyCertSign" @echo >>$@ "subjectKeyIdentifier=hash" @echo >>$@ "authorityKeyIdentifier=keyid" ############################################################################### # # Generate a signed key # # openssl x509 -text -inform PEM -noout -in key4.x509 # ############################################################################### key4.x509: key4.x509_unsigned key3.priv key3.x509 openssl x509 \ -req -in key4.x509_unsigned \ -out key4.x509 \ -extfile key4.genkey -extensions myexts \ -CA key3.x509 \ -CAkey key3.priv \ -CAcreateserial key4.priv key4.x509_unsigned: key4.genkey openssl req -new -nodes -utf8 -sha1 -days 36500 \ -batch -outform PEM \ -config key4.genkey \ -keyout key4.priv \ -out key4.x509_unsigned key4.genkey: @echo Generating X.509 key generation config @echo >$@ "[ req ]" @echo >>$@ "default_bits = 4096" @echo >>$@ "distinguished_name = req_distinguished_name" @echo >>$@ "prompt = no" @echo >>$@ "string_mask = utf8only" @echo >>$@ "x509_extensions = myexts" @echo >>$@ @echo >>$@ "[ req_distinguished_name ]" @echo >>$@ "O = Magrathea" @echo >>$@ "CN = PKCS7 key 4" @echo >>$@ "emailAddress = slartibartfast@magrathea.h2g2" @echo >>$@ @echo >>$@ "[ myexts ]" @echo >>$@ "basicConstraints=critical,CA:TRUE" @echo >>$@ "keyUsage=digitalSignature,keyCertSign" @echo >>$@ "subjectKeyIdentifier=hash" @echo >>$@ "authorityKeyIdentifier=keyid" ############################################################################### # # Generate a couple of signing keys # # openssl x509 -text -inform PEM -noout -in key3.x509 # ############################################################################### key3.priv key3.x509: key3.genkey openssl req -new -nodes -utf8 -sha1 -days 36500 \ -batch -x509 -outform PEM \ -config key3.genkey \ -keyout key3.priv \ -out key3.x509 key3.genkey: @echo Generating X.509 key generation config @echo >$@ "[ req ]" @echo >>$@ "default_bits = 4096" @echo >>$@ "distinguished_name = req_distinguished_name" @echo >>$@ "prompt = no" @echo >>$@ "string_mask = utf8only" @echo >>$@ "x509_extensions = myexts" @echo >>$@ @echo >>$@ "[ req_distinguished_name ]" @echo >>$@ "O = Magrathea" @echo >>$@ "CN = PKCS7 key 3" @echo >>$@ "emailAddress = slartibartfast@magrathea.h2g2" @echo >>$@ @echo >>$@ "[ myexts ]" @echo >>$@ "basicConstraints=critical,CA:TRUE" @echo >>$@ "keyUsage=digitalSignature,keyCertSign" @echo >>$@ "subjectKeyIdentifier=hash" @echo >>$@ "authorityKeyIdentifier=keyid" clean: $(RM) *~ $(RM) key1.* key2.* key3.* key4.* stuff.* out certs Signed-off-by: David Howells --- crypto/asymmetric_keys/Kconfig | 13 +++++ crypto/asymmetric_keys/Makefile | 7 +++ crypto/asymmetric_keys/pkcs7_key_type.c | 97 +++++++++++++++++++++++++++++++++ 3 files changed, 117 insertions(+) create mode 100644 crypto/asymmetric_keys/pkcs7_key_type.c (limited to 'crypto') diff --git a/crypto/asymmetric_keys/Kconfig b/crypto/asymmetric_keys/Kconfig index a7cec9dd6154..b6df198d1b6f 100644 --- a/crypto/asymmetric_keys/Kconfig +++ b/crypto/asymmetric_keys/Kconfig @@ -46,4 +46,17 @@ config PKCS7_MESSAGE_PARSER 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. + endif # ASYMMETRIC_KEY_TYPE diff --git a/crypto/asymmetric_keys/Makefile b/crypto/asymmetric_keys/Makefile index d63cb4320b96..92d0e9af24d5 100644 --- a/crypto/asymmetric_keys/Makefile +++ b/crypto/asymmetric_keys/Makefile @@ -40,3 +40,10 @@ $(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 diff --git a/crypto/asymmetric_keys/pkcs7_key_type.c b/crypto/asymmetric_keys/pkcs7_key_type.c new file mode 100644 index 000000000000..b1797d2516e2 --- /dev/null +++ b/crypto/asymmetric_keys/pkcs7_key_type.c @@ -0,0 +1,97 @@ +/* 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 +#include +#include +#include +#include +#include "pkcs7_parser.h" + +/* + * Instantiate a PKCS#7 wrapped and validated key. + */ +int pkcs7_instantiate(struct key *key, 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_instantiate(key, 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 + */ +struct key_type key_type_pkcs7 = { + .name = "pkcs7_test", + .def_lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT, + .instantiate = pkcs7_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); -- cgit v1.2.3 From 452069867c1dcfe9cb60fd89bb1c92f5ec2fb3c8 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 8 Jul 2014 17:21:01 +0100 Subject: KEYS: X.509: Fix a spelling mistake Signed-off-by: David Howells Acked-by: Vivek Goyal --- crypto/asymmetric_keys/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'crypto') diff --git a/crypto/asymmetric_keys/Kconfig b/crypto/asymmetric_keys/Kconfig index b6df198d1b6f..14cac2860afa 100644 --- a/crypto/asymmetric_keys/Kconfig +++ b/crypto/asymmetric_keys/Kconfig @@ -33,7 +33,7 @@ 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. -- cgit v1.2.3 From 26d1164be37f1145a96af15f294122876d8e5c77 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 1 Jul 2014 16:02:51 +0100 Subject: pefile: Parse a PE binary to find a key and a signature contained therein Parse a PE binary to find a key and a signature contained therein. Later patches will check the signature and add the key if the signature checks out. Signed-off-by: David Howells Acked-by: Vivek Goyal Reviewed-by: Kees Cook --- crypto/asymmetric_keys/Kconfig | 9 ++ crypto/asymmetric_keys/Makefile | 8 ++ crypto/asymmetric_keys/verify_pefile.c | 163 +++++++++++++++++++++++++++++++++ crypto/asymmetric_keys/verify_pefile.h | 37 ++++++++ include/linux/verify_pefile.h | 18 ++++ 5 files changed, 235 insertions(+) create mode 100644 crypto/asymmetric_keys/verify_pefile.c create mode 100644 crypto/asymmetric_keys/verify_pefile.h create mode 100644 include/linux/verify_pefile.h (limited to 'crypto') diff --git a/crypto/asymmetric_keys/Kconfig b/crypto/asymmetric_keys/Kconfig index 14cac2860afa..ca41be5631c7 100644 --- a/crypto/asymmetric_keys/Kconfig +++ b/crypto/asymmetric_keys/Kconfig @@ -59,4 +59,13 @@ config PKCS7_TEST_KEY 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 92d0e9af24d5..3e4de5297015 100644 --- a/crypto/asymmetric_keys/Makefile +++ b/crypto/asymmetric_keys/Makefile @@ -47,3 +47,11 @@ clean-files += pkcs7-asn1.c pkcs7-asn1.h 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 diff --git a/crypto/asymmetric_keys/verify_pefile.c b/crypto/asymmetric_keys/verify_pefile.c new file mode 100644 index 000000000000..aec7c509404e --- /dev/null +++ b/crypto/asymmetric_keys/verify_pefile.c @@ -0,0 +1,163 @@ +/* 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 +#include +#include +#include +#include +#include +#include +#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; +} + +/** + * 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 pefile_context ctx; + int ret; + + kenter(""); + + memset(&ctx, 0, sizeof(ctx)); + ret = pefile_parse_binary(pebuf, pelen, &ctx); + if (ret < 0) + return ret; + + return -ENOANO; // Not yet complete +} diff --git a/crypto/asymmetric_keys/verify_pefile.h b/crypto/asymmetric_keys/verify_pefile.h new file mode 100644 index 000000000000..e165d23458d4 --- /dev/null +++ b/crypto/asymmetric_keys/verify_pefile.h @@ -0,0 +1,37 @@ +/* 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 +#include +#include + +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__) diff --git a/include/linux/verify_pefile.h b/include/linux/verify_pefile.h new file mode 100644 index 000000000000..ac34819214f9 --- /dev/null +++ b/include/linux/verify_pefile.h @@ -0,0 +1,18 @@ +/* Signed PE file verification + * + * 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. + */ + +#ifndef _LINUX_VERIFY_PEFILE_H +#define _LINUX_VERIFY_PEFILE_H + +extern int verify_pefile_signature(const void *pebuf, unsigned pelen, + struct key *trusted_keyring, bool *_trusted); + +#endif /* _LINUX_VERIFY_PEFILE_H */ -- cgit v1.2.3 From 09dacbbda935895a4898e517e1248c11ca493216 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 1 Jul 2014 16:02:51 +0100 Subject: pefile: Strip the wrapper off of the cert data block The certificate data block in a PE binary has a wrapper around the PKCS#7 signature we actually want to get at. Strip this off and check that we've got something that appears to be a PKCS#7 signature. Signed-off-by: David Howells Acked-by: Vivek Goyal Reviewed-by: Kees Cook --- crypto/asymmetric_keys/verify_pefile.c | 71 ++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) (limited to 'crypto') diff --git a/crypto/asymmetric_keys/verify_pefile.c b/crypto/asymmetric_keys/verify_pefile.c index aec7c509404e..2f5268cb843d 100644 --- a/crypto/asymmetric_keys/verify_pefile.c +++ b/crypto/asymmetric_keys/verify_pefile.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include "verify_pefile.h" @@ -118,6 +119,72 @@ static int pefile_parse_binary(const void *pebuf, unsigned int pelen, 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; +} + /** * verify_pefile_signature - Verify the signature on a PE binary image * @pebuf: Buffer containing the PE binary image @@ -159,5 +226,9 @@ int verify_pefile_signature(const void *pebuf, unsigned pelen, if (ret < 0) return ret; + ret = pefile_strip_sig_wrapper(pebuf, &ctx); + if (ret < 0) + return ret; + return -ENOANO; // Not yet complete } -- cgit v1.2.3 From 3968280c7699f11e27a21aeafacf50bc86c2ed25 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 1 Jul 2014 16:02:51 +0100 Subject: pefile: Parse the presumed PKCS#7 content of the certificate blob Parse the content of the certificate blob, presuming it to be PKCS#7 format. Signed-off-by: David Howells Acked-by: Vivek Goyal Reviewed-by: Kees Cook --- crypto/asymmetric_keys/verify_pefile.c | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) (limited to 'crypto') diff --git a/crypto/asymmetric_keys/verify_pefile.c b/crypto/asymmetric_keys/verify_pefile.c index 2f5268cb843d..13f3b44b5046 100644 --- a/crypto/asymmetric_keys/verify_pefile.c +++ b/crypto/asymmetric_keys/verify_pefile.c @@ -216,7 +216,10 @@ static int pefile_strip_sig_wrapper(const void *pebuf, 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(""); @@ -230,5 +233,21 @@ int verify_pefile_signature(const void *pebuf, unsigned pelen, if (ret < 0) return ret; - return -ENOANO; // Not yet complete + 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 = -ENOANO; // Not yet complete + +error: + pkcs7_free_message(ctx.pkcs7); + return ret; } -- cgit v1.2.3 From 4c0b4b1d1ae0cbc86f150e2905a1c3d2a17b7c1e Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 1 Jul 2014 16:02:52 +0100 Subject: pefile: Parse the "Microsoft individual code signing" data blob The PKCS#7 certificate should contain a "Microsoft individual code signing" data blob as its signed content. This blob contains a digest of the signed content of the PE binary and the OID of the digest algorithm used (typically SHA256). Signed-off-by: David Howells Acked-by: Vivek Goyal Reviewed-by: Kees Cook --- crypto/asymmetric_keys/Makefile | 9 ++- crypto/asymmetric_keys/mscode.asn1 | 28 ++++++++ crypto/asymmetric_keys/mscode_parser.c | 120 +++++++++++++++++++++++++++++++++ crypto/asymmetric_keys/verify_pefile.c | 7 ++ crypto/asymmetric_keys/verify_pefile.h | 5 ++ include/linux/oid_registry.h | 7 +- 6 files changed, 174 insertions(+), 2 deletions(-) create mode 100644 crypto/asymmetric_keys/mscode.asn1 create mode 100644 crypto/asymmetric_keys/mscode_parser.c (limited to 'crypto') diff --git a/crypto/asymmetric_keys/Makefile b/crypto/asymmetric_keys/Makefile index 3e4de5297015..e47fcd9ac5e8 100644 --- a/crypto/asymmetric_keys/Makefile +++ b/crypto/asymmetric_keys/Makefile @@ -54,4 +54,11 @@ pkcs7_test_key-y := \ obj-$(CONFIG_SIGNED_PE_FILE_VERIFICATION) += verify_signed_pefile.o verify_signed_pefile-y := \ - verify_pefile.o + 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/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..09336c32b3d4 --- /dev/null +++ b/crypto/asymmetric_keys/mscode_parser.c @@ -0,0 +1,120 @@ +/* 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 +#include +#include +#include +#include +#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; + } + + if (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/verify_pefile.c b/crypto/asymmetric_keys/verify_pefile.c index 13f3b44b5046..b975918e82d2 100644 --- a/crypto/asymmetric_keys/verify_pefile.c +++ b/crypto/asymmetric_keys/verify_pefile.c @@ -245,6 +245,13 @@ int verify_pefile_signature(const void *pebuf, unsigned pelen, 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); + ret = -ENOANO; // Not yet complete error: diff --git a/crypto/asymmetric_keys/verify_pefile.h b/crypto/asymmetric_keys/verify_pefile.h index e165d23458d4..55d5f7ebc45a 100644 --- a/crypto/asymmetric_keys/verify_pefile.h +++ b/crypto/asymmetric_keys/verify_pefile.h @@ -35,3 +35,8 @@ struct pefile_context { 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/include/linux/oid_registry.h b/include/linux/oid_registry.h index edeff85cb1e8..c2bbf672b84e 100644 --- a/include/linux/oid_registry.h +++ b/include/linux/oid_registry.h @@ -52,8 +52,13 @@ enum OID { OID_md4, /* 1.2.840.113549.2.4 */ OID_md5, /* 1.2.840.113549.2.5 */ - OID_certAuthInfoAccess, /* 1.3.6.1.5.5.7.1.1 */ + /* Microsoft Authenticode & Software Publishing */ + OID_msIndirectData, /* 1.3.6.1.4.1.311.2.1.4 */ + OID_msPeImageDataObjId, /* 1.3.6.1.4.1.311.2.1.15 */ + OID_msIndividualSPKeyPurpose, /* 1.3.6.1.4.1.311.2.1.21 */ OID_msOutlookExpress, /* 1.3.6.1.4.1.311.16.4 */ + + OID_certAuthInfoAccess, /* 1.3.6.1.5.5.7.1.1 */ OID_sha1, /* 1.3.14.3.2.26 */ OID_sha256, /* 2.16.840.1.101.3.4.2.1 */ -- cgit v1.2.3 From dd7d66f21b9eb6a3979d8c9ba910eba772cfbbc9 Mon Sep 17 00:00:00 2001 From: Vivek Goyal Date: Tue, 8 Jul 2014 18:10:46 +0100 Subject: pefile: Handle pesign using the wrong OID The pesign utility had a bug where it was using OID_msIndividualSPKeyPurpose instead of OID_msPeImageDataObjId - so allow both OIDs. Signed-off-by: Vivek Goyal Acked-by: Vivek Goyal --- crypto/asymmetric_keys/mscode_parser.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'crypto') diff --git a/crypto/asymmetric_keys/mscode_parser.c b/crypto/asymmetric_keys/mscode_parser.c index 09336c32b3d4..214a992123cd 100644 --- a/crypto/asymmetric_keys/mscode_parser.c +++ b/crypto/asymmetric_keys/mscode_parser.c @@ -58,7 +58,13 @@ int mscode_note_content_type(void *context, size_t hdrlen, return -EBADMSG; } - if (oid != OID_msIndividualSPKeyPurpose) { + /* + * 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; } -- cgit v1.2.3 From af316fc442ef23901bbfcec5af55e69ca6ce9563 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 1 Jul 2014 16:02:52 +0100 Subject: pefile: Digest the PE binary and compare to the PKCS#7 data Digest the signed parts of the PE binary, canonicalising the section table before we need it, and then compare the the resulting digest to the one in the PKCS#7 signed content. Signed-off-by: David Howells Acked-by: Vivek Goyal Reviewed-by: Kees Cook --- crypto/asymmetric_keys/verify_pefile.c | 197 +++++++++++++++++++++++++++++++++ 1 file changed, 197 insertions(+) (limited to 'crypto') diff --git a/crypto/asymmetric_keys/verify_pefile.c b/crypto/asymmetric_keys/verify_pefile.c index b975918e82d2..029a36510e80 100644 --- a/crypto/asymmetric_keys/verify_pefile.c +++ b/crypto/asymmetric_keys/verify_pefile.c @@ -185,6 +185,192 @@ static int pefile_strip_sig_wrapper(const void *pebuf, 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 @@ -252,6 +438,17 @@ int verify_pefile_signature(const void *pebuf, unsigned pelen, 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 = -ENOANO; // Not yet complete error: -- cgit v1.2.3 From 98801c002f7e573b4a86bcd5b234864d375e98a0 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 1 Jul 2014 16:02:52 +0100 Subject: pefile: Validate PKCS#7 trust chain Validate the PKCS#7 trust chain against the contents of the system keyring. Signed-off-by: David Howells Acked-by: Vivek Goyal --- crypto/asymmetric_keys/verify_pefile.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'crypto') diff --git a/crypto/asymmetric_keys/verify_pefile.c b/crypto/asymmetric_keys/verify_pefile.c index 029a36510e80..79175e6ea0b2 100644 --- a/crypto/asymmetric_keys/verify_pefile.c +++ b/crypto/asymmetric_keys/verify_pefile.c @@ -449,7 +449,7 @@ int verify_pefile_signature(const void *pebuf, unsigned pelen, if (ret < 0) goto error; - ret = -ENOANO; // Not yet complete + ret = pkcs7_validate_trust(pkcs7, trusted_keyring, _trusted); error: pkcs7_free_message(ctx.pkcs7); -- cgit v1.2.3 From 3be4beaf7c91ec9c6fefa5f11173af37113d10ae Mon Sep 17 00:00:00 2001 From: Mimi Zohar Date: Tue, 20 Aug 2013 14:36:27 -0400 Subject: KEYS: verify a certificate is signed by a 'trusted' key Only public keys, with certificates signed by an existing 'trusted' key on the system trusted keyring, should be added to a trusted keyring. This patch adds support for verifying a certificate's signature. This is derived from David Howells pkcs7_request_asymmetric_key() patch. Changelog v6: - on error free key - Dmitry - validate trust only for not already trusted keys - Dmitry - formatting cleanup Changelog: - define get_system_trusted_keyring() to fix kbuild issues Signed-off-by: Mimi Zohar Signed-off-by: David Howells Acked-by: Dmitry Kasatkin --- crypto/asymmetric_keys/x509_public_key.c | 87 +++++++++++++++++++++++++++++++- include/keys/system_keyring.h | 10 +++- 2 files changed, 95 insertions(+), 2 deletions(-) (limited to 'crypto') diff --git a/crypto/asymmetric_keys/x509_public_key.c b/crypto/asymmetric_keys/x509_public_key.c index 382ef0d2ff2e..436fbd8552fc 100644 --- a/crypto/asymmetric_keys/x509_public_key.c +++ b/crypto/asymmetric_keys/x509_public_key.c @@ -18,11 +18,61 @@ #include #include #include +#include #include #include "asymmetric_keys.h" #include "public_key.h" #include "x509_parser.h" +/* + * Find a key in the given keyring by issuer and authority. + */ +static struct key *x509_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; + + /* 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); +} + /* * Set up the signature parameters in an X.509 certificate. This involves * digesting the signed data and extracting the signature. @@ -102,6 +152,37 @@ 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) +{ + const struct public_key *pk; + struct key *key; + int ret = 1; + + if (!trust_keyring) + return -EOPNOTSUPP; + + key = x509_request_asymmetric_key(trust_keyring, + cert->issuer, strlen(cert->issuer), + cert->authority, + strlen(cert->authority)); + if (!IS_ERR(key)) { + pk = key->payload.data; + ret = x509_check_signature(pk, cert); + key_put(key); + } + return ret; +} + /* * Attempt to parse a data blob for a key as an X509 certificate. */ @@ -155,9 +236,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 */ diff --git a/include/keys/system_keyring.h b/include/keys/system_keyring.h index 8dabc399bd1d..72665eb80692 100644 --- a/include/keys/system_keyring.h +++ b/include/keys/system_keyring.h @@ -17,7 +17,15 @@ #include extern struct key *system_trusted_keyring; - +static inline struct key *get_system_trusted_keyring(void) +{ + return system_trusted_keyring; +} +#else +static inline struct key *get_system_trusted_keyring(void) +{ + return NULL; +} #endif #endif /* _KEYS_SYSTEM_KEYRING_H */ -- cgit v1.2.3 From b3426827c848d252ed4ca5f4d3085551be083e12 Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Tue, 17 Jun 2014 11:56:57 +0300 Subject: KEYS: make partial key id matching as a dedicated function To avoid code duplication this patch refactors asymmetric_key_match(), making partial ID string match a separate function. This patch also implicitly fixes a bug in the code. asymmetric_key_match() allows to match the key by its subtype. But subtype matching could be undone if asymmetric_key_id(key) would return NULL. This patch first checks for matching spec and then for its value. Signed-off-by: Dmitry Kasatkin Signed-off-by: Mimi Zohar --- crypto/asymmetric_keys/asymmetric_keys.h | 2 ++ crypto/asymmetric_keys/asymmetric_type.c | 50 ++++++++++++++++++++------------ 2 files changed, 33 insertions(+), 19 deletions(-) (limited to 'crypto') 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..1fd1d304a15a 100644 --- a/crypto/asymmetric_keys/asymmetric_type.c +++ b/crypto/asymmetric_keys/asymmetric_type.c @@ -22,6 +22,34 @@ MODULE_LICENSE("GPL"); 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:" + */ +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:" */ + 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; +} + /* * Match asymmetric keys on (part of) their name * We have some shorthand methods for matching keys. We allow: @@ -34,9 +62,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 +82,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) -- cgit v1.2.3 From ffb70f61bab1482a3bd0f85fd8f1e9c9909df2ca Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Tue, 17 Jun 2014 11:56:58 +0300 Subject: KEYS: validate certificate trust only with selected key Instead of allowing public keys, with certificates signed by any key on the system trusted keyring, to be added to a trusted keyring, this patch further restricts the certificates to those signed by a particular key on the system keyring. This patch defines a new kernel parameter 'ca_keys' to identify the specific key which must be used for trust validation of certificates. Simplified Mimi's "KEYS: define an owner trusted keyring" patch. Changelog: - support for builtin x509 public keys only - export "asymmetric_keyid_match" - remove ifndefs MODULE - rename kernel boot parameter from keys_ownerid to ca_keys Signed-off-by: Dmitry Kasatkin Signed-off-by: Mimi Zohar --- Documentation/kernel-parameters.txt | 5 +++++ crypto/asymmetric_keys/asymmetric_type.c | 1 + crypto/asymmetric_keys/x509_public_key.c | 19 +++++++++++++++++++ 3 files changed, 25 insertions(+) (limited to 'crypto') diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 8b2ab548b6e4..bdb193afe176 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -566,6 +566,11 @@ bytes respectively. Such letter suffixes can also be entirely omitted. possible to determine what the correct size should be. This option provides an override for these situations. + ca_keys= [KEYS] This parameter identifies a specific key(s) on + the system trusted keyring to be used for certificate + trust validation. + format: id: + ccw_timeout_log [S390] See Documentation/s390/CommonIO for details. diff --git a/crypto/asymmetric_keys/asymmetric_type.c b/crypto/asymmetric_keys/asymmetric_type.c index 1fd1d304a15a..c948df5c4ecd 100644 --- a/crypto/asymmetric_keys/asymmetric_type.c +++ b/crypto/asymmetric_keys/asymmetric_type.c @@ -49,6 +49,7 @@ int asymmetric_keyid_match(const char *kid, const char *id) return 1; } +EXPORT_SYMBOL_GPL(asymmetric_keyid_match); /* * Match asymmetric keys on (part of) their name diff --git a/crypto/asymmetric_keys/x509_public_key.c b/crypto/asymmetric_keys/x509_public_key.c index 436fbd8552fc..d376195e1d08 100644 --- a/crypto/asymmetric_keys/x509_public_key.c +++ b/crypto/asymmetric_keys/x509_public_key.c @@ -24,6 +24,22 @@ #include "public_key.h" #include "x509_parser.h" +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' */ + + return 1; +} +__setup("ca_keys=", ca_keys_setup); +#endif + /* * Find a key in the given keyring by issuer and authority. */ @@ -171,6 +187,9 @@ static int x509_validate_trust(struct x509_certificate *cert, 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, strlen(cert->issuer), cert->authority, -- cgit v1.2.3 From 32c4741cb66703a3c282f41d77deff4afd93342a Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Tue, 17 Jun 2014 11:56:59 +0300 Subject: KEYS: validate certificate trust only with builtin keys Instead of allowing public keys, with certificates signed by any key on the system trusted keyring, to be added to a trusted keyring, this patch further restricts the certificates to those signed only by builtin keys on the system keyring. This patch defines a new option 'builtin' for the kernel parameter 'keys_ownerid' to allow trust validation using builtin keys. Simplified Mimi's "KEYS: define an owner trusted keyring" patch Changelog v7: - rename builtin_keys to use_builtin_keys Signed-off-by: Dmitry Kasatkin Signed-off-by: Mimi Zohar --- Documentation/kernel-parameters.txt | 2 +- crypto/asymmetric_keys/x509_public_key.c | 9 ++++++--- include/linux/key.h | 1 + kernel/system_keyring.c | 1 + 4 files changed, 9 insertions(+), 4 deletions(-) (limited to 'crypto') diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index bdb193afe176..90c12c591168 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -569,7 +569,7 @@ bytes respectively. Such letter suffixes can also be entirely omitted. ca_keys= [KEYS] This parameter identifies a specific key(s) on the system trusted keyring to be used for certificate trust validation. - format: id: + format: { id: | builtin } ccw_timeout_log [S390] See Documentation/s390/CommonIO for details. diff --git a/crypto/asymmetric_keys/x509_public_key.c b/crypto/asymmetric_keys/x509_public_key.c index d376195e1d08..927ce755ff67 100644 --- a/crypto/asymmetric_keys/x509_public_key.c +++ b/crypto/asymmetric_keys/x509_public_key.c @@ -24,6 +24,7 @@ #include "public_key.h" #include "x509_parser.h" +static bool use_builtin_keys; static char *ca_keyid; #ifndef MODULE @@ -34,6 +35,8 @@ static int __init ca_keys_setup(char *str) 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; } @@ -180,7 +183,6 @@ EXPORT_SYMBOL_GPL(x509_check_signature); static int x509_validate_trust(struct x509_certificate *cert, struct key *trust_keyring) { - const struct public_key *pk; struct key *key; int ret = 1; @@ -195,8 +197,9 @@ static int x509_validate_trust(struct x509_certificate *cert, cert->authority, strlen(cert->authority)); if (!IS_ERR(key)) { - pk = key->payload.data; - ret = x509_check_signature(pk, cert); + if (!use_builtin_keys + || test_bit(KEY_FLAG_BUILTIN, &key->flags)) + ret = x509_check_signature(key->payload.data, cert); key_put(key); } return ret; diff --git a/include/linux/key.h b/include/linux/key.h index 017b0826642f..65316f7ae794 100644 --- a/include/linux/key.h +++ b/include/linux/key.h @@ -170,6 +170,7 @@ struct key { #define KEY_FLAG_INVALIDATED 7 /* set if key has been invalidated */ #define KEY_FLAG_TRUSTED 8 /* set if key is trusted */ #define KEY_FLAG_TRUSTED_ONLY 9 /* set if keyring only accepts links to trusted keys */ +#define KEY_FLAG_BUILTIN 10 /* set if key is builtin */ /* the key type and key description string * - the desc is used to match a key against search criteria diff --git a/kernel/system_keyring.c b/kernel/system_keyring.c index 52ebc70263f4..875f64e8935b 100644 --- a/kernel/system_keyring.c +++ b/kernel/system_keyring.c @@ -89,6 +89,7 @@ static __init int load_system_certificate_list(void) pr_err("Problem loading in-kernel X.509 certificate (%ld)\n", PTR_ERR(key)); } else { + set_bit(KEY_FLAG_BUILTIN, &key_ref_to_ptr(key)->flags); pr_notice("Loaded X.509 cert '%s'\n", key_ref_to_ptr(key)->description); key_ref_put(key); -- cgit v1.2.3 From 26c18217330b66f28e52debf5cb66d3374e0fd2d Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Thu, 17 Jul 2014 20:45:41 +0100 Subject: RSA: Don't select non-existent symbol You can select MPILIB_EXTRA all you want, it doesn't exist ;-) Surprised kconfig doesn't complain about that... Signed-off-by: Jean Delvare Acked-by: Marek Vasut Signed-off-by: David Howells Cc: Herbert Xu Cc: "David S. Miller" --- crypto/asymmetric_keys/Kconfig | 1 - 1 file changed, 1 deletion(-) (limited to 'crypto') diff --git a/crypto/asymmetric_keys/Kconfig b/crypto/asymmetric_keys/Kconfig index 03a6eb95ab50..0320c7d4b3e1 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). -- cgit v1.2.3 From 6a09d17bb66a533c165be81e8a4c3557f68e1a3b Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 18 Jul 2014 18:56:34 +0100 Subject: KEYS: Provide a generic instantiation function Provide a generic instantiation function for key types that use the preparse hook. This makes it easier to prereserve key quota before keyrings get locked to retain the new key. Signed-off-by: David Howells Acked-by: Steve Dickson Acked-by: Jeff Layton Reviewed-by: Sage Weil --- crypto/asymmetric_keys/asymmetric_type.c | 25 +------------------------ include/linux/key-type.h | 2 ++ security/keys/key.c | 30 ++++++++++++++++++++++++++++++ 3 files changed, 33 insertions(+), 24 deletions(-) (limited to 'crypto') diff --git a/crypto/asymmetric_keys/asymmetric_type.c b/crypto/asymmetric_keys/asymmetric_type.c index b77eb5304788..c1fe0fcee8e3 100644 --- a/crypto/asymmetric_keys/asymmetric_type.c +++ b/crypto/asymmetric_keys/asymmetric_type.c @@ -163,29 +163,6 @@ static void asymmetric_key_free_preparse(struct key_preparsed_payload *prep) kfree(prep->description); } -/* - * 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 */ @@ -205,7 +182,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/include/linux/key-type.h b/include/linux/key-type.h index a74c3a84dfdd..88503dca2a57 100644 --- a/include/linux/key-type.h +++ b/include/linux/key-type.h @@ -159,5 +159,7 @@ static inline int key_negate_and_link(struct key *key, return key_reject_and_link(key, timeout, ENOKEY, keyring, instkey); } +extern int generic_key_instantiate(struct key *key, struct key_preparsed_payload *prep); + #endif /* CONFIG_KEYS */ #endif /* _LINUX_KEY_TYPE_H */ diff --git a/security/keys/key.c b/security/keys/key.c index 2048a110e7f1..7c9acbf106b6 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -1023,6 +1023,36 @@ void key_invalidate(struct key *key) } EXPORT_SYMBOL(key_invalidate); +/** + * generic_key_instantiate - Simple instantiation of a key from preparsed data + * @key: The key to be instantiated + * @prep: The preparsed data to load. + * + * Instantiate a key from preparsed data. We assume we can just copy the data + * in directly and clear the old pointers. + * + * This can be pointed to directly by the key type instantiate op pointer. + */ +int generic_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]; + rcu_assign_keypointer(key, prep->payload); + prep->type_data[0] = NULL; + prep->type_data[1] = NULL; + prep->payload = NULL; + } + pr_devel("<==%s() = %d\n", __func__, ret); + return ret; +} +EXPORT_SYMBOL(generic_key_instantiate); + /** * register_key_type - Register a type of key. * @ktype: The new key type. -- cgit v1.2.3 From fc7c70e0b6b637bbf6cf8b9cee547d5ae83899c9 Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 18 Jul 2014 18:56:34 +0100 Subject: KEYS: struct key_preparsed_payload should have two payload pointers struct key_preparsed_payload should have two payload pointers to correspond with those in struct key. Signed-off-by: David Howells Acked-by: Steve Dickson Acked-by: Jeff Layton Reviewed-by: Sage Weil --- crypto/asymmetric_keys/asymmetric_type.c | 2 +- crypto/asymmetric_keys/x509_public_key.c | 2 +- include/linux/key-type.h | 2 +- security/keys/encrypted-keys/encrypted.c | 2 +- security/keys/key.c | 6 ++++-- 5 files changed, 8 insertions(+), 6 deletions(-) (limited to 'crypto') diff --git a/crypto/asymmetric_keys/asymmetric_type.c b/crypto/asymmetric_keys/asymmetric_type.c index c1fe0fcee8e3..21960a4e74e8 100644 --- a/crypto/asymmetric_keys/asymmetric_type.c +++ b/crypto/asymmetric_keys/asymmetric_type.c @@ -156,7 +156,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]); diff --git a/crypto/asymmetric_keys/x509_public_key.c b/crypto/asymmetric_keys/x509_public_key.c index 382ef0d2ff2e..3fc8a0634ed7 100644 --- a/crypto/asymmetric_keys/x509_public_key.c +++ b/crypto/asymmetric_keys/x509_public_key.c @@ -177,7 +177,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; diff --git a/include/linux/key-type.h b/include/linux/key-type.h index 88503dca2a57..d2b4845d74bf 100644 --- a/include/linux/key-type.h +++ b/include/linux/key-type.h @@ -41,7 +41,7 @@ struct key_construction { struct key_preparsed_payload { char *description; /* Proposed key description (or NULL) */ void *type_data[2]; /* Private key-type data */ - void *payload; /* Proposed payload */ + void *payload[2]; /* Proposed payload */ const void *data; /* Raw data */ size_t datalen; /* Raw datalen */ size_t quotalen; /* Quota length for proposed payload */ diff --git a/security/keys/encrypted-keys/encrypted.c b/security/keys/encrypted-keys/encrypted.c index 5fe443d120af..d252c5704f8a 100644 --- a/security/keys/encrypted-keys/encrypted.c +++ b/security/keys/encrypted-keys/encrypted.c @@ -811,7 +811,7 @@ static int encrypted_instantiate(struct key *key, goto out; } - rcu_assign_keypointer(key, epayload); + prep->payload[0] = epayload; out: kfree(datablob); return ret; diff --git a/security/keys/key.c b/security/keys/key.c index 7c9acbf106b6..03620a35a4dc 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -1043,10 +1043,12 @@ int generic_key_instantiate(struct key *key, struct key_preparsed_payload *prep) if (ret == 0) { key->type_data.p[0] = prep->type_data[0]; key->type_data.p[1] = prep->type_data[1]; - rcu_assign_keypointer(key, prep->payload); + rcu_assign_keypointer(key, prep->payload[0]); + key->payload.data2[1] = prep->payload[1]; prep->type_data[0] = NULL; prep->type_data[1] = NULL; - prep->payload = NULL; + prep->payload[0] = NULL; + prep->payload[1] = NULL; } pr_devel("<==%s() = %d\n", __func__, ret); return ret; -- cgit v1.2.3 From 8f3438ccea149647ad1849651d1e14c7b8b85e63 Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 25 Jul 2014 11:33:53 +0100 Subject: PKCS#7: Missing inclusion of linux/err.h crypto/asymmetric_keys/pkcs7_key_type.c needs to #include linux/err.h rather than relying on getting it through other headers. Without this, the powerpc allyesconfig build fails. Reported-by: Stephen Rothwell Signed-off-by: David Howells --- crypto/asymmetric_keys/pkcs7_key_type.c | 1 + 1 file changed, 1 insertion(+) (limited to 'crypto') diff --git a/crypto/asymmetric_keys/pkcs7_key_type.c b/crypto/asymmetric_keys/pkcs7_key_type.c index c2091f7bd15d..197ecdc0a5a1 100644 --- a/crypto/asymmetric_keys/pkcs7_key_type.c +++ b/crypto/asymmetric_keys/pkcs7_key_type.c @@ -11,6 +11,7 @@ #define pr_fmt(fmt) "PKCS7key: "fmt #include +#include #include #include #include -- cgit v1.2.3 From 63d2551ea7e2c34b533f2b6b0646cc825d9ee509 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Mon, 28 Jul 2014 21:17:12 +0800 Subject: PKCS#7: fix sparse non static symbol warning Fixes the following sparse warnings: crypto/asymmetric_keys/pkcs7_key_type.c:73:17: warning: symbol 'key_type_pkcs7' was not declared. Should it be static? Signed-off-by: Wei Yongjun Signed-off-by: David Howells --- crypto/asymmetric_keys/pkcs7_key_type.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'crypto') diff --git a/crypto/asymmetric_keys/pkcs7_key_type.c b/crypto/asymmetric_keys/pkcs7_key_type.c index 197ecdc0a5a1..3de5fb011de0 100644 --- a/crypto/asymmetric_keys/pkcs7_key_type.c +++ b/crypto/asymmetric_keys/pkcs7_key_type.c @@ -70,7 +70,7 @@ error: * user defined keys take an arbitrary string as the description and an * arbitrary blob of data as the payload */ -struct key_type key_type_pkcs7 = { +static struct key_type key_type_pkcs7 = { .name = "pkcs7_test", .def_lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT, .preparse = pkcs7_preparse, -- cgit v1.2.3 From 185de09c6aa9d38ec04e34b2d9a996561963f895 Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 9 Jul 2014 16:48:00 +0100 Subject: X.509: x509_request_asymmetric_keys() doesn't need string length arguments x509_request_asymmetric_keys() doesn't need the lengths of the NUL-terminated strings passing in as it can work that out for itself. Signed-off-by: David Howells Acked-by: Mimi Zohar --- crypto/asymmetric_keys/x509_public_key.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'crypto') diff --git a/crypto/asymmetric_keys/x509_public_key.c b/crypto/asymmetric_keys/x509_public_key.c index a0f7cd196c9b..4ae982234d78 100644 --- a/crypto/asymmetric_keys/x509_public_key.c +++ b/crypto/asymmetric_keys/x509_public_key.c @@ -48,11 +48,10 @@ __setup("ca_keys=", ca_keys_setup); */ static struct key *x509_request_asymmetric_key(struct key *keyring, const char *signer, - size_t signer_len, - const char *authority, - size_t auth_len) + const char *authority) { key_ref_t key; + size_t signer_len = strlen(signer), auth_len = strlen(authority); char *id; /* Construct an identifier. */ @@ -193,9 +192,7 @@ static int x509_validate_trust(struct x509_certificate *cert, return -EPERM; key = x509_request_asymmetric_key(trust_keyring, - cert->issuer, strlen(cert->issuer), - cert->authority, - strlen(cert->authority)); + cert->issuer, cert->authority); if (!IS_ERR(key)) { if (!use_builtin_keys || test_bit(KEY_FLAG_BUILTIN, &key->flags)) -- cgit v1.2.3 From 5ce43ad28262115a1eab866392f8cfb985094160 Mon Sep 17 00:00:00 2001 From: David Howells Date: Mon, 28 Jul 2014 14:11:32 +0100 Subject: PKCS#7: Use x509_request_asymmetric_key() pkcs7_request_asymmetric_key() and x509_request_asymmetric_key() do the same thing, the latter being a copy of the former created by the IMA folks, so drop the PKCS#7 version as the X.509 location is more general. Whilst we're at it, rename the arguments of x509_request_asymmetric_key() to better reflect what the values being passed in are intended to match on an X.509 cert. Signed-off-by: David Howells Acked-by: Mimi Zohar --- crypto/asymmetric_keys/pkcs7_trust.c | 61 +++----------------------------- crypto/asymmetric_keys/x509_public_key.c | 36 +++++++++++-------- include/crypto/public_key.h | 4 +++ 3 files changed, 29 insertions(+), 72 deletions(-) (limited to 'crypto') diff --git a/crypto/asymmetric_keys/pkcs7_trust.c b/crypto/asymmetric_keys/pkcs7_trust.c index b6b045131403..e666eb011a85 100644 --- a/crypto/asymmetric_keys/pkcs7_trust.c +++ b/crypto/asymmetric_keys/pkcs7_trust.c @@ -20,55 +20,6 @@ #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); -} - /** * Check the trust on one PKCS#7 SignedInfo block. */ @@ -98,10 +49,8 @@ int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, /* 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)); + 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. @@ -133,10 +82,8 @@ int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, return -ENOKEY; } - key = pkcs7_request_asymmetric_key( - trust_keyring, - last->issuer, strlen(last->issuer), - last->authority, strlen(last->authority)); + key = x509_request_asymmetric_key(trust_keyring, last->issuer, + last->authority); if (IS_ERR(key)) return PTR_ERR(key) == -ENOMEM ? -ENOMEM : -ENOKEY; x509 = last; diff --git a/crypto/asymmetric_keys/x509_public_key.c b/crypto/asymmetric_keys/x509_public_key.c index 4ae982234d78..da1e5fc85346 100644 --- a/crypto/asymmetric_keys/x509_public_key.c +++ b/crypto/asymmetric_keys/x509_public_key.c @@ -43,35 +43,41 @@ static int __init ca_keys_setup(char *str) __setup("ca_keys=", ca_keys_setup); #endif -/* - * Find a key in the given keyring by issuer and authority. +/** + * 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. */ -static struct key *x509_request_asymmetric_key(struct key *keyring, - const char *signer, - const char *authority) +struct key *x509_request_asymmetric_key(struct key *keyring, + const char *subject, + const char *key_id) { key_ref_t key; - size_t signer_len = strlen(signer), auth_len = strlen(authority); + size_t subject_len = strlen(subject), key_id_len = strlen(key_id); char *id; - /* Construct an identifier. */ - id = kmalloc(signer_len + 2 + auth_len + 1, GFP_KERNEL); + /* Construct an identifier ":". */ + id = kmalloc(subject_len + 2 + key_id_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; + 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 module key '%s' err %ld\n", - id, PTR_ERR(key)); + pr_debug("Request for key '%s' err %ld\n", id, PTR_ERR(key)); kfree(id); if (IS_ERR(key)) { diff --git a/include/crypto/public_key.h b/include/crypto/public_key.h index fc09732613ad..0d164c6af539 100644 --- a/include/crypto/public_key.h +++ b/include/crypto/public_key.h @@ -98,4 +98,8 @@ struct key; extern int verify_signature(const struct key *key, const struct public_key_signature *sig); +extern struct key *x509_request_asymmetric_key(struct key *keyring, + const char *issuer, + const char *key_id); + #endif /* _LINUX_PUBLIC_KEY_H */ -- cgit v1.2.3 From 412eccbadfbb1521e62ae53db57d782d6bc36993 Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 31 Jul 2014 14:46:44 +0100 Subject: PKCS#7: X.509 certificate issuer and subject are mandatory fields in the ASN.1 X.509 certificate issuer and subject fields are mandatory fields in the ASN.1 and so their existence needn't be tested for. They are guaranteed to end up with an empty string if the name material has nothing we can use (see x509_fabricate_name()). Reported-by: Dan Carpenter Signed-off-by: David Howells Acked-by: Vivek Goyal --- crypto/asymmetric_keys/pkcs7_verify.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'crypto') diff --git a/crypto/asymmetric_keys/pkcs7_verify.c b/crypto/asymmetric_keys/pkcs7_verify.c index 51ff36f3a913..c62cf8006e1f 100644 --- a/crypto/asymmetric_keys/pkcs7_verify.c +++ b/crypto/asymmetric_keys/pkcs7_verify.c @@ -190,14 +190,12 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7, if (ret < 0) return ret; - if (x509->issuer) - pr_debug("- issuer %s\n", x509->issuer); + pr_debug("- issuer %s\n", x509->issuer); if (x509->authority) pr_debug("- authkeyid %s\n", x509->authority); if (!x509->authority || - (x509->subject && - strcmp(x509->subject, x509->issuer) == 0)) { + 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 -- cgit v1.2.3 From cf5b50fd2d70fdd907d433bcebaf8d89a9490334 Mon Sep 17 00:00:00 2001 From: David Howells Date: Sun, 3 Aug 2014 12:54:48 +0100 Subject: X.509: Need to export x509_request_asymmetric_key() Need to export x509_request_asymmetric_key() so that PKCS#7 can use it if compiled as a module. Reported-by: James Morris Signed-off-by: David Howells --- crypto/asymmetric_keys/x509_public_key.c | 1 + 1 file changed, 1 insertion(+) (limited to 'crypto') diff --git a/crypto/asymmetric_keys/x509_public_key.c b/crypto/asymmetric_keys/x509_public_key.c index da1e5fc85346..f3d62307e6ee 100644 --- a/crypto/asymmetric_keys/x509_public_key.c +++ b/crypto/asymmetric_keys/x509_public_key.c @@ -96,6 +96,7 @@ struct key *x509_request_asymmetric_key(struct key *keyring, 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 -- cgit v1.2.3