public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH 00/17] KEYS: PKCS#7 and PE file signature checking for kexec
@ 2014-07-09 15:15 David Howells
  2014-07-09 15:15 ` [PATCH01/17] X.509: Add bits needed for PKCS#7 David Howells
                   ` (17 more replies)
  0 siblings, 18 replies; 23+ messages in thread
From: David Howells @ 2014-07-09 15:15 UTC (permalink / raw)
  To: keyrings; +Cc: linux-security-module, kexec, linux-kernel


Here's a set of patches that implements signature checking on PKCS#7 message
and PE files in the kernel.

The intention is to use the PE file signature checker to validate binaries
passed to kexec().  This signature in a PE file involves a PKCS#7 message.  The
code attempts to follow the certificate chain back to a ring of public keys
in the kernel.

The PKCS#7 patches provide the following facility:

 (1) Parse an ASN.1 PKCS#7 message and pick out useful bits such as the data
     content and the X.509 certificates used to sign it and all the data
     signatures.

 (2) Verify all the data signatures against the set of X.509 certificates
     available in the message.

 (3) Follow the certificate chains and verify that:

     (a) for every self-signed X.509 certificate, check that it validly signed
     	 itself, and:

     (b) for every non-self-signed certificate, if we have a 'parent'
     	 certificate, the former is validly signed by the latter.

 (4) Look for intersections between the certificate chains and the trusted
     keyring, if any intersections are found, verify that the trusted
     certificates signed the intersection point in the chain.

 (5) For testing purposes, a key type can be made available that will take a
     PKCS#7 message, check that the message is trustworthy, and if so, add its
     data content into the key.

The commits can be found on this branch also:

	http://git.kernel.org/cgit/linux/kernel/git/dhowells/linux-modsign.git/log/?h=pkcs7

and are tagged with:

	keys-pkcs7-20140708

The PE file patches provide the following facility:

 (1) Extract the signature from the PE file.  This is a PKCS#7 message
     containing, as its data, a hash of the signed parts of the file.

 (2) Digest the signed parts of the file.

 (3) Compare the digest with the one from the PKCS#7 message.

 (4) Verify that the PKCS#7 message intersects with the keys in the keyring.

The commits can be found on this branch also:

	http://git.kernel.org/cgit/linux/kernel/git/dhowells/linux-modsign.git/log/?h=pefile

and are tagged with:

	keys-pefile-20140709

David
---
David Howells (16):
      X.509: Add bits needed for PKCS#7
      X.509: Export certificate parse and free functions
      PKCS#7: Implement a parser [RFC 2315]
      PKCS#7: Digest the data in a signed-data message
      PKCS#7: Find the right key in the PKCS#7 key list and verify the signature
      PKCS#7: Verify internal certificate chain
      PKCS#7: Find intersection between PKCS#7 message and known, trusted keys
      PKCS#7: Provide a key type for testing PKCS#7
      KEYS: X.509: Fix a spelling mistake
      Provide PE binary definitions
      pefile: Parse a PE binary to find a key and a signature contained therein
      pefile: Strip the wrapper off of the cert data block
      pefile: Parse the presumed PKCS#7 content of the certificate blob
      pefile: Parse the "Microsoft individual code signing" data blob
      pefile: Digest the PE binary and compare to the PKCS#7 data
      pefile: Validate PKCS#7 trust chain

Vivek Goyal (1):
      pefile: Handle pesign using the wrong OID


 crypto/asymmetric_keys/Kconfig            |   33 ++
 crypto/asymmetric_keys/Makefile           |   37 ++
 crypto/asymmetric_keys/mscode.asn1        |   28 ++
 crypto/asymmetric_keys/mscode_parser.c    |  126 ++++++++
 crypto/asymmetric_keys/pkcs7.asn1         |  127 ++++++++
 crypto/asymmetric_keys/pkcs7_key_type.c   |   97 ++++++
 crypto/asymmetric_keys/pkcs7_parser.c     |  396 +++++++++++++++++++++++++
 crypto/asymmetric_keys/pkcs7_parser.h     |   61 ++++
 crypto/asymmetric_keys/pkcs7_trust.c      |  219 ++++++++++++++
 crypto/asymmetric_keys/pkcs7_verify.c     |  323 ++++++++++++++++++++
 crypto/asymmetric_keys/verify_pefile.c    |  457 +++++++++++++++++++++++++++++
 crypto/asymmetric_keys/verify_pefile.h    |   42 +++
 crypto/asymmetric_keys/x509.asn1          |    2 
 crypto/asymmetric_keys/x509_cert_parser.c |   20 +
 crypto/asymmetric_keys/x509_parser.h      |   13 +
 include/crypto/pkcs7.h                    |   36 ++
 include/linux/oid_registry.h              |    8 -
 include/linux/pe.h                        |  448 ++++++++++++++++++++++++++++
 include/linux/verify_pefile.h             |   18 +
 19 files changed, 2487 insertions(+), 4 deletions(-)
 create mode 100644 crypto/asymmetric_keys/mscode.asn1
 create mode 100644 crypto/asymmetric_keys/mscode_parser.c
 create mode 100644 crypto/asymmetric_keys/pkcs7.asn1
 create mode 100644 crypto/asymmetric_keys/pkcs7_key_type.c
 create mode 100644 crypto/asymmetric_keys/pkcs7_parser.c
 create mode 100644 crypto/asymmetric_keys/pkcs7_parser.h
 create mode 100644 crypto/asymmetric_keys/pkcs7_trust.c
 create mode 100644 crypto/asymmetric_keys/pkcs7_verify.c
 create mode 100644 crypto/asymmetric_keys/verify_pefile.c
 create mode 100644 crypto/asymmetric_keys/verify_pefile.h
 create mode 100644 include/crypto/pkcs7.h
 create mode 100644 include/linux/pe.h
 create mode 100644 include/linux/verify_pefile.h


^ permalink raw reply	[flat|nested] 23+ messages in thread

* [PATCH01/17] X.509: Add bits needed for PKCS#7
  2014-07-09 15:15 [PATCH 00/17] KEYS: PKCS#7 and PE file signature checking for kexec David Howells
@ 2014-07-09 15:15 ` David Howells
  2014-07-09 15:15 ` [PATCH02/17] X.509: Export certificate parse and free functions David Howells
                   ` (16 subsequent siblings)
  17 siblings, 0 replies; 23+ messages in thread
From: David Howells @ 2014-07-09 15:15 UTC (permalink / raw)
  To: keyrings; +Cc: linux-security-module, kexec, linux-kernel

PKCS#7 validation requires access to the serial number and the raw names in an
X.509 certificate.

Signed-off-by: David Howells <dhowells@redhat.com>
Reviewed-by: Kees Cook <keescook@chromium.org>
Reviewed-by: Josh Boyer <jwboyer@redhat.com>
---

 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(-)


diff --git a/crypto/asymmetric_keys/x509.asn1 b/crypto/asymmetric_keys/x509.asn1
index bf32b3d..aae0cde 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 2989316..4a8df29 100644
--- a/crypto/asymmetric_keys/x509_cert_parser.c
+++ b/crypto/asymmetric_keys/x509_cert_parser.c
@@ -211,6 +211,19 @@ int x509_note_signature(void *context, size_t hdrlen,
 }
 
 /*
+ * Note the certificate serial number
+ */
+int x509_note_serial(void *context, size_t hdrlen,
+		     unsigned char tag,
+		     const void *value, size_t vlen)
+{
+	struct x509_parse_context *ctx = context;
+	ctx->cert->raw_serial = value;
+	ctx->cert->raw_serial_size = vlen;
+	return 0;
+}
+
+/*
  * Note some of the name segments from which we'll fabricate a name.
  */
 int x509_extract_name_segment(void *context, size_t hdrlen,
@@ -322,6 +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 87d9cc2..1b76f20 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;
 };
 
 /*


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH02/17] X.509: Export certificate parse and free functions
  2014-07-09 15:15 [PATCH 00/17] KEYS: PKCS#7 and PE file signature checking for kexec David Howells
  2014-07-09 15:15 ` [PATCH01/17] X.509: Add bits needed for PKCS#7 David Howells
@ 2014-07-09 15:15 ` David Howells
  2014-07-09 15:15 ` [PATCH03/17] PKCS#7: Implement a parser [RFC 2315] David Howells
                   ` (15 subsequent siblings)
  17 siblings, 0 replies; 23+ messages in thread
From: David Howells @ 2014-07-09 15:15 UTC (permalink / raw)
  To: keyrings; +Cc: linux-security-module, kexec, linux-kernel

Export certificate parse and free functions for use by modules.

Signed-off-by: David Howells <dhowells@redhat.com>
Acked-by: Vivek Goyal <vgoyal@redhat.com>
Reviewed-by: Kees Cook <keescook@chromium.org>
Reviewed-by: Josh Boyer <jwboyer@redhat.com>
---

 crypto/asymmetric_keys/x509_cert_parser.c |    3 +++
 1 file changed, 3 insertions(+)


diff --git a/crypto/asymmetric_keys/x509_cert_parser.c b/crypto/asymmetric_keys/x509_cert_parser.c
index 4a8df29..ac72348 100644
--- a/crypto/asymmetric_keys/x509_cert_parser.c
+++ b/crypto/asymmetric_keys/x509_cert_parser.c
@@ -11,6 +11,7 @@
 
 #define pr_fmt(fmt) "X.509: "fmt
 #include <linux/kernel.h>
+#include <linux/export.h>
 #include <linux/slab.h>
 #include <linux/err.h>
 #include <linux/oid_registry.h>
@@ -52,6 +53,7 @@ void x509_free_certificate(struct x509_certificate *cert)
 		kfree(cert);
 	}
 }
+EXPORT_SYMBOL_GPL(x509_free_certificate);
 
 /*
  * Parse an X.509 certificate
@@ -97,6 +99,7 @@ error_no_ctx:
 error_no_cert:
 	return ERR_PTR(ret);
 }
+EXPORT_SYMBOL_GPL(x509_cert_parse);
 
 /*
  * Note an OID when we find one for later processing when we know how


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH03/17] PKCS#7: Implement a parser [RFC 2315]
  2014-07-09 15:15 [PATCH 00/17] KEYS: PKCS#7 and PE file signature checking for kexec David Howells
  2014-07-09 15:15 ` [PATCH01/17] X.509: Add bits needed for PKCS#7 David Howells
  2014-07-09 15:15 ` [PATCH02/17] X.509: Export certificate parse and free functions David Howells
@ 2014-07-09 15:15 ` David Howells
  2014-07-09 15:15 ` [PATCH04/17] PKCS#7: Digest the data in a signed-data message David Howells
                   ` (14 subsequent siblings)
  17 siblings, 0 replies; 23+ messages in thread
From: David Howells @ 2014-07-09 15:15 UTC (permalink / raw)
  To: keyrings; +Cc: linux-security-module, kexec, linux-kernel

Implement a parser for a PKCS#7 signed-data message as described in part of
RFC 2315.

Signed-off-by: David Howells <dhowells@redhat.com>
Acked-by: Vivek Goyal <vgoyal@redhat.com>
Reviewed-by: Kees Cook <keescook@chromium.org>
---

 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


diff --git a/crypto/asymmetric_keys/Kconfig b/crypto/asymmetric_keys/Kconfig
index 03a6eb9..a7cec9d 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 0727204..59d8cad 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 0000000..a5a14ef
--- /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 0000000..42e56aa
--- /dev/null
+++ b/crypto/asymmetric_keys/pkcs7_parser.c
@@ -0,0 +1,396 @@
+/* PKCS#7 parser
+ *
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#define pr_fmt(fmt) "PKCS7: "fmt
+#include <linux/kernel.h>
+#include <linux/export.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/oid_registry.h>
+#include "public_key.h"
+#include "pkcs7_parser.h"
+#include "pkcs7-asn1.h"
+
+struct pkcs7_parse_context {
+	struct pkcs7_message	*msg;		/* Message being constructed */
+	struct pkcs7_signed_info *sinfo;	/* SignedInfo being constructed */
+	struct pkcs7_signed_info **ppsinfo;
+	struct x509_certificate *certs;		/* Certificate cache */
+	struct x509_certificate **ppcerts;
+	unsigned long	data;			/* Start of data */
+	enum OID	last_oid;		/* Last OID encountered */
+	unsigned	x509_index;
+	unsigned	sinfo_index;
+};
+
+/**
+ * pkcs7_free_message - Free a PKCS#7 message
+ * @pkcs7: The PKCS#7 message to free
+ */
+void pkcs7_free_message(struct pkcs7_message *pkcs7)
+{
+	struct x509_certificate *cert;
+	struct pkcs7_signed_info *sinfo;
+
+	if (pkcs7) {
+		while (pkcs7->certs) {
+			cert = pkcs7->certs;
+			pkcs7->certs = cert->next;
+			x509_free_certificate(cert);
+		}
+		while (pkcs7->crl) {
+			cert = pkcs7->crl;
+			pkcs7->crl = cert->next;
+			x509_free_certificate(cert);
+		}
+		while (pkcs7->signed_infos) {
+			sinfo = pkcs7->signed_infos;
+			pkcs7->signed_infos = sinfo->next;
+			mpi_free(sinfo->sig.mpi[0]);
+			kfree(sinfo->sig.digest);
+			kfree(sinfo);
+		}
+		kfree(pkcs7);
+	}
+}
+EXPORT_SYMBOL_GPL(pkcs7_free_message);
+
+/**
+ * pkcs7_parse_message - Parse a PKCS#7 message
+ * @data: The raw binary ASN.1 encoded message to be parsed
+ * @datalen: The size of the encoded message
+ */
+struct pkcs7_message *pkcs7_parse_message(const void *data, size_t datalen)
+{
+	struct pkcs7_parse_context *ctx;
+	struct pkcs7_message *msg;
+	long ret;
+
+	ret = -ENOMEM;
+	msg = kzalloc(sizeof(struct pkcs7_message), GFP_KERNEL);
+	if (!msg)
+		goto error_no_sig;
+	ctx = kzalloc(sizeof(struct pkcs7_parse_context), GFP_KERNEL);
+	if (!ctx)
+		goto error_no_ctx;
+	ctx->sinfo = kzalloc(sizeof(struct pkcs7_signed_info), GFP_KERNEL);
+	if (!ctx->sinfo)
+		goto error_no_sinfo;
+
+	ctx->msg = msg;
+	ctx->data = (unsigned long)data;
+	ctx->ppcerts = &ctx->certs;
+	ctx->ppsinfo = &ctx->msg->signed_infos;
+
+	/* Attempt to decode the signature */
+	ret = asn1_ber_decoder(&pkcs7_decoder, ctx, data, datalen);
+	if (ret < 0)
+		goto error_decode;
+
+	while (ctx->certs) {
+		struct x509_certificate *cert = ctx->certs;
+		ctx->certs = cert->next;
+		x509_free_certificate(cert);
+	}
+	mpi_free(ctx->sinfo->sig.mpi[0]);
+	kfree(ctx->sinfo->sig.digest);
+	kfree(ctx->sinfo);
+	kfree(ctx);
+	return msg;
+
+error_decode:
+	mpi_free(ctx->sinfo->sig.mpi[0]);
+	kfree(ctx->sinfo->sig.digest);
+	kfree(ctx->sinfo);
+error_no_sinfo:
+	kfree(ctx);
+error_no_ctx:
+	pkcs7_free_message(msg);
+error_no_sig:
+	return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(pkcs7_parse_message);
+
+/**
+ * pkcs7_get_content_data - Get access to the PKCS#7 content
+ * @pkcs7: The preparsed PKCS#7 message to access
+ * @_data: Place to return a pointer to the data
+ * @_data_len: Place to return the data length
+ * @want_wrapper: True if the ASN.1 object header should be included in the data
+ *
+ * Get access to the data content of the PKCS#7 message, including, optionally,
+ * the header of the ASN.1 object that contains it.  Returns -ENODATA if the
+ * data object was missing from the message.
+ */
+int pkcs7_get_content_data(const struct pkcs7_message *pkcs7,
+			   const void **_data, size_t *_data_len,
+			   bool want_wrapper)
+{
+	size_t wrapper;
+
+	if (!pkcs7->data)
+		return -ENODATA;
+
+	wrapper = want_wrapper ? pkcs7->data_hdrlen : 0;
+	*_data = pkcs7->data - wrapper;
+	*_data_len = pkcs7->data_len + wrapper;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(pkcs7_get_content_data);
+
+/*
+ * Note an OID when we find one for later processing when we know how
+ * to interpret it.
+ */
+int pkcs7_note_OID(void *context, size_t hdrlen,
+		   unsigned char tag,
+		   const void *value, size_t vlen)
+{
+	struct pkcs7_parse_context *ctx = context;
+
+	ctx->last_oid = look_up_OID(value, vlen);
+	if (ctx->last_oid == OID__NR) {
+		char buffer[50];
+		sprint_oid(value, vlen, buffer, sizeof(buffer));
+		printk("PKCS7: Unknown OID: [%lu] %s\n",
+		       (unsigned long)value - ctx->data, buffer);
+	}
+	return 0;
+}
+
+/*
+ * Note the digest algorithm for the signature.
+ */
+int pkcs7_sig_note_digest_algo(void *context, size_t hdrlen,
+			       unsigned char tag,
+			       const void *value, size_t vlen)
+{
+	struct pkcs7_parse_context *ctx = context;
+
+	switch (ctx->last_oid) {
+	case OID_md4:
+		ctx->sinfo->sig.pkey_hash_algo = HASH_ALGO_MD4;
+		break;
+	case OID_md5:
+		ctx->sinfo->sig.pkey_hash_algo = HASH_ALGO_MD5;
+		break;
+	case OID_sha1:
+		ctx->sinfo->sig.pkey_hash_algo = HASH_ALGO_SHA1;
+		break;
+	case OID_sha256:
+		ctx->sinfo->sig.pkey_hash_algo = HASH_ALGO_SHA256;
+		break;
+	default:
+		printk("Unsupported digest algo: %u\n", ctx->last_oid);
+		return -ENOPKG;
+	}
+	return 0;
+}
+
+/*
+ * Note the public key algorithm for the signature.
+ */
+int pkcs7_sig_note_pkey_algo(void *context, size_t hdrlen,
+			     unsigned char tag,
+			     const void *value, size_t vlen)
+{
+	struct pkcs7_parse_context *ctx = context;
+
+	switch (ctx->last_oid) {
+	case OID_rsaEncryption:
+		ctx->sinfo->sig.pkey_algo = PKEY_ALGO_RSA;
+		break;
+	default:
+		printk("Unsupported pkey algo: %u\n", ctx->last_oid);
+		return -ENOPKG;
+	}
+	return 0;
+}
+
+/*
+ * Extract a certificate and store it in the context.
+ */
+int pkcs7_extract_cert(void *context, size_t hdrlen,
+		       unsigned char tag,
+		       const void *value, size_t vlen)
+{
+	struct pkcs7_parse_context *ctx = context;
+	struct x509_certificate *x509;
+
+	if (tag != ((ASN1_UNIV << 6) | ASN1_CONS_BIT | ASN1_SEQ)) {
+		pr_debug("Cert began with tag %02x at %lu\n",
+			 tag, (unsigned long)ctx - ctx->data);
+		return -EBADMSG;
+	}
+
+	/* We have to correct for the header so that the X.509 parser can start
+	 * from the beginning.  Note that since X.509 stipulates DER, there
+	 * probably shouldn't be an EOC trailer - but it is in PKCS#7 (which
+	 * stipulates BER).
+	 */
+	value -= hdrlen;
+	vlen += hdrlen;
+
+	if (((u8*)value)[1] == 0x80)
+		vlen += 2; /* Indefinite length - there should be an EOC */
+
+	x509 = x509_cert_parse(value, vlen);
+	if (IS_ERR(x509))
+		return PTR_ERR(x509);
+
+	pr_debug("Got cert for %s\n", x509->subject);
+	pr_debug("- fingerprint %s\n", x509->fingerprint);
+
+	x509->index = ++ctx->x509_index;
+	*ctx->ppcerts = x509;
+	ctx->ppcerts = &x509->next;
+	return 0;
+}
+
+/*
+ * Save the certificate list
+ */
+int pkcs7_note_certificate_list(void *context, size_t hdrlen,
+				unsigned char tag,
+				const void *value, size_t vlen)
+{
+	struct pkcs7_parse_context *ctx = context;
+
+	pr_devel("Got cert list (%02x)\n", tag);
+
+	*ctx->ppcerts = ctx->msg->certs;
+	ctx->msg->certs = ctx->certs;
+	ctx->certs = NULL;
+	ctx->ppcerts = &ctx->certs;
+	return 0;
+}
+
+/*
+ * Extract the data from the message and store that and its content type OID in
+ * the context.
+ */
+int pkcs7_note_data(void *context, size_t hdrlen,
+		    unsigned char tag,
+		    const void *value, size_t vlen)
+{
+	struct pkcs7_parse_context *ctx = context;
+
+	pr_debug("Got data\n");
+
+	ctx->msg->data = value;
+	ctx->msg->data_len = vlen;
+	ctx->msg->data_hdrlen = hdrlen;
+	ctx->msg->data_type = ctx->last_oid;
+	return 0;
+}
+
+/*
+ * Parse authenticated attributes
+ */
+int pkcs7_sig_note_authenticated_attr(void *context, size_t hdrlen,
+				      unsigned char tag,
+				      const void *value, size_t vlen)
+{
+	struct pkcs7_parse_context *ctx = context;
+
+	pr_devel("AuthAttr: %02x %zu [%*ph]\n", tag, vlen, (unsigned)vlen, value);
+
+	switch (ctx->last_oid) {
+	case OID_messageDigest:
+		if (tag != ASN1_OTS)
+			return -EBADMSG;
+		ctx->sinfo->msgdigest = value;
+		ctx->sinfo->msgdigest_len = vlen;
+		return 0;
+	default:
+		return 0;
+	}
+}
+
+/*
+ * Note the set of auth attributes for digestion purposes [RFC2315 9.3]
+ */
+int pkcs7_sig_note_set_of_authattrs(void *context, size_t hdrlen,
+				    unsigned char tag,
+				    const void *value, size_t vlen)
+{
+	struct pkcs7_parse_context *ctx = context;
+
+	/* We need to switch the 'CONT 0' to a 'SET OF' when we digest */
+	ctx->sinfo->authattrs = value - (hdrlen - 1);
+	ctx->sinfo->authattrs_len = vlen + (hdrlen - 1);
+	return 0;
+}
+
+/*
+ * Note the issuing certificate serial number
+ */
+int pkcs7_sig_note_serial(void *context, size_t hdrlen,
+			  unsigned char tag,
+			  const void *value, size_t vlen)
+{
+	struct pkcs7_parse_context *ctx = context;
+	ctx->sinfo->raw_serial = value;
+	ctx->sinfo->raw_serial_size = vlen;
+	return 0;
+}
+
+/*
+ * Note the issuer's name
+ */
+int pkcs7_sig_note_issuer(void *context, size_t hdrlen,
+			  unsigned char tag,
+			  const void *value, size_t vlen)
+{
+	struct pkcs7_parse_context *ctx = context;
+	ctx->sinfo->raw_issuer = value;
+	ctx->sinfo->raw_issuer_size = vlen;
+	return 0;
+}
+
+/*
+ * Note the signature data
+ */
+int pkcs7_sig_note_signature(void *context, size_t hdrlen,
+			     unsigned char tag,
+			     const void *value, size_t vlen)
+{
+	struct pkcs7_parse_context *ctx = context;
+	MPI mpi;
+
+	BUG_ON(ctx->sinfo->sig.pkey_algo != PKEY_ALGO_RSA);
+
+	mpi = mpi_read_raw_data(value, vlen);
+	if (!mpi)
+		return -ENOMEM;
+
+	ctx->sinfo->sig.mpi[0] = mpi;
+	ctx->sinfo->sig.nr_mpi = 1;
+	return 0;
+}
+
+/*
+ * Note a signature information block
+ */
+int pkcs7_note_signed_info(void *context, size_t hdrlen,
+			   unsigned char tag,
+			   const void *value, size_t vlen)
+{
+	struct pkcs7_parse_context *ctx = context;
+
+	ctx->sinfo->index = ++ctx->sinfo_index;
+	*ctx->ppsinfo = ctx->sinfo;
+	ctx->ppsinfo = &ctx->sinfo->next;
+	ctx->sinfo = kzalloc(sizeof(struct pkcs7_signed_info), GFP_KERNEL);
+	if (!ctx->sinfo)
+		return -ENOMEM;
+	return 0;
+}
diff --git a/crypto/asymmetric_keys/pkcs7_parser.h b/crypto/asymmetric_keys/pkcs7_parser.h
new file mode 100644
index 0000000..d25f4d1
--- /dev/null
+++ b/crypto/asymmetric_keys/pkcs7_parser.h
@@ -0,0 +1,61 @@
+/* PKCS#7 crypto data parser internal definitions
+ *
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#include <linux/oid_registry.h>
+#include <crypto/pkcs7.h>
+#include "x509_parser.h"
+
+#define kenter(FMT, ...) \
+	pr_devel("==> %s("FMT")\n", __func__, ##__VA_ARGS__)
+#define kleave(FMT, ...) \
+	pr_devel("<== %s()"FMT"\n", __func__, ##__VA_ARGS__)
+
+struct pkcs7_signed_info {
+	struct pkcs7_signed_info *next;
+	struct x509_certificate *signer; /* Signing certificate (in msg->certs) */
+	unsigned index;
+	bool trusted;
+
+	/* Message digest - the digest of the Content Data (or NULL) */
+	const void	*msgdigest;
+	unsigned	msgdigest_len;
+
+	/* Authenticated Attribute data (or NULL) */
+	unsigned	authattrs_len;
+	const void	*authattrs;
+
+	/* Issuing cert serial number and issuer's name */
+	const void	*raw_serial;
+	unsigned	raw_serial_size;
+	unsigned	raw_issuer_size;
+	const void	*raw_issuer;
+
+	/* Message signature.
+	 *
+	 * This contains the generated digest of _either_ the Content Data or
+	 * the Authenticated Attributes [RFC2315 9.3].  If the latter, one of
+	 * the attributes contains the digest of the the Content Data within
+	 * it.
+	 */
+	struct public_key_signature sig;
+};
+
+struct pkcs7_message {
+	struct x509_certificate *certs;	/* Certificate list */
+	struct x509_certificate *crl;	/* Revocation list */
+	struct pkcs7_signed_info *signed_infos;
+
+	/* Content Data (or NULL) */
+	enum OID	data_type;	/* Type of Data */
+	size_t		data_len;	/* Length of Data */
+	size_t		data_hdrlen;	/* Length of Data ASN.1 header */
+	const void	*data;		/* Content Data (or 0) */
+};
diff --git a/include/crypto/pkcs7.h b/include/crypto/pkcs7.h
new file mode 100644
index 0000000..8f2628f
--- /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 6926db7..edeff85 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 */


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH04/17] PKCS#7: Digest the data in a signed-data message
  2014-07-09 15:15 [PATCH 00/17] KEYS: PKCS#7 and PE file signature checking for kexec David Howells
                   ` (2 preceding siblings ...)
  2014-07-09 15:15 ` [PATCH03/17] PKCS#7: Implement a parser [RFC 2315] David Howells
@ 2014-07-09 15:15 ` David Howells
  2014-07-09 15:15 ` [PATCH05/17] PKCS#7: Find the right key in the PKCS#7 key list and verify the signature David Howells
                   ` (13 subsequent siblings)
  17 siblings, 0 replies; 23+ messages in thread
From: David Howells @ 2014-07-09 15:15 UTC (permalink / raw)
  To: keyrings; +Cc: linux-security-module, kexec, linux-kernel

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 <dhowells@redhat.com>
Acked-by: Vivek Goyal <vgoyal@redhat.com>
Reviewed-by: Kees Cook <keescook@chromium.org>
---

 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


diff --git a/crypto/asymmetric_keys/Makefile b/crypto/asymmetric_keys/Makefile
index 59d8cad..b6b39e7 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 0000000..0bb408a
--- /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 <linux/kernel.h>
+#include <linux/export.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/asn1.h>
+#include <crypto/hash.h>
+#include "public_key.h"
+#include "pkcs7_parser.h"
+
+/*
+ * Digest the relevant parts of the PKCS#7 data
+ */
+static int pkcs7_digest(struct pkcs7_message *pkcs7,
+			struct pkcs7_signed_info *sinfo)
+{
+	struct crypto_shash *tfm;
+	struct shash_desc *desc;
+	size_t digest_size, desc_size;
+	void *digest;
+	int ret;
+
+	kenter(",%u,%u", sinfo->index, sinfo->sig.pkey_hash_algo);
+
+	if (sinfo->sig.pkey_hash_algo >= PKEY_HASH__LAST ||
+	    !hash_algo_name[sinfo->sig.pkey_hash_algo])
+		return -ENOPKG;
+
+	/* Allocate the hashing algorithm we're going to need and find out how
+	 * big the hash operational data will be.
+	 */
+	tfm = crypto_alloc_shash(hash_algo_name[sinfo->sig.pkey_hash_algo],
+				 0, 0);
+	if (IS_ERR(tfm))
+		return (PTR_ERR(tfm) == -ENOENT) ? -ENOPKG : PTR_ERR(tfm);
+
+	desc_size = crypto_shash_descsize(tfm) + sizeof(*desc);
+	sinfo->sig.digest_size = digest_size = crypto_shash_digestsize(tfm);
+
+	ret = -ENOMEM;
+	digest = kzalloc(digest_size + desc_size, GFP_KERNEL);
+	if (!digest)
+		goto error_no_desc;
+
+	desc = digest + digest_size;
+	desc->tfm   = tfm;
+	desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
+
+	/* Digest the message [RFC2315 9.3] */
+	ret = crypto_shash_init(desc);
+	if (ret < 0)
+		goto error;
+	ret = crypto_shash_finup(desc, pkcs7->data, pkcs7->data_len, digest);
+	if (ret < 0)
+		goto error;
+	pr_devel("MsgDigest = [%*ph]\n", 8, digest);
+
+	/* However, if there are authenticated attributes, there must be a
+	 * message digest attribute amongst them which corresponds to the
+	 * digest we just calculated.
+	 */
+	if (sinfo->msgdigest) {
+		u8 tag;
+
+		if (sinfo->msgdigest_len != sinfo->sig.digest_size) {
+			pr_debug("Sig %u: Invalid digest size (%u)\n",
+				 sinfo->index, sinfo->msgdigest_len);
+			ret = -EBADMSG;
+			goto error;
+		}
+
+		if (memcmp(digest, sinfo->msgdigest, sinfo->msgdigest_len) != 0) {
+			pr_debug("Sig %u: Message digest doesn't match\n",
+				 sinfo->index);
+			ret = -EKEYREJECTED;
+			goto error;
+		}
+
+		/* We then calculate anew, using the authenticated attributes
+		 * as the contents of the digest instead.  Note that we need to
+		 * convert the attributes from a CONT.0 into a SET before we
+		 * hash it.
+		 */
+		memset(digest, 0, sinfo->sig.digest_size);
+
+		ret = crypto_shash_init(desc);
+		if (ret < 0)
+			goto error;
+		tag = ASN1_CONS_BIT | ASN1_SET;
+		ret = crypto_shash_update(desc, &tag, 1);
+		if (ret < 0)
+			goto error;
+		ret = crypto_shash_finup(desc, sinfo->authattrs,
+					 sinfo->authattrs_len, digest);
+		if (ret < 0)
+			goto error;
+		pr_devel("AADigest = [%*ph]\n", 8, digest);
+	}
+
+	sinfo->sig.digest = digest;
+	digest = NULL;
+
+error:
+	kfree(digest);
+error_no_desc:
+	crypto_free_shash(tfm);
+	kleave(" = %d", ret);
+	return ret;
+}
+
+/*
+/*
+ * 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);


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH05/17] PKCS#7: Find the right key in the PKCS#7 key list and verify the signature
  2014-07-09 15:15 [PATCH 00/17] KEYS: PKCS#7 and PE file signature checking for kexec David Howells
                   ` (3 preceding siblings ...)
  2014-07-09 15:15 ` [PATCH04/17] PKCS#7: Digest the data in a signed-data message David Howells
@ 2014-07-09 15:15 ` David Howells
  2014-07-09 15:16 ` [PATCH06/17] PKCS#7: Verify internal certificate chain David Howells
                   ` (12 subsequent siblings)
  17 siblings, 0 replies; 23+ messages in thread
From: David Howells @ 2014-07-09 15:15 UTC (permalink / raw)
  To: keyrings; +Cc: linux-security-module, kexec, linux-kernel

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 <dhowells@redhat.com>
Acked-by: Vivek Goyal <vgoyal@redhat.com>
Reviewed-by: Kees Cook <keescook@chromium.org>
---

 crypto/asymmetric_keys/pkcs7_verify.c |   64 +++++++++++++++++++++++++++++++++
 include/crypto/pkcs7.h                |    5 +++
 2 files changed, 69 insertions(+)


diff --git a/crypto/asymmetric_keys/pkcs7_verify.c b/crypto/asymmetric_keys/pkcs7_verify.c
index 0bb408a..745e5c8 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 8f2628f..8ba0f38 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);


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH06/17] PKCS#7: Verify internal certificate chain
  2014-07-09 15:15 [PATCH 00/17] KEYS: PKCS#7 and PE file signature checking for kexec David Howells
                   ` (4 preceding siblings ...)
  2014-07-09 15:15 ` [PATCH05/17] PKCS#7: Find the right key in the PKCS#7 key list and verify the signature David Howells
@ 2014-07-09 15:16 ` David Howells
  2014-07-10 17:06   ` Valdis.Kletnieks
  2014-07-09 15:16 ` [PATCH07/17] PKCS#7: Find intersection between PKCS#7 message and known, trusted keys David Howells
                   ` (11 subsequent siblings)
  17 siblings, 1 reply; 23+ messages in thread
From: David Howells @ 2014-07-09 15:16 UTC (permalink / raw)
  To: keyrings; +Cc: linux-security-module, kexec, linux-kernel

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 <dhowells@redhat.com>
Acked-by: Vivek Goyal <vgoyal@redhat.com>
Reviewed-by: Kees Cook <keescook@chromium.org>
---

 crypto/asymmetric_keys/pkcs7_verify.c |   88 +++++++++++++++++++++++++++++++++
 1 file changed, 87 insertions(+), 1 deletion(-)


diff --git a/crypto/asymmetric_keys/pkcs7_verify.c b/crypto/asymmetric_keys/pkcs7_verify.c
index 745e5c8..51ff36f 100644
--- a/crypto/asymmetric_keys/pkcs7_verify.c
+++ b/crypto/asymmetric_keys/pkcs7_verify.c
@@ -170,6 +170,91 @@ static int pkcs7_find_key(struct pkcs7_message *pkcs7,
 }
 
 /*
+ * 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.
  */
 static int pkcs7_verify_one(struct pkcs7_message *pkcs7,
@@ -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);
 }
 
 /**


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH07/17] PKCS#7: Find intersection between PKCS#7 message and known, trusted keys
  2014-07-09 15:15 [PATCH 00/17] KEYS: PKCS#7 and PE file signature checking for kexec David Howells
                   ` (5 preceding siblings ...)
  2014-07-09 15:16 ` [PATCH06/17] PKCS#7: Verify internal certificate chain David Howells
@ 2014-07-09 15:16 ` David Howells
  2014-07-09 15:16 ` [PATCH08/17] PKCS#7: Provide a key type for testing PKCS#7 David Howells
                   ` (10 subsequent siblings)
  17 siblings, 0 replies; 23+ messages in thread
From: David Howells @ 2014-07-09 15:16 UTC (permalink / raw)
  To: keyrings; +Cc: linux-security-module, kexec, linux-kernel

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 <dhowells@redhat.com>
Acked-by: Vivek Goyal <vgoyal@redhat.com>
Reviewed-by: Kees Cook <keescook@chromium.org>
---

 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


diff --git a/crypto/asymmetric_keys/Makefile b/crypto/asymmetric_keys/Makefile
index b6b39e7..d63cb43 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 0000000..b6b0451
--- /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 <linux/kernel.h>
+#include <linux/export.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/asn1.h>
+#include <linux/key.h>
+#include <keys/asymmetric-type.h>
+#include "public_key.h"
+#include "pkcs7_parser.h"
+
+/*
+ * 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 8ba0f38..691c791 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;
 
 /*
@@ -23,6 +24,13 @@ extern int pkcs7_get_content_data(const struct pkcs7_message *pkcs7,
 				  bool want_wrapper);
 
 /*
+ * pkcs7_trust.c
+ */
+extern int pkcs7_validate_trust(struct pkcs7_message *pkcs7,
+				struct key *trust_keyring,
+				bool *_trusted);
+
+/*
  * pkcs7_verify.c
  */
 extern int pkcs7_verify(struct pkcs7_message *pkcs7);


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH08/17] PKCS#7: Provide a key type for testing PKCS#7
  2014-07-09 15:15 [PATCH 00/17] KEYS: PKCS#7 and PE file signature checking for kexec David Howells
                   ` (6 preceding siblings ...)
  2014-07-09 15:16 ` [PATCH07/17] PKCS#7: Find intersection between PKCS#7 message and known, trusted keys David Howells
@ 2014-07-09 15:16 ` David Howells
  2014-07-09 15:16 ` [PATCH09/17] KEYS: X.509: Fix a spelling mistake David Howells
                   ` (9 subsequent siblings)
  17 siblings, 0 replies; 23+ messages in thread
From: David Howells @ 2014-07-09 15:16 UTC (permalink / raw)
  To: keyrings; +Cc: linux-security-module, kexec, linux-kernel

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 <stuff.pkcs7

The PKCS#7 wrapper is validated against the trusted certificates available and
then stripped off.  If successful, the key can be read, which will give the
data content of the PKCS#7 message.

A suitable message can be created by running make on the attached Makefile.
This will produce a file called stuff.pkcs7 for test loading.  The key3.x509
file should be put into the kernel source tree before it is built and
converted to DER form:

	openssl x509 -in .../pkcs7/key3.x509 -outform DER -out key3.x509

###############################################################################
#
# Create a pkcs7 message and sign it twice
#
#	openssl x509 -text -inform PEM -noout -in key2.x509
#
###############################################################################
stuff.pkcs7: stuff.txt key2.priv key2.x509 key4.priv key4.x509 certs
	$(RM) $@
	openssl smime -sign \
		-signer key2.x509 \
		-inkey key2.priv \
		-signer key4.x509 \
		-inkey key4.priv \
		-in stuff.txt \
		-certfile certs \
		-out $@ -binary -outform DER -nodetach
	openssl pkcs7 -inform DER -in stuff.pkcs7  -print_certs -noout
	openssl asn1parse -inform DER -in stuff.pkcs7  -i >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 <dhowells@redhat.com>
---

 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


diff --git a/crypto/asymmetric_keys/Kconfig b/crypto/asymmetric_keys/Kconfig
index a7cec9d..b6df198 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 d63cb43..92d0e9a 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 0000000..b1797d2
--- /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 <linux/key.h>
+#include <linux/key-type.h>
+#include <crypto/pkcs7.h>
+#include <keys/user-type.h>
+#include <keys/system_keyring.h>
+#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);


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH09/17] KEYS: X.509: Fix a spelling mistake
  2014-07-09 15:15 [PATCH 00/17] KEYS: PKCS#7 and PE file signature checking for kexec David Howells
                   ` (7 preceding siblings ...)
  2014-07-09 15:16 ` [PATCH08/17] PKCS#7: Provide a key type for testing PKCS#7 David Howells
@ 2014-07-09 15:16 ` David Howells
  2014-07-09 15:16 ` [PATCH10/17] Provide PE binary definitions David Howells
                   ` (8 subsequent siblings)
  17 siblings, 0 replies; 23+ messages in thread
From: David Howells @ 2014-07-09 15:16 UTC (permalink / raw)
  To: keyrings; +Cc: linux-security-module, kexec, linux-kernel

Signed-off-by: David Howells <dhowells@redhat.com>
Acked-by: Vivek Goyal <vgoyal@redhat.com>
---

 crypto/asymmetric_keys/Kconfig |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)


diff --git a/crypto/asymmetric_keys/Kconfig b/crypto/asymmetric_keys/Kconfig
index b6df198..14cac28 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.
 


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH10/17] Provide PE binary definitions
  2014-07-09 15:15 [PATCH 00/17] KEYS: PKCS#7 and PE file signature checking for kexec David Howells
                   ` (8 preceding siblings ...)
  2014-07-09 15:16 ` [PATCH09/17] KEYS: X.509: Fix a spelling mistake David Howells
@ 2014-07-09 15:16 ` David Howells
  2014-07-09 15:16 ` [PATCH11/17] pefile: Parse a PE binary to find a key and a signature contained therein David Howells
                   ` (7 subsequent siblings)
  17 siblings, 0 replies; 23+ messages in thread
From: David Howells @ 2014-07-09 15:16 UTC (permalink / raw)
  To: keyrings; +Cc: linux-security-module, kexec, linux-kernel

Provide some PE binary structural and constant definitions as taken from the
pesign package sources.

Signed-off-by: David Howells <dhowells@redhat.com>
Acked-by: Vivek Goyal <vgoyal@redhat.com>
Reviewed-by: Kees Cook <keescook@chromium.org>
---

 include/linux/pe.h |  448 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 448 insertions(+)
 create mode 100644 include/linux/pe.h


diff --git a/include/linux/pe.h b/include/linux/pe.h
new file mode 100644
index 0000000..e170b95
--- /dev/null
+++ b/include/linux/pe.h
@@ -0,0 +1,448 @@
+/*
+ * Copyright 2011 Red Hat, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author(s): Peter Jones <pjones@redhat.com>
+ */
+#ifndef __LINUX_PE_H
+#define __LINUX_PE_H
+
+#include <linux/types.h>
+
+#define MZ_MAGIC	0x5a4d	/* "MZ" */
+
+struct mz_hdr {
+	uint16_t magic;		/* MZ_MAGIC */
+	uint16_t lbsize;	/* size of last used block */
+	uint16_t blocks;	/* pages in file, 0x3 */
+	uint16_t relocs;	/* relocations */
+	uint16_t hdrsize;	/* header size in "paragraphs" */
+	uint16_t min_extra_pps;	/* .bss */
+	uint16_t max_extra_pps;	/* runtime limit for the arena size */
+	uint16_t ss;		/* relative stack segment */
+	uint16_t sp;		/* initial %sp register */
+	uint16_t checksum;	/* word checksum */
+	uint16_t ip;		/* initial %ip register */
+	uint16_t cs;		/* initial %cs relative to load segment */
+	uint16_t reloc_table_offset;	/* offset of the first relocation */
+	uint16_t overlay_num;	/* overlay number.  set to 0. */
+	uint16_t reserved0[4];	/* reserved */
+	uint16_t oem_id;	/* oem identifier */
+	uint16_t oem_info;	/* oem specific */
+	uint16_t reserved1[10];	/* reserved */
+	uint32_t peaddr;	/* address of pe header */
+	char     message[64];	/* message to print */
+};
+
+struct mz_reloc {
+	uint16_t offset;
+	uint16_t segment;
+};
+
+#define PE_MAGIC		0x00004550	/* "PE\0\0" */
+#define PE_OPT_MAGIC_PE32	0x010b
+#define PE_OPT_MAGIC_PE32_ROM	0x0107
+#define PE_OPT_MAGIC_PE32PLUS	0x020b
+
+/* machine type */
+#define	IMAGE_FILE_MACHINE_UNKNOWN	0x0000
+#define	IMAGE_FILE_MACHINE_AM33		0x01d3
+#define	IMAGE_FILE_MACHINE_AMD64	0x8664
+#define	IMAGE_FILE_MACHINE_ARM		0x01c0
+#define	IMAGE_FILE_MACHINE_ARMV7	0x01c4
+#define	IMAGE_FILE_MACHINE_EBC		0x0ebc
+#define	IMAGE_FILE_MACHINE_I386		0x014c
+#define	IMAGE_FILE_MACHINE_IA64		0x0200
+#define	IMAGE_FILE_MACHINE_M32R		0x9041
+#define	IMAGE_FILE_MACHINE_MIPS16	0x0266
+#define	IMAGE_FILE_MACHINE_MIPSFPU	0x0366
+#define	IMAGE_FILE_MACHINE_MIPSFPU16	0x0466
+#define	IMAGE_FILE_MACHINE_POWERPC	0x01f0
+#define	IMAGE_FILE_MACHINE_POWERPCFP	0x01f1
+#define	IMAGE_FILE_MACHINE_R4000	0x0166
+#define	IMAGE_FILE_MACHINE_SH3		0x01a2
+#define	IMAGE_FILE_MACHINE_SH3DSP	0x01a3
+#define	IMAGE_FILE_MACHINE_SH3E		0x01a4
+#define	IMAGE_FILE_MACHINE_SH4		0x01a6
+#define	IMAGE_FILE_MACHINE_SH5		0x01a8
+#define	IMAGE_FILE_MACHINE_THUMB	0x01c2
+#define	IMAGE_FILE_MACHINE_WCEMIPSV2	0x0169
+
+/* flags */
+#define IMAGE_FILE_RELOCS_STRIPPED           0x0001
+#define IMAGE_FILE_EXECUTABLE_IMAGE          0x0002
+#define IMAGE_FILE_LINE_NUMS_STRIPPED        0x0004
+#define IMAGE_FILE_LOCAL_SYMS_STRIPPED       0x0008
+#define IMAGE_FILE_AGGRESSIVE_WS_TRIM        0x0010
+#define IMAGE_FILE_LARGE_ADDRESS_AWARE       0x0020
+#define IMAGE_FILE_16BIT_MACHINE             0x0040
+#define IMAGE_FILE_BYTES_REVERSED_LO         0x0080
+#define IMAGE_FILE_32BIT_MACHINE             0x0100
+#define IMAGE_FILE_DEBUG_STRIPPED            0x0200
+#define IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP   0x0400
+#define IMAGE_FILE_NET_RUN_FROM_SWAP         0x0800
+#define IMAGE_FILE_SYSTEM                    0x1000
+#define IMAGE_FILE_DLL                       0x2000
+#define IMAGE_FILE_UP_SYSTEM_ONLY            0x4000
+#define IMAGE_FILE_BYTES_REVERSED_HI         0x8000
+
+struct pe_hdr {
+	uint32_t magic;		/* PE magic */
+	uint16_t machine;	/* machine type */
+	uint16_t sections;	/* number of sections */
+	uint32_t timestamp;	/* time_t */
+	uint32_t symbol_table;	/* symbol table offset */
+	uint32_t symbols;	/* number of symbols */
+	uint16_t opt_hdr_size;	/* size of optional header */
+	uint16_t flags;		/* flags */
+};
+
+#define IMAGE_FILE_OPT_ROM_MAGIC	0x107
+#define IMAGE_FILE_OPT_PE32_MAGIC	0x10b
+#define IMAGE_FILE_OPT_PE32_PLUS_MAGIC	0x20b
+
+#define IMAGE_SUBSYSTEM_UNKNOWN			 0
+#define IMAGE_SUBSYSTEM_NATIVE			 1
+#define IMAGE_SUBSYSTEM_WINDOWS_GUI		 2
+#define IMAGE_SUBSYSTEM_WINDOWS_CUI		 3
+#define IMAGE_SUBSYSTEM_POSIX_CUI		 7
+#define IMAGE_SUBSYSTEM_WINDOWS_CE_GUI		 9
+#define IMAGE_SUBSYSTEM_EFI_APPLICATION		10
+#define IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER	11
+#define IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER	12
+#define IMAGE_SUBSYSTEM_EFI_ROM_IMAGE		13
+#define IMAGE_SUBSYSTEM_XBOX			14
+
+#define IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE          0x0040
+#define IMAGE_DLL_CHARACTERISTICS_FORCE_INTEGRITY       0x0080
+#define IMAGE_DLL_CHARACTERISTICS_NX_COMPAT             0x0100
+#define IMAGE_DLLCHARACTERISTICS_NO_ISOLATION           0x0200
+#define IMAGE_DLLCHARACTERISTICS_NO_SEH                 0x0400
+#define IMAGE_DLLCHARACTERISTICS_NO_BIND                0x0800
+#define IMAGE_DLLCHARACTERISTICS_WDM_DRIVER             0x2000
+#define IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE  0x8000
+
+/* the fact that pe32 isn't padded where pe32+ is 64-bit means union won't
+ * work right.  vomit. */
+struct pe32_opt_hdr {
+	/* "standard" header */
+	uint16_t magic;		/* file type */
+	uint8_t  ld_major;	/* linker major version */
+	uint8_t  ld_minor;	/* linker minor version */
+	uint32_t text_size;	/* size of text section(s) */
+	uint32_t data_size;	/* size of data section(s) */
+	uint32_t bss_size;	/* size of bss section(s) */
+	uint32_t entry_point;	/* file offset of entry point */
+	uint32_t code_base;	/* relative code addr in ram */
+	uint32_t data_base;	/* relative data addr in ram */
+	/* "windows" header */
+	uint32_t image_base;	/* preferred load address */
+	uint32_t section_align;	/* alignment in bytes */
+	uint32_t file_align;	/* file alignment in bytes */
+	uint16_t os_major;	/* major OS version */
+	uint16_t os_minor;	/* minor OS version */
+	uint16_t image_major;	/* major image version */
+	uint16_t image_minor;	/* minor image version */
+	uint16_t subsys_major;	/* major subsystem version */
+	uint16_t subsys_minor;	/* minor subsystem version */
+	uint32_t win32_version;	/* reserved, must be 0 */
+	uint32_t image_size;	/* image size */
+	uint32_t header_size;	/* header size rounded up to
+				   file_align */
+	uint32_t csum;		/* checksum */
+	uint16_t subsys;	/* subsystem */
+	uint16_t dll_flags;	/* more flags! */
+	uint32_t stack_size_req;/* amt of stack requested */
+	uint32_t stack_size;	/* amt of stack required */
+	uint32_t heap_size_req;	/* amt of heap requested */
+	uint32_t heap_size;	/* amt of heap required */
+	uint32_t loader_flags;	/* reserved, must be 0 */
+	uint32_t data_dirs;	/* number of data dir entries */
+};
+
+struct pe32plus_opt_hdr {
+	uint16_t magic;		/* file type */
+	uint8_t  ld_major;	/* linker major version */
+	uint8_t  ld_minor;	/* linker minor version */
+	uint32_t text_size;	/* size of text section(s) */
+	uint32_t data_size;	/* size of data section(s) */
+	uint32_t bss_size;	/* size of bss section(s) */
+	uint32_t entry_point;	/* file offset of entry point */
+	uint32_t code_base;	/* relative code addr in ram */
+	/* "windows" header */
+	uint64_t image_base;	/* preferred load address */
+	uint32_t section_align;	/* alignment in bytes */
+	uint32_t file_align;	/* file alignment in bytes */
+	uint16_t os_major;	/* major OS version */
+	uint16_t os_minor;	/* minor OS version */
+	uint16_t image_major;	/* major image version */
+	uint16_t image_minor;	/* minor image version */
+	uint16_t subsys_major;	/* major subsystem version */
+	uint16_t subsys_minor;	/* minor subsystem version */
+	uint32_t win32_version;	/* reserved, must be 0 */
+	uint32_t image_size;	/* image size */
+	uint32_t header_size;	/* header size rounded up to
+				   file_align */
+	uint32_t csum;		/* checksum */
+	uint16_t subsys;	/* subsystem */
+	uint16_t dll_flags;	/* more flags! */
+	uint64_t stack_size_req;/* amt of stack requested */
+	uint64_t stack_size;	/* amt of stack required */
+	uint64_t heap_size_req;	/* amt of heap requested */
+	uint64_t heap_size;	/* amt of heap required */
+	uint32_t loader_flags;	/* reserved, must be 0 */
+	uint32_t data_dirs;	/* number of data dir entries */
+};
+
+struct data_dirent {
+	uint32_t virtual_address;	/* relative to load address */
+	uint32_t size;
+};
+
+struct data_directory {
+	struct data_dirent exports;		/* .edata */
+	struct data_dirent imports;		/* .idata */
+	struct data_dirent resources;		/* .rsrc */
+	struct data_dirent exceptions;		/* .pdata */
+	struct data_dirent certs;		/* certs */
+	struct data_dirent base_relocations;	/* .reloc */
+	struct data_dirent debug;		/* .debug */
+	struct data_dirent arch;		/* reservered */
+	struct data_dirent global_ptr;		/* global pointer reg. Size=0 */
+	struct data_dirent tls;			/* .tls */
+	struct data_dirent load_config;		/* load configuration structure */
+	struct data_dirent bound_imports;	/* no idea */
+	struct data_dirent import_addrs;	/* import address table */
+	struct data_dirent delay_imports;	/* delay-load import table */
+	struct data_dirent clr_runtime_hdr;	/* .cor (object only) */
+	struct data_dirent reserved;
+};
+
+struct section_header {
+	char name[8];			/* name or "/12\0" string tbl offset */
+	uint32_t virtual_size;		/* size of loaded section in ram */
+	uint32_t virtual_address;	/* relative virtual address */
+	uint32_t raw_data_size;		/* size of the section */
+	uint32_t data_addr;		/* file pointer to first page of sec */
+	uint32_t relocs;		/* file pointer to relocation entries */
+	uint32_t line_numbers;		/* line numbers! */
+	uint16_t num_relocs;		/* number of relocations */
+	uint16_t num_lin_numbers;	/* srsly. */
+	uint32_t flags;
+};
+
+/* they actually defined 0x00000000 as well, but I think we'll skip that one. */
+#define IMAGE_SCN_RESERVED_0	0x00000001
+#define IMAGE_SCN_RESERVED_1	0x00000002
+#define IMAGE_SCN_RESERVED_2	0x00000004
+#define IMAGE_SCN_TYPE_NO_PAD	0x00000008 /* don't pad - obsolete */
+#define IMAGE_SCN_RESERVED_3	0x00000010
+#define IMAGE_SCN_CNT_CODE	0x00000020 /* .text */
+#define IMAGE_SCN_CNT_INITIALIZED_DATA 0x00000040 /* .data */
+#define IMAGE_SCN_CNT_UNINITIALIZED_DATA 0x00000080 /* .bss */
+#define IMAGE_SCN_LNK_OTHER	0x00000100 /* reserved */
+#define IMAGE_SCN_LNK_INFO	0x00000200 /* .drectve comments */
+#define IMAGE_SCN_RESERVED_4	0x00000400
+#define IMAGE_SCN_LNK_REMOVE	0x00000800 /* .o only - scn to be rm'd*/
+#define IMAGE_SCN_LNK_COMDAT	0x00001000 /* .o only - COMDAT data */
+#define IMAGE_SCN_RESERVED_5	0x00002000 /* spec omits this */
+#define IMAGE_SCN_RESERVED_6	0x00004000 /* spec omits this */
+#define IMAGE_SCN_GPREL		0x00008000 /* global pointer referenced data */
+/* spec lists 0x20000 twice, I suspect they meant 0x10000 for one of them */
+#define IMAGE_SCN_MEM_PURGEABLE	0x00010000 /* reserved for "future" use */
+#define IMAGE_SCN_16BIT		0x00020000 /* reserved for "future" use */
+#define IMAGE_SCN_LOCKED	0x00040000 /* reserved for "future" use */
+#define IMAGE_SCN_PRELOAD	0x00080000 /* reserved for "future" use */
+/* and here they just stuck a 1-byte integer in the middle of a bitfield */
+#define IMAGE_SCN_ALIGN_1BYTES	0x00100000 /* it does what it says on the box */
+#define IMAGE_SCN_ALIGN_2BYTES	0x00200000
+#define IMAGE_SCN_ALIGN_4BYTES	0x00300000
+#define IMAGE_SCN_ALIGN_8BYTES	0x00400000
+#define IMAGE_SCN_ALIGN_16BYTES	0x00500000
+#define IMAGE_SCN_ALIGN_32BYTES	0x00600000
+#define IMAGE_SCN_ALIGN_64BYTES	0x00700000
+#define IMAGE_SCN_ALIGN_128BYTES 0x00800000
+#define IMAGE_SCN_ALIGN_256BYTES 0x00900000
+#define IMAGE_SCN_ALIGN_512BYTES 0x00a00000
+#define IMAGE_SCN_ALIGN_1024BYTES 0x00b00000
+#define IMAGE_SCN_ALIGN_2048BYTES 0x00c00000
+#define IMAGE_SCN_ALIGN_4096BYTES 0x00d00000
+#define IMAGE_SCN_ALIGN_8192BYTES 0x00e00000
+#define IMAGE_SCN_LNK_NRELOC_OVFL 0x01000000 /* extended relocations */
+#define IMAGE_SCN_MEM_DISCARDABLE 0x02000000 /* scn can be discarded */
+#define IMAGE_SCN_MEM_NOT_CACHED 0x04000000 /* cannot be cached */
+#define IMAGE_SCN_MEM_NOT_PAGED	0x08000000 /* not pageable */
+#define IMAGE_SCN_MEM_SHARED	0x10000000 /* can be shared */
+#define IMAGE_SCN_MEM_EXECUTE	0x20000000 /* can be executed as code */
+#define IMAGE_SCN_MEM_READ	0x40000000 /* readable */
+#define IMAGE_SCN_MEM_WRITE	0x80000000 /* writeable */
+
+enum x64_coff_reloc_type {
+	IMAGE_REL_AMD64_ABSOLUTE = 0,
+	IMAGE_REL_AMD64_ADDR64,
+	IMAGE_REL_AMD64_ADDR32,
+	IMAGE_REL_AMD64_ADDR32N,
+	IMAGE_REL_AMD64_REL32,
+	IMAGE_REL_AMD64_REL32_1,
+	IMAGE_REL_AMD64_REL32_2,
+	IMAGE_REL_AMD64_REL32_3,
+	IMAGE_REL_AMD64_REL32_4,
+	IMAGE_REL_AMD64_REL32_5,
+	IMAGE_REL_AMD64_SECTION,
+	IMAGE_REL_AMD64_SECREL,
+	IMAGE_REL_AMD64_SECREL7,
+	IMAGE_REL_AMD64_TOKEN,
+	IMAGE_REL_AMD64_SREL32,
+	IMAGE_REL_AMD64_PAIR,
+	IMAGE_REL_AMD64_SSPAN32,
+};
+
+enum arm_coff_reloc_type {
+	IMAGE_REL_ARM_ABSOLUTE,
+	IMAGE_REL_ARM_ADDR32,
+	IMAGE_REL_ARM_ADDR32N,
+	IMAGE_REL_ARM_BRANCH2,
+	IMAGE_REL_ARM_BRANCH1,
+	IMAGE_REL_ARM_SECTION,
+	IMAGE_REL_ARM_SECREL,
+};
+
+enum sh_coff_reloc_type {
+	IMAGE_REL_SH3_ABSOLUTE,
+	IMAGE_REL_SH3_DIRECT16,
+	IMAGE_REL_SH3_DIRECT32,
+	IMAGE_REL_SH3_DIRECT8,
+	IMAGE_REL_SH3_DIRECT8_WORD,
+	IMAGE_REL_SH3_DIRECT8_LONG,
+	IMAGE_REL_SH3_DIRECT4,
+	IMAGE_REL_SH3_DIRECT4_WORD,
+	IMAGE_REL_SH3_DIRECT4_LONG,
+	IMAGE_REL_SH3_PCREL8_WORD,
+	IMAGE_REL_SH3_PCREL8_LONG,
+	IMAGE_REL_SH3_PCREL12_WORD,
+	IMAGE_REL_SH3_STARTOF_SECTION,
+	IMAGE_REL_SH3_SIZEOF_SECTION,
+	IMAGE_REL_SH3_SECTION,
+	IMAGE_REL_SH3_SECREL,
+	IMAGE_REL_SH3_DIRECT32_NB,
+	IMAGE_REL_SH3_GPREL4_LONG,
+	IMAGE_REL_SH3_TOKEN,
+	IMAGE_REL_SHM_PCRELPT,
+	IMAGE_REL_SHM_REFLO,
+	IMAGE_REL_SHM_REFHALF,
+	IMAGE_REL_SHM_RELLO,
+	IMAGE_REL_SHM_RELHALF,
+	IMAGE_REL_SHM_PAIR,
+	IMAGE_REL_SHM_NOMODE,
+};
+
+enum ppc_coff_reloc_type {
+	IMAGE_REL_PPC_ABSOLUTE,
+	IMAGE_REL_PPC_ADDR64,
+	IMAGE_REL_PPC_ADDR32,
+	IMAGE_REL_PPC_ADDR24,
+	IMAGE_REL_PPC_ADDR16,
+	IMAGE_REL_PPC_ADDR14,
+	IMAGE_REL_PPC_REL24,
+	IMAGE_REL_PPC_REL14,
+	IMAGE_REL_PPC_ADDR32N,
+	IMAGE_REL_PPC_SECREL,
+	IMAGE_REL_PPC_SECTION,
+	IMAGE_REL_PPC_SECREL16,
+	IMAGE_REL_PPC_REFHI,
+	IMAGE_REL_PPC_REFLO,
+	IMAGE_REL_PPC_PAIR,
+	IMAGE_REL_PPC_SECRELLO,
+	IMAGE_REL_PPC_GPREL,
+	IMAGE_REL_PPC_TOKEN,
+};
+
+enum x86_coff_reloc_type {
+	IMAGE_REL_I386_ABSOLUTE,
+	IMAGE_REL_I386_DIR16,
+	IMAGE_REL_I386_REL16,
+	IMAGE_REL_I386_DIR32,
+	IMAGE_REL_I386_DIR32NB,
+	IMAGE_REL_I386_SEG12,
+	IMAGE_REL_I386_SECTION,
+	IMAGE_REL_I386_SECREL,
+	IMAGE_REL_I386_TOKEN,
+	IMAGE_REL_I386_SECREL7,
+	IMAGE_REL_I386_REL32,
+};
+
+enum ia64_coff_reloc_type {
+	IMAGE_REL_IA64_ABSOLUTE,
+	IMAGE_REL_IA64_IMM14,
+	IMAGE_REL_IA64_IMM22,
+	IMAGE_REL_IA64_IMM64,
+	IMAGE_REL_IA64_DIR32,
+	IMAGE_REL_IA64_DIR64,
+	IMAGE_REL_IA64_PCREL21B,
+	IMAGE_REL_IA64_PCREL21M,
+	IMAGE_REL_IA64_PCREL21F,
+	IMAGE_REL_IA64_GPREL22,
+	IMAGE_REL_IA64_LTOFF22,
+	IMAGE_REL_IA64_SECTION,
+	IMAGE_REL_IA64_SECREL22,
+	IMAGE_REL_IA64_SECREL64I,
+	IMAGE_REL_IA64_SECREL32,
+	IMAGE_REL_IA64_DIR32NB,
+	IMAGE_REL_IA64_SREL14,
+	IMAGE_REL_IA64_SREL22,
+	IMAGE_REL_IA64_SREL32,
+	IMAGE_REL_IA64_UREL32,
+	IMAGE_REL_IA64_PCREL60X,
+	IMAGE_REL_IA64_PCREL60B,
+	IMAGE_REL_IA64_PCREL60F,
+	IMAGE_REL_IA64_PCREL60I,
+	IMAGE_REL_IA64_PCREL60M,
+	IMAGE_REL_IA64_IMMGPREL6,
+	IMAGE_REL_IA64_TOKEN,
+	IMAGE_REL_IA64_GPREL32,
+	IMAGE_REL_IA64_ADDEND,
+};
+
+struct coff_reloc {
+	uint32_t virtual_address;
+	uint32_t symbol_table_index;
+	union {
+		enum x64_coff_reloc_type  x64_type;
+		enum arm_coff_reloc_type  arm_type;
+		enum sh_coff_reloc_type   sh_type;
+		enum ppc_coff_reloc_type  ppc_type;
+		enum x86_coff_reloc_type  x86_type;
+		enum ia64_coff_reloc_type ia64_type;
+		uint16_t data;
+	};
+};
+
+/*
+ * Definitions for the contents of the certs data block
+ */
+#define WIN_CERT_TYPE_PKCS_SIGNED_DATA	0x0002
+#define WIN_CERT_TYPE_EFI_OKCS115	0x0EF0
+#define WIN_CERT_TYPE_EFI_GUID		0x0EF1
+
+#define WIN_CERT_REVISION_1_0	0x0100
+#define WIN_CERT_REVISION_2_0	0x0200
+
+struct win_certificate {
+	uint32_t length;
+	uint16_t revision;
+	uint16_t cert_type;
+};
+
+#endif /* __LINUX_PE_H */


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH11/17] pefile: Parse a PE binary to find a key and a signature contained therein
  2014-07-09 15:15 [PATCH 00/17] KEYS: PKCS#7 and PE file signature checking for kexec David Howells
                   ` (9 preceding siblings ...)
  2014-07-09 15:16 ` [PATCH10/17] Provide PE binary definitions David Howells
@ 2014-07-09 15:16 ` David Howells
  2014-07-09 15:16 ` [PATCH12/17] pefile: Strip the wrapper off of the cert data block David Howells
                   ` (6 subsequent siblings)
  17 siblings, 0 replies; 23+ messages in thread
From: David Howells @ 2014-07-09 15:16 UTC (permalink / raw)
  To: keyrings; +Cc: linux-security-module, kexec, linux-kernel

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 <dhowells@redhat.com>
Acked-by: Vivek Goyal <vgoyal@redhat.com>
Reviewed-by: Kees Cook <keescook@chromium.org>
---

 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


diff --git a/crypto/asymmetric_keys/Kconfig b/crypto/asymmetric_keys/Kconfig
index 14cac28..ca41be5 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 92d0e9a..3e4de52 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 0000000..aec7c50
--- /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 <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/pe.h>
+#include <crypto/pkcs7.h>
+#include <crypto/hash.h>
+#include "verify_pefile.h"
+
+/*
+ * Parse a PE binary.
+ */
+static int pefile_parse_binary(const void *pebuf, unsigned int pelen,
+			       struct pefile_context *ctx)
+{
+	const struct mz_hdr *mz = pebuf;
+	const struct pe_hdr *pe;
+	const struct pe32_opt_hdr *pe32;
+	const struct pe32plus_opt_hdr *pe64;
+	const struct data_directory *ddir;
+	const struct data_dirent *dde;
+	const struct section_header *secs, *sec;
+	size_t cursor, datalen = pelen;
+
+	kenter("");
+
+#define chkaddr(base, x, s)						\
+	do {								\
+		if ((x) < base || (s) >= datalen || (x) > datalen - (s)) \
+			return -ELIBBAD;				\
+	} while (0)
+
+	chkaddr(0, 0, sizeof(*mz));
+	if (mz->magic != MZ_MAGIC)
+		return -ELIBBAD;
+	cursor = sizeof(*mz);
+
+	chkaddr(cursor, mz->peaddr, sizeof(*pe));
+	pe = pebuf + mz->peaddr;
+	if (pe->magic != PE_MAGIC)
+		return -ELIBBAD;
+	cursor = mz->peaddr + sizeof(*pe);
+
+	chkaddr(0, cursor, sizeof(pe32->magic));
+	pe32 = pebuf + cursor;
+	pe64 = pebuf + cursor;
+
+	switch (pe32->magic) {
+	case PE_OPT_MAGIC_PE32:
+		chkaddr(0, cursor, sizeof(*pe32));
+		ctx->image_checksum_offset =
+			(unsigned long)&pe32->csum - (unsigned long)pebuf;
+		ctx->header_size = pe32->header_size;
+		cursor += sizeof(*pe32);
+		ctx->n_data_dirents = pe32->data_dirs;
+		break;
+
+	case PE_OPT_MAGIC_PE32PLUS:
+		chkaddr(0, cursor, sizeof(*pe64));
+		ctx->image_checksum_offset =
+			(unsigned long)&pe64->csum - (unsigned long)pebuf;
+		ctx->header_size = pe64->header_size;
+		cursor += sizeof(*pe64);
+		ctx->n_data_dirents = pe64->data_dirs;
+		break;
+
+	default:
+		pr_debug("Unknown PEOPT magic = %04hx\n", pe32->magic);
+		return -ELIBBAD;
+	}
+
+	pr_debug("checksum @ %x\n", ctx->image_checksum_offset);
+	pr_debug("header size = %x\n", ctx->header_size);
+
+	if (cursor >= ctx->header_size || ctx->header_size >= datalen)
+		return -ELIBBAD;
+
+	if (ctx->n_data_dirents > (ctx->header_size - cursor) / sizeof(*dde))
+		return -ELIBBAD;
+
+	ddir = pebuf + cursor;
+	cursor += sizeof(*dde) * ctx->n_data_dirents;
+
+	ctx->cert_dirent_offset =
+		(unsigned long)&ddir->certs - (unsigned long)pebuf;
+	ctx->certs_size = ddir->certs.size;
+
+	if (!ddir->certs.virtual_address || !ddir->certs.size) {
+		pr_debug("Unsigned PE binary\n");
+		return -EKEYREJECTED;
+	}
+
+	chkaddr(ctx->header_size, ddir->certs.virtual_address,
+		ddir->certs.size);
+	ctx->sig_offset = ddir->certs.virtual_address;
+	ctx->sig_len = ddir->certs.size;
+	pr_debug("cert = %x @%x [%*ph]\n",
+		 ctx->sig_len, ctx->sig_offset,
+		 ctx->sig_len, pebuf + ctx->sig_offset);
+
+	ctx->n_sections = pe->sections;
+	if (ctx->n_sections > (ctx->header_size - cursor) / sizeof(*sec))
+		return -ELIBBAD;
+	ctx->secs = secs = pebuf + cursor;
+
+	return 0;
+}
+
+/**
+ * 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 0000000..e165d23
--- /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 <linux/verify_pefile.h>
+#include <crypto/pkcs7.h>
+#include <crypto/hash_info.h>
+
+struct pefile_context {
+	unsigned	header_size;
+	unsigned	image_checksum_offset;
+	unsigned	cert_dirent_offset;
+	unsigned	n_data_dirents;
+	unsigned	n_sections;
+	unsigned	certs_size;
+	unsigned	sig_offset;
+	unsigned	sig_len;
+	const struct section_header *secs;
+	struct pkcs7_message *pkcs7;
+
+	/* PKCS#7 MS Individual Code Signing content */
+	const void	*digest;		/* Digest */
+	unsigned	digest_len;		/* Digest length */
+	enum hash_algo	digest_algo;		/* Digest algorithm */
+};
+
+#define kenter(FMT, ...)					\
+	pr_devel("==> %s("FMT")\n", __func__, ##__VA_ARGS__)
+#define kleave(FMT, ...) \
+	pr_devel("<== %s()"FMT"\n", __func__, ##__VA_ARGS__)
diff --git a/include/linux/verify_pefile.h b/include/linux/verify_pefile.h
new file mode 100644
index 0000000..ac34819
--- /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 */


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH12/17] pefile: Strip the wrapper off of the cert data block
  2014-07-09 15:15 [PATCH 00/17] KEYS: PKCS#7 and PE file signature checking for kexec David Howells
                   ` (10 preceding siblings ...)
  2014-07-09 15:16 ` [PATCH11/17] pefile: Parse a PE binary to find a key and a signature contained therein David Howells
@ 2014-07-09 15:16 ` David Howells
  2014-07-09 15:16 ` [PATCH13/17] pefile: Parse the presumed PKCS#7 content of the certificate blob David Howells
                   ` (5 subsequent siblings)
  17 siblings, 0 replies; 23+ messages in thread
From: David Howells @ 2014-07-09 15:16 UTC (permalink / raw)
  To: keyrings; +Cc: linux-security-module, kexec, linux-kernel

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 <dhowells@redhat.com>
Acked-by: Vivek Goyal <vgoyal@redhat.com>
Reviewed-by: Kees Cook <keescook@chromium.org>
---

 crypto/asymmetric_keys/verify_pefile.c |   71 ++++++++++++++++++++++++++++++++
 1 file changed, 71 insertions(+)


diff --git a/crypto/asymmetric_keys/verify_pefile.c b/crypto/asymmetric_keys/verify_pefile.c
index aec7c50..2f5268c 100644
--- a/crypto/asymmetric_keys/verify_pefile.c
+++ b/crypto/asymmetric_keys/verify_pefile.c
@@ -15,6 +15,7 @@
 #include <linux/slab.h>
 #include <linux/err.h>
 #include <linux/pe.h>
+#include <linux/asn1.h>
 #include <crypto/pkcs7.h>
 #include <crypto/hash.h>
 #include "verify_pefile.h"
@@ -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
 }


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH13/17] pefile: Parse the presumed PKCS#7 content of the certificate blob
  2014-07-09 15:15 [PATCH 00/17] KEYS: PKCS#7 and PE file signature checking for kexec David Howells
                   ` (11 preceding siblings ...)
  2014-07-09 15:16 ` [PATCH12/17] pefile: Strip the wrapper off of the cert data block David Howells
@ 2014-07-09 15:16 ` David Howells
  2014-07-09 15:16 ` [PATCH14/17] pefile: Parse the "Microsoft individual code signing" data blob David Howells
                   ` (4 subsequent siblings)
  17 siblings, 0 replies; 23+ messages in thread
From: David Howells @ 2014-07-09 15:16 UTC (permalink / raw)
  To: keyrings; +Cc: linux-security-module, kexec, linux-kernel

Parse the content of the certificate blob, presuming it to be PKCS#7 format.

Signed-off-by: David Howells <dhowells@redhat.com>
Acked-by: Vivek Goyal <vgoyal@redhat.com>
Reviewed-by: Kees Cook <keescook@chromium.org>
---

 crypto/asymmetric_keys/verify_pefile.c |   21 ++++++++++++++++++++-
 1 file changed, 20 insertions(+), 1 deletion(-)


diff --git a/crypto/asymmetric_keys/verify_pefile.c b/crypto/asymmetric_keys/verify_pefile.c
index 2f5268c..13f3b44 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;
 }


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH14/17] pefile: Parse the "Microsoft individual code signing" data blob
  2014-07-09 15:15 [PATCH 00/17] KEYS: PKCS#7 and PE file signature checking for kexec David Howells
                   ` (12 preceding siblings ...)
  2014-07-09 15:16 ` [PATCH13/17] pefile: Parse the presumed PKCS#7 content of the certificate blob David Howells
@ 2014-07-09 15:16 ` David Howells
  2014-07-09 15:17 ` [PATCH15/17] pefile: Handle pesign using the wrong OID David Howells
                   ` (3 subsequent siblings)
  17 siblings, 0 replies; 23+ messages in thread
From: David Howells @ 2014-07-09 15:16 UTC (permalink / raw)
  To: keyrings; +Cc: linux-security-module, kexec, linux-kernel

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 <dhowells@redhat.com>
Acked-by: Vivek Goyal <vgoyal@redhat.com>
Reviewed-by: Kees Cook <keescook@chromium.org>
---

 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


diff --git a/crypto/asymmetric_keys/Makefile b/crypto/asymmetric_keys/Makefile
index 3e4de52..e47fcd9 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 0000000..6d09ba4
--- /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 0000000..09336c3
--- /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 <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/oid_registry.h>
+#include <crypto/pkcs7.h>
+#include "verify_pefile.h"
+#include "mscode-asn1.h"
+
+/*
+ * Parse a Microsoft Individual Code Signing blob
+ */
+int mscode_parse(struct pefile_context *ctx)
+{
+	const void *content_data;
+	size_t data_len;
+	int ret;
+
+	ret = pkcs7_get_content_data(ctx->pkcs7, &content_data, &data_len, 1);
+
+	if (ret) {
+		pr_debug("PKCS#7 message does not contain data\n");
+		return ret;
+	}
+
+	pr_devel("Data: %zu [%*ph]\n", data_len, (unsigned)(data_len),
+		 content_data);
+
+	return asn1_ber_decoder(&mscode_decoder, ctx, content_data, data_len);
+}
+
+/*
+ * Check the content type OID
+ */
+int mscode_note_content_type(void *context, size_t hdrlen,
+			     unsigned char tag,
+			     const void *value, size_t vlen)
+{
+	enum OID oid;
+
+	oid = look_up_OID(value, vlen);
+	if (oid == OID__NR) {
+		char buffer[50];
+
+		sprint_oid(value, vlen, buffer, sizeof(buffer));
+		pr_err("Unknown OID: %s\n", buffer);
+		return -EBADMSG;
+	}
+
+	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 13f3b44..b975918 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 e165d23..55d5f7e 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 edeff85..c2bbf67 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 */
 


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH15/17] pefile: Handle pesign using the wrong OID
  2014-07-09 15:15 [PATCH 00/17] KEYS: PKCS#7 and PE file signature checking for kexec David Howells
                   ` (13 preceding siblings ...)
  2014-07-09 15:16 ` [PATCH14/17] pefile: Parse the "Microsoft individual code signing" data blob David Howells
@ 2014-07-09 15:17 ` David Howells
  2014-07-09 15:17 ` [PATCH16/17] pefile: Digest the PE binary and compare to the PKCS#7 data David Howells
                   ` (2 subsequent siblings)
  17 siblings, 0 replies; 23+ messages in thread
From: David Howells @ 2014-07-09 15:17 UTC (permalink / raw)
  To: keyrings; +Cc: linux-security-module, kexec, linux-kernel

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 <vgoyal@redhat.com>
Acked-by: Vivek Goyal <vgoyal@redhat.com>
---

 crypto/asymmetric_keys/mscode_parser.c |    8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)


diff --git a/crypto/asymmetric_keys/mscode_parser.c b/crypto/asymmetric_keys/mscode_parser.c
index 09336c3..214a992 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;
 	}


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH16/17] pefile: Digest the PE binary and compare to the PKCS#7 data
  2014-07-09 15:15 [PATCH 00/17] KEYS: PKCS#7 and PE file signature checking for kexec David Howells
                   ` (14 preceding siblings ...)
  2014-07-09 15:17 ` [PATCH15/17] pefile: Handle pesign using the wrong OID David Howells
@ 2014-07-09 15:17 ` David Howells
  2014-07-09 15:17 ` [PATCH17/17] pefile: Validate PKCS#7 trust chain David Howells
  2014-07-09 16:03 ` [PATCH 00/17] KEYS: PKCS#7 and PE file signature checking for kexec Borislav Petkov
  17 siblings, 0 replies; 23+ messages in thread
From: David Howells @ 2014-07-09 15:17 UTC (permalink / raw)
  To: keyrings; +Cc: linux-security-module, kexec, linux-kernel

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 <dhowells@redhat.com>
Acked-by: Vivek Goyal <vgoyal@redhat.com>
Reviewed-by: Kees Cook <keescook@chromium.org>
---

 crypto/asymmetric_keys/verify_pefile.c |  197 ++++++++++++++++++++++++++++++++
 1 file changed, 197 insertions(+)


diff --git a/crypto/asymmetric_keys/verify_pefile.c b/crypto/asymmetric_keys/verify_pefile.c
index b975918..029a365 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:


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH17/17] pefile: Validate PKCS#7 trust chain
  2014-07-09 15:15 [PATCH 00/17] KEYS: PKCS#7 and PE file signature checking for kexec David Howells
                   ` (15 preceding siblings ...)
  2014-07-09 15:17 ` [PATCH16/17] pefile: Digest the PE binary and compare to the PKCS#7 data David Howells
@ 2014-07-09 15:17 ` David Howells
  2014-07-09 16:03 ` [PATCH 00/17] KEYS: PKCS#7 and PE file signature checking for kexec Borislav Petkov
  17 siblings, 0 replies; 23+ messages in thread
From: David Howells @ 2014-07-09 15:17 UTC (permalink / raw)
  To: keyrings; +Cc: linux-security-module, kexec, linux-kernel

Validate the PKCS#7 trust chain against the contents of the system keyring.

Signed-off-by: David Howells <dhowells@redhat.com>
Acked-by: Vivek Goyal <vgoyal@redhat.com>
---

 crypto/asymmetric_keys/verify_pefile.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)


diff --git a/crypto/asymmetric_keys/verify_pefile.c b/crypto/asymmetric_keys/verify_pefile.c
index 029a365..79175e6 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);


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* Re: [PATCH 00/17] KEYS: PKCS#7 and PE file signature checking for kexec
  2014-07-09 15:15 [PATCH 00/17] KEYS: PKCS#7 and PE file signature checking for kexec David Howells
                   ` (16 preceding siblings ...)
  2014-07-09 15:17 ` [PATCH17/17] pefile: Validate PKCS#7 trust chain David Howells
@ 2014-07-09 16:03 ` Borislav Petkov
  2014-07-09 16:21   ` Vivek Goyal
  2014-07-09 16:28   ` David Howells
  17 siblings, 2 replies; 23+ messages in thread
From: Borislav Petkov @ 2014-07-09 16:03 UTC (permalink / raw)
  To: David Howells, Vivek Goyal
  Cc: keyrings, linux-security-module, kexec, linux-kernel, x86-ml

Hi David,

On Wed, Jul 09, 2014 at 04:15:25PM +0100, David Howells wrote:
> David Howells (16):
>       X.509: Add bits needed for PKCS#7
>       X.509: Export certificate parse and free functions
>       PKCS#7: Implement a parser [RFC 2315]
>       PKCS#7: Digest the data in a signed-data message
>       PKCS#7: Find the right key in the PKCS#7 key list and verify the signature
>       PKCS#7: Verify internal certificate chain
>       PKCS#7: Find intersection between PKCS#7 message and known, trusted keys
>       PKCS#7: Provide a key type for testing PKCS#7
>       KEYS: X.509: Fix a spelling mistake
>       Provide PE binary definitions
>       pefile: Parse a PE binary to find a key and a signature contained therein
>       pefile: Strip the wrapper off of the cert data block
>       pefile: Parse the presumed PKCS#7 content of the certificate blob
>       pefile: Parse the "Microsoft individual code signing" data blob
>       pefile: Digest the PE binary and compare to the PKCS#7 data
>       pefile: Validate PKCS#7 trust chain
> 
> Vivek Goyal (1):
>       pefile: Handle pesign using the wrong OID

let me see if I get this straight:

this current submission is supposed to replace

http://lkml.kernel.org/r/20140708131504.28621.61165.stgit@warthog.procyon.org.uk

and Vivek's one:

http://lkml.kernel.org/r/1404421641-12691-1-git-send-email-vgoyal@redhat.com

(which added those parsers to arch/x86/kernel/ - not a good place anyway.)

?

The kexec bits with the sig verif will come ontop, it seems. What's the
story guys?

Thanks.

-- 
Regards/Gruss,
    Boris.

Sent from a fat crate under my desk. Formatting is fine.
--

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH 00/17] KEYS: PKCS#7 and PE file signature checking for kexec
  2014-07-09 16:03 ` [PATCH 00/17] KEYS: PKCS#7 and PE file signature checking for kexec Borislav Petkov
@ 2014-07-09 16:21   ` Vivek Goyal
  2014-07-09 16:28   ` David Howells
  1 sibling, 0 replies; 23+ messages in thread
From: Vivek Goyal @ 2014-07-09 16:21 UTC (permalink / raw)
  To: Borislav Petkov
  Cc: David Howells, keyrings, linux-security-module, kexec,
	linux-kernel, x86-ml, Andrew Morton, H. Peter Anvin

On Wed, Jul 09, 2014 at 06:03:49PM +0200, Borislav Petkov wrote:
> Hi David,
> 
> On Wed, Jul 09, 2014 at 04:15:25PM +0100, David Howells wrote:
> > David Howells (16):
> >       X.509: Add bits needed for PKCS#7
> >       X.509: Export certificate parse and free functions
> >       PKCS#7: Implement a parser [RFC 2315]
> >       PKCS#7: Digest the data in a signed-data message
> >       PKCS#7: Find the right key in the PKCS#7 key list and verify the signature
> >       PKCS#7: Verify internal certificate chain
> >       PKCS#7: Find intersection between PKCS#7 message and known, trusted keys
> >       PKCS#7: Provide a key type for testing PKCS#7
> >       KEYS: X.509: Fix a spelling mistake
> >       Provide PE binary definitions
> >       pefile: Parse a PE binary to find a key and a signature contained therein
> >       pefile: Strip the wrapper off of the cert data block
> >       pefile: Parse the presumed PKCS#7 content of the certificate blob
> >       pefile: Parse the "Microsoft individual code signing" data blob
> >       pefile: Digest the PE binary and compare to the PKCS#7 data
> >       pefile: Validate PKCS#7 trust chain
> > 
> > Vivek Goyal (1):
> >       pefile: Handle pesign using the wrong OID
> 
> let me see if I get this straight:
> 
> this current submission is supposed to replace
> 
> http://lkml.kernel.org/r/20140708131504.28621.61165.stgit@warthog.procyon.org.uk
> 
> and Vivek's one:
> 
> http://lkml.kernel.org/r/1404421641-12691-1-git-send-email-vgoyal@redhat.com
> 
> (which added those parsers to arch/x86/kernel/ - not a good place anyway.)
> 
> ?
> 
> The kexec bits with the sig verif will come ontop, it seems. What's the
> story guys?

[CC akpm and hpa]

Hi Boris,

Yes, this posting will replace above two postings as you said. David
howells anyway did all the original work. So he has refreshed all the
paches, fixed things, put them in right order and reposted.

These are the core patches required to make signature verification work
in bzImage.  We just need one more kexec patch on top which can call into
pefile logic to verify signature of file.

And that patch is sitting right now here.

https://git.kernel.org/cgit/linux/kernel/git/dhowells/linux-modsign.git/commit/?h=kexec-pefile&id=7de6fb25559c1dc42e203646c44202cbe2e2fc2c

I am planning to post this single patch for review with explicit mention
to david howells's posting.

So now we have 3 pieces.

- Kexec new system call patches in -mm tree.
- PKCS7 and PEFILE signature verification patches as posted in this
  patch series.
- Final kexec patch which makes use of PEFILE function to do singature
  verification. I will post that patch soon.

I am hoping that last patch can go through david howells tree as it is
dependent on PKCS7 and PEFILE changes.

Thanks
Vivek

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH 00/17] KEYS: PKCS#7 and PE file signature checking for kexec
  2014-07-09 16:03 ` [PATCH 00/17] KEYS: PKCS#7 and PE file signature checking for kexec Borislav Petkov
  2014-07-09 16:21   ` Vivek Goyal
@ 2014-07-09 16:28   ` David Howells
  1 sibling, 0 replies; 23+ messages in thread
From: David Howells @ 2014-07-09 16:28 UTC (permalink / raw)
  To: Borislav Petkov
  Cc: dhowells, Vivek Goyal, keyrings, linux-security-module, kexec,
	linux-kernel, x86-ml

Borislav Petkov <bp@alien8.de> wrote:

> let me see if I get this straight:
> 
> this current submission is supposed to replace
> 
> http://lkml.kernel.org/r/20140708131504.28621.61165.stgit@warthog.procyon.org.uk

Yes.  It's an extension of that.  Plus I did it wrong and managed to lose the
actual first patch, I realise.

> and Vivek's one:
> 
> http://lkml.kernel.org/r/1404421641-12691-1-git-send-email-vgoyal@redhat.com
> 
> (which added those parsers to arch/x86/kernel/ - not a good place anyway.)

Yep.

> The kexec bits with the sig verif will come ontop, it seems. What's the
> story guys?

Here's an example of what I think it could look like:

	http://git.kernel.org/cgit/linux/kernel/git/dhowells/linux-modsign.git/log/?h=kexec-pefile

I did the following:

 (1) Rebased all but the last of Vivek's patches onto the same base as my
     pkcs7 and pefile patches.

 (2) Substituted on of Andrew Morton's patches for one of Vivek's.

 (3) Stacked a merge of my stuff on top of Vivek's.

 (4) Stacked Vivek's final patch on top of that and fixed it to work with my
     changed stuff.

I'm leaving that to Vivek as to when he wants to push that out, though - and
whether or not he wants to push from my tree or his own.

David

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH06/17] PKCS#7: Verify internal certificate chain
  2014-07-09 15:16 ` [PATCH06/17] PKCS#7: Verify internal certificate chain David Howells
@ 2014-07-10 17:06   ` Valdis.Kletnieks
  2014-07-10 20:37     ` David Howells
  0 siblings, 1 reply; 23+ messages in thread
From: Valdis.Kletnieks @ 2014-07-10 17:06 UTC (permalink / raw)
  To: David Howells; +Cc: keyrings, linux-security-module, kexec, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 405 bytes --]

On Wed, 09 Jul 2014 16:16:04 +0100, David Howells said:
> 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.

What happens if we see a signature that we shouldn't be able to verify?  Or should
that changelog entry be reduced to "If any signature fails", period?

[-- Attachment #2: Type: application/pgp-signature, Size: 848 bytes --]

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH06/17] PKCS#7: Verify internal certificate chain
  2014-07-10 17:06   ` Valdis.Kletnieks
@ 2014-07-10 20:37     ` David Howells
  0 siblings, 0 replies; 23+ messages in thread
From: David Howells @ 2014-07-10 20:37 UTC (permalink / raw)
  To: Valdis.Kletnieks
  Cc: dhowells, keyrings, linux-security-module, kexec, linux-kernel

Valdis.Kletnieks@vt.edu wrote:

> > 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.
> 
> What happens if we see a signature that we shouldn't be able to verify?  Or
> should that changelog entry be reduced to "If any signature fails", period?

No.

When I say "any signature that we should be able to verify" I mean that a
signature for which we have an appropriate public key.

If we don't have a public key for a signature, we prune the trust chain at
that point.

What I mean is that the PKCS#7 message can have several signatures applied to
it.  We can form a chain from each signature going back through the X.509
certificates included in the PKCS#7 message:

	PKCS#7 ---> X.509 ---> X.509 ---> X.509 ---> X.509

where the PKCS#7 message and each X.509 cert has a signature that the next
X.509 cert in the chain can be used to verify with the public key contained
therein.

Any of the signatures in any of the chains can form an intersection point with
the keyring of public keys provided.  If there's a verified match on one or
more of them, we permit the message.

If any "Y ---> X.509" verification is rejected, we reject the whole message
because there's something wrong.  If an intersection point verification is
rejected, again we reject the whole message.

If there are no intersection points, we also reject the message, but with
ENOKEY rather than EKEYREJECTED.

David

^ permalink raw reply	[flat|nested] 23+ messages in thread

end of thread, other threads:[~2014-07-10 20:38 UTC | newest]

Thread overview: 23+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2014-07-09 15:15 [PATCH 00/17] KEYS: PKCS#7 and PE file signature checking for kexec David Howells
2014-07-09 15:15 ` [PATCH01/17] X.509: Add bits needed for PKCS#7 David Howells
2014-07-09 15:15 ` [PATCH02/17] X.509: Export certificate parse and free functions David Howells
2014-07-09 15:15 ` [PATCH03/17] PKCS#7: Implement a parser [RFC 2315] David Howells
2014-07-09 15:15 ` [PATCH04/17] PKCS#7: Digest the data in a signed-data message David Howells
2014-07-09 15:15 ` [PATCH05/17] PKCS#7: Find the right key in the PKCS#7 key list and verify the signature David Howells
2014-07-09 15:16 ` [PATCH06/17] PKCS#7: Verify internal certificate chain David Howells
2014-07-10 17:06   ` Valdis.Kletnieks
2014-07-10 20:37     ` David Howells
2014-07-09 15:16 ` [PATCH07/17] PKCS#7: Find intersection between PKCS#7 message and known, trusted keys David Howells
2014-07-09 15:16 ` [PATCH08/17] PKCS#7: Provide a key type for testing PKCS#7 David Howells
2014-07-09 15:16 ` [PATCH09/17] KEYS: X.509: Fix a spelling mistake David Howells
2014-07-09 15:16 ` [PATCH10/17] Provide PE binary definitions David Howells
2014-07-09 15:16 ` [PATCH11/17] pefile: Parse a PE binary to find a key and a signature contained therein David Howells
2014-07-09 15:16 ` [PATCH12/17] pefile: Strip the wrapper off of the cert data block David Howells
2014-07-09 15:16 ` [PATCH13/17] pefile: Parse the presumed PKCS#7 content of the certificate blob David Howells
2014-07-09 15:16 ` [PATCH14/17] pefile: Parse the "Microsoft individual code signing" data blob David Howells
2014-07-09 15:17 ` [PATCH15/17] pefile: Handle pesign using the wrong OID David Howells
2014-07-09 15:17 ` [PATCH16/17] pefile: Digest the PE binary and compare to the PKCS#7 data David Howells
2014-07-09 15:17 ` [PATCH17/17] pefile: Validate PKCS#7 trust chain David Howells
2014-07-09 16:03 ` [PATCH 00/17] KEYS: PKCS#7 and PE file signature checking for kexec Borislav Petkov
2014-07-09 16:21   ` Vivek Goyal
2014-07-09 16:28   ` David Howells

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox