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 <crypto/public_key.h>
21 #include "pkcs7_parser.h"
22 
23 /**
24  * Check the trust on one PKCS#7 SignedInfo block.
25  */
26 static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7,
27 				    struct pkcs7_signed_info *sinfo,
28 				    struct key *trust_keyring)
29 {
30 	struct public_key_signature *sig = sinfo->sig;
31 	struct x509_certificate *x509, *last = NULL, *p;
32 	struct key *key;
33 	int ret;
34 
35 	kenter(",%u,", sinfo->index);
36 
37 	if (sinfo->unsupported_crypto) {
38 		kleave(" = -ENOPKG [cached]");
39 		return -ENOPKG;
40 	}
41 
42 	for (x509 = sinfo->signer; x509; x509 = x509->signer) {
43 		if (x509->seen) {
44 			if (x509->verified)
45 				goto verified;
46 			kleave(" = -ENOKEY [cached]");
47 			return -ENOKEY;
48 		}
49 		x509->seen = true;
50 
51 		/* Look to see if this certificate is present in the trusted
52 		 * keys.
53 		 */
54 		key = find_asymmetric_key(trust_keyring,
55 					  x509->id, x509->skid, false);
56 		if (!IS_ERR(key)) {
57 			/* One of the X.509 certificates in the PKCS#7 message
58 			 * is apparently the same as one we already trust.
59 			 * Verify that the trusted variant can also validate
60 			 * the signature on the descendant.
61 			 */
62 			pr_devel("sinfo %u: Cert %u as key %x\n",
63 				 sinfo->index, x509->index, key_serial(key));
64 			goto matched;
65 		}
66 		if (key == ERR_PTR(-ENOMEM))
67 			return -ENOMEM;
68 
69 		 /* Self-signed certificates form roots of their own, and if we
70 		  * don't know them, then we can't accept them.
71 		  */
72 		if (x509->signer == x509) {
73 			kleave(" = -ENOKEY [unknown self-signed]");
74 			return -ENOKEY;
75 		}
76 
77 		might_sleep();
78 		last = x509;
79 		sig = last->sig;
80 	}
81 
82 	/* No match - see if the root certificate has a signer amongst the
83 	 * trusted keys.
84 	 */
85 	if (last && (last->sig->auth_ids[0] || last->sig->auth_ids[1])) {
86 		key = find_asymmetric_key(trust_keyring,
87 					  last->sig->auth_ids[0],
88 					  last->sig->auth_ids[1],
89 					  false);
90 		if (!IS_ERR(key)) {
91 			x509 = last;
92 			pr_devel("sinfo %u: Root cert %u signer is key %x\n",
93 				 sinfo->index, x509->index, key_serial(key));
94 			goto matched;
95 		}
96 		if (PTR_ERR(key) != -ENOKEY)
97 			return PTR_ERR(key);
98 	}
99 
100 	/* As a last resort, see if we have a trusted public key that matches
101 	 * the signed info directly.
102 	 */
103 	key = find_asymmetric_key(trust_keyring,
104 				  sinfo->sig->auth_ids[0], NULL, false);
105 	if (!IS_ERR(key)) {
106 		pr_devel("sinfo %u: Direct signer is key %x\n",
107 			 sinfo->index, key_serial(key));
108 		x509 = NULL;
109 		sig = sinfo->sig;
110 		goto matched;
111 	}
112 	if (PTR_ERR(key) != -ENOKEY)
113 		return PTR_ERR(key);
114 
115 	kleave(" = -ENOKEY [no backref]");
116 	return -ENOKEY;
117 
118 matched:
119 	ret = verify_signature(key, sig);
120 	key_put(key);
121 	if (ret < 0) {
122 		if (ret == -ENOMEM)
123 			return ret;
124 		kleave(" = -EKEYREJECTED [verify %d]", ret);
125 		return -EKEYREJECTED;
126 	}
127 
128 verified:
129 	if (x509) {
130 		x509->verified = true;
131 		for (p = sinfo->signer; p != x509; p = p->signer)
132 			p->verified = true;
133 	}
134 	kleave(" = 0");
135 	return 0;
136 }
137 
138 /**
139  * pkcs7_validate_trust - Validate PKCS#7 trust chain
140  * @pkcs7: The PKCS#7 certificate to validate
141  * @trust_keyring: Signing certificates to use as starting points
142  *
143  * Validate that the certificate chain inside the PKCS#7 message intersects
144  * keys we already know and trust.
145  *
146  * Returns, in order of descending priority:
147  *
148  *  (*) -EKEYREJECTED if a signature failed to match for which we have a valid
149  *	key, or:
150  *
151  *  (*) 0 if at least one signature chain intersects with the keys in the trust
152  *	keyring, or:
153  *
154  *  (*) -ENOPKG if a suitable crypto module couldn't be found for a check on a
155  *	chain.
156  *
157  *  (*) -ENOKEY if we couldn't find a match for any of the signature chains in
158  *	the message.
159  *
160  * May also return -ENOMEM.
161  */
162 int pkcs7_validate_trust(struct pkcs7_message *pkcs7,
163 			 struct key *trust_keyring)
164 {
165 	struct pkcs7_signed_info *sinfo;
166 	struct x509_certificate *p;
167 	int cached_ret = -ENOKEY;
168 	int ret;
169 
170 	for (p = pkcs7->certs; p; p = p->next)
171 		p->seen = false;
172 
173 	for (sinfo = pkcs7->signed_infos; sinfo; sinfo = sinfo->next) {
174 		ret = pkcs7_validate_trust_one(pkcs7, sinfo, trust_keyring);
175 		switch (ret) {
176 		case -ENOKEY:
177 			continue;
178 		case -ENOPKG:
179 			if (cached_ret == -ENOKEY)
180 				cached_ret = -ENOPKG;
181 			continue;
182 		case 0:
183 			cached_ret = 0;
184 			continue;
185 		default:
186 			return ret;
187 		}
188 	}
189 
190 	return cached_ret;
191 }
192 EXPORT_SYMBOL_GPL(pkcs7_validate_trust);
193