Just for clarity. No functional change. Signed-off-by: Daniel Kiper <daniel.ki...@oracle.com> --- grub-core/Makefile.core.def | 4 +- grub-core/commands/pgp.c | 1018 +++++++++++++++++++++++++++++++++++++++++++ grub-core/commands/verify.c | 1018 ------------------------------------------- 3 files changed, 1020 insertions(+), 1020 deletions(-) create mode 100644 grub-core/commands/pgp.c delete mode 100644 grub-core/commands/verify.c
diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index dfcc95d..3008b58 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -888,8 +888,8 @@ module = { }; module = { - name = verify; - common = commands/verify.c; + name = pgp; + common = commands/pgp.c; cflags = '$(CFLAGS_POSIX)'; cppflags = '-I$(srcdir)/lib/posix_wrap'; }; diff --git a/grub-core/commands/pgp.c b/grub-core/commands/pgp.c new file mode 100644 index 0000000..d5d7c0f --- /dev/null +++ b/grub-core/commands/pgp.c @@ -0,0 +1,1018 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2013 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <grub/types.h> +#include <grub/misc.h> +#include <grub/mm.h> +#include <grub/err.h> +#include <grub/dl.h> +#include <grub/file.h> +#include <grub/command.h> +#include <grub/crypto.h> +#include <grub/i18n.h> +#include <grub/gcrypt/gcrypt.h> +#include <grub/pubkey.h> +#include <grub/env.h> +#include <grub/kernel.h> +#include <grub/extcmd.h> +#include <grub/verify.h> + +GRUB_MOD_LICENSE ("GPLv3+"); + +enum + { + OPTION_SKIP_SIG = 0 + }; + +static const struct grub_arg_option options[] = + { + {"skip-sig", 's', 0, + N_("Skip signature-checking of the public key file."), 0, ARG_TYPE_NONE}, + {0, 0, 0, 0, 0, 0} + }; + +static grub_err_t +read_packet_header (grub_file_t sig, grub_uint8_t *out_type, grub_size_t *len) +{ + grub_uint8_t type; + grub_uint8_t l; + grub_uint16_t l16; + grub_uint32_t l32; + + /* New format. */ + switch (grub_file_read (sig, &type, sizeof (type))) + { + case 1: + break; + case 0: + { + *out_type = 0xff; + return 0; + } + default: + if (grub_errno) + return grub_errno; + /* TRANSLATORS: it's about GNUPG signatures. */ + return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); + } + + if (type == 0) + { + *out_type = 0xfe; + return 0; + } + + if (!(type & 0x80)) + return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); + if (type & 0x40) + { + *out_type = (type & 0x3f); + if (grub_file_read (sig, &l, sizeof (l)) != 1) + return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); + if (l < 192) + { + *len = l; + return 0; + } + if (l < 224) + { + *len = (l - 192) << GRUB_CHAR_BIT; + if (grub_file_read (sig, &l, sizeof (l)) != 1) + return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); + *len |= l; + return 0; + } + if (l == 255) + { + if (grub_file_read (sig, &l32, sizeof (l32)) != sizeof (l32)) + return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); + *len = grub_be_to_cpu32 (l32); + return 0; + } + return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); + } + *out_type = ((type >> 2) & 0xf); + switch (type & 0x3) + { + case 0: + if (grub_file_read (sig, &l, sizeof (l)) != sizeof (l)) + return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); + *len = l; + return 0; + case 1: + if (grub_file_read (sig, &l16, sizeof (l16)) != sizeof (l16)) + return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); + *len = grub_be_to_cpu16 (l16); + return 0; + case 2: + if (grub_file_read (sig, &l32, sizeof (l32)) != sizeof (l32)) + return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); + *len = grub_be_to_cpu32 (l32); + return 0; + } + return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); +} + +struct signature_v4_header +{ + grub_uint8_t type; + grub_uint8_t pkeyalgo; + grub_uint8_t hash; + grub_uint16_t hashed_sub; +} GRUB_PACKED; + +const char *hashes[] = { + [0x01] = "md5", + [0x02] = "sha1", + [0x03] = "ripemd160", + [0x08] = "sha256", + [0x09] = "sha384", + [0x0a] = "sha512", + [0x0b] = "sha224" +}; + +struct gcry_pk_spec *grub_crypto_pk_dsa; +struct gcry_pk_spec *grub_crypto_pk_ecdsa; +struct gcry_pk_spec *grub_crypto_pk_rsa; + +static int +dsa_pad (gcry_mpi_t *hmpi, grub_uint8_t *hval, + const gcry_md_spec_t *hash, struct grub_public_subkey *sk); +static int +rsa_pad (gcry_mpi_t *hmpi, grub_uint8_t *hval, + const gcry_md_spec_t *hash, struct grub_public_subkey *sk); + +struct +{ + const char *name; + grub_size_t nmpisig; + grub_size_t nmpipub; + struct gcry_pk_spec **algo; + int (*pad) (gcry_mpi_t *hmpi, grub_uint8_t *hval, + const gcry_md_spec_t *hash, struct grub_public_subkey *sk); + const char *module; +} pkalgos[] = + { + [1] = { "rsa", 1, 2, &grub_crypto_pk_rsa, rsa_pad, "gcry_rsa" }, + [3] = { "rsa", 1, 2, &grub_crypto_pk_rsa, rsa_pad, "gcry_rsa" }, + [17] = { "dsa", 2, 4, &grub_crypto_pk_dsa, dsa_pad, "gcry_dsa" }, + }; + +struct grub_public_key +{ + struct grub_public_key *next; + struct grub_public_subkey *subkeys; +}; + +struct grub_public_subkey +{ + struct grub_public_subkey *next; + grub_uint8_t type; + grub_uint32_t fingerprint[5]; + gcry_mpi_t mpis[10]; +}; + +static void +free_pk (struct grub_public_key *pk) +{ + struct grub_public_subkey *nsk, *sk; + for (sk = pk->subkeys; sk; sk = nsk) + { + grub_size_t i; + for (i = 0; i < ARRAY_SIZE (sk->mpis); i++) + if (sk->mpis[i]) + gcry_mpi_release (sk->mpis[i]); + nsk = sk->next; + grub_free (sk); + } + grub_free (pk); +} + +#define READBUF_SIZE 4096 + +struct grub_public_key * +grub_load_public_key (grub_file_t f) +{ + grub_err_t err; + struct grub_public_key *ret; + struct grub_public_subkey **last = 0; + void *fingerprint_context = NULL; + grub_uint8_t *buffer = NULL; + + ret = grub_zalloc (sizeof (*ret)); + if (!ret) + { + grub_free (fingerprint_context); + return NULL; + } + + buffer = grub_zalloc (READBUF_SIZE); + fingerprint_context = grub_zalloc (GRUB_MD_SHA1->contextsize); + + if (!buffer || !fingerprint_context) + goto fail; + + last = &ret->subkeys; + + while (1) + { + grub_uint8_t type; + grub_size_t len; + grub_uint8_t v, pk; + grub_uint32_t creation_time; + grub_off_t pend; + struct grub_public_subkey *sk; + grub_size_t i; + grub_uint16_t len_be; + + err = read_packet_header (f, &type, &len); + + if (err) + goto fail; + if (type == 0xfe) + continue; + if (type == 0xff) + { + grub_free (fingerprint_context); + grub_free (buffer); + return ret; + } + + grub_dprintf ("crypt", "len = %x\n", (int) len); + + pend = grub_file_tell (f) + len; + if (type != 6 && type != 14 + && type != 5 && type != 7) + { + grub_file_seek (f, pend); + continue; + } + + if (grub_file_read (f, &v, sizeof (v)) != sizeof (v)) + { + grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); + goto fail; + } + + grub_dprintf ("crypt", "v = %x\n", v); + + if (v != 4) + { + grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); + goto fail; + } + if (grub_file_read (f, &creation_time, sizeof (creation_time)) != sizeof (creation_time)) + { + grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); + goto fail; + } + + grub_dprintf ("crypt", "time = %x\n", creation_time); + + if (grub_file_read (f, &pk, sizeof (pk)) != sizeof (pk)) + { + grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); + goto fail; + } + + grub_dprintf ("crypt", "pk = %x\n", pk); + + if (pk >= ARRAY_SIZE (pkalgos) || pkalgos[pk].name == NULL) + { + grub_file_seek (f, pend); + continue; + } + + sk = grub_zalloc (sizeof (struct grub_public_subkey)); + if (!sk) + goto fail; + + grub_memset (fingerprint_context, 0, GRUB_MD_SHA1->contextsize); + GRUB_MD_SHA1->init (fingerprint_context); + GRUB_MD_SHA1->write (fingerprint_context, "\x99", 1); + len_be = grub_cpu_to_be16 (len); + GRUB_MD_SHA1->write (fingerprint_context, &len_be, sizeof (len_be)); + GRUB_MD_SHA1->write (fingerprint_context, &v, sizeof (v)); + GRUB_MD_SHA1->write (fingerprint_context, &creation_time, sizeof (creation_time)); + GRUB_MD_SHA1->write (fingerprint_context, &pk, sizeof (pk)); + + for (i = 0; i < pkalgos[pk].nmpipub; i++) + { + grub_uint16_t l; + grub_size_t lb; + if (grub_file_read (f, &l, sizeof (l)) != sizeof (l)) + { + grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); + break; + } + + lb = (grub_be_to_cpu16 (l) + GRUB_CHAR_BIT - 1) / GRUB_CHAR_BIT; + if (lb > READBUF_SIZE - sizeof (grub_uint16_t)) + { + grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); + break; + } + if (grub_file_read (f, buffer + sizeof (grub_uint16_t), lb) != (grub_ssize_t) lb) + { + grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); + break; + } + grub_memcpy (buffer, &l, sizeof (l)); + + GRUB_MD_SHA1->write (fingerprint_context, buffer, lb + sizeof (grub_uint16_t)); + + if (gcry_mpi_scan (&sk->mpis[i], GCRYMPI_FMT_PGP, + buffer, lb + sizeof (grub_uint16_t), 0)) + { + grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); + break; + } + } + + if (i < pkalgos[pk].nmpipub) + { + grub_free (sk); + goto fail; + } + + GRUB_MD_SHA1->final (fingerprint_context); + + grub_memcpy (sk->fingerprint, GRUB_MD_SHA1->read (fingerprint_context), 20); + + *last = sk; + last = &sk->next; + + grub_dprintf ("crypt", "actual pos: %x, expected: %x\n", (int)grub_file_tell (f), (int)pend); + + grub_file_seek (f, pend); + } + fail: + free_pk (ret); + grub_free (fingerprint_context); + grub_free (buffer); + return NULL; +} + +struct grub_public_key *grub_pk_trusted; + +struct grub_public_subkey * +grub_crypto_pk_locate_subkey (grub_uint64_t keyid, struct grub_public_key *pkey) +{ + struct grub_public_subkey *sk; + for (sk = pkey->subkeys; sk; sk = sk->next) + if (grub_memcmp (sk->fingerprint + 3, &keyid, 8) == 0) + return sk; + return 0; +} + +struct grub_public_subkey * +grub_crypto_pk_locate_subkey_in_trustdb (grub_uint64_t keyid) +{ + struct grub_public_key *pkey; + struct grub_public_subkey *sk; + for (pkey = grub_pk_trusted; pkey; pkey = pkey->next) + { + sk = grub_crypto_pk_locate_subkey (keyid, pkey); + if (sk) + return sk; + } + return 0; +} + + +static int +dsa_pad (gcry_mpi_t *hmpi, grub_uint8_t *hval, + const gcry_md_spec_t *hash, struct grub_public_subkey *sk) +{ + unsigned nbits = gcry_mpi_get_nbits (sk->mpis[1]); + grub_dprintf ("crypt", "must be %u bits got %d bits\n", nbits, + (int)(8 * hash->mdlen)); + return gcry_mpi_scan (hmpi, GCRYMPI_FMT_USG, hval, + nbits / 8 < (unsigned) hash->mdlen ? nbits / 8 + : (unsigned) hash->mdlen, 0); +} + +static int +rsa_pad (gcry_mpi_t *hmpi, grub_uint8_t *hval, + const gcry_md_spec_t *hash, struct grub_public_subkey *sk) +{ + grub_size_t tlen, emlen, fflen; + grub_uint8_t *em, *emptr; + unsigned nbits = gcry_mpi_get_nbits (sk->mpis[0]); + int ret; + tlen = hash->mdlen + hash->asnlen; + emlen = (nbits + 7) / 8; + if (emlen < tlen + 11) + return 1; + + em = grub_malloc (emlen); + if (!em) + return 1; + + em[0] = 0x00; + em[1] = 0x01; + fflen = emlen - tlen - 3; + for (emptr = em + 2; emptr < em + 2 + fflen; emptr++) + *emptr = 0xff; + *emptr++ = 0x00; + grub_memcpy (emptr, hash->asnoid, hash->asnlen); + emptr += hash->asnlen; + grub_memcpy (emptr, hval, hash->mdlen); + + ret = gcry_mpi_scan (hmpi, GCRYMPI_FMT_USG, em, emlen, 0); + grub_free (em); + return ret; +} + +struct grub_pubkey_context +{ + grub_file_t sig; + struct signature_v4_header v4; + grub_uint8_t v; + const gcry_md_spec_t *hash; + void *hash_context; +}; + +static grub_err_t +grub_verify_signature_init (struct grub_pubkey_context *ctxt, grub_file_t sig) +{ + grub_size_t len; + grub_uint8_t h; + grub_uint8_t t; + grub_err_t err; + grub_uint8_t type = 0; + grub_uint8_t pk; + + grub_memset (ctxt, 0, sizeof (*ctxt)); + + err = read_packet_header (sig, &type, &len); + if (err) + return err; + + if (type != 0x2) + return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); + + if (grub_file_read (sig, &ctxt->v, sizeof (ctxt->v)) != sizeof (ctxt->v)) + return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); + + if (ctxt->v != 4) + return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); + + if (grub_file_read (sig, &ctxt->v4, sizeof (ctxt->v4)) != sizeof (ctxt->v4)) + return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); + + h = ctxt->v4.hash; + t = ctxt->v4.type; + pk = ctxt->v4.pkeyalgo; + + if (t != 0) + return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); + + if (h >= ARRAY_SIZE (hashes) || hashes[h] == NULL) + return grub_error (GRUB_ERR_BAD_SIGNATURE, "unknown hash"); + + if (pk >= ARRAY_SIZE (pkalgos) || pkalgos[pk].name == NULL) + return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); + + ctxt->hash = grub_crypto_lookup_md_by_name (hashes[h]); + if (!ctxt->hash) + return grub_error (GRUB_ERR_BAD_SIGNATURE, "hash `%s' not loaded", hashes[h]); + + grub_dprintf ("crypt", "alive\n"); + + ctxt->sig = sig; + + ctxt->hash_context = grub_zalloc (ctxt->hash->contextsize); + if (!ctxt->hash_context) + return grub_errno; + + ctxt->hash->init (ctxt->hash_context); + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_pubkey_write (void *ctxt_, void *buf, grub_size_t size) +{ + struct grub_pubkey_context *ctxt = ctxt_; + ctxt->hash->write (ctxt->hash_context, buf, size); + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_verify_signature_real (struct grub_pubkey_context *ctxt, + struct grub_public_key *pkey) +{ + gcry_mpi_t mpis[10]; + grub_uint8_t pk = ctxt->v4.pkeyalgo; + grub_size_t i; + grub_uint8_t *readbuf = NULL; + unsigned char *hval; + grub_ssize_t rem = grub_be_to_cpu16 (ctxt->v4.hashed_sub); + grub_uint32_t headlen = grub_cpu_to_be32 (rem + 6); + grub_uint8_t s; + grub_uint16_t unhashed_sub; + grub_ssize_t r; + grub_uint8_t hash_start[2]; + gcry_mpi_t hmpi; + grub_uint64_t keyid = 0; + struct grub_public_subkey *sk; + + readbuf = grub_malloc (READBUF_SIZE); + if (!readbuf) + goto fail; + + ctxt->hash->write (ctxt->hash_context, &ctxt->v, sizeof (ctxt->v)); + ctxt->hash->write (ctxt->hash_context, &ctxt->v4, sizeof (ctxt->v4)); + while (rem) + { + r = grub_file_read (ctxt->sig, readbuf, + rem < READBUF_SIZE ? rem : READBUF_SIZE); + if (r < 0) + goto fail; + if (r == 0) + break; + ctxt->hash->write (ctxt->hash_context, readbuf, r); + rem -= r; + } + ctxt->hash->write (ctxt->hash_context, &ctxt->v, sizeof (ctxt->v)); + s = 0xff; + ctxt->hash->write (ctxt->hash_context, &s, sizeof (s)); + ctxt->hash->write (ctxt->hash_context, &headlen, sizeof (headlen)); + r = grub_file_read (ctxt->sig, &unhashed_sub, sizeof (unhashed_sub)); + if (r != sizeof (unhashed_sub)) + goto fail; + { + grub_uint8_t *ptr; + grub_uint32_t l; + rem = grub_be_to_cpu16 (unhashed_sub); + if (rem > READBUF_SIZE) + goto fail; + r = grub_file_read (ctxt->sig, readbuf, rem); + if (r != rem) + goto fail; + for (ptr = readbuf; ptr < readbuf + rem; ptr += l) + { + if (*ptr < 192) + l = *ptr++; + else if (*ptr < 255) + { + if (ptr + 1 >= readbuf + rem) + break; + l = (((ptr[0] & ~192) << GRUB_CHAR_BIT) | ptr[1]) + 192; + ptr += 2; + } + else + { + if (ptr + 5 >= readbuf + rem) + break; + l = grub_be_to_cpu32 (grub_get_unaligned32 (ptr + 1)); + ptr += 5; + } + if (*ptr == 0x10 && l >= 8) + keyid = grub_get_unaligned64 (ptr + 1); + } + } + + ctxt->hash->final (ctxt->hash_context); + + grub_dprintf ("crypt", "alive\n"); + + hval = ctxt->hash->read (ctxt->hash_context); + + if (grub_file_read (ctxt->sig, hash_start, sizeof (hash_start)) != sizeof (hash_start)) + goto fail; + if (grub_memcmp (hval, hash_start, sizeof (hash_start)) != 0) + goto fail; + + grub_dprintf ("crypt", "@ %x\n", (int)grub_file_tell (ctxt->sig)); + + for (i = 0; i < pkalgos[pk].nmpisig; i++) + { + grub_uint16_t l; + grub_size_t lb; + grub_dprintf ("crypt", "alive\n"); + if (grub_file_read (ctxt->sig, &l, sizeof (l)) != sizeof (l)) + goto fail; + grub_dprintf ("crypt", "alive\n"); + lb = (grub_be_to_cpu16 (l) + 7) / 8; + grub_dprintf ("crypt", "l = 0x%04x\n", grub_be_to_cpu16 (l)); + if (lb > READBUF_SIZE - sizeof (grub_uint16_t)) + goto fail; + grub_dprintf ("crypt", "alive\n"); + if (grub_file_read (ctxt->sig, readbuf + sizeof (grub_uint16_t), lb) != (grub_ssize_t) lb) + goto fail; + grub_dprintf ("crypt", "alive\n"); + grub_memcpy (readbuf, &l, sizeof (l)); + grub_dprintf ("crypt", "alive\n"); + + if (gcry_mpi_scan (&mpis[i], GCRYMPI_FMT_PGP, + readbuf, lb + sizeof (grub_uint16_t), 0)) + goto fail; + grub_dprintf ("crypt", "alive\n"); + } + + if (pkey) + sk = grub_crypto_pk_locate_subkey (keyid, pkey); + else + sk = grub_crypto_pk_locate_subkey_in_trustdb (keyid); + if (!sk) + { + /* TRANSLATORS: %08x is 32-bit key id. */ + grub_error (GRUB_ERR_BAD_SIGNATURE, N_("public key %08x not found"), + keyid); + goto fail; + } + + if (pkalgos[pk].pad (&hmpi, hval, ctxt->hash, sk)) + goto fail; + if (!*pkalgos[pk].algo) + { + grub_dl_load (pkalgos[pk].module); + grub_errno = GRUB_ERR_NONE; + } + + if (!*pkalgos[pk].algo) + { + grub_error (GRUB_ERR_BAD_SIGNATURE, N_("module `%s' isn't loaded"), + pkalgos[pk].module); + goto fail; + } + if ((*pkalgos[pk].algo)->verify (0, hmpi, mpis, sk->mpis, 0, 0)) + goto fail; + + grub_free (readbuf); + + return GRUB_ERR_NONE; + + fail: + grub_free (readbuf); + if (!grub_errno) + return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); + return grub_errno; +} + +static void +grub_pubkey_close_real (struct grub_pubkey_context *ctxt) +{ + if (ctxt->sig) + grub_file_close (ctxt->sig); + if (ctxt->hash_context) + grub_free (ctxt->hash_context); +} + +static void +grub_pubkey_close (void *ctxt) +{ + grub_pubkey_close_real (ctxt); + grub_free (ctxt); +} + +grub_err_t +grub_verify_signature (grub_file_t f, grub_file_t sig, + struct grub_public_key *pkey) +{ + grub_err_t err; + struct grub_pubkey_context ctxt; + grub_uint8_t *readbuf = NULL; + err = grub_verify_signature_init (&ctxt, sig); + if (err) + return err; + + readbuf = grub_zalloc (READBUF_SIZE); + if (!readbuf) + goto fail; + + while (1) + { + grub_ssize_t r; + r = grub_file_read (f, readbuf, READBUF_SIZE); + if (r < 0) + goto fail; + if (r == 0) + break; + err = grub_pubkey_write (&ctxt, readbuf, r); + if (err) + return err; + } + + grub_verify_signature_real (&ctxt, pkey); + fail: + grub_pubkey_close_real (&ctxt); + return grub_errno; +} + +static grub_err_t +grub_cmd_trust (grub_extcmd_context_t ctxt, + int argc, char **args) +{ + grub_file_t pkf; + struct grub_public_key *pk = NULL; + + if (argc < 1) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("one argument expected")); + + pkf = grub_file_open (args[0], + GRUB_FILE_TYPE_PUBLIC_KEY_TRUST + | GRUB_FILE_TYPE_NO_DECOMPRESS + | (ctxt->state[OPTION_SKIP_SIG].set + ? GRUB_FILE_TYPE_SKIP_SIGNATURE + : 0)); + if (!pkf) + return grub_errno; + pk = grub_load_public_key (pkf); + if (!pk) + { + grub_file_close (pkf); + return grub_errno; + } + grub_file_close (pkf); + + pk->next = grub_pk_trusted; + grub_pk_trusted = pk; + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_cmd_list (grub_command_t cmd __attribute__ ((unused)), + int argc __attribute__ ((unused)), + char **args __attribute__ ((unused))) +{ + struct grub_public_key *pk = NULL; + struct grub_public_subkey *sk = NULL; + + for (pk = grub_pk_trusted; pk; pk = pk->next) + for (sk = pk->subkeys; sk; sk = sk->next) + { + unsigned i; + for (i = 0; i < 20; i += 2) + grub_printf ("%02x%02x ", ((grub_uint8_t *) sk->fingerprint)[i], + ((grub_uint8_t *) sk->fingerprint)[i + 1]); + grub_printf ("\n"); + } + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_cmd_distrust (grub_command_t cmd __attribute__ ((unused)), + int argc, char **args) +{ + grub_uint32_t keyid, keyid_be; + struct grub_public_key **pkey; + struct grub_public_subkey *sk; + + if (argc < 1) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("one argument expected")); + keyid = grub_strtoull (args[0], 0, 16); + if (grub_errno) + return grub_errno; + keyid_be = grub_cpu_to_be32 (keyid); + + for (pkey = &grub_pk_trusted; *pkey; pkey = &((*pkey)->next)) + { + struct grub_public_key *next; + for (sk = (*pkey)->subkeys; sk; sk = sk->next) + if (grub_memcmp (sk->fingerprint + 4, &keyid_be, 4) == 0) + break; + if (!sk) + continue; + next = (*pkey)->next; + free_pk (*pkey); + *pkey = next; + return GRUB_ERR_NONE; + } + /* TRANSLATORS: %08x is 32-bit key id. */ + return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("public key %08x not found"), keyid); +} + +static grub_err_t +grub_cmd_verify_signature (grub_extcmd_context_t ctxt, + int argc, char **args) +{ + grub_file_t f = NULL, sig = NULL; + grub_err_t err = GRUB_ERR_NONE; + struct grub_public_key *pk = NULL; + + grub_dprintf ("crypt", "alive\n"); + + if (argc < 2) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("two arguments expected")); + + grub_dprintf ("crypt", "alive\n"); + + if (argc > 2) + { + grub_file_t pkf; + pkf = grub_file_open (args[2], + GRUB_FILE_TYPE_PUBLIC_KEY + | GRUB_FILE_TYPE_NO_DECOMPRESS + | (ctxt->state[OPTION_SKIP_SIG].set + ? GRUB_FILE_TYPE_SKIP_SIGNATURE + : 0)); + if (!pkf) + return grub_errno; + pk = grub_load_public_key (pkf); + if (!pk) + { + grub_file_close (pkf); + return grub_errno; + } + grub_file_close (pkf); + } + + f = grub_file_open (args[0], GRUB_FILE_TYPE_VERIFY_SIGNATURE); + if (!f) + { + err = grub_errno; + goto fail; + } + + sig = grub_file_open (args[1], + GRUB_FILE_TYPE_SIGNATURE + | GRUB_FILE_TYPE_NO_DECOMPRESS); + if (!sig) + { + err = grub_errno; + goto fail; + } + + err = grub_verify_signature (f, sig, pk); + fail: + if (sig) + grub_file_close (sig); + if (f) + grub_file_close (f); + if (pk) + free_pk (pk); + return err; +} + +static int sec = 0; + +static grub_err_t +grub_pubkey_init (grub_file_t io, enum grub_file_type type __attribute__ ((unused)), + void **context, enum grub_verify_flags *flags) +{ + grub_file_t sig; + char *fsuf, *ptr; + grub_err_t err; + + if (!sec) + { + *flags = GRUB_VERIFY_FLAGS_SKIP_VERIFICATION; + return GRUB_ERR_NONE; + } + + fsuf = grub_malloc (grub_strlen (io->name) + sizeof (".sig")); + if (!fsuf) + return grub_errno; + ptr = grub_stpcpy (fsuf, io->name); + grub_memcpy (ptr, ".sig", sizeof (".sig")); + + sig = grub_file_open (fsuf, GRUB_FILE_TYPE_SIGNATURE); + grub_free (fsuf); + if (!sig) + return grub_errno; + + + struct grub_pubkey_context *ctxt = grub_malloc (sizeof (*ctxt)); + if (!ctxt) + { + grub_file_close (sig); + return grub_errno; + } + err = grub_verify_signature_init (ctxt, sig); + if (err) + { + grub_pubkey_close (ctxt); + return err; + } + *context = ctxt; + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_pubkey_fini (void *ctxt) +{ + return grub_verify_signature_real (ctxt, NULL); +} + +static char * +grub_env_write_sec (struct grub_env_var *var __attribute__ ((unused)), + const char *val) +{ + sec = (*val == '1') || (*val == 'e'); + return grub_strdup (sec ? "enforce" : "no"); +} + +static grub_ssize_t +pseudo_read (struct grub_file *file, char *buf, grub_size_t len) +{ + grub_memcpy (buf, (grub_uint8_t *) file->data + file->offset, len); + return len; +} + + +/* Filesystem descriptor. */ +struct grub_fs pseudo_fs = + { + .name = "pseudo", + .read = pseudo_read + }; + +struct grub_file_verifier grub_pubkey_verifier = + { + .name = "pgp", + .init = grub_pubkey_init, + .fini = grub_pubkey_fini, + .write = grub_pubkey_write, + .close = grub_pubkey_close, + }; + +static grub_extcmd_t cmd, cmd_trust; +static grub_command_t cmd_distrust, cmd_list; + +GRUB_MOD_INIT(verify) +{ + const char *val; + struct grub_module_header *header; + + val = grub_env_get ("check_signatures"); + if (val && (val[0] == '1' || val[0] == 'e')) + sec = 1; + else + sec = 0; + + grub_register_variable_hook ("check_signatures", 0, grub_env_write_sec); + grub_env_export ("check_signatures"); + + grub_pk_trusted = 0; + FOR_MODULES (header) + { + struct grub_file pseudo_file; + struct grub_public_key *pk = NULL; + + grub_memset (&pseudo_file, 0, sizeof (pseudo_file)); + + /* Not an ELF module, skip. */ + if (header->type != OBJ_TYPE_PUBKEY) + continue; + + pseudo_file.fs = &pseudo_fs; + pseudo_file.size = (header->size - sizeof (struct grub_module_header)); + pseudo_file.data = (char *) header + sizeof (struct grub_module_header); + + pk = grub_load_public_key (&pseudo_file); + if (!pk) + grub_fatal ("error loading initial key: %s\n", grub_errmsg); + + pk->next = grub_pk_trusted; + grub_pk_trusted = pk; + } + + if (!val) + grub_env_set ("check_signatures", grub_pk_trusted ? "enforce" : "no"); + + cmd = grub_register_extcmd ("verify_detached", grub_cmd_verify_signature, 0, + N_("[-s|--skip-sig] FILE SIGNATURE_FILE [PUBKEY_FILE]"), + N_("Verify detached signature."), + options); + cmd_trust = grub_register_extcmd ("trust", grub_cmd_trust, 0, + N_("[-s|--skip-sig] PUBKEY_FILE"), + N_("Add PUBKEY_FILE to trusted keys."), + options); + cmd_list = grub_register_command ("list_trusted", grub_cmd_list, + 0, + N_("Show the list of trusted keys.")); + cmd_distrust = grub_register_command ("distrust", grub_cmd_distrust, + N_("PUBKEY_ID"), + N_("Remove PUBKEY_ID from trusted keys.")); + + grub_verifier_register (&grub_pubkey_verifier); +} + +GRUB_MOD_FINI(verify) +{ + grub_verifier_unregister (&grub_pubkey_verifier); + grub_unregister_extcmd (cmd); + grub_unregister_extcmd (cmd_trust); + grub_unregister_command (cmd_list); + grub_unregister_command (cmd_distrust); +} diff --git a/grub-core/commands/verify.c b/grub-core/commands/verify.c deleted file mode 100644 index d5d7c0f..0000000 --- a/grub-core/commands/verify.c +++ /dev/null @@ -1,1018 +0,0 @@ -/* - * GRUB -- GRand Unified Bootloader - * Copyright (C) 2013 Free Software Foundation, Inc. - * - * GRUB is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * GRUB is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with GRUB. If not, see <http://www.gnu.org/licenses/>. - */ - -#include <grub/types.h> -#include <grub/misc.h> -#include <grub/mm.h> -#include <grub/err.h> -#include <grub/dl.h> -#include <grub/file.h> -#include <grub/command.h> -#include <grub/crypto.h> -#include <grub/i18n.h> -#include <grub/gcrypt/gcrypt.h> -#include <grub/pubkey.h> -#include <grub/env.h> -#include <grub/kernel.h> -#include <grub/extcmd.h> -#include <grub/verify.h> - -GRUB_MOD_LICENSE ("GPLv3+"); - -enum - { - OPTION_SKIP_SIG = 0 - }; - -static const struct grub_arg_option options[] = - { - {"skip-sig", 's', 0, - N_("Skip signature-checking of the public key file."), 0, ARG_TYPE_NONE}, - {0, 0, 0, 0, 0, 0} - }; - -static grub_err_t -read_packet_header (grub_file_t sig, grub_uint8_t *out_type, grub_size_t *len) -{ - grub_uint8_t type; - grub_uint8_t l; - grub_uint16_t l16; - grub_uint32_t l32; - - /* New format. */ - switch (grub_file_read (sig, &type, sizeof (type))) - { - case 1: - break; - case 0: - { - *out_type = 0xff; - return 0; - } - default: - if (grub_errno) - return grub_errno; - /* TRANSLATORS: it's about GNUPG signatures. */ - return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); - } - - if (type == 0) - { - *out_type = 0xfe; - return 0; - } - - if (!(type & 0x80)) - return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); - if (type & 0x40) - { - *out_type = (type & 0x3f); - if (grub_file_read (sig, &l, sizeof (l)) != 1) - return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); - if (l < 192) - { - *len = l; - return 0; - } - if (l < 224) - { - *len = (l - 192) << GRUB_CHAR_BIT; - if (grub_file_read (sig, &l, sizeof (l)) != 1) - return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); - *len |= l; - return 0; - } - if (l == 255) - { - if (grub_file_read (sig, &l32, sizeof (l32)) != sizeof (l32)) - return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); - *len = grub_be_to_cpu32 (l32); - return 0; - } - return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); - } - *out_type = ((type >> 2) & 0xf); - switch (type & 0x3) - { - case 0: - if (grub_file_read (sig, &l, sizeof (l)) != sizeof (l)) - return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); - *len = l; - return 0; - case 1: - if (grub_file_read (sig, &l16, sizeof (l16)) != sizeof (l16)) - return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); - *len = grub_be_to_cpu16 (l16); - return 0; - case 2: - if (grub_file_read (sig, &l32, sizeof (l32)) != sizeof (l32)) - return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); - *len = grub_be_to_cpu32 (l32); - return 0; - } - return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); -} - -struct signature_v4_header -{ - grub_uint8_t type; - grub_uint8_t pkeyalgo; - grub_uint8_t hash; - grub_uint16_t hashed_sub; -} GRUB_PACKED; - -const char *hashes[] = { - [0x01] = "md5", - [0x02] = "sha1", - [0x03] = "ripemd160", - [0x08] = "sha256", - [0x09] = "sha384", - [0x0a] = "sha512", - [0x0b] = "sha224" -}; - -struct gcry_pk_spec *grub_crypto_pk_dsa; -struct gcry_pk_spec *grub_crypto_pk_ecdsa; -struct gcry_pk_spec *grub_crypto_pk_rsa; - -static int -dsa_pad (gcry_mpi_t *hmpi, grub_uint8_t *hval, - const gcry_md_spec_t *hash, struct grub_public_subkey *sk); -static int -rsa_pad (gcry_mpi_t *hmpi, grub_uint8_t *hval, - const gcry_md_spec_t *hash, struct grub_public_subkey *sk); - -struct -{ - const char *name; - grub_size_t nmpisig; - grub_size_t nmpipub; - struct gcry_pk_spec **algo; - int (*pad) (gcry_mpi_t *hmpi, grub_uint8_t *hval, - const gcry_md_spec_t *hash, struct grub_public_subkey *sk); - const char *module; -} pkalgos[] = - { - [1] = { "rsa", 1, 2, &grub_crypto_pk_rsa, rsa_pad, "gcry_rsa" }, - [3] = { "rsa", 1, 2, &grub_crypto_pk_rsa, rsa_pad, "gcry_rsa" }, - [17] = { "dsa", 2, 4, &grub_crypto_pk_dsa, dsa_pad, "gcry_dsa" }, - }; - -struct grub_public_key -{ - struct grub_public_key *next; - struct grub_public_subkey *subkeys; -}; - -struct grub_public_subkey -{ - struct grub_public_subkey *next; - grub_uint8_t type; - grub_uint32_t fingerprint[5]; - gcry_mpi_t mpis[10]; -}; - -static void -free_pk (struct grub_public_key *pk) -{ - struct grub_public_subkey *nsk, *sk; - for (sk = pk->subkeys; sk; sk = nsk) - { - grub_size_t i; - for (i = 0; i < ARRAY_SIZE (sk->mpis); i++) - if (sk->mpis[i]) - gcry_mpi_release (sk->mpis[i]); - nsk = sk->next; - grub_free (sk); - } - grub_free (pk); -} - -#define READBUF_SIZE 4096 - -struct grub_public_key * -grub_load_public_key (grub_file_t f) -{ - grub_err_t err; - struct grub_public_key *ret; - struct grub_public_subkey **last = 0; - void *fingerprint_context = NULL; - grub_uint8_t *buffer = NULL; - - ret = grub_zalloc (sizeof (*ret)); - if (!ret) - { - grub_free (fingerprint_context); - return NULL; - } - - buffer = grub_zalloc (READBUF_SIZE); - fingerprint_context = grub_zalloc (GRUB_MD_SHA1->contextsize); - - if (!buffer || !fingerprint_context) - goto fail; - - last = &ret->subkeys; - - while (1) - { - grub_uint8_t type; - grub_size_t len; - grub_uint8_t v, pk; - grub_uint32_t creation_time; - grub_off_t pend; - struct grub_public_subkey *sk; - grub_size_t i; - grub_uint16_t len_be; - - err = read_packet_header (f, &type, &len); - - if (err) - goto fail; - if (type == 0xfe) - continue; - if (type == 0xff) - { - grub_free (fingerprint_context); - grub_free (buffer); - return ret; - } - - grub_dprintf ("crypt", "len = %x\n", (int) len); - - pend = grub_file_tell (f) + len; - if (type != 6 && type != 14 - && type != 5 && type != 7) - { - grub_file_seek (f, pend); - continue; - } - - if (grub_file_read (f, &v, sizeof (v)) != sizeof (v)) - { - grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); - goto fail; - } - - grub_dprintf ("crypt", "v = %x\n", v); - - if (v != 4) - { - grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); - goto fail; - } - if (grub_file_read (f, &creation_time, sizeof (creation_time)) != sizeof (creation_time)) - { - grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); - goto fail; - } - - grub_dprintf ("crypt", "time = %x\n", creation_time); - - if (grub_file_read (f, &pk, sizeof (pk)) != sizeof (pk)) - { - grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); - goto fail; - } - - grub_dprintf ("crypt", "pk = %x\n", pk); - - if (pk >= ARRAY_SIZE (pkalgos) || pkalgos[pk].name == NULL) - { - grub_file_seek (f, pend); - continue; - } - - sk = grub_zalloc (sizeof (struct grub_public_subkey)); - if (!sk) - goto fail; - - grub_memset (fingerprint_context, 0, GRUB_MD_SHA1->contextsize); - GRUB_MD_SHA1->init (fingerprint_context); - GRUB_MD_SHA1->write (fingerprint_context, "\x99", 1); - len_be = grub_cpu_to_be16 (len); - GRUB_MD_SHA1->write (fingerprint_context, &len_be, sizeof (len_be)); - GRUB_MD_SHA1->write (fingerprint_context, &v, sizeof (v)); - GRUB_MD_SHA1->write (fingerprint_context, &creation_time, sizeof (creation_time)); - GRUB_MD_SHA1->write (fingerprint_context, &pk, sizeof (pk)); - - for (i = 0; i < pkalgos[pk].nmpipub; i++) - { - grub_uint16_t l; - grub_size_t lb; - if (grub_file_read (f, &l, sizeof (l)) != sizeof (l)) - { - grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); - break; - } - - lb = (grub_be_to_cpu16 (l) + GRUB_CHAR_BIT - 1) / GRUB_CHAR_BIT; - if (lb > READBUF_SIZE - sizeof (grub_uint16_t)) - { - grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); - break; - } - if (grub_file_read (f, buffer + sizeof (grub_uint16_t), lb) != (grub_ssize_t) lb) - { - grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); - break; - } - grub_memcpy (buffer, &l, sizeof (l)); - - GRUB_MD_SHA1->write (fingerprint_context, buffer, lb + sizeof (grub_uint16_t)); - - if (gcry_mpi_scan (&sk->mpis[i], GCRYMPI_FMT_PGP, - buffer, lb + sizeof (grub_uint16_t), 0)) - { - grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); - break; - } - } - - if (i < pkalgos[pk].nmpipub) - { - grub_free (sk); - goto fail; - } - - GRUB_MD_SHA1->final (fingerprint_context); - - grub_memcpy (sk->fingerprint, GRUB_MD_SHA1->read (fingerprint_context), 20); - - *last = sk; - last = &sk->next; - - grub_dprintf ("crypt", "actual pos: %x, expected: %x\n", (int)grub_file_tell (f), (int)pend); - - grub_file_seek (f, pend); - } - fail: - free_pk (ret); - grub_free (fingerprint_context); - grub_free (buffer); - return NULL; -} - -struct grub_public_key *grub_pk_trusted; - -struct grub_public_subkey * -grub_crypto_pk_locate_subkey (grub_uint64_t keyid, struct grub_public_key *pkey) -{ - struct grub_public_subkey *sk; - for (sk = pkey->subkeys; sk; sk = sk->next) - if (grub_memcmp (sk->fingerprint + 3, &keyid, 8) == 0) - return sk; - return 0; -} - -struct grub_public_subkey * -grub_crypto_pk_locate_subkey_in_trustdb (grub_uint64_t keyid) -{ - struct grub_public_key *pkey; - struct grub_public_subkey *sk; - for (pkey = grub_pk_trusted; pkey; pkey = pkey->next) - { - sk = grub_crypto_pk_locate_subkey (keyid, pkey); - if (sk) - return sk; - } - return 0; -} - - -static int -dsa_pad (gcry_mpi_t *hmpi, grub_uint8_t *hval, - const gcry_md_spec_t *hash, struct grub_public_subkey *sk) -{ - unsigned nbits = gcry_mpi_get_nbits (sk->mpis[1]); - grub_dprintf ("crypt", "must be %u bits got %d bits\n", nbits, - (int)(8 * hash->mdlen)); - return gcry_mpi_scan (hmpi, GCRYMPI_FMT_USG, hval, - nbits / 8 < (unsigned) hash->mdlen ? nbits / 8 - : (unsigned) hash->mdlen, 0); -} - -static int -rsa_pad (gcry_mpi_t *hmpi, grub_uint8_t *hval, - const gcry_md_spec_t *hash, struct grub_public_subkey *sk) -{ - grub_size_t tlen, emlen, fflen; - grub_uint8_t *em, *emptr; - unsigned nbits = gcry_mpi_get_nbits (sk->mpis[0]); - int ret; - tlen = hash->mdlen + hash->asnlen; - emlen = (nbits + 7) / 8; - if (emlen < tlen + 11) - return 1; - - em = grub_malloc (emlen); - if (!em) - return 1; - - em[0] = 0x00; - em[1] = 0x01; - fflen = emlen - tlen - 3; - for (emptr = em + 2; emptr < em + 2 + fflen; emptr++) - *emptr = 0xff; - *emptr++ = 0x00; - grub_memcpy (emptr, hash->asnoid, hash->asnlen); - emptr += hash->asnlen; - grub_memcpy (emptr, hval, hash->mdlen); - - ret = gcry_mpi_scan (hmpi, GCRYMPI_FMT_USG, em, emlen, 0); - grub_free (em); - return ret; -} - -struct grub_pubkey_context -{ - grub_file_t sig; - struct signature_v4_header v4; - grub_uint8_t v; - const gcry_md_spec_t *hash; - void *hash_context; -}; - -static grub_err_t -grub_verify_signature_init (struct grub_pubkey_context *ctxt, grub_file_t sig) -{ - grub_size_t len; - grub_uint8_t h; - grub_uint8_t t; - grub_err_t err; - grub_uint8_t type = 0; - grub_uint8_t pk; - - grub_memset (ctxt, 0, sizeof (*ctxt)); - - err = read_packet_header (sig, &type, &len); - if (err) - return err; - - if (type != 0x2) - return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); - - if (grub_file_read (sig, &ctxt->v, sizeof (ctxt->v)) != sizeof (ctxt->v)) - return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); - - if (ctxt->v != 4) - return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); - - if (grub_file_read (sig, &ctxt->v4, sizeof (ctxt->v4)) != sizeof (ctxt->v4)) - return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); - - h = ctxt->v4.hash; - t = ctxt->v4.type; - pk = ctxt->v4.pkeyalgo; - - if (t != 0) - return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); - - if (h >= ARRAY_SIZE (hashes) || hashes[h] == NULL) - return grub_error (GRUB_ERR_BAD_SIGNATURE, "unknown hash"); - - if (pk >= ARRAY_SIZE (pkalgos) || pkalgos[pk].name == NULL) - return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); - - ctxt->hash = grub_crypto_lookup_md_by_name (hashes[h]); - if (!ctxt->hash) - return grub_error (GRUB_ERR_BAD_SIGNATURE, "hash `%s' not loaded", hashes[h]); - - grub_dprintf ("crypt", "alive\n"); - - ctxt->sig = sig; - - ctxt->hash_context = grub_zalloc (ctxt->hash->contextsize); - if (!ctxt->hash_context) - return grub_errno; - - ctxt->hash->init (ctxt->hash_context); - - return GRUB_ERR_NONE; -} - -static grub_err_t -grub_pubkey_write (void *ctxt_, void *buf, grub_size_t size) -{ - struct grub_pubkey_context *ctxt = ctxt_; - ctxt->hash->write (ctxt->hash_context, buf, size); - return GRUB_ERR_NONE; -} - -static grub_err_t -grub_verify_signature_real (struct grub_pubkey_context *ctxt, - struct grub_public_key *pkey) -{ - gcry_mpi_t mpis[10]; - grub_uint8_t pk = ctxt->v4.pkeyalgo; - grub_size_t i; - grub_uint8_t *readbuf = NULL; - unsigned char *hval; - grub_ssize_t rem = grub_be_to_cpu16 (ctxt->v4.hashed_sub); - grub_uint32_t headlen = grub_cpu_to_be32 (rem + 6); - grub_uint8_t s; - grub_uint16_t unhashed_sub; - grub_ssize_t r; - grub_uint8_t hash_start[2]; - gcry_mpi_t hmpi; - grub_uint64_t keyid = 0; - struct grub_public_subkey *sk; - - readbuf = grub_malloc (READBUF_SIZE); - if (!readbuf) - goto fail; - - ctxt->hash->write (ctxt->hash_context, &ctxt->v, sizeof (ctxt->v)); - ctxt->hash->write (ctxt->hash_context, &ctxt->v4, sizeof (ctxt->v4)); - while (rem) - { - r = grub_file_read (ctxt->sig, readbuf, - rem < READBUF_SIZE ? rem : READBUF_SIZE); - if (r < 0) - goto fail; - if (r == 0) - break; - ctxt->hash->write (ctxt->hash_context, readbuf, r); - rem -= r; - } - ctxt->hash->write (ctxt->hash_context, &ctxt->v, sizeof (ctxt->v)); - s = 0xff; - ctxt->hash->write (ctxt->hash_context, &s, sizeof (s)); - ctxt->hash->write (ctxt->hash_context, &headlen, sizeof (headlen)); - r = grub_file_read (ctxt->sig, &unhashed_sub, sizeof (unhashed_sub)); - if (r != sizeof (unhashed_sub)) - goto fail; - { - grub_uint8_t *ptr; - grub_uint32_t l; - rem = grub_be_to_cpu16 (unhashed_sub); - if (rem > READBUF_SIZE) - goto fail; - r = grub_file_read (ctxt->sig, readbuf, rem); - if (r != rem) - goto fail; - for (ptr = readbuf; ptr < readbuf + rem; ptr += l) - { - if (*ptr < 192) - l = *ptr++; - else if (*ptr < 255) - { - if (ptr + 1 >= readbuf + rem) - break; - l = (((ptr[0] & ~192) << GRUB_CHAR_BIT) | ptr[1]) + 192; - ptr += 2; - } - else - { - if (ptr + 5 >= readbuf + rem) - break; - l = grub_be_to_cpu32 (grub_get_unaligned32 (ptr + 1)); - ptr += 5; - } - if (*ptr == 0x10 && l >= 8) - keyid = grub_get_unaligned64 (ptr + 1); - } - } - - ctxt->hash->final (ctxt->hash_context); - - grub_dprintf ("crypt", "alive\n"); - - hval = ctxt->hash->read (ctxt->hash_context); - - if (grub_file_read (ctxt->sig, hash_start, sizeof (hash_start)) != sizeof (hash_start)) - goto fail; - if (grub_memcmp (hval, hash_start, sizeof (hash_start)) != 0) - goto fail; - - grub_dprintf ("crypt", "@ %x\n", (int)grub_file_tell (ctxt->sig)); - - for (i = 0; i < pkalgos[pk].nmpisig; i++) - { - grub_uint16_t l; - grub_size_t lb; - grub_dprintf ("crypt", "alive\n"); - if (grub_file_read (ctxt->sig, &l, sizeof (l)) != sizeof (l)) - goto fail; - grub_dprintf ("crypt", "alive\n"); - lb = (grub_be_to_cpu16 (l) + 7) / 8; - grub_dprintf ("crypt", "l = 0x%04x\n", grub_be_to_cpu16 (l)); - if (lb > READBUF_SIZE - sizeof (grub_uint16_t)) - goto fail; - grub_dprintf ("crypt", "alive\n"); - if (grub_file_read (ctxt->sig, readbuf + sizeof (grub_uint16_t), lb) != (grub_ssize_t) lb) - goto fail; - grub_dprintf ("crypt", "alive\n"); - grub_memcpy (readbuf, &l, sizeof (l)); - grub_dprintf ("crypt", "alive\n"); - - if (gcry_mpi_scan (&mpis[i], GCRYMPI_FMT_PGP, - readbuf, lb + sizeof (grub_uint16_t), 0)) - goto fail; - grub_dprintf ("crypt", "alive\n"); - } - - if (pkey) - sk = grub_crypto_pk_locate_subkey (keyid, pkey); - else - sk = grub_crypto_pk_locate_subkey_in_trustdb (keyid); - if (!sk) - { - /* TRANSLATORS: %08x is 32-bit key id. */ - grub_error (GRUB_ERR_BAD_SIGNATURE, N_("public key %08x not found"), - keyid); - goto fail; - } - - if (pkalgos[pk].pad (&hmpi, hval, ctxt->hash, sk)) - goto fail; - if (!*pkalgos[pk].algo) - { - grub_dl_load (pkalgos[pk].module); - grub_errno = GRUB_ERR_NONE; - } - - if (!*pkalgos[pk].algo) - { - grub_error (GRUB_ERR_BAD_SIGNATURE, N_("module `%s' isn't loaded"), - pkalgos[pk].module); - goto fail; - } - if ((*pkalgos[pk].algo)->verify (0, hmpi, mpis, sk->mpis, 0, 0)) - goto fail; - - grub_free (readbuf); - - return GRUB_ERR_NONE; - - fail: - grub_free (readbuf); - if (!grub_errno) - return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); - return grub_errno; -} - -static void -grub_pubkey_close_real (struct grub_pubkey_context *ctxt) -{ - if (ctxt->sig) - grub_file_close (ctxt->sig); - if (ctxt->hash_context) - grub_free (ctxt->hash_context); -} - -static void -grub_pubkey_close (void *ctxt) -{ - grub_pubkey_close_real (ctxt); - grub_free (ctxt); -} - -grub_err_t -grub_verify_signature (grub_file_t f, grub_file_t sig, - struct grub_public_key *pkey) -{ - grub_err_t err; - struct grub_pubkey_context ctxt; - grub_uint8_t *readbuf = NULL; - err = grub_verify_signature_init (&ctxt, sig); - if (err) - return err; - - readbuf = grub_zalloc (READBUF_SIZE); - if (!readbuf) - goto fail; - - while (1) - { - grub_ssize_t r; - r = grub_file_read (f, readbuf, READBUF_SIZE); - if (r < 0) - goto fail; - if (r == 0) - break; - err = grub_pubkey_write (&ctxt, readbuf, r); - if (err) - return err; - } - - grub_verify_signature_real (&ctxt, pkey); - fail: - grub_pubkey_close_real (&ctxt); - return grub_errno; -} - -static grub_err_t -grub_cmd_trust (grub_extcmd_context_t ctxt, - int argc, char **args) -{ - grub_file_t pkf; - struct grub_public_key *pk = NULL; - - if (argc < 1) - return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("one argument expected")); - - pkf = grub_file_open (args[0], - GRUB_FILE_TYPE_PUBLIC_KEY_TRUST - | GRUB_FILE_TYPE_NO_DECOMPRESS - | (ctxt->state[OPTION_SKIP_SIG].set - ? GRUB_FILE_TYPE_SKIP_SIGNATURE - : 0)); - if (!pkf) - return grub_errno; - pk = grub_load_public_key (pkf); - if (!pk) - { - grub_file_close (pkf); - return grub_errno; - } - grub_file_close (pkf); - - pk->next = grub_pk_trusted; - grub_pk_trusted = pk; - - return GRUB_ERR_NONE; -} - -static grub_err_t -grub_cmd_list (grub_command_t cmd __attribute__ ((unused)), - int argc __attribute__ ((unused)), - char **args __attribute__ ((unused))) -{ - struct grub_public_key *pk = NULL; - struct grub_public_subkey *sk = NULL; - - for (pk = grub_pk_trusted; pk; pk = pk->next) - for (sk = pk->subkeys; sk; sk = sk->next) - { - unsigned i; - for (i = 0; i < 20; i += 2) - grub_printf ("%02x%02x ", ((grub_uint8_t *) sk->fingerprint)[i], - ((grub_uint8_t *) sk->fingerprint)[i + 1]); - grub_printf ("\n"); - } - - return GRUB_ERR_NONE; -} - -static grub_err_t -grub_cmd_distrust (grub_command_t cmd __attribute__ ((unused)), - int argc, char **args) -{ - grub_uint32_t keyid, keyid_be; - struct grub_public_key **pkey; - struct grub_public_subkey *sk; - - if (argc < 1) - return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("one argument expected")); - keyid = grub_strtoull (args[0], 0, 16); - if (grub_errno) - return grub_errno; - keyid_be = grub_cpu_to_be32 (keyid); - - for (pkey = &grub_pk_trusted; *pkey; pkey = &((*pkey)->next)) - { - struct grub_public_key *next; - for (sk = (*pkey)->subkeys; sk; sk = sk->next) - if (grub_memcmp (sk->fingerprint + 4, &keyid_be, 4) == 0) - break; - if (!sk) - continue; - next = (*pkey)->next; - free_pk (*pkey); - *pkey = next; - return GRUB_ERR_NONE; - } - /* TRANSLATORS: %08x is 32-bit key id. */ - return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("public key %08x not found"), keyid); -} - -static grub_err_t -grub_cmd_verify_signature (grub_extcmd_context_t ctxt, - int argc, char **args) -{ - grub_file_t f = NULL, sig = NULL; - grub_err_t err = GRUB_ERR_NONE; - struct grub_public_key *pk = NULL; - - grub_dprintf ("crypt", "alive\n"); - - if (argc < 2) - return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("two arguments expected")); - - grub_dprintf ("crypt", "alive\n"); - - if (argc > 2) - { - grub_file_t pkf; - pkf = grub_file_open (args[2], - GRUB_FILE_TYPE_PUBLIC_KEY - | GRUB_FILE_TYPE_NO_DECOMPRESS - | (ctxt->state[OPTION_SKIP_SIG].set - ? GRUB_FILE_TYPE_SKIP_SIGNATURE - : 0)); - if (!pkf) - return grub_errno; - pk = grub_load_public_key (pkf); - if (!pk) - { - grub_file_close (pkf); - return grub_errno; - } - grub_file_close (pkf); - } - - f = grub_file_open (args[0], GRUB_FILE_TYPE_VERIFY_SIGNATURE); - if (!f) - { - err = grub_errno; - goto fail; - } - - sig = grub_file_open (args[1], - GRUB_FILE_TYPE_SIGNATURE - | GRUB_FILE_TYPE_NO_DECOMPRESS); - if (!sig) - { - err = grub_errno; - goto fail; - } - - err = grub_verify_signature (f, sig, pk); - fail: - if (sig) - grub_file_close (sig); - if (f) - grub_file_close (f); - if (pk) - free_pk (pk); - return err; -} - -static int sec = 0; - -static grub_err_t -grub_pubkey_init (grub_file_t io, enum grub_file_type type __attribute__ ((unused)), - void **context, enum grub_verify_flags *flags) -{ - grub_file_t sig; - char *fsuf, *ptr; - grub_err_t err; - - if (!sec) - { - *flags = GRUB_VERIFY_FLAGS_SKIP_VERIFICATION; - return GRUB_ERR_NONE; - } - - fsuf = grub_malloc (grub_strlen (io->name) + sizeof (".sig")); - if (!fsuf) - return grub_errno; - ptr = grub_stpcpy (fsuf, io->name); - grub_memcpy (ptr, ".sig", sizeof (".sig")); - - sig = grub_file_open (fsuf, GRUB_FILE_TYPE_SIGNATURE); - grub_free (fsuf); - if (!sig) - return grub_errno; - - - struct grub_pubkey_context *ctxt = grub_malloc (sizeof (*ctxt)); - if (!ctxt) - { - grub_file_close (sig); - return grub_errno; - } - err = grub_verify_signature_init (ctxt, sig); - if (err) - { - grub_pubkey_close (ctxt); - return err; - } - *context = ctxt; - return GRUB_ERR_NONE; -} - -static grub_err_t -grub_pubkey_fini (void *ctxt) -{ - return grub_verify_signature_real (ctxt, NULL); -} - -static char * -grub_env_write_sec (struct grub_env_var *var __attribute__ ((unused)), - const char *val) -{ - sec = (*val == '1') || (*val == 'e'); - return grub_strdup (sec ? "enforce" : "no"); -} - -static grub_ssize_t -pseudo_read (struct grub_file *file, char *buf, grub_size_t len) -{ - grub_memcpy (buf, (grub_uint8_t *) file->data + file->offset, len); - return len; -} - - -/* Filesystem descriptor. */ -struct grub_fs pseudo_fs = - { - .name = "pseudo", - .read = pseudo_read - }; - -struct grub_file_verifier grub_pubkey_verifier = - { - .name = "pgp", - .init = grub_pubkey_init, - .fini = grub_pubkey_fini, - .write = grub_pubkey_write, - .close = grub_pubkey_close, - }; - -static grub_extcmd_t cmd, cmd_trust; -static grub_command_t cmd_distrust, cmd_list; - -GRUB_MOD_INIT(verify) -{ - const char *val; - struct grub_module_header *header; - - val = grub_env_get ("check_signatures"); - if (val && (val[0] == '1' || val[0] == 'e')) - sec = 1; - else - sec = 0; - - grub_register_variable_hook ("check_signatures", 0, grub_env_write_sec); - grub_env_export ("check_signatures"); - - grub_pk_trusted = 0; - FOR_MODULES (header) - { - struct grub_file pseudo_file; - struct grub_public_key *pk = NULL; - - grub_memset (&pseudo_file, 0, sizeof (pseudo_file)); - - /* Not an ELF module, skip. */ - if (header->type != OBJ_TYPE_PUBKEY) - continue; - - pseudo_file.fs = &pseudo_fs; - pseudo_file.size = (header->size - sizeof (struct grub_module_header)); - pseudo_file.data = (char *) header + sizeof (struct grub_module_header); - - pk = grub_load_public_key (&pseudo_file); - if (!pk) - grub_fatal ("error loading initial key: %s\n", grub_errmsg); - - pk->next = grub_pk_trusted; - grub_pk_trusted = pk; - } - - if (!val) - grub_env_set ("check_signatures", grub_pk_trusted ? "enforce" : "no"); - - cmd = grub_register_extcmd ("verify_detached", grub_cmd_verify_signature, 0, - N_("[-s|--skip-sig] FILE SIGNATURE_FILE [PUBKEY_FILE]"), - N_("Verify detached signature."), - options); - cmd_trust = grub_register_extcmd ("trust", grub_cmd_trust, 0, - N_("[-s|--skip-sig] PUBKEY_FILE"), - N_("Add PUBKEY_FILE to trusted keys."), - options); - cmd_list = grub_register_command ("list_trusted", grub_cmd_list, - 0, - N_("Show the list of trusted keys.")); - cmd_distrust = grub_register_command ("distrust", grub_cmd_distrust, - N_("PUBKEY_ID"), - N_("Remove PUBKEY_ID from trusted keys.")); - - grub_verifier_register (&grub_pubkey_verifier); -} - -GRUB_MOD_FINI(verify) -{ - grub_verifier_unregister (&grub_pubkey_verifier); - grub_unregister_extcmd (cmd); - grub_unregister_extcmd (cmd_trust); - grub_unregister_command (cmd_list); - grub_unregister_command (cmd_distrust); -} -- 1.7.10.4 _______________________________________________ Grub-devel mailing list Grub-devel@gnu.org https://lists.gnu.org/mailman/listinfo/grub-devel