/*
* VAS_EBOOT -- GRand Unified Bootloader
* Copyright (C) 2013 Free Software Foundation, Inc.
*
* VAS_EBOOT 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.
*
* VAS_EBOOT 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 VAS_EBOOT. If not, see .
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
VAS_EBOOT_MOD_LICENSE ("GPLv3+");
enum
{
OPTION_SKIP_SIG = 0
};
static const struct VasEBoot_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 VasEBoot_err_t
read_packet_header (VasEBoot_file_t sig, VasEBoot_uint8_t *out_type, VasEBoot_size_t *len)
{
VasEBoot_uint8_t type;
VasEBoot_uint8_t l;
VasEBoot_uint16_t l16;
VasEBoot_uint32_t l32;
/* New format. */
switch (VasEBoot_file_read (sig, &type, sizeof (type)))
{
case 1:
break;
case 0:
{
*out_type = 0xff;
return 0;
}
default:
if (VasEBoot_errno)
return VasEBoot_errno;
/* TRANSLATORS: it's about GNUPG signatures. */
return VasEBoot_error (VAS_EBOOT_ERR_BAD_SIGNATURE, N_("bad signature"));
}
if (type == 0)
{
*out_type = 0xfe;
return 0;
}
if (!(type & 0x80))
return VasEBoot_error (VAS_EBOOT_ERR_BAD_SIGNATURE, N_("bad signature"));
if (type & 0x40)
{
*out_type = (type & 0x3f);
if (VasEBoot_file_read (sig, &l, sizeof (l)) != 1)
return VasEBoot_error (VAS_EBOOT_ERR_BAD_SIGNATURE, N_("bad signature"));
if (l < 192)
{
*len = l;
return 0;
}
if (l < 224)
{
*len = (l - 192) << VAS_EBOOT_CHAR_BIT;
if (VasEBoot_file_read (sig, &l, sizeof (l)) != 1)
return VasEBoot_error (VAS_EBOOT_ERR_BAD_SIGNATURE, N_("bad signature"));
*len |= l;
return 0;
}
if (l == 255)
{
if (VasEBoot_file_read (sig, &l32, sizeof (l32)) != sizeof (l32))
return VasEBoot_error (VAS_EBOOT_ERR_BAD_SIGNATURE, N_("bad signature"));
*len = VasEBoot_be_to_cpu32 (l32);
return 0;
}
return VasEBoot_error (VAS_EBOOT_ERR_BAD_SIGNATURE, N_("bad signature"));
}
*out_type = ((type >> 2) & 0xf);
switch (type & 0x3)
{
case 0:
if (VasEBoot_file_read (sig, &l, sizeof (l)) != sizeof (l))
return VasEBoot_error (VAS_EBOOT_ERR_BAD_SIGNATURE, N_("bad signature"));
*len = l;
return 0;
case 1:
if (VasEBoot_file_read (sig, &l16, sizeof (l16)) != sizeof (l16))
return VasEBoot_error (VAS_EBOOT_ERR_BAD_SIGNATURE, N_("bad signature"));
*len = VasEBoot_be_to_cpu16 (l16);
return 0;
case 2:
if (VasEBoot_file_read (sig, &l32, sizeof (l32)) != sizeof (l32))
return VasEBoot_error (VAS_EBOOT_ERR_BAD_SIGNATURE, N_("bad signature"));
*len = VasEBoot_be_to_cpu32 (l32);
return 0;
}
return VasEBoot_error (VAS_EBOOT_ERR_BAD_SIGNATURE, N_("bad signature"));
}
struct signature_v4_header
{
VasEBoot_uint8_t type;
VasEBoot_uint8_t pkeyalgo;
VasEBoot_uint8_t hash;
VasEBoot_uint16_t hashed_sub;
} VAS_EBOOT_PACKED;
struct
{
const char *name;
const char *sigsexp;
VasEBoot_size_t nmpisig;
const char *pubsexp;
VasEBoot_size_t nmpipub;
struct gcry_pk_spec **algo;
const char *padding;
const char *module;
} pkalgos[] =
{
[1] = { "rsa", "(sig-val (rsa (s %M)))", 1, "(public-key (dsa (n %M) (e %M)))", 2, &VasEBoot_crypto_pk_rsa, "pkcs1", "gcry_rsa" },
[3] = { "rsa", "(sig-val (rsa (s %M)))", 1, "(public-key (dsa (n %M) (e %M)))", 2, &VasEBoot_crypto_pk_rsa, "pkcs1", "gcry_rsa" },
[17] = { "dsa", "(sig-val (dsa (r %M) (s %M)))", 2,
"(public-key (dsa (p %M) (q %M) (g %M) (y %M)))",
4, &VasEBoot_crypto_pk_dsa, "raw", "gcry_dsa" },
};
struct VasEBoot_public_key
{
struct VasEBoot_public_key *next;
struct VasEBoot_public_subkey *subkeys;
};
struct VasEBoot_public_subkey
{
struct VasEBoot_public_subkey *next;
VasEBoot_uint8_t type;
VasEBoot_uint32_t fingerprint[5];
gcry_mpi_t mpis[10];
};
static void
free_pk (struct VasEBoot_public_key *pk)
{
struct VasEBoot_public_subkey *nsk, *sk;
for (sk = pk->subkeys; sk; sk = nsk)
{
VasEBoot_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;
VasEBoot_free (sk);
}
VasEBoot_free (pk);
}
#define READBUF_SIZE 4096
struct VasEBoot_public_key *
VasEBoot_load_public_key (VasEBoot_file_t f)
{
VasEBoot_err_t err;
struct VasEBoot_public_key *ret;
struct VasEBoot_public_subkey **last = 0;
void *fingerprint_context = NULL;
VasEBoot_uint8_t *buffer = NULL;
ret = VasEBoot_zalloc (sizeof (*ret));
if (!ret)
{
VasEBoot_free (fingerprint_context);
return NULL;
}
buffer = VasEBoot_zalloc (READBUF_SIZE);
fingerprint_context = VasEBoot_zalloc (VAS_EBOOT_MD_SHA1->contextsize);
if (!buffer || !fingerprint_context)
goto fail;
last = &ret->subkeys;
while (1)
{
VasEBoot_uint8_t type;
VasEBoot_size_t len;
VasEBoot_uint8_t v, pk;
VasEBoot_uint32_t creation_time;
VasEBoot_off_t pend;
struct VasEBoot_public_subkey *sk;
VasEBoot_size_t i;
VasEBoot_uint16_t len_be;
err = read_packet_header (f, &type, &len);
if (err)
goto fail;
if (type == 0xfe)
continue;
if (type == 0xff)
{
VasEBoot_free (fingerprint_context);
VasEBoot_free (buffer);
return ret;
}
VasEBoot_dprintf ("crypt", "len = %x\n", (int) len);
pend = VasEBoot_file_tell (f) + len;
if (type != 6 && type != 14
&& type != 5 && type != 7)
{
VasEBoot_file_seek (f, pend);
continue;
}
if (VasEBoot_file_read (f, &v, sizeof (v)) != sizeof (v))
{
VasEBoot_error (VAS_EBOOT_ERR_BAD_SIGNATURE, N_("bad signature"));
goto fail;
}
VasEBoot_dprintf ("crypt", "v = %x\n", v);
if (v != 4)
{
VasEBoot_error (VAS_EBOOT_ERR_BAD_SIGNATURE, N_("bad signature"));
goto fail;
}
if (VasEBoot_file_read (f, &creation_time, sizeof (creation_time)) != sizeof (creation_time))
{
VasEBoot_error (VAS_EBOOT_ERR_BAD_SIGNATURE, N_("bad signature"));
goto fail;
}
VasEBoot_dprintf ("crypt", "time = %x\n", creation_time);
if (VasEBoot_file_read (f, &pk, sizeof (pk)) != sizeof (pk))
{
VasEBoot_error (VAS_EBOOT_ERR_BAD_SIGNATURE, N_("bad signature"));
goto fail;
}
VasEBoot_dprintf ("crypt", "pk = %x\n", pk);
if (pk >= ARRAY_SIZE (pkalgos) || pkalgos[pk].name == NULL)
{
VasEBoot_file_seek (f, pend);
continue;
}
sk = VasEBoot_zalloc (sizeof (struct VasEBoot_public_subkey));
if (!sk)
goto fail;
VasEBoot_memset (fingerprint_context, 0, VAS_EBOOT_MD_SHA1->contextsize);
VAS_EBOOT_MD_SHA1->init (fingerprint_context, 0);
VAS_EBOOT_MD_SHA1->write (fingerprint_context, "\x99", 1);
len_be = VasEBoot_cpu_to_be16 (len);
VAS_EBOOT_MD_SHA1->write (fingerprint_context, &len_be, sizeof (len_be));
VAS_EBOOT_MD_SHA1->write (fingerprint_context, &v, sizeof (v));
VAS_EBOOT_MD_SHA1->write (fingerprint_context, &creation_time, sizeof (creation_time));
VAS_EBOOT_MD_SHA1->write (fingerprint_context, &pk, sizeof (pk));
for (i = 0; i < pkalgos[pk].nmpipub; i++)
{
VasEBoot_uint16_t l;
VasEBoot_size_t lb;
if (VasEBoot_file_read (f, &l, sizeof (l)) != sizeof (l))
{
VasEBoot_error (VAS_EBOOT_ERR_BAD_SIGNATURE, N_("bad signature"));
break;
}
lb = (VasEBoot_be_to_cpu16 (l) + VAS_EBOOT_CHAR_BIT - 1) / VAS_EBOOT_CHAR_BIT;
if (lb > READBUF_SIZE - sizeof (VasEBoot_uint16_t))
{
VasEBoot_error (VAS_EBOOT_ERR_BAD_SIGNATURE, N_("bad signature"));
break;
}
if (VasEBoot_file_read (f, buffer + sizeof (VasEBoot_uint16_t), lb) != (VasEBoot_ssize_t) lb)
{
VasEBoot_error (VAS_EBOOT_ERR_BAD_SIGNATURE, N_("bad signature"));
break;
}
VasEBoot_memcpy (buffer, &l, sizeof (l));
VAS_EBOOT_MD_SHA1->write (fingerprint_context, buffer, lb + sizeof (VasEBoot_uint16_t));
if (_gcry_mpi_scan (&sk->mpis[i], GCRYMPI_FMT_PGP,
buffer, lb + sizeof (VasEBoot_uint16_t), 0))
{
VasEBoot_error (VAS_EBOOT_ERR_BAD_SIGNATURE, N_("bad signature"));
break;
}
}
if (i < pkalgos[pk].nmpipub)
{
VasEBoot_free (sk);
goto fail;
}
VAS_EBOOT_MD_SHA1->final (fingerprint_context);
VasEBoot_memcpy (sk->fingerprint, VAS_EBOOT_MD_SHA1->read (fingerprint_context), 20);
*last = sk;
last = &sk->next;
VasEBoot_dprintf ("crypt", "actual pos: %x, expected: %x\n", (int)VasEBoot_file_tell (f), (int)pend);
VasEBoot_file_seek (f, pend);
}
fail:
free_pk (ret);
VasEBoot_free (fingerprint_context);
VasEBoot_free (buffer);
return NULL;
}
struct VasEBoot_public_key *VasEBoot_pk_trusted;
struct VasEBoot_public_subkey *
VasEBoot_crypto_pk_locate_subkey (VasEBoot_uint64_t keyid, struct VasEBoot_public_key *pkey)
{
struct VasEBoot_public_subkey *sk;
for (sk = pkey->subkeys; sk; sk = sk->next)
if (VasEBoot_memcmp (sk->fingerprint + 3, &keyid, 8) == 0)
return sk;
return 0;
}
struct VasEBoot_public_subkey *
VasEBoot_crypto_pk_locate_subkey_in_trustdb (VasEBoot_uint64_t keyid)
{
struct VasEBoot_public_key *pkey;
struct VasEBoot_public_subkey *sk;
for (pkey = VasEBoot_pk_trusted; pkey; pkey = pkey->next)
{
sk = VasEBoot_crypto_pk_locate_subkey (keyid, pkey);
if (sk)
return sk;
}
return 0;
}
struct VasEBoot_pubkey_context
{
VasEBoot_file_t sig;
struct signature_v4_header v4;
VasEBoot_uint8_t v;
const gcry_md_spec_t *hash;
void *hash_context;
};
static VasEBoot_err_t
VasEBoot_verify_signature_init (struct VasEBoot_pubkey_context *ctxt, VasEBoot_file_t sig)
{
VasEBoot_size_t len;
VasEBoot_uint8_t h;
VasEBoot_uint8_t t;
VasEBoot_uint8_t pk;
VasEBoot_err_t err;
VasEBoot_uint8_t type = 0;
VasEBoot_memset (ctxt, 0, sizeof (*ctxt));
err = read_packet_header (sig, &type, &len);
if (err)
return err;
if (type != 0x2)
return VasEBoot_error (VAS_EBOOT_ERR_BAD_SIGNATURE, N_("bad signature"));
if (VasEBoot_file_read (sig, &ctxt->v, sizeof (ctxt->v)) != sizeof (ctxt->v))
return VasEBoot_error (VAS_EBOOT_ERR_BAD_SIGNATURE, N_("bad signature"));
if (ctxt->v != 4)
return VasEBoot_error (VAS_EBOOT_ERR_BAD_SIGNATURE, N_("bad signature"));
if (VasEBoot_file_read (sig, &ctxt->v4, sizeof (ctxt->v4)) != sizeof (ctxt->v4))
return VasEBoot_error (VAS_EBOOT_ERR_BAD_SIGNATURE, N_("bad signature"));
h = ctxt->v4.hash;
t = ctxt->v4.type;
pk = ctxt->v4.pkeyalgo;
if (t != 0)
return VasEBoot_error (VAS_EBOOT_ERR_BAD_SIGNATURE, N_("bad signature"));
if (pk >= ARRAY_SIZE (pkalgos) || pkalgos[pk].name == NULL)
return VasEBoot_error (VAS_EBOOT_ERR_BAD_SIGNATURE, N_("bad signature"));
ctxt->hash = VasEBoot_crypto_lookup_md_by_algo (h);
if (!ctxt->hash)
return VasEBoot_error (VAS_EBOOT_ERR_BAD_SIGNATURE, "hash `%d' not loaded", h);
VasEBoot_dprintf ("crypt", "alive\n");
ctxt->hash_context = VasEBoot_zalloc (ctxt->hash->contextsize);
if (!ctxt->hash_context)
return VasEBoot_errno;
ctxt->hash->init (ctxt->hash_context, 0);
ctxt->sig = sig;
return VAS_EBOOT_ERR_NONE;
}
static VasEBoot_err_t
VasEBoot_pubkey_write (void *ctxt_, void *buf, VasEBoot_size_t size)
{
struct VasEBoot_pubkey_context *ctxt = ctxt_;
ctxt->hash->write (ctxt->hash_context, buf, size);
return VAS_EBOOT_ERR_NONE;
}
static VasEBoot_err_t
VasEBoot_verify_signature_real (struct VasEBoot_pubkey_context *ctxt,
struct VasEBoot_public_key *pkey)
{
gcry_mpi_t mpis[10];
VasEBoot_uint8_t pk = ctxt->v4.pkeyalgo;
VasEBoot_size_t i;
VasEBoot_uint8_t *readbuf = NULL;
unsigned char *hval;
VasEBoot_ssize_t rem = VasEBoot_be_to_cpu16 (ctxt->v4.hashed_sub);
VasEBoot_uint32_t headlen = VasEBoot_cpu_to_be32 (rem + 6);
VasEBoot_uint8_t s;
VasEBoot_uint16_t unhashed_sub;
VasEBoot_ssize_t r;
VasEBoot_uint8_t hash_start[2];
VasEBoot_uint64_t keyid = 0;
struct VasEBoot_public_subkey *sk;
readbuf = VasEBoot_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 = VasEBoot_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 = VasEBoot_file_read (ctxt->sig, &unhashed_sub, sizeof (unhashed_sub));
if (r != sizeof (unhashed_sub))
goto fail;
{
VasEBoot_uint8_t *ptr;
VasEBoot_uint32_t l;
rem = VasEBoot_be_to_cpu16 (unhashed_sub);
if (rem > READBUF_SIZE)
goto fail;
r = VasEBoot_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) << VAS_EBOOT_CHAR_BIT) | ptr[1]) + 192;
ptr += 2;
}
else
{
if (ptr + 5 >= readbuf + rem)
break;
l = VasEBoot_be_to_cpu32 (VasEBoot_get_unaligned32 (ptr + 1));
ptr += 5;
}
if (*ptr == 0x10 && l >= 8)
keyid = VasEBoot_get_unaligned64 (ptr + 1);
}
}
ctxt->hash->final (ctxt->hash_context);
VasEBoot_dprintf ("crypt", "alive\n");
hval = ctxt->hash->read (ctxt->hash_context);
if (VasEBoot_file_read (ctxt->sig, hash_start, sizeof (hash_start)) != sizeof (hash_start))
goto fail;
if (VasEBoot_memcmp (hval, hash_start, sizeof (hash_start)) != 0)
goto fail;
VasEBoot_dprintf ("crypt", "@ %x\n", (int)VasEBoot_file_tell (ctxt->sig));
for (i = 0; i < pkalgos[pk].nmpisig; i++)
{
VasEBoot_uint16_t l;
VasEBoot_size_t lb;
VasEBoot_dprintf ("crypt", "alive\n");
if (VasEBoot_file_read (ctxt->sig, &l, sizeof (l)) != sizeof (l))
goto fail;
VasEBoot_dprintf ("crypt", "alive\n");
lb = (VasEBoot_be_to_cpu16 (l) + 7) / 8;
VasEBoot_dprintf ("crypt", "l = 0x%04x\n", VasEBoot_be_to_cpu16 (l));
if (lb > READBUF_SIZE - sizeof (VasEBoot_uint16_t))
goto fail;
VasEBoot_dprintf ("crypt", "alive\n");
if (VasEBoot_file_read (ctxt->sig, readbuf + sizeof (VasEBoot_uint16_t), lb) != (VasEBoot_ssize_t) lb)
goto fail;
VasEBoot_dprintf ("crypt", "alive\n");
VasEBoot_memcpy (readbuf, &l, sizeof (l));
VasEBoot_dprintf ("crypt", "alive\n");
if (_gcry_mpi_scan (&mpis[i], GCRYMPI_FMT_PGP,
readbuf, lb + sizeof (VasEBoot_uint16_t), 0))
goto fail;
VasEBoot_dprintf ("crypt", "alive\n");
}
if (pkey)
sk = VasEBoot_crypto_pk_locate_subkey (keyid, pkey);
else
sk = VasEBoot_crypto_pk_locate_subkey_in_trustdb (keyid);
if (!sk)
{
/* TRANSLATORS: %08x is 32-bit key id. */
VasEBoot_error (VAS_EBOOT_ERR_BAD_SIGNATURE,
N_("public key %08" PRIxVAS_EBOOT_UINT64_T " not found"), keyid);
goto fail;
}
if (!*pkalgos[pk].algo)
{
VasEBoot_dl_load (pkalgos[pk].module);
VasEBoot_errno = VAS_EBOOT_ERR_NONE;
}
if (!*pkalgos[pk].algo)
{
VasEBoot_error (VAS_EBOOT_ERR_BAD_SIGNATURE, N_("module `%s' isn't loaded"),
pkalgos[pk].module);
goto fail;
}
gcry_sexp_t hsexp, pubkey, sig;
VasEBoot_size_t errof;
if(_gcry_sexp_build(&hsexp, &errof, "(data (flags %s) (hash %s %b))", pkalgos[pk].padding, ctxt->hash->name, ctxt->hash->mdlen, hval))
goto fail;
if(_gcry_sexp_build(&pubkey, &errof, pkalgos[pk].pubsexp, sk->mpis[0], sk->mpis[1], sk->mpis[2], sk->mpis[3]))
goto fail;
if(_gcry_sexp_build(&sig, &errof, pkalgos[pk].sigsexp, mpis[0], mpis[1]))
goto fail;
_gcry_sexp_dump(sig);
_gcry_sexp_dump(hsexp);
_gcry_sexp_dump(pubkey);
if ((*pkalgos[pk].algo)->verify (sig, hsexp, pubkey))
goto fail;
VasEBoot_free (readbuf);
return VAS_EBOOT_ERR_NONE;
fail:
VasEBoot_free (readbuf);
if (!VasEBoot_errno)
return VasEBoot_error (VAS_EBOOT_ERR_BAD_SIGNATURE, N_("bad signature"));
return VasEBoot_errno;
}
static void
VasEBoot_pubkey_close_real (struct VasEBoot_pubkey_context *ctxt)
{
if (ctxt->sig)
VasEBoot_file_close (ctxt->sig);
if (ctxt->hash_context)
VasEBoot_free (ctxt->hash_context);
}
static void
VasEBoot_pubkey_close (void *ctxt)
{
VasEBoot_pubkey_close_real (ctxt);
VasEBoot_free (ctxt);
}
VasEBoot_err_t
VasEBoot_verify_signature (VasEBoot_file_t f, const char *fsig,
struct VasEBoot_public_key *pkey)
{
VasEBoot_file_t sig;
VasEBoot_err_t err;
struct VasEBoot_pubkey_context ctxt;
VasEBoot_uint8_t *readbuf = NULL;
sig = VasEBoot_file_open (fsig,
VAS_EBOOT_FILE_TYPE_SIGNATURE
| VAS_EBOOT_FILE_TYPE_NO_DECOMPRESS);
if (!sig)
return VasEBoot_errno;
err = VasEBoot_verify_signature_init (&ctxt, sig);
if (err)
{
VasEBoot_file_close (sig);
return err;
}
readbuf = VasEBoot_zalloc (READBUF_SIZE);
if (!readbuf)
goto fail;
while (1)
{
VasEBoot_ssize_t r;
r = VasEBoot_file_read (f, readbuf, READBUF_SIZE);
if (r < 0)
goto fail;
if (r == 0)
break;
err = VasEBoot_pubkey_write (&ctxt, readbuf, r);
if (err)
return err;
}
VasEBoot_verify_signature_real (&ctxt, pkey);
fail:
VasEBoot_pubkey_close_real (&ctxt);
return VasEBoot_errno;
}
static VasEBoot_err_t
VasEBoot_cmd_trust (VasEBoot_extcmd_context_t ctxt,
int argc, char **args)
{
VasEBoot_file_t pkf;
struct VasEBoot_public_key *pk = NULL;
if (argc < 1)
return VasEBoot_error (VAS_EBOOT_ERR_BAD_ARGUMENT, N_("one argument expected"));
pkf = VasEBoot_file_open (args[0],
VAS_EBOOT_FILE_TYPE_PUBLIC_KEY_TRUST
| VAS_EBOOT_FILE_TYPE_NO_DECOMPRESS
| (ctxt->state[OPTION_SKIP_SIG].set
? VAS_EBOOT_FILE_TYPE_SKIP_SIGNATURE
: VAS_EBOOT_FILE_TYPE_NONE));
if (!pkf)
return VasEBoot_errno;
pk = VasEBoot_load_public_key (pkf);
if (!pk)
{
VasEBoot_file_close (pkf);
return VasEBoot_errno;
}
VasEBoot_file_close (pkf);
pk->next = VasEBoot_pk_trusted;
VasEBoot_pk_trusted = pk;
return VAS_EBOOT_ERR_NONE;
}
static VasEBoot_err_t
VasEBoot_cmd_list (VasEBoot_command_t cmd __attribute__ ((unused)),
int argc __attribute__ ((unused)),
char **args __attribute__ ((unused)))
{
struct VasEBoot_public_key *pk = NULL;
struct VasEBoot_public_subkey *sk = NULL;
for (pk = VasEBoot_pk_trusted; pk; pk = pk->next)
for (sk = pk->subkeys; sk; sk = sk->next)
{
unsigned i;
for (i = 0; i < 20; i += 2)
VasEBoot_printf ("%02x%02x ", ((VasEBoot_uint8_t *) sk->fingerprint)[i],
((VasEBoot_uint8_t *) sk->fingerprint)[i + 1]);
VasEBoot_printf ("\n");
}
return VAS_EBOOT_ERR_NONE;
}
static VasEBoot_err_t
VasEBoot_cmd_distrust (VasEBoot_command_t cmd __attribute__ ((unused)),
int argc, char **args)
{
VasEBoot_uint32_t keyid, keyid_be;
struct VasEBoot_public_key **pkey;
struct VasEBoot_public_subkey *sk;
if (argc < 1)
return VasEBoot_error (VAS_EBOOT_ERR_BAD_ARGUMENT, N_("one argument expected"));
keyid = VasEBoot_strtoull (args[0], 0, 16);
if (VasEBoot_errno)
return VasEBoot_errno;
keyid_be = VasEBoot_cpu_to_be32 (keyid);
for (pkey = &VasEBoot_pk_trusted; *pkey; pkey = &((*pkey)->next))
{
struct VasEBoot_public_key *next;
for (sk = (*pkey)->subkeys; sk; sk = sk->next)
if (VasEBoot_memcmp (sk->fingerprint + 4, &keyid_be, 4) == 0)
break;
if (!sk)
continue;
next = (*pkey)->next;
free_pk (*pkey);
*pkey = next;
return VAS_EBOOT_ERR_NONE;
}
/* TRANSLATORS: %08x is 32-bit key id. */
return VasEBoot_error (VAS_EBOOT_ERR_BAD_SIGNATURE, N_("public key %08x not found"), keyid);
}
static VasEBoot_err_t
VasEBoot_cmd_verify_signature (VasEBoot_extcmd_context_t ctxt,
int argc, char **args)
{
VasEBoot_file_t f = NULL;
VasEBoot_err_t err = VAS_EBOOT_ERR_NONE;
struct VasEBoot_public_key *pk = NULL;
VasEBoot_dprintf ("crypt", "alive\n");
if (argc < 2)
return VasEBoot_error (VAS_EBOOT_ERR_BAD_ARGUMENT, N_("two arguments expected"));
VasEBoot_dprintf ("crypt", "alive\n");
if (argc > 2)
{
VasEBoot_file_t pkf;
pkf = VasEBoot_file_open (args[2],
VAS_EBOOT_FILE_TYPE_PUBLIC_KEY
| VAS_EBOOT_FILE_TYPE_NO_DECOMPRESS
| (ctxt->state[OPTION_SKIP_SIG].set
? VAS_EBOOT_FILE_TYPE_SKIP_SIGNATURE
: VAS_EBOOT_FILE_TYPE_NONE));
if (!pkf)
return VasEBoot_errno;
pk = VasEBoot_load_public_key (pkf);
if (!pk)
{
VasEBoot_file_close (pkf);
return VasEBoot_errno;
}
VasEBoot_file_close (pkf);
}
f = VasEBoot_file_open (args[0], VAS_EBOOT_FILE_TYPE_VERIFY_SIGNATURE);
if (!f)
{
err = VasEBoot_errno;
goto fail;
}
err = VasEBoot_verify_signature (f, args[1], pk);
fail:
if (f)
VasEBoot_file_close (f);
if (pk)
free_pk (pk);
return err;
}
static int sec = 0;
static VasEBoot_err_t
VasEBoot_pubkey_init (VasEBoot_file_t io, enum VasEBoot_file_type type __attribute__ ((unused)),
void **context, enum VasEBoot_verify_flags *flags)
{
VasEBoot_file_t sig;
char *fsuf, *ptr;
VasEBoot_err_t err;
struct VasEBoot_pubkey_context *ctxt;
if (!sec)
{
*flags = VAS_EBOOT_VERIFY_FLAGS_SKIP_VERIFICATION;
return VAS_EBOOT_ERR_NONE;
}
fsuf = VasEBoot_malloc (VasEBoot_strlen (io->name) + sizeof (".sig"));
if (!fsuf)
return VasEBoot_errno;
ptr = VasEBoot_stpcpy (fsuf, io->name);
VasEBoot_memcpy (ptr, ".sig", sizeof (".sig"));
sig = VasEBoot_file_open (fsuf, VAS_EBOOT_FILE_TYPE_SIGNATURE);
VasEBoot_free (fsuf);
if (!sig)
return VasEBoot_errno;
ctxt = VasEBoot_malloc (sizeof (*ctxt));
if (!ctxt)
{
VasEBoot_file_close (sig);
return VasEBoot_errno;
}
err = VasEBoot_verify_signature_init (ctxt, sig);
if (err)
{
VasEBoot_free (ctxt);
VasEBoot_file_close (sig);
return err;
}
*context = ctxt;
return VAS_EBOOT_ERR_NONE;
}
static VasEBoot_err_t
VasEBoot_pubkey_fini (void *ctxt)
{
return VasEBoot_verify_signature_real (ctxt, NULL);
}
static char *
VasEBoot_env_write_sec (struct VasEBoot_env_var *var __attribute__ ((unused)),
const char *val)
{
sec = (*val == '1') || (*val == 'e');
return VasEBoot_strdup (sec ? "enforce" : "no");
}
static VasEBoot_ssize_t
pseudo_read (struct VasEBoot_file *file, char *buf, VasEBoot_size_t len)
{
VasEBoot_memcpy (buf, (VasEBoot_uint8_t *) file->data + file->offset, len);
return len;
}
/* Filesystem descriptor. */
struct VasEBoot_fs pseudo_fs =
{
.name = "pseudo",
.fs_read = pseudo_read
};
struct VasEBoot_file_verifier VasEBoot_pubkey_verifier =
{
.name = "pgp",
.init = VasEBoot_pubkey_init,
.fini = VasEBoot_pubkey_fini,
.write = VasEBoot_pubkey_write,
.close = VasEBoot_pubkey_close,
};
static VasEBoot_extcmd_t cmd, cmd_trust;
static VasEBoot_command_t cmd_distrust, cmd_list;
VAS_EBOOT_MOD_INIT(pgp)
{
const char *val;
struct VasEBoot_module_header *header;
val = VasEBoot_env_get ("check_signatures");
if (val && (val[0] == '1' || val[0] == 'e'))
sec = 1;
else
sec = 0;
VasEBoot_register_variable_hook ("check_signatures", 0, VasEBoot_env_write_sec);
VasEBoot_env_export ("check_signatures");
VasEBoot_pk_trusted = 0;
FOR_MODULES (header)
{
struct VasEBoot_file pseudo_file;
struct VasEBoot_public_key *pk = NULL;
VasEBoot_memset (&pseudo_file, 0, sizeof (pseudo_file));
/* Not an ELF module, skip. */
if (header->type != OBJ_TYPE_GPG_PUBKEY)
continue;
pseudo_file.fs = &pseudo_fs;
pseudo_file.size = (header->size - sizeof (struct VasEBoot_module_header));
pseudo_file.data = (char *) header + sizeof (struct VasEBoot_module_header);
pk = VasEBoot_load_public_key (&pseudo_file);
if (!pk)
VasEBoot_fatal ("error loading initial key: %s\n", VasEBoot_errmsg);
pk->next = VasEBoot_pk_trusted;
VasEBoot_pk_trusted = pk;
}
if (!val)
VasEBoot_env_set ("check_signatures", VasEBoot_pk_trusted ? "enforce" : "no");
cmd = VasEBoot_register_extcmd ("verify_detached", VasEBoot_cmd_verify_signature, 0,
N_("[-s|--skip-sig] FILE SIGNATURE_FILE [PUBKEY_FILE]"),
N_("Verify detached signature."),
options);
cmd_trust = VasEBoot_register_extcmd ("trust", VasEBoot_cmd_trust, 0,
N_("[-s|--skip-sig] PUBKEY_FILE"),
N_("Add PUBKEY_FILE to trusted keys."),
options);
cmd_list = VasEBoot_register_command ("list_trusted", VasEBoot_cmd_list,
0,
N_("Show the list of trusted keys."));
cmd_distrust = VasEBoot_register_command ("distrust", VasEBoot_cmd_distrust,
N_("PUBKEY_ID"),
N_("Remove PUBKEY_ID from trusted keys."));
VasEBoot_verifier_register (&VasEBoot_pubkey_verifier);
}
VAS_EBOOT_MOD_FINI(pgp)
{
VasEBoot_register_variable_hook ("check_signatures", NULL, NULL);
VasEBoot_env_unset ("check_signatures");
VasEBoot_verifier_unregister (&VasEBoot_pubkey_verifier);
VasEBoot_unregister_extcmd (cmd);
VasEBoot_unregister_extcmd (cmd_trust);
VasEBoot_unregister_command (cmd_list);
VasEBoot_unregister_command (cmd_distrust);
}