aboutsummaryrefslogtreecommitdiffstats
path: root/crypto
diff options
context:
space:
mode:
authorDavid Howells <dhowells@redhat.com>2014-07-01 11:02:51 -0400
committerDavid Howells <dhowells@redhat.com>2014-07-09 09:58:37 -0400
commit26d1164be37f1145a96af15f294122876d8e5c77 (patch)
tree3cf981b54d3a275710d840c3674e09271c00c36d /crypto
parent9c87e0f10e281f782312e7b6aa202f2d434c84bf (diff)
pefile: Parse a PE binary to find a key and a signature contained therein
Parse a PE binary to find a key and a signature contained therein. Later patches will check the signature and add the key if the signature checks out. Signed-off-by: David Howells <dhowells@redhat.com> Acked-by: Vivek Goyal <vgoyal@redhat.com> Reviewed-by: Kees Cook <keescook@chromium.org>
Diffstat (limited to 'crypto')
-rw-r--r--crypto/asymmetric_keys/Kconfig9
-rw-r--r--crypto/asymmetric_keys/Makefile8
-rw-r--r--crypto/asymmetric_keys/verify_pefile.c163
-rw-r--r--crypto/asymmetric_keys/verify_pefile.h37
4 files changed, 217 insertions, 0 deletions
diff --git a/crypto/asymmetric_keys/Kconfig b/crypto/asymmetric_keys/Kconfig
index 14cac2860afa..ca41be5631c7 100644
--- a/crypto/asymmetric_keys/Kconfig
+++ b/crypto/asymmetric_keys/Kconfig
@@ -59,4 +59,13 @@ config PKCS7_TEST_KEY
59 59
60 This is intended for testing the PKCS#7 parser. 60 This is intended for testing the PKCS#7 parser.
61 61
62config SIGNED_PE_FILE_VERIFICATION
63 bool "Support for PE file signature verification"
64 depends on PKCS7_MESSAGE_PARSER=y
65 select ASN1
66 select OID_REGISTRY
67 help
68 This option provides support for verifying the signature(s) on a
69 signed PE binary.
70
62endif # ASYMMETRIC_KEY_TYPE 71endif # ASYMMETRIC_KEY_TYPE
diff --git a/crypto/asymmetric_keys/Makefile b/crypto/asymmetric_keys/Makefile
index 92d0e9af24d5..3e4de5297015 100644
--- a/crypto/asymmetric_keys/Makefile
+++ b/crypto/asymmetric_keys/Makefile
@@ -47,3 +47,11 @@ clean-files += pkcs7-asn1.c pkcs7-asn1.h
47obj-$(CONFIG_PKCS7_TEST_KEY) += pkcs7_test_key.o 47obj-$(CONFIG_PKCS7_TEST_KEY) += pkcs7_test_key.o
48pkcs7_test_key-y := \ 48pkcs7_test_key-y := \
49 pkcs7_key_type.o 49 pkcs7_key_type.o
50
51#
52# Signed PE binary-wrapped key handling
53#
54obj-$(CONFIG_SIGNED_PE_FILE_VERIFICATION) += verify_signed_pefile.o
55
56verify_signed_pefile-y := \
57 verify_pefile.o
diff --git a/crypto/asymmetric_keys/verify_pefile.c b/crypto/asymmetric_keys/verify_pefile.c
new file mode 100644
index 000000000000..aec7c509404e
--- /dev/null
+++ b/crypto/asymmetric_keys/verify_pefile.c
@@ -0,0 +1,163 @@
1/* Parse a signed PE binary
2 *
3 * Copyright (C) 2014 Red Hat, Inc. All Rights Reserved.
4 * Written by David Howells (dhowells@redhat.com)
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public Licence
8 * as published by the Free Software Foundation; either version
9 * 2 of the Licence, or (at your option) any later version.
10 */
11
12#define pr_fmt(fmt) "PEFILE: "fmt
13#include <linux/module.h>
14#include <linux/kernel.h>
15#include <linux/slab.h>
16#include <linux/err.h>
17#include <linux/pe.h>
18#include <crypto/pkcs7.h>
19#include <crypto/hash.h>
20#include "verify_pefile.h"
21
22/*
23 * Parse a PE binary.
24 */
25static int pefile_parse_binary(const void *pebuf, unsigned int pelen,
26 struct pefile_context *ctx)
27{
28 const struct mz_hdr *mz = pebuf;
29 const struct pe_hdr *pe;
30 const struct pe32_opt_hdr *pe32;
31 const struct pe32plus_opt_hdr *pe64;
32 const struct data_directory *ddir;
33 const struct data_dirent *dde;
34 const struct section_header *secs, *sec;
35 size_t cursor, datalen = pelen;
36
37 kenter("");
38
39#define chkaddr(base, x, s) \
40 do { \
41 if ((x) < base || (s) >= datalen || (x) > datalen - (s)) \
42 return -ELIBBAD; \
43 } while (0)
44
45 chkaddr(0, 0, sizeof(*mz));
46 if (mz->magic != MZ_MAGIC)
47 return -ELIBBAD;
48 cursor = sizeof(*mz);
49
50 chkaddr(cursor, mz->peaddr, sizeof(*pe));
51 pe = pebuf + mz->peaddr;
52 if (pe->magic != PE_MAGIC)
53 return -ELIBBAD;
54 cursor = mz->peaddr + sizeof(*pe);
55
56 chkaddr(0, cursor, sizeof(pe32->magic));
57 pe32 = pebuf + cursor;
58 pe64 = pebuf + cursor;
59
60 switch (pe32->magic) {
61 case PE_OPT_MAGIC_PE32:
62 chkaddr(0, cursor, sizeof(*pe32));
63 ctx->image_checksum_offset =
64 (unsigned long)&pe32->csum - (unsigned long)pebuf;
65 ctx->header_size = pe32->header_size;
66 cursor += sizeof(*pe32);
67 ctx->n_data_dirents = pe32->data_dirs;
68 break;
69
70 case PE_OPT_MAGIC_PE32PLUS:
71 chkaddr(0, cursor, sizeof(*pe64));
72 ctx->image_checksum_offset =
73 (unsigned long)&pe64->csum - (unsigned long)pebuf;
74 ctx->header_size = pe64->header_size;
75 cursor += sizeof(*pe64);
76 ctx->n_data_dirents = pe64->data_dirs;
77 break;
78
79 default:
80 pr_debug("Unknown PEOPT magic = %04hx\n", pe32->magic);
81 return -ELIBBAD;
82 }
83
84 pr_debug("checksum @ %x\n", ctx->image_checksum_offset);
85 pr_debug("header size = %x\n", ctx->header_size);
86
87 if (cursor >= ctx->header_size || ctx->header_size >= datalen)
88 return -ELIBBAD;
89
90 if (ctx->n_data_dirents > (ctx->header_size - cursor) / sizeof(*dde))
91 return -ELIBBAD;
92
93 ddir = pebuf + cursor;
94 cursor += sizeof(*dde) * ctx->n_data_dirents;
95
96 ctx->cert_dirent_offset =
97 (unsigned long)&ddir->certs - (unsigned long)pebuf;
98 ctx->certs_size = ddir->certs.size;
99
100 if (!ddir->certs.virtual_address || !ddir->certs.size) {
101 pr_debug("Unsigned PE binary\n");
102 return -EKEYREJECTED;
103 }
104
105 chkaddr(ctx->header_size, ddir->certs.virtual_address,
106 ddir->certs.size);
107 ctx->sig_offset = ddir->certs.virtual_address;
108 ctx->sig_len = ddir->certs.size;
109 pr_debug("cert = %x @%x [%*ph]\n",
110 ctx->sig_len, ctx->sig_offset,
111 ctx->sig_len, pebuf + ctx->sig_offset);
112
113 ctx->n_sections = pe->sections;
114 if (ctx->n_sections > (ctx->header_size - cursor) / sizeof(*sec))
115 return -ELIBBAD;
116 ctx->secs = secs = pebuf + cursor;
117
118 return 0;
119}
120
121/**
122 * verify_pefile_signature - Verify the signature on a PE binary image
123 * @pebuf: Buffer containing the PE binary image
124 * @pelen: Length of the binary image
125 * @trust_keyring: Signing certificates to use as starting points
126 * @_trusted: Set to true if trustworth, false otherwise
127 *
128 * Validate that the certificate chain inside the PKCS#7 message inside the PE
129 * binary image intersects keys we already know and trust.
130 *
131 * Returns, in order of descending priority:
132 *
133 * (*) -ELIBBAD if the image cannot be parsed, or:
134 *
135 * (*) -EKEYREJECTED if a signature failed to match for which we have a valid
136 * key, or:
137 *
138 * (*) 0 if at least one signature chain intersects with the keys in the trust
139 * keyring, or:
140 *
141 * (*) -ENOPKG if a suitable crypto module couldn't be found for a check on a
142 * chain.
143 *
144 * (*) -ENOKEY if we couldn't find a match for any of the signature chains in
145 * the message.
146 *
147 * May also return -ENOMEM.
148 */
149int verify_pefile_signature(const void *pebuf, unsigned pelen,
150 struct key *trusted_keyring, bool *_trusted)
151{
152 struct pefile_context ctx;
153 int ret;
154
155 kenter("");
156
157 memset(&ctx, 0, sizeof(ctx));
158 ret = pefile_parse_binary(pebuf, pelen, &ctx);
159 if (ret < 0)
160 return ret;
161
162 return -ENOANO; // Not yet complete
163}
diff --git a/crypto/asymmetric_keys/verify_pefile.h b/crypto/asymmetric_keys/verify_pefile.h
new file mode 100644
index 000000000000..e165d23458d4
--- /dev/null
+++ b/crypto/asymmetric_keys/verify_pefile.h
@@ -0,0 +1,37 @@
1/* PE Binary parser bits
2 *
3 * Copyright (C) 2014 Red Hat, Inc. All Rights Reserved.
4 * Written by David Howells (dhowells@redhat.com)
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public Licence
8 * as published by the Free Software Foundation; either version
9 * 2 of the Licence, or (at your option) any later version.
10 */
11
12#include <linux/verify_pefile.h>
13#include <crypto/pkcs7.h>
14#include <crypto/hash_info.h>
15
16struct pefile_context {
17 unsigned header_size;
18 unsigned image_checksum_offset;
19 unsigned cert_dirent_offset;
20 unsigned n_data_dirents;
21 unsigned n_sections;
22 unsigned certs_size;
23 unsigned sig_offset;
24 unsigned sig_len;
25 const struct section_header *secs;
26 struct pkcs7_message *pkcs7;
27
28 /* PKCS#7 MS Individual Code Signing content */
29 const void *digest; /* Digest */
30 unsigned digest_len; /* Digest length */
31 enum hash_algo digest_algo; /* Digest algorithm */
32};
33
34#define kenter(FMT, ...) \
35 pr_devel("==> %s("FMT")\n", __func__, ##__VA_ARGS__)
36#define kleave(FMT, ...) \
37 pr_devel("<== %s()"FMT"\n", __func__, ##__VA_ARGS__)