Commit | Line | Data |
---|---|---|
c26fd69f DH |
1 | /* Instantiate a public key crypto key from an X.509 Certificate |
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) "X.509: "fmt | |
13 | #include <linux/module.h> | |
14 | #include <linux/kernel.h> | |
15 | #include <linux/slab.h> | |
16 | #include <linux/err.h> | |
17 | #include <linux/mpi.h> | |
18 | #include <linux/asn1_decoder.h> | |
19 | #include <keys/asymmetric-subtype.h> | |
20 | #include <keys/asymmetric-parser.h> | |
09fbc473 | 21 | #include <keys/system_keyring.h> |
c26fd69f DH |
22 | #include <crypto/hash.h> |
23 | #include "asymmetric_keys.h" | |
24 | #include "public_key.h" | |
25 | #include "x509_parser.h" | |
26 | ||
09fbc473 MZ |
27 | /* |
28 | * Find a key in the given keyring by issuer and authority. | |
29 | */ | |
30 | static struct key *x509_request_asymmetric_key( | |
31 | struct key *keyring, | |
32 | const char *signer, size_t signer_len, | |
33 | const char *authority, size_t auth_len) | |
34 | { | |
35 | key_ref_t key; | |
36 | char *id; | |
37 | ||
38 | /* Construct an identifier. */ | |
39 | id = kmalloc(signer_len + 2 + auth_len + 1, GFP_KERNEL); | |
40 | if (!id) | |
41 | return ERR_PTR(-ENOMEM); | |
42 | ||
43 | memcpy(id, signer, signer_len); | |
44 | id[signer_len + 0] = ':'; | |
45 | id[signer_len + 1] = ' '; | |
46 | memcpy(id + signer_len + 2, authority, auth_len); | |
47 | id[signer_len + 2 + auth_len] = 0; | |
48 | ||
49 | pr_debug("Look up: \"%s\"\n", id); | |
50 | ||
51 | key = keyring_search(make_key_ref(keyring, 1), | |
52 | &key_type_asymmetric, id); | |
53 | if (IS_ERR(key)) | |
54 | pr_debug("Request for module key '%s' err %ld\n", | |
55 | id, PTR_ERR(key)); | |
56 | kfree(id); | |
57 | ||
58 | if (IS_ERR(key)) { | |
59 | switch (PTR_ERR(key)) { | |
60 | /* Hide some search errors */ | |
61 | case -EACCES: | |
62 | case -ENOTDIR: | |
63 | case -EAGAIN: | |
64 | return ERR_PTR(-ENOKEY); | |
65 | default: | |
66 | return ERR_CAST(key); | |
67 | } | |
68 | } | |
69 | ||
70 | pr_devel("<==%s() = 0 [%x]\n", __func__, key_serial(key_ref_to_ptr(key))); | |
71 | return key_ref_to_ptr(key); | |
72 | } | |
73 | ||
c26fd69f | 74 | /* |
b426beb6 DH |
75 | * Set up the signature parameters in an X.509 certificate. This involves |
76 | * digesting the signed data and extracting the signature. | |
c26fd69f | 77 | */ |
b426beb6 | 78 | int x509_get_sig_params(struct x509_certificate *cert) |
c26fd69f | 79 | { |
c26fd69f DH |
80 | struct crypto_shash *tfm; |
81 | struct shash_desc *desc; | |
82 | size_t digest_size, desc_size; | |
b426beb6 | 83 | void *digest; |
c26fd69f DH |
84 | int ret; |
85 | ||
86 | pr_devel("==>%s()\n", __func__); | |
b426beb6 DH |
87 | |
88 | if (cert->sig.rsa.s) | |
89 | return 0; | |
90 | ||
91 | cert->sig.rsa.s = mpi_read_raw_data(cert->raw_sig, cert->raw_sig_size); | |
92 | if (!cert->sig.rsa.s) | |
93 | return -ENOMEM; | |
94 | cert->sig.nr_mpi = 1; | |
95 | ||
c26fd69f DH |
96 | /* Allocate the hashing algorithm we're going to need and find out how |
97 | * big the hash operational data will be. | |
98 | */ | |
3fe78ca2 | 99 | tfm = crypto_alloc_shash(hash_algo_name[cert->sig.pkey_hash_algo], 0, 0); |
c26fd69f DH |
100 | if (IS_ERR(tfm)) |
101 | return (PTR_ERR(tfm) == -ENOENT) ? -ENOPKG : PTR_ERR(tfm); | |
102 | ||
103 | desc_size = crypto_shash_descsize(tfm) + sizeof(*desc); | |
104 | digest_size = crypto_shash_digestsize(tfm); | |
105 | ||
b426beb6 DH |
106 | /* We allocate the hash operational data storage on the end of the |
107 | * digest storage space. | |
c26fd69f DH |
108 | */ |
109 | ret = -ENOMEM; | |
b426beb6 DH |
110 | digest = kzalloc(digest_size + desc_size, GFP_KERNEL); |
111 | if (!digest) | |
112 | goto error; | |
c26fd69f | 113 | |
b426beb6 DH |
114 | cert->sig.digest = digest; |
115 | cert->sig.digest_size = digest_size; | |
c26fd69f | 116 | |
b426beb6 DH |
117 | desc = digest + digest_size; |
118 | desc->tfm = tfm; | |
119 | desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP; | |
c26fd69f DH |
120 | |
121 | ret = crypto_shash_init(desc); | |
122 | if (ret < 0) | |
123 | goto error; | |
b426beb6 DH |
124 | might_sleep(); |
125 | ret = crypto_shash_finup(desc, cert->tbs, cert->tbs_size, digest); | |
126 | error: | |
127 | crypto_free_shash(tfm); | |
128 | pr_devel("<==%s() = %d\n", __func__, ret); | |
129 | return ret; | |
130 | } | |
131 | EXPORT_SYMBOL_GPL(x509_get_sig_params); | |
c26fd69f | 132 | |
b426beb6 DH |
133 | /* |
134 | * Check the signature on a certificate using the provided public key | |
135 | */ | |
136 | int x509_check_signature(const struct public_key *pub, | |
137 | struct x509_certificate *cert) | |
138 | { | |
139 | int ret; | |
c26fd69f | 140 | |
b426beb6 | 141 | pr_devel("==>%s()\n", __func__); |
c26fd69f | 142 | |
b426beb6 DH |
143 | ret = x509_get_sig_params(cert); |
144 | if (ret < 0) | |
145 | return ret; | |
c26fd69f | 146 | |
b426beb6 | 147 | ret = public_key_verify_signature(pub, &cert->sig); |
c26fd69f | 148 | pr_debug("Cert Verification: %d\n", ret); |
c26fd69f DH |
149 | return ret; |
150 | } | |
b426beb6 | 151 | EXPORT_SYMBOL_GPL(x509_check_signature); |
c26fd69f | 152 | |
09fbc473 MZ |
153 | /* |
154 | * Check the new certificate against the ones in the trust keyring. If one of | |
155 | * those is the signing key and validates the new certificate, then mark the | |
156 | * new certificate as being trusted. | |
157 | * | |
158 | * Return 0 if the new certificate was successfully validated, 1 if we couldn't | |
159 | * find a matching parent certificate in the trusted list and an error if there | |
160 | * is a matching certificate but the signature check fails. | |
161 | */ | |
162 | static int x509_validate_trust(struct x509_certificate *cert, | |
163 | struct key *trust_keyring) | |
164 | { | |
165 | const struct public_key *pk; | |
166 | struct key *key; | |
167 | int ret = 1; | |
168 | ||
169 | key = x509_request_asymmetric_key(trust_keyring, | |
170 | cert->issuer, strlen(cert->issuer), | |
171 | cert->authority, | |
172 | strlen(cert->authority)); | |
173 | if (!IS_ERR(key)) { | |
174 | pk = key->payload.data; | |
175 | ret = x509_check_signature(pk, cert); | |
176 | } | |
177 | return ret; | |
178 | } | |
179 | ||
c26fd69f DH |
180 | /* |
181 | * Attempt to parse a data blob for a key as an X509 certificate. | |
182 | */ | |
183 | static int x509_key_preparse(struct key_preparsed_payload *prep) | |
184 | { | |
185 | struct x509_certificate *cert; | |
c26fd69f DH |
186 | size_t srlen, sulen; |
187 | char *desc = NULL; | |
188 | int ret; | |
189 | ||
190 | cert = x509_cert_parse(prep->data, prep->datalen); | |
191 | if (IS_ERR(cert)) | |
192 | return PTR_ERR(cert); | |
193 | ||
194 | pr_devel("Cert Issuer: %s\n", cert->issuer); | |
195 | pr_devel("Cert Subject: %s\n", cert->subject); | |
2ecdb23b DH |
196 | |
197 | if (cert->pub->pkey_algo >= PKEY_ALGO__LAST || | |
198 | cert->sig.pkey_algo >= PKEY_ALGO__LAST || | |
199 | cert->sig.pkey_hash_algo >= PKEY_HASH__LAST || | |
200 | !pkey_algo[cert->pub->pkey_algo] || | |
201 | !pkey_algo[cert->sig.pkey_algo] || | |
3fe78ca2 | 202 | !hash_algo_name[cert->sig.pkey_hash_algo]) { |
2ecdb23b DH |
203 | ret = -ENOPKG; |
204 | goto error_free_cert; | |
205 | } | |
206 | ||
67f7d60b | 207 | pr_devel("Cert Key Algo: %s\n", pkey_algo_name[cert->pub->pkey_algo]); |
2f1c4fef | 208 | pr_devel("Cert Valid From: %04ld-%02d-%02d %02d:%02d:%02d\n", |
a5752d11 DH |
209 | cert->valid_from.tm_year + 1900, cert->valid_from.tm_mon + 1, |
210 | cert->valid_from.tm_mday, cert->valid_from.tm_hour, | |
211 | cert->valid_from.tm_min, cert->valid_from.tm_sec); | |
2f1c4fef | 212 | pr_devel("Cert Valid To: %04ld-%02d-%02d %02d:%02d:%02d\n", |
a5752d11 DH |
213 | cert->valid_to.tm_year + 1900, cert->valid_to.tm_mon + 1, |
214 | cert->valid_to.tm_mday, cert->valid_to.tm_hour, | |
215 | cert->valid_to.tm_min, cert->valid_to.tm_sec); | |
3fe78ca2 DK |
216 | pr_devel("Cert Signature: %s\n", |
217 | hash_algo_name[cert->sig.pkey_hash_algo]); | |
c26fd69f | 218 | |
17334cab DH |
219 | if (!cert->fingerprint) { |
220 | pr_warn("Cert for '%s' must have a SubjKeyId extension\n", | |
c26fd69f DH |
221 | cert->subject); |
222 | ret = -EKEYREJECTED; | |
223 | goto error_free_cert; | |
224 | } | |
225 | ||
67f7d60b | 226 | cert->pub->algo = pkey_algo[cert->pub->pkey_algo]; |
c26fd69f DH |
227 | cert->pub->id_type = PKEY_ID_X509; |
228 | ||
17334cab DH |
229 | /* Check the signature on the key if it appears to be self-signed */ |
230 | if (!cert->authority || | |
231 | strcmp(cert->fingerprint, cert->authority) == 0) { | |
09fbc473 | 232 | ret = x509_check_signature(cert->pub, cert); /* self-signed */ |
c26fd69f DH |
233 | if (ret < 0) |
234 | goto error_free_cert; | |
09fbc473 MZ |
235 | } else { |
236 | ret = x509_validate_trust(cert, system_trusted_keyring); | |
237 | if (!ret) | |
238 | prep->trusted = 1; | |
c26fd69f DH |
239 | } |
240 | ||
241 | /* Propose a description */ | |
242 | sulen = strlen(cert->subject); | |
243 | srlen = strlen(cert->fingerprint); | |
244 | ret = -ENOMEM; | |
245 | desc = kmalloc(sulen + 2 + srlen + 1, GFP_KERNEL); | |
246 | if (!desc) | |
247 | goto error_free_cert; | |
248 | memcpy(desc, cert->subject, sulen); | |
249 | desc[sulen] = ':'; | |
250 | desc[sulen + 1] = ' '; | |
251 | memcpy(desc + sulen + 2, cert->fingerprint, srlen); | |
252 | desc[sulen + 2 + srlen] = 0; | |
253 | ||
254 | /* We're pinning the module by being linked against it */ | |
255 | __module_get(public_key_subtype.owner); | |
256 | prep->type_data[0] = &public_key_subtype; | |
257 | prep->type_data[1] = cert->fingerprint; | |
258 | prep->payload = cert->pub; | |
259 | prep->description = desc; | |
260 | prep->quotalen = 100; | |
261 | ||
262 | /* We've finished with the certificate */ | |
263 | cert->pub = NULL; | |
264 | cert->fingerprint = NULL; | |
265 | desc = NULL; | |
266 | ret = 0; | |
267 | ||
268 | error_free_cert: | |
269 | x509_free_certificate(cert); | |
270 | return ret; | |
271 | } | |
272 | ||
273 | static struct asymmetric_key_parser x509_key_parser = { | |
274 | .owner = THIS_MODULE, | |
275 | .name = "x509", | |
276 | .parse = x509_key_preparse, | |
277 | }; | |
278 | ||
279 | /* | |
280 | * Module stuff | |
281 | */ | |
282 | static int __init x509_key_init(void) | |
283 | { | |
284 | return register_asymmetric_key_parser(&x509_key_parser); | |
285 | } | |
286 | ||
287 | static void __exit x509_key_exit(void) | |
288 | { | |
289 | unregister_asymmetric_key_parser(&x509_key_parser); | |
290 | } | |
291 | ||
292 | module_init(x509_key_init); | |
293 | module_exit(x509_key_exit); | |
e19aaa7d KK |
294 | |
295 | MODULE_DESCRIPTION("X.509 certificate parser"); | |
296 | MODULE_LICENSE("GPL"); |