diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index 9590e87d9..9af31c098 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -894,6 +894,11 @@ module = { cppflags = '-I$(srcdir)/lib/posix_wrap'; }; +module = { + name = verifiers; + common = commands/verifiers.c; +}; + module = { name = hdparm; common = commands/hdparm.c; diff --git a/grub-core/commands/verifiers.c b/grub-core/commands/verifiers.c new file mode 100644 index 000000000..fde88318d --- /dev/null +++ b/grub-core/commands/verifiers.c @@ -0,0 +1,197 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2017 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 . + * + * Verifiers helper. + */ + +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +struct grub_file_verifier *grub_file_verifiers; + +struct grub_verified +{ + grub_file_t file; + void *buf; +}; +typedef struct grub_verified *grub_verified_t; + +static void +verified_free (grub_verified_t verified) +{ + if (verified) + { + grub_free (verified->buf); + grub_free (verified); + } +} + +static grub_ssize_t +verified_read (struct grub_file *file, char *buf, grub_size_t len) +{ + grub_verified_t verified = file->data; + + grub_memcpy (buf, (char *) verified->buf + file->offset, len); + return len; +} + +static grub_err_t +verified_close (struct grub_file *file) +{ + grub_verified_t verified = file->data; + + grub_file_close (verified->file); + verified_free (verified); + file->data = 0; + + /* Device and name are freed by parent. */ + file->device = 0; + file->name = 0; + + return grub_errno; +} + +struct grub_fs verified_fs = +{ + .name = "verified_read", + .read = verified_read, + .close = verified_close +}; + +static grub_file_t +grub_verifiers_open (grub_file_t io, enum grub_file_type type) +{ + grub_verified_t verified = NULL; + struct grub_file_verifier *ver; + void *context; + grub_file_t ret = 0; + grub_err_t err; + + grub_dprintf ("verify", "file: %s type: %d\n", io->name, type); + + if ((type & GRUB_FILE_TYPE_MASK) == GRUB_FILE_TYPE_SIGNATURE + || (type & GRUB_FILE_TYPE_MASK) == GRUB_FILE_TYPE_VERIFY_SIGNATURE + || (type & GRUB_FILE_TYPE_SKIP_SIGNATURE)) + return io; + + if (io->device->disk && + (io->device->disk->dev->id == GRUB_DISK_DEVICE_MEMDISK_ID + || io->device->disk->dev->id == GRUB_DISK_DEVICE_PROCFS_ID)) + return io; + + FOR_LIST_ELEMENTS(ver, grub_file_verifiers) + { + enum grub_verify_flags flags = 0; + err = ver->init (io, type, &context, &flags); + if (err) + goto fail_noclose; + if (!(flags & GRUB_VERIFY_FLAGS_SKIP_VERIFICATION)) + break; + } + + if (!ver) + /* No verifiers wanted to verify. Just return underlying file. */ + return io; + + ret = grub_malloc (sizeof (*ret)); + if (!ret) + { + goto fail; + } + *ret = *io; + + ret->fs = &verified_fs; + ret->not_easily_seekable = 0; + if (ret->size >> (sizeof (grub_size_t) * GRUB_CHAR_BIT - 1)) + { + grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + N_("big file signature isn't implemented yet")); + goto fail; + } + verified = grub_malloc (sizeof (*verified)); + if (!verified) + { + goto fail; + } + verified->buf = grub_malloc (ret->size); + if (!verified->buf) + { + goto fail; + } + if (grub_file_read (io, verified->buf, ret->size) != (grub_ssize_t) ret->size) + { + if (!grub_errno) + grub_error (GRUB_ERR_FILE_READ_ERROR, N_("premature end of file %s"), + io->name); + goto fail; + } + + err = ver->write (context, verified->buf, ret->size); + if (err) + goto fail; + + err = ver->fini ? ver->fini (context) : GRUB_ERR_NONE; + if (err) + goto fail; + + if (ver->close) + ver->close (context); + + FOR_LIST_ELEMENTS_NEXT(ver, grub_file_verifiers) + { + enum grub_verify_flags flags = 0; + err = ver->init (io, type, &context, &flags); + if (err) + goto fail_noclose; + if (flags & GRUB_VERIFY_FLAGS_SKIP_VERIFICATION) + continue; + err = ver->write (context, verified->buf, ret->size); + if (err) + goto fail; + + err = ver->fini ? ver->fini (context) : GRUB_ERR_NONE; + if (err) + goto fail; + + if (ver->close) + ver->close (context); + } + + verified->file = io; + ret->data = verified; + return ret; + + fail: + ver->close (context); + fail_noclose: + verified_free (verified); + grub_free (ret); + return NULL; +} + +GRUB_MOD_INIT(verifiers) +{ + grub_file_filter_register (GRUB_FILE_FILTER_VERIFY, grub_verifiers_open); +} + +GRUB_MOD_FINI(verifiers) +{ + grub_file_filter_unregister (GRUB_FILE_FILTER_VERIFY); +} diff --git a/grub-core/commands/verify.c b/grub-core/commands/verify.c index f0dfeceeb..29e74a640 100644 --- a/grub-core/commands/verify.c +++ b/grub-core/commands/verify.c @@ -30,16 +30,10 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); -struct grub_verified -{ - grub_file_t file; - void *buf; -}; -typedef struct grub_verified *grub_verified_t; - enum { OPTION_SKIP_SIG = 0 @@ -445,23 +439,27 @@ rsa_pad (gcry_mpi_t *hmpi, grub_uint8_t *hval, 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_real (char *buf, grub_size_t size, - grub_file_t f, grub_file_t sig, - struct grub_public_key *pkey) +grub_verify_signature_init (struct grub_pubkey_context *ctxt, grub_file_t sig) { grub_size_t len; - grub_uint8_t v; grub_uint8_t h; grub_uint8_t t; grub_uint8_t pk; - const gcry_md_spec_t *hash; - struct signature_v4_header v4; grub_err_t err; - grub_size_t i; - gcry_mpi_t mpis[10]; grub_uint8_t type = 0; + grub_memset (ctxt, 0, sizeof (*ctxt)); + err = read_packet_header (sig, &type, &len); if (err) return err; @@ -469,18 +467,18 @@ grub_verify_signature_real (char *buf, grub_size_t size, if (type != 0x2) return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); - if (grub_file_read (sig, &v, sizeof (v)) != sizeof (v)) + if (grub_file_read (sig, &ctxt->v, sizeof (ctxt->v)) != sizeof (ctxt->v)) return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); - if (v != 4) + if (ctxt->v != 4) return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); - if (grub_file_read (sig, &v4, sizeof (v4)) != sizeof (v4)) + if (grub_file_read (sig, &ctxt->v4, sizeof (ctxt->v4)) != sizeof (ctxt->v4)) return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); - h = v4.hash; - t = v4.type; - pk = v4.pkeyalgo; + 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")); @@ -491,183 +489,233 @@ grub_verify_signature_real (char *buf, grub_size_t size, if (pk >= ARRAY_SIZE (pkalgos) || pkalgos[pk].name == NULL) return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); - hash = grub_crypto_lookup_md_by_name (hashes[h]); - if (!hash) + 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"); - { - void *context = NULL; - unsigned char *hval; - grub_ssize_t rem = grub_be_to_cpu16 (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; - grub_uint8_t *readbuf = NULL; + ctxt->sig = sig; - context = grub_zalloc (hash->contextsize); - readbuf = grub_zalloc (READBUF_SIZE); - if (!context || !readbuf) - goto fail; + ctxt->hash_context = grub_zalloc (ctxt->hash->contextsize); + if (!ctxt->hash_context) + return grub_errno; - hash->init (context); - if (buf) - hash->write (context, buf, size); - else - while (1) - { - r = grub_file_read (f, readbuf, READBUF_SIZE); - if (r < 0) - goto fail; - if (r == 0) - break; - hash->write (context, readbuf, r); - } + ctxt->hash->init (ctxt->hash_context); - hash->write (context, &v, sizeof (v)); - hash->write (context, &v4, sizeof (v4)); - while (rem) - { - r = grub_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 = grub_file_read (sig, &unhashed_sub, sizeof (unhashed_sub)); - if (r != sizeof (unhashed_sub)) - goto fail; + 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) { - grub_uint8_t *ptr; - grub_uint32_t l; - rem = grub_be_to_cpu16 (unhashed_sub); - if (rem > READBUF_SIZE) + r = grub_file_read (ctxt->sig, readbuf, + rem < READBUF_SIZE ? rem : READBUF_SIZE); + if (r < 0) goto fail; - r = grub_file_read (sig, readbuf, rem); - if (r != rem) + 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; - 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); - } + 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"); } - hash->final (context); - - grub_dprintf ("crypt", "alive\n"); - - hval = hash->read (context); - - if (grub_file_read (sig, hash_start, sizeof (hash_start)) != sizeof (hash_start)) + 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 (grub_memcmp (hval, hash_start, sizeof (hash_start)) != 0) + } + + 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_dprintf ("crypt", "@ %x\n", (int)grub_file_tell (sig)); + grub_free (readbuf); - 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 (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 (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"); + return GRUB_ERR_NONE; - if (gcry_mpi_scan (&mpis[i], GCRYMPI_FMT_PGP, - readbuf, lb + sizeof (grub_uint16_t), 0)) - goto fail; - grub_dprintf ("crypt", "alive\n"); - } + fail: + grub_free (readbuf); + if (!grub_errno) + return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); + return grub_errno; +} - 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; - } +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); +} - if (pkalgos[pk].pad (&hmpi, hval, 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 (context); - grub_free (readbuf); - - return GRUB_ERR_NONE; - - fail: - grub_free (context); - grub_free (readbuf); - if (!grub_errno) - return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); - return grub_errno; - } +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) { - return grub_verify_signature_real (0, 0, f, sig, 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 @@ -819,134 +867,52 @@ grub_cmd_verify_signature (grub_extcmd_context_t ctxt, static int sec = 0; -static void -verified_free (grub_verified_t verified) -{ - if (verified) - { - grub_free (verified->buf); - grub_free (verified); - } -} - -static grub_ssize_t -verified_read (struct grub_file *file, char *buf, grub_size_t len) -{ - grub_verified_t verified = file->data; - - grub_memcpy (buf, (char *) verified->buf + file->offset, len); - return len; -} - static grub_err_t -verified_close (struct grub_file *file) -{ - grub_verified_t verified = file->data; - - grub_file_close (verified->file); - verified_free (verified); - file->data = 0; - - /* device and name are freed by parent */ - file->device = 0; - file->name = 0; - - return grub_errno; -} - -struct grub_fs verified_fs = -{ - .name = "verified_read", - .read = verified_read, - .close = verified_close -}; - -static grub_file_t -grub_pubkey_open (grub_file_t io, enum grub_file_type type) +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; - grub_file_t ret; - grub_verified_t verified; - - if ((type & GRUB_FILE_TYPE_MASK) == GRUB_FILE_TYPE_SIGNATURE - || (type & GRUB_FILE_TYPE_MASK) == GRUB_FILE_TYPE_VERIFY_SIGNATURE - || (type & GRUB_FILE_TYPE_SKIP_SIGNATURE)) - return io; + struct grub_pubkey_context *ctxt; if (!sec) - return io; - if (io->device->disk && - (io->device->disk->dev->id == GRUB_DISK_DEVICE_MEMDISK_ID - || io->device->disk->dev->id == GRUB_DISK_DEVICE_PROCFS_ID)) - return io; + { + *flags = GRUB_VERIFY_FLAGS_SKIP_VERIFICATION; + return GRUB_ERR_NONE; + } + fsuf = grub_malloc (grub_strlen (io->name) + sizeof (".sig")); if (!fsuf) - return NULL; + 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 NULL; + return grub_errno; - ret = grub_malloc (sizeof (*ret)); - if (!ret) + ctxt = grub_malloc (sizeof (*ctxt)); + if (!ctxt) { grub_file_close (sig); - return NULL; + return grub_errno; } - *ret = *io; - - ret->fs = &verified_fs; - ret->not_easily_seekable = 0; - if (ret->size >> (sizeof (grub_size_t) * GRUB_CHAR_BIT - 1)) - { - grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, - "big file signature isn't implemented yet"); - grub_file_close (sig); - grub_free (ret); - return NULL; - } - verified = grub_malloc (sizeof (*verified)); - if (!verified) - { - grub_file_close (sig); - grub_free (ret); - return NULL; - } - verified->buf = grub_malloc (ret->size); - if (!verified->buf) - { - grub_file_close (sig); - verified_free (verified); - grub_free (ret); - return NULL; - } - if (grub_file_read (io, verified->buf, ret->size) != (grub_ssize_t) ret->size) - { - if (!grub_errno) - grub_error (GRUB_ERR_FILE_READ_ERROR, N_("premature end of file %s"), - io->name); - grub_file_close (sig); - verified_free (verified); - grub_free (ret); - return NULL; - } - - err = grub_verify_signature_real (verified->buf, ret->size, 0, sig, NULL); - grub_file_close (sig); + err = grub_verify_signature_init (ctxt, sig); if (err) { - verified_free (verified); - grub_free (ret); - return NULL; + grub_pubkey_close (ctxt); + return err; } - verified->file = io; - ret->data = verified; - return ret; + *context = ctxt; + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_pubkey_fini (void *ctxt) +{ + return grub_verify_signature_real (ctxt, NULL); } static char * @@ -970,8 +936,16 @@ 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, + }; static grub_extcmd_t cmd, cmd_trust; static grub_command_t cmd_distrust, cmd_list; @@ -986,8 +960,6 @@ GRUB_MOD_INIT(verify) sec = 1; else sec = 0; - - grub_file_filter_register (GRUB_FILE_FILTER_PUBKEY, grub_pubkey_open); grub_register_variable_hook ("check_signatures", 0, grub_env_write_sec); grub_env_export ("check_signatures"); @@ -1033,11 +1005,13 @@ GRUB_MOD_INIT(verify) 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_file_filter_unregister (GRUB_FILE_FILTER_PUBKEY); + grub_verifier_unregister (&grub_pubkey_verifier); grub_unregister_extcmd (cmd); grub_unregister_extcmd (cmd_trust); grub_unregister_command (cmd_list); diff --git a/include/grub/file.h b/include/grub/file.h index 5b47c5f91..19dda67f6 100644 --- a/include/grub/file.h +++ b/include/grub/file.h @@ -171,7 +171,7 @@ extern grub_disk_read_hook_t EXPORT_VAR(grub_file_progress_hook); /* Filters with lower ID are executed first. */ typedef enum grub_file_filter_id { - GRUB_FILE_FILTER_PUBKEY, + GRUB_FILE_FILTER_VERIFY, GRUB_FILE_FILTER_GZIO, GRUB_FILE_FILTER_XZIO, GRUB_FILE_FILTER_LZOPIO, diff --git a/include/grub/list.h b/include/grub/list.h index d170ff6da..b13acb962 100644 --- a/include/grub/list.h +++ b/include/grub/list.h @@ -35,6 +35,7 @@ void EXPORT_FUNC(grub_list_push) (grub_list_t *head, grub_list_t item); void EXPORT_FUNC(grub_list_remove) (grub_list_t item); #define FOR_LIST_ELEMENTS(var, list) for ((var) = (list); (var); (var) = (var)->next) +#define FOR_LIST_ELEMENTS_NEXT(var, list) for ((var) = (var)->next; (var); (var) = (var)->next) #define FOR_LIST_ELEMENTS_SAFE(var, nxt, list) for ((var) = (list), (nxt) = ((var) ? (var)->next : 0); (var); (var) = (nxt), ((nxt) = (var) ? (var)->next : 0)) static inline void * diff --git a/include/grub/verify.h b/include/grub/verify.h new file mode 100644 index 000000000..298120f57 --- /dev/null +++ b/include/grub/verify.h @@ -0,0 +1,65 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2017 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 . + */ + +#include +#include + +enum grub_verify_flags + { + GRUB_VERIFY_FLAGS_SKIP_VERIFICATION = 1, + GRUB_VERIFY_FLAGS_SINGLE_CHUNK = 2 + }; + +struct grub_file_verifier +{ + struct grub_file_verifier *next; + struct grub_file_verifier **prev; + + const char *name; + + /* + * Check if file needs to be verified and set up context. + * init/read/fini is structured in the same way as hash interface. + */ + grub_err_t (*init) (grub_file_t io, enum grub_file_type type, + void **context, enum grub_verify_flags *flags); + + /* + * Right now we pass the whole file in one call but it may + * change in the future. If you insist on single buffer you + * need to set GRUB_VERIFY_FLAGS_SINGLE_CHUNK in verify_flags. + */ + grub_err_t (*write) (void *context, void *buf, grub_size_t size); + + grub_err_t (*fini) (void *context); + void (*close) (void *context); +}; + +extern struct grub_file_verifier *grub_file_verifiers; + +static inline void +grub_verifier_register (struct grub_file_verifier *ver) +{ + grub_list_push (GRUB_AS_LIST_P (&grub_file_verifiers), GRUB_AS_LIST (ver)); +} + +static inline void +grub_verifier_unregister (struct grub_file_verifier *ver) +{ + grub_list_remove (GRUB_AS_LIST (ver)); +}