From efb81a423eb745a5a4545649ea38e28229e02b6a 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 +++++++++ 1 file changed, 9 insertions(+) (limited to 'crypto/asymmetric_keys/Kconfig') diff --git a/crypto/asymmetric_keys/Kconfig b/crypto/asymmetric_keys/Kconfig index 03a6eb95..a7cec9dd 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 -- cgit v1.2.3 From c6fef8f06aa1dc39d370a0ce1d6e856e2349acb2 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/asymmetric_keys/Kconfig') diff --git a/crypto/asymmetric_keys/Kconfig b/crypto/asymmetric_keys/Kconfig index a7cec9dd..b6df198d 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 d63cb432..92d0e9af 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 00000000..b1797d25 --- /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 bf080f0ad3d766e1cc27cfd88827f716288e2a4d 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/asymmetric_keys/Kconfig') diff --git a/crypto/asymmetric_keys/Kconfig b/crypto/asymmetric_keys/Kconfig index b6df198d..14cac286 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 0cf8e6a6bfa2cedf073fc13aeeb00e8b79d319e2 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 ++++++++ 4 files changed, 217 insertions(+) create mode 100644 crypto/asymmetric_keys/verify_pefile.c create mode 100644 crypto/asymmetric_keys/verify_pefile.h (limited to 'crypto/asymmetric_keys/Kconfig') diff --git a/crypto/asymmetric_keys/Kconfig b/crypto/asymmetric_keys/Kconfig index 14cac286..ca41be56 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 92d0e9af..3e4de529 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 00000000..aec7c509 --- /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 00000000..e165d234 --- /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__) -- cgit v1.2.3 From fa39821af068655bf0ddf7ace229287b23834696 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/asymmetric_keys/Kconfig') diff --git a/crypto/asymmetric_keys/Kconfig b/crypto/asymmetric_keys/Kconfig index 03a6eb95..0320c7d4 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