xref: /openbmc/linux/crypto/asymmetric_keys/pkcs7_trust.c (revision 08815b62d700e4fbeb72a01986ad051c3dd84a15)
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  */
26 static 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  */
75 int 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 
144 matched:
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 
155 verified:
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  */
191 int 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 }
219 EXPORT_SYMBOL_GPL(pkcs7_validate_trust);
220