03a713b7ab
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>
1096 lines
27 KiB
C
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);
|
|
}
|