Branch data Line data Source code
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 : 0 : int pkcs7_validate_trust(struct pkcs7_message *pkcs7,
159 : : struct key *trust_keyring)
160 : : {
161 : 0 : struct pkcs7_signed_info *sinfo;
162 : 0 : struct x509_certificate *p;
163 : 0 : int cached_ret = -ENOKEY;
164 : 0 : int ret;
165 : :
166 [ # # ]: 0 : for (p = pkcs7->certs; p; p = p->next)
167 : 0 : p->seen = false;
168 : :
169 [ # # ]: 0 : for (sinfo = pkcs7->signed_infos; sinfo; sinfo = sinfo->next) {
170 : 0 : ret = pkcs7_validate_trust_one(pkcs7, sinfo, trust_keyring);
171 [ # # # # ]: 0 : switch (ret) {
172 : 0 : case -ENOKEY:
173 : 0 : continue;
174 : 0 : case -ENOPKG:
175 [ # # ]: 0 : if (cached_ret == -ENOKEY)
176 : 0 : cached_ret = -ENOPKG;
177 : 0 : continue;
178 : 0 : case 0:
179 : 0 : cached_ret = 0;
180 : 0 : continue;
181 : : default:
182 : : return ret;
183 : : }
184 : : }
185 : :
186 : : return cached_ret;
187 : : }
188 : : EXPORT_SYMBOL_GPL(pkcs7_validate_trust);
|