/* * VasEBoot -- GRand Unified Bootloader * Copyright (C) 2013 Free Software Foundation, Inc. * * VasEBoot 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. * * VasEBoot 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 VasEBoot. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include VasEBoot_MOD_LICENSE ("GPLv3+"); struct VasEBoot_verified { VasEBoot_file_t file; void *buf; }; typedef struct VasEBoot_verified *VasEBoot_verified_t; 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_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", .read = pseudo_read }; 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 (VasEBoot_ERR_BAD_SIGNATURE, N_("bad signature")); } if (type == 0) { *out_type = 0xfe; return 0; } if (!(type & 0x80)) return VasEBoot_error (VasEBoot_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 (VasEBoot_ERR_BAD_SIGNATURE, N_("bad signature")); if (l < 192) { *len = l; return 0; } if (l < 224) { *len = (l - 192) << VasEBoot_CHAR_BIT; if (VasEBoot_file_read (sig, &l, sizeof (l)) != 1) return VasEBoot_error (VasEBoot_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 (VasEBoot_ERR_BAD_SIGNATURE, N_("bad signature")); *len = VasEBoot_be_to_cpu32 (l32); return 0; } return VasEBoot_error (VasEBoot_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 (VasEBoot_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 (VasEBoot_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 (VasEBoot_ERR_BAD_SIGNATURE, N_("bad signature")); *len = VasEBoot_be_to_cpu32 (l32); return 0; } return VasEBoot_error (VasEBoot_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; } VasEBoot_PACKED; const char *hashes[] = { [0x01] = "md5", [0x02] = "sha1", [0x03] = "ripemd160", [0x08] = "sha256", [0x09] = "sha384", [0x0a] = "sha512", [0x0b] = "sha224" }; struct gcry_pk_spec *VasEBoot_crypto_pk_dsa; struct gcry_pk_spec *VasEBoot_crypto_pk_ecdsa; struct gcry_pk_spec *VasEBoot_crypto_pk_rsa; static int dsa_pad (gcry_mpi_t *hmpi, VasEBoot_uint8_t *hval, const gcry_md_spec_t *hash, struct VasEBoot_public_subkey *sk); static int rsa_pad (gcry_mpi_t *hmpi, VasEBoot_uint8_t *hval, const gcry_md_spec_t *hash, struct VasEBoot_public_subkey *sk); struct { const char *name; VasEBoot_size_t nmpisig; VasEBoot_size_t nmpipub; struct gcry_pk_spec **algo; int (*pad) (gcry_mpi_t *hmpi, VasEBoot_uint8_t *hval, const gcry_md_spec_t *hash, struct VasEBoot_public_subkey *sk); const char *module; } pkalgos[] = { [1] = { "rsa", 1, 2, &VasEBoot_crypto_pk_rsa, rsa_pad, "gcry_rsa" }, [3] = { "rsa", 1, 2, &VasEBoot_crypto_pk_rsa, rsa_pad, "gcry_rsa" }, [17] = { "dsa", 2, 4, &VasEBoot_crypto_pk_dsa, dsa_pad, "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 (VasEBoot_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 (VasEBoot_ERR_BAD_SIGNATURE, N_("bad signature")); goto fail; } VasEBoot_dprintf ("crypt", "v = %x\n", v); if (v != 4) { VasEBoot_error (VasEBoot_ERR_BAD_SIGNATURE, N_("bad signature")); goto fail; } if (VasEBoot_file_read (f, &creation_time, sizeof (creation_time)) != sizeof (creation_time)) { VasEBoot_error (VasEBoot_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 (VasEBoot_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, VasEBoot_MD_SHA1->contextsize); VasEBoot_MD_SHA1->init (fingerprint_context); VasEBoot_MD_SHA1->write (fingerprint_context, "\x99", 1); len_be = VasEBoot_cpu_to_be16 (len); VasEBoot_MD_SHA1->write (fingerprint_context, &len_be, sizeof (len_be)); VasEBoot_MD_SHA1->write (fingerprint_context, &v, sizeof (v)); VasEBoot_MD_SHA1->write (fingerprint_context, &creation_time, sizeof (creation_time)); VasEBoot_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 (VasEBoot_ERR_BAD_SIGNATURE, N_("bad signature")); break; } lb = (VasEBoot_be_to_cpu16 (l) + VasEBoot_CHAR_BIT - 1) / VasEBoot_CHAR_BIT; if (lb > READBUF_SIZE - sizeof (VasEBoot_uint16_t)) { VasEBoot_error (VasEBoot_ERR_BAD_SIGNATURE, N_("bad signature")); break; } if (VasEBoot_file_read (f, buffer + sizeof (VasEBoot_uint16_t), lb) != (VasEBoot_ssize_t) lb) { VasEBoot_error (VasEBoot_ERR_BAD_SIGNATURE, N_("bad signature")); break; } VasEBoot_memcpy (buffer, &l, sizeof (l)); VasEBoot_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 (VasEBoot_ERR_BAD_SIGNATURE, N_("bad signature")); break; } } if (i < pkalgos[pk].nmpipub) { VasEBoot_free (sk); goto fail; } VasEBoot_MD_SHA1->final (fingerprint_context); VasEBoot_memcpy (sk->fingerprint, VasEBoot_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; } static int dsa_pad (gcry_mpi_t *hmpi, VasEBoot_uint8_t *hval, const gcry_md_spec_t *hash, struct VasEBoot_public_subkey *sk) { unsigned nbits = gcry_mpi_get_nbits (sk->mpis[1]); VasEBoot_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, VasEBoot_uint8_t *hval, const gcry_md_spec_t *hash, struct VasEBoot_public_subkey *sk) { VasEBoot_size_t tlen, emlen, fflen; VasEBoot_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 = VasEBoot_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; VasEBoot_memcpy (emptr, hash->asnoid, hash->asnlen); emptr += hash->asnlen; VasEBoot_memcpy (emptr, hval, hash->mdlen); ret = gcry_mpi_scan (hmpi, GCRYMPI_FMT_USG, em, emlen, 0); VasEBoot_free (em); return ret; } static VasEBoot_err_t VasEBoot_verify_signature_real (char *buf, VasEBoot_size_t size, VasEBoot_file_t f, VasEBoot_file_t sig, struct VasEBoot_public_key *pkey) { VasEBoot_size_t len; VasEBoot_uint8_t v; VasEBoot_uint8_t h; VasEBoot_uint8_t t; VasEBoot_uint8_t pk; const gcry_md_spec_t *hash; struct signature_v4_header v4; VasEBoot_err_t err; VasEBoot_size_t i; gcry_mpi_t mpis[10]; VasEBoot_uint8_t type = 0; err = read_packet_header (sig, &type, &len); if (err) return err; if (type != 0x2) return VasEBoot_error (VasEBoot_ERR_BAD_SIGNATURE, N_("bad signature")); if (VasEBoot_file_read (sig, &v, sizeof (v)) != sizeof (v)) return VasEBoot_error (VasEBoot_ERR_BAD_SIGNATURE, N_("bad signature")); if (v != 4) return VasEBoot_error (VasEBoot_ERR_BAD_SIGNATURE, N_("bad signature")); if (VasEBoot_file_read (sig, &v4, sizeof (v4)) != sizeof (v4)) return VasEBoot_error (VasEBoot_ERR_BAD_SIGNATURE, N_("bad signature")); h = v4.hash; t = v4.type; pk = v4.pkeyalgo; if (t != 0) return VasEBoot_error (VasEBoot_ERR_BAD_SIGNATURE, N_("bad signature")); if (h >= ARRAY_SIZE (hashes) || hashes[h] == NULL) return VasEBoot_error (VasEBoot_ERR_BAD_SIGNATURE, "unknown hash"); if (pk >= ARRAY_SIZE (pkalgos) || pkalgos[pk].name == NULL) return VasEBoot_error (VasEBoot_ERR_BAD_SIGNATURE, N_("bad signature")); hash = VasEBoot_crypto_lookup_md_by_name (hashes[h]); if (!hash) return VasEBoot_error (VasEBoot_ERR_BAD_SIGNATURE, "hash `%s' not loaded", hashes[h]); VasEBoot_dprintf ("crypt", "alive\n"); { void *context = NULL; unsigned char *hval; VasEBoot_ssize_t rem = VasEBoot_be_to_cpu16 (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]; gcry_mpi_t hmpi; VasEBoot_uint64_t keyid = 0; struct VasEBoot_public_subkey *sk; VasEBoot_uint8_t *readbuf = NULL; context = VasEBoot_zalloc (hash->contextsize); readbuf = VasEBoot_zalloc (READBUF_SIZE); if (!context || !readbuf) goto fail; hash->init (context); if (buf) hash->write (context, buf, size); else while (1) { r = VasEBoot_file_read (f, readbuf, READBUF_SIZE); if (r < 0) goto fail; if (r == 0) break; hash->write (context, readbuf, r); } hash->write (context, &v, sizeof (v)); hash->write (context, &v4, sizeof (v4)); while (rem) { r = VasEBoot_file_read (sig, readbuf, rem < READBUF_SIZE ? rem : READBUF_SIZE); if (r < 0) goto fail; if (r == 0) break; hash->write (context, readbuf, r); rem -= r; } hash->write (context, &v, sizeof (v)); s = 0xff; hash->write (context, &s, sizeof (s)); hash->write (context, &headlen, sizeof (headlen)); r = VasEBoot_file_read (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 (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) << VasEBoot_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); } } hash->final (context); VasEBoot_dprintf ("crypt", "alive\n"); hval = hash->read (context); if (VasEBoot_file_read (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 (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 (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 (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 (VasEBoot_ERR_BAD_SIGNATURE, N_("public key %08x not found"), keyid); goto fail; } if (pkalgos[pk].pad (&hmpi, hval, hash, sk)) goto fail; if (!*pkalgos[pk].algo) { VasEBoot_dl_load (pkalgos[pk].module); VasEBoot_errno = VasEBoot_ERR_NONE; } if (!*pkalgos[pk].algo) { VasEBoot_error (VasEBoot_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; VasEBoot_free (context); VasEBoot_free (readbuf); return VasEBoot_ERR_NONE; fail: VasEBoot_free (context); VasEBoot_free (readbuf); if (!VasEBoot_errno) return VasEBoot_error (VasEBoot_ERR_BAD_SIGNATURE, N_("bad signature")); return VasEBoot_errno; } } VasEBoot_err_t VasEBoot_verify_signature (VasEBoot_file_t f, VasEBoot_file_t sig, struct VasEBoot_public_key *pkey) { return VasEBoot_verify_signature_real (0, 0, f, sig, pkey); } 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 (VasEBoot_ERR_BAD_ARGUMENT, N_("one argument expected")); VasEBoot_file_filter_disable_compression (); if (ctxt->state[OPTION_SKIP_SIG].set) VasEBoot_file_filter_disable_pubkey (); pkf = VasEBoot_file_open (args[0]); 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 VasEBoot_ERR_NONE; } static VasEBoot_err_t VasEBoot_cmd_trust_var (VasEBoot_command_t cmd __attribute__ ((unused)), int argc, char **args) { struct VasEBoot_file pseudo_file; const char *var; char *data; struct VasEBoot_public_key *pk = NULL; unsigned int i, idx0, idx1; if (argc < 1) return VasEBoot_error (VasEBoot_ERR_BAD_ARGUMENT, N_("one argument expected")); var = VasEBoot_env_get (args[0]); if (!var) return VasEBoot_error (VasEBoot_ERR_BAD_ARGUMENT, N_("unknown variable")); data = VasEBoot_zalloc (VasEBoot_strlen (var) / 2); if (!data) return VasEBoot_error (VasEBoot_ERR_OUT_OF_MEMORY, N_("can't allocate memory for key")); /* For the want of sscanf() */ for (i = 0; i < VasEBoot_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); } VasEBoot_memset (&pseudo_file, 0, sizeof (pseudo_file)); pseudo_file.fs = &pseudo_fs; pseudo_file.size = VasEBoot_strlen (var) / 2; pseudo_file.data = data; pk = VasEBoot_load_public_key (&pseudo_file); if (!pk) { VasEBoot_free(data); return VasEBoot_errno; } pk->next = VasEBoot_pk_trusted; VasEBoot_pk_trusted = pk; VasEBoot_free(data); return VasEBoot_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 VasEBoot_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 (VasEBoot_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 VasEBoot_ERR_NONE; } /* TRANSLATORS: %08x is 32-bit key id. */ return VasEBoot_error (VasEBoot_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, sig = NULL; VasEBoot_err_t err = VasEBoot_ERR_NONE; struct VasEBoot_public_key *pk = NULL; VasEBoot_dprintf ("crypt", "alive\n"); if (argc < 2) return VasEBoot_error (VasEBoot_ERR_BAD_ARGUMENT, N_("two arguments expected")); VasEBoot_dprintf ("crypt", "alive\n"); if (argc > 2) { VasEBoot_file_t pkf; VasEBoot_file_filter_disable_compression (); if (ctxt->state[OPTION_SKIP_SIG].set) VasEBoot_file_filter_disable_pubkey (); pkf = VasEBoot_file_open (args[2]); if (!pkf) return VasEBoot_errno; pk = VasEBoot_load_public_key (pkf); if (!pk) { VasEBoot_file_close (pkf); return VasEBoot_errno; } VasEBoot_file_close (pkf); } VasEBoot_file_filter_disable_all (); f = VasEBoot_file_open (args[0]); if (!f) { err = VasEBoot_errno; goto fail; } VasEBoot_file_filter_disable_all (); sig = VasEBoot_file_open (args[1]); if (!sig) { err = VasEBoot_errno; goto fail; } err = VasEBoot_verify_signature (f, sig, pk); fail: if (sig) VasEBoot_file_close (sig); if (f) VasEBoot_file_close (f); if (pk) free_pk (pk); return err; } static int sec = 0; static void verified_free (VasEBoot_verified_t verified) { if (verified) { VasEBoot_free (verified->buf); VasEBoot_free (verified); } } static VasEBoot_ssize_t verified_read (struct VasEBoot_file *file, char *buf, VasEBoot_size_t len) { VasEBoot_verified_t verified = file->data; VasEBoot_memcpy (buf, (char *) verified->buf + file->offset, len); return len; } static VasEBoot_err_t verified_close (struct VasEBoot_file *file) { VasEBoot_verified_t verified = file->data; VasEBoot_file_close (verified->file); verified_free (verified); file->data = 0; /* device and name are freed by parent */ file->device = 0; file->name = 0; return VasEBoot_errno; } struct VasEBoot_fs verified_fs = { .name = "verified_read", .read = verified_read, .close = verified_close }; static VasEBoot_file_t VasEBoot_pubkey_open (VasEBoot_file_t io, const char *filename) { VasEBoot_file_t sig; char *fsuf, *ptr; VasEBoot_err_t err; VasEBoot_file_filter_t curfilt[VasEBoot_FILE_FILTER_MAX]; VasEBoot_file_t ret; VasEBoot_verified_t verified; if (!sec) return io; if (io->device->disk && (io->device->disk->dev->id == VasEBoot_DISK_DEVICE_MEMDISK_ID || io->device->disk->dev->id == VasEBoot_DISK_DEVICE_PROCFS_ID)) return io; fsuf = VasEBoot_malloc (VasEBoot_strlen (filename) + sizeof (".sig")); if (!fsuf) return NULL; ptr = VasEBoot_stpcpy (fsuf, filename); VasEBoot_memcpy (ptr, ".sig", sizeof (".sig")); VasEBoot_memcpy (curfilt, VasEBoot_file_filters_enabled, sizeof (curfilt)); VasEBoot_file_filter_disable_all (); sig = VasEBoot_file_open (fsuf); VasEBoot_memcpy (VasEBoot_file_filters_enabled, curfilt, sizeof (curfilt)); VasEBoot_free (fsuf); if (!sig) return NULL; ret = VasEBoot_malloc (sizeof (*ret)); if (!ret) { VasEBoot_file_close (sig); return NULL; } *ret = *io; ret->fs = &verified_fs; ret->not_easily_seekable = 0; if (ret->size >> (sizeof (VasEBoot_size_t) * VasEBoot_CHAR_BIT - 1)) { VasEBoot_error (VasEBoot_ERR_NOT_IMPLEMENTED_YET, "big file signature isn't implemented yet"); VasEBoot_file_close (sig); VasEBoot_free (ret); return NULL; } verified = VasEBoot_malloc (sizeof (*verified)); if (!verified) { VasEBoot_file_close (sig); VasEBoot_free (ret); return NULL; } verified->buf = VasEBoot_malloc (ret->size); if (!verified->buf) { VasEBoot_file_close (sig); VasEBoot_free (verified); VasEBoot_free (ret); return NULL; } if (VasEBoot_file_read (io, verified->buf, ret->size) != (VasEBoot_ssize_t) ret->size) { if (!VasEBoot_errno) VasEBoot_error (VasEBoot_ERR_FILE_READ_ERROR, N_("premature end of file %s"), filename); VasEBoot_file_close (sig); verified_free (verified); VasEBoot_free (ret); return NULL; } err = VasEBoot_verify_signature_real (verified->buf, ret->size, 0, sig, NULL); VasEBoot_file_close (sig); if (err) { verified_free (verified); VasEBoot_free (ret); return NULL; } verified->file = io; ret->data = verified; return ret; } 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_extcmd_t cmd, cmd_trust; static VasEBoot_command_t cmd_trust_var, cmd_distrust, cmd_list; VasEBoot_MOD_INIT(verify) { 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_file_filter_register (VasEBoot_FILE_FILTER_PUBKEY, VasEBoot_pubkey_open); 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_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_trust_var = VasEBoot_register_command ("trust_var", VasEBoot_cmd_trust_var, N_("PUBKEY_VAR"), N_("Add the contents of PUBKEY_VAR to trusted keys.")); 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_MOD_FINI(verify) { VasEBoot_file_filter_unregister (VasEBoot_FILE_FILTER_PUBKEY); VasEBoot_unregister_extcmd (cmd); VasEBoot_unregister_extcmd (cmd_trust); VasEBoot_unregister_command (cmd_trust_var); VasEBoot_unregister_command (cmd_list); VasEBoot_unregister_command (cmd_distrust); }