aboutsummaryrefslogtreecommitdiffstats
path: root/crypto
diff options
context:
space:
mode:
authorDavid Howells <dhowells@redhat.com>2014-07-01 11:40:20 -0400
committerDavid Howells <dhowells@redhat.com>2014-07-08 08:50:15 -0400
commit08815b62d700e4fbeb72a01986ad051c3dd84a15 (patch)
tree9a3df820a9716348fa947986d2310d50e14f9964 /crypto
parent8c76d79393ccc9b89d9af402d79a49a9cd43c5aa (diff)
PKCS#7: Find intersection between PKCS#7 message and known, trusted keys
Find the intersection between the X.509 certificate chain contained in a PKCS#7 message and a set of keys that we already know and trust. Signed-off-by: David Howells <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/Makefile1
-rw-r--r--crypto/asymmetric_keys/pkcs7_trust.c219
2 files changed, 220 insertions, 0 deletions
diff --git a/crypto/asymmetric_keys/Makefile b/crypto/asymmetric_keys/Makefile
index b6b39e7bea01..d63cb4320b96 100644
--- a/crypto/asymmetric_keys/Makefile
+++ b/crypto/asymmetric_keys/Makefile
@@ -33,6 +33,7 @@ obj-$(CONFIG_PKCS7_MESSAGE_PARSER) += pkcs7_message.o
33pkcs7_message-y := \ 33pkcs7_message-y := \
34 pkcs7-asn1.o \ 34 pkcs7-asn1.o \
35 pkcs7_parser.o \ 35 pkcs7_parser.o \
36 pkcs7_trust.o \
36 pkcs7_verify.o 37 pkcs7_verify.o
37 38
38$(obj)/pkcs7_parser.o: $(obj)/pkcs7-asn1.h 39$(obj)/pkcs7_parser.o: $(obj)/pkcs7-asn1.h
diff --git a/crypto/asymmetric_keys/pkcs7_trust.c b/crypto/asymmetric_keys/pkcs7_trust.c
new file mode 100644
index 000000000000..b6b045131403
--- /dev/null
+++ b/crypto/asymmetric_keys/pkcs7_trust.c
@@ -0,0 +1,219 @@
1/* Validate the trust chain of a PKCS#7 message.
2 *
3 * Copyright (C) 2012 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) "PKCS7: "fmt
13#include <linux/kernel.h>
14#include <linux/export.h>
15#include <linux/slab.h>
16#include <linux/err.h>
17#include <linux/asn1.h>
18#include <linux/key.h>
19#include <keys/asymmetric-type.h>
20#include "public_key.h"
21#include "pkcs7_parser.h"
22
23/*
24 * Request an asymmetric key.
25 */
26static struct key *pkcs7_request_asymmetric_key(
27 struct key *keyring,
28 const char *signer, size_t signer_len,
29 const char *authority, size_t auth_len)
30{
31 key_ref_t key;
32 char *id;
33
34 kenter(",%zu,,%zu", signer_len, auth_len);
35
36 /* Construct an identifier. */
37 id = kmalloc(signer_len + 2 + auth_len + 1, GFP_KERNEL);
38 if (!id)
39 return ERR_PTR(-ENOMEM);
40
41 memcpy(id, signer, signer_len);
42 id[signer_len + 0] = ':';
43 id[signer_len + 1] = ' ';
44 memcpy(id + signer_len + 2, authority, auth_len);
45 id[signer_len + 2 + auth_len] = 0;
46
47 pr_debug("Look up: \"%s\"\n", id);
48
49 key = keyring_search(make_key_ref(keyring, 1),
50 &key_type_asymmetric, id);
51 if (IS_ERR(key))
52 pr_debug("Request for module key '%s' err %ld\n",
53 id, PTR_ERR(key));
54 kfree(id);
55
56 if (IS_ERR(key)) {
57 switch (PTR_ERR(key)) {
58 /* Hide some search errors */
59 case -EACCES:
60 case -ENOTDIR:
61 case -EAGAIN:
62 return ERR_PTR(-ENOKEY);
63 default:
64 return ERR_CAST(key);
65 }
66 }
67
68 pr_devel("<==%s() = 0 [%x]\n", __func__, key_serial(key_ref_to_ptr(key)));
69 return key_ref_to_ptr(key);
70}
71
72/**
73 * Check the trust on one PKCS#7 SignedInfo block.
74 */
75int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7,
76 struct pkcs7_signed_info *sinfo,
77 struct key *trust_keyring)
78{
79 struct public_key_signature *sig = &sinfo->sig;
80 struct x509_certificate *x509, *last = NULL, *p;
81 struct key *key;
82 bool trusted;
83 int ret;
84
85 kenter(",%u,", sinfo->index);
86
87 for (x509 = sinfo->signer; x509; x509 = x509->signer) {
88 if (x509->seen) {
89 if (x509->verified) {
90 trusted = x509->trusted;
91 goto verified;
92 }
93 kleave(" = -ENOKEY [cached]");
94 return -ENOKEY;
95 }
96 x509->seen = true;
97
98 /* Look to see if this certificate is present in the trusted
99 * keys.
100 */
101 key = pkcs7_request_asymmetric_key(
102 trust_keyring,
103 x509->subject, strlen(x509->subject),
104 x509->fingerprint, strlen(x509->fingerprint));
105 if (!IS_ERR(key))
106 /* One of the X.509 certificates in the PKCS#7 message
107 * is apparently the same as one we already trust.
108 * Verify that the trusted variant can also validate
109 * the signature on the descendant.
110 */
111 goto matched;
112 if (key == ERR_PTR(-ENOMEM))
113 return -ENOMEM;
114
115 /* Self-signed certificates form roots of their own, and if we
116 * don't know them, then we can't accept them.
117 */
118 if (x509->next == x509) {
119 kleave(" = -ENOKEY [unknown self-signed]");
120 return -ENOKEY;
121 }
122
123 might_sleep();
124 last = x509;
125 sig = &last->sig;
126 }
127
128 /* No match - see if the root certificate has a signer amongst the
129 * trusted keys.
130 */
131 if (!last || !last->issuer || !last->authority) {
132 kleave(" = -ENOKEY [no backref]");
133 return -ENOKEY;
134 }
135
136 key = pkcs7_request_asymmetric_key(
137 trust_keyring,
138 last->issuer, strlen(last->issuer),
139 last->authority, strlen(last->authority));
140 if (IS_ERR(key))
141 return PTR_ERR(key) == -ENOMEM ? -ENOMEM : -ENOKEY;
142 x509 = last;
143
144matched:
145 ret = verify_signature(key, sig);
146 trusted = test_bit(KEY_FLAG_TRUSTED, &key->flags);
147 key_put(key);
148 if (ret < 0) {
149 if (ret == -ENOMEM)
150 return ret;
151 kleave(" = -EKEYREJECTED [verify %d]", ret);
152 return -EKEYREJECTED;
153 }
154
155verified:
156 x509->verified = true;
157 for (p = sinfo->signer; p != x509; p = p->signer) {
158 p->verified = true;
159 p->trusted = trusted;
160 }
161 sinfo->trusted = trusted;
162 kleave(" = 0");
163 return 0;
164}
165
166/**
167 * pkcs7_validate_trust - Validate PKCS#7 trust chain
168 * @pkcs7: The PKCS#7 certificate to validate
169 * @trust_keyring: Signing certificates to use as starting points
170 * @_trusted: Set to true if trustworth, false otherwise
171 *
172 * Validate that the certificate chain inside the PKCS#7 message intersects
173 * keys we already know and trust.
174 *
175 * Returns, in order of descending priority:
176 *
177 * (*) -EKEYREJECTED if a signature failed to match for which we have a valid
178 * key, or:
179 *
180 * (*) 0 if at least one signature chain intersects with the keys in the trust
181 * keyring, or:
182 *
183 * (*) -ENOPKG if a suitable crypto module couldn't be found for a check on a
184 * chain.
185 *
186 * (*) -ENOKEY if we couldn't find a match for any of the signature chains in
187 * the message.
188 *
189 * May also return -ENOMEM.
190 */
191int pkcs7_validate_trust(struct pkcs7_message *pkcs7,
192 struct key *trust_keyring,
193 bool *_trusted)
194{
195 struct pkcs7_signed_info *sinfo;
196 struct x509_certificate *p;
197 int cached_ret = 0, ret;
198
199 for (p = pkcs7->certs; p; p = p->next)
200 p->seen = false;
201
202 for (sinfo = pkcs7->signed_infos; sinfo; sinfo = sinfo->next) {
203 ret = pkcs7_validate_trust_one(pkcs7, sinfo, trust_keyring);
204 if (ret < 0) {
205 if (ret == -ENOPKG) {
206 cached_ret = -ENOPKG;
207 } else if (ret == -ENOKEY) {
208 if (cached_ret == 0)
209 cached_ret = -ENOKEY;
210 } else {
211 return ret;
212 }
213 }
214 *_trusted |= sinfo->trusted;
215 }
216
217 return cached_ret;
218}
219EXPORT_SYMBOL_GPL(pkcs7_validate_trust);