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 : 207 : static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7,
23 : : struct pkcs7_signed_info *sinfo,
24 : : struct key *trust_keyring)
25 : : {
26 : 207 : 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 [ + - ]: 207 : if (sinfo->unsupported_crypto) {
34 : : kleave(" = -ENOPKG [cached]");
35 : : return -ENOPKG;
36 : : }
37 : :
38 [ + - ]: 207 : for (x509 = sinfo->signer; x509; x509 = x509->signer) {
39 [ - + ]: 207 : if (x509->seen) {
40 [ # # ]: 0 : if (x509->verified)
41 : : goto verified;
42 : : kleave(" = -ENOKEY [cached]");
43 : : return -ENOKEY;
44 : : }
45 : 207 : x509->seen = true;
46 : :
47 : : /* Look to see if this certificate is present in the trusted
48 : : * keys.
49 : : */
50 : 207 : key = find_asymmetric_key(trust_keyring,
51 : 207 : x509->id, x509->skid, false);
52 [ - + ]: 207 : 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 [ # # ]: 0 : 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 [ # # ]: 0 : if (x509->signer == x509) {
69 : : kleave(" = -ENOKEY [unknown self-signed]");
70 : : return -ENOKEY;
71 : : }
72 : :
73 : 0 : might_sleep();
74 : : last = x509;
75 : 0 : sig = last->sig;
76 : : }
77 : :
78 : : /* No match - see if the root certificate has a signer amongst the
79 : : * trusted keys.
80 : : */
81 [ # # # # : 0 : if (last && (last->sig->auth_ids[0] || last->sig->auth_ids[1])) {
# # ]
82 : 0 : key = find_asymmetric_key(trust_keyring,
83 : : last->sig->auth_ids[0],
84 : 0 : last->sig->auth_ids[1],
85 : : false);
86 [ # # ]: 0 : if (!IS_ERR(key)) {
87 : 0 : x509 = last;
88 : : pr_devel("sinfo %u: Root cert %u signer is key %x\n",
89 : : sinfo->index, x509->index, key_serial(key));
90 : 0 : goto matched;
91 : : }
92 [ # # ]: 0 : 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 : 0 : key = find_asymmetric_key(trust_keyring,
100 : 0 : sinfo->sig->auth_ids[0], NULL, false);
101 [ # # ]: 0 : 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 : 0 : sig = sinfo->sig;
106 : 0 : goto matched;
107 : : }
108 [ # # ]: 0 : if (PTR_ERR(key) != -ENOKEY)
109 : 0 : return PTR_ERR(key);
110 : :
111 : : kleave(" = -ENOKEY [no backref]");
112 : : return -ENOKEY;
113 : :
114 : : matched:
115 : 207 : ret = verify_signature(key, sig);
116 : 207 : key_put(key);
117 [ - + ]: 207 : if (ret < 0) {
118 [ # # ]: 0 : if (ret == -ENOMEM)
119 : 0 : return ret;
120 : : kleave(" = -EKEYREJECTED [verify %d]", ret);
121 : : return -EKEYREJECTED;
122 : : }
123 : :
124 : : verified:
125 [ + - ]: 207 : if (x509) {
126 : 207 : x509->verified = true;
127 [ - + ]: 207 : for (p = sinfo->signer; p != x509; p = p->signer)
128 : 0 : 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 : 207 : 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 [ + + ]: 414 : for (p = pkcs7->certs; p; p = p->next)
167 : 207 : p->seen = false;
168 : :
169 [ + + ]: 414 : for (sinfo = pkcs7->signed_infos; sinfo; sinfo = sinfo->next) {
170 : 207 : ret = pkcs7_validate_trust_one(pkcs7, sinfo, trust_keyring);
171 [ - - + - ]: 207 : switch (ret) {
172 : : case -ENOKEY:
173 : 0 : continue;
174 : : case -ENOPKG:
175 [ # # ]: 0 : if (cached_ret == -ENOKEY)
176 : : cached_ret = -ENOPKG;
177 : 0 : continue;
178 : : case 0:
179 : : cached_ret = 0;
180 : 207 : continue;
181 : : default:
182 : 0 : return ret;
183 : : }
184 : : }
185 : :
186 : 207 : return cached_ret;
187 : : }
188 : : EXPORT_SYMBOL_GPL(pkcs7_validate_trust);
|