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