mirror of
https://git.savannah.gnu.org/git/grub.git
synced 2026-10-25 08:39:28 +00:00
PE signatures. Doesn't work yet
This commit is contained in:
1 parent
8128bd056e
commit
371bc1c048
3 files changed
+449
-7
No files matched your search
+426
-7
@@ -30,6 +30,7 @@
|
||||
#include <grub/env.h>
|
||||
#include <grub/kernel.h>
|
||||
#include <grub/extcmd.h>
|
||||
#include <grub/efi/pe32.h>
|
||||
|
||||
GRUB_MOD_LICENSE ("GPLv3+");
|
||||
|
||||
@@ -432,6 +433,356 @@ rsa_pad (gcry_mpi_t *hmpi, grub_uint8_t *hval,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static grub_err_t
|
||||
get (char *buf, grub_size_t size,
|
||||
grub_file_t f, void *out,
|
||||
grub_off_t off, grub_size_t sz)
|
||||
{
|
||||
if (buf)
|
||||
{
|
||||
if (off > ~(grub_uint32_t) sz
|
||||
|| off + sz > size)
|
||||
return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature"));
|
||||
grub_memcpy (out, buf + off, sz);
|
||||
return GRUB_ERR_NONE;
|
||||
}
|
||||
if (grub_file_seek (f, off) == (grub_size_t) -1)
|
||||
return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature"));
|
||||
if (grub_file_read (f, out, sz) != (grub_ssize_t) sz)
|
||||
return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature"));
|
||||
return GRUB_ERR_NONE;
|
||||
}
|
||||
|
||||
static grub_err_t
|
||||
read_len (char *buf, grub_size_t size,
|
||||
grub_file_t f, grub_off_t *curoff, grub_uint32_t *endoff)
|
||||
{
|
||||
grub_uint8_t cb;
|
||||
unsigned ss, rl;
|
||||
grub_uint32_t v = 0;
|
||||
grub_err_t err;
|
||||
|
||||
err = get (buf, size, f, &cb, (*curoff)++, 1);
|
||||
if (err)
|
||||
return err;
|
||||
if (!(cb & 0x80))
|
||||
{
|
||||
*endoff = *curoff + (cb & 0x7f);
|
||||
return GRUB_ERR_NONE;
|
||||
}
|
||||
|
||||
ss = cb & 0x7f;
|
||||
if (ss > 4)
|
||||
rl = 4;
|
||||
else
|
||||
rl = ss;
|
||||
*curoff += (ss - rl);
|
||||
err = get (buf, size, f, (char *) &v + (4 - rl), *curoff,
|
||||
rl);
|
||||
if (err)
|
||||
return err;
|
||||
*curoff += rl;
|
||||
*endoff = *curoff + (grub_be_to_cpu32 (v) & 0x7fffff);
|
||||
return GRUB_ERR_NONE;
|
||||
}
|
||||
|
||||
#define MAX_FULLASN 128
|
||||
|
||||
static grub_err_t
|
||||
grub_verify_pe_signature_real (char *buf, grub_size_t size,
|
||||
grub_file_t f,
|
||||
struct grub_public_key *pkey)
|
||||
{
|
||||
grub_uint8_t mz[2];
|
||||
const gcry_md_spec_t *hash;
|
||||
grub_uint32_t coff_offset, opt_offset;
|
||||
union
|
||||
{
|
||||
struct grub_pe32_optional_header o32;
|
||||
struct grub_pe64_optional_header o64;
|
||||
} opt_head;
|
||||
struct grub_pe32_data_directory *certtab;
|
||||
grub_err_t err;
|
||||
void *context = NULL;
|
||||
void *read_buf = NULL;
|
||||
grub_uint8_t *hval;
|
||||
grub_off_t curoff;
|
||||
grub_uint8_t cb;
|
||||
grub_uint8_t full_asn[MAX_FULLASN];
|
||||
/*
|
||||
hash as:
|
||||
offs[0] - offs[1]
|
||||
skip: checksum
|
||||
offs[2] - offs[3]
|
||||
skip: cert entry
|
||||
offs[4] - offs[5]
|
||||
skip: cert
|
||||
offs[6] - offs[7]
|
||||
*/
|
||||
grub_uint32_t offs[8];
|
||||
grub_uint32_t endoff[5];
|
||||
grub_uint32_t full_asn_offset, full_asn_offset_end;
|
||||
grub_size_t i;
|
||||
|
||||
err = get (buf, size, f, mz, 0, 2);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (mz[0] != 'M' || mz[1] != 'Z')
|
||||
goto fail;
|
||||
|
||||
err = get (buf, size, f, &coff_offset, 0x3c, 4);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
opt_offset = grub_cpu_to_le32 (coff_offset) + sizeof (struct grub_pe32_coff_header) + 4;
|
||||
|
||||
err = get (buf, size, f, &opt_head, opt_offset, sizeof (opt_head));
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
grub_dprintf ("crypt", "opt_offset = %x\n", (int) opt_offset);
|
||||
|
||||
if (opt_head.o32.magic == grub_cpu_to_le16_compile_time (GRUB_PE32_PE32_MAGIC))
|
||||
{
|
||||
offs[1] = (char *) &opt_head.o32.checksum - (char *) &opt_head + opt_offset;
|
||||
certtab = &opt_head.o32.certificate_table;
|
||||
}
|
||||
else if (opt_head.o64.magic == grub_cpu_to_le16_compile_time (GRUB_PE32_PE64_MAGIC))
|
||||
{
|
||||
offs[1] = (char *) &opt_head.o64.checksum - (char *) &opt_head + opt_offset;
|
||||
certtab = &opt_head.o64.certificate_table;
|
||||
}
|
||||
else
|
||||
return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature"));
|
||||
|
||||
if (certtab->size == 0)
|
||||
goto fail;
|
||||
|
||||
offs[0] = 0;
|
||||
offs[2] = offs[1] + 4;
|
||||
offs[3] = (char *) certtab - (char *) &opt_head + opt_offset;
|
||||
offs[4] = offs[3] + sizeof (*certtab);
|
||||
offs[5] = grub_le_to_cpu32 (certtab->rva);
|
||||
offs[6] = offs[5] + grub_le_to_cpu32 (certtab->size);
|
||||
offs[7] = buf ? size : grub_file_size (f);
|
||||
|
||||
/* Verify that offset sequence is valid. */
|
||||
for (i = 0; i < 7; i++)
|
||||
if (offs[i + 1] < offs[i])
|
||||
goto fail;
|
||||
|
||||
grub_dprintf ("crypt", "sig @%x+%x\n", (int)certtab->rva,
|
||||
(int)certtab->size);
|
||||
|
||||
|
||||
curoff = grub_le_to_cpu32 (certtab->rva) + 8;
|
||||
err = get (buf, size, f, &cb, curoff++, 1);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (cb != 0x30)
|
||||
goto fail;
|
||||
|
||||
/* into. */
|
||||
err = read_len (buf, size, f, &curoff, &endoff[0]);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = get (buf, size, f, &cb, curoff++, 1);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (cb != 0x06)
|
||||
goto fail;
|
||||
|
||||
/* skip. */
|
||||
err = read_len (buf, size, f, &curoff, &endoff[1]);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
curoff = endoff[1];
|
||||
|
||||
err = get (buf, size, f, &cb, curoff++, 1);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (cb != 0xa0)
|
||||
goto fail;
|
||||
|
||||
/* into. */
|
||||
err = read_len (buf, size, f, &curoff, &endoff[1]);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = get (buf, size, f, &cb, curoff++, 1);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (cb != 0x30)
|
||||
goto fail;
|
||||
|
||||
/* into. */
|
||||
err = read_len (buf, size, f, &curoff, &endoff[2]);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = get (buf, size, f, &cb, curoff++, 1);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (cb != 0x02)
|
||||
goto fail;
|
||||
|
||||
/* skip */
|
||||
err = read_len (buf, size, f, &curoff, &endoff[2]);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
curoff = endoff[2];
|
||||
|
||||
err = get (buf, size, f, &cb, curoff++, 1);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (cb != 0x31)
|
||||
goto fail;
|
||||
|
||||
err = get (buf, size, f, &cb, curoff++, 1);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (cb != 0x0f)
|
||||
goto fail;
|
||||
|
||||
err = read_len (buf, size, f, &curoff, &endoff[3]);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
curoff = endoff[3];
|
||||
|
||||
err = get (buf, size, f, &cb, curoff++, 1);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (cb != 0x03)
|
||||
goto fail;
|
||||
|
||||
err = read_len (buf, size, f, &curoff, &endoff[3]);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
curoff = endoff[3];
|
||||
|
||||
err = get (buf, size, f, &cb, curoff++, 1);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (cb != 0xa0)
|
||||
goto fail;
|
||||
|
||||
err = read_len (buf, size, f, &curoff, &endoff[3]);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
curoff = endoff[3];
|
||||
|
||||
grub_dprintf ("crypt", "off: %x\n",
|
||||
(int)curoff - grub_le_to_cpu32 (certtab->rva));
|
||||
|
||||
/* At this point we have the full ASN at current offset */
|
||||
full_asn_offset = curoff;
|
||||
err = get (buf, size, f, &cb, curoff++, 1);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (cb != 0x30)
|
||||
goto fail;
|
||||
|
||||
err = read_len (buf, size, f, &curoff, &endoff[3]);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
curoff = endoff[3];
|
||||
full_asn_offset_end = curoff;
|
||||
|
||||
grub_dprintf ("crypt", "off: %x\n",
|
||||
(int)full_asn_offset_end - grub_le_to_cpu32 (certtab->rva));
|
||||
|
||||
if (full_asn_offset_end - full_asn_offset > MAX_FULLASN)
|
||||
goto fail;
|
||||
|
||||
err = get (buf, size, f, full_asn, full_asn_offset,
|
||||
full_asn_offset_end - full_asn_offset);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
hash = grub_crypto_lookup_md_by_asn (full_asn,
|
||||
full_asn_offset_end - full_asn_offset);
|
||||
if (!hash)
|
||||
return grub_error (GRUB_ERR_BAD_SIGNATURE, "hash not loaded");
|
||||
|
||||
if (hash->asnlen + hash->mdlen != full_asn_offset_end - full_asn_offset)
|
||||
goto fail;
|
||||
|
||||
context = grub_zalloc (hash->contextsize);
|
||||
if (!context)
|
||||
goto fail;
|
||||
|
||||
hash->init (context);
|
||||
|
||||
if (buf)
|
||||
{
|
||||
for (i = 0; i <= 3; i++)
|
||||
hash->write (context, buf + offs[2 * i],
|
||||
offs[2 * i + 1] - offs[2 * i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
read_buf = grub_malloc (READBUF_SIZE);
|
||||
for (i = 0; i <= 3; i++)
|
||||
{
|
||||
grub_size_t rem;
|
||||
grub_ssize_t r;
|
||||
if (grub_file_seek (f, offs[2 * i]) == (grub_size_t) -1)
|
||||
goto fail;
|
||||
rem = offs[2 * i + 1] - offs[2 * i];
|
||||
COMPILE_TIME_ASSERT (sizeof (rem) >= sizeof (offs[0]));
|
||||
while (rem)
|
||||
{
|
||||
r = grub_file_read (f, read_buf,
|
||||
rem < READBUF_SIZE ? rem : READBUF_SIZE);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
if (r == 0)
|
||||
break;
|
||||
hash->write (context, read_buf, r);
|
||||
rem -= r;
|
||||
}
|
||||
if (rem)
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
hash->final (context);
|
||||
hval = hash->read (context);
|
||||
|
||||
if (grub_memcmp (full_asn + hash->asnlen,
|
||||
hval, hash->mdlen) != 0)
|
||||
goto fail;
|
||||
for (i = 0; i < hash->mdlen; i++)
|
||||
grub_printf ("%02x ", hval[i]);
|
||||
grub_printf ("\n");
|
||||
|
||||
(void) pkey;
|
||||
|
||||
return GRUB_ERR_NONE;
|
||||
fail:
|
||||
grub_free (context);
|
||||
grub_free (read_buf);
|
||||
return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature"));
|
||||
}
|
||||
|
||||
static grub_err_t
|
||||
grub_verify_signature_real (char *buf, grub_size_t size,
|
||||
grub_file_t f, grub_file_t sig,
|
||||
@@ -530,6 +881,8 @@ grub_verify_signature_real (char *buf, grub_size_t size,
|
||||
hash->write (context, readbuf, r);
|
||||
rem -= r;
|
||||
}
|
||||
if (rem)
|
||||
goto fail;
|
||||
hash->write (context, &v, sizeof (v));
|
||||
s = 0xff;
|
||||
hash->write (context, &s, sizeof (s));
|
||||
@@ -804,6 +1157,57 @@ grub_cmd_verify_signature (grub_extcmd_context_t ctxt,
|
||||
return err;
|
||||
}
|
||||
|
||||
static grub_err_t
|
||||
grub_cmd_verify_pe_signature (grub_extcmd_context_t ctxt,
|
||||
int argc, char **args)
|
||||
{
|
||||
grub_file_t f = NULL;
|
||||
grub_err_t err = GRUB_ERR_NONE;
|
||||
struct grub_public_key *pk = NULL;
|
||||
|
||||
grub_dprintf ("crypt", "alive\n");
|
||||
|
||||
if (argc < 1)
|
||||
return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("one argument expected"));
|
||||
|
||||
grub_dprintf ("crypt", "alive\n");
|
||||
|
||||
if (argc > 1)
|
||||
{
|
||||
grub_file_t pkf;
|
||||
pkf = grub_file_open (args[1],
|
||||
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;
|
||||
}
|
||||
|
||||
err = grub_verify_pe_signature_real (0, 0, f, pk);
|
||||
fail:
|
||||
if (f)
|
||||
grub_file_close (f);
|
||||
if (pk)
|
||||
free_pk (pk);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int sec = 0;
|
||||
|
||||
static grub_ssize_t
|
||||
@@ -848,13 +1252,6 @@ grub_pubkey_open (grub_file_t io, enum grub_file_type type)
|
||||
fsuf = grub_malloc (grub_strlen (io->name) + sizeof (".sig"));
|
||||
if (!fsuf)
|
||||
return NULL;
|
||||
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 NULL;
|
||||
|
||||
ret = grub_malloc (sizeof (*ret));
|
||||
if (!ret)
|
||||
@@ -883,8 +1280,26 @@ grub_pubkey_open (grub_file_t io, enum grub_file_type type)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
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)
|
||||
grub_errno = GRUB_ERR_NONE;
|
||||
|
||||
if (sig)
|
||||
{
|
||||
err = grub_verify_signature_real (ret->data, ret->size, 0, sig, NULL);
|
||||
grub_file_close (sig);
|
||||
if (err == GRUB_ERR_BAD_SIGNATURE)
|
||||
{
|
||||
grub_errno = GRUB_ERR_NONE;
|
||||
err = grub_verify_pe_signature_real (ret->data, ret->size, 0, 0);
|
||||
}
|
||||
}
|
||||
else
|
||||
err = grub_verify_pe_signature_real (ret->data, ret->size, 0, 0);
|
||||
if (err)
|
||||
return NULL;
|
||||
io->device = 0;
|
||||
@@ -966,6 +1381,10 @@ GRUB_MOD_INIT(verify)
|
||||
N_("[-s|--skip-sig] FILE SIGNATURE_FILE [PUBKEY_FILE]"),
|
||||
N_("Verify detached signature."),
|
||||
options);
|
||||
cmd = grub_register_extcmd ("verify_pe", grub_cmd_verify_pe_signature, 0,
|
||||
N_("[-s|--skip-sig] FILE [PUBKEY_FILE]"),
|
||||
N_("Verify PE signature."),
|
||||
options);
|
||||
cmd_trust = grub_register_extcmd ("trust", grub_cmd_trust, 0,
|
||||
N_("[-s|--skip-sig] PUBKEY_FILE"),
|
||||
N_("Add PKFILE to trusted keys."),
|
||||
|
||||
@@ -150,6 +150,27 @@ grub_crypto_lookup_md_by_name (const char *name)
|
||||
}
|
||||
}
|
||||
|
||||
const gcry_md_spec_t *
|
||||
grub_crypto_lookup_md_by_asn (const void *asn, grub_size_t asnlen)
|
||||
{
|
||||
const gcry_md_spec_t *md;
|
||||
int first = 1;
|
||||
while (1)
|
||||
{
|
||||
for (md = grub_digests; md; md = md->next)
|
||||
if (md->asnlen
|
||||
&& (grub_size_t) md->asnlen <= asnlen
|
||||
&& grub_memcmp (asn, md->asnoid, md->asnlen) == 0)
|
||||
return md;
|
||||
if (grub_crypto_autoload_hook && first)
|
||||
/* FIXME */
|
||||
grub_crypto_autoload_hook ("sha256");
|
||||
else
|
||||
return NULL;
|
||||
first = 0;
|
||||
}
|
||||
}
|
||||
|
||||
const gcry_cipher_spec_t *
|
||||
grub_crypto_lookup_cipher_by_name (const char *name)
|
||||
{
|
||||
|
||||
@@ -260,6 +260,8 @@ struct grub_crypto_hmac_handle;
|
||||
|
||||
const gcry_cipher_spec_t *
|
||||
grub_crypto_lookup_cipher_by_name (const char *name);
|
||||
const gcry_md_spec_t *
|
||||
grub_crypto_lookup_md_by_asn (const void *asn, grub_size_t asnlen);
|
||||
|
||||
grub_crypto_cipher_handle_t
|
||||
grub_crypto_cipher_open (const struct gcry_cipher_spec *cipher);
|
||||
|
||||
Reference in new issue
Block a user