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->next == 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 		goto matched;
110 	}
111 	if (PTR_ERR(key) != -ENOKEY)
112 		return PTR_ERR(key);
113 
114 	kleave(" = -ENOKEY [no backref]");
115 	return -ENOKEY;
116 
117 matched:
118 	ret = verify_signature(key, sig);
119 	key_put(key);
120 	if (ret < 0) {
121 		if (ret == -ENOMEM)
122 			return ret;
123 		kleave(" = -EKEYREJECTED [verify %d]", ret);
124 		return -EKEYREJECTED;
125 	}
126 
127 verified:
128 	if (x509) {
129 		x509->verified = true;
130 		for (p = sinfo->signer; p != x509; p = p->signer)
131 			p->verified = true;
132 	}
133 	kleave(" = 0");
134 	return 0;
135 }
136 
137 /**
138  * pkcs7_validate_trust - Validate PKCS#7 trust chain
139  * @pkcs7: The PKCS#7 certificate to validate
140  * @trust_keyring: Signing certificates to use as starting points
141  *
142  * Validate that the certificate chain inside the PKCS#7 message intersects
143  * keys we already know and trust.
144  *
145  * Returns, in order of descending priority:
146  *
147  *  (*) -EKEYREJECTED if a signature failed to match for which we have a valid
148  *	key, or:
149  *
150  *  (*) 0 if at least one signature chain intersects with the keys in the trust
151  *	keyring, or:
152  *
153  *  (*) -ENOPKG if a suitable crypto module couldn't be found for a check on a
154  *	chain.
155  *
156  *  (*) -ENOKEY if we couldn't find a match for any of the signature chains in
157  *	the message.
158  *
159  * May also return -ENOMEM.
160  */
161 int pkcs7_validate_trust(struct pkcs7_message *pkcs7,
162 			 struct key *trust_keyring)
163 {
164 	struct pkcs7_signed_info *sinfo;
165 	struct x509_certificate *p;
166 	int cached_ret = -ENOKEY;
167 	int ret;
168 
169 	for (p = pkcs7->certs; p; p = p->next)
170 		p->seen = false;
171 
172 	for (sinfo = pkcs7->signed_infos; sinfo; sinfo = sinfo->next) {
173 		ret = pkcs7_validate_trust_one(pkcs7, sinfo, trust_keyring);
174 		switch (ret) {
175 		case -ENOKEY:
176 			continue;
177 		case -ENOPKG:
178 			if (cached_ret == -ENOKEY)
179 				cached_ret = -ENOPKG;
180 			continue;
181 		case 0:
182 			cached_ret = 0;
183 			continue;
184 		default:
185 			return ret;
186 		}
187 	}
188 
189 	return cached_ret;
190 }
191 EXPORT_SYMBOL_GPL(pkcs7_validate_trust);
192