]>
Commit | Line | Data |
---|---|---|
5e3b8dcb VS |
1 | /* |
2 | * GRUB -- GRand Unified Bootloader | |
1a78d573 | 3 | * Copyright (C) 2013 Free Software Foundation, Inc. |
5e3b8dcb VS |
4 | * |
5 | * GRUB is free software: you can redistribute it and/or modify | |
6 | * it under the terms of the GNU General Public License as published by | |
7 | * the Free Software Foundation, either version 3 of the License, or | |
8 | * (at your option) any later version. | |
9 | * | |
10 | * GRUB is distributed in the hope that it will be useful, | |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | * GNU General Public License for more details. | |
14 | * | |
15 | * You should have received a copy of the GNU General Public License | |
16 | * along with GRUB. If not, see <http://www.gnu.org/licenses/>. | |
17 | */ | |
18 | ||
19 | #include <grub/types.h> | |
20 | #include <grub/misc.h> | |
21 | #include <grub/mm.h> | |
22 | #include <grub/err.h> | |
23 | #include <grub/dl.h> | |
24 | #include <grub/file.h> | |
25 | #include <grub/command.h> | |
26 | #include <grub/crypto.h> | |
27 | #include <grub/i18n.h> | |
28 | #include <grub/gcrypt/gcrypt.h> | |
29 | #include <grub/pubkey.h> | |
30 | #include <grub/env.h> | |
31 | #include <grub/kernel.h> | |
0d711431 | 32 | #include <grub/extcmd.h> |
5e3b8dcb VS |
33 | |
34 | GRUB_MOD_LICENSE ("GPLv3+"); | |
35 | ||
0d711431 VS |
36 | enum |
37 | { | |
38 | OPTION_SKIP_SIG = 0 | |
39 | }; | |
40 | ||
41 | static const struct grub_arg_option options[] = | |
42 | { | |
43 | {"skip-sig", 's', 0, | |
44 | N_("Skip signature-checking of the signature file."), 0, ARG_TYPE_NONE}, | |
45 | {0, 0, 0, 0, 0, 0} | |
46 | }; | |
47 | ||
5e3b8dcb VS |
48 | static grub_err_t |
49 | read_packet_header (grub_file_t sig, grub_uint8_t *out_type, grub_size_t *len) | |
50 | { | |
51 | grub_uint8_t type; | |
52 | grub_uint8_t l; | |
53 | grub_uint16_t l16; | |
54 | grub_uint32_t l32; | |
55 | ||
56 | /* New format. */ | |
57 | switch (grub_file_read (sig, &type, sizeof (type))) | |
58 | { | |
59 | case 1: | |
60 | break; | |
61 | case 0: | |
62 | { | |
63 | *out_type = 0xff; | |
64 | return 0; | |
65 | } | |
66 | default: | |
67 | if (grub_errno) | |
68 | return grub_errno; | |
f8e98fee VS |
69 | /* TRANSLATORS: it's about GNUPG signatures. */ |
70 | return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); | |
5e3b8dcb VS |
71 | } |
72 | ||
73 | if (type == 0) | |
74 | { | |
75 | *out_type = 0xfe; | |
76 | return 0; | |
77 | } | |
78 | ||
79 | if (!(type & 0x80)) | |
f8e98fee | 80 | return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); |
5e3b8dcb VS |
81 | if (type & 0x40) |
82 | { | |
83 | *out_type = (type & 0x3f); | |
84 | if (grub_file_read (sig, &l, sizeof (l)) != 1) | |
f8e98fee | 85 | return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); |
5e3b8dcb VS |
86 | if (l < 192) |
87 | { | |
88 | *len = l; | |
89 | return 0; | |
90 | } | |
91 | if (l < 224) | |
92 | { | |
52eab656 | 93 | *len = (l - 192) << GRUB_CHAR_BIT; |
5e3b8dcb | 94 | if (grub_file_read (sig, &l, sizeof (l)) != 1) |
f8e98fee | 95 | return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); |
5e3b8dcb VS |
96 | *len |= l; |
97 | return 0; | |
98 | } | |
99 | if (l == 255) | |
100 | { | |
101 | if (grub_file_read (sig, &l32, sizeof (l32)) != sizeof (l32)) | |
f8e98fee | 102 | return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); |
5e3b8dcb VS |
103 | *len = grub_be_to_cpu32 (l32); |
104 | return 0; | |
105 | } | |
f8e98fee | 106 | return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); |
5e3b8dcb VS |
107 | } |
108 | *out_type = ((type >> 2) & 0xf); | |
109 | switch (type & 0x3) | |
110 | { | |
111 | case 0: | |
112 | if (grub_file_read (sig, &l, sizeof (l)) != sizeof (l)) | |
f8e98fee | 113 | return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); |
5e3b8dcb VS |
114 | *len = l; |
115 | return 0; | |
116 | case 1: | |
117 | if (grub_file_read (sig, &l16, sizeof (l16)) != sizeof (l16)) | |
f8e98fee | 118 | return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); |
5e3b8dcb VS |
119 | *len = grub_be_to_cpu16 (l16); |
120 | return 0; | |
121 | case 2: | |
122 | if (grub_file_read (sig, &l32, sizeof (l32)) != sizeof (l32)) | |
f8e98fee | 123 | return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); |
5e3b8dcb VS |
124 | *len = grub_be_to_cpu32 (l32); |
125 | return 0; | |
126 | } | |
f8e98fee | 127 | return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); |
5e3b8dcb VS |
128 | } |
129 | ||
130 | struct signature_v4_header | |
131 | { | |
132 | grub_uint8_t type; | |
133 | grub_uint8_t pkeyalgo; | |
134 | grub_uint8_t hash; | |
135 | grub_uint16_t hashed_sub; | |
7e47e27b | 136 | } GRUB_PACKED; |
5e3b8dcb VS |
137 | |
138 | const char *hashes[] = { | |
40f1c000 AB |
139 | [0x01] = "md5", |
140 | [0x02] = "sha1", | |
141 | [0x03] = "ripemd160", | |
d7a6506e VS |
142 | [0x08] = "sha256", |
143 | [0x09] = "sha384", | |
144 | [0x0a] = "sha512", | |
145 | [0x0b] = "sha224" | |
5e3b8dcb VS |
146 | }; |
147 | ||
1106c3f0 VS |
148 | struct gcry_pk_spec *grub_crypto_pk_dsa; |
149 | struct gcry_pk_spec *grub_crypto_pk_ecdsa; | |
150 | struct gcry_pk_spec *grub_crypto_pk_rsa; | |
151 | ||
152 | static int | |
153 | dsa_pad (gcry_mpi_t *hmpi, grub_uint8_t *hval, | |
154 | const gcry_md_spec_t *hash, struct grub_public_subkey *sk); | |
155 | static int | |
156 | rsa_pad (gcry_mpi_t *hmpi, grub_uint8_t *hval, | |
157 | const gcry_md_spec_t *hash, struct grub_public_subkey *sk); | |
158 | ||
5e3b8dcb VS |
159 | struct |
160 | { | |
161 | const char *name; | |
162 | grub_size_t nmpisig; | |
163 | grub_size_t nmpipub; | |
1106c3f0 VS |
164 | struct gcry_pk_spec **algo; |
165 | int (*pad) (gcry_mpi_t *hmpi, grub_uint8_t *hval, | |
166 | const gcry_md_spec_t *hash, struct grub_public_subkey *sk); | |
167 | const char *module; | |
5e3b8dcb VS |
168 | } pkalgos[] = |
169 | { | |
1106c3f0 VS |
170 | [1] = { "rsa", 1, 2, &grub_crypto_pk_rsa, rsa_pad, "gcry_rsa" }, |
171 | [3] = { "rsa", 1, 2, &grub_crypto_pk_rsa, rsa_pad, "gcry_rsa" }, | |
172 | [17] = { "dsa", 2, 4, &grub_crypto_pk_dsa, dsa_pad, "gcry_dsa" }, | |
5e3b8dcb VS |
173 | }; |
174 | ||
175 | struct grub_public_key | |
176 | { | |
177 | struct grub_public_key *next; | |
178 | struct grub_public_subkey *subkeys; | |
179 | }; | |
180 | ||
181 | struct grub_public_subkey | |
182 | { | |
183 | struct grub_public_subkey *next; | |
184 | grub_uint8_t type; | |
185 | grub_uint32_t fingerprint[5]; | |
186 | gcry_mpi_t mpis[10]; | |
187 | }; | |
188 | ||
189 | static void | |
190 | free_pk (struct grub_public_key *pk) | |
191 | { | |
192 | struct grub_public_subkey *nsk, *sk; | |
193 | for (sk = pk->subkeys; sk; sk = nsk) | |
194 | { | |
7bbb60cf VS |
195 | grub_size_t i; |
196 | for (i = 0; i < ARRAY_SIZE (sk->mpis); i++) | |
197 | if (sk->mpis[i]) | |
198 | gcry_mpi_release (sk->mpis[i]); | |
5e3b8dcb VS |
199 | nsk = sk->next; |
200 | grub_free (sk); | |
201 | } | |
202 | grub_free (pk); | |
203 | } | |
204 | ||
4f84ae0e VS |
205 | #define READBUF_SIZE 4096 |
206 | ||
5e3b8dcb VS |
207 | struct grub_public_key * |
208 | grub_load_public_key (grub_file_t f) | |
209 | { | |
210 | grub_err_t err; | |
211 | struct grub_public_key *ret; | |
212 | struct grub_public_subkey **last = 0; | |
4f84ae0e VS |
213 | void *fingerprint_context = NULL; |
214 | grub_uint8_t *buffer = NULL; | |
5e3b8dcb VS |
215 | |
216 | ret = grub_zalloc (sizeof (*ret)); | |
217 | if (!ret) | |
1dcb2715 VS |
218 | { |
219 | grub_free (fingerprint_context); | |
220 | return NULL; | |
221 | } | |
5e3b8dcb | 222 | |
4f84ae0e VS |
223 | buffer = grub_zalloc (READBUF_SIZE); |
224 | fingerprint_context = grub_zalloc (GRUB_MD_SHA1->contextsize); | |
225 | ||
226 | if (!buffer || !fingerprint_context) | |
227 | goto fail; | |
228 | ||
5e3b8dcb VS |
229 | last = &ret->subkeys; |
230 | ||
231 | while (1) | |
232 | { | |
233 | grub_uint8_t type; | |
234 | grub_size_t len; | |
235 | grub_uint8_t v, pk; | |
236 | grub_uint32_t creation_time; | |
237 | grub_off_t pend; | |
238 | struct grub_public_subkey *sk; | |
239 | grub_size_t i; | |
240 | grub_uint16_t len_be; | |
5e3b8dcb VS |
241 | |
242 | err = read_packet_header (f, &type, &len); | |
243 | ||
244 | if (err) | |
245 | goto fail; | |
246 | if (type == 0xfe) | |
247 | continue; | |
248 | if (type == 0xff) | |
1dcb2715 VS |
249 | { |
250 | grub_free (fingerprint_context); | |
7bbb60cf | 251 | grub_free (buffer); |
1dcb2715 VS |
252 | return ret; |
253 | } | |
5e3b8dcb VS |
254 | |
255 | grub_dprintf ("crypt", "len = %x\n", (int) len); | |
256 | ||
257 | pend = grub_file_tell (f) + len; | |
258 | if (type != 6 && type != 14 | |
259 | && type != 5 && type != 7) | |
260 | { | |
261 | grub_file_seek (f, pend); | |
262 | continue; | |
263 | } | |
264 | ||
265 | if (grub_file_read (f, &v, sizeof (v)) != sizeof (v)) | |
266 | { | |
f8e98fee | 267 | grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); |
5e3b8dcb VS |
268 | goto fail; |
269 | } | |
270 | ||
271 | grub_dprintf ("crypt", "v = %x\n", v); | |
272 | ||
273 | if (v != 4) | |
274 | { | |
f8e98fee | 275 | grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); |
5e3b8dcb VS |
276 | goto fail; |
277 | } | |
278 | if (grub_file_read (f, &creation_time, sizeof (creation_time)) != sizeof (creation_time)) | |
279 | { | |
f8e98fee | 280 | grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); |
5e3b8dcb VS |
281 | goto fail; |
282 | } | |
283 | ||
284 | grub_dprintf ("crypt", "time = %x\n", creation_time); | |
285 | ||
286 | if (grub_file_read (f, &pk, sizeof (pk)) != sizeof (pk)) | |
287 | { | |
f8e98fee | 288 | grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); |
5e3b8dcb VS |
289 | goto fail; |
290 | } | |
291 | ||
292 | grub_dprintf ("crypt", "pk = %x\n", pk); | |
293 | ||
294 | if (pk >= ARRAY_SIZE (pkalgos) || pkalgos[pk].name == NULL) | |
295 | { | |
296 | grub_file_seek (f, pend); | |
297 | continue; | |
298 | } | |
299 | ||
300 | sk = grub_zalloc (sizeof (struct grub_public_subkey)); | |
301 | if (!sk) | |
302 | goto fail; | |
303 | ||
304 | grub_memset (fingerprint_context, 0, sizeof (fingerprint_context)); | |
305 | GRUB_MD_SHA1->init (fingerprint_context); | |
306 | GRUB_MD_SHA1->write (fingerprint_context, "\x99", 1); | |
307 | len_be = grub_cpu_to_be16 (len); | |
308 | GRUB_MD_SHA1->write (fingerprint_context, &len_be, sizeof (len_be)); | |
309 | GRUB_MD_SHA1->write (fingerprint_context, &v, sizeof (v)); | |
310 | GRUB_MD_SHA1->write (fingerprint_context, &creation_time, sizeof (creation_time)); | |
311 | GRUB_MD_SHA1->write (fingerprint_context, &pk, sizeof (pk)); | |
312 | ||
313 | for (i = 0; i < pkalgos[pk].nmpipub; i++) | |
314 | { | |
315 | grub_uint16_t l; | |
316 | grub_size_t lb; | |
5e3b8dcb VS |
317 | if (grub_file_read (f, &l, sizeof (l)) != sizeof (l)) |
318 | { | |
f8e98fee | 319 | grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); |
5e3b8dcb VS |
320 | goto fail; |
321 | } | |
322 | ||
52eab656 | 323 | lb = (grub_be_to_cpu16 (l) + GRUB_CHAR_BIT - 1) / GRUB_CHAR_BIT; |
4f84ae0e | 324 | if (lb > READBUF_SIZE - sizeof (grub_uint16_t)) |
5e3b8dcb | 325 | { |
f8e98fee | 326 | grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); |
5e3b8dcb VS |
327 | goto fail; |
328 | } | |
329 | if (grub_file_read (f, buffer + sizeof (grub_uint16_t), lb) != (grub_ssize_t) lb) | |
330 | { | |
f8e98fee | 331 | grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); |
5e3b8dcb VS |
332 | goto fail; |
333 | } | |
334 | grub_memcpy (buffer, &l, sizeof (l)); | |
335 | ||
336 | GRUB_MD_SHA1->write (fingerprint_context, buffer, lb + sizeof (grub_uint16_t)); | |
337 | ||
338 | if (gcry_mpi_scan (&sk->mpis[i], GCRYMPI_FMT_PGP, | |
339 | buffer, lb + sizeof (grub_uint16_t), 0)) | |
340 | { | |
f8e98fee | 341 | grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); |
5e3b8dcb VS |
342 | goto fail; |
343 | } | |
344 | } | |
345 | ||
346 | GRUB_MD_SHA1->final (fingerprint_context); | |
347 | ||
348 | grub_memcpy (sk->fingerprint, GRUB_MD_SHA1->read (fingerprint_context), 20); | |
349 | ||
350 | *last = sk; | |
351 | last = &sk->next; | |
352 | ||
5e3b8dcb VS |
353 | grub_dprintf ("crypt", "actual pos: %x, expected: %x\n", (int)grub_file_tell (f), (int)pend); |
354 | ||
355 | grub_file_seek (f, pend); | |
356 | } | |
357 | fail: | |
358 | free_pk (ret); | |
1dcb2715 | 359 | grub_free (fingerprint_context); |
4f84ae0e | 360 | grub_free (buffer); |
5e3b8dcb VS |
361 | return NULL; |
362 | } | |
363 | ||
364 | struct grub_public_key *grub_pk_trusted; | |
365 | ||
366 | struct grub_public_subkey * | |
367 | grub_crypto_pk_locate_subkey (grub_uint64_t keyid, struct grub_public_key *pkey) | |
368 | { | |
369 | struct grub_public_subkey *sk; | |
370 | for (sk = pkey->subkeys; sk; sk = sk->next) | |
371 | if (grub_memcmp (sk->fingerprint + 3, &keyid, 8) == 0) | |
372 | return sk; | |
373 | return 0; | |
374 | } | |
375 | ||
376 | struct grub_public_subkey * | |
377 | grub_crypto_pk_locate_subkey_in_trustdb (grub_uint64_t keyid) | |
378 | { | |
379 | struct grub_public_key *pkey; | |
380 | struct grub_public_subkey *sk; | |
381 | for (pkey = grub_pk_trusted; pkey; pkey = pkey->next) | |
382 | { | |
383 | sk = grub_crypto_pk_locate_subkey (keyid, pkey); | |
384 | if (sk) | |
385 | return sk; | |
386 | } | |
387 | return 0; | |
388 | } | |
389 | ||
1106c3f0 VS |
390 | |
391 | static int | |
392 | dsa_pad (gcry_mpi_t *hmpi, grub_uint8_t *hval, | |
393 | const gcry_md_spec_t *hash, struct grub_public_subkey *sk) | |
394 | { | |
395 | unsigned nbits = gcry_mpi_get_nbits (sk->mpis[1]); | |
396 | grub_dprintf ("crypt", "must be %u bits got %d bits\n", nbits, | |
397 | (int)(8 * hash->mdlen)); | |
398 | return gcry_mpi_scan (hmpi, GCRYMPI_FMT_USG, hval, | |
399 | nbits / 8 < (unsigned) hash->mdlen ? nbits / 8 | |
400 | : (unsigned) hash->mdlen, 0); | |
401 | } | |
402 | ||
403 | static int | |
404 | rsa_pad (gcry_mpi_t *hmpi, grub_uint8_t *hval, | |
405 | const gcry_md_spec_t *hash, struct grub_public_subkey *sk) | |
406 | { | |
407 | grub_size_t tlen, emlen, fflen; | |
408 | grub_uint8_t *em, *emptr; | |
409 | unsigned nbits = gcry_mpi_get_nbits (sk->mpis[0]); | |
410 | int ret; | |
411 | tlen = hash->mdlen + hash->asnlen; | |
412 | emlen = (nbits + 7) / 8; | |
413 | if (emlen < tlen + 11) | |
414 | return 1; | |
415 | ||
416 | em = grub_malloc (emlen); | |
417 | if (!em) | |
418 | return 1; | |
419 | ||
420 | em[0] = 0x00; | |
421 | em[1] = 0x01; | |
422 | fflen = emlen - tlen - 3; | |
423 | for (emptr = em + 2; emptr < em + 2 + fflen; emptr++) | |
424 | *emptr = 0xff; | |
425 | *emptr++ = 0x00; | |
426 | grub_memcpy (emptr, hash->asnoid, hash->asnlen); | |
427 | emptr += hash->asnlen; | |
428 | grub_memcpy (emptr, hval, hash->mdlen); | |
429 | ||
430 | ret = gcry_mpi_scan (hmpi, GCRYMPI_FMT_USG, em, emlen, 0); | |
431 | grub_free (em); | |
432 | return ret; | |
433 | } | |
434 | ||
1a78d573 VS |
435 | static grub_err_t |
436 | grub_verify_signature_real (char *buf, grub_size_t size, | |
437 | grub_file_t f, grub_file_t sig, | |
438 | struct grub_public_key *pkey) | |
5e3b8dcb VS |
439 | { |
440 | grub_size_t len; | |
441 | grub_uint8_t v; | |
442 | grub_uint8_t h; | |
443 | grub_uint8_t t; | |
444 | grub_uint8_t pk; | |
445 | const gcry_md_spec_t *hash; | |
446 | struct signature_v4_header v4; | |
447 | grub_err_t err; | |
448 | grub_size_t i; | |
449 | gcry_mpi_t mpis[10]; | |
450 | grub_uint8_t type; | |
451 | ||
452 | err = read_packet_header (sig, &type, &len); | |
453 | if (err) | |
454 | return err; | |
455 | ||
456 | if (type != 0x2) | |
f8e98fee | 457 | return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); |
5e3b8dcb VS |
458 | |
459 | if (grub_file_read (sig, &v, sizeof (v)) != sizeof (v)) | |
f8e98fee | 460 | return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); |
5e3b8dcb VS |
461 | |
462 | if (v != 4) | |
f8e98fee | 463 | return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); |
5e3b8dcb VS |
464 | |
465 | if (grub_file_read (sig, &v4, sizeof (v4)) != sizeof (v4)) | |
f8e98fee | 466 | return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); |
5e3b8dcb VS |
467 | |
468 | h = v4.hash; | |
469 | t = v4.type; | |
470 | pk = v4.pkeyalgo; | |
471 | ||
472 | if (t != 0) | |
f8e98fee | 473 | return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); |
5e3b8dcb VS |
474 | |
475 | if (h >= ARRAY_SIZE (hashes) || hashes[h] == NULL) | |
476 | return grub_error (GRUB_ERR_BAD_SIGNATURE, "unknown hash"); | |
477 | ||
478 | if (pk >= ARRAY_SIZE (pkalgos) || pkalgos[pk].name == NULL) | |
f8e98fee | 479 | return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); |
5e3b8dcb VS |
480 | |
481 | hash = grub_crypto_lookup_md_by_name (hashes[h]); | |
482 | if (!hash) | |
483 | return grub_error (GRUB_ERR_BAD_SIGNATURE, "hash `%s' not loaded", hashes[h]); | |
484 | ||
485 | grub_dprintf ("crypt", "alive\n"); | |
486 | ||
487 | { | |
1dcb2715 | 488 | void *context = NULL; |
5e3b8dcb VS |
489 | unsigned char *hval; |
490 | grub_ssize_t rem = grub_be_to_cpu16 (v4.hashed_sub); | |
491 | grub_uint32_t headlen = grub_cpu_to_be32 (rem + 6); | |
492 | grub_uint8_t s; | |
493 | grub_uint16_t unhashed_sub; | |
494 | grub_ssize_t r; | |
495 | grub_uint8_t hash_start[2]; | |
496 | gcry_mpi_t hmpi; | |
497 | grub_uint64_t keyid = 0; | |
498 | struct grub_public_subkey *sk; | |
4f84ae0e | 499 | grub_uint8_t *readbuf = NULL; |
5e3b8dcb | 500 | |
1dcb2715 | 501 | context = grub_zalloc (hash->contextsize); |
4f84ae0e VS |
502 | readbuf = grub_zalloc (READBUF_SIZE); |
503 | if (!context || !readbuf) | |
504 | goto fail; | |
1dcb2715 | 505 | |
5e3b8dcb | 506 | hash->init (context); |
1a78d573 VS |
507 | if (buf) |
508 | hash->write (context, buf, size); | |
509 | else | |
510 | while (1) | |
511 | { | |
4f84ae0e | 512 | r = grub_file_read (f, readbuf, READBUF_SIZE); |
1a78d573 | 513 | if (r < 0) |
1dcb2715 | 514 | goto fail; |
1a78d573 VS |
515 | if (r == 0) |
516 | break; | |
517 | hash->write (context, readbuf, r); | |
518 | } | |
5e3b8dcb VS |
519 | |
520 | hash->write (context, &v, sizeof (v)); | |
521 | hash->write (context, &v4, sizeof (v4)); | |
522 | while (rem) | |
523 | { | |
4f84ae0e VS |
524 | r = grub_file_read (sig, readbuf, |
525 | rem < READBUF_SIZE ? rem : READBUF_SIZE); | |
5e3b8dcb | 526 | if (r < 0) |
1dcb2715 | 527 | goto fail; |
5e3b8dcb VS |
528 | if (r == 0) |
529 | break; | |
530 | hash->write (context, readbuf, r); | |
531 | rem -= r; | |
532 | } | |
533 | hash->write (context, &v, sizeof (v)); | |
534 | s = 0xff; | |
535 | hash->write (context, &s, sizeof (s)); | |
536 | hash->write (context, &headlen, sizeof (headlen)); | |
537 | r = grub_file_read (sig, &unhashed_sub, sizeof (unhashed_sub)); | |
538 | if (r != sizeof (unhashed_sub)) | |
1dcb2715 | 539 | goto fail; |
5e3b8dcb | 540 | { |
5e3b8dcb VS |
541 | grub_uint8_t *ptr; |
542 | grub_uint32_t l; | |
543 | rem = grub_be_to_cpu16 (unhashed_sub); | |
4f84ae0e | 544 | if (rem > READBUF_SIZE) |
1dcb2715 | 545 | goto fail; |
5e3b8dcb VS |
546 | r = grub_file_read (sig, readbuf, rem); |
547 | if (r != rem) | |
1dcb2715 | 548 | goto fail; |
5e3b8dcb VS |
549 | for (ptr = readbuf; ptr < readbuf + rem; ptr += l) |
550 | { | |
551 | if (*ptr < 192) | |
552 | l = *ptr++; | |
553 | else if (*ptr < 255) | |
554 | { | |
555 | if (ptr + 1 >= readbuf + rem) | |
556 | break; | |
52eab656 | 557 | l = (((ptr[0] & ~192) << GRUB_CHAR_BIT) | ptr[1]) + 192; |
5e3b8dcb VS |
558 | ptr += 2; |
559 | } | |
560 | else | |
561 | { | |
562 | if (ptr + 5 >= readbuf + rem) | |
563 | break; | |
564 | l = grub_be_to_cpu32 (grub_get_unaligned32 (ptr + 1)); | |
565 | ptr += 5; | |
566 | } | |
567 | if (*ptr == 0x10 && l >= 8) | |
568 | keyid = grub_get_unaligned64 (ptr + 1); | |
569 | } | |
570 | } | |
571 | ||
572 | hash->final (context); | |
573 | ||
574 | grub_dprintf ("crypt", "alive\n"); | |
575 | ||
576 | hval = hash->read (context); | |
577 | ||
578 | if (grub_file_read (sig, hash_start, sizeof (hash_start)) != sizeof (hash_start)) | |
1dcb2715 | 579 | goto fail; |
5e3b8dcb | 580 | if (grub_memcmp (hval, hash_start, sizeof (hash_start)) != 0) |
1dcb2715 | 581 | goto fail; |
5e3b8dcb VS |
582 | |
583 | grub_dprintf ("crypt", "@ %x\n", (int)grub_file_tell (sig)); | |
584 | ||
585 | for (i = 0; i < pkalgos[pk].nmpisig; i++) | |
586 | { | |
587 | grub_uint16_t l; | |
588 | grub_size_t lb; | |
5e3b8dcb VS |
589 | grub_dprintf ("crypt", "alive\n"); |
590 | if (grub_file_read (sig, &l, sizeof (l)) != sizeof (l)) | |
1dcb2715 | 591 | goto fail; |
5e3b8dcb VS |
592 | grub_dprintf ("crypt", "alive\n"); |
593 | lb = (grub_be_to_cpu16 (l) + 7) / 8; | |
594 | grub_dprintf ("crypt", "l = 0x%04x\n", grub_be_to_cpu16 (l)); | |
4f84ae0e | 595 | if (lb > READBUF_SIZE - sizeof (grub_uint16_t)) |
1dcb2715 | 596 | goto fail; |
5e3b8dcb | 597 | grub_dprintf ("crypt", "alive\n"); |
4f84ae0e | 598 | if (grub_file_read (sig, readbuf + sizeof (grub_uint16_t), lb) != (grub_ssize_t) lb) |
1dcb2715 | 599 | goto fail; |
5e3b8dcb | 600 | grub_dprintf ("crypt", "alive\n"); |
4f84ae0e | 601 | grub_memcpy (readbuf, &l, sizeof (l)); |
5e3b8dcb VS |
602 | grub_dprintf ("crypt", "alive\n"); |
603 | ||
604 | if (gcry_mpi_scan (&mpis[i], GCRYMPI_FMT_PGP, | |
4f84ae0e | 605 | readbuf, lb + sizeof (grub_uint16_t), 0)) |
1dcb2715 | 606 | goto fail; |
5e3b8dcb VS |
607 | grub_dprintf ("crypt", "alive\n"); |
608 | } | |
609 | ||
610 | if (pkey) | |
611 | sk = grub_crypto_pk_locate_subkey (keyid, pkey); | |
612 | else | |
613 | sk = grub_crypto_pk_locate_subkey_in_trustdb (keyid); | |
614 | if (!sk) | |
1dcb2715 VS |
615 | { |
616 | /* TRANSLATORS: %08x is 32-bit key id. */ | |
617 | grub_error (GRUB_ERR_BAD_SIGNATURE, N_("public key %08x not found"), | |
618 | keyid); | |
619 | goto fail; | |
620 | } | |
5e3b8dcb | 621 | |
1106c3f0 | 622 | if (pkalgos[pk].pad (&hmpi, hval, hash, sk)) |
1dcb2715 VS |
623 | goto fail; |
624 | if (!*pkalgos[pk].algo) | |
625 | { | |
626 | grub_dl_load (pkalgos[pk].module); | |
627 | grub_errno = GRUB_ERR_NONE; | |
628 | } | |
629 | ||
1106c3f0 | 630 | if (!*pkalgos[pk].algo) |
1dcb2715 VS |
631 | { |
632 | grub_error (GRUB_ERR_BAD_SIGNATURE, N_("module `%s' isn't loaded"), | |
633 | pkalgos[pk].module); | |
634 | goto fail; | |
635 | } | |
1106c3f0 | 636 | if ((*pkalgos[pk].algo)->verify (0, hmpi, mpis, sk->mpis, 0, 0)) |
1dcb2715 VS |
637 | goto fail; |
638 | ||
7bbb60cf VS |
639 | grub_free (context); |
640 | grub_free (readbuf); | |
641 | ||
1dcb2715 VS |
642 | return GRUB_ERR_NONE; |
643 | ||
644 | fail: | |
645 | grub_free (context); | |
4f84ae0e | 646 | grub_free (readbuf); |
1dcb2715 | 647 | if (!grub_errno) |
f8e98fee | 648 | return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); |
1dcb2715 | 649 | return grub_errno; |
5e3b8dcb | 650 | } |
5e3b8dcb VS |
651 | } |
652 | ||
1a78d573 VS |
653 | grub_err_t |
654 | grub_verify_signature (grub_file_t f, grub_file_t sig, | |
655 | struct grub_public_key *pkey) | |
656 | { | |
657 | return grub_verify_signature_real (0, 0, f, sig, pkey); | |
658 | } | |
659 | ||
5e3b8dcb | 660 | static grub_err_t |
0d711431 VS |
661 | grub_cmd_trust (grub_extcmd_context_t ctxt, |
662 | int argc, char **args) | |
5e3b8dcb VS |
663 | { |
664 | grub_file_t pkf; | |
665 | struct grub_public_key *pk = NULL; | |
666 | ||
667 | if (argc < 1) | |
f8e98fee | 668 | return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("one argument expected")); |
5e3b8dcb | 669 | |
0d711431 VS |
670 | grub_file_filter_disable_compression (); |
671 | if (ctxt->state[OPTION_SKIP_SIG].set) | |
672 | grub_file_filter_disable_pubkey (); | |
5e3b8dcb VS |
673 | pkf = grub_file_open (args[0]); |
674 | if (!pkf) | |
675 | return grub_errno; | |
676 | pk = grub_load_public_key (pkf); | |
677 | if (!pk) | |
678 | { | |
679 | grub_file_close (pkf); | |
680 | return grub_errno; | |
681 | } | |
682 | grub_file_close (pkf); | |
683 | ||
684 | pk->next = grub_pk_trusted; | |
685 | grub_pk_trusted = pk; | |
686 | ||
687 | return GRUB_ERR_NONE; | |
688 | } | |
689 | ||
adcc6020 VS |
690 | static grub_err_t |
691 | grub_cmd_list (grub_command_t cmd __attribute__ ((unused)), | |
692 | int argc __attribute__ ((unused)), | |
693 | char **args __attribute__ ((unused))) | |
694 | { | |
695 | struct grub_public_key *pk = NULL; | |
696 | struct grub_public_subkey *sk = NULL; | |
697 | ||
698 | for (pk = grub_pk_trusted; pk; pk = pk->next) | |
699 | for (sk = pk->subkeys; sk; sk = sk->next) | |
700 | { | |
701 | unsigned i; | |
702 | for (i = 0; i < 20; i += 2) | |
703 | grub_printf ("%02x%02x ", ((grub_uint8_t *) sk->fingerprint)[i], | |
704 | ((grub_uint8_t *) sk->fingerprint)[i + 1]); | |
705 | grub_printf ("\n"); | |
706 | } | |
707 | ||
708 | return GRUB_ERR_NONE; | |
709 | } | |
710 | ||
5e3b8dcb VS |
711 | static grub_err_t |
712 | grub_cmd_distrust (grub_command_t cmd __attribute__ ((unused)), | |
713 | int argc, char **args) | |
714 | { | |
715 | grub_uint32_t keyid, keyid_be; | |
716 | struct grub_public_key **pkey; | |
717 | struct grub_public_subkey *sk; | |
718 | ||
719 | if (argc < 1) | |
f8e98fee | 720 | return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("one argument expected")); |
5e3b8dcb VS |
721 | keyid = grub_strtoull (args[0], 0, 16); |
722 | if (grub_errno) | |
723 | return grub_errno; | |
724 | keyid_be = grub_cpu_to_be32 (keyid); | |
725 | ||
726 | for (pkey = &grub_pk_trusted; *pkey; pkey = &((*pkey)->next)) | |
727 | { | |
728 | struct grub_public_key *next; | |
729 | for (sk = (*pkey)->subkeys; sk; sk = sk->next) | |
730 | if (grub_memcmp (sk->fingerprint + 4, &keyid_be, 4) == 0) | |
731 | break; | |
732 | if (!sk) | |
733 | continue; | |
734 | next = (*pkey)->next; | |
735 | free_pk (*pkey); | |
736 | *pkey = next; | |
737 | return GRUB_ERR_NONE; | |
738 | } | |
f8e98fee VS |
739 | /* TRANSLATORS: %08x is 32-bit key id. */ |
740 | return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("public key %08x not found"), keyid); | |
5e3b8dcb VS |
741 | } |
742 | ||
743 | static grub_err_t | |
0d711431 | 744 | grub_cmd_verify_signature (grub_extcmd_context_t ctxt, |
5e3b8dcb VS |
745 | int argc, char **args) |
746 | { | |
7bbb60cf VS |
747 | grub_file_t f = NULL, sig = NULL; |
748 | grub_err_t err = GRUB_ERR_NONE; | |
5e3b8dcb VS |
749 | struct grub_public_key *pk = NULL; |
750 | ||
751 | grub_dprintf ("crypt", "alive\n"); | |
752 | ||
753 | if (argc < 2) | |
f8e98fee | 754 | return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("two arguments expected")); |
5e3b8dcb VS |
755 | |
756 | grub_dprintf ("crypt", "alive\n"); | |
757 | ||
758 | if (argc > 2) | |
759 | { | |
760 | grub_file_t pkf; | |
0d711431 VS |
761 | grub_file_filter_disable_compression (); |
762 | if (ctxt->state[OPTION_SKIP_SIG].set) | |
763 | grub_file_filter_disable_pubkey (); | |
5e3b8dcb VS |
764 | pkf = grub_file_open (args[2]); |
765 | if (!pkf) | |
766 | return grub_errno; | |
767 | pk = grub_load_public_key (pkf); | |
768 | if (!pk) | |
769 | { | |
770 | grub_file_close (pkf); | |
771 | return grub_errno; | |
772 | } | |
773 | grub_file_close (pkf); | |
774 | } | |
775 | ||
776 | grub_file_filter_disable_all (); | |
777 | f = grub_file_open (args[0]); | |
778 | if (!f) | |
7bbb60cf VS |
779 | { |
780 | err = grub_errno; | |
781 | goto fail; | |
782 | } | |
5e3b8dcb VS |
783 | |
784 | grub_file_filter_disable_all (); | |
785 | sig = grub_file_open (args[1]); | |
786 | if (!sig) | |
787 | { | |
7bbb60cf VS |
788 | err = grub_errno; |
789 | goto fail; | |
5e3b8dcb VS |
790 | } |
791 | ||
792 | err = grub_verify_signature (f, sig, pk); | |
7bbb60cf VS |
793 | fail: |
794 | if (sig) | |
795 | grub_file_close (sig); | |
796 | if (f) | |
797 | grub_file_close (f); | |
798 | if (pk) | |
799 | free_pk (pk); | |
5e3b8dcb VS |
800 | return err; |
801 | } | |
802 | ||
803 | static int sec = 0; | |
804 | ||
1a78d573 VS |
805 | static grub_ssize_t |
806 | verified_read (struct grub_file *file, char *buf, grub_size_t len) | |
807 | { | |
808 | grub_memcpy (buf, (char *) file->data + file->offset, len); | |
809 | return len; | |
810 | } | |
811 | ||
812 | static grub_err_t | |
813 | verified_close (struct grub_file *file) | |
814 | { | |
815 | grub_free (file->data); | |
816 | file->data = 0; | |
817 | return GRUB_ERR_NONE; | |
818 | } | |
819 | ||
820 | struct grub_fs verified_fs = | |
821 | { | |
822 | .name = "verified_read", | |
823 | .read = verified_read, | |
824 | .close = verified_close | |
825 | }; | |
826 | ||
5e3b8dcb VS |
827 | static grub_file_t |
828 | grub_pubkey_open (grub_file_t io, const char *filename) | |
829 | { | |
830 | grub_file_t sig; | |
831 | char *fsuf, *ptr; | |
832 | grub_err_t err; | |
833 | grub_file_filter_t curfilt[GRUB_FILE_FILTER_MAX]; | |
1a78d573 | 834 | grub_file_t ret; |
5e3b8dcb VS |
835 | |
836 | if (!sec) | |
837 | return io; | |
1a78d573 VS |
838 | if (io->device->disk && io->device->disk->id == GRUB_DISK_DEVICE_MEMDISK_ID) |
839 | return io; | |
5e3b8dcb VS |
840 | fsuf = grub_malloc (grub_strlen (filename) + sizeof (".sig")); |
841 | if (!fsuf) | |
842 | return NULL; | |
843 | ptr = grub_stpcpy (fsuf, filename); | |
844 | grub_memcpy (ptr, ".sig", sizeof (".sig")); | |
845 | ||
846 | grub_memcpy (curfilt, grub_file_filters_enabled, | |
847 | sizeof (curfilt)); | |
848 | grub_file_filter_disable_all (); | |
849 | sig = grub_file_open (fsuf); | |
850 | grub_memcpy (grub_file_filters_enabled, curfilt, | |
851 | sizeof (curfilt)); | |
852 | grub_free (fsuf); | |
853 | if (!sig) | |
854 | return NULL; | |
855 | ||
1a78d573 VS |
856 | ret = grub_malloc (sizeof (*ret)); |
857 | if (!ret) | |
858 | return NULL; | |
859 | *ret = *io; | |
860 | ||
861 | ret->fs = &verified_fs; | |
862 | ret->not_easily_seekable = 0; | |
863 | if (ret->size >> (sizeof (grub_size_t) * GRUB_CHAR_BIT - 1)) | |
864 | { | |
865 | grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, | |
866 | "big file signature isn't implemented yet"); | |
867 | return NULL; | |
868 | } | |
869 | ret->data = grub_malloc (ret->size); | |
870 | if (!ret->data) | |
871 | { | |
872 | grub_free (ret); | |
873 | return NULL; | |
874 | } | |
875 | if (grub_file_read (io, ret->data, ret->size) != (grub_ssize_t) ret->size) | |
876 | { | |
877 | if (!grub_errno) | |
878 | grub_error (GRUB_ERR_FILE_READ_ERROR, N_("premature end of file %s"), | |
879 | filename); | |
880 | return NULL; | |
881 | } | |
882 | ||
883 | err = grub_verify_signature_real (ret->data, ret->size, 0, sig, NULL); | |
5e3b8dcb VS |
884 | grub_file_close (sig); |
885 | if (err) | |
886 | return NULL; | |
1a78d573 VS |
887 | io->device = 0; |
888 | grub_file_close (io); | |
889 | return ret; | |
5e3b8dcb VS |
890 | } |
891 | ||
892 | static char * | |
893 | grub_env_write_sec (struct grub_env_var *var __attribute__ ((unused)), | |
894 | const char *val) | |
895 | { | |
896 | sec = (*val == '1') || (*val == 'e'); | |
897 | return grub_strdup (sec ? "enforce" : "no"); | |
898 | } | |
899 | ||
900 | static grub_ssize_t | |
901 | pseudo_read (struct grub_file *file, char *buf, grub_size_t len) | |
902 | { | |
903 | grub_memcpy (buf, (grub_uint8_t *) file->data + file->offset, len); | |
904 | return len; | |
905 | } | |
906 | ||
907 | ||
908 | /* Filesystem descriptor. */ | |
909 | struct grub_fs pseudo_fs = | |
910 | { | |
911 | .name = "pseudo", | |
912 | .read = pseudo_read | |
913 | }; | |
914 | ||
5e3b8dcb | 915 | |
0d711431 VS |
916 | static grub_extcmd_t cmd, cmd_trust; |
917 | static grub_command_t cmd_distrust, cmd_list; | |
5e3b8dcb VS |
918 | |
919 | GRUB_MOD_INIT(verify) | |
920 | { | |
921 | const char *val; | |
922 | struct grub_module_header *header; | |
923 | ||
924 | val = grub_env_get ("check_signatures"); | |
925 | if (val && (val[0] == '1' || val[0] == 'e')) | |
926 | sec = 1; | |
927 | else | |
928 | sec = 0; | |
929 | ||
930 | grub_file_filter_register (GRUB_FILE_FILTER_PUBKEY, grub_pubkey_open); | |
931 | ||
932 | grub_register_variable_hook ("check_signatures", 0, grub_env_write_sec); | |
933 | grub_env_export ("check_signatures"); | |
934 | ||
935 | grub_pk_trusted = 0; | |
936 | FOR_MODULES (header) | |
937 | { | |
938 | struct grub_file pseudo_file; | |
939 | struct grub_public_key *pk = NULL; | |
940 | ||
941 | grub_memset (&pseudo_file, 0, sizeof (pseudo_file)); | |
942 | ||
943 | /* Not an ELF module, skip. */ | |
944 | if (header->type != OBJ_TYPE_PUBKEY) | |
945 | continue; | |
946 | ||
947 | pseudo_file.fs = &pseudo_fs; | |
948 | pseudo_file.size = (header->size - sizeof (struct grub_module_header)); | |
949 | pseudo_file.data = (char *) header + sizeof (struct grub_module_header); | |
950 | ||
951 | pk = grub_load_public_key (&pseudo_file); | |
952 | if (!pk) | |
953 | grub_fatal ("error loading initial key: %s\n", grub_errmsg); | |
954 | ||
955 | pk->next = grub_pk_trusted; | |
956 | grub_pk_trusted = pk; | |
957 | } | |
958 | ||
959 | if (!val) | |
960 | grub_env_set ("check_signatures", grub_pk_trusted ? "enforce" : "no"); | |
961 | ||
0d711431 VS |
962 | cmd = grub_register_extcmd ("verify_detached", grub_cmd_verify_signature, 0, |
963 | N_("[-s|--skip-sig] FILE SIGNATURE_FILE [PUBKEY_FILE]"), | |
964 | N_("Verify detached signature."), | |
965 | options); | |
966 | cmd_trust = grub_register_extcmd ("trust", grub_cmd_trust, 0, | |
967 | N_("[-s|--skip-sig] PUBKEY_FILE"), | |
968 | N_("Add PKFILE to trusted keys."), | |
969 | options); | |
adcc6020 VS |
970 | cmd_list = grub_register_command ("list_trusted", grub_cmd_list, |
971 | 0, | |
972 | N_("List trusted keys.")); | |
5e3b8dcb | 973 | cmd_distrust = grub_register_command ("distrust", grub_cmd_distrust, |
f8e98fee | 974 | N_("PUBKEY_ID"), |
5e3b8dcb VS |
975 | N_("Remove KEYID from trusted keys.")); |
976 | } | |
977 | ||
978 | GRUB_MOD_FINI(verify) | |
979 | { | |
980 | grub_file_filter_unregister (GRUB_FILE_FILTER_PUBKEY); | |
0d711431 VS |
981 | grub_unregister_extcmd (cmd); |
982 | grub_unregister_extcmd (cmd_trust); | |
adcc6020 | 983 | grub_unregister_command (cmd_list); |
5e3b8dcb VS |
984 | grub_unregister_command (cmd_distrust); |
985 | } |