grub/grub-core/commands/verify.c
Vladimir Serbinenko 03a713b7ab verifiers: Framework core
Verifiers framework provides core file verification functionality which
can be used by various security mechanisms, e.g., UEFI secure boot, TPM,
PGP signature verification, etc.

The patch contains PGP code changes and probably they should be extracted
to separate patch for the sake of clarity.

Signed-off-by: Vladimir Serbinenko <phcoder@gmail.com>
Signed-off-by: Daniel Kiper <daniel.kiper@oracle.com>
Reviewed-by: Ross Philipson <ross.philipson@oracle.com>
2020-09-21 13:31:01 -04:00

1096 lines
27 KiB
C

/*
* 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_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");
}
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,
};
verifiers: Framework core
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);
}