/* * 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 . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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_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 }; 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_uint8_t pk; grub_err_t err; grub_uint8_t type = 0; 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 : GRUB_FILE_TYPE_NONE)); 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_trust_var (grub_command_t cmd __attribute__ ((unused)), int argc, char **args) { struct grub_file pseudo_file; const char *var; char *data; struct grub_public_key *pk = NULL; unsigned int i, idx0, idx1; if (argc < 1) return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("one argument expected")); var = grub_env_get (args[0]); if (!var) return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("unknown variable")); data = grub_zalloc (grub_strlen (var) / 2); if (!data) return grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("can't allocate memory for key")); /* For the want of sscanf() */ for (i = 0; i < grub_strlen (var); i += 2) { if (var[i] < 0x40) idx0 = var[i] - 0x30; else idx0 = var[i] - 0x57; if (var[i+1] < 0x40) idx1 = var[i+1] - 0x30; else idx1 = var[i+1] - 0x57; data[i/2] = ((idx0 << 4) & 0xf0) | (idx1 & 0x0f); } grub_memset (&pseudo_file, 0, sizeof (pseudo_file)); pseudo_file.fs = &pseudo_fs; pseudo_file.size = grub_strlen (var) / 2; pseudo_file.data = data; pk = grub_load_public_key (&pseudo_file); if (!pk) { grub_free(data); return grub_errno; } pk->next = grub_pk_trusted; grub_pk_trusted = pk; grub_free(data); 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 : GRUB_FILE_TYPE_NONE)); 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; struct grub_pubkey_context *ctxt; 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; 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"); } 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_trust_var, 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_trust_var = grub_register_command ("trust_var", grub_cmd_trust_var, N_("PUBKEY_VAR"), N_("Add the contents of PUBKEY_VAR to trusted keys.")); 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_trust_var); grub_unregister_command (cmd_list); grub_unregister_command (cmd_distrust); }